From 8f02e3e462dc7d4bd3ba781592a1a5fa7c3152cf Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Sat, 2 Jan 2021 12:40:59 +0100
Subject: [PATCH 001/518] Fix PRIdS define for Windows MINGW64 and DJGPP

Make the PRIdS define into "zu" instead of "Iu" for MINGW64
Fix checking for "DJGPP" instead of "__DJGPP__"
---
 src/m_misc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/m_misc.c b/src/m_misc.c
index 42890cb08a..ef48dde004 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -62,9 +62,9 @@ typedef off_t off64_t;
 
 #if defined(__MINGW32__) && ((__GNUC__ > 7) || (__GNUC__ == 6 && __GNUC_MINOR__ >= 3)) && (__GNUC__ < 8)
 #define PRIdS "u"
-#elif defined (_WIN32)
+#elif defined(_WIN32) && !defined(__MINGW64__)
 #define PRIdS "Iu"
-#elif defined (DJGPP)
+#elif defined(__DJGPP__)
 #define PRIdS "u"
 #else
 #define PRIdS "zu"
-- 
GitLab


From a1a58143ec3e39b9cf76cdaeb84c6a07646a73e4 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Tue, 31 Aug 2021 19:00:05 -0700
Subject: [PATCH 002/518] Fix IPv6 address checks

- SOCK_cmpaddr returned inverted truth for IPv6 addresses.
  This would prevent making a connection.
- Account for IPv6 address resolution. (bug: 5a627482)
---
 src/i_tcp.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/src/i_tcp.c b/src/i_tcp.c
index cae97a7d10..3d5d302f84 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -427,7 +427,7 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
 			&& (b->ip4.sin_port == 0 || (a->ip4.sin_port == b->ip4.sin_port));
 #ifdef HAVE_IPV6
 	else if (b->any.sa_family == AF_INET6)
-		return memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, sizeof(b->ip6.sin6_addr))
+		return !memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, sizeof(b->ip6.sin6_addr))
 			&& (b->ip6.sin6_port == 0 || (a->ip6.sin6_port == b->ip6.sin6_port));
 #endif
 	else
@@ -1156,6 +1156,7 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
 	SINT8 newnode = -1;
 	struct my_addrinfo *ai = NULL, *runp, hints;
 	int gaie;
+	size_t i;
 
 	 if (!port || !port[0])
 		port = DEFAULTPORT;
@@ -1183,13 +1184,20 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
 
 	while (runp != NULL)
 	{
-		// find ip of the server
-		if (sendto(mysockets[0], NULL, 0, 0, runp->ai_addr, runp->ai_addrlen) == 0)
+		// test ip address of server
+		for (i = 0; i < mysocketses; ++i)
 		{
-			memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
-			break;
+			if (runp->ai_addr->sa_family == myfamily[i])
+			{
+				memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
+				break;
+			}
 		}
-		runp = runp->ai_next;
+
+		if (i < mysocketses)
+			runp = runp->ai_next;
+		else
+			break;
 	}
 	I_freeaddrinfo(ai);
 	return newnode;
-- 
GitLab


From f8a749be091176d1548fd2dc09eda4901d152d29 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Tue, 31 Aug 2021 19:03:23 -0700
Subject: [PATCH 003/518] Correctly set IPV6_V6ONLY

setsockopt level for v6 options is IPPROTO_IPV6.
---
 src/i_tcp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/i_tcp.c b/src/i_tcp.c
index 3d5d302f84..50d1a57fa5 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -788,7 +788,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 		// make it IPv6 ony
 		opt = true;
 		opts = (socklen_t)sizeof(opt);
-		if (setsockopt(s, SOL_SOCKET, IPV6_V6ONLY, (char *)&opt, opts))
+		if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, opts))
 		{
 			CONS_Alert(CONS_WARNING, M_GetText("Could not limit IPv6 bind\n")); // I do not care anymore
 		}
-- 
GitLab


From 0422870df85932748d8166b3ce46dde3f87f0417 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Tue, 31 Aug 2021 19:54:27 -0700
Subject: [PATCH 004/518] Use RFC2732 notation for IPv6 addresses

This fixes keepbody and the connect command (also
'-connect' parameter).

connect ::1
connect [::1]:5029
---
 src/d_clisrv.c | 19 ++++++++-----------
 src/d_net.c    | 26 +++++++++++++++++---------
 src/i_net.h    | 11 +++++++++++
 src/i_tcp.c    | 21 +++++++++++++++++----
 4 files changed, 53 insertions(+), 24 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index aa7bc8a6cd..e9a70c8ecb 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1171,9 +1171,9 @@ static boolean CL_SendJoin(void)
 
 static INT32 FindRejoinerNum(SINT8 node)
 {
-	char strippednodeaddress[64];
+	char addressbuffer[64];
 	const char *nodeaddress;
-	char *port;
+	const char *strippednodeaddress;
 	INT32 i;
 
 	// Make sure there is no dead dress before proceeding to the stripping
@@ -1184,10 +1184,8 @@ static INT32 FindRejoinerNum(SINT8 node)
 		return -1;
 
 	// Strip the address of its port
-	strcpy(strippednodeaddress, nodeaddress);
-	port = strchr(strippednodeaddress, ':');
-	if (port)
-		*port = '\0';
+	strcpy(addressbuffer, nodeaddress);
+	strippednodeaddress = I_NetSplitAddress(addressbuffer, NULL);
 
 	// Check if any player matches the stripped address
 	for (i = 0; i < MAXPLAYERS; i++)
@@ -3426,14 +3424,13 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 
 		if (server && I_GetNodeAddress)
 		{
+			char addressbuffer[64];
 			const char *address = I_GetNodeAddress(node);
-			char *port = NULL;
 			if (address) // MI: fix msvcrt.dll!_mbscat crash?
 			{
-				strcpy(playeraddress[newplayernum], address);
-				port = strchr(playeraddress[newplayernum], ':');
-				if (port)
-					*port = '\0';
+				strcpy(addressbuffer, address);
+				strcpy(playeraddress[newplayernum],
+						I_NetSplitAddress(addressbuffer, NULL));
 			}
 		}
 	}
diff --git a/src/d_net.c b/src/d_net.c
index 9e5abe24a0..6906ac123e 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -1200,26 +1200,34 @@ static void Internal_FreeNodenum(INT32 nodenum)
 	(void)nodenum;
 }
 
+char *I_NetSplitAddress(char *host, char **port)
+{
+	boolean v4 = (strchr(host, '.') != NULL);
+
+	host = strtok(host, v4 ? ":" : "[]");
+
+	if (port)
+		*port = strtok(NULL, ":");
+
+	return host;
+}
+
 SINT8 I_NetMakeNode(const char *hostname)
 {
 	SINT8 newnode = -1;
 	if (I_NetMakeNodewPort)
 	{
 		char *localhostname = strdup(hostname);
-		char  *t = localhostname;
-		const char *port;
+		char *port;
 		if (!localhostname)
 			return newnode;
+
 		// retrieve portnum from address!
-		strtok(localhostname, ":");
-		port = strtok(NULL, ":");
+		hostname = I_NetSplitAddress(localhostname, &port);
 
-		// remove the port in the hostname as we've it already
-		while ((*t != ':') && (*t != '\0'))
-			t++;
-		*t = '\0';
+		CONS_Printf("%s %s\n", hostname, port);
 
-		newnode = I_NetMakeNodewPort(localhostname, port);
+		newnode = I_NetMakeNodewPort(hostname, port);
 		free(localhostname);
 	}
 	return newnode;
diff --git a/src/i_net.h b/src/i_net.h
index dbc82db65c..2d09af750e 100644
--- a/src/i_net.h
+++ b/src/i_net.h
@@ -109,6 +109,17 @@ extern boolean (*I_NetCanSend)(void);
 */
 extern void (*I_NetFreeNodenum)(INT32 nodenum);
 
+/**
+	\brief	split a string into address and port
+
+	\param	address	string to split
+
+	\param	port	double pointer to hold port component (optional)
+
+	\return	address component
+*/
+extern char *I_NetSplitAddress(char *address, char **port);
+
 /**	\brief	open a connection with specified address
 
 	\param	address	address to connect to
diff --git a/src/i_tcp.c b/src/i_tcp.c
index 50d1a57fa5..ca55515ab2 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -340,8 +340,14 @@ static inline void I_UPnP_rem(const char *port, const char * servicetype)
 
 static const char *SOCK_AddrToStr(mysockaddr_t *sk)
 {
-	static char s[64]; // 255.255.255.255:65535 or IPv6:65535
+	static char s[64]; // 255.255.255.255:65535 or
+	// [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
 #ifdef HAVE_NTOP
+#ifdef HAVE_IPV6
+	int v6 = (sk->any.sa_family == AF_INET6);
+#else
+	int v6 = 0;
+#endif
 	void *addr;
 
 	if(sk->any.sa_family == AF_INET)
@@ -355,14 +361,21 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk)
 
 	if(addr == NULL)
 		sprintf(s, "No address");
-	else if(inet_ntop(sk->any.sa_family, addr, s, sizeof (s)) == NULL)
+	else if(inet_ntop(sk->any.sa_family, addr, &s[v6], sizeof (s) - v6) == NULL)
 		sprintf(s, "Unknown family type, error #%u", errno);
 #ifdef HAVE_IPV6
-	else if(sk->any.sa_family == AF_INET6 && sk->ip6.sin6_port != 0)
-		strcat(s, va(":%d", ntohs(sk->ip6.sin6_port)));
+	else if(sk->any.sa_family == AF_INET6)
+	{
+		s[0] = '[';
+		strcat(s, "]");
+
+		if (sk->ip6.sin6_port != 0)
+			strcat(s, va(":%d", ntohs(sk->ip6.sin6_port)));
+	}
 #endif
 	else if(sk->any.sa_family == AF_INET  && sk->ip4.sin_port  != 0)
 		strcat(s, va(":%d", ntohs(sk->ip4.sin_port)));
+
 #else
 	if (sk->any.sa_family == AF_INET)
 	{
-- 
GitLab


From 44bafe0a325768e8cf8cf04e7802804961751865 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Tue, 31 Aug 2021 20:00:20 -0700
Subject: [PATCH 005/518] Remove debug print

---
 src/d_net.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/d_net.c b/src/d_net.c
index 6906ac123e..ecf446529f 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -1225,8 +1225,6 @@ SINT8 I_NetMakeNode(const char *hostname)
 		// retrieve portnum from address!
 		hostname = I_NetSplitAddress(localhostname, &port);
 
-		CONS_Printf("%s %s\n", hostname, port);
-
 		newnode = I_NetMakeNodewPort(hostname, port);
 		free(localhostname);
 	}
-- 
GitLab


From 32ad1f2067d05d2e82a233b67c68d649e4ceaabe Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Tue, 31 Aug 2021 20:02:09 -0700
Subject: [PATCH 006/518] Make IPv6 default

An IPv4 socket is still made. The '-ipv6' parameter is
replaced with '-noipv6', self explanatory.
---
 src/i_tcp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/i_tcp.c b/src/i_tcp.c
index ca55515ab2..7878d1fbbb 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -857,7 +857,7 @@ static boolean UDP_Socket(void)
 	struct my_addrinfo *ai, *runp, hints;
 	int gaie;
 #ifdef HAVE_IPV6
-	const INT32 b_ipv6 = M_CheckParm("-ipv6");
+	const INT32 b_ipv6 = !M_CheckParm("-noipv6");
 #endif
 	const char *serv;
 
-- 
GitLab


From c9631565d62f6899b8f052bba698ebe13e4db69e Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Wed, 1 Sep 2021 17:35:41 -0700
Subject: [PATCH 007/518] Fix address structure handling in UDP_Bind

Fixed instances of copying only sizeof (struct sockaddr)
bytes (not enough for struct sockaddr_in6), as well as
trying to getsockname into an insufficient buffer.
---
 src/i_tcp.c | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/src/i_tcp.c b/src/i_tcp.c
index 7878d1fbbb..9cdcc6ec47 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -748,8 +748,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 	unsigned long trueval = true;
 #endif
 	mysockaddr_t straddr;
-	struct sockaddr_in sin;
-	socklen_t len = sizeof(sin);
+	socklen_t len = sizeof(straddr);
 
 	if (s == (SOCKET_TYPE)ERRSOCKET)
 		return (SOCKET_TYPE)ERRSOCKET;
@@ -767,14 +766,12 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 	}
 #endif
 
-	straddr.any = *addr;
+	memcpy(&straddr, addr, addrlen);
 	I_OutputMsg("Binding to %s\n", SOCK_AddrToStr(&straddr));
 
 	if (family == AF_INET)
 	{
-		mysockaddr_t tmpaddr;
-		tmpaddr.any = *addr ;
-		if (tmpaddr.ip4.sin_addr.s_addr == htonl(INADDR_ANY))
+		if (straddr.ip4.sin_addr.s_addr == htonl(INADDR_ANY))
 		{
 			opt = true;
 			opts = (socklen_t)sizeof(opt);
@@ -791,7 +788,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 #ifdef HAVE_IPV6
 	else if (family == AF_INET6)
 	{
-		if (memcmp(addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL
+		if (memcmp(&straddr.ip6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL
 		{
 			opt = true;
 			opts = (socklen_t)sizeof(opt);
@@ -843,10 +840,17 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 			CONS_Printf(M_GetText("Network system buffer set to: %dKb\n"), opt>>10);
 	}
 
-	if (getsockname(s, (struct sockaddr *)&sin, &len) == -1)
+	if (getsockname(s, &straddr.any, &len) == -1)
 		CONS_Alert(CONS_WARNING, M_GetText("Failed to get port number\n"));
 	else
-		current_port = (UINT16)ntohs(sin.sin_port);
+	{
+		if (family == AF_INET)
+			current_port = (UINT16)ntohs(straddr.ip4.sin_port);
+#ifdef HAVE_IPV6
+		else if (family == AF_INET6)
+			current_port = (UINT16)ntohs(straddr.ip6.sin6_port);
+#endif
+	}
 
 	return s;
 }
-- 
GitLab


From 7976d0476d9ab3148c1e4dd24c477c686aa35b14 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Wed, 1 Sep 2021 18:02:39 -0700
Subject: [PATCH 008/518] Enbiglarge ip field of server list to fit IPv6
 addresses

---
 src/mserv.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/mserv.h b/src/mserv.h
index 7a3b3d8ec8..b97abb634c 100644
--- a/src/mserv.h
+++ b/src/mserv.h
@@ -33,7 +33,7 @@ typedef union
 typedef struct
 {
 	msg_header_t header;
-	char ip[16];
+	char ip[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
 	char port[8];
 	char name[32];
 	INT32 room;
-- 
GitLab


From 31943f30a1829a51620f8d7d3437634105185cde Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Wed, 1 Sep 2021 18:32:18 -0700
Subject: [PATCH 009/518] Support connecting by IPv6 address in the menu

Font will shrink once exceeding a certain number of
characters.
---
 src/m_menu.c | 56 ++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 39 insertions(+), 17 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index db2aa09c63..35dae1d031 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -968,7 +968,7 @@ static menuitem_t MP_MainMenu[] =
 {
 	{IT_HEADER, NULL, "Join a game", NULL, 0},
 	{IT_STRING|IT_CALL,       NULL, "Server browser...",     M_ConnectMenuModChecks,          12},
-	{IT_STRING|IT_KEYHANDLER, NULL, "Specify IPv4 address:", M_HandleConnectIP,      22},
+	{IT_STRING|IT_KEYHANDLER, NULL, "Specify server address:", M_HandleConnectIP,    22},
 	{IT_HEADER, NULL, "Host a game", NULL, 54},
 	{IT_STRING|IT_CALL,       NULL, "Internet/LAN...",       M_StartServerMenu,      66},
 	{IT_STRING|IT_CALL,       NULL, "Splitscreen...",        M_StartSplitServerMenu, 76},
@@ -11605,7 +11605,35 @@ static void M_StartServerMenu(INT32 choice)
 // CONNECT VIA IP
 // ==============
 
-static char setupm_ip[28];
+static char setupm_ip[sizeof "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535"];
+
+static void M_DrawConnectIP(void)
+{
+	INT32 x = currentMenu->x;
+	INT32 y = currentMenu->y + 22;
+
+	INT32 opt = V_ALLOWLOWERCASE;
+	INT32 width;
+
+	V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 159);
+
+	// draw name string
+	if (strlen(setupm_ip) > 30)/* w stands for wide boi */
+	{
+		V_DrawThinString(x+8,y+12, opt, setupm_ip);
+		width = V_ThinStringWidth(setupm_ip, opt);
+	}
+	else
+	{
+		V_DrawString(x+8,y+12, opt, setupm_ip);
+		width = V_StringWidth(setupm_ip, opt);
+	}
+
+	// draw text cursor for name
+	if (itemOn == 2 //0
+	    && skullAnimCounter < 4)   //blink cursor
+		V_DrawCharacter(x+8+width,y+12,'_',false);
+}
 
 // Draw the funky Connect IP menu. Tails 11-19-2002
 // So much work for such a little thing!
@@ -11626,17 +11654,7 @@ static void M_DrawMPMainMenu(void)
 	V_DrawRightAlignedString(BASEVIDWIDTH-x, y+116,
 		((itemOn == 8) ? V_YELLOWMAP : 0), "(splitscreen)");
 
-	y += 22;
-
-	V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 159);
-
-	// draw name string
-	V_DrawString(x+8,y+12, V_ALLOWLOWERCASE, setupm_ip);
-
-	// draw text cursor for name
-	if (itemOn == 2 //0
-	    && skullAnimCounter < 4)   //blink cursor
-		V_DrawCharacter(x+8+V_StringWidth(setupm_ip, V_ALLOWLOWERCASE),y+12,'_',false);
+	M_DrawConnectIP();
 }
 
 // Tails 11-19-2002
@@ -11719,7 +11737,7 @@ static void M_HandleConnectIP(INT32 choice)
 						const char *paste = I_ClipboardPaste();
 
 						if (paste != NULL) {
-							strncat(setupm_ip, paste, 28-1 - l); // Concat the ip field with clipboard
+							strncat(setupm_ip, paste, (sizeof setupm_ip)-1 - l); // Concat the ip field with clipboard
 							if (strlen(paste) != 0) // Don't play sound if nothing was pasted
 								S_StartSound(NULL,sfx_menu1); // Tails
 						}
@@ -11753,7 +11771,7 @@ static void M_HandleConnectIP(INT32 choice)
 							const char *paste = I_ClipboardPaste();
 
 							if (paste != NULL) {
-								strncat(setupm_ip, paste, 28-1 - l); // Concat the ip field with clipboard
+								strncat(setupm_ip, paste, (sizeof setupm_ip)-1 - l); // Concat the ip field with clipboard
 								if (strlen(paste) != 0) // Don't play sound if nothing was pasted
 									S_StartSound(NULL,sfx_menu1); // Tails
 							}
@@ -11770,11 +11788,15 @@ static void M_HandleConnectIP(INT32 choice)
 				}
 			}
 
-			if (l >= 28-1)
+			if (l >= (sizeof setupm_ip)-1)
 				break;
 
 			// Rudimentary number and period enforcing - also allows letters so hostnames can be used instead
-			if ((choice >= '-' && choice <= ':') || (choice >= 'A' && choice <= 'Z') || (choice >= 'a' && choice <= 'z'))
+			// and square brackets for RFC 2732 IPv6 addresses
+			if ((choice >= '-' && choice <= ':') ||
+					(choice == '[' || choice == ']') ||
+					(choice >= 'A' && choice <= 'Z') ||
+					(choice >= 'a' && choice <= 'z'))
 			{
 				S_StartSound(NULL,sfx_menu1); // Tails
 				setupm_ip[l] = (char)choice;
-- 
GitLab


From 46a5ce4325fbe23234a8167bd16bc4ef6bf7ceee Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Wed, 1 Sep 2021 18:41:41 -0700
Subject: [PATCH 010/518] Only restrict master server connection to IPv4 if
 -noipv6

---
 src/http-mserv.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/http-mserv.c b/src/http-mserv.c
index f9134ba500..a0912a81ee 100644
--- a/src/http-mserv.c
+++ b/src/http-mserv.c
@@ -196,7 +196,9 @@ HMS_connect (const char *format, ...)
 
 	curl_easy_setopt(curl, CURLOPT_URL, url);
 	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
-	curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+
+	if (M_CheckParm("-noipv6"))
+		curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
 
 	curl_easy_setopt(curl, CURLOPT_TIMEOUT, cv_masterserver_timeout.value);
 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read);
-- 
GitLab


From 39fdbac2ace76cc1ead1b92b4f32d2aa17ad99d2 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Fri, 3 Sep 2021 16:41:08 -0700
Subject: [PATCH 011/518] Only test -noipv6 if enabled at compile time

---
 src/http-mserv.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/http-mserv.c b/src/http-mserv.c
index a0912a81ee..0ab16f9898 100644
--- a/src/http-mserv.c
+++ b/src/http-mserv.c
@@ -197,7 +197,9 @@ HMS_connect (const char *format, ...)
 	curl_easy_setopt(curl, CURLOPT_URL, url);
 	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
 
+#ifndef NO_IPV6
 	if (M_CheckParm("-noipv6"))
+#endif
 		curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
 
 	curl_easy_setopt(curl, CURLOPT_TIMEOUT, cv_masterserver_timeout.value);
-- 
GitLab


From b2fe36fe5b504ae2221a4306f76e663fff38090f Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Fri, 3 Sep 2021 16:48:29 -0700
Subject: [PATCH 012/518] Add back address resolution test

Mistakenly removed by a1a58143e
---
 src/i_tcp.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/i_tcp.c b/src/i_tcp.c
index 9cdcc6ec47..48a65faae3 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -1204,7 +1204,11 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
 		// test ip address of server
 		for (i = 0; i < mysocketses; ++i)
 		{
-			if (runp->ai_addr->sa_family == myfamily[i])
+			/* sendto tests that there is a network to this
+				address */
+			if (runp->ai_addr->sa_family == myfamily[i] &&
+					sendto(mysockets[i], NULL, 0, 0,
+						runp->ai_addr, runp->ai_addrlen) == 0)
 			{
 				memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
 				break;
-- 
GitLab


From 4424f6ba9a59b06c10d0bb683c8e00cded1d90db Mon Sep 17 00:00:00 2001
From: tertu marybig <flameshadowxeroshin@gmail.com>
Date: Tue, 28 Sep 2021 08:31:14 -0500
Subject: [PATCH 013/518] Remove FixedMul and FixedDiv2 asm implementations
 Actually works this time

---
 src/m_fixed.c |  44 ----------------
 src/m_fixed.h | 136 ++++++++------------------------------------------
 2 files changed, 22 insertions(+), 158 deletions(-)

diff --git a/src/m_fixed.c b/src/m_fixed.c
index d40ccd98e3..315ac0bde5 100644
--- a/src/m_fixed.c
+++ b/src/m_fixed.c
@@ -21,50 +21,6 @@
 #include "doomdef.h"
 #include "m_fixed.h"
 
-#ifdef __USE_C_FIXEDMUL__
-
-/**	\brief	The FixedMul function
-
-	\param	a	fixed_t number
-	\param	b	fixed_t number
-
-	\return	a*b>>FRACBITS
-
-*/
-fixed_t FixedMul(fixed_t a, fixed_t b)
-{
-	// Need to cast to unsigned before shifting to avoid undefined behaviour
-	// for negative integers
-	return (fixed_t)(((UINT64)((INT64)a * b)) >> FRACBITS);
-}
-
-#endif //__USE_C_FIXEDMUL__
-
-#ifdef __USE_C_FIXEDDIV__
-/**	\brief	The FixedDiv2 function
-
-	\param	a	fixed_t number
-	\param	b	fixed_t number
-
-	\return	a/b * FRACUNIT
-
-*/
-fixed_t FixedDiv2(fixed_t a, fixed_t b)
-{
-	INT64 ret;
-
-	if (b == 0)
-		I_Error("FixedDiv: divide by zero");
-
-	ret = (((INT64)a * FRACUNIT)) / b;
-
-	if ((ret > INT32_MAX) || (ret < INT32_MIN))
-		I_Error("FixedDiv: divide by zero");
-	return (fixed_t)ret;
-}
-
-#endif // __USE_C_FIXEDDIV__
-
 fixed_t FixedSqrt(fixed_t x)
 {
 #ifdef HAVE_SQRT
diff --git a/src/m_fixed.h b/src/m_fixed.h
index 1cf2f00d1e..83b1710f77 100644
--- a/src/m_fixed.h
+++ b/src/m_fixed.h
@@ -53,127 +53,35 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FloatToFixed(float f)
 #define FIXED_TO_FLOAT(x) FixedToFloat(x) // (((float)(x)) / ((float)FRACUNIT))
 #define FLOAT_TO_FIXED(f) FloatToFixed(f) // (fixed_t)((f) * ((float)FRACUNIT))
 
+/**	\brief	The FixedMul function
 
-#if defined (__WATCOMC__) && FRACBITS == 16
-	#pragma aux FixedMul =  \
-		"imul ebx",         \
-		"shrd eax,edx,16"   \
-		parm    [eax] [ebx] \
-		value   [eax]       \
-		modify exact [eax edx]
-
-	#pragma aux FixedDiv2 = \
-		"cdq",              \
-		"shld edx,eax,16",  \
-		"sal eax,16",       \
-		"idiv ebx"          \
-		parm    [eax] [ebx] \
-		value   [eax]       \
-		modify exact [eax edx]
-#elif defined (__GNUC__) && defined (__i386__) && !defined (NOASM)
-	// i386 linux, cygwin or mingw
-	FUNCMATH FUNCINLINE static inline fixed_t FixedMul(fixed_t a, fixed_t b) // asm
-	{
-		fixed_t ret;
-		asm
-		(
-			 "imull %2;"           // a*b
-			 "shrdl %3,%%edx,%0;"  // shift logical right FRACBITS bits
-			:"=a" (ret)            // eax is always the result and the first operand (%0,%1)
-			:"0" (a), "r" (b)      // and %2 is what we use imull on with what in %1
-			, "I" (FRACBITS)       // %3 holds FRACBITS (normally 16)
-			:"cc", "%edx"         // edx and condition codes clobbered
-		);
-		return ret;
-	}
+	\param	a	fixed_t number
+	\param	b	fixed_t number
 
-	FUNCMATH FUNCINLINE static inline fixed_t FixedDiv2(fixed_t a, fixed_t b)
-	{
-		fixed_t ret;
-		asm
-		(
-			  "movl  %1,%%edx;"    // these two instructions allow the next two to pair, on the Pentium processor.
-			  "sarl  $31,%%edx;"   // shift arithmetic right 31 on EDX
-			  "shldl %3,%1,%%edx;" // DP shift logical left FRACBITS on EDX
-			  "sall  %3,%0;"       // shift arithmetic left FRACBITS on EAX
-			  "idivl %2;"          // EDX/b = EAX
-			: "=a" (ret)
-			: "0" (a), "r" (b)
-			, "I" (FRACBITS)
-			: "%edx"
-		);
-		return ret;
-	}
-#elif defined (__GNUC__) && defined (__arm__) && !defined(__thumb__) && !defined(NOASM) //ARMv4 ASM
-	FUNCMATH FUNCINLINE static inline fixed_t FixedMul(fixed_t a, fixed_t b) // let abuse smull
-	{
-		fixed_t ret;
-		asm
-		(
-			  "smull %[lo], r1, %[a], %[b];"
-			  "mov %[lo], %[lo], lsr %3;"
-			  "orr %[lo], %[lo], r1, lsl %3;"
-			: [lo] "=&r" (ret) // rhi, rlo and rm must be distinct registers
-			: [a] "r" (a), [b] "r" (b)
-			, "i" (FRACBITS)
-			: "r1"
-		);
-		return ret;
-	}
+	\return	a*b>>FRACBITS
 
-	#define __USE_C_FIXEDDIV__ // no double or asm div in ARM land
-#elif defined (__GNUC__) && defined (__ppc__) && !defined(NOASM) && 0 // WII: PPC CPU
-	FUNCMATH FUNCINLINE static inline fixed_t FixedMul(fixed_t a, fixed_t b) // asm
-	{
-		fixed_t ret, hi, lo;
-		asm
-		(
-			  "mullw %0, %2, %3;"
-			  "mulhw %1, %2, %3"
-			: "=r" (hi), "=r" (lo)
-			: "r" (a), "r" (b)
-			, "I" (FRACBITS)
-		);
-		ret = (INT64)((hi>>FRACBITS)+lo)<<FRACBITS;
-		return ret;
-	}
+*/
+FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedMul(fixed_t a, fixed_t b)
+{
+	// Need to cast to unsigned before shifting to avoid undefined behaviour
+	// for negative integers
+	return (fixed_t)(((UINT64)((INT64)a * b)) >> FRACBITS);
+}
 
-	#define __USE_C_FIXEDDIV__// Alam: I am lazy
-#elif defined (__GNUC__) && defined (__mips__) && !defined(NOASM) && 0 // PSP: MIPS CPU
-	FUNCMATH FUNCINLINE static inline fixed_t FixedMul(fixed_t a, fixed_t b) // asm
-	{
-		fixed_t ret;
-		asm
-		(
-			  "mult %3, %4;"    // a*b=h<32+l
-			: "=r" (ret), "=l" (a), "=h" (b) //todo: abuse shr opcode
-			: "0" (a), "r" (b)
-			, "I" (FRACBITS)
-			//: "+l", "+h"
-		);
-		ret = (INT64)((a>>FRACBITS)+b)<<FRACBITS;
-		return ret;
-	}
+/**	\brief	The FixedDiv2 function
 
-	#define __USE_C_FIXEDDIV__ // no 64b asm div in MIPS land
-#elif defined (__GNUC__) && defined (__sh__) && 0 // DC: SH4 CPU
-#elif defined (__GNUC__) && defined (__m68k__) && 0 // DEAD: Motorola 6800 CPU
-#elif defined (_MSC_VER) && defined(USEASM) && FRACBITS == 16
-	// Microsoft Visual C++ (no asm inline)
-	fixed_t __cdecl FixedMul(fixed_t a, fixed_t b);
-	fixed_t __cdecl FixedDiv2(fixed_t a, fixed_t b);
-#else
-	#define __USE_C_FIXEDMUL__
-	#define __USE_C_FIXEDDIV__
-#endif
+	\param	a	fixed_t number
+	\param	b	fixed_t number
 
-#ifdef __USE_C_FIXEDMUL__
-FUNCMATH fixed_t FixedMul(fixed_t a, fixed_t b);
-#endif
+	\return	a/b * FRACUNIT
 
-#ifdef __USE_C_FIXEDDIV__
-FUNCMATH fixed_t FixedDiv2(fixed_t a, fixed_t b);
-#endif
+*/
+FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedDiv2(fixed_t a, fixed_t b)
+{
+	// This does not check for division overflow or division by 0!
+	// That is the caller's responsibility.
+	return (fixed_t)(((INT64)a * FRACUNIT) / b);
+}
 
 /**	\brief	The FixedInt function
 
-- 
GitLab


From 895c3bf19bd0922e87c772bfc895991b274f40e6 Mon Sep 17 00:00:00 2001
From: katsy <katmint@live.com>
Date: Tue, 7 Dec 2021 17:10:38 -0600
Subject: [PATCH 014/518] fix spike ring catapult

---
 src/p_map.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_map.c b/src/p_map.c
index 836e75c4e8..7e754bed99 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -1144,7 +1144,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	}
 
 	// When solid spikes move, assume they just popped up and teleport things on top of them to hurt.
-	if (tmthing->type == MT_SPIKE && tmthing->flags & MF_SOLID)
+	if (tmthing->type == MT_SPIKE && (thing->flags & MF_SOLID) && (tmthing->flags & MF_SOLID))
 	{
 		if (thing->z > tmthing->z + tmthing->height)
 			return true; // overhead
-- 
GitLab


From d764d68d02c1f7609c65534f7cfa07da724c42cd Mon Sep 17 00:00:00 2001
From: lachablock <lachsrb2@gmail.com>
Date: Thu, 13 Jan 2022 18:53:26 +1100
Subject: [PATCH 015/518] Turn dispoffset into a mobj field

---
 src/hardware/hw_glob.h |  2 +-
 src/hardware/hw_main.c |  2 +-
 src/lua_mobjlib.c      | 10 +++++++++-
 src/p_mobj.c           |  2 ++
 src/p_mobj.h           |  1 +
 src/p_saveg.c          |  9 +++++++++
 src/r_things.c         |  2 +-
 src/r_things.h         |  2 +-
 8 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index 37d77b4673..68c5dd14dd 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -84,7 +84,7 @@ typedef struct gl_vissprite_s
 
 	//Hurdler: 25/04/2000: now support colormap in hardware mode
 	UINT8 *colormap;
-	INT32 dispoffset; // copy of info->dispoffset, affects ordering but not drawing
+	INT32 dispoffset; // copy of mobj->dispoffset, affects ordering but not drawing
 
 	patch_t *gpatch;
 	mobj_t *mobj; // NOTE: This is a precipmobj_t if precip is true !!! Watch out.
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index d2982afe44..ab2b7cae29 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -5052,7 +5052,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 			return;
 	}
 
-	dispoffset = thing->info->dispoffset;
+	dispoffset = thing->dispoffset;
 
 	this_scale = FIXED_TO_FLOAT(thing->scale);
 	spritexscale = FIXED_TO_FLOAT(thing->spritexscale);
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index cf8ccab2ce..ffdfe999d5 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -96,7 +96,8 @@ enum mobj_e {
 	mobj_standingslope,
 	mobj_colorized,
 	mobj_mirrored,
-	mobj_shadowscale
+	mobj_shadowscale,
+	mobj_dispoffset
 };
 
 static const char *const mobj_opt[] = {
@@ -173,6 +174,7 @@ static const char *const mobj_opt[] = {
 	"colorized",
 	"mirrored",
 	"shadowscale",
+	"dispoffset",
 	NULL};
 
 #define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field])
@@ -439,6 +441,9 @@ static int mobj_get(lua_State *L)
 	case mobj_shadowscale:
 		lua_pushfixed(L, mo->shadowscale);
 		break;
+	case mobj_dispoffset:
+		lua_pushinteger(L, mo->dispoffset);
+		break;
 	default: // extra custom variables in Lua memory
 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
 		I_Assert(lua_istable(L, -1));
@@ -803,6 +808,9 @@ static int mobj_set(lua_State *L)
 	case mobj_shadowscale:
 		mo->shadowscale = luaL_checkfixed(L, 3);
 		break;
+	case mobj_dispoffset:
+		mo->dispoffset = luaL_checkinteger(L, 3);
+		break;
 	default:
 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
 		I_Assert(lua_istable(L, -1));
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 87e20fd4ac..10a01dbaa3 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -10499,6 +10499,8 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 
 	mobj->reactiontime = info->reactiontime;
 
+	mobj->dispoffset = info->dispoffset;
+
 	mobj->lastlook = -1; // stuff moved in P_enemy.P_LookForPlayer
 
 	// do not set the state with P_SetMobjState,
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 2d096385bd..d078137d88 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -390,6 +390,7 @@ typedef struct mobj_s
 	boolean colorized; // Whether the mobj uses the rainbow colormap
 	boolean mirrored; // The object's rotations will be mirrored left to right, e.g., see frame AL from the right and AR from the left
 	fixed_t shadowscale; // If this object casts a shadow, and the size relative to radius
+	INT32 dispoffset; // copy of info->dispoffset, so mobjs can be sorted independently of their type
 
 	// WARNING: New fields must be added separately to savegame and Lua.
 } mobj_t;
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 722340f41f..17c47c7a17 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1486,6 +1486,7 @@ typedef enum
 	MD2_SPRITEXOFFSET = 1<<20,
 	MD2_SPRITEYOFFSET = 1<<21,
 	MD2_FLOORSPRITESLOPE = 1<<22,
+	MD2_DISPOFFSET = 1<<23
 } mobj_diff2_t;
 
 typedef enum
@@ -1720,6 +1721,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		|| (slope->normal.z != FRACUNIT))
 			diff2 |= MD2_FLOORSPRITESLOPE;
 	}
+	if (mobj->dispoffset != mobj->info->dispoffset)
+		diff2 |= MD2_DISPOFFSET;
 
 	if (diff2 != 0)
 		diff |= MD_MORE;
@@ -1895,6 +1898,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		WRITEFIXED(save_p, slope->normal.y);
 		WRITEFIXED(save_p, slope->normal.z);
 	}
+	if (diff2 & MD2_DISPOFFSET)
+		WRITEINT32(save_p, mobj->dispoffset);
 
 	WRITEUINT32(save_p, mobj->mobjnum);
 }
@@ -2942,6 +2947,10 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		slope->normal.y = READFIXED(save_p);
 		slope->normal.z = READFIXED(save_p);
 	}
+	if (diff2 & MD2_DISPOFFSET)
+		mobj->dispoffset = READINT32(save_p);
+	else
+		mobj->dispoffset = mobj->info->dispoffset;
 
 	if (diff & MD_REDFLAG)
 	{
diff --git a/src/r_things.c b/src/r_things.c
index accd1e2b3c..1dee173567 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1437,7 +1437,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	fixed_t paperoffset = 0, paperdistance = 0;
 	angle_t centerangle = 0;
 
-	INT32 dispoffset = thing->info->dispoffset;
+	INT32 dispoffset = thing->dispoffset;
 
 	//SoM: 3/17/2000
 	fixed_t gz = 0, gzt = 0;
diff --git a/src/r_things.h b/src/r_things.h
index b1ff32b1ee..6a6ff3042a 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -209,7 +209,7 @@ typedef struct vissprite_s
 
 	INT16 clipbot[MAXVIDWIDTH], cliptop[MAXVIDWIDTH];
 
-	INT32 dispoffset; // copy of info->dispoffset, affects ordering but not drawing
+	INT32 dispoffset; // copy of mobj->dispoffset, affects ordering but not drawing
 } vissprite_t;
 
 extern UINT32 visspritecount;
-- 
GitLab


From 3bff9011d8a7baed3281a3be3c664fc7d0bdc41c Mon Sep 17 00:00:00 2001
From: Hannu Hanhi <hhanhipublic@gmail.com>
Date: Fri, 21 Jan 2022 16:16:13 +0200
Subject: [PATCH 016/518] LUA_EnumLib optimizations for constant value access

- constants are stored into _G after first use

- made LUA_EvalMath's dedicated Lua state permanent so it can also benefit from these optimizations
---
 src/deh_lua.c    | 169 +++++++++++++++++++++++++++++------------------
 src/deh_lua.h    |  15 +++++
 src/deh_soc.c    |   3 +
 src/lua_script.c |  25 +++----
 4 files changed, 135 insertions(+), 77 deletions(-)

diff --git a/src/deh_lua.c b/src/deh_lua.c
index a2ffca95b0..0bd906c132 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -10,20 +10,7 @@
 /// \file  deh_lua.c
 /// \brief Lua SOC library
 
-#include "g_game.h"
-#include "s_sound.h"
-#include "z_zone.h"
-#include "m_menu.h"
-#include "m_misc.h"
-#include "p_local.h"
-#include "st_stuff.h"
-#include "fastcmp.h"
-#include "lua_script.h"
-#include "lua_libs.h"
-
-#include "dehacked.h"
 #include "deh_lua.h"
-#include "deh_tables.h"
 
 // freeslot takes a name (string only!)
 // and allocates it to the appropriate free slot.
@@ -89,6 +76,8 @@ static inline int lib_freeslot(lua_State *L)
 				strncpy(sprnames[j],word,4);
 				//sprnames[j][4] = 0;
 				used_spr[(j-SPR_FIRSTFREESLOT)/8] |= 1<<(j%8); // Okay, this sprite slot has been named now.
+				// Lua needs to update the value in _G if it exists
+				LUA_UpdateSprName(word, j);
 				lua_pushinteger(L, j);
 				r++;
 				break;
@@ -216,18 +205,27 @@ static int lib_dummysuper(lua_State *L)
 	return luaL_error(L, "Can't call super() outside of hardcode-replacing A_Action functions being called by state changes!"); // convoluted, I know. @_@;;
 }
 
-static inline int lib_getenum(lua_State *L)
+static void CacheAndPushConstant(lua_State *L, const char *name, lua_Integer value)
+{
+	// "cache" into _G
+	lua_pushstring(L, name);
+	lua_pushinteger(L, value);
+	lua_rawset(L, LUA_GLOBALSINDEX);
+	// push
+	lua_pushinteger(L, value);
+}
+
+// Search for a matching constant variable.
+// Result is stored into _G for faster subsequent use. (Except for SPR_ in the SOC parser)
+static int ScanConstants(lua_State *L, boolean mathlib, const char *word)
 {
-	const char *word, *p;
+	const char *p;
 	fixed_t i;
-	boolean mathlib = lua_toboolean(L, lua_upvalueindex(1));
-	if (lua_type(L,2) != LUA_TSTRING)
-		return 0;
-	word = lua_tostring(L,2);
+
 	if (strlen(word) == 1) { // Assume sprite frame if length 1.
 		if (*word >= 'A' && *word <= '~')
 		{
-			lua_pushinteger(L, *word-'A');
+			CacheAndPushConstant(L, word, *word-'A');
 			return 1;
 		}
 		if (mathlib) return luaL_error(L, "constant '%s' could not be parsed.\n", word);
@@ -237,7 +235,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+3;
 		for (i = 0; MOBJFLAG_LIST[i]; i++)
 			if (fastcmp(p, MOBJFLAG_LIST[i])) {
-				lua_pushinteger(L, ((lua_Integer)1<<i));
+				CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "mobjflag '%s' could not be found.\n", word);
@@ -247,7 +245,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+4;
 		for (i = 0; MOBJFLAG2_LIST[i]; i++)
 			if (fastcmp(p, MOBJFLAG2_LIST[i])) {
-				lua_pushinteger(L, ((lua_Integer)1<<i));
+				CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "mobjflag2 '%s' could not be found.\n", word);
@@ -257,12 +255,12 @@ static inline int lib_getenum(lua_State *L)
 		p = word+4;
 		for (i = 0; MOBJEFLAG_LIST[i]; i++)
 			if (fastcmp(p, MOBJEFLAG_LIST[i])) {
-				lua_pushinteger(L, ((lua_Integer)1<<i));
+				CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
 				return 1;
 			}
 		if (fastcmp(p, "REVERSESUPER"))
 		{
-			lua_pushinteger(L, (lua_Integer)MFE_REVERSESUPER);
+			CacheAndPushConstant(L, word, (lua_Integer)MFE_REVERSESUPER);
 			return 1;
 		}
 		if (mathlib) return luaL_error(L, "mobjeflag '%s' could not be found.\n", word);
@@ -272,7 +270,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+4;
 		for (i = 0; i < 4; i++)
 			if (MAPTHINGFLAG_LIST[i] && fastcmp(p, MAPTHINGFLAG_LIST[i])) {
-				lua_pushinteger(L, ((lua_Integer)1<<i));
+				CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "mapthingflag '%s' could not be found.\n", word);
@@ -282,17 +280,17 @@ static inline int lib_getenum(lua_State *L)
 		p = word+3;
 		for (i = 0; PLAYERFLAG_LIST[i]; i++)
 			if (fastcmp(p, PLAYERFLAG_LIST[i])) {
-				lua_pushinteger(L, ((lua_Integer)1<<i));
+				CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
 				return 1;
 			}
 		if (fastcmp(p, "FULLSTASIS"))
 		{
-			lua_pushinteger(L, (lua_Integer)PF_FULLSTASIS);
+			CacheAndPushConstant(L, word, (lua_Integer)PF_FULLSTASIS);
 			return 1;
 		}
 		else if (fastcmp(p, "USEDOWN")) // Remove case when 2.3 nears release...
 		{
-			lua_pushinteger(L, (lua_Integer)PF_SPINDOWN);
+			CacheAndPushConstant(L, word, (lua_Integer)PF_SPINDOWN);
 			return 1;
 		}
 		if (mathlib) return luaL_error(L, "playerflag '%s' could not be found.\n", word);
@@ -302,7 +300,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word;
 		for (i = 0; Gametype_ConstantNames[i]; i++)
 			if (fastcmp(p, Gametype_ConstantNames[i])) {
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "gametype '%s' could not be found.\n", word);
@@ -312,7 +310,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+4;
 		for (i = 0; GAMETYPERULE_LIST[i]; i++)
 			if (fastcmp(p, GAMETYPERULE_LIST[i])) {
-				lua_pushinteger(L, ((lua_Integer)1<<i));
+				CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "game type rule '%s' could not be found.\n", word);
@@ -322,7 +320,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+4;
 		for (i = 0; TYPEOFLEVEL[i].name; i++)
 			if (fastcmp(p, TYPEOFLEVEL[i].name)) {
-				lua_pushinteger(L, TYPEOFLEVEL[i].flag);
+				CacheAndPushConstant(L, word, TYPEOFLEVEL[i].flag);
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "typeoflevel '%s' could not be found.\n", word);
@@ -332,7 +330,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+3;
 		for (i = 0; i < 16; i++)
 			if (ML_LIST[i] && fastcmp(p, ML_LIST[i])) {
-				lua_pushinteger(L, ((lua_Integer)1<<i));
+				CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "linedef flag '%s' could not be found.\n", word);
@@ -344,13 +342,13 @@ static inline int lib_getenum(lua_State *L)
 			if (!FREE_STATES[i])
 				break;
 			if (fastcmp(p, FREE_STATES[i])) {
-				lua_pushinteger(L, S_FIRSTFREESLOT+i);
+				CacheAndPushConstant(L, word, S_FIRSTFREESLOT+i);
 				return 1;
 			}
 		}
 		for (i = 0; i < S_FIRSTFREESLOT; i++)
 			if (fastcmp(p, STATE_LIST[i]+2)) {
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		return luaL_error(L, "state '%s' does not exist.\n", word);
@@ -361,13 +359,13 @@ static inline int lib_getenum(lua_State *L)
 			if (!FREE_MOBJS[i])
 				break;
 			if (fastcmp(p, FREE_MOBJS[i])) {
-				lua_pushinteger(L, MT_FIRSTFREESLOT+i);
+				CacheAndPushConstant(L, word, MT_FIRSTFREESLOT+i);
 				return 1;
 			}
 		}
 		for (i = 0; i < MT_FIRSTFREESLOT; i++)
 			if (fastcmp(p, MOBJTYPE_LIST[i]+3)) {
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		return luaL_error(L, "mobjtype '%s' does not exist.\n", word);
@@ -376,7 +374,12 @@ static inline int lib_getenum(lua_State *L)
 		p = word+4;
 		for (i = 0; i < NUMSPRITES; i++)
 			if (!sprnames[i][4] && fastncmp(p,sprnames[i],4)) {
-				lua_pushinteger(L, i);
+				// updating overridden sprnames is not implemented for soc parser,
+				// so don't use cache
+				if (mathlib)
+					lua_pushinteger(L, i);
+				else
+					CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "sprite '%s' could not be found.\n", word);
@@ -391,12 +394,12 @@ static inline int lib_getenum(lua_State *L)
 				// the spr2names entry will have "_" on the end, as in "RUN_"
 				if (spr2names[i][3] == '_' && !p[3]) {
 					if (fastncmp(p,spr2names[i],3)) {
-						lua_pushinteger(L, i);
+						CacheAndPushConstant(L, word, i);
 						return 1;
 					}
 				}
 				else if (fastncmp(p,spr2names[i],4)) {
-					lua_pushinteger(L, i);
+					CacheAndPushConstant(L, word, i);
 					return 1;
 				}
 			}
@@ -407,7 +410,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+4;
 		for (i = 0; i < NUMSFX; i++)
 			if (S_sfx[i].name && fastcmp(p, S_sfx[i].name)) {
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		return 0;
@@ -416,7 +419,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+4;
 		for (i = 0; i < NUMSFX; i++)
 			if (S_sfx[i].name && fasticmp(p, S_sfx[i].name)) {
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		return luaL_error(L, "sfx '%s' could not be found.\n", word);
@@ -425,7 +428,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+2;
 		for (i = 0; i < NUMSFX; i++)
 			if (S_sfx[i].name && fasticmp(p, S_sfx[i].name)) {
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "sfx '%s' could not be found.\n", word);
@@ -435,7 +438,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+3;
 		for (i = 0; i < NUMPOWERS; i++)
 			if (fasticmp(p, POWERS_LIST[i])) {
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		return 0;
@@ -444,7 +447,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+3;
 		for (i = 0; i < NUMPOWERS; i++)
 			if (fastcmp(p, POWERS_LIST[i])) {
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		return luaL_error(L, "power '%s' could not be found.\n", word);
@@ -453,7 +456,7 @@ static inline int lib_getenum(lua_State *L)
 		p = word+4;
 		for (i = 0; i < NUMHUDITEMS; i++)
 			if (fastcmp(p, HUDITEMS_LIST[i])) {
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "huditem '%s' could not be found.\n", word);
@@ -465,13 +468,13 @@ static inline int lib_getenum(lua_State *L)
 			if (!FREE_SKINCOLORS[i])
 				break;
 			if (fastcmp(p, FREE_SKINCOLORS[i])) {
-				lua_pushinteger(L, SKINCOLOR_FIRSTFREESLOT+i);
+				CacheAndPushConstant(L, word, SKINCOLOR_FIRSTFREESLOT+i);
 				return 1;
 			}
 		}
 		for (i = 0; i < SKINCOLOR_FIRSTFREESLOT; i++)
 			if (fastcmp(p, COLOR_ENUMS[i])) {
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		return luaL_error(L, "skincolor '%s' could not be found.\n", word);
@@ -482,7 +485,7 @@ static inline int lib_getenum(lua_State *L)
 		for (i = 0; NIGHTSGRADE_LIST[i]; i++)
 			if (*p == NIGHTSGRADE_LIST[i])
 			{
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "NiGHTS grade '%s' could not be found.\n", word);
@@ -492,13 +495,41 @@ static inline int lib_getenum(lua_State *L)
 		p = word+3;
 		for (i = 0; i < NUMMENUTYPES; i++)
 			if (fastcmp(p, MENUTYPES_LIST[i])) {
-				lua_pushinteger(L, i);
+				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
 		if (mathlib) return luaL_error(L, "menutype '%s' could not be found.\n", word);
 		return 0;
 	}
-	else if (!mathlib && fastncmp("A_",word,2)) {
+
+	if (fastcmp(word, "BT_USE")) // Remove case when 2.3 nears release...
+	{
+		CacheAndPushConstant(L, word, (lua_Integer)BT_SPIN);
+		return 1;
+	}
+
+	for (i = 0; INT_CONST[i].n; i++)
+		if (fastcmp(word,INT_CONST[i].n)) {
+			CacheAndPushConstant(L, word, INT_CONST[i].v);
+			return 1;
+		}
+
+	return 0;
+}
+
+static inline int lib_getenum(lua_State *L)
+{
+	const char *word;
+	fixed_t i;
+	boolean mathlib = lua_toboolean(L, lua_upvalueindex(1));
+	if (lua_type(L,2) != LUA_TSTRING)
+		return 0;
+	word = lua_tostring(L,2);
+
+	// check actions, super and globals first, as they don't have _G caching implemented
+	// so they benefit from being checked first
+
+	if (!mathlib && fastncmp("A_",word,2)) {
 		char *caps;
 		// Try to get a Lua action first.
 		/// \todo Push a closure that sets superactions[] and superstack.
@@ -537,25 +568,33 @@ static inline int lib_getenum(lua_State *L)
 			}
 		return 0;
 	}
-
-	if (fastcmp(word, "BT_USE")) // Remove case when 2.3 nears release...
-	{
-		lua_pushinteger(L, (lua_Integer)BT_SPIN);
+	else if ((!mathlib && LUA_PushGlobals(L, word)) || ScanConstants(L, mathlib, word))
 		return 1;
-	}
-
-	for (i = 0; INT_CONST[i].n; i++)
-		if (fastcmp(word,INT_CONST[i].n)) {
-			lua_pushinteger(L, INT_CONST[i].v);
-			return 1;
-		}
 
 	if (mathlib) return luaL_error(L, "constant '%s' could not be parsed.\n", word);
 
-	// DYNAMIC variables too!!
-	// Try not to add anything that would break netgames or timeattack replays here.
-	// You know, like consoleplayer, displayplayer, secondarydisplayplayer, or gametime.
-	return LUA_PushGlobals(L, word);
+	return 0;
+}
+
+// If a sprname has been "cached" to _G, update it to a new value.
+void LUA_UpdateSprName(const char *name, lua_Integer value)
+{
+	char fullname[9] = "SPR_XXXX";
+
+	if (!gL)
+		return;
+
+	strncpy(&fullname[4], name, 4);
+	lua_pushstring(gL, fullname);
+	lua_rawget(gL, LUA_GLOBALSINDEX);
+
+	if (!lua_isnil(gL, -1))
+	{
+		lua_pop(gL, 1);
+		lua_pushstring(gL, name);
+		lua_pushinteger(gL, value);
+		lua_rawset(gL, LUA_GLOBALSINDEX);
+	}
 }
 
 int LUA_EnumLib(lua_State *L)
diff --git a/src/deh_lua.h b/src/deh_lua.h
index 9df4028bdc..76c68860f3 100644
--- a/src/deh_lua.h
+++ b/src/deh_lua.h
@@ -13,6 +13,21 @@
 #ifndef __DEH_LUA_H__
 #define __DEH_LUA_H__
 
+#include "g_game.h"
+#include "s_sound.h"
+#include "z_zone.h"
+#include "m_menu.h"
+#include "m_misc.h"
+#include "p_local.h"
+#include "st_stuff.h"
+#include "fastcmp.h"
+#include "lua_script.h"
+#include "lua_libs.h"
+
+#include "dehacked.h"
+#include "deh_tables.h"
+
+void LUA_UpdateSprName(const char *name, lua_Integer value);
 boolean LUA_SetLuaAction(void *state, const char *actiontocompare);
 const char *LUA_GetActionName(void *action);
 void LUA_SetActionByName(void *state, const char *actiontocompare);
diff --git a/src/deh_soc.c b/src/deh_soc.c
index 3a611f3ba1..e1979789e8 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -45,6 +45,7 @@
 #include "dehacked.h"
 #include "deh_soc.h"
 #include "deh_lua.h" // included due to some LUA_SetLuaAction hack smh
+// also used for LUA_UpdateSprName
 #include "deh_tables.h"
 
 // Loops through every constant and operation in word and performs its calculations, returning the final value.
@@ -439,6 +440,8 @@ void readfreeslots(MYFILE *f)
 					strncpy(sprnames[i],word,4);
 					//sprnames[i][4] = 0;
 					used_spr[(i-SPR_FIRSTFREESLOT)/8] |= 1<<(i%8); // Okay, this sprite slot has been named now.
+					// Lua needs to update the value in _G if it exists
+					LUA_UpdateSprName(word, i);
 					break;
 				}
 			}
diff --git a/src/lua_script.c b/src/lua_script.c
index a1376ca2e3..d7670fe351 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -694,20 +694,23 @@ void LUA_DumpFile(const char *filename)
 
 fixed_t LUA_EvalMath(const char *word)
 {
-	lua_State *L = NULL;
+	static lua_State *L = NULL;
 	char buf[1024], *b;
 	const char *p;
 	fixed_t res = 0;
 
-	// make a new state so SOC can't interefere with scripts
-	// allocate state
-	L = lua_newstate(LUA_Alloc, NULL);
-	lua_atpanic(L, LUA_Panic);
-
-	// open only enum lib
-	lua_pushcfunction(L, LUA_EnumLib);
-	lua_pushboolean(L, true);
-	lua_call(L, 1, 0);
+	if (!L)
+	{
+		// make a new state so SOC can't interefere with scripts
+		// allocate state
+		L = lua_newstate(LUA_Alloc, NULL);
+		lua_atpanic(L, LUA_Panic);
+
+		// open only enum lib
+		lua_pushcfunction(L, LUA_EnumLib);
+		lua_pushboolean(L, true);
+		lua_call(L, 1, 0);
+	}
 
 	// change ^ into ^^ for Lua.
 	strcpy(buf, "return ");
@@ -732,8 +735,6 @@ fixed_t LUA_EvalMath(const char *word)
 	else
 		res = lua_tointeger(L, -1);
 
-	// clean up and return.
-	lua_close(L);
 	return res;
 }
 
-- 
GitLab


From d194bbeacd2af26e7f06e77e5218df9142363e75 Mon Sep 17 00:00:00 2001
From: Hannu Hanhi <hhanhipublic@gmail.com>
Date: Tue, 25 Jan 2022 00:43:38 +0200
Subject: [PATCH 017/518] Fix Lua stack overflow in LUA_UpdateSprName

---
 src/deh_lua.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/deh_lua.c b/src/deh_lua.c
index 0bd906c132..1f6a0659cb 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -590,11 +590,12 @@ void LUA_UpdateSprName(const char *name, lua_Integer value)
 
 	if (!lua_isnil(gL, -1))
 	{
-		lua_pop(gL, 1);
 		lua_pushstring(gL, name);
 		lua_pushinteger(gL, value);
 		lua_rawset(gL, LUA_GLOBALSINDEX);
 	}
+
+	lua_pop(gL, 1); // pop the rawget result
 }
 
 int LUA_EnumLib(lua_State *L)
-- 
GitLab


From f5d4eb3e7aa6de763c2dda58800a2702a5da36e4 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Mon, 31 Jan 2022 14:39:51 +0100
Subject: [PATCH 018/518] Turn F11 into a fullscreen toggle, and process it
 inside menus as well.

---
 src/m_menu.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 3c1d8d7caa..7d4e4b8aef 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -1347,7 +1347,7 @@ static menuitem_t OP_VideoOptionsMenu[] =
 #endif
 
 	{IT_HEADER, NULL, "Color Profile", NULL, 30},
-	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Brightness (F11)", &cv_globalgamma,36},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Brightness", &cv_globalgamma,36},
 	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Saturation", &cv_globalsaturation, 41},
 	{IT_SUBMENU|IT_STRING, NULL, "Advanced Settings...",     &OP_ColorOptionsDef,  46},
 
@@ -3399,8 +3399,8 @@ boolean M_Responder(event_t *ev)
 				M_QuitSRB2(0);
 				return true;
 
-			case KEY_F11: // Gamma Level
-				CV_AddValue(&cv_globalgamma, 1);
+			case KEY_F11: // Fullscreen toggle, also processed inside menus
+				CV_SetValue(&cv_fullscreen, !cv_fullscreen.value);
 				return true;
 
 			// Spymode on F12 handled in game logic
@@ -3579,6 +3579,10 @@ boolean M_Responder(event_t *ev)
 			//	M_SetupNextMenu(currentMenu->prevMenu);
 			return false;
 
+		case KEY_F11: // Fullscreen toggle, also processed outside menus
+			CV_SetValue(&cv_fullscreen, !cv_fullscreen.value);
+			return true;
+
 		default:
 			CON_Responder(ev);
 			break;
-- 
GitLab


From 020ce6a1ddda6d185ade732197c165f99a2c4931 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Mon, 31 Jan 2022 14:40:41 +0100
Subject: [PATCH 019/518] Rudimentary support for separate fullscreen/windowed
 resolutions.

---
 src/d_netcmd.c | 4 ++++
 src/screen.c   | 9 ++++++++-
 src/screen.h   | 1 +
 3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index fe7e7678fe..4ad6c8c51f 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -872,6 +872,10 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_scr_width);
 	CV_RegisterVar(&cv_scr_height);
 
+	CV_RegisterVar(&cv_usewindowedres);
+	CV_RegisterVar(&cv_scr_width_w);
+	CV_RegisterVar(&cv_scr_height_w);
+
 	CV_RegisterVar(&cv_soundtest);
 
 	CV_RegisterVar(&cv_perfstats);
diff --git a/src/screen.c b/src/screen.c
index 770f1c8026..19e6bf1bd8 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -69,6 +69,10 @@ consvar_t cv_scr_height = CVAR_INIT ("scr_height", "800", CV_SAVE, CV_Unsigned,
 consvar_t cv_scr_depth = CVAR_INIT ("scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL);
 consvar_t cv_renderview = CVAR_INIT ("renderview", "On", 0, CV_OnOff, NULL);
 
+consvar_t cv_usewindowedres = CVAR_INIT ("usewindowedres", "No", CV_SAVE, CV_YesNo, NULL);
+consvar_t cv_scr_width_w = CVAR_INIT ("scr_width_w", "640", CV_SAVE, CV_Unsigned, NULL);
+consvar_t cv_scr_height_w = CVAR_INIT ("scr_height_w", "400", CV_SAVE, CV_Unsigned, NULL);
+
 CV_PossibleValue_t cv_renderer_t[] = {
 	{1, "Software"},
 #ifdef HWRENDER
@@ -405,7 +409,10 @@ void SCR_ChangeFullscreen(void)
 	if (graphics_started)
 	{
 		VID_PrepareModeList();
-		setmodeneeded = VID_GetModeForSize(vid.width, vid.height) + 1;
+		if (cv_usewindowedres.value == 1 && cv_fullscreen.value == 0)
+			setmodeneeded = VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value) + 1;
+		else
+			setmodeneeded = VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value) + 1;
 	}
 	return;
 #endif
diff --git a/src/screen.h b/src/screen.h
index 67880e2b96..3077973d24 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -190,6 +190,7 @@ extern INT32 scr_bpp;
 extern UINT8 *scr_borderpatch; // patch used to fill the view borders
 
 extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen;
+extern consvar_t cv_scr_width_w, cv_scr_height_w, cv_usewindowedres;
 // wait for page flipping to end or not
 extern consvar_t cv_vidwait;
 
-- 
GitLab


From 0cfc75070d92fa9e636a0e970c813d7d25bf1e11 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Fri, 4 Feb 2022 14:42:10 +0100
Subject: [PATCH 020/518] Improved support for separate resolutions: - Windowed
 mode resolution can now be changed via the resolutions menu. - F11 can also
 be used while in this menu. - Removed usewindowedres cvar for ease of use. -
 When starting in windowed mode, use the correct resolution.

---
 src/d_netcmd.c |  2 --
 src/m_menu.c   | 16 ++++++++++++----
 src/screen.c   | 35 +++++++++++++++++++++++------------
 src/screen.h   |  4 ++--
 4 files changed, 37 insertions(+), 20 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 4ad6c8c51f..eda70a170d 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -871,8 +871,6 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_scr_depth);
 	CV_RegisterVar(&cv_scr_width);
 	CV_RegisterVar(&cv_scr_height);
-
-	CV_RegisterVar(&cv_usewindowedres);
 	CV_RegisterVar(&cv_scr_width_w);
 	CV_RegisterVar(&cv_scr_height_w);
 
diff --git a/src/m_menu.c b/src/m_menu.c
index 7d4e4b8aef..f07dffe5c3 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -1337,13 +1337,13 @@ static menuitem_t OP_VideoOptionsMenu[] =
 	{IT_STRING | IT_CALL,  NULL, "Set Resolution...",       M_VideoModeMenu,          6},
 
 #if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL)
-	{IT_STRING|IT_CVAR,      NULL, "Fullscreen",             &cv_fullscreen,         11},
+	{IT_STRING|IT_CVAR,      NULL, "Fullscreen (F11)",          &cv_fullscreen,      11},
 #endif
 	{IT_STRING | IT_CVAR, NULL, "Vertical Sync",                &cv_vidwait,         16},
 #ifdef HWRENDER
 	{IT_STRING | IT_CVAR, NULL, "Renderer",                     &cv_renderer,        21},
 #else
-	{IT_TRANSTEXT | IT_PAIR, "Renderer", "Software",            &cv_renderer,           21},
+	{IT_TRANSTEXT | IT_PAIR, "Renderer", "Software",            &cv_renderer,        21},
 #endif
 
 	{IT_HEADER, NULL, "Color Profile", NULL, 30},
@@ -13217,14 +13217,18 @@ static void M_DrawVideoMode(void)
 	}
 	else
 	{
-		M_CentreText(OP_VideoModeDef.y + 116,
+		M_CentreText(OP_VideoModeDef.y + 100,
 			va("Current mode is %c%dx%d",
 				(SCR_IsAspectCorrect(vid.width, vid.height)) ? 0x83 : 0x80,
 				vid.width, vid.height));
-		M_CentreText(OP_VideoModeDef.y + 124,
+		M_CentreText(OP_VideoModeDef.y + 116,
 			va("Default mode is %c%dx%d",
 				(SCR_IsAspectCorrect(cv_scr_width.value, cv_scr_height.value)) ? 0x83 : 0x80,
 				cv_scr_width.value, cv_scr_height.value));
+		M_CentreText(OP_VideoModeDef.y + 124,
+			va("Windowed mode is %c%dx%d",
+				(SCR_IsAspectCorrect(cv_scr_width_w.value, cv_scr_height_w.value)) ? 0x83 : 0x80,
+				cv_scr_width_w.value, cv_scr_height_w.value));
 
 		V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 138,
 			V_GREENMAP, "Green modes are recommended.");
@@ -13438,6 +13442,10 @@ static void M_HandleVideoMode(INT32 ch)
 				M_ClearMenus(true);
 			break;
 
+		case KEY_F11:
+			CV_SetValue(&cv_fullscreen, !cv_fullscreen.value);
+			break;
+
 		default:
 			break;
 	}
diff --git a/src/screen.c b/src/screen.c
index 19e6bf1bd8..96e436193a 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -66,12 +66,10 @@ static CV_PossibleValue_t scr_depth_cons_t[] = {{8, "8 bits"}, {16, "16 bits"},
 //added : 03-02-98: default screen mode, as loaded/saved in config
 consvar_t cv_scr_width = CVAR_INIT ("scr_width", "1280", CV_SAVE, CV_Unsigned, NULL);
 consvar_t cv_scr_height = CVAR_INIT ("scr_height", "800", CV_SAVE, CV_Unsigned, NULL);
-consvar_t cv_scr_depth = CVAR_INIT ("scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL);
-consvar_t cv_renderview = CVAR_INIT ("renderview", "On", 0, CV_OnOff, NULL);
-
-consvar_t cv_usewindowedres = CVAR_INIT ("usewindowedres", "No", CV_SAVE, CV_YesNo, NULL);
 consvar_t cv_scr_width_w = CVAR_INIT ("scr_width_w", "640", CV_SAVE, CV_Unsigned, NULL);
 consvar_t cv_scr_height_w = CVAR_INIT ("scr_height_w", "400", CV_SAVE, CV_Unsigned, NULL);
+consvar_t cv_scr_depth = CVAR_INIT ("scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL);
+consvar_t cv_renderview = CVAR_INIT ("renderview", "On", 0, CV_OnOff, NULL);
 
 CV_PossibleValue_t cv_renderer_t[] = {
 	{1, "Software"},
@@ -370,10 +368,16 @@ void SCR_CheckDefaultMode(void)
 	}
 	else
 	{
-		CONS_Printf(M_GetText("Default resolution: %d x %d (%d bits)\n"), cv_scr_width.value,
-			cv_scr_height.value, cv_scr_depth.value);
-		// see note above
-		setmodeneeded = VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value) + 1;
+		if (cv_fullscreen.value == 0)
+		{
+			CONS_Printf(M_GetText("Default windowed resolution: %d x %d (%d bits)\n"), cv_scr_width_w.value, cv_scr_height_w.value, cv_scr_depth.value);
+			setmodeneeded = VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value) + 1; // see note above
+		}
+		else
+		{
+			CONS_Printf(M_GetText("Default resolution: %d x %d (%d bits)\n"), cv_scr_width.value, cv_scr_height.value, cv_scr_depth.value);
+			setmodeneeded = VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value) + 1; // see note above
+		}
 	}
 
 	if (cv_renderer.value != (signed)rendermode)
@@ -392,9 +396,16 @@ void SCR_CheckDefaultMode(void)
 void SCR_SetDefaultMode(void)
 {
 	// remember the default screen size
-	CV_SetValue(&cv_scr_width, vid.width);
-	CV_SetValue(&cv_scr_height, vid.height);
-	CV_SetValue(&cv_scr_depth, vid.bpp*8);
+	if (cv_fullscreen.value == 0)
+	{
+		CV_SetValue(&cv_scr_width_w, vid.width);
+		CV_SetValue(&cv_scr_height_w, vid.height);
+	}
+	else
+	{
+		CV_SetValue(&cv_scr_width, vid.width);
+		CV_SetValue(&cv_scr_height, vid.height);
+	}
 }
 
 // Change fullscreen on/off according to cv_fullscreen
@@ -409,7 +420,7 @@ void SCR_ChangeFullscreen(void)
 	if (graphics_started)
 	{
 		VID_PrepareModeList();
-		if (cv_usewindowedres.value == 1 && cv_fullscreen.value == 0)
+		if (cv_fullscreen.value == 0)
 			setmodeneeded = VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value) + 1;
 		else
 			setmodeneeded = VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value) + 1;
diff --git a/src/screen.h b/src/screen.h
index 3077973d24..30582793f1 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -189,8 +189,8 @@ extern CV_PossibleValue_t cv_renderer_t[];
 extern INT32 scr_bpp;
 extern UINT8 *scr_borderpatch; // patch used to fill the view borders
 
-extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen;
-extern consvar_t cv_scr_width_w, cv_scr_height_w, cv_usewindowedres;
+extern consvar_t cv_scr_width, cv_scr_height, cv_scr_width_w, cv_scr_height_w, cv_scr_depth, cv_fullscreen;
+extern consvar_t cv_renderview, cv_renderer;
 // wait for page flipping to end or not
 extern consvar_t cv_vidwait;
 
-- 
GitLab


From 5fdc0b889bfc5c9429911c5c42d6a1209fc637ec Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Fri, 4 Feb 2022 14:50:45 +0100
Subject: [PATCH 021/518] Replace F10's near-useless game quit option with a
 renderer toggle.

---
 src/m_menu.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index f07dffe5c3..b54baa9ddc 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -1341,7 +1341,7 @@ static menuitem_t OP_VideoOptionsMenu[] =
 #endif
 	{IT_STRING | IT_CVAR, NULL, "Vertical Sync",                &cv_vidwait,         16},
 #ifdef HWRENDER
-	{IT_STRING | IT_CVAR, NULL, "Renderer",                     &cv_renderer,        21},
+	{IT_STRING | IT_CVAR, NULL, "Renderer (F10)",               &cv_renderer,        21},
 #else
 	{IT_TRANSTEXT | IT_PAIR, "Renderer", "Software",            &cv_renderer,        21},
 #endif
@@ -3395,8 +3395,11 @@ boolean M_Responder(event_t *ev)
 			// Screenshots on F8 now handled elsewhere
 			// Same with Moviemode on F9
 
-			case KEY_F10: // Quit SRB2
-				M_QuitSRB2(0);
+			case KEY_F10: // Renderer toggle, also processed inside menus
+				if (cv_renderer.value == render_soft)
+					CV_SetValue(&cv_renderer, render_opengl);
+				else if (cv_renderer.value == render_opengl)
+					CV_SetValue(&cv_renderer, render_soft);
 				return true;
 
 			case KEY_F11: // Fullscreen toggle, also processed inside menus
@@ -3579,6 +3582,13 @@ boolean M_Responder(event_t *ev)
 			//	M_SetupNextMenu(currentMenu->prevMenu);
 			return false;
 
+		case KEY_F10: // Renderer toggle, also processed outside menus
+			if (cv_renderer.value == render_soft)
+				CV_SetValue(&cv_renderer, render_opengl);
+			else if (cv_renderer.value == render_opengl)
+				CV_SetValue(&cv_renderer, render_soft);
+			return true;
+
 		case KEY_F11: // Fullscreen toggle, also processed outside menus
 			CV_SetValue(&cv_fullscreen, !cv_fullscreen.value);
 			return true;
-- 
GitLab


From 599644e885b9624eb72aecfe67b11939a8f4af46 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 5 Feb 2022 11:32:48 +0100
Subject: [PATCH 022/518] Update resolutions menu, remove M_CentreText.

---
 src/m_menu.c | 49 +++++++++++++++++++++++--------------------------
 1 file changed, 23 insertions(+), 26 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index b54baa9ddc..c034aee48d 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -4228,15 +4228,6 @@ static void M_DrawSaveLoadBorder(INT32 x,INT32 y)
 }
 #endif
 
-// horizontally centered text
-static void M_CentreText(INT32 y, const char *string)
-{
-	INT32 x;
-	//added : 02-02-98 : centre on 320, because V_DrawString centers on vid.width...
-	x = (BASEVIDWIDTH - V_StringWidth(string, V_OLDSPACING))>>1;
-	V_DrawString(x,y,V_OLDSPACING,string);
-}
-
 //
 // M_DrawMapEmblems
 //
@@ -12818,12 +12809,12 @@ static void M_DrawControl(void)
 	if (tutorialmode && tutorialgcs)
 	{
 		if ((gametic / TICRATE) % 2)
-			M_CentreText(30, "\202EXIT THE TUTORIAL TO CHANGE THE CONTROLS");
+			V_DrawCenteredString(BASEVIDWIDTH/2, 30, 0, "\202EXIT THE TUTORIAL TO CHANGE THE CONTROLS");
 		else
-			M_CentreText(30, "EXIT THE TUTORIAL TO CHANGE THE CONTROLS");
+			V_DrawCenteredString(BASEVIDWIDTH/2, 30, 0, "EXIT THE TUTORIAL TO CHANGE THE CONTROLS");
 	}
 	else
-		M_CentreText(30,
+		V_DrawCenteredString(BASEVIDWIDTH/2, 30, 0,
 		    (setupcontrols_secondaryplayer ? "SET CONTROLS FOR SECONDARY PLAYER" :
 		                                     "PRESS ENTER TO CHANGE, BACKSPACE TO CLEAR"));
 
@@ -13188,11 +13179,11 @@ static void M_DrawVideoMode(void)
 	// draw title
 	M_DrawMenuTitle();
 
-	V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y,
-		V_YELLOWMAP, "Choose mode, reselect to change default");
+	V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y, V_YELLOWMAP, "Choose mode, reselect to change default");
+	V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y+8, V_YELLOWMAP, "Press F11 to toggle fullscreen");
 
 	row = 41;
-	col = OP_VideoModeDef.y + 14;
+	col = OP_VideoModeDef.y + 24;
 	for (i = 0; i < vidm_nummodes; i++)
 	{
 		if (i == vidm_selected)
@@ -13205,7 +13196,7 @@ static void M_DrawVideoMode(void)
 		if ((i % vidm_column_size) == (vidm_column_size-1))
 		{
 			row += 7*13;
-			col = OP_VideoModeDef.y + 14;
+			col = OP_VideoModeDef.y + 24;
 		}
 	}
 
@@ -13213,29 +13204,31 @@ static void M_DrawVideoMode(void)
 	{
 		INT32 testtime = (vidm_testingmode/TICRATE) + 1;
 
-		M_CentreText(OP_VideoModeDef.y + 116,
+		V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 116, 0,
 			va("Previewing mode %c%dx%d",
 				(SCR_IsAspectCorrect(vid.width, vid.height)) ? 0x83 : 0x80,
 				vid.width, vid.height));
-		M_CentreText(OP_VideoModeDef.y + 138,
+		V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 138, 0,
 			"Press ENTER again to keep this mode");
-		M_CentreText(OP_VideoModeDef.y + 150,
+		V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 150, 0,
 			va("Wait %d second%s", testtime, (testtime > 1) ? "s" : ""));
-		M_CentreText(OP_VideoModeDef.y + 158,
+		V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 158, 0,
 			"or press ESC to return");
-
 	}
 	else
 	{
-		M_CentreText(OP_VideoModeDef.y + 100,
+		V_DrawFill(60, OP_VideoModeDef.y + 98, 200, 12, 159);
+		V_DrawFill(60, OP_VideoModeDef.y + 114, 200, 20, 159);
+
+		V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 100, 0,
 			va("Current mode is %c%dx%d",
 				(SCR_IsAspectCorrect(vid.width, vid.height)) ? 0x83 : 0x80,
 				vid.width, vid.height));
-		M_CentreText(OP_VideoModeDef.y + 116,
+		V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 116, (cv_fullscreen.value ? 0 : V_TRANSLUCENT),
 			va("Default mode is %c%dx%d",
 				(SCR_IsAspectCorrect(cv_scr_width.value, cv_scr_height.value)) ? 0x83 : 0x80,
 				cv_scr_width.value, cv_scr_height.value));
-		M_CentreText(OP_VideoModeDef.y + 124,
+		V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 124, (cv_fullscreen.value ? V_TRANSLUCENT : 0),
 			va("Windowed mode is %c%dx%d",
 				(SCR_IsAspectCorrect(cv_scr_width_w.value, cv_scr_height_w.value)) ? 0x83 : 0x80,
 				cv_scr_width_w.value, cv_scr_height_w.value));
@@ -13250,7 +13243,7 @@ static void M_DrawVideoMode(void)
 
 	// Draw the cursor for the VidMode menu
 	i = 41 - 10 + ((vidm_selected / vidm_column_size)*7*13);
-	j = OP_VideoModeDef.y + 14 + ((vidm_selected % vidm_column_size)*8);
+	j = OP_VideoModeDef.y + 24 + ((vidm_selected % vidm_column_size)*8);
 
 	V_DrawScaledPatch(i - 8, j, 0,
 		W_CachePatchName("M_CURSOR", PU_PATCH));
@@ -13433,11 +13426,14 @@ static void M_HandleVideoMode(INT32 ch)
 			break;
 
 		case KEY_ENTER:
-			S_StartSound(NULL, sfx_menu1);
 			if (vid.modenum == modedescs[vidm_selected].modenum)
+			{
+				S_StartSound(NULL, sfx_strpst);
 				SCR_SetDefaultMode();
+			}
 			else
 			{
+				S_StartSound(NULL, sfx_menu1);
 				vidm_testingmode = 15*TICRATE;
 				vidm_previousmode = vid.modenum;
 				if (!setmodeneeded) // in case the previous setmode was not finished
@@ -13453,6 +13449,7 @@ static void M_HandleVideoMode(INT32 ch)
 			break;
 
 		case KEY_F11:
+			S_StartSound(NULL, sfx_menu1);
 			CV_SetValue(&cv_fullscreen, !cv_fullscreen.value);
 			break;
 
-- 
GitLab


From 35f2937fd114d16cf3e5374fa80c1dcca1220fb4 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 6 Feb 2022 16:13:45 +0100
Subject: [PATCH 023/518] Minor code cleanup & remove some unneeded resolution
 info from logs.

---
 src/screen.c      | 34 +++++++++++-----------------------
 src/sdl/i_video.c |  4 ++--
 2 files changed, 13 insertions(+), 25 deletions(-)

diff --git a/src/screen.c b/src/screen.c
index 96e436193a..f21ea2a93c 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -368,16 +368,13 @@ void SCR_CheckDefaultMode(void)
 	}
 	else
 	{
-		if (cv_fullscreen.value == 0)
-		{
-			CONS_Printf(M_GetText("Default windowed resolution: %d x %d (%d bits)\n"), cv_scr_width_w.value, cv_scr_height_w.value, cv_scr_depth.value);
-			setmodeneeded = VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value) + 1; // see note above
-		}
-		else
-		{
-			CONS_Printf(M_GetText("Default resolution: %d x %d (%d bits)\n"), cv_scr_width.value, cv_scr_height.value, cv_scr_depth.value);
+		CONS_Printf(M_GetText("Default resolution: %d x %d\n"), cv_scr_width.value, cv_scr_height.value);
+		CONS_Printf(M_GetText("Windowed resolution: %d x %d\n"), cv_scr_width_w.value, cv_scr_height_w.value);
+		CONS_Printf(M_GetText("Default bit depth: %d bits\n"), cv_scr_depth.value);
+		if (cv_fullscreen.value)
 			setmodeneeded = VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value) + 1; // see note above
-		}
+		else
+			setmodeneeded = VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value) + 1; // see note above
 	}
 
 	if (cv_renderer.value != (signed)rendermode)
@@ -395,17 +392,8 @@ void SCR_CheckDefaultMode(void)
 // sets the modenum as the new default video mode to be saved in the config file
 void SCR_SetDefaultMode(void)
 {
-	// remember the default screen size
-	if (cv_fullscreen.value == 0)
-	{
-		CV_SetValue(&cv_scr_width_w, vid.width);
-		CV_SetValue(&cv_scr_height_w, vid.height);
-	}
-	else
-	{
-		CV_SetValue(&cv_scr_width, vid.width);
-		CV_SetValue(&cv_scr_height, vid.height);
-	}
+	CV_SetValue(cv_fullscreen.value ? &cv_scr_width : &cv_scr_width_w, vid.width);
+	CV_SetValue(cv_fullscreen.value ? &cv_scr_height : &cv_scr_height_w, vid.height);
 }
 
 // Change fullscreen on/off according to cv_fullscreen
@@ -420,10 +408,10 @@ void SCR_ChangeFullscreen(void)
 	if (graphics_started)
 	{
 		VID_PrepareModeList();
-		if (cv_fullscreen.value == 0)
-			setmodeneeded = VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value) + 1;
-		else
+		if (cv_fullscreen.value)
 			setmodeneeded = VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value) + 1;
+		else
+			setmodeneeded = VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value) + 1;
 	}
 	return;
 #endif
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index ed766ff23d..1642695d0b 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -1795,7 +1795,7 @@ void I_StartupGraphics(void)
 	borderlesswindow = M_CheckParm("-borderless");
 
 	//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2);
-	VID_Command_ModeList_f();
+	//VID_Command_ModeList_f();
 
 #ifdef HWRENDER
 	if (rendermode == render_opengl)
@@ -1843,7 +1843,7 @@ void I_StartupGraphics(void)
 	realwidth = (Uint16)vid.width;
 	realheight = (Uint16)vid.height;
 
-	VID_Command_Info_f();
+	//VID_Command_Info_f();
 	SDLdoUngrabMouse();
 
 	SDL_RaiseWindow(window);
-- 
GitLab


From dcdbdec2bf8fffbc4b2d99ac89612cb9516cfcb9 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 6 Feb 2022 15:16:30 -0500
Subject: [PATCH 024/518] Make super for A_Actions act as described

---
 src/deh_lua.c     | 61 +++++++++++++++++++++++++++++++-------------
 src/deh_tables.h  |  1 +
 src/dehacked.h    |  5 ++--
 src/info.h        |  3 ++-
 src/lua_infolib.c | 64 ++++++++++++++++++++++++++++++++++-------------
 src/lua_script.c  | 14 ++++++++++-
 6 files changed, 109 insertions(+), 39 deletions(-)

diff --git a/src/deh_lua.c b/src/deh_lua.c
index a2ffca95b0..43e25a49d1 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -195,25 +195,32 @@ static inline int lib_freeslot(lua_State *L)
 // Arguments: mobj_t actor, int var1, int var2
 static int action_call(lua_State *L)
 {
-	//actionf_t *action = lua_touserdata(L,lua_upvalueindex(1));
 	actionf_t *action = *((actionf_t **)luaL_checkudata(L, 1, META_ACTION));
 	mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
+
 	var1 = (INT32)luaL_optinteger(L, 3, 0);
 	var2 = (INT32)luaL_optinteger(L, 4, 0);
+
 	if (!actor)
+	{
 		return LUA_ErrInvalid(L, "mobj_t");
+	}
+
 	action->acp1(actor);
 	return 0;
 }
 
 // Hardcoded A_Action name to call for super() or NULL if super() would be invalid.
 // Set in lua_infolib.
-const char *superactions[MAXRECURSION];
+const char *luaactions[MAX_ACTION_RECURSION];
+UINT8 luaactionstack = 0;
 UINT8 superstack = 0;
 
 static int lib_dummysuper(lua_State *L)
 {
-	return luaL_error(L, "Can't call super() outside of hardcode-replacing A_Action functions being called by state changes!"); // convoluted, I know. @_@;;
+	// TODO: Now that the restriction on only being allowed in state changes was lifted,
+	// it'd be nice to have super extend to Lua A_ functions too :)
+	return luaL_error(L, "Can't call super() outside of hardcode-replacing A_Action functions!");
 }
 
 static inline int lib_getenum(lua_State *L)
@@ -500,42 +507,62 @@ static inline int lib_getenum(lua_State *L)
 	}
 	else if (!mathlib && fastncmp("A_",word,2)) {
 		char *caps;
-		// Try to get a Lua action first.
-		/// \todo Push a closure that sets superactions[] and superstack.
+
+		// Hardcoded actions come first.
+		// Trying to call them will invoke LUA_CallAction, which will handle super properly.
+		// Retrieving them from this metatable allows them to be case-insensitive!
+		for (i = 0; actionpointers[i].name; i++)
+		{
+			if (fasticmp(word, actionpointers[i].name))
+			{
+				// We push the actionf_t* itself as userdata!
+				LUA_PushUserdata(L, &actionpointers[i].action, META_ACTION);
+				return 1;
+			}
+		}
+
+		// Now try to get Lua actions.
+		/// \todo Push a closure that sets luaactions[] and luaactionstack.
+		/// This would be part one of a step to get super functions working for custom A_ functions.
+		/// Custom functions.
 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS);
+
 		// actions are stored in all uppercase.
 		caps = Z_StrDup(word);
 		strupr(caps);
 		lua_getfield(L, -1, caps);
 		Z_Free(caps);
+
 		if (!lua_isnil(L, -1))
+		{
 			return 1; // Success! :D That was easy.
+		}
+
 		// Welp, that failed.
 		lua_pop(L, 2); // pop nil and LREG_ACTIONS
-
-		// Hardcoded actions as callable Lua functions!
-		// Retrieving them from this metatable allows them to be case-insensitive!
-		for (i = 0; actionpointers[i].name; i++)
-			if (fasticmp(word, actionpointers[i].name)) {
-				// We push the actionf_t* itself as userdata!
-				LUA_PushUserdata(L, &actionpointers[i].action, META_ACTION);
-				return 1;
-			}
 		return 0;
 	}
 	else if (!mathlib && fastcmp("super",word))
 	{
-		if (!superstack)
+		if (!luaactionstack)
 		{
+			// Not in A_ action routine
 			lua_pushcfunction(L, lib_dummysuper);
 			return 1;
 		}
+
 		for (i = 0; actionpointers[i].name; i++)
-			if (fasticmp(superactions[superstack-1], actionpointers[i].name)) {
+		{
+			if (fasticmp(luaactions[luaactionstack-1], actionpointers[i].name))
+			{
 				LUA_PushUserdata(L, &actionpointers[i].action, META_ACTION);
 				return 1;
 			}
-		return 0;
+		}
+
+		// Not a hardcoded A_ action.
+		lua_pushcfunction(L, lib_dummysuper);
+		return 1;
 	}
 
 	if (fastcmp(word, "BT_USE")) // Remove case when 2.3 nears release...
diff --git a/src/deh_tables.h b/src/deh_tables.h
index 1f265cc999..70347f7dd7 100644
--- a/src/deh_tables.h
+++ b/src/deh_tables.h
@@ -30,6 +30,7 @@ extern UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite
 	memset(FREE_MOBJS,0,sizeof(char *) * NUMMOBJFREESLOTS);\
 	memset(FREE_SKINCOLORS,0,sizeof(char *) * NUMCOLORFREESLOTS);\
 	memset(used_spr,0,sizeof(UINT8) * ((NUMSPRITEFREESLOTS / 8) + 1));\
+	memset(actionsoverridden, LUA_REFNIL, sizeof(actionsoverridden));\
 }
 
 struct flickytypes_s {
diff --git a/src/dehacked.h b/src/dehacked.h
index 1b200e2466..e29aef6ff8 100644
--- a/src/dehacked.h
+++ b/src/dehacked.h
@@ -40,8 +40,9 @@ extern boolean gamedataadded;
 extern boolean titlechanged;
 extern boolean introchanged;
 
-#define MAXRECURSION 30
-extern const char *superactions[MAXRECURSION];
+#define MAX_ACTION_RECURSION 30
+extern const char *luaactions[MAX_ACTION_RECURSION];
+extern UINT8 luaactionstack;
 extern UINT8 superstack;
 
 // If the dehacked patch does not match this version, we throw a warning
diff --git a/src/info.h b/src/info.h
index 031a08b431..6e9460beca 100644
--- a/src/info.h
+++ b/src/info.h
@@ -18,6 +18,7 @@
 #include "d_think.h"
 #include "sounds.h"
 #include "m_fixed.h"
+#include "dehacked.h" // MAX_ACTION_RECURSION
 
 // deh_tables.c now has lists for the more named enums! PLEASE keep them up to date!
 // For great modding!!
@@ -554,7 +555,7 @@ void A_DragonWing();
 void A_DragonSegment();
 void A_ChangeHeight();
 
-extern boolean actionsoverridden[NUMACTIONS];
+extern int actionsoverridden[NUMACTIONS][MAX_ACTION_RECURSION];
 
 // ratio of states to sprites to mobj types is roughly 6 : 1 : 1
 #define NUMMOBJFREESLOTS 512
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index af2d99a0c0..e20202495a 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -66,7 +66,7 @@ const char *const sfxinfo_wopt[] = {
 	"caption",
 	NULL};
 
-boolean actionsoverridden[NUMACTIONS] = {false};
+int actionsoverridden[NUMACTIONS][MAX_ACTION_RECURSION];
 
 //
 // Sprite Names
@@ -645,8 +645,8 @@ static void A_Lua(mobj_t *actor)
 		if (lua_rawequal(gL, -1, -4))
 		{
 			found = true;
-			superactions[superstack] = lua_tostring(gL, -2); // "A_ACTION"
-			++superstack;
+			luaactions[luaactionstack] = lua_tostring(gL, -2); // "A_ACTION"
+			++luaactionstack;
 			lua_pop(gL, 2); // pop the name and function
 			break;
 		}
@@ -661,8 +661,8 @@ static void A_Lua(mobj_t *actor)
 
 	if (found)
 	{
-		--superstack;
-		superactions[superstack] = NULL;
+		--luaactionstack;
+		luaactions[luaactionstack] = NULL;
 	}
 }
 
@@ -816,18 +816,46 @@ boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
 {
 	I_Assert(actor != NULL);
 
-	if (!actionsoverridden[actionnum]) // The action is not overriden,
-		return false; // action not called.
+	if (actionsoverridden[actionnum][0] == LUA_REFNIL)
+	{
+		// The action was not overridden at all,
+		// so call the hardcoded version.
+		return false;
+	}
+
+	if (luaactionstack && fasticmp(actionpointers[actionnum].name, luaactions[luaactionstack-1]))
+	{
+		// The action is calling itself,
+		// so look up the next Lua reference in its stack.
+
+		// 0 is just the reference to the one we're calling,
+		// so we increment here.
+		superstack++;
+
+		if (superstack >= MAX_ACTION_RECURSION)
+		{
+			CONS_Alert(CONS_WARNING, "Max Lua super recursion reached! Cool it on calling super!\n");
+			return false;
+		}
+	}
+	else
+	{
+		// Not calling itself, reset the super counter.
+		superstack = 0;
+	}
 
-	if (superstack && fasticmp(actionpointers[actionnum].name, superactions[superstack-1])) // the action is calling itself,
-		return false; // let it call the hardcoded function instead.
+	if (actionsoverridden[actionnum][superstack] == LUA_REFNIL)
+	{
+		// No Lua reference beyond this point.
+		// Let it call the hardcoded function instead.
+		return false;
+	}
 
+	// Push error function
 	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
-	// grab function by uppercase name.
-	lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS);
-	lua_getfield(gL, -1, actionpointers[actionnum].name);
-	lua_remove(gL, -2); // pop LREG_ACTIONS
+	// Push function by reference.
+	lua_getref(gL, actionsoverridden[actionnum][superstack]);
 
 	if (lua_isnil(gL, -1)) // no match
 	{
@@ -835,7 +863,7 @@ boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
 		return false; // action not called.
 	}
 
-	if (superstack == MAXRECURSION)
+	if (luaactionstack >= MAX_ACTION_RECURSION)
 	{
 		CONS_Alert(CONS_WARNING, "Max Lua Action recursion reached! Cool it on the calling A_Action functions from inside A_Action functions!\n");
 		lua_pop(gL, 2); // pop function and error handler
@@ -849,14 +877,14 @@ boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
 	lua_pushinteger(gL, var1);
 	lua_pushinteger(gL, var2);
 
-	superactions[superstack] = actionpointers[actionnum].name;
-	++superstack;
+	luaactions[luaactionstack] = actionpointers[actionnum].name;
+	++luaactionstack;
 
 	LUA_Call(gL, 3, 0, -(2 + 3));
 	lua_pop(gL, -1); // Error handler
 
-	--superstack;
-	superactions[superstack] = NULL;
+	--luaactionstack;
+	luaactions[luaactionstack] = NULL;
 	return true; // action successfully called.
 }
 
diff --git a/src/lua_script.c b/src/lua_script.c
index a1376ca2e3..5441d315ed 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -485,7 +485,19 @@ static int setglobals(lua_State *L)
 
 		actionnum = LUA_GetActionNumByName(name);
 		if (actionnum < NUMACTIONS)
-			actionsoverridden[actionnum] = true;
+		{
+			int i;
+
+			for (i = MAX_ACTION_RECURSION-1; i > 0; i--)
+			{
+				// Move other references deeper.
+				actionsoverridden[actionnum][i] = actionsoverridden[actionnum][i - 1];
+			}
+
+			// Add the new reference.
+			lua_pushvalue(L, 2);
+			actionsoverridden[actionnum][0] = luaL_ref(L, LUA_REGISTRYINDEX);
+		}
 
 		Z_Free(name);
 		return 0;
-- 
GitLab


From c53f57a22fcf5e26164c41b29ba5be8e675d82a9 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 6 Feb 2022 18:31:10 -0500
Subject: [PATCH 025/518] Handle super stack more intelligently

Counted per action type, and it is decremented after the function is called instead of keeping its old value or even resetting completely when using multiple actions together.
---
 src/deh_lua.c     |  1 -
 src/dehacked.h    |  1 -
 src/lua_infolib.c | 30 ++++++++++++++++++++----------
 3 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/src/deh_lua.c b/src/deh_lua.c
index 43e25a49d1..62a1717a84 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -214,7 +214,6 @@ static int action_call(lua_State *L)
 // Set in lua_infolib.
 const char *luaactions[MAX_ACTION_RECURSION];
 UINT8 luaactionstack = 0;
-UINT8 superstack = 0;
 
 static int lib_dummysuper(lua_State *L)
 {
diff --git a/src/dehacked.h b/src/dehacked.h
index e29aef6ff8..bf4d5c1215 100644
--- a/src/dehacked.h
+++ b/src/dehacked.h
@@ -43,7 +43,6 @@ extern boolean introchanged;
 #define MAX_ACTION_RECURSION 30
 extern const char *luaactions[MAX_ACTION_RECURSION];
 extern UINT8 luaactionstack;
-extern UINT8 superstack;
 
 // If the dehacked patch does not match this version, we throw a warning
 #define PATCHVERSION 220
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index e20202495a..ad7bc7b6f8 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -812,6 +812,7 @@ boolean LUA_SetLuaAction(void *stv, const char *action)
 	return true; // action successfully set.
 }
 
+static UINT8 superstack[NUMACTIONS];
 boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
 {
 	I_Assert(actor != NULL);
@@ -819,7 +820,7 @@ boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
 	if (actionsoverridden[actionnum][0] == LUA_REFNIL)
 	{
 		// The action was not overridden at all,
-		// so call the hardcoded version.
+		// so just call the hardcoded version.
 		return false;
 	}
 
@@ -830,24 +831,27 @@ boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
 
 		// 0 is just the reference to the one we're calling,
 		// so we increment here.
-		superstack++;
+		superstack[actionnum]++;
 
-		if (superstack >= MAX_ACTION_RECURSION)
+		if (superstack[actionnum] >= MAX_ACTION_RECURSION)
 		{
 			CONS_Alert(CONS_WARNING, "Max Lua super recursion reached! Cool it on calling super!\n");
+			superstack[actionnum] = 0;
 			return false;
 		}
 	}
-	else
-	{
-		// Not calling itself, reset the super counter.
-		superstack = 0;
-	}
 
-	if (actionsoverridden[actionnum][superstack] == LUA_REFNIL)
+	if (actionsoverridden[actionnum][superstack[actionnum]] == LUA_REFNIL)
 	{
 		// No Lua reference beyond this point.
 		// Let it call the hardcoded function instead.
+
+		if (superstack[actionnum])
+		{
+			// Decrement super stack
+			superstack[actionnum]--;
+		}
+
 		return false;
 	}
 
@@ -855,7 +859,7 @@ boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
 	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	// Push function by reference.
-	lua_getref(gL, actionsoverridden[actionnum][superstack]);
+	lua_getref(gL, actionsoverridden[actionnum][superstack[actionnum]]);
 
 	if (lua_isnil(gL, -1)) // no match
 	{
@@ -883,6 +887,12 @@ boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor)
 	LUA_Call(gL, 3, 0, -(2 + 3));
 	lua_pop(gL, -1); // Error handler
 
+	if (superstack[actionnum])
+	{
+		// Decrement super stack
+		superstack[actionnum]--;
+	}
+
 	--luaactionstack;
 	luaactions[luaactionstack] = NULL;
 	return true; // action successfully called.
-- 
GitLab


From 7d1faf7e2c0f4db8c4d953dd40f7b899b4bfadef Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 19 Feb 2022 12:12:04 +0100
Subject: [PATCH 026/518] Handle invalid resolutions, make Backspace work in
 resolution menu.

---
 src/m_menu.c | 16 ++++++++++++++--
 src/screen.c |  9 +++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index c034aee48d..220d67a3fb 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -13226,11 +13226,11 @@ static void M_DrawVideoMode(void)
 				vid.width, vid.height));
 		V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 116, (cv_fullscreen.value ? 0 : V_TRANSLUCENT),
 			va("Default mode is %c%dx%d",
-				(SCR_IsAspectCorrect(cv_scr_width.value, cv_scr_height.value)) ? 0x83 : 0x80,
+				(SCR_IsAspectCorrect(cv_scr_width.value, cv_scr_height.value)) ? 0x83 : (!(VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value)+1) ? 0x85 : 0x80),
 				cv_scr_width.value, cv_scr_height.value));
 		V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 124, (cv_fullscreen.value ? V_TRANSLUCENT : 0),
 			va("Windowed mode is %c%dx%d",
-				(SCR_IsAspectCorrect(cv_scr_width_w.value, cv_scr_height_w.value)) ? 0x83 : 0x80,
+				(SCR_IsAspectCorrect(cv_scr_width_w.value, cv_scr_height_w.value)) ? 0x83 : (!(VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value)+1) ? 0x85 : 0x80),
 				cv_scr_width_w.value, cv_scr_height_w.value));
 
 		V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 138,
@@ -13448,6 +13448,18 @@ static void M_HandleVideoMode(INT32 ch)
 				M_ClearMenus(true);
 			break;
 
+		case KEY_BACKSPACE:
+			S_StartSound(NULL, sfx_menu1);
+			CV_Set(&cv_scr_width, cv_scr_width.defaultvalue);
+			CV_Set(&cv_scr_height, cv_scr_height.defaultvalue);
+			CV_Set(&cv_scr_width_w, cv_scr_width_w.defaultvalue);
+			CV_Set(&cv_scr_height_w, cv_scr_height_w.defaultvalue);
+			if (cv_fullscreen.value)
+				setmodeneeded = VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value)+1;
+			else
+				setmodeneeded = VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value)+1;
+			break;
+
 		case KEY_F11:
 			S_StartSound(NULL, sfx_menu1);
 			CV_SetValue(&cv_fullscreen, !cv_fullscreen.value);
diff --git a/src/screen.c b/src/screen.c
index f21ea2a93c..3a548bece1 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -375,6 +375,9 @@ void SCR_CheckDefaultMode(void)
 			setmodeneeded = VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value) + 1; // see note above
 		else
 			setmodeneeded = VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value) + 1; // see note above
+
+		if (setmodeneeded <= 0)
+			CONS_Alert(CONS_WARNING, "Invalid resolution given, defaulting to base resolution\n");
 	}
 
 	if (cv_renderer.value != (signed)rendermode)
@@ -412,6 +415,12 @@ void SCR_ChangeFullscreen(void)
 			setmodeneeded = VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value) + 1;
 		else
 			setmodeneeded = VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value) + 1;
+
+		if (setmodeneeded <= 0) // hacky safeguard
+		{
+			CONS_Alert(CONS_WARNING, "Invalid resolution given, defaulting to base resolution.\n");
+			setmodeneeded = VID_GetModeForSize(BASEVIDWIDTH, BASEVIDHEIGHT) + 1;
+		}
 	}
 	return;
 #endif
-- 
GitLab


From f077021591132c3e9c565169e70e030bd378bbf0 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 19 Feb 2022 15:15:06 +0100
Subject: [PATCH 027/518] Clean up renderer toggle at SteelT's suggestion.

---
 src/m_menu.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 220d67a3fb..c3c7db923b 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3396,10 +3396,7 @@ boolean M_Responder(event_t *ev)
 			// Same with Moviemode on F9
 
 			case KEY_F10: // Renderer toggle, also processed inside menus
-				if (cv_renderer.value == render_soft)
-					CV_SetValue(&cv_renderer, render_opengl);
-				else if (cv_renderer.value == render_opengl)
-					CV_SetValue(&cv_renderer, render_soft);
+				CV_AddValue(&cv_renderer, 1);
 				return true;
 
 			case KEY_F11: // Fullscreen toggle, also processed inside menus
@@ -3583,10 +3580,7 @@ boolean M_Responder(event_t *ev)
 			return false;
 
 		case KEY_F10: // Renderer toggle, also processed outside menus
-			if (cv_renderer.value == render_soft)
-				CV_SetValue(&cv_renderer, render_opengl);
-			else if (cv_renderer.value == render_opengl)
-				CV_SetValue(&cv_renderer, render_soft);
+			CV_AddValue(&cv_renderer, 1);
 			return true;
 
 		case KEY_F11: // Fullscreen toggle, also processed outside menus
-- 
GitLab


From f7b166da07d821c655cc804111d419ab3c14c694 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 27 Feb 2022 07:56:45 -0500
Subject: [PATCH 028/518] Refresh sprite2s

Allows for custom characters to be loaded first, then a wad that adds a custom sprite2, and the custom character's sprite2s won't be discarded.
---
 src/deh_lua.c |   3 +
 src/deh_soc.c |   2 +
 src/r_skins.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/r_skins.h |   2 +
 4 files changed, 158 insertions(+), 5 deletions(-)

diff --git a/src/deh_lua.c b/src/deh_lua.c
index a2ffca95b0..7984a38c82 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -188,6 +188,9 @@ static inline int lib_freeslot(lua_State *L)
 		lua_remove(L, 1);
 		continue;
 	}
+
+	R_RefreshSprite2();
+
 	return r;
 }
 
diff --git a/src/deh_soc.c b/src/deh_soc.c
index 3a611f3ba1..dd33594ea2 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -513,6 +513,8 @@ void readfreeslots(MYFILE *f)
 	} while (!myfeof(f)); // finish when the line is empty
 
 	Z_Free(s);
+
+	R_RefreshSprite2();
 }
 
 void readthing(MYFILE *f, INT32 num)
diff --git a/src/r_skins.c b/src/r_skins.c
index 86c0bbc544..0102804036 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -499,7 +499,7 @@ static UINT16 W_CheckForPatchSkinMarkerInPwad(UINT16 wadid, UINT16 startlump)
 	return INT16_MAX; // not found
 }
 
-static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, skin_t *skin)
+static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, skin_t *skin, UINT8 start_spr2)
 {
 	UINT16 newlastlump;
 	UINT8 sprite2;
@@ -521,7 +521,7 @@ static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, ski
 	{
 		newlastlump++;
 		// load all sprite sets we are aware of... for super!
-		for (sprite2 = 0; sprite2 < free_spr2; sprite2++)
+		for (sprite2 = start_spr2; sprite2 < free_spr2; sprite2++)
 			R_AddSingleSpriteDef(spr2names[sprite2], &skin->sprites[FF_SPR2SUPER|sprite2], wadnum, newlastlump, *lastlump);
 
 		newlastlump--;
@@ -529,7 +529,7 @@ static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, ski
 	}
 
 	// load all sprite sets we are aware of... for normal stuff.
-	for (sprite2 = 0; sprite2 < free_spr2; sprite2++)
+	for (sprite2 = start_spr2; sprite2 < free_spr2; sprite2++)
 		R_AddSingleSpriteDef(spr2names[sprite2], &skin->sprites[sprite2], wadnum, *lump, *lastlump);
 
 	if (skin->sprites[0].numframes == 0)
@@ -795,7 +795,7 @@ next_token:
 		free(buf2);
 
 		// Add sprites
-		R_LoadSkinSprites(wadnum, &lump, &lastlump, skin);
+		R_LoadSkinSprites(wadnum, &lump, &lastlump, skin, 0);
 		//ST_LoadFaceGraphics(numskins); -- nah let's do this elsewhere
 
 		R_FlushTranslationColormapCache();
@@ -928,7 +928,7 @@ next_token:
 		}
 
 		// Patch sprites
-		R_LoadSkinSprites(wadnum, &lump, &lastlump, skin);
+		R_LoadSkinSprites(wadnum, &lump, &lastlump, skin, 0);
 		//ST_LoadFaceGraphics(skinnum); -- nah let's do this elsewhere
 
 		R_FlushTranslationColormapCache();
@@ -941,3 +941,149 @@ next_token:
 
 #undef HUDNAMEWRITE
 #undef SYMBOLCONVERT
+
+static UINT16 W_CheckForEitherSkinMarkerInPwad(UINT16 wadid, UINT16 startlump)
+{
+	UINT16 i;
+	const char *S_SKIN = "S_SKIN";
+	const char *P_SKIN = "P_SKIN";
+	lumpinfo_t *lump_p;
+
+	// scan forward, start at <startlump>
+	if (startlump < wadfiles[wadid]->numlumps)
+	{
+		lump_p = wadfiles[wadid]->lumpinfo + startlump;
+		for (i = startlump; i < wadfiles[wadid]->numlumps; i++, lump_p++)
+			if (memcmp(lump_p->name,S_SKIN,6)==0 || memcmp(lump_p->name,P_SKIN,6)==0)
+				return i;
+	}
+	return INT16_MAX; // not found
+}
+
+static void R_RefreshSprite2ForWad(UINT16 wadnum, UINT8 start_spr2)
+{
+	UINT16 lump, lastlump = 0;
+	char *buf;
+	char *buf2;
+	char *stoken;
+	char *value;
+	size_t size;
+	skin_t *skin;
+	boolean noskincomplain;
+
+	//
+	// search for all skin patch markers in pwad
+	//
+
+	while ((lump = W_CheckForEitherSkinMarkerInPwad(wadnum, lastlump)) != INT16_MAX)
+	{
+		INT32 skinnum = 0;
+
+		// advance by default
+		lastlump = lump + 1;
+
+		buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE);
+		size = W_LumpLengthPwad(wadnum, lump);
+
+		// for strtok
+		buf2 = malloc(size+1);
+		if (!buf2)
+			I_Error("R_RefreshSprite2ForWad: No more free memory\n");
+		M_Memcpy(buf2,buf,size);
+		buf2[size] = '\0';
+
+		skin = NULL;
+		noskincomplain = false;
+
+		/*
+		Parse. Has more phases than the parser in R_AddSkins because it needs to have the patching name first (no default skin name is acceptible for patching, unlike skin creation)
+		*/
+
+		stoken = strtok(buf2, "\r\n= ");
+		while (stoken)
+		{
+			if ((stoken[0] == '/' && stoken[1] == '/')
+				|| (stoken[0] == '#'))// skip comments
+			{
+				stoken = strtok(NULL, "\r\n"); // skip end of line
+				goto next_token;              // find the real next token
+			}
+
+			value = strtok(NULL, "\r\n= ");
+
+			if (!value)
+				I_Error("R_RefreshSprite2ForWad: syntax error in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename);
+
+			if (!stricmp(stoken, "name"))
+			{
+				strlwr(value);
+				skinnum = R_SkinAvailable(value);
+				if (skinnum != -1)
+					skin = &skins[skinnum];
+				else
+				{
+					CONS_Debug(DBG_SETUP, "R_RefreshSprite2ForWad: unknown skin name in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename);
+					noskincomplain = true;
+				}
+			}
+
+			if (!skin)
+				break;
+
+next_token:
+			stoken = strtok(NULL, "\r\n= ");
+		}
+		free(buf2);
+
+		if (!skin) // Didn't include a name parameter? What a waste.
+		{
+			if (!noskincomplain)
+				CONS_Debug(DBG_SETUP, "R_RefreshSprite2ForWad: no skin name given in P_SKIN lump #%d (WAD %s)\n", lump, wadfiles[wadnum]->filename);
+			continue;
+		}
+
+		// Update sprites, in the range of (start_spr2 - free_spr2-1)
+		R_LoadSkinSprites(wadnum, &lump, &lastlump, skin, start_spr2);
+		//R_FlushTranslationColormapCache(); // I don't think this is needed for what we're doing?
+	}
+}
+
+static playersprite_t old_spr2 = SPR2_FIRSTFREESLOT;
+void R_RefreshSprite2(void)
+{
+	// Sprite2s being defined by custom wads can create situations where
+	// a custom character might want to add support, but due to load order,
+	// might not be defined in time.
+
+	// The trick where you load characters then level packs to keep savedata
+	// in particular will practically garantuee a level pack can NEVER add custom animations,
+	// because custom character's Sprite2s will not be added.
+
+	// So, go through every file, and reload the sprite2s that were added.
+
+	INT32 i;
+
+	if (old_spr2 > free_spr2)
+	{
+#ifdef PARANOIA
+		I_Error("R_RefreshSprite2: old_spr2 is too high?! (old_spr2: %d, free_spr2: %d)\n", old_spr2, free_spr2);
+#else
+		// Just silently fix
+		old_spr2 = free_spr2;
+#endif
+	}
+
+	if (old_spr2 == free_spr2)
+	{
+		// No sprite2s were added since the last time we did freeslots.
+		return;
+	}
+
+	for (i = 0; i < numwadfiles; i++)
+	{
+		R_RefreshSprite2ForWad(i, old_spr2);
+	}
+
+	// Update previous value.
+	old_spr2 = free_spr2;
+}
diff --git a/src/r_skins.h b/src/r_skins.h
index a38997f4dd..2ef1bdd650 100644
--- a/src/r_skins.h
+++ b/src/r_skins.h
@@ -100,4 +100,6 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile);
 
 UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player);
 
+void R_RefreshSprite2(void);
+
 #endif //__R_SKINS__
-- 
GitLab


From 6a969c163b4f72b98c5d24d1eafe30e02045c044 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Wed, 6 Apr 2022 15:31:25 +0200
Subject: [PATCH 029/518] Make boss 3's shockwave into a customizable action.

---
 src/deh_tables.c |  1 +
 src/info.h       |  2 ++
 src/p_enemy.c    | 51 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 54 insertions(+)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index 8dbe314cc1..bed81e60c9 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -198,6 +198,7 @@ actionpointer_t actionpointers[] =
 	{{A_Boss3TakeDamage},        "A_BOSS3TAKEDAMAGE"},
 	{{A_Boss3Path},              "A_BOSS3PATH"},
 	{{A_Boss3ShockThink},        "A_BOSS3SHOCKTHINK"},
+	{{A_Shockwave},              "A_SHOCKWAVE"},
 	{{A_LinedefExecute},         "A_LINEDEFEXECUTE"},
 	{{A_PlaySeeSound},           "A_PLAYSEESOUND"},
 	{{A_PlayAttackSound},        "A_PLAYATTACKSOUND"},
diff --git a/src/info.h b/src/info.h
index 1b7a201cea..04c048dd8a 100644
--- a/src/info.h
+++ b/src/info.h
@@ -151,6 +151,7 @@ enum actionnum
 	A_BOSS3TAKEDAMAGE,
 	A_BOSS3PATH,
 	A_BOSS3SHOCKTHINK,
+	A_SHOCKWAVE,
 	A_LINEDEFEXECUTE,
 	A_PLAYSEESOUND,
 	A_PLAYATTACKSOUND,
@@ -414,6 +415,7 @@ void A_Boss1Spikeballs();
 void A_Boss3TakeDamage();
 void A_Boss3Path();
 void A_Boss3ShockThink();
+void A_Shockwave();
 void A_LinedefExecute();
 void A_PlaySeeSound();
 void A_PlayAttackSound();
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 87aa5ff2f8..efa413a444 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -173,6 +173,7 @@ void A_Boss1Spikeballs(mobj_t *actor);
 void A_Boss3TakeDamage(mobj_t *actor);
 void A_Boss3Path(mobj_t *actor);
 void A_Boss3ShockThink(mobj_t *actor);
+void A_Shockwave(mobj_t *actor);
 void A_LinedefExecute(mobj_t *actor);
 void A_PlaySeeSound(mobj_t *actor);
 void A_PlayAttackSound(mobj_t *actor);
@@ -8326,6 +8327,56 @@ void A_Boss3ShockThink(mobj_t *actor)
 	}
 }
 
+// Function: A_Shockwave
+//
+// Description: Spawns a shockwave of objects. Best used to spawn objects that call A_Boss3ShockThink.
+//
+// var1 = object spawned
+// var2 = amount of objects spawned
+//
+void A_Shockwave(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+	INT32 i;
+
+	angle_t ang = 0, interval;
+	mobj_t *shock = NULL, *sfirst = NULL, *sprev = NULL;
+
+	if (LUA_CallAction(A_SHOCKWAVE, actor))
+		return;
+
+	if (locvar2 == 0)
+		locvar2 = 24; // a sensible default, just in case
+
+	interval = FixedAngle((360 << FRACBITS) / locvar2);
+
+	for (i = 0; i < locvar2; i++)
+	{
+		shock = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
+		P_SetTarget(&shock->target, actor);
+		shock->fuse = shock->info->painchance;
+
+		if (i % 2 == 0)
+			P_SetMobjState(shock, shock->state->nextstate);
+
+		if (!sprev)
+			sfirst = shock;
+		else
+		{
+			if (i == locvar2 - 1)
+				P_SetTarget(&shock->hnext, sfirst);
+			P_SetTarget(&sprev->hnext, shock);
+		}
+
+		P_Thrust(shock, ang, shock->info->speed);
+		ang += interval;
+		sprev = shock;
+	}
+	
+	S_StartSound(actor, shock->info->seesound);
+}
+
 // Function: A_LinedefExecute
 //
 // Description: Object's location is used to set the calling sector. The tag used is var1. Optionally, if var2 is set, the actor's angle (multiplied by var2) is added to the tag number as well.
-- 
GitLab


From f564090c80c1ea54ebfebcdac0ea64618c5d0c6b Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Wed, 7 Sep 2022 14:00:56 +0200
Subject: [PATCH 030/518] Two small renderer switching changes: - Allow
 renderer switching in resolution menu - Fix "(F10)" suffix not being shown on
 the renderer option

---
 src/m_menu.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index b527c4c1a5..19c5eaeaf3 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -2157,7 +2157,7 @@ static void M_VideoOptions(INT32 choice)
 	{
 		OP_VideoOptionsMenu[op_video_renderer].status = (IT_STRING | IT_CVAR);
 		OP_VideoOptionsMenu[op_video_renderer].patch = NULL;
-		OP_VideoOptionsMenu[op_video_renderer].text = "Renderer";
+		OP_VideoOptionsMenu[op_video_renderer].text = "Renderer (F10)";
 	}
 #endif
 
@@ -13456,6 +13456,10 @@ static void M_HandleVideoMode(INT32 ch)
 				setmodeneeded = VID_GetModeForSize(cv_scr_width_w.value, cv_scr_height_w.value)+1;
 			break;
 
+		case KEY_F10: // Renderer toggle, also processed inside menus
+			CV_AddValue(&cv_renderer, 1);
+			break;
+
 		case KEY_F11:
 			S_StartSound(NULL, sfx_menu1);
 			CV_SetValue(&cv_fullscreen, !cv_fullscreen.value);
-- 
GitLab


From 3da9fb636aebd8311e4119f7f6af65a924a0e0df Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 17 Sep 2022 10:51:16 +0200
Subject: [PATCH 031/518] Add plane scroller features to binary map format: -
 Added actions for scrolling floor + ceiling simultaneously - Added flag to
 use X offset for speed, instead of line length

---
 extras/conf/SRB2-22.cfg                      | 89 +++++++++++++++++++-
 extras/conf/udb/Includes/SRB222_linedefs.cfg | 47 ++++++++++-
 src/p_setup.c                                | 13 ++-
 3 files changed, 146 insertions(+), 3 deletions(-)

diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index 969f645f3f..0bf009f79b 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -2824,36 +2824,63 @@ linedeftypes
 		{
 			title = "Scroll Floor Texture";
 			prefix = "(510)";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		511
 		{
 			title = "Scroll Floor Texture (Accelerative)";
 			prefix = "(511)";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		512
 		{
 			title = "Scroll Floor Texture (Displacement)";
 			prefix = "(512)";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		513
 		{
 			title = "Scroll Ceiling Texture";
 			prefix = "(513)";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		514
 		{
 			title = "Scroll Ceiling Texture (Accelerative)";
 			prefix = "(514)";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		515
 		{
 			title = "Scroll Ceiling Texture (Displacement)";
 			prefix = "(515)";
+			flags8192text = "[13] Use angle and X offset";
+		}
+
+		516
+		{
+			title = "Scroll Floor and Ceiling Texture";
+			prefix = "(516)";
+			flags8192text = "[13] Use angle and X offset";
+		}
+
+		517
+		{
+			title = "Scroll Floor and Ceiling Texture (Accelerative)";
+			prefix = "(517)";
+			flags8192text = "[13] Use angle and X offset";
+		}
+
+		518
+		{
+			title = "Scroll Floor and Ceiling Texture (Displacement)";
+			prefix = "(518)";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		520
@@ -2861,6 +2888,7 @@ linedeftypes
 			title = "Carry Objects on Floor";
 			prefix = "(520)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		521
@@ -2868,6 +2896,7 @@ linedeftypes
 			title = "Carry Objects on Floor (Accelerative)";
 			prefix = "(521)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		522
@@ -2875,6 +2904,7 @@ linedeftypes
 			title = "Carry Objects on Floor (Displacement)";
 			prefix = "(522)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		523
@@ -2882,6 +2912,7 @@ linedeftypes
 			title = "Carry Objects on Ceiling";
 			prefix = "(523)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		524
@@ -2889,6 +2920,7 @@ linedeftypes
 			title = "Carry Objects on Ceiling (Accelerative)";
 			prefix = "(524)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		525
@@ -2896,6 +2928,31 @@ linedeftypes
 			title = "Carry Objects on Ceiling (Displacement)";
 			prefix = "(525)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
+		}
+
+		526
+		{
+			title = "Carry Objects on Floor and Ceiling";
+			prefix = "(526)";
+			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
+		}
+
+		527
+		{
+			title = "Carry Objects on Floor and Ceiling (Accelerative)";
+			prefix = "(527)";
+			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
+		}
+
+		528
+		{
+			title = "Carry Objects on Floor and Ceiling (Displacement)";
+			prefix = "(528)";
+			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		530
@@ -2903,6 +2960,7 @@ linedeftypes
 			title = "Scroll Floor Texture and Carry Objects";
 			prefix = "(530)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		531
@@ -2910,6 +2968,7 @@ linedeftypes
 			title = "Scroll Floor Texture and Carry Objects (Accelerative)";
 			prefix = "(531)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		532
@@ -2917,6 +2976,7 @@ linedeftypes
 			title = "Scroll Floor Texture and Carry Objects (Displacement)";
 			prefix = "(532)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		533
@@ -2924,6 +2984,7 @@ linedeftypes
 			title = "Scroll Ceiling Texture and Carry Objects";
 			prefix = "(533)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		534
@@ -2931,6 +2992,7 @@ linedeftypes
 			title = "Scroll Ceiling Texture and Carry Objects (Accelerative)";
 			prefix = "(534)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		535
@@ -2938,6 +3000,31 @@ linedeftypes
 			title = "Scroll Ceiling Texture and Carry Objects (Displacement)";
 			prefix = "(535)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
+		}
+
+		536
+		{
+			title = "Scroll Floor and Ceiling Texture and Carry Objects";
+			prefix = "(536)";
+			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
+		}
+
+		537
+		{
+			title = "Scroll Floor and Ceiling Texture and Carry Objects (Accelerative)";
+			prefix = "(537)";
+			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
+		}
+
+		538
+		{
+			title = "Scroll Floor and Ceiling Texture and Carry Objects (Displacement)";
+			prefix = "(538)";
+			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use angle and X offset";
 		}
 	}
 
@@ -6949,7 +7036,7 @@ thingtypes
 	{
 		color = 10; // Green
 		title = "Tutorial";
-		
+
 		799
 		{
 			title = "Tutorial Plant";
diff --git a/extras/conf/udb/Includes/SRB222_linedefs.cfg b/extras/conf/udb/Includes/SRB222_linedefs.cfg
index fff9edf109..4b9e5fd97b 100644
--- a/extras/conf/udb/Includes/SRB222_linedefs.cfg
+++ b/extras/conf/udb/Includes/SRB222_linedefs.cfg
@@ -1286,6 +1286,21 @@ doom
 			title = "Scroll Ceiling Texture (Displacement)";
 			prefix = "(515)";
 		}
+		516
+		{
+			title = "Scroll Floor and Ceiling Texture";
+			prefix = "(516)";
+		}
+		517
+		{
+			title = "Scroll Floor and Ceiling Texture (Accelerative)";
+			prefix = "(517)";
+		}
+		518
+		{
+			title = "Scroll Floor and Ceiling Texture (Displacement)";
+			prefix = "(518)";
+		}
 		520
 		{
 			title = "Carry Objects on Floor";
@@ -1316,6 +1331,21 @@ doom
 			title = "Carry Objects on Ceiling (Displacement)";
 			prefix = "(525)";
 		}
+		526
+		{
+			title = "Carry Objects on Floor and Ceiling";
+			prefix = "(526)";
+		}
+		527
+		{
+			title = "Carry Objects on Floor and Ceiling (Accelerative)";
+			prefix = "(527)";
+		}
+		528
+		{
+			title = "Carry Objects on Floor and Ceiling (Displacement)";
+			prefix = "(528)";
+		}
 		530
 		{
 			title = "Scroll Floor Texture and Carry Objects";
@@ -1346,6 +1376,21 @@ doom
 			title = "Scroll Ceiling Texture and Carry Objects (Displacement)";
 			prefix = "(535)";
 		}
+		536
+		{
+			title = "Scroll Floor and Ceiling Texture and Carry Objects";
+			prefix = "(536)";
+		}
+		537
+		{
+			title = "Scroll Floor and Ceiling Texture and Carry Objects (Accelerative)";
+			prefix = "(537)";
+		}
+		538
+		{
+			title = "Scroll Floor and Ceiling Texture and Carry Objects (Displacement)";
+			prefix = "(538)";
+		}
 	}
 
 	pusher
@@ -2593,7 +2638,7 @@ udmf
 				}
 			}
 		}
-		
+
 		190
 		{
 			title = "Rising";
diff --git a/src/p_setup.c b/src/p_setup.c
index 146d5d3023..9712528efe 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -5594,25 +5594,36 @@ static void P_ConvertBinaryLinedefTypes(void)
 		case 513: //Scroll ceiling texture
 		case 514: //Scroll ceiling texture (accelerative)
 		case 515: //Scroll ceiling texture (displacement)
+		case 516: //Scroll floor and ceiling texture
+		case 517: //Scroll floor and ceiling texture (accelerative)
+		case 518: //Scroll floor and ceiling texture (displacement)
 		case 520: //Carry objects on floor
 		case 521: //Carry objects on floor (accelerative)
 		case 522: //Carry objects on floor (displacement)
 		case 523: //Carry objects on ceiling
 		case 524: //Carry objects on ceiling (accelerative)
 		case 525: //Carry objects on ceiling (displacement)
+		case 526: //Carry objects on floor and ceiling
+		case 527: //Carry objects on floor and ceiling (accelerative)
+		case 528: //Carry objects on floor and ceiling (displacement)
 		case 530: //Scroll floor texture and carry objects
 		case 531: //Scroll floor texture and carry objects (accelerative)
 		case 532: //Scroll floor texture and carry objects (displacement)
 		case 533: //Scroll ceiling texture and carry objects
 		case 534: //Scroll ceiling texture and carry objects (accelerative)
 		case 535: //Scroll ceiling texture and carry objects (displacement)
+		case 536: //Scroll floor and ceiling texture and carry objects
+		case 537: //Scroll floor and ceiling texture and carry objects (accelerative)
+		case 538: //Scroll floor and ceiling texture and carry objects (displacement)
 			lines[i].args[0] = tag;
-			lines[i].args[1] = ((lines[i].special % 10) < 3) ? TMP_FLOOR : TMP_CEILING;
+			lines[i].args[1] = ((lines[i].special % 10) < 6) ? (((lines[i].special % 10) < 3) ? TMP_FLOOR : TMP_CEILING) : TMP_BOTH;
 			lines[i].args[2] = ((lines[i].special - 510)/10 + 1) % 3;
 			lines[i].args[3] = R_PointToDist2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y) >> FRACBITS;
 			lines[i].args[4] = (lines[i].special % 10) % 3;
 			if (lines[i].args[2] != TMS_SCROLLONLY && !(lines[i].flags & ML_NOCLIMB))
 				lines[i].args[4] |= TMST_NONEXCLUSIVE;
+			if (lines[i].flags & ML_EFFECT6)
+				lines[i].args[3] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
 			lines[i].special = 510;
 			break;
 		case 540: //Floor friction
-- 
GitLab


From fe8485cc2ffd6ca761e8423f08039ba5672bce29 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 17 Sep 2022 11:20:51 +0200
Subject: [PATCH 032/518] Add flag to set wind/current/push/pull strength using
 X offset

---
 extras/conf/SRB2-22.cfg | 19 +++++++++++++------
 src/p_setup.c           |  9 +++++----
 2 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index 0bf009f79b..6cd2f0d04f 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -3036,48 +3036,54 @@ linedeftypes
 		{
 			title = "Wind";
 			prefix = "(541)";
-			flags512text = "[9] Player slides";
 			flags64text = "[6] Exclusive";
+			flags512text = "[9] Player slides";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		542
 		{
 			title = "Upwards Wind";
 			prefix = "(542)";
-			flags512text = "[9] Player slides";
 			flags64text = "[6] Exclusive";
+			flags512text = "[9] Player slides";
+			flags8192text = "[13] Use X offset";
 		}
 
 		543
 		{
 			title = "Downwards Wind";
 			prefix = "(543)";
-			flags512text = "[9] Player slides";
 			flags64text = "[6] Exclusive";
+			flags512text = "[9] Player slides";
+			flags8192text = "[13] Use X offset";
 		}
 
 		544
 		{
 			title = "Current";
 			prefix = "(544)";
-			flags512text = "[9] Player slides";
 			flags64text = "[6] Exclusive";
+			flags512text = "[9] Player slides";
+			flags8192text = "[13] Use angle and X offset";
 		}
 
 		545
 		{
 			title = "Upwards Current";
 			prefix = "(545)";
-			flags512text = "[9] Player slides";
 			flags64text = "[6] Exclusive";
+			flags512text = "[9] Player slides";
+			flags8192text = "[13] Use X offset";
 		}
 
 		546
 		{
 			title = "Downwards Current";
 			prefix = "(546)";
-			flags512text = "[9] Player slides";
 			flags64text = "[6] Exclusive";
+			flags512text = "[9] Player slides";
+			flags8192text = "[13] Use X offset";
 		}
 
 		547
@@ -3085,6 +3091,7 @@ linedeftypes
 			title = "Push/Pull";
 			prefix = "(547)";
 			flags64text = "[6] Exclusive";
+			flags8192text = "[13] Use X offset";
 		}
 	}
 
diff --git a/src/p_setup.c b/src/p_setup.c
index 9712528efe..acee2bb69f 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -5651,17 +5651,18 @@ static void P_ConvertBinaryLinedefTypes(void)
 		case 544: //Current
 		case 545: //Upwards current
 		case 546: //Downwards current
+			fixed_t speed = (lines[i].flags & ML_EFFECT6) ? sides[lines[i].sidenum[0]].textureoffset : R_PointToDist2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y);
 			lines[i].args[0] = tag;
 			switch ((lines[i].special - 541) % 3)
 			{
 				case 0:
-					lines[i].args[1] = R_PointToDist2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y) >> FRACBITS;
+					lines[i].args[1] = speed >> FRACBITS;
 					break;
 				case 1:
-					lines[i].args[2] = R_PointToDist2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y) >> FRACBITS;
+					lines[i].args[2] = speed >> FRACBITS;
 					break;
 				case 2:
-					lines[i].args[2] = -R_PointToDist2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y) >> FRACBITS;
+					lines[i].args[2] = -speed >> FRACBITS;
 					break;
 			}
 			lines[i].args[3] = (lines[i].special >= 544) ? p_current : p_wind;
@@ -6308,7 +6309,7 @@ static void P_ConvertBinaryThingTypes(void)
 			}
 
 			mapthings[i].args[0] = mapthings[i].angle;
-			mapthings[i].args[1] = P_AproxDistance(line->dx >> FRACBITS, line->dy >> FRACBITS);
+			mapthings[i].args[1] = (line->flags & ML_EFFECT6) ? sides[line->sidenum[0]].textureoffset >> FRACBITS : P_AproxDistance(line->dx >> FRACBITS, line->dy >> FRACBITS);
 			if (mapthings[i].type == 755)
 				mapthings[i].args[1] *= -1;
 			if (mapthings[i].options & MTF_OBJECTSPECIAL)
-- 
GitLab


From cc3d4acdcdd18d97140b40824c2f46ee71476d4a Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 20 Sep 2022 20:42:19 +0200
Subject: [PATCH 033/518] Fix AppVeyor build failure

---
 src/p_setup.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index acee2bb69f..a85e27e990 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -5651,18 +5651,19 @@ static void P_ConvertBinaryLinedefTypes(void)
 		case 544: //Current
 		case 545: //Upwards current
 		case 546: //Downwards current
-			fixed_t speed = (lines[i].flags & ML_EFFECT6) ? sides[lines[i].sidenum[0]].textureoffset : R_PointToDist2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y);
+		{
+			fixed_t strength = (lines[i].flags & ML_EFFECT6) ? sides[lines[i].sidenum[0]].textureoffset : R_PointToDist2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y);
 			lines[i].args[0] = tag;
 			switch ((lines[i].special - 541) % 3)
 			{
 				case 0:
-					lines[i].args[1] = speed >> FRACBITS;
+					lines[i].args[1] = strength >> FRACBITS;
 					break;
 				case 1:
-					lines[i].args[2] = speed >> FRACBITS;
+					lines[i].args[2] = strength >> FRACBITS;
 					break;
 				case 2:
-					lines[i].args[2] = -speed >> FRACBITS;
+					lines[i].args[2] = -strength >> FRACBITS;
 					break;
 			}
 			lines[i].args[3] = (lines[i].special >= 544) ? p_current : p_wind;
@@ -5672,6 +5673,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 				lines[i].args[4] |= TMPF_NONEXCLUSIVE;
 			lines[i].special = 541;
 			break;
+		}
 		case 600: //Floor lighting
 		case 601: //Ceiling lighting
 			lines[i].args[0] = tag;
-- 
GitLab


From 049bfd7bd4c0b3ba27cfcf3f38fb7653ebeaaf76 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Sun, 9 Oct 2022 17:17:16 +0200
Subject: [PATCH 034/518] Minor code cleanup in P_ConvertBinaryLinedefTypes

---
 src/p_setup.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index a85e27e990..1f8d316a76 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -5618,12 +5618,10 @@ static void P_ConvertBinaryLinedefTypes(void)
 			lines[i].args[0] = tag;
 			lines[i].args[1] = ((lines[i].special % 10) < 6) ? (((lines[i].special % 10) < 3) ? TMP_FLOOR : TMP_CEILING) : TMP_BOTH;
 			lines[i].args[2] = ((lines[i].special - 510)/10 + 1) % 3;
-			lines[i].args[3] = R_PointToDist2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y) >> FRACBITS;
+			lines[i].args[3] = ((lines[i].flags & ML_EFFECT6) ? sides[lines[i].sidenum[0]].textureoffset : R_PointToDist2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y)) >> FRACBITS;
 			lines[i].args[4] = (lines[i].special % 10) % 3;
 			if (lines[i].args[2] != TMS_SCROLLONLY && !(lines[i].flags & ML_NOCLIMB))
 				lines[i].args[4] |= TMST_NONEXCLUSIVE;
-			if (lines[i].flags & ML_EFFECT6)
-				lines[i].args[3] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
 			lines[i].special = 510;
 			break;
 		case 540: //Floor friction
-- 
GitLab


From 2714ac44b47af785e3e4d7532731192911ae0879 Mon Sep 17 00:00:00 2001
From: katsy <katmint@live.com>
Date: Wed, 12 Oct 2022 20:38:29 -0500
Subject: [PATCH 035/518] reallow score chains from rolling

---
 src/p_user.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/p_user.c b/src/p_user.c
index 2f522ad4b1..bcc9cb20b1 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8412,12 +8412,12 @@ void P_MovePlayer(player_t *player)
 		}
 	}
 
-	// End your chain if you're on the ground or climbing a wall.
+	// End your chain if you're on the ground while not rolling, or climbing a wall.
 	// But not if invincible! Allow for some crazy long chains with it.
 	// Also keep in mind the PF_JUMPED check.
 	// If we lacked this, stepping up while jumping up would reset score.
 	// (for instance, when climbing up off a wall.)
-	if ((onground || player->climbing) && !(player->pflags & PF_JUMPED) && player->powers[pw_invulnerability] <= 1)
+	if ((onground || player->climbing) && ((player->pflags & (PF_STARTDASH|PF_SPINNING)) != PF_SPINNING) && !(player->pflags & PF_JUMPED) && player->powers[pw_invulnerability] <= 1)
 		P_ResetScore(player);
 
 	// Show the "THOK!" graphic when spinning quickly across the ground. (even applies to non-spinners, in the case of zoom tubes)
-- 
GitLab


From 1b43cdddd59ebeb6ea76721ba953370476478f0f Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 27 Feb 2022 10:11:55 -0500
Subject: [PATCH 036/518] Allow saving in modified games.

---
 src/d_main.c   |  6 ++++
 src/d_netcmd.c |  6 ++--
 src/f_finale.c | 17 +++------
 src/g_game.c   | 97 +++++++++++++++++++++++++++++++++-----------------
 src/hu_stuff.c |  2 +-
 src/m_cond.c   |  3 --
 src/m_menu.c   | 30 ++++++----------
 src/p_inter.c  |  2 +-
 src/p_mobj.c   |  6 +---
 src/p_setup.c  |  6 ++--
 src/p_spec.c   |  2 +-
 src/y_inter.c  |  4 +--
 12 files changed, 97 insertions(+), 84 deletions(-)

diff --git a/src/d_main.c b/src/d_main.c
index 3566e7f3d2..b1f09aaa89 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -1473,6 +1473,12 @@ void D_SRB2Main(void)
 	//--------------------------------------------------------- CONFIG.CFG
 	M_FirstLoadConfig(); // WARNING : this do a "COM_BufExecute()"
 
+	if (M_CheckParm("-gamedata") && M_IsNextParm())
+	{
+		// Moved from G_LoadGameData itself, as it would cause some crazy
+		// confusion issues when loading mods.
+		strlcpy(gamedatafilename, M_GetNextParm(), sizeof gamedatafilename);
+	}
 	G_LoadGameData();
 
 #if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL)
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 4e90db0dcc..73bfe9e174 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -4613,11 +4613,11 @@ static void Fishcake_OnChange(void)
 static void Command_Isgamemodified_f(void)
 {
 	if (savemoddata)
-		CONS_Printf(M_GetText("modifiedgame is true, but you can save emblem and time data in this mod.\n"));
+		CONS_Printf(M_GetText("modifiedgame is true, but you can save time data in this mod.\n"));
 	else if (modifiedgame)
-		CONS_Printf(M_GetText("modifiedgame is true, extras will not be unlocked\n"));
+		CONS_Printf(M_GetText("modifiedgame is true, time data can't be saved\n"));
 	else
-		CONS_Printf(M_GetText("modifiedgame is false, you can unlock extras\n"));
+		CONS_Printf(M_GetText("modifiedgame is false, you can save time data\n"));
 }
 
 static void Command_Cheats_f(void)
diff --git a/src/f_finale.c b/src/f_finale.c
index bca8e3ba69..73f6281a1b 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -1575,7 +1575,9 @@ void F_GameEvaluationDrawer(void)
 	{
 		V_DrawString(8, 16, V_YELLOWMAP, "Unlocked:");
 
-		if (!(netgame) && (!modifiedgame || savemoddata))
+		if (netgame)
+			V_DrawString(8, 96, V_YELLOWMAP, "Multiplayer games\ncan't unlock\nextras!");
+		else
 		{
 			INT32 startcoord = 32;
 
@@ -1590,10 +1592,6 @@ void F_GameEvaluationDrawer(void)
 				}
 			}
 		}
-		else if (netgame)
-			V_DrawString(8, 96, V_YELLOWMAP, "Multiplayer games\ncan't unlock\nextras!");
-		else
-			V_DrawString(8, 96, V_YELLOWMAP, "Modified games\ncan't unlock\nextras!");
 	}
 #endif
 
@@ -1657,7 +1655,7 @@ void F_GameEvaluationTicker(void)
 			HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Multiplayer games can't unlock extras!");
 			S_StartSound(NULL, sfx_s3k68);
 		}
-		else if (!modifiedgame || savemoddata)
+		else
 		{
 			++timesBeaten;
 
@@ -1672,13 +1670,6 @@ void F_GameEvaluationTicker(void)
 
 			G_SaveGameData();
 		}
-		else
-		{
-			HU_SetCEchoFlags(V_YELLOWMAP|V_RETURN8);
-			HU_SetCEchoDuration(6);
-			HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Modified games can't unlock extras!");
-			S_StartSound(NULL, sfx_s3k68);
-		}
 	}
 }
 
diff --git a/src/g_game.c b/src/g_game.c
index 349d905586..edddcc0509 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -755,7 +755,7 @@ void G_SetGameModified(boolean silent)
 	savemoddata = false;
 
 	if (!silent)
-		CONS_Alert(CONS_NOTICE, M_GetText("Game must be restarted to record statistics.\n"));
+		CONS_Alert(CONS_NOTICE, M_GetText("Game must be restarted to play Record Attack.\n"));
 
 	// If in record attack recording, cancel it.
 	if (modeattacking)
@@ -3826,8 +3826,7 @@ static void G_UpdateVisited(void)
 {
 	boolean spec = G_IsSpecialStage(gamemap);
 	// Update visitation flags?
-	if ((!modifiedgame || savemoddata) // Not modified
-		&& !multiplayer && !demoplayback && (gametype == GT_COOP) // SP/RA/NiGHTS mode
+	if (!multiplayer && !demoplayback && (gametype == GT_COOP) // SP/RA/NiGHTS mode
 		&& !stagefailed) // Did not fail the stage
 	{
 		UINT8 earnedEmblems;
@@ -3895,14 +3894,18 @@ static void G_HandleSaveLevel(void)
 					remove(liveeventbackup);
 				cursaveslot = 0;
 			}
-			else if ((!modifiedgame || savemoddata) && !(netgame || multiplayer || ultimatemode || demorecording || metalrecording || modeattacking))
+			else if (!(netgame || multiplayer || ultimatemode || demorecording || metalrecording || modeattacking))
+			{
 				G_SaveGame((UINT32)cursaveslot, spstage_start);
+			}
 		}
 	}
 	// and doing THIS here means you don't lose your progress if you close the game mid-intermission
 	else if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking)
-		&& (!modifiedgame || savemoddata) && cursaveslot > 0 && CanSaveLevel(lastmap+1))
+		&& cursaveslot > 0 && CanSaveLevel(lastmap+1))
+	{
 		G_SaveGame((UINT32)cursaveslot, lastmap+1); // not nextmap+1 to route around special stages
+	}
 }
 
 //
@@ -4178,8 +4181,10 @@ static void G_DoContinued(void)
 	tokenlist = 0;
 	token = 0;
 
-	if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && (!modifiedgame || savemoddata) && cursaveslot > 0)
+	if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && cursaveslot > 0)
+	{
 		G_SaveGameOver((UINT32)cursaveslot, true);
+	}
 
 	// Reset # of lives
 	pl->lives = (ultimatemode) ? 1 : startinglivesbalance[numgameovers];
@@ -4244,13 +4249,17 @@ void G_LoadGameSettings(void)
 	S_InitRuntimeSounds();
 }
 
+#define GAMEDATA_ID 0x86E4A27C // Change every major version, as usual
+#define COMPAT_GAMEDATA_ID 0xFCAFE211 // Can be removed entirely for 2.3
+
 // G_LoadGameData
 // Loads the main data file, which stores information such as emblems found, etc.
 void G_LoadGameData(void)
 {
 	size_t length;
 	INT32 i, j;
-	UINT8 modded = false;
+
+	UINT32 versionID;
 	UINT8 rtemp;
 
 	//For records
@@ -4261,6 +4270,9 @@ void G_LoadGameData(void)
 	UINT8 recmares;
 	INT32 curmare;
 
+	// Stop saving, until we successfully load it again.
+	gamedataloaded = false;
+
 	// Clear things so previously read gamedata doesn't transfer
 	// to new gamedata
 	G_ClearRecords(); // main and nights records
@@ -4268,27 +4280,35 @@ void G_LoadGameData(void)
 	totalplaytime = 0; // total play time (separate from all)
 
 	if (M_CheckParm("-nodata"))
-		return; // Don't load.
-
-	// Allow saving of gamedata beyond this point
-	gamedataloaded = true;
-
-	if (M_CheckParm("-gamedata") && M_IsNextParm())
 	{
-		strlcpy(gamedatafilename, M_GetNextParm(), sizeof gamedatafilename);
+		// Don't load at all.
+		return;
 	}
 
 	if (M_CheckParm("-resetdata"))
-		return; // Don't load (essentially, reset).
+	{
+		// Don't load, but do save. (essentially, reset)
+		gamedataloaded = true;
+		return; 
+	}
 
 	length = FIL_ReadFile(va(pandf, srb2home, gamedatafilename), &savebuffer);
-	if (!length) // Aw, no game data. Their loss!
+	if (!length)
+	{
+		// No gamedata. We can save a new one.
+		gamedataloaded = true;
 		return;
+	}
 
 	save_p = savebuffer;
 
 	// Version check
-	if (READUINT32(save_p) != 0xFCAFE211)
+	versionID = READUINT32(save_p);
+	if (versionID != GAMEDATA_ID
+#ifdef COMPAT_GAMEDATA_ID // backwards compat behavior
+		&& versionID != COMPAT_GAMEDATA_ID
+#endif
+		)
 	{
 		const char *gdfolder = "the SRB2 folder";
 		if (strcmp(srb2home,"."))
@@ -4301,13 +4321,26 @@ void G_LoadGameData(void)
 
 	totalplaytime = READUINT32(save_p);
 
-	modded = READUINT8(save_p);
+#ifdef COMPAT_GAMEDATA_ID
+	// Ignore for backwards compat, it'll get fixed when saving.
+	if (versionID == COMPAT_GAMEDATA_ID)
+	{
+		// Old files use a UINT8 here.
+		READUINT8(save_p);
+	}
+	else
+#endif
+	{
+		// Quick & dirty hash for what mod this save file is for.
+		UINT32 modID = READUINT32(save_p);
+		UINT32 expectedID = quickncasehash(timeattackfolder, sizeof timeattackfolder);
 
-	// Aha! Someone's been screwing with the save file!
-	if ((modded && !savemoddata))
-		goto datacorrupt;
-	else if (modded != true && modded != false)
-		goto datacorrupt;
+		if (modID != expectedID)
+		{
+			// Aha! Someone's been screwing with the save file!
+			goto datacorrupt;
+		}
+	}
 
 	// TODO put another cipher on these things? meh, I don't care...
 	for (i = 0; i < NUMMAPS; i++)
@@ -4393,6 +4426,12 @@ void G_LoadGameData(void)
 	Z_Free(savebuffer);
 	save_p = NULL;
 
+	// Don't consider loaded until it's a success!
+	// It used to do this much earlier, but this would cause the gamedata to
+	// save over itself when it I_Errors from the corruption landing point below,
+	// which can accidentally delete players' legitimate data if the code ever has any tiny mistakes!
+	gamedataloaded = true;
+
 	// Silent update unlockables in case they're out of sync with conditions
 	M_SilentUpdateUnlockablesAndEmblems();
 
@@ -4432,20 +4471,12 @@ void G_SaveGameData(void)
 		return;
 	}
 
-	if (modifiedgame && !savemoddata)
-	{
-		free(savebuffer);
-		save_p = savebuffer = NULL;
-		return;
-	}
-
 	// Version test
-	WRITEUINT32(save_p, 0xFCAFE211);
+	WRITEUINT32(save_p, GAMEDATA_ID);
 
 	WRITEUINT32(save_p, totalplaytime);
 
-	btemp = (UINT8)(savemoddata || modifiedgame);
-	WRITEUINT8(save_p, btemp);
+	WRITEUINT32(save_p, quickncasehash(timeattackfolder, sizeof timeattackfolder));
 
 	// TODO put another cipher on these things? meh, I don't care...
 	for (i = 0; i < NUMMAPS; i++)
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index c037abcd79..ebef023194 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -2994,7 +2994,7 @@ static void HU_DrawCoopOverlay(void)
 		V_DrawSmallScaledPatch(148, 172, 0, tokenicon);
 	}
 
-	if (LUA_HudEnabled(hud_tabemblems) && (!modifiedgame || savemoddata))
+	if (LUA_HudEnabled(hud_tabemblems))
 	{
 		V_DrawString(160, 144, 0, va("- %d/%d", M_CountEmblems(), numemblems+numextraemblems));
 		V_DrawScaledPatch(128, 144 - emblemicon->height/4, 0, emblemicon);
diff --git a/src/m_cond.c b/src/m_cond.c
index 1406317c59..58ee71feca 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -200,9 +200,6 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
 	char cechoText[992] = "";
 	UINT8 cechoLines = 0;
 
-	if (modifiedgame && !savemoddata)
-		return false;
-
 	M_CheckUnlockConditions();
 
 	// Go through extra emblems
diff --git a/src/m_menu.c b/src/m_menu.c
index 83b788fd56..55733e552a 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -757,12 +757,12 @@ static menuitem_t SR_EmblemHintMenu[] =
 static menuitem_t SP_MainMenu[] =
 {
 	// Note: If changing the positions here, also change them in M_SinglePlayerMenu()
-	{IT_CALL | IT_STRING,                       NULL, "Start Game",    M_LoadGame,                 76},
-	{IT_SECRET,                                 NULL, "Record Attack", M_TimeAttack,               84},
-	{IT_SECRET,                                 NULL, "NiGHTS Mode",   M_NightsAttack,             92},
-	{IT_SECRET,                                 NULL, "Marathon Run",  M_Marathon,                100},
-	{IT_CALL | IT_STRING,                       NULL, "Tutorial",      M_StartTutorial,           108},
-	{IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics",    M_Statistics,              116}
+	{IT_CALL | IT_STRING,	NULL, "Start Game",    M_LoadGame,                 76},
+	{IT_SECRET,				NULL, "Record Attack", M_TimeAttack,               84},
+	{IT_SECRET,				NULL, "NiGHTS Mode",   M_NightsAttack,             92},
+	{IT_SECRET,				NULL, "Marathon Run",  M_Marathon,                100},
+	{IT_CALL | IT_STRING,	NULL, "Tutorial",      M_StartTutorial,           108},
+	{IT_CALL | IT_STRING,	NULL, "Statistics",    M_Statistics,              116}
 };
 
 enum
@@ -3514,9 +3514,11 @@ boolean M_Responder(event_t *ev)
 			{
 				if (((currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_CALL
 				 || (currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_SUBMENU)
-                 && (currentMenu->menuitems[itemOn].status & IT_CALLTYPE))
+				 && (currentMenu->menuitems[itemOn].status & IT_CALLTYPE))
 				{
 #ifndef DEVELOP
+					// TODO: Replays are scary, so I left the remaining instances of this alone.
+					// It'd be nice to get rid of this once and for all though!
 					if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && modifiedgame && !savemoddata)
 					{
 						S_StartSound(NULL, sfx_skid);
@@ -8701,12 +8703,6 @@ static void M_DrawLoad(void)
 		loadgameoffset = 0;
 
 	M_DrawLoadGameData();
-
-	if (modifiedgame && !savemoddata)
-	{
-		V_DrawCenteredThinString(BASEVIDWIDTH/2, 184, 0, "\x85WARNING: \x80The game is modified.");
-		V_DrawCenteredThinString(BASEVIDWIDTH/2, 192, 0, "Progress will not be saved.");
-	}
 }
 
 //
@@ -9008,18 +9004,12 @@ static void M_HandleLoadSave(INT32 choice)
 			break;
 
 		case KEY_ENTER:
-			if (ultimate_selectable && saveSlotSelected == NOSAVESLOT && !savemoddata && !modifiedgame)
+			if (ultimate_selectable && saveSlotSelected == NOSAVESLOT)
 			{
 				loadgamescroll = 0;
 				S_StartSound(NULL, sfx_skid);
 				M_StartMessage("Are you sure you want to play\n\x85ultimate mode\x80? It isn't remotely fair,\nand you don't even get an emblem for it.\n\n(Press 'Y' to confirm)\n",M_SaveGameUltimateResponse,MM_YESNO);
 			}
-			else if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected-1].lives == -42 && !(!modifiedgame || savemoddata))
-			{
-				loadgamescroll = 0;
-				S_StartSound(NULL, sfx_skid);
-				M_StartMessage(M_GetText("This cannot be done in a modified game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
-			}
 			else if (saveSlotSelected == NOSAVESLOT || savegameinfo[saveSlotSelected-1].lives != -666) // don't allow loading of "bad saves"
 			{
 				loadgamescroll = 0;
diff --git a/src/p_inter.c b/src/p_inter.c
index dd3e0f9c27..1094c30459 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2583,7 +2583,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 				if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && numgameovers < maxgameovers)
 				{
 					numgameovers++;
-					if ((!modifiedgame || savemoddata) && cursaveslot > 0)
+					if (cursaveslot > 0)
 						G_SaveGameOver((UINT32)cursaveslot, (target->player->continues <= 0));
 				}
 			}
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 4533a2ce88..ac91e0f7f0 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -11972,11 +11972,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
 		break;
 	case MT_EMBLEM:
 		if (netgame || multiplayer)
-			return false; // Single player
-
-		if (modifiedgame && !savemoddata)
-			return false; // No cheating!!
-
+			return false; // Single player (You're next on my shit list)
 		break;
 	default:
 		break;
diff --git a/src/p_setup.c b/src/p_setup.c
index deb308da22..c41a5d94e2 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -7746,7 +7746,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 	nextmapoverride = 0;
 	skipstats = 0;
 
-	if (!(netgame || multiplayer || demoplayback) && (!modifiedgame || savemoddata))
+	if (!(netgame || multiplayer || demoplayback))
 		mapvisited[gamemap-1] |= MV_VISITED;
 	else if (netgame || multiplayer)
 		mapvisited[gamemap-1] |= MV_MP; // you want to record that you've been there this session, but not permanently
@@ -7764,8 +7764,10 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 		{
 			// I'd love to do this in the menu code instead of here, but everything's a mess and I can't guarantee saving proper player struct info before the first act's started. You could probably refactor it, but it'd be a lot of effort. Easier to just work off known good code. ~toast 22/06/2020
 			if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || marathonmode)
-			&& (!modifiedgame || savemoddata) && cursaveslot > 0)
+				&& cursaveslot > 0)
+			{
 				G_SaveGame((UINT32)cursaveslot, gamemap);
+			}
 			// If you're looking for saving sp file progression (distinct from G_SaveGameOver), check G_DoCompleted.
 		}
 		lastmaploaded = gamemap; // HAS to be set after saving!!
diff --git a/src/p_spec.c b/src/p_spec.c
index 82337d2f6b..94659e0d70 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2937,7 +2937,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			break;
 
 		case 441: // Trigger unlockable
-			if ((!modifiedgame || savemoddata) && !(netgame || multiplayer))
+			if (!(netgame || multiplayer))
 			{
 				INT32 trigid = line->args[0];
 
diff --git a/src/y_inter.c b/src/y_inter.c
index 7faceff50e..e0f246eeea 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -1092,7 +1092,7 @@ void Y_Ticker(void)
 			S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching!
 
 			// Update when done with tally
-			if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && !demoplayback)
+			if (!(netgame || multiplayer) && !demoplayback)
 			{
 				if (M_UpdateUnlockablesAndExtraEmblems())
 					S_StartSound(NULL, sfx_s3k68);
@@ -1223,7 +1223,7 @@ void Y_Ticker(void)
 			S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching!
 
 			// Update when done with tally
-			if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && !demoplayback)
+			if (!(netgame || multiplayer) && !demoplayback)
 			{
 				if (M_UpdateUnlockablesAndExtraEmblems())
 					S_StartSound(NULL, sfx_s3k68);
-- 
GitLab


From bcfe0da8fc10ed8b3417a11e2dcdb26aeda9bd1f Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 27 Feb 2022 10:22:44 -0500
Subject: [PATCH 037/518] Use old modded behavior when loading old files,
 instead of ignoring

Let's not pretend script-kiddie edited old files are now perfectly A-OK :p
---
 src/g_game.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/src/g_game.c b/src/g_game.c
index edddcc0509..892802d910 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -4322,11 +4322,20 @@ void G_LoadGameData(void)
 	totalplaytime = READUINT32(save_p);
 
 #ifdef COMPAT_GAMEDATA_ID
-	// Ignore for backwards compat, it'll get fixed when saving.
 	if (versionID == COMPAT_GAMEDATA_ID)
 	{
-		// Old files use a UINT8 here.
-		READUINT8(save_p);
+		// We'll temporarily use the old condition when loading an older file.
+		// The proper mod-specific hash will get saved in afterwards.
+		boolean modded = READUINT8(save_p);
+
+		if (modded && !savemoddata)
+		{
+			goto datacorrupt;
+		}
+		else if (modded != true && modded != false)
+		{
+			goto datacorrupt;
+		}
 	}
 	else
 #endif
-- 
GitLab


From 4a520e63c67bfe8d9a8320553d1fd71908df3243 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 27 Feb 2022 10:33:58 -0500
Subject: [PATCH 038/518] Don't allow a gamedata named the same as the default
 time attack folder

That'd be kinda scary!
---
 src/deh_soc.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/deh_soc.c b/src/deh_soc.c
index 583776ee7d..db427b3492 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -2901,7 +2901,9 @@ static boolean GoodDataFileName(const char *s)
 	p = s + strlen(s) - strlen(tail);
 	if (p <= s) return false; // too short
 	if (!fasticmp(p, tail)) return false; // doesn't end in .dat
-	if (fasticmp(s, "gamedata.dat")) return false;
+
+	if (fasticmp(s, "gamedata.dat")) return false; // Don't overwrite default gamedata
+	if (fasticmp(s, "main.dat")) return false; // Don't overwrite default time attack replays
 
 	return true;
 }
-- 
GitLab


From a22fa1c455cea774946f7032fd74e2e8155c9d9d Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 28 Feb 2022 12:43:00 -0500
Subject: [PATCH 039/518] Compromise on cheats setting modified game

Instead of modifying the game, cheats now set a separate "cheats were used in this session" variable, which returns some of the old behavior.

HOWEVER, cheats will STILL allow spawning / collecting emblems & unlocking unlockables. Cheats will purely prevent saving progress. (It was always frustrating that devmode would make debugging unlockable features harder...)

Lastly, the function to set no-saving was exposed to Lua (`G_SetUsedCheats(silent)`). Just thought it'd be useful for large-scale gamedata-using mods that want to add their own cheat commands.
---
 src/d_clisrv.c    |  3 +++
 src/d_clisrv.h    |  1 +
 src/d_main.c      |  2 +-
 src/d_netcmd.c    | 20 ++++++++++----------
 src/doomstat.h    |  1 +
 src/g_game.c      | 31 ++++++++++++++++++++++++++++---
 src/g_game.h      |  1 +
 src/lua_baselib.c | 11 +++++++++++
 src/lua_script.c  |  3 +++
 src/m_cheat.c     | 20 ++++++++++----------
 src/m_menu.c      | 20 ++++++++++++++++----
 src/p_inter.c     |  2 +-
 src/p_setup.c     |  2 +-
 13 files changed, 87 insertions(+), 30 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 4cd6333c5e..f95b952f39 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1448,6 +1448,7 @@ static boolean SV_SendServerConfig(INT32 node)
 	netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
 	netbuffer->u.servercfg.gametype = (UINT8)gametype;
 	netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
+	netbuffer->u.servercfg.usedCheats = (UINT8)usedCheats;
 
 	memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
 
@@ -4341,6 +4342,8 @@ static void HandlePacketFromAwayNode(SINT8 node)
 				maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
 				G_SetGametype(netbuffer->u.servercfg.gametype);
 				modifiedgame = netbuffer->u.servercfg.modifiedgame;
+				if (netbuffer->u.servercfg.usedCheats)
+					G_SetUsedCheats(true);
 				memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
 			}
 
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index e078641224..f3896c7ea2 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -158,6 +158,7 @@ typedef struct
 
 	UINT8 gametype;
 	UINT8 modifiedgame;
+	UINT8 usedCheats;
 
 	char server_context[8]; // Unique context id, generated at server startup.
 } ATTRPACK serverconfig_pak;
diff --git a/src/d_main.c b/src/d_main.c
index b1f09aaa89..9df1d8fab9 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -1506,7 +1506,7 @@ void D_SRB2Main(void)
 		else
 		{
 			if (!M_CheckParm("-server"))
-				G_SetGameModified(true);
+				G_SetUsedCheats(true);
 			autostart = true;
 		}
 	}
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 73bfe9e174..847f973419 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1877,7 +1877,7 @@ static void Command_Map_f(void)
 	const char *gametypename;
 	boolean newresetplayers;
 
-	boolean mustmodifygame;
+	boolean wouldSetCheats;
 
 	INT32 newmapnum;
 
@@ -1898,11 +1898,11 @@ static void Command_Map_f(void)
 	option_gametype =   COM_CheckPartialParm("-g");
 	newresetplayers = ! COM_CheckParm("-noresetplayers");
 
-	mustmodifygame =
-		!( netgame     || multiplayer ) &&
-		(!modifiedgame || savemoddata );
+	wouldSetCheats =
+		!( netgame || multiplayer ) &&
+		!( usedCheats );
 
-	if (mustmodifygame && !option_force)
+	if (wouldSetCheats && !option_force)
 	{
 		/* May want to be more descriptive? */
 		CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
@@ -1956,9 +1956,9 @@ static void Command_Map_f(void)
 		return;
 	}
 
-	if (mustmodifygame && option_force)
+	if (wouldSetCheats && option_force)
 	{
-		G_SetGameModified(false);
+		G_SetUsedCheats(false);
 	}
 
 	// new gametype value
@@ -4259,7 +4259,7 @@ static void Ringslinger_OnChange(void)
 	}
 
 	if (cv_ringslinger.value) // Only if it's been turned on
-		G_SetGameModified(multiplayer);
+		G_SetUsedCheats(false);
 }
 
 static void Gravity_OnChange(void)
@@ -4280,7 +4280,7 @@ static void Gravity_OnChange(void)
 #endif
 
 	if (!CV_IsSetToDefault(&cv_gravity))
-		G_SetGameModified(multiplayer);
+		G_SetUsedCheats(false);
 	gravity = cv_gravity.value;
 }
 
@@ -4596,7 +4596,7 @@ static void Fishcake_OnChange(void)
 	// so don't make modifiedgame always on!
 	if (cv_debug)
 	{
-		G_SetGameModified(multiplayer);
+		G_SetUsedCheats(false);
 	}
 
 	else if (cv_debug != cv_fishcake.value)
diff --git a/src/doomstat.h b/src/doomstat.h
index bce43416b8..490054cf97 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -75,6 +75,7 @@ extern SINT8 startinglivesbalance[maxgameovers+1];
 extern boolean modifiedgame;
 extern UINT16 mainwads;
 extern boolean savemoddata; // This mod saves time/emblem data.
+extern boolean usedCheats;
 extern boolean disableSpeedAdjust; // Don't alter the duration of player states if true
 extern boolean imcontinuing; // Temporary flag while continuing
 extern boolean metalrecording;
diff --git a/src/g_game.c b/src/g_game.c
index 892802d910..e75bb433ea 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -96,6 +96,7 @@ SINT8 startinglivesbalance[maxgameovers+1] = {3, 5, 7, 9, 12, 15, 20, 25, 30, 40
 UINT16 mainwads = 0;
 boolean modifiedgame; // Set if homebrew PWAD stuff has been added.
 boolean savemoddata = false;
+boolean usedCheats = false; // Set when a gamedata-preventing cheat command is used.
 UINT8 paused;
 UINT8 modeattacking = ATTACKING_NONE;
 boolean disableSpeedAdjust = false;
@@ -764,6 +765,23 @@ void G_SetGameModified(boolean silent)
 		Command_ExitGame_f();
 }
 
+void G_SetUsedCheats(boolean silent)
+{
+	if (usedCheats)
+		return;
+
+	usedCheats = true;
+
+	if (!silent)
+		CONS_Alert(CONS_NOTICE, M_GetText("Game must be restarted to save progress.\n"));
+
+	// If in record attack recording, cancel it.
+	if (modeattacking)
+		M_EndModeAttackRun();
+	else if (marathonmode)
+		Command_ExitGame_f();
+}
+
 /** Builds an original game map name from a map number.
   * The complexity is due to MAPA0-MAPZZ.
   *
@@ -3894,7 +3912,7 @@ static void G_HandleSaveLevel(void)
 					remove(liveeventbackup);
 				cursaveslot = 0;
 			}
-			else if (!(netgame || multiplayer || ultimatemode || demorecording || metalrecording || modeattacking))
+			else if (!usedCheats && !(netgame || multiplayer || ultimatemode || demorecording || metalrecording || modeattacking))
 			{
 				G_SaveGame((UINT32)cursaveslot, spstage_start);
 			}
@@ -3902,7 +3920,7 @@ static void G_HandleSaveLevel(void)
 	}
 	// and doing THIS here means you don't lose your progress if you close the game mid-intermission
 	else if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking)
-		&& cursaveslot > 0 && CanSaveLevel(lastmap+1))
+		&& !usedCheats && cursaveslot > 0 && CanSaveLevel(lastmap+1))
 	{
 		G_SaveGame((UINT32)cursaveslot, lastmap+1); // not nextmap+1 to route around special stages
 	}
@@ -4181,7 +4199,7 @@ static void G_DoContinued(void)
 	tokenlist = 0;
 	token = 0;
 
-	if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && cursaveslot > 0)
+	if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && !usedCheats && cursaveslot > 0)
 	{
 		G_SaveGameOver((UINT32)cursaveslot, true);
 	}
@@ -4480,6 +4498,13 @@ void G_SaveGameData(void)
 		return;
 	}
 
+	if (usedCheats)
+	{
+		free(savebuffer);
+		save_p = savebuffer = NULL;
+		return;
+	}
+
 	// Version test
 	WRITEUINT32(save_p, GAMEDATA_ID);
 
diff --git a/src/g_game.h b/src/g_game.h
index dca043f2e0..a781e23f99 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -243,6 +243,7 @@ void G_LoadGameData(void);
 void G_LoadGameSettings(void);
 
 void G_SetGameModified(boolean silent);
+void G_SetUsedCheats(boolean silent);
 
 void G_SetGamestate(gamestate_t newstate);
 
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 29adb478ab..031a155d23 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -3612,6 +3612,16 @@ static int lib_gRemovePlayer(lua_State *L)
 }
 
 
+static int lib_gSetUsedCheats(lua_State *L)
+{
+	// Let large-scale level packs using Lua be able to add cheat commands.
+	boolean silent = lua_optboolean(L, 1);
+	//NOHUD
+	//INLEVEL
+	G_SetUsedCheats(silent);
+	return 0;
+}
+
 static int Lcheckmapnumber (lua_State *L, int idx, const char *fun)
 {
 	if (ISINLEVEL)
@@ -4213,6 +4223,7 @@ static luaL_Reg lib[] = {
 	{"G_AddGametype", lib_gAddGametype},
 	{"G_AddPlayer", lib_gAddPlayer},
 	{"G_RemovePlayer", lib_gRemovePlayer},
+	{"G_SetUsedCheats", lib_gSetUsedCheats},
 	{"G_BuildMapName",lib_gBuildMapName},
 	{"G_BuildMapTitle",lib_gBuildMapTitle},
 	{"G_FindMap",lib_gFindMap},
diff --git a/src/lua_script.c b/src/lua_script.c
index 4d40715450..5f16bca8a7 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -204,6 +204,9 @@ int LUA_PushGlobals(lua_State *L, const char *word)
 	} else if (fastcmp(word,"modifiedgame")) {
 		lua_pushboolean(L, modifiedgame && !savemoddata);
 		return 1;
+	} else if (fastcmp(word,"usedCheats")) {
+		lua_pushboolean(L, usedCheats);
+		return 1;
 	} else if (fastcmp(word,"menuactive")) {
 		lua_pushboolean(L, menuactive);
 		return 1;
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 89c8009aed..78fb3a5057 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -81,7 +81,7 @@ static UINT8 cheatf_warp(void)
 	S_StartSound(0, sfx_itemup);
 
 	// Temporarily unlock stuff.
-	G_SetGameModified(false);
+	G_SetUsedCheats(false);
 	unlockables[31].unlocked = true; // credits
 	unlockables[30].unlocked = true; // sound test
 	unlockables[28].unlocked = true; // level select
@@ -106,7 +106,7 @@ static UINT8 cheatf_devmode(void)
 	S_StartSound(0, sfx_itemup);
 
 	// Just unlock all the things and turn on -debug and console devmode.
-	G_SetGameModified(false);
+	G_SetUsedCheats(false);
 	for (i = 0; i < MAXUNLOCKABLES; i++)
 		unlockables[i].unlocked = true;
 	devparm = true;
@@ -275,7 +275,7 @@ void Command_CheatNoClip_f(void)
 	plyr->pflags ^= PF_NOCLIP;
 	CONS_Printf(M_GetText("No Clipping %s\n"), plyr->pflags & PF_NOCLIP ? M_GetText("On") : M_GetText("Off"));
 
-	G_SetGameModified(multiplayer);
+	G_SetUsedCheats(false);
 }
 
 void Command_CheatGod_f(void)
@@ -290,7 +290,7 @@ void Command_CheatGod_f(void)
 	plyr->pflags ^= PF_GODMODE;
 	CONS_Printf(M_GetText("Cheese Mode %s\n"), plyr->pflags & PF_GODMODE ? M_GetText("On") : M_GetText("Off"));
 
-	G_SetGameModified(multiplayer);
+	G_SetUsedCheats(false);
 }
 
 void Command_CheatNoTarget_f(void)
@@ -305,7 +305,7 @@ void Command_CheatNoTarget_f(void)
 	plyr->pflags ^= PF_INVIS;
 	CONS_Printf(M_GetText("SEP Field %s\n"), plyr->pflags & PF_INVIS ? M_GetText("On") : M_GetText("Off"));
 
-	G_SetGameModified(multiplayer);
+	G_SetUsedCheats(false);
 }
 
 void Command_Scale_f(void)
@@ -879,7 +879,7 @@ void Command_Devmode_f(void)
 		return;
 	}
 
-	G_SetGameModified(multiplayer);
+	G_SetUsedCheats(false);
 }
 
 void Command_Setrings_f(void)
@@ -905,7 +905,7 @@ void Command_Setrings_f(void)
 			// no totalsphere addition to revert
 		}
 
-		G_SetGameModified(multiplayer);
+		G_SetUsedCheats(false);
 	}
 }
 
@@ -928,7 +928,7 @@ void Command_Setlives_f(void)
 			P_GivePlayerLives(&players[consoleplayer], atoi(COM_Argv(1)));
 		}
 
-		G_SetGameModified(multiplayer);
+		G_SetUsedCheats(false);
 	}
 }
 
@@ -955,7 +955,7 @@ void Command_Setcontinues_f(void)
 
 		players[consoleplayer].continues = numcontinues;
 
-		G_SetGameModified(multiplayer);
+		G_SetUsedCheats(false);
 	}
 }
 
@@ -1446,7 +1446,7 @@ void Command_ObjectPlace_f(void)
 	REQUIRE_SINGLEPLAYER;
 	REQUIRE_NOULTIMATE;
 
-	G_SetGameModified(multiplayer);
+	G_SetUsedCheats(false);
 
 	silent = COM_CheckParm("-silent");
 
diff --git a/src/m_menu.c b/src/m_menu.c
index 55733e552a..81567662ad 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -7082,7 +7082,7 @@ static void M_AllowSuper(INT32 choice)
 	M_StartMessage(M_GetText("You are now capable of turning super.\nRemember to get all the emeralds!\n"),NULL,MM_NOTHING);
 	SR_PandorasBox[6].status = IT_GRAYEDOUT;
 
-	G_SetGameModified(multiplayer);
+	G_SetUsedCheats(false);
 }
 
 static void M_GetAllEmeralds(INT32 choice)
@@ -7093,7 +7093,7 @@ static void M_GetAllEmeralds(INT32 choice)
 	M_StartMessage(M_GetText("You now have all 7 emeralds.\nUse them wisely.\nWith great power comes great ring drain.\n"),NULL,MM_NOTHING);
 	SR_PandorasBox[7].status = IT_GRAYEDOUT;
 
-	G_SetGameModified(multiplayer);
+	G_SetUsedCheats(false);
 }
 
 static void M_DestroyRobotsResponse(INT32 ch)
@@ -7104,7 +7104,7 @@ static void M_DestroyRobotsResponse(INT32 ch)
 	// Destroy all robots
 	P_DestroyRobots();
 
-	G_SetGameModified(multiplayer);
+	G_SetUsedCheats(false);
 }
 
 static void M_DestroyRobots(INT32 choice)
@@ -8703,6 +8703,12 @@ static void M_DrawLoad(void)
 		loadgameoffset = 0;
 
 	M_DrawLoadGameData();
+
+	if (usedCheats)
+	{
+		V_DrawCenteredThinString(BASEVIDWIDTH/2, 184, 0, "\x85WARNING:\x80 Cheats have been activated.");
+		V_DrawCenteredThinString(BASEVIDWIDTH/2, 192, 0, "Progress will not be saved.");
+	}
 }
 
 //
@@ -9004,12 +9010,18 @@ static void M_HandleLoadSave(INT32 choice)
 			break;
 
 		case KEY_ENTER:
-			if (ultimate_selectable && saveSlotSelected == NOSAVESLOT)
+			if (ultimate_selectable && saveSlotSelected == NOSAVESLOT && !usedCheats)
 			{
 				loadgamescroll = 0;
 				S_StartSound(NULL, sfx_skid);
 				M_StartMessage("Are you sure you want to play\n\x85ultimate mode\x80? It isn't remotely fair,\nand you don't even get an emblem for it.\n\n(Press 'Y' to confirm)\n",M_SaveGameUltimateResponse,MM_YESNO);
 			}
+			else if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected-1].lives == -42 && usedCheats)
+			{
+				loadgamescroll = 0;
+				S_StartSound(NULL, sfx_skid);
+				M_StartMessage(M_GetText("This cannot be done in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
+			}
 			else if (saveSlotSelected == NOSAVESLOT || savegameinfo[saveSlotSelected-1].lives != -666) // don't allow loading of "bad saves"
 			{
 				loadgamescroll = 0;
diff --git a/src/p_inter.c b/src/p_inter.c
index 1094c30459..b86bb39a68 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2583,7 +2583,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 				if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && numgameovers < maxgameovers)
 				{
 					numgameovers++;
-					if (cursaveslot > 0)
+					if (!usedCheats && cursaveslot > 0)
 						G_SaveGameOver((UINT32)cursaveslot, (target->player->continues <= 0));
 				}
 			}
diff --git a/src/p_setup.c b/src/p_setup.c
index c41a5d94e2..a965c5142c 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -7764,7 +7764,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 		{
 			// I'd love to do this in the menu code instead of here, but everything's a mess and I can't guarantee saving proper player struct info before the first act's started. You could probably refactor it, but it'd be a lot of effort. Easier to just work off known good code. ~toast 22/06/2020
 			if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || marathonmode)
-				&& cursaveslot > 0)
+				&& !usedCheats && cursaveslot > 0)
 			{
 				G_SaveGame((UINT32)cursaveslot, gamemap);
 			}
-- 
GitLab


From f082acbbdbc6dcb535804c4c050f0a95ea3fb773 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 28 Feb 2022 12:48:18 -0500
Subject: [PATCH 040/518] Don't allow Record Attack in cheated games

---
 src/m_menu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 81567662ad..6d1dec19ec 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3519,7 +3519,7 @@ boolean M_Responder(event_t *ev)
 #ifndef DEVELOP
 					// TODO: Replays are scary, so I left the remaining instances of this alone.
 					// It'd be nice to get rid of this once and for all though!
-					if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && modifiedgame && !savemoddata)
+					if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && (modifiedgame && !savemoddata) && !usedCheats)
 					{
 						S_StartSound(NULL, sfx_skid);
 						M_StartMessage(M_GetText("This cannot be done in a modified game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
-- 
GitLab


From 947dbda045929e0577a52a898bd40e71a6066ac1 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Thu, 3 Mar 2022 10:48:44 -0500
Subject: [PATCH 041/518] Use savemoddata for ultimate file check

---
 src/m_menu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 6d1dec19ec..c5c49a7b8f 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -9010,7 +9010,7 @@ static void M_HandleLoadSave(INT32 choice)
 			break;
 
 		case KEY_ENTER:
-			if (ultimate_selectable && saveSlotSelected == NOSAVESLOT && !usedCheats)
+			if (ultimate_selectable && saveSlotSelected == NOSAVESLOT && !savemoddata)
 			{
 				loadgamescroll = 0;
 				S_StartSound(NULL, sfx_skid);
-- 
GitLab


From d3ff5342dd4cec3583523a8d53e6d1d6f7b7b591 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 1 May 2022 19:34:18 -0400
Subject: [PATCH 042/518] Minor adjustments

---
 src/m_cheat.c | 6 ------
 src/m_menu.c  | 2 +-
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/src/m_cheat.c b/src/m_cheat.c
index 78fb3a5057..18a5a86099 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -72,9 +72,6 @@ static UINT8 cheatf_ultimate(void)
 
 static UINT8 cheatf_warp(void)
 {
-	if (modifiedgame)
-		return 0;
-
 	if (menuactive && currentMenu != &MainDef)
 		return 0; // Only on the main menu!
 
@@ -97,9 +94,6 @@ static UINT8 cheatf_devmode(void)
 {
 	UINT8 i;
 
-	if (modifiedgame)
-		return 0;
-
 	if (menuactive && currentMenu != &MainDef)
 		return 0; // Only on the main menu!
 
diff --git a/src/m_menu.c b/src/m_menu.c
index c5c49a7b8f..d26712f1e2 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -6711,7 +6711,7 @@ static void M_DrawAddons(void)
 
 	// draw save icon
 	x = BASEVIDWIDTH - x - 16;
-	V_DrawSmallScaledPatch(x, y + 4, ((!modifiedgame || savemoddata) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]);
+	V_DrawSmallScaledPatch(x, y + 4, (!usedCheats ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]);
 
 	if (modifiedgame)
 		V_DrawSmallScaledPatch(x, y + 4, 0, addonsp[NUM_EXT+2]);
-- 
GitLab


From 5103253e0b3828db68e1d58a6d3d0749d90ba8e2 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Wed, 12 Oct 2022 00:57:15 -0400
Subject: [PATCH 043/518] Allow unlockable executors again

---
 src/p_spec.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index 94659e0d70..810a7cd3f2 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1797,7 +1797,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			{ // Unlockable triggers required
 				INT32 trigid = triggerline->args[1];
 
-				if ((modifiedgame && !savemoddata) || (netgame || multiplayer))
+				if (netgame || multiplayer)
 					return false;
 				else if (trigid < 0 || trigid > 31) // limited by 32 bit variable
 				{
@@ -1812,7 +1812,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			{ // An unlockable itself must be unlocked!
 				INT32 unlockid = triggerline->args[1];
 
-				if ((modifiedgame && !savemoddata) || (netgame || multiplayer))
+				if (netgame || multiplayer)
 					return false;
 				else if (unlockid < 0 || unlockid >= MAXUNLOCKABLES) // limited by unlockable count
 				{
-- 
GitLab


From 969dc4813a4ebd41e1710e941bbd36f002c1796f Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Sat, 19 Nov 2022 19:57:08 +0100
Subject: [PATCH 044/518] Allow cosmetic add-ons mid-save

---
 src/p_setup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index deb308da22..d5e6100b72 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -8074,7 +8074,7 @@ static boolean P_LoadAddon(UINT16 wadnum, UINT16 numlumps)
 		ST_Start();
 
 	// Prevent savefile cheating
-	if (cursaveslot > 0)
+	if (modifiedgame && (cursaveslot > 0))
 		cursaveslot = 0;
 
 	if (replacedcurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer))
-- 
GitLab


From 256d9b5fdb3411a79278e3ba2ec5000bc889770f Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Sun, 20 Nov 2022 11:50:01 +0100
Subject: [PATCH 045/518] Uncap console opening/closing animation

---
 src/console.c | 34 ++++++++++++++++++++--------------
 1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/src/console.c b/src/console.c
index 40fb43121f..9af9eb9a84 100644
--- a/src/console.c
+++ b/src/console.c
@@ -643,33 +643,39 @@ static void CON_ChangeHeight(void)
 //
 static void CON_MoveConsole(void)
 {
-	fixed_t conspeed;
+	static fixed_t fracmovement = 0;
 
 	Lock_state();
 
-	conspeed = FixedDiv(cons_speed.value*vid.fdupy, FRACUNIT);
-
 	// instant
 	if (!cons_speed.value)
 	{
 		con_curlines = con_destlines;
+		Unlock_state();
 		return;
 	}
 
-	// up/down move to dest
-	if (con_curlines < con_destlines)
+	// Not instant - Increment fracmovement fractionally
+	fracmovement += FixedMul(cons_speed.value*vid.fdupy, renderdeltatics);
+
+	if (con_curlines < con_destlines) // Move the console downwards
 	{
-		con_curlines += FixedInt(conspeed);
-		if (con_curlines > con_destlines)
-			con_curlines = con_destlines;
+		con_curlines += FixedInt(fracmovement); // Move by fracmovement's integer value
+		if (con_curlines > con_destlines) // If we surpassed the destination...
+			con_curlines = con_destlines; // ...clamp to it!
 	}
-	else if (con_curlines > con_destlines)
+	else // Move the console upwards
 	{
-		con_curlines -= FixedInt(conspeed);
+		con_curlines -= FixedInt(fracmovement);
 		if (con_curlines < con_destlines)
 			con_curlines = con_destlines;
+
+		if (con_destlines == 0) // If the console is being closed, not just moved up...
+			con_tick = 0; // ...don't show the blinking cursor
 	}
 
+	fracmovement %= FRACUNIT; // Reset fracmovement's integer value, but keep the fraction
+
 	Unlock_state();
 }
 
@@ -752,10 +758,6 @@ void CON_Ticker(void)
 			CON_ChangeHeight();
 	}
 
-	// console movement
-	if (con_destlines != con_curlines)
-		CON_MoveConsole();
-
 	// clip the view, so that the part under the console is not drawn
 	con_clipviewtop = -1;
 	if (cons_backpic.value) // clip only when using an opaque background
@@ -1866,6 +1868,10 @@ void CON_Drawer(void)
 			CON_ClearHUD();
 	}
 
+	// console movement
+	if (con_curlines != con_destlines)
+		CON_MoveConsole();
+
 	if (con_curlines > 0)
 		CON_DrawConsole();
 	else if (gamestate == GS_LEVEL
-- 
GitLab


From 435e1f6e7e1fab7e47dfc2df2ab60660583fe53d Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Sun, 20 Nov 2022 11:50:57 +0100
Subject: [PATCH 046/518] Make con_height adjustable on the fly

---
 src/console.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/src/console.c b/src/console.c
index 9af9eb9a84..fb023c67eb 100644
--- a/src/console.c
+++ b/src/console.c
@@ -110,6 +110,7 @@ static void CON_RecalcSize(void);
 static void CON_ChangeHeight(void);
 
 static void CON_DrawBackpic(void);
+static void CONS_height_Change(void);
 static void CONS_hudlines_Change(void);
 static void CONS_backcolor_Change(void);
 
@@ -136,7 +137,7 @@ static CV_PossibleValue_t speed_cons_t[] = {{0, "MIN"}, {64, "MAX"}, {0, NULL}};
 static consvar_t cons_speed = CVAR_INIT ("con_speed", "8", CV_SAVE, speed_cons_t, NULL);
 
 // percentage of screen height to use for console
-static consvar_t cons_height = CVAR_INIT ("con_height", "50", CV_SAVE, CV_Unsigned, NULL);
+static consvar_t cons_height = CVAR_INIT ("con_height", "50", CV_CALL|CV_SAVE, CV_Unsigned, CONS_height_Change);
 
 static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"}, {0, NULL}};
 // whether to use console background picture, or translucent mode
@@ -156,6 +157,18 @@ consvar_t cons_backcolor = CVAR_INIT ("con_backcolor", "Green", CV_CALL|CV_SAVE,
 
 static void CON_Print(char *msg);
 
+// Change the console height on demand
+//
+static void CONS_height_Change(void)
+{
+	Lock_state();
+
+	if (con_destlines > 0 && !con_startup) // If the console is open (as in, not using "bind")...
+		CON_ChangeHeight(); // ...update its height now, not only when it's closed and re-opened
+
+	Unlock_state();
+}
+
 //
 //
 static void CONS_hudlines_Change(void)
-- 
GitLab


From 396db189e7cf541f325d653ef5be7fa5ed1b2037 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Sun, 20 Nov 2022 11:51:40 +0100
Subject: [PATCH 047/518] Draw the input prompt while the console is moving

---
 src/console.c | 48 ++++++++++++++++++++++++------------------------
 1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/src/console.c b/src/console.c
index fb023c67eb..6f85dbf1ce 100644
--- a/src/console.c
+++ b/src/console.c
@@ -61,7 +61,7 @@ static boolean con_started = false; // console has been initialised
 static boolean con_forcepic = true; // at startup toggle console translucency when first off
        boolean con_recalc;          // set true when screen size has changed
 
-static tic_t con_tick; // console ticker for anim or blinking prompt cursor
+static tic_t con_tick; // console ticker for blinking prompt cursor
                         // con_scrollup should use time (currenttime - lasttime)..
 
 static boolean consoletoggle; // true when console key pushed, ticker will handle
@@ -1824,41 +1824,41 @@ static void CON_DrawConsole(void)
 	}
 
 	// draw console text lines from top to bottom
-	if (con_curlines < minheight)
-		return;
-
-	i = con_cy - con_scrollup;
+	if (con_curlines >= minheight)
+	{
+		i = con_cy - con_scrollup;
 
-	// skip the last empty line due to the cursor being at the start of a new line
-	i--;
+		// skip the last empty line due to the cursor being at the start of a new line
+		i--;
 
-	i -= (con_curlines - minheight) / charheight;
+		i -= (con_curlines - minheight) / charheight;
 
-	if (rendermode == render_none) return;
+		if (rendermode == render_none) return;
 
-	for (y = (con_curlines-minheight) % charheight; y <= con_curlines-minheight; y += charheight, i++)
-	{
-		INT32 x;
-		size_t c;
+		for (y = (con_curlines-minheight) % charheight; y <= con_curlines-minheight; y += charheight, i++)
+		{
+			INT32 x;
+			size_t c;
 
-		p = (UINT8 *)&con_buffer[((i > 0 ? i : 0)%con_totallines)*con_width];
+			p = (UINT8 *)&con_buffer[((i > 0 ? i : 0)%con_totallines)*con_width];
 
-		for (c = 0, x = charwidth; c < con_width; c++, x += charwidth, p++)
-		{
-			while (*p & 0x80)
+			for (c = 0, x = charwidth; c < con_width; c++, x += charwidth, p++)
 			{
-				charflags = (*p & 0x7f) << V_CHARCOLORSHIFT;
-				p++;
-				c++;
+				while (*p & 0x80)
+				{
+					charflags = (*p & 0x7f) << V_CHARCOLORSHIFT;
+					p++;
+					c++;
+				}
+				if (c >= con_width)
+					break;
+				V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true);
 			}
-			if (c >= con_width)
-				break;
-			V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true);
 		}
 	}
 
 	// draw prompt if enough place (not while game startup)
-	if ((con_curlines == con_destlines) && (con_curlines >= minheight) && !con_startup)
+	if ((con_curlines >= (minheight-charheight)) && !con_startup)
 		CON_DrawInput();
 }
 
-- 
GitLab


From 3a2834e781f42137bd17376e3e78da3d479ffbf9 Mon Sep 17 00:00:00 2001
From: ashifolfi <anticream39@yahoo.com>
Date: Sun, 20 Nov 2022 15:25:53 -0500
Subject: [PATCH 048/518] add R_TextureNameForNum and expose to lua

---
 src/lua_baselib.c | 18 ++++++++++++++++++
 src/r_textures.c  | 20 ++++++++++++++++++++
 src/r_textures.h  |  4 ++++
 3 files changed, 42 insertions(+)

diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 29adb478ab..9cfde9706f 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -2832,6 +2832,22 @@ static int lib_rTextureNumForName(lua_State *L)
 	return 1;
 }
 
+static int lib_rCheckTextureNameForNum(lua_State *L)
+{
+	INT32 num = luaL_checkstring(L, 1);
+	//HUDSAFE
+	lua_pushstring(L, R_CheckTextureNameForNum(num));
+	return 1;
+}
+
+static int lib_rTextureNameForNum(lua_State *L)
+{
+	INT32 num = luaL_checkstring(L, 1);
+	//HUDSAFE
+	lua_pushstring(L, R_TextureNameForNum(num));
+	return 1;
+}
+
 // R_DRAW
 ////////////
 static int lib_rGetColorByName(lua_State *L)
@@ -4174,6 +4190,8 @@ static luaL_Reg lib[] = {
 	// r_data
 	{"R_CheckTextureNumForName",lib_rCheckTextureNumForName},
 	{"R_TextureNumForName",lib_rTextureNumForName},
+	{"R_CheckTextureNameForNum", lib_rCheckTextureNameForNum},
+	{"R_TextureNameForNum", lib_rTextureNameForNum},
 
 	// r_draw
 	{"R_GetColorByName", lib_rGetColorByName},
diff --git a/src/r_textures.c b/src/r_textures.c
index 98c2788a24..2a9fc9953a 100644
--- a/src/r_textures.c
+++ b/src/r_textures.c
@@ -1662,6 +1662,26 @@ INT32 R_CheckTextureNumForName(const char *name)
 	return -1;
 }
 
+// i was fully expecting this to take like an hour to figure out not 2 mintues and 5 lines of c - ashi
+const char *R_CheckTextureNameForNum(INT32 num)
+{
+	if (textures[num] && textures[num]->name)
+	{
+		return textures[num]->name;
+	}
+	return "-";
+}
+
+const char *R_TextureNameForNum(INT32 num)
+{
+	const char *result = R_CheckTextureNameForNum(num);
+
+	if (strcmp(result, "-"))
+		return "REDWALL";
+
+	return result;
+}
+
 //
 // R_TextureNumForName
 //
diff --git a/src/r_textures.h b/src/r_textures.h
index b9b48da8c3..64be59ee39 100644
--- a/src/r_textures.h
+++ b/src/r_textures.h
@@ -104,6 +104,10 @@ INT32 R_TextureNumForName(const char *name);
 INT32 R_CheckTextureNumForName(const char *name);
 lumpnum_t R_GetFlatNumForName(const char *name);
 
+// Returns the texture name for the texture number (in case you ever needed it)
+const char *R_TextureNumForName(INT32 num);
+const char *R_CheckTextureNumForName(INT32 num);
+
 extern INT32 numtextures;
 
 #endif
-- 
GitLab


From e48f7d15381753e6ac49ea8c8607cc1a957617f0 Mon Sep 17 00:00:00 2001
From: ashifolfi <anticream39@yahoo.com>
Date: Sun, 20 Nov 2022 16:06:43 -0500
Subject: [PATCH 049/518] actually use correct names in the header

---
 src/r_textures.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/r_textures.h b/src/r_textures.h
index 64be59ee39..4fb65f7e5f 100644
--- a/src/r_textures.h
+++ b/src/r_textures.h
@@ -105,8 +105,8 @@ INT32 R_CheckTextureNumForName(const char *name);
 lumpnum_t R_GetFlatNumForName(const char *name);
 
 // Returns the texture name for the texture number (in case you ever needed it)
-const char *R_TextureNumForName(INT32 num);
-const char *R_CheckTextureNumForName(INT32 num);
+const char *R_CheckTextureNameForNum(INT32 num);
+const char *R_TextureNameForNum(INT32 num);
 
 extern INT32 numtextures;
 
-- 
GitLab


From 1b14dff0e9cb7cea73866a4a6abf51927d507606 Mon Sep 17 00:00:00 2001
From: ashifolfi <anticream39@yahoo.com>
Date: Sun, 20 Nov 2022 16:06:47 -0500
Subject: [PATCH 050/518] add comments + better checktexturename if statement

---
 src/r_textures.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/r_textures.c b/src/r_textures.c
index 2a9fc9953a..34b2f0637d 100644
--- a/src/r_textures.c
+++ b/src/r_textures.c
@@ -1662,21 +1662,30 @@ INT32 R_CheckTextureNumForName(const char *name)
 	return -1;
 }
 
-// i was fully expecting this to take like an hour to figure out not 2 mintues and 5 lines of c - ashi
+//
+// R_CheckTextureNameForNum
+//
+// because sidedefs use numbers and sometimes you want names
+// returns no texture marker if no texture was found
+//
 const char *R_CheckTextureNameForNum(INT32 num)
 {
-	if (textures[num] && textures[num]->name)
-	{
+	if (num > 0 && num < numtextures)
 		return textures[num]->name;
-	}
+	
 	return "-";
 }
 
+//
+// R_TextureNameForNum
+//
+// calls R_CheckTextureNameForNum and returns REDWALL if result is a no texture marker
+//
 const char *R_TextureNameForNum(INT32 num)
 {
 	const char *result = R_CheckTextureNameForNum(num);
 
-	if (strcmp(result, "-"))
+	if (strcmp(result, "-") == 0)
 		return "REDWALL";
 
 	return result;
-- 
GitLab


From 47e981d2a876940f581332afd379caca6568f87e Mon Sep 17 00:00:00 2001
From: ashifolfi <anticream39@yahoo.com>
Date: Sun, 20 Nov 2022 16:07:14 -0500
Subject: [PATCH 051/518] actually check the right type in lua

---
 src/lua_baselib.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 9cfde9706f..efbc804729 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -2834,7 +2834,7 @@ static int lib_rTextureNumForName(lua_State *L)
 
 static int lib_rCheckTextureNameForNum(lua_State *L)
 {
-	INT32 num = luaL_checkstring(L, 1);
+	INT32 num = (INT32)luaL_checkinteger(L, 1);
 	//HUDSAFE
 	lua_pushstring(L, R_CheckTextureNameForNum(num));
 	return 1;
@@ -2842,7 +2842,7 @@ static int lib_rCheckTextureNameForNum(lua_State *L)
 
 static int lib_rTextureNameForNum(lua_State *L)
 {
-	INT32 num = luaL_checkstring(L, 1);
+	INT32 num = (INT32)luaL_checkinteger(L, 1);
 	//HUDSAFE
 	lua_pushstring(L, R_TextureNameForNum(num));
 	return 1;
-- 
GitLab


From 356bdb25a1b4d36db3fe4c3a385a14f34e5e687a Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Tue, 6 Sep 2022 12:36:59 -0700
Subject: [PATCH 052/518] Add a hitbox renderer to Software mode

renderhitbox
- Tangible - collision activating objects, minus rings
- All - every object
- Intangible - the opposite of Tangible, also no rings
- Rings - rings
---
 src/Sourcefile |   1 +
 src/d_netcmd.c |   1 +
 src/p_mobj.h   |   2 +-
 src/r_bbox.c   | 290 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/r_things.c |   8 ++
 src/r_things.h |   3 +
 src/screen.h   |   2 +-
 7 files changed, 305 insertions(+), 2 deletions(-)
 create mode 100644 src/r_bbox.c

diff --git a/src/Sourcefile b/src/Sourcefile
index de90bb6091..fb08c21713 100644
--- a/src/Sourcefile
+++ b/src/Sourcefile
@@ -64,6 +64,7 @@ r_skins.c
 r_sky.c
 r_splats.c
 r_things.c
+r_bbox.c
 r_textures.c
 r_patch.c
 r_patchrotation.c
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 4e90db0dcc..ceb94fb1f3 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -868,6 +868,7 @@ void D_RegisterClientCommands(void)
 	// screen.c
 	CV_RegisterVar(&cv_fullscreen);
 	CV_RegisterVar(&cv_renderview);
+	CV_RegisterVar(&cv_renderhitbox);
 	CV_RegisterVar(&cv_renderer);
 	CV_RegisterVar(&cv_scr_depth);
 	CV_RegisterVar(&cv_scr_width);
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 60601692cf..a7e25f461e 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -122,7 +122,7 @@ typedef enum
 	MF_AMBIENT          = 1<<10,
 	// Slide this object when it hits a wall.
 	MF_SLIDEME          = 1<<11,
-	// Player cheat.
+	// Don't collide with walls or solid objects. Two MF_NOCLIP objects can't touch each other at all!
 	MF_NOCLIP           = 1<<12,
 	// Allow moves to any height, no gravity. For active floaters.
 	MF_FLOAT            = 1<<13,
diff --git a/src/r_bbox.c b/src/r_bbox.c
new file mode 100644
index 0000000000..e3b5216bfc
--- /dev/null
+++ b/src/r_bbox.c
@@ -0,0 +1,290 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1993-1996 by id Software, Inc.
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2021 by Sonic Team Junior.
+// Copyright (C)      2022 by Kart Krew.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  r_bbox.c
+/// \brief Boundary box (cube) renderer
+
+#include "doomdef.h"
+#include "command.h"
+#include "r_local.h"
+#include "screen.h" // cv_renderhitbox
+#include "v_video.h" // V_DrawFill
+
+enum {
+	RENDERHITBOX_OFF,
+	RENDERHITBOX_TANGIBLE,
+	RENDERHITBOX_ALL,
+	RENDERHITBOX_INTANGIBLE,
+	RENDERHITBOX_RINGS,
+};
+
+static CV_PossibleValue_t renderhitbox_cons_t[] = {
+	{RENDERHITBOX_OFF, "Off"},
+	{RENDERHITBOX_TANGIBLE, "Tangible"},
+	{RENDERHITBOX_ALL, "All"},
+	{RENDERHITBOX_INTANGIBLE, "Intangible"},
+	{RENDERHITBOX_RINGS, "Rings"},
+	{0}};
+
+consvar_t cv_renderhitbox = CVAR_INIT ("renderhitbox", "Off", 0, renderhitbox_cons_t, NULL);
+
+struct bbox_col {
+	INT32 x;
+	INT32 y;
+	INT32 h;
+};
+
+struct bbox_config {
+	fixed_t height;
+	fixed_t tz;
+	struct bbox_col col[4];
+	UINT8 color;
+};
+
+static inline void
+raster_bbox_seg
+(		INT32 x,
+		fixed_t y,
+		fixed_t h,
+		UINT8 pixel)
+{
+	y /= FRACUNIT;
+
+	if (y < 0)
+		y = 0;
+
+	h = y + (FixedCeil(abs(h)) / FRACUNIT);
+
+	if (h >= viewheight)
+		h = viewheight;
+
+	while (y < h)
+	{
+		topleft[x + y * vid.width] = pixel;
+		y++;
+	}
+}
+
+static void
+draw_bbox_col
+(		struct bbox_config * bb,
+		int p,
+		fixed_t tx,
+		fixed_t ty)
+{
+	struct bbox_col *col = &bb->col[p];
+
+	fixed_t xscale = FixedDiv(projection, ty);
+	fixed_t yscale = FixedDiv(projectiony, ty);
+
+	col->x = (centerxfrac + FixedMul(tx, xscale)) / FRACUNIT;
+	col->y = (centeryfrac - FixedMul(bb->tz, yscale));
+	col->h = FixedMul(bb->height, yscale);
+
+	// Using this function is TOO EASY!
+	V_DrawFill(
+			viewwindowx + col->x,
+			viewwindowy + col->y / FRACUNIT, 1,
+			col->h / FRACUNIT, V_NOSCALESTART | bb->color);
+}
+
+static void
+draw_bbox_row
+(		struct bbox_config * bb,
+		int p1,
+		int p2)
+{
+	struct bbox_col
+		*a = &bb->col[p1],
+		*b = &bb->col[p2];
+
+	INT32 x1, x2; // left, right
+	INT32 dx; // width
+
+	fixed_t y1, y2; // top, bottom
+	fixed_t s1, s2; // top and bottom increment
+
+	if (a->x > b->x)
+	{
+		struct bbox_col *c = a;
+		a = b;
+		b = c;
+	}
+
+	x1 = a->x;
+	x2 = b->x;
+
+	if (x2 >= viewwidth)
+		x2 = viewwidth - 1;
+
+	if (x1 == x2 || x1 >= viewwidth || x2 < 0)
+		return;
+
+	dx = x2 - x1;
+
+	y1 = a->y;
+	y2 = b->y;
+	s1 = (y2 - y1) / dx;
+
+	y2 = y1 + a->h;
+	s2 = ((b->y + b->h) - y2) / dx;
+
+	// FixedCeil needs a minimum!!! :D :D
+
+	if (s1 == 0)
+		s1 = 1;
+
+	if (s2 == 0)
+		s2 = 1;
+
+	if (x1 < 0)
+	{
+		y1 -= x1 * s1;
+		y2 -= x1 * s2;
+		x1 = 0;
+	}
+
+	while (x1 < x2)
+	{
+		raster_bbox_seg(x1, y1, s1, bb->color);
+		raster_bbox_seg(x1, y2, s2, bb->color);
+
+		y1 += s1;
+		y2 += s2;
+
+		x1++;
+	}
+}
+
+static UINT8
+get_bbox_color (mobj_t *thing)
+{
+	UINT32 flags = thing->flags;
+
+	if (thing->player)
+		return 255; // 0FF
+
+	if (flags & (MF_NOCLIPTHING))
+		return 7; // BFBFBF
+
+	if (flags & (MF_BOSS|MF_MISSILE|MF_ENEMY|MF_PAIN))
+		return 35; // F00
+
+	if (flags & (MF_SPECIAL|MF_MONITOR))
+		return 73; // FF0
+
+	if (flags & (MF_NOCLIP))
+		return 152; // 00F
+
+	return 0; // FFF
+}
+
+void R_DrawThingBoundingBox(mobj_t *thing)
+{
+	fixed_t rs, rc; // radius offsets
+	fixed_t gx, gy; // origin
+	fixed_t tx, ty; // translated coordinates
+
+	struct bbox_config bb = {0};
+
+	rs = FixedMul(thing->radius, viewsin);
+	rc = FixedMul(thing->radius, viewcos);
+
+	gx = thing->x - viewx;
+	gy = thing->y - viewy;
+
+	tx = FixedMul(gx, viewsin) - FixedMul(gy, viewcos);
+	ty = FixedMul(gx, viewcos) + FixedMul(gy, viewsin);
+
+	bb.height = thing->height;
+	bb.tz = (thing->z + bb.height) - viewz;
+	bb.color = get_bbox_color(thing);
+
+	// 1--3
+	// |  |
+	// 0--2
+
+	// left
+
+	tx -= rs;
+	ty -= rc;
+
+	draw_bbox_col(&bb, 0, tx + rc, ty - rs); // bottom
+	draw_bbox_col(&bb, 1, tx - rc, ty + rs); // top
+
+	// right
+
+	tx += rs + rs;
+	ty += rc + rc;
+
+	draw_bbox_col(&bb, 2, tx + rc, ty - rs); // bottom
+	draw_bbox_col(&bb, 3, tx - rc, ty + rs); // top
+
+	// connect all four columns
+
+	draw_bbox_row(&bb, 0, 1);
+	draw_bbox_row(&bb, 1, 3);
+	draw_bbox_row(&bb, 3, 2);
+	draw_bbox_row(&bb, 2, 0);
+}
+
+static boolean is_tangible (mobj_t *thing)
+{
+	// These objects can never touch another
+	if (thing->flags & (MF_NOCLIPTHING))
+	{
+		return false;
+	}
+
+	// These objects probably do nothing! :D
+	if ((thing->flags & (MF_SPECIAL|MF_SOLID|MF_SHOOTABLE
+					|MF_PUSHABLE|MF_BOSS|MF_MISSILE|MF_SPRING
+					|MF_BOUNCE|MF_MONITOR|MF_FIRE|MF_ENEMY
+					|MF_PAIN|MF_STICKY
+					|MF_GRENADEBOUNCE)) == 0U)
+	{
+		return false;
+	}
+
+	return true;
+}
+
+boolean R_ThingBoundingBoxVisible(mobj_t *thing)
+{
+	INT32 cvmode = cv_renderhitbox.value;
+
+	switch (cvmode)
+	{
+		case RENDERHITBOX_OFF:
+			return false;
+
+		case RENDERHITBOX_ALL:
+			return true;
+
+		case RENDERHITBOX_INTANGIBLE:
+			return !is_tangible(thing);
+
+		case RENDERHITBOX_TANGIBLE:
+			// Exclude rings from here, lots of them!
+			if (thing->type == MT_RING)
+			{
+				return false;
+			}
+
+			return is_tangible(thing);
+
+		case RENDERHITBOX_RINGS:
+			return (thing->type == MT_RING);
+
+		default:
+			return false;
+	}
+}
diff --git a/src/r_things.c b/src/r_things.c
index fed873fd6d..65047176dc 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -2939,6 +2939,14 @@ static void R_DrawSprite(vissprite_t *spr)
 		R_DrawFloorSplat(spr);
 	else
 		R_DrawVisSprite(spr);
+
+	if (R_ThingBoundingBoxVisible(spr->mobj))
+	{
+		// fuck you fuck you fuck you FUCK YOU
+		// (shadows are linked to their mobj)
+		if (!(spr->cut & SC_SHADOW))
+			R_DrawThingBoundingBox(spr->mobj);
+	}
 }
 
 // Special drawer for precipitation sprites Tails 08-18-2002
diff --git a/src/r_things.h b/src/r_things.h
index 35eeb9ce16..4ba649c30a 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -66,6 +66,9 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel);
 void R_InitSprites(void);
 void R_ClearSprites(void);
 
+boolean R_ThingBoundingBoxVisible(mobj_t *thing);
+void R_DrawThingBoundingBox(mobj_t *thing);
+
 boolean R_ThingVisible (mobj_t *thing);
 
 boolean R_ThingVisibleWithinDist (mobj_t *thing,
diff --git a/src/screen.h b/src/screen.h
index add048b25a..5a69be6599 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -199,7 +199,7 @@ extern CV_PossibleValue_t cv_renderer_t[];
 extern INT32 scr_bpp;
 extern UINT8 *scr_borderpatch; // patch used to fill the view borders
 
-extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen;
+extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_renderhitbox, cv_fullscreen;
 // wait for page flipping to end or not
 extern consvar_t cv_vidwait;
 extern consvar_t cv_timescale;
-- 
GitLab


From 694804cd96cafb1bfb146406f775e2bd23b1fb7a Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Sat, 10 Sep 2022 21:27:37 -0700
Subject: [PATCH 053/518] Refactor hitbox renderer to project vissprites

Properly accounts for portals (skyboxes).
---
 src/r_bbox.c   |  46 ++++++---------
 src/r_things.c | 155 ++++++++++++++++++++++++++++++++++++++++++-------
 src/r_things.h |   9 ++-
 3 files changed, 161 insertions(+), 49 deletions(-)

diff --git a/src/r_bbox.c b/src/r_bbox.c
index e3b5216bfc..bbac0f56b0 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -165,11 +165,11 @@ draw_bbox_row
 }
 
 static UINT8
-get_bbox_color (mobj_t *thing)
+get_bbox_color (vissprite_t *vis)
 {
-	UINT32 flags = thing->flags;
+	UINT32 flags = vis->mobjflags;
 
-	if (thing->player)
+	if (vis->mobj->player)
 		return 255; // 0FF
 
 	if (flags & (MF_NOCLIPTHING))
@@ -187,26 +187,21 @@ get_bbox_color (mobj_t *thing)
 	return 0; // FFF
 }
 
-void R_DrawThingBoundingBox(mobj_t *thing)
+void R_DrawThingBoundingBox(vissprite_t *vis)
 {
-	fixed_t rs, rc; // radius offsets
-	fixed_t gx, gy; // origin
-	fixed_t tx, ty; // translated coordinates
+	// radius offsets
+	fixed_t rs = vis->scale;
+	fixed_t rc = vis->xscale;
 
-	struct bbox_config bb = {0};
+	// translated coordinates
+	fixed_t tx = vis->gx;
+	fixed_t ty = vis->gy;
 
-	rs = FixedMul(thing->radius, viewsin);
-	rc = FixedMul(thing->radius, viewcos);
-
-	gx = thing->x - viewx;
-	gy = thing->y - viewy;
-
-	tx = FixedMul(gx, viewsin) - FixedMul(gy, viewcos);
-	ty = FixedMul(gx, viewcos) + FixedMul(gy, viewsin);
-
-	bb.height = thing->height;
-	bb.tz = (thing->z + bb.height) - viewz;
-	bb.color = get_bbox_color(thing);
+	struct bbox_config bb = {
+		.height = vis->thingheight,
+		.tz = vis->texturemid,
+		.color = get_bbox_color(vis),
+	};
 
 	// 1--3
 	// |  |
@@ -214,18 +209,15 @@ void R_DrawThingBoundingBox(mobj_t *thing)
 
 	// left
 
-	tx -= rs;
-	ty -= rc;
-
-	draw_bbox_col(&bb, 0, tx + rc, ty - rs); // bottom
+	draw_bbox_col(&bb, 0, tx, ty); // bottom
 	draw_bbox_col(&bb, 1, tx - rc, ty + rs); // top
 
 	// right
 
-	tx += rs + rs;
-	ty += rc + rc;
+	tx += rs;
+	ty += rc;
 
-	draw_bbox_col(&bb, 2, tx + rc, ty - rs); // bottom
+	draw_bbox_col(&bb, 2, tx, ty); // bottom
 	draw_bbox_col(&bb, 3, tx - rc, ty + rs); // top
 
 	// connect all four columns
diff --git a/src/r_things.c b/src/r_things.c
index 65047176dc..6e4062219a 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1424,6 +1424,91 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 	objectsdrawn++;
 }
 
+static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis)
+{
+	fixed_t gx, gy;
+	fixed_t tx, tz;
+
+	vissprite_t *box;
+
+	if (!R_ThingBoundingBoxVisible(thing))
+	{
+		return;
+	}
+
+	// 1--3
+	// |  |
+	// 0--2
+
+	// start in the (0) corner
+	gx = thing->x - thing->radius - viewx;
+	gy = thing->y - thing->radius - viewy;
+
+	tz = FixedMul(gx, viewcos) + FixedMul(gy, viewsin);
+
+	// thing is behind view plane?
+	// if parent vis is visible, ignore this
+	if (!vis && (tz < FixedMul(MINZ, thing->scale)))
+	{
+		return;
+	}
+
+	tx = FixedMul(gx, viewsin) - FixedMul(gy, viewcos);
+
+	// too far off the side?
+	if (!vis && abs(tx) > FixedMul(tz, fovtan)<<2)
+	{
+		return;
+	}
+
+	box = R_NewVisSprite();
+	box->mobj = thing;
+	box->mobjflags = thing->flags;
+	box->thingheight = thing->height;
+	box->cut = SC_BBOX;
+
+	box->gx = tx;
+	box->gy = tz;
+
+	box->scale = 2 * FixedMul(thing->radius, viewsin);
+	box->xscale = 2 * FixedMul(thing->radius, viewcos);
+
+	box->pz = thing->z;
+	box->pzt = box->pz + box->thingheight;
+
+	box->gzt = box->pzt;
+	box->gz = box->pz;
+	box->texturemid = box->gzt - viewz;
+
+	if (vis)
+	{
+		box->x1 = vis->x1;
+		box->x2 = vis->x2;
+		box->szt = vis->szt;
+		box->sz = vis->sz;
+
+		box->sortscale = vis->sortscale; // link sorting to sprite
+		box->dispoffset = vis->dispoffset + 5;
+
+		box->cut |= SC_LINKDRAW;
+	}
+	else
+	{
+		fixed_t xscale = FixedDiv(projection, tz);
+		fixed_t yscale = FixedDiv(projectiony, tz);
+		fixed_t top = (centeryfrac - FixedMul(box->texturemid, yscale));
+
+		box->x1 = (centerxfrac + FixedMul(box->gx, xscale)) / FRACUNIT;
+		box->x2 = box->x1;
+
+		box->szt = top / FRACUNIT;
+		box->sz = (top + FixedMul(box->thingheight, yscale)) / FRACUNIT;
+
+		box->sortscale = yscale;
+		box->dispoffset = 0;
+	}
+}
+
 //
 // R_ProjectSprite
 // Generates a vissprite for a thing
@@ -2181,6 +2266,8 @@ static void R_ProjectSprite(mobj_t *thing)
 	if (oldthing->shadowscale && cv_shadow.value)
 		R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, basetz);
 
+	R_ProjectBoundingBox(oldthing, vis);
+
 	// Debug
 	++objectsdrawn;
 }
@@ -2406,8 +2493,26 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel)
 	hoop_limit_dist = (fixed_t)(cv_drawdist_nights.value) << FRACBITS;
 	for (thing = sec->thinglist; thing; thing = thing->snext)
 	{
-		if (R_ThingVisibleWithinDist(thing, limit_dist, hoop_limit_dist))
-			R_ProjectSprite(thing);
+		if (R_ThingWithinDist(thing, limit_dist, hoop_limit_dist))
+		{
+			const INT32 oldobjectsdrawn = objectsdrawn;
+
+			if (R_ThingVisible(thing))
+			{
+				R_ProjectSprite(thing);
+			}
+
+			// I'm so smart :^)
+			if (objectsdrawn == oldobjectsdrawn)
+			{
+				/*
+				Object is invisible OR is off screen but
+				render its bbox even if the latter because
+				radius could be bigger than sprite.
+				*/
+				R_ProjectBoundingBox(thing, NULL);
+			}
+		}
 	}
 
 	// no, no infinite draw distance for precipitation. this option at zero is supposed to turn it off
@@ -2495,6 +2600,10 @@ static void R_SortVisSprites(vissprite_t* vsprsortedhead, UINT32 start, UINT32 e
 			if (dsfirst->cut & SC_SHADOW)
 				continue;
 
+			// don't connect to your bounding box!
+			if (dsfirst->cut & SC_BBOX)
+				continue;
+
 			// don't connect if it's not the tracer
 			if (dsfirst->mobj != ds->mobj)
 				continue;
@@ -2935,18 +3044,12 @@ static void R_DrawSprite(vissprite_t *spr)
 	mfloorclip = spr->clipbot;
 	mceilingclip = spr->cliptop;
 
-	if (spr->cut & SC_SPLAT)
+	if (spr->cut & SC_BBOX)
+		R_DrawThingBoundingBox(spr);
+	else if (spr->cut & SC_SPLAT)
 		R_DrawFloorSplat(spr);
 	else
 		R_DrawVisSprite(spr);
-
-	if (R_ThingBoundingBoxVisible(spr->mobj))
-	{
-		// fuck you fuck you fuck you FUCK YOU
-		// (shadows are linked to their mobj)
-		if (!(spr->cut & SC_SHADOW))
-			R_DrawThingBoundingBox(spr->mobj);
-	}
 }
 
 // Special drawer for precipitation sprites Tails 08-18-2002
@@ -3182,9 +3285,13 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
 	for (; clippedvissprites < visspritecount; clippedvissprites++)
 	{
 		vissprite_t *spr = R_GetVisSprite(clippedvissprites);
-		INT32 x1 = (spr->cut & SC_SPLAT) ? 0 : spr->x1;
-		INT32 x2 = (spr->cut & SC_SPLAT) ? viewwidth : spr->x2;
-		R_ClipVisSprite(spr, x1, x2, dsstart, portal);
+
+		if (!(spr->cut & SC_BBOX)) // Do not clip bounding boxes
+		{
+			INT32 x1 = (spr->cut & SC_SPLAT) ? 0 : spr->x1;
+			INT32 x2 = (spr->cut & SC_SPLAT) ? viewwidth : spr->x2;
+			R_ClipVisSprite(spr, x1, x2, dsstart, portal);
+		}
 	}
 }
 
@@ -3198,16 +3305,11 @@ boolean R_ThingVisible (mobj_t *thing)
 	));
 }
 
-boolean R_ThingVisibleWithinDist (mobj_t *thing,
+boolean R_ThingWithinDist (mobj_t *thing,
 		fixed_t      limit_dist,
 		fixed_t hoop_limit_dist)
 {
-	fixed_t approx_dist;
-
-	if (! R_ThingVisible(thing))
-		return false;
-
-	approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y);
+	const fixed_t approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y);
 
 	if (thing->sprite == SPR_HOOP)
 	{
@@ -3223,6 +3325,17 @@ boolean R_ThingVisibleWithinDist (mobj_t *thing,
 	return true;
 }
 
+// For OpenGL, TODO: REMOVE!!
+boolean R_ThingVisibleWithinDist (mobj_t *thing,
+		fixed_t      limit_dist,
+		fixed_t hoop_limit_dist)
+{
+	if (! R_ThingVisible(thing))
+		return false;
+
+	return R_ThingWithinDist(thing, limit_dist, hoop_limit_dist);
+}
+
 /* Check if precipitation may be drawn from our current view. */
 boolean R_PrecipThingVisible (precipmobj_t *precipthing,
 		fixed_t limit_dist)
diff --git a/src/r_things.h b/src/r_things.h
index 4ba649c30a..f1855dfd80 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -67,10 +67,13 @@ void R_InitSprites(void);
 void R_ClearSprites(void);
 
 boolean R_ThingBoundingBoxVisible(mobj_t *thing);
-void R_DrawThingBoundingBox(mobj_t *thing);
 
 boolean R_ThingVisible (mobj_t *thing);
 
+boolean R_ThingWithinDist (mobj_t *thing,
+		fixed_t        draw_dist,
+		fixed_t nights_draw_dist);
+
 boolean R_ThingVisibleWithinDist (mobj_t *thing,
 		fixed_t        draw_dist,
 		fixed_t nights_draw_dist);
@@ -135,6 +138,7 @@ typedef enum
 	SC_SHADOW     = 1<<10,
 	SC_SHEAR      = 1<<11,
 	SC_SPLAT      = 1<<12,
+	SC_BBOX       = 1<<13,
 	// masks
 	SC_CUTMASK    = SC_TOP|SC_BOTTOM,
 	SC_FLAGMASK   = ~SC_CUTMASK
@@ -224,6 +228,9 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal);
 void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, drawseg_t* dsstart, portal_t* portal);
 
 boolean R_SpriteIsFlashing(vissprite_t *vis);
+
+void R_DrawThingBoundingBox(vissprite_t *spr);
+
 UINT8 *R_GetSpriteTranslation(vissprite_t *vis);
 
 // ----------
-- 
GitLab


From 99653de13431d91ae9259dfd6c002861ea396156 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Sun, 11 Sep 2022 06:25:08 -0700
Subject: [PATCH 054/518] Do not render viewmobj or skybox viewpoint hitbox

If you are a spectator (or in first person), the hitbox
exists right ontop of you and hitboxes don't render
correctly if they are too close to the viewpoint.
---
 src/r_bbox.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/src/r_bbox.c b/src/r_bbox.c
index bbac0f56b0..3d46aa566f 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -253,6 +253,27 @@ boolean R_ThingBoundingBoxVisible(mobj_t *thing)
 {
 	INT32 cvmode = cv_renderhitbox.value;
 
+	// Do not render bbox for these
+	switch (thing->type)
+	{
+		default:
+			// First person / awayviewmobj -- rendering
+			// a bbox too close to the viewpoint causes
+			// anomalies and these are exactly on the
+			// viewpoint!
+			if (thing != r_viewmobj)
+			{
+				break;
+			}
+			// FALLTHRU
+
+		case MT_SKYBOX:
+			// Ditto for skybox viewpoint but because they
+			// are rendered using portals in Software,
+			// r_viewmobj does not point here.
+			return false;
+	}
+
 	switch (cvmode)
 	{
 		case RENDERHITBOX_OFF:
-- 
GitLab


From 7527fdbb568358bc56755220945255c8adaefeae Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Sun, 11 Sep 2022 07:08:10 -0700
Subject: [PATCH 055/518] Remedy some quirky rendering of hitboxes if your
 viewpoint is too close

It's not correct but it's better than before.
---
 src/r_bbox.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/r_bbox.c b/src/r_bbox.c
index 3d46aa566f..e1749225ca 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -82,8 +82,13 @@ draw_bbox_col
 {
 	struct bbox_col *col = &bb->col[p];
 
-	fixed_t xscale = FixedDiv(projection, ty);
-	fixed_t yscale = FixedDiv(projectiony, ty);
+	fixed_t xscale, yscale;
+
+	if (ty < FRACUNIT) // projection breaks down here
+		ty = FRACUNIT;
+
+	xscale = FixedDiv(projection, ty);
+	yscale = FixedDiv(projectiony, ty);
 
 	col->x = (centerxfrac + FixedMul(tx, xscale)) / FRACUNIT;
 	col->y = (centeryfrac - FixedMul(bb->tz, yscale));
-- 
GitLab


From 5b53017a09eb70c94ffaae259350d765de901988 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Sun, 11 Sep 2022 16:54:15 -0700
Subject: [PATCH 056/518] r_opengl: add PF_WireFrame and SHADER_NONE

Draw lines instead of tris and disable shader entirely.
---
 src/hardware/hw_defs.h           | 4 +++-
 src/hardware/r_opengl/r_opengl.c | 8 +++++++-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index fca9b80a30..bb27c34fa4 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -136,6 +136,7 @@ typedef struct
 // Predefined shader types
 enum
 {
+	SHADER_NONE = -1,
 	SHADER_DEFAULT = 0,
 
 	SHADER_FLOOR,
@@ -237,7 +238,8 @@ enum EPolyFlags
 	PF_RemoveYWrap      = 0x00010000,   // Forces clamp texture on Y
 	PF_ForceWrapX       = 0x00020000,   // Forces repeat texture on X
 	PF_ForceWrapY       = 0x00040000,   // Forces repeat texture on Y
-	PF_Ripple           = 0x00100000    // Water ripple effect. The current backend doesn't use it for anything.
+	PF_Ripple           = 0x00100000,   // Water ripple effect. The current backend doesn't use it for anything.
+	PF_WireFrame        = 0x00200000,   // Draws vertices as lines instead of triangles
 };
 
 
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 9d1630abbb..3a97f9315a 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -1030,6 +1030,12 @@ EXPORT void HWRAPI(LoadCustomShader) (int number, char *code, size_t size, boole
 EXPORT void HWRAPI(SetShader) (int type)
 {
 #ifdef GL_SHADERS
+	if (type == SHADER_NONE)
+	{
+		HWRAPI(UnSetShader)();
+		return;
+	}
+
 	if (gl_allowshaders != HWD_SHADEROPTION_OFF)
 	{
 		gl_shader_t *shader = gl_shaderstate.current;
@@ -2290,7 +2296,7 @@ EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUI
 
 	pglVertexPointer(3, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].x);
 	pglTexCoordPointer(2, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].s);
-	pglDrawArrays(GL_TRIANGLE_FAN, 0, iNumPts);
+	pglDrawArrays(PolyFlags & PF_WireFrame ? GL_LINES : GL_TRIANGLE_FAN, 0, iNumPts);
 
 	if (PolyFlags & PF_RemoveYWrap)
 		pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-- 
GitLab


From bfaf2bc6f3635f3e95b8bf6172d9a2f6d3e22edf Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Sun, 11 Sep 2022 16:57:58 -0700
Subject: [PATCH 057/518] OpenGL hitbox renderer

I apologize for that vertex array.
---
 src/hardware/hw_glob.h |   1 +
 src/hardware/hw_main.c | 118 +++++++++++++++++++++++++++++++++++++++--
 src/r_bbox.c           |   9 ++--
 src/r_things.h         |   1 +
 4 files changed, 120 insertions(+), 9 deletions(-)

diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index 1ec9101c3b..504cdf1489 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -81,6 +81,7 @@ typedef struct gl_vissprite_s
 
 	boolean flip, vflip;
 	boolean precip; // Tails 08-25-2002
+	boolean bbox;
 	boolean rotated;
 	UINT8 translucency;       //alpha level 0-255
 
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 3cb7275a0b..24d5925bde 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -66,6 +66,7 @@ static void HWR_ProjectSprite(mobj_t *thing);
 #ifdef HWPRECIP
 static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing);
 #endif
+static void HWR_ProjectBoundingBox(mobj_t *thing);
 
 void HWR_AddTransparentFloor(levelflat_t *levelflat, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap);
 void HWR_AddTransparentPolyobjectFloor(levelflat_t *levelflat, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight,
@@ -4036,6 +4037,54 @@ static void HWR_SplitSprite(gl_vissprite_t *spr)
 		HWR_LinkDrawHackAdd(wallVerts, spr);
 }
 
+static void HWR_DrawBoundingBox(gl_vissprite_t *vis)
+{
+	FOutVector v[24];
+	FSurfaceInfo Surf = {0};
+
+	//
+	// create a cube (side view)
+	//
+	//  5--4  3
+	//        |
+	//        |
+	//  0--1  2
+	//
+	// repeat this 4 times (overhead)
+	//
+	//
+	// 17    20  21    11
+	//    16 15  14 10
+	// 27 22  *--*  07 12
+	//        |  |
+	// 26 23  *--*  06 13
+	//    24 00  01 02
+	// 25    05  04    03
+	//
+
+	v[000].x = v[005].x = v[015].x = v[016].x = v[017].x = v[020].x =
+		v[022].x = v[023].x = v[024].x = v[025].x = v[026].x = v[027].x = vis->x1; // west
+
+	v[001].x = v[002].x = v[003].x = v[004].x = v[006].x = v[007].x =
+		v[010].x = v[011].x = v[012].x = v[013].x = v[014].x = v[021].x = vis->x2; // east
+
+	v[000].z = v[001].z = v[002].z = v[003].z = v[004].z = v[005].z =
+		v[006].z = v[013].z = v[023].z = v[024].z = v[025].z = v[026].z = vis->z1; // south
+
+	v[007].z = v[010].z = v[011].z = v[012].z = v[014].z = v[015].z =
+		v[016].z = v[017].z = v[020].z = v[021].z = v[022].z = v[027].z = vis->z2; // north
+
+	v[000].y = v[001].y = v[002].y = v[006].y = v[007].y = v[010].y =
+		v[014].y = v[015].y = v[016].y = v[022].y = v[023].y = v[024].y = vis->gz; // bottom
+
+	v[003].y = v[004].y = v[005].y = v[011].y = v[012].y = v[013].y =
+		v[017].y = v[020].y = v[021].y = v[025].y = v[026].y = v[027].y = vis->gzt; // top
+
+	Surf.PolyColor = V_GetColor(R_GetBoundingBoxColor(vis->mobj));
+
+	HWR_ProcessPolygon(&Surf, v, 24, PF_Modulated|PF_NoTexture|PF_WireFrame, SHADER_NONE, false);
+}
+
 // -----------------+
 // HWR_DrawSprite   : Draw flat sprites
 //                  : (monsters, bonuses, weapons, lights, ...)
@@ -4480,9 +4529,16 @@ static int CompareVisSprites(const void *p1, const void *p2)
 	int transparency1;
 	int transparency2;
 
+	int linkdraw1;
+	int linkdraw2;
+
+	// bbox doesn't need to be sorted
+	if (spr1->bbox || spr2->bbox)
+		return 0;
+
 	// check for precip first, because then sprX->mobj is actually a precipmobj_t and does not have flags2 or tracer
-	int linkdraw1 = !spr1->precip && (spr1->mobj->flags2 & MF2_LINKDRAW) && spr1->mobj->tracer;
-	int linkdraw2 = !spr2->precip && (spr2->mobj->flags2 & MF2_LINKDRAW) && spr2->mobj->tracer;
+	linkdraw1 = !spr1->precip && (spr1->mobj->flags2 & MF2_LINKDRAW) && spr1->mobj->tracer;
+	linkdraw2 = !spr2->precip && (spr2->mobj->flags2 & MF2_LINKDRAW) && spr2->mobj->tracer;
 
 	// ^ is the XOR operation
 	// if comparing a linkdraw and non-linkdraw sprite or 2 linkdraw sprites with different tracers, then use
@@ -4852,6 +4908,9 @@ static void HWR_DrawSprites(void)
 	for (i = 0; i < gl_visspritecount; i++)
 	{
 		gl_vissprite_t *spr = gl_vsprorder[i];
+		if (spr->bbox)
+			HWR_DrawBoundingBox(spr);
+		else
 #ifdef HWPRECIP
 		if (spr->precip)
 			HWR_DrawPrecipitationSprite(spr);
@@ -4951,8 +5010,15 @@ static void HWR_AddSprites(sector_t *sec)
 	hoop_limit_dist = (fixed_t)(cv_drawdist_nights.value) << FRACBITS;
 	for (thing = sec->thinglist; thing; thing = thing->snext)
 	{
-		if (R_ThingVisibleWithinDist(thing, limit_dist, hoop_limit_dist))
-			HWR_ProjectSprite(thing);
+		if (R_ThingWithinDist(thing, limit_dist, hoop_limit_dist))
+		{
+			if (R_ThingVisible(thing))
+			{
+				HWR_ProjectSprite(thing);
+			}
+
+			HWR_ProjectBoundingBox(thing);
+		}
 	}
 
 #ifdef HWPRECIP
@@ -5462,6 +5528,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	vis->vflip = vflip;
 
 	vis->precip = false;
+	vis->bbox = false;
 
 	vis->angle = interp.angle;
 }
@@ -5584,6 +5651,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
 	vis->gz = vis->gzt - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height);
 
 	vis->precip = true;
+	vis->bbox = false;
 
 	// okay... this is a hack, but weather isn't networked, so it should be ok
 	if (!(thing->precipflags & PCF_THUNK))
@@ -5597,6 +5665,48 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
 }
 #endif
 
+static void HWR_ProjectBoundingBox(mobj_t *thing)
+{
+	gl_vissprite_t *vis;
+	float tr_x, tr_y;
+	float tz;
+	float rad;
+
+	if (!thing)
+		return;
+
+	if (!R_ThingBoundingBoxVisible(thing))
+		return;
+
+	// transform the origin point
+	tr_x = FIXED_TO_FLOAT(thing->x) - gl_viewx;
+	tr_y = FIXED_TO_FLOAT(thing->y) - gl_viewy;
+
+	// rotation around vertical axis
+	tz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin);
+
+	// thing is behind view plane?
+	if (tz < ZCLIP_PLANE)
+		return;
+
+	tr_x += gl_viewx;
+	tr_y += gl_viewy;
+
+	rad = FIXED_TO_FLOAT(thing->radius);
+
+	vis = HWR_NewVisSprite();
+	vis->x1 = tr_x - rad;
+	vis->x2 = tr_x + rad;
+	vis->z1 = tr_y - rad;
+	vis->z2 = tr_y + rad;
+	vis->gz = FIXED_TO_FLOAT(thing->z);
+	vis->gzt = vis->gz + FIXED_TO_FLOAT(thing->height);
+	vis->mobj = thing;
+
+	vis->precip = false;
+	vis->bbox = true;
+}
+
 // ==========================================================================
 // Sky dome rendering, ported from PrBoom+
 // ==========================================================================
diff --git a/src/r_bbox.c b/src/r_bbox.c
index e1749225ca..9108ec0c0b 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -169,12 +169,11 @@ draw_bbox_row
 	}
 }
 
-static UINT8
-get_bbox_color (vissprite_t *vis)
+UINT8 R_GetBoundingBoxColor(mobj_t *thing)
 {
-	UINT32 flags = vis->mobjflags;
+	UINT32 flags = thing->flags;
 
-	if (vis->mobj->player)
+	if (thing->player)
 		return 255; // 0FF
 
 	if (flags & (MF_NOCLIPTHING))
@@ -205,7 +204,7 @@ void R_DrawThingBoundingBox(vissprite_t *vis)
 	struct bbox_config bb = {
 		.height = vis->thingheight,
 		.tz = vis->texturemid,
-		.color = get_bbox_color(vis),
+		.color = R_GetBoundingBoxColor(vis->mobj),
 	};
 
 	// 1--3
diff --git a/src/r_things.h b/src/r_things.h
index f1855dfd80..9aa1003da7 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -66,6 +66,7 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel);
 void R_InitSprites(void);
 void R_ClearSprites(void);
 
+UINT8 R_GetBoundingBoxColor(mobj_t *thing);
 boolean R_ThingBoundingBoxVisible(mobj_t *thing);
 
 boolean R_ThingVisible (mobj_t *thing);
-- 
GitLab


From 7855bae8a13f4787ecc6badb11fbf2c96e582515 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Sun, 11 Sep 2022 16:59:25 -0700
Subject: [PATCH 058/518] Remove R_ThingVisibleWithinDist

It's no longer used!
---
 src/r_things.c | 11 -----------
 src/r_things.h |  4 ----
 2 files changed, 15 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index 6e4062219a..953f12ea6e 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -3325,17 +3325,6 @@ boolean R_ThingWithinDist (mobj_t *thing,
 	return true;
 }
 
-// For OpenGL, TODO: REMOVE!!
-boolean R_ThingVisibleWithinDist (mobj_t *thing,
-		fixed_t      limit_dist,
-		fixed_t hoop_limit_dist)
-{
-	if (! R_ThingVisible(thing))
-		return false;
-
-	return R_ThingWithinDist(thing, limit_dist, hoop_limit_dist);
-}
-
 /* Check if precipitation may be drawn from our current view. */
 boolean R_PrecipThingVisible (precipmobj_t *precipthing,
 		fixed_t limit_dist)
diff --git a/src/r_things.h b/src/r_things.h
index 9aa1003da7..84d5967abb 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -75,10 +75,6 @@ boolean R_ThingWithinDist (mobj_t *thing,
 		fixed_t        draw_dist,
 		fixed_t nights_draw_dist);
 
-boolean R_ThingVisibleWithinDist (mobj_t *thing,
-		fixed_t        draw_dist,
-		fixed_t nights_draw_dist);
-
 boolean R_PrecipThingVisible (precipmobj_t *precipthing,
 		fixed_t precip_draw_dist);
 
-- 
GitLab


From f7fa9fcc190f0ba4fb8ad3ef15465f6c37ea9443 Mon Sep 17 00:00:00 2001
From: toaster <rollerorbital@gmail.com>
Date: Mon, 12 Sep 2022 12:58:39 +0100
Subject: [PATCH 059/518] Fix compilation issue with nested defines for
 r_opengl.c UnSetShader

---
 src/hardware/r_opengl/r_opengl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 3a97f9315a..98929f0303 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -1032,7 +1032,7 @@ EXPORT void HWRAPI(SetShader) (int type)
 #ifdef GL_SHADERS
 	if (type == SHADER_NONE)
 	{
-		HWRAPI(UnSetShader)();
+		UnSetShader();
 		return;
 	}
 
-- 
GitLab


From 8430dfa0639c06b0d48b34a76e1abe14af4885ce Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Tue, 25 Oct 2022 12:03:18 -0700
Subject: [PATCH 060/518] Fix copyright year in r_bbox.c

---
 src/r_bbox.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/r_bbox.c b/src/r_bbox.c
index 9108ec0c0b..cc225eaecd 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -2,8 +2,8 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2021 by Sonic Team Junior.
 // Copyright (C)      2022 by Kart Krew.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
-- 
GitLab


From 5411d522e5143caed15d23693bba175f0b1d19f3 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Wed, 26 Oct 2022 13:53:35 -0700
Subject: [PATCH 061/518] r_bbox.c: use size_t to access column array

---
 src/r_bbox.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/r_bbox.c b/src/r_bbox.c
index cc225eaecd..b45007bb2a 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -76,7 +76,7 @@ raster_bbox_seg
 static void
 draw_bbox_col
 (		struct bbox_config * bb,
-		int p,
+		size_t p,
 		fixed_t tx,
 		fixed_t ty)
 {
@@ -104,8 +104,8 @@ draw_bbox_col
 static void
 draw_bbox_row
 (		struct bbox_config * bb,
-		int p1,
-		int p2)
+		size_t p1,
+		size_t p2)
 {
 	struct bbox_col
 		*a = &bb->col[p1],
-- 
GitLab


From 34cc1d1cc4a8c27c86b71ee7c94d0c1fc177f187 Mon Sep 17 00:00:00 2001
From: Ace Lite <lightacecz@gmail.com>
Date: Wed, 23 Nov 2022 17:09:54 +0000
Subject: [PATCH 062/518] Exposed floor/ceiling pic x/y offsets to Lua.

---
 src/lua_maplib.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index b4565121d3..4b90bc0135 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -33,7 +33,13 @@ enum sector_e {
 	sector_floorheight,
 	sector_ceilingheight,
 	sector_floorpic,
+	sector_floorxoffs,
+	sector_flooryoffs,
+	sector_floorpicangle,	
 	sector_ceilingpic,
+	sector_ceilingxoffs,
+	sector_ceilingyoffs,
+	sector_ceilingpicangle,	
 	sector_lightlevel,
 	sector_floorlightlevel,
 	sector_floorlightabsolute,
@@ -63,7 +69,13 @@ static const char *const sector_opt[] = {
 	"floorheight",
 	"ceilingheight",
 	"floorpic",
+	"floorxoffs",
+	"flooryoffs",
+	"floorpicangle",	
 	"ceilingpic",
+	"ceilingxoffs",
+	"ceilingyoffs",
+	"ceilingpicangle",	
 	"lightlevel",
 	"floorlightlevel",
 	"floorlightabsolute",
@@ -607,6 +619,21 @@ static int sector_get(lua_State *L)
 		lua_pushlstring(L, levelflat->name, i);
 		return 1;
 	}
+	case sector_floorxoffs:
+	{
+		lua_pushfixed(L, sector->floor_xoffs);
+		return 1;
+	}
+	case sector_flooryoffs:
+	{
+		lua_pushfixed(L, sector->floor_yoffs);
+		return 1;
+	}
+	case sector_floorpicangle: 
+	{
+		lua_pushangle(L, sector->floorpic_angle);
+		return 1;
+	}	
 	case sector_ceilingpic: // ceilingpic
 	{
 		levelflat_t *levelflat = &levelflats[sector->ceilingpic];
@@ -616,6 +643,21 @@ static int sector_get(lua_State *L)
 		lua_pushlstring(L, levelflat->name, i);
 		return 1;
 	}
+	case sector_ceilingxoffs:
+	{
+		lua_pushfixed(L, sector->ceiling_xoffs);
+		return 1;
+	}
+	case sector_ceilingyoffs:
+	{
+		lua_pushfixed(L, sector->ceiling_yoffs);
+		return 1;
+	}
+	case sector_ceilingpicangle:
+	{
+		lua_pushangle(L, sector->ceilingpic_angle);
+		return 1;
+	}	
 	case sector_lightlevel:
 		lua_pushinteger(L, sector->lightlevel);
 		return 1;
@@ -751,9 +793,27 @@ static int sector_set(lua_State *L)
 	case sector_floorpic:
 		sector->floorpic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
 		break;
+	case sector_floorxoffs:
+		sector->floor_xoffs = luaL_checkfixed(L, 3);
+		break;
+	case sector_flooryoffs:
+		sector->floor_yoffs = luaL_checkfixed(L, 3);
+		break;
+	case sector_floorpicangle:
+		sector->floorpic_angle = luaL_checkangle(L, 3);
+		break;		
 	case sector_ceilingpic:
 		sector->ceilingpic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
 		break;
+	case sector_ceilingxoffs:
+		sector->ceiling_xoffs = luaL_checkfixed(L, 3);
+		break;
+	case sector_ceilingyoffs:
+		sector->ceiling_yoffs = luaL_checkfixed(L, 3);
+		break;
+	case sector_ceilingpicangle:
+		sector->ceilingpic_angle = luaL_checkangle(L, 3);
+		break;
 	case sector_lightlevel:
 		sector->lightlevel = (INT16)luaL_checkinteger(L, 3);
 		break;
-- 
GitLab


From e789bf1fb241148d6dbd54c2e0efc7059b032b44 Mon Sep 17 00:00:00 2001
From: Ace Lite <lightacecz@gmail.com>
Date: Wed, 23 Nov 2022 17:29:44 +0000
Subject: [PATCH 063/518] Fix for consistency just *angle for *_angle

---
 src/lua_maplib.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 4b90bc0135..ff732af291 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -35,11 +35,11 @@ enum sector_e {
 	sector_floorpic,
 	sector_floorxoffs,
 	sector_flooryoffs,
-	sector_floorpicangle,	
+	sector_floorpic_angle,	
 	sector_ceilingpic,
 	sector_ceilingxoffs,
 	sector_ceilingyoffs,
-	sector_ceilingpicangle,	
+	sector_ceilingpic_angle,	
 	sector_lightlevel,
 	sector_floorlightlevel,
 	sector_floorlightabsolute,
@@ -71,11 +71,11 @@ static const char *const sector_opt[] = {
 	"floorpic",
 	"floorxoffs",
 	"flooryoffs",
-	"floorpicangle",	
+	"floorpic_angle",	
 	"ceilingpic",
 	"ceilingxoffs",
 	"ceilingyoffs",
-	"ceilingpicangle",	
+	"ceilingpic_angle",	
 	"lightlevel",
 	"floorlightlevel",
 	"floorlightabsolute",
@@ -629,7 +629,7 @@ static int sector_get(lua_State *L)
 		lua_pushfixed(L, sector->floor_yoffs);
 		return 1;
 	}
-	case sector_floorpicangle: 
+	case sector_floorpic_angle: 
 	{
 		lua_pushangle(L, sector->floorpic_angle);
 		return 1;
@@ -653,7 +653,7 @@ static int sector_get(lua_State *L)
 		lua_pushfixed(L, sector->ceiling_yoffs);
 		return 1;
 	}
-	case sector_ceilingpicangle:
+	case sector_ceilingpic_angle:
 	{
 		lua_pushangle(L, sector->ceilingpic_angle);
 		return 1;
@@ -799,7 +799,7 @@ static int sector_set(lua_State *L)
 	case sector_flooryoffs:
 		sector->floor_yoffs = luaL_checkfixed(L, 3);
 		break;
-	case sector_floorpicangle:
+	case sector_floorpic_angle:
 		sector->floorpic_angle = luaL_checkangle(L, 3);
 		break;		
 	case sector_ceilingpic:
@@ -811,7 +811,7 @@ static int sector_set(lua_State *L)
 	case sector_ceilingyoffs:
 		sector->ceiling_yoffs = luaL_checkfixed(L, 3);
 		break;
-	case sector_ceilingpicangle:
+	case sector_ceilingpic_angle:
 		sector->ceilingpic_angle = luaL_checkangle(L, 3);
 		break;
 	case sector_lightlevel:
-- 
GitLab


From 27c6afb80b2a67ec1197dca5d9a96e9fec6de512 Mon Sep 17 00:00:00 2001
From: Ace Lite <lightacecz@gmail.com>
Date: Thu, 24 Nov 2022 18:51:53 +0000
Subject: [PATCH 064/518] Yikes. how did I miss "_" on every single variable

---
 src/lua_maplib.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index ff732af291..0def825e14 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -33,12 +33,12 @@ enum sector_e {
 	sector_floorheight,
 	sector_ceilingheight,
 	sector_floorpic,
-	sector_floorxoffs,
-	sector_flooryoffs,
+	sector_floor_xoffs,
+	sector_floor_yoffs,
 	sector_floorpic_angle,	
 	sector_ceilingpic,
-	sector_ceilingxoffs,
-	sector_ceilingyoffs,
+	sector_ceiling_xoffs,
+	sector_ceiling_yoffs,
 	sector_ceilingpic_angle,	
 	sector_lightlevel,
 	sector_floorlightlevel,
@@ -69,12 +69,12 @@ static const char *const sector_opt[] = {
 	"floorheight",
 	"ceilingheight",
 	"floorpic",
-	"floorxoffs",
-	"flooryoffs",
+	"floor_xoffs",
+	"floor_yoffs",
 	"floorpic_angle",	
 	"ceilingpic",
-	"ceilingxoffs",
-	"ceilingyoffs",
+	"ceiling_xoffs",
+	"ceiling_yoffs",
 	"ceilingpic_angle",	
 	"lightlevel",
 	"floorlightlevel",
@@ -619,12 +619,12 @@ static int sector_get(lua_State *L)
 		lua_pushlstring(L, levelflat->name, i);
 		return 1;
 	}
-	case sector_floorxoffs:
+	case sector_floor_xoffs:
 	{
 		lua_pushfixed(L, sector->floor_xoffs);
 		return 1;
 	}
-	case sector_flooryoffs:
+	case sector_floor_yoffs:
 	{
 		lua_pushfixed(L, sector->floor_yoffs);
 		return 1;
@@ -643,12 +643,12 @@ static int sector_get(lua_State *L)
 		lua_pushlstring(L, levelflat->name, i);
 		return 1;
 	}
-	case sector_ceilingxoffs:
+	case sector_ceiling_xoffs:
 	{
 		lua_pushfixed(L, sector->ceiling_xoffs);
 		return 1;
 	}
-	case sector_ceilingyoffs:
+	case sector_ceiling_yoffs:
 	{
 		lua_pushfixed(L, sector->ceiling_yoffs);
 		return 1;
@@ -793,10 +793,10 @@ static int sector_set(lua_State *L)
 	case sector_floorpic:
 		sector->floorpic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
 		break;
-	case sector_floorxoffs:
+	case sector_floor_xoffs:
 		sector->floor_xoffs = luaL_checkfixed(L, 3);
 		break;
-	case sector_flooryoffs:
+	case sector_floor_yoffs:
 		sector->floor_yoffs = luaL_checkfixed(L, 3);
 		break;
 	case sector_floorpic_angle:
@@ -805,10 +805,10 @@ static int sector_set(lua_State *L)
 	case sector_ceilingpic:
 		sector->ceilingpic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
 		break;
-	case sector_ceilingxoffs:
+	case sector_ceiling_xoffs:
 		sector->ceiling_xoffs = luaL_checkfixed(L, 3);
 		break;
-	case sector_ceilingyoffs:
+	case sector_ceiling_yoffs:
 		sector->ceiling_yoffs = luaL_checkfixed(L, 3);
 		break;
 	case sector_ceilingpic_angle:
-- 
GitLab


From 9d4a3b91c3c33d8e8945d90a61584336c41e461f Mon Sep 17 00:00:00 2001
From: Ace Lite <lightacecz@gmail.com>
Date: Fri, 25 Nov 2022 22:27:41 +0000
Subject: [PATCH 065/518] change names of Lua variables for last time.

---
 src/lua_maplib.c | 48 ++++++++++++++++++++++++------------------------
 1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 0def825e14..7908a1d728 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -33,13 +33,13 @@ enum sector_e {
 	sector_floorheight,
 	sector_ceilingheight,
 	sector_floorpic,
-	sector_floor_xoffs,
-	sector_floor_yoffs,
-	sector_floorpic_angle,	
+	sector_floorxoffset,
+	sector_flooryoffset,
+	sector_floorangle,	
 	sector_ceilingpic,
-	sector_ceiling_xoffs,
-	sector_ceiling_yoffs,
-	sector_ceilingpic_angle,	
+	sector_ceilingxoffset,
+	sector_ceilingyoffset,
+	sector_ceilingangle,
 	sector_lightlevel,
 	sector_floorlightlevel,
 	sector_floorlightabsolute,
@@ -69,13 +69,13 @@ static const char *const sector_opt[] = {
 	"floorheight",
 	"ceilingheight",
 	"floorpic",
-	"floor_xoffs",
-	"floor_yoffs",
-	"floorpic_angle",	
+	"floorxoffset",
+	"flooryoffset",
+	"floorangle",
 	"ceilingpic",
-	"ceiling_xoffs",
-	"ceiling_yoffs",
-	"ceilingpic_angle",	
+	"ceilingxoffset",
+	"ceilingyoffset",
+	"ceilingangle",	
 	"lightlevel",
 	"floorlightlevel",
 	"floorlightabsolute",
@@ -619,17 +619,17 @@ static int sector_get(lua_State *L)
 		lua_pushlstring(L, levelflat->name, i);
 		return 1;
 	}
-	case sector_floor_xoffs:
+	case sector_floorxoffset:
 	{
 		lua_pushfixed(L, sector->floor_xoffs);
 		return 1;
 	}
-	case sector_floor_yoffs:
+	case sector_flooryoffset:
 	{
 		lua_pushfixed(L, sector->floor_yoffs);
 		return 1;
 	}
-	case sector_floorpic_angle: 
+	case sector_floorangle: 
 	{
 		lua_pushangle(L, sector->floorpic_angle);
 		return 1;
@@ -643,17 +643,17 @@ static int sector_get(lua_State *L)
 		lua_pushlstring(L, levelflat->name, i);
 		return 1;
 	}
-	case sector_ceiling_xoffs:
+	case sector_ceilingxoffset:
 	{
 		lua_pushfixed(L, sector->ceiling_xoffs);
 		return 1;
 	}
-	case sector_ceiling_yoffs:
+	case sector_ceilingyoffset:
 	{
 		lua_pushfixed(L, sector->ceiling_yoffs);
 		return 1;
 	}
-	case sector_ceilingpic_angle:
+	case sector_ceilingangle:
 	{
 		lua_pushangle(L, sector->ceilingpic_angle);
 		return 1;
@@ -793,25 +793,25 @@ static int sector_set(lua_State *L)
 	case sector_floorpic:
 		sector->floorpic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
 		break;
-	case sector_floor_xoffs:
+	case sector_floorxoffset:
 		sector->floor_xoffs = luaL_checkfixed(L, 3);
 		break;
-	case sector_floor_yoffs:
+	case sector_flooryoffset:
 		sector->floor_yoffs = luaL_checkfixed(L, 3);
 		break;
-	case sector_floorpic_angle:
+	case sector_floorangle:
 		sector->floorpic_angle = luaL_checkangle(L, 3);
 		break;		
 	case sector_ceilingpic:
 		sector->ceilingpic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
 		break;
-	case sector_ceiling_xoffs:
+	case sector_ceilingxoffset:
 		sector->ceiling_xoffs = luaL_checkfixed(L, 3);
 		break;
-	case sector_ceiling_yoffs:
+	case sector_ceilingyoffset:
 		sector->ceiling_yoffs = luaL_checkfixed(L, 3);
 		break;
-	case sector_ceilingpic_angle:
+	case sector_ceilingangle:
 		sector->ceilingpic_angle = luaL_checkangle(L, 3);
 		break;
 	case sector_lightlevel:
-- 
GitLab


From 3217984f55720b30a5f43df32cacdb6f89466ccf Mon Sep 17 00:00:00 2001
From: Ace Lite <lightacecz@gmail.com>
Date: Fri, 25 Nov 2022 23:01:27 +0000
Subject: [PATCH 066/518] c-side offset/angle variable renamed

---
 src/hardware/hw_main.c |  32 ++++++-------
 src/lua_maplib.c       |  24 +++++-----
 src/p_saveg.c          |  36 +++++++-------
 src/p_setup.c          | 106 ++++++++++++++++++++---------------------
 src/p_spec.c           |  32 ++++++-------
 src/r_bsp.c            |  58 +++++++++++-----------
 src/r_defs.h           |   8 ++--
 src/r_fps.c            |  16 +++----
 src/r_segs.c           |  12 ++---
 9 files changed, 162 insertions(+), 162 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 3cb7275a0b..2b376544a6 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -459,30 +459,30 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool
 	{
 		if (!isceiling) // it's a floor
 		{
-			scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatwidth;
-			scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatheight;
-			angle = FOFsector->floorpic_angle;
+			scrollx = FIXED_TO_FLOAT(FOFsector->floorxoffset)/fflatwidth;
+			scrolly = FIXED_TO_FLOAT(FOFsector->flooryoffset)/fflatheight;
+			angle = FOFsector->floorangle;
 		}
 		else // it's a ceiling
 		{
 			scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth;
 			scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight;
-			angle = FOFsector->ceilingpic_angle;
+			angle = FOFsector->ceilingangle;
 		}
 	}
 	else if (gl_frontsector)
 	{
 		if (!isceiling) // it's a floor
 		{
-			scrollx = FIXED_TO_FLOAT(gl_frontsector->floor_xoffs)/fflatwidth;
-			scrolly = FIXED_TO_FLOAT(gl_frontsector->floor_yoffs)/fflatheight;
-			angle = gl_frontsector->floorpic_angle;
+			scrollx = FIXED_TO_FLOAT(gl_frontsector->floorxoffset)/fflatwidth;
+			scrolly = FIXED_TO_FLOAT(gl_frontsector->flooryoffset)/fflatheight;
+			angle = gl_frontsector->floorangle;
 		}
 		else // it's a ceiling
 		{
 			scrollx = FIXED_TO_FLOAT(gl_frontsector->ceiling_xoffs)/fflatwidth;
 			scrolly = FIXED_TO_FLOAT(gl_frontsector->ceiling_yoffs)/fflatheight;
-			angle = gl_frontsector->ceilingpic_angle;
+			angle = gl_frontsector->ceilingangle;
 		}
 	}
 
@@ -2719,30 +2719,30 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling,
 	{
 		if (!isceiling) // it's a floor
 		{
-			scrollx = FIXED_TO_FLOAT(FOFsector->floor_xoffs)/fflatwidth;
-			scrolly = FIXED_TO_FLOAT(FOFsector->floor_yoffs)/fflatheight;
-			angle = FOFsector->floorpic_angle;
+			scrollx = FIXED_TO_FLOAT(FOFsector->floorxoffset)/fflatwidth;
+			scrolly = FIXED_TO_FLOAT(FOFsector->flooryoffset)/fflatheight;
+			angle = FOFsector->floorangle;
 		}
 		else // it's a ceiling
 		{
 			scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth;
 			scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight;
-			angle = FOFsector->ceilingpic_angle;
+			angle = FOFsector->ceilingangle;
 		}
 	}
 	else if (gl_frontsector)
 	{
 		if (!isceiling) // it's a floor
 		{
-			scrollx = FIXED_TO_FLOAT(gl_frontsector->floor_xoffs)/fflatwidth;
-			scrolly = FIXED_TO_FLOAT(gl_frontsector->floor_yoffs)/fflatheight;
-			angle = gl_frontsector->floorpic_angle;
+			scrollx = FIXED_TO_FLOAT(gl_frontsector->floorxoffset)/fflatwidth;
+			scrolly = FIXED_TO_FLOAT(gl_frontsector->flooryoffset)/fflatheight;
+			angle = gl_frontsector->floorangle;
 		}
 		else // it's a ceiling
 		{
 			scrollx = FIXED_TO_FLOAT(gl_frontsector->ceiling_xoffs)/fflatwidth;
 			scrolly = FIXED_TO_FLOAT(gl_frontsector->ceiling_yoffs)/fflatheight;
-			angle = gl_frontsector->ceilingpic_angle;
+			angle = gl_frontsector->ceilingangle;
 		}
 	}
 
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 7908a1d728..79909a6be9 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -621,17 +621,17 @@ static int sector_get(lua_State *L)
 	}
 	case sector_floorxoffset:
 	{
-		lua_pushfixed(L, sector->floor_xoffs);
+		lua_pushfixed(L, sector->floorxoffset);
 		return 1;
 	}
 	case sector_flooryoffset:
 	{
-		lua_pushfixed(L, sector->floor_yoffs);
+		lua_pushfixed(L, sector->flooryoffset);
 		return 1;
 	}
 	case sector_floorangle: 
 	{
-		lua_pushangle(L, sector->floorpic_angle);
+		lua_pushangle(L, sector->floorangle);
 		return 1;
 	}	
 	case sector_ceilingpic: // ceilingpic
@@ -645,17 +645,17 @@ static int sector_get(lua_State *L)
 	}
 	case sector_ceilingxoffset:
 	{
-		lua_pushfixed(L, sector->ceiling_xoffs);
+		lua_pushfixed(L, sector->ceilingxoffset);
 		return 1;
 	}
 	case sector_ceilingyoffset:
 	{
-		lua_pushfixed(L, sector->ceiling_yoffs);
+		lua_pushfixed(L, sector->ceilingyoffset);
 		return 1;
 	}
 	case sector_ceilingangle:
 	{
-		lua_pushangle(L, sector->ceilingpic_angle);
+		lua_pushangle(L, sector->ceilingangle);
 		return 1;
 	}	
 	case sector_lightlevel:
@@ -794,25 +794,25 @@ static int sector_set(lua_State *L)
 		sector->floorpic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
 		break;
 	case sector_floorxoffset:
-		sector->floor_xoffs = luaL_checkfixed(L, 3);
+		sector->floorxoffset = luaL_checkfixed(L, 3);
 		break;
 	case sector_flooryoffset:
-		sector->floor_yoffs = luaL_checkfixed(L, 3);
+		sector->flooryoffset = luaL_checkfixed(L, 3);
 		break;
 	case sector_floorangle:
-		sector->floorpic_angle = luaL_checkangle(L, 3);
+		sector->floorangle = luaL_checkangle(L, 3);
 		break;		
 	case sector_ceilingpic:
 		sector->ceilingpic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
 		break;
 	case sector_ceilingxoffset:
-		sector->ceiling_xoffs = luaL_checkfixed(L, 3);
+		sector->ceilingxoffset = luaL_checkfixed(L, 3);
 		break;
 	case sector_ceilingyoffset:
-		sector->ceiling_yoffs = luaL_checkfixed(L, 3);
+		sector->ceilingyoffset = luaL_checkfixed(L, 3);
 		break;
 	case sector_ceilingangle:
-		sector->ceilingpic_angle = luaL_checkangle(L, 3);
+		sector->ceilingangle = luaL_checkangle(L, 3);
 		break;
 	case sector_lightlevel:
 		sector->lightlevel = (INT16)luaL_checkinteger(L, 3);
diff --git a/src/p_saveg.c b/src/p_saveg.c
index ce7353b95a..464372d688 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1022,17 +1022,17 @@ static void ArchiveSectors(void)
 		if (ss->special != spawnss->special)
 			diff |= SD_SPECIAL;
 
-		if (ss->floor_xoffs != spawnss->floor_xoffs)
+		if (ss->floorxoffset != spawnss->floorxoffset)
 			diff2 |= SD_FXOFFS;
-		if (ss->floor_yoffs != spawnss->floor_yoffs)
+		if (ss->flooryoffset != spawnss->flooryoffset)
 			diff2 |= SD_FYOFFS;
-		if (ss->ceiling_xoffs != spawnss->ceiling_xoffs)
+		if (ss->ceilingxoffset != spawnss->ceilingxoffset)
 			diff2 |= SD_CXOFFS;
-		if (ss->ceiling_yoffs != spawnss->ceiling_yoffs)
+		if (ss->ceilingyoffset != spawnss->ceilingyoffset)
 			diff2 |= SD_CYOFFS;
-		if (ss->floorpic_angle != spawnss->floorpic_angle)
+		if (ss->floorangle != spawnss->floorangle)
 			diff2 |= SD_FLOORANG;
-		if (ss->ceilingpic_angle != spawnss->ceilingpic_angle)
+		if (ss->ceilingangle != spawnss->ceilingangle)
 			diff2 |= SD_CEILANG;
 
 		if (!Tag_Compare(&ss->tags, &spawnss->tags))
@@ -1095,17 +1095,17 @@ static void ArchiveSectors(void)
 			if (diff & SD_SPECIAL)
 				WRITEINT16(save_p, ss->special);
 			if (diff2 & SD_FXOFFS)
-				WRITEFIXED(save_p, ss->floor_xoffs);
+				WRITEFIXED(save_p, ss->floorxoffset);
 			if (diff2 & SD_FYOFFS)
-				WRITEFIXED(save_p, ss->floor_yoffs);
+				WRITEFIXED(save_p, ss->flooryoffset);
 			if (diff2 & SD_CXOFFS)
-				WRITEFIXED(save_p, ss->ceiling_xoffs);
+				WRITEFIXED(save_p, ss->ceilingxoffset);
 			if (diff2 & SD_CYOFFS)
-				WRITEFIXED(save_p, ss->ceiling_yoffs);
+				WRITEFIXED(save_p, ss->ceilingyoffset);
 			if (diff2 & SD_FLOORANG)
-				WRITEANGLE(save_p, ss->floorpic_angle);
+				WRITEANGLE(save_p, ss->floorangle);
 			if (diff2 & SD_CEILANG)
-				WRITEANGLE(save_p, ss->ceilingpic_angle);
+				WRITEANGLE(save_p, ss->ceilingangle);
 			if (diff2 & SD_TAG)
 			{
 				WRITEUINT32(save_p, ss->tags.count);
@@ -1196,17 +1196,17 @@ static void UnArchiveSectors(void)
 			sectors[i].special = READINT16(save_p);
 
 		if (diff2 & SD_FXOFFS)
-			sectors[i].floor_xoffs = READFIXED(save_p);
+			sectors[i].floorxoffset = READFIXED(save_p);
 		if (diff2 & SD_FYOFFS)
-			sectors[i].floor_yoffs = READFIXED(save_p);
+			sectors[i].flooryoffset = READFIXED(save_p);
 		if (diff2 & SD_CXOFFS)
-			sectors[i].ceiling_xoffs = READFIXED(save_p);
+			sectors[i].ceilingxoffset = READFIXED(save_p);
 		if (diff2 & SD_CYOFFS)
-			sectors[i].ceiling_yoffs = READFIXED(save_p);
+			sectors[i].ceilingyoffset = READFIXED(save_p);
 		if (diff2 & SD_FLOORANG)
-			sectors[i].floorpic_angle  = READANGLE(save_p);
+			sectors[i].floorangle  = READANGLE(save_p);
 		if (diff2 & SD_CEILANG)
-			sectors[i].ceilingpic_angle = READANGLE(save_p);
+			sectors[i].ceilingangle = READANGLE(save_p);
 		if (diff2 & SD_TAG)
 		{
 			size_t ncount = READUINT32(save_p);
diff --git a/src/p_setup.c b/src/p_setup.c
index deb308da22..20790a48b8 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1049,10 +1049,10 @@ static void P_LoadSectors(UINT8 *data)
 		ss->special = SHORT(ms->special);
 		Tag_FSet(&ss->tags, SHORT(ms->tag));
 
-		ss->floor_xoffs = ss->floor_yoffs = 0;
-		ss->ceiling_xoffs = ss->ceiling_yoffs = 0;
+		ss->floorxoffset = ss->flooryoffset = 0;
+		ss->ceilingxoffset = ss->ceilingyoffset = 0;
 
-		ss->floorpic_angle = ss->ceilingpic_angle = 0;
+		ss->floorangle = ss->ceilingangle = 0;
 
 		ss->floorlightlevel = ss->ceilinglightlevel = 0;
 		ss->floorlightabsolute = ss->ceilinglightabsolute = false;
@@ -1578,19 +1578,19 @@ static void ParseTextmapSectorParameter(UINT32 i, const char *param, const char
 			if ((id = strchr(id, ' ')))
 				id++;
 		}
-	}
+	}	
 	else if (fastcmp(param, "xpanningfloor"))
-		sectors[i].floor_xoffs = FLOAT_TO_FIXED(atof(val));
+		sectors[i].floorxoffset = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "ypanningfloor"))
-		sectors[i].floor_yoffs = FLOAT_TO_FIXED(atof(val));
+		sectors[i].flooryoffset = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "xpanningceiling"))
-		sectors[i].ceiling_xoffs = FLOAT_TO_FIXED(atof(val));
+		sectors[i].ceilingxoffset = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "ypanningceiling"))
-		sectors[i].ceiling_yoffs = FLOAT_TO_FIXED(atof(val));
+		sectors[i].ceilingyoffset = FLOAT_TO_FIXED(atof(val));
 	else if (fastcmp(param, "rotationfloor"))
-		sectors[i].floorpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
+		sectors[i].floorangle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
 	else if (fastcmp(param, "rotationceiling"))
-		sectors[i].ceilingpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
+		sectors[i].ceilingangle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
 	else if (fastcmp(param, "floorplane_a"))
 	{
 		textmap_planefloor.defined |= PD_A;
@@ -1959,47 +1959,47 @@ static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, cons
  */
 static void TextmapFixFlatOffsets(sector_t *sec)
 {
-	if (sec->floorpic_angle)
+	if (sec->floorangle)
 	{
-		fixed_t pc = FINECOSINE(sec->floorpic_angle>>ANGLETOFINESHIFT);
-		fixed_t ps = FINESINE  (sec->floorpic_angle>>ANGLETOFINESHIFT);
-		fixed_t xoffs = sec->floor_xoffs;
-		fixed_t yoffs = sec->floor_yoffs;
-		sec->floor_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
-		sec->floor_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+		fixed_t pc = FINECOSINE(sec->floorangle>>ANGLETOFINESHIFT);
+		fixed_t ps = FINESINE  (sec->floorangle>>ANGLETOFINESHIFT);
+		fixed_t xoffs = sec->floorxoffset;
+		fixed_t yoffs = sec->flooryoffset;
+		sec->floorxoffset = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+		sec->flooryoffset = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
 	}
 
-	if (sec->ceilingpic_angle)
+	if (sec->ceilingangle)
 	{
-		fixed_t pc = FINECOSINE(sec->ceilingpic_angle>>ANGLETOFINESHIFT);
-		fixed_t ps = FINESINE  (sec->ceilingpic_angle>>ANGLETOFINESHIFT);
-		fixed_t xoffs = sec->ceiling_xoffs;
-		fixed_t yoffs = sec->ceiling_yoffs;
-		sec->ceiling_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
-		sec->ceiling_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+		fixed_t pc = FINECOSINE(sec->ceilingangle>>ANGLETOFINESHIFT);
+		fixed_t ps = FINESINE  (sec->ceilingangle>>ANGLETOFINESHIFT);
+		fixed_t xoffs = sec->ceilingxoffset;
+		fixed_t yoffs = sec->ceilingyoffset;
+		sec->ceilingxoffset = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+		sec->ceilingyoffset = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
 	}
 }
 
 static void TextmapUnfixFlatOffsets(sector_t *sec)
 {
-	if (sec->floorpic_angle)
+	if (sec->floorangle)
 	{
-		fixed_t pc = FINECOSINE(sec->floorpic_angle >> ANGLETOFINESHIFT);
-		fixed_t ps = FINESINE(sec->floorpic_angle >> ANGLETOFINESHIFT);
-		fixed_t xoffs = sec->floor_xoffs;
-		fixed_t yoffs = sec->floor_yoffs;
-		sec->floor_xoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
-		sec->floor_yoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+		fixed_t pc = FINECOSINE(sec->floorangle >> ANGLETOFINESHIFT);
+		fixed_t ps = FINESINE(sec->floorangle >> ANGLETOFINESHIFT);
+		fixed_t xoffs = sec->floorxoffset;
+		fixed_t yoffs = sec->flooryoffset;
+		sec->floorxoffset = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+		sec->flooryoffset = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
 	}
 
-	if (sec->ceilingpic_angle)
+	if (sec->ceilingangle)
 	{
-		fixed_t pc = FINECOSINE(sec->ceilingpic_angle >> ANGLETOFINESHIFT);
-		fixed_t ps = FINESINE(sec->ceilingpic_angle >> ANGLETOFINESHIFT);
-		fixed_t xoffs = sec->ceiling_xoffs;
-		fixed_t yoffs = sec->ceiling_yoffs;
-		sec->ceiling_xoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
-		sec->ceiling_yoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+		fixed_t pc = FINECOSINE(sec->ceilingangle >> ANGLETOFINESHIFT);
+		fixed_t ps = FINESINE(sec->ceilingangle >> ANGLETOFINESHIFT);
+		fixed_t xoffs = sec->ceilingxoffset;
+		fixed_t yoffs = sec->ceilingyoffset;
+		sec->ceilingxoffset = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+		sec->ceilingyoffset = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
 	}
 }
 
@@ -2492,18 +2492,18 @@ static void P_WriteTextmap(void)
 		}
 		sector_t tempsec = wsectors[i];
 		TextmapUnfixFlatOffsets(&tempsec);
-		if (tempsec.floor_xoffs != 0)
-			fprintf(f, "xpanningfloor = %f;\n", FIXED_TO_FLOAT(tempsec.floor_xoffs));
-		if (tempsec.floor_yoffs != 0)
-			fprintf(f, "ypanningfloor = %f;\n", FIXED_TO_FLOAT(tempsec.floor_yoffs));
-		if (tempsec.ceiling_xoffs != 0)
-			fprintf(f, "xpanningceiling = %f;\n", FIXED_TO_FLOAT(tempsec.ceiling_xoffs));
-		if (tempsec.ceiling_yoffs != 0)
-			fprintf(f, "ypanningceiling = %f;\n", FIXED_TO_FLOAT(tempsec.ceiling_yoffs));
-		if (wsectors[i].floorpic_angle != 0)
-			fprintf(f, "rotationfloor = %f;\n", FIXED_TO_FLOAT(AngleFixed(wsectors[i].floorpic_angle)));
-		if (wsectors[i].ceilingpic_angle != 0)
-			fprintf(f, "rotationceiling = %f;\n", FIXED_TO_FLOAT(AngleFixed(wsectors[i].ceilingpic_angle)));
+		if (tempsec.floorxoffset != 0)
+			fprintf(f, "xpanningfloor = %f;\n", FIXED_TO_FLOAT(tempsec.floorxoffset));
+		if (tempsec.flooryoffset != 0)
+			fprintf(f, "ypanningfloor = %f;\n", FIXED_TO_FLOAT(tempsec.flooryoffset));
+		if (tempsec.ceilingxoffset != 0)
+			fprintf(f, "xpanningceiling = %f;\n", FIXED_TO_FLOAT(tempsec.ceilingxoffset));
+		if (tempsec.ceilingyoffset != 0)
+			fprintf(f, "ypanningceiling = %f;\n", FIXED_TO_FLOAT(tempsec.ceilingyoffset));
+		if (wsectors[i].floorangle != 0)
+			fprintf(f, "rotationfloor = %f;\n", FIXED_TO_FLOAT(AngleFixed(wsectors[i].floorangle)));
+		if (wsectors[i].ceilingangle != 0)
+			fprintf(f, "rotationceiling = %f;\n", FIXED_TO_FLOAT(AngleFixed(wsectors[i].ceilingangle)));
         if (wsectors[i].extra_colormap)
 		{
 			INT32 lightcolor = P_RGBAToColor(wsectors[i].extra_colormap->rgba);
@@ -2708,10 +2708,10 @@ static void P_LoadTextmap(void)
 		sc->special = 0;
 		Tag_FSet(&sc->tags, 0);
 
-		sc->floor_xoffs = sc->floor_yoffs = 0;
-		sc->ceiling_xoffs = sc->ceiling_yoffs = 0;
+		sc->floorxoffset = sc->flooryoffset = 0;
+		sc->ceilingxoffset = sc->ceilingyoffset = 0;
 
-		sc->floorpic_angle = sc->ceilingpic_angle = 0;
+		sc->floorangle = sc->ceilingangle = 0;
 
 		sc->floorlightlevel = sc->ceilinglightlevel = 0;
 		sc->floorlightabsolute = sc->ceilinglightabsolute = false;
diff --git a/src/p_spec.c b/src/p_spec.c
index 82337d2f6b..d781ea7d71 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -5570,17 +5570,17 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, I
 	fflr->target = sec;
 	fflr->bottomheight = &sec2->floorheight;
 	fflr->bottompic = &sec2->floorpic;
-	fflr->bottomxoffs = &sec2->floor_xoffs;
-	fflr->bottomyoffs = &sec2->floor_yoffs;
-	fflr->bottomangle = &sec2->floorpic_angle;
+	fflr->bottomxoffs = &sec2->floorxoffset;
+	fflr->bottomyoffs = &sec2->flooryoffset;
+	fflr->bottomangle = &sec2->floorangle;
 
 	// Add the ceiling
 	fflr->topheight = &sec2->ceilingheight;
 	fflr->toppic = &sec2->ceilingpic;
 	fflr->toplightlevel = &sec2->lightlevel;
-	fflr->topxoffs = &sec2->ceiling_xoffs;
-	fflr->topyoffs = &sec2->ceiling_yoffs;
-	fflr->topangle = &sec2->ceilingpic_angle;
+	fflr->topxoffs = &sec2->ceilingxoffset;
+	fflr->topyoffs = &sec2->ceilingyoffset;
+	fflr->topangle = &sec2->ceilingangle;
 
 	// Add slopes
 	fflr->t_slope = &sec2->c_slope;
@@ -6098,16 +6098,16 @@ void P_ApplyFlatAlignment(sector_t *sector, angle_t flatangle, fixed_t xoffs, fi
 {
 	if (floor)
 	{
-		sector->floorpic_angle = flatangle;
-		sector->floor_xoffs += xoffs;
-		sector->floor_yoffs += yoffs;
+		sector->floorangle = flatangle;
+		sector->floorxoffset += xoffs;
+		sector->flooryoffset += yoffs;
 	}
 
 	if (ceiling)
 	{
-		sector->ceilingpic_angle = flatangle;
-		sector->ceiling_xoffs += xoffs;
-		sector->ceiling_yoffs += yoffs;
+		sector->ceilingangle = flatangle;
+		sector->ceilingxoffset += xoffs;
+		sector->ceilingyoffset += yoffs;
 	}
 
 }
@@ -7349,14 +7349,14 @@ void T_Scroll(scroll_t *s)
 
 		case sc_floor: // scroll floor texture
 			sec = sectors + s->affectee;
-			sec->floor_xoffs += dx;
-			sec->floor_yoffs += dy;
+			sec->floorxoffset += dx;
+			sec->flooryoffset += dy;
 			break;
 
 		case sc_ceiling: // scroll ceiling texture
 			sec = sectors + s->affectee;
-			sec->ceiling_xoffs += dx;
-			sec->ceiling_yoffs += dy;
+			sec->ceilingxoffset += dx;
+			sec->ceilingyoffset += dy;
 			break;
 
 		case sc_carry:
diff --git a/src/r_bsp.c b/src/r_bsp.c
index bf238a4357..129c6ba008 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -275,9 +275,9 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
 			tempsec->ceilingheight = s->floorheight - 1, !back)) || viewz <= s->floorheight)
 		{ // head-below-floor hack
 			tempsec->floorpic = s->floorpic;
-			tempsec->floor_xoffs = s->floor_xoffs;
-			tempsec->floor_yoffs = s->floor_yoffs;
-			tempsec->floorpic_angle = s->floorpic_angle;
+			tempsec->floorxoffset = s->floorxoffset;
+			tempsec->flooryoffset = s->flooryoffset;
+			tempsec->floorangle = s->floorangle;
 
 			if (underwater)
 			{
@@ -285,16 +285,16 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
 				{
 					tempsec->floorheight = tempsec->ceilingheight+1;
 					tempsec->ceilingpic = tempsec->floorpic;
-					tempsec->ceiling_xoffs = tempsec->floor_xoffs;
-					tempsec->ceiling_yoffs = tempsec->floor_yoffs;
-					tempsec->ceilingpic_angle = tempsec->floorpic_angle;
+					tempsec->ceilingxoffset = tempsec->floorxoffset;
+					tempsec->ceilingyoffset = tempsec->flooryoffset;
+					tempsec->ceilingangle = tempsec->floorangle;
 				}
 				else
 				{
 					tempsec->ceilingpic = s->ceilingpic;
-					tempsec->ceiling_xoffs = s->ceiling_xoffs;
-					tempsec->ceiling_yoffs = s->ceiling_yoffs;
-					tempsec->ceilingpic_angle = s->ceilingpic_angle;
+					tempsec->ceilingxoffset = s->ceilingxoffset;
+					tempsec->ceilingyoffset = s->ceilingyoffset;
+					tempsec->ceilingangle = s->ceilingangle;
 				}
 			}
 
@@ -315,25 +315,25 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel,
 			tempsec->floorheight = s->ceilingheight + 1;
 
 			tempsec->floorpic = tempsec->ceilingpic = s->ceilingpic;
-			tempsec->floor_xoffs = tempsec->ceiling_xoffs = s->ceiling_xoffs;
-			tempsec->floor_yoffs = tempsec->ceiling_yoffs = s->ceiling_yoffs;
-			tempsec->floorpic_angle = tempsec->ceilingpic_angle = s->ceilingpic_angle;
+			tempsec->floorxoffset = tempsec->ceilingxoffset = s->ceilingxoffset;
+			tempsec->flooryoffset = tempsec->ceilingyoffset = s->ceilingyoffset;
+			tempsec->floorangle = tempsec->ceilingangle = s->ceilingangle;
 
 			if (s->floorpic == skyflatnum) // SKYFIX?
 			{
 				tempsec->ceilingheight = tempsec->floorheight-1;
 				tempsec->floorpic = tempsec->ceilingpic;
-				tempsec->floor_xoffs = tempsec->ceiling_xoffs;
-				tempsec->floor_yoffs = tempsec->ceiling_yoffs;
-				tempsec->floorpic_angle = tempsec->ceilingpic_angle;
+				tempsec->floorxoffset = tempsec->ceilingxoffset;
+				tempsec->flooryoffset = tempsec->ceilingyoffset;
+				tempsec->floorangle = tempsec->ceilingangle;
 			}
 			else
 			{
 				tempsec->ceilingheight = sec->ceilingheight;
 				tempsec->floorpic = s->floorpic;
-				tempsec->floor_xoffs = s->floor_xoffs;
-				tempsec->floor_yoffs = s->floor_yoffs;
-				tempsec->floorpic_angle = s->floorpic_angle;
+				tempsec->floorxoffset = s->floorxoffset;
+				tempsec->flooryoffset = s->flooryoffset;
+				tempsec->floorangle = s->floorangle;
 			}
 
 			tempsec->lightlevel = s->lightlevel;
@@ -363,12 +363,12 @@ boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back)
 		&& back->lightlevel == front->lightlevel
 		&& !line->sidedef->midtexture
 		// Check offsets too!
-		&& back->floor_xoffs == front->floor_xoffs
-		&& back->floor_yoffs == front->floor_yoffs
-		&& back->floorpic_angle == front->floorpic_angle
-		&& back->ceiling_xoffs == front->ceiling_xoffs
-		&& back->ceiling_yoffs == front->ceiling_yoffs
-		&& back->ceilingpic_angle == front->ceilingpic_angle
+		&& back->floorxoffset == front->floorxoffset
+		&& back->flooryoffset == front->flooryoffset
+		&& back->floorangle == front->floorangle
+		&& back->ceilingxoffset == front->ceilingxoffset
+		&& back->ceilingyoffset == front->ceilingyoffset
+		&& back->ceilingangle == front->ceilingangle
 		// Consider altered lighting.
 		&& back->floorlightlevel == front->floorlightlevel
 		&& back->floorlightabsolute == front->floorlightabsolute
@@ -909,7 +909,7 @@ static void R_Subsector(size_t num)
 		|| (frontsector->heightsec != -1 && sectors[frontsector->heightsec].ceilingpic == skyflatnum))
 	{
 		floorplane = R_FindPlane(frontsector->floorheight, frontsector->floorpic, floorlightlevel,
-			frontsector->floor_xoffs, frontsector->floor_yoffs, frontsector->floorpic_angle, floorcolormap, NULL, NULL, frontsector->f_slope);
+			frontsector->floorxoffset, frontsector->flooryoffset, frontsector->floorangle, floorcolormap, NULL, NULL, frontsector->f_slope);
 	}
 	else
 		floorplane = NULL;
@@ -919,7 +919,7 @@ static void R_Subsector(size_t num)
 		|| (frontsector->heightsec != -1 && sectors[frontsector->heightsec].floorpic == skyflatnum))
 	{
 		ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic,
-			ceilinglightlevel, frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle,
+			ceilinglightlevel, frontsector->ceilingxoffset, frontsector->ceilingyoffset, frontsector->ceilingangle,
 			ceilingcolormap, NULL, NULL, frontsector->c_slope);
 	}
 	else
@@ -1033,8 +1033,8 @@ static void R_Subsector(size_t num)
 			{
 				light = R_GetPlaneLight(frontsector, polysec->floorheight, viewz < polysec->floorheight);
 				ffloor[numffloors].plane = R_FindPlane(polysec->floorheight, polysec->floorpic,
-					(light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->floor_xoffs, polysec->floor_yoffs,
-					polysec->floorpic_angle-po->angle,
+					(light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->floorxoffset, polysec->flooryoffset,
+					polysec->floorangle-po->angle,
 					(light == -1 ? frontsector->extra_colormap : *frontsector->lightlist[light].extra_colormap), NULL, po,
 					NULL); // will ffloors be slopable eventually?
 
@@ -1057,7 +1057,7 @@ static void R_Subsector(size_t num)
 			{
 				light = R_GetPlaneLight(frontsector, polysec->floorheight, viewz < polysec->floorheight);
 				ffloor[numffloors].plane = R_FindPlane(polysec->ceilingheight, polysec->ceilingpic,
-					(light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->ceiling_xoffs, polysec->ceiling_yoffs, polysec->ceilingpic_angle-po->angle,
+					(light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->ceilingxoffset, polysec->ceilingyoffset, polysec->ceilingangle-po->angle,
 					(light == -1 ? frontsector->extra_colormap : *frontsector->lightlist[light].extra_colormap), NULL, po,
 					NULL); // will ffloors be slopable eventually?
 
diff --git a/src/r_defs.h b/src/r_defs.h
index dbede806e2..7da162de7b 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -423,12 +423,12 @@ typedef struct sector_s
 	void *fadecolormapdata; // fade colormap thinker
 
 	// floor and ceiling texture offsets
-	fixed_t floor_xoffs, floor_yoffs;
-	fixed_t ceiling_xoffs, ceiling_yoffs;
+	fixed_t floorxoffset, flooryoffset;
+	fixed_t ceilingxoffset, ceilingyoffset;
 
 	// flat angle
-	angle_t floorpic_angle;
-	angle_t ceilingpic_angle;
+	angle_t floorangle;
+	angle_t ceilingangle;
 
 	INT32 heightsec; // other sector, or -1 if no other sector
 	INT32 camsec; // used for camera clipping
diff --git a/src/r_fps.c b/src/r_fps.c
index 2d30c9f019..3d7d3f7ae8 100644
--- a/src/r_fps.c
+++ b/src/r_fps.c
@@ -426,8 +426,8 @@ void R_CreateInterpolator_SectorScroll(thinker_t *thinker, sector_t *sector, boo
 	}
 	else
 	{
-		interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs = sector->floor_xoffs;
-		interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs = sector->floor_yoffs;
+		interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs = sector->floorxoffset;
+		interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs = sector->flooryoffset;
 	}
 }
 
@@ -490,9 +490,9 @@ static void UpdateLevelInterpolatorState(levelinterpolator_t *interp)
 		break;
 	case LVLINTERP_SectorScroll:
 		interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs;
-		interp->sectorscroll.bakxoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceiling_xoffs : interp->sectorscroll.sector->floor_xoffs;
+		interp->sectorscroll.bakxoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceiling_xoffs : interp->sectorscroll.sector->floorxoffset;
 		interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs;
-		interp->sectorscroll.bakyoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceiling_yoffs : interp->sectorscroll.sector->floor_yoffs;
+		interp->sectorscroll.bakyoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceiling_yoffs : interp->sectorscroll.sector->flooryoffset;
 		break;
 	case LVLINTERP_SideScroll:
 		interp->sidescroll.oldtextureoffset = interp->sidescroll.baktextureoffset;
@@ -583,8 +583,8 @@ void R_ApplyLevelInterpolators(fixed_t frac)
 			}
 			else
 			{
-				interp->sectorscroll.sector->floor_xoffs = R_LerpFixed(interp->sectorscroll.oldxoffs, interp->sectorscroll.bakxoffs, frac);
-				interp->sectorscroll.sector->floor_yoffs = R_LerpFixed(interp->sectorscroll.oldyoffs, interp->sectorscroll.bakyoffs, frac);
+				interp->sectorscroll.sector->floorxoffset = R_LerpFixed(interp->sectorscroll.oldxoffs, interp->sectorscroll.bakxoffs, frac);
+				interp->sectorscroll.sector->flooryoffset = R_LerpFixed(interp->sectorscroll.oldyoffs, interp->sectorscroll.bakyoffs, frac);
 			}
 			break;
 		case LVLINTERP_SideScroll:
@@ -638,8 +638,8 @@ void R_RestoreLevelInterpolators(void)
 			}
 			else
 			{
-				interp->sectorscroll.sector->floor_xoffs = interp->sectorscroll.bakxoffs;
-				interp->sectorscroll.sector->floor_yoffs = interp->sectorscroll.bakyoffs;
+				interp->sectorscroll.sector->floorxoffset = interp->sectorscroll.bakxoffs;
+				interp->sectorscroll.sector->flooryoffset = interp->sectorscroll.bakyoffs;
 			}
 			break;
 		case LVLINTERP_SideScroll:
diff --git a/src/r_segs.c b/src/r_segs.c
index 43a7f945fd..6815240542 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -1906,9 +1906,9 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 			|| backsector->floorpic != frontsector->floorpic
 			|| backsector->lightlevel != frontsector->lightlevel
 			//SoM: 3/22/2000: Check floor x and y offsets.
-			|| backsector->floor_xoffs != frontsector->floor_xoffs
-			|| backsector->floor_yoffs != frontsector->floor_yoffs
-			|| backsector->floorpic_angle != frontsector->floorpic_angle
+			|| backsector->floorxoffset != frontsector->floorxoffset
+			|| backsector->flooryoffset != frontsector->flooryoffset
+			|| backsector->floorangle != frontsector->floorangle
 			//SoM: 3/22/2000: Prevents bleeding.
 			|| (frontsector->heightsec != -1 && frontsector->floorpic != skyflatnum)
 			|| backsector->floorlightlevel != frontsector->floorlightlevel
@@ -1939,9 +1939,9 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 			|| backsector->ceilingpic != frontsector->ceilingpic
 			|| backsector->lightlevel != frontsector->lightlevel
 			//SoM: 3/22/2000: Check floor x and y offsets.
-			|| backsector->ceiling_xoffs != frontsector->ceiling_xoffs
-			|| backsector->ceiling_yoffs != frontsector->ceiling_yoffs
-			|| backsector->ceilingpic_angle != frontsector->ceilingpic_angle
+			|| backsector->ceilingxoffset != frontsector->ceilingxoffset
+			|| backsector->ceilingyoffset != frontsector->ceilingyoffset
+			|| backsector->ceilingangle != frontsector->ceilingangle
 			//SoM: 3/22/2000: Prevents bleeding.
 			|| (frontsector->heightsec != -1 && frontsector->ceilingpic != skyflatnum)
 			|| backsector->ceilinglightlevel != frontsector->ceilinglightlevel
-- 
GitLab


From c5daa248d16f803cf16b0836980cadf973e758e9 Mon Sep 17 00:00:00 2001
From: Ace Lite <lightacecz@gmail.com>
Date: Fri, 25 Nov 2022 23:06:07 +0000
Subject: [PATCH 067/518] r_fps.c ceiling_y/xoffs renamed

---
 src/r_fps.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/r_fps.c b/src/r_fps.c
index 3d7d3f7ae8..661da8ba2a 100644
--- a/src/r_fps.c
+++ b/src/r_fps.c
@@ -421,8 +421,8 @@ void R_CreateInterpolator_SectorScroll(thinker_t *thinker, sector_t *sector, boo
 	interp->sectorscroll.ceiling = ceiling;
 	if (ceiling)
 	{
-		interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs = sector->ceiling_xoffs;
-		interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs = sector->ceiling_yoffs;
+		interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs = sector->ceilingxoffset;
+		interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs = sector->ceilingyoffset;
 	}
 	else
 	{
@@ -490,9 +490,9 @@ static void UpdateLevelInterpolatorState(levelinterpolator_t *interp)
 		break;
 	case LVLINTERP_SectorScroll:
 		interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs;
-		interp->sectorscroll.bakxoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceiling_xoffs : interp->sectorscroll.sector->floorxoffset;
+		interp->sectorscroll.bakxoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceilingxoffset : interp->sectorscroll.sector->floorxoffset;
 		interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs;
-		interp->sectorscroll.bakyoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceiling_yoffs : interp->sectorscroll.sector->flooryoffset;
+		interp->sectorscroll.bakyoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceilingyoffset : interp->sectorscroll.sector->flooryoffset;
 		break;
 	case LVLINTERP_SideScroll:
 		interp->sidescroll.oldtextureoffset = interp->sidescroll.baktextureoffset;
@@ -578,8 +578,8 @@ void R_ApplyLevelInterpolators(fixed_t frac)
 		case LVLINTERP_SectorScroll:
 			if (interp->sectorscroll.ceiling)
 			{
-				interp->sectorscroll.sector->ceiling_xoffs = R_LerpFixed(interp->sectorscroll.oldxoffs, interp->sectorscroll.bakxoffs, frac);
-				interp->sectorscroll.sector->ceiling_yoffs = R_LerpFixed(interp->sectorscroll.oldyoffs, interp->sectorscroll.bakyoffs, frac);
+				interp->sectorscroll.sector->ceilingxoffset = R_LerpFixed(interp->sectorscroll.oldxoffs, interp->sectorscroll.bakxoffs, frac);
+				interp->sectorscroll.sector->ceilingyoffset = R_LerpFixed(interp->sectorscroll.oldyoffs, interp->sectorscroll.bakyoffs, frac);
 			}
 			else
 			{
@@ -633,8 +633,8 @@ void R_RestoreLevelInterpolators(void)
 		case LVLINTERP_SectorScroll:
 			if (interp->sectorscroll.ceiling)
 			{
-				interp->sectorscroll.sector->ceiling_xoffs = interp->sectorscroll.bakxoffs;
-				interp->sectorscroll.sector->ceiling_yoffs = interp->sectorscroll.bakyoffs;
+				interp->sectorscroll.sector->ceilingxoffset = interp->sectorscroll.bakxoffs;
+				interp->sectorscroll.sector->ceilingyoffset = interp->sectorscroll.bakyoffs;
 			}
 			else
 			{
-- 
GitLab


From 656f7f94acf7390a17aff9b4a4fd92771ea5953b Mon Sep 17 00:00:00 2001
From: Ace Lite <lightacecz@gmail.com>
Date: Fri, 25 Nov 2022 23:06:22 +0000
Subject: [PATCH 068/518] hw_main.c ceiling_y/xoffs renamed

---
 src/hardware/hw_main.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 2b376544a6..c1043f8bbd 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -465,8 +465,8 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool
 		}
 		else // it's a ceiling
 		{
-			scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth;
-			scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight;
+			scrollx = FIXED_TO_FLOAT(FOFsector->ceilingxoffset)/fflatwidth;
+			scrolly = FIXED_TO_FLOAT(FOFsector->ceilingyoffset)/fflatheight;
 			angle = FOFsector->ceilingangle;
 		}
 	}
@@ -480,8 +480,8 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool
 		}
 		else // it's a ceiling
 		{
-			scrollx = FIXED_TO_FLOAT(gl_frontsector->ceiling_xoffs)/fflatwidth;
-			scrolly = FIXED_TO_FLOAT(gl_frontsector->ceiling_yoffs)/fflatheight;
+			scrollx = FIXED_TO_FLOAT(gl_frontsector->ceilingxoffset)/fflatwidth;
+			scrolly = FIXED_TO_FLOAT(gl_frontsector->ceilingyoffset)/fflatheight;
 			angle = gl_frontsector->ceilingangle;
 		}
 	}
@@ -2725,8 +2725,8 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling,
 		}
 		else // it's a ceiling
 		{
-			scrollx = FIXED_TO_FLOAT(FOFsector->ceiling_xoffs)/fflatwidth;
-			scrolly = FIXED_TO_FLOAT(FOFsector->ceiling_yoffs)/fflatheight;
+			scrollx = FIXED_TO_FLOAT(FOFsector->ceilingxoffset)/fflatwidth;
+			scrolly = FIXED_TO_FLOAT(FOFsector->ceilingyoffset)/fflatheight;
 			angle = FOFsector->ceilingangle;
 		}
 	}
@@ -2740,8 +2740,8 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling,
 		}
 		else // it's a ceiling
 		{
-			scrollx = FIXED_TO_FLOAT(gl_frontsector->ceiling_xoffs)/fflatwidth;
-			scrolly = FIXED_TO_FLOAT(gl_frontsector->ceiling_yoffs)/fflatheight;
+			scrollx = FIXED_TO_FLOAT(gl_frontsector->ceilingxoffset)/fflatwidth;
+			scrolly = FIXED_TO_FLOAT(gl_frontsector->ceilingyoffset)/fflatheight;
 			angle = gl_frontsector->ceilingangle;
 		}
 	}
-- 
GitLab


From da9786b593040200fe56f6771e58ed6a16a01ed2 Mon Sep 17 00:00:00 2001
From: Ace Lite <lightacecz@gmail.com>
Date: Fri, 25 Nov 2022 23:16:11 +0000
Subject: [PATCH 069/518] exposed floorlightsec, ceilinglightsec variables.

---
 src/lua_maplib.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 79909a6be9..d3a00782b7 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -43,8 +43,10 @@ enum sector_e {
 	sector_lightlevel,
 	sector_floorlightlevel,
 	sector_floorlightabsolute,
+	sector_floorlightsec,	
 	sector_ceilinglightlevel,
 	sector_ceilinglightabsolute,
+	sector_ceilinglightsec,
 	sector_special,
 	sector_tag,
 	sector_taglist,
@@ -79,8 +81,10 @@ static const char *const sector_opt[] = {
 	"lightlevel",
 	"floorlightlevel",
 	"floorlightabsolute",
+	"floorlightsec",
 	"ceilinglightlevel",
 	"ceilinglightabsolute",
+	"ceilinglightsec",	
 	"special",
 	"tag",
 	"taglist",
@@ -667,12 +671,18 @@ static int sector_get(lua_State *L)
 	case sector_floorlightabsolute:
 		lua_pushboolean(L, sector->floorlightabsolute);
 		return 1;
+	case sector_floorlightsec:
+		lua_pushinteger(L, sector->floorlightsec);
+		return 1;		
 	case sector_ceilinglightlevel:
 		lua_pushinteger(L, sector->ceilinglightlevel);
 		return 1;
 	case sector_ceilinglightabsolute:
 		lua_pushboolean(L, sector->ceilinglightabsolute);
 		return 1;
+	case sector_ceilinglightsec:
+		lua_pushinteger(L, sector->ceilinglightsec);
+		return 1;		
 	case sector_special:
 		lua_pushinteger(L, sector->special);
 		return 1;
@@ -801,7 +811,7 @@ static int sector_set(lua_State *L)
 		break;
 	case sector_floorangle:
 		sector->floorangle = luaL_checkangle(L, 3);
-		break;		
+		break;				
 	case sector_ceilingpic:
 		sector->ceilingpic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
 		break;
@@ -823,12 +833,18 @@ static int sector_set(lua_State *L)
 	case sector_floorlightabsolute:
 		sector->floorlightabsolute = luaL_checkboolean(L, 3);
 		break;
+	case sector_floorlightsec:
+		sector->floorlightsec = (INT32)luaL_checkinteger(L, 3);
+		break;		
 	case sector_ceilinglightlevel:
 		sector->ceilinglightlevel = (INT16)luaL_checkinteger(L, 3);
 		break;
 	case sector_ceilinglightabsolute:
 		sector->ceilinglightabsolute = luaL_checkboolean(L, 3);
 		break;
+	case sector_ceilinglightsec:
+		sector->ceilinglightsec = (INT32)luaL_checkinteger(L, 3);
+		break;		
 	case sector_special:
 		sector->special = (INT16)luaL_checkinteger(L, 3);
 		break;
-- 
GitLab


From bc519ad132334b1e764884a468de490c007e4a35 Mon Sep 17 00:00:00 2001
From: Arthur <spaddlewit@gmail.com>
Date: Sat, 31 Dec 2022 22:07:22 -0500
Subject: [PATCH 070/518] Fix for issue #933 - special stage tokens should
 divert player until after the special stage with a custom exit map

---
 src/g_game.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/g_game.c b/src/g_game.c
index b4a127a731..ab399cfa2b 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -4161,7 +4161,7 @@ static void G_DoCompleted(void)
 	{
 		token--;
 
-		if (!nextmapoverride)
+//		if (!nextmapoverride) // Having a token should pull the player into the special stage before going to the overridden map (Issue #933)
 			for (i = 0; i < 7; i++)
 				if (!(emeralds & (1<<i)))
 				{
-- 
GitLab


From 52384053ceef84ebf49a40ac9fc0a9071bae89a2 Mon Sep 17 00:00:00 2001
From: Arthur <spaddlewit@gmail.com>
Date: Sat, 31 Dec 2022 22:46:08 -0500
Subject: [PATCH 071/518] When attaching to a wall to do a climb, the second
 sidedef wasn't be handled properly.

---
 src/p_map.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/p_map.c b/src/p_map.c
index 5c8ccbb193..232bf3a508 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -3506,11 +3506,9 @@ static void PTR_GlideClimbTraverse(line_t *li)
 			if (fofline)
 				whichside = 0;
 
-			if (!whichside)
-			{
-				slidemo->player->lastsidehit = checkline->sidenum[whichside];
-				slidemo->player->lastlinehit = (INT16)(checkline - lines);
-			}
+                        // Even if you attach to the second side of a linedef, we want to know the last hit.
+			slidemo->player->lastsidehit = checkline->sidenum[whichside];
+			slidemo->player->lastlinehit = (INT16)(checkline - lines);
 
 			P_Thrust(slidemo, slidemo->angle, FixedMul(5*FRACUNIT, slidemo->scale));
 		}
-- 
GitLab


From 3a04e1174372fd2b605b99220d127a6cc1291ecc Mon Sep 17 00:00:00 2001
From: katsy <katmint@live.com>
Date: Thu, 5 Jan 2023 14:11:54 -0600
Subject: [PATCH 072/518] allow targeting invulnerable bosses

---
 src/g_game.c |  1 -
 src/p_user.c | 12 ++----------
 2 files changed, 2 insertions(+), 11 deletions(-)

diff --git a/src/g_game.c b/src/g_game.c
index b4a127a731..cd29a08f8c 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1453,7 +1453,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 		if (
 			P_MobjWasRemoved(ticcmd_ztargetfocus[forplayer]) ||
 			!ticcmd_ztargetfocus[forplayer]->health ||
-			(ticcmd_ztargetfocus[forplayer]->flags2 & MF2_FRET) ||
 			(ticcmd_ztargetfocus[forplayer]->type == MT_EGGMOBILE3 && !ticcmd_ztargetfocus[forplayer]->movecount) // Sea Egg is moving around underground and shouldn't be tracked
 		)
 			P_SetTarget(&ticcmd_ztargetfocus[forplayer], NULL);
diff --git a/src/p_user.c b/src/p_user.c
index 4ca4e6c8ad..0ca49ded07 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -9059,10 +9059,6 @@ mobj_t *P_LookForFocusTarget(player_t *player, mobj_t *exclude, SINT8 direction,
 
 		switch (mo->type)
 		{
-		case MT_TNTBARREL:
-			if (lockonflags & LOCK_INTERESTS)
-				break;
-			/*FALLTHRU*/
 		case MT_PLAYER: // Don't chase other players!
 		case MT_DETON:
 			continue; // Don't be STUPID, Sonic!
@@ -9083,17 +9079,13 @@ mobj_t *P_LookForFocusTarget(player_t *player, mobj_t *exclude, SINT8 direction,
 			/*FALLTHRU*/
 		default:
 
-			if ((lockonflags & LOCK_BOSS) && ((mo->flags & (MF_BOSS|MF_SHOOTABLE)) == (MF_BOSS|MF_SHOOTABLE))) // allows if it has the flags desired XOR it has the invert aimable flag
-			{
-				if (mo->flags2 & MF2_FRET)
-					continue;
+			if ((lockonflags & LOCK_BOSS) && (mo->flags & MF_BOSS)) // always allow targeting bosses
 				break;
-			}
 
 			if ((lockonflags & LOCK_ENEMY) && (!((mo->flags & (MF_ENEMY|MF_SHOOTABLE)) == (MF_ENEMY|MF_SHOOTABLE)) != !(mo->flags2 & MF2_INVERTAIMABLE))) // allows if it has the flags desired XOR it has the invert aimable flag
 				break;
 
-			if ((lockonflags & LOCK_INTERESTS) && (mo->flags & (MF_PUSHABLE|MF_MONITOR))) // allows if it has the flags desired XOR it has the invert aimable flag
+			if ((lockonflags & LOCK_INTERESTS) && (mo->flags & (MF_PUSHABLE|MF_MONITOR))) // allows if it has the flags desired
 				break;
 
 			continue; // not a valid object
-- 
GitLab


From cc6eac0886317f4dab85743ae20f23c5f03004cc Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Sat, 4 Feb 2023 17:34:43 +0100
Subject: [PATCH 073/518] Fix con_hudlines being off by one

---
 src/console.c | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/src/console.c b/src/console.c
index 40fb43121f..0f3105caa1 100644
--- a/src/console.c
+++ b/src/console.c
@@ -72,8 +72,8 @@ static INT32 con_curlines;  // vid lines currently used by console
 
        INT32 con_clipviewtop; // (useless)
 
-static INT32 con_hudlines;        // number of console heads up message lines
-static INT32 con_hudtime[MAXHUDLINES];      // remaining time of display for hud msg lines
+static UINT8 con_hudlines;             // number of console heads up message lines
+static INT32 con_hudtime[MAXHUDLINES]; // remaining time of display for hud msg lines
 
        INT32 con_clearlines;      // top screen lines to refresh when view reduced
        boolean con_hudupdate;   // when messages scroll, we need a backgrnd refresh
@@ -128,7 +128,8 @@ static char con_buffer[CON_BUFFERSIZE];
 static consvar_t cons_msgtimeout = CVAR_INIT ("con_hudtime", "5", CV_SAVE, CV_Unsigned, NULL);
 
 // number of lines displayed on the HUD
-static consvar_t cons_hudlines = CVAR_INIT ("con_hudlines", "5", CV_CALL|CV_SAVE, CV_Unsigned, CONS_hudlines_Change);
+static CV_PossibleValue_t hudlines_cons_t[] = {{1, "MIN"}, {MAXHUDLINES, "MAX"}, {0, "None"}, {0, NULL}};
+static consvar_t cons_hudlines = CVAR_INIT ("con_hudlines", "5", CV_CALL|CV_SAVE, hudlines_cons_t, CONS_hudlines_Change);
 
 // number of lines console move per frame
 // (con_speed needs a limit, apparently)
@@ -168,11 +169,6 @@ static void CONS_hudlines_Change(void)
 	for (i = 0; i < con_hudlines; i++)
 		con_hudtime[i] = 0;
 
-	if (cons_hudlines.value < 1)
-		cons_hudlines.value = 1;
-	else if (cons_hudlines.value > MAXHUDLINES)
-		cons_hudlines.value = MAXHUDLINES;
-
 	con_hudlines = cons_hudlines.value;
 
 	Unlock_state();
@@ -1328,7 +1324,8 @@ boolean CON_Responder(event_t *ev)
 static void CON_Linefeed(void)
 {
 	// set time for heads up messages
-	con_hudtime[con_cy%con_hudlines] = cons_msgtimeout.value*TICRATE;
+	if (con_hudlines)
+		con_hudtime[con_cy%con_hudlines] = cons_msgtimeout.value*TICRATE;
 
 	con_cy++;
 	con_cx = 0;
@@ -1684,7 +1681,7 @@ static void CON_DrawHudlines(void)
 	INT32 charwidth = 8 * con_scalefactor;
 	INT32 charheight = 8 * con_scalefactor;
 
-	if (con_hudlines <= 0)
+	if (!con_hudlines)
 		return;
 
 	if (chat_on && OLDCHAT)
@@ -1692,7 +1689,7 @@ static void CON_DrawHudlines(void)
 	else
 		y = 0;
 
-	for (i = con_cy - con_hudlines+1; i <= con_cy; i++)
+	for (i = con_cy - con_hudlines; i <= con_cy; i++)
 	{
 		size_t c;
 		INT32 x;
-- 
GitLab


From 702b2acfd39d8665ce3290eecb2abd84460198aa Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Sat, 4 Feb 2023 17:35:44 +0100
Subject: [PATCH 074/518] Limit con_hudtime to 24 hours

Also rename cons_msgtimeout to cons_hudtime for consistency
---
 src/console.c | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/src/console.c b/src/console.c
index 0f3105caa1..b99b137206 100644
--- a/src/console.c
+++ b/src/console.c
@@ -72,8 +72,8 @@ static INT32 con_curlines;  // vid lines currently used by console
 
        INT32 con_clipviewtop; // (useless)
 
-static UINT8 con_hudlines;             // number of console heads up message lines
-static INT32 con_hudtime[MAXHUDLINES]; // remaining time of display for hud msg lines
+static UINT8  con_hudlines;             // number of console heads up message lines
+static UINT32 con_hudtime[MAXHUDLINES]; // remaining time of display for hud msg lines
 
        INT32 con_clearlines;      // top screen lines to refresh when view reduced
        boolean con_hudupdate;   // when messages scroll, we need a backgrnd refresh
@@ -125,7 +125,9 @@ static void CONS_backcolor_Change(void);
 static char con_buffer[CON_BUFFERSIZE];
 
 // how many seconds the hud messages lasts on the screen
-static consvar_t cons_msgtimeout = CVAR_INIT ("con_hudtime", "5", CV_SAVE, CV_Unsigned, NULL);
+// CV_Unsigned can overflow when multiplied by TICRATE later, so let's use a 24-hour limit instead
+static CV_PossibleValue_t hudtime_cons_t[] = {{0, "MIN"}, {86400, "MAX"}, {0, NULL}};
+static consvar_t cons_hudtime = CVAR_INIT ("con_hudtime", "5", CV_SAVE, hudtime_cons_t, NULL);
 
 // number of lines displayed on the HUD
 static CV_PossibleValue_t hudlines_cons_t[] = {{1, "MIN"}, {MAXHUDLINES, "MAX"}, {0, "None"}, {0, NULL}};
@@ -460,7 +462,7 @@ void CON_Init(void)
 
 		Unlock_state();
 
-		CV_RegisterVar(&cons_msgtimeout);
+		CV_RegisterVar(&cons_hudtime);
 		CV_RegisterVar(&cons_hudlines);
 		CV_RegisterVar(&cons_speed);
 		CV_RegisterVar(&cons_height);
@@ -773,9 +775,8 @@ void CON_Ticker(void)
 	// make overlay messages disappear after a while
 	for (i = 0; i < con_hudlines; i++)
 	{
-		con_hudtime[i]--;
-		if (con_hudtime[i] < 0)
-			con_hudtime[i] = 0;
+		if (con_hudtime[i])
+			con_hudtime[i]--;
 	}
 
 	Unlock_state();
@@ -1325,7 +1326,7 @@ static void CON_Linefeed(void)
 {
 	// set time for heads up messages
 	if (con_hudlines)
-		con_hudtime[con_cy%con_hudlines] = cons_msgtimeout.value*TICRATE;
+		con_hudtime[con_cy%con_hudlines] = cons_hudtime.value*TICRATE;
 
 	con_cy++;
 	con_cx = 0;
-- 
GitLab


From b184067048b4c5548d152c2481db9baa0d2866bd Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 14 Mar 2023 14:19:53 +0100
Subject: [PATCH 075/518] Add HUD icons for timed NiGHTS powerups

---
 src/st_stuff.c | 89 ++++++++++++++++++++++++++++++++++----------------
 1 file changed, 61 insertions(+), 28 deletions(-)

diff --git a/src/st_stuff.c b/src/st_stuff.c
index 3e75750a8f..65e4c5e03e 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -108,6 +108,9 @@ static patch_t *sneakers;
 static patch_t *gravboots;
 static patch_t *nonicon;
 static patch_t *nonicon2;
+static patch_t *nightopianhelper;
+static patch_t *linkfreeze;
+static patch_t *superparaloop;
 static patch_t *bluestat;
 static patch_t *byelstat;
 static patch_t *orngstat;
@@ -313,6 +316,10 @@ void ST_LoadGraphics(void)
 	nonicon2 = W_CachePatchName("NONICON2", PU_HUDGFX);
 
 	// NiGHTS HUD things
+	nightopianhelper = W_CachePatchName("NHLPICON", PU_HUDGFX);
+	linkfreeze = W_CachePatchName("NLFZICON", PU_HUDGFX);
+	superparaloop = W_CachePatchName("NSPRICON", PU_HUDGFX);
+
 	bluestat = W_CachePatchName("BLUESTAT", PU_HUDGFX);
 	byelstat = W_CachePatchName("BYELSTAT", PU_HUDGFX);
 	orngstat = W_CachePatchName("ORNGSTAT", PU_HUDGFX);
@@ -1448,6 +1455,21 @@ void ST_drawWipeTitleCard(void)
 	}
 }
 
+#define ICONSEP (16+4) // matches weapon rings HUD
+
+static INT32 ST_powerupHUDoffset(UINT16 timer)
+{
+	if (timer > 7)
+		return ICONSEP;
+	else
+	{
+		UINT8 a = ICONSEP, b = 7-timer;
+		while (b--)
+			a = 2*a/3;
+		return a;
+	}
+}
+
 static void ST_drawPowerupHUD(void)
 {
 	patch_t *p = NULL;
@@ -1455,7 +1477,6 @@ static void ST_drawPowerupHUD(void)
 	INT32 offs = hudinfo[HUD_POWERUPS].x;
 	const UINT8 q = ((splitscreen && stplyr == &players[secondarydisplayplayer]) ? 1 : 0);
 	static INT32 flagoffs[2] = {0, 0}, shieldoffs[2] = {0, 0}, finishoffs[2] = {0, 0};
-#define ICONSEP (16+4) // matches weapon rings HUD
 
 	if (F_GetPromptHideHud(hudinfo[HUD_POWERUPS].y))
 		return;
@@ -1567,15 +1588,7 @@ static void ST_drawPowerupHUD(void)
 		DRAWTIMERICON(invincibility, invulntime)
 	}
 
-	if (invulntime > 7)
-		offs -= ICONSEP;
-	else
-	{
-		UINT8 a = ICONSEP, b = 7-invulntime;
-		while (b--)
-			a = 2*a/3;
-		offs -= a;
-	}
+	offs -= ST_powerupHUDoffset(invulntime);
 
 	// Super Sneakers
 	if (stplyr->powers[pw_sneakers] > 3*TICRATE || (stplyr->powers[pw_sneakers] && leveltime & 1))
@@ -1583,15 +1596,7 @@ static void ST_drawPowerupHUD(void)
 		DRAWTIMERICON(sneakers, stplyr->powers[pw_sneakers])
 	}
 
-	if (stplyr->powers[pw_sneakers] > 7)
-		offs -= ICONSEP;
-	else
-	{
-		UINT8 a = ICONSEP, b = 7-stplyr->powers[pw_sneakers];
-		while (b--)
-			a = 2*a/3;
-		offs -= a;
-	}
+	offs -= ST_powerupHUDoffset(stplyr->powers[pw_sneakers]);
 
 	// Gravity Boots
 	if (stplyr->powers[pw_gravityboots] > 3*TICRATE || (stplyr->powers[pw_gravityboots] && leveltime & 1))
@@ -1599,6 +1604,36 @@ static void ST_drawPowerupHUD(void)
 		DRAWTIMERICON(gravboots, stplyr->powers[pw_gravityboots])
 	}
 
+	offs -= ST_powerupHUDoffset(stplyr->powers[pw_gravityboots]);
+
+// --------------------
+// NiGHTS timer-based powerups
+// --------------------
+
+	// Nightopian Helper
+	if (stplyr->powers[pw_nights_helper] > 3*TICRATE || (stplyr->powers[pw_nights_helper] && leveltime & 1))
+	{
+		DRAWTIMERICON(nightopianhelper, stplyr->powers[pw_nights_helper])
+	}
+
+	offs -= ST_powerupHUDoffset(stplyr->powers[pw_nights_helper]);
+
+	// Link Freeze
+	if (stplyr->powers[pw_nights_linkfreeze] > 3*TICRATE || (stplyr->powers[pw_nights_linkfreeze] && leveltime & 1))
+	{
+		DRAWTIMERICON(linkfreeze, stplyr->powers[pw_nights_linkfreeze])
+	}
+
+	offs -= ST_powerupHUDoffset(stplyr->powers[pw_nights_linkfreeze]);
+
+	// Super Paraloop
+	if (stplyr->powers[pw_nights_superloop] > 3*TICRATE || (stplyr->powers[pw_nights_superloop] && leveltime & 1))
+	{
+		DRAWTIMERICON(superparaloop, stplyr->powers[pw_nights_superloop])
+	}
+
+	//offs -= ST_powerupHUDoffset(stplyr->powers[pw_nights_superloop]);
+
 #undef DRAWTIMERICON
 #undef ICONSEP
 }
@@ -2740,18 +2775,16 @@ static void ST_overlayDrawer(void)
 		}
 
 		// This is where we draw all the fun cheese if you have the chasecam off!
-		if (!(maptol & TOL_NIGHTS))
+		if ((stplyr == &players[displayplayer] && !camera.chase)
+		|| ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase))
 		{
-			if ((stplyr == &players[displayplayer] && !camera.chase)
-			|| ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase))
-			{
-				ST_drawFirstPersonHUD();
-				if (cv_powerupdisplay.value)
-					ST_drawPowerupHUD();  // same as it ever was...
-			}
-			else if (cv_powerupdisplay.value == 2)
+			ST_drawFirstPersonHUD();
+			if (cv_powerupdisplay.value)
 				ST_drawPowerupHUD();  // same as it ever was...
 		}
+		else if (cv_powerupdisplay.value == 2)
+			ST_drawPowerupHUD();  // same as it ever was...
+		
 	}
 	else if (!(netgame || multiplayer) && cv_powerupdisplay.value == 2)
 		ST_drawPowerupHUD(); // same as it ever was...
-- 
GitLab


From 1304874a8f4e92e0507505e7ace02861c5ece0d1 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Thu, 16 Mar 2023 19:17:42 +0100
Subject: [PATCH 076/518] Remove incorrect early returns in P_CheckSector

---
 src/p_map.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/src/p_map.c b/src/p_map.c
index 54e2003ba2..1cdccbbcc1 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -4509,7 +4509,6 @@ boolean P_CheckSector(sector_t *sector, boolean crunch)
 								continue;
 
 							PIT_ChangeSector(mo, true);
-							return nofit;
 						}
 					}
 				}
@@ -4539,10 +4538,7 @@ boolean P_CheckSector(sector_t *sector, boolean crunch)
 				{
 					n->visited = true;
 					if (!(n->m_thing->flags & MF_NOBLOCKMAP))
-					{
 						PIT_ChangeSector(n->m_thing, true);
-						return nofit;
-					}
 					break;
 				}
 			} while (n);
@@ -4562,10 +4558,7 @@ boolean P_CheckSector(sector_t *sector, boolean crunch)
 			{
 				n->visited = true; // mark thing as processed
 				if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
-				{
 					PIT_ChangeSector(n->m_thing, true); // process it
-					return nofit;
-				}
 				break; // exit and start over
 			}
 	} while (n); // repeat from scratch until all things left are marked valid
-- 
GitLab


From 0667bf74feb7f1bf46304d04046608cbae27cba0 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Thu, 16 Mar 2023 19:30:21 +0100
Subject: [PATCH 077/518] Remove duplicated code in P_CheckSector

---
 src/p_map.c | 148 ++++++++++------------------------------------------
 1 file changed, 29 insertions(+), 119 deletions(-)

diff --git a/src/p_map.c b/src/p_map.c
index 1cdccbbcc1..d5f6ce1c19 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -4340,28 +4340,11 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
 	return true;
 }
 
-//
-// P_CheckSector
-//
-boolean P_CheckSector(sector_t *sector, boolean crunch)
+static boolean P_CheckSectorHelper(sector_t *sector, boolean realcrush)
 {
 	msecnode_t *n;
 	size_t i;
 
-	nofit = false;
-	crushchange = crunch;
-
-	// killough 4/4/98: scan list front-to-back until empty or exhausted,
-	// restarting from beginning after each thing is processed. Avoids
-	// crashes, and is sure to examine all things in the sector, and only
-	// the things which are in the sector, until a steady-state is reached.
-	// Things can arbitrarily be inserted and removed and it won't mess up.
-	//
-	// killough 4/7/98: simplified to avoid using complicated counter
-
-
-	// First, let's see if anything will keep it from crushing.
-
 	// Sal: This stupid function chain is required to fix polyobjects not being able to crush.
 	// Monster Iestyn: don't use P_CheckSector actually just look for objects in the blockmap instead
 	validcount++;
@@ -4398,11 +4381,8 @@ boolean P_CheckSector(sector_t *sector, boolean crunch)
 							if (!P_MobjInsidePolyobj(po, mo))
 								continue;
 
-							if (!PIT_ChangeSector(mo, false))
-							{
-								nofit = true;
-								return nofit;
-							}
+							if (!PIT_ChangeSector(mo, realcrush) && !realcrush)
+								return false;
 						}
 					}
 				}
@@ -4434,11 +4414,8 @@ boolean P_CheckSector(sector_t *sector, boolean crunch)
 					n->visited = true;
 					if (!(n->m_thing->flags & MF_NOBLOCKMAP))
 					{
-						if (!PIT_ChangeSector(n->m_thing, false))
-						{
-							nofit = true;
-							return nofit;
-						}
+						if (!PIT_ChangeSector(n->m_thing, realcrush) && !realcrush)
+							return false;
 					}
 					break;
 				}
@@ -4460,108 +4437,41 @@ boolean P_CheckSector(sector_t *sector, boolean crunch)
 				n->visited = true; // mark thing as processed
 				if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
 				{
-					if (!PIT_ChangeSector(n->m_thing, false)) // process it
-					{
-						nofit = true;
-						return nofit;
-					}
+					if (!PIT_ChangeSector(n->m_thing, realcrush) && !realcrush) // process it
+						return false;
 				}
 				break; // exit and start over
 			}
 	} while (n); // repeat from scratch until all things left are marked valid
 
-	// Nothing blocked us, so lets crush for real!
-
-	// Sal: This stupid function chain is required to fix polyobjects not being able to crush.
-	// Monster Iestyn: don't use P_CheckSector actually just look for objects in the blockmap instead
-	validcount++;
-
-	for (i = 0; i < sector->linecount; i++)
-	{
-		if (sector->lines[i]->polyobj)
-		{
-			polyobj_t *po = sector->lines[i]->polyobj;
-			if (po->validcount == validcount)
-				continue; // skip if already checked
-			if (!(po->flags & POF_SOLID))
-				continue;
-			if (po->lines[0]->backsector == sector) // Make sure you're currently checking the control sector
-			{
-				INT32 x, y;
-				po->validcount = validcount;
-
-				for (y = po->blockbox[BOXBOTTOM]; y <= po->blockbox[BOXTOP]; ++y)
-				{
-					for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
-					{
-						mobj_t *mo;
-
-						if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
-							continue;
-
-						mo = blocklinks[y * bmapwidth + x];
+	return true;
+}
 
-						for (; mo; mo = mo->bnext)
-						{
-							// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
+//
+// P_CheckSector
+//
+boolean P_CheckSector(sector_t *sector, boolean crunch)
+{
+	nofit = false;
+	crushchange = crunch;
 
-							if (!P_MobjInsidePolyobj(po, mo))
-								continue;
+	// killough 4/4/98: scan list front-to-back until empty or exhausted,
+	// restarting from beginning after each thing is processed. Avoids
+	// crashes, and is sure to examine all things in the sector, and only
+	// the things which are in the sector, until a steady-state is reached.
+	// Things can arbitrarily be inserted and removed and it won't mess up.
+	//
+	// killough 4/7/98: simplified to avoid using complicated counter
 
-							PIT_ChangeSector(mo, true);
-						}
-					}
-				}
-			}
-		}
-	}
-	if (sector->numattached)
+	// First, let's see if anything will keep it from crushing.
+	if (!P_CheckSectorHelper(sector, false))
 	{
-		sector_t *sec;
-		for (i = 0; i < sector->numattached; i++)
-		{
-			sec = &sectors[sector->attached[i]];
-			for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
-				n->visited = false;
-
-			sec->moved = true;
-
-			P_RecalcPrecipInSector(sec);
-
-			if (!sector->attachedsolid[i])
-				continue;
-
-			do
-			{
-				for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
-				if (!n->visited)
-				{
-					n->visited = true;
-					if (!(n->m_thing->flags & MF_NOBLOCKMAP))
-						PIT_ChangeSector(n->m_thing, true);
-					break;
-				}
-			} while (n);
-		}
+		nofit = true;
+		return nofit;
 	}
 
-	// Mark all things invalid
-	sector->moved = true;
-
-	for (n = sector->touching_thinglist; n; n = n->m_thinglist_next)
-		n->visited = false;
-
-	do
-	{
-		for (n = sector->touching_thinglist; n; n = n->m_thinglist_next) // go through list
-			if (!n->visited) // unprocessed thing found
-			{
-				n->visited = true; // mark thing as processed
-				if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
-					PIT_ChangeSector(n->m_thing, true); // process it
-				break; // exit and start over
-			}
-	} while (n); // repeat from scratch until all things left are marked valid
+	// Nothing blocked us, so lets crush for real!
+	P_CheckSectorHelper(sector, true);
 
 	return nofit;
 }
-- 
GitLab


From 411b79e4564a35dc946944c714da98c7a56ba628 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Thu, 16 Mar 2023 19:38:30 +0100
Subject: [PATCH 078/518] P_CheckSector: Remove unnecessary static variables

---
 src/p_map.c | 40 ++++++++++++----------------------------
 1 file changed, 12 insertions(+), 28 deletions(-)

diff --git a/src/p_map.c b/src/p_map.c
index d5f6ce1c19..3172f57edc 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -4231,13 +4231,11 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
 //  the way it was and call P_CheckSector (? was P_ChangeSector - Graue) again
 //  to undo the changes.
 //
-static boolean crushchange;
-static boolean nofit;
 
 //
 // PIT_ChangeSector
 //
-static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
+static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush, boolean crunch)
 {
 	mobj_t *killer = NULL;
 	//If a thing is both pushable and vulnerable, it doesn't block the crusher because it gets killed.
@@ -4261,11 +4259,7 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
 	if (thing->z + thing->height > thing->ceilingz && thing->z <= thing->ceilingz)
 	{
 		if (immunepushable && thing->z + thing->height > thing->subsector->sector->ceilingheight)
-		{
-			//Thing is a pushable and blocks the moving ceiling
-			nofit = true;
-			return false;
-		}
+			return false; //Thing is a pushable and blocks the moving ceiling
 
 		//Check FOFs in the sector
 		if (thing->subsector->sector->ffloors && (realcrush || immunepushable))
@@ -4291,11 +4285,7 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
 				if (bottomheight <= thing->ceilingz && abs(delta1) >= abs(delta2))
 				{
 					if (immunepushable)
-					{
-						//FOF is blocked by pushable
-						nofit = true;
-						return false;
-					}
+						return false; //FOF is blocked by pushable
 					else
 					{
 						//If the thing was crushed by a crumbling FOF, reward the player who made it crumble!
@@ -4333,14 +4323,14 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
 		}
 	}
 
-	if (realcrush && crushchange)
+	if (realcrush && crunch)
 		P_DamageMobj(thing, NULL, NULL, 1, 0);
 
 	// keep checking (crush other things)
 	return true;
 }
 
-static boolean P_CheckSectorHelper(sector_t *sector, boolean realcrush)
+static boolean P_CheckSectorHelper(sector_t *sector, boolean realcrush, boolean crunch)
 {
 	msecnode_t *n;
 	size_t i;
@@ -4381,7 +4371,7 @@ static boolean P_CheckSectorHelper(sector_t *sector, boolean realcrush)
 							if (!P_MobjInsidePolyobj(po, mo))
 								continue;
 
-							if (!PIT_ChangeSector(mo, realcrush) && !realcrush)
+							if (!PIT_ChangeSector(mo, realcrush, crunch) && !realcrush)
 								return false;
 						}
 					}
@@ -4414,7 +4404,7 @@ static boolean P_CheckSectorHelper(sector_t *sector, boolean realcrush)
 					n->visited = true;
 					if (!(n->m_thing->flags & MF_NOBLOCKMAP))
 					{
-						if (!PIT_ChangeSector(n->m_thing, realcrush) && !realcrush)
+						if (!PIT_ChangeSector(n->m_thing, realcrush, crunch) && !realcrush)
 							return false;
 					}
 					break;
@@ -4437,7 +4427,7 @@ static boolean P_CheckSectorHelper(sector_t *sector, boolean realcrush)
 				n->visited = true; // mark thing as processed
 				if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
 				{
-					if (!PIT_ChangeSector(n->m_thing, realcrush) && !realcrush) // process it
+					if (!PIT_ChangeSector(n->m_thing, realcrush, crunch) && !realcrush) // process it
 						return false;
 				}
 				break; // exit and start over
@@ -4452,9 +4442,6 @@ static boolean P_CheckSectorHelper(sector_t *sector, boolean realcrush)
 //
 boolean P_CheckSector(sector_t *sector, boolean crunch)
 {
-	nofit = false;
-	crushchange = crunch;
-
 	// killough 4/4/98: scan list front-to-back until empty or exhausted,
 	// restarting from beginning after each thing is processed. Avoids
 	// crashes, and is sure to examine all things in the sector, and only
@@ -4464,16 +4451,13 @@ boolean P_CheckSector(sector_t *sector, boolean crunch)
 	// killough 4/7/98: simplified to avoid using complicated counter
 
 	// First, let's see if anything will keep it from crushing.
-	if (!P_CheckSectorHelper(sector, false))
-	{
-		nofit = true;
-		return nofit;
-	}
+	if (!P_CheckSectorHelper(sector, false, crunch))
+		return true;
 
 	// Nothing blocked us, so lets crush for real!
-	P_CheckSectorHelper(sector, true);
+	P_CheckSectorHelper(sector, true, crunch);
 
-	return nofit;
+	return false;
 }
 
 /*
-- 
GitLab


From afa1a9ab6a0cf917ed41cf3ba6227a03f90f127a Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Thu, 16 Mar 2023 20:03:42 +0100
Subject: [PATCH 079/518] Split P_CheckSector further

---
 src/p_map.c | 156 ++++++++++++++++++++++++++++------------------------
 1 file changed, 85 insertions(+), 71 deletions(-)

diff --git a/src/p_map.c b/src/p_map.c
index 3172f57edc..e1ebe6ae99 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -4330,9 +4330,8 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush, boolean crunch
 	return true;
 }
 
-static boolean P_CheckSectorHelper(sector_t *sector, boolean realcrush, boolean crunch)
+static boolean P_CheckSectorPolyObjects(sector_t *sector, boolean realcrush, boolean crunch)
 {
-	msecnode_t *n;
 	size_t i;
 
 	// Sal: This stupid function chain is required to fix polyobjects not being able to crush.
@@ -4341,102 +4340,117 @@ static boolean P_CheckSectorHelper(sector_t *sector, boolean realcrush, boolean
 
 	for (i = 0; i < sector->linecount; i++)
 	{
-		if (sector->lines[i]->polyobj)
+		INT32 x, y;
+		polyobj_t *po = sector->lines[i]->polyobj;
+
+		if (!po)
+			continue;
+		if (po->validcount == validcount)
+			continue; // skip if already checked
+		if (!(po->flags & POF_SOLID))
+			continue;
+		if (po->lines[0]->backsector != sector) // Make sure you're currently checking the control sector
+			continue;
+
+		po->validcount = validcount;
+
+		for (y = po->blockbox[BOXBOTTOM]; y <= po->blockbox[BOXTOP]; ++y)
 		{
-			polyobj_t *po = sector->lines[i]->polyobj;
-			if (po->validcount == validcount)
-				continue; // skip if already checked
-			if (!(po->flags & POF_SOLID))
-				continue;
-			if (po->lines[0]->backsector == sector) // Make sure you're currently checking the control sector
+			for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
 			{
-				INT32 x, y;
-				po->validcount = validcount;
-
-				for (y = po->blockbox[BOXBOTTOM]; y <= po->blockbox[BOXTOP]; ++y)
-				{
-					for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
-					{
-						mobj_t *mo;
+				mobj_t *mo;
 
-						if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
-							continue;
+				if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
+					continue;
 
-						mo = blocklinks[y * bmapwidth + x];
+				mo = blocklinks[y * bmapwidth + x];
 
-						for (; mo; mo = mo->bnext)
-						{
-							// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
+				for (; mo; mo = mo->bnext)
+				{
+					// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
 
-							if (!P_MobjInsidePolyobj(po, mo))
-								continue;
+					if (!P_MobjInsidePolyobj(po, mo))
+						continue;
 
-							if (!PIT_ChangeSector(mo, realcrush, crunch) && !realcrush)
-								return false;
-						}
-					}
+					if (!PIT_ChangeSector(mo, realcrush, crunch) && !realcrush)
+						return false;
 				}
 			}
 		}
 	}
 
-	if (sector->numattached)
+	return true;
+}
+
+static boolean P_CheckTouchingThinglist(sector_t *sector, boolean realcrush, boolean crunch)
+{
+	msecnode_t *n;
+
+	for (n = sector->touching_thinglist; n; n = n->m_thinglist_next)
+		n->visited = false;
+
+	do
 	{
-		sector_t *sec;
-		for (i = 0; i < sector->numattached; i++)
+		for (n = sector->touching_thinglist; n; n = n->m_thinglist_next) // go through list
 		{
-			sec = &sectors[sector->attached[i]];
-			for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
-				n->visited = false;
-
-			sec->moved = true;
+			if (n->visited)
+				continue;
 
-			P_RecalcPrecipInSector(sec);
+			n->visited = true; // mark thing as processed
 
-			if (!sector->attachedsolid[i])
+			if (n->m_thing->flags & MF_NOBLOCKMAP) //jff 4/7/98 don't do these
 				continue;
 
-			do
-			{
-				for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
-				if (!n->visited)
-				{
-					n->visited = true;
-					if (!(n->m_thing->flags & MF_NOBLOCKMAP))
-					{
-						if (!PIT_ChangeSector(n->m_thing, realcrush, crunch) && !realcrush)
-							return false;
-					}
-					break;
-				}
-			} while (n);
+			if (!PIT_ChangeSector(n->m_thing, realcrush, crunch) && !realcrush) // process it
+				return false;
+
+			break; // exit and start over
 		}
-	}
+	} while (n); // repeat from scratch until all things left are marked valid
 
-	// Mark all things invalid
-	sector->moved = true;
+	return true;
+}
 
-	for (n = sector->touching_thinglist; n; n = n->m_thinglist_next)
-		n->visited = false;
+static boolean P_CheckSectorFFloors(sector_t *sector, boolean realcrush, boolean crunch)
+{
+	sector_t *sec;
+	size_t i;
 
-	do
+	if (!sector->numattached)
+		return true;
+
+	for (i = 0; i < sector->numattached; i++)
 	{
-		for (n = sector->touching_thinglist; n; n = n->m_thinglist_next) // go through list
-			if (!n->visited) // unprocessed thing found
-			{
-				n->visited = true; // mark thing as processed
-				if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
-				{
-					if (!PIT_ChangeSector(n->m_thing, realcrush, crunch) && !realcrush) // process it
-						return false;
-				}
-				break; // exit and start over
-			}
-	} while (n); // repeat from scratch until all things left are marked valid
+		sec = &sectors[sector->attached[i]];
+
+		sec->moved = true;
+
+		P_RecalcPrecipInSector(sec);
+
+		if (!sector->attachedsolid[i])
+			continue;
+
+		if (!P_CheckTouchingThinglist(sec, realcrush, crunch))
+			return false;
+	}
 
 	return true;
 }
 
+static boolean P_CheckSectorHelper(sector_t *sector, boolean realcrush, boolean crunch)
+{
+	if (!P_CheckSectorPolyObjects(sector, realcrush, crunch))
+		return false;
+
+	if (!P_CheckSectorFFloors(sector, realcrush, crunch))
+		return false;
+
+	// Mark all things invalid
+	sector->moved = true;
+
+	return P_CheckTouchingThinglist(sector, realcrush, crunch);
+}
+
 //
 // P_CheckSector
 //
-- 
GitLab


From fff6683173e0a2ec61a5a2a8ab7e33f85ad714c3 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Thu, 16 Mar 2023 20:38:28 +0100
Subject: [PATCH 080/518] Clean up PIT_ChangeSector

---
 src/p_map.c | 67 +++++++++++++++++++++++++++++++----------------------
 1 file changed, 39 insertions(+), 28 deletions(-)

diff --git a/src/p_map.c b/src/p_map.c
index e1ebe6ae99..8ea1a6392a 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -4271,43 +4271,54 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush, boolean crunch
 
 			for (rover = thing->subsector->sector->ffloors; rover; rover = rover->next)
 			{
-				if (!(((rover->fofflags & FOF_BLOCKPLAYER) && thing->player)
-				|| ((rover->fofflags & FOF_BLOCKOTHERS) && !thing->player)) || !(rover->fofflags & FOF_EXISTS))
+				thinker_t *think;
+
+				if (!(rover->fofflags & FOF_EXISTS))
+					continue;
+				if (thing->player && !(rover->fofflags & FOF_BLOCKPLAYER))
+					continue;
+				if (!thing->player && !(rover->fofflags & FOF_BLOCKOTHERS))
 					continue;
 
 				topheight = *rover->topheight;
 				bottomheight = *rover->bottomheight;
-				//topheight    = P_GetFFloorTopZAt   (rover, thing->x, thing->y);
-				//bottomheight = P_GetFFloorBottomZAt(rover, thing->x, thing->y);
+
+				if (bottomheight > thing->ceilingz)
+					continue;
 
 				delta1 = thing->z - (bottomheight + topheight)/2;
 				delta2 = thingtop - (bottomheight + topheight)/2;
-				if (bottomheight <= thing->ceilingz && abs(delta1) >= abs(delta2))
+				if (abs(delta1) < abs(delta2))
+					continue;
+
+				if (immunepushable)
+					return false; //FOF is blocked by pushable
+
+				if (!realcrush)
+					continue;
+
+				//If the thing was crushed by a crumbling FOF, reward the player who made it crumble!
+				for (think = thlist[THINK_MAIN].next; think != &thlist[THINK_MAIN]; think = think->next)
 				{
-					if (immunepushable)
-						return false; //FOF is blocked by pushable
-					else
-					{
-						//If the thing was crushed by a crumbling FOF, reward the player who made it crumble!
-						thinker_t *think;
-						crumble_t *crumbler;
+					crumble_t *crumbler;
 
-						for (think = thlist[THINK_MAIN].next; think != &thlist[THINK_MAIN]; think = think->next)
-						{
-							if (think->function.acp1 != (actionf_p1)T_StartCrumble)
-								continue;
-
-							crumbler = (crumble_t *)think;
-
-							if (crumbler->player && crumbler->player->mo
-								&& crumbler->player->mo != thing
-								&& crumbler->actionsector == thing->subsector->sector
-								&& crumbler->sector == rover->master->frontsector)
-							{
-								killer = crumbler->player->mo;
-							}
-						}
-					}
+					if (think->function.acp1 != (actionf_p1)T_StartCrumble)
+						continue;
+
+					crumbler = (crumble_t *)think;
+
+					if (!crumbler->player)
+						continue;
+					if (!crumbler->player->mo)
+						continue;
+					if (crumbler->player->mo == thing)
+						continue;
+					if (crumbler->actionsector != thing->subsector->sector)
+						continue;
+					if (crumbler->sector != rover->master->frontsector)
+						continue;
+
+					killer = crumbler->player->mo;
 				}
 			}
 		}
-- 
GitLab


From 498c9da85943886fda0538e1689fc66dbfebe64d Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Wed, 29 Mar 2023 16:41:45 +0200
Subject: [PATCH 081/518] Don't attract bomb spheres with Attraction shield

---
 src/p_mobj.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index 30e0183de5..f5a951d654 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -9686,7 +9686,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
 		P_RingThinker(mobj);
 		if (mobj->flags2 & MF2_NIGHTSPULL)
 			P_NightsItemChase(mobj);
-		else
+		else if (mobj->type != MT_BOMBSPHERE) // prevent shields from attracting bomb spheres
 			A_AttractChase(mobj);
 		return false;
 		// Flung items
-- 
GitLab


From 98dcf2d228141015c6fcdc2a054454357037dec6 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Mon, 24 Apr 2023 23:23:47 +0200
Subject: [PATCH 082/518] Tweak yellow/green/blue/red/peridot text colors

---
 src/console.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/console.c b/src/console.c
index 33d59046e4..5ada6dd947 100644
--- a/src/console.c
+++ b/src/console.c
@@ -382,16 +382,16 @@ static void CON_SetupColormaps(void)
 
 	//                      0x1       0x3                           0x9                           0xF
 	colset(magentamap, 177, 177, 178, 178, 178, 180, 180, 180, 182, 182, 182, 182, 184, 184, 184, 185);
-	colset(yellowmap,   82,  82,  73,  73,  73,  64,  64,  64,  66,  66,  66,  66,  67,  67,  67,  68);
-	colset(lgreenmap,   96,  96,  98,  98,  98, 101, 101, 101, 104, 104, 104, 104, 106, 106, 106, 107);
-	colset(bluemap,    146, 146, 147, 147, 147, 149, 149, 149, 152, 152, 152, 152, 155, 155, 155, 157);
-	colset(redmap,      32,  32,  33,  33,  33,  35,  35,  35,  39,  39,  39,  39,  42,  42,  42,  44);
+	colset(yellowmap,   82,  82,  73,  73,  73,  74,  74,  74,  66,  66,  66,  66,  67,  67,  67,  68);
+	colset(lgreenmap,   96,  96,  98,  98,  98, 100, 100, 100, 103, 103, 103, 103, 105, 105, 105, 107);
+	colset(bluemap,    146, 146, 147, 147, 147, 148, 148, 148, 149, 149, 149, 149, 150, 150, 150, 151);
+	colset(redmap,      32,  32,  33,  33,  33,  34,  34,  34,  35,  35,  35,  35,  37,  37,  37,  39);
 	colset(graymap,      8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23);
 	colset(orangemap,   50,  50,  52,  52,  52,  54,  54,  54,  56,  56,  56,  56,  59,  59,  59,  60);
 	colset(skymap,     129, 129, 130, 130, 130, 131, 131, 131, 133, 133, 133, 133, 135, 135, 135, 136);
 	colset(purplemap,  160, 160, 161, 161, 161, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 165);
 	colset(aquamap,    120, 120, 121, 121, 121, 122, 122, 122, 123, 123, 123, 123, 124, 124, 124, 125);
-	colset(peridotmap,  72,  72, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191, 191, 191,  94);
+	colset(peridotmap,  73,  73, 188, 188, 188, 189, 189, 189, 190, 190, 190, 190, 191, 191, 191,  94);
 	colset(azuremap,   144, 144, 145, 145, 145, 146, 146, 146, 170, 170, 170, 170, 171, 171, 171, 172);
 	colset(brownmap,   219, 219, 221, 221, 221, 222, 222, 222, 224, 224, 224, 224, 227, 227, 227, 229);
 	colset(rosymap,    200, 200, 201, 201, 201, 202, 202, 202, 203, 203, 203, 203, 204, 204, 204, 205);
-- 
GitLab


From 901c7362ed65434ad83c623506d8a00fc4b9099d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sun, 30 Apr 2023 14:05:10 +0200
Subject: [PATCH 083/518] Fix segfault when P_RemoveMobj is called within
 A_FaceTarget action

---
 src/p_enemy.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/src/p_enemy.c b/src/p_enemy.c
index 63d430eb68..af629a2997 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -1571,6 +1571,8 @@ void A_PointyThink(mobj_t *actor)
 	// Okay, we found the closest player. Let's move based on his movement.
 	P_SetTarget(&actor->target, player->mo);
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	if (P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y) < P_AproxDistance(player->mo->x + player->mo->momx - actor->x, player->mo->y + player->mo->momy - actor->y))
 		sign = -1; // Player is moving away
@@ -1686,6 +1688,8 @@ void A_HoodFire(mobj_t *actor)
 	}
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	if (!(arrow = P_SpawnMissile(actor, actor->target, (mobjtype_t)locvar1)))
 		return;
@@ -2266,6 +2270,8 @@ void A_VultureVtol(mobj_t *actor)
 	actor->flags |= MF_FLOAT;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	S_StopSound(actor);
 
@@ -2364,6 +2370,9 @@ void A_VultureHover(mobj_t *actor)
 	P_VultureHoverParticle(actor);
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
+
 	targetz = actor->target->z + actor->target->height / 2;
 	for (i = -1; i <= 1; i++)
 	{
@@ -2680,6 +2689,8 @@ void A_LobShot(mobj_t *actor)
 		return;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	if (actor->eflags & MFE_VERTICALFLIP)
 	{
@@ -2775,6 +2786,8 @@ void A_FireShot(mobj_t *actor)
 		return;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	if (actor->eflags & MFE_VERTICALFLIP)
 		z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
@@ -2813,6 +2826,8 @@ void A_SuperFireShot(mobj_t *actor)
 		return;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	if (actor->eflags & MFE_VERTICALFLIP)
 		z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
@@ -2860,6 +2875,8 @@ void A_BossFireShot(mobj_t *actor)
 		return;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	switch (locvar2)
 	{
@@ -2947,6 +2964,8 @@ void A_Boss7FireMissiles(mobj_t *actor)
 	}
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	S_StartSound(NULL, locvar2);
 
@@ -3331,6 +3350,8 @@ void A_SkullAttack(mobj_t *actor)
 	if (actor->info->activesound)
 		S_StartSound(actor, actor->info->activesound);
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
 
@@ -3442,6 +3463,9 @@ void A_BossZoom(mobj_t *actor)
 	if (actor->info->attacksound)
 		S_StartAttackSound(actor, actor->info->attacksound);
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
+
 	an = actor->angle >> ANGLETOFINESHIFT;
 	actor->momx = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINECOSINE(an));
 	actor->momy = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINESINE(an));
@@ -5539,6 +5563,9 @@ void A_JetgShoot(mobj_t *actor)
 		return;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
+
 	P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
 
 	if (ultimatemode)
@@ -5573,6 +5600,9 @@ void A_ShootBullet(mobj_t *actor)
 		return;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
+
 	P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
 
 	if (actor->info->attacksound)
@@ -7431,6 +7461,8 @@ void A_Boss7Chase(mobj_t *actor)
 		&& (actor->target->player->powers[pw_carry] == CR_GENERIC))
 	{
 		A_FaceTarget(actor);
+		if (P_MobjWasRemoved(actor))
+			return;
 		P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
 		actor->movecount = TICRATE + P_RandomByte()/2;
 		return;
@@ -7448,6 +7480,8 @@ void A_Boss7Chase(mobj_t *actor)
 				if (actor->z < 1056*FRACUNIT)
 				{
 					A_FaceTarget(actor);
+					if (P_MobjWasRemoved(actor))
+						return;
 					P_SetMobjState(actor, actor->info->xdeathstate);
 					actor->movecount = 7*TICRATE + P_RandomByte();
 					break;
@@ -7456,6 +7490,8 @@ void A_Boss7Chase(mobj_t *actor)
 				/* FALLTHRU */
 			case 1: // Chaingun Goop
 				A_FaceTarget(actor);
+				if (P_MobjWasRemoved(actor))
+					return;
 				P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
 
 				if (actor->health > actor->info->damage)
@@ -7465,6 +7501,8 @@ void A_Boss7Chase(mobj_t *actor)
 				break;
 			case 2: // Homing Missile
 				A_FaceTarget(actor);
+				if (P_MobjWasRemoved(actor))
+					return;
 				P_SetMobjState(actor, actor->info->missilestate);
 				S_StartSound(0, sfx_beflap);
 				break;
@@ -8182,6 +8220,9 @@ void A_Boss3Path(mobj_t *actor)
 		P_SetTarget(&actor->target, actor->tracer->target);
 		var1 = 0, var2 = 0;
 		A_FaceTarget(actor);
+		if (P_MobjWasRemoved(actor))
+			return;
+
 		if (actor->tracer->state == &states[actor->tracer->info->missilestate])
 			P_SetMobjState(actor, actor->info->missilestate);
 		return;
@@ -9856,6 +9897,8 @@ void A_SplitShot(mobj_t *actor)
 		return;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 	{
 		const angle_t an = (actor->angle + ANGLE_90) >> ANGLETOFINESHIFT;
 		const fixed_t fasin = FINESINE(an);
@@ -9920,6 +9963,9 @@ void A_MultiShot(mobj_t *actor)
 	if (actor->target)
 		A_FaceTarget(actor);
 
+	if (P_MobjWasRemoved(actor))
+		return;
+
 	if(loc1lw > 90)
 		ad = FixedMul(90*FRACUNIT, actor->scale);
 	else
@@ -11064,6 +11110,8 @@ void A_VileTarget(mobj_t *actor)
 		return;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	// Determine object to spawn
 	if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
@@ -11151,6 +11199,8 @@ void A_VileAttack(mobj_t *actor)
 		return;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	if (locvar1 <= 0 || locvar1 >= NUMSFX)
 		soundtoplay = sfx_brakrx;
@@ -11469,6 +11519,8 @@ void A_BrakFireShot(mobj_t *actor)
 		return;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	x = actor->x
 		+ P_ReturnThrustX(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale))
@@ -11586,6 +11638,9 @@ void A_BrakLobShot(mobj_t *actor)
 
 	// Okay, complicated math done. Let's fire our object already, sheesh.
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
+
 	if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
 		typeOfShot = MT_CANNONBALL;
 	else typeOfShot = (mobjtype_t)locvar1;
@@ -12670,6 +12725,8 @@ void A_WhoCaresIfYourSonIsABee(mobj_t *actor)
 		return;
 
 	A_FaceTarget(actor);
+	if (P_MobjWasRemoved(actor))
+		return;
 
 	if (actor->extravalue1)
 		actor->extravalue1--;
-- 
GitLab


From 03971f58a924e41c0f794a9dcbe459052835a381 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Thu, 4 May 2023 22:42:51 +0200
Subject: [PATCH 084/518] Fix segfault when shields are removed after thinking

---
 src/p_mobj.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index ee4440b73b..0704c15a70 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -6765,7 +6765,8 @@ void P_RunShields(void)
 	// run shields
 	for (i = 0; i < numshields; i++)
 	{
-		P_ShieldLook(shields[i], shields[i]->threshold);
+		if (!P_MobjWasRemoved(shields[i]))
+			P_ShieldLook(shields[i], shields[i]->threshold);
 		P_SetTarget(&shields[i], NULL);
 	}
 	numshields = 0;
-- 
GitLab


From b487a71533e4d3058b703362b6a828d51069c6cc Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 14 May 2023 16:26:34 +0200
Subject: [PATCH 085/518] Fix light fades being unable to lower light levels

---
 src/p_lights.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/p_lights.c b/src/p_lights.c
index 75455da736..4b6a3673b5 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -353,8 +353,8 @@ void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean
 	else
 	{
 		// Speed means increment per tic (literally speed).
-		ll->timer = FixedDiv((destvalue<<FRACBITS) - ll->fixedcurlevel, speed<<FRACBITS)>>FRACBITS;
-		ll->fixedpertic = speed<<FRACBITS;
+		ll->timer = abs(FixedDiv((destvalue<<FRACBITS) - ll->fixedcurlevel, speed<<FRACBITS)>>FRACBITS);
+		ll->fixedpertic = ll->destlevel < ll->sourcelevel ? -speed<<FRACBITS : speed<<FRACBITS;
 	}
 }
 
-- 
GitLab


From 6bb3ee226f825a013bd17b200d4d491e3170d780 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Tue, 16 May 2023 19:16:14 +0200
Subject: [PATCH 086/518] Fix segfault when going up steep slopes in rare cases

---
 src/p_slopes.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_slopes.c b/src/p_slopes.c
index cf7807d4e5..48a13a07d6 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -869,7 +869,7 @@ fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope)
 	vector3_t slopemom, axis;
 	angle_t ang;
 
-	if (mo->standingslope->flags & SL_NOPHYSICS)
+	if (slope->flags & SL_NOPHYSICS)
 		return 0;
 
 	// If there's physics, time for launching.
-- 
GitLab


From d4951f7cdd804a237ded2ed66711d69901673636 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Tue, 16 May 2023 22:18:11 +0200
Subject: [PATCH 087/518] Fix segfault when Crushstaceans hit a player with
 Armageddon shield

---
 src/p_enemy.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_enemy.c b/src/p_enemy.c
index ca947fc206..618665c974 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -2231,7 +2231,7 @@ void A_CrushclawLaunch(mobj_t *actor)
 		}
 	}
 
-	if (!actor->target)
+	if (P_MobjWasRemoved(actor->target))
 		return;
 
 	{
-- 
GitLab


From 2f98cd3b972b35427ef42978af46bade61874f5e Mon Sep 17 00:00:00 2001
From: Jaime Ita Passos <jp6781615@gmail.com>
Date: Fri, 19 May 2023 14:26:30 -0300
Subject: [PATCH 088/518] Fix I_GetFreeMem

---
 src/android/i_system.c |  2 +-
 src/dummy/i_system.c   |  2 +-
 src/i_system.h         |  2 +-
 src/sdl/i_system.c     | 13 ++++++-------
 src/z_zone.c           | 10 +++++-----
 5 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/src/android/i_system.c b/src/android/i_system.c
index f0edcc17ef..ff8b88de53 100644
--- a/src/android/i_system.c
+++ b/src/android/i_system.c
@@ -24,7 +24,7 @@ static INT64 start_time; // as microseconds since the epoch
 
 // I should probably return how much memory is remaining
 // for this process, considering Android's process memory limit.
-UINT32 I_GetFreeMem(UINT32 *total)
+size_t I_GetFreeMem(size_t *total)
 {
   // what the heck?  sysinfo() is partially missing in bionic?
   /* struct sysinfo si; */
diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c
index 997115ad0c..00550c410f 100644
--- a/src/dummy/i_system.c
+++ b/src/dummy/i_system.c
@@ -8,7 +8,7 @@ UINT8 graphics_started = 0;
 
 UINT8 keyboard_started = 0;
 
-UINT32 I_GetFreeMem(UINT32 *total)
+size_t I_GetFreeMem(UINT32 *total)
 {
 	*total = 0;
 	return 0;
diff --git a/src/i_system.h b/src/i_system.h
index 957150fe66..deea9f8a8d 100644
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -40,7 +40,7 @@ extern UINT8 keyboard_started;
 
 	\return	free memory in the system
 */
-UINT32 I_GetFreeMem(UINT32 *total);
+size_t I_GetFreeMem(size_t *total);
 
 /**	\brief	Returns precise time value for performance measurement. The precise
             time should be a monotonically increasing counter, and will wrap.
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index e328bedc29..67ee8d6684 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -2962,8 +2962,7 @@ static long get_entry(const char* name, const char* buf)
 }
 #endif
 
-// quick fix for compil
-UINT32 I_GetFreeMem(UINT32 *total)
+size_t I_GetFreeMem(size_t *total)
 {
 #ifdef FREEBSD
 	struct vmmeter sum;
@@ -3011,14 +3010,14 @@ UINT32 I_GetFreeMem(UINT32 *total)
 	info.dwLength = sizeof (MEMORYSTATUS);
 	GlobalMemoryStatus( &info );
 	if (total)
-		*total = (UINT32)info.dwTotalPhys;
-	return (UINT32)info.dwAvailPhys;
+		*total = (size_t)info.dwTotalPhys;
+	return (size_t)info.dwAvailPhys;
 #elif defined (__linux__)
 	/* Linux */
 	char buf[1024];
 	char *memTag;
-	UINT32 freeKBytes;
-	UINT32 totalKBytes;
+	size_t freeKBytes;
+	size_t totalKBytes;
 	INT32 n;
 	INT32 meminfo_fd = -1;
 	long Cached;
@@ -3049,7 +3048,7 @@ UINT32 I_GetFreeMem(UINT32 *total)
 	}
 
 	memTag += sizeof (MEMTOTAL);
-	totalKBytes = atoi(memTag);
+	totalKBytes = (size_t)atoi(memTag);
 
 	if ((memTag = strstr(buf, MEMAVAILABLE)) == NULL)
 	{
diff --git a/src/z_zone.c b/src/z_zone.c
index c012816ffd..11c4bcb2c7 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -106,14 +106,14 @@ static void Command_Memdump_f(void);
   */
 void Z_Init(void)
 {
-	UINT32 total, memfree;
+	size_t total, memfree;
 
 	memset(&head, 0x00, sizeof(head));
 
 	head.next = head.prev = &head;
 
 	memfree = I_GetFreeMem(&total)>>20;
-	CONS_Printf("System memory: %uMB - Free: %uMB\n", total>>20, memfree);
+	CONS_Printf("System memory: %sMB - Free: %sMB\n", sizeu1(total>>20), sizeu2(memfree));
 
 	// Note: This allocates memory. Watch out.
 	COM_AddCommand("memfree", Command_Memfree_f, COM_LUA);
@@ -791,7 +791,7 @@ size_t Z_TagsUsage(INT32 lowtag, INT32 hightag)
   */
 static void Command_Memfree_f(void)
 {
-	UINT32 freebytes, totalbytes;
+	size_t freebytes, totalbytes;
 
 	Z_CheckHeap(-1);
 	CONS_Printf("\x82%s", M_GetText("Memory Info\n"));
@@ -824,8 +824,8 @@ static void Command_Memfree_f(void)
 
 	CONS_Printf("\x82%s", M_GetText("System Memory Info\n"));
 	freebytes = I_GetFreeMem(&totalbytes);
-	CONS_Printf(M_GetText("    Total physical memory: %7u KB\n"), totalbytes>>10);
-	CONS_Printf(M_GetText("Available physical memory: %7u KB\n"), freebytes>>10);
+	CONS_Printf(M_GetText("    Total physical memory: %s KB\n"), sizeu1(totalbytes>>10));
+	CONS_Printf(M_GetText("Available physical memory: %s KB\n"), sizeu1(freebytes>>10));
 }
 
 #ifdef ZDEBUG
-- 
GitLab


From 7dc74fc364b688a44279a7d29322798fb838fd99 Mon Sep 17 00:00:00 2001
From: Jaime Ita Passos <jp6781615@gmail.com>
Date: Fri, 19 May 2023 15:12:20 -0300
Subject: [PATCH 089/518] Fix declaration of I_GetFreeMem in
 src/dummy/i_system.c

---
 src/dummy/i_system.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c
index 00550c410f..8556c02486 100644
--- a/src/dummy/i_system.c
+++ b/src/dummy/i_system.c
@@ -8,7 +8,7 @@ UINT8 graphics_started = 0;
 
 UINT8 keyboard_started = 0;
 
-size_t I_GetFreeMem(UINT32 *total)
+size_t I_GetFreeMem(size_t *total)
 {
 	*total = 0;
 	return 0;
-- 
GitLab


From eb1492fe6e501001a2271fa133bd76c0b0612715 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 23 May 2023 17:51:56 +0200
Subject: [PATCH 090/518] Zone Builder config updates

---
 extras/conf/SRB2-22.cfg | 197 +++++++++++++++++++++++-----------------
 1 file changed, 112 insertions(+), 85 deletions(-)

diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index ae905b637b..b5078cd0cb 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -41,6 +41,9 @@ linetagindicatesectors = true;
 
 // The format interface handles the map data format - DoomMapSetIO for SRB2DB2, SRB2MapSetIO for Zone Builder
 formatinterface = "SRB2MapSetIO";
+	
+//Maximum safe map size check (0 means skip check)
+safeboundary = 0;
 
 //Sky textures for vanilla maps
 defaultskytextures
@@ -77,7 +80,7 @@ defaultskytextures
 defaultlumpname = "MAP01";
 
 // Default testing parameters
-testparameters = "-file \"%AP\" \"%F\" -warp %L";
+testparameters = "-folder \"%AF\" -file \"%AA\" \"%F\" -warp %L";
 testshortpaths = true;
 
 // Default nodebuilder configurations
@@ -437,6 +440,8 @@ sectortypes
 	144 = "Egg Capsule";
 	160 = "Special Stage Time/Spheres Parameters <deprecated>";
 	176 = "Custom Global Gravity <deprecated>";
+	512 = "Wind/Current <deprecated>";
+	1024 = "Conveyor Belt <deprecated>";
 	1280 = "Speed Pad";
 	1536 = "Flip Gravity on Jump";
 	4096 = "Star Post Activator";
@@ -496,6 +501,8 @@ gen_sectortypes
 	third
 	{
 		0 = "Normal";
+		512 = "Wind/Current <deprecated>";
+		1024 = "Conveyor Belt <deprecated>";		 
 		1280 = "Speed Pad";
 		1536 = "Flip Gravity on Jump";
 	}
@@ -578,7 +585,7 @@ linedeftypes
 			title = "Per-Sector Gravity";
 			prefix = "(1)";
 			flags64text = "[6] Flip in reverse gravity";
-			flags8192text = "[13] Reverse while inside";
+			flags8192text = "[13] Cancel MF2_OBJECTFLIP";
 		}
 
 		5
@@ -641,35 +648,35 @@ linedeftypes
 
 		96
 		{
-			title = "Apply Tag to Tagged Sectors";
+			title = "Add Front Sector Tag to Tagged Sectors";
 			prefix = "(96)";
 			flags1024text = "[10] Offsets are target tags";
-			flags8192text = "[13] Use front side offsets";
-			flags32768text = "[15] Use back side offsets";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
 		}
 
 		97
 		{
-			title = "Apply Tag to Front Sector";
+			title = "Add Tag to Front Sector";
 			prefix = "(97)";
-			flags8192text = "[13] Use front side offsets";
-			flags32768text = "[15] Use back side offsets";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
 		}
 
 		98
 		{
-			title = "Apply Tag to Back Sector";
+			title = "Add Tag to Back Sector";
 			prefix = "(98)";
-			flags8192text = "[13] Use front side offsets";
-			flags32768text = "[15] Use back side offsets";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
 		}
 
 		99
 		{
-			title = "Apply Tag to Front and Back Sectors";
+			title = "Add Tag to Front and Back Sectors";
 			prefix = "(99)";
-			flags8192text = "[13] Use front side offsets";
-			flags32768text = "[15] Use back side offsets";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
 		}
 
 		540
@@ -990,6 +997,7 @@ linedeftypes
 			flags128text = "[7] Only block non-players";
 			3dfloor = true;
 			3dfloorflags = "47";
+			invisiblefof = true;
 		}
 
 		140
@@ -1227,6 +1235,7 @@ linedeftypes
 			prefix = "(223)";
 			3dfloor = true;
 			3dfloorflags = "41";
+			invisiblefof = true;
 		}
 	}
 
@@ -1524,6 +1533,7 @@ linedeftypes
 			prefix = "(200)";
 			3dfloor = true;
 			3dfloorflags = "20201";
+			invisiblefof = true;
 		}
 
 		201
@@ -1532,6 +1542,7 @@ linedeftypes
 			prefix = "(201)";
 			3dfloor = true;
 			3dfloorflags = "201";
+			invisiblefof = true;
 		}
 
 		202
@@ -1539,7 +1550,8 @@ linedeftypes
 			title = "Fog Block";
 			prefix = "(202)";
 			3dfloor = true;
-			3dfloorflags = "3EF19";
+			3dfloorflags = "3EF01";
+			invisiblefof = true;
 		}
 
 		250
@@ -3624,7 +3636,7 @@ thingtypes
 
 		3328 = "3D Mode Start";
 	}
-
+	
 	starts
 	{
 		color = 1; // Blue
@@ -3814,7 +3826,7 @@ thingtypes
 
 	enemies
 	{
-		color = 9; // Light_Blue
+		color = 9; // Light Blue
 		arrow = 1;
 		title = "Enemies";
 
@@ -4124,7 +4136,7 @@ thingtypes
 
 	bosses
 	{
-		color = 8; // Dark_Gray
+		color = 4; // Dark Red
 		arrow = 1;
 		title = "Bosses";
 
@@ -4223,6 +4235,7 @@ thingtypes
 			sprite = "internal:capsule";
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		292
 		{
@@ -4310,13 +4323,13 @@ thingtypes
 		308
 		{
 			title = "CTF Team Ring (Red)";
-			sprite = "internal:TRNGA0r";
+			sprite = "internal:TRNGA0R";
 			width = 16;
 		}
 		309
 		{
 			title = "CTF Team Ring (Blue)";
-			sprite = "internal:TRNGA0b";
+			sprite = "internal:TRNGA0B";
 			width = 16;
 		}
 		330
@@ -4353,7 +4366,7 @@ thingtypes
 
 	collectibles
 	{
-		color = 10; // Light_Green
+		color = 10; // Light Green
 		title = "Other Collectibles";
 		width = 16;
 		height = 32;
@@ -4457,6 +4470,7 @@ thingtypes
 		flags8text = "[8] Random (Weak)";
 		angletext = "Tag";
 		fixedrotation = 1;
+		tagthing = true;
 
 		400
 		{
@@ -4589,6 +4603,7 @@ thingtypes
 		flags1text = "[1] Run linedef executor on pop";
 		angletext = "Tag";
 		fixedrotation = 1;
+		tagthing = true;
 
 		431
 		{
@@ -4659,7 +4674,7 @@ thingtypes
 
 	generic
 	{
-		color = 11; // Light_Cyan
+		color = 11; // Light Cyan
 		title = "Generic Items & Hazards";
 
 		500
@@ -4765,7 +4780,7 @@ thingtypes
 
 	springs
 	{
-		color = 12; // Light_Red
+		color = 12; // Light Red
 		title = "Springs and Fans";
 		width = 20;
 		height = 16;
@@ -4927,13 +4942,13 @@ thingtypes
 		{
 			arrow = 0;
 			title = "5 Vertical Rings (Yellow Spring)";
-			sprite = "RINGA0";
+			sprite = "internal:ringverticalyellow";
 		}
 		601
 		{
 			arrow = 0;
 			title = "5 Vertical Rings (Red Spring)";
-			sprite = "RINGA0";
+			sprite = "internal:ringverticalred";
 			height = 1024;
 		}
 		602
@@ -4951,7 +4966,7 @@ thingtypes
 		604
 		{
 			title = "Circle of Rings";
-			sprite = "RINGA0";
+			sprite = "internal:circlering";
 			width = 96;
 			height = 192;
 			unflippable = true;
@@ -4960,7 +4975,7 @@ thingtypes
 		605
 		{
 			title = "Circle of Rings (Big)";
-			sprite = "RINGA0";
+			sprite = "internal:circlebigring";
 			width = 192;
 			unflippable = true;
 			centerHitbox = true;
@@ -4968,7 +4983,7 @@ thingtypes
 		606
 		{
 			title = "Circle of Blue Spheres";
-			sprite = "SPHRA0";
+			sprite = "internal:circlesphere";
 			width = 96;
 			height = 192;
 			unflippable = true;
@@ -4977,7 +4992,7 @@ thingtypes
 		607
 		{
 			title = "Circle of Blue Spheres (Big)";
-			sprite = "SPHRA0";
+			sprite = "internal:circlebigsphere";
 			width = 192;
 			unflippable = true;
 			centerHitbox = true;
@@ -4985,7 +5000,7 @@ thingtypes
 		608
 		{
 			title = "Circle of Rings and Spheres";
-			sprite = "SPHRA0";
+			sprite = "internal:circleringsphere";
 			width = 96;
 			height = 192;
 			unflippable = true;
@@ -4994,86 +5009,84 @@ thingtypes
 		609
 		{
 			title = "Circle of Rings and Spheres (Big)";
-			sprite = "SPHRA0";
+			sprite = "internal:circlebigringsphere";
 			width = 192;
 			unflippable = true;
 			centerHitbox = true;
 		}
 	}
 
-	invisible
+	ambience
 	{
-		color = 15; // White
-		title = "Misc. Invisible";
+		color = 8; // Dark Gray
+		title = "Ambience";
 		width = 8;
 		height = 16;
-		sprite = "UNKNA0";
+		sprite = "internal:ambiance";
 
 		700
 		{
 			title = "Water Ambience A (Large)";
-			sprite = "internal:ambiance";
 		}
 
 		701
 		{
 			title = "Water Ambience B (Large)";
-			sprite = "internal:ambiance";
 		}
 
 		702
 		{
 			title = "Water Ambience C (Medium)";
-			sprite = "internal:ambiance";
 		}
 
 		703
 		{
 			title = "Water Ambience D (Medium)";
-			sprite = "internal:ambiance";
 		}
 
 		704
 		{
 			title = "Water Ambience E (Small)";
-			sprite = "internal:ambiance";
 		}
 
 		705
 		{
 			title = "Water Ambience F (Small)";
-			sprite = "internal:ambiance";
 		}
 
 		706
 		{
 			title = "Water Ambience G (Extra Large)";
-			sprite = "internal:ambiance";
 		}
 
 		707
 		{
 			title = "Water Ambience H (Extra Large)";
-			sprite = "internal:ambiance";
 		}
 
 		708
 		{
 			title = "Disco Ambience";
-			sprite = "internal:ambiance";
 		}
 
 		709
 		{
 			title = "Volcano Ambience";
-			sprite = "internal:ambiance";
 		}
 
 		710
 		{
 			title = "Machine Ambience";
-			sprite = "internal:ambiance";
 		}
+	}
+
+	invisible
+	{
+		color = 15; // White
+		title = "Misc. Invisible";
+		width = 8;
+		height = 16;
+		sprite = "UNKNA0";
 
 		750
 		{
@@ -5083,6 +5096,7 @@ thingtypes
 			fixedrotation = 1;
 			parametertext = "Absolute?";
 			flagsvaluetext = "Absolute Z";
+			tagthing = true;
 		}
 
 		751
@@ -5128,20 +5142,22 @@ thingtypes
 		756
 		{
 			title = "Blast Linedef Executor";
-			sprite = "TOADA0";
+			sprite = "internal:blastexec";
 			width = 32;
 			height = 16;
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		757
 		{
 			title = "Fan Particle Generator";
-			sprite = "PRTLA0";
+			sprite = "internal:fanparticles";
 			width = 8;
 			height = 16;
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		758
 		{
@@ -5154,6 +5170,8 @@ thingtypes
 			sprite = "internal:polyanchor";
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
+			unflippable = true;
 		}
 
 		761
@@ -5162,6 +5180,8 @@ thingtypes
 			sprite = "internal:polycenter";
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
+			unflippable = true;
 		}
 
 		762
@@ -5170,6 +5190,8 @@ thingtypes
 			sprite = "internal:polycentercrush";
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
+			unflippable = true;
 		}
 		780
 		{
@@ -5183,7 +5205,7 @@ thingtypes
 
 	greenflower
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Greenflower";
 
 		800
@@ -5288,7 +5310,7 @@ thingtypes
 
 	technohill
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Techno Hill";
 
 		900
@@ -5332,7 +5354,7 @@ thingtypes
 
 	deepsea
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Deep Sea";
 
 		1000
@@ -5467,7 +5489,7 @@ thingtypes
 
 	castleeggman
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Castle Eggman";
 
 		1100
@@ -5516,6 +5538,7 @@ thingtypes
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1105
 		{
@@ -5528,6 +5551,7 @@ thingtypes
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1106
 		{
@@ -5540,23 +5564,25 @@ thingtypes
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1107
 		{
 			title = "Chain Spawnpoint";
-			sprite = "BMCHA0";
+			sprite = "BMCHB0";
 			width = 17;
 			height = 34;
 			flags8text = "[8] Double size";
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1108
 		{
 			arrow = 1;
 			title = "Hidden Chain Spawnpoint";
-			sprite = "internal:chain3";
+			sprite = "SMCHA0";
 			width = 17;
 			height = 34;
 			flags8text = "[8] Double size";
@@ -5572,6 +5598,7 @@ thingtypes
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1110
 		{
@@ -5583,6 +5610,7 @@ thingtypes
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1111
 		{
@@ -5736,7 +5764,7 @@ thingtypes
 
 	aridcanyon
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Arid Canyon";
 
 		1200
@@ -5764,6 +5792,7 @@ thingtypes
 			height = 16;
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1203
 		{
@@ -5955,7 +5984,7 @@ thingtypes
 
 	redvolcano
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Red Volcano";
 
 		1300
@@ -6055,7 +6084,7 @@ thingtypes
 
 	botanicserenity
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Botanic Serenity";
 		width = 16;
 		height = 32;
@@ -6298,7 +6327,7 @@ thingtypes
 
 	azuretemple
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Azure Temple";
 
 		1500
@@ -6374,7 +6403,7 @@ thingtypes
 
 	dreamhill
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Dream Hill";
 
 		1600
@@ -6402,8 +6431,8 @@ thingtypes
 
 	nightstrk
 	{
-		color = 13; // Pink
-		title = "NiGHTS Track";
+		color = 16; // Light Pink
+		title = "NiGHTS Track & Basics";
 		width = 8;
 		height = 4096;
 		sprite = "UNKNA0";
@@ -6438,6 +6467,19 @@ thingtypes
 			flagsvaluetext = "Order";
 			parametertext = "Mare";
 		}
+		1703
+		{
+			title = "Ideya Drone";
+			sprite = "NDRNA1";
+			width = 16;
+			height = 56;
+			flags1text = "[1] Align player to middle";
+			flags4text = "[4] Align player to top";
+			flags8text = "[8] Die upon time up";
+			angletext = "Time limit";
+			fixedrotation = 1;
+			parametertext = "Height";
+		}
 		1710
 		{
 			title = "Ideya Capture";
@@ -6455,20 +6497,6 @@ thingtypes
 		title = "NiGHTS Items";
 		width = 16;
 		height = 32;
-
-		1703
-		{
-			title = "Ideya Drone";
-			sprite = "NDRNA1";
-			width = 16;
-			height = 56;
-			flags1text = "[1] Align player to middle";
-			flags4text = "[4] Align player to top";
-			flags8text = "[8] Die upon time up";
-			angletext = "Time limit";
-			fixedrotation = 1;
-			parametertext = "Height";
-		}
 		1704
 		{
 			arrow = 1;
@@ -6479,13 +6507,12 @@ thingtypes
 			unflippable = true;
 			flagsvaluetext = "Pitch";
 			angletext = "Yaw";
-			fixedrotation = 1;
 		}
 		1705
 		{
 			arrow = 1;
 			title = "Hoop (Generic)";
-			sprite = "HOOPA0";
+			sprite = "internal:nightshoop";
 			width = 80;
 			height = 160;
 			unflippable = true;
@@ -6502,7 +6529,6 @@ thingtypes
 			height = 24;
 			flags8height = 24;
 			flags8text = "[8] Float";
-			unflippable = true;
 		}
 		1707
 		{
@@ -6547,7 +6573,7 @@ thingtypes
 			flags2text = "[2] Radius +32";
 			flags4text = "[4] Radius +64";
 			flags8text = "[8] Radius +128";
-			sprite = "HOOPA0";
+			sprite = "internal:nightshoop";
 			width = 80;
 			height = 160;
 			unflippable = true;
@@ -6657,7 +6683,7 @@ thingtypes
 
 	christmasdisco
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Christmas & Disco";
 
 		1850
@@ -6760,7 +6786,7 @@ thingtypes
 
 	stalagmites
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Stalagmites";
 		width = 16;
 		height = 40;
@@ -6839,7 +6865,7 @@ thingtypes
 
 	hauntedheights
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Haunted Heights";
 
 		2000
@@ -6928,7 +6954,7 @@ thingtypes
 
 	frozenhillside
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Frozen Hillside";
 
 		2100
@@ -6979,7 +7005,7 @@ thingtypes
 
 	tutorial
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Tutorial";
 
 		799
@@ -6990,10 +7016,11 @@ thingtypes
 			height = 144;
 			parametertext = "Start frame";
 		}
+	}
 
 	flickies
 	{
-		color = 10; // Green
+		color = 3; // Teal
 		title = "Flickies";
 		width = 8;
 		height = 20;
-- 
GitLab


From b9c86a0f7f579253f622fc00ca8e5ac9b513f89d Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 23 May 2023 17:51:56 +0200
Subject: [PATCH 091/518] Zone Builder config updates

---
 extras/conf/SRB2-22.cfg | 197 +++++++++++++++++++++++-----------------
 1 file changed, 112 insertions(+), 85 deletions(-)

diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index ae905b637b..b5078cd0cb 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -41,6 +41,9 @@ linetagindicatesectors = true;
 
 // The format interface handles the map data format - DoomMapSetIO for SRB2DB2, SRB2MapSetIO for Zone Builder
 formatinterface = "SRB2MapSetIO";
+	
+//Maximum safe map size check (0 means skip check)
+safeboundary = 0;
 
 //Sky textures for vanilla maps
 defaultskytextures
@@ -77,7 +80,7 @@ defaultskytextures
 defaultlumpname = "MAP01";
 
 // Default testing parameters
-testparameters = "-file \"%AP\" \"%F\" -warp %L";
+testparameters = "-folder \"%AF\" -file \"%AA\" \"%F\" -warp %L";
 testshortpaths = true;
 
 // Default nodebuilder configurations
@@ -437,6 +440,8 @@ sectortypes
 	144 = "Egg Capsule";
 	160 = "Special Stage Time/Spheres Parameters <deprecated>";
 	176 = "Custom Global Gravity <deprecated>";
+	512 = "Wind/Current <deprecated>";
+	1024 = "Conveyor Belt <deprecated>";
 	1280 = "Speed Pad";
 	1536 = "Flip Gravity on Jump";
 	4096 = "Star Post Activator";
@@ -496,6 +501,8 @@ gen_sectortypes
 	third
 	{
 		0 = "Normal";
+		512 = "Wind/Current <deprecated>";
+		1024 = "Conveyor Belt <deprecated>";		 
 		1280 = "Speed Pad";
 		1536 = "Flip Gravity on Jump";
 	}
@@ -578,7 +585,7 @@ linedeftypes
 			title = "Per-Sector Gravity";
 			prefix = "(1)";
 			flags64text = "[6] Flip in reverse gravity";
-			flags8192text = "[13] Reverse while inside";
+			flags8192text = "[13] Cancel MF2_OBJECTFLIP";
 		}
 
 		5
@@ -641,35 +648,35 @@ linedeftypes
 
 		96
 		{
-			title = "Apply Tag to Tagged Sectors";
+			title = "Add Front Sector Tag to Tagged Sectors";
 			prefix = "(96)";
 			flags1024text = "[10] Offsets are target tags";
-			flags8192text = "[13] Use front side offsets";
-			flags32768text = "[15] Use back side offsets";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
 		}
 
 		97
 		{
-			title = "Apply Tag to Front Sector";
+			title = "Add Tag to Front Sector";
 			prefix = "(97)";
-			flags8192text = "[13] Use front side offsets";
-			flags32768text = "[15] Use back side offsets";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
 		}
 
 		98
 		{
-			title = "Apply Tag to Back Sector";
+			title = "Add Tag to Back Sector";
 			prefix = "(98)";
-			flags8192text = "[13] Use front side offsets";
-			flags32768text = "[15] Use back side offsets";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
 		}
 
 		99
 		{
-			title = "Apply Tag to Front and Back Sectors";
+			title = "Add Tag to Front and Back Sectors";
 			prefix = "(99)";
-			flags8192text = "[13] Use front side offsets";
-			flags32768text = "[15] Use back side offsets";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
 		}
 
 		540
@@ -990,6 +997,7 @@ linedeftypes
 			flags128text = "[7] Only block non-players";
 			3dfloor = true;
 			3dfloorflags = "47";
+			invisiblefof = true;
 		}
 
 		140
@@ -1227,6 +1235,7 @@ linedeftypes
 			prefix = "(223)";
 			3dfloor = true;
 			3dfloorflags = "41";
+			invisiblefof = true;
 		}
 	}
 
@@ -1524,6 +1533,7 @@ linedeftypes
 			prefix = "(200)";
 			3dfloor = true;
 			3dfloorflags = "20201";
+			invisiblefof = true;
 		}
 
 		201
@@ -1532,6 +1542,7 @@ linedeftypes
 			prefix = "(201)";
 			3dfloor = true;
 			3dfloorflags = "201";
+			invisiblefof = true;
 		}
 
 		202
@@ -1539,7 +1550,8 @@ linedeftypes
 			title = "Fog Block";
 			prefix = "(202)";
 			3dfloor = true;
-			3dfloorflags = "3EF19";
+			3dfloorflags = "3EF01";
+			invisiblefof = true;
 		}
 
 		250
@@ -3624,7 +3636,7 @@ thingtypes
 
 		3328 = "3D Mode Start";
 	}
-
+	
 	starts
 	{
 		color = 1; // Blue
@@ -3814,7 +3826,7 @@ thingtypes
 
 	enemies
 	{
-		color = 9; // Light_Blue
+		color = 9; // Light Blue
 		arrow = 1;
 		title = "Enemies";
 
@@ -4124,7 +4136,7 @@ thingtypes
 
 	bosses
 	{
-		color = 8; // Dark_Gray
+		color = 4; // Dark Red
 		arrow = 1;
 		title = "Bosses";
 
@@ -4223,6 +4235,7 @@ thingtypes
 			sprite = "internal:capsule";
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		292
 		{
@@ -4310,13 +4323,13 @@ thingtypes
 		308
 		{
 			title = "CTF Team Ring (Red)";
-			sprite = "internal:TRNGA0r";
+			sprite = "internal:TRNGA0R";
 			width = 16;
 		}
 		309
 		{
 			title = "CTF Team Ring (Blue)";
-			sprite = "internal:TRNGA0b";
+			sprite = "internal:TRNGA0B";
 			width = 16;
 		}
 		330
@@ -4353,7 +4366,7 @@ thingtypes
 
 	collectibles
 	{
-		color = 10; // Light_Green
+		color = 10; // Light Green
 		title = "Other Collectibles";
 		width = 16;
 		height = 32;
@@ -4457,6 +4470,7 @@ thingtypes
 		flags8text = "[8] Random (Weak)";
 		angletext = "Tag";
 		fixedrotation = 1;
+		tagthing = true;
 
 		400
 		{
@@ -4589,6 +4603,7 @@ thingtypes
 		flags1text = "[1] Run linedef executor on pop";
 		angletext = "Tag";
 		fixedrotation = 1;
+		tagthing = true;
 
 		431
 		{
@@ -4659,7 +4674,7 @@ thingtypes
 
 	generic
 	{
-		color = 11; // Light_Cyan
+		color = 11; // Light Cyan
 		title = "Generic Items & Hazards";
 
 		500
@@ -4765,7 +4780,7 @@ thingtypes
 
 	springs
 	{
-		color = 12; // Light_Red
+		color = 12; // Light Red
 		title = "Springs and Fans";
 		width = 20;
 		height = 16;
@@ -4927,13 +4942,13 @@ thingtypes
 		{
 			arrow = 0;
 			title = "5 Vertical Rings (Yellow Spring)";
-			sprite = "RINGA0";
+			sprite = "internal:ringverticalyellow";
 		}
 		601
 		{
 			arrow = 0;
 			title = "5 Vertical Rings (Red Spring)";
-			sprite = "RINGA0";
+			sprite = "internal:ringverticalred";
 			height = 1024;
 		}
 		602
@@ -4951,7 +4966,7 @@ thingtypes
 		604
 		{
 			title = "Circle of Rings";
-			sprite = "RINGA0";
+			sprite = "internal:circlering";
 			width = 96;
 			height = 192;
 			unflippable = true;
@@ -4960,7 +4975,7 @@ thingtypes
 		605
 		{
 			title = "Circle of Rings (Big)";
-			sprite = "RINGA0";
+			sprite = "internal:circlebigring";
 			width = 192;
 			unflippable = true;
 			centerHitbox = true;
@@ -4968,7 +4983,7 @@ thingtypes
 		606
 		{
 			title = "Circle of Blue Spheres";
-			sprite = "SPHRA0";
+			sprite = "internal:circlesphere";
 			width = 96;
 			height = 192;
 			unflippable = true;
@@ -4977,7 +4992,7 @@ thingtypes
 		607
 		{
 			title = "Circle of Blue Spheres (Big)";
-			sprite = "SPHRA0";
+			sprite = "internal:circlebigsphere";
 			width = 192;
 			unflippable = true;
 			centerHitbox = true;
@@ -4985,7 +5000,7 @@ thingtypes
 		608
 		{
 			title = "Circle of Rings and Spheres";
-			sprite = "SPHRA0";
+			sprite = "internal:circleringsphere";
 			width = 96;
 			height = 192;
 			unflippable = true;
@@ -4994,86 +5009,84 @@ thingtypes
 		609
 		{
 			title = "Circle of Rings and Spheres (Big)";
-			sprite = "SPHRA0";
+			sprite = "internal:circlebigringsphere";
 			width = 192;
 			unflippable = true;
 			centerHitbox = true;
 		}
 	}
 
-	invisible
+	ambience
 	{
-		color = 15; // White
-		title = "Misc. Invisible";
+		color = 8; // Dark Gray
+		title = "Ambience";
 		width = 8;
 		height = 16;
-		sprite = "UNKNA0";
+		sprite = "internal:ambiance";
 
 		700
 		{
 			title = "Water Ambience A (Large)";
-			sprite = "internal:ambiance";
 		}
 
 		701
 		{
 			title = "Water Ambience B (Large)";
-			sprite = "internal:ambiance";
 		}
 
 		702
 		{
 			title = "Water Ambience C (Medium)";
-			sprite = "internal:ambiance";
 		}
 
 		703
 		{
 			title = "Water Ambience D (Medium)";
-			sprite = "internal:ambiance";
 		}
 
 		704
 		{
 			title = "Water Ambience E (Small)";
-			sprite = "internal:ambiance";
 		}
 
 		705
 		{
 			title = "Water Ambience F (Small)";
-			sprite = "internal:ambiance";
 		}
 
 		706
 		{
 			title = "Water Ambience G (Extra Large)";
-			sprite = "internal:ambiance";
 		}
 
 		707
 		{
 			title = "Water Ambience H (Extra Large)";
-			sprite = "internal:ambiance";
 		}
 
 		708
 		{
 			title = "Disco Ambience";
-			sprite = "internal:ambiance";
 		}
 
 		709
 		{
 			title = "Volcano Ambience";
-			sprite = "internal:ambiance";
 		}
 
 		710
 		{
 			title = "Machine Ambience";
-			sprite = "internal:ambiance";
 		}
+	}
+
+	invisible
+	{
+		color = 15; // White
+		title = "Misc. Invisible";
+		width = 8;
+		height = 16;
+		sprite = "UNKNA0";
 
 		750
 		{
@@ -5083,6 +5096,7 @@ thingtypes
 			fixedrotation = 1;
 			parametertext = "Absolute?";
 			flagsvaluetext = "Absolute Z";
+			tagthing = true;
 		}
 
 		751
@@ -5128,20 +5142,22 @@ thingtypes
 		756
 		{
 			title = "Blast Linedef Executor";
-			sprite = "TOADA0";
+			sprite = "internal:blastexec";
 			width = 32;
 			height = 16;
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		757
 		{
 			title = "Fan Particle Generator";
-			sprite = "PRTLA0";
+			sprite = "internal:fanparticles";
 			width = 8;
 			height = 16;
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		758
 		{
@@ -5154,6 +5170,8 @@ thingtypes
 			sprite = "internal:polyanchor";
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
+			unflippable = true;
 		}
 
 		761
@@ -5162,6 +5180,8 @@ thingtypes
 			sprite = "internal:polycenter";
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
+			unflippable = true;
 		}
 
 		762
@@ -5170,6 +5190,8 @@ thingtypes
 			sprite = "internal:polycentercrush";
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
+			unflippable = true;
 		}
 		780
 		{
@@ -5183,7 +5205,7 @@ thingtypes
 
 	greenflower
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Greenflower";
 
 		800
@@ -5288,7 +5310,7 @@ thingtypes
 
 	technohill
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Techno Hill";
 
 		900
@@ -5332,7 +5354,7 @@ thingtypes
 
 	deepsea
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Deep Sea";
 
 		1000
@@ -5467,7 +5489,7 @@ thingtypes
 
 	castleeggman
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Castle Eggman";
 
 		1100
@@ -5516,6 +5538,7 @@ thingtypes
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1105
 		{
@@ -5528,6 +5551,7 @@ thingtypes
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1106
 		{
@@ -5540,23 +5564,25 @@ thingtypes
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1107
 		{
 			title = "Chain Spawnpoint";
-			sprite = "BMCHA0";
+			sprite = "BMCHB0";
 			width = 17;
 			height = 34;
 			flags8text = "[8] Double size";
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1108
 		{
 			arrow = 1;
 			title = "Hidden Chain Spawnpoint";
-			sprite = "internal:chain3";
+			sprite = "SMCHA0";
 			width = 17;
 			height = 34;
 			flags8text = "[8] Double size";
@@ -5572,6 +5598,7 @@ thingtypes
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1110
 		{
@@ -5583,6 +5610,7 @@ thingtypes
 			angletext = "Tag";
 			parametertext = "Spokes";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1111
 		{
@@ -5736,7 +5764,7 @@ thingtypes
 
 	aridcanyon
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Arid Canyon";
 
 		1200
@@ -5764,6 +5792,7 @@ thingtypes
 			height = 16;
 			angletext = "Tag";
 			fixedrotation = 1;
+			tagthing = true;
 		}
 		1203
 		{
@@ -5955,7 +5984,7 @@ thingtypes
 
 	redvolcano
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Red Volcano";
 
 		1300
@@ -6055,7 +6084,7 @@ thingtypes
 
 	botanicserenity
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Botanic Serenity";
 		width = 16;
 		height = 32;
@@ -6298,7 +6327,7 @@ thingtypes
 
 	azuretemple
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Azure Temple";
 
 		1500
@@ -6374,7 +6403,7 @@ thingtypes
 
 	dreamhill
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Dream Hill";
 
 		1600
@@ -6402,8 +6431,8 @@ thingtypes
 
 	nightstrk
 	{
-		color = 13; // Pink
-		title = "NiGHTS Track";
+		color = 16; // Light Pink
+		title = "NiGHTS Track & Basics";
 		width = 8;
 		height = 4096;
 		sprite = "UNKNA0";
@@ -6438,6 +6467,19 @@ thingtypes
 			flagsvaluetext = "Order";
 			parametertext = "Mare";
 		}
+		1703
+		{
+			title = "Ideya Drone";
+			sprite = "NDRNA1";
+			width = 16;
+			height = 56;
+			flags1text = "[1] Align player to middle";
+			flags4text = "[4] Align player to top";
+			flags8text = "[8] Die upon time up";
+			angletext = "Time limit";
+			fixedrotation = 1;
+			parametertext = "Height";
+		}
 		1710
 		{
 			title = "Ideya Capture";
@@ -6455,20 +6497,6 @@ thingtypes
 		title = "NiGHTS Items";
 		width = 16;
 		height = 32;
-
-		1703
-		{
-			title = "Ideya Drone";
-			sprite = "NDRNA1";
-			width = 16;
-			height = 56;
-			flags1text = "[1] Align player to middle";
-			flags4text = "[4] Align player to top";
-			flags8text = "[8] Die upon time up";
-			angletext = "Time limit";
-			fixedrotation = 1;
-			parametertext = "Height";
-		}
 		1704
 		{
 			arrow = 1;
@@ -6479,13 +6507,12 @@ thingtypes
 			unflippable = true;
 			flagsvaluetext = "Pitch";
 			angletext = "Yaw";
-			fixedrotation = 1;
 		}
 		1705
 		{
 			arrow = 1;
 			title = "Hoop (Generic)";
-			sprite = "HOOPA0";
+			sprite = "internal:nightshoop";
 			width = 80;
 			height = 160;
 			unflippable = true;
@@ -6502,7 +6529,6 @@ thingtypes
 			height = 24;
 			flags8height = 24;
 			flags8text = "[8] Float";
-			unflippable = true;
 		}
 		1707
 		{
@@ -6547,7 +6573,7 @@ thingtypes
 			flags2text = "[2] Radius +32";
 			flags4text = "[4] Radius +64";
 			flags8text = "[8] Radius +128";
-			sprite = "HOOPA0";
+			sprite = "internal:nightshoop";
 			width = 80;
 			height = 160;
 			unflippable = true;
@@ -6657,7 +6683,7 @@ thingtypes
 
 	christmasdisco
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Christmas & Disco";
 
 		1850
@@ -6760,7 +6786,7 @@ thingtypes
 
 	stalagmites
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Stalagmites";
 		width = 16;
 		height = 40;
@@ -6839,7 +6865,7 @@ thingtypes
 
 	hauntedheights
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Haunted Heights";
 
 		2000
@@ -6928,7 +6954,7 @@ thingtypes
 
 	frozenhillside
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Frozen Hillside";
 
 		2100
@@ -6979,7 +7005,7 @@ thingtypes
 
 	tutorial
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Tutorial";
 
 		799
@@ -6990,10 +7016,11 @@ thingtypes
 			height = 144;
 			parametertext = "Start frame";
 		}
+	}
 
 	flickies
 	{
-		color = 10; // Green
+		color = 3; // Teal
 		title = "Flickies";
 		width = 8;
 		height = 20;
-- 
GitLab


From b2313aa4bda91da0bf4575c67bb5eeac4647f0c7 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Mon, 22 May 2023 17:55:04 +0200
Subject: [PATCH 092/518] Rewrite F_SkyScroll to fix overflows

---
 src/d_clisrv.c |  2 +-
 src/f_finale.c | 73 +++++++++++++++++++++-----------------------------
 src/f_finale.h |  4 +--
 src/g_game.c   |  6 +++--
 src/m_menu.c   |  6 ++---
 5 files changed, 40 insertions(+), 51 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 94384e46ab..839f70f7eb 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2486,7 +2486,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 		{
 			if (!snake)
 			{
-				F_MenuPresTicker(true); // title sky
+				F_MenuPresTicker(); // title sky
 				F_TitleScreenTicker(true);
 				F_TitleScreenDrawer();
 			}
diff --git a/src/f_finale.c b/src/f_finale.c
index 6560c24f10..47d4627ec5 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -63,7 +63,6 @@ static tic_t stoptimer;
 static boolean keypressed = false;
 
 // (no longer) De-Demo'd Title Screen
-static INT32 menuanimtimer; // Title screen: background animation timing
 mobj_t *titlemapcameraref = NULL;
 
 // menu presentation state
@@ -75,6 +74,8 @@ INT32 curbgyspeed;
 boolean curbghide;
 boolean hidetitlemap;		// WARNING: set to false by M_SetupNextMenu and M_ClearMenus
 
+static fixed_t curbgx = 0;
+static fixed_t curbgy = 0;
 static UINT8  curDemo = 0;
 static UINT32 demoDelayLeft;
 static UINT32 demoIdleLeft;
@@ -2258,7 +2259,8 @@ void F_GameEndTicker(void)
 
 void F_InitMenuPresValues(void)
 {
-	menuanimtimer = 0;
+	curbgx = 0;
+	curbgy = 0;
 	prevMenuId = 0;
 	activeMenuId = MainDef.menuid;
 
@@ -2291,17 +2293,11 @@ void F_InitMenuPresValues(void)
 //
 // F_SkyScroll
 //
-void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname)
+void F_SkyScroll(const char *patchname)
 {
-	INT32 xscrolled, x, xneg = (scrollxspeed > 0) - (scrollxspeed < 0), tilex;
-	INT32 yscrolled, y, yneg = (scrollyspeed > 0) - (scrollyspeed < 0), tiley;
-	boolean xispos = (scrollxspeed >= 0), yispos = (scrollyspeed >= 0);
+	INT32 x, basey = 0;
 	INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
-	INT16 patwidth, patheight;
-	INT32 pw, ph; // scaled by dupz
 	patch_t *pat;
-	INT32 i, j;
-	fixed_t fracmenuanimtimer, xscrolltimer, yscrolltimer;
 
 	if (rendermode == render_none)
 		return;
@@ -2312,43 +2308,34 @@ void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname)
 		return;
 	}
 
-	if (!scrollxspeed && !scrollyspeed)
+	pat = W_CachePatchName(patchname, PU_PATCH_LOWPRIORITY);
+
+	if (!curbgxspeed && !curbgyspeed)
 	{
-		V_DrawPatchFill(W_CachePatchName(patchname, PU_PATCH_LOWPRIORITY));
+		V_DrawPatchFill(pat);
+		W_UnlockCachedPatch(pat);
 		return;
 	}
 
-	pat = W_CachePatchName(patchname, PU_PATCH_LOWPRIORITY);
-
-	patwidth = pat->width;
-	patheight = pat->height;
-	pw = patwidth * dupz;
-	ph = patheight * dupz;
+	// Modulo the background scrolling to prevent jumps from integer overflows
+	// We already load the background patch here, so we can modulo it here
+	// to avoid also having to load the patch in F_MenuPresTicker
+	curbgx %= pat->width  * 16;
+	curbgy %= pat->height * 16;
 
-	tilex = max(FixedCeil(FixedDiv(vid.width, pw)) >> FRACBITS, 1)+2; // one tile on both sides of center
-	tiley = max(FixedCeil(FixedDiv(vid.height, ph)) >> FRACBITS, 1)+2;
+	// Ooh, fancy frame interpolation
+	x     = ((curbgx*dupz) + FixedInt((rendertimefrac-FRACUNIT) * curbgxspeed*dupz)) / 16;
+	basey = ((curbgy*dupz) + FixedInt((rendertimefrac-FRACUNIT) * curbgyspeed*dupz)) / 16;
 
-	fracmenuanimtimer = (menuanimtimer * FRACUNIT) - (FRACUNIT - rendertimefrac);
-	xscrolltimer = ((fracmenuanimtimer*scrollxspeed)/16 + patwidth*xneg*FRACUNIT) % (patwidth * FRACUNIT);
-	yscrolltimer = ((fracmenuanimtimer*scrollyspeed)/16 + patheight*yneg*FRACUNIT) % (patheight * FRACUNIT);
+	if (x     > 0) // Make sure that we don't leave the left or top sides empty
+		x     -= pat->width  * dupz;
+	if (basey > 0)
+		basey -= pat->height * dupz;
 
-	// coordinate offsets
-	xscrolled = FixedInt(xscrolltimer * dupz);
-	yscrolled = FixedInt(yscrolltimer * dupz);
-
-	for (x = (xispos) ? -pw*(tilex-1)+pw : 0, i = 0;
-		i < tilex;
-		x += pw, i++)
+	for (; x < vid.width; x += pat->width * dupz)
 	{
-		for (y = (yispos) ? -ph*(tiley-1)+ph : 0, j = 0;
-			j < tiley;
-			y += ph, j++)
-		{
-			V_DrawScaledPatch(
-				(xispos) ? xscrolled - x : x + xscrolled,
-				(yispos) ? yscrolled - y : y + yscrolled,
-				V_NOSCALESTART, pat);
-		}
+		for (INT32 y = basey; y < vid.height; y += pat->height * dupz)
+			V_DrawScaledPatch(x, y, V_NOSCALESTART, pat);
 	}
 
 	W_UnlockCachedPatch(pat);
@@ -2671,7 +2658,7 @@ void F_TitleScreenDrawer(void)
 	if (curbgcolor >= 0)
 		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, curbgcolor);
 	else if (!curbghide || !titlemapinaction || gamestate == GS_WAITINGPLAYERS)
-		F_SkyScroll(curbgxspeed, curbgyspeed, curbgname);
+		F_SkyScroll(curbgname);
 
 	// Don't draw outside of the title screen, or if the patch isn't there.
 	if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS)
@@ -3426,10 +3413,10 @@ luahook:
 
 // separate animation timer for backgrounds, since we also count
 // during GS_TIMEATTACK
-void F_MenuPresTicker(boolean run)
+void F_MenuPresTicker(void)
 {
-	if (run)
-		menuanimtimer++;
+	curbgx += curbgxspeed;
+	curbgy += curbgyspeed;
 }
 
 // (no longer) De-Demo'd Title Screen
diff --git a/src/f_finale.h b/src/f_finale.h
index 6ea1b5537c..7f53bfbad5 100644
--- a/src/f_finale.h
+++ b/src/f_finale.h
@@ -40,7 +40,7 @@ void F_TextPromptTicker(void);
 void F_GameEndDrawer(void);
 void F_IntroDrawer(void);
 void F_TitleScreenDrawer(void);
-void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname);
+void F_SkyScroll(const char *patchname);
 
 void F_GameEvaluationDrawer(void);
 void F_StartGameEvaluation(void);
@@ -131,7 +131,7 @@ extern UINT16 curtttics;
 #define TITLEBACKGROUNDACTIVE (curfadevalue >= 0 || curbgname[0])
 
 void F_InitMenuPresValues(void);
-void F_MenuPresTicker(boolean run);
+void F_MenuPresTicker(void);
 
 //
 // WIPE
diff --git a/src/g_game.c b/src/g_game.c
index 0d5181b07a..2935daa550 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2390,7 +2390,8 @@ void G_Ticker(boolean run)
 			break;
 
 		case GS_TIMEATTACK:
-			F_MenuPresTicker(run);
+			if (run)
+				F_MenuPresTicker();
 			break;
 
 		case GS_INTRO:
@@ -2438,7 +2439,8 @@ void G_Ticker(boolean run)
 				// then intentionally fall through
 			/* FALLTHRU */
 		case GS_WAITINGPLAYERS:
-			F_MenuPresTicker(run);
+			if (run)
+				F_MenuPresTicker();
 			F_TitleScreenTicker(run);
 			break;
 
diff --git a/src/m_menu.c b/src/m_menu.c
index e879e9c14d..7efe81f432 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -5964,7 +5964,7 @@ static void M_DrawLevelPlatterMenu(void)
 			V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, curbgcolor);
 		else if (!curbghide || !titlemapinaction)
 		{
-			F_SkyScroll(curbgxspeed, curbgyspeed, curbgname);
+			F_SkyScroll(curbgname);
 			// Draw and animate foreground
 			if (!strncmp("RECATKBG", curbgname, 8))
 				M_DrawRecordAttackForeground();
@@ -6226,7 +6226,7 @@ static void M_DrawMessageMenu(void)
 			}
 			else
 			{
-				F_SkyScroll(curbgxspeed, curbgyspeed, curbgname);
+				F_SkyScroll(curbgname);
 				if (!strncmp("RECATKBG", curbgname, 8))
 					M_DrawRecordAttackForeground();
 			}
@@ -9873,7 +9873,7 @@ void M_DrawTimeAttackMenu(void)
 		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, curbgcolor);
 	else if (!curbghide || !titlemapinaction)
 	{
-		F_SkyScroll(curbgxspeed, curbgyspeed, curbgname);
+		F_SkyScroll(curbgname);
 		// Draw and animate foreground
 		if (!strncmp("RECATKBG", curbgname, 8))
 			M_DrawRecordAttackForeground();
-- 
GitLab


From 3f5e7ff0d06b2a0aa7fdb1c42e9ebdc34b13129c Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Mon, 22 May 2023 20:53:17 +0200
Subject: [PATCH 093/518] Interpolate shadows when scaling mobjs

---
 src/hardware/hw_main.c | 2 ++
 src/r_things.c         | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 343fa43bf8..6e72145713 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -3606,6 +3606,8 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale)
 
 	scalemul = FixedMul(FRACUNIT - floordiff/640, scale);
 	scalemul = FixedMul(scalemul, (thing->radius*2) / gpatch->height);
+	if ((thing->scale != thing->old_scale) && (thing->scale >= FRACUNIT/1024)) // Interpolate shadows when scaling mobjs
+		scalemul = FixedMul(scalemul, FixedDiv(interp.scale, thing->scale));
 
 	fscale = FIXED_TO_FLOAT(scalemul);
 	fx = FIXED_TO_FLOAT(interp.x);
diff --git a/src/r_things.c b/src/r_things.c
index 2916482fb2..62d97ff330 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1324,6 +1324,8 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 	if (trans >= 9) return;
 
 	scalemul = FixedMul(FRACUNIT - floordiff/640, scale);
+	if ((thing->scale != thing->old_scale) && (thing->scale >= FRACUNIT/1024)) // Interpolate shadows when scaling mobjs
+		scalemul = FixedMul(scalemul, FixedDiv(interp.scale, thing->scale));
 
 	patch = W_CachePatchName("DSHADOW", PU_SPRITE);
 	xscale = FixedDiv(projection, tz);
-- 
GitLab


From 05f1a9a2c8a0da9ed4ab00a0abac3d439b3ec08c Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Mon, 29 May 2023 14:38:22 +0200
Subject: [PATCH 094/518] Suppress libdivide warnings in GCC/Clang

---
 src/libdivide.h    | 9 +++++++++
 src/r_draw8_npo2.c | 9 +++++++++
 2 files changed, 18 insertions(+)

diff --git a/src/libdivide.h b/src/libdivide.h
index 1a589c7e55..71a5ae6be1 100644
--- a/src/libdivide.h
+++ b/src/libdivide.h
@@ -581,6 +581,11 @@ static inline void libdivide_u128_shift(uint64_t *u1, uint64_t *u0, int32_t sign
 
 ////////// UINT32
 
+#if defined(__GNUC__) || defined(__clang__) // Suppress intentional compiler warnings
+    #pragma GCC diagnostic push
+    #pragma GCC diagnostic ignored "-Waggregate-return"
+#endif
+
 static inline struct libdivide_u32_t libdivide_internal_u32_gen(uint32_t d, int branchfree) {
     struct libdivide_u32_t result;
     uint32_t floor_log_2_d;
@@ -647,6 +652,10 @@ struct libdivide_u32_t libdivide_u32_gen(uint32_t d) {
     return ret;
 }*/
 
+#if defined(__GNUC__) || defined(__clang__) // Stop suppressing intentional compiler warnings
+    #pragma GCC diagnostic pop
+#endif
+
 uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom) {
     uint8_t more = denom->more;
     if (!denom->magic) {
diff --git a/src/r_draw8_npo2.c b/src/r_draw8_npo2.c
index faf1cdba80..91f3b06c42 100644
--- a/src/r_draw8_npo2.c
+++ b/src/r_draw8_npo2.c
@@ -18,6 +18,11 @@
 #define SPANSIZE 16
 #define INVSPAN 0.0625f
 
+#if defined(__GNUC__) || defined(__clang__) // Suppress intentional libdivide compiler warnings - Also added to libdivide.h
+    #pragma GCC diagnostic push
+    #pragma GCC diagnostic ignored "-Waggregate-return"
+#endif
+
 /**	\brief The R_DrawSpan_NPO2_8 function
 	Draws the actual span.
 */
@@ -1572,3 +1577,7 @@ void R_DrawTiltedWaterSpan_NPO2_8(void)
 	}
 #endif
 }
+
+#if defined(__GNUC__) || defined(__clang__) // Stop suppressing intentional libdivide compiler warnings
+    #pragma GCC diagnostic pop
+#endif
-- 
GitLab


From 58b3b8e0750effcc1da5149305c8781003289366 Mon Sep 17 00:00:00 2001
From: SMS Alfredo <65426124+SMS-Alfredo@users.noreply.github.com>
Date: Wed, 31 May 2023 22:33:16 -0500
Subject: [PATCH 095/518] Fix Flung Spheres not using the Blue Sphere
 collection effect

---
 src/p_inter.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_inter.c b/src/p_inter.c
index 873448dcd6..86fc2a6ae4 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -563,7 +563,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			special->momx = special->momy = special->momz = 0;
 			P_GivePlayerSpheres(player, 1);
 
-			if (special->type == MT_BLUESPHERE)
+			if (special->type == MT_BLUESPHERE || special->type == MT_FLINGBLUESPHERE)
 			{
 				special->destscale = ((player->powers[pw_carry] == CR_NIGHTSMODE) ? 4 : 2)*special->scale;
 				if (states[special->info->deathstate].tics > 0)
-- 
GitLab


From 49fa46d80efd95047f05834bde41dbbd7311d418 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 27 Feb 2022 16:52:05 -0500
Subject: [PATCH 096/518] Online emblems

Currently, emblems share with everyone. Will add an option to toggle this.
---
 src/d_clisrv.c     |   3 +
 src/d_main.c       |   9 +-
 src/d_netcmd.c     |  14 +--
 src/deh_soc.c      |   2 +-
 src/dehacked.c     |   2 +-
 src/doomstat.h     |  54 ----------
 src/f_finale.c     |  43 ++++----
 src/g_game.c       | 251 ++++++++++++++++++++++-----------------------
 src/g_game.h       |  25 ++---
 src/hu_stuff.c     |   2 +-
 src/m_cheat.c      |   4 +-
 src/m_cond.c       | 248 +++++++++++++++++++++++++++++---------------
 src/m_cond.h       | 123 ++++++++++++++++++----
 src/m_menu.c       | 155 +++++++++++++++-------------
 src/m_random.c     |   5 +-
 src/p_inter.c      |  18 +++-
 src/p_mobj.c       |  19 ++--
 src/p_saveg.c      | 225 ++++++++++++++++++++++++++++++++++++++++
 src/p_setup.c      |  11 +-
 src/p_spec.c       |   7 +-
 src/p_tick.c       |   5 +-
 src/r_skins.c      |  13 +--
 src/s_sound.c      |   5 +-
 src/sdl/i_system.c |   6 +-
 src/st_stuff.c     |   9 +-
 src/y_inter.c      |  16 +--
 26 files changed, 824 insertions(+), 450 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 64e5aff6b2..18f0179f80 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -3645,6 +3645,9 @@ void SV_ResetServer(void)
 
 	CV_RevertNetVars();
 
+	// Ensure synched when creating a new server
+	M_CopyGameData(serverGamedata, clientGamedata);
+
 	DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
 }
 
diff --git a/src/d_main.c b/src/d_main.c
index 6506c9d4ee..5861f98865 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -1350,6 +1350,9 @@ void D_SRB2Main(void)
 	CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
 	Z_Init();
 
+	clientGamedata = M_NewGameDataStruct();
+	serverGamedata = M_NewGameDataStruct();
+
 	// Do this up here so that WADs loaded through the command line can use ExecCfg
 	COM_Init();
 
@@ -1479,7 +1482,9 @@ void D_SRB2Main(void)
 		// confusion issues when loading mods.
 		strlcpy(gamedatafilename, M_GetNextParm(), sizeof gamedatafilename);
 	}
-	G_LoadGameData();
+
+	G_LoadGameData(clientGamedata);
+	M_CopyGameData(serverGamedata, clientGamedata);
 
 #if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL)
 	VID_PrepareModeList(); // Regenerate Modelist according to cv_fullscreen
@@ -1710,7 +1715,7 @@ void D_SRB2Main(void)
 			// ... unless you're in a dedicated server.  Yes, technically this means you can view any level by
 			// running a dedicated server and joining it yourself, but that's better than making dedicated server's
 			// lives hell.
-			else if (!dedicated && M_MapLocked(pstartmap))
+			else if (!dedicated && M_MapLocked(pstartmap, serverGamedata))
 				I_Error("You need to unlock this level before you can warp to it!\n");
 			else
 			{
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 80a084e167..af44e53d63 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -2036,7 +2036,7 @@ static void Command_Map_f(void)
 	// ... unless you're in a dedicated server.  Yes, technically this means you can view any level by
 	// running a dedicated server and joining it yourself, but that's better than making dedicated server's
 	// lives hell.
-	if (!dedicated && M_MapLocked(newmapnum))
+	if (!dedicated && M_MapLocked(newmapnum, serverGamedata))
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
 		Z_Free(realmapname);
@@ -3945,18 +3945,12 @@ void ItemFinder_OnChange(void)
 	if (!cv_itemfinder.value)
 		return; // it's fine.
 
-	if (!M_SecretUnlocked(SECRET_ITEMFINDER))
+	if (!M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata))
 	{
 		CONS_Printf(M_GetText("You haven't earned this yet.\n"));
 		CV_StealthSetValue(&cv_itemfinder, 0);
 		return;
 	}
-	else if (netgame || multiplayer)
-	{
-		CONS_Printf(M_GetText("This only works in single player.\n"));
-		CV_StealthSetValue(&cv_itemfinder, 0);
-		return;
-	}
 }
 
 /** Deals with a pointlimit change by printing the change to the console.
@@ -4305,7 +4299,7 @@ void D_GameTypeChanged(INT32 lastgametype)
 
 static void Ringslinger_OnChange(void)
 {
-	if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && cv_ringslinger.value && !cv_debug)
+	if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !netgame && cv_ringslinger.value && !cv_debug)
 	{
 		CONS_Printf(M_GetText("You haven't earned this yet.\n"));
 		CV_StealthSetValue(&cv_ringslinger, 0);
@@ -4318,7 +4312,7 @@ static void Ringslinger_OnChange(void)
 
 static void Gravity_OnChange(void)
 {
-	if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && !cv_debug
+	if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !netgame && !cv_debug
 		&& strcmp(cv_gravity.string, cv_gravity.defaultvalue))
 	{
 		CONS_Printf(M_GetText("You haven't earned this yet.\n"));
diff --git a/src/deh_soc.c b/src/deh_soc.c
index 3a3942c14f..81adbc9d21 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -3849,7 +3849,7 @@ void readmaincfg(MYFILE *f)
 				if (!GoodDataFileName(word2))
 					I_Error("Maincfg: bad data file name '%s'\n", word2);
 
-				G_SaveGameData();
+				G_SaveGameData(clientGamedata);
 				strlcpy(gamedatafilename, word2, sizeof (gamedatafilename));
 				strlwr(gamedatafilename);
 				savemoddata = true;
diff --git a/src/dehacked.c b/src/dehacked.c
index 17768eb7f4..fd2a701715 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -575,7 +575,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 	} // end while
 
 	if (gamedataadded)
-		G_LoadGameData();
+		G_LoadGameData(clientGamedata);
 
 	if (gamestate == GS_TITLESCREEN)
 	{
diff --git a/src/doomstat.h b/src/doomstat.h
index 847c10b8c9..5875bd01f8 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -132,8 +132,6 @@ extern INT32 postimgparam2;
 extern INT32 viewwindowx, viewwindowy;
 extern INT32 viewwidth, scaledviewwidth;
 
-extern boolean gamedataloaded;
-
 // Player taking events, and displaying.
 extern INT32 consoleplayer;
 extern INT32 displayplayer;
@@ -495,8 +493,6 @@ typedef struct
 extern tolinfo_t TYPEOFLEVEL[NUMTOLNAMES];
 extern UINT32 lastcustomtol;
 
-extern tic_t totalplaytime;
-
 extern boolean stagefailed;
 
 // Emeralds stored as bits to throw savegame hackers off.
@@ -515,52 +511,6 @@ extern INT32 luabanks[NUM_LUABANKS];
 
 extern INT32 nummaprings; //keep track of spawned rings/coins
 
-/** Time attack information, currently a very small structure.
-  */
-typedef struct
-{
-	tic_t time;   ///< Time in which the level was finished.
-	UINT32 score; ///< Score when the level was finished.
-	UINT16 rings; ///< Rings when the level was finished.
-} recorddata_t;
-
-/** Setup for one NiGHTS map.
-  * These are dynamically allocated because I am insane
-  */
-#define GRADE_F 0
-#define GRADE_E 1
-#define GRADE_D 2
-#define GRADE_C 3
-#define GRADE_B 4
-#define GRADE_A 5
-#define GRADE_S 6
-
-typedef struct
-{
-	// 8 mares, 1 overall (0)
-	UINT8	nummares;
-	UINT32	score[9];
-	UINT8	grade[9];
-	tic_t	time[9];
-} nightsdata_t;
-
-extern nightsdata_t *nightsrecords[NUMMAPS];
-extern recorddata_t *mainrecords[NUMMAPS];
-
-// mapvisited is now a set of flags that says what we've done in the map.
-#define MV_VISITED      1
-#define MV_BEATEN       2
-#define MV_ALLEMERALDS  4
-#define MV_ULTIMATE     8
-#define MV_PERFECT     16
-#define MV_PERFECTRA   32
-#define MV_MAX         63 // used in gamedata check, update whenever MV's are added
-#define MV_MP         128
-extern UINT8 mapvisited[NUMMAPS];
-
-// Temporary holding place for nights data for the current map
-extern nightsdata_t ntemprecords;
-
 extern UINT32 token; ///< Number of tokens collected in a level
 extern UINT32 tokenlist; ///< List of tokens collected
 extern boolean gottoken; ///< Did you get a token? Used for end of act
@@ -616,10 +566,6 @@ extern INT32 cheats;
 
 extern tic_t hidetime;
 
-extern UINT32 timesBeaten; // # of times the game has been beaten.
-extern UINT32 timesBeatenWithEmeralds;
-extern UINT32 timesBeatenUltimate;
-
 // ===========================
 // Internal parameters, fixed.
 // ===========================
diff --git a/src/f_finale.c b/src/f_finale.c
index 68b2641a1d..4bb640d501 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -1411,7 +1411,7 @@ boolean F_CreditResponder(event_t *event)
 			break;
 	}
 
-	if (!(timesBeaten) && !(netgame || multiplayer) && !cv_debug)
+	if (!(serverGamedata->timesBeaten) && !(netgame || multiplayer) && !cv_debug)
 		return false;
 
 	if (event->type != ev_keydown)
@@ -1573,23 +1573,17 @@ void F_GameEvaluationDrawer(void)
 #if 0 // the following looks like hot garbage the more unlockables we add, and we now have a lot of unlockables
 	if (finalecount >= 5*TICRATE)
 	{
+		INT32 startcoord = 32;
 		V_DrawString(8, 16, V_YELLOWMAP, "Unlocked:");
 
-		if (netgame)
-			V_DrawString(8, 96, V_YELLOWMAP, "Multiplayer games\ncan't unlock\nextras!");
-		else
+		for (i = 0; i < MAXUNLOCKABLES; i++)
 		{
-			INT32 startcoord = 32;
-
-			for (i = 0; i < MAXUNLOCKABLES; i++)
+			if (unlockables[i].conditionset && unlockables[i].conditionset < MAXCONDITIONSETS
+				&& unlockables[i].type && !unlockables[i].nocecho)
 			{
-				if (unlockables[i].conditionset && unlockables[i].conditionset < MAXCONDITIONSETS
-					&& unlockables[i].type && !unlockables[i].nocecho)
-				{
-					if (unlockables[i].unlocked)
-						V_DrawString(8, startcoord, 0, unlockables[i].name);
-					startcoord += 8;
-				}
+				if (clientGamedata->unlocked[i])
+					V_DrawString(8, startcoord, 0, unlockables[i].name);
+				startcoord += 8;
 			}
 		}
 	}
@@ -1657,18 +1651,27 @@ void F_GameEvaluationTicker(void)
 		}
 		else
 		{
-			++timesBeaten;
+			serverGamedata->timesBeaten++;
+			clientGamedata->timesBeaten++;
 
 			if (ALL7EMERALDS(emeralds))
-				++timesBeatenWithEmeralds;
+			{
+				serverGamedata->timesBeatenWithEmeralds++;
+				clientGamedata->timesBeatenWithEmeralds++;
+			}
 
 			if (ultimatemode)
-				++timesBeatenUltimate;
+			{
+				serverGamedata->timesBeatenUltimate++;
+				clientGamedata->timesBeatenUltimate++;
+			}
+
+			M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
 
-			if (M_UpdateUnlockablesAndExtraEmblems())
+			if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
 				S_StartSound(NULL, sfx_s3k68);
 
-			G_SaveGameData();
+			G_SaveGameData(clientGamedata);
 		}
 	}
 }
@@ -2183,7 +2186,7 @@ void F_EndingDrawer(void)
 			//colset(linkmap,  164, 165, 169); -- the ideal purple colour to represent a clicked in-game link, but not worth it just for a soundtest-controlled secret
 			V_DrawCenteredString(BASEVIDWIDTH/2, 8, V_ALLOWLOWERCASE|(trans<<V_ALPHASHIFT), str);
 			V_DrawCharacter(32, BASEVIDHEIGHT-16, '>'|(trans<<V_ALPHASHIFT), false);
-			V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
+			V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((serverGamedata->timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
 		}
 
 		if (finalecount > STOPPINGPOINT-(20+(2*TICRATE)))
diff --git a/src/g_game.c b/src/g_game.c
index 7386b2a84c..854bf9bbb4 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -185,18 +185,6 @@ INT32 tokenbits; // Used for setting token bits
 // Old Special Stage
 INT32 sstimer; // Time allotted in the special stage
 
-tic_t totalplaytime;
-boolean gamedataloaded = false;
-
-// Time attack data for levels
-// These are dynamically allocated for space reasons now
-recorddata_t *mainrecords[NUMMAPS]   = {NULL};
-nightsdata_t *nightsrecords[NUMMAPS] = {NULL};
-UINT8 mapvisited[NUMMAPS];
-
-// Temporary holding place for nights data for the current map
-nightsdata_t ntemprecords;
-
 UINT32 bluescore, redscore; // CTF and Team Match team scores
 
 // ring count... for PERFECT!
@@ -252,11 +240,6 @@ INT32 cheats; //for multiplayer cheat commands
 
 tic_t hidetime;
 
-// Grading
-UINT32 timesBeaten;
-UINT32 timesBeatenWithEmeralds;
-UINT32 timesBeatenUltimate;
-
 typedef struct joystickvector2_s
 {
 	INT32 xaxis;
@@ -452,86 +435,86 @@ INT16 rw_maximums[NUM_WEAPONS] =
 };
 
 // Allocation for time and nights data
-void G_AllocMainRecordData(INT16 i)
+void G_AllocMainRecordData(INT16 i, gamedata_t *data)
 {
-	if (!mainrecords[i])
-		mainrecords[i] = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL);
-	memset(mainrecords[i], 0, sizeof(recorddata_t));
+	if (!data->mainrecords[i])
+		data->mainrecords[i] = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL);
+	memset(data->mainrecords[i], 0, sizeof(recorddata_t));
 }
 
-void G_AllocNightsRecordData(INT16 i)
+void G_AllocNightsRecordData(INT16 i, gamedata_t *data)
 {
-	if (!nightsrecords[i])
-		nightsrecords[i] = Z_Malloc(sizeof(nightsdata_t), PU_STATIC, NULL);
-	memset(nightsrecords[i], 0, sizeof(nightsdata_t));
+	if (!data->nightsrecords[i])
+		data->nightsrecords[i] = Z_Malloc(sizeof(nightsdata_t), PU_STATIC, NULL);
+	memset(data->nightsrecords[i], 0, sizeof(nightsdata_t));
 }
 
 // MAKE SURE YOU SAVE DATA BEFORE CALLING THIS
-void G_ClearRecords(void)
+void G_ClearRecords(gamedata_t *data)
 {
 	INT16 i;
 	for (i = 0; i < NUMMAPS; ++i)
 	{
-		if (mainrecords[i])
+		if (data->mainrecords[i])
 		{
-			Z_Free(mainrecords[i]);
-			mainrecords[i] = NULL;
+			Z_Free(data->mainrecords[i]);
+			data->mainrecords[i] = NULL;
 		}
-		if (nightsrecords[i])
+		if (data->nightsrecords[i])
 		{
-			Z_Free(nightsrecords[i]);
-			nightsrecords[i] = NULL;
+			Z_Free(data->nightsrecords[i]);
+			data->nightsrecords[i] = NULL;
 		}
 	}
 }
 
 // For easy retrieval of records
-UINT32 G_GetBestScore(INT16 map)
+UINT32 G_GetBestScore(INT16 map, gamedata_t *data)
 {
-	if (!mainrecords[map-1])
+	if (!data->mainrecords[map-1])
 		return 0;
 
-	return mainrecords[map-1]->score;
+	return data->mainrecords[map-1]->score;
 }
 
-tic_t G_GetBestTime(INT16 map)
+tic_t G_GetBestTime(INT16 map, gamedata_t *data)
 {
-	if (!mainrecords[map-1] || mainrecords[map-1]->time <= 0)
+	if (!data->mainrecords[map-1] || data->mainrecords[map-1]->time <= 0)
 		return (tic_t)UINT32_MAX;
 
-	return mainrecords[map-1]->time;
+	return data->mainrecords[map-1]->time;
 }
 
-UINT16 G_GetBestRings(INT16 map)
+UINT16 G_GetBestRings(INT16 map, gamedata_t *data)
 {
-	if (!mainrecords[map-1])
+	if (!data->mainrecords[map-1])
 		return 0;
 
-	return mainrecords[map-1]->rings;
+	return data->mainrecords[map-1]->rings;
 }
 
-UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare)
+UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare, gamedata_t *data)
 {
-	if (!nightsrecords[map-1])
+	if (!data->nightsrecords[map-1])
 		return 0;
 
-	return nightsrecords[map-1]->score[mare];
+	return data->nightsrecords[map-1]->score[mare];
 }
 
-tic_t G_GetBestNightsTime(INT16 map, UINT8 mare)
+tic_t G_GetBestNightsTime(INT16 map, UINT8 mare, gamedata_t *data)
 {
-	if (!nightsrecords[map-1] || nightsrecords[map-1]->time[mare] <= 0)
+	if (!data->nightsrecords[map-1] || data->nightsrecords[map-1]->time[mare] <= 0)
 		return (tic_t)UINT32_MAX;
 
-	return nightsrecords[map-1]->time[mare];
+	return data->nightsrecords[map-1]->time[mare];
 }
 
-UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare)
+UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare, gamedata_t *data)
 {
-	if (!nightsrecords[map-1])
+	if (!data->nightsrecords[map-1])
 		return 0;
 
-	return nightsrecords[map-1]->grade[mare];
+	return data->nightsrecords[map-1]->grade[mare];
 }
 
 // For easy adding of NiGHTS records
@@ -553,7 +536,7 @@ void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare)
 // Update replay files/data, etc. for Record Attack
 // See G_SetNightsRecords for NiGHTS Attack.
 //
-static void G_UpdateRecordReplays(void)
+static void G_UpdateRecordReplays(gamedata_t *data)
 {
 	const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
 	char *gpath;
@@ -561,17 +544,17 @@ static void G_UpdateRecordReplays(void)
 	UINT8 earnedEmblems;
 
 	// Record new best time
-	if (!mainrecords[gamemap-1])
-		G_AllocMainRecordData(gamemap-1);
+	if (!data->mainrecords[gamemap-1])
+		G_AllocMainRecordData(gamemap-1, data);
 
-	if (players[consoleplayer].score > mainrecords[gamemap-1]->score)
-		mainrecords[gamemap-1]->score = players[consoleplayer].score;
+	if (players[consoleplayer].score > data->mainrecords[gamemap-1]->score)
+		data->mainrecords[gamemap-1]->score = players[consoleplayer].score;
 
-	if ((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time))
-		mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
+	if ((data->mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < data->mainrecords[gamemap-1]->time))
+		data->mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
 
-	if ((UINT16)(players[consoleplayer].rings) > mainrecords[gamemap-1]->rings)
-		mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
+	if ((UINT16)(players[consoleplayer].rings) > data->mainrecords[gamemap-1]->rings)
+		data->mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
 
 	// Save demo!
 	bestdemo[255] = '\0';
@@ -627,14 +610,14 @@ static void G_UpdateRecordReplays(void)
 	free(gpath);
 
 	// Check emblems when level data is updated
-	if ((earnedEmblems = M_CheckLevelEmblems()))
+	if ((earnedEmblems = M_CheckLevelEmblems(data)))
 		CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
 
 	// Update timeattack menu's replay availability.
 	Nextmap_OnChange();
 }
 
-void G_SetNightsRecords(void)
+void G_SetNightsRecords(gamedata_t *data)
 {
 	INT32 i;
 	UINT32 totalscore = 0;
@@ -675,9 +658,9 @@ void G_SetNightsRecords(void)
 	{
 		nightsdata_t *maprecords;
 
-		if (!nightsrecords[gamemap-1])
-			G_AllocNightsRecordData(gamemap-1);
-		maprecords = nightsrecords[gamemap-1];
+		if (!data->nightsrecords[gamemap-1])
+			G_AllocNightsRecordData(gamemap-1, data);
+		maprecords = data->nightsrecords[gamemap-1];
 
 		if (maprecords->nummares != ntemprecords.nummares)
 			maprecords->nummares = ntemprecords.nummares;
@@ -739,7 +722,7 @@ void G_SetNightsRecords(void)
 	}
 	free(gpath);
 
-	if ((earnedEmblems = M_CheckLevelEmblems()))
+	if ((earnedEmblems = M_CheckLevelEmblems(data)))
 		CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for NiGHTS records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
 
 	// If the mare count changed, this will update the score display
@@ -3838,7 +3821,7 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
 	for (ix = 0; ix < NUMMAPS; ix++)
 		if (mapheaderinfo[ix] && (mapheaderinfo[ix]->typeoflevel & tolflags) == tolflags
 		 && ix != pprevmap // Don't pick the same map.
-		 && (dedicated || !M_MapLocked(ix+1)) // Don't pick locked maps.
+		 && (dedicated || !M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps.
 		)
 			okmaps[numokmaps++] = ix;
 
@@ -3855,7 +3838,7 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
 //
 // G_UpdateVisited
 //
-static void G_UpdateVisited(void)
+static void G_UpdateVisited(gamedata_t *data, boolean silent)
 {
 	boolean spec = G_IsSpecialStage(gamemap);
 	// Update visitation flags?
@@ -3865,30 +3848,37 @@ static void G_UpdateVisited(void)
 		UINT8 earnedEmblems;
 
 		// Update visitation flags
-		mapvisited[gamemap-1] |= MV_BEATEN;
+		data->mapvisited[gamemap-1] |= MV_BEATEN;
 		// eh, what the hell
 		if (ultimatemode)
-			mapvisited[gamemap-1] |= MV_ULTIMATE;
+			data->mapvisited[gamemap-1] |= MV_ULTIMATE;
 		// may seem incorrect but IS possible in what the main game uses as mp special stages, and nummaprings will be -1 in NiGHTS
 		if (nummaprings > 0 && players[consoleplayer].rings >= nummaprings)
 		{
-			mapvisited[gamemap-1] |= MV_PERFECT;
+			data->mapvisited[gamemap-1] |= MV_PERFECT;
 			if (modeattacking)
-				mapvisited[gamemap-1] |= MV_PERFECTRA;
+				data->mapvisited[gamemap-1] |= MV_PERFECTRA;
 		}
 		if (!spec)
 		{
 			// not available to special stages because they can only really be done in one order in an unmodified game, so impossible for first six and trivial for seventh
 			if (ALL7EMERALDS(emeralds))
-				mapvisited[gamemap-1] |= MV_ALLEMERALDS;
+				data->mapvisited[gamemap-1] |= MV_ALLEMERALDS;
 		}
 
-		if (modeattacking == ATTACKING_RECORD)
-			G_UpdateRecordReplays();
-		else if (modeattacking == ATTACKING_NIGHTS)
-			G_SetNightsRecords();
+		if (silent)
+		{
+			M_CheckLevelEmblems(data);
+		}
+		else
+		{
+			if (modeattacking == ATTACKING_RECORD)
+				G_UpdateRecordReplays(data);
+			else if (modeattacking == ATTACKING_NIGHTS)
+				G_SetNightsRecords(data);
+		}
 
-		if ((earnedEmblems = M_CompletionEmblems()))
+		if ((earnedEmblems = M_CompletionEmblems(data)) && !silent)
 			CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
 	}
 }
@@ -4091,7 +4081,8 @@ static void G_DoCompleted(void)
 
 	if ((skipstats && !modeattacking) || (modeattacking && stagefailed) || (intertype == int_none))
 	{
-		G_UpdateVisited();
+		G_UpdateVisited(serverGamedata, true);
+		G_UpdateVisited(clientGamedata, false);
 		G_HandleSaveLevel();
 		G_AfterIntermission();
 	}
@@ -4100,7 +4091,8 @@ static void G_DoCompleted(void)
 		G_SetGamestate(GS_INTERMISSION);
 		Y_StartIntermission();
 		Y_LoadIntermissionData();
-		G_UpdateVisited();
+		G_UpdateVisited(serverGamedata, true);
+		G_UpdateVisited(clientGamedata, false);
 		G_HandleSaveLevel();
 	}
 }
@@ -4287,7 +4279,7 @@ void G_LoadGameSettings(void)
 
 // G_LoadGameData
 // Loads the main data file, which stores information such as emblems found, etc.
-void G_LoadGameData(void)
+void G_LoadGameData(gamedata_t *data)
 {
 	size_t length;
 	INT32 i, j;
@@ -4304,13 +4296,13 @@ void G_LoadGameData(void)
 	INT32 curmare;
 
 	// Stop saving, until we successfully load it again.
-	gamedataloaded = false;
+	data->loaded = false;
 
 	// Clear things so previously read gamedata doesn't transfer
 	// to new gamedata
-	G_ClearRecords(); // main and nights records
-	M_ClearSecrets(); // emblems, unlocks, maps visited, etc
-	totalplaytime = 0; // total play time (separate from all)
+	G_ClearRecords(data); // main and nights records
+	M_ClearSecrets(data); // emblems, unlocks, maps visited, etc
+	data->totalplaytime = 0; // total play time (separate from all)
 
 	if (M_CheckParm("-nodata"))
 	{
@@ -4321,7 +4313,7 @@ void G_LoadGameData(void)
 	if (M_CheckParm("-resetdata"))
 	{
 		// Don't load, but do save. (essentially, reset)
-		gamedataloaded = true;
+		data->loaded = true;
 		return; 
 	}
 
@@ -4329,7 +4321,7 @@ void G_LoadGameData(void)
 	if (!length)
 	{
 		// No gamedata. We can save a new one.
-		gamedataloaded = true;
+		data->loaded = true;
 		return;
 	}
 
@@ -4352,7 +4344,7 @@ void G_LoadGameData(void)
 		I_Error("Game data is from another version of SRB2.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
 	}
 
-	totalplaytime = READUINT32(save_p);
+	data->totalplaytime = READUINT32(save_p);
 
 #ifdef COMPAT_GAMEDATA_ID
 	if (versionID == COMPAT_GAMEDATA_ID)
@@ -4386,7 +4378,7 @@ void G_LoadGameData(void)
 
 	// TODO put another cipher on these things? meh, I don't care...
 	for (i = 0; i < NUMMAPS; i++)
-		if ((mapvisited[i] = READUINT8(save_p)) > MV_MAX)
+		if ((data->mapvisited[i] = READUINT8(save_p)) > MV_MAX)
 			goto datacorrupt;
 
 	// To save space, use one bit per collected/achieved/unlocked flag
@@ -4394,34 +4386,34 @@ void G_LoadGameData(void)
 	{
 		rtemp = READUINT8(save_p);
 		for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
-			emblemlocations[j+i].collected = ((rtemp >> j) & 1);
+			data->collected[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
 	for (i = 0; i < MAXEXTRAEMBLEMS;)
 	{
 		rtemp = READUINT8(save_p);
 		for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
-			extraemblems[j+i].collected = ((rtemp >> j) & 1);
+			data->extraCollected[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
 	for (i = 0; i < MAXUNLOCKABLES;)
 	{
 		rtemp = READUINT8(save_p);
 		for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
-			unlockables[j+i].unlocked = ((rtemp >> j) & 1);
+			data->unlocked[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
 	for (i = 0; i < MAXCONDITIONSETS;)
 	{
 		rtemp = READUINT8(save_p);
 		for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
-			conditionSets[j+i].achieved = ((rtemp >> j) & 1);
+			data->achieved[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
 
-	timesBeaten = READUINT32(save_p);
-	timesBeatenWithEmeralds = READUINT32(save_p);
-	timesBeatenUltimate = READUINT32(save_p);
+	data->timesBeaten = READUINT32(save_p);
+	data->timesBeatenWithEmeralds = READUINT32(save_p);
+	data->timesBeatenUltimate = READUINT32(save_p);
 
 	// Main records
 	for (i = 0; i < NUMMAPS; ++i)
@@ -4436,10 +4428,10 @@ void G_LoadGameData(void)
 
 		if (recscore || rectime || recrings)
 		{
-			G_AllocMainRecordData((INT16)i);
-			mainrecords[i]->score = recscore;
-			mainrecords[i]->time = rectime;
-			mainrecords[i]->rings = recrings;
+			G_AllocMainRecordData((INT16)i, data);
+			data->mainrecords[i]->score = recscore;
+			data->mainrecords[i]->time = rectime;
+			data->mainrecords[i]->rings = recrings;
 		}
 	}
 
@@ -4449,19 +4441,21 @@ void G_LoadGameData(void)
 		if ((recmares = READUINT8(save_p)) == 0)
 			continue;
 
-		G_AllocNightsRecordData((INT16)i);
+		G_AllocNightsRecordData((INT16)i, data);
 
 		for (curmare = 0; curmare < (recmares+1); ++curmare)
 		{
-			nightsrecords[i]->score[curmare] = READUINT32(save_p);
-			nightsrecords[i]->grade[curmare] = READUINT8(save_p);
-			nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
+			data->nightsrecords[i]->score[curmare] = READUINT32(save_p);
+			data->nightsrecords[i]->grade[curmare] = READUINT8(save_p);
+			data->nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
 
-			if (nightsrecords[i]->grade[curmare] > GRADE_S)
+			if (data->nightsrecords[i]->grade[curmare] > GRADE_S)
+			{
 				goto datacorrupt;
+			}
 		}
 
-		nightsrecords[i]->nummares = recmares;
+		data->nightsrecords[i]->nummares = recmares;
 	}
 
 	// done
@@ -4472,10 +4466,11 @@ void G_LoadGameData(void)
 	// It used to do this much earlier, but this would cause the gamedata to
 	// save over itself when it I_Errors from the corruption landing point below,
 	// which can accidentally delete players' legitimate data if the code ever has any tiny mistakes!
-	gamedataloaded = true;
+	data->loaded = true;
 
 	// Silent update unlockables in case they're out of sync with conditions
-	M_SilentUpdateUnlockablesAndEmblems();
+	M_SilentUpdateUnlockablesAndEmblems(data);
+	M_SilentUpdateSkinAvailabilites();
 
 	return;
 
@@ -4495,7 +4490,7 @@ void G_LoadGameData(void)
 
 // G_SaveGameData
 // Saves the main data file, which stores information such as emblems found, etc.
-void G_SaveGameData(void)
+void G_SaveGameData(gamedata_t *data)
 {
 	size_t length;
 	INT32 i, j;
@@ -4503,7 +4498,7 @@ void G_SaveGameData(void)
 
 	INT32 curmare;
 
-	if (!gamedataloaded)
+	if (!data->loaded)
 		return; // If never loaded (-nodata), don't save
 
 	save_p = savebuffer = (UINT8 *)malloc(GAMEDATASIZE);
@@ -4523,20 +4518,20 @@ void G_SaveGameData(void)
 	// Version test
 	WRITEUINT32(save_p, GAMEDATA_ID);
 
-	WRITEUINT32(save_p, totalplaytime);
+	WRITEUINT32(save_p, data->totalplaytime);
 
 	WRITEUINT32(save_p, quickncasehash(timeattackfolder, sizeof timeattackfolder));
 
 	// TODO put another cipher on these things? meh, I don't care...
 	for (i = 0; i < NUMMAPS; i++)
-		WRITEUINT8(save_p, (mapvisited[i] & MV_MAX));
+		WRITEUINT8(save_p, (data->mapvisited[i] & MV_MAX));
 
 	// To save space, use one bit per collected/achieved/unlocked flag
 	for (i = 0; i < MAXEMBLEMS;)
 	{
 		btemp = 0;
 		for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
-			btemp |= (emblemlocations[j+i].collected << j);
+			btemp |= (data->collected[j+i] << j);
 		WRITEUINT8(save_p, btemp);
 		i += j;
 	}
@@ -4544,7 +4539,7 @@ void G_SaveGameData(void)
 	{
 		btemp = 0;
 		for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
-			btemp |= (extraemblems[j+i].collected << j);
+			btemp |= (data->extraCollected[j+i] << j);
 		WRITEUINT8(save_p, btemp);
 		i += j;
 	}
@@ -4552,7 +4547,7 @@ void G_SaveGameData(void)
 	{
 		btemp = 0;
 		for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
-			btemp |= (unlockables[j+i].unlocked << j);
+			btemp |= (data->unlocked[j+i] << j);
 		WRITEUINT8(save_p, btemp);
 		i += j;
 	}
@@ -4560,23 +4555,23 @@ void G_SaveGameData(void)
 	{
 		btemp = 0;
 		for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
-			btemp |= (conditionSets[j+i].achieved << j);
+			btemp |= (data->achieved[j+i] << j);
 		WRITEUINT8(save_p, btemp);
 		i += j;
 	}
 
-	WRITEUINT32(save_p, timesBeaten);
-	WRITEUINT32(save_p, timesBeatenWithEmeralds);
-	WRITEUINT32(save_p, timesBeatenUltimate);
+	WRITEUINT32(save_p, data->timesBeaten);
+	WRITEUINT32(save_p, data->timesBeatenWithEmeralds);
+	WRITEUINT32(save_p, data->timesBeatenUltimate);
 
 	// Main records
 	for (i = 0; i < NUMMAPS; i++)
 	{
-		if (mainrecords[i])
+		if (data->mainrecords[i])
 		{
-			WRITEUINT32(save_p, mainrecords[i]->score);
-			WRITEUINT32(save_p, mainrecords[i]->time);
-			WRITEUINT16(save_p, mainrecords[i]->rings);
+			WRITEUINT32(save_p, data->mainrecords[i]->score);
+			WRITEUINT32(save_p, data->mainrecords[i]->time);
+			WRITEUINT16(save_p, data->mainrecords[i]->rings);
 		}
 		else
 		{
@@ -4590,19 +4585,19 @@ void G_SaveGameData(void)
 	// NiGHTS records
 	for (i = 0; i < NUMMAPS; i++)
 	{
-		if (!nightsrecords[i] || !nightsrecords[i]->nummares)
+		if (!data->nightsrecords[i] || !data->nightsrecords[i]->nummares)
 		{
 			WRITEUINT8(save_p, 0);
 			continue;
 		}
 
-		WRITEUINT8(save_p, nightsrecords[i]->nummares);
+		WRITEUINT8(save_p, data->nightsrecords[i]->nummares);
 
-		for (curmare = 0; curmare < (nightsrecords[i]->nummares + 1); ++curmare)
+		for (curmare = 0; curmare < (data->nightsrecords[i]->nummares + 1); ++curmare)
 		{
-			WRITEUINT32(save_p, nightsrecords[i]->score[curmare]);
-			WRITEUINT8(save_p, nightsrecords[i]->grade[curmare]);
-			WRITEUINT32(save_p, nightsrecords[i]->time[curmare]);
+			WRITEUINT32(save_p, data->nightsrecords[i]->score[curmare]);
+			WRITEUINT8(save_p, data->nightsrecords[i]->grade[curmare]);
+			WRITEUINT32(save_p, data->nightsrecords[i]->time[curmare]);
 		}
 	}
 
diff --git a/src/g_game.h b/src/g_game.h
index 144360db4f..6cda7ca9cc 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -19,6 +19,7 @@
 #include "d_event.h"
 #include "g_demo.h"
 #include "m_cheat.h" // objectplacing
+#include "m_cond.h"
 
 extern char gamedatafilename[64];
 extern char timeattackfolder[64];
@@ -183,7 +184,7 @@ boolean G_IsTitleCardAvailable(void);
 // Can be called by the startup code or M_Responder, calls P_SetupLevel.
 void G_LoadGame(UINT32 slot, INT16 mapoverride);
 
-void G_SaveGameData(void);
+void G_SaveGameData(gamedata_t *data);
 
 void G_SaveGame(UINT32 slot, INT16 mapnum);
 
@@ -239,7 +240,7 @@ void G_SetModeAttackRetryFlag(void);
 void G_ClearModeAttackRetryFlag(void);
 boolean G_GetModeAttackRetryFlag(void);
 
-void G_LoadGameData(void);
+void G_LoadGameData(gamedata_t *data);
 void G_LoadGameSettings(void);
 
 void G_SetGameModified(boolean silent);
@@ -248,19 +249,19 @@ void G_SetUsedCheats(boolean silent);
 void G_SetGamestate(gamestate_t newstate);
 
 // Gamedata record shit
-void G_AllocMainRecordData(INT16 i);
-void G_AllocNightsRecordData(INT16 i);
-void G_ClearRecords(void);
+void G_AllocMainRecordData(INT16 i, gamedata_t *data);
+void G_AllocNightsRecordData(INT16 i, gamedata_t *data);
+void G_ClearRecords(gamedata_t *data);
 
-UINT32 G_GetBestScore(INT16 map);
-tic_t G_GetBestTime(INT16 map);
-UINT16 G_GetBestRings(INT16 map);
-UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare);
-tic_t G_GetBestNightsTime(INT16 map, UINT8 mare);
-UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare);
+UINT32 G_GetBestScore(INT16 map, gamedata_t *data);
+tic_t G_GetBestTime(INT16 map, gamedata_t *data);
+UINT16 G_GetBestRings(INT16 map, gamedata_t *data);
+UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare, gamedata_t *data);
+tic_t G_GetBestNightsTime(INT16 map, UINT8 mare, gamedata_t *data);
+UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare, gamedata_t *data);
 
 void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare);
-void G_SetNightsRecords(void);
+void G_SetNightsRecords(gamedata_t *data);
 
 FUNCMATH INT32 G_TicsToHours(tic_t tics);
 FUNCMATH INT32 G_TicsToMinutes(tic_t tics, boolean full);
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 55ebe0d4d9..eceb6bbafa 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -2996,7 +2996,7 @@ static void HU_DrawCoopOverlay(void)
 
 	if (LUA_HudEnabled(hud_tabemblems))
 	{
-		V_DrawString(160, 144, 0, va("- %d/%d", M_CountEmblems(), numemblems+numextraemblems));
+		V_DrawString(160, 144, 0, va("- %d/%d", M_CountEmblems(clientGamedata), numemblems+numextraemblems));
 		V_DrawScaledPatch(128, 144 - emblemicon->height/4, 0, emblemicon);
 	}
 
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 9d257b48b6..934779982e 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -102,7 +102,7 @@ static UINT8 cheatf_devmode(void)
 	// Just unlock all the things and turn on -debug and console devmode.
 	G_SetUsedCheats(false);
 	for (i = 0; i < MAXUNLOCKABLES; i++)
-		unlockables[i].unlocked = true;
+		clientGamedata->unlocked[i] = true;
 	devparm = true;
 	cv_debug |= 0x8000;
 
@@ -238,7 +238,7 @@ boolean cht_Responder(event_t *ev)
 }
 
 // Console cheat commands rely on these a lot...
-#define REQUIRE_PANDORA if (!M_SecretUnlocked(SECRET_PANDORA) && !cv_debug)\
+#define REQUIRE_PANDORA if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !cv_debug)\
 { CONS_Printf(M_GetText("You haven't earned this yet.\n")); return; }
 
 #define REQUIRE_DEVMODE if (!cv_debug)\
diff --git a/src/m_cond.c b/src/m_cond.c
index bd6faadfd3..55f35830ad 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -21,6 +21,9 @@
 #include "r_skins.h" // numskins
 #include "r_draw.h" // R_GetColorByName
 
+gamedata_t *clientGamedata; // Our gamedata
+gamedata_t *serverGamedata; // Server's gamedata
+
 // Map triggers for linedef executors
 // 32 triggers, one bit each
 UINT32 unlocktriggers;
@@ -41,6 +44,70 @@ unlockable_t unlockables[MAXUNLOCKABLES];
 INT32 numemblems = 0;
 INT32 numextraemblems = 0;
 
+// Temporary holding place for nights data for the current map
+nightsdata_t ntemprecords;
+
+// Create a new gamedata_t, for start-up
+gamedata_t *M_NewGameDataStruct(void)
+{
+	gamedata_t *data = Z_Calloc(sizeof (*data), PU_STATIC, NULL);
+	M_ClearSecrets(data);
+	G_ClearRecords(data);
+	return data;
+}
+
+void M_CopyGameData(gamedata_t *dest, gamedata_t *src)
+{
+	INT32 i, j;
+
+	M_ClearSecrets(dest);
+	G_ClearRecords(dest);
+
+	dest->loaded = src->loaded;
+	dest->totalplaytime = src->totalplaytime;
+
+	dest->timesBeaten = src->timesBeaten;
+	dest->timesBeatenWithEmeralds = src->timesBeatenWithEmeralds;
+	dest->timesBeatenUltimate = src->timesBeatenUltimate;
+
+	memcpy(dest->achieved, src->achieved, sizeof(dest->achieved));
+	memcpy(dest->collected, src->collected, sizeof(dest->collected));
+	memcpy(dest->extraCollected, src->extraCollected, sizeof(dest->extraCollected));
+	memcpy(dest->unlocked, src->unlocked, sizeof(dest->unlocked));
+
+	memcpy(dest->mapvisited, src->mapvisited, sizeof(dest->mapvisited));
+
+	// Main records
+	for (i = 0; i < NUMMAPS; ++i)
+	{
+		if (!src->mainrecords[i])
+			continue;
+
+		G_AllocMainRecordData((INT16)i, dest);
+		dest->mainrecords[i]->score = src->mainrecords[i]->score;
+		dest->mainrecords[i]->time = src->mainrecords[i]->time;
+		dest->mainrecords[i]->rings = src->mainrecords[i]->rings;
+	}
+
+	// Nights records
+	for (i = 0; i < NUMMAPS; ++i)
+	{
+		if (!src->nightsrecords[i] || !src->nightsrecords[i]->nummares)
+			continue;
+
+		G_AllocNightsRecordData((INT16)i, dest);
+
+		for (j = 0; j < (src->nightsrecords[i]->nummares + 1); j++)
+		{
+			dest->nightsrecords[i]->score[j] = src->nightsrecords[i]->score[j];
+			dest->nightsrecords[i]->grade[j] = src->nightsrecords[i]->grade[j];
+			dest->nightsrecords[i]->time[j] = src->nightsrecords[i]->time[j];
+		}
+
+		dest->nightsrecords[i]->nummares = src->nightsrecords[i]->nummares;
+	}
+}
+
 void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2)
 {
 	condition_t *cond;
@@ -70,89 +137,90 @@ void M_ClearConditionSet(UINT8 set)
 		conditionSets[set - 1].condition = NULL;
 		conditionSets[set - 1].numconditions = 0;
 	}
-	conditionSets[set - 1].achieved = false;
+	clientGamedata->achieved[set - 1] = serverGamedata->achieved[set - 1] = false;
 }
 
 // Clear ALL secrets.
-void M_ClearSecrets(void)
+void M_ClearSecrets(gamedata_t *data)
 {
 	INT32 i;
 
-	memset(mapvisited, 0, sizeof(mapvisited));
+	memset(data->mapvisited, 0, sizeof(data->mapvisited));
 
 	for (i = 0; i < MAXEMBLEMS; ++i)
-		emblemlocations[i].collected = false;
+		data->collected[i] = false;
 	for (i = 0; i < MAXEXTRAEMBLEMS; ++i)
-		extraemblems[i].collected = false;
+		data->extraCollected[i] = false;
 	for (i = 0; i < MAXUNLOCKABLES; ++i)
-		unlockables[i].unlocked = false;
+		data->unlocked[i] = false;
 	for (i = 0; i < MAXCONDITIONSETS; ++i)
-		conditionSets[i].achieved = false;
+		data->achieved[i] = false;
 
-	timesBeaten = timesBeatenWithEmeralds = timesBeatenUltimate = 0;
+	data->timesBeaten = data->timesBeatenWithEmeralds = data->timesBeatenUltimate = 0;
 
 	// Re-unlock any always unlocked things
-	M_SilentUpdateUnlockablesAndEmblems();
+	M_SilentUpdateUnlockablesAndEmblems(data);
+	M_SilentUpdateSkinAvailabilites();
 }
 
 // ----------------------
 // Condition set checking
 // ----------------------
-static UINT8 M_CheckCondition(condition_t *cn)
+static UINT8 M_CheckCondition(condition_t *cn, gamedata_t *data)
 {
 	switch (cn->type)
 	{
 		case UC_PLAYTIME: // Requires total playing time >= x
-			return (totalplaytime >= (unsigned)cn->requirement);
+			return (data->totalplaytime >= (unsigned)cn->requirement);
 		case UC_GAMECLEAR: // Requires game beaten >= x times
-			return (timesBeaten >= (unsigned)cn->requirement);
+			return (data->timesBeaten >= (unsigned)cn->requirement);
 		case UC_ALLEMERALDS: // Requires game beaten with all 7 emeralds >= x times
-			return (timesBeatenWithEmeralds >= (unsigned)cn->requirement);
+			return (data->timesBeatenWithEmeralds >= (unsigned)cn->requirement);
 		case UC_ULTIMATECLEAR: // Requires game beaten on ultimate >= x times (in other words, never)
-			return (timesBeatenUltimate >= (unsigned)cn->requirement);
+			return (data->timesBeatenUltimate >= (unsigned)cn->requirement);
 		case UC_OVERALLSCORE: // Requires overall score >= x
-			return (M_GotHighEnoughScore(cn->requirement));
+			return (M_GotHighEnoughScore(cn->requirement, data));
 		case UC_OVERALLTIME: // Requires overall time <= x
-			return (M_GotLowEnoughTime(cn->requirement));
+			return (M_GotLowEnoughTime(cn->requirement, data));
 		case UC_OVERALLRINGS: // Requires overall rings >= x
-			return (M_GotHighEnoughRings(cn->requirement));
+			return (M_GotHighEnoughRings(cn->requirement, data));
 		case UC_MAPVISITED: // Requires map x to be visited
-			return ((mapvisited[cn->requirement - 1] & MV_VISITED) == MV_VISITED);
+			return ((data->mapvisited[cn->requirement - 1] & MV_VISITED) == MV_VISITED);
 		case UC_MAPBEATEN: // Requires map x to be beaten
-			return ((mapvisited[cn->requirement - 1] & MV_BEATEN) == MV_BEATEN);
+			return ((data->mapvisited[cn->requirement - 1] & MV_BEATEN) == MV_BEATEN);
 		case UC_MAPALLEMERALDS: // Requires map x to be beaten with all emeralds in possession
-			return ((mapvisited[cn->requirement - 1] & MV_ALLEMERALDS) == MV_ALLEMERALDS);
+			return ((data->mapvisited[cn->requirement - 1] & MV_ALLEMERALDS) == MV_ALLEMERALDS);
 		case UC_MAPULTIMATE: // Requires map x to be beaten on ultimate
-			return ((mapvisited[cn->requirement - 1] & MV_ULTIMATE) == MV_ULTIMATE);
+			return ((data->mapvisited[cn->requirement - 1] & MV_ULTIMATE) == MV_ULTIMATE);
 		case UC_MAPPERFECT: // Requires map x to be beaten with a perfect bonus
-			return ((mapvisited[cn->requirement - 1] & MV_PERFECT) == MV_PERFECT);
+			return ((data->mapvisited[cn->requirement - 1] & MV_PERFECT) == MV_PERFECT);
 		case UC_MAPSCORE: // Requires score on map >= x
-			return (G_GetBestScore(cn->extrainfo1) >= (unsigned)cn->requirement);
+			return (G_GetBestScore(cn->extrainfo1, data) >= (unsigned)cn->requirement);
 		case UC_MAPTIME: // Requires time on map <= x
-			return (G_GetBestTime(cn->extrainfo1) <= (unsigned)cn->requirement);
+			return (G_GetBestTime(cn->extrainfo1, data) <= (unsigned)cn->requirement);
 		case UC_MAPRINGS: // Requires rings on map >= x
-			return (G_GetBestRings(cn->extrainfo1) >= cn->requirement);
+			return (G_GetBestRings(cn->extrainfo1, data) >= cn->requirement);
 		case UC_NIGHTSSCORE:
-			return (G_GetBestNightsScore(cn->extrainfo1, (UINT8)cn->extrainfo2) >= (unsigned)cn->requirement);
+			return (G_GetBestNightsScore(cn->extrainfo1, (UINT8)cn->extrainfo2, data) >= (unsigned)cn->requirement);
 		case UC_NIGHTSTIME:
-			return (G_GetBestNightsTime(cn->extrainfo1, (UINT8)cn->extrainfo2) <= (unsigned)cn->requirement);
+			return (G_GetBestNightsTime(cn->extrainfo1, (UINT8)cn->extrainfo2, data) <= (unsigned)cn->requirement);
 		case UC_NIGHTSGRADE:
-			return (G_GetBestNightsGrade(cn->extrainfo1, (UINT8)cn->extrainfo2) >= cn->requirement);
+			return (G_GetBestNightsGrade(cn->extrainfo1, (UINT8)cn->extrainfo2, data) >= cn->requirement);
 		case UC_TRIGGER: // requires map trigger set
 			return !!(unlocktriggers & (1 << cn->requirement));
 		case UC_TOTALEMBLEMS: // Requires number of emblems >= x
-			return (M_GotEnoughEmblems(cn->requirement));
+			return (M_GotEnoughEmblems(cn->requirement, data));
 		case UC_EMBLEM: // Requires emblem x to be obtained
-			return emblemlocations[cn->requirement-1].collected;
+			return data->collected[cn->requirement-1];
 		case UC_EXTRAEMBLEM: // Requires extra emblem x to be obtained
-			return extraemblems[cn->requirement-1].collected;
+			return data->extraCollected[cn->requirement-1];
 		case UC_CONDITIONSET: // requires condition set x to already be achieved
-			return M_Achieved(cn->requirement-1);
+			return M_Achieved(cn->requirement-1, data);
 	}
 	return false;
 }
 
-static UINT8 M_CheckConditionSet(conditionset_t *c)
+static UINT8 M_CheckConditionSet(conditionset_t *c, gamedata_t *data)
 {
 	UINT32 i;
 	UINT32 lastID = 0;
@@ -173,13 +241,13 @@ static UINT8 M_CheckConditionSet(conditionset_t *c)
 			continue;
 
 		lastID = cn->id;
-		achievedSoFar = M_CheckCondition(cn);
+		achievedSoFar = M_CheckCondition(cn, data);
 	}
 
 	return achievedSoFar;
 }
 
-void M_CheckUnlockConditions(void)
+void M_CheckUnlockConditions(gamedata_t *data)
 {
 	INT32 i;
 	conditionset_t *c;
@@ -187,27 +255,27 @@ void M_CheckUnlockConditions(void)
 	for (i = 0; i < MAXCONDITIONSETS; ++i)
 	{
 		c = &conditionSets[i];
-		if (!c->numconditions || c->achieved)
+		if (!c->numconditions || data->achieved[i])
 			continue;
 
-		c->achieved = (M_CheckConditionSet(c));
+		data->achieved[i] = (M_CheckConditionSet(c, data));
 	}
 }
 
-UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
+UINT8 M_UpdateUnlockablesAndExtraEmblems(gamedata_t *data)
 {
 	INT32 i;
 	char cechoText[992] = "";
 	UINT8 cechoLines = 0;
 
-	M_CheckUnlockConditions();
+	M_CheckUnlockConditions(data);
 
 	// Go through extra emblems
 	for (i = 0; i < numextraemblems; ++i)
 	{
-		if (extraemblems[i].collected || !extraemblems[i].conditionset)
+		if (data->extraCollected[i] || !extraemblems[i].conditionset)
 			continue;
-		if ((extraemblems[i].collected = M_Achieved(extraemblems[i].conditionset - 1)) != false)
+		if ((data->extraCollected[i] = M_Achieved(extraemblems[i].conditionset - 1, data)) != false)
 		{
 			strcat(cechoText, va(M_GetText("Got \"%s\" emblem!\\"), extraemblems[i].name));
 			++cechoLines;
@@ -217,14 +285,14 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
 	// Fun part: if any of those unlocked we need to go through the
 	// unlock conditions AGAIN just in case an emblem reward was reached
 	if (cechoLines)
-		M_CheckUnlockConditions();
+		M_CheckUnlockConditions(data);
 
 	// Go through unlockables
 	for (i = 0; i < MAXUNLOCKABLES; ++i)
 	{
-		if (unlockables[i].unlocked || !unlockables[i].conditionset)
+		if (data->unlocked[i] || !unlockables[i].conditionset)
 			continue;
-		if ((unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1)) != false)
+		if ((data->unlocked[i] = M_Achieved(unlockables[i].conditionset - 1, data)) != false)
 		{
 			if (unlockables[i].nocecho)
 				continue;
@@ -248,45 +316,49 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
 		HU_DoCEcho(slashed);
 		return true;
 	}
+
 	return false;
 }
 
 // Used when loading gamedata to make sure all unlocks are synched with conditions
-void M_SilentUpdateUnlockablesAndEmblems(void)
+void M_SilentUpdateUnlockablesAndEmblems(gamedata_t *data)
 {
 	INT32 i;
 	boolean checkAgain = false;
 
 	// Just in case they aren't to sync
-	M_CheckUnlockConditions();
-	M_CheckLevelEmblems();
+	M_CheckUnlockConditions(data);
+	M_CheckLevelEmblems(data);
 
 	// Go through extra emblems
 	for (i = 0; i < numextraemblems; ++i)
 	{
-		if (extraemblems[i].collected || !extraemblems[i].conditionset)
+		if (data->extraCollected[i] || !extraemblems[i].conditionset)
 			continue;
-		if ((extraemblems[i].collected = M_Achieved(extraemblems[i].conditionset - 1)) != false)
+		if ((data->extraCollected[i] = M_Achieved(extraemblems[i].conditionset - 1, data)) != false)
 			checkAgain = true;
 	}
 
 	// check again if extra emblems unlocked, blah blah, etc
 	if (checkAgain)
-		M_CheckUnlockConditions();
+		M_CheckUnlockConditions(data);
 
 	// Go through unlockables
 	for (i = 0; i < MAXUNLOCKABLES; ++i)
 	{
-		if (unlockables[i].unlocked || !unlockables[i].conditionset)
+		if (data->unlocked[i] || !unlockables[i].conditionset)
 			continue;
-		unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1);
+		data->unlocked[i] = M_Achieved(unlockables[i].conditionset - 1, data);
 	}
+}
 
+void M_SilentUpdateSkinAvailabilites(void)
+{
 	players[consoleplayer].availabilities = players[1].availabilities = R_GetSkinAvailabilities(); // players[1] is supposed to be for 2p
 }
 
 // Emblem unlocking shit
-UINT8 M_CheckLevelEmblems(void)
+UINT8 M_CheckLevelEmblems(gamedata_t *data)
 {
 	INT32 i;
 	INT32 valToReach;
@@ -297,7 +369,7 @@ UINT8 M_CheckLevelEmblems(void)
 	// Update Score, Time, Rings emblems
 	for (i = 0; i < numemblems; ++i)
 	{
-		if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].type == ET_MAP || emblemlocations[i].collected)
+		if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].type == ET_MAP || data->collected[i])
 			continue;
 
 		levelnum = emblemlocations[i].level;
@@ -306,32 +378,32 @@ UINT8 M_CheckLevelEmblems(void)
 		switch (emblemlocations[i].type)
 		{
 			case ET_SCORE: // Requires score on map >= x
-				res = (G_GetBestScore(levelnum) >= (unsigned)valToReach);
+				res = (G_GetBestScore(levelnum, data) >= (unsigned)valToReach);
 				break;
 			case ET_TIME: // Requires time on map <= x
-				res = (G_GetBestTime(levelnum) <= (unsigned)valToReach);
+				res = (G_GetBestTime(levelnum, data) <= (unsigned)valToReach);
 				break;
 			case ET_RINGS: // Requires rings on map >= x
-				res = (G_GetBestRings(levelnum) >= valToReach);
+				res = (G_GetBestRings(levelnum, data) >= valToReach);
 				break;
 			case ET_NGRADE: // Requires NiGHTS grade on map >= x
-				res = (G_GetBestNightsGrade(levelnum, 0) >= valToReach);
+				res = (G_GetBestNightsGrade(levelnum, 0, data) >= valToReach);
 				break;
 			case ET_NTIME: // Requires NiGHTS time on map <= x
-				res = (G_GetBestNightsTime(levelnum, 0) <= (unsigned)valToReach);
+				res = (G_GetBestNightsTime(levelnum, 0, data) <= (unsigned)valToReach);
 				break;
 			default: // unreachable but shuts the compiler up.
 				continue;
 		}
 
-		emblemlocations[i].collected = res;
+		data->collected[i] = res;
 		if (res)
 			++somethingUnlocked;
 	}
 	return somethingUnlocked;
 }
 
-UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough.
+UINT8 M_CompletionEmblems(gamedata_t *data) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough.
 {
 	INT32 i;
 	INT32 embtype;
@@ -342,7 +414,7 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
 
 	for (i = 0; i < numemblems; ++i)
 	{
-		if (emblemlocations[i].type != ET_MAP || emblemlocations[i].collected)
+		if (emblemlocations[i].type != ET_MAP || data->collected[i])
 			continue;
 
 		levelnum = emblemlocations[i].level;
@@ -358,9 +430,9 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
 		if (embtype & ME_PERFECT)
 			flags |= MV_PERFECT;
 
-		res = ((mapvisited[levelnum - 1] & flags) == flags);
+		res = ((data->mapvisited[levelnum - 1] & flags) == flags);
 
-		emblemlocations[i].collected = res;
+		data->collected[i] = res;
 		if (res)
 			++somethingUnlocked;
 	}
@@ -370,48 +442,54 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
 // -------------------
 // Quick unlock checks
 // -------------------
-UINT8 M_AnySecretUnlocked(void)
+UINT8 M_AnySecretUnlocked(gamedata_t *data)
 {
 	INT32 i;
 	for (i = 0; i < MAXUNLOCKABLES; ++i)
 	{
-		if (!unlockables[i].nocecho && unlockables[i].unlocked)
+		if (!unlockables[i].nocecho && data->unlocked[i])
 			return true;
 	}
 	return false;
 }
 
-UINT8 M_SecretUnlocked(INT32 type)
+UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data)
 {
 	INT32 i;
 	for (i = 0; i < MAXUNLOCKABLES; ++i)
 	{
-		if (unlockables[i].type == type && unlockables[i].unlocked)
+		if (unlockables[i].type == type && data->unlocked[i])
 			return true;
 	}
 	return false;
 }
 
-UINT8 M_MapLocked(INT32 mapnum)
+UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data)
 {
 	if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0)
+	{
 		return false;
-	if (!unlockables[mapheaderinfo[mapnum-1]->unlockrequired].unlocked)
+	}
+
+	if (!data->unlocked[mapheaderinfo[mapnum-1]->unlockrequired])
+	{
 		return true;
+	}
+
 	return false;
 }
 
-INT32 M_CountEmblems(void)
+INT32 M_CountEmblems(gamedata_t *data)
 {
 	INT32 found = 0, i;
 	for (i = 0; i < numemblems; ++i)
 	{
-		if (emblemlocations[i].collected)
+		if (data->collected[i])
 			found++;
 	}
 	for (i = 0; i < numextraemblems; ++i)
 	{
-		if (extraemblems[i].collected)
+		if (data->extraCollected[i])
 			found++;
 	}
 	return found;
@@ -423,23 +501,23 @@ INT32 M_CountEmblems(void)
 
 // Theoretically faster than using M_CountEmblems()
 // Stops when it reaches the target number of emblems.
-UINT8 M_GotEnoughEmblems(INT32 number)
+UINT8 M_GotEnoughEmblems(INT32 number, gamedata_t *data)
 {
 	INT32 i, gottenemblems = 0;
 	for (i = 0; i < numemblems; ++i)
 	{
-		if (emblemlocations[i].collected)
+		if (data->collected[i])
 			if (++gottenemblems >= number) return true;
 	}
 	for (i = 0; i < numextraemblems; ++i)
 	{
-		if (extraemblems[i].collected)
+		if (data->extraCollected[i])
 			if (++gottenemblems >= number) return true;
 	}
 	return false;
 }
 
-UINT8 M_GotHighEnoughScore(INT32 tscore)
+UINT8 M_GotHighEnoughScore(INT32 tscore, gamedata_t *data)
 {
 	INT32 mscore = 0;
 	INT32 i;
@@ -448,16 +526,16 @@ UINT8 M_GotHighEnoughScore(INT32 tscore)
 	{
 		if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
 			continue;
-		if (!mainrecords[i])
+		if (!data->mainrecords[i])
 			continue;
 
-		if ((mscore += mainrecords[i]->score) > tscore)
+		if ((mscore += data->mainrecords[i]->score) > tscore)
 			return true;
 	}
 	return false;
 }
 
-UINT8 M_GotLowEnoughTime(INT32 tictime)
+UINT8 M_GotLowEnoughTime(INT32 tictime, gamedata_t *data)
 {
 	INT32 curtics = 0;
 	INT32 i;
@@ -467,15 +545,15 @@ UINT8 M_GotLowEnoughTime(INT32 tictime)
 		if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
 			continue;
 
-		if (!mainrecords[i] || !mainrecords[i]->time)
+		if (!data->mainrecords[i] || !data->mainrecords[i]->time)
 			return false;
-		else if ((curtics += mainrecords[i]->time) > tictime)
+		else if ((curtics += data->mainrecords[i]->time) > tictime)
 			return false;
 	}
 	return true;
 }
 
-UINT8 M_GotHighEnoughRings(INT32 trings)
+UINT8 M_GotHighEnoughRings(INT32 trings, gamedata_t *data)
 {
 	INT32 mrings = 0;
 	INT32 i;
@@ -484,10 +562,10 @@ UINT8 M_GotHighEnoughRings(INT32 trings)
 	{
 		if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
 			continue;
-		if (!mainrecords[i])
+		if (!data->mainrecords[i])
 			continue;
 
-		if ((mrings += mainrecords[i]->rings) > trings)
+		if ((mrings += data->mainrecords[i]->rings) > trings)
 			return true;
 	}
 	return false;
diff --git a/src/m_cond.h b/src/m_cond.h
index d49dc920b3..6a3da79ece 100644
--- a/src/m_cond.h
+++ b/src/m_cond.h
@@ -10,7 +10,11 @@
 /// \file  m_cond.h
 /// \brief Unlockable condition system for SRB2 version 2.1
 
+#ifndef __M_COND__
+#define __M_COND__
+
 #include "doomdef.h"
+#include "doomdata.h"
 
 // --------
 // Typedefs
@@ -61,8 +65,6 @@ typedef struct
 {
 	UINT32 numconditions;   /// <- number of conditions.
 	condition_t *condition; /// <- All conditionals to be checked.
-	UINT8 achieved;         /// <- Whether this conditional has been achieved already or not.
-	                        ///    (Conditional checking is skipped if true -- it's assumed you can't relock an unlockable)
 } conditionset_t;
 
 // Emblem information
@@ -94,7 +96,6 @@ typedef struct
 	INT32 var;       ///< If needed, specifies information on the target amount to achieve (or target skin)
 	char *stringVar; ///< String version
 	char hint[110];  ///< Hint for emblem hints menu
-	UINT8 collected; ///< Do you have this emblem?
 } emblem_t;
 typedef struct
 {
@@ -104,7 +105,6 @@ typedef struct
 	UINT8 showconditionset; ///< Condition set that shows this emblem.
 	UINT8 sprite;           ///< emblem sprite to use, 0 - 25
 	UINT16 color;           ///< skincolor to use
-	UINT8 collected;        ///< Do you have this emblem?
 } extraemblem_t;
 
 // Unlockable information
@@ -120,7 +120,6 @@ typedef struct
 	char *stringVar;
 	UINT8 nocecho;
 	UINT8 nochecklist;
-	UINT8 unlocked;
 } unlockable_t;
 
 #define SECRET_NONE         -6 // Does nil.  Use with levels locked by UnlockRequired
@@ -143,6 +142,83 @@ typedef struct
 #define MAXEXTRAEMBLEMS   16
 #define MAXUNLOCKABLES    32
 
+/** Time attack information, currently a very small structure.
+  */
+typedef struct
+{
+	tic_t time;   ///< Time in which the level was finished.
+	UINT32 score; ///< Score when the level was finished.
+	UINT16 rings; ///< Rings when the level was finished.
+} recorddata_t;
+
+/** Setup for one NiGHTS map.
+  * These are dynamically allocated because I am insane
+  */
+#define GRADE_F 0
+#define GRADE_E 1
+#define GRADE_D 2
+#define GRADE_C 3
+#define GRADE_B 4
+#define GRADE_A 5
+#define GRADE_S 6
+
+typedef struct
+{
+	// 8 mares, 1 overall (0)
+	UINT8	nummares;
+	UINT32	score[9];
+	UINT8	grade[9];
+	tic_t	time[9];
+} nightsdata_t;
+
+// mapvisited is now a set of flags that says what we've done in the map.
+#define MV_VISITED      1
+#define MV_BEATEN       2
+#define MV_ALLEMERALDS  4
+#define MV_ULTIMATE     8
+#define MV_PERFECT     16
+#define MV_PERFECTRA   32
+#define MV_MAX         63 // used in gamedata check, update whenever MV's are added
+
+// Temporary holding place for nights data for the current map
+extern nightsdata_t ntemprecords;
+
+// GAMEDATA STRUCTURE
+// Everything that would get saved in gamedata.dat
+typedef struct
+{
+	// WHENEVER OR NOT WE'RE READY TO SAVE
+	boolean loaded;
+
+	// CONDITION SETS ACHIEVED
+	boolean achieved[MAXCONDITIONSETS];
+
+	// EMBLEMS COLLECTED
+	boolean collected[MAXEMBLEMS];
+
+	// EXTRA EMBLEMS COLLECTED
+	boolean extraCollected[MAXEXTRAEMBLEMS];
+
+	// UNLOCKABLES UNLOCKED
+	boolean unlocked[MAXUNLOCKABLES];
+
+	// TIME ATTACK DATA
+	recorddata_t *mainrecords[NUMMAPS];
+	nightsdata_t *nightsrecords[NUMMAPS];
+	UINT8 mapvisited[NUMMAPS];
+
+	// # OF TIMES THE GAME HAS BEEN BEATEN
+	UINT32 timesBeaten;
+	UINT32 timesBeatenWithEmeralds;
+	UINT32 timesBeatenUltimate;
+
+	// PLAY TIME
+	UINT32 totalplaytime;
+} gamedata_t;
+
+extern gamedata_t *clientGamedata;
+extern gamedata_t *serverGamedata;
+
 extern conditionset_t conditionSets[MAXCONDITIONSETS];
 extern emblem_t emblemlocations[MAXEMBLEMS];
 extern extraemblem_t extraemblems[MAXEXTRAEMBLEMS];
@@ -153,25 +229,30 @@ extern INT32 numextraemblems;
 
 extern UINT32 unlocktriggers;
 
+gamedata_t *M_NewGameDataStruct(void);
+void M_CopyGameData(gamedata_t *dest, gamedata_t *src);
+
 // Condition set setup
 void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2);
 
 // Clearing secrets
 void M_ClearConditionSet(UINT8 set);
-void M_ClearSecrets(void);
+void M_ClearSecrets(gamedata_t *data);
 
 // Updating conditions and unlockables
-void M_CheckUnlockConditions(void);
-UINT8 M_UpdateUnlockablesAndExtraEmblems(void);
-void M_SilentUpdateUnlockablesAndEmblems(void);
-UINT8 M_CheckLevelEmblems(void);
-UINT8 M_CompletionEmblems(void);
+void M_CheckUnlockConditions(gamedata_t *data);
+UINT8 M_UpdateUnlockablesAndExtraEmblems(gamedata_t *data);
+void M_SilentUpdateUnlockablesAndEmblems(gamedata_t *data);
+UINT8 M_CheckLevelEmblems(gamedata_t *data);
+UINT8 M_CompletionEmblems(gamedata_t *data);
+
+void M_SilentUpdateSkinAvailabilites(void);
 
 // Checking unlockable status
-UINT8 M_AnySecretUnlocked(void);
-UINT8 M_SecretUnlocked(INT32 type);
-UINT8 M_MapLocked(INT32 mapnum);
-INT32 M_CountEmblems(void);
+UINT8 M_AnySecretUnlocked(gamedata_t *data);
+UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data);
+UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data);
+INT32 M_CountEmblems(gamedata_t *data);
 
 // Emblem shit
 emblem_t *M_GetLevelEmblems(INT32 mapnum);
@@ -183,12 +264,14 @@ const char *M_GetExtraEmblemPatch(extraemblem_t *em, boolean big);
 // If you're looking to compare stats for unlocks or what not, use these
 // They stop checking upon reaching the target number so they
 // should be (theoretically?) slightly faster.
-UINT8 M_GotEnoughEmblems(INT32 number);
-UINT8 M_GotHighEnoughScore(INT32 tscore);
-UINT8 M_GotLowEnoughTime(INT32 tictime);
-UINT8 M_GotHighEnoughRings(INT32 trings);
+UINT8 M_GotEnoughEmblems(INT32 number, gamedata_t *data);
+UINT8 M_GotHighEnoughScore(INT32 tscore, gamedata_t *data);
+UINT8 M_GotLowEnoughTime(INT32 tictime, gamedata_t *data);
+UINT8 M_GotHighEnoughRings(INT32 trings, gamedata_t *data);
 
 INT32 M_UnlockableSkinNum(unlockable_t *unlock);
 INT32 M_EmblemSkinNum(emblem_t *emblem);
 
-#define M_Achieved(a) ((a) >= MAXCONDITIONSETS || conditionSets[a].achieved)
+#define M_Achieved(a, data) ((a) >= MAXCONDITIONSETS || data->achieved[a])
+
+#endif
diff --git a/src/m_menu.c b/src/m_menu.c
index 64a1c94048..2214e6a6a5 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -2284,6 +2284,7 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt);
 // Nextmap.  Used for Level select.
 void Nextmap_OnChange(void)
 {
+	gamedata_t *data = clientGamedata;
 	char *leveltitle;
 	char tabase[256];
 #ifdef OLDNREPLAYNAME
@@ -2301,7 +2302,7 @@ void Nextmap_OnChange(void)
 	{
 		CV_StealthSetValue(&cv_dummymares, 0);
 		// Hide the record changing CVAR if only one mare is available.
-		if (!nightsrecords[cv_nextmap.value-1] || nightsrecords[cv_nextmap.value-1]->nummares < 2)
+		if (!data->nightsrecords[cv_nextmap.value-1] || data->nightsrecords[cv_nextmap.value-1]->nummares < 2)
 			SP_NightsAttackMenu[narecords].status = IT_DISABLED;
 		else
 			SP_NightsAttackMenu[narecords].status = IT_STRING|IT_CVAR;
@@ -2432,14 +2433,15 @@ void Nextmap_OnChange(void)
 
 static void Dummymares_OnChange(void)
 {
-	if (!nightsrecords[cv_nextmap.value-1])
+	gamedata_t *data = clientGamedata;
+	if (!data->nightsrecords[cv_nextmap.value-1])
 	{
 		CV_StealthSetValue(&cv_dummymares, 0);
 		return;
 	}
 	else
 	{
-		UINT8 mares = nightsrecords[cv_nextmap.value-1]->nummares;
+		UINT8 mares = data->nightsrecords[cv_nextmap.value-1]->nummares;
 
 		if (cv_dummymares.value < 0)
 			CV_StealthSetValue(&cv_dummymares, mares);
@@ -3670,9 +3672,9 @@ void M_StartControlPanel(void)
 	if (!Playing())
 	{
 		// Secret menu!
-		MainMenu[singleplr].alphaKey = (M_AnySecretUnlocked()) ? 76 : 84;
-		MainMenu[multiplr].alphaKey = (M_AnySecretUnlocked()) ? 84 : 92;
-		MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
+		MainMenu[singleplr].alphaKey = (M_AnySecretUnlocked(clientGamedata)) ? 76 : 84;
+		MainMenu[multiplr].alphaKey = (M_AnySecretUnlocked(clientGamedata)) ? 84 : 92;
+		MainMenu[secrets].status = (M_AnySecretUnlocked(clientGamedata)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
 
 		currentMenu = &MainDef;
 		itemOn = singleplr;
@@ -3680,14 +3682,14 @@ void M_StartControlPanel(void)
 	else if (modeattacking)
 	{
 		currentMenu = &MAPauseDef;
-		MAPauseMenu[mapause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
+		MAPauseMenu[mapause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
 		itemOn = mapause_continue;
 	}
 	else if (!(netgame || multiplayer)) // Single Player
 	{
 		if (gamestate != GS_LEVEL || ultimatemode) // intermission, so gray out stuff.
 		{
-			SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA)) ? (IT_GRAYEDOUT) : (IT_DISABLED);
+			SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA, serverGamedata)) ? (IT_GRAYEDOUT) : (IT_DISABLED);
 			SPauseMenu[spause_retry].status = IT_GRAYEDOUT;
 		}
 		else
@@ -3696,7 +3698,7 @@ void M_StartControlPanel(void)
 			if (players[consoleplayer].playerstate != PST_LIVE)
 				++numlives;
 
-			SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
+			SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
 
 			// The list of things that can disable retrying is (was?) a little too complex
 			// for me to want to use the short if statement syntax
@@ -3710,7 +3712,7 @@ void M_StartControlPanel(void)
 		SPauseMenu[spause_levelselect].status = (gamecomplete == 1) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
 
 		// And emblem hints.
-		SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
+		SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
 
 		// Shift up Pandora's Box if both pandora and levelselect are active
 		/*if (SPauseMenu[spause_pandora].status != (IT_DISABLED)
@@ -4259,7 +4261,7 @@ static void M_DrawMapEmblems(INT32 mapnum, INT32 x, INT32 y, boolean norecordatt
 			x -= 4;
 		lasttype = curtype;
 
-		if (emblem->collected)
+		if (clientGamedata->collected[emblem - emblemlocations])
 			V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
 			                       R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
 		else
@@ -4692,6 +4694,8 @@ static void M_DrawGenericScrollMenu(void)
 
 static void M_DrawPauseMenu(void)
 {
+	gamedata_t *data = clientGamedata;
+
 	if (!netgame && !multiplayer && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
 	{
 		emblem_t *emblem_detail[3] = {NULL, NULL, NULL};
@@ -4720,7 +4724,7 @@ static void M_DrawPauseMenu(void)
 				{
 					case ET_SCORE:
 						snprintf(targettext, 9, "%d", emblem->var);
-						snprintf(currenttext, 9, "%u", G_GetBestScore(gamemap));
+						snprintf(currenttext, 9, "%u", G_GetBestScore(gamemap, data));
 
 						targettext[8] = 0;
 						currenttext[8] = 0;
@@ -4734,7 +4738,7 @@ static void M_DrawPauseMenu(void)
 							G_TicsToSeconds((tic_t)emblemslot),
 							G_TicsToCentiseconds((tic_t)emblemslot));
 
-						emblemslot = (INT32)G_GetBestTime(gamemap); // dumb hack pt ii
+						emblemslot = (INT32)G_GetBestTime(gamemap, data); // dumb hack pt ii
 						if ((tic_t)emblemslot == UINT32_MAX)
 							snprintf(currenttext, 9, "-:--.--");
 						else
@@ -4750,7 +4754,7 @@ static void M_DrawPauseMenu(void)
 						break;
 					case ET_RINGS:
 						snprintf(targettext, 9, "%d", emblem->var);
-						snprintf(currenttext, 9, "%u", G_GetBestRings(gamemap));
+						snprintf(currenttext, 9, "%u", G_GetBestRings(gamemap, data));
 
 						targettext[8] = 0;
 						currenttext[8] = 0;
@@ -4758,8 +4762,8 @@ static void M_DrawPauseMenu(void)
 						emblemslot = 2;
 						break;
 					case ET_NGRADE:
-						snprintf(targettext, 9, "%u", P_GetScoreForGradeOverall(gamemap, emblem->var));
-						snprintf(currenttext, 9, "%u", G_GetBestNightsScore(gamemap, 0));
+						snprintf(targettext, 9, "%u", P_GetScoreForGrade(gamemap, 0, emblem->var));
+						snprintf(currenttext, 9, "%u", G_GetBestNightsScore(gamemap, 0, data));
 
 						targettext[8] = 0;
 						currenttext[8] = 0;
@@ -4773,7 +4777,7 @@ static void M_DrawPauseMenu(void)
 							G_TicsToSeconds((tic_t)emblemslot),
 							G_TicsToCentiseconds((tic_t)emblemslot));
 
-						emblemslot = (INT32)G_GetBestNightsTime(gamemap, 0); // dumb hack pt iv
+						emblemslot = (INT32)G_GetBestNightsTime(gamemap, 0, data); // dumb hack pt iv
 						if ((tic_t)emblemslot == UINT32_MAX)
 							snprintf(currenttext, 9, "-:--.--");
 						else
@@ -4807,7 +4811,7 @@ static void M_DrawPauseMenu(void)
 			if (!emblem)
 				continue;
 
-			if (emblem->collected)
+			if (data->collected[emblem - emblemlocations])
 				V_DrawSmallMappedPatch(40, 44 + (i*8), 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
 				                       R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
 			else
@@ -5019,7 +5023,9 @@ static void M_PatchSkinNameTable(void)
 //
 static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
 {
-	if (M_MapLocked(mapnum+1))
+	gamedata_t *data = serverGamedata;
+
+	if (M_MapLocked(mapnum+1, data))
 		return false; // not unlocked
 
 	switch (levellistmode)
@@ -5032,7 +5038,7 @@ static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
 				return true;
 
 #ifndef DEVELOP
-			if (mapvisited[mapnum]) // MV_MP
+			if (data->mapvisited[mapnum])
 #endif
 				return true;
 
@@ -5040,7 +5046,7 @@ static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
 		case LLM_RECORDATTACK:
 		case LLM_NIGHTSATTACK:
 #ifndef DEVELOP
-			if (mapvisited[mapnum] & MV_MAX)
+			if (data->mapvisited[mapnum])
 				return true;
 
 			if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED)
@@ -5071,7 +5077,7 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt)
 	if (!mapheaderinfo[mapnum]->lvlttl[0])
 		return false;
 
-	/*if (M_MapLocked(mapnum+1))
+	/*if (M_MapLocked(mapnum+1, serverGamedata))
 		return false; // not unlocked*/
 
 	switch (levellistmode)
@@ -6904,7 +6910,7 @@ static void M_HandleAddons(INT32 choice)
 		closefilemenu(true);
 
 		// secrets disabled by addfile...
-		MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
+		MainMenu[secrets].status = (M_AnySecretUnlocked(clientGamedata)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
 
 		if (currentMenu->prevMenu)
 			M_SetupNextMenu(currentMenu->prevMenu);
@@ -7150,7 +7156,9 @@ static boolean checklist_cangodown; // uuuueeerggghhhh HACK
 
 static void M_HandleChecklist(INT32 choice)
 {
+	gamedata_t *data = clientGamedata;
 	INT32 j;
+
 	switch (choice)
 	{
 		case KEY_DOWNARROW:
@@ -7167,7 +7175,7 @@ static void M_HandleChecklist(INT32 choice)
 						continue;
 					if (unlockables[j].conditionset > MAXCONDITIONSETS)
 						continue;
-					if (!unlockables[j].unlocked && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset))
+					if (!data->unlocked[j] && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset, data))
 						continue;
 					if (unlockables[j].conditionset == unlockables[check_on].conditionset)
 						continue;
@@ -7192,7 +7200,7 @@ static void M_HandleChecklist(INT32 choice)
 						continue;
 					if (unlockables[j].conditionset > MAXCONDITIONSETS)
 						continue;
-					if (!unlockables[j].unlocked && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset))
+					if (!data->unlocked[j] && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset, data))
 						continue;
 					if (j && unlockables[j].conditionset == unlockables[j-1].conditionset)
 						continue;
@@ -7218,6 +7226,9 @@ static void M_HandleChecklist(INT32 choice)
 
 static void M_DrawChecklist(void)
 {
+	gamedata_t *data = clientGamedata;
+	INT32 emblemCount = M_CountEmblems(data);
+
 	INT32 i = check_on, j = 0, y = currentMenu->y, emblems = numemblems+numextraemblems;
 	UINT32 condnum, previd, maxcond;
 	condition_t *cond;
@@ -7228,7 +7239,7 @@ static void M_DrawChecklist(void)
 	// draw emblem counter
 	if (emblems > 0)
 	{
-		V_DrawString(42, 20, (emblems == M_CountEmblems()) ? V_GREENMAP : 0, va("%d/%d", M_CountEmblems(), emblems));
+		V_DrawString(42, 20, (emblems == emblemCount) ? V_GREENMAP : 0, va("%d/%d", emblemCount, emblems));
 		V_DrawSmallScaledPatch(28, 20, 0, W_CachePatchName("EMBLICON", PU_PATCH));
 	}
 
@@ -7239,13 +7250,13 @@ static void M_DrawChecklist(void)
 	{
 		if (unlockables[i].name[0] == 0 //|| unlockables[i].nochecklist
 		|| !unlockables[i].conditionset || unlockables[i].conditionset > MAXCONDITIONSETS
-		|| (!unlockables[i].unlocked && unlockables[i].showconditionset && !M_Achieved(unlockables[i].showconditionset)))
+		|| (!data->unlocked[i] && unlockables[i].showconditionset && !M_Achieved(unlockables[i].showconditionset, data)))
 		{
 			i += 1;
 			continue;
 		}
 
-		V_DrawString(currentMenu->x, y, ((unlockables[i].unlocked) ? V_GREENMAP : V_TRANSLUCENT)|V_ALLOWLOWERCASE, ((unlockables[i].unlocked || !unlockables[i].nochecklist) ? unlockables[i].name : M_CreateSecretMenuOption(unlockables[i].name)));
+		V_DrawString(currentMenu->x, y, ((data->unlocked[i]) ? V_GREENMAP : V_TRANSLUCENT)|V_ALLOWLOWERCASE, ((data->unlocked[i] || !unlockables[i].nochecklist) ? unlockables[i].name : M_CreateSecretMenuOption(unlockables[i].name)));
 
 		for (j = i+1; j < MAXUNLOCKABLES; j++)
 		{
@@ -7323,7 +7334,7 @@ static void M_DrawChecklist(void)
 
 									if (title)
 									{
-										const char *level = ((M_MapLocked(cond[condnum].requirement) || !((mapheaderinfo[cond[condnum].requirement-1]->menuflags & LF2_NOVISITNEEDED) || (mapvisited[cond[condnum].requirement-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
+										const char *level = ((M_MapLocked(cond[condnum].requirement, data) || !((mapheaderinfo[cond[condnum].requirement-1]->menuflags & LF2_NOVISITNEEDED) || (data->mapvisited[cond[condnum].requirement-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
 
 										switch (cond[condnum].type)
 										{
@@ -7356,7 +7367,7 @@ static void M_DrawChecklist(void)
 
 									if (title)
 									{
-										const char *level = ((M_MapLocked(cond[condnum].extrainfo1) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
+										const char *level = ((M_MapLocked(cond[condnum].extrainfo1, data) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (data->mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
 
 										switch (cond[condnum].type)
 										{
@@ -7425,7 +7436,7 @@ static void M_DrawChecklist(void)
 
 									if (title)
 									{
-										const char *level = ((M_MapLocked(cond[condnum].extrainfo1) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
+										const char *level = ((M_MapLocked(cond[condnum].extrainfo1, data) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (data->mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
 
 										switch (cond[condnum].type)
 										{
@@ -7488,7 +7499,7 @@ static void M_DrawChecklist(void)
 
 		/*V_DrawString(160, 8+(24*j), V_RETURN8, V_WordWrap(160, 292, 0, unlockables[i].objective));
 
-		if (unlockables[i].unlocked)
+		if (data->unlocked[i])
 			V_DrawString(308, 8+(24*j), V_YELLOWMAP, "Y");
 		else
 			V_DrawString(308, 8+(24*j), V_YELLOWMAP, "N");*/
@@ -7517,7 +7528,7 @@ static void M_EmblemHints(INT32 choice)
 
 	(void)choice;
 	SR_EmblemHintMenu[0].status = (local > NUMHINTS*2) ? (IT_STRING | IT_ARROWS) : (IT_DISABLED);
-	SR_EmblemHintMenu[1].status = (M_SecretUnlocked(SECRET_ITEMFINDER)) ? (IT_CVAR|IT_STRING) : (IT_SECRET);
+	SR_EmblemHintMenu[1].status = (M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata)) ? (IT_CVAR|IT_STRING) : (IT_SECRET);
 	hintpage = 1;
 	SR_EmblemHintDef.prevMenu = currentMenu;
 	M_SetupNextMenu(&SR_EmblemHintDef);
@@ -7577,7 +7588,7 @@ static void M_DrawEmblemHints(void)
 
 		if (totalemblems >= ((hintpage-1)*(NUMHINTS*2) + 1) && totalemblems < (hintpage*NUMHINTS*2)+1){
 
-			if (emblem->collected)
+			if (clientGamedata->collected[i])
 			{
 				collected = V_GREENMAP;
 				V_DrawMappedPatch(x, y+4, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
@@ -8117,7 +8128,7 @@ static void M_SecretsMenu(INT32 choice)
 
 		SR_MainMenu[i].status = IT_SECRET;
 
-		if (unlockables[ul].unlocked)
+		if (clientGamedata->unlocked[ul])
 		{
 			switch (unlockables[ul].type)
 			{
@@ -8216,7 +8227,7 @@ static void M_SinglePlayerMenu(INT32 choice)
 
 	levellistmode = LLM_RECORDATTACK;
 	if (M_GametypeHasLevels(-1))
-		SP_MainMenu[sprecordattack].status = (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING : IT_SECRET;
+		SP_MainMenu[sprecordattack].status = (M_SecretUnlocked(SECRET_RECORDATTACK, clientGamedata)) ? IT_CALL|IT_STRING : IT_SECRET;
 	else // If Record Attack is nonexistent in the current add-on...
 	{
 		SP_MainMenu[sprecordattack].status = IT_NOTHING|IT_DISABLED; // ...hide and disable the Record Attack option...
@@ -8226,7 +8237,7 @@ static void M_SinglePlayerMenu(INT32 choice)
 
 	levellistmode = LLM_NIGHTSATTACK;
 	if (M_GametypeHasLevels(-1))
-		SP_MainMenu[spnightsmode].status = (M_SecretUnlocked(SECRET_NIGHTSMODE)) ? IT_CALL|IT_STRING : IT_SECRET;
+		SP_MainMenu[spnightsmode].status = (M_SecretUnlocked(SECRET_NIGHTSMODE, clientGamedata)) ? IT_CALL|IT_STRING : IT_SECRET;
 	else // If NiGHTS Mode is nonexistent in the current add-on...
 	{
 		SP_MainMenu[spnightsmode].status = IT_NOTHING|IT_DISABLED; // ...hide and disable the NiGHTS Mode option...
@@ -8249,7 +8260,7 @@ static void M_SinglePlayerMenu(INT32 choice)
 		SP_MainMenu[spnightsmode]  .alphaKey += 8;
 	}
 	else // Otherwise, if Marathon Run is allowed and Record Attack is unlocked, unlock Marathon Run!
-		SP_MainMenu[spmarathon].status = (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED : IT_SECRET;
+		SP_MainMenu[spmarathon].status = (M_SecretUnlocked(SECRET_RECORDATTACK, clientGamedata)) ? IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED : IT_SECRET;
 
 
 	if (tutorialmap) // If there's a tutorial available in the current add-on...
@@ -9626,7 +9637,7 @@ static void M_Statistics(INT32 choice)
 		if (!(mapheaderinfo[i]->typeoflevel & TOL_SP) || (mapheaderinfo[i]->menuflags & LF2_HIDEINSTATS))
 			continue;
 
-		if (!(mapvisited[i] & MV_MAX))
+		if (!(clientGamedata->mapvisited[i] & MV_MAX))
 			continue;
 
 		statsMapList[j++] = i;
@@ -9643,6 +9654,7 @@ static void M_Statistics(INT32 choice)
 
 static void M_DrawStatsMaps(int location)
 {
+	gamedata_t *data = clientGamedata;
 	INT32 y = 80, i = -1;
 	INT16 mnum;
 	extraemblem_t *exemblem;
@@ -9710,14 +9722,14 @@ static void M_DrawStatsMaps(int location)
 		{
 			exemblem = &extraemblems[i];
 
-			if (exemblem->collected)
+			if (data->extraCollected[i])
 				V_DrawSmallMappedPatch(292, y, 0, W_CachePatchName(M_GetExtraEmblemPatch(exemblem, false), PU_PATCH),
 				                       R_GetTranslationColormap(TC_DEFAULT, M_GetExtraEmblemColor(exemblem), GTC_CACHE));
 			else
 				V_DrawSmallScaledPatch(292, y, 0, W_CachePatchName("NEEDIT", PU_PATCH));
 
 			V_DrawString(20, y, V_YELLOWMAP|V_ALLOWLOWERCASE,
-				(!exemblem->collected && exemblem->showconditionset && !M_Achieved(exemblem->showconditionset))
+				(!data->extraCollected[i] && exemblem->showconditionset && !M_Achieved(exemblem->showconditionset, data))
 				? M_CreateSecretMenuOption(exemblem->description)
 				: exemblem->description);
 		}
@@ -9734,6 +9746,7 @@ bottomarrow:
 
 static void M_DrawLevelStats(void)
 {
+	gamedata_t *data = clientGamedata;
 	char beststr[40];
 
 	tic_t besttime = 0;
@@ -9748,9 +9761,9 @@ static void M_DrawLevelStats(void)
 
 	V_DrawString(20, 24, V_YELLOWMAP, "Total Play Time:");
 	V_DrawCenteredString(BASEVIDWIDTH/2, 32, 0, va("%i hours, %i minutes, %i seconds",
-	                         G_TicsToHours(totalplaytime),
-	                         G_TicsToMinutes(totalplaytime, false),
-	                         G_TicsToSeconds(totalplaytime)));
+	                         G_TicsToHours(data->totalplaytime),
+	                         G_TicsToMinutes(data->totalplaytime, false),
+	                         G_TicsToSeconds(data->totalplaytime)));
 
 	for (i = 0; i < NUMMAPS; i++)
 	{
@@ -9759,25 +9772,25 @@ static void M_DrawLevelStats(void)
 		if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
 			continue;
 
-		if (!mainrecords[i])
+		if (!data->mainrecords[i])
 		{
 			mapsunfinished++;
 			bestunfinished[0] = bestunfinished[1] = bestunfinished[2] = true;
 			continue;
 		}
 
-		if (mainrecords[i]->score > 0)
-			bestscore += mainrecords[i]->score;
+		if (data->mainrecords[i]->score > 0)
+			bestscore += data->mainrecords[i]->score;
 		else
 			mapunfinished = bestunfinished[0] = true;
 
-		if (mainrecords[i]->time > 0)
-			besttime += mainrecords[i]->time;
+		if (data->mainrecords[i]->time > 0)
+			besttime += data->mainrecords[i]->time;
 		else
 			mapunfinished = bestunfinished[1] = true;
 
-		if (mainrecords[i]->rings > 0)
-			bestrings += mainrecords[i]->rings;
+		if (data->mainrecords[i]->rings > 0)
+			bestrings += data->mainrecords[i]->rings;
 		else
 			mapunfinished = bestunfinished[2] = true;
 
@@ -9792,7 +9805,7 @@ static void M_DrawLevelStats(void)
 	else
 		V_DrawString(20, 56, V_GREENMAP, "(complete)");
 
-	V_DrawString(36, 64, 0, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems));
+	V_DrawString(36, 64, 0, va("x %d/%d", M_CountEmblems(data), numemblems+numextraemblems));
 	V_DrawSmallScaledPatch(20, 64, 0, W_CachePatchName("EMBLICON", PU_PATCH));
 
 	sprintf(beststr, "%u", bestscore);
@@ -9859,6 +9872,7 @@ static void M_HandleLevelStats(INT32 choice)
 // Drawing function for Time Attack
 void M_DrawTimeAttackMenu(void)
 {
+	gamedata_t *data = clientGamedata;
 	INT32 i, x, y, empatx, empaty, cursory = 0;
 	UINT16 dispstatus;
 	patch_t *PictureOfUrFace;	// my WHAT
@@ -10017,7 +10031,7 @@ void M_DrawTimeAttackMenu(void)
 			empatx = empatch->leftoffset / 2;
 			empaty = empatch->topoffset / 2;
 
-			if (em->collected)
+			if (data->collected[em - emblemlocations])
 				V_DrawSmallMappedPatch(104+76+empatx, yHeight+lsheadingheight/2+empaty, 0, empatch,
 				                       R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
 			else
@@ -10030,34 +10044,34 @@ void M_DrawTimeAttackMenu(void)
 		// Draw in-level emblems.
 		M_DrawMapEmblems(cv_nextmap.value, 288, 28, true);
 
-		if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->score)
+		if (!data->mainrecords[cv_nextmap.value-1] || !data->mainrecords[cv_nextmap.value-1]->score)
 			sprintf(beststr, "(none)");
 		else
-			sprintf(beststr, "%u", mainrecords[cv_nextmap.value-1]->score);
+			sprintf(beststr, "%u", data->mainrecords[cv_nextmap.value-1]->score);
 
 		V_DrawString(104-72, 33+lsheadingheight/2, V_YELLOWMAP, "SCORE:");
 		V_DrawRightAlignedString(104+64, 33+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
 		V_DrawRightAlignedString(104+72, 43+lsheadingheight/2, V_ALLOWLOWERCASE, reqscore);
 
-		if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->time)
+		if (!data->mainrecords[cv_nextmap.value-1] || !data->mainrecords[cv_nextmap.value-1]->time)
 			sprintf(beststr, "(none)");
 		else
-			sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(mainrecords[cv_nextmap.value-1]->time, true),
-			                                 G_TicsToSeconds(mainrecords[cv_nextmap.value-1]->time),
-			                                 G_TicsToCentiseconds(mainrecords[cv_nextmap.value-1]->time));
+			sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(data->mainrecords[cv_nextmap.value-1]->time, true),
+			                                 G_TicsToSeconds(data->mainrecords[cv_nextmap.value-1]->time),
+			                                 G_TicsToCentiseconds(data->mainrecords[cv_nextmap.value-1]->time));
 
 		V_DrawString(104-72, 53+lsheadingheight/2, V_YELLOWMAP, "TIME:");
 		V_DrawRightAlignedString(104+64, 53+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
 		V_DrawRightAlignedString(104+72, 63+lsheadingheight/2, V_ALLOWLOWERCASE, reqtime);
 
-		if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->rings)
+		if (!data->mainrecords[cv_nextmap.value-1] || !data->mainrecords[cv_nextmap.value-1]->rings)
 			sprintf(beststr, "(none)");
 		else
-			sprintf(beststr, "%hu", mainrecords[cv_nextmap.value-1]->rings);
+			sprintf(beststr, "%hu", data->mainrecords[cv_nextmap.value-1]->rings);
 
 		V_DrawString(104-72, 73+lsheadingheight/2, V_YELLOWMAP, "RINGS:");
 
-		V_DrawRightAlignedString(104+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE|((mapvisited[cv_nextmap.value-1] & MV_PERFECTRA) ? V_YELLOWMAP : 0), beststr);
+		V_DrawRightAlignedString(104+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE|((data->mapvisited[cv_nextmap.value-1] & MV_PERFECTRA) ? V_YELLOWMAP : 0), beststr);
 
 		V_DrawRightAlignedString(104+72, 83+lsheadingheight/2, V_ALLOWLOWERCASE, reqrings);
 	}
@@ -10151,6 +10165,7 @@ static void M_TimeAttack(INT32 choice)
 // Drawing function for Nights Attack
 void M_DrawNightsAttackMenu(void)
 {
+	gamedata_t *data = clientGamedata;
 	INT32 i, x, y, cursory = 0;
 	UINT16 dispstatus;
 
@@ -10217,10 +10232,10 @@ void M_DrawNightsAttackMenu(void)
 		lumpnum_t lumpnum;
 		char beststr[40];
 
-		//UINT8 bestoverall	= G_GetBestNightsGrade(cv_nextmap.value, 0);
-		UINT8 bestgrade		= G_GetBestNightsGrade(cv_nextmap.value, cv_dummymares.value);
-		UINT32 bestscore	= G_GetBestNightsScore(cv_nextmap.value, cv_dummymares.value);
-		tic_t besttime		= G_GetBestNightsTime(cv_nextmap.value, cv_dummymares.value);
+		//UINT8 bestoverall	= G_GetBestNightsGrade(cv_nextmap.value, 0, data);
+		UINT8 bestgrade		= G_GetBestNightsGrade(cv_nextmap.value, cv_dummymares.value, data);
+		UINT32 bestscore	= G_GetBestNightsScore(cv_nextmap.value, cv_dummymares.value, data);
+		tic_t besttime		= G_GetBestNightsTime(cv_nextmap.value, cv_dummymares.value, data);
 
 		M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false);
 
@@ -10301,7 +10316,7 @@ void M_DrawNightsAttackMenu(void)
 						goto skipThisOne;
 				}
 
-				if (em->collected)
+				if (data->collected[em - emblemlocations])
 					V_DrawSmallMappedPatch(xpos, yHeight+lsheadingheight/2, 0, W_CachePatchName(M_GetEmblemPatch(em, false), PU_PATCH),
 																 R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
 				else
@@ -12544,12 +12559,12 @@ static void M_EraseDataResponse(INT32 ch)
 
 	// Delete the data
 	if (erasecontext != 1)
-		G_ClearRecords();
+		G_ClearRecords(clientGamedata);
 	if (erasecontext != 0)
-		M_ClearSecrets();
+		M_ClearSecrets(clientGamedata);
 	if (erasecontext == 2)
 	{
-		totalplaytime = 0;
+		clientGamedata->totalplaytime = 0;
 		F_StartIntro();
 	}
 	BwehHehHe();
diff --git a/src/m_random.c b/src/m_random.c
index 3d0774a60b..8b5138b9c8 100644
--- a/src/m_random.c
+++ b/src/m_random.c
@@ -14,12 +14,11 @@
 
 #include "doomdef.h"
 #include "doomtype.h"
-#include "doomstat.h" // totalplaytime
 
 #include "m_random.h"
 #include "m_fixed.h"
 
-
+#include "m_cond.h" // totalplaytime
 
 // ---------------------------
 // RNG functions (not synched)
@@ -252,5 +251,5 @@ void P_SetRandSeedD(const char *rfile, INT32 rline, UINT32 seed)
   */
 UINT32 M_RandomizedSeed(void)
 {
-	return ((totalplaytime & 0xFFFF) << 16)|M_RandomFixed();
+	return ((serverGamedata->totalplaytime & 0xFFFF) << 16) | M_RandomFixed();
 }
diff --git a/src/p_inter.c b/src/p_inter.c
index 873448dcd6..f2d20912f0 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -740,10 +740,22 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			{
 				if (demoplayback || (player->bot && player->bot != BOT_MPAI) || special->health <= 0 || special->health > MAXEMBLEMS)
 					return;
-				emblemlocations[special->health-1].collected = true;
 
-				M_UpdateUnlockablesAndExtraEmblems();
-				G_SaveGameData();
+				if (emblemlocations[special->health-1].type == ET_SKIN)
+				{
+					INT32 skinnum = M_EmblemSkinNum(&emblemlocations[special->health-1]);
+
+					if (player->skin != skinnum)
+					{
+						return;
+					}
+				}
+
+				clientGamedata->collected[special->health-1] = serverGamedata->collected[special->health-1] = true;
+
+				M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
+				M_UpdateUnlockablesAndExtraEmblems(clientGamedata);
+				G_SaveGameData(clientGamedata);
 				break;
 			}
 
diff --git a/src/p_mobj.c b/src/p_mobj.c
index eeaf547769..6563e6f0a7 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -12004,10 +12004,6 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
 			return false; // You already got this token
 
 		break;
-	case MT_EMBLEM:
-		if (netgame || multiplayer)
-			return false; // Single player (You're next on my shit list)
-		break;
 	default:
 		break;
 	}
@@ -12175,15 +12171,20 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
 	emcolor = M_GetEmblemColor(&emblemlocations[j]); // workaround for compiler complaint about bad function casting
 	mobj->color = (UINT16)emcolor;
 
-	validEmblem = !emblemlocations[j].collected;
+	validEmblem = true;
 
-	if (emblemlocations[j].type == ET_SKIN)
+	if (!netgame)
 	{
-		INT32 skinnum = M_EmblemSkinNum(&emblemlocations[j]);
+		validEmblem = !serverGamedata->collected[j];
 
-		if (players[0].skin != skinnum)
+		if (emblemlocations[j].type == ET_SKIN && !multiplayer)
 		{
-			validEmblem = false;
+			INT32 skinnum = M_EmblemSkinNum(&emblemlocations[j]);
+
+			if (players[0].skin != skinnum)
+			{
+				validEmblem = false;
+			}
 		}
 	}
 
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 8c8a783225..c18319c698 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -47,6 +47,7 @@ UINT8 *save_p;
 #define ARCHIVEBLOCK_POBJS    0x7F928546
 #define ARCHIVEBLOCK_THINKERS 0x7F37037C
 #define ARCHIVEBLOCK_SPECIALS 0x7F228378
+#define ARCHIVEBLOCK_EMBLEMS  0x7F4A5445
 
 // Note: This cannot be bigger
 // than an UINT16
@@ -4339,6 +4340,8 @@ static void P_NetArchiveMisc(boolean resending)
 
 	WRITEUINT32(save_p, hidetime);
 
+	WRITEUINT32(save_p, unlocktriggers);
+
 	// Is it paused?
 	if (paused)
 		WRITEUINT8(save_p, 0x2f);
@@ -4437,6 +4440,8 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
 
 	hidetime = READUINT32(save_p);
 
+	unlocktriggers = READUINT32(save_p);
+
 	// Is it paused?
 	if (READUINT8(save_p) == 0x2f)
 		paused = true;
@@ -4444,6 +4449,224 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
 	return true;
 }
 
+
+static inline void P_NetArchiveEmblems(void)
+{
+	gamedata_t *data = serverGamedata;
+	INT32 i, j;
+	UINT8 btemp;
+	INT32 curmare;
+
+	WRITEUINT32(save_p, ARCHIVEBLOCK_EMBLEMS);
+
+	// These should be synchronized before savegame loading by the wad files being the same anyway,
+	// but just in case, for now, we'll leave them here for testing. It would be very bad if they mismatch.
+	WRITEUINT8(save_p, (UINT8)savemoddata);
+	WRITEINT32(save_p, numemblems);
+	WRITEINT32(save_p, numextraemblems);
+
+	// The rest of this is lifted straight from G_SaveGameData in g_game.c
+	// TODO: Optimize this to only send information about emblems, unlocks, etc. which actually exist
+	//       There is no need to go all the way up to MAXEMBLEMS when wads are guaranteed to be the same.
+
+	WRITEUINT32(save_p, data->totalplaytime);
+
+	// TODO put another cipher on these things? meh, I don't care...
+	for (i = 0; i < NUMMAPS; i++)
+		WRITEUINT8(save_p, (data->mapvisited[i] & MV_MAX));
+
+	// To save space, use one bit per collected/achieved/unlocked flag
+	for (i = 0; i < MAXEMBLEMS;)
+	{
+		btemp = 0;
+		for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
+			btemp |= (data->collected[j+i] << j);
+		WRITEUINT8(save_p, btemp);
+		i += j;
+	}
+	for (i = 0; i < MAXEXTRAEMBLEMS;)
+	{
+		btemp = 0;
+		for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
+			btemp |= (data->extraCollected[j+i] << j);
+		WRITEUINT8(save_p, btemp);
+		i += j;
+	}
+	for (i = 0; i < MAXUNLOCKABLES;)
+	{
+		btemp = 0;
+		for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
+			btemp |= (data->unlocked[j+i] << j);
+		WRITEUINT8(save_p, btemp);
+		i += j;
+	}
+	for (i = 0; i < MAXCONDITIONSETS;)
+	{
+		btemp = 0;
+		for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
+			btemp |= (data->achieved[j+i] << j);
+		WRITEUINT8(save_p, btemp);
+		i += j;
+	}
+
+	WRITEUINT32(save_p, data->timesBeaten);
+	WRITEUINT32(save_p, data->timesBeatenWithEmeralds);
+	WRITEUINT32(save_p, data->timesBeatenUltimate);
+
+	// Main records
+	for (i = 0; i < NUMMAPS; i++)
+	{
+		if (data->mainrecords[i])
+		{
+			WRITEUINT32(save_p, data->mainrecords[i]->score);
+			WRITEUINT32(save_p, data->mainrecords[i]->time);
+			WRITEUINT16(save_p, data->mainrecords[i]->rings);
+		}
+		else
+		{
+			WRITEUINT32(save_p, 0);
+			WRITEUINT32(save_p, 0);
+			WRITEUINT16(save_p, 0);
+		}
+	}
+
+	// NiGHTS records
+	for (i = 0; i < NUMMAPS; i++)
+	{
+		if (!data->nightsrecords[i] || !data->nightsrecords[i]->nummares)
+		{
+			WRITEUINT8(save_p, 0);
+			continue;
+		}
+
+		WRITEUINT8(save_p, data->nightsrecords[i]->nummares);
+
+		for (curmare = 0; curmare < (data->nightsrecords[i]->nummares + 1); ++curmare)
+		{
+			WRITEUINT32(save_p, data->nightsrecords[i]->score[curmare]);
+			WRITEUINT8(save_p, data->nightsrecords[i]->grade[curmare]);
+			WRITEUINT32(save_p, data->nightsrecords[i]->time[curmare]);
+		}
+	}
+}
+
+static inline void P_NetUnArchiveEmblems(void)
+{
+	gamedata_t *data = serverGamedata;
+	INT32 i, j;
+	UINT8 rtemp;
+	UINT32 recscore;
+	tic_t rectime;
+	UINT16 recrings;
+	UINT8 recmares;
+	INT32 curmare;
+
+	if (READUINT32(save_p) != ARCHIVEBLOCK_EMBLEMS)
+		I_Error("Bad $$$.sav at archive block Emblems");
+
+	savemoddata = (boolean)READUINT8(save_p); // this one is actually necessary because savemoddata stays false otherwise for some reason.
+
+	if (numemblems != READINT32(save_p))
+		I_Error("numemblems mismatch");
+	if (numextraemblems != READINT32(save_p))
+		I_Error("numextraemblems mismatch");
+
+	// This shouldn't happen, but if something really fucked up happens and you transfer
+	// the SERVER player's gamedata over your own CLIENT gamedata,
+	// then this prevents it from being saved over yours.
+	data->loaded = false;
+
+	M_ClearSecrets(data);
+	G_ClearRecords(data);
+
+	// The rest of this is lifted straight from G_LoadGameData in g_game.c
+	// TODO: Optimize this to only read information about emblems, unlocks, etc. which actually exist
+	//       There is no need to go all the way up to MAXEMBLEMS when wads are guaranteed to be the same.
+
+	data->totalplaytime = READUINT32(save_p);
+
+	// TODO put another cipher on these things? meh, I don't care...
+	for (i = 0; i < NUMMAPS; i++)
+		if ((data->mapvisited[i] = READUINT8(save_p)) > MV_MAX)
+			I_Error("Bad $$$.sav dearchiving Emblems");
+
+	// To save space, use one bit per collected/achieved/unlocked flag
+	for (i = 0; i < MAXEMBLEMS;)
+	{
+		rtemp = READUINT8(save_p);
+		for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
+			data->collected[j+i] = ((rtemp >> j) & 1);
+		i += j;
+	}
+	for (i = 0; i < MAXEXTRAEMBLEMS;)
+	{
+		rtemp = READUINT8(save_p);
+		for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
+			data->extraCollected[j+i] = ((rtemp >> j) & 1);
+		i += j;
+	}
+	for (i = 0; i < MAXUNLOCKABLES;)
+	{
+		rtemp = READUINT8(save_p);
+		for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
+			data->unlocked[j+i] = ((rtemp >> j) & 1);
+		i += j;
+	}
+	for (i = 0; i < MAXCONDITIONSETS;)
+	{
+		rtemp = READUINT8(save_p);
+		for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
+			data->achieved[j+i] = ((rtemp >> j) & 1);
+		i += j;
+	}
+
+	data->timesBeaten = READUINT32(save_p);
+	data->timesBeatenWithEmeralds = READUINT32(save_p);
+	data->timesBeatenUltimate = READUINT32(save_p);
+
+	// Main records
+	for (i = 0; i < NUMMAPS; ++i)
+	{
+		recscore = READUINT32(save_p);
+		rectime  = (tic_t)READUINT32(save_p);
+		recrings = READUINT16(save_p);
+
+		if (recrings > 10000 || recscore > MAXSCORE)
+			I_Error("Bad $$$.sav dearchiving Emblems");
+
+		if (recscore || rectime || recrings)
+		{
+			G_AllocMainRecordData((INT16)i, data);
+			data->mainrecords[i]->score = recscore;
+			data->mainrecords[i]->time = rectime;
+			data->mainrecords[i]->rings = recrings;
+		}
+	}
+
+	// Nights records
+	for (i = 0; i < NUMMAPS; ++i)
+	{
+		if ((recmares = READUINT8(save_p)) == 0)
+			continue;
+
+		G_AllocNightsRecordData((INT16)i, data);
+
+		for (curmare = 0; curmare < (recmares+1); ++curmare)
+		{
+			data->nightsrecords[i]->score[curmare] = READUINT32(save_p);
+			data->nightsrecords[i]->grade[curmare] = READUINT8(save_p);
+			data->nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
+
+			if (data->nightsrecords[i]->grade[curmare] > GRADE_S)
+			{
+				I_Error("Bad $$$.sav dearchiving Emblems");
+			}
+		}
+
+		data->nightsrecords[i]->nummares = recmares;
+	}
+}
+
 static inline void P_ArchiveLuabanksAndConsistency(void)
 {
 	UINT8 i, banksinuse = NUM_LUABANKS;
@@ -4507,6 +4730,7 @@ void P_SaveNetGame(boolean resending)
 
 	CV_SaveNetVars(&save_p);
 	P_NetArchiveMisc(resending);
+	P_NetArchiveEmblems();
 
 	// Assign the mobjnumber for pointer tracking
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
@@ -4559,6 +4783,7 @@ boolean P_LoadNetGame(boolean reloading)
 	CV_LoadNetVars(&save_p);
 	if (!P_NetUnArchiveMisc(reloading))
 		return false;
+	P_NetUnArchiveEmblems();
 	P_NetUnArchivePlayers();
 	if (gamestate == GS_LEVEL)
 	{
diff --git a/src/p_setup.c b/src/p_setup.c
index c8b0936b80..70a2c0a8bd 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -7460,7 +7460,7 @@ static void P_WriteLetter(void)
 {
 	char *buf, *b;
 
-	if (!unlockables[28].unlocked) // pandora's box
+	if (!serverGamedata->unlocked[28]) // pandora's box
 		return;
 
 	if (modeattacking)
@@ -7804,10 +7804,11 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 	nextmapoverride = 0;
 	skipstats = 0;
 
-	if (!(netgame || multiplayer || demoplayback))
-		mapvisited[gamemap-1] |= MV_VISITED;
-	else if (netgame || multiplayer)
-		mapvisited[gamemap-1] |= MV_MP; // you want to record that you've been there this session, but not permanently
+	if (!demoplayback)
+	{
+		clientGamedata->mapvisited[gamemap-1] |= MV_VISITED;
+		serverGamedata->mapvisited[gamemap-1] |= MV_VISITED;
+	}
 
 	levelloading = false;
 
diff --git a/src/p_spec.c b/src/p_spec.c
index aa04a723eb..48185e9087 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2937,7 +2937,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			break;
 
 		case 441: // Trigger unlockable
-			if (!(netgame || multiplayer))
 			{
 				INT32 trigid = line->args[0];
 
@@ -2948,10 +2947,12 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					unlocktriggers |= 1 << trigid;
 
 					// Unlocked something?
-					if (M_UpdateUnlockablesAndExtraEmblems())
+					M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
+
+					if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
 					{
 						S_StartSound(NULL, sfx_s3k68);
-						G_SaveGameData(); // only save if unlocked something
+						G_SaveGameData(clientGamedata); // only save if unlocked something
 					}
 				}
 			}
diff --git a/src/p_tick.c b/src/p_tick.c
index 0357258e81..b1fd367ed9 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -675,7 +675,10 @@ void P_Ticker(boolean run)
 
 	// Keep track of how long they've been playing!
 	if (!demoplayback) // Don't increment if a demo is playing.
-		totalplaytime++;
+	{
+		clientGamedata->totalplaytime++;
+		serverGamedata->totalplaytime++;
+	}
 
 	if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))
 		P_DoSpecialStageStuff();
diff --git a/src/r_skins.c b/src/r_skins.c
index e59e085b8c..2c031ee851 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -194,7 +194,7 @@ UINT32 R_GetSkinAvailabilities(void)
 			return 0;
 		}
 
-		if (unlockables[i].unlocked)
+		if (clientGamedata->unlocked[i])
 		{
 			response |= (1 << unlockShift);
 		}
@@ -242,11 +242,12 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
 		// Force 3.
 		return true;
 	}
+
 	if (playernum != -1 && players[playernum].bot)
-    {
-        //Force 4.
-        return true;
-    }
+	{
+		// Force 4.
+		return true;
+	}
 
 	// We will now check if this skin is supposed to be locked or not.
 
@@ -284,7 +285,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
 	else
 	{
 		// We want to check our global unlockables.
-		return (unlockables[unlockID].unlocked);
+		return (clientGamedata->unlocked[unlockID]);
 	}
 }
 
diff --git a/src/s_sound.c b/src/s_sound.c
index 111b6ce256..ada1a0fd2f 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -1692,6 +1692,7 @@ UINT8 soundtestpage = 1;
 //
 boolean S_PrepareSoundTest(void)
 {
+	gamedata_t *data = clientGamedata;
 	musicdef_t *def;
 	INT32 pos = numsoundtestdefs = 0;
 
@@ -1717,9 +1718,9 @@ boolean S_PrepareSoundTest(void)
 		if (!(def->soundtestpage & soundtestpage))
 			continue;
 		soundtestdefs[pos++] = def;
-		if (def->soundtestcond > 0 && !(mapvisited[def->soundtestcond-1] & MV_BEATEN))
+		if (def->soundtestcond > 0 && !(data->mapvisited[def->soundtestcond-1] & MV_BEATEN))
 			continue;
-		if (def->soundtestcond < 0 && !M_Achieved(-1-def->soundtestcond))
+		if (def->soundtestcond < 0 && !M_Achieved(-1-def->soundtestcond, data))
 			continue;
 		def->allowed = true;
 	}
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 67ee8d6684..66eeffa30b 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -2352,7 +2352,7 @@ void I_Quit(void)
 #ifndef NONET
 	D_SaveBan(); // save the ban list
 #endif
-	G_SaveGameData(); // Tails 12-08-2002
+	G_SaveGameData(clientGamedata); // Tails 12-08-2002
 	//added:16-02-98: when recording a demo, should exit using 'q' key,
 	//        but sometimes we forget and use 'F10'.. so save here too.
 
@@ -2436,7 +2436,7 @@ void I_Error(const char *error, ...)
 		if (errorcount == 8)
 		{
 			M_SaveConfig(NULL);
-			G_SaveGameData();
+			G_SaveGameData(clientGamedata);
 		}
 		if (errorcount > 20)
 		{
@@ -2469,7 +2469,7 @@ void I_Error(const char *error, ...)
 #ifndef NONET
 	D_SaveBan(); // save the ban list
 #endif
-	G_SaveGameData(); // Tails 12-08-2002
+	G_SaveGameData(clientGamedata); // Tails 12-08-2002
 
 	// Shutdown. Here might be other errors.
 	if (demorecording)
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 206c932733..1f0ca277f5 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -1697,7 +1697,7 @@ static void ST_drawNightsRecords(void)
 			ST_DrawNightsOverlayNum((BASEVIDWIDTH/2 + 56)<<FRACBITS, 160<<FRACBITS, FRACUNIT, aflag, stplyr->lastmarescore, nightsnum, SKINCOLOR_AZURE);
 
 			// If new record, say so!
-			if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1) <= stplyr->lastmarescore)
+			if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1, clientGamedata) <= stplyr->lastmarescore)
 			{
 				if (stplyr->texttimer & 16)
 					V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_YELLOWMAP|aflag, "* NEW RECORD *");
@@ -2563,8 +2563,11 @@ static void ST_doItemFinderIconsAndSound(void)
 
 		emblems[stemblems++] = i;
 
-		if (!emblemlocations[i].collected)
+		if (!(clientGamedata->collected[i] && serverGamedata->collected[i]))
+		{
+			// It can be worth collecting again if the server doesn't have it.
 			++stunfound;
+		}
 
 		if (stemblems >= 16)
 			break;
@@ -2723,7 +2726,7 @@ static void ST_overlayDrawer(void)
 			ST_drawRaceHUD();
 
 		// Emerald Hunt Indicators
-		if (cv_itemfinder.value && M_SecretUnlocked(SECRET_ITEMFINDER))
+		if (cv_itemfinder.value && M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata))
 			ST_doItemFinderIconsAndSound();
 		else
 			ST_doHuntIconsAndSound();
diff --git a/src/y_inter.c b/src/y_inter.c
index 02d01233e1..6e7d362a77 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -1092,12 +1092,14 @@ void Y_Ticker(void)
 			S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching!
 
 			// Update when done with tally
-			if (!(netgame || multiplayer) && !demoplayback)
+			if (!demoplayback)
 			{
-				if (M_UpdateUnlockablesAndExtraEmblems())
+				M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
+
+				if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
 					S_StartSound(NULL, sfx_s3k68);
 
-				G_SaveGameData();
+				G_SaveGameData(clientGamedata);
 			}
 		}
 		else if (!(intertic & 1))
@@ -1228,12 +1230,14 @@ void Y_Ticker(void)
 			S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching!
 
 			// Update when done with tally
-			if (!(netgame || multiplayer) && !demoplayback)
+			if (!demoplayback)
 			{
-				if (M_UpdateUnlockablesAndExtraEmblems())
+				M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
+
+				if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
 					S_StartSound(NULL, sfx_s3k68);
 
-				G_SaveGameData();
+				G_SaveGameData(clientGamedata);
 			}
 		}
 		else if (!(intertic & 1))
-- 
GitLab


From 303d636f8e31358f63bb9b31ffef290fb74144b5 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 28 Feb 2022 10:58:18 -0500
Subject: [PATCH 097/518] Individual emblems mode

---
 src/deh_soc.c  |  4 +++
 src/doomstat.h |  3 ++
 src/g_game.c   |  1 +
 src/p_inter.c  | 98 +++++++++++++++++++++++++++++++++++++++++++-------
 src/p_local.h  |  2 ++
 src/p_mobj.c   | 48 +++++++------------------
 src/p_setup.c  |  2 +-
 src/st_stuff.c |  3 +-
 8 files changed, 109 insertions(+), 52 deletions(-)

diff --git a/src/deh_soc.c b/src/deh_soc.c
index 81adbc9d21..f2f3e04b8d 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -3839,6 +3839,10 @@ void readmaincfg(MYFILE *f)
 			{
 				useContinues = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y');
 			}
+			else if (fastcmp(word, "SHAREEMBLEMS"))
+			{
+				shareEmblems = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y');
+			}
 
 			else if (fastcmp(word, "GAMEDATA"))
 			{
diff --git a/src/doomstat.h b/src/doomstat.h
index 5875bd01f8..a812cc304f 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -543,9 +543,12 @@ extern UINT8 useBlackRock;
 
 extern UINT8 use1upSound;
 extern UINT8 maxXtraLife; // Max extra lives from rings
+
 extern UINT8 useContinues;
 #define continuesInSession (!multiplayer && (ultimatemode || (useContinues && !marathonmode) || (!modeattacking && !(cursaveslot > 0))))
 
+extern UINT8 shareEmblems;
+
 extern mobj_t *hunt1, *hunt2, *hunt3; // Emerald hunt locations
 
 // For racing
diff --git a/src/g_game.c b/src/g_game.c
index 854bf9bbb4..066b43cad3 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -215,6 +215,7 @@ UINT8 ammoremovaltics = 2*TICRATE;
 UINT8 use1upSound = 0;
 UINT8 maxXtraLife = 2; // Max extra lives from rings
 UINT8 useContinues = 0; // Set to 1 to enable continues outside of no-save scenarioes
+UINT8 shareEmblems = 0; // Set to 1 to share all picked up emblems in multiplayer
 
 UINT8 introtoplay;
 UINT8 creditscutscene;
diff --git a/src/p_inter.c b/src/p_inter.c
index f2d20912f0..02ae222e31 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -164,6 +164,62 @@ boolean P_CanPickupItem(player_t *player, boolean weapon)
 	return true;
 }
 
+boolean P_CanPickupEmblem(player_t *player, INT32 emblemID)
+{
+	emblem_t *emblem = NULL;
+
+	if (emblemID < 0 || emblemID >= numemblems)
+	{
+		// Invalid emblem ID, can't pickup.
+		return false;
+	}
+
+	emblem = &emblemlocations[emblemID];
+
+	if (demoplayback)
+	{
+		// Never collect emblems in replays.
+		return false;
+	}
+
+	if (player->bot && player->bot != BOT_MPAI)
+	{
+		// Your little lap-dog can't grab these for you.
+		return false;
+	}
+
+	if (emblem->type == ET_SKIN)
+	{
+		INT32 skinnum = M_EmblemSkinNum(emblem);
+
+		if (player->skin != skinnum)
+		{
+			// Incorrect skin to pick up this emblem.
+			return false;
+		}
+	}
+
+	return true;
+}
+
+boolean P_EmblemWasCollected(INT32 emblemID)
+{
+	if (emblemID < 0 || emblemID >= numemblems)
+	{
+		// Invalid emblem ID, can't pickup.
+		return true;
+	}
+
+	if (shareEmblems && !serverGamedata->collected[emblemID])
+	{
+		// It can be worth collecting again if we're sharing emblems
+		// and the server doesn't have it.
+		return false;
+	}
+
+	return clientGamedata->collected[emblemID];
+}
+
 //
 // P_DoNightsScore
 //
@@ -738,25 +794,41 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 		// Secret emblem thingy
 		case MT_EMBLEM:
 			{
-				if (demoplayback || (player->bot && player->bot != BOT_MPAI) || special->health <= 0 || special->health > MAXEMBLEMS)
-					return;
+				mobj_t *spark = NULL;
+				boolean prevCollected;
 
-				if (emblemlocations[special->health-1].type == ET_SKIN)
+				if (!P_CanPickupEmblem(player, special->health - 1))
 				{
-					INT32 skinnum = M_EmblemSkinNum(&emblemlocations[special->health-1]);
+					return;
+				}
 
-					if (player->skin != skinnum)
-					{
-						return;
-					}
+				prevCollected = P_EmblemWasCollected(special->health - 1);
+
+				if (((player - players) == serverplayer) || shareEmblems)
+				{
+					serverGamedata->collected[special->health-1] = true;
+					M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
 				}
 
-				clientGamedata->collected[special->health-1] = serverGamedata->collected[special->health-1] = true;
+				if (P_IsLocalPlayer(player) || shareEmblems)
+				{
+					clientGamedata->collected[special->health-1] = true;
+					M_UpdateUnlockablesAndExtraEmblems(clientGamedata);
+					G_SaveGameData(clientGamedata);
+				}
 
-				M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
-				M_UpdateUnlockablesAndExtraEmblems(clientGamedata);
-				G_SaveGameData(clientGamedata);
-				break;
+				// This always spawns the object to prevent mobjnum issues,
+				// but makes the effect invisible to whoever it doesn't matter to.
+				spark = P_SpawnMobjFromMobj(special, 0, 0, 0, MT_SPARK);
+				if (prevCollected == false && P_EmblemWasCollected(special->health - 1) == true)
+				{
+					S_StartSound(special, special->info->deathsound);
+				}
+				else
+				{
+					spark->flags2 |= MF2_DONTDRAW;
+				}
+				return;
 			}
 
 		// CTF Flags
diff --git a/src/p_local.h b/src/p_local.h
index cc060e4eee..3c84d6fe2f 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -510,6 +510,8 @@ void P_ClearStarPost(INT32 postnum);
 void P_ResetStarposts(void);
 
 boolean P_CanPickupItem(player_t *player, boolean weapon);
+boolean P_CanPickupEmblem(player_t *player, INT32 emblemID);
+boolean P_EmblemWasCollected(INT32 emblemID);
 void P_DoNightsScore(player_t *player);
 void P_DoMatchSuper(player_t *player);
 
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 6563e6f0a7..f198a1a69a 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -9716,6 +9716,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
 			A_AttractChase(mobj);
 		break;
 	case MT_EMBLEM:
+		if (P_EmblemWasCollected(mobj->health - 1) || !P_CanPickupEmblem(&players[consoleplayer], mobj->health - 1))
+			mobj->frame |= (tr_trans50 << FF_TRANSSHIFT);
+		else
+			mobj->frame &= ~FF_TRANSMASK;
+
 		if (mobj->flags2 & MF2_NIGHTSPULL)
 			P_NightsItemChase(mobj);
 		break;
@@ -12146,7 +12151,6 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
 	INT32 j;
 	emblem_t* emblem = M_GetLevelEmblems(gamemap);
 	skincolornum_t emcolor;
-	boolean validEmblem = true;
 
 	while (emblem)
 	{
@@ -12171,47 +12175,19 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
 	emcolor = M_GetEmblemColor(&emblemlocations[j]); // workaround for compiler complaint about bad function casting
 	mobj->color = (UINT16)emcolor;
 
-	validEmblem = true;
+	mobj->frame &= ~FF_TRANSMASK;
 
-	if (!netgame)
+	if (emblemlocations[j].type == ET_GLOBAL)
 	{
-		validEmblem = !serverGamedata->collected[j];
-
-		if (emblemlocations[j].type == ET_SKIN && !multiplayer)
+		mobj->reactiontime = emblemlocations[j].var;
+		if (emblemlocations[j].var & GE_NIGHTSITEM)
 		{
-			INT32 skinnum = M_EmblemSkinNum(&emblemlocations[j]);
-
-			if (players[0].skin != skinnum)
-			{
-				validEmblem = false;
-			}
+			mobj->flags |= MF_NIGHTSITEM;
+			mobj->flags &= ~MF_SPECIAL;
+			mobj->flags2 |= MF2_DONTDRAW;
 		}
 	}
 
-	if (validEmblem == false)
-	{
-		P_UnsetThingPosition(mobj);
-		mobj->flags |= MF_NOCLIP;
-		mobj->flags &= ~MF_SPECIAL;
-		mobj->flags |= MF_NOBLOCKMAP;
-		mobj->frame |= (tr_trans50 << FF_TRANSSHIFT);
-		P_SetThingPosition(mobj);
-	}
-	else
-	{
-		mobj->frame &= ~FF_TRANSMASK;
-
-		if (emblemlocations[j].type == ET_GLOBAL)
-		{
-			mobj->reactiontime = emblemlocations[j].var;
-			if (emblemlocations[j].var & GE_NIGHTSITEM)
-			{
-				mobj->flags |= MF_NIGHTSITEM;
-				mobj->flags &= ~MF_SPECIAL;
-				mobj->flags2 |= MF2_DONTDRAW;
-			}
-		}
-	}
 	return true;
 }
 
diff --git a/src/p_setup.c b/src/p_setup.c
index 70a2c0a8bd..74645e8771 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -876,7 +876,7 @@ static void P_SpawnMapThings(boolean spawnemblems)
 	size_t i;
 	mapthing_t *mt;
 
-        // Spawn axis points first so they are at the front of the list for fast searching.
+	// Spawn axis points first so they are at the front of the list for fast searching.
 	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
 	{
 		switch (mt->type)
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 1f0ca277f5..986b712194 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2563,9 +2563,8 @@ static void ST_doItemFinderIconsAndSound(void)
 
 		emblems[stemblems++] = i;
 
-		if (!(clientGamedata->collected[i] && serverGamedata->collected[i]))
+		if (!P_EmblemWasCollected(i))
 		{
-			// It can be worth collecting again if the server doesn't have it.
 			++stunfound;
 		}
 
-- 
GitLab


From 29c61fac88f88aa63474444f8fad73a3f9a49e13 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 28 Feb 2022 10:58:34 -0500
Subject: [PATCH 098/518] Allow completion emblems in multiplayer

---
 src/g_game.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/g_game.c b/src/g_game.c
index 066b43cad3..c26968ac0d 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -3843,7 +3843,7 @@ static void G_UpdateVisited(gamedata_t *data, boolean silent)
 {
 	boolean spec = G_IsSpecialStage(gamemap);
 	// Update visitation flags?
-	if (!multiplayer && !demoplayback && (gametype == GT_COOP) // SP/RA/NiGHTS mode
+	if (!demoplayback && (gametype == GT_COOP) // SP/RA/NiGHTS mode
 		&& !stagefailed) // Did not fail the stage
 	{
 		UINT8 earnedEmblems;
-- 
GitLab


From d7c5e16f6c0c82a9d8ca214b010094f65b5e61f3 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 28 Feb 2022 11:50:12 -0500
Subject: [PATCH 099/518] Play sound globally if emblems are shared

---
 src/p_inter.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_inter.c b/src/p_inter.c
index 02ae222e31..b5266e09f6 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -822,7 +822,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				spark = P_SpawnMobjFromMobj(special, 0, 0, 0, MT_SPARK);
 				if (prevCollected == false && P_EmblemWasCollected(special->health - 1) == true)
 				{
-					S_StartSound(special, special->info->deathsound);
+					S_StartSound((shareEmblems ? NULL : special), special->info->deathsound);
 				}
 				else
 				{
-- 
GitLab


From 30f6ae6e5606bbdb6564558fef18918a127ac0f3 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 28 Feb 2022 13:48:59 -0500
Subject: [PATCH 100/518] Add read access to shareEmblems (as well as a few
 other MAINCFG variables that weren't)

---
 src/lua_script.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/lua_script.c b/src/lua_script.c
index 9c7636ebe6..75e9c29a0c 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -306,6 +306,18 @@ int LUA_PushGlobals(lua_State *L, const char *word)
 		lua_pushinteger(L, ammoremovaltics);
 		return 1;
 	// end timers
+	} else if (fastcmp(word,"use1upSound")) {
+		lua_pushinteger(L, use1upSound);
+		return 1;
+	} else if (fastcmp(word,"maxXtraLife")) {
+		lua_pushinteger(L, maxXtraLife);
+		return 1;
+	} else if (fastcmp(word,"useContinues")) {
+		lua_pushinteger(L, useContinues);
+		return 1;
+	} else if (fastcmp(word,"shareEmblems")) {
+		lua_pushinteger(L, shareEmblems);
+		return 1;
 	} else if (fastcmp(word,"gametype")) {
 		lua_pushinteger(L, gametype);
 		return 1;
-- 
GitLab


From ffb76334ff37a0c5b2064450d0b895773515a4fe Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 28 Feb 2022 13:58:23 -0500
Subject: [PATCH 101/518] Don't check time attack emblems in multiplayer

(Maybe some day...)
---
 src/g_game.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/g_game.c b/src/g_game.c
index c26968ac0d..b3dffd0e62 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -3869,7 +3869,8 @@ static void G_UpdateVisited(gamedata_t *data, boolean silent)
 
 		if (silent)
 		{
-			M_CheckLevelEmblems(data);
+			if (modeattacking)
+				M_CheckLevelEmblems(data);
 		}
 		else
 		{
-- 
GitLab


From ffe591afeee2b48875235e86bc3480ce99850cb3 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 28 Feb 2022 17:36:02 -0500
Subject: [PATCH 102/518] Tie emblem spawning to Coop gametypes

---
 src/g_game.c | 6 +++++-
 src/p_mobj.c | 4 ++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/g_game.c b/src/g_game.c
index b3dffd0e62..106682aeee 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -3843,16 +3843,19 @@ static void G_UpdateVisited(gamedata_t *data, boolean silent)
 {
 	boolean spec = G_IsSpecialStage(gamemap);
 	// Update visitation flags?
-	if (!demoplayback && (gametype == GT_COOP) // SP/RA/NiGHTS mode
+	if (!demoplayback
+		&& G_CoopGametype() // Campaign mode
 		&& !stagefailed) // Did not fail the stage
 	{
 		UINT8 earnedEmblems;
 
 		// Update visitation flags
 		data->mapvisited[gamemap-1] |= MV_BEATEN;
+
 		// eh, what the hell
 		if (ultimatemode)
 			data->mapvisited[gamemap-1] |= MV_ULTIMATE;
+
 		// may seem incorrect but IS possible in what the main game uses as mp special stages, and nummaprings will be -1 in NiGHTS
 		if (nummaprings > 0 && players[consoleplayer].rings >= nummaprings)
 		{
@@ -3860,6 +3863,7 @@ static void G_UpdateVisited(gamedata_t *data, boolean silent)
 			if (modeattacking)
 				data->mapvisited[gamemap-1] |= MV_PERFECTRA;
 		}
+
 		if (!spec)
 		{
 			// not available to special stages because they can only really be done in one order in an unmodified game, so impossible for first six and trivial for seventh
diff --git a/src/p_mobj.c b/src/p_mobj.c
index f198a1a69a..e79977c487 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -12009,6 +12009,10 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
 			return false; // You already got this token
 
 		break;
+	case MT_EMBLEM:
+		if (!G_CoopGametype())
+			return false; // Gametype's not right
+		break;
 	default:
 		break;
 	}
-- 
GitLab


From 122ddade61d60259f8b7d66fd27cb2c60ed44452 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 28 Feb 2022 18:52:38 -0500
Subject: [PATCH 103/518] Draw level stats on pause & emblem hints menus in
 multiplayer

---
 src/m_menu.c | 30 +++++++++++++++++-------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 2214e6a6a5..2d8db8b24d 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -555,19 +555,22 @@ static menuitem_t MPauseMenu[] =
 	{IT_STRING | IT_CALL,    NULL, "Add-ons...",                M_Addons,               8},
 	{IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...",         &MISC_ScrambleTeamDef, 16},
 	{IT_STRING | IT_CALL,    NULL, "Switch Gametype/Level...",  M_MapChange,           24},
+	{IT_STRING | IT_CALL,    NULL, "Emblem Hints...",           M_EmblemHints,         32},
 
-	{IT_STRING | IT_CALL,    NULL, "Continue",                  M_SelectableClearMenus,40},
-	{IT_STRING | IT_CALL,    NULL, "Player 1 Setup",            M_SetupMultiPlayer,    48}, // splitscreen
-	{IT_STRING | IT_CALL,    NULL, "Player 2 Setup",            M_SetupMultiPlayer2,   56}, // splitscreen
+	{IT_STRING | IT_CALL,    NULL, "Continue",                  M_SelectableClearMenus,48},
 
-	{IT_STRING | IT_CALL,    NULL, "Spectate",                  M_ConfirmSpectate,     48},
-	{IT_STRING | IT_CALL,    NULL, "Enter Game",                M_ConfirmEnterGame,    48},
-	{IT_STRING | IT_SUBMENU, NULL, "Switch Team...",            &MISC_ChangeTeamDef,   48},
-	{IT_STRING | IT_CALL,    NULL, "Player Setup",              M_SetupMultiPlayer,    56}, // alone
-	{IT_STRING | IT_CALL,    NULL, "Options",                   M_Options,             64},
+	{IT_STRING | IT_CALL,    NULL, "Player 1 Setup",            M_SetupMultiPlayer,    56}, // splitscreen
+	{IT_STRING | IT_CALL,    NULL, "Player 2 Setup",            M_SetupMultiPlayer2,   64},
 
-	{IT_STRING | IT_CALL,    NULL, "Return to Title",           M_EndGame,             80},
-	{IT_STRING | IT_CALL,    NULL, "Quit Game",                 M_QuitSRB2,            88},
+	{IT_STRING | IT_CALL,    NULL, "Spectate",                  M_ConfirmSpectate,     56}, // alone
+	{IT_STRING | IT_CALL,    NULL, "Enter Game",                M_ConfirmEnterGame,    56},
+	{IT_STRING | IT_SUBMENU, NULL, "Switch Team...",            &MISC_ChangeTeamDef,   56},
+	{IT_STRING | IT_CALL,    NULL, "Player Setup",              M_SetupMultiPlayer,    64},
+
+	{IT_STRING | IT_CALL,    NULL, "Options",                   M_Options,             72},
+
+	{IT_STRING | IT_CALL,    NULL, "Return to Title",           M_EndGame,             88},
+	{IT_STRING | IT_CALL,    NULL, "Quit Game",                 M_QuitSRB2,            96},
 };
 
 typedef enum
@@ -575,6 +578,7 @@ typedef enum
 	mpause_addons = 0,
 	mpause_scramble,
 	mpause_switchmap,
+	mpause_hints,
 
 	mpause_continue,
 	mpause_psetupsplit,
@@ -3747,12 +3751,10 @@ void M_StartControlPanel(void)
 		if (splitscreen)
 		{
 			MPauseMenu[mpause_psetupsplit].status = MPauseMenu[mpause_psetupsplit2].status = IT_STRING | IT_CALL;
-			MPauseMenu[mpause_psetup].text = "Player 1 Setup";
 		}
 		else
 		{
 			MPauseMenu[mpause_psetup].status = IT_STRING | IT_CALL;
-			MPauseMenu[mpause_psetup].text = "Player Setup";
 
 			if (G_GametypeHasTeams())
 				MPauseMenu[mpause_switchteam].status = IT_STRING | IT_SUBMENU;
@@ -3762,6 +3764,8 @@ void M_StartControlPanel(void)
 				MPauseMenu[mpause_spectate].status = IT_GRAYEDOUT;
 		}
 
+		MPauseMenu[mpause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata) && G_CoopGametype()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
+
 		currentMenu = &MPauseDef;
 		itemOn = mpause_continue;
 	}
@@ -4696,7 +4700,7 @@ static void M_DrawPauseMenu(void)
 {
 	gamedata_t *data = clientGamedata;
 
-	if (!netgame && !multiplayer && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
+	if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)
 	{
 		emblem_t *emblem_detail[3] = {NULL, NULL, NULL};
 		char emblem_text[3][20];
-- 
GitLab


From fb5b8ce1be2a88c7afae55816c62e1928a5b88f5 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 28 Feb 2022 19:14:02 -0500
Subject: [PATCH 104/518] Show tab emblems in Coop

---
 src/hu_stuff.c | 21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)

diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index eceb6bbafa..091e2b2fba 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -2862,18 +2862,6 @@ static void HU_DrawRankings(void)
 			V_DrawCenteredString(256, 16, 0, va("%d", cv_pointlimit.value));
 		}
 	}
-	else if (gametyperankings[gametype] == GT_COOP)
-	{
-		INT32 totalscore = 0;
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i])
-				totalscore += players[i].score;
-		}
-
-		V_DrawCenteredString(256, 8, 0, "TOTAL SCORE");
-		V_DrawCenteredString(256, 16, 0, va("%u", totalscore));
-	}
 	else
 	{
 		if (circuitmap)
@@ -3029,6 +3017,15 @@ static void HU_DrawNetplayCoopOverlay(void)
 		V_DrawSmallScaledPatch(148, 6, 0, tokenicon);
 	}
 
+	if (G_CoopGametype() && LUA_HudEnabled(hud_tabemblems))
+	{
+		V_DrawCenteredString(256, 14, 0, "/");
+		V_DrawString(256 + 4, 14, 0, va("%d", numemblems + numextraemblems));
+		V_DrawRightAlignedString(256 - 4, 14, 0, va("%d", M_CountEmblems(clientGamedata)));
+
+		V_DrawSmallScaledPatch(256 - (emblemicon->width / 4), 6, 0, emblemicon);
+	}
+
 	if (!LUA_HudEnabled(hud_coopemeralds))
 		return;
 
-- 
GitLab


From 87e468f365d58f012b62a3a0a7851d4bf26a9872 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Thu, 3 Mar 2022 10:26:04 -0500
Subject: [PATCH 105/518] Allow emerald hunt radar to function if emblem radar
 is on but all emblems have been collected.

---
 src/st_stuff.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/st_stuff.c b/src/st_stuff.c
index 986b712194..7eab0442f4 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2545,7 +2545,7 @@ static void ST_doHuntIconsAndSound(void)
 		S_StartSound(NULL, sfx_emfind);
 }
 
-static void ST_doItemFinderIconsAndSound(void)
+static boolean ST_doItemFinderIconsAndSound(void)
 {
 	INT32 emblems[16];
 	thinker_t *th;
@@ -2556,6 +2556,12 @@ static void ST_doItemFinderIconsAndSound(void)
 	INT32 interval = 0, newinterval = 0;
 	INT32 soffset;
 
+	if (!(cv_itemfinder.value && M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata)))
+	{
+		// Not unlocked, or not enabled. Use emerald hunt radar.
+		return false;
+	}
+
 	for (i = 0; i < numemblems; ++i)
 	{
 		if (emblemlocations[i].type > ET_SKIN || emblemlocations[i].level != gamemap)
@@ -2573,7 +2579,10 @@ static void ST_doItemFinderIconsAndSound(void)
 	}
 	// Found all/none exist? Don't waste our time
 	if (!stunfound)
-		return;
+	{
+		// Allow emerald hunt radar to function after they're all collected.
+		return false;
+	}
 
 	// Scan thinkers to find emblem mobj with these ids
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
@@ -2607,6 +2616,8 @@ static void ST_doItemFinderIconsAndSound(void)
 
 	if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0 && renderisnewtic)
 		S_StartSound(NULL, sfx_emfind);
+
+	return true;
 }
 
 //
@@ -2725,9 +2736,7 @@ static void ST_overlayDrawer(void)
 			ST_drawRaceHUD();
 
 		// Emerald Hunt Indicators
-		if (cv_itemfinder.value && M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata))
-			ST_doItemFinderIconsAndSound();
-		else
+		if (!ST_doItemFinderIconsAndSound());
 			ST_doHuntIconsAndSound();
 
 		if(!P_IsLocalPlayer(stplyr))
-- 
GitLab


From cb54b1e5ce1daee0cd5e20bb507eeb375cb6ff5e Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Thu, 3 Mar 2022 10:26:36 -0500
Subject: [PATCH 106/518] Fix check that does LevelEmblems but not
 CompletionEmblems on startup

---
 src/m_cond.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/m_cond.c b/src/m_cond.c
index 55f35830ad..a54988f67a 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -329,6 +329,7 @@ void M_SilentUpdateUnlockablesAndEmblems(gamedata_t *data)
 	// Just in case they aren't to sync
 	M_CheckUnlockConditions(data);
 	M_CheckLevelEmblems(data);
+	M_CompletionEmblems(data);
 
 	// Go through extra emblems
 	for (i = 0; i < numextraemblems; ++i)
-- 
GitLab


From bc00b1335848e9827f492161baa14e2401dc45af Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Fri, 4 Mar 2022 11:29:02 -0500
Subject: [PATCH 107/518] Fix Emblem Radar detecting already collected emblems

---
 src/st_stuff.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/st_stuff.c b/src/st_stuff.c
index 7eab0442f4..42f1f89ec5 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2577,6 +2577,7 @@ static boolean ST_doItemFinderIconsAndSound(void)
 		if (stemblems >= 16)
 			break;
 	}
+
 	// Found all/none exist? Don't waste our time
 	if (!stunfound)
 	{
@@ -2602,6 +2603,9 @@ static boolean ST_doItemFinderIconsAndSound(void)
 		{
 			if (mo2->health == emblems[i] + 1)
 			{
+				if (P_EmblemWasCollected(emblems[i]))
+					break;
+
 				soffset = (i * 20) - ((stemblems - 1) * 10);
 
 				newinterval = ST_drawEmeraldHuntIcon(mo2, itemhoming, soffset);
@@ -2736,7 +2740,7 @@ static void ST_overlayDrawer(void)
 			ST_drawRaceHUD();
 
 		// Emerald Hunt Indicators
-		if (!ST_doItemFinderIconsAndSound());
+		if (!ST_doItemFinderIconsAndSound())
 			ST_doHuntIconsAndSound();
 
 		if(!P_IsLocalPlayer(stplyr))
-- 
GitLab


From 903a47966d62df8571250646bc11858fc1c222bd Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Fri, 4 Mar 2022 11:31:41 -0500
Subject: [PATCH 108/518] Swap hints & level select on multiplayer pause menu

This makes it consistent with SP's pause menu order. (Although admittedly I prefer how the other order looks.)
---
 src/m_menu.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 2d8db8b24d..0ad9183c7f 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -554,8 +554,8 @@ static menuitem_t MPauseMenu[] =
 {
 	{IT_STRING | IT_CALL,    NULL, "Add-ons...",                M_Addons,               8},
 	{IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...",         &MISC_ScrambleTeamDef, 16},
-	{IT_STRING | IT_CALL,    NULL, "Switch Gametype/Level...",  M_MapChange,           24},
-	{IT_STRING | IT_CALL,    NULL, "Emblem Hints...",           M_EmblemHints,         32},
+	{IT_STRING | IT_CALL,    NULL, "Emblem Hints...",           M_EmblemHints,         24},
+	{IT_STRING | IT_CALL,    NULL, "Switch Gametype/Level...",  M_MapChange,           32},
 
 	{IT_STRING | IT_CALL,    NULL, "Continue",                  M_SelectableClearMenus,48},
 
@@ -577,8 +577,8 @@ typedef enum
 {
 	mpause_addons = 0,
 	mpause_scramble,
-	mpause_switchmap,
 	mpause_hints,
+	mpause_switchmap,
 
 	mpause_continue,
 	mpause_psetupsplit,
-- 
GitLab


From 9b6a47783dbfbc32998e1a30ca5e9717b1fc2552 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Fri, 4 Mar 2022 12:32:23 -0500
Subject: [PATCH 109/518] Show the level select option used to start the level
 in the pause menu.

Previously, level select only appeared in the SP pause menu if you load a complete save file.

Now, entering the game through an Extras menu level select shows that level select. Simply makes it more convenient, as you don't need to exit to the main menu again whenever you want to get to another level from an unlocked level select.

Tested all ways you can start a new map from the menu that I can think of (New Save File, Complete Save File, Mid-game Save File, several different Level Select types, custom Warp, Record Attack, Marathon, Tutorial), and could not smuggle wrong level selects into any.
---
 src/m_menu.c | 34 +++++++++++++++++++++++++++++++---
 1 file changed, 31 insertions(+), 3 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 0ad9183c7f..a40eddae9f 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -241,6 +241,7 @@ static void M_EmblemHints(INT32 choice);
 static void M_HandleEmblemHints(INT32 choice);
 UINT32 hintpage = 1;
 static void M_HandleChecklist(INT32 choice);
+static void M_PauseLevelSelect(INT32 choice);
 menu_t SR_MainDef, SR_UnlockChecklistDef;
 
 static UINT8 check_on;
@@ -601,7 +602,7 @@ static menuitem_t SPauseMenu[] =
 	// Pandora's Box will be shifted up if both options are available
 	{IT_CALL | IT_STRING,    NULL, "Pandora's Box...",     M_PandorasBox,         16},
 	{IT_CALL | IT_STRING,    NULL, "Emblem Hints...",      M_EmblemHints,         24},
-	{IT_CALL | IT_STRING,    NULL, "Level Select...",      M_LoadGameLevelSelect, 32},
+	{IT_CALL | IT_STRING,    NULL, "Level Select...",      M_PauseLevelSelect,    32},
 
 	{IT_CALL | IT_STRING,    NULL, "Continue",             M_SelectableClearMenus,48},
 	{IT_CALL | IT_STRING,    NULL, "Retry",                M_Retry,               56},
@@ -3713,7 +3714,7 @@ void M_StartControlPanel(void)
 		}
 
 		// We can always use level select though. :33
-		SPauseMenu[spause_levelselect].status = (gamecomplete == 1) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
+		SPauseMenu[spause_levelselect].status = (maplistoption != 0) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
 
 		// And emblem hints.
 		SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
@@ -7138,7 +7139,6 @@ static void M_LevelSelectWarp(INT32 choice)
 	}
 
 	startmap = (INT16)(cv_nextmap.value);
-
 	fromlevelselect = true;
 
 	if (fromloadgame)
@@ -7662,6 +7662,26 @@ static void M_HandleEmblemHints(INT32 choice)
 
 }
 
+static void M_PauseLevelSelect(INT32 choice)
+{
+	(void)choice;
+
+	SP_LevelSelectDef.prevMenu = currentMenu;
+	levellistmode = LLM_LEVELSELECT;
+
+	// maplistoption is NOT specified, so that this
+	// transfers the level select list from the menu
+	// used to enter the game to the pause menu.
+
+	if (!M_PrepareLevelPlatter(-1, true))
+	{
+		M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING);
+		return;
+	}
+
+	M_SetupNextMenu(&SP_LevelSelectDef);
+}
+
 /*static void M_DrawSkyRoom(void)
 {
 	INT32 i, y = 0;
@@ -8169,6 +8189,7 @@ INT32 ultimate_selectable = false;
 static void M_NewGame(void)
 {
 	fromlevelselect = false;
+	maplistoption = 0;
 
 	startmap = spstage_start;
 	CV_SetValue(&cv_newgametype, GT_COOP); // Graue 09-08-2004
@@ -8180,6 +8201,7 @@ static void M_CustomWarp(INT32 choice)
 {
 	INT32 ul = skyRoomMenuTranslations[choice-1];
 
+	maplistoption = 0;
 	startmap = (INT16)(unlockables[ul].variable);
 
 	M_SetupChoosePlayer(0);
@@ -8372,6 +8394,7 @@ static void M_StartTutorial(INT32 choice)
 	M_ClearMenus(true);
 	gamecomplete = 0;
 	cursaveslot = 0;
+	maplistoption = 0;
 	G_DeferedInitNew(false, G_BuildMapName(tutorialmap), 0, false, false);
 }
 
@@ -8734,6 +8757,10 @@ static void M_LoadSelect(INT32 choice)
 {
 	(void)choice;
 
+	// Reset here, if we want a level select
+	// M_LoadGameLevelSelect will set it for us.
+	maplistoption = 0;
+
 	if (saveSlotSelected == NOSAVESLOT) //last slot is play without saving
 	{
 		M_NewGame();
@@ -10674,6 +10701,7 @@ static void M_Marathon(INT32 choice)
 	}
 
 	fromlevelselect = false;
+	maplistoption = 0;
 
 	startmap = spmarathon_start;
 	CV_SetValue(&cv_newgametype, GT_COOP); // Graue 09-08-2004
-- 
GitLab


From 897b81b8400522ec0d6a761d19bc2eb49c3e74f4 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Fri, 4 Mar 2022 15:28:11 -0500
Subject: [PATCH 110/518] Don't load game from pause level select without save
 slot

---
 src/m_menu.c | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index a40eddae9f..ef149cec5b 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -1829,6 +1829,10 @@ menu_t SP_LevelSelectDef = MAPPLATTERMENUSTYLE(
 	MTREE4(MN_SP_MAIN, MN_SP_LOAD, MN_SP_PLAYER, MN_SP_LEVELSELECT),
 	NULL, SP_LevelSelectMenu);
 
+menu_t SP_PauseLevelSelectDef = MAPPLATTERMENUSTYLE(
+	MTREE4(MN_SP_MAIN, MN_SP_LOAD, MN_SP_PLAYER, MN_SP_LEVELSELECT),
+	NULL, SP_LevelSelectMenu);
+
 menu_t SP_LevelStatsDef =
 {
 	MTREE2(MN_SP_MAIN, MN_SP_LEVELSTATS),
@@ -7129,6 +7133,7 @@ static void M_DestroyRobots(INT32 choice)
 static void M_LevelSelectWarp(INT32 choice)
 {
 	boolean fromloadgame = (currentMenu == &SP_LevelSelectDef);
+	boolean frompause = (currentMenu == &SP_PauseLevelSelectDef);
 
 	(void)choice;
 
@@ -7146,7 +7151,20 @@ static void M_LevelSelectWarp(INT32 choice)
 	else
 	{
 		cursaveslot = 0;
-		M_SetupChoosePlayer(0);
+
+		if (frompause)
+		{
+			M_ClearMenus(true);
+
+			D_MapChange(startmap, gametype, false, false, 1, false, fromlevelselect);
+			COM_BufAddText("dummyconsvar 1\n");
+
+			if (levelselect.rows)
+				Z_Free(levelselect.rows);
+			levelselect.rows = NULL;
+		}
+		else
+			M_SetupChoosePlayer(0);
 	}
 }
 
@@ -7666,7 +7684,7 @@ static void M_PauseLevelSelect(INT32 choice)
 {
 	(void)choice;
 
-	SP_LevelSelectDef.prevMenu = currentMenu;
+	SP_PauseLevelSelectDef.prevMenu = currentMenu;
 	levellistmode = LLM_LEVELSELECT;
 
 	// maplistoption is NOT specified, so that this
@@ -7679,7 +7697,7 @@ static void M_PauseLevelSelect(INT32 choice)
 		return;
 	}
 
-	M_SetupNextMenu(&SP_LevelSelectDef);
+	M_SetupNextMenu(&SP_PauseLevelSelectDef);
 }
 
 /*static void M_DrawSkyRoom(void)
-- 
GitLab


From d8f6ad217c02ab04d1dceb3e084ce2857a8ad886 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sat, 5 Mar 2022 01:41:24 -0500
Subject: [PATCH 111/518] Don't give completion emblems when getting a game
 over in multiplayer (or any other kind of level reset)

---
 src/g_game.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/g_game.c b/src/g_game.c
index 106682aeee..aafbdea76b 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -3153,6 +3153,9 @@ void G_DoReborn(INT32 playernum)
 
 	if (resetlevel)
 	{
+		// Don't give completion emblems for reloading the level...
+		stagefailed = true;
+
 		// reload the level from scratch
 		if (countdowntimeup)
 		{
-- 
GitLab


From 3b15d9b4fe0c3f9b3a0fc1b6d3d947f39ac65e5a Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 6 Mar 2022 22:52:20 -0500
Subject: [PATCH 112/518] Make the level select behave more identically to
 G_LoadGame

Noticed some oddities with D_MapChange here with very rarely not changing player position when the map is loaded.
---
 src/m_menu.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index ef149cec5b..c025ae2867 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -7156,8 +7156,8 @@ static void M_LevelSelectWarp(INT32 choice)
 		{
 			M_ClearMenus(true);
 
-			D_MapChange(startmap, gametype, false, false, 1, false, fromlevelselect);
-			COM_BufAddText("dummyconsvar 1\n");
+			G_DeferedInitNew(false, G_BuildMapName(startmap), cv_skin.value, false, fromlevelselect); // Not sure about using cv_skin here, but it seems fine in testing.
+			COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this
 
 			if (levelselect.rows)
 				Z_Free(levelselect.rows);
-- 
GitLab


From 9cce2195d4e41f537a911a1de9aa17a0caba0b91 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 7 Mar 2022 16:31:10 -0500
Subject: [PATCH 113/518] Make ShareEmblems more Top Down style

---
 src/p_inter.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_inter.c b/src/p_inter.c
index b5266e09f6..750e9cc34c 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -810,7 +810,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 					M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
 				}
 
-				if (P_IsLocalPlayer(player) || shareEmblems)
+				if (P_IsLocalPlayer(player) /*|| shareEmblems*/)
 				{
 					clientGamedata->collected[special->health-1] = true;
 					M_UpdateUnlockablesAndExtraEmblems(clientGamedata);
-- 
GitLab


From d751ad5cf262b0557a86a2e5d028cabce0c06909 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Wed, 9 Mar 2022 03:28:38 -0500
Subject: [PATCH 114/518] Remove "Multiplayer games can't unlock extras!"

---
 src/f_finale.c | 40 +++++++++++++++-------------------------
 1 file changed, 15 insertions(+), 25 deletions(-)

diff --git a/src/f_finale.c b/src/f_finale.c
index 4bb640d501..929a08eaff 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -1642,37 +1642,27 @@ void F_GameEvaluationTicker(void)
 
 	if (finalecount == 5*TICRATE)
 	{
-		if (netgame || multiplayer) // modify this when we finally allow unlocking stuff in 2P
+		serverGamedata->timesBeaten++;
+		clientGamedata->timesBeaten++;
+
+		if (ALL7EMERALDS(emeralds))
 		{
-			HU_SetCEchoFlags(V_YELLOWMAP|V_RETURN8);
-			HU_SetCEchoDuration(6);
-			HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Multiplayer games can't unlock extras!");
-			S_StartSound(NULL, sfx_s3k68);
+			serverGamedata->timesBeatenWithEmeralds++;
+			clientGamedata->timesBeatenWithEmeralds++;
 		}
-		else
-		{
-			serverGamedata->timesBeaten++;
-			clientGamedata->timesBeaten++;
-
-			if (ALL7EMERALDS(emeralds))
-			{
-				serverGamedata->timesBeatenWithEmeralds++;
-				clientGamedata->timesBeatenWithEmeralds++;
-			}
 
-			if (ultimatemode)
-			{
-				serverGamedata->timesBeatenUltimate++;
-				clientGamedata->timesBeatenUltimate++;
-			}
+		if (ultimatemode)
+		{
+			serverGamedata->timesBeatenUltimate++;
+			clientGamedata->timesBeatenUltimate++;
+		}
 
-			M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
+		M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
 
-			if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
-				S_StartSound(NULL, sfx_s3k68);
+		if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
+			S_StartSound(NULL, sfx_s3k68);
 
-			G_SaveGameData(clientGamedata);
-		}
+		G_SaveGameData(clientGamedata);
 	}
 }
 
-- 
GitLab


From 29f55471ddc163bddaa8bf5fba75ef4aec2fef63 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Wed, 12 Oct 2022 01:18:02 -0400
Subject: [PATCH 115/518] Fix instances reverted to old unlocked variable

---
 src/m_cheat.c |  6 +++---
 src/p_spec.c  | 10 +++-------
 2 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/src/m_cheat.c b/src/m_cheat.c
index 934779982e..e370335f83 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -79,9 +79,9 @@ static UINT8 cheatf_warp(void)
 
 	// Temporarily unlock stuff.
 	G_SetUsedCheats(false);
-	unlockables[31].unlocked = true; // credits
-	unlockables[30].unlocked = true; // sound test
-	unlockables[28].unlocked = true; // level select
+	clientGamedata->unlocked[31] = true; // credits
+	clientGamedata->unlocked[30] = true; // sound test
+	clientGamedata->unlocked[28] = true; // level select
 
 	// Refresh secrets menu existing.
 	M_ClearMenus(true);
diff --git a/src/p_spec.c b/src/p_spec.c
index 48185e9087..71ea145b9e 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1795,9 +1795,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			{ // Unlockable triggers required
 				INT32 trigid = triggerline->args[1];
 
-				if (netgame || multiplayer)
-					return false;
-				else if (trigid < 0 || trigid > 31) // limited by 32 bit variable
+				if (trigid < 0 || trigid > 31) // limited by 32 bit variable
 				{
 					CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger (sidedef %hu): bad trigger ID %d\n", triggerline->sidenum[0], trigid);
 					return false;
@@ -1810,14 +1808,12 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			{ // An unlockable itself must be unlocked!
 				INT32 unlockid = triggerline->args[1];
 
-				if (netgame || multiplayer)
-					return false;
-				else if (unlockid < 0 || unlockid >= MAXUNLOCKABLES) // limited by unlockable count
+				if (unlockid < 0 || unlockid >= MAXUNLOCKABLES) // limited by unlockable count
 				{
 					CONS_Debug(DBG_GAMELOGIC, "Unlockable check (sidedef %hu): bad unlockable ID %d\n", triggerline->sidenum[0], unlockid);
 					return false;
 				}
-				else if (!(unlockables[unlockid-1].unlocked))
+				else if (!(serverGamedata->unlocked[unlockid-1]))
 					return false;
 			}
 			break;
-- 
GitLab


From c1e641be437ae0e3e037f1f518466734ff47a8ab Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 31 Oct 2022 18:09:18 -0400
Subject: [PATCH 116/518] Improve emblem sharing conditions

---
 src/p_inter.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/p_inter.c b/src/p_inter.c
index 750e9cc34c..bd3c15a456 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -796,6 +796,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			{
 				mobj_t *spark = NULL;
 				boolean prevCollected;
+				const boolean isServer = ((player - players) == serverplayer);
 
 				if (!P_CanPickupEmblem(player, special->health - 1))
 				{
@@ -804,13 +805,13 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 				prevCollected = P_EmblemWasCollected(special->health - 1);
 
-				if (((player - players) == serverplayer) || shareEmblems)
+				if (isServer || shareEmblems)
 				{
 					serverGamedata->collected[special->health-1] = true;
 					M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
 				}
 
-				if (P_IsLocalPlayer(player) /*|| shareEmblems*/)
+				if (P_IsLocalPlayer(player) || (isServer && shareEmblems))
 				{
 					clientGamedata->collected[special->health-1] = true;
 					M_UpdateUnlockablesAndExtraEmblems(clientGamedata);
-- 
GitLab


From 645dd7d66278372c8fd9ec3357286887683c8f0b Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 31 Oct 2022 18:26:41 -0400
Subject: [PATCH 117/518] Stop endlessly chasing NIGHTSPULL emblems

---
 src/p_inter.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/p_inter.c b/src/p_inter.c
index bd3c15a456..907388a8c5 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -798,6 +798,17 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				boolean prevCollected;
 				const boolean isServer = ((player - players) == serverplayer);
 
+				if ((special->flags2 & MF2_NIGHTSPULL)
+					&& (toucher == special->tracer))
+				{
+					// Since collecting may not remove the object,
+					// we need to manually stop it from chasing.
+					P_SetTarget(&special->tracer, NULL);
+					special->flags2 &= ~MF2_NIGHTSPULL;
+					special->movefactor = 0;
+					special->momx = special->momy = special->momz = 0;
+				}
+
 				if (!P_CanPickupEmblem(player, special->health - 1))
 				{
 					return;
-- 
GitLab


From cf228757a1c310cf0ba22452f2b5967f54a9583f Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 31 Oct 2022 18:46:47 -0400
Subject: [PATCH 118/518] Emblems disappear on collection again, only for SP

---
 src/p_inter.c | 41 +++++++++++++++++++++++++++++------------
 1 file changed, 29 insertions(+), 12 deletions(-)

diff --git a/src/p_inter.c b/src/p_inter.c
index 907388a8c5..5adfdb852b 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -794,9 +794,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 		// Secret emblem thingy
 		case MT_EMBLEM:
 			{
-				mobj_t *spark = NULL;
-				boolean prevCollected;
-				const boolean isServer = ((player - players) == serverplayer);
+				const boolean toucherIsServer = ((player - players) == serverplayer);
+				const boolean consoleIsServer = (consoleplayer == serverplayer);
+				boolean prevCollected = false;
 
 				if ((special->flags2 & MF2_NIGHTSPULL)
 					&& (toucher == special->tracer))
@@ -816,31 +816,48 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 				prevCollected = P_EmblemWasCollected(special->health - 1);
 
-				if (isServer || shareEmblems)
+				if (toucherIsServer || shareEmblems)
 				{
 					serverGamedata->collected[special->health-1] = true;
 					M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
 				}
 
-				if (P_IsLocalPlayer(player) || (isServer && shareEmblems))
+				if (P_IsLocalPlayer(player) || (consoleIsServer && shareEmblems))
 				{
 					clientGamedata->collected[special->health-1] = true;
 					M_UpdateUnlockablesAndExtraEmblems(clientGamedata);
 					G_SaveGameData(clientGamedata);
 				}
 
-				// This always spawns the object to prevent mobjnum issues,
-				// but makes the effect invisible to whoever it doesn't matter to.
-				spark = P_SpawnMobjFromMobj(special, 0, 0, 0, MT_SPARK);
-				if (prevCollected == false && P_EmblemWasCollected(special->health - 1) == true)
+				if (netgame)
 				{
-					S_StartSound((shareEmblems ? NULL : special), special->info->deathsound);
+					// This always spawns the object to prevent mobjnum issues,
+					// but makes the effect invisible to whoever it doesn't matter to.
+					mobj_t *spark = P_SpawnMobjFromMobj(special, 0, 0, 0, MT_SPARK);
+
+					if (prevCollected == false && P_EmblemWasCollected(special->health - 1) == true)
+					{
+						// Play the sound if it was collected.
+						S_StartSound((shareEmblems ? NULL : special), special->info->deathsound);
+					}
+					else
+					{
+						// We didn't collect it, make it invisible to us.
+						spark->flags2 |= MF2_DONTDRAW;
+					}
+
+					return;
 				}
 				else
 				{
-					spark->flags2 |= MF2_DONTDRAW;
+					if (prevCollected == false && P_EmblemWasCollected(special->health - 1) == true)
+					{
+						// Disappear when collecting for local games.
+						break;
+					}
+
+					return;
 				}
-				return;
 			}
 
 		// CTF Flags
-- 
GitLab


From e06956a53b06872b4eb2c9bd184177c3b1322b77 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Wed, 16 Nov 2022 12:40:46 -0500
Subject: [PATCH 119/518] Fix skin-only emblems on emblem radar

---
 src/st_stuff.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/st_stuff.c b/src/st_stuff.c
index 42f1f89ec5..59c50b168c 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2569,7 +2569,7 @@ static boolean ST_doItemFinderIconsAndSound(void)
 
 		emblems[stemblems++] = i;
 
-		if (!P_EmblemWasCollected(i))
+		if (!P_EmblemWasCollected(i) && P_CanPickupEmblem(stplyr, i))
 		{
 			++stunfound;
 		}
@@ -2603,7 +2603,7 @@ static boolean ST_doItemFinderIconsAndSound(void)
 		{
 			if (mo2->health == emblems[i] + 1)
 			{
-				if (P_EmblemWasCollected(emblems[i]))
+				if (P_EmblemWasCollected(emblems[i]) || !P_CanPickupEmblem(stplyr, emblems[i]))
 					break;
 
 				soffset = (i * 20) - ((stemblems - 1) * 10);
-- 
GitLab


From 35b676e313378a08d9369a503612ddd0d795832a Mon Sep 17 00:00:00 2001
From: SMS Alfredo <65426124+SMS-Alfredo@users.noreply.github.com>
Date: Fri, 2 Jun 2023 19:52:03 -0500
Subject: [PATCH 120/518] Fix Camera Interpolation Not Being Reset

---
 src/r_main.c | 63 +++++++++++++++++++++++++---------------------------
 1 file changed, 30 insertions(+), 33 deletions(-)

diff --git a/src/r_main.c b/src/r_main.c
index ebf7a28bf1..55bb9c4ffe 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -1092,34 +1092,12 @@ subsector_t *R_PointInSubsectorOrNull(fixed_t x, fixed_t y)
 void R_SetupFrame(player_t *player)
 {
 	camera_t *thiscam;
-	boolean chasecam = false;
-
-	if (splitscreen && player == &players[secondarydisplayplayer]
-		&& player != &players[consoleplayer])
-	{
+	boolean chasecam = R_ViewpointHasChasecam(player);
+	
+	if (splitscreen && player == &players[secondarydisplayplayer] && player != &players[consoleplayer])
 		thiscam = &camera2;
-		chasecam = (cv_chasecam2.value != 0);
-		R_SetViewContext(VIEWCONTEXT_PLAYER2);
-	}
 	else
-	{
 		thiscam = &camera;
-		chasecam = (cv_chasecam.value != 0);
-		R_SetViewContext(VIEWCONTEXT_PLAYER1);
-	}
-
-	if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode)
-		chasecam = true; // force chasecam on
-	else if (player->spectator) // no spectator chasecam
-		chasecam = false; // force chasecam off
-
-	if (chasecam && !thiscam->chase)
-	{
-		P_ResetCamera(player, thiscam);
-		thiscam->chase = true;
-	}
-	else if (!chasecam)
-		thiscam->chase = false;
 
 	newview->sky = false;
 
@@ -1348,11 +1326,37 @@ boolean R_ViewpointHasChasecam(player_t *player)
 {
 	camera_t *thiscam;
 	boolean chasecam = false;
+	boolean isplayer2 = (splitscreen && player == &players[secondarydisplayplayer] && player != &players[consoleplayer]);
 
-	if (splitscreen && player == &players[secondarydisplayplayer] && player != &players[consoleplayer])
+	if (isplayer2)
 	{
 		thiscam = &camera2;
 		chasecam = (cv_chasecam2.value != 0);
+	}
+	else
+	{
+		thiscam = &camera;
+		chasecam = (cv_chasecam.value != 0);
+	}
+
+	if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode)
+		chasecam = true; // force chasecam on
+	else if (player->spectator) // no spectator chasecam
+		chasecam = false; // force chasecam off
+		
+	if (chasecam && !thiscam->chase)
+	{
+		P_ResetCamera(player, thiscam);
+		thiscam->chase = true;
+	}
+	else if (!chasecam && thiscam->chase)
+	{
+		P_ResetCamera(player, thiscam);
+		thiscam->chase = false;
+	}
+	
+	if (isplayer2)
+	{
 		R_SetViewContext(VIEWCONTEXT_PLAYER2);
 		if (thiscam->reset)
 		{
@@ -1362,8 +1366,6 @@ boolean R_ViewpointHasChasecam(player_t *player)
 	}
 	else
 	{
-		thiscam = &camera;
-		chasecam = (cv_chasecam.value != 0);
 		R_SetViewContext(VIEWCONTEXT_PLAYER1);
 		if (thiscam->reset)
 		{
@@ -1372,11 +1374,6 @@ boolean R_ViewpointHasChasecam(player_t *player)
 		}
 	}
 
-	if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode)
-		chasecam = true; // force chasecam on
-	else if (player->spectator) // no spectator chasecam
-		chasecam = false; // force chasecam off
-
 	return chasecam;
 }
 
-- 
GitLab


From 7c75f61be74a6a152640df01bcb60d4c07fd7e03 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 3 Jun 2023 13:05:33 +0200
Subject: [PATCH 121/518] Fix buffer overflow when displaying a scrolling
 background

---
 src/m_menu.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/m_menu.c b/src/m_menu.c
index 64a1c94048..2fc0b889fe 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -2807,6 +2807,7 @@ void M_SetMenuCurBackground(const char *defaultname)
 {
 	char name[9];
 	strncpy(name, defaultname, 8);
+	name[8] = '\0';
 	M_IterateMenuTree(MIT_SetCurBackground, &name);
 }
 
-- 
GitLab


From aa43aa7c8c0a1c17174b7b84ffa9e9a282f4264c Mon Sep 17 00:00:00 2001
From: SMS Alfredo <65426124+SMS-Alfredo@users.noreply.github.com>
Date: Sat, 3 Jun 2023 15:43:58 -0500
Subject: [PATCH 122/518] Call P_ResetCamera Upon Switching To/From an Away
 View Camera

---
 src/lua_playerlib.c |  4 ++++
 src/p_spec.c        |  5 +++++
 src/p_user.c        | 17 +++++++++++++----
 3 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index f7e14e78f9..f79b1e45e9 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -710,6 +710,10 @@ static int player_set(lua_State *L)
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->awayviewmobj, mo);
+		if (plr == &players[displayplayer])
+			P_ResetCamera(plr, &camera); // reset p1 camera on p1 getting an awayviewmobj
+		else if (splitscreen && plr == &players[secondarydisplayplayer])
+			P_ResetCamera(plr, &camera2);  // reset p2 camera on p2 getting an awayviewmobj
 	}
 	else if (fastcmp(field,"awayviewtics"))
 	{
diff --git a/src/p_spec.c b/src/p_spec.c
index aa04a723eb..8489a227f3 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2669,6 +2669,11 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				{
 					P_SetTarget(&mo->player->awayviewmobj, altview);
 					mo->player->awayviewtics = line->args[1];
+					
+					if (mo->player == &players[displayplayer])
+						P_ResetCamera(mo->player, &camera); // reset p1 camera on p1 getting an awayviewmobj
+					else if (splitscreen && mo->player == &players[secondarydisplayplayer])
+						P_ResetCamera(mo->player, &camera2);  // reset p2 camera on p2 getting an awayviewmobj
 				}
 
 				aim = udmf ? altview->spawnpoint->pitch : line->args[2];
diff --git a/src/p_user.c b/src/p_user.c
index 0f3282da55..42fd0c3104 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -11507,15 +11507,24 @@ void P_PlayerThink(player_t *player)
 	if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj))
 	{
 		P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid
-		player->awayviewtics = 0; // reset to zero
+		player->awayviewtics = 1; // reset to one, the below code will immediately set it to zero
+	}
+	
+	if (player->awayviewtics && player->awayviewtics != -1)
+	{
+		player->awayviewtics--;
+		if (!(player->awayviewtics))
+		{
+			if (player == &players[displayplayer])
+				P_ResetCamera(player, &camera); // reset p1 camera on p1 running out of awayviewtics
+			else if (splitscreen && player == &players[secondarydisplayplayer])
+				P_ResetCamera(player, &camera2);  // reset p2 camera on p2 running out of awayviewtics
+		}
 	}
 
 	if (player->flashcount)
 		player->flashcount--;
 
-	if (player->awayviewtics && player->awayviewtics != -1)
-		player->awayviewtics--;
-
 	/// \note do this in the cheat code
 	if (player->pflags & PF_NOCLIP)
 		player->mo->flags |= MF_NOCLIP;
-- 
GitLab


From 7fa7653494dace135afd2fb84184c73bd51c1609 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 5 Jun 2023 13:54:09 -0400
Subject: [PATCH 123/518] Port increment_move radius clamps from Ring Racers

Fixes inconsistencies with thin players, like with Liftoff Gantry's scale section, and with large characters colliding with steep slopes instead of going up them.
---
 src/p_map.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/src/p_map.c b/src/p_map.c
index 27d5ea7810..1070489578 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -2728,8 +2728,22 @@ increment_move
 	fixed_t thingtop;
 	floatok = false;
 
-	if (radius < MAXRADIUS/2)
-		radius = MAXRADIUS/2;
+	// This makes sure that there are no freezes from computing extremely small movements.
+	// Originally was MAXRADIUS/2, but that can cause some bad inconsistencies for small players.
+	radius = max(radius, thing->scale);
+
+	// And we also have to prevent Big Large (tm) movements, as those can skip too far
+	// across slopes and cause us to fail step up checks on them when we otherwise shouldn't.
+	radius = min(radius, 16 * thing->scale);
+
+	// (This whole "step" system is flawed; it was OK before, but the addition of slopes has
+	// exposed the problems with doing it like this. The right thing to do would be to use
+	// raycasting for physics to fix colliding in weird order, double-checking collisions,
+	// randomly colliding with slopes instead of going up them, etc. I don't feel like porting
+	// that from RR, as its both a huge sweeping change and still incomplete at the time of
+	// writing. Clamping radius to make our steps more precise will work just fine as long
+	// as you keep all of your crazy intentions to poke any of the other deep-rooted movement
+	// code to yourself. -- Sal 6/5/2023)
 
 	do {
 		if (thing->flags & MF_NOCLIP) {
-- 
GitLab


From 1cc870485dd93445bb4933be98dc58b1cdafee5b Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 15 Mar 2022 20:35:06 +0100
Subject: [PATCH 124/518] Increase unlockable limits: - MAXUNLOCKABLES from 32
 to 80 - MAXEXTRAEMBLEMS from 16 to 48 - COMPAT_GAMEDATA_ID is used to account
 for the old values to prevent losing records. - Also fixes linedef actions
 319/320.

---
 src/g_game.c  | 32 +++++++++++++++++++++++---------
 src/m_cond.h  |  4 ++--
 src/m_menu.c  | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/r_skins.c |  3 ++-
 4 files changed, 75 insertions(+), 12 deletions(-)

diff --git a/src/g_game.c b/src/g_game.c
index b239800447..47e670bfec 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -4309,6 +4309,12 @@ void G_LoadGameData(gamedata_t *data)
 	// Stop saving, until we successfully load it again.
 	data->loaded = false;
 
+	// Backwards compat stuff
+	INT32 max_emblems = MAXEMBLEMS;
+	INT32 max_extraemblems = MAXEXTRAEMBLEMS;
+	INT32 max_unlockables = MAXUNLOCKABLES;
+	INT32 max_conditionsets = MAXCONDITIONSETS;
+
 	// Clear things so previously read gamedata doesn't transfer
 	// to new gamedata
 	G_ClearRecords(data); // main and nights records
@@ -4325,7 +4331,7 @@ void G_LoadGameData(gamedata_t *data)
 	{
 		// Don't load, but do save. (essentially, reset)
 		data->loaded = true;
-		return; 
+		return;
 	}
 
 	length = FIL_ReadFile(va(pandf, srb2home, gamedatafilename), &savebuffer);
@@ -4355,6 +4361,14 @@ void G_LoadGameData(gamedata_t *data)
 		I_Error("Game data is from another version of SRB2.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
 	}
 
+#ifdef COMPAT_GAMEDATA_ID // Account for lower MAXUNLOCKABLES and MAXEXTRAEMBLEMS from older versions
+	if (versionID == COMPAT_GAMEDATA_ID)
+	{
+		max_extraemblems = 16;
+		max_unlockables = 32;
+	}
+#endif
+
 	data->totalplaytime = READUINT32(save_p);
 
 #ifdef COMPAT_GAMEDATA_ID
@@ -4393,31 +4407,31 @@ void G_LoadGameData(gamedata_t *data)
 			goto datacorrupt;
 
 	// To save space, use one bit per collected/achieved/unlocked flag
-	for (i = 0; i < MAXEMBLEMS;)
+	for (i = 0; i < max_emblems;)
 	{
 		rtemp = READUINT8(save_p);
-		for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
+		for (j = 0; j < 8 && j+i < max_emblems; ++j)
 			data->collected[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
-	for (i = 0; i < MAXEXTRAEMBLEMS;)
+	for (i = 0; i < max_extraemblems;)
 	{
 		rtemp = READUINT8(save_p);
-		for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
+		for (j = 0; j < 8 && j+i < max_extraemblems; ++j)
 			data->extraCollected[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
-	for (i = 0; i < MAXUNLOCKABLES;)
+	for (i = 0; i < max_unlockables;)
 	{
 		rtemp = READUINT8(save_p);
-		for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
+		for (j = 0; j < 8 && j+i < max_unlockables; ++j)
 			data->unlocked[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
-	for (i = 0; i < MAXCONDITIONSETS;)
+	for (i = 0; i < max_conditionsets;)
 	{
 		rtemp = READUINT8(save_p);
-		for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
+		for (j = 0; j < 8 && j+i < max_conditionsets; ++j)
 			data->achieved[j+i] = ((rtemp >> j) & 1);
 		i += j;
 	}
diff --git a/src/m_cond.h b/src/m_cond.h
index 6a3da79ece..95c3e1ebeb 100644
--- a/src/m_cond.h
+++ b/src/m_cond.h
@@ -139,8 +139,8 @@ typedef struct
 // you seriously need to get a life.
 #define MAXCONDITIONSETS 128
 #define MAXEMBLEMS       512
-#define MAXEXTRAEMBLEMS   16
-#define MAXUNLOCKABLES    32
+#define MAXEXTRAEMBLEMS   48
+#define MAXUNLOCKABLES    80
 
 /** Time attack information, currently a very small structure.
   */
diff --git a/src/m_menu.c b/src/m_menu.c
index f9f52335d0..1c064d228b 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -728,6 +728,54 @@ static menuitem_t SR_MainMenu[] =
 	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom30
 	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom31
 	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom32
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom33
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom34
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom35
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom36
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom37
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom38
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom39
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom40
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom41
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom42
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom43
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom44
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom45
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom46
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom47
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom48
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom49
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom50
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom51
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom52
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom53
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom54
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom55
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom56
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom57
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom58
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom59
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom60
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom61
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom62
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom63
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom64
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom65
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom66
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom67
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom68
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom69
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom70
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom71
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom72
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom73
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom74
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom75
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom76
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom77
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom78
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom79
+	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom80
 
 };
 
diff --git a/src/r_skins.c b/src/r_skins.c
index 2c031ee851..5bcde27b11 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -190,7 +190,8 @@ UINT32 R_GetSkinAvailabilities(void)
 			// This crash is impossible to trigger as is,
 			// but it could happen if MAXUNLOCKABLES is ever made higher than 32,
 			// and someone makes a mod that has 33+ unlockable characters. :V
-			I_Error("Too many unlockable characters\n");
+			// 2022/03/15: MAXUNLOCKABLES is now higher than 32
+			I_Error("Too many unlockable characters! (maximum is 32)\n");
 			return 0;
 		}
 
-- 
GitLab


From 99f09339866e3fc30d100d7353d0866a41ed0981 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 20 Mar 2022 14:10:18 +0100
Subject: [PATCH 125/518] Change how SR_MainMenu is initialized.

---
 src/m_menu.c | 93 ++++------------------------------------------------
 1 file changed, 7 insertions(+), 86 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 1c064d228b..51f5b71732 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -693,90 +693,10 @@ static menuitem_t SR_PandorasBox[] =
 };
 
 // Sky Room Custom Unlocks
-static menuitem_t SR_MainMenu[] =
+static menuitem_t SR_MainMenu[MAXUNLOCKABLES+1] =
 {
 	{IT_STRING|IT_SUBMENU,NULL, "Extras Checklist", &SR_UnlockChecklistDef, 0},
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom1
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom2
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom3
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom4
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom5
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom6
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom7
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom8
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom9
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom10
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom11
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom12
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom13
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom14
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom15
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom16
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom17
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom18
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom19
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom20
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom21
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom22
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom23
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom24
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom25
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom26
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom27
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom28
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom29
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom30
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom31
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom32
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom33
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom34
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom35
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom36
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom37
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom38
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom39
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom40
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom41
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom42
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom43
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom44
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom45
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom46
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom47
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom48
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom49
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom50
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom51
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom52
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom53
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom54
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom55
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom56
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom57
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom58
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom59
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom60
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom61
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom62
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom63
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom64
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom65
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom66
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom67
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom68
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom69
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom70
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom71
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom72
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom73
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom74
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom75
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom76
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom77
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom78
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom79
-	{IT_DISABLED,         NULL, "",   NULL,                 0}, // Custom80
-
+	// The remaining (MAXUNLOCKABLES) items are now initialized in M_SecretsMenu
 };
 
 static menuitem_t SR_LevelSelectMenu[] =
@@ -8177,14 +8097,15 @@ static void M_SecretsMenu(INT32 choice)
 
 	(void)choice;
 
-	// Clear all before starting
-	for (i = 1; i < MAXUNLOCKABLES+1; ++i)
-		SR_MainMenu[i].status = IT_DISABLED;
+	// Initialize array with placeholder entries
+	menuitem_t placeholder = {IT_DISABLED, NULL, "", NULL, 0};
+	for (i = 1; i <= MAXUNLOCKABLES; ++i)
+		SR_MainMenu[i] = placeholder;
 
 	memset(skyRoomMenuTranslations, 0, sizeof(skyRoomMenuTranslations));
 	memset(done, 0, sizeof(done));
 
-	for (i = 1; i < MAXUNLOCKABLES+1; ++i)
+	for (i = 1; i <= MAXUNLOCKABLES; ++i)
 	{
 		curheight = UINT16_MAX;
 		ul = -1;
-- 
GitLab


From d9937953ad1d1e57c08da5f684c7021dcd5a5663 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Wed, 24 May 2023 12:54:19 +0200
Subject: [PATCH 126/518] Fix linedef type 319/320 again

---
 src/p_spec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index 71ea145b9e..d1973d9517 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1808,7 +1808,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			{ // An unlockable itself must be unlocked!
 				INT32 unlockid = triggerline->args[1];
 
-				if (unlockid < 0 || unlockid >= MAXUNLOCKABLES) // limited by unlockable count
+				if (unlockid <= 0 || unlockid > MAXUNLOCKABLES) // limited by unlockable count
 				{
 					CONS_Debug(DBG_GAMELOGIC, "Unlockable check (sidedef %hu): bad unlockable ID %d\n", triggerline->sidenum[0], unlockid);
 					return false;
-- 
GitLab


From d8dd9884511b9be445dbd4211fbe94747d5cf9ff Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 16 Mar 2023 11:47:21 +0100
Subject: [PATCH 127/518] Don't cancel P_LinedefExecute early

---
 src/p_spec.c | 52 ++++++++++++++++++++++++----------------------------
 src/p_spec.h |  2 +-
 2 files changed, 25 insertions(+), 29 deletions(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index 71ea145b9e..2f6e89c281 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1741,14 +1741,13 @@ static boolean P_ActivateLinedefExecutorsInSector(line_t *triggerline, mobj_t *a
 
 /** Used by P_LinedefExecute to check a trigger linedef's conditions
   * The linedef executor specials in the trigger linedef's sector are run if all conditions are met.
-  * Return false cancels P_LinedefExecute, this happens if a condition is not met.
   *
   * \param triggerline Trigger linedef to check conditions for; should NEVER be NULL.
   * \param actor Object initiating the action; should not be NULL.
   * \param caller Sector in which the action was started. May be NULL.
   * \sa P_ProcessLineSpecial, P_LinedefExecute
   */
-boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller)
+void P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller)
 {
 	INT16 specialtype = triggerline->special;
 
@@ -1761,12 +1760,12 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 		if (caller->triggerer == TO_PLAYEREMERALDS)
 		{
 			if (!(ALL7EMERALDS(emeralds)))
-				return false;
+				return;
 		}
 		else if (caller->triggerer == TO_PLAYERNIGHTS)
 		{
 			if (!P_CheckPlayerMareOld(triggerline))
-				return false;
+				return;
 		}
 	}
 
@@ -1774,22 +1773,22 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 	{
 		case 303:
 			if (!P_CheckPlayerRings(triggerline, actor))
-				return false;
+				return;
 			break;
 		case 305:
 			if (!(actor && actor->player && actor->player->charability == triggerline->args[1]))
-				return false;
+				return;
 			break;
 		case 309:
 			// Only red/blue team members can activate this.
 			if (!(actor && actor->player))
-				return false;
+				return;
 			if (actor->player->ctfteam != ((triggerline->args[1] == TMT_RED) ? 1 : 2))
-				return false;
+				return;
 			break;
 		case 314:
 			if (!P_CheckPushables(triggerline, caller))
-				return false;
+				return;
 			break;
 		case 317:
 			{ // Unlockable triggers required
@@ -1798,10 +1797,10 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 				if (trigid < 0 || trigid > 31) // limited by 32 bit variable
 				{
 					CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger (sidedef %hu): bad trigger ID %d\n", triggerline->sidenum[0], trigid);
-					return false;
+					return;
 				}
 				else if (!(unlocktriggers & (1 << trigid)))
-					return false;
+					return;
 			}
 			break;
 		case 319:
@@ -1811,10 +1810,10 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 				if (unlockid < 0 || unlockid >= MAXUNLOCKABLES) // limited by unlockable count
 				{
 					CONS_Debug(DBG_GAMELOGIC, "Unlockable check (sidedef %hu): bad unlockable ID %d\n", triggerline->sidenum[0], unlockid);
-					return false;
+					return;
 				}
 				else if (!(serverGamedata->unlocked[unlockid-1]))
-					return false;
+					return;
 			}
 			break;
 		case 321:
@@ -1822,7 +1821,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			if (triggerline->callcount > 0)
 			{
 				if (--triggerline->callcount > 0)
-					return false;
+					return;
 			}
 			break;
 		case 323: // nightserize
@@ -1830,15 +1829,15 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 		case 327: // nights lap
 		case 329: // nights egg capsule touch
 			if (!P_CheckNightsTriggerLine(triggerline, actor))
-				return false;
+				return;
 			break;
 		case 331:
 			if (!(actor && actor->player))
-				return false;
+				return;
 			if (!triggerline->stringargs[0])
-				return false;
+				return;
 			if (!(stricmp(triggerline->stringargs[0], skins[actor->player->skin].name) == 0) ^ !!(triggerline->args[1]))
-				return false;
+				return;
 			break;
 		case 334: // object dye
 			{
@@ -1846,22 +1845,22 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 				UINT16 color = (actor->player ? actor->player->powers[pw_dye] : actor->color);
 
 				if (!!(triggerline->args[1]) ^ (triggercolor != color))
-					return false;
+					return;
 			}
 			break;
 		case 337: // emerald check
 			if (!P_CheckEmeralds(triggerline->args[2], (UINT16)triggerline->args[1]))
-				return false;
+				return;
 			break;
 		case 340: // NiGHTS mare
 			if (!P_CheckPlayerMare(triggerline))
-				return false;
+				return;
 			break;
 		case 343: // gravity check
 			if (triggerline->args[1] == TMG_TEMPREVERSE && (!(actor->flags2 & MF2_OBJECTFLIP) != !(actor->player->powers[pw_gravityboots])))
-				return false;
+				return;
 			if ((triggerline->args[1] == TMG_NORMAL) != !(actor->eflags & MFE_VERTICALFLIP))
-				return false;
+				return;
 			break;
 		default:
 			break;
@@ -1872,7 +1871,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 	/////////////////////////////////
 
 	if (!P_ActivateLinedefExecutorsInSector(triggerline, actor, caller))
-		return false;
+		return;
 
 	// "Trigger on X calls" linedefs reset if args[2] is set
 	if (specialtype == 321 && triggerline->args[2])
@@ -1905,8 +1904,6 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			&& triggerline->args[0] == TMT_ONCE)
 			triggerline->special = 0;
 	}
-
-	return true;
 }
 
 /** Runs a linedef executor.
@@ -1959,8 +1956,7 @@ void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller)
 		if (lines[masterline].special == 321 && lines[masterline].args[0] > TMXT_EACHTIMEMASK) // Trigger after X calls
 			continue;
 
-		if (!P_RunTriggerLinedef(&lines[masterline], actor, caller))
-			return; // cancel P_LinedefExecute if function returns false
+		P_RunTriggerLinedef(&lines[masterline], actor, caller); // Even if it fails, there might be more linedefs to trigger
 	}
 }
 
diff --git a/src/p_spec.h b/src/p_spec.h
index 91dfccb70f..c8701749f8 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -527,7 +527,7 @@ boolean P_IsMobjTouchingPolyobj(mobj_t *mo, polyobj_t *po, sector_t *polysec);
 
 void P_SwitchWeather(INT32 weathernum);
 
-boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller);
+void P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller);
 void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller);
 void P_RunNightserizeExecutors(mobj_t *actor);
 void P_RunDeNightserizeExecutors(mobj_t *actor);
-- 
GitLab


From b8a25ae53c5db7017adf2411ebae254ac2ed85c1 Mon Sep 17 00:00:00 2001
From: sphere <spherallic@gmail.com>
Date: Tue, 13 Jun 2023 00:05:58 +0000
Subject: [PATCH 128/518] Add interpolation to hitbox viewer, tweak hitbox
 colors

---
 src/d_netcmd.c         |  1 +
 src/hardware/hw_main.c | 19 ++++++++++++++++---
 src/r_bbox.c           | 14 ++++++++++++--
 src/r_things.c         | 22 ++++++++++++++++++----
 src/screen.h           |  2 +-
 5 files changed, 48 insertions(+), 10 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 420621a77a..0f859a569f 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -873,6 +873,7 @@ void D_RegisterClientCommands(void)
 	// screen.c
 	CV_RegisterVar(&cv_fullscreen);
 	CV_RegisterVar(&cv_renderview);
+	CV_RegisterVar(&cv_renderhitboxinterpolation);
 	CV_RegisterVar(&cv_renderhitbox);
 	CV_RegisterVar(&cv_renderer);
 	CV_RegisterVar(&cv_scr_depth);
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 09235ce622..e7550fd6e8 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -5686,9 +5686,22 @@ static void HWR_ProjectBoundingBox(mobj_t *thing)
 	if (!R_ThingBoundingBoxVisible(thing))
 		return;
 
+	// uncapped/interpolation
+	boolean interpolate = cv_renderhitboxinterpolation.value;
+	interpmobjstate_t interp = {0};
+
+	if (R_UsingFrameInterpolation() && !paused && interpolate)
+	{
+		R_InterpolateMobjState(thing, rendertimefrac, &interp);
+	}
+	else
+	{
+		R_InterpolateMobjState(thing, FRACUNIT, &interp);
+	}
+
 	// transform the origin point
-	tr_x = FIXED_TO_FLOAT(thing->x) - gl_viewx;
-	tr_y = FIXED_TO_FLOAT(thing->y) - gl_viewy;
+	tr_x = FIXED_TO_FLOAT(interp.x) - gl_viewx;
+	tr_y = FIXED_TO_FLOAT(interp.y) - gl_viewy;
 
 	// rotation around vertical axis
 	tz = (tr_x * gl_viewcos) + (tr_y * gl_viewsin);
@@ -5707,7 +5720,7 @@ static void HWR_ProjectBoundingBox(mobj_t *thing)
 	vis->x2 = tr_x + rad;
 	vis->z1 = tr_y - rad;
 	vis->z2 = tr_y + rad;
-	vis->gz = FIXED_TO_FLOAT(thing->z);
+	vis->gz = FIXED_TO_FLOAT(interp.z);
 	vis->gzt = vis->gz + FIXED_TO_FLOAT(thing->height);
 	vis->mobj = thing;
 
diff --git a/src/r_bbox.c b/src/r_bbox.c
index b45007bb2a..7ed693fd0a 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -35,6 +35,7 @@ static CV_PossibleValue_t renderhitbox_cons_t[] = {
 	{0}};
 
 consvar_t cv_renderhitbox = CVAR_INIT ("renderhitbox", "Off", 0, renderhitbox_cons_t, NULL);
+consvar_t cv_renderhitboxinterpolation = CVAR_INIT ("renderhitbox_interpolation", "On", CV_SAVE, CV_OnOff, NULL);
 
 struct bbox_col {
 	INT32 x;
@@ -179,12 +180,21 @@ UINT8 R_GetBoundingBoxColor(mobj_t *thing)
 	if (flags & (MF_NOCLIPTHING))
 		return 7; // BFBFBF
 
-	if (flags & (MF_BOSS|MF_MISSILE|MF_ENEMY|MF_PAIN))
+	if (flags & (MF_BOSS|MF_ENEMY))
 		return 35; // F00
 
+	if (flags & (MF_MISSILE|MF_PAIN))
+		return 54; // F70
+
 	if (flags & (MF_SPECIAL|MF_MONITOR))
 		return 73; // FF0
 
+	if (flags & MF_PUSHABLE)
+		return 112; // 0F0
+
+	if (flags & (MF_SPRING))
+		return 181; // F0F
+
 	if (flags & (MF_NOCLIP))
 		return 152; // 00F
 
@@ -299,7 +309,7 @@ boolean R_ThingBoundingBoxVisible(mobj_t *thing)
 			return is_tangible(thing);
 
 		case RENDERHITBOX_RINGS:
-			return (thing->type == MT_RING);
+			return (thing->type == MT_RING || thing->type == MT_BLUESPHERE);
 
 		default:
 			return false;
diff --git a/src/r_things.c b/src/r_things.c
index 7eecb041e3..38237e5b30 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1436,19 +1436,33 @@ static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis)
 		return;
 	}
 
+	// uncapped/interpolation
+	boolean interpolate = cv_renderhitboxinterpolation.value;
+	interpmobjstate_t interp = {0};
+
+	// do interpolation
+	if (R_UsingFrameInterpolation() && !paused && interpolate)
+	{
+		R_InterpolateMobjState(thing, rendertimefrac, &interp);
+	}
+	else
+	{
+		R_InterpolateMobjState(thing, FRACUNIT, &interp);
+	}
+
 	// 1--3
 	// |  |
 	// 0--2
 
 	// start in the (0) corner
-	gx = thing->x - thing->radius - viewx;
-	gy = thing->y - thing->radius - viewy;
+	gx = interp.x - thing->radius - viewx;
+	gy = interp.y - thing->radius - viewy;
 
 	tz = FixedMul(gx, viewcos) + FixedMul(gy, viewsin);
 
 	// thing is behind view plane?
 	// if parent vis is visible, ignore this
-	if (!vis && (tz < FixedMul(MINZ, thing->scale)))
+	if (!vis && (tz < FixedMul(MINZ, interp.scale)))
 	{
 		return;
 	}
@@ -1473,7 +1487,7 @@ static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis)
 	box->scale = 2 * FixedMul(thing->radius, viewsin);
 	box->xscale = 2 * FixedMul(thing->radius, viewcos);
 
-	box->pz = thing->z;
+	box->pz = interp.z;
 	box->pzt = box->pz + box->thingheight;
 
 	box->gzt = box->pzt;
diff --git a/src/screen.h b/src/screen.h
index a0e04f85e9..b021a419dd 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -199,7 +199,7 @@ extern CV_PossibleValue_t cv_renderer_t[];
 extern INT32 scr_bpp;
 extern UINT8 *scr_borderpatch; // patch used to fill the view borders
 
-extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_renderhitbox, cv_fullscreen;
+extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_renderhitbox, cv_renderhitboxinterpolation, cv_fullscreen;
 // wait for page flipping to end or not
 extern consvar_t cv_vidwait;
 extern consvar_t cv_timescale;
-- 
GitLab


From 37223f13f1319df814d683f86b3a61ff83b1b123 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 13 Jun 2023 10:38:44 +0200
Subject: [PATCH 129/518] Remove debug print on generic item circle spawn

---
 src/p_mobj.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index e79977c487..a04351ae77 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -13687,7 +13687,6 @@ void P_SpawnItemPattern(mapthing_t *mthing, boolean bonustime)
 		UINT8 numitemtypes;
 		if (!udmf)
 			return;
-		CONS_Printf("Itemstring: %s\n", mthing->stringargs[0]);
 		P_ParseItemTypes(mthing->stringargs[0], itemtypes, &numitemtypes);
 		P_SpawnItemCircle(mthing, itemtypes, numitemtypes, mthing->args[0], mthing->args[1] << FRACBITS, bonustime);
 		return;
-- 
GitLab


From 4d4910c918fde1a45bec771c84f688d8ef28cfc9 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Tue, 13 Jun 2023 14:02:19 +0200
Subject: [PATCH 130/518] Use 99999999 instead of 86400, make "MIN" 0

Nines might be more pleasing than an exact number for con_hudtime
It'd be bad to make "MIN" in an old config suddenly start displaying lines for con_hudlines
---
 src/console.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/console.c b/src/console.c
index b99b137206..d432bba723 100644
--- a/src/console.c
+++ b/src/console.c
@@ -125,12 +125,12 @@ static void CONS_backcolor_Change(void);
 static char con_buffer[CON_BUFFERSIZE];
 
 // how many seconds the hud messages lasts on the screen
-// CV_Unsigned can overflow when multiplied by TICRATE later, so let's use a 24-hour limit instead
-static CV_PossibleValue_t hudtime_cons_t[] = {{0, "MIN"}, {86400, "MAX"}, {0, NULL}};
+// CV_Unsigned can overflow when multiplied by TICRATE later, so let's use a 3-year limit instead
+static CV_PossibleValue_t hudtime_cons_t[] = {{0, "MIN"}, {99999999, "MAX"}, {0, NULL}};
 static consvar_t cons_hudtime = CVAR_INIT ("con_hudtime", "5", CV_SAVE, hudtime_cons_t, NULL);
 
 // number of lines displayed on the HUD
-static CV_PossibleValue_t hudlines_cons_t[] = {{1, "MIN"}, {MAXHUDLINES, "MAX"}, {0, "None"}, {0, NULL}};
+static CV_PossibleValue_t hudlines_cons_t[] = {{0, "MIN"}, {MAXHUDLINES, "MAX"}, {0, NULL}};
 static consvar_t cons_hudlines = CVAR_INIT ("con_hudlines", "5", CV_CALL|CV_SAVE, hudlines_cons_t, CONS_hudlines_Change);
 
 // number of lines console move per frame
-- 
GitLab


From f921a9987d11dc805aaca944577ce982ed800068 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Tue, 13 Jun 2023 19:06:12 +0200
Subject: [PATCH 131/518] Interpolate models when scaling mobjs

---
 src/hardware/hw_md2.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 6b1d0c6fcb..38e8e8fc42 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1660,7 +1660,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 #endif
 
 		// SRB2CBTODO: MD2 scaling support
-		finalscale *= FIXED_TO_FLOAT(spr->mobj->scale);
+		finalscale *= FIXED_TO_FLOAT(interp.scale);
 
 		p.flip = atransform.flip;
 #ifdef USE_FTRANSFORM_MIRROR
-- 
GitLab


From 5c3f66dc36371beb02f4e7a74ecca79fc7c5beed Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Tue, 13 Jun 2023 21:26:40 -0400
Subject: [PATCH 132/518] Remove "singletics" code in F_WriteText

This was added in 2.1 for whatever reason. All instances of F_WriteText are already capped to the framerate properly by other means; so this code being present just makes timing differences between different netgame clients / other inaccuracies cause the text speed to slow down more than intended.
---
 src/f_finale.c | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/src/f_finale.c b/src/f_finale.c
index f529b45647..299a6a054c 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -224,7 +224,6 @@ static INT32 cutscene_writeptr = 0;
 static INT32 cutscene_textcount = 0;
 static INT32 cutscene_textspeed = 0;
 static UINT8 cutscene_boostspeed = 0;
-static tic_t cutscene_lasttextwrite = 0;
 
 // STJR Intro
 char stjrintro[9] = "STJRI000";
@@ -240,11 +239,6 @@ static UINT8 F_WriteText(void)
 {
 	INT32 numtowrite = 1;
 	const char *c;
-	tic_t ltw = I_GetTime();
-
-	if (cutscene_lasttextwrite == ltw)
-		return 1; // singletics prevention
-	cutscene_lasttextwrite = ltw;
 
 	if (cutscene_boostspeed)
 	{
-- 
GitLab


From bed7e434e7fbc340fa36424a4f1eb67848e8b164 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 10 Jun 2023 12:12:41 +0200
Subject: [PATCH 133/518] Add absolute z height flag for mapthings in UDMF

---
 src/doomdata.h |  4 ++++
 src/p_mobj.c   | 17 +++++++++--------
 src/p_mobj.h   |  2 +-
 src/p_setup.c  |  2 ++
 4 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/src/doomdata.h b/src/doomdata.h
index 4c5bdefaf9..45cbb25579 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -62,6 +62,10 @@ enum
 #define MTF_AMBUSH 8
 
 // Do not use bit five or after, as they are used for object z-offsets.
+// Unless it's exclusive to UDMF.
+
+// Flag to use Z as absolute spawn height, ignoring the floor and ceiling.
+#define MTF_ABSOLUTEZ 16
 
 #if defined(_MSC_VER)
 #pragma pack(1)
diff --git a/src/p_mobj.c b/src/p_mobj.c
index a04351ae77..0622ff601a 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -11812,7 +11812,7 @@ void P_MovePlayerToStarpost(INT32 playernum)
 mapthing_t *huntemeralds[MAXHUNTEMERALDS];
 INT32 numhuntemeralds;
 
-fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x, const fixed_t y, const fixed_t dz, const fixed_t offset, const boolean flip, const fixed_t scale)
+fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x, const fixed_t y, const fixed_t dz, const fixed_t offset, const boolean flip, const fixed_t scale, const boolean absolutez)
 {
 	const subsector_t *ss = R_PointInSubsector(x, y);
 
@@ -11822,9 +11822,9 @@ fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x, const f
 
 	// Establish height.
 	if (flip)
-		return P_GetSectorCeilingZAt(ss->sector, x, y) - dz - FixedMul(scale, offset + mobjinfo[mobjtype].height);
+		return (absolutez ? dz : P_GetSectorCeilingZAt(ss->sector, x, y) - dz) - FixedMul(scale, offset + mobjinfo[mobjtype].height);
 	else
-		return P_GetSectorFloorZAt(ss->sector, x, y) + dz + FixedMul(scale, offset);
+		return (absolutez ? dz : P_GetSectorFloorZAt(ss->sector, x, y) + dz) + FixedMul(scale, offset);
 }
 
 fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mthing, const fixed_t x, const fixed_t y)
@@ -11832,6 +11832,7 @@ fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mt
 	fixed_t dz = mthing->z << FRACBITS; // Base offset from the floor.
 	fixed_t offset = 0; // Specific scaling object offset.
 	boolean flip = (!!(mobjinfo[mobjtype].flags & MF_SPAWNCEILING) ^ !!(mthing->options & MTF_OBJECTFLIP));
+	boolean absolutez = !!(mthing->options & MTF_ABSOLUTEZ);
 
 	switch (mobjtype)
 	{
@@ -11887,7 +11888,7 @@ fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mt
 			offset += mthing->args[0] ? 0 : 24*FRACUNIT;
 	}
 
-	if (!(dz + offset)) // Snap to the surfaces when there's no offset set.
+	if (!(dz + offset) && !absolutez) // Snap to the surfaces when there's no offset set.
 	{
 		if (flip)
 			return ONCEILINGZ;
@@ -11895,7 +11896,7 @@ fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mt
 			return ONFLOORZ;
 	}
 
-	return P_GetMobjSpawnHeight(mobjtype, x, y, dz, offset, flip, mthing->scale);
+	return P_GetMobjSpawnHeight(mobjtype, x, y, dz, offset, flip, mthing->scale, absolutez);
 }
 
 static boolean P_SpawnNonMobjMapThing(mapthing_t *mthing)
@@ -13389,7 +13390,7 @@ void P_SpawnHoop(mapthing_t *mthing)
 	vector4_t v, res;
 	fixed_t x = mthing->x << FRACBITS;
 	fixed_t y = mthing->y << FRACBITS;
-	fixed_t z = P_GetMobjSpawnHeight(MT_HOOP, x, y, mthing->z << FRACBITS, 0, false, mthing->scale);
+	fixed_t z = P_GetMobjSpawnHeight(MT_HOOP, x, y, mthing->z << FRACBITS, 0, false, mthing->scale, !!(mthing->options & MTF_ABSOLUTEZ));
 
 	hoopcenter = P_SpawnMobj(x, y, z, MT_HOOPCENTER);
 	hoopcenter->spawnpoint = mthing;
@@ -13516,7 +13517,7 @@ static void P_SpawnItemRow(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 numi
 			itemtypes[r] = P_GetMobjtypeSubstitute(&dummything, itemtypes[r]);
 		}
 	}
-	z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, 0, mthing->options & MTF_OBJECTFLIP, mthing->scale);
+	z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, 0, mthing->options & MTF_OBJECTFLIP, mthing->scale, !!(mthing->options & MTF_ABSOLUTEZ));
 
 	for (r = 0; r < numitems; r++)
 	{
@@ -13575,7 +13576,7 @@ static void P_SpawnItemCircle(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 n
 			itemtypes[i] = P_GetMobjtypeSubstitute(&dummything, itemtypes[i]);
 		}
 	}
-	z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, 0, false, mthing->scale);
+	z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, 0, false, mthing->scale, !!(mthing->options & MTF_ABSOLUTEZ));
 
 	for (i = 0; i < numitems; i++)
 	{
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 6717c4add0..d7d6600780 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -489,7 +489,7 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing);
 void P_MovePlayerToStarpost(INT32 playernum);
 void P_AfterPlayerSpawn(INT32 playernum);
 
-fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x, const fixed_t y, const fixed_t dz, const fixed_t offset, const boolean flip, const fixed_t scale);
+fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x, const fixed_t y, const fixed_t dz, const fixed_t offset, const boolean flip, const fixed_t scale, const boolean absolutez);
 fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mthing, const fixed_t x, const fixed_t y);
 
 mobj_t *P_SpawnMapThing(mapthing_t *mthing);
diff --git a/src/p_setup.c b/src/p_setup.c
index 74645e8771..167e1baacd 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1916,6 +1916,8 @@ static void ParseTextmapThingParameter(UINT32 i, const char *param, const char *
 	// Flags
 	else if (fastcmp(param, "flip") && fastcmp("true", val))
 		mapthings[i].options |= MTF_OBJECTFLIP;
+	else if (fastcmp(param, "absolutez") && fastcmp("true", val))
+		mapthings[i].options |= MTF_ABSOLUTEZ;
 
 	else if (fastncmp(param, "stringarg", 9) && strlen(param) > 9)
 	{
-- 
GitLab


From f416c6a98d78cefc6acd9d94396ce9e438f784c3 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 11 Jun 2023 12:42:10 +0200
Subject: [PATCH 134/518] Add absolute Z flag to MAPTHINGFLAG_LIST

---
 src/deh_lua.c    | 4 ++--
 src/deh_tables.c | 5 +++--
 src/deh_tables.h | 2 +-
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/deh_lua.c b/src/deh_lua.c
index 6dabb7e2d9..a8bc633777 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -271,8 +271,8 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word)
 	}
 	else if (fastncmp("MTF_", word, 4)) {
 		p = word+4;
-		for (i = 0; i < 4; i++)
-			if (MAPTHINGFLAG_LIST[i] && fastcmp(p, MAPTHINGFLAG_LIST[i])) {
+		for (i = 0; MAPTHINGFLAG_LIST[i]; i++)
+			if (fastcmp(p, MAPTHINGFLAG_LIST[i])) {
 				CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
 				return 1;
 			}
diff --git a/src/deh_tables.c b/src/deh_tables.c
index c3b2cfccd6..31e5a93f64 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4409,11 +4409,12 @@ const char *const MOBJEFLAG_LIST[] = {
 	NULL
 };
 
-const char *const MAPTHINGFLAG_LIST[4] = {
+const char *const MAPTHINGFLAG_LIST[] = {
 	"EXTRA", // Extra flag for objects.
 	"OBJECTFLIP", // Reverse gravity flag for objects.
 	"OBJECTSPECIAL", // Special flag used with certain objects.
-	"AMBUSH" // Deaf monsters/do not react to sound.
+	"AMBUSH", // Deaf monsters/do not react to sound.
+	"ABSOLUTEZ" // Absolute spawn height flag for objects.
 };
 
 const char *const PLAYERFLAG_LIST[] = {
diff --git a/src/deh_tables.h b/src/deh_tables.h
index 8943ab71a4..61452e8acf 100644
--- a/src/deh_tables.h
+++ b/src/deh_tables.h
@@ -61,7 +61,7 @@ extern const char *const MOBJTYPE_LIST[];
 extern const char *const MOBJFLAG_LIST[];
 extern const char *const MOBJFLAG2_LIST[]; // \tMF2_(\S+).*// (.+) --> \t"\1", // \2
 extern const char *const MOBJEFLAG_LIST[];
-extern const char *const MAPTHINGFLAG_LIST[4];
+extern const char *const MAPTHINGFLAG_LIST[];
 extern const char *const PLAYERFLAG_LIST[];
 extern const char *const GAMETYPERULE_LIST[];
 extern const char *const ML_LIST[]; // Linedef flags
-- 
GitLab


From 90d95d13057a24b1556dd43fa02dd8b78a8a0b7f Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 11 Jun 2023 13:40:18 +0200
Subject: [PATCH 135/518] Clean options field of binary-converted things

---
 src/p_mobj.c  | 6 +++---
 src/p_setup.c | 5 +++--
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index 0622ff601a..b7295da4cc 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -13390,7 +13390,7 @@ void P_SpawnHoop(mapthing_t *mthing)
 	vector4_t v, res;
 	fixed_t x = mthing->x << FRACBITS;
 	fixed_t y = mthing->y << FRACBITS;
-	fixed_t z = P_GetMobjSpawnHeight(MT_HOOP, x, y, mthing->z << FRACBITS, 0, false, mthing->scale, !!(mthing->options & MTF_ABSOLUTEZ));
+	fixed_t z = P_GetMobjSpawnHeight(MT_HOOP, x, y, mthing->z << FRACBITS, 0, false, mthing->scale, mthing->options & MTF_ABSOLUTEZ);
 
 	hoopcenter = P_SpawnMobj(x, y, z, MT_HOOPCENTER);
 	hoopcenter->spawnpoint = mthing;
@@ -13517,7 +13517,7 @@ static void P_SpawnItemRow(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 numi
 			itemtypes[r] = P_GetMobjtypeSubstitute(&dummything, itemtypes[r]);
 		}
 	}
-	z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, 0, mthing->options & MTF_OBJECTFLIP, mthing->scale, !!(mthing->options & MTF_ABSOLUTEZ));
+	z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, 0, mthing->options & MTF_OBJECTFLIP, mthing->scale, mthing->options & MTF_ABSOLUTEZ);
 
 	for (r = 0; r < numitems; r++)
 	{
@@ -13576,7 +13576,7 @@ static void P_SpawnItemCircle(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 n
 			itemtypes[i] = P_GetMobjtypeSubstitute(&dummything, itemtypes[i]);
 		}
 	}
-	z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, 0, false, mthing->scale, !!(mthing->options & MTF_ABSOLUTEZ));
+	z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, 0, false, mthing->scale, mthing->options & MTF_ABSOLUTEZ);
 
 	for (i = 0; i < numitems; i++)
 	{
diff --git a/src/p_setup.c b/src/p_setup.c
index 167e1baacd..4823305996 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -6681,7 +6681,6 @@ static void P_ConvertBinaryThingTypes(void)
 			break;
 		case 1704: //NiGHTS bumper
 			mapthings[i].pitch = 30 * (((mapthings[i].options & 15) + 9) % 12);
-			mapthings[i].options &= ~0xF;
 			break;
 		case 1705: //Hoop
 		case 1713: //Hoop (Customizable)
@@ -6690,7 +6689,6 @@ static void P_ConvertBinaryThingTypes(void)
 			mapthings[i].angle = (mapthings[i].extrainfo == 1) ? oldangle - 90  : ((oldangle >> 8)*360)/256;
 			mapthings[i].pitch = (mapthings[i].extrainfo == 1) ? oldangle / 360 : ((oldangle & 255)*360)/256;
 			mapthings[i].args[0] = (mapthings[i].type == 1705) ? 96 : (mapthings[i].options & 0xF)*16 + 32;
-			mapthings[i].options &= ~0xF;
 			mapthings[i].type = 1713;
 			break;
 		}
@@ -6718,6 +6716,9 @@ static void P_ConvertBinaryThingTypes(void)
 		default:
 			break;
 		}
+		
+		// Clear binary thing height hacks, to prevent interfering with UDMF-only flags
+		mapthings[i].options &= 8;
 	}
 }
 
-- 
GitLab


From 056d3dcf31ea2c7443b27eaf6982eda9f63d243f Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 11 Jun 2023 17:15:36 +0200
Subject: [PATCH 136/518] Actually clear options field properly

---
 src/p_setup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 4823305996..66d7bff425 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -6718,7 +6718,7 @@ static void P_ConvertBinaryThingTypes(void)
 		}
 		
 		// Clear binary thing height hacks, to prevent interfering with UDMF-only flags
-		mapthings[i].options &= 8;
+		mapthings[i].options &= 0xF;
 	}
 }
 
-- 
GitLab


From 2f2de7d3d357e6e27369cb2fb51fceadefb1899b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sun, 18 Jun 2023 18:05:16 +0200
Subject: [PATCH 137/518] Optimize field lookups in Lua metatables

---
 src/lua_consolelib.c |   56 +-
 src/lua_hudlib.c     |   14 +-
 src/lua_infolib.c    |  231 ++++++--
 src/lua_maplib.c     |  308 ++++++++---
 src/lua_mobjlib.c    |  235 +++++---
 src/lua_playerlib.c  | 1243 +++++++++++++++++++++++++++++++-----------
 src/lua_script.c     |   36 +-
 src/lua_script.h     |    4 +-
 src/lua_skinlib.c    |    7 +-
 9 files changed, 1597 insertions(+), 537 deletions(-)

diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index dcaad14160..1d15b3b145 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -566,27 +566,59 @@ static luaL_Reg lib[] = {
 	{NULL, NULL}
 };
 
+enum cvar_e
+{
+	cvar_name,
+	cvar_defaultvalue,
+	cvar_flags,
+	cvar_value,
+	cvar_string,
+	cvar_changed,
+};
+
+static const char *const cvar_opt[] = {
+	"name",
+	"defaultvalue",
+	"flags",
+	"value",
+	"string",
+	"changed",
+	NULL,
+};
+
+static int cvar_fields_ref = LUA_NOREF;
+
 static int cvar_get(lua_State *L)
 {
 	consvar_t *cvar = *(consvar_t **)luaL_checkudata(L, 1, META_CVAR);
-	const char *field = luaL_checkstring(L, 2);
+	enum cvar_e field = Lua_optoption(L, 2, -1, cvar_fields_ref);
 
-	if(fastcmp(field,"name"))
+	switch (field)
+	{
+	case cvar_name:
 		lua_pushstring(L, cvar->name);
-	else if(fastcmp(field,"defaultvalue"))
+		break;
+	case cvar_defaultvalue:
 		lua_pushstring(L, cvar->defaultvalue);
-	else if(fastcmp(field,"flags"))
+		break;
+	case cvar_flags:
 		lua_pushinteger(L, cvar->flags);
-	else if(fastcmp(field,"value"))
+		break;
+	case cvar_value:
 		lua_pushinteger(L, cvar->value);
-	else if(fastcmp(field,"string"))
+		break;
+	case cvar_string:
 		lua_pushstring(L, cvar->string);
-	else if(fastcmp(field,"changed"))
+		break;
+	case cvar_changed:
 		lua_pushboolean(L, cvar->changed);
-	else if (devparm)
-		return luaL_error(L, LUA_QL("consvar_t") " has no field named " LUA_QS, field);
-	else
-		return 0;
+		break;
+	default:
+		if (devparm)
+			return luaL_error(L, LUA_QL("consvar_t") " has no field named " LUA_QS, field);
+		else
+			return 0;
+	}
 	return 1;
 }
 
@@ -598,6 +630,8 @@ int LUA_ConsoleLib(lua_State *L)
 		lua_setfield(L, -2, "__index");
 	lua_pop(L,1);
 
+	cvar_fields_ref = Lua_CreateFieldTable(L, cvar_opt);
+
 	// Set empty registry tables
 	lua_newtable(L);
 	lua_setfield(L, LUA_REGISTRYINDEX, "COM_Command");
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 1a05997572..c7f67e93ac 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -95,6 +95,8 @@ static const char *const patch_opt[] = {
 	"topoffset",
 	NULL};
 
+static int patch_fields_ref = LUA_NOREF;
+
 // alignment types for v.drawString
 enum align {
 	align_left = 0,
@@ -196,6 +198,8 @@ static const char *const camera_opt[] = {
 	"momz",
 	NULL};
 
+static int camera_fields_ref = LUA_NOREF;
+
 static int lib_getHudInfo(lua_State *L)
 {
 	UINT32 i;
@@ -276,7 +280,7 @@ static int colormap_get(lua_State *L)
 static int patch_get(lua_State *L)
 {
 	patch_t *patch = *((patch_t **)luaL_checkudata(L, 1, META_PATCH));
-	enum patch field = luaL_checkoption(L, 2, NULL, patch_opt);
+	enum patch field = Lua_optoption(L, 2, -1, patch_fields_ref);
 
 	// patches are invalidated when switching renderers
 	if (!patch) {
@@ -316,7 +320,7 @@ static int patch_set(lua_State *L)
 static int camera_get(lua_State *L)
 {
 	camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA));
-	enum cameraf field = luaL_checkoption(L, 2, NULL, camera_opt);
+	enum cameraf field = Lua_optoption(L, 2, -1, camera_fields_ref);
 
 	// cameras should always be valid unless I'm a nutter
 	I_Assert(cam != NULL);
@@ -372,7 +376,7 @@ static int camera_get(lua_State *L)
 static int camera_set(lua_State *L)
 {
 	camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA));
-	enum cameraf field = luaL_checkoption(L, 2, NULL, camera_opt);
+	enum cameraf field = Lua_optoption(L, 2, -1, camera_fields_ref);
 
 	I_Assert(cam != NULL);
 
@@ -1444,6 +1448,8 @@ int LUA_HudLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L,1);
 
+	patch_fields_ref = Lua_CreateFieldTable(L, patch_opt);
+
 	luaL_newmetatable(L, META_CAMERA);
 		lua_pushcfunction(L, camera_get);
 		lua_setfield(L, -2, "__index");
@@ -1452,6 +1458,8 @@ int LUA_HudLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L,1);
 
+	camera_fields_ref = Lua_CreateFieldTable(L, camera_opt);
+
 	luaL_register(L, "hud", lib_hud);
 	return 0;
 }
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 7388632d3c..fb07ccebb5 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1106,75 +1106,161 @@ static int lib_mobjinfolen(lua_State *L)
 	return 1;
 }
 
+enum mobjinfo_e
+{
+	mobjinfo_doomednum,
+	mobjinfo_spawnstate,
+	mobjinfo_spawnhealth,
+	mobjinfo_seestate,
+	mobjinfo_seesound,
+	mobjinfo_reactiontime,
+	mobjinfo_attacksound,
+	mobjinfo_painstate,
+	mobjinfo_painchance,
+	mobjinfo_painsound,
+	mobjinfo_meleestate,
+	mobjinfo_missilestate,
+	mobjinfo_deathstate,
+	mobjinfo_xdeathstate,
+	mobjinfo_deathsound,
+	mobjinfo_speed,
+	mobjinfo_radius,
+	mobjinfo_height,
+	mobjinfo_dispoffset,
+	mobjinfo_mass,
+	mobjinfo_damage,
+	mobjinfo_activesound,
+	mobjinfo_flags,
+	mobjinfo_raisestate,
+};
+
+const char *const mobjinfo_opt[] = {
+	"doomednum",
+	"spawnstate",
+	"spawnhealth",
+	"seestate",
+	"seesound",
+	"reactiontime",
+	"attacksound",
+	"painstate",
+	"painchance",
+	"painsound",
+	"meleestate",
+	"missilestate",
+	"deathstate",
+	"xdeathstate",
+	"deathsound",
+	"speed",
+	"radius",
+	"height",
+	"dispoffset",
+	"mass",
+	"damage",
+	"activesound",
+	"flags",
+	"raisestate",
+	NULL,
+};
+
+static int mobjinfo_fields_ref = LUA_NOREF;
+
 // mobjinfo_t *, field -> number
 static int mobjinfo_get(lua_State *L)
 {
 	mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
-	const char *field = luaL_checkstring(L, 2);
+	enum mobjinfo_e field = luaL_checkoption(L, 2, mobjinfo_opt[0], mobjinfo_opt);
 
 	I_Assert(info != NULL);
 	I_Assert(info >= mobjinfo);
 
-	if (fastcmp(field,"doomednum"))
+	switch (field)
+	{
+	case mobjinfo_doomednum:
 		lua_pushinteger(L, info->doomednum);
-	else if (fastcmp(field,"spawnstate"))
+		break;
+	case mobjinfo_spawnstate:
 		lua_pushinteger(L, info->spawnstate);
-	else if (fastcmp(field,"spawnhealth"))
+		break;
+	case mobjinfo_spawnhealth:
 		lua_pushinteger(L, info->spawnhealth);
-	else if (fastcmp(field,"seestate"))
+		break;
+	case mobjinfo_seestate:
 		lua_pushinteger(L, info->seestate);
-	else if (fastcmp(field,"seesound"))
+		break;
+	case mobjinfo_seesound:
 		lua_pushinteger(L, info->seesound);
-	else if (fastcmp(field,"reactiontime"))
+		break;
+	case mobjinfo_reactiontime:
 		lua_pushinteger(L, info->reactiontime);
-	else if (fastcmp(field,"attacksound"))
+		break;
+	case mobjinfo_attacksound:
 		lua_pushinteger(L, info->attacksound);
-	else if (fastcmp(field,"painstate"))
+		break;
+	case mobjinfo_painstate:
 		lua_pushinteger(L, info->painstate);
-	else if (fastcmp(field,"painchance"))
+		break;
+	case mobjinfo_painchance:
 		lua_pushinteger(L, info->painchance);
-	else if (fastcmp(field,"painsound"))
+		break;
+	case mobjinfo_painsound:
 		lua_pushinteger(L, info->painsound);
-	else if (fastcmp(field,"meleestate"))
+		break;
+	case mobjinfo_meleestate:
 		lua_pushinteger(L, info->meleestate);
-	else if (fastcmp(field,"missilestate"))
+		break;
+	case mobjinfo_missilestate:
 		lua_pushinteger(L, info->missilestate);
-	else if (fastcmp(field,"deathstate"))
+		break;
+	case mobjinfo_deathstate:
 		lua_pushinteger(L, info->deathstate);
-	else if (fastcmp(field,"xdeathstate"))
+		break;
+	case mobjinfo_xdeathstate:
 		lua_pushinteger(L, info->xdeathstate);
-	else if (fastcmp(field,"deathsound"))
+		break;
+	case mobjinfo_deathsound:
 		lua_pushinteger(L, info->deathsound);
-	else if (fastcmp(field,"speed"))
+		break;
+	case mobjinfo_speed:
 		lua_pushinteger(L, info->speed); // sometimes it's fixed_t, sometimes it's not...
-	else if (fastcmp(field,"radius"))
+		break;
+	case mobjinfo_radius:
 		lua_pushfixed(L, info->radius);
-	else if (fastcmp(field,"height"))
+		break;
+	case mobjinfo_height:
 		lua_pushfixed(L, info->height);
-	else if (fastcmp(field,"dispoffset"))
+		break;
+	case mobjinfo_dispoffset:
 		lua_pushinteger(L, info->dispoffset);
-	else if (fastcmp(field,"mass"))
+		break;
+	case mobjinfo_mass:
 		lua_pushinteger(L, info->mass);
-	else if (fastcmp(field,"damage"))
+		break;
+	case mobjinfo_damage:
 		lua_pushinteger(L, info->damage);
-	else if (fastcmp(field,"activesound"))
+		break;
+	case mobjinfo_activesound:
 		lua_pushinteger(L, info->activesound);
-	else if (fastcmp(field,"flags"))
+		break;
+	case mobjinfo_flags:
 		lua_pushinteger(L, info->flags);
-	else if (fastcmp(field,"raisestate"))
+		break;
+	case mobjinfo_raisestate:
 		lua_pushinteger(L, info->raisestate);
-	else {
+		break;
+	default:
 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
 		I_Assert(lua_istable(L, -1));
 		lua_pushlightuserdata(L, info);
 		lua_rawget(L, -2);
 		if (!lua_istable(L, -1)) { // no extra values table
-			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", field);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", lua_tostring(L, 2));
 			return 0;
 		}
-		lua_getfield(L, -1, field);
+		lua_pushvalue(L, 2); // field name
+		lua_gettable(L, -2);
 		if (lua_isnil(L, -1)) // no value for this field
-			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", field);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", lua_tostring(L, 2));
+		break;
 	}
 	return 1;
 }
@@ -1183,7 +1269,7 @@ static int mobjinfo_get(lua_State *L)
 static int mobjinfo_set(lua_State *L)
 {
 	mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
-	const char *field = luaL_checkstring(L, 2);
+	enum mobjinfo_e field = Lua_optoption(L, 2, -1, mobjinfo_fields_ref);
 
 	if (hud_running)
 		return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!");
@@ -1193,55 +1279,81 @@ static int mobjinfo_set(lua_State *L)
 	I_Assert(info != NULL);
 	I_Assert(info >= mobjinfo);
 
-	if (fastcmp(field,"doomednum"))
+	switch (field)
+	{
+	case mobjinfo_doomednum:
 		info->doomednum = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"spawnstate"))
+		break;
+	case mobjinfo_spawnstate:
 		info->spawnstate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"spawnhealth"))
+		break;
+	case mobjinfo_spawnhealth:
 		info->spawnhealth = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"seestate"))
+		break;
+	case mobjinfo_seestate:
 		info->seestate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"seesound"))
+		break;
+	case mobjinfo_seesound:
 		info->seesound = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"reactiontime"))
+		break;
+	case mobjinfo_reactiontime:
 		info->reactiontime = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"attacksound"))
+		break;
+	case mobjinfo_attacksound:
 		info->attacksound = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"painstate"))
+		break;
+	case mobjinfo_painstate:
 		info->painstate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"painchance"))
+		break;
+	case mobjinfo_painchance:
 		info->painchance = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"painsound"))
+		break;
+	case mobjinfo_painsound:
 		info->painsound = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"meleestate"))
+		break;
+	case mobjinfo_meleestate:
 		info->meleestate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"missilestate"))
+		break;
+	case mobjinfo_missilestate:
 		info->missilestate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"deathstate"))
+		break;
+	case mobjinfo_deathstate:
 		info->deathstate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"xdeathstate"))
+		break;
+	case mobjinfo_xdeathstate:
 		info->xdeathstate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"deathsound"))
+		break;
+	case mobjinfo_deathsound:
 		info->deathsound = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"speed"))
+		break;
+	case mobjinfo_speed:
 		info->speed = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"radius"))
+		break;
+	case mobjinfo_radius:
 		info->radius = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"height"))
+		break;
+	case mobjinfo_height:
 		info->height = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"dispoffset"))
+		break;
+	case mobjinfo_dispoffset:
 		info->dispoffset = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"mass"))
+		break;
+	case mobjinfo_mass:
 		info->mass = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"damage"))
+		break;
+	case mobjinfo_damage:
 		info->damage = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"activesound"))
+		break;
+	case mobjinfo_activesound:
 		info->activesound = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"flags"))
+		break;
+	case mobjinfo_flags:
 		info->flags = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"raisestate"))
+		break;
+	case mobjinfo_raisestate:
 		info->raisestate = luaL_checkinteger(L, 3);
-	else {
+		break;
+	default:
 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
 		I_Assert(lua_istable(L, -1));
 		lua_pushlightuserdata(L, info);
@@ -1249,18 +1361,17 @@ static int mobjinfo_set(lua_State *L)
 		if (lua_isnil(L, -1)) {
 			// This index doesn't have a table for extra values yet, let's make one.
 			lua_pop(L, 1);
-			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "mobjinfo_t", field);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "mobjinfo_t", lua_tostring(L, 2));
 			lua_newtable(L);
 			lua_pushlightuserdata(L, info);
 			lua_pushvalue(L, -2); // ext value table
 			lua_rawset(L, -4); // LREG_EXTVARS table
 		}
+		lua_pushvalue(L, 2); // key
 		lua_pushvalue(L, 3); // value to store
-		lua_setfield(L, -2, field);
+		lua_settable(L, -3);
 		lua_pop(L, 2);
 	}
-	//else
-		//return luaL_error(L, LUA_QL("mobjinfo_t") " has no field named " LUA_QS, field);
 	return 0;
 }
 
@@ -1788,6 +1899,8 @@ int LUA_InfoLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	mobjinfo_fields_ref = Lua_CreateFieldTable(L, mobjinfo_opt);
+
 	luaL_newmetatable(L, META_SKINCOLOR);
 		lua_pushcfunction(L, skincolor_get);
 		lua_setfield(L, -2, "__index");
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 898651520d..dc477c81f5 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -88,6 +88,8 @@ static const char *const sector_opt[] = {
 	"gravity",
 	NULL};
 
+static int sector_fields_ref = LUA_NOREF;
+
 enum subsector_e {
 	subsector_valid = 0,
 	subsector_sector,
@@ -104,6 +106,8 @@ static const char *const subsector_opt[] = {
 	"polyList",
 	NULL};
 
+static int subsector_fields_ref = LUA_NOREF;
+
 enum line_e {
 	line_valid = 0,
 	line_v1,
@@ -156,6 +160,8 @@ static const char *const line_opt[] = {
 	"callcount",
 	NULL};
 
+static int line_fields_ref = LUA_NOREF;
+
 enum side_e {
 	side_valid = 0,
 	side_textureoffset,
@@ -184,6 +190,8 @@ static const char *const side_opt[] = {
 	"text",
 	NULL};
 
+static int side_fields_ref = LUA_NOREF;
+
 enum vertex_e {
 	vertex_valid = 0,
 	vertex_x,
@@ -204,6 +212,8 @@ static const char *const vertex_opt[] = {
 	"ceilingzset",
 	NULL};
 
+static int vertex_fields_ref = LUA_NOREF;
+
 enum ffloor_e {
 	ffloor_valid = 0,
 	ffloor_topheight,
@@ -256,6 +266,8 @@ static const char *const ffloor_opt[] = {
 	"bouncestrength",
 	NULL};
 
+static int ffloor_fields_ref = LUA_NOREF;
+
 #ifdef HAVE_LUA_SEGS
 enum seg_e {
 	seg_valid = 0,
@@ -285,6 +297,8 @@ static const char *const seg_opt[] = {
 	"polyseg",
 	NULL};
 
+static int seg_fields_ref = LUA_NOREF;
+
 enum node_e {
 	node_valid = 0,
 	node_x,
@@ -305,6 +319,8 @@ static const char *const node_opt[] = {
 	"children",
 	NULL};
 
+static int node_fields_ref = LUA_NOREF;
+
 enum nodechild_e {
 	nodechild_valid = 0,
 	nodechild_right,
@@ -356,6 +372,8 @@ static const char *const slope_opt[] = {
 	"flags",
 	NULL};
 
+static int slope_fields_ref = LUA_NOREF;
+
 // shared by both vector2_t and vector3_t
 enum vector_e {
 	vector_x = 0,
@@ -575,7 +593,7 @@ static int sectorlines_num(lua_State *L)
 static int sector_get(lua_State *L)
 {
 	sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
-	enum sector_e field = luaL_checkoption(L, 2, sector_opt[0], sector_opt);
+	enum sector_e field = Lua_optoption(L, 2, sector_valid, sector_fields_ref);
 	INT16 i;
 
 	if (!sector)
@@ -697,7 +715,7 @@ static int sector_get(lua_State *L)
 static int sector_set(lua_State *L)
 {
 	sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
-	enum sector_e field = luaL_checkoption(L, 2, sector_opt[0], sector_opt);
+	enum sector_e field = Lua_optoption(L, 2, sector_valid, sector_fields_ref);
 
 	if (!sector)
 		return luaL_error(L, "accessed sector_t doesn't exist anymore.");
@@ -814,7 +832,7 @@ static int sector_num(lua_State *L)
 static int subsector_get(lua_State *L)
 {
 	subsector_t *subsector = *((subsector_t **)luaL_checkudata(L, 1, META_SUBSECTOR));
-	enum subsector_e field = luaL_checkoption(L, 2, subsector_opt[0], subsector_opt);
+	enum subsector_e field = Lua_optoption(L, 2, subsector_valid, subsector_fields_ref);
 
 	if (!subsector)
 	{
@@ -898,7 +916,7 @@ static int linestringargs_len(lua_State *L)
 static int line_get(lua_State *L)
 {
 	line_t *line = *((line_t **)luaL_checkudata(L, 1, META_LINE));
-	enum line_e field = luaL_checkoption(L, 2, line_opt[0], line_opt);
+	enum line_e field = Lua_optoption(L, 2, line_valid, line_fields_ref);
 
 	if (!line)
 	{
@@ -1057,7 +1075,7 @@ static int sidenum_get(lua_State *L)
 static int side_get(lua_State *L)
 {
 	side_t *side = *((side_t **)luaL_checkudata(L, 1, META_SIDE));
-	enum side_e field = luaL_checkoption(L, 2, side_opt[0], side_opt);
+	enum side_e field = Lua_optoption(L, 2, side_valid, side_fields_ref);
 
 	if (!side)
 	{
@@ -1110,7 +1128,7 @@ static int side_get(lua_State *L)
 static int side_set(lua_State *L)
 {
 	side_t *side = *((side_t **)luaL_checkudata(L, 1, META_SIDE));
-	enum side_e field = luaL_checkoption(L, 2, side_opt[0], side_opt);
+	enum side_e field = Lua_optoption(L, 2, side_valid, side_fields_ref);
 
 	if (!side)
 	{
@@ -1166,7 +1184,7 @@ static int side_num(lua_State *L)
 static int vertex_get(lua_State *L)
 {
 	vertex_t *vertex = *((vertex_t **)luaL_checkudata(L, 1, META_VERTEX));
-	enum vertex_e field = luaL_checkoption(L, 2, vertex_opt[0], vertex_opt);
+	enum vertex_e field = Lua_optoption(L, 2, vertex_valid, vertex_fields_ref);
 
 	if (!vertex)
 	{
@@ -1220,7 +1238,7 @@ static int vertex_num(lua_State *L)
 static int seg_get(lua_State *L)
 {
 	seg_t *seg = *((seg_t **)luaL_checkudata(L, 1, META_SEG));
-	enum seg_e field = luaL_checkoption(L, 2, seg_opt[0], seg_opt);
+	enum seg_e field = Lua_optoption(L, 2, seg_valid, seg_fields_ref);
 
 	if (!seg)
 	{
@@ -1284,7 +1302,7 @@ static int seg_num(lua_State *L)
 static int node_get(lua_State *L)
 {
 	node_t *node = *((node_t **)luaL_checkudata(L, 1, META_NODE));
-	enum node_e field = luaL_checkoption(L, 2, node_opt[0], node_opt);
+	enum node_e field = Lua_optoption(L, 2, node_valid, node_fields_ref);
 
 	if (!node)
 	{
@@ -1920,7 +1938,7 @@ static INT32 P_GetOldFOFFlags(ffloor_t *fflr)
 static int ffloor_get(lua_State *L)
 {
 	ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
-	enum ffloor_e field = luaL_checkoption(L, 2, ffloor_opt[0], ffloor_opt);
+	enum ffloor_e field = Lua_optoption(L, 2, ffloor_valid, ffloor_fields_ref);
 	INT16 i;
 
 	if (!ffloor)
@@ -2102,7 +2120,7 @@ static void P_SetOldFOFFlags(ffloor_t *fflr, oldffloortype_e oldflags)
 static int ffloor_set(lua_State *L)
 {
 	ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
-	enum ffloor_e field = luaL_checkoption(L, 2, ffloor_opt[0], ffloor_opt);
+	enum ffloor_e field = Lua_optoption(L, 2, ffloor_valid, ffloor_fields_ref);
 
 	if (!ffloor)
 		return luaL_error(L, "accessed ffloor_t doesn't exist anymore.");
@@ -2197,7 +2215,7 @@ static int ffloor_set(lua_State *L)
 static int slope_get(lua_State *L)
 {
 	pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE));
-	enum slope_e field = luaL_checkoption(L, 2, slope_opt[0], slope_opt);
+	enum slope_e field = Lua_optoption(L, 2, slope_valid, slope_fields_ref);
 
 	if (!slope)
 	{
@@ -2241,7 +2259,7 @@ static int slope_get(lua_State *L)
 static int slope_set(lua_State *L)
 {
 	pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE));
-	enum slope_e field = luaL_checkoption(L, 2, slope_opt[0], slope_opt);
+	enum slope_e field = Lua_optoption(L, 2, slope_valid, slope_fields_ref);
 
 	if (!slope)
 		return luaL_error(L, "accessed pslope_t doesn't exist anymore.");
@@ -2405,110 +2423,258 @@ static int lib_nummapheaders(lua_State *L)
 // mapheader_t //
 /////////////////
 
+enum mapheaderinfo_e
+{
+	mapheaderinfo_lvlttl,
+	mapheaderinfo_subttl,
+	mapheaderinfo_actnum,
+	mapheaderinfo_typeoflevel,
+	mapheaderinfo_nextlevel,
+	mapheaderinfo_marathonnext,
+	mapheaderinfo_keywords,
+	mapheaderinfo_musname,
+	mapheaderinfo_mustrack,
+	mapheaderinfo_muspos,
+	mapheaderinfo_musinterfadeout,
+	mapheaderinfo_musintername,
+	mapheaderinfo_muspostbossname,
+	mapheaderinfo_muspostbosstrack,
+	mapheaderinfo_muspostbosspos,
+	mapheaderinfo_muspostbossfadein,
+	mapheaderinfo_musforcereset,
+	mapheaderinfo_forcecharacter,
+	mapheaderinfo_weather,
+	mapheaderinfo_skynum,
+	mapheaderinfo_skybox_scalex,
+	mapheaderinfo_skybox_scaley,
+	mapheaderinfo_skybox_scalez,
+	mapheaderinfo_interscreen,
+	mapheaderinfo_runsoc,
+	mapheaderinfo_scriptname,
+	mapheaderinfo_precutscenenum,
+	mapheaderinfo_cutscenenum,
+	mapheaderinfo_countdown,
+	mapheaderinfo_palette,
+	mapheaderinfo_numlaps,
+	mapheaderinfo_unlockrequired,
+	mapheaderinfo_levelselect,
+	mapheaderinfo_bonustype,
+	mapheaderinfo_ltzzpatch,
+	mapheaderinfo_ltzztext,
+	mapheaderinfo_ltactdiamond,
+	mapheaderinfo_maxbonuslives,
+	mapheaderinfo_levelflags,
+	mapheaderinfo_menuflags,
+	mapheaderinfo_selectheading,
+	mapheaderinfo_startrings,
+	mapheaderinfo_sstimer,
+	mapheaderinfo_ssspheres,
+	mapheaderinfo_gravity,
+};
+
+static const char *const mapheaderinfo_opt[] = {
+	"lvlttl",
+	"subttl",
+	"actnum",
+	"typeoflevel",
+	"nextlevel",
+	"marathonnext",
+	"keywords",
+	"musname",
+	"mustrack",
+	"muspos",
+	"musinterfadeout",
+	"musintername",
+	"muspostbossname",
+	"muspostbosstrack",
+	"muspostbosspos",
+	"muspostbossfadein",
+	"musforcereset",
+	"forcecharacter",
+	"weather",
+	"skynum",
+	"skybox_scalex",
+	"skybox_scaley",
+	"skybox_scalez",
+	"interscreen",
+	"runsoc",
+	"scriptname",
+	"precutscenenum",
+	"cutscenenum",
+	"countdown",
+	"palette",
+	"numlaps",
+	"unlockrequired",
+	"levelselect",
+	"bonustype",
+	"ltzzpatch",
+	"ltzztext",
+	"ltactdiamond",
+	"maxbonuslives",
+	"levelflags",
+	"menuflags",
+	"selectheading",
+	"startrings",
+	"sstimer",
+	"ssspheres",
+	"gravity",
+	NULL,
+};
+
+static int mapheaderinfo_fields_ref = LUA_NOREF;
+
 static int mapheaderinfo_get(lua_State *L)
 {
 	mapheader_t *header = *((mapheader_t **)luaL_checkudata(L, 1, META_MAPHEADER));
-	const char *field = luaL_checkstring(L, 2);
+	enum mapheaderinfo_e field = Lua_optoption(L, 2, -1, mapheaderinfo_fields_ref);
 	INT16 i;
-	if (fastcmp(field,"lvlttl"))
+
+	switch (field)
+	{
+	case mapheaderinfo_lvlttl:
 		lua_pushstring(L, header->lvlttl);
-	else if (fastcmp(field,"subttl"))
+		break;
+	case mapheaderinfo_subttl:
 		lua_pushstring(L, header->subttl);
-	else if (fastcmp(field,"actnum"))
+		break;
+	case mapheaderinfo_actnum:
 		lua_pushinteger(L, header->actnum);
-	else if (fastcmp(field,"typeoflevel"))
+		break;
+	case mapheaderinfo_typeoflevel:
 		lua_pushinteger(L, header->typeoflevel);
-	else if (fastcmp(field,"nextlevel"))
+		break;
+	case mapheaderinfo_nextlevel:
 		lua_pushinteger(L, header->nextlevel);
-	else if (fastcmp(field,"marathonnext"))
+		break;
+	case mapheaderinfo_marathonnext:
 		lua_pushinteger(L, header->marathonnext);
-	else if (fastcmp(field,"keywords"))
+		break;
+	case mapheaderinfo_keywords:
 		lua_pushstring(L, header->keywords);
-	else if (fastcmp(field,"musname"))
+		break;
+	case mapheaderinfo_musname:
 		lua_pushstring(L, header->musname);
-	else if (fastcmp(field,"mustrack"))
+		break;
+	case mapheaderinfo_mustrack:
 		lua_pushinteger(L, header->mustrack);
-	else if (fastcmp(field,"muspos"))
+		break;
+	case mapheaderinfo_muspos:
 		lua_pushinteger(L, header->muspos);
-	else if (fastcmp(field,"musinterfadeout"))
+		break;
+	case mapheaderinfo_musinterfadeout:
 		lua_pushinteger(L, header->musinterfadeout);
-	else if (fastcmp(field,"musintername"))
+		break;
+	case mapheaderinfo_musintername:
 		lua_pushstring(L, header->musintername);
-	else if (fastcmp(field,"muspostbossname"))
+		break;
+	case mapheaderinfo_muspostbossname:
 		lua_pushstring(L, header->muspostbossname);
-	else if (fastcmp(field,"muspostbosstrack"))
+		break;
+	case mapheaderinfo_muspostbosstrack:
 		lua_pushinteger(L, header->muspostbosstrack);
-	else if (fastcmp(field,"muspostbosspos"))
+		break;
+	case mapheaderinfo_muspostbosspos:
 		lua_pushinteger(L, header->muspostbosspos);
-	else if (fastcmp(field,"muspostbossfadein"))
+		break;
+	case mapheaderinfo_muspostbossfadein:
 		lua_pushinteger(L, header->muspostbossfadein);
-	else if (fastcmp(field,"musforcereset"))
+		break;
+	case mapheaderinfo_musforcereset:
 		lua_pushinteger(L, header->musforcereset);
-	else if (fastcmp(field,"forcecharacter"))
+		break;
+	case mapheaderinfo_forcecharacter:
 		lua_pushstring(L, header->forcecharacter);
-	else if (fastcmp(field,"weather"))
+		break;
+	case mapheaderinfo_weather:
 		lua_pushinteger(L, header->weather);
-	else if (fastcmp(field,"skynum"))
+		break;
+	case mapheaderinfo_skynum:
 		lua_pushinteger(L, header->skynum);
-	else if (fastcmp(field,"skybox_scalex"))
+		break;
+	case mapheaderinfo_skybox_scalex:
 		lua_pushinteger(L, header->skybox_scalex);
-	else if (fastcmp(field,"skybox_scaley"))
+		break;
+	case mapheaderinfo_skybox_scaley:
 		lua_pushinteger(L, header->skybox_scaley);
-	else if (fastcmp(field,"skybox_scalez"))
+		break;
+	case mapheaderinfo_skybox_scalez:
 		lua_pushinteger(L, header->skybox_scalez);
-	else if (fastcmp(field,"interscreen")) {
+		break;
+	case mapheaderinfo_interscreen:
 		for (i = 0; i < 8; i++)
 			if (!header->interscreen[i])
 				break;
 		lua_pushlstring(L, header->interscreen, i);
-	} else if (fastcmp(field,"runsoc"))
+		break;
+	case mapheaderinfo_runsoc:
 		lua_pushstring(L, header->runsoc);
-	else if (fastcmp(field,"scriptname"))
+		break;
+	case mapheaderinfo_scriptname:
 		lua_pushstring(L, header->scriptname);
-	else if (fastcmp(field,"precutscenenum"))
+		break;
+	case mapheaderinfo_precutscenenum:
 		lua_pushinteger(L, header->precutscenenum);
-	else if (fastcmp(field,"cutscenenum"))
+		break;
+	case mapheaderinfo_cutscenenum:
 		lua_pushinteger(L, header->cutscenenum);
-	else if (fastcmp(field,"countdown"))
+		break;
+	case mapheaderinfo_countdown:
 		lua_pushinteger(L, header->countdown);
-	else if (fastcmp(field,"palette"))
+		break;
+	case mapheaderinfo_palette:
 		lua_pushinteger(L, header->palette);
-	else if (fastcmp(field,"numlaps"))
+		break;
+	case mapheaderinfo_numlaps:
 		lua_pushinteger(L, header->numlaps);
-	else if (fastcmp(field,"unlockrequired"))
+		break;
+	case mapheaderinfo_unlockrequired:
 		lua_pushinteger(L, header->unlockrequired);
-	else if (fastcmp(field,"levelselect"))
+		break;
+	case mapheaderinfo_levelselect:
 		lua_pushinteger(L, header->levelselect);
-	else if (fastcmp(field,"bonustype"))
+		break;
+	case mapheaderinfo_bonustype:
 		lua_pushinteger(L, header->bonustype);
-	else if (fastcmp(field,"ltzzpatch"))
+		break;
+	case mapheaderinfo_ltzzpatch:
 		lua_pushstring(L, header->ltzzpatch);
-	else if (fastcmp(field,"ltzztext"))
+		break;
+	case mapheaderinfo_ltzztext:
 		lua_pushstring(L, header->ltzztext);
-	else if (fastcmp(field,"ltactdiamond"))
+		break;
+	case mapheaderinfo_ltactdiamond:
 		lua_pushstring(L, header->ltactdiamond);
-	else if (fastcmp(field,"maxbonuslives"))
+		break;
+	case mapheaderinfo_maxbonuslives:
 		lua_pushinteger(L, header->maxbonuslives);
-	else if (fastcmp(field,"levelflags"))
+		break;
+	case mapheaderinfo_levelflags:
 		lua_pushinteger(L, header->levelflags);
-	else if (fastcmp(field,"menuflags"))
+		break;
+	case mapheaderinfo_menuflags:
 		lua_pushinteger(L, header->menuflags);
-	else if (fastcmp(field,"selectheading"))
+		break;
+	case mapheaderinfo_selectheading:
 		lua_pushstring(L, header->selectheading);
-	else if (fastcmp(field,"startrings"))
+		break;
+	case mapheaderinfo_startrings:
 		lua_pushinteger(L, header->startrings);
-	else if (fastcmp(field, "sstimer"))
+		break;
+	case mapheaderinfo_sstimer:
 		lua_pushinteger(L, header->sstimer);
-	else if (fastcmp(field, "ssspheres"))
+		break;
+	case mapheaderinfo_ssspheres:
 		lua_pushinteger(L, header->ssspheres);
-	else if (fastcmp(field, "gravity"))
+		break;
+	case mapheaderinfo_gravity:
 		lua_pushfixed(L, header->gravity);
+		break;
 	// TODO add support for reading numGradedMares and grades
-	else {
+	default:
 		// Read custom vars now
 		// (note: don't include the "LUA." in your lua scripts!)
 		UINT8 j = 0;
-		for (;j < header->numCustomOptions && !fastcmp(field, header->customopts[j].option); ++j);
+		for (;j < header->numCustomOptions && !fastcmp(lua_tostring(L, 2), header->customopts[j].option); ++j);
 
 		if(j < header->numCustomOptions)
 			lua_pushstring(L, header->customopts[j].value);
@@ -2539,6 +2705,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	sector_fields_ref = Lua_CreateFieldTable(L, sector_opt);
+
 	luaL_newmetatable(L, META_SUBSECTOR);
 		lua_pushcfunction(L, subsector_get);
 		lua_setfield(L, -2, "__index");
@@ -2547,6 +2715,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	subsector_fields_ref = Lua_CreateFieldTable(L, subsector_opt);
+
 	luaL_newmetatable(L, META_LINE);
 		lua_pushcfunction(L, line_get);
 		lua_setfield(L, -2, "__index");
@@ -2555,6 +2725,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	line_fields_ref = Lua_CreateFieldTable(L, line_opt);
+
 	luaL_newmetatable(L, META_LINEARGS);
 		lua_pushcfunction(L, lineargs_get);
 		lua_setfield(L, -2, "__index");
@@ -2587,6 +2759,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	side_fields_ref = Lua_CreateFieldTable(L, side_opt);
+
 	luaL_newmetatable(L, META_VERTEX);
 		lua_pushcfunction(L, vertex_get);
 		lua_setfield(L, -2, "__index");
@@ -2595,6 +2769,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	vertex_fields_ref = Lua_CreateFieldTable(L, vertex_opt);
+
 	luaL_newmetatable(L, META_FFLOOR);
 		lua_pushcfunction(L, ffloor_get);
 		lua_setfield(L, -2, "__index");
@@ -2603,6 +2779,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L, 1);
 
+	ffloor_fields_ref = Lua_CreateFieldTable(L, ffloor_opt);
+
 #ifdef HAVE_LUA_SEGS
 	luaL_newmetatable(L, META_SEG);
 		lua_pushcfunction(L, seg_get);
@@ -2612,6 +2790,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	seg_fields_ref = Lua_CreateFieldTable(L, seg_opt);
+
 	luaL_newmetatable(L, META_NODE);
 		lua_pushcfunction(L, node_get);
 		lua_setfield(L, -2, "__index");
@@ -2620,6 +2800,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	node_fields_ref = Lua_CreateFieldTable(L, node_opt);
+
 	luaL_newmetatable(L, META_NODEBBOX);
 		//lua_pushcfunction(L, nodebbox_get);
 		//lua_setfield(L, -2, "__index");
@@ -2646,6 +2828,8 @@ int LUA_MapLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L, 1);
 
+	slope_fields_ref = Lua_CreateFieldTable(L, slope_opt);
+
 	luaL_newmetatable(L, META_VECTOR2);
 		lua_pushcfunction(L, vector2_get);
 		lua_setfield(L, -2, "__index");
@@ -2664,6 +2848,8 @@ int LUA_MapLib(lua_State *L)
 		//lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	mapheaderinfo_fields_ref = Lua_CreateFieldTable(L, mapheaderinfo_opt);
+
 	LUA_PushTaggableObjectArray(L, "sectors",
 			lib_iterateSectors,
 			lib_getSector,
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 5c2b8b4d3c..09d244c910 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -179,10 +179,12 @@ static const char *const mobj_opt[] = {
 
 #define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field])
 
+static int mobj_fields_ref = LUA_NOREF;
+
 static int mobj_get(lua_State *L)
 {
 	mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
-	enum mobj_e field = Lua_optoption(L, 2, NULL, mobj_opt);
+	enum mobj_e field = Lua_optoption(L, 2, -1, mobj_fields_ref);
 	lua_settop(L, 2);
 
 	if (!mo || !ISINLEVEL) {
@@ -467,7 +469,7 @@ static int mobj_get(lua_State *L)
 static int mobj_set(lua_State *L)
 {
 	mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
-	enum mobj_e field = Lua_optoption(L, 2, mobj_opt[0], mobj_opt);
+	enum mobj_e field = Lua_optoption(L, 2, mobj_valid, mobj_fields_ref);
 	lua_settop(L, 3);
 
 	INLEVEL
@@ -876,14 +878,55 @@ static int thingstringargs_len(lua_State *L)
 	return 1;
 }
 
+enum mapthing_e {
+	mapthing_valid = 0,
+	mapthing_x,
+	mapthing_y,
+	mapthing_angle,
+	mapthing_pitch,
+	mapthing_roll,
+	mapthing_type,
+	mapthing_options,
+	mapthing_scale,
+	mapthing_z,
+	mapthing_extrainfo,
+	mapthing_tag,
+	mapthing_taglist,
+	mapthing_args,
+	mapthing_stringargs,
+	mapthing_mobj,
+};
+
+const char *const mapthing_opt[] = {
+	"valid",
+	"x",
+	"y",
+	"angle",
+	"pitch",
+	"roll",
+	"type",
+	"options",
+	"scale",
+	"z",
+	"extrainfo",
+	"tag",
+	"taglist",
+	"args",
+	"stringargs",
+	"mobj",
+	NULL,
+};
+
+static int mapthing_fields_ref = LUA_NOREF;
+
 static int mapthing_get(lua_State *L)
 {
 	mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
-	const char *field = luaL_checkstring(L, 2);
-	lua_Integer number;
+	enum mapthing_e field = Lua_optoption(L, 2, -1, mapthing_fields_ref);
+	lua_settop(L, 2);
 
 	if (!mt) {
-		if (fastcmp(field,"valid")) {
+		if (field == mapthing_valid) {
 			lua_pushboolean(L, false);
 			return 1;
 		}
@@ -892,62 +935,71 @@ static int mapthing_get(lua_State *L)
 		return 0;
 	}
 
-	if (fastcmp(field,"valid")) {
-		lua_pushboolean(L, true);
-		return 1;
-	} else if(fastcmp(field,"x"))
-		number = mt->x;
-	else if(fastcmp(field,"y"))
-		number = mt->y;
-	else if(fastcmp(field,"angle"))
-		number = mt->angle;
-	else if(fastcmp(field,"pitch"))
-		number = mt->pitch;
-	else if(fastcmp(field,"roll"))
-		number = mt->roll;
-	else if(fastcmp(field,"type"))
-		number = mt->type;
-	else if(fastcmp(field,"options"))
-		number = mt->options;
-	else if(fastcmp(field,"scale"))
-		number = mt->scale;
-	else if(fastcmp(field,"z"))
-		number = mt->z;
-	else if(fastcmp(field,"extrainfo"))
-		number = mt->extrainfo;
-	else if(fastcmp(field,"tag"))
-		number = Tag_FGet(&mt->tags);
-	else if(fastcmp(field,"taglist"))
-	{
-		LUA_PushUserdata(L, &mt->tags, META_TAGLIST);
-		return 1;
-	}
-	else if(fastcmp(field,"args"))
-	{
-		LUA_PushUserdata(L, mt->args, META_THINGARGS);
-		return 1;
-	}
-	else if(fastcmp(field,"stringargs"))
+	switch (field)
 	{
-		LUA_PushUserdata(L, mt->stringargs, META_THINGSTRINGARGS);
-		return 1;
+		case mapthing_valid:
+			lua_pushboolean(L, true);
+			break;
+		case mapthing_x:
+			lua_pushinteger(L, mt->x);
+			break;
+		case mapthing_y:
+			lua_pushinteger(L, mt->y);
+			break;
+		case mapthing_angle:
+			lua_pushinteger(L, mt->angle);
+			break;
+		case mapthing_pitch:
+			lua_pushinteger(L, mt->pitch);
+			break;
+		case mapthing_roll:
+			lua_pushinteger(L, mt->roll);
+			break;
+		case mapthing_type:
+			lua_pushinteger(L, mt->type);
+			break;
+		case mapthing_options:
+			lua_pushinteger(L, mt->options);
+			break;
+		case mapthing_scale:
+			lua_pushinteger(L, mt->scale);
+			break;
+		case mapthing_z:
+			lua_pushinteger(L, mt->z);
+			break;
+		case mapthing_extrainfo:
+			lua_pushinteger(L, mt->extrainfo);
+			break;
+		case mapthing_tag:
+			lua_pushinteger(L, Tag_FGet(&mt->tags));
+			break;
+		case mapthing_taglist:
+			LUA_PushUserdata(L, &mt->tags, META_TAGLIST);
+			break;
+		case mapthing_args:
+			LUA_PushUserdata(L, mt->args, META_THINGARGS);
+			break;
+		case mapthing_stringargs:
+			LUA_PushUserdata(L, mt->stringargs, META_THINGSTRINGARGS);
+			break;
+		case mapthing_mobj:
+			LUA_PushUserdata(L, mt->mobj, META_MOBJ);
+			break;
+		default:
+			if (devparm)
+				return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
+			else
+				return 0;
 	}
-	else if(fastcmp(field,"mobj")) {
-		LUA_PushUserdata(L, mt->mobj, META_MOBJ);
-		return 1;
-	} else if (devparm)
-		return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
-	else
-		return 0;
 
-	lua_pushinteger(L, number);
 	return 1;
 }
 
 static int mapthing_set(lua_State *L)
 {
 	mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
-	const char *field = luaL_checkstring(L, 2);
+	enum mapthing_e field = Lua_optoption(L, 2, -1, mapthing_fields_ref);
+	lua_settop(L, 3);
 
 	if (!mt)
 		return luaL_error(L, "accessed mapthing_t doesn't exist anymore.");
@@ -957,39 +1009,52 @@ static int mapthing_set(lua_State *L)
 	if (hook_cmd_running)
 		return luaL_error(L, "Do not alter mapthing_t in CMD building code!");
 
-	if(fastcmp(field,"x"))
-		mt->x = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"y"))
-		mt->y = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"angle"))
-		mt->angle = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"pitch"))
-		mt->pitch = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"roll"))
-		mt->roll = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"type"))
-		mt->type = (UINT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"options"))
-		mt->options = (UINT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"scale"))
-		mt->scale = luaL_checkfixed(L, 3);
-	else if(fastcmp(field,"z"))
-		mt->z = (INT16)luaL_checkinteger(L, 3);
-	else if(fastcmp(field,"extrainfo"))
+	switch (field)
 	{
-		INT32 extrainfo = luaL_checkinteger(L, 3);
-		if (extrainfo & ~15)
-			return luaL_error(L, "mapthing_t extrainfo set %d out of range (%d - %d)", extrainfo, 0, 15);
-		mt->extrainfo = (UINT8)extrainfo;
+		case mapthing_x:
+			mt->x = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_y:
+			mt->y = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_angle:
+			mt->angle = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_pitch:
+			mt->pitch = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_roll:
+			mt->roll = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_type:
+			mt->type = (UINT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_options:
+			mt->options = (UINT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_scale:
+			mt->scale = luaL_checkfixed(L, 3);
+			break;
+		case mapthing_z:
+			mt->z = (INT16)luaL_checkinteger(L, 3);
+			break;
+		case mapthing_extrainfo:
+			INT32 extrainfo = luaL_checkinteger(L, 3);
+			if (extrainfo & ~15)
+				return luaL_error(L, "mapthing_t extrainfo set %d out of range (%d - %d)", extrainfo, 0, 15);
+			mt->extrainfo = (UINT8)extrainfo;
+			break;
+		case mapthing_tag:
+			Tag_FSet(&mt->tags, (INT16)luaL_checkinteger(L, 3));
+			break;
+		case mapthing_taglist:
+			return LUA_ErrSetDirectly(L, "mapthing_t", "taglist");
+		case mapthing_mobj:
+			mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
+			break;
+		default:
+			return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
 	}
-	else if (fastcmp(field,"tag"))
-		Tag_FSet(&mt->tags, (INT16)luaL_checkinteger(L, 3));
-	else if (fastcmp(field,"taglist"))
-		return LUA_ErrSetDirectly(L, "mapthing_t", "taglist");
-	else if(fastcmp(field,"mobj"))
-		mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
-	else
-		return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
 
 	return 0;
 }
@@ -1051,6 +1116,8 @@ int LUA_MobjLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L,1);
 
+	mobj_fields_ref = Lua_CreateFieldTable(L, mobj_opt);
+
 	luaL_newmetatable(L, META_THINGARGS);
 		lua_pushcfunction(L, thingargs_get);
 		lua_setfield(L, -2, "__index");
@@ -1078,6 +1145,8 @@ int LUA_MobjLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L,1);
 
+	mapthing_fields_ref = Lua_CreateFieldTable(L, mapthing_opt);
+
 	LUA_PushTaggableObjectArray(L, "mapthings",
 			lib_iterateMapthings,
 			lib_getMapthing,
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index f7e14e78f9..1d06e081b1 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -80,322 +80,764 @@ static int lib_lenPlayer(lua_State *L)
 	return 1;
 }
 
+enum player_e
+{
+	player_valid,
+	player_name,
+	player_realmo,
+	player_mo,
+	player_cmd,
+	player_playerstate,
+	player_camerascale,
+	player_shieldscale,
+	player_viewz,
+	player_viewheight,
+	player_deltaviewheight,
+	player_bob,
+	player_viewrollangle,
+	player_aiming,
+	player_drawangle,
+	player_rings,
+	player_spheres,
+	player_pity,
+	player_currentweapon,
+	player_ringweapons,
+	player_ammoremoval,
+	player_ammoremovaltimer,
+	player_ammoremovalweapon,
+	player_powers,
+	player_pflags,
+	player_panim,
+	player_flashcount,
+	player_flashpal,
+	player_skincolor,
+	player_skin,
+	player_availabilities,
+	player_score,
+	player_dashspeed,
+	player_normalspeed,
+	player_runspeed,
+	player_thrustfactor,
+	player_accelstart,
+	player_acceleration,
+	player_charability,
+	player_charability2,
+	player_charflags,
+	player_thokitem,
+	player_spinitem,
+	player_revitem,
+	player_followitem,
+	player_followmobj,
+	player_actionspd,
+	player_mindash,
+	player_maxdash,
+	player_jumpfactor,
+	player_height,
+	player_spinheight,
+	player_lives,
+	player_continues,
+	player_xtralife,
+	player_gotcontinue,
+	player_speed,
+	player_secondjump,
+	player_fly1,
+	player_scoreadd,
+	player_glidetime,
+	player_climbing,
+	player_deadtimer,
+	player_exiting,
+	player_homing,
+	player_dashmode,
+	player_skidtime,
+	player_cmomx,
+	player_cmomy,
+	player_rmomx,
+	player_rmomy,
+	player_numboxes,
+	player_totalring,
+	player_realtime,
+	player_laps,
+	player_ctfteam,
+	player_gotflag,
+	player_weapondelay,
+	player_tossdelay,
+	player_starpostx,
+	player_starposty,
+	player_starpostz,
+	player_starpostnum,
+	player_starposttime,
+	player_starpostangle,
+	player_starpostscale,
+	player_angle_pos,
+	player_old_angle_pos,
+	player_axis1,
+	player_axis2,
+	player_bumpertime,
+	player_flyangle,
+	player_drilltimer,
+	player_linkcount,
+	player_linktimer,
+	player_anotherflyangle,
+	player_nightstime,
+	player_drillmeter,
+	player_drilldelay,
+	player_bonustime,
+	player_capsule,
+	player_drone,
+	player_oldscale,
+	player_mare,
+	player_marelap,
+	player_marebonuslap,
+	player_marebegunat,
+	player_startedtime,
+	player_finishedtime,
+	player_lapbegunat,
+	player_lapstartedtime,
+	player_finishedspheres,
+	player_finishedrings,
+	player_marescore,
+	player_lastmarescore,
+	player_totalmarescore,
+	player_lastmare,
+	player_lastmarelap,
+	player_lastmarebonuslap,
+	player_totalmarelap,
+	player_totalmarebonuslap,
+	player_maxlink,
+	player_texttimer,
+	player_textvar,
+	player_lastsidehit,
+	player_lastlinehit,
+	player_losstime,
+	player_timeshit,
+	player_onconveyor,
+	player_awayviewmobj,
+	player_awayviewtics,
+	player_awayviewaiming,
+	player_spectator,
+	player_outofcoop,
+	player_bot,
+	player_botleader,
+	player_lastbuttons,
+	player_blocked,
+	player_jointime,
+	player_quittime,
+#ifdef HWRENDER
+	player_fovadd,
+#endif
+};
+
+static const char *const player_opt[] = {
+	"valid",
+	"name",
+	"realmo",
+	"mo",
+	"cmd",
+	"playerstate",
+	"camerascale",
+	"shieldscale",
+	"viewz",
+	"viewheight",
+	"deltaviewheight",
+	"bob",
+	"viewrollangle",
+	"aiming",
+	"drawangle",
+	"rings",
+	"spheres",
+	"pity",
+	"currentweapon",
+	"ringweapons",
+	"ammoremoval",
+	"ammoremovaltimer",
+	"ammoremovalweapon",
+	"powers",
+	"pflags",
+	"panim",
+	"flashcount",
+	"flashpal",
+	"skincolor",
+	"skin",
+	"availabilities",
+	"score",
+	"dashspeed",
+	"normalspeed",
+	"runspeed",
+	"thrustfactor",
+	"accelstart",
+	"acceleration",
+	"charability",
+	"charability2",
+	"charflags",
+	"thokitem",
+	"spinitem",
+	"revitem",
+	"followitem",
+	"followmobj",
+	"actionspd",
+	"mindash",
+	"maxdash",
+	"jumpfactor",
+	"height",
+	"spinheight",
+	"lives",
+	"continues",
+	"xtralife",
+	"gotcontinue",
+	"speed",
+	"secondjump",
+	"fly1",
+	"scoreadd",
+	"glidetime",
+	"climbing",
+	"deadtimer",
+	"exiting",
+	"homing",
+	"dashmode",
+	"skidtime",
+	"cmomx",
+	"cmomy",
+	"rmomx",
+	"rmomy",
+	"numboxes",
+	"totalring",
+	"realtime",
+	"laps",
+	"ctfteam",
+	"gotflag",
+	"weapondelay",
+	"tossdelay",
+	"starpostx",
+	"starposty",
+	"starpostz",
+	"starpostnum",
+	"starposttime",
+	"starpostangle",
+	"starpostscale",
+	"angle_pos",
+	"old_angle_pos",
+	"axis1",
+	"axis2",
+	"bumpertime",
+	"flyangle",
+	"drilltimer",
+	"linkcount",
+	"linktimer",
+	"anotherflyangle",
+	"nightstime",
+	"drillmeter",
+	"drilldelay",
+	"bonustime",
+	"capsule",
+	"drone",
+	"oldscale",
+	"mare",
+	"marelap",
+	"marebonuslap",
+	"marebegunat",
+	"startedtime",
+	"finishedtime",
+	"lapbegunat",
+	"lapstartedtime",
+	"finishedspheres",
+	"finishedrings",
+	"marescore",
+	"lastmarescore",
+	"totalmarescore",
+	"lastmare",
+	"lastmarelap",
+	"lastmarebonuslap",
+	"totalmarelap",
+	"totalmarebonuslap",
+	"maxlink",
+	"texttimer",
+	"textvar",
+	"lastsidehit",
+	"lastlinehit",
+	"losstime",
+	"timeshit",
+	"onconveyor",
+	"awayviewmobj",
+	"awayviewtics",
+	"awayviewaiming",
+	"spectator",
+	"outofcoop",
+	"bot",
+	"botleader",
+	"lastbuttons",
+	"blocked",
+	"jointime",
+	"quittime",
+#ifdef HWRENDER
+	"fovadd",
+#endif
+	NULL,
+};
+
+static int player_fields_ref = LUA_NOREF;
+
 static int player_get(lua_State *L)
 {
 	player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
-	const char *field = luaL_checkstring(L, 2);
+	enum player_e field = Lua_optoption(L, 2, -1, player_fields_ref);
+	lua_settop(L, 2);
 
-	if (!plr) {
-		if (fastcmp(field,"valid")) {
+	if (!plr)
+	{
+		if (field == player_valid)
+		{
 			lua_pushboolean(L, false);
 			return 1;
 		}
 		return LUA_ErrInvalid(L, "player_t");
 	}
 
-	if (fastcmp(field,"valid"))
+	switch (field)
+	{
+	case player_valid:
 		lua_pushboolean(L, true);
-	else if (fastcmp(field,"name"))
+		break;
+	case player_name:
 		lua_pushstring(L, player_names[plr-players]);
-	else if (fastcmp(field,"realmo"))
+		break;
+	case player_realmo:
 		LUA_PushUserdata(L, plr->mo, META_MOBJ);
+		break;
 	// Kept for backward-compatibility
 	// Should be fixed to work like "realmo" later
-	else if (fastcmp(field,"mo"))
-	{
+	case player_mo:
 		if (plr->spectator)
 			lua_pushnil(L);
 		else
 			LUA_PushUserdata(L, plr->mo, META_MOBJ);
-	}
-	else if (fastcmp(field,"cmd"))
+		break;
+	case player_cmd:
 		LUA_PushUserdata(L, &plr->cmd, META_TICCMD);
-	else if (fastcmp(field,"playerstate"))
+		break;
+	case player_playerstate:
 		lua_pushinteger(L, plr->playerstate);
-	else if (fastcmp(field,"camerascale"))
+		break;
+	case player_camerascale:
 		lua_pushfixed(L, plr->camerascale);
-	else if (fastcmp(field,"shieldscale"))
+		break;
+	case player_shieldscale:
 		lua_pushfixed(L, plr->shieldscale);
-	else if (fastcmp(field,"viewz"))
+		break;
+	case player_viewz:
 		lua_pushfixed(L, plr->viewz);
-	else if (fastcmp(field,"viewheight"))
+		break;
+	case player_viewheight:
 		lua_pushfixed(L, plr->viewheight);
-	else if (fastcmp(field,"deltaviewheight"))
+		break;
+	case player_deltaviewheight:
 		lua_pushfixed(L, plr->deltaviewheight);
-	else if (fastcmp(field,"bob"))
+		break;
+	case player_bob:
 		lua_pushfixed(L, plr->bob);
-	else if (fastcmp(field,"viewrollangle"))
+		break;
+	case player_viewrollangle:
 		lua_pushangle(L, plr->viewrollangle);
-	else if (fastcmp(field,"aiming"))
+		break;
+	case player_aiming:
 		lua_pushangle(L, plr->aiming);
-	else if (fastcmp(field,"drawangle"))
+		break;
+	case player_drawangle:
 		lua_pushangle(L, plr->drawangle);
-	else if (fastcmp(field,"rings"))
+		break;
+	case player_rings:
 		lua_pushinteger(L, plr->rings);
-	else if (fastcmp(field,"spheres"))
+		break;
+	case player_spheres:
 		lua_pushinteger(L, plr->spheres);
-	else if (fastcmp(field,"pity"))
+		break;
+	case player_pity:
 		lua_pushinteger(L, plr->pity);
-	else if (fastcmp(field,"currentweapon"))
+		break;
+	case player_currentweapon:
 		lua_pushinteger(L, plr->currentweapon);
-	else if (fastcmp(field,"ringweapons"))
+		break;
+	case player_ringweapons:
 		lua_pushinteger(L, plr->ringweapons);
-	else if (fastcmp(field,"ammoremoval"))
+		break;
+	case player_ammoremoval:
 		lua_pushinteger(L, plr->ammoremoval);
-	else if (fastcmp(field,"ammoremovaltimer"))
+		break;
+	case player_ammoremovaltimer:
 		lua_pushinteger(L, plr->ammoremovaltimer);
-	else if (fastcmp(field,"ammoremovalweapon"))
+		break;
+	case player_ammoremovalweapon:
 		lua_pushinteger(L, plr->ammoremovalweapon);
-	else if (fastcmp(field,"powers"))
+		break;
+	case player_powers:
 		LUA_PushUserdata(L, plr->powers, META_POWERS);
-	else if (fastcmp(field,"pflags"))
+		break;
+	case player_pflags:
 		lua_pushinteger(L, plr->pflags);
-	else if (fastcmp(field,"panim"))
+		break;
+	case player_panim:
 		lua_pushinteger(L, plr->panim);
-	else if (fastcmp(field,"flashcount"))
+		break;
+	case player_flashcount:
 		lua_pushinteger(L, plr->flashcount);
-	else if (fastcmp(field,"flashpal"))
+		break;
+	case player_flashpal:
 		lua_pushinteger(L, plr->flashpal);
-	else if (fastcmp(field,"skincolor"))
+		break;
+	case player_skincolor:
 		lua_pushinteger(L, plr->skincolor);
-	else if (fastcmp(field,"skin"))
+		break;
+	case player_skin:
 		lua_pushinteger(L, plr->skin);
-	else if (fastcmp(field,"availabilities"))
+		break;
+	case player_availabilities:
 		lua_pushinteger(L, plr->availabilities);
-	else if (fastcmp(field,"score"))
+		break;
+	case player_score:
 		lua_pushinteger(L, plr->score);
-	else if (fastcmp(field,"dashspeed"))
+		break;
+	case player_dashspeed:
 		lua_pushfixed(L, plr->dashspeed);
-	else if (fastcmp(field,"normalspeed"))
+		break;
+	case player_normalspeed:
 		lua_pushfixed(L, plr->normalspeed);
-	else if (fastcmp(field,"runspeed"))
+		break;
+	case player_runspeed:
 		lua_pushfixed(L, plr->runspeed);
-	else if (fastcmp(field,"thrustfactor"))
+		break;
+	case player_thrustfactor:
 		lua_pushinteger(L, plr->thrustfactor);
-	else if (fastcmp(field,"accelstart"))
+		break;
+	case player_accelstart:
 		lua_pushinteger(L, plr->accelstart);
-	else if (fastcmp(field,"acceleration"))
+		break;
+	case player_acceleration:
 		lua_pushinteger(L, plr->acceleration);
-	else if (fastcmp(field,"charability"))
+		break;
+	case player_charability:
 		lua_pushinteger(L, plr->charability);
-	else if (fastcmp(field,"charability2"))
+		break;
+	case player_charability2:
 		lua_pushinteger(L, plr->charability2);
-	else if (fastcmp(field,"charflags"))
+		break;
+	case player_charflags:
 		lua_pushinteger(L, plr->charflags);
-	else if (fastcmp(field,"thokitem"))
+		break;
+	case player_thokitem:
 		lua_pushinteger(L, plr->thokitem);
-	else if (fastcmp(field,"spinitem"))
+		break;
+	case player_spinitem:
 		lua_pushinteger(L, plr->spinitem);
-	else if (fastcmp(field,"revitem"))
+		break;
+	case player_revitem:
 		lua_pushinteger(L, plr->revitem);
-	else if (fastcmp(field,"followitem"))
+		break;
+	case player_followitem:
 		lua_pushinteger(L, plr->followitem);
-	else if (fastcmp(field,"followmobj"))
+		break;
+	case player_followmobj:
 		LUA_PushUserdata(L, plr->followmobj, META_MOBJ);
-	else if (fastcmp(field,"actionspd"))
+		break;
+	case player_actionspd:
 		lua_pushfixed(L, plr->actionspd);
-	else if (fastcmp(field,"mindash"))
+		break;
+	case player_mindash:
 		lua_pushfixed(L, plr->mindash);
-	else if (fastcmp(field,"maxdash"))
+		break;
+	case player_maxdash:
 		lua_pushfixed(L, plr->maxdash);
-	else if (fastcmp(field,"jumpfactor"))
+		break;
+	case player_jumpfactor:
 		lua_pushfixed(L, plr->jumpfactor);
-	else if (fastcmp(field,"height"))
+		break;
+	case player_height:
 		lua_pushfixed(L, plr->height);
-	else if (fastcmp(field,"spinheight"))
+		break;
+	case player_spinheight:
 		lua_pushfixed(L, plr->spinheight);
-	else if (fastcmp(field,"lives"))
+		break;
+	case player_lives:
 		lua_pushinteger(L, plr->lives);
-	else if (fastcmp(field,"continues"))
+		break;
+	case player_continues:
 		lua_pushinteger(L, plr->continues);
-	else if (fastcmp(field,"xtralife"))
+		break;
+	case player_xtralife:
 		lua_pushinteger(L, plr->xtralife);
-	else if (fastcmp(field,"gotcontinue"))
+		break;
+	case player_gotcontinue:
 		lua_pushinteger(L, plr->gotcontinue);
-	else if (fastcmp(field,"speed"))
+		break;
+	case player_speed:
 		lua_pushfixed(L, plr->speed);
-	else if (fastcmp(field,"secondjump"))
+		break;
+	case player_secondjump:
 		lua_pushinteger(L, plr->secondjump);
-	else if (fastcmp(field,"fly1"))
+		break;
+	case player_fly1:
 		lua_pushinteger(L, plr->fly1);
-	else if (fastcmp(field,"scoreadd"))
+		break;
+	case player_scoreadd:
 		lua_pushinteger(L, plr->scoreadd);
-	else if (fastcmp(field,"glidetime"))
+		break;
+	case player_glidetime:
 		lua_pushinteger(L, plr->glidetime);
-	else if (fastcmp(field,"climbing"))
+		break;
+	case player_climbing:
 		lua_pushinteger(L, plr->climbing);
-	else if (fastcmp(field,"deadtimer"))
+		break;
+	case player_deadtimer:
 		lua_pushinteger(L, plr->deadtimer);
-	else if (fastcmp(field,"exiting"))
+		break;
+	case player_exiting:
 		lua_pushinteger(L, plr->exiting);
-	else if (fastcmp(field,"homing"))
+		break;
+	case player_homing:
 		lua_pushinteger(L, plr->homing);
-	else if (fastcmp(field,"dashmode"))
+		break;
+	case player_dashmode:
 		lua_pushinteger(L, plr->dashmode);
-	else if (fastcmp(field,"skidtime"))
+		break;
+	case player_skidtime:
 		lua_pushinteger(L, plr->skidtime);
-	else if (fastcmp(field,"cmomx"))
+		break;
+	case player_cmomx:
 		lua_pushfixed(L, plr->cmomx);
-	else if (fastcmp(field,"cmomy"))
+		break;
+	case player_cmomy:
 		lua_pushfixed(L, plr->cmomy);
-	else if (fastcmp(field,"rmomx"))
+		break;
+	case player_rmomx:
 		lua_pushfixed(L, plr->rmomx);
-	else if (fastcmp(field,"rmomy"))
+		break;
+	case player_rmomy:
 		lua_pushfixed(L, plr->rmomy);
-	else if (fastcmp(field,"numboxes"))
+		break;
+	case player_numboxes:
 		lua_pushinteger(L, plr->numboxes);
-	else if (fastcmp(field,"totalring"))
+		break;
+	case player_totalring:
 		lua_pushinteger(L, plr->totalring);
-	else if (fastcmp(field,"realtime"))
+		break;
+	case player_realtime:
 		lua_pushinteger(L, plr->realtime);
-	else if (fastcmp(field,"laps"))
+		break;
+	case player_laps:
 		lua_pushinteger(L, plr->laps);
-	else if (fastcmp(field,"ctfteam"))
+		break;
+	case player_ctfteam:
 		lua_pushinteger(L, plr->ctfteam);
-	else if (fastcmp(field,"gotflag"))
+		break;
+	case player_gotflag:
 		lua_pushinteger(L, plr->gotflag);
-	else if (fastcmp(field,"weapondelay"))
+		break;
+	case player_weapondelay:
 		lua_pushinteger(L, plr->weapondelay);
-	else if (fastcmp(field,"tossdelay"))
+		break;
+	case player_tossdelay:
 		lua_pushinteger(L, plr->tossdelay);
-	else if (fastcmp(field,"starpostx"))
+		break;
+	case player_starpostx:
 		lua_pushinteger(L, plr->starpostx);
-	else if (fastcmp(field,"starposty"))
+		break;
+	case player_starposty:
 		lua_pushinteger(L, plr->starposty);
-	else if (fastcmp(field,"starpostz"))
+		break;
+	case player_starpostz:
 		lua_pushinteger(L, plr->starpostz);
-	else if (fastcmp(field,"starpostnum"))
+		break;
+	case player_starpostnum:
 		lua_pushinteger(L, plr->starpostnum);
-	else if (fastcmp(field,"starposttime"))
+		break;
+	case player_starposttime:
 		lua_pushinteger(L, plr->starposttime);
-	else if (fastcmp(field,"starpostangle"))
+		break;
+	case player_starpostangle:
 		lua_pushangle(L, plr->starpostangle);
-	else if (fastcmp(field,"starpostscale"))
+		break;
+	case player_starpostscale:
 		lua_pushfixed(L, plr->starpostscale);
-	else if (fastcmp(field,"angle_pos"))
+		break;
+	case player_angle_pos:
 		lua_pushangle(L, plr->angle_pos);
-	else if (fastcmp(field,"old_angle_pos"))
+		break;
+	case player_old_angle_pos:
 		lua_pushangle(L, plr->old_angle_pos);
-	else if (fastcmp(field,"axis1"))
+		break;
+	case player_axis1:
 		LUA_PushUserdata(L, plr->axis1, META_MOBJ);
-	else if (fastcmp(field,"axis2"))
+		break;
+	case player_axis2:
 		LUA_PushUserdata(L, plr->axis2, META_MOBJ);
-	else if (fastcmp(field,"bumpertime"))
+		break;
+	case player_bumpertime:
 		lua_pushinteger(L, plr->bumpertime);
-	else if (fastcmp(field,"flyangle"))
+		break;
+	case player_flyangle:
 		lua_pushinteger(L, plr->flyangle);
-	else if (fastcmp(field,"drilltimer"))
+		break;
+	case player_drilltimer:
 		lua_pushinteger(L, plr->drilltimer);
-	else if (fastcmp(field,"linkcount"))
+		break;
+	case player_linkcount:
 		lua_pushinteger(L, plr->linkcount);
-	else if (fastcmp(field,"linktimer"))
+		break;
+	case player_linktimer:
 		lua_pushinteger(L, plr->linktimer);
-	else if (fastcmp(field,"anotherflyangle"))
+		break;
+	case player_anotherflyangle:
 		lua_pushinteger(L, plr->anotherflyangle);
-	else if (fastcmp(field,"nightstime"))
+		break;
+	case player_nightstime:
 		lua_pushinteger(L, plr->nightstime);
-	else if (fastcmp(field,"drillmeter"))
+		break;
+	case player_drillmeter:
 		lua_pushinteger(L, plr->drillmeter);
-	else if (fastcmp(field,"drilldelay"))
+		break;
+	case player_drilldelay:
 		lua_pushinteger(L, plr->drilldelay);
-	else if (fastcmp(field,"bonustime"))
+		break;
+	case player_bonustime:
 		lua_pushboolean(L, plr->bonustime);
-	else if (fastcmp(field,"capsule"))
+		break;
+	case player_capsule:
 		LUA_PushUserdata(L, plr->capsule, META_MOBJ);
-	else if (fastcmp(field,"drone"))
+		break;
+	case player_drone:
 		LUA_PushUserdata(L, plr->drone, META_MOBJ);
-	else if (fastcmp(field,"oldscale"))
+		break;
+	case player_oldscale:
 		lua_pushfixed(L, plr->oldscale);
-	else if (fastcmp(field,"mare"))
+		break;
+	case player_mare:
 		lua_pushinteger(L, plr->mare);
-	else if (fastcmp(field,"marelap"))
+		break;
+	case player_marelap:
 		lua_pushinteger(L, plr->marelap);
-	else if (fastcmp(field,"marebonuslap"))
+		break;
+	case player_marebonuslap:
 		lua_pushinteger(L, plr->marebonuslap);
-	else if (fastcmp(field,"marebegunat"))
+		break;
+	case player_marebegunat:
 		lua_pushinteger(L, plr->marebegunat);
-	else if (fastcmp(field,"startedtime"))
+		break;
+	case player_startedtime:
 		lua_pushinteger(L, plr->startedtime);
-	else if (fastcmp(field,"finishedtime"))
+		break;
+	case player_finishedtime:
 		lua_pushinteger(L, plr->finishedtime);
-	else if (fastcmp(field,"lapbegunat"))
+		break;
+	case player_lapbegunat:
 		lua_pushinteger(L, plr->lapbegunat);
-	else if (fastcmp(field,"lapstartedtime"))
+		break;
+	case player_lapstartedtime:
 		lua_pushinteger(L, plr->lapstartedtime);
-	else if (fastcmp(field,"finishedspheres"))
+		break;
+	case player_finishedspheres:
 		lua_pushinteger(L, plr->finishedspheres);
-	else if (fastcmp(field,"finishedrings"))
+		break;
+	case player_finishedrings:
 		lua_pushinteger(L, plr->finishedrings);
-	else if (fastcmp(field,"marescore"))
+		break;
+	case player_marescore:
 		lua_pushinteger(L, plr->marescore);
-	else if (fastcmp(field,"lastmarescore"))
+		break;
+	case player_lastmarescore:
 		lua_pushinteger(L, plr->lastmarescore);
-	else if (fastcmp(field,"totalmarescore"))
+		break;
+	case player_totalmarescore:
 		lua_pushinteger(L, plr->totalmarescore);
-	else if (fastcmp(field,"lastmare"))
+		break;
+	case player_lastmare:
 		lua_pushinteger(L, plr->lastmare);
-	else if (fastcmp(field,"lastmarelap"))
+		break;
+	case player_lastmarelap:
 		lua_pushinteger(L, plr->lastmarelap);
-	else if (fastcmp(field,"lastmarebonuslap"))
+		break;
+	case player_lastmarebonuslap:
 		lua_pushinteger(L, plr->lastmarebonuslap);
-	else if (fastcmp(field,"totalmarelap"))
+		break;
+	case player_totalmarelap:
 		lua_pushinteger(L, plr->totalmarelap);
-	else if (fastcmp(field,"totalmarebonuslap"))
+		break;
+	case player_totalmarebonuslap:
 		lua_pushinteger(L, plr->totalmarebonuslap);
-	else if (fastcmp(field,"maxlink"))
+		break;
+	case player_maxlink:
 		lua_pushinteger(L, plr->maxlink);
-	else if (fastcmp(field,"texttimer"))
+		break;
+	case player_texttimer:
 		lua_pushinteger(L, plr->texttimer);
-	else if (fastcmp(field,"textvar"))
+		break;
+	case player_textvar:
 		lua_pushinteger(L, plr->textvar);
-	else if (fastcmp(field,"lastsidehit"))
+		break;
+	case player_lastsidehit:
 		lua_pushinteger(L, plr->lastsidehit);
-	else if (fastcmp(field,"lastlinehit"))
+		break;
+	case player_lastlinehit:
 		lua_pushinteger(L, plr->lastlinehit);
-	else if (fastcmp(field,"losstime"))
+		break;
+	case player_losstime:
 		lua_pushinteger(L, plr->losstime);
-	else if (fastcmp(field,"timeshit"))
+		break;
+	case player_timeshit:
 		lua_pushinteger(L, plr->timeshit);
-	else if (fastcmp(field,"onconveyor"))
+		break;
+	case player_onconveyor:
 		lua_pushinteger(L, plr->onconveyor);
-	else if (fastcmp(field,"awayviewmobj"))
+		break;
+	case player_awayviewmobj:
 		LUA_PushUserdata(L, plr->awayviewmobj, META_MOBJ);
-	else if (fastcmp(field,"awayviewtics"))
+		break;
+	case player_awayviewtics:
 		lua_pushinteger(L, plr->awayviewtics);
-	else if (fastcmp(field,"awayviewaiming"))
+		break;
+	case player_awayviewaiming:
 		lua_pushangle(L, plr->awayviewaiming);
-	else if (fastcmp(field,"spectator"))
+		break;
+	case player_spectator:
 		lua_pushboolean(L, plr->spectator);
-	else if (fastcmp(field,"outofcoop"))
+		break;
+	case player_outofcoop:
 		lua_pushboolean(L, plr->outofcoop);
-	else if (fastcmp(field,"bot"))
+		break;
+	case player_bot:
 		lua_pushinteger(L, plr->bot);
-	else if (fastcmp(field,"botleader"))
+		break;
+	case player_botleader:
 		LUA_PushUserdata(L, plr->botleader, META_PLAYER);
-	else if (fastcmp(field,"lastbuttons"))
+		break;
+	case player_lastbuttons:
 		lua_pushinteger(L, plr->lastbuttons);
-	else if (fastcmp(field,"blocked"))
+		break;
+	case player_blocked:
 		lua_pushboolean(L, plr->blocked);
-	else if (fastcmp(field,"jointime"))
+		break;
+	case player_jointime:
 		lua_pushinteger(L, plr->jointime);
-	else if (fastcmp(field,"quittime"))
+		break;
+	case player_quittime:
 		lua_pushinteger(L, plr->quittime);
+		break;
 #ifdef HWRENDER
-	else if (fastcmp(field,"fovadd"))
+	case player_fovadd:
 		lua_pushfixed(L, plr->fovadd);
+		break;
 #endif
-	else {
+	default:
 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
 		I_Assert(lua_istable(L, -1));
 		lua_pushlightuserdata(L, plr);
 		lua_rawget(L, -2);
 		if (!lua_istable(L, -1)) { // no extra values table
-			CONS_Debug(DBG_LUA, M_GetText("'%s' has no extvars table or field named '%s'; returning nil.\n"), "player_t", field);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no extvars table or field named '%s'; returning nil.\n"), "player_t", lua_tostring(L, 2));
 			return 0;
 		}
-		lua_getfield(L, -1, field);
+		lua_pushvalue(L, 2); // field name
+		lua_gettable(L, -2);
 		if (lua_isnil(L, -1)) // no value for this field
-			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "player_t", field);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "player_t", lua_tostring(L, 2));
+		break;
 	}
 
 	return 1;
@@ -405,7 +847,7 @@ static int player_get(lua_State *L)
 static int player_set(lua_State *L)
 {
 	player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
-	const char *field = luaL_checkstring(L, 2);
+	enum player_e field = Lua_optoption(L, 2, player_cmd, player_fields_ref);
 	if (!plr)
 		return LUA_ErrInvalid(L, "player_t");
 
@@ -414,337 +856,473 @@ static int player_set(lua_State *L)
 	if (hook_cmd_running)
 		return luaL_error(L, "Do not alter player_t in CMD building code!");
 
-	if (fastcmp(field,"mo") || fastcmp(field,"realmo")) {
+	switch (field)
+	{
+	case player_mo:
+	case player_realmo:
+	{
 		mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		plr->mo->player = NULL; // remove player pointer from old mobj
 		(newmo->player = plr)->mo = newmo; // set player pointer for new mobj, and set new mobj as the player's mobj
+		break;
 	}
-	else if (fastcmp(field,"cmd"))
+	case player_cmd:
 		return NOSET;
-	else if (fastcmp(field,"playerstate"))
+	case player_playerstate:
 		plr->playerstate = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"camerascale"))
+		break;
+	case player_camerascale:
 		plr->camerascale = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"shieldscale"))
+		break;
+	case player_shieldscale:
 		plr->shieldscale = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"viewz"))
+		break;
+	case player_viewz:
 		plr->viewz = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"viewheight"))
+		break;
+	case player_viewheight:
 		plr->viewheight = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"deltaviewheight"))
+		break;
+	case player_deltaviewheight:
 		plr->deltaviewheight = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"bob"))
+		break;
+	case player_bob:
 		plr->bob = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"viewrollangle"))
+		break;
+	case player_viewrollangle:
 		plr->viewrollangle = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"aiming")) {
+		break;
+	case player_aiming:
+	{
 		plr->aiming = luaL_checkangle(L, 3);
 		if (plr == &players[consoleplayer])
 			localaiming = plr->aiming;
 		else if (plr == &players[secondarydisplayplayer])
 			localaiming2 = plr->aiming;
+		break;
 	}
-	else if (fastcmp(field,"drawangle"))
+	case player_drawangle:
 		plr->drawangle = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"rings"))
+		break;
+	case player_rings:
 		plr->rings = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"spheres"))
+		break;
+	case player_spheres:
 		plr->spheres = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"pity"))
+		break;
+	case player_pity:
 		plr->pity = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"currentweapon"))
+		break;
+	case player_currentweapon:
 		plr->currentweapon = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"ringweapons"))
+		break;
+	case player_ringweapons:
 		plr->ringweapons = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"ammoremoval"))
+		break;
+	case player_ammoremoval:
 		plr->ammoremoval = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"ammoremovaltimer"))
+		break;
+	case player_ammoremovaltimer:
 		plr->ammoremovaltimer = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"ammoremovalweapon"))
+		break;
+	case player_ammoremovalweapon:
 		plr->ammoremovalweapon = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"powers"))
+		break;
+	case player_powers:
 		return NOSET;
-	else if (fastcmp(field,"pflags"))
+	case player_pflags:
 		plr->pflags = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"panim"))
+		break;
+	case player_panim:
 		plr->panim = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"flashcount"))
+		break;
+	case player_flashcount:
 		plr->flashcount = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"flashpal"))
+		break;
+	case player_flashpal:
 		plr->flashpal = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"skincolor"))
+		break;
+	case player_skincolor:
 	{
 		UINT16 newcolor = (UINT16)luaL_checkinteger(L,3);
 		if (newcolor >= numskincolors)
 			return luaL_error(L, "player.skincolor %d out of range (0 - %d).", newcolor, numskincolors-1);
 		plr->skincolor = newcolor;
+		break;
 	}
-	else if (fastcmp(field,"skin"))
+	case player_skin:
 		return NOSET;
-	else if (fastcmp(field,"availabilities"))
+	case player_availabilities:
 		return NOSET;
-	else if (fastcmp(field,"score"))
+	case player_score:
 		plr->score = (UINT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"dashspeed"))
+		break;
+	case player_dashspeed:
 		plr->dashspeed = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"normalspeed"))
+		break;
+	case player_normalspeed:
 		plr->normalspeed = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"runspeed"))
+		break;
+	case player_runspeed:
 		plr->runspeed = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"thrustfactor"))
+		break;
+	case player_thrustfactor:
 		plr->thrustfactor = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"accelstart"))
+		break;
+	case player_accelstart:
 		plr->accelstart = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"acceleration"))
+		break;
+	case player_acceleration:
 		plr->acceleration = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"charability"))
+		break;
+	case player_charability:
 		plr->charability = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"charability2"))
+		break;
+	case player_charability2:
 		plr->charability2 = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"charflags"))
+		break;
+	case player_charflags:
 		plr->charflags = (UINT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"thokitem"))
+		break;
+	case player_thokitem:
 		plr->thokitem = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"spinitem"))
+		break;
+	case player_spinitem:
 		plr->spinitem = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"revitem"))
+		break;
+	case player_revitem:
 		plr->revitem = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"followitem"))
+		break;
+	case player_followitem:
 		plr->followitem = luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"followmobj"))
+		break;
+	case player_followmobj:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->followmobj, mo);
+		break;
 	}
-	else if (fastcmp(field,"actionspd"))
+	case player_actionspd:
 		plr->actionspd = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"mindash"))
+		break;
+	case player_mindash:
 		plr->mindash = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"maxdash"))
+		break;
+	case player_maxdash:
 		plr->maxdash = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"jumpfactor"))
+		break;
+	case player_jumpfactor:
 		plr->jumpfactor = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"height"))
+		break;
+	case player_height:
 		plr->height = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"spinheight"))
+		break;
+	case player_spinheight:
 		plr->spinheight = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"lives"))
+		break;
+	case player_lives:
 		plr->lives = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"continues"))
+		break;
+	case player_continues:
 		plr->continues = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"xtralife"))
+		break;
+	case player_xtralife:
 		plr->xtralife = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"gotcontinue"))
+		break;
+	case player_gotcontinue:
 		plr->gotcontinue = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"speed"))
+		break;
+	case player_speed:
 		plr->speed = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"secondjump"))
+		break;
+	case player_secondjump:
 		plr->secondjump = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"fly1"))
+		break;
+	case player_fly1:
 		plr->fly1 = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"scoreadd"))
+		break;
+	case player_scoreadd:
 		plr->scoreadd = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"glidetime"))
+		break;
+	case player_glidetime:
 		plr->glidetime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"climbing"))
+		break;
+	case player_climbing:
 		plr->climbing = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"deadtimer"))
+		break;
+	case player_deadtimer:
 		plr->deadtimer = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"exiting"))
+		break;
+	case player_exiting:
 		plr->exiting = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"homing"))
+		break;
+	case player_homing:
 		plr->homing = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"dashmode"))
+		break;
+	case player_dashmode:
 		plr->dashmode = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"skidtime"))
+		break;
+	case player_skidtime:
 		plr->skidtime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"cmomx"))
+		break;
+	case player_cmomx:
 		plr->cmomx = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"cmomy"))
+		break;
+	case player_cmomy:
 		plr->cmomy = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"rmomx"))
+		break;
+	case player_rmomx:
 		plr->rmomx = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"rmomy"))
+		break;
+	case player_rmomy:
 		plr->rmomy = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"numboxes"))
+		break;
+	case player_numboxes:
 		plr->numboxes = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"totalring"))
+		break;
+	case player_totalring:
 		plr->totalring = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"realtime"))
+		break;
+	case player_realtime:
 		plr->realtime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"laps"))
+		break;
+	case player_laps:
 		plr->laps = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"ctfteam"))
+		break;
+	case player_ctfteam:
 		plr->ctfteam = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"gotflag"))
+		break;
+	case player_gotflag:
 		plr->gotflag = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"weapondelay"))
+		break;
+	case player_weapondelay:
 		plr->weapondelay = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"tossdelay"))
+		break;
+	case player_tossdelay:
 		plr->tossdelay = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starpostx"))
+		break;
+	case player_starpostx:
 		plr->starpostx = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starposty"))
+		break;
+	case player_starposty:
 		plr->starposty = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starpostz"))
+		break;
+	case player_starpostz:
 		plr->starpostz = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starpostnum"))
+		break;
+	case player_starpostnum:
 		plr->starpostnum = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starposttime"))
+		break;
+	case player_starposttime:
 		plr->starposttime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"starpostangle"))
+		break;
+	case player_starpostangle:
 		plr->starpostangle = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"starpostscale"))
+		break;
+	case player_starpostscale:
 		plr->starpostscale = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"angle_pos"))
+		break;
+	case player_angle_pos:
 		plr->angle_pos = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"old_angle_pos"))
+		break;
+	case player_old_angle_pos:
 		plr->old_angle_pos = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"axis1"))
+		break;
+	case player_axis1:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->axis1, mo);
+		break;
 	}
-	else if (fastcmp(field,"axis2"))
+	case player_axis2:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->axis2, mo);
+		break;
 	}
-	else if (fastcmp(field,"bumpertime"))
+	case player_bumpertime:
 		plr->bumpertime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"flyangle"))
+		break;
+	case player_flyangle:
 		plr->flyangle = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"drilltimer"))
+		break;
+	case player_drilltimer:
 		plr->drilltimer = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"linkcount"))
+		break;
+	case player_linkcount:
 		plr->linkcount = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"linktimer"))
+		break;
+	case player_linktimer:
 		plr->linktimer = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"anotherflyangle"))
+		break;
+	case player_anotherflyangle:
 		plr->anotherflyangle = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"nightstime"))
+		break;
+	case player_nightstime:
 		plr->nightstime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"drillmeter"))
+		break;
+	case player_drillmeter:
 		plr->drillmeter = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"drilldelay"))
+		break;
+	case player_drilldelay:
 		plr->drilldelay = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"bonustime"))
+		break;
+	case player_bonustime:
 		plr->bonustime = luaL_checkboolean(L, 3);
-	else if (fastcmp(field,"capsule"))
+		break;
+	case player_capsule:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->capsule, mo);
+		break;
 	}
-	else if (fastcmp(field,"drone"))
+	case player_drone:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->drone, mo);
+		break;
 	}
-	else if (fastcmp(field,"oldscale"))
+	case player_oldscale:
 		plr->oldscale = luaL_checkfixed(L, 3);
-	else if (fastcmp(field,"mare"))
+		break;
+	case player_mare:
 		plr->mare = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"marelap"))
+		break;
+	case player_marelap:
 		plr->marelap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"marebonuslap"))
+		break;
+	case player_marebonuslap:
 		plr->marebonuslap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"marebegunat"))
+		break;
+	case player_marebegunat:
 		plr->marebegunat = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"startedtime"))
+		break;
+	case player_startedtime:
 		plr->startedtime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"finishedtime"))
+		break;
+	case player_finishedtime:
 		plr->finishedtime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lapbegunat"))
+		break;
+	case player_lapbegunat:
 		plr->lapbegunat = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lapstartedtime"))
+		break;
+	case player_lapstartedtime:
 		plr->lapstartedtime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"finishedspheres"))
+		break;
+	case player_finishedspheres:
 		plr->finishedspheres = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"finishedrings"))
+		break;
+	case player_finishedrings:
 		plr->finishedrings = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"marescore"))
+		break;
+	case player_marescore:
 		plr->marescore = (UINT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastmarescore"))
+		break;
+	case player_lastmarescore:
 		plr->lastmarescore = (UINT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"totalmarescore"))
+		break;
+	case player_totalmarescore:
 		plr->totalmarescore = (UINT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastmare"))
+		break;
+	case player_lastmare:
 		plr->lastmare = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastmarelap"))
+		break;
+	case player_lastmarelap:
 		plr->lastmarelap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastmarebonuslap"))
+		break;
+	case player_lastmarebonuslap:
 		plr->lastmarebonuslap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"totalmarelap"))
+		break;
+	case player_totalmarelap:
 		plr->totalmarelap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"totalmarebonuslap"))
+		break;
+	case player_totalmarebonuslap:
 		plr->totalmarebonuslap = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"maxlink"))
+		break;
+	case player_maxlink:
 		plr->maxlink = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"texttimer"))
+		break;
+	case player_texttimer:
 		plr->texttimer = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"textvar"))
+		break;
+	case player_textvar:
 		plr->textvar = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastsidehit"))
+		break;
+	case player_lastsidehit:
 		plr->lastsidehit = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"lastlinehit"))
+		break;
+	case player_lastlinehit:
 		plr->lastlinehit = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"losstime"))
+		break;
+	case player_losstime:
 		plr->losstime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"timeshit"))
+		break;
+	case player_timeshit:
 		plr->timeshit = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"onconveyor"))
+		break;
+	case player_onconveyor:
 		plr->onconveyor = (INT32)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"awayviewmobj"))
+		break;
+	case player_awayviewmobj:
 	{
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 		P_SetTarget(&plr->awayviewmobj, mo);
+		break;
 	}
-	else if (fastcmp(field,"awayviewtics"))
-	{
+	case player_awayviewtics:
 		plr->awayviewtics = (INT32)luaL_checkinteger(L, 3);
 		if (plr->awayviewtics && !plr->awayviewmobj) // awayviewtics must ALWAYS have an awayviewmobj set!!
 			P_SetTarget(&plr->awayviewmobj, plr->mo); // but since the script might set awayviewmobj immediately AFTER setting awayviewtics, use player mobj as filler for now.
-	}
-	else if (fastcmp(field,"awayviewaiming"))
+		break;
+	case player_awayviewaiming:
 		plr->awayviewaiming = luaL_checkangle(L, 3);
-	else if (fastcmp(field,"spectator"))
+		break;
+	case player_spectator:
 		plr->spectator = lua_toboolean(L, 3);
-	else if (fastcmp(field,"outofcoop"))
+		break;
+	case player_outofcoop:
 		plr->outofcoop = lua_toboolean(L, 3);
-	else if (fastcmp(field,"bot"))
+		break;
+	case player_bot:
 		return NOSET;
-	else if (fastcmp(field,"botleader"))
+	case player_botleader:
 	{
 		player_t *player = NULL;
 		if (!lua_isnil(L, 3))
 			player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER));
 		plr->botleader = player;
+		break;
 	}
-	else if (fastcmp(field,"lastbuttons"))
+	case player_lastbuttons:
 		plr->lastbuttons = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"blocked"))
+		break;
+	case player_blocked:
 		plr->blocked = (UINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"jointime"))
+		break;
+	case player_jointime:
 		plr->jointime = (tic_t)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"quittime"))
+		break;
+	case player_quittime:
 		plr->quittime = (tic_t)luaL_checkinteger(L, 3);
+		break;
 #ifdef HWRENDER
-	else if (fastcmp(field,"fovadd"))
+	case player_fovadd:
 		plr->fovadd = luaL_checkfixed(L, 3);
+		break;
 #endif
-	else {
+	default:
 		lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
 		I_Assert(lua_istable(L, -1));
 		lua_pushlightuserdata(L, plr);
@@ -752,15 +1330,17 @@ static int player_set(lua_State *L)
 		if (lua_isnil(L, -1)) {
 			// This index doesn't have a table for extra values yet, let's make one.
 			lua_pop(L, 1);
-			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "player_t", field);
+			CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "player_t", lua_tostring(L, 2));
 			lua_newtable(L);
 			lua_pushlightuserdata(L, plr);
 			lua_pushvalue(L, -2); // ext value table
 			lua_rawset(L, -4); // LREG_EXTVARS table
 		}
+		lua_pushvalue(L, 2); // key
 		lua_pushvalue(L, 3); // value to store
-		lua_setfield(L, -2, field);
+		lua_settable(L, -3);
 		lua_pop(L, 2);
+		break;
 	}
 
 	return 0;
@@ -814,27 +1394,58 @@ static int power_len(lua_State *L)
 #define NOFIELD luaL_error(L, LUA_QL("ticcmd_t") " has no field named " LUA_QS, field)
 #define NOSET luaL_error(L, LUA_QL("ticcmd_t") " field " LUA_QS " should not be set directly.", field)
 
+enum ticcmd_e
+{
+	ticcmd_forwardmove,
+	ticcmd_sidemove,
+	ticcmd_angleturn,
+	ticcmd_aiming,
+	ticcmd_buttons,
+	ticcmd_latency,
+};
+
+static const char *const ticcmd_opt[] = {
+	"forwardmove",
+	"sidemove",
+	"angleturn",
+	"aiming",
+	"buttons",
+	"latency",
+	NULL,
+};
+
+static int ticcmd_fields_ref = LUA_NOREF;
+
 static int ticcmd_get(lua_State *L)
 {
 	ticcmd_t *cmd = *((ticcmd_t **)luaL_checkudata(L, 1, META_TICCMD));
-	const char *field = luaL_checkstring(L, 2);
+	enum ticcmd_e field = Lua_optoption(L, 2, -1, ticcmd_fields_ref);
 	if (!cmd)
 		return LUA_ErrInvalid(L, "player_t");
 
-	if (fastcmp(field,"forwardmove"))
+	switch (field)
+	{
+	case ticcmd_forwardmove:
 		lua_pushinteger(L, cmd->forwardmove);
-	else if (fastcmp(field,"sidemove"))
+		break;
+	case ticcmd_sidemove:
 		lua_pushinteger(L, cmd->sidemove);
-	else if (fastcmp(field,"angleturn"))
+		break;
+	case ticcmd_angleturn:
 		lua_pushinteger(L, cmd->angleturn);
-	else if (fastcmp(field,"aiming"))
+		break;
+	case ticcmd_aiming:
 		lua_pushinteger(L, cmd->aiming);
-	else if (fastcmp(field,"buttons"))
+		break;
+	case ticcmd_buttons:
 		lua_pushinteger(L, cmd->buttons);
-	else if (fastcmp(field,"latency"))
+		break;
+	case ticcmd_latency:
 		lua_pushinteger(L, cmd->latency);
-	else
+		break;
+	default:
 		return NOFIELD;
+	}
 
 	return 1;
 }
@@ -842,27 +1453,35 @@ static int ticcmd_get(lua_State *L)
 static int ticcmd_set(lua_State *L)
 {
 	ticcmd_t *cmd = *((ticcmd_t **)luaL_checkudata(L, 1, META_TICCMD));
-	const char *field = luaL_checkstring(L, 2);
+	enum ticcmd_e field = Lua_optoption(L, 2, -1, ticcmd_fields_ref);
 	if (!cmd)
 		return LUA_ErrInvalid(L, "ticcmd_t");
 
 	if (hud_running)
 		return luaL_error(L, "Do not alter player_t in HUD rendering code!");
 
-	if (fastcmp(field,"forwardmove"))
+	switch (field)
+	{
+	case ticcmd_forwardmove:
 		cmd->forwardmove = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"sidemove"))
+		break;
+	case ticcmd_sidemove:
 		cmd->sidemove = (SINT8)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"angleturn"))
+		break;
+	case ticcmd_angleturn:
 		cmd->angleturn = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"aiming"))
+		break;
+	case ticcmd_aiming:
 		cmd->aiming = (INT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"buttons"))
+		break;
+	case ticcmd_buttons:
 		cmd->buttons = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"latency"))
+		break;
+	case ticcmd_latency:
 		return NOSET;
-	else
+	default:
 		return NOFIELD;
+	}
 
 	return 0;
 }
@@ -883,6 +1502,8 @@ int LUA_PlayerLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L,1);
 
+	player_fields_ref = Lua_CreateFieldTable(L, player_opt);
+
 	luaL_newmetatable(L, META_POWERS);
 		lua_pushcfunction(L, power_get);
 		lua_setfield(L, -2, "__index");
@@ -902,6 +1523,8 @@ int LUA_PlayerLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L,1);
 
+	ticcmd_fields_ref = Lua_CreateFieldTable(L, ticcmd_opt);
+
 	lua_newuserdata(L, 0);
 		lua_createtable(L, 0, 2);
 			lua_pushcfunction(L, lib_getPlayer);
diff --git a/src/lua_script.c b/src/lua_script.c
index 9cb146c128..6893265d57 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -1729,17 +1729,39 @@ void LUA_UnArchive(void)
 }
 
 // For mobj_t, player_t, etc. to take custom variables.
-int Lua_optoption(lua_State *L, int narg,
-	const char *def, const char *const lst[])
+int Lua_optoption(lua_State *L, int narg, int def, int list_ref)
 {
-	const char *name = (def) ? luaL_optstring(L, narg, def) :  luaL_checkstring(L, narg);
-	int i;
-	for (i=0; lst[i]; i++)
-		if (fastcmp(lst[i], name))
-			return i;
+	if (lua_isnoneornil(L, narg))
+		return def;
+
+	I_Assert(lua_checkstack(L, 2));
+	luaL_checkstring(L, narg);
+
+	lua_rawgeti(L, LUA_REGISTRYINDEX, list_ref);
+	I_Assert(lua_istable(L, -1));
+	lua_pushvalue(L, narg);
+	lua_rawget(L, -2);
+
+	if (lua_isnumber(L, -1))
+		return lua_tointeger(L, -1);
 	return -1;
 }
 
+int Lua_CreateFieldTable(lua_State *L, const char *const lst[])
+{
+	int i;
+
+	lua_newtable(L);
+	for (i = 0; lst[i] != NULL; i++)
+	{
+		lua_pushstring(L, lst[i]);
+		lua_pushinteger(L, i);
+		lua_settable(L, -3);
+	}
+
+	return luaL_ref(L, LUA_REGISTRYINDEX);
+}
+
 void LUA_PushTaggableObjectArray
 (		lua_State *L,
 		const char *field,
diff --git a/src/lua_script.h b/src/lua_script.h
index fe04e5e608..d0b06a719e 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -57,8 +57,8 @@ int LUA_PushGlobals(lua_State *L, const char *word);
 int LUA_CheckGlobals(lua_State *L, const char *word);
 void Got_Luacmd(UINT8 **cp, INT32 playernum); // lua_consolelib.c
 void LUA_CVarChanged(void *cvar); // lua_consolelib.c
-int Lua_optoption(lua_State *L, int narg,
-	const char *def, const char *const lst[]);
+int Lua_optoption(lua_State *L, int narg, int def, int list_ref);
+int Lua_CreateFieldTable(lua_State *L, const char *const lst[]);
 void LUA_HookNetArchive(lua_CFunction archFunc);
 
 void LUA_PushTaggableObjectArray
diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c
index 5c21b04c3a..b7890a6c71 100644
--- a/src/lua_skinlib.c
+++ b/src/lua_skinlib.c
@@ -55,6 +55,7 @@ enum skin {
 	skin_soundsid,
 	skin_sprites
 };
+
 static const char *const skin_opt[] = {
 	"valid",
 	"name",
@@ -95,10 +96,12 @@ static const char *const skin_opt[] = {
 
 #define UNIMPLEMENTED luaL_error(L, LUA_QL("skin_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", skin_opt[field])
 
+static int skin_fields_ref = LUA_NOREF;
+
 static int skin_get(lua_State *L)
 {
 	skin_t *skin = *((skin_t **)luaL_checkudata(L, 1, META_SKIN));
-	enum skin field = luaL_checkoption(L, 2, NULL, skin_opt);
+	enum skin field = Lua_optoption(L, 2, -1, skin_fields_ref);
 
 	// skins are always valid, only added, never removed
 	I_Assert(skin != NULL);
@@ -376,6 +379,8 @@ int LUA_SkinLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L,1);
 
+	skin_fields_ref = Lua_CreateFieldTable(L, skin_opt);
+
 	luaL_newmetatable(L, META_SOUNDSID);
 		lua_pushcfunction(L, soundsid_get);
 		lua_setfield(L, -2, "__index");
-- 
GitLab


From 17356f93bf1f77f582f68494d4ee2b1574ff36cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Mon, 19 Jun 2023 22:19:39 +0200
Subject: [PATCH 138/518] Fix segfault when ghost dies from a death pit

---
 src/p_mobj.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index a04351ae77..3eab29c095 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -3139,7 +3139,8 @@ boolean P_SceneryZMovement(mobj_t *mo)
 
 	if (P_CheckDeathPitCollide(mo))
 	{
-		P_RemoveMobj(mo);
+		if (mo->type != MT_GHOST)  // ghosts play death animations instead, so don't remove them
+			P_RemoveMobj(mo);
 		return false;
 	}
 
-- 
GitLab


From bbe6c12323703736722dc9b1813fe79f1be3eb0d Mon Sep 17 00:00:00 2001
From: Lach <54614944+lachablock@users.noreply.github.com>
Date: Wed, 26 Oct 2022 22:44:25 +1100
Subject: [PATCH 139/518] Skincolor changes: - Tweaked Orange and Cobalt -
 Added Pepper, Jade, Headlight, Master, Mauve, and Taffy - Fixed Mario mode
 invincibility color cycle (Opposite color shades are placeholder for now)

---
 src/deh_tables.c |  6 ++++++
 src/doomdef.h    |  6 ++++++
 src/info.c       | 10 ++++++++--
 src/p_user.c     |  2 +-
 4 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index c3b2cfccd6..79e01ef75d 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4623,6 +4623,7 @@ const char *COLOR_ENUMS[] = {
 	// Viv's vivid colours (toast 21/07/17)
 	"RUBY",     	// SKINCOLOR_RUBY,
 	"SALMON",     	// SKINCOLOR_SALMON,
+	"PEPPER",     	// SKINCOLOR_PEPPER,
 	"RED",     		// SKINCOLOR_RED,
 	"CRIMSON",     	// SKINCOLOR_CRIMSON,
 	"FLAME",     	// SKINCOLOR_FLAME,
@@ -4644,7 +4645,10 @@ const char *COLOR_ENUMS[] = {
 	"GREEN",     	// SKINCOLOR_GREEN,
 	"FOREST",     	// SKINCOLOR_FOREST,
 	"EMERALD",     	// SKINCOLOR_EMERALD,
+	"JADE",     	// SKINCOLOR_JADE,
+	"HEADLIGHT",	// SKINCOLOR_HEADLIGHT,
 	"MINT",     	// SKINCOLOR_MINT,
+	"MASTER",     	// SKINCOLOR_MASTER,
 	"SEAFOAM",     	// SKINCOLOR_SEAFOAM,
 	"AQUA",     	// SKINCOLOR_AQUA,
 	"TEAL",     	// SKINCOLOR_TEAL,
@@ -4666,8 +4670,10 @@ const char *COLOR_ENUMS[] = {
 	"NEON",     	// SKINCOLOR_NEON,
 	"VIOLET",     	// SKINCOLOR_VIOLET,
 	"LILAC",     	// SKINCOLOR_LILAC,
+	"MAUVE",     	// SKINCOLOR_MAUVE,
 	"PLUM",     	// SKINCOLOR_PLUM,
 	"RASPBERRY",  	// SKINCOLOR_RASPBERRY,
+	"TAFFY",     	// SKINCOLOR_TAFFY,
 	"ROSY",     	// SKINCOLOR_ROSY,
 
 	// Super special awesome Super flashing colors!
diff --git a/src/doomdef.h b/src/doomdef.h
index d521d4fbbb..a2243f5f37 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -275,6 +275,7 @@ typedef enum
 	// Viv's vivid colours (toast 21/07/17)
 	SKINCOLOR_RUBY,
 	SKINCOLOR_SALMON,
+	SKINCOLOR_PEPPER,
 	SKINCOLOR_RED,
 	SKINCOLOR_CRIMSON,
 	SKINCOLOR_FLAME,
@@ -296,7 +297,10 @@ typedef enum
 	SKINCOLOR_GREEN,
 	SKINCOLOR_FOREST,
 	SKINCOLOR_EMERALD,
+	SKINCOLOR_JADE,
+	SKINCOLOR_HEADLIGHT,
 	SKINCOLOR_MINT,
+	SKINCOLOR_MASTER,
 	SKINCOLOR_SEAFOAM,
 	SKINCOLOR_AQUA,
 	SKINCOLOR_TEAL,
@@ -318,8 +322,10 @@ typedef enum
 	SKINCOLOR_NEON,
 	SKINCOLOR_VIOLET,
 	SKINCOLOR_LILAC,
+	SKINCOLOR_MAUVE,
 	SKINCOLOR_PLUM,
 	SKINCOLOR_RASPBERRY,
+	SKINCOLOR_TAFFY,
 	SKINCOLOR_ROSY,
 
 	FIRSTSUPERCOLOR,
diff --git a/src/info.c b/src/info.c
index 09452a74f5..fbcdcfc808 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21600,6 +21600,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	// Viv's vivid colours (toast 21/07/17)
 	{"Ruby",       {0xb0, 0xb0, 0xc9, 0xca, 0xcc, 0x26, 0x27, 0x28, 0x29, 0x2a, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfd}, SKINCOLOR_EMERALD,    10, V_REDMAP,     true}, // SKINCOLOR_RUBY
 	{"Salmon",     {0xd0, 0xd0, 0xd1, 0xd2, 0x20, 0x21, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e}, SKINCOLOR_FOREST,     6,  V_REDMAP,     true}, // SKINCOLOR_SALMON
+	{"Pepper",     { 210,   32,   33,   34,   35,   35,   36,   37,   38,   39,   41,   43,   45,   45,   46,   47}, SKINCOLOR_MASTER,     8,  V_REDMAP,     true}, // SKINCOLOR_PEPPER
 	{"Red",        {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x47, 0x2e, 0x2f}, SKINCOLOR_GREEN,      10, V_REDMAP,     true}, // SKINCOLOR_RED
 	{"Crimson",    {0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x1f}, SKINCOLOR_ICY,        10, V_REDMAP,     true}, // SKINCOLOR_CRIMSON
 	{"Flame",      {0x31, 0x32, 0x33, 0x36, 0x22, 0x22, 0x25, 0x25, 0x25, 0xcd, 0xcf, 0xcf, 0xc5, 0xc5, 0xc7, 0xc7}, SKINCOLOR_PURPLE,     8,  V_REDMAP,     true}, // SKINCOLOR_FLAME
@@ -21609,7 +21610,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Sunset",     {0x51, 0x52, 0x40, 0x40, 0x34, 0x36, 0xd5, 0xd5, 0xd6, 0xd7, 0xcf, 0xcf, 0xc6, 0xc6, 0xc7, 0xfe}, SKINCOLOR_SAPPHIRE,   5,  V_ORANGEMAP,  true}, // SKINCOLOR_SUNSET
 	{"Copper",     {0x58, 0x54, 0x40, 0x34, 0x35, 0x38, 0x3a, 0x3c, 0x3d, 0x2a, 0x2b, 0x2c, 0x2c, 0xba, 0xba, 0xbb}, SKINCOLOR_BLUEBELL,   5,  V_ORANGEMAP,  true}, // SKINCOLOR_COPPER
 	{"Apricot",    {0x00, 0xd8, 0xd9, 0xda, 0xdb, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e}, SKINCOLOR_CYAN,       4,  V_ORANGEMAP,  true}, // SKINCOLOR_APRICOT
-	{"Orange",     {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x2c}, SKINCOLOR_BLUE,       4,  V_ORANGEMAP,  true}, // SKINCOLOR_ORANGE
+	{"Orange",     {  49,   50,   51,   52,   53,   54,   55,   57,   58,   59,   60,   42,   44,   45,   46,   46}, SKINCOLOR_BLUE,       4,  V_ORANGEMAP,  true}, // SKINCOLOR_ORANGE
 	{"Rust",       {0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3f, 0x2c, 0x2d, 0x47, 0x2e, 0x2f, 0x2f}, SKINCOLOR_YOGURT,     8,  V_ORANGEMAP,  true}, // SKINCOLOR_RUST
 	{"Gold",       {0x51, 0x51, 0x54, 0x54, 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x3f, 0x2d, 0x2e, 0x2f, 0x2f}, SKINCOLOR_LAVENDER,   10, V_YELLOWMAP,  true}, // SKINCOLOR_GOLD
 	{"Sandy",      {0x53, 0x40, 0x41, 0x42, 0x43, 0xe6, 0xe9, 0xe9, 0xea, 0xec, 0xec, 0xc6, 0xc6, 0xc7, 0xc7, 0xfe}, SKINCOLOR_SKY,        8,  V_YELLOWMAP,  true}, // SKINCOLOR_SANDY
@@ -21621,7 +21622,10 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Green",      {0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_RED,        6,  V_GREENMAP,   true}, // SKINCOLOR_GREEN
 	{"Forest",     {0x65, 0x66, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f}, SKINCOLOR_SALMON,     9,  V_GREENMAP,   true}, // SKINCOLOR_FOREST
 	{"Emerald",    {0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77}, SKINCOLOR_RUBY,       4,  V_GREENMAP,   true}, // SKINCOLOR_EMERALD
+	{"Jade",       { 128,  120,  121,  122,  122,  113,  114,  114,  115,  116,  117,  118,  119,  110,  111,   30}, SKINCOLOR_TAFFY,      8,  V_GREENMAP,   true}, // SKINCOLOR_JADE
+	{"Headlight",  {   0,   80,   81,   82,   73,   84,   64,   65,   91,   91,  124,  125,  126,  137,  138,  139}, SKINCOLOR_MAUVE,      8,  V_YELLOWMAP,  true}, // SKINCOLOR_HEADLIGHT
 	{"Mint",       {0x00, 0x00, 0x58, 0x58, 0x59, 0x62, 0x62, 0x62, 0x64, 0x67, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a}, SKINCOLOR_VIOLET,     5,  V_GREENMAP,   true}, // SKINCOLOR_MINT
+	{"Master",     {   0,   80,   88,   96,  112,  113,   99,  100,  124,  125,  126,  117,  107,  118,  119,  111}, SKINCOLOR_PEPPER,     8,  V_GREENMAP,   true}, // SKINCOLOR_MASTER
 	{"Seafoam",    {0x01, 0x58, 0x59, 0x5a, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0xfd, 0xfd}, SKINCOLOR_PLUM,       6,  V_AQUAMAP,    true}, // SKINCOLOR_SEAFOAM
 	{"Aqua",       {0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x76, 0x77}, SKINCOLOR_ROSY,       7,  V_AQUAMAP,    true}, // SKINCOLOR_AQUA
 	{"Teal",       {0x78, 0x78, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0x8a}, SKINCOLOR_PEACHY,     7,  V_SKYMAP,     true}, // SKINCOLOR_TEAL
@@ -21633,7 +21637,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Sapphire",   {0x80, 0x83, 0x86, 0x87, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_SUNSET,     5,  V_SKYMAP,     true}, // SKINCOLOR_SAPPHIRE
 	{"Cornflower", {0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x9a, 0x9c, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e}, SKINCOLOR_YELLOW,     4,  V_BLUEMAP,    true}, // SKINCOLOR_CORNFLOWER
 	{"Blue",       {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_ORANGE,     5,  V_BLUEMAP,    true}, // SKINCOLOR_BLUE
-	{"Cobalt",     {0x93, 0x94, 0x95, 0x96, 0x98, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfd, 0xfe, 0xfe}, SKINCOLOR_PERIDOT,    5,  V_BLUEMAP,    true}, // SKINCOLOR_COBALT
+	{"Cobalt",     { 145,  147,  149,  150,  151,  153,  154,  155,  156,  157,  158,  159,  253,  253,  254,  254}, SKINCOLOR_PERIDOT,    5,  V_BLUEMAP,    true}, // SKINCOLOR_COBALT
 	{"Vapor",      {0x80, 0x81, 0x83, 0x86, 0x94, 0x94, 0xa3, 0xa3, 0xa4, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_LILAC,      4,  V_SKYMAP,     true}, // SKINCOLOR_VAPOR
 	{"Dusk",       {0x92, 0x93, 0x94, 0x94, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_OLIVE,      0,  V_BLUEMAP,    true}, // SKINCOLOR_DUSK
 	{"Pastel",     {0x90, 0x90, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8}, SKINCOLOR_BUBBLEGUM,  9,  V_PURPLEMAP,  true}, // SKINCOLOR_PASTEL
@@ -21643,8 +21647,10 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Neon",       {0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb, 0xc7, 0xc7, 0x1d, 0x1d, 0x1e}, SKINCOLOR_CERULEAN,   2,  V_MAGENTAMAP, true}, // SKINCOLOR_NEON
 	{"Violet",     {0xd0, 0xd1, 0xd2, 0xca, 0xcc, 0xb8, 0xb9, 0xb9, 0xba, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfe, 0xfe}, SKINCOLOR_MINT,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_VIOLET
 	{"Lilac",      {0x00, 0xd0, 0xd1, 0xd2, 0xd3, 0xc1, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xfe, 0x1f}, SKINCOLOR_VAPOR,      4,  V_ROSYMAP,    true}, // SKINCOLOR_LILAC
+	{"Mauve",      { 176,  177,  178,  192,  193,  194,  195,  195,  196,  185,  185,  186,  186,  187,  187,  253}, SKINCOLOR_HEADLIGHT,  8,  V_PURPLEMAP,  true}, // SKINCOLOR_MAUVE
 	{"Plum",       {0xc8, 0xd3, 0xd5, 0xd6, 0xd7, 0xce, 0xcf, 0xb9, 0xb9, 0xba, 0xba, 0xa9, 0xa9, 0xa9, 0xfd, 0xfe}, SKINCOLOR_MINT,       7,  V_ROSYMAP,    true}, // SKINCOLOR_PLUM
 	{"Raspberry",  {0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xce, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfe, 0xfe}, SKINCOLOR_APPLE,      13, V_ROSYMAP,    true}, // SKINCOLOR_RASPBERRY
+	{"Taffy",      {   1,  176,  176,  177,  178,  179,  202,  203,  204,  204,  206,  207,   44,   44,   46,   47}, SKINCOLOR_JADE,       8,  V_ROSYMAP,    true}, // SKINCOLOR_TAFFY
 	{"Rosy",       {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_AQUA,       1,  V_ROSYMAP,    true}, // SKINCOLOR_ROSY
 
 	// super
diff --git a/src/p_user.c b/src/p_user.c
index 0f3282da55..38af2843b7 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2984,7 +2984,7 @@ static void P_CheckInvincibilityTimer(player_t *player)
 		return;
 
 	if (mariomode && !player->powers[pw_super])
-		player->mo->color = (UINT16)(SKINCOLOR_RUBY + (leveltime % (numskincolors - SKINCOLOR_RUBY))); // Passes through all saturated colours
+		player->mo->color = (UINT16)(SKINCOLOR_RUBY + (leveltime % (FIRSTSUPERCOLOR - SKINCOLOR_RUBY))); // Passes through all saturated colours
 	else if (leveltime % (TICRATE/7) == 0)
 	{
 		mobj_t *sparkle = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_IVSP);
-- 
GitLab


From 222e409cde6e212b2eec61268b78ac83a7cf1051 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 27 Oct 2022 00:24:12 +0200
Subject: [PATCH 140/518] More skincolor changes: - Tweaked Bubblegum - Added 8
 new colors by Alice - Added 22 new colors by sphere

---
 src/deh_tables.c | 31 +++++++++++++++++++++++++++++++
 src/doomdef.h    | 31 +++++++++++++++++++++++++++++++
 src/info.c       | 33 ++++++++++++++++++++++++++++++++-
 3 files changed, 94 insertions(+), 1 deletion(-)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index 79e01ef75d..7ee78218b3 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4609,19 +4609,28 @@ const char *COLOR_ENUMS[] = {
 	// Desaturated
 	"AETHER",     	// SKINCOLOR_AETHER,
 	"SLATE",     	// SKINCOLOR_SLATE,
+	"MERCURY",     	// SKINCOLOR_MERCURY,
 	"BLUEBELL",   	// SKINCOLOR_BLUEBELL,
 	"PINK",     	// SKINCOLOR_PINK,
+	"ROSEWOOD",     	// SKINCOLOR_ROSEWOOD,
 	"YOGURT",     	// SKINCOLOR_YOGURT,
+	"LATTE",     	// SKINCOLOR_LATTE,
 	"BROWN",     	// SKINCOLOR_BROWN,
 	"BRONZE",     	// SKINCOLOR_BRONZE,
+	"SEPIA",     	// SKINCOLOR_SEPIA,
+	"ECRU",     	// SKINCOLOR_ECRU,
 	"TAN",     		// SKINCOLOR_TAN,
 	"BEIGE",     	// SKINCOLOR_BEIGE,
+	"ROSEBUSH",   // SKINCOLOR_ROSEBUSH,
 	"MOSS",     	// SKINCOLOR_MOSS,
 	"AZURE",     	// SKINCOLOR_AZURE,
+	"EGGPLANT",     	// SKINCOLOR_EGGPLANT,
 	"LAVENDER",     // SKINCOLOR_LAVENDER,
 
 	// Viv's vivid colours (toast 21/07/17)
+	// Tweaks & additions (Lach, sphere, Alice, MotorRoach 26/10/22)
 	"RUBY",     	// SKINCOLOR_RUBY,
+	"CHERRY",     	// SKINCOLOR_CHERRY,
 	"SALMON",     	// SKINCOLOR_SALMON,
 	"PEPPER",     	// SKINCOLOR_PEPPER,
 	"RED",     		// SKINCOLOR_RED,
@@ -4630,18 +4639,24 @@ const char *COLOR_ENUMS[] = {
 	"KETCHUP",     	// SKINCOLOR_KETCHUP,
 	"PEACHY",     	// SKINCOLOR_PEACHY,
 	"QUAIL",     	// SKINCOLOR_QUAIL,
+	"FOUNDATION",     	// SKINCOLOR_FOUNDATION,
 	"SUNSET",     	// SKINCOLOR_SUNSET,
 	"COPPER",     	// SKINCOLOR_COPPER,
 	"APRICOT",     	// SKINCOLOR_APRICOT,
 	"ORANGE",     	// SKINCOLOR_ORANGE,
+	"PUMPKIN",     	// SKINCOLOR_PUMPKIN,
 	"RUST",     	// SKINCOLOR_RUST,
 	"GOLD",     	// SKINCOLOR_GOLD,
 	"SANDY",     	// SKINCOLOR_SANDY,
 	"YELLOW",     	// SKINCOLOR_YELLOW,
 	"OLIVE",     	// SKINCOLOR_OLIVE,
+	"KIWI",     	// SKINCOLOR_KIWI,
+	"LEMON",     	// SKINCOLOR_LEMON,
 	"LIME",     	// SKINCOLOR_LIME,
+	"MINDARO",     	// SKINCOLOR_MINDARO,
 	"PERIDOT",     	// SKINCOLOR_PERIDOT,
 	"APPLE",     	// SKINCOLOR_APPLE,
+	"CHARTREUSE",    // SKINCOLOR_CHARTREUSE,
 	"GREEN",     	// SKINCOLOR_GREEN,
 	"FOREST",     	// SKINCOLOR_FOREST,
 	"EMERALD",     	// SKINCOLOR_EMERALD,
@@ -4649,32 +4664,48 @@ const char *COLOR_ENUMS[] = {
 	"HEADLIGHT",	// SKINCOLOR_HEADLIGHT,
 	"MINT",     	// SKINCOLOR_MINT,
 	"MASTER",     	// SKINCOLOR_MASTER,
+	"BOTTLE",     	// SKINCOLOR_BOTTLE,
 	"SEAFOAM",     	// SKINCOLOR_SEAFOAM,
+	"ISLAND",     	// SKINCOLOR_ISLAND,
 	"AQUA",     	// SKINCOLOR_AQUA,
 	"TEAL",     	// SKINCOLOR_TEAL,
 	"WAVE",     	// SKINCOLOR_WAVE,
 	"CYAN",     	// SKINCOLOR_CYAN,
+	"TURQUOISE",    // SKINCOLOR_TURQUOISE,
 	"SKY",     		// SKINCOLOR_SKY,
+	"MARINE",     		// SKINCOLOR_MARINE,
 	"CERULEAN",     // SKINCOLOR_CERULEAN,
+	"DREAM",     // SKINCOLOR_DREAM,
 	"ICY",     		// SKINCOLOR_ICY,
+	"DAYBREAK",     		// SKINCOLOR_DAYBREAK,
 	"SAPPHIRE",     // SKINCOLOR_SAPPHIRE,
+	"ARCTIC",     		// SKINCOLOR_ARCTIC,
 	"CORNFLOWER",   // SKINCOLOR_CORNFLOWER,
 	"BLUE",     	// SKINCOLOR_BLUE,
 	"COBALT",     	// SKINCOLOR_COBALT,
+	"MIDNIGHT",     	// SKINCOLOR_MIDNIGHT,
+	"GALAXY",     	// SKINCOLOR_GALAXY,
 	"VAPOR",     	// SKINCOLOR_VAPOR,
 	"DUSK",     	// SKINCOLOR_DUSK,
+	"MAJESTY",     	// SKINCOLOR_MAJESTY,
 	"PASTEL",     	// SKINCOLOR_PASTEL,
 	"PURPLE",     	// SKINCOLOR_PURPLE,
+	"NOBLE",     	// SKINCOLOR_NOBLE,
+	"FUCHSIA",     	// SKINCOLOR_FUCHSIA,
 	"BUBBLEGUM",    // SKINCOLOR_BUBBLEGUM,
 	"MAGENTA",     	// SKINCOLOR_MAGENTA,
 	"NEON",     	// SKINCOLOR_NEON,
 	"VIOLET",     	// SKINCOLOR_VIOLET,
+	"ROYAL",     	// SKINCOLOR_ROYAL,
 	"LILAC",     	// SKINCOLOR_LILAC,
 	"MAUVE",     	// SKINCOLOR_MAUVE,
+	"EVENTIDE",   	// SKINCOLOR_EVENTIDE,
 	"PLUM",     	// SKINCOLOR_PLUM,
 	"RASPBERRY",  	// SKINCOLOR_RASPBERRY,
 	"TAFFY",     	// SKINCOLOR_TAFFY,
 	"ROSY",     	// SKINCOLOR_ROSY,
+	"FANCY",  		// SKINCOLOR_FANCY,
+	"SANGRIA",     	// SKINCOLOR_SANGRIA,
 
 	// Super special awesome Super flashing colors!
 	"SUPERSILVER1",	// SKINCOLOR_SUPERSILVER1
diff --git a/src/doomdef.h b/src/doomdef.h
index a2243f5f37..f21000cda0 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -261,19 +261,28 @@ typedef enum
 	// Desaturated
 	SKINCOLOR_AETHER,
 	SKINCOLOR_SLATE,
+	SKINCOLOR_MERCURY,
 	SKINCOLOR_BLUEBELL,
 	SKINCOLOR_PINK,
+  SKINCOLOR_ROSEWOOD,
 	SKINCOLOR_YOGURT,
+  SKINCOLOR_LATTE,
 	SKINCOLOR_BROWN,
 	SKINCOLOR_BRONZE,
+  SKINCOLOR_SEPIA,
+	SKINCOLOR_ECRU,
 	SKINCOLOR_TAN,
 	SKINCOLOR_BEIGE,
+  SKINCOLOR_ROSEBUSH,
 	SKINCOLOR_MOSS,
 	SKINCOLOR_AZURE,
+	SKINCOLOR_EGGPLANT,
 	SKINCOLOR_LAVENDER,
 
 	// Viv's vivid colours (toast 21/07/17)
+	// Tweaks & additions (Lach, sphere, Alice, MotorRoach 26/10/22)
 	SKINCOLOR_RUBY,
+	SKINCOLOR_CHERRY,
 	SKINCOLOR_SALMON,
 	SKINCOLOR_PEPPER,
 	SKINCOLOR_RED,
@@ -282,18 +291,24 @@ typedef enum
 	SKINCOLOR_KETCHUP,
 	SKINCOLOR_PEACHY,
 	SKINCOLOR_QUAIL,
+	SKINCOLOR_FOUNDATION,
 	SKINCOLOR_SUNSET,
 	SKINCOLOR_COPPER,
 	SKINCOLOR_APRICOT,
 	SKINCOLOR_ORANGE,
+	SKINCOLOR_PUMPKIN,
 	SKINCOLOR_RUST,
 	SKINCOLOR_GOLD,
 	SKINCOLOR_SANDY,
 	SKINCOLOR_YELLOW,
 	SKINCOLOR_OLIVE,
+	SKINCOLOR_KIWI,
+	SKINCOLOR_LEMON,
 	SKINCOLOR_LIME,
+	SKINCOLOR_MINDARO,
 	SKINCOLOR_PERIDOT,
 	SKINCOLOR_APPLE,
+	SKINCOLOR_CHARTREUSE,
 	SKINCOLOR_GREEN,
 	SKINCOLOR_FOREST,
 	SKINCOLOR_EMERALD,
@@ -301,32 +316,48 @@ typedef enum
 	SKINCOLOR_HEADLIGHT,
 	SKINCOLOR_MINT,
 	SKINCOLOR_MASTER,
+	SKINCOLOR_BOTTLE,
 	SKINCOLOR_SEAFOAM,
+	SKINCOLOR_ISLAND,
 	SKINCOLOR_AQUA,
 	SKINCOLOR_TEAL,
 	SKINCOLOR_WAVE,
 	SKINCOLOR_CYAN,
+	SKINCOLOR_TURQUOISE,
 	SKINCOLOR_SKY,
+	SKINCOLOR_MARINE,
 	SKINCOLOR_CERULEAN,
+	SKINCOLOR_DREAM,
 	SKINCOLOR_ICY,
+	SKINCOLOR_DAYBREAK,
 	SKINCOLOR_SAPPHIRE, // sweet mother, i cannot weave – slender aphrodite has overcome me with longing for a girl
+	SKINCOLOR_ARCTIC,
 	SKINCOLOR_CORNFLOWER,
 	SKINCOLOR_BLUE,
 	SKINCOLOR_COBALT,
+	SKINCOLOR_MIDNIGHT,
+	SKINCOLOR_GALAXY,
 	SKINCOLOR_VAPOR,
 	SKINCOLOR_DUSK,
+	SKINCOLOR_MAJESTY,
 	SKINCOLOR_PASTEL,
 	SKINCOLOR_PURPLE,
+	SKINCOLOR_NOBLE,
+	SKINCOLOR_FUCHSIA,
 	SKINCOLOR_BUBBLEGUM,
 	SKINCOLOR_MAGENTA,
 	SKINCOLOR_NEON,
 	SKINCOLOR_VIOLET,
+	SKINCOLOR_ROYAL,
 	SKINCOLOR_LILAC,
 	SKINCOLOR_MAUVE,
+	SKINCOLOR_EVENTIDE,
 	SKINCOLOR_PLUM,
 	SKINCOLOR_RASPBERRY,
 	SKINCOLOR_TAFFY,
 	SKINCOLOR_ROSY,
+	SKINCOLOR_FANCY,
+	SKINCOLOR_SANGRIA,
 
 	FIRSTSUPERCOLOR,
 
diff --git a/src/info.c b/src/info.c
index fbcdcfc808..10b05dee49 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21586,19 +21586,28 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	// Desaturated
 	{"Aether",   {0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x91, 0x91, 0x91, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf}, SKINCOLOR_GREY,    15, 0,           true}, // SKINCOLOR_AETHER
 	{"Slate",    {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0xaa, 0xaa, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_SILVER,  12, 0,           true}, // SKINCOLOR_SLATE
+	{"Mercury",  {   0,    3,    4,    7,   11,   12,   14,   15,  171,  172,  173,  155,  157,  159,  253,  254}, SKINCOLOR_ECRU,    15, V_AZUREMAP,  true}, // SKINCOLOR_MERCURY
 	{"Bluebell", {0x90, 0x91, 0x92, 0x93, 0x94, 0x94, 0x95, 0xac, 0xac, 0xad, 0xad, 0xa8, 0xa8, 0xa9, 0xfd, 0xfe}, SKINCOLOR_COPPER,  4,  V_BLUEMAP,   true}, // SKINCOLOR_BLUEBELL
 	{"Pink",     {0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0x2b, 0x2c, 0x2e}, SKINCOLOR_AZURE,   9,  V_REDMAP,    true}, // SKINCOLOR_PINK
+	{"Rosewood", { 209,  210,  211,  212,  213,  214,  228,  230,  232,  234,  235,  237,   26,   27,   28,   29}, SKINCOLOR_SEPIA,   5,  V_BROWNMAP,  true}, // SKINCOLOR_ROSEWOOD
 	{"Yogurt",   {0xd0, 0x30, 0xd8, 0xd9, 0xda, 0xdb, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe3, 0xe6, 0xe8, 0xe9}, SKINCOLOR_RUST,    7,  V_BROWNMAP,  true}, // SKINCOLOR_YOGURT
+	{"Latte",    {  48,  217,  219,  221,  223,  224,  226,  228,   68,   69,   70,   70,   44,   45,   46,   47}, SKINCOLOR_BOTTLE,  14, V_BROWNMAP,  true}, // SKINCOLOR_LATTE
 	{"Brown",    {0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef}, SKINCOLOR_TAN,     2,  V_BROWNMAP,  true}, // SKINCOLOR_BROWN
 	{"Bronze",   {0xde, 0xe0, 0xe1, 0xe4, 0xe7, 0xe9, 0xeb, 0xec, 0xed, 0xed, 0xed, 0x19, 0x19, 0x1b, 0x1d, 0x1e}, SKINCOLOR_KETCHUP, 0,  V_BROWNMAP,  true}, // SKINCOLOR_BRONZE
+	{"Sepia",    {  88,   84,   85,   86,  224,  226,  228,  230,  232,  235,  236,  237,  238,  239,   28,   28}, SKINCOLOR_ROSEWOOD,5,  V_BROWNMAP,  true}, // SKINCOLOR_SEPIA
+	{"Ecru",     {  80,   83,   84,   85,   86,  242,  243,  245,  230,  232,  234,  236,  238,  239,   47,   47}, SKINCOLOR_MERCURY, 15, V_BROWNMAP,  true}, // SKINCOLOR_ECRU
 	{"Tan",      {0x51, 0x51, 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0xf5, 0xf5, 0xf9, 0xf9, 0xed, 0xed}, SKINCOLOR_BROWN,   12, V_BROWNMAP,  true}, // SKINCOLOR_TAN
 	{"Beige",    {0x54, 0x55, 0x56, 0x56, 0xf2, 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xed, 0xed}, SKINCOLOR_MOSS,    5,  V_BROWNMAP,  true}, // SKINCOLOR_BEIGE
+	{"Rosebush", { 208,  216,  209,   85,   90,   91,   91,   92,  191,   93,   94,  107,  109,  110,  111,  111}, SKINCOLOR_EGGPLANT,5,  V_GREENMAP,  true}, // SKINCOLOR_ROSEBUSH
 	{"Moss",     {0x58, 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f, 0x5f}, SKINCOLOR_BEIGE,   13, V_GREENMAP,  true}, // SKINCOLOR_MOSS
 	{"Azure",    {0x90, 0x90, 0x91, 0x91, 0xaa, 0xaa, 0xab, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf}, SKINCOLOR_PINK,    5,  V_AZUREMAP,  true}, // SKINCOLOR_AZURE
+	{"Eggplant", {   4,   8,    11,   11,   16,  195,  195,  195,  196,  186,  187,  187,  254,  254,   30,   31}, SKINCOLOR_ROSEBUSH,5,  V_PURPLEMAP, true}, // SKINCOLOR_EGGPLANT
 	{"Lavender", {0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7}, SKINCOLOR_GOLD,    4,  V_PURPLEMAP, true}, // SKINCOLOR_LAVENDER
 
 	// Viv's vivid colours (toast 21/07/17)
+	// Tweaks & additions (Lach, sphere, Alice, MotorRoach 26/10/22)
 	{"Ruby",       {0xb0, 0xb0, 0xc9, 0xca, 0xcc, 0x26, 0x27, 0x28, 0x29, 0x2a, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfd}, SKINCOLOR_EMERALD,    10, V_REDMAP,     true}, // SKINCOLOR_RUBY
+	{"Cherry",     { 202,  203,  204,  205,  206,   40,   41,   42,   43,   44,  186,  187,   28,   29,   30,   31}, SKINCOLOR_MIDNIGHT,   10, V_REDMAP,     true}, // SKINCOLOR_CHERRY
 	{"Salmon",     {0xd0, 0xd0, 0xd1, 0xd2, 0x20, 0x21, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e}, SKINCOLOR_FOREST,     6,  V_REDMAP,     true}, // SKINCOLOR_SALMON
 	{"Pepper",     { 210,   32,   33,   34,   35,   35,   36,   37,   38,   39,   41,   43,   45,   45,   46,   47}, SKINCOLOR_MASTER,     8,  V_REDMAP,     true}, // SKINCOLOR_PEPPER
 	{"Red",        {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x47, 0x2e, 0x2f}, SKINCOLOR_GREEN,      10, V_REDMAP,     true}, // SKINCOLOR_RED
@@ -21607,18 +21616,24 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Ketchup",    {0x48, 0x49, 0x40, 0x33, 0x34, 0x36, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2b, 0x2c, 0x47, 0x2e, 0x2f}, SKINCOLOR_BRONZE,     8,  V_REDMAP,     true}, // SKINCOLOR_KETCHUP
 	{"Peachy",     {0xd0, 0x30, 0x31, 0x31, 0x32, 0x32, 0xdc, 0xdc, 0xdc, 0xd3, 0xd4, 0xd4, 0xcc, 0xcd, 0xce, 0xcf}, SKINCOLOR_TEAL,       7,  V_ROSYMAP,    true}, // SKINCOLOR_PEACHY
 	{"Quail",      {0xd8, 0xd9, 0xdb, 0xdc, 0xde, 0xdf, 0xd5, 0xd5, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0x1d, 0x1f}, SKINCOLOR_WAVE,       5,  V_BROWNMAP,   true}, // SKINCOLOR_QUAIL
+	{"Foundation", {  80,   81,   82,   84,  219,  221,  221,  212,  213,  214,  215,  195,  196,  186,  187,   30}, SKINCOLOR_DREAM,      9,  V_ORANGEMAP,  true}, // SKINCOLOR_FOUNDATION
 	{"Sunset",     {0x51, 0x52, 0x40, 0x40, 0x34, 0x36, 0xd5, 0xd5, 0xd6, 0xd7, 0xcf, 0xcf, 0xc6, 0xc6, 0xc7, 0xfe}, SKINCOLOR_SAPPHIRE,   5,  V_ORANGEMAP,  true}, // SKINCOLOR_SUNSET
 	{"Copper",     {0x58, 0x54, 0x40, 0x34, 0x35, 0x38, 0x3a, 0x3c, 0x3d, 0x2a, 0x2b, 0x2c, 0x2c, 0xba, 0xba, 0xbb}, SKINCOLOR_BLUEBELL,   5,  V_ORANGEMAP,  true}, // SKINCOLOR_COPPER
 	{"Apricot",    {0x00, 0xd8, 0xd9, 0xda, 0xdb, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e}, SKINCOLOR_CYAN,       4,  V_ORANGEMAP,  true}, // SKINCOLOR_APRICOT
 	{"Orange",     {  49,   50,   51,   52,   53,   54,   55,   57,   58,   59,   60,   42,   44,   45,   46,   46}, SKINCOLOR_BLUE,       4,  V_ORANGEMAP,  true}, // SKINCOLOR_ORANGE
+	{"Pumpkin",    {  51,   52,   53,   54,   56,   58,   59,   59,   61,   61,   63,   45,   46,   47,   47,   31}, SKINCOLOR_ARCTIC,     10, V_ORANGEMAP,  true}, // SKINCOLOR_PUMPKIN
 	{"Rust",       {0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3f, 0x2c, 0x2d, 0x47, 0x2e, 0x2f, 0x2f}, SKINCOLOR_YOGURT,     8,  V_ORANGEMAP,  true}, // SKINCOLOR_RUST
 	{"Gold",       {0x51, 0x51, 0x54, 0x54, 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x3f, 0x2d, 0x2e, 0x2f, 0x2f}, SKINCOLOR_LAVENDER,   10, V_YELLOWMAP,  true}, // SKINCOLOR_GOLD
 	{"Sandy",      {0x53, 0x40, 0x41, 0x42, 0x43, 0xe6, 0xe9, 0xe9, 0xea, 0xec, 0xec, 0xc6, 0xc6, 0xc7, 0xc7, 0xfe}, SKINCOLOR_SKY,        8,  V_YELLOWMAP,  true}, // SKINCOLOR_SANDY
 	{"Yellow",     {0x52, 0x53, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4e, 0x4f, 0xed}, SKINCOLOR_CORNFLOWER, 8,  V_YELLOWMAP,  true}, // SKINCOLOR_YELLOW
 	{"Olive",      {0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0xe7, 0xe7, 0xe9, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7, 0xfd}, SKINCOLOR_DUSK,       3,  V_YELLOWMAP,  true}, // SKINCOLOR_OLIVE
+	{"Kiwi",       {  88,   64,  188,  189,  189,   76,   76,   67,   67,   68,   69,   70,   45,   46,   47,   47}, SKINCOLOR_MARINE,     7,  V_PERIDOTMAP, true}, // SKINCOLOR_KIWI
+	{"Lemon",      {   0,   80,   81,   83,   73,   73,   74,   74,   76,   76,  191,  191,   79,   79,  110,  111}, SKINCOLOR_FUCHSIA,    6,  V_YELLOWMAP,  true}, // SKINCOLOR_LEMON
 	{"Lime",       {0x50, 0x51, 0x52, 0x53, 0x48, 0xbc, 0xbd, 0xbe, 0xbe, 0xbf, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_MAGENTA,    9,  V_PERIDOTMAP, true}, // SKINCOLOR_LIME
+	{"Mindaro",    {   0,   80,   81,   82,   83,   64,  188,  189,  189,  190,  115,  116,  117,  118,  119,  111}, SKINCOLOR_MAJESTY,    11, V_PERIDOTMAP, true}, // SKINCOLOR_MINDARO
 	{"Peridot",    {0x58, 0x58, 0xbc, 0xbc, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0x5e, 0x5e, 0x5f, 0x5f, 0x77, 0x77}, SKINCOLOR_COBALT,     2,  V_PERIDOTMAP, true}, // SKINCOLOR_PERIDOT
 	{"Apple",      {0x49, 0x49, 0xbc, 0xbd, 0xbe, 0xbe, 0xbe, 0x67, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6d}, SKINCOLOR_RASPBERRY,  13, V_PERIDOTMAP, true}, // SKINCOLOR_APPLE
+	{"Chartreuse", {  80,   82,   72,   73,  188,  188,  113,  114,  114,  125,  126,  137,  138,  139,  253,  254}, SKINCOLOR_NOBLE,      12, V_PERIDOTMAP, true}, // SKINCOLOR_CHARTREUSE
 	{"Green",      {0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_RED,        6,  V_GREENMAP,   true}, // SKINCOLOR_GREEN
 	{"Forest",     {0x65, 0x66, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f}, SKINCOLOR_SALMON,     9,  V_GREENMAP,   true}, // SKINCOLOR_FOREST
 	{"Emerald",    {0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77}, SKINCOLOR_RUBY,       4,  V_GREENMAP,   true}, // SKINCOLOR_EMERALD
@@ -21626,32 +21641,48 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Headlight",  {   0,   80,   81,   82,   73,   84,   64,   65,   91,   91,  124,  125,  126,  137,  138,  139}, SKINCOLOR_MAUVE,      8,  V_YELLOWMAP,  true}, // SKINCOLOR_HEADLIGHT
 	{"Mint",       {0x00, 0x00, 0x58, 0x58, 0x59, 0x62, 0x62, 0x62, 0x64, 0x67, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a}, SKINCOLOR_VIOLET,     5,  V_GREENMAP,   true}, // SKINCOLOR_MINT
 	{"Master",     {   0,   80,   88,   96,  112,  113,   99,  100,  124,  125,  126,  117,  107,  118,  119,  111}, SKINCOLOR_PEPPER,     8,  V_GREENMAP,   true}, // SKINCOLOR_MASTER
+	{"Bottle",     {   0,    1,    3,    4,    5,  140,  141,  141,  124,  125,  126,  127,  118,  119,  111,  111}, SKINCOLOR_LATTE,      14, V_AQUAMAP,    true}, // SKINCOLOR_BOTTLE
 	{"Seafoam",    {0x01, 0x58, 0x59, 0x5a, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0xfd, 0xfd}, SKINCOLOR_PLUM,       6,  V_AQUAMAP,    true}, // SKINCOLOR_SEAFOAM
+	{"Island",     {  96,   97,  113,  113,  114,  124,  142,  136,  136,  150,  151,  153,  168,  168,  169,  169}, SKINCOLOR_GALAXY,     7,  V_AQUAMAP,    true}, // SKINCOLOR_ISLAND
 	{"Aqua",       {0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x76, 0x77}, SKINCOLOR_ROSY,       7,  V_AQUAMAP,    true}, // SKINCOLOR_AQUA
 	{"Teal",       {0x78, 0x78, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0x8a}, SKINCOLOR_PEACHY,     7,  V_SKYMAP,     true}, // SKINCOLOR_TEAL
 	{"Wave",       {0x00, 0x78, 0x78, 0x79, 0x8d, 0x87, 0x88, 0x89, 0x89, 0xae, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_QUAIL,      5,  V_SKYMAP,     true}, // SKINCOLOR_WAVE
 	{"Cyan",       {0x80, 0x81, 0xff, 0xff, 0x83, 0x83, 0x8d, 0x8d, 0x8d, 0x8e, 0x7e, 0x7f, 0x76, 0x76, 0x77, 0x6e}, SKINCOLOR_APRICOT,    6,  V_SKYMAP,     true}, // SKINCOLOR_CYAN
+	{"Turquoise",  {  0,   120,  121,  122,  123,  141,  141,  135,  136,  136,  150,  153,  155,  157,  159,  253}, SKINCOLOR_SANGRIA,    7,  V_SKYMAP,     true}, // SKINCOLOR_TURQUOISE
 	{"Sky",        {0x80, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x89, 0x8a, 0x8b}, SKINCOLOR_SANDY,      1,  V_SKYMAP,     true}, // SKINCOLOR_SKY
+	{"Marine",     { 144,  146,  147,  147,  148,  135,  136,  136,  137,  137,  127,  118,  119,  111,  111,  111}, SKINCOLOR_KIWI,       10, V_SKYMAP,     true}, // SKINCOLOR_MARINE
 	{"Cerulean",   {0x85, 0x86, 0x87, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0xfd, 0xfd, 0xfd, 0x1f, 0x1f, 0x1f}, SKINCOLOR_NEON,       4,  V_SKYMAP,     true}, // SKINCOLOR_CERULEAN
+	{"Dream",      {  80,  208,  200,  200,  146,  146,  133,  134,  135,  136,  137,  138,  139,  139,  254,  254}, SKINCOLOR_FOUNDATION, 9,  V_SKYMAP,     true}, // SKINCOLOR_DREAM
 	{"Icy",        {0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0x83, 0x83, 0x86, 0x87, 0x95, 0x95, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_CRIMSON,    0,  V_SKYMAP,     true}, // SKINCOLOR_ICY
+	{"Daybreak",   {  80,   81,   82,   72,   64,    9,   11,  171,  149,  150,  151,  153,  156,  157,  159,  253}, SKINCOLOR_EVENTIDE,   12, V_BLUEMAP,    true}, // SKINCOLOR_DAYBREAK
 	{"Sapphire",   {0x80, 0x83, 0x86, 0x87, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_SUNSET,     5,  V_SKYMAP,     true}, // SKINCOLOR_SAPPHIRE
+	{"Arctic",     {   0,    1,    3,    4,  146,  146,  147,  148,  148,  149,  150,  153,  156,  159,  253,  254}, SKINCOLOR_PUMPKIN,    10, V_BLUEMAP,    true}, // SKINCOLOR_ARCTIC
 	{"Cornflower", {0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x9a, 0x9c, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e}, SKINCOLOR_YELLOW,     4,  V_BLUEMAP,    true}, // SKINCOLOR_CORNFLOWER
 	{"Blue",       {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_ORANGE,     5,  V_BLUEMAP,    true}, // SKINCOLOR_BLUE
 	{"Cobalt",     { 145,  147,  149,  150,  151,  153,  154,  155,  156,  157,  158,  159,  253,  253,  254,  254}, SKINCOLOR_PERIDOT,    5,  V_BLUEMAP,    true}, // SKINCOLOR_COBALT
+	{"Midnight",   { 171,  171,  172,  173,  173,  174,  155,  156,  157,  159,  253,  253,  254,  254,   31,   31}, SKINCOLOR_CHERRY,     10, V_GRAYMAP,    true}, // SKINCOLOR_MIDNIGHT
+	{"Galaxy",     { 160,  161,  162,  163,  164,  165,  166,  167,  154,  155,  156,  157,  159,  253,  254,   31}, SKINCOLOR_ISLAND,     7,  V_PURPLEMAP,  true}, // SKINCOLOR_GALAXY
 	{"Vapor",      {0x80, 0x81, 0x83, 0x86, 0x94, 0x94, 0xa3, 0xa3, 0xa4, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_LILAC,      4,  V_SKYMAP,     true}, // SKINCOLOR_VAPOR
 	{"Dusk",       {0x92, 0x93, 0x94, 0x94, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_OLIVE,      0,  V_BLUEMAP,    true}, // SKINCOLOR_DUSK
+	{"Majesty",    {   0,    1,  176,  160,  160,  161,  162,  162,  163,  172,  173,  174,  174,  175,  139,  139}, SKINCOLOR_MINDARO,    11, V_PURPLEMAP,  true}, // SKINCOLOR_MAJESTY
 	{"Pastel",     {0x90, 0x90, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8}, SKINCOLOR_BUBBLEGUM,  9,  V_PURPLEMAP,  true}, // SKINCOLOR_PASTEL
 	{"Purple",     {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_FLAME,      7,  V_PURPLEMAP,  true}, // SKINCOLOR_PURPLE
-	{"Bubblegum",  {0x00, 0xd0, 0xd0, 0xc8, 0xc8, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8}, SKINCOLOR_PASTEL,     8,  V_MAGENTAMAP, true}, // SKINCOLOR_BUBBLEGUM
+	{"Noble",      { 144,  146,  147,  148,  148,  164,  164,  165,  165,  185,  186,  186,  187,  187,   28,   29}, SKINCOLOR_CHARTREUSE, 12, V_PURPLEMAP,  true}, // SKINCOLOR_NOBLE
+	{"Fuchsia",    { 200,  201,  203,  204,  204,  183,  184,  184,  165,  166,  167,  168,  169,  159,  253,  254}, SKINCOLOR_LEMON,      6,  V_PURPLEMAP,  true}, // SKINCOLOR_FUCHSIA
+	{"Bubblegum",  {   0,  208,  208,  176,  177,  178,  179,  180,  181,  183,  165,  166,  167,  168,  169,  253}, SKINCOLOR_PASTEL,     8,  V_MAGENTAMAP, true}, // SKINCOLOR_BUBBLEGUM
 	{"Magenta",    {0xb3, 0xb3, 0xb4, 0xb5, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb}, SKINCOLOR_LIME,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_MAGENTA
 	{"Neon",       {0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb, 0xc7, 0xc7, 0x1d, 0x1d, 0x1e}, SKINCOLOR_CERULEAN,   2,  V_MAGENTAMAP, true}, // SKINCOLOR_NEON
 	{"Violet",     {0xd0, 0xd1, 0xd2, 0xca, 0xcc, 0xb8, 0xb9, 0xb9, 0xba, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfe, 0xfe}, SKINCOLOR_MINT,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_VIOLET
+	{"Royal",      { 208,  209,  192,  192,  192,  193,  193,  194,  194,  172,  173,  174,  175,  175,  139,  139}, SKINCOLOR_FANCY,      9,  V_PURPLEMAP,  true}, // SKINCOLOR_ROYAL
 	{"Lilac",      {0x00, 0xd0, 0xd1, 0xd2, 0xd3, 0xc1, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xfe, 0x1f}, SKINCOLOR_VAPOR,      4,  V_ROSYMAP,    true}, // SKINCOLOR_LILAC
 	{"Mauve",      { 176,  177,  178,  192,  193,  194,  195,  195,  196,  185,  185,  186,  186,  187,  187,  253}, SKINCOLOR_HEADLIGHT,  8,  V_PURPLEMAP,  true}, // SKINCOLOR_MAUVE
+	{"Eventide",   {  51,   52,   53,   33,   34,  204,  183,  183,  184,  184,  166,  167,  168,  169,  253,  254}, SKINCOLOR_DAYBREAK,   12, V_REDMAP,     true}, // SKINCOLOR_EVENTIDE
 	{"Plum",       {0xc8, 0xd3, 0xd5, 0xd6, 0xd7, 0xce, 0xcf, 0xb9, 0xb9, 0xba, 0xba, 0xa9, 0xa9, 0xa9, 0xfd, 0xfe}, SKINCOLOR_MINT,       7,  V_ROSYMAP,    true}, // SKINCOLOR_PLUM
 	{"Raspberry",  {0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xce, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfe, 0xfe}, SKINCOLOR_APPLE,      13, V_ROSYMAP,    true}, // SKINCOLOR_RASPBERRY
 	{"Taffy",      {   1,  176,  176,  177,  178,  179,  202,  203,  204,  204,  206,  207,   44,   44,   46,   47}, SKINCOLOR_JADE,       8,  V_ROSYMAP,    true}, // SKINCOLOR_TAFFY
 	{"Rosy",       {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_AQUA,       1,  V_ROSYMAP,    true}, // SKINCOLOR_ROSY
+	{"Fancy",      {   0,  208,   49,  210,  210,  202,  202,  203,  204,  204,  205,  206,  207,  207,  186,  186}, SKINCOLOR_ROYAL,      9,  V_ROSYMAP,    true}, // SKINCOLOR_FANCY
+	{"Sangria",    { 210,   32,   33,   34,   35,  215,  215,  207,  207,  185,  186,  186,  186,  169,  169,  253}, SKINCOLOR_TURQUOISE,  7,  V_ROSYMAP,    true}, // SKINCOLOR_SANGRIA
 
 	// super
 	{"Super Silver 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03}, SKINCOLOR_BLACK, 15, 0,         false}, // SKINCOLOR_SUPERSILVER1
-- 
GitLab


From edbfb92b1128d628177a80c201ecde5a09df267f Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 27 Oct 2022 00:37:15 +0200
Subject: [PATCH 141/518] Correct tabs & spacing

---
 src/deh_tables.c | 24 ++++++++++++------------
 src/doomdef.h    |  8 ++++----
 src/info.c       | 38 +++++++++++++++++++-------------------
 3 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index 7ee78218b3..d349e35d09 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4612,20 +4612,20 @@ const char *COLOR_ENUMS[] = {
 	"MERCURY",     	// SKINCOLOR_MERCURY,
 	"BLUEBELL",   	// SKINCOLOR_BLUEBELL,
 	"PINK",     	// SKINCOLOR_PINK,
-	"ROSEWOOD",     	// SKINCOLOR_ROSEWOOD,
+	"ROSEWOOD",   	// SKINCOLOR_ROSEWOOD,
 	"YOGURT",     	// SKINCOLOR_YOGURT,
 	"LATTE",     	// SKINCOLOR_LATTE,
 	"BROWN",     	// SKINCOLOR_BROWN,
 	"BRONZE",     	// SKINCOLOR_BRONZE,
 	"SEPIA",     	// SKINCOLOR_SEPIA,
 	"ECRU",     	// SKINCOLOR_ECRU,
-	"TAN",     		// SKINCOLOR_TAN,
+	"TAN",      	// SKINCOLOR_TAN,
 	"BEIGE",     	// SKINCOLOR_BEIGE,
-	"ROSEBUSH",   // SKINCOLOR_ROSEBUSH,
+	"ROSEBUSH",  	// SKINCOLOR_ROSEBUSH,
 	"MOSS",     	// SKINCOLOR_MOSS,
 	"AZURE",     	// SKINCOLOR_AZURE,
-	"EGGPLANT",     	// SKINCOLOR_EGGPLANT,
-	"LAVENDER",     // SKINCOLOR_LAVENDER,
+	"EGGPLANT",  	// SKINCOLOR_EGGPLANT,
+	"LAVENDER",   	// SKINCOLOR_LAVENDER,
 
 	// Viv's vivid colours (toast 21/07/17)
 	// Tweaks & additions (Lach, sphere, Alice, MotorRoach 26/10/22)
@@ -4639,7 +4639,7 @@ const char *COLOR_ENUMS[] = {
 	"KETCHUP",     	// SKINCOLOR_KETCHUP,
 	"PEACHY",     	// SKINCOLOR_PEACHY,
 	"QUAIL",     	// SKINCOLOR_QUAIL,
-	"FOUNDATION",     	// SKINCOLOR_FOUNDATION,
+	"FOUNDATION",   // SKINCOLOR_FOUNDATION,
 	"SUNSET",     	// SKINCOLOR_SUNSET,
 	"COPPER",     	// SKINCOLOR_COPPER,
 	"APRICOT",     	// SKINCOLOR_APRICOT,
@@ -4656,7 +4656,7 @@ const char *COLOR_ENUMS[] = {
 	"MINDARO",     	// SKINCOLOR_MINDARO,
 	"PERIDOT",     	// SKINCOLOR_PERIDOT,
 	"APPLE",     	// SKINCOLOR_APPLE,
-	"CHARTREUSE",    // SKINCOLOR_CHARTREUSE,
+	"CHARTREUSE",   // SKINCOLOR_CHARTREUSE,
 	"GREEN",     	// SKINCOLOR_GREEN,
 	"FOREST",     	// SKINCOLOR_FOREST,
 	"EMERALD",     	// SKINCOLOR_EMERALD,
@@ -4673,17 +4673,17 @@ const char *COLOR_ENUMS[] = {
 	"CYAN",     	// SKINCOLOR_CYAN,
 	"TURQUOISE",    // SKINCOLOR_TURQUOISE,
 	"SKY",     		// SKINCOLOR_SKY,
-	"MARINE",     		// SKINCOLOR_MARINE,
+	"MARINE",     	// SKINCOLOR_MARINE,
 	"CERULEAN",     // SKINCOLOR_CERULEAN,
-	"DREAM",     // SKINCOLOR_DREAM,
+	"DREAM",     	// SKINCOLOR_DREAM,
 	"ICY",     		// SKINCOLOR_ICY,
-	"DAYBREAK",     		// SKINCOLOR_DAYBREAK,
+	"DAYBREAK",     // SKINCOLOR_DAYBREAK,
 	"SAPPHIRE",     // SKINCOLOR_SAPPHIRE,
-	"ARCTIC",     		// SKINCOLOR_ARCTIC,
+	"ARCTIC",     	// SKINCOLOR_ARCTIC,
 	"CORNFLOWER",   // SKINCOLOR_CORNFLOWER,
 	"BLUE",     	// SKINCOLOR_BLUE,
 	"COBALT",     	// SKINCOLOR_COBALT,
-	"MIDNIGHT",     	// SKINCOLOR_MIDNIGHT,
+	"MIDNIGHT",     // SKINCOLOR_MIDNIGHT,
 	"GALAXY",     	// SKINCOLOR_GALAXY,
 	"VAPOR",     	// SKINCOLOR_VAPOR,
 	"DUSK",     	// SKINCOLOR_DUSK,
diff --git a/src/doomdef.h b/src/doomdef.h
index f21000cda0..171c4b69cb 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -264,16 +264,16 @@ typedef enum
 	SKINCOLOR_MERCURY,
 	SKINCOLOR_BLUEBELL,
 	SKINCOLOR_PINK,
-  SKINCOLOR_ROSEWOOD,
+	SKINCOLOR_ROSEWOOD,
 	SKINCOLOR_YOGURT,
-  SKINCOLOR_LATTE,
+	SKINCOLOR_LATTE,
 	SKINCOLOR_BROWN,
 	SKINCOLOR_BRONZE,
-  SKINCOLOR_SEPIA,
+	SKINCOLOR_SEPIA,
 	SKINCOLOR_ECRU,
 	SKINCOLOR_TAN,
 	SKINCOLOR_BEIGE,
-  SKINCOLOR_ROSEBUSH,
+	SKINCOLOR_ROSEBUSH,
 	SKINCOLOR_MOSS,
 	SKINCOLOR_AZURE,
 	SKINCOLOR_EGGPLANT,
diff --git a/src/info.c b/src/info.c
index 10b05dee49..ab5a0e2541 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21584,25 +21584,25 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Black",  {0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1f, 0x1f}, SKINCOLOR_WHITE,  7,  V_GRAYMAP, true}, // SKINCOLOR_BLACK
 
 	// Desaturated
-	{"Aether",   {0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x91, 0x91, 0x91, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf}, SKINCOLOR_GREY,    15, 0,           true}, // SKINCOLOR_AETHER
-	{"Slate",    {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0xaa, 0xaa, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_SILVER,  12, 0,           true}, // SKINCOLOR_SLATE
-	{"Mercury",  {   0,    3,    4,    7,   11,   12,   14,   15,  171,  172,  173,  155,  157,  159,  253,  254}, SKINCOLOR_ECRU,    15, V_AZUREMAP,  true}, // SKINCOLOR_MERCURY
-	{"Bluebell", {0x90, 0x91, 0x92, 0x93, 0x94, 0x94, 0x95, 0xac, 0xac, 0xad, 0xad, 0xa8, 0xa8, 0xa9, 0xfd, 0xfe}, SKINCOLOR_COPPER,  4,  V_BLUEMAP,   true}, // SKINCOLOR_BLUEBELL
-	{"Pink",     {0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0x2b, 0x2c, 0x2e}, SKINCOLOR_AZURE,   9,  V_REDMAP,    true}, // SKINCOLOR_PINK
-	{"Rosewood", { 209,  210,  211,  212,  213,  214,  228,  230,  232,  234,  235,  237,   26,   27,   28,   29}, SKINCOLOR_SEPIA,   5,  V_BROWNMAP,  true}, // SKINCOLOR_ROSEWOOD
-	{"Yogurt",   {0xd0, 0x30, 0xd8, 0xd9, 0xda, 0xdb, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe3, 0xe6, 0xe8, 0xe9}, SKINCOLOR_RUST,    7,  V_BROWNMAP,  true}, // SKINCOLOR_YOGURT
-	{"Latte",    {  48,  217,  219,  221,  223,  224,  226,  228,   68,   69,   70,   70,   44,   45,   46,   47}, SKINCOLOR_BOTTLE,  14, V_BROWNMAP,  true}, // SKINCOLOR_LATTE
-	{"Brown",    {0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef}, SKINCOLOR_TAN,     2,  V_BROWNMAP,  true}, // SKINCOLOR_BROWN
-	{"Bronze",   {0xde, 0xe0, 0xe1, 0xe4, 0xe7, 0xe9, 0xeb, 0xec, 0xed, 0xed, 0xed, 0x19, 0x19, 0x1b, 0x1d, 0x1e}, SKINCOLOR_KETCHUP, 0,  V_BROWNMAP,  true}, // SKINCOLOR_BRONZE
-	{"Sepia",    {  88,   84,   85,   86,  224,  226,  228,  230,  232,  235,  236,  237,  238,  239,   28,   28}, SKINCOLOR_ROSEWOOD,5,  V_BROWNMAP,  true}, // SKINCOLOR_SEPIA
-	{"Ecru",     {  80,   83,   84,   85,   86,  242,  243,  245,  230,  232,  234,  236,  238,  239,   47,   47}, SKINCOLOR_MERCURY, 15, V_BROWNMAP,  true}, // SKINCOLOR_ECRU
-	{"Tan",      {0x51, 0x51, 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0xf5, 0xf5, 0xf9, 0xf9, 0xed, 0xed}, SKINCOLOR_BROWN,   12, V_BROWNMAP,  true}, // SKINCOLOR_TAN
-	{"Beige",    {0x54, 0x55, 0x56, 0x56, 0xf2, 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xed, 0xed}, SKINCOLOR_MOSS,    5,  V_BROWNMAP,  true}, // SKINCOLOR_BEIGE
-	{"Rosebush", { 208,  216,  209,   85,   90,   91,   91,   92,  191,   93,   94,  107,  109,  110,  111,  111}, SKINCOLOR_EGGPLANT,5,  V_GREENMAP,  true}, // SKINCOLOR_ROSEBUSH
-	{"Moss",     {0x58, 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f, 0x5f}, SKINCOLOR_BEIGE,   13, V_GREENMAP,  true}, // SKINCOLOR_MOSS
-	{"Azure",    {0x90, 0x90, 0x91, 0x91, 0xaa, 0xaa, 0xab, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf}, SKINCOLOR_PINK,    5,  V_AZUREMAP,  true}, // SKINCOLOR_AZURE
-	{"Eggplant", {   4,   8,    11,   11,   16,  195,  195,  195,  196,  186,  187,  187,  254,  254,   30,   31}, SKINCOLOR_ROSEBUSH,5,  V_PURPLEMAP, true}, // SKINCOLOR_EGGPLANT
-	{"Lavender", {0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7}, SKINCOLOR_GOLD,    4,  V_PURPLEMAP, true}, // SKINCOLOR_LAVENDER
+	{"Aether",   {0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x91, 0x91, 0x91, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf}, SKINCOLOR_GREY,     15, 0,           true}, // SKINCOLOR_AETHER
+	{"Slate",    {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0xaa, 0xaa, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_SILVER,   12, 0,           true}, // SKINCOLOR_SLATE
+	{"Mercury",  {   0,    3,    4,    7,   11,   12,   14,   15,  171,  172,  173,  155,  157,  159,  253,  254}, SKINCOLOR_ECRU,     15, V_AZUREMAP,  true}, // SKINCOLOR_MERCURY
+	{"Bluebell", {0x90, 0x91, 0x92, 0x93, 0x94, 0x94, 0x95, 0xac, 0xac, 0xad, 0xad, 0xa8, 0xa8, 0xa9, 0xfd, 0xfe}, SKINCOLOR_COPPER,   4,  V_BLUEMAP,   true}, // SKINCOLOR_BLUEBELL
+	{"Pink",     {0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0x2b, 0x2c, 0x2e}, SKINCOLOR_AZURE,    9,  V_REDMAP,    true}, // SKINCOLOR_PINK
+	{"Rosewood", { 209,  210,  211,  212,  213,  214,  228,  230,  232,  234,  235,  237,   26,   27,   28,   29}, SKINCOLOR_SEPIA,    5,  V_BROWNMAP,  true}, // SKINCOLOR_ROSEWOOD
+	{"Yogurt",   {0xd0, 0x30, 0xd8, 0xd9, 0xda, 0xdb, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe3, 0xe6, 0xe8, 0xe9}, SKINCOLOR_RUST,     7,  V_BROWNMAP,  true}, // SKINCOLOR_YOGURT
+	{"Latte",    {  48,  217,  219,  221,  223,  224,  226,  228,   68,   69,   70,   70,   44,   45,   46,   47}, SKINCOLOR_BOTTLE,   14, V_BROWNMAP,  true}, // SKINCOLOR_LATTE
+	{"Brown",    {0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef}, SKINCOLOR_TAN,      2,  V_BROWNMAP,  true}, // SKINCOLOR_BROWN
+	{"Bronze",   {0xde, 0xe0, 0xe1, 0xe4, 0xe7, 0xe9, 0xeb, 0xec, 0xed, 0xed, 0xed, 0x19, 0x19, 0x1b, 0x1d, 0x1e}, SKINCOLOR_KETCHUP,  0,  V_BROWNMAP,  true}, // SKINCOLOR_BRONZE
+	{"Sepia",    {  88,   84,   85,   86,  224,  226,  228,  230,  232,  235,  236,  237,  238,  239,   28,   28}, SKINCOLOR_ROSEWOOD, 5,  V_BROWNMAP,  true}, // SKINCOLOR_SEPIA
+	{"Ecru",     {  80,   83,   84,   85,   86,  242,  243,  245,  230,  232,  234,  236,  238,  239,   47,   47}, SKINCOLOR_MERCURY,  15, V_BROWNMAP,  true}, // SKINCOLOR_ECRU
+	{"Tan",      {0x51, 0x51, 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0xf5, 0xf5, 0xf9, 0xf9, 0xed, 0xed}, SKINCOLOR_BROWN,    12, V_BROWNMAP,  true}, // SKINCOLOR_TAN
+	{"Beige",    {0x54, 0x55, 0x56, 0x56, 0xf2, 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xed, 0xed}, SKINCOLOR_MOSS,     5,  V_BROWNMAP,  true}, // SKINCOLOR_BEIGE
+	{"Rosebush", { 208,  216,  209,   85,   90,   91,   91,   92,  191,   93,   94,  107,  109,  110,  111,  111}, SKINCOLOR_EGGPLANT, 5,  V_GREENMAP,  true}, // SKINCOLOR_ROSEBUSH
+	{"Moss",     {0x58, 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f, 0x5f}, SKINCOLOR_BEIGE,    13, V_GREENMAP,  true}, // SKINCOLOR_MOSS
+	{"Azure",    {0x90, 0x90, 0x91, 0x91, 0xaa, 0xaa, 0xab, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf}, SKINCOLOR_PINK,     5,  V_AZUREMAP,  true}, // SKINCOLOR_AZURE
+	{"Eggplant", {   4,   8,    11,   11,   16,  195,  195,  195,  196,  186,  187,  187,  254,  254,   30,   31}, SKINCOLOR_ROSEBUSH, 5,  V_PURPLEMAP, true}, // SKINCOLOR_EGGPLANT
+	{"Lavender", {0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7}, SKINCOLOR_GOLD,     4,  V_PURPLEMAP, true}, // SKINCOLOR_LAVENDER
 
 	// Viv's vivid colours (toast 21/07/17)
 	// Tweaks & additions (Lach, sphere, Alice, MotorRoach 26/10/22)
-- 
GitLab


From 647e6aac12d215e2263543172dbae3619f1fb9ef Mon Sep 17 00:00:00 2001
From: Lach <54614944+lachablock@users.noreply.github.com>
Date: Thu, 27 Oct 2022 20:06:26 +1100
Subject: [PATCH 142/518] Simplify the way skincolors' invshades are used

---
 src/m_menu.c  | 2 +-
 src/p_enemy.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index f9f52335d0..c09e129988 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -8520,7 +8520,7 @@ static void M_DrawLoadGameData(void)
 					if (charskin->prefoppositecolor)
 					{
 						col = charskin->prefoppositecolor;
-						col = skincolors[col].ramp[skincolors[skincolors[col].invcolor].invshade];
+						col = skincolors[col].ramp[skincolors[charskin->prefcolor].invshade];
 					}
 					else
 					{
diff --git a/src/p_enemy.c b/src/p_enemy.c
index c7d87b88ad..fe062cffc6 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -5321,7 +5321,7 @@ void A_SignPlayer(mobj_t *actor)
 
 	actor->tracer->color = signcolor;
 	if (signcolor && signcolor < numskincolors)
-		signframe += (15 - skincolors[skincolors[signcolor].invcolor].invshade);
+		signframe += (15 - skincolors[facecolor].invshade);
 	actor->tracer->frame = signframe;
 }
 
-- 
GitLab


From 458b47464577cd23b5d1f5c3a9f88599598b1531 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 27 Oct 2022 11:22:33 +0200
Subject: [PATCH 143/518] Even more skincolor changes: - Changed Bronze - Added
 Boulder & Volcanic - Tweaked Galaxy

---
 src/deh_tables.c | 2 ++
 src/doomdef.h    | 2 ++
 src/info.c       | 8 +++++---
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index d349e35d09..ec60d7311f 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4616,6 +4616,7 @@ const char *COLOR_ENUMS[] = {
 	"YOGURT",     	// SKINCOLOR_YOGURT,
 	"LATTE",     	// SKINCOLOR_LATTE,
 	"BROWN",     	// SKINCOLOR_BROWN,
+	"BOULDER",   	// SKINCOLOR_BOULDER
 	"BRONZE",     	// SKINCOLOR_BRONZE,
 	"SEPIA",     	// SKINCOLOR_SEPIA,
 	"ECRU",     	// SKINCOLOR_ECRU,
@@ -4706,6 +4707,7 @@ const char *COLOR_ENUMS[] = {
 	"ROSY",     	// SKINCOLOR_ROSY,
 	"FANCY",  		// SKINCOLOR_FANCY,
 	"SANGRIA",     	// SKINCOLOR_SANGRIA,
+	"VOLCANIC",    	// SKINCOLOR_VOLCANIC,
 
 	// Super special awesome Super flashing colors!
 	"SUPERSILVER1",	// SKINCOLOR_SUPERSILVER1
diff --git a/src/doomdef.h b/src/doomdef.h
index 171c4b69cb..aa200274ae 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -268,6 +268,7 @@ typedef enum
 	SKINCOLOR_YOGURT,
 	SKINCOLOR_LATTE,
 	SKINCOLOR_BROWN,
+	SKINCOLOR_BOULDER,
 	SKINCOLOR_BRONZE,
 	SKINCOLOR_SEPIA,
 	SKINCOLOR_ECRU,
@@ -358,6 +359,7 @@ typedef enum
 	SKINCOLOR_ROSY,
 	SKINCOLOR_FANCY,
 	SKINCOLOR_SANGRIA,
+	SKINCOLOR_VOLCANIC,
 
 	FIRSTSUPERCOLOR,
 
diff --git a/src/info.c b/src/info.c
index ab5a0e2541..ec5a6c72d4 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21593,7 +21593,8 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Yogurt",   {0xd0, 0x30, 0xd8, 0xd9, 0xda, 0xdb, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe3, 0xe6, 0xe8, 0xe9}, SKINCOLOR_RUST,     7,  V_BROWNMAP,  true}, // SKINCOLOR_YOGURT
 	{"Latte",    {  48,  217,  219,  221,  223,  224,  226,  228,   68,   69,   70,   70,   44,   45,   46,   47}, SKINCOLOR_BOTTLE,   14, V_BROWNMAP,  true}, // SKINCOLOR_LATTE
 	{"Brown",    {0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef}, SKINCOLOR_TAN,      2,  V_BROWNMAP,  true}, // SKINCOLOR_BROWN
-	{"Bronze",   {0xde, 0xe0, 0xe1, 0xe4, 0xe7, 0xe9, 0xeb, 0xec, 0xed, 0xed, 0xed, 0x19, 0x19, 0x1b, 0x1d, 0x1e}, SKINCOLOR_KETCHUP,  0,  V_BROWNMAP,  true}, // SKINCOLOR_BRONZE
+	{"Boulder",  {0xde, 0xe0, 0xe1, 0xe4, 0xe7, 0xe9, 0xeb, 0xec, 0xed, 0xed, 0xed, 0x19, 0x19, 0x1b, 0x1d, 0x1e}, SKINCOLOR_KETCHUP,  0,  V_BROWNMAP,  true}, // SKINCOLOR_BOULDER
+	{"Bronze",   {  82,   84,   50,   51,  223,  228,  230,  232,  234,  236,  237,  238,  239,  239,   30,   31}, SKINCOLOR_VOLCANIC, 9,  V_BROWNMAP,  true}, // SKINCOLOR_BRONZE
 	{"Sepia",    {  88,   84,   85,   86,  224,  226,  228,  230,  232,  235,  236,  237,  238,  239,   28,   28}, SKINCOLOR_ROSEWOOD, 5,  V_BROWNMAP,  true}, // SKINCOLOR_SEPIA
 	{"Ecru",     {  80,   83,   84,   85,   86,  242,  243,  245,  230,  232,  234,  236,  238,  239,   47,   47}, SKINCOLOR_MERCURY,  15, V_BROWNMAP,  true}, // SKINCOLOR_ECRU
 	{"Tan",      {0x51, 0x51, 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0xf5, 0xf5, 0xf9, 0xf9, 0xed, 0xed}, SKINCOLOR_BROWN,    12, V_BROWNMAP,  true}, // SKINCOLOR_TAN
@@ -21613,7 +21614,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Red",        {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x47, 0x2e, 0x2f}, SKINCOLOR_GREEN,      10, V_REDMAP,     true}, // SKINCOLOR_RED
 	{"Crimson",    {0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x1f}, SKINCOLOR_ICY,        10, V_REDMAP,     true}, // SKINCOLOR_CRIMSON
 	{"Flame",      {0x31, 0x32, 0x33, 0x36, 0x22, 0x22, 0x25, 0x25, 0x25, 0xcd, 0xcf, 0xcf, 0xc5, 0xc5, 0xc7, 0xc7}, SKINCOLOR_PURPLE,     8,  V_REDMAP,     true}, // SKINCOLOR_FLAME
-	{"Ketchup",    {0x48, 0x49, 0x40, 0x33, 0x34, 0x36, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2b, 0x2c, 0x47, 0x2e, 0x2f}, SKINCOLOR_BRONZE,     8,  V_REDMAP,     true}, // SKINCOLOR_KETCHUP
+	{"Ketchup",    {0x48, 0x49, 0x40, 0x33, 0x34, 0x36, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2b, 0x2c, 0x47, 0x2e, 0x2f}, SKINCOLOR_BOULDER,    8,  V_REDMAP,     true}, // SKINCOLOR_KETCHUP
 	{"Peachy",     {0xd0, 0x30, 0x31, 0x31, 0x32, 0x32, 0xdc, 0xdc, 0xdc, 0xd3, 0xd4, 0xd4, 0xcc, 0xcd, 0xce, 0xcf}, SKINCOLOR_TEAL,       7,  V_ROSYMAP,    true}, // SKINCOLOR_PEACHY
 	{"Quail",      {0xd8, 0xd9, 0xdb, 0xdc, 0xde, 0xdf, 0xd5, 0xd5, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0x1d, 0x1f}, SKINCOLOR_WAVE,       5,  V_BROWNMAP,   true}, // SKINCOLOR_QUAIL
 	{"Foundation", {  80,   81,   82,   84,  219,  221,  221,  212,  213,  214,  215,  195,  196,  186,  187,   30}, SKINCOLOR_DREAM,      9,  V_ORANGEMAP,  true}, // SKINCOLOR_FOUNDATION
@@ -21661,7 +21662,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Blue",       {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_ORANGE,     5,  V_BLUEMAP,    true}, // SKINCOLOR_BLUE
 	{"Cobalt",     { 145,  147,  149,  150,  151,  153,  154,  155,  156,  157,  158,  159,  253,  253,  254,  254}, SKINCOLOR_PERIDOT,    5,  V_BLUEMAP,    true}, // SKINCOLOR_COBALT
 	{"Midnight",   { 171,  171,  172,  173,  173,  174,  155,  156,  157,  159,  253,  253,  254,  254,   31,   31}, SKINCOLOR_CHERRY,     10, V_GRAYMAP,    true}, // SKINCOLOR_MIDNIGHT
-	{"Galaxy",     { 160,  161,  162,  163,  164,  165,  166,  167,  154,  155,  156,  157,  159,  253,  254,   31}, SKINCOLOR_ISLAND,     7,  V_PURPLEMAP,  true}, // SKINCOLOR_GALAXY
+	{"Galaxy",     { 160,  161,  162,  163,  164,  165,  166,  166,  154,  155,  156,  157,  159,  253,  254,   31}, SKINCOLOR_ISLAND,     7,  V_PURPLEMAP,  true}, // SKINCOLOR_GALAXY
 	{"Vapor",      {0x80, 0x81, 0x83, 0x86, 0x94, 0x94, 0xa3, 0xa3, 0xa4, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_LILAC,      4,  V_SKYMAP,     true}, // SKINCOLOR_VAPOR
 	{"Dusk",       {0x92, 0x93, 0x94, 0x94, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_OLIVE,      0,  V_BLUEMAP,    true}, // SKINCOLOR_DUSK
 	{"Majesty",    {   0,    1,  176,  160,  160,  161,  162,  162,  163,  172,  173,  174,  174,  175,  139,  139}, SKINCOLOR_MINDARO,    11, V_PURPLEMAP,  true}, // SKINCOLOR_MAJESTY
@@ -21683,6 +21684,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Rosy",       {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_AQUA,       1,  V_ROSYMAP,    true}, // SKINCOLOR_ROSY
 	{"Fancy",      {   0,  208,   49,  210,  210,  202,  202,  203,  204,  204,  205,  206,  207,  207,  186,  186}, SKINCOLOR_ROYAL,      9,  V_ROSYMAP,    true}, // SKINCOLOR_FANCY
 	{"Sangria",    { 210,   32,   33,   34,   35,  215,  215,  207,  207,  185,  186,  186,  186,  169,  169,  253}, SKINCOLOR_TURQUOISE,  7,  V_ROSYMAP,    true}, // SKINCOLOR_SANGRIA
+	{"Volcanic",   {  35,   38,   41,   42,   44,   46,   46,  169,  169,  159,  253,  254,   30,   30,   31,   31}, SKINCOLOR_BRONZE,     9,  V_REDMAP,     true}, // SKINCOLOR_VOLCANIC
 
 	// super
 	{"Super Silver 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03}, SKINCOLOR_BLACK, 15, 0,         false}, // SKINCOLOR_SUPERSILVER1
-- 
GitLab


From 4cd85b522838403cf98e91ef1de988dcdf471144 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 27 Oct 2022 13:22:43 +0200
Subject: [PATCH 144/518] Support PgUp/PgDn for color select, similar to sound
 test & addons menu

---
 src/m_menu.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/src/m_menu.c b/src/m_menu.c
index c09e129988..d07bf62e12 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -12268,6 +12268,23 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 			}
 			break;
 
+		case KEY_PGUP:
+		case KEY_PGDN:
+			{
+				UINT8 i;
+				if (itemOn == 2) // player color
+				{
+					S_StartSound(NULL,sfx_menu1);
+					for (i = 0; i < 13; i++) // or (282-charw)/(2*indexwidth)
+					{
+						setupm_fakecolor = (choice == KEY_PGUP) ? setupm_fakecolor->prev : setupm_fakecolor->next;
+						while (!skincolors[setupm_fakecolor->color].accessible) // skip inaccessible colors
+							setupm_fakecolor = (choice == KEY_PGUP) ? setupm_fakecolor->prev : setupm_fakecolor->next;
+					}
+				}
+			}
+			break;
+
 		case KEY_ESCAPE:
 			exitmenu = true;
 			break;
-- 
GitLab


From 766c346df59f6cc8383e159458db463a9bf42895 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 29 Oct 2022 00:07:40 +0200
Subject: [PATCH 145/518] Add grid view for skincolors

---
 src/m_menu.c | 209 +++++++++++++++++++++++++++++++++++++++++----------
 src/m_menu.h |   2 +
 2 files changed, 170 insertions(+), 41 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index d07bf62e12..63b345692e 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -389,6 +389,7 @@ static void M_DrawRoomMenu(void);
 #endif
 static void M_DrawJoystick(void);
 static void M_DrawSetupMultiPlayerMenu(void);
+static void M_DrawColorRamp(INT32 x, INT32 y, INT32 w, INT32 h, skincolor_t color);
 
 // Handling functions
 static boolean M_ExitPandorasBox(void);
@@ -12010,6 +12011,7 @@ static consvar_t   *setupm_cvdefaultskin;
 static consvar_t   *setupm_cvdefaultcolor;
 static INT32        setupm_fakeskin;
 static menucolor_t *setupm_fakecolor;
+static boolean      colorgrid;
 
 static void M_DrawSetupMultiPlayerMenu(void)
 {
@@ -12059,7 +12061,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
 			'\x1D' | V_YELLOWMAP, false);
 	}
 
-	x = BASEVIDWIDTH/2;
+	x = colorgrid ? 92 : BASEVIDWIDTH/2;
 	y += 11;
 
 	// anim the player in the box
@@ -12116,61 +12118,121 @@ faildraw:
 #undef chary
 
 colordraw:
-	x = MP_PlayerSetupDef.x;
-	y += 75;
-
-	M_DrawLevelPlatterHeader(y - (lsheadingheight - 12), "Color", true, false);
-	if (itemOn == 2)
-		cursory = y;
 
-	// draw color string
-	V_DrawRightAlignedString(BASEVIDWIDTH - x, y,
-	             ((MP_PlayerSetupMenu[2].status & IT_TYPE) == IT_SPACE ? V_TRANSLUCENT : 0)|(itemOn == 2 ? V_YELLOWMAP : 0)|V_ALLOWLOWERCASE,
-	             skincolors[setupm_fakecolor->color].name);
+#define indexwidth 8
 
-	if (itemOn == 2 && (MP_PlayerSetupMenu[2].status & IT_TYPE) != IT_SPACE)
+	if (colorgrid) // Draw color grid & skip the later options
 	{
-		V_DrawCharacter(BASEVIDWIDTH - x - 10 - V_StringWidth(skincolors[setupm_fakecolor->color].name, V_ALLOWLOWERCASE) - (skullAnimCounter/5), y,
-			'\x1C' | V_YELLOWMAP, false);
-		V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y,
-			'\x1D' | V_YELLOWMAP, false);
-	}
+		UINT16 pos;
+		INT16 cx = 96, cy = 66;
+		INT16 i, j;
+		INT32 w = indexwidth; // Width of a singular color block
+		boolean stoprow = false;
+		menucolor_t *mc; // Last accessed color
 
-	y += 11;
+		x = 132;
+		y = 66;
+		pos = min(max(0, 16*((M_GetColorIndex(setupm_fakecolor->color)-1)/16) - 64), 16*(M_GetColorIndex(menucolortail->color)/16-1) - 128);
+		mc = M_GetColorFromIndex(pos);
 
-#define indexwidth 8
+		// Draw grid
+		V_DrawFill(x-2, y-2, 132, 132, 159);
+		for (j = 0; j < 8; j++)
+		{
+			for (i = 0; i < 16; i++)
+			{
+				if (skincolors[mc->color].accessible && !stoprow)
+				{
+					M_DrawColorRamp(x + i*w, y + j*16, w, 1, skincolors[mc->color]);
+					if (mc->color == setupm_fakecolor->color) // store current color position
+						{
+							cx = x + i*w;
+							cy = y + j*16;
+						}
+				}
+				mc = mc->next;
+				while (!skincolors[mc->color].accessible && !stoprow) // Find accessible color after this one
+				{
+					mc = mc->next;
+					if (mc == menucolortail) stoprow = true;
+				}
+			}
+		}
+
+		// Draw arrows, if needed
+		if (pos > 0)
+			V_DrawCharacter(264, y - (skullAnimCounter/5), '\x1A' | V_YELLOWMAP, false);
+		if (!stoprow)
+			V_DrawCharacter(264, y+120 + (skullAnimCounter/5), '\x1B' | V_YELLOWMAP, false);
+
+		// Draw cursor & current skincolor
+		V_DrawFill(cx - 2, cy - 2, 12, 20, 0);
+		V_DrawFill(cx - 1, cy - 1, 11, 19, 31);
+		V_DrawFill(    cx,     cy,  9, 17, 0);
+		M_DrawColorRamp(cx, cy, w, 1, skincolors[setupm_fakecolor->color]);
+
+		// Draw color string (with background)
+		V_DrawFill(55, 148,  74, 1, 73);
+		V_DrawFill(55, 149,  74, 1, 26);
+		M_DrawColorRamp(55, 150, 74, 1, skincolors[setupm_fakecolor->color]);
+		V_DrawRightAlignedString(x-2,166,
+								 ((MP_PlayerSetupMenu[2].status & IT_TYPE) == IT_SPACE ? V_TRANSLUCENT : 0)|(itemOn == 2 ? V_YELLOWMAP : 0)|V_ALLOWLOWERCASE,
+								 skincolors[setupm_fakecolor->color].name);
+
+		return; // Don't draw anything after this
+	}
+	else // Draw color strip & the rest of the menu options
 	{
 		const INT32 numcolors = (282-charw)/(2*indexwidth); // Number of colors per side
 		INT32 w = indexwidth; // Width of a singular color block
 		menucolor_t *mc = setupm_fakecolor->prev; // Last accessed color
-		UINT8 h;
 		INT16 i;
 
+		x = MP_PlayerSetupDef.x;
+		y += 75;
+
+		// Draw color header & string
+		M_DrawLevelPlatterHeader(y - (lsheadingheight - 12), "Color...", true, false);
+		V_DrawRightAlignedString(BASEVIDWIDTH - x, y,
+								 ((MP_PlayerSetupMenu[2].status & IT_TYPE) == IT_SPACE ? V_TRANSLUCENT : 0)|(itemOn == 2 ? V_YELLOWMAP : 0)|V_ALLOWLOWERCASE,
+								 skincolors[setupm_fakecolor->color].name);
+
+		// Draw horizontal arrows
+		if (itemOn == 2)
+		{
+			cursory = y;
+			if ((MP_PlayerSetupMenu[2].status & IT_TYPE) != IT_SPACE)
+			{
+				V_DrawCharacter(BASEVIDWIDTH - x - 10 - V_StringWidth(skincolors[setupm_fakecolor->color].name, V_ALLOWLOWERCASE) - (skullAnimCounter/5), y,
+					'\x1C' | V_YELLOWMAP, false);
+				V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y,
+					'\x1D' | V_YELLOWMAP, false);
+			}
+		}
+
 		// Draw color in the middle
 		x += numcolors*w;
-		for (h = 0; h < 16; h++)
-			V_DrawFill(x, y+h, charw, 1, skincolors[setupm_fakecolor->color].ramp[h]);
+		y += 11;
+		M_DrawColorRamp(x, y, charw, 1, skincolors[setupm_fakecolor->color]);
 
-		//Draw colors from middle to left
-		for (i=0; i<numcolors; i++) {
+		// Draw colors from middle to left
+		for (i = 0; i < numcolors; i++)
+		{
 			x -= w;
-			// Find accessible color before this one
-			while (!skincolors[mc->color].accessible)
+			while (!skincolors[mc->color].accessible) // Find accessible color before this one
 				mc = mc->prev;
-			for (h = 0; h < 16; h++)
-				V_DrawFill(x, y+h, w, 1, skincolors[mc->color].ramp[h]);
+			M_DrawColorRamp(x, y, w, 1, skincolors[mc->color]);
 			mc = mc->prev;
 		}
 
 		// Draw colors from middle to right
 		mc = setupm_fakecolor->next;
 		x += numcolors*w + charw;
-		for (i=0; i<numcolors; i++) {
-			// Find accessible color after this one
-			while (!skincolors[mc->color].accessible)
+		for (i = 0; i < numcolors; i++)
+		{
+			while (!skincolors[mc->color].accessible) // Find accessible color after this one
 				mc = mc->next;
-			for (h = 0; h < 16; h++)
-				V_DrawFill(x, y+h, w, 1, skincolors[mc->color].ramp[h]);
+			M_DrawColorRamp(x, y, w, 1, skincolors[mc->color]);
 			x += w;
 			mc = mc->next;
 		}
@@ -12195,6 +12257,13 @@ colordraw:
 		W_CachePatchName("M_CURSOR", PU_PATCH));
 }
 
+static void M_DrawColorRamp(INT32 x, INT32 y, INT32 w, INT32 h, skincolor_t color)
+{
+	UINT8 i;
+	for (i = 0; i < 16; i++)
+		V_DrawFill(x, y+(i*h), w, h, color.ramp[i]);
+}
+
 // Handle 1P/2P MP Setup
 static void M_HandleSetupMultiPlayer(INT32 choice)
 {
@@ -12205,13 +12274,25 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 	switch (choice)
 	{
 		case KEY_DOWNARROW:
-			M_NextOpt();
-			S_StartSound(NULL,sfx_menu1); // Tails
-			break;
-
 		case KEY_UPARROW:
-			M_PrevOpt();
-			S_StartSound(NULL,sfx_menu1); // Tails
+			{
+				UINT8 i;
+				if (itemOn == 2 && colorgrid)
+				{
+					for (i = 0; i < 16; i++)
+					{
+						setupm_fakecolor = (choice == KEY_UPARROW) ? setupm_fakecolor->prev : setupm_fakecolor->next;
+						while (!skincolors[setupm_fakecolor->color].accessible) // skip inaccessible colors
+							setupm_fakecolor = (choice == KEY_UPARROW) ? setupm_fakecolor->prev : setupm_fakecolor->next;
+					}
+				}
+				else if (choice == KEY_UPARROW)
+					M_PrevOpt();
+				else
+					M_NextOpt();
+
+				S_StartSound(NULL,sfx_menu1);
+			}
 			break;
 
 		case KEY_LEFTARROW:
@@ -12246,6 +12327,13 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 				COM_BufAddText (va("%s %d\n",setupm_cvdefaultcolor->name,setupm_fakecolor->color));
 				break;
 			}
+			else if (itemOn == 2)
+			{
+				if (!colorgrid)
+					S_StartSound(NULL,sfx_menu1);
+				colorgrid = !colorgrid;
+				break;
+			}
 			/* FALLTHRU */
 		case KEY_RIGHTARROW:
 			if (itemOn == 1)       //player skin
@@ -12275,7 +12363,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 				if (itemOn == 2) // player color
 				{
 					S_StartSound(NULL,sfx_menu1);
-					for (i = 0; i < 13; i++) // or (282-charw)/(2*indexwidth)
+					for (i = 0; i < (colorgrid ? 64 : 13); i++) // or (282-charw)/(2*indexwidth)
 					{
 						setupm_fakecolor = (choice == KEY_PGUP) ? setupm_fakecolor->prev : setupm_fakecolor->next;
 						while (!skincolors[setupm_fakecolor->color].accessible) // skip inaccessible colors
@@ -12286,7 +12374,10 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 			break;
 
 		case KEY_ESCAPE:
-			exitmenu = true;
+			if (itemOn == 2 && colorgrid)
+				colorgrid = false;
+			else
+				exitmenu = true;
 			break;
 
 		case KEY_BACKSPACE:
@@ -12596,6 +12687,42 @@ UINT16 M_GetColorAfter(UINT16 color) {
 	}
 }
 
+UINT16 M_GetColorIndex(UINT16 color) {
+	menucolor_t *look;
+	UINT16 i = 0;
+
+	if (color >= numskincolors) {
+		CONS_Printf("M_GetColorIndex: color %d does not exist.\n",color);
+		return 0;
+	}
+
+	for (look=menucolorhead;;look=look->next) {
+		if (look->color == color)
+			return i;
+		if (look==menucolortail)
+			return 0;
+		i++;
+	}
+}
+
+menucolor_t* M_GetColorFromIndex(UINT16 index) {
+	menucolor_t *look = menucolorhead;
+	UINT16 i = 0;
+
+	if (index >= numskincolors) {
+		CONS_Printf("M_GetColorIndex: index %d does not exist.\n",index);
+		return 0;
+	}
+
+	for (i = 0; i <= index; i++) {
+		if (look==menucolortail)
+			return menucolorhead;
+		look=look->next;
+	}
+
+	return look;
+}
+
 void M_InitPlayerSetupColors(void) {
 	UINT8 i;
 	numskincolors = SKINCOLOR_FIRSTFREESLOT;
diff --git a/src/m_menu.h b/src/m_menu.h
index 35c77cc438..c925c7f49c 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -482,6 +482,8 @@ void M_MoveColorBefore(UINT16 color, UINT16 targ);
 void M_MoveColorAfter(UINT16 color, UINT16 targ);
 UINT16 M_GetColorBefore(UINT16 color);
 UINT16 M_GetColorAfter(UINT16 color);
+UINT16 M_GetColorIndex(UINT16 color);
+menucolor_t* M_GetColorFromIndex(UINT16 index);
 void M_InitPlayerSetupColors(void);
 void M_FreePlayerSetupColors(void);
 
-- 
GitLab


From c9b6693c765fcabf1523d58ca6e6197dd0609b5b Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 29 Oct 2022 00:08:32 +0200
Subject: [PATCH 146/518] Allow pausing & rotation of player sprites in player
 setup

---
 src/m_menu.c | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 63b345692e..b4b5409b0c 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -11999,6 +11999,8 @@ static void M_HandleConnectIP(INT32 choice)
 static fixed_t    multi_tics;
 static UINT8      multi_frame;
 static UINT8      multi_spr2;
+static UINT8      multi_angle;
+static boolean    multi_paused;
 
 // this is set before entering the MultiPlayer setup menu,
 // for either player 1 or 2
@@ -12066,7 +12068,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
 
 	// anim the player in the box
 	multi_tics -= renderdeltatics;
-	while (multi_tics <= 0)
+	while (!multi_paused && multi_tics <= 0)
 	{
 		multi_frame++;
 		multi_tics += 4*FRACUNIT;
@@ -12089,8 +12091,8 @@ static void M_DrawSetupMultiPlayerMenu(void)
 		multi_frame = 0;
 
 	sprframe = &sprdef->spriteframes[multi_frame];
-	patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
-	if (sprframe->flip & 1) // Only for first sprite
+	patch = W_CachePatchNum(sprframe->lumppat[multi_angle], PU_PATCH);
+	if (sprframe->flip & 1 || multi_angle >= 5) // Only for first sprite
 		flags |= V_FLIP; // This sprite is left/right flipped!
 
 #define chary (y+64)
@@ -12408,6 +12410,20 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 			}
 			break;
 
+		case KEY_KEYPAD5:
+			multi_paused = !multi_paused;
+			break;
+
+		case KEY_KEYPAD4:
+			if (multi_angle <= 0) multi_angle = 7;
+			else multi_angle--;
+			break;
+
+		case KEY_KEYPAD6:
+			if (multi_angle >= 7) multi_angle = 0;
+			else multi_angle++;
+			break;
+
 		default:
 			if (itemOn != 0 || choice < 32 || choice > 127)
 				break;
@@ -12447,6 +12463,7 @@ static void M_SetupMultiPlayer(INT32 choice)
 
 	multi_frame = 0;
 	multi_tics = 4*FRACUNIT;
+	multi_angle = 0;
 	strcpy(setupm_name, cv_playername.string);
 
 	// set for player 1
@@ -12491,6 +12508,7 @@ static void M_SetupMultiPlayer2(INT32 choice)
 
 	multi_frame = 0;
 	multi_tics = 4*FRACUNIT;
+	multi_angle = 0;
 	strcpy (setupm_name, cv_playername2.string);
 
 	// set for splitscreen secondary player
-- 
GitLab


From 853c9958d7c044629608d5316715333016c207b5 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 31 Dec 2022 02:40:44 +0100
Subject: [PATCH 147/518] Remove unfinished sprite rotation

---
 src/m_menu.c | 21 +++++----------------
 1 file changed, 5 insertions(+), 16 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index b4b5409b0c..a1c22bb3e4 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -11999,7 +11999,6 @@ static void M_HandleConnectIP(INT32 choice)
 static fixed_t    multi_tics;
 static UINT8      multi_frame;
 static UINT8      multi_spr2;
-static UINT8      multi_angle;
 static boolean    multi_paused;
 
 // this is set before entering the MultiPlayer setup menu,
@@ -12091,8 +12090,8 @@ static void M_DrawSetupMultiPlayerMenu(void)
 		multi_frame = 0;
 
 	sprframe = &sprdef->spriteframes[multi_frame];
-	patch = W_CachePatchNum(sprframe->lumppat[multi_angle], PU_PATCH);
-	if (sprframe->flip & 1 || multi_angle >= 5) // Only for first sprite
+	patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
+	if (sprframe->flip & 1) // Only for first sprite
 		flags |= V_FLIP; // This sprite is left/right flipped!
 
 #define chary (y+64)
@@ -12410,20 +12409,10 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 			}
 			break;
 
-		case KEY_KEYPAD5:
+		case KEY_PAUSE:
 			multi_paused = !multi_paused;
 			break;
 
-		case KEY_KEYPAD4:
-			if (multi_angle <= 0) multi_angle = 7;
-			else multi_angle--;
-			break;
-
-		case KEY_KEYPAD6:
-			if (multi_angle >= 7) multi_angle = 0;
-			else multi_angle++;
-			break;
-
 		default:
 			if (itemOn != 0 || choice < 32 || choice > 127)
 				break;
@@ -12463,7 +12452,7 @@ static void M_SetupMultiPlayer(INT32 choice)
 
 	multi_frame = 0;
 	multi_tics = 4*FRACUNIT;
-	multi_angle = 0;
+
 	strcpy(setupm_name, cv_playername.string);
 
 	// set for player 1
@@ -12508,7 +12497,7 @@ static void M_SetupMultiPlayer2(INT32 choice)
 
 	multi_frame = 0;
 	multi_tics = 4*FRACUNIT;
-	multi_angle = 0;
+	
 	strcpy (setupm_name, cv_playername2.string);
 
 	// set for splitscreen secondary player
-- 
GitLab


From 5e6850a7f78ddbef1414a82b9e0395ffddfd66b4 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 31 Dec 2022 03:18:26 +0100
Subject: [PATCH 148/518] Some more color tweaks: - Added 6 new colors to match
 Chaos Emeralds - Changed Taffy & Sapphire (3rd emerald) - Renamed old Emerald
 to Shamrock

---
 src/deh_tables.c | 10 ++++++++--
 src/doomdef.h    |  8 +++++++-
 src/info.c       | 12 +++++++++---
 3 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index ec60d7311f..1344fb400b 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4609,6 +4609,7 @@ const char *COLOR_ENUMS[] = {
 	// Desaturated
 	"AETHER",     	// SKINCOLOR_AETHER,
 	"SLATE",     	// SKINCOLOR_SLATE,
+	"STONE",     	// SKINCOLOR_STONE,
 	"MERCURY",     	// SKINCOLOR_MERCURY,
 	"BLUEBELL",   	// SKINCOLOR_BLUEBELL,
 	"PINK",     	// SKINCOLOR_PINK,
@@ -4637,6 +4638,7 @@ const char *COLOR_ENUMS[] = {
 	"RED",     		// SKINCOLOR_RED,
 	"CRIMSON",     	// SKINCOLOR_CRIMSON,
 	"FLAME",     	// SKINCOLOR_FLAME,
+	"GARNET",      	// SKINCOLOR_GARNET,
 	"KETCHUP",     	// SKINCOLOR_KETCHUP,
 	"PEACHY",     	// SKINCOLOR_PEACHY,
 	"QUAIL",     	// SKINCOLOR_QUAIL,
@@ -4649,6 +4651,7 @@ const char *COLOR_ENUMS[] = {
 	"RUST",     	// SKINCOLOR_RUST,
 	"GOLD",     	// SKINCOLOR_GOLD,
 	"SANDY",     	// SKINCOLOR_SANDY,
+	"TOPAZ",     	// SKINCOLOR_TOPAZ,
 	"YELLOW",     	// SKINCOLOR_YELLOW,
 	"OLIVE",     	// SKINCOLOR_OLIVE,
 	"KIWI",     	// SKINCOLOR_KIWI,
@@ -4660,11 +4663,12 @@ const char *COLOR_ENUMS[] = {
 	"CHARTREUSE",   // SKINCOLOR_CHARTREUSE,
 	"GREEN",     	// SKINCOLOR_GREEN,
 	"FOREST",     	// SKINCOLOR_FOREST,
-	"EMERALD",     	// SKINCOLOR_EMERALD,
+	"SHAMROCK",    	// SKINCOLOR_SHAMROCK,
 	"JADE",     	// SKINCOLOR_JADE,
 	"HEADLIGHT",	// SKINCOLOR_HEADLIGHT,
 	"MINT",     	// SKINCOLOR_MINT,
 	"MASTER",     	// SKINCOLOR_MASTER,
+	"EMERALD",     	// SKINCOLOR_EMERALD,
 	"BOTTLE",     	// SKINCOLOR_BOTTLE,
 	"SEAFOAM",     	// SKINCOLOR_SEAFOAM,
 	"ISLAND",     	// SKINCOLOR_ISLAND,
@@ -4678,6 +4682,7 @@ const char *COLOR_ENUMS[] = {
 	"CERULEAN",     // SKINCOLOR_CERULEAN,
 	"DREAM",     	// SKINCOLOR_DREAM,
 	"ICY",     		// SKINCOLOR_ICY,
+	"AQUAMARINE",  	// SKINCOLOR_AQUAMARINE,
 	"DAYBREAK",     // SKINCOLOR_DAYBREAK,
 	"SAPPHIRE",     // SKINCOLOR_SAPPHIRE,
 	"ARCTIC",     	// SKINCOLOR_ARCTIC,
@@ -4693,7 +4698,8 @@ const char *COLOR_ENUMS[] = {
 	"PURPLE",     	// SKINCOLOR_PURPLE,
 	"NOBLE",     	// SKINCOLOR_NOBLE,
 	"FUCHSIA",     	// SKINCOLOR_FUCHSIA,
-	"BUBBLEGUM",    // SKINCOLOR_BUBBLEGUM,
+	"BUBBLEGUM",   	// SKINCOLOR_BUBBLEGUM,
+	"AMETHYST",    	// SKINCOLOR_AMETHYST,
 	"MAGENTA",     	// SKINCOLOR_MAGENTA,
 	"NEON",     	// SKINCOLOR_NEON,
 	"VIOLET",     	// SKINCOLOR_VIOLET,
diff --git a/src/doomdef.h b/src/doomdef.h
index aa200274ae..8bd311e6a1 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -261,6 +261,7 @@ typedef enum
 	// Desaturated
 	SKINCOLOR_AETHER,
 	SKINCOLOR_SLATE,
+	SKINCOLOR_STONE,
 	SKINCOLOR_MERCURY,
 	SKINCOLOR_BLUEBELL,
 	SKINCOLOR_PINK,
@@ -289,6 +290,7 @@ typedef enum
 	SKINCOLOR_RED,
 	SKINCOLOR_CRIMSON,
 	SKINCOLOR_FLAME,
+	SKINCOLOR_GARNET,
 	SKINCOLOR_KETCHUP,
 	SKINCOLOR_PEACHY,
 	SKINCOLOR_QUAIL,
@@ -301,6 +303,7 @@ typedef enum
 	SKINCOLOR_RUST,
 	SKINCOLOR_GOLD,
 	SKINCOLOR_SANDY,
+	SKINCOLOR_TOPAZ,
 	SKINCOLOR_YELLOW,
 	SKINCOLOR_OLIVE,
 	SKINCOLOR_KIWI,
@@ -312,11 +315,12 @@ typedef enum
 	SKINCOLOR_CHARTREUSE,
 	SKINCOLOR_GREEN,
 	SKINCOLOR_FOREST,
-	SKINCOLOR_EMERALD,
+	SKINCOLOR_SHAMROCK,
 	SKINCOLOR_JADE,
 	SKINCOLOR_HEADLIGHT,
 	SKINCOLOR_MINT,
 	SKINCOLOR_MASTER,
+	SKINCOLOR_EMERALD,
 	SKINCOLOR_BOTTLE,
 	SKINCOLOR_SEAFOAM,
 	SKINCOLOR_ISLAND,
@@ -330,6 +334,7 @@ typedef enum
 	SKINCOLOR_CERULEAN,
 	SKINCOLOR_DREAM,
 	SKINCOLOR_ICY,
+	SKINCOLOR_AQUAMARINE,
 	SKINCOLOR_DAYBREAK,
 	SKINCOLOR_SAPPHIRE, // sweet mother, i cannot weave – slender aphrodite has overcome me with longing for a girl
 	SKINCOLOR_ARCTIC,
@@ -346,6 +351,7 @@ typedef enum
 	SKINCOLOR_NOBLE,
 	SKINCOLOR_FUCHSIA,
 	SKINCOLOR_BUBBLEGUM,
+	SKINCOLOR_AMETHYST,
 	SKINCOLOR_MAGENTA,
 	SKINCOLOR_NEON,
 	SKINCOLOR_VIOLET,
diff --git a/src/info.c b/src/info.c
index ec5a6c72d4..052be8c876 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21586,6 +21586,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	// Desaturated
 	{"Aether",   {0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x91, 0x91, 0x91, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf}, SKINCOLOR_GREY,     15, 0,           true}, // SKINCOLOR_AETHER
 	{"Slate",    {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0xaa, 0xaa, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_SILVER,   12, 0,           true}, // SKINCOLOR_SLATE
+	{"Stone",    {   0,    4,    8,    9,   11,   12,   14,   15,  171,  172,  173,  174,  175,   27,   29,   31}, SKINCOLOR_TOPAZ,    15, 0,           true}, // SKINCOLOR_STONE
 	{"Mercury",  {   0,    3,    4,    7,   11,   12,   14,   15,  171,  172,  173,  155,  157,  159,  253,  254}, SKINCOLOR_ECRU,     15, V_AZUREMAP,  true}, // SKINCOLOR_MERCURY
 	{"Bluebell", {0x90, 0x91, 0x92, 0x93, 0x94, 0x94, 0x95, 0xac, 0xac, 0xad, 0xad, 0xa8, 0xa8, 0xa9, 0xfd, 0xfe}, SKINCOLOR_COPPER,   4,  V_BLUEMAP,   true}, // SKINCOLOR_BLUEBELL
 	{"Pink",     {0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0x2b, 0x2c, 0x2e}, SKINCOLOR_AZURE,    9,  V_REDMAP,    true}, // SKINCOLOR_PINK
@@ -21614,6 +21615,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Red",        {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x47, 0x2e, 0x2f}, SKINCOLOR_GREEN,      10, V_REDMAP,     true}, // SKINCOLOR_RED
 	{"Crimson",    {0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x1f}, SKINCOLOR_ICY,        10, V_REDMAP,     true}, // SKINCOLOR_CRIMSON
 	{"Flame",      {0x31, 0x32, 0x33, 0x36, 0x22, 0x22, 0x25, 0x25, 0x25, 0xcd, 0xcf, 0xcf, 0xc5, 0xc5, 0xc7, 0xc7}, SKINCOLOR_PURPLE,     8,  V_REDMAP,     true}, // SKINCOLOR_FLAME
+	{"Garnet",     {   0,   83,   50,   53,   34,   35,   37,   38,   39,   40,   42,   44,   45,   46,   47,   47}, SKINCOLOR_AQUAMARINE, 8,  V_REDMAP,     true}, // SKINCOLOR_GARNET
 	{"Ketchup",    {0x48, 0x49, 0x40, 0x33, 0x34, 0x36, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2b, 0x2c, 0x47, 0x2e, 0x2f}, SKINCOLOR_BOULDER,    8,  V_REDMAP,     true}, // SKINCOLOR_KETCHUP
 	{"Peachy",     {0xd0, 0x30, 0x31, 0x31, 0x32, 0x32, 0xdc, 0xdc, 0xdc, 0xd3, 0xd4, 0xd4, 0xcc, 0xcd, 0xce, 0xcf}, SKINCOLOR_TEAL,       7,  V_ROSYMAP,    true}, // SKINCOLOR_PEACHY
 	{"Quail",      {0xd8, 0xd9, 0xdb, 0xdc, 0xde, 0xdf, 0xd5, 0xd5, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0x1d, 0x1f}, SKINCOLOR_WAVE,       5,  V_BROWNMAP,   true}, // SKINCOLOR_QUAIL
@@ -21626,6 +21628,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Rust",       {0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3f, 0x2c, 0x2d, 0x47, 0x2e, 0x2f, 0x2f}, SKINCOLOR_YOGURT,     8,  V_ORANGEMAP,  true}, // SKINCOLOR_RUST
 	{"Gold",       {0x51, 0x51, 0x54, 0x54, 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x3f, 0x2d, 0x2e, 0x2f, 0x2f}, SKINCOLOR_LAVENDER,   10, V_YELLOWMAP,  true}, // SKINCOLOR_GOLD
 	{"Sandy",      {0x53, 0x40, 0x41, 0x42, 0x43, 0xe6, 0xe9, 0xe9, 0xea, 0xec, 0xec, 0xc6, 0xc6, 0xc7, 0xc7, 0xfe}, SKINCOLOR_SKY,        8,  V_YELLOWMAP,  true}, // SKINCOLOR_SANDY
+	{"Topaz",      {   0,   81,   72,   73,   64,   65,   66,   66,   54,   55,   57,   59,   60,   42,   43,   45}, SKINCOLOR_STONE     , 8,  V_YELLOWMAP,  true}, // SKINCOLOR_TOPAZ
 	{"Yellow",     {0x52, 0x53, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4e, 0x4f, 0xed}, SKINCOLOR_CORNFLOWER, 8,  V_YELLOWMAP,  true}, // SKINCOLOR_YELLOW
 	{"Olive",      {0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0xe7, 0xe7, 0xe9, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7, 0xfd}, SKINCOLOR_DUSK,       3,  V_YELLOWMAP,  true}, // SKINCOLOR_OLIVE
 	{"Kiwi",       {  88,   64,  188,  189,  189,   76,   76,   67,   67,   68,   69,   70,   45,   46,   47,   47}, SKINCOLOR_MARINE,     7,  V_PERIDOTMAP, true}, // SKINCOLOR_KIWI
@@ -21637,11 +21640,12 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Chartreuse", {  80,   82,   72,   73,  188,  188,  113,  114,  114,  125,  126,  137,  138,  139,  253,  254}, SKINCOLOR_NOBLE,      12, V_PERIDOTMAP, true}, // SKINCOLOR_CHARTREUSE
 	{"Green",      {0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_RED,        6,  V_GREENMAP,   true}, // SKINCOLOR_GREEN
 	{"Forest",     {0x65, 0x66, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f}, SKINCOLOR_SALMON,     9,  V_GREENMAP,   true}, // SKINCOLOR_FOREST
-	{"Emerald",    {0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77}, SKINCOLOR_RUBY,       4,  V_GREENMAP,   true}, // SKINCOLOR_EMERALD
+	{"Shamrock",   {0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77}, SKINCOLOR_AMETHYST,   4,  V_GREENMAP,   true}, // SKINCOLOR_SHAMROCK
 	{"Jade",       { 128,  120,  121,  122,  122,  113,  114,  114,  115,  116,  117,  118,  119,  110,  111,   30}, SKINCOLOR_TAFFY,      8,  V_GREENMAP,   true}, // SKINCOLOR_JADE
 	{"Headlight",  {   0,   80,   81,   82,   73,   84,   64,   65,   91,   91,  124,  125,  126,  137,  138,  139}, SKINCOLOR_MAUVE,      8,  V_YELLOWMAP,  true}, // SKINCOLOR_HEADLIGHT
 	{"Mint",       {0x00, 0x00, 0x58, 0x58, 0x59, 0x62, 0x62, 0x62, 0x64, 0x67, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a}, SKINCOLOR_VIOLET,     5,  V_GREENMAP,   true}, // SKINCOLOR_MINT
 	{"Master",     {   0,   80,   88,   96,  112,  113,   99,  100,  124,  125,  126,  117,  107,  118,  119,  111}, SKINCOLOR_PEPPER,     8,  V_GREENMAP,   true}, // SKINCOLOR_MASTER
+	{"Emerald",    {  80,   96,  112,  113,  114,  114,  125,  125,  126,  126,  137,  137,  138,  138,  139,  139}, SKINCOLOR_RUBY,       4,  V_GREENMAP,   true}, // SKINCOLOR_EMERALD
 	{"Bottle",     {   0,    1,    3,    4,    5,  140,  141,  141,  124,  125,  126,  127,  118,  119,  111,  111}, SKINCOLOR_LATTE,      14, V_AQUAMAP,    true}, // SKINCOLOR_BOTTLE
 	{"Seafoam",    {0x01, 0x58, 0x59, 0x5a, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0xfd, 0xfd}, SKINCOLOR_PLUM,       6,  V_AQUAMAP,    true}, // SKINCOLOR_SEAFOAM
 	{"Island",     {  96,   97,  113,  113,  114,  124,  142,  136,  136,  150,  151,  153,  168,  168,  169,  169}, SKINCOLOR_GALAXY,     7,  V_AQUAMAP,    true}, // SKINCOLOR_ISLAND
@@ -21655,8 +21659,9 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Cerulean",   {0x85, 0x86, 0x87, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0xfd, 0xfd, 0xfd, 0x1f, 0x1f, 0x1f}, SKINCOLOR_NEON,       4,  V_SKYMAP,     true}, // SKINCOLOR_CERULEAN
 	{"Dream",      {  80,  208,  200,  200,  146,  146,  133,  134,  135,  136,  137,  138,  139,  139,  254,  254}, SKINCOLOR_FOUNDATION, 9,  V_SKYMAP,     true}, // SKINCOLOR_DREAM
 	{"Icy",        {0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0x83, 0x83, 0x86, 0x87, 0x95, 0x95, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_CRIMSON,    0,  V_SKYMAP,     true}, // SKINCOLOR_ICY
+	{"Aquamarine", {   0,  120,  121,  131,  132,  133,  134,  134,  135,  135,  149,  149,  172,  173,  174,  175}, SKINCOLOR_GARNET,     0,  V_SKYMAP,     true}, // SKINCOLOR_AQUAMARINE
 	{"Daybreak",   {  80,   81,   82,   72,   64,    9,   11,  171,  149,  150,  151,  153,  156,  157,  159,  253}, SKINCOLOR_EVENTIDE,   12, V_BLUEMAP,    true}, // SKINCOLOR_DAYBREAK
-	{"Sapphire",   {0x80, 0x83, 0x86, 0x87, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_SUNSET,     5,  V_SKYMAP,     true}, // SKINCOLOR_SAPPHIRE
+	{"Sapphire",   {0x80, 0x82, 0x86, 0x87, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_SUNSET,     5,  V_SKYMAP,     true}, // SKINCOLOR_SAPPHIRE
 	{"Arctic",     {   0,    1,    3,    4,  146,  146,  147,  148,  148,  149,  150,  153,  156,  159,  253,  254}, SKINCOLOR_PUMPKIN,    10, V_BLUEMAP,    true}, // SKINCOLOR_ARCTIC
 	{"Cornflower", {0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x9a, 0x9c, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e}, SKINCOLOR_YELLOW,     4,  V_BLUEMAP,    true}, // SKINCOLOR_CORNFLOWER
 	{"Blue",       {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_ORANGE,     5,  V_BLUEMAP,    true}, // SKINCOLOR_BLUE
@@ -21671,6 +21676,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Noble",      { 144,  146,  147,  148,  148,  164,  164,  165,  165,  185,  186,  186,  187,  187,   28,   29}, SKINCOLOR_CHARTREUSE, 12, V_PURPLEMAP,  true}, // SKINCOLOR_NOBLE
 	{"Fuchsia",    { 200,  201,  203,  204,  204,  183,  184,  184,  165,  166,  167,  168,  169,  159,  253,  254}, SKINCOLOR_LEMON,      6,  V_PURPLEMAP,  true}, // SKINCOLOR_FUCHSIA
 	{"Bubblegum",  {   0,  208,  208,  176,  177,  178,  179,  180,  181,  183,  165,  166,  167,  168,  169,  253}, SKINCOLOR_PASTEL,     8,  V_MAGENTAMAP, true}, // SKINCOLOR_BUBBLEGUM
+	{"Amethyst",   { 252,  177,  179,  180,  181,  181,  182,  182,  183,  164,  166,  167,  167,  168,  169,  159}, SKINCOLOR_EMERALD,    8,  V_MAGENTAMAP, true}, // SKINCOLOR_AMETHYST
 	{"Magenta",    {0xb3, 0xb3, 0xb4, 0xb5, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb}, SKINCOLOR_LIME,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_MAGENTA
 	{"Neon",       {0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb, 0xc7, 0xc7, 0x1d, 0x1d, 0x1e}, SKINCOLOR_CERULEAN,   2,  V_MAGENTAMAP, true}, // SKINCOLOR_NEON
 	{"Violet",     {0xd0, 0xd1, 0xd2, 0xca, 0xcc, 0xb8, 0xb9, 0xb9, 0xba, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfe, 0xfe}, SKINCOLOR_MINT,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_VIOLET
@@ -21680,7 +21686,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Eventide",   {  51,   52,   53,   33,   34,  204,  183,  183,  184,  184,  166,  167,  168,  169,  253,  254}, SKINCOLOR_DAYBREAK,   12, V_REDMAP,     true}, // SKINCOLOR_EVENTIDE
 	{"Plum",       {0xc8, 0xd3, 0xd5, 0xd6, 0xd7, 0xce, 0xcf, 0xb9, 0xb9, 0xba, 0xba, 0xa9, 0xa9, 0xa9, 0xfd, 0xfe}, SKINCOLOR_MINT,       7,  V_ROSYMAP,    true}, // SKINCOLOR_PLUM
 	{"Raspberry",  {0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xce, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfe, 0xfe}, SKINCOLOR_APPLE,      13, V_ROSYMAP,    true}, // SKINCOLOR_RASPBERRY
-	{"Taffy",      {   1,  176,  176,  177,  178,  179,  202,  203,  204,  204,  206,  207,   44,   44,   46,   47}, SKINCOLOR_JADE,       8,  V_ROSYMAP,    true}, // SKINCOLOR_TAFFY
+	{"Taffy",      {   1,  176,  176,  177,  178,  179,  202,  203,  204,  204,  205,  206,  207,   44,   45,   46}, SKINCOLOR_JADE,       8,  V_ROSYMAP,    true}, // SKINCOLOR_TAFFY
 	{"Rosy",       {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_AQUA,       1,  V_ROSYMAP,    true}, // SKINCOLOR_ROSY
 	{"Fancy",      {   0,  208,   49,  210,  210,  202,  202,  203,  204,  204,  205,  206,  207,  207,  186,  186}, SKINCOLOR_ROYAL,      9,  V_ROSYMAP,    true}, // SKINCOLOR_FANCY
 	{"Sangria",    { 210,   32,   33,   34,   35,  215,  215,  207,  207,  185,  186,  186,  186,  169,  169,  253}, SKINCOLOR_TURQUOISE,  7,  V_ROSYMAP,    true}, // SKINCOLOR_SANGRIA
-- 
GitLab


From 70b69552f9f42ee1a94b29583f75ebc50ddb2f56 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 31 Dec 2022 14:30:47 +0100
Subject: [PATCH 149/518] Add toggle for invcolor background, plus: - Renamed
 Amethyst to Crystal - Moved some of the new colors - Tweaked a couple of
 invshades

---
 src/deh_tables.c |  6 +++---
 src/doomdef.h    |  6 +++---
 src/info.c       | 22 +++++++++++-----------
 src/m_menu.c     |  8 +++++++-
 4 files changed, 24 insertions(+), 18 deletions(-)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index 1344fb400b..d174afb128 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4649,9 +4649,9 @@ const char *COLOR_ENUMS[] = {
 	"ORANGE",     	// SKINCOLOR_ORANGE,
 	"PUMPKIN",     	// SKINCOLOR_PUMPKIN,
 	"RUST",     	// SKINCOLOR_RUST,
+	"TOPAZ",     	// SKINCOLOR_TOPAZ,
 	"GOLD",     	// SKINCOLOR_GOLD,
 	"SANDY",     	// SKINCOLOR_SANDY,
-	"TOPAZ",     	// SKINCOLOR_TOPAZ,
 	"YELLOW",     	// SKINCOLOR_YELLOW,
 	"OLIVE",     	// SKINCOLOR_OLIVE,
 	"KIWI",     	// SKINCOLOR_KIWI,
@@ -4677,12 +4677,12 @@ const char *COLOR_ENUMS[] = {
 	"WAVE",     	// SKINCOLOR_WAVE,
 	"CYAN",     	// SKINCOLOR_CYAN,
 	"TURQUOISE",    // SKINCOLOR_TURQUOISE,
+	"AQUAMARINE",  	// SKINCOLOR_AQUAMARINE,
 	"SKY",     		// SKINCOLOR_SKY,
 	"MARINE",     	// SKINCOLOR_MARINE,
 	"CERULEAN",     // SKINCOLOR_CERULEAN,
 	"DREAM",     	// SKINCOLOR_DREAM,
 	"ICY",     		// SKINCOLOR_ICY,
-	"AQUAMARINE",  	// SKINCOLOR_AQUAMARINE,
 	"DAYBREAK",     // SKINCOLOR_DAYBREAK,
 	"SAPPHIRE",     // SKINCOLOR_SAPPHIRE,
 	"ARCTIC",     	// SKINCOLOR_ARCTIC,
@@ -4699,7 +4699,7 @@ const char *COLOR_ENUMS[] = {
 	"NOBLE",     	// SKINCOLOR_NOBLE,
 	"FUCHSIA",     	// SKINCOLOR_FUCHSIA,
 	"BUBBLEGUM",   	// SKINCOLOR_BUBBLEGUM,
-	"AMETHYST",    	// SKINCOLOR_AMETHYST,
+	"CRYSTAL",    	// SKINCOLOR_CRYSTAL,
 	"MAGENTA",     	// SKINCOLOR_MAGENTA,
 	"NEON",     	// SKINCOLOR_NEON,
 	"VIOLET",     	// SKINCOLOR_VIOLET,
diff --git a/src/doomdef.h b/src/doomdef.h
index 8bd311e6a1..d19543e232 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -301,9 +301,9 @@ typedef enum
 	SKINCOLOR_ORANGE,
 	SKINCOLOR_PUMPKIN,
 	SKINCOLOR_RUST,
+	SKINCOLOR_TOPAZ,
 	SKINCOLOR_GOLD,
 	SKINCOLOR_SANDY,
-	SKINCOLOR_TOPAZ,
 	SKINCOLOR_YELLOW,
 	SKINCOLOR_OLIVE,
 	SKINCOLOR_KIWI,
@@ -329,12 +329,12 @@ typedef enum
 	SKINCOLOR_WAVE,
 	SKINCOLOR_CYAN,
 	SKINCOLOR_TURQUOISE,
+	SKINCOLOR_AQUAMARINE,
 	SKINCOLOR_SKY,
 	SKINCOLOR_MARINE,
 	SKINCOLOR_CERULEAN,
 	SKINCOLOR_DREAM,
 	SKINCOLOR_ICY,
-	SKINCOLOR_AQUAMARINE,
 	SKINCOLOR_DAYBREAK,
 	SKINCOLOR_SAPPHIRE, // sweet mother, i cannot weave – slender aphrodite has overcome me with longing for a girl
 	SKINCOLOR_ARCTIC,
@@ -351,7 +351,7 @@ typedef enum
 	SKINCOLOR_NOBLE,
 	SKINCOLOR_FUCHSIA,
 	SKINCOLOR_BUBBLEGUM,
-	SKINCOLOR_AMETHYST,
+	SKINCOLOR_CRYSTAL,
 	SKINCOLOR_MAGENTA,
 	SKINCOLOR_NEON,
 	SKINCOLOR_VIOLET,
diff --git a/src/info.c b/src/info.c
index 052be8c876..2b6d6affb5 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21592,7 +21592,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Pink",     {0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0x2b, 0x2c, 0x2e}, SKINCOLOR_AZURE,    9,  V_REDMAP,    true}, // SKINCOLOR_PINK
 	{"Rosewood", { 209,  210,  211,  212,  213,  214,  228,  230,  232,  234,  235,  237,   26,   27,   28,   29}, SKINCOLOR_SEPIA,    5,  V_BROWNMAP,  true}, // SKINCOLOR_ROSEWOOD
 	{"Yogurt",   {0xd0, 0x30, 0xd8, 0xd9, 0xda, 0xdb, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe3, 0xe6, 0xe8, 0xe9}, SKINCOLOR_RUST,     7,  V_BROWNMAP,  true}, // SKINCOLOR_YOGURT
-	{"Latte",    {  48,  217,  219,  221,  223,  224,  226,  228,   68,   69,   70,   70,   44,   45,   46,   47}, SKINCOLOR_BOTTLE,   14, V_BROWNMAP,  true}, // SKINCOLOR_LATTE
+	{"Latte",    {  48,  217,  219,  221,  223,  224,  226,  228,   68,   69,   70,   70,   44,   45,   46,   47}, SKINCOLOR_BOTTLE,   12, V_BROWNMAP,  true}, // SKINCOLOR_LATTE
 	{"Brown",    {0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef}, SKINCOLOR_TAN,      2,  V_BROWNMAP,  true}, // SKINCOLOR_BROWN
 	{"Boulder",  {0xde, 0xe0, 0xe1, 0xe4, 0xe7, 0xe9, 0xeb, 0xec, 0xed, 0xed, 0xed, 0x19, 0x19, 0x1b, 0x1d, 0x1e}, SKINCOLOR_KETCHUP,  0,  V_BROWNMAP,  true}, // SKINCOLOR_BOULDER
 	{"Bronze",   {  82,   84,   50,   51,  223,  228,  230,  232,  234,  236,  237,  238,  239,  239,   30,   31}, SKINCOLOR_VOLCANIC, 9,  V_BROWNMAP,  true}, // SKINCOLOR_BRONZE
@@ -21627,11 +21627,11 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Pumpkin",    {  51,   52,   53,   54,   56,   58,   59,   59,   61,   61,   63,   45,   46,   47,   47,   31}, SKINCOLOR_ARCTIC,     10, V_ORANGEMAP,  true}, // SKINCOLOR_PUMPKIN
 	{"Rust",       {0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3f, 0x2c, 0x2d, 0x47, 0x2e, 0x2f, 0x2f}, SKINCOLOR_YOGURT,     8,  V_ORANGEMAP,  true}, // SKINCOLOR_RUST
 	{"Gold",       {0x51, 0x51, 0x54, 0x54, 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x3f, 0x2d, 0x2e, 0x2f, 0x2f}, SKINCOLOR_LAVENDER,   10, V_YELLOWMAP,  true}, // SKINCOLOR_GOLD
+	{"Topaz",      {   0,   81,   72,   73,   64,   65,   66,   66,   54,   55,   57,   59,   60,   42,   43,   45}, SKINCOLOR_STONE     , 10, V_YELLOWMAP,  true}, // SKINCOLOR_TOPAZ
 	{"Sandy",      {0x53, 0x40, 0x41, 0x42, 0x43, 0xe6, 0xe9, 0xe9, 0xea, 0xec, 0xec, 0xc6, 0xc6, 0xc7, 0xc7, 0xfe}, SKINCOLOR_SKY,        8,  V_YELLOWMAP,  true}, // SKINCOLOR_SANDY
-	{"Topaz",      {   0,   81,   72,   73,   64,   65,   66,   66,   54,   55,   57,   59,   60,   42,   43,   45}, SKINCOLOR_STONE     , 8,  V_YELLOWMAP,  true}, // SKINCOLOR_TOPAZ
 	{"Yellow",     {0x52, 0x53, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4e, 0x4f, 0xed}, SKINCOLOR_CORNFLOWER, 8,  V_YELLOWMAP,  true}, // SKINCOLOR_YELLOW
 	{"Olive",      {0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0xe7, 0xe7, 0xe9, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7, 0xfd}, SKINCOLOR_DUSK,       3,  V_YELLOWMAP,  true}, // SKINCOLOR_OLIVE
-	{"Kiwi",       {  88,   64,  188,  189,  189,   76,   76,   67,   67,   68,   69,   70,   45,   46,   47,   47}, SKINCOLOR_MARINE,     7,  V_PERIDOTMAP, true}, // SKINCOLOR_KIWI
+	{"Kiwi",       {  88,   64,  188,  189,  189,   76,   76,   67,   67,   68,   69,   70,   45,   46,   47,   47}, SKINCOLOR_MARINE,     9,  V_PERIDOTMAP, true}, // SKINCOLOR_KIWI
 	{"Lemon",      {   0,   80,   81,   83,   73,   73,   74,   74,   76,   76,  191,  191,   79,   79,  110,  111}, SKINCOLOR_FUCHSIA,    6,  V_YELLOWMAP,  true}, // SKINCOLOR_LEMON
 	{"Lime",       {0x50, 0x51, 0x52, 0x53, 0x48, 0xbc, 0xbd, 0xbe, 0xbe, 0xbf, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_MAGENTA,    9,  V_PERIDOTMAP, true}, // SKINCOLOR_LIME
 	{"Mindaro",    {   0,   80,   81,   82,   83,   64,  188,  189,  189,  190,  115,  116,  117,  118,  119,  111}, SKINCOLOR_MAJESTY,    11, V_PERIDOTMAP, true}, // SKINCOLOR_MINDARO
@@ -21640,13 +21640,13 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Chartreuse", {  80,   82,   72,   73,  188,  188,  113,  114,  114,  125,  126,  137,  138,  139,  253,  254}, SKINCOLOR_NOBLE,      12, V_PERIDOTMAP, true}, // SKINCOLOR_CHARTREUSE
 	{"Green",      {0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_RED,        6,  V_GREENMAP,   true}, // SKINCOLOR_GREEN
 	{"Forest",     {0x65, 0x66, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f}, SKINCOLOR_SALMON,     9,  V_GREENMAP,   true}, // SKINCOLOR_FOREST
-	{"Shamrock",   {0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77}, SKINCOLOR_AMETHYST,   4,  V_GREENMAP,   true}, // SKINCOLOR_SHAMROCK
-	{"Jade",       { 128,  120,  121,  122,  122,  113,  114,  114,  115,  116,  117,  118,  119,  110,  111,   30}, SKINCOLOR_TAFFY,      8,  V_GREENMAP,   true}, // SKINCOLOR_JADE
+	{"Shamrock",   {0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77}, SKINCOLOR_CRYSTAL,    10, V_GREENMAP,   true}, // SKINCOLOR_SHAMROCK
+	{"Jade",       { 128,  120,  121,  122,  122,  113,  114,  114,  115,  116,  117,  118,  119,  110,  111,   30}, SKINCOLOR_TAFFY,      10, V_GREENMAP,   true}, // SKINCOLOR_JADE
 	{"Headlight",  {   0,   80,   81,   82,   73,   84,   64,   65,   91,   91,  124,  125,  126,  137,  138,  139}, SKINCOLOR_MAUVE,      8,  V_YELLOWMAP,  true}, // SKINCOLOR_HEADLIGHT
 	{"Mint",       {0x00, 0x00, 0x58, 0x58, 0x59, 0x62, 0x62, 0x62, 0x64, 0x67, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a}, SKINCOLOR_VIOLET,     5,  V_GREENMAP,   true}, // SKINCOLOR_MINT
 	{"Master",     {   0,   80,   88,   96,  112,  113,   99,  100,  124,  125,  126,  117,  107,  118,  119,  111}, SKINCOLOR_PEPPER,     8,  V_GREENMAP,   true}, // SKINCOLOR_MASTER
-	{"Emerald",    {  80,   96,  112,  113,  114,  114,  125,  125,  126,  126,  137,  137,  138,  138,  139,  139}, SKINCOLOR_RUBY,       4,  V_GREENMAP,   true}, // SKINCOLOR_EMERALD
-	{"Bottle",     {   0,    1,    3,    4,    5,  140,  141,  141,  124,  125,  126,  127,  118,  119,  111,  111}, SKINCOLOR_LATTE,      14, V_AQUAMAP,    true}, // SKINCOLOR_BOTTLE
+	{"Emerald",    {  80,   96,  112,  113,  114,  114,  125,  125,  126,  126,  137,  137,  138,  138,  139,  139}, SKINCOLOR_RUBY,       7,  V_GREENMAP,   true}, // SKINCOLOR_EMERALD
+	{"Bottle",     {   0,    1,    3,    4,    5,  140,  141,  141,  124,  125,  126,  127,  118,  119,  111,  111}, SKINCOLOR_LATTE,      12, V_AQUAMAP,    true}, // SKINCOLOR_BOTTLE
 	{"Seafoam",    {0x01, 0x58, 0x59, 0x5a, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0xfd, 0xfd}, SKINCOLOR_PLUM,       6,  V_AQUAMAP,    true}, // SKINCOLOR_SEAFOAM
 	{"Island",     {  96,   97,  113,  113,  114,  124,  142,  136,  136,  150,  151,  153,  168,  168,  169,  169}, SKINCOLOR_GALAXY,     7,  V_AQUAMAP,    true}, // SKINCOLOR_ISLAND
 	{"Aqua",       {0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x76, 0x77}, SKINCOLOR_ROSY,       7,  V_AQUAMAP,    true}, // SKINCOLOR_AQUA
@@ -21654,12 +21654,12 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Wave",       {0x00, 0x78, 0x78, 0x79, 0x8d, 0x87, 0x88, 0x89, 0x89, 0xae, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_QUAIL,      5,  V_SKYMAP,     true}, // SKINCOLOR_WAVE
 	{"Cyan",       {0x80, 0x81, 0xff, 0xff, 0x83, 0x83, 0x8d, 0x8d, 0x8d, 0x8e, 0x7e, 0x7f, 0x76, 0x76, 0x77, 0x6e}, SKINCOLOR_APRICOT,    6,  V_SKYMAP,     true}, // SKINCOLOR_CYAN
 	{"Turquoise",  {  0,   120,  121,  122,  123,  141,  141,  135,  136,  136,  150,  153,  155,  157,  159,  253}, SKINCOLOR_SANGRIA,    7,  V_SKYMAP,     true}, // SKINCOLOR_TURQUOISE
+	{"Aquamarine", {   0,  120,  121,  131,  132,  133,  134,  134,  135,  135,  149,  149,  172,  173,  174,  175}, SKINCOLOR_GARNET,     8,  V_SKYMAP,     true}, // SKINCOLOR_AQUAMARINE
 	{"Sky",        {0x80, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x89, 0x8a, 0x8b}, SKINCOLOR_SANDY,      1,  V_SKYMAP,     true}, // SKINCOLOR_SKY
-	{"Marine",     { 144,  146,  147,  147,  148,  135,  136,  136,  137,  137,  127,  118,  119,  111,  111,  111}, SKINCOLOR_KIWI,       10, V_SKYMAP,     true}, // SKINCOLOR_MARINE
+	{"Marine",     { 144,  146,  147,  147,  148,  135,  136,  136,  137,  137,  127,  118,  119,  111,  111,  111}, SKINCOLOR_KIWI,       13, V_SKYMAP,     true}, // SKINCOLOR_MARINE
 	{"Cerulean",   {0x85, 0x86, 0x87, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0xfd, 0xfd, 0xfd, 0x1f, 0x1f, 0x1f}, SKINCOLOR_NEON,       4,  V_SKYMAP,     true}, // SKINCOLOR_CERULEAN
 	{"Dream",      {  80,  208,  200,  200,  146,  146,  133,  134,  135,  136,  137,  138,  139,  139,  254,  254}, SKINCOLOR_FOUNDATION, 9,  V_SKYMAP,     true}, // SKINCOLOR_DREAM
 	{"Icy",        {0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0x83, 0x83, 0x86, 0x87, 0x95, 0x95, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_CRIMSON,    0,  V_SKYMAP,     true}, // SKINCOLOR_ICY
-	{"Aquamarine", {   0,  120,  121,  131,  132,  133,  134,  134,  135,  135,  149,  149,  172,  173,  174,  175}, SKINCOLOR_GARNET,     0,  V_SKYMAP,     true}, // SKINCOLOR_AQUAMARINE
 	{"Daybreak",   {  80,   81,   82,   72,   64,    9,   11,  171,  149,  150,  151,  153,  156,  157,  159,  253}, SKINCOLOR_EVENTIDE,   12, V_BLUEMAP,    true}, // SKINCOLOR_DAYBREAK
 	{"Sapphire",   {0x80, 0x82, 0x86, 0x87, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_SUNSET,     5,  V_SKYMAP,     true}, // SKINCOLOR_SAPPHIRE
 	{"Arctic",     {   0,    1,    3,    4,  146,  146,  147,  148,  148,  149,  150,  153,  156,  159,  253,  254}, SKINCOLOR_PUMPKIN,    10, V_BLUEMAP,    true}, // SKINCOLOR_ARCTIC
@@ -21676,14 +21676,14 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Noble",      { 144,  146,  147,  148,  148,  164,  164,  165,  165,  185,  186,  186,  187,  187,   28,   29}, SKINCOLOR_CHARTREUSE, 12, V_PURPLEMAP,  true}, // SKINCOLOR_NOBLE
 	{"Fuchsia",    { 200,  201,  203,  204,  204,  183,  184,  184,  165,  166,  167,  168,  169,  159,  253,  254}, SKINCOLOR_LEMON,      6,  V_PURPLEMAP,  true}, // SKINCOLOR_FUCHSIA
 	{"Bubblegum",  {   0,  208,  208,  176,  177,  178,  179,  180,  181,  183,  165,  166,  167,  168,  169,  253}, SKINCOLOR_PASTEL,     8,  V_MAGENTAMAP, true}, // SKINCOLOR_BUBBLEGUM
-	{"Amethyst",   { 252,  177,  179,  180,  181,  181,  182,  182,  183,  164,  166,  167,  167,  168,  169,  159}, SKINCOLOR_EMERALD,    8,  V_MAGENTAMAP, true}, // SKINCOLOR_AMETHYST
+	{"Crystal",    { 252,  177,  179,  180,  181,  181,  182,  182,  183,  164,  166,  167,  167,  168,  169,  159}, SKINCOLOR_EMERALD,    8,  V_MAGENTAMAP, true}, // SKINCOLOR_CRYSTAL
 	{"Magenta",    {0xb3, 0xb3, 0xb4, 0xb5, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb}, SKINCOLOR_LIME,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_MAGENTA
 	{"Neon",       {0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb, 0xc7, 0xc7, 0x1d, 0x1d, 0x1e}, SKINCOLOR_CERULEAN,   2,  V_MAGENTAMAP, true}, // SKINCOLOR_NEON
 	{"Violet",     {0xd0, 0xd1, 0xd2, 0xca, 0xcc, 0xb8, 0xb9, 0xb9, 0xba, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfe, 0xfe}, SKINCOLOR_MINT,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_VIOLET
 	{"Royal",      { 208,  209,  192,  192,  192,  193,  193,  194,  194,  172,  173,  174,  175,  175,  139,  139}, SKINCOLOR_FANCY,      9,  V_PURPLEMAP,  true}, // SKINCOLOR_ROYAL
 	{"Lilac",      {0x00, 0xd0, 0xd1, 0xd2, 0xd3, 0xc1, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xfe, 0x1f}, SKINCOLOR_VAPOR,      4,  V_ROSYMAP,    true}, // SKINCOLOR_LILAC
 	{"Mauve",      { 176,  177,  178,  192,  193,  194,  195,  195,  196,  185,  185,  186,  186,  187,  187,  253}, SKINCOLOR_HEADLIGHT,  8,  V_PURPLEMAP,  true}, // SKINCOLOR_MAUVE
-	{"Eventide",   {  51,   52,   53,   33,   34,  204,  183,  183,  184,  184,  166,  167,  168,  169,  253,  254}, SKINCOLOR_DAYBREAK,   12, V_REDMAP,     true}, // SKINCOLOR_EVENTIDE
+	{"Eventide",   {  51,   52,   53,   33,   34,  204,  183,  183,  184,  184,  166,  167,  168,  169,  253,  254}, SKINCOLOR_DAYBREAK,   13, V_MAGENTAMAP, true}, // SKINCOLOR_EVENTIDE
 	{"Plum",       {0xc8, 0xd3, 0xd5, 0xd6, 0xd7, 0xce, 0xcf, 0xb9, 0xb9, 0xba, 0xba, 0xa9, 0xa9, 0xa9, 0xfd, 0xfe}, SKINCOLOR_MINT,       7,  V_ROSYMAP,    true}, // SKINCOLOR_PLUM
 	{"Raspberry",  {0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xce, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfe, 0xfe}, SKINCOLOR_APPLE,      13, V_ROSYMAP,    true}, // SKINCOLOR_RASPBERRY
 	{"Taffy",      {   1,  176,  176,  177,  178,  179,  202,  203,  204,  204,  205,  206,  207,   44,   45,   46}, SKINCOLOR_JADE,       8,  V_ROSYMAP,    true}, // SKINCOLOR_TAFFY
diff --git a/src/m_menu.c b/src/m_menu.c
index a1c22bb3e4..cf61e7fb1a 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -12000,6 +12000,7 @@ static fixed_t    multi_tics;
 static UINT8      multi_frame;
 static UINT8      multi_spr2;
 static boolean    multi_paused;
+static boolean    multi_invcolor;
 
 // this is set before entering the MultiPlayer setup menu,
 // for either player 1 or 2
@@ -12076,7 +12077,8 @@ static void M_DrawSetupMultiPlayerMenu(void)
 #define charw 74
 
 	// draw box around character
-	V_DrawFill(x-(charw/2), y, charw, 84, 159);
+	V_DrawFill(x-(charw/2), y, charw, 84,
+		multi_invcolor ?skincolors[skincolors[setupm_fakecolor->color].invcolor].ramp[skincolors[setupm_fakecolor->color].invshade] : 159);
 
 	sprdef = &skins[setupm_fakeskin].sprites[multi_spr2];
 
@@ -12413,6 +12415,10 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 			multi_paused = !multi_paused;
 			break;
 
+		case KEY_INS:
+			multi_invcolor = !multi_invcolor;
+			break;
+
 		default:
 			if (itemOn != 0 || choice < 32 || choice > 127)
 				break;
-- 
GitLab


From 1bd593d17337a2664c58c46c316613cde8e48781 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 3 Jan 2023 23:08:39 +0100
Subject: [PATCH 150/518] Use extra colors for very high NiGHTS links, plus: -
 Replaced Mindaro with Goldenrod - Changed Topaz, and fixed inconsistency with
 its placement - Small tweaks to Kiwi, Seafoam, Noble and Sangria - Changed
 other NiGHTS link colors a little (& removed old iterations)

---
 src/deh_tables.c |  4 ++--
 src/doomdef.h    |  4 ++--
 src/info.c       | 14 +++++++-------
 src/st_stuff.c   | 38 ++++++++------------------------------
 4 files changed, 19 insertions(+), 41 deletions(-)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index d174afb128..268a18ad4f 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4649,15 +4649,15 @@ const char *COLOR_ENUMS[] = {
 	"ORANGE",     	// SKINCOLOR_ORANGE,
 	"PUMPKIN",     	// SKINCOLOR_PUMPKIN,
 	"RUST",     	// SKINCOLOR_RUST,
-	"TOPAZ",     	// SKINCOLOR_TOPAZ,
 	"GOLD",     	// SKINCOLOR_GOLD,
+	"TOPAZ",     	// SKINCOLOR_TOPAZ,
 	"SANDY",     	// SKINCOLOR_SANDY,
+	"GOLDENROD",   	// SKINCOLOR_GOLDENROD,
 	"YELLOW",     	// SKINCOLOR_YELLOW,
 	"OLIVE",     	// SKINCOLOR_OLIVE,
 	"KIWI",     	// SKINCOLOR_KIWI,
 	"LEMON",     	// SKINCOLOR_LEMON,
 	"LIME",     	// SKINCOLOR_LIME,
-	"MINDARO",     	// SKINCOLOR_MINDARO,
 	"PERIDOT",     	// SKINCOLOR_PERIDOT,
 	"APPLE",     	// SKINCOLOR_APPLE,
 	"CHARTREUSE",   // SKINCOLOR_CHARTREUSE,
diff --git a/src/doomdef.h b/src/doomdef.h
index d19543e232..31cdac9c4e 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -301,15 +301,15 @@ typedef enum
 	SKINCOLOR_ORANGE,
 	SKINCOLOR_PUMPKIN,
 	SKINCOLOR_RUST,
-	SKINCOLOR_TOPAZ,
 	SKINCOLOR_GOLD,
+	SKINCOLOR_TOPAZ,
 	SKINCOLOR_SANDY,
+	SKINCOLOR_GOLDENROD,
 	SKINCOLOR_YELLOW,
 	SKINCOLOR_OLIVE,
 	SKINCOLOR_KIWI,
 	SKINCOLOR_LEMON,
 	SKINCOLOR_LIME,
-	SKINCOLOR_MINDARO,
 	SKINCOLOR_PERIDOT,
 	SKINCOLOR_APPLE,
 	SKINCOLOR_CHARTREUSE,
diff --git a/src/info.c b/src/info.c
index 2b6d6affb5..085966b9c5 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21627,14 +21627,14 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Pumpkin",    {  51,   52,   53,   54,   56,   58,   59,   59,   61,   61,   63,   45,   46,   47,   47,   31}, SKINCOLOR_ARCTIC,     10, V_ORANGEMAP,  true}, // SKINCOLOR_PUMPKIN
 	{"Rust",       {0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3f, 0x2c, 0x2d, 0x47, 0x2e, 0x2f, 0x2f}, SKINCOLOR_YOGURT,     8,  V_ORANGEMAP,  true}, // SKINCOLOR_RUST
 	{"Gold",       {0x51, 0x51, 0x54, 0x54, 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x3f, 0x2d, 0x2e, 0x2f, 0x2f}, SKINCOLOR_LAVENDER,   10, V_YELLOWMAP,  true}, // SKINCOLOR_GOLD
-	{"Topaz",      {   0,   81,   72,   73,   64,   65,   66,   66,   54,   55,   57,   59,   60,   42,   43,   45}, SKINCOLOR_STONE     , 10, V_YELLOWMAP,  true}, // SKINCOLOR_TOPAZ
+	{"Topaz",      {   0,   81,   83,   73,   74,   74,   65,   52,   53,   54,   56,   58,   60,   42,   43,   45}, SKINCOLOR_STONE     , 10, V_YELLOWMAP,  true}, // SKINCOLOR_TOPAZ
 	{"Sandy",      {0x53, 0x40, 0x41, 0x42, 0x43, 0xe6, 0xe9, 0xe9, 0xea, 0xec, 0xec, 0xc6, 0xc6, 0xc7, 0xc7, 0xfe}, SKINCOLOR_SKY,        8,  V_YELLOWMAP,  true}, // SKINCOLOR_SANDY
+	{"Goldenrod",  {   0,   80,   81,   81,   83,   73,   73,   64,   65,   66,   67,   68,   69,   62,   44,   45}, SKINCOLOR_MAJESTY,    11, V_YELLOWMAP,  true}, // SKINCOLOR_GOLDENROD
 	{"Yellow",     {0x52, 0x53, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4e, 0x4f, 0xed}, SKINCOLOR_CORNFLOWER, 8,  V_YELLOWMAP,  true}, // SKINCOLOR_YELLOW
 	{"Olive",      {0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0xe7, 0xe7, 0xe9, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7, 0xfd}, SKINCOLOR_DUSK,       3,  V_YELLOWMAP,  true}, // SKINCOLOR_OLIVE
-	{"Kiwi",       {  88,   64,  188,  189,  189,   76,   76,   67,   67,   68,   69,   70,   45,   46,   47,   47}, SKINCOLOR_MARINE,     9,  V_PERIDOTMAP, true}, // SKINCOLOR_KIWI
+	{"Kiwi",       {  88,   89,  188,  189,  189,   76,   76,   67,   67,   68,   69,   70,   45,   46,   47,   47}, SKINCOLOR_MARINE,     9,  V_PERIDOTMAP, true}, // SKINCOLOR_KIWI
 	{"Lemon",      {   0,   80,   81,   83,   73,   73,   74,   74,   76,   76,  191,  191,   79,   79,  110,  111}, SKINCOLOR_FUCHSIA,    6,  V_YELLOWMAP,  true}, // SKINCOLOR_LEMON
 	{"Lime",       {0x50, 0x51, 0x52, 0x53, 0x48, 0xbc, 0xbd, 0xbe, 0xbe, 0xbf, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_MAGENTA,    9,  V_PERIDOTMAP, true}, // SKINCOLOR_LIME
-	{"Mindaro",    {   0,   80,   81,   82,   83,   64,  188,  189,  189,  190,  115,  116,  117,  118,  119,  111}, SKINCOLOR_MAJESTY,    11, V_PERIDOTMAP, true}, // SKINCOLOR_MINDARO
 	{"Peridot",    {0x58, 0x58, 0xbc, 0xbc, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0x5e, 0x5e, 0x5f, 0x5f, 0x77, 0x77}, SKINCOLOR_COBALT,     2,  V_PERIDOTMAP, true}, // SKINCOLOR_PERIDOT
 	{"Apple",      {0x49, 0x49, 0xbc, 0xbd, 0xbe, 0xbe, 0xbe, 0x67, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6d}, SKINCOLOR_RASPBERRY,  13, V_PERIDOTMAP, true}, // SKINCOLOR_APPLE
 	{"Chartreuse", {  80,   82,   72,   73,  188,  188,  113,  114,  114,  125,  126,  137,  138,  139,  253,  254}, SKINCOLOR_NOBLE,      12, V_PERIDOTMAP, true}, // SKINCOLOR_CHARTREUSE
@@ -21647,7 +21647,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Master",     {   0,   80,   88,   96,  112,  113,   99,  100,  124,  125,  126,  117,  107,  118,  119,  111}, SKINCOLOR_PEPPER,     8,  V_GREENMAP,   true}, // SKINCOLOR_MASTER
 	{"Emerald",    {  80,   96,  112,  113,  114,  114,  125,  125,  126,  126,  137,  137,  138,  138,  139,  139}, SKINCOLOR_RUBY,       7,  V_GREENMAP,   true}, // SKINCOLOR_EMERALD
 	{"Bottle",     {   0,    1,    3,    4,    5,  140,  141,  141,  124,  125,  126,  127,  118,  119,  111,  111}, SKINCOLOR_LATTE,      12, V_AQUAMAP,    true}, // SKINCOLOR_BOTTLE
-	{"Seafoam",    {0x01, 0x58, 0x59, 0x5a, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0xfd, 0xfd}, SKINCOLOR_PLUM,       6,  V_AQUAMAP,    true}, // SKINCOLOR_SEAFOAM
+	{"Seafoam",    {0x01, 0x58, 0x59, 0x5a, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a, 0x8b, 0xfd, 0xfd}, SKINCOLOR_PLUM,       6,  V_AQUAMAP,    true}, // SKINCOLOR_SEAFOAM
 	{"Island",     {  96,   97,  113,  113,  114,  124,  142,  136,  136,  150,  151,  153,  168,  168,  169,  169}, SKINCOLOR_GALAXY,     7,  V_AQUAMAP,    true}, // SKINCOLOR_ISLAND
 	{"Aqua",       {0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x76, 0x77}, SKINCOLOR_ROSY,       7,  V_AQUAMAP,    true}, // SKINCOLOR_AQUA
 	{"Teal",       {0x78, 0x78, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0x8a}, SKINCOLOR_PEACHY,     7,  V_SKYMAP,     true}, // SKINCOLOR_TEAL
@@ -21670,10 +21670,10 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Galaxy",     { 160,  161,  162,  163,  164,  165,  166,  166,  154,  155,  156,  157,  159,  253,  254,   31}, SKINCOLOR_ISLAND,     7,  V_PURPLEMAP,  true}, // SKINCOLOR_GALAXY
 	{"Vapor",      {0x80, 0x81, 0x83, 0x86, 0x94, 0x94, 0xa3, 0xa3, 0xa4, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_LILAC,      4,  V_SKYMAP,     true}, // SKINCOLOR_VAPOR
 	{"Dusk",       {0x92, 0x93, 0x94, 0x94, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_OLIVE,      0,  V_BLUEMAP,    true}, // SKINCOLOR_DUSK
-	{"Majesty",    {   0,    1,  176,  160,  160,  161,  162,  162,  163,  172,  173,  174,  174,  175,  139,  139}, SKINCOLOR_MINDARO,    11, V_PURPLEMAP,  true}, // SKINCOLOR_MAJESTY
+	{"Majesty",    {   0,    1,  176,  160,  160,  161,  162,  162,  163,  172,  173,  174,  174,  175,  139,  139}, SKINCOLOR_GOLDENROD,  11, V_PURPLEMAP,  true}, // SKINCOLOR_MAJESTY
 	{"Pastel",     {0x90, 0x90, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8}, SKINCOLOR_BUBBLEGUM,  9,  V_PURPLEMAP,  true}, // SKINCOLOR_PASTEL
 	{"Purple",     {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_FLAME,      7,  V_PURPLEMAP,  true}, // SKINCOLOR_PURPLE
-	{"Noble",      { 144,  146,  147,  148,  148,  164,  164,  165,  165,  185,  186,  186,  187,  187,   28,   29}, SKINCOLOR_CHARTREUSE, 12, V_PURPLEMAP,  true}, // SKINCOLOR_NOBLE
+	{"Noble",      { 144,  146,  147,  148,  149,  164,  164,  165,  166,  185,  186,  186,  187,  187,   28,   29}, SKINCOLOR_CHARTREUSE, 12, V_PURPLEMAP,  true}, // SKINCOLOR_NOBLE
 	{"Fuchsia",    { 200,  201,  203,  204,  204,  183,  184,  184,  165,  166,  167,  168,  169,  159,  253,  254}, SKINCOLOR_LEMON,      6,  V_PURPLEMAP,  true}, // SKINCOLOR_FUCHSIA
 	{"Bubblegum",  {   0,  208,  208,  176,  177,  178,  179,  180,  181,  183,  165,  166,  167,  168,  169,  253}, SKINCOLOR_PASTEL,     8,  V_MAGENTAMAP, true}, // SKINCOLOR_BUBBLEGUM
 	{"Crystal",    { 252,  177,  179,  180,  181,  181,  182,  182,  183,  164,  166,  167,  167,  168,  169,  159}, SKINCOLOR_EMERALD,    8,  V_MAGENTAMAP, true}, // SKINCOLOR_CRYSTAL
@@ -21689,7 +21689,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Taffy",      {   1,  176,  176,  177,  178,  179,  202,  203,  204,  204,  205,  206,  207,   44,   45,   46}, SKINCOLOR_JADE,       8,  V_ROSYMAP,    true}, // SKINCOLOR_TAFFY
 	{"Rosy",       {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_AQUA,       1,  V_ROSYMAP,    true}, // SKINCOLOR_ROSY
 	{"Fancy",      {   0,  208,   49,  210,  210,  202,  202,  203,  204,  204,  205,  206,  207,  207,  186,  186}, SKINCOLOR_ROYAL,      9,  V_ROSYMAP,    true}, // SKINCOLOR_FANCY
-	{"Sangria",    { 210,   32,   33,   34,   35,  215,  215,  207,  207,  185,  186,  186,  186,  169,  169,  253}, SKINCOLOR_TURQUOISE,  7,  V_ROSYMAP,    true}, // SKINCOLOR_SANGRIA
+	{"Sangria",    { 210,   32,   33,   34,   34,  215,  215,  207,  207,  185,  186,  186,  186,  169,  169,  253}, SKINCOLOR_TURQUOISE,  7,  V_ROSYMAP,    true}, // SKINCOLOR_SANGRIA
 	{"Volcanic",   {  35,   38,   41,   42,   44,   46,   46,  169,  169,  159,  253,  254,   30,   30,   31,   31}, SKINCOLOR_BRONZE,     9,  V_REDMAP,     true}, // SKINCOLOR_VOLCANIC
 
 	// super
diff --git a/src/st_stuff.c b/src/st_stuff.c
index dcab2bb9c5..484216e30d 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -1751,43 +1751,21 @@ static void ST_drawNightsRecords(void)
 	}
 }
 
-// 2.0-1: [21:42] <+Rob> Beige - Lavender - Steel Blue - Peach - Orange - Purple - Silver - Yellow - Pink - Red - Blue - Green - Cyan - Gold
-/*#define NUMLINKCOLORS 14
-static skincolornum_t linkColor[NUMLINKCOLORS] =
-{SKINCOLOR_BEIGE,  SKINCOLOR_LAVENDER, SKINCOLOR_AZURE, SKINCOLOR_PEACH, SKINCOLOR_ORANGE,
- SKINCOLOR_MAGENTA, SKINCOLOR_SILVER, SKINCOLOR_SUPERGOLD4, SKINCOLOR_PINK,  SKINCOLOR_RED,
- SKINCOLOR_BLUE, SKINCOLOR_GREEN, SKINCOLOR_CYAN, SKINCOLOR_GOLD};*/
-
-// 2.2 indev list: (unix time 1470866042) <Rob> Emerald, Aqua, Cyan, Blue, Pastel, Purple, Magenta, Rosy, Red, Orange, Gold, Yellow, Peridot
-/*#define NUMLINKCOLORS 13
-static skincolornum_t linkColor[NUMLINKCOLORS] =
-{SKINCOLOR_EMERALD, SKINCOLOR_AQUA, SKINCOLOR_CYAN, SKINCOLOR_BLUE, SKINCOLOR_PASTEL,
- SKINCOLOR_PURPLE, SKINCOLOR_MAGENTA, SKINCOLOR_ROSY, SKINCOLOR_RED,  SKINCOLOR_ORANGE,
- SKINCOLOR_GOLD, SKINCOLOR_YELLOW, SKINCOLOR_PERIDOT};*/
-
-// 2.2 indev list again: [19:59:52] <baldobo> Ruby > Red > Flame > Sunset > Orange > Gold > Yellow > Lime > Green > Aqua  > cyan > Sky > Blue > Pastel > Purple > Bubblegum > Magenta > Rosy > repeat
-// [20:00:25] <baldobo> Also Icy for the link freeze text color
-// [20:04:03] <baldobo> I would start it on lime
-/*#define NUMLINKCOLORS 18
-static skincolornum_t linkColor[NUMLINKCOLORS] =
-{SKINCOLOR_LIME, SKINCOLOR_EMERALD, SKINCOLOR_AQUA, SKINCOLOR_CYAN, SKINCOLOR_SKY,
- SKINCOLOR_SAPPHIRE, SKINCOLOR_PASTEL, SKINCOLOR_PURPLE, SKINCOLOR_BUBBLEGUM, SKINCOLOR_MAGENTA,
- SKINCOLOR_ROSY, SKINCOLOR_RUBY, SKINCOLOR_RED, SKINCOLOR_FLAME, SKINCOLOR_SUNSET,
- SKINCOLOR_ORANGE, SKINCOLOR_GOLD, SKINCOLOR_YELLOW};*/
-
-// 2.2+ list for real this time: https://wiki.srb2.org/wiki/User:Rob/Sandbox (check history around 31/10/17, spoopy)
+// NiGHTS link colors; 3 sets with increasingly fancy colors (1 to 299, 300 to 599, 600 and above)
 #define NUMLINKCOLORS 12
-static skincolornum_t linkColor[2][NUMLINKCOLORS] = {
-{SKINCOLOR_EMERALD, SKINCOLOR_AQUA, SKINCOLOR_SKY, SKINCOLOR_BLUE, SKINCOLOR_PURPLE, SKINCOLOR_MAGENTA,
+static skincolornum_t linkColor[3][NUMLINKCOLORS] = {
+{SKINCOLOR_SHAMROCK, SKINCOLOR_AQUA, SKINCOLOR_SKY, SKINCOLOR_BLUE, SKINCOLOR_PURPLE, SKINCOLOR_MAGENTA,
  SKINCOLOR_ROSY, SKINCOLOR_RED, SKINCOLOR_ORANGE, SKINCOLOR_GOLD, SKINCOLOR_YELLOW, SKINCOLOR_PERIDOT},
-{SKINCOLOR_SEAFOAM, SKINCOLOR_CYAN, SKINCOLOR_WAVE, SKINCOLOR_SAPPHIRE, SKINCOLOR_VAPOR, SKINCOLOR_BUBBLEGUM,
- SKINCOLOR_VIOLET, SKINCOLOR_RUBY, SKINCOLOR_FLAME, SKINCOLOR_SUNSET, SKINCOLOR_SANDY, SKINCOLOR_LIME}};
+{SKINCOLOR_EMERALD, SKINCOLOR_AQUAMARINE, SKINCOLOR_WAVE, SKINCOLOR_SAPPHIRE, SKINCOLOR_GALAXY, SKINCOLOR_CRYSTAL,
+ SKINCOLOR_TAFFY, SKINCOLOR_RUBY, SKINCOLOR_GARNET, SKINCOLOR_TOPAZ, SKINCOLOR_LEMON, SKINCOLOR_LIME},
+{SKINCOLOR_ISLAND, SKINCOLOR_TURQUOISE, SKINCOLOR_DREAM, SKINCOLOR_DAYBREAK, SKINCOLOR_VAPOR, SKINCOLOR_FUCHSIA,
+ SKINCOLOR_VIOLET, SKINCOLOR_EVENTIDE, SKINCOLOR_KETCHUP, SKINCOLOR_FOUNDATION, SKINCOLOR_HEADLIGHT, SKINCOLOR_CHARTREUSE}};
 
 static void ST_drawNiGHTSLink(void)
 {
 	static INT32 prevsel[2] = {0, 0}, prevtime[2] = {0, 0};
 	const UINT8 q = ((splitscreen && stplyr == &players[secondarydisplayplayer]) ? 1 : 0);
-	INT32 sel = ((stplyr->linkcount-1) / 5) % NUMLINKCOLORS, aflag = V_PERPLAYER, mag = ((stplyr->linkcount-1 >= 300) ? 1 : 0);
+	INT32 sel = ((stplyr->linkcount-1) / 5) % NUMLINKCOLORS, aflag = V_PERPLAYER, mag = ((stplyr->linkcount-1 >= 300) ? (stplyr->linkcount-1 >= 600) ? 2 : 1 : 0);
 	skincolornum_t colornum;
 	fixed_t x, y, scale;
 
-- 
GitLab


From 73eb8fc358bc0d474b28369d5d48b7953e1111d5 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Mon, 13 Mar 2023 19:52:41 +0100
Subject: [PATCH 151/518] A couple more changes: - Swapped invcolors of new
 character skincolors - Renamed Stone to Meteorite & changed text color to
 gray - Tweaked some colors' invshades

---
 src/deh_tables.c |  2 +-
 src/doomdef.h    |  2 +-
 src/info.c       | 92 ++++++++++++++++++++++++------------------------
 3 files changed, 48 insertions(+), 48 deletions(-)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index 268a18ad4f..d53680c19a 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4609,7 +4609,7 @@ const char *COLOR_ENUMS[] = {
 	// Desaturated
 	"AETHER",     	// SKINCOLOR_AETHER,
 	"SLATE",     	// SKINCOLOR_SLATE,
-	"STONE",     	// SKINCOLOR_STONE,
+	"METEORITE",   	// SKINCOLOR_METEORITE,
 	"MERCURY",     	// SKINCOLOR_MERCURY,
 	"BLUEBELL",   	// SKINCOLOR_BLUEBELL,
 	"PINK",     	// SKINCOLOR_PINK,
diff --git a/src/doomdef.h b/src/doomdef.h
index 31cdac9c4e..20b87f230e 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -261,7 +261,7 @@ typedef enum
 	// Desaturated
 	SKINCOLOR_AETHER,
 	SKINCOLOR_SLATE,
-	SKINCOLOR_STONE,
+	SKINCOLOR_METEORITE,
 	SKINCOLOR_MERCURY,
 	SKINCOLOR_BLUEBELL,
 	SKINCOLOR_PINK,
diff --git a/src/info.c b/src/info.c
index 085966b9c5..5a304240d4 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21584,76 +21584,76 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Black",  {0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1f, 0x1f}, SKINCOLOR_WHITE,  7,  V_GRAYMAP, true}, // SKINCOLOR_BLACK
 
 	// Desaturated
-	{"Aether",   {0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x91, 0x91, 0x91, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf}, SKINCOLOR_GREY,     15, 0,           true}, // SKINCOLOR_AETHER
-	{"Slate",    {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0xaa, 0xaa, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_SILVER,   12, 0,           true}, // SKINCOLOR_SLATE
-	{"Stone",    {   0,    4,    8,    9,   11,   12,   14,   15,  171,  172,  173,  174,  175,   27,   29,   31}, SKINCOLOR_TOPAZ,    15, 0,           true}, // SKINCOLOR_STONE
-	{"Mercury",  {   0,    3,    4,    7,   11,   12,   14,   15,  171,  172,  173,  155,  157,  159,  253,  254}, SKINCOLOR_ECRU,     15, V_AZUREMAP,  true}, // SKINCOLOR_MERCURY
-	{"Bluebell", {0x90, 0x91, 0x92, 0x93, 0x94, 0x94, 0x95, 0xac, 0xac, 0xad, 0xad, 0xa8, 0xa8, 0xa9, 0xfd, 0xfe}, SKINCOLOR_COPPER,   4,  V_BLUEMAP,   true}, // SKINCOLOR_BLUEBELL
-	{"Pink",     {0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0x2b, 0x2c, 0x2e}, SKINCOLOR_AZURE,    9,  V_REDMAP,    true}, // SKINCOLOR_PINK
-	{"Rosewood", { 209,  210,  211,  212,  213,  214,  228,  230,  232,  234,  235,  237,   26,   27,   28,   29}, SKINCOLOR_SEPIA,    5,  V_BROWNMAP,  true}, // SKINCOLOR_ROSEWOOD
-	{"Yogurt",   {0xd0, 0x30, 0xd8, 0xd9, 0xda, 0xdb, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe3, 0xe6, 0xe8, 0xe9}, SKINCOLOR_RUST,     7,  V_BROWNMAP,  true}, // SKINCOLOR_YOGURT
-	{"Latte",    {  48,  217,  219,  221,  223,  224,  226,  228,   68,   69,   70,   70,   44,   45,   46,   47}, SKINCOLOR_BOTTLE,   12, V_BROWNMAP,  true}, // SKINCOLOR_LATTE
-	{"Brown",    {0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef}, SKINCOLOR_TAN,      2,  V_BROWNMAP,  true}, // SKINCOLOR_BROWN
-	{"Boulder",  {0xde, 0xe0, 0xe1, 0xe4, 0xe7, 0xe9, 0xeb, 0xec, 0xed, 0xed, 0xed, 0x19, 0x19, 0x1b, 0x1d, 0x1e}, SKINCOLOR_KETCHUP,  0,  V_BROWNMAP,  true}, // SKINCOLOR_BOULDER
-	{"Bronze",   {  82,   84,   50,   51,  223,  228,  230,  232,  234,  236,  237,  238,  239,  239,   30,   31}, SKINCOLOR_VOLCANIC, 9,  V_BROWNMAP,  true}, // SKINCOLOR_BRONZE
-	{"Sepia",    {  88,   84,   85,   86,  224,  226,  228,  230,  232,  235,  236,  237,  238,  239,   28,   28}, SKINCOLOR_ROSEWOOD, 5,  V_BROWNMAP,  true}, // SKINCOLOR_SEPIA
-	{"Ecru",     {  80,   83,   84,   85,   86,  242,  243,  245,  230,  232,  234,  236,  238,  239,   47,   47}, SKINCOLOR_MERCURY,  15, V_BROWNMAP,  true}, // SKINCOLOR_ECRU
-	{"Tan",      {0x51, 0x51, 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0xf5, 0xf5, 0xf9, 0xf9, 0xed, 0xed}, SKINCOLOR_BROWN,    12, V_BROWNMAP,  true}, // SKINCOLOR_TAN
-	{"Beige",    {0x54, 0x55, 0x56, 0x56, 0xf2, 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xed, 0xed}, SKINCOLOR_MOSS,     5,  V_BROWNMAP,  true}, // SKINCOLOR_BEIGE
-	{"Rosebush", { 208,  216,  209,   85,   90,   91,   91,   92,  191,   93,   94,  107,  109,  110,  111,  111}, SKINCOLOR_EGGPLANT, 5,  V_GREENMAP,  true}, // SKINCOLOR_ROSEBUSH
-	{"Moss",     {0x58, 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f, 0x5f}, SKINCOLOR_BEIGE,    13, V_GREENMAP,  true}, // SKINCOLOR_MOSS
-	{"Azure",    {0x90, 0x90, 0x91, 0x91, 0xaa, 0xaa, 0xab, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf}, SKINCOLOR_PINK,     5,  V_AZUREMAP,  true}, // SKINCOLOR_AZURE
-	{"Eggplant", {   4,   8,    11,   11,   16,  195,  195,  195,  196,  186,  187,  187,  254,  254,   30,   31}, SKINCOLOR_ROSEBUSH, 5,  V_PURPLEMAP, true}, // SKINCOLOR_EGGPLANT
-	{"Lavender", {0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7}, SKINCOLOR_GOLD,     4,  V_PURPLEMAP, true}, // SKINCOLOR_LAVENDER
+	{"Aether",    {0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x91, 0x91, 0x91, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf}, SKINCOLOR_GREY,      15, 0,           true}, // SKINCOLOR_AETHER
+	{"Slate",     {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0xaa, 0xaa, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_SILVER,    12, 0,           true}, // SKINCOLOR_SLATE
+	{"Meteorite", {   0,    4,    8,    9,   11,   12,   14,   15,  171,  172,  173,  174,  175,   27,   29,   31}, SKINCOLOR_TOPAZ,     15, V_GRAYMAP,   true}, // SKINCOLOR_METEORITE
+	{"Mercury",   {   0,    3,    4,    7,   11,   12,   14,   15,  171,  172,  173,  155,  157,  159,  253,  254}, SKINCOLOR_ECRU,      15, V_AZUREMAP,  true}, // SKINCOLOR_MERCURY
+	{"Bluebell",  {0x90, 0x91, 0x92, 0x93, 0x94, 0x94, 0x95, 0xac, 0xac, 0xad, 0xad, 0xa8, 0xa8, 0xa9, 0xfd, 0xfe}, SKINCOLOR_COPPER,    4,  V_BLUEMAP,   true}, // SKINCOLOR_BLUEBELL
+	{"Pink",      {0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0x2b, 0x2c, 0x2e}, SKINCOLOR_AZURE,     9,  V_REDMAP,    true}, // SKINCOLOR_PINK
+	{"Rosewood",  { 209,  210,  211,  212,  213,  214,  228,  230,  232,  234,  235,  237,   26,   27,   28,   29}, SKINCOLOR_SEPIA,     5,  V_BROWNMAP,  true}, // SKINCOLOR_ROSEWOOD
+	{"Yogurt",    {0xd0, 0x30, 0xd8, 0xd9, 0xda, 0xdb, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe3, 0xe6, 0xe8, 0xe9}, SKINCOLOR_RUST,      7,  V_BROWNMAP,  true}, // SKINCOLOR_YOGURT
+	{"Latte",     {  48,  217,  219,  221,  223,  224,  226,  228,   68,   69,   70,   70,   44,   45,   46,   47}, SKINCOLOR_BOTTLE,    12, V_BROWNMAP,  true}, // SKINCOLOR_LATTE
+	{"Brown",     {0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef}, SKINCOLOR_TAN,       2,  V_BROWNMAP,  true}, // SKINCOLOR_BROWN
+	{"Boulder",   {0xde, 0xe0, 0xe1, 0xe4, 0xe7, 0xe9, 0xeb, 0xec, 0xed, 0xed, 0xed, 0x19, 0x19, 0x1b, 0x1d, 0x1e}, SKINCOLOR_KETCHUP,   0,  V_BROWNMAP,  true}, // SKINCOLOR_BOULDER
+	{"Bronze",    {  82,   84,   50,   51,  223,  228,  230,  232,  234,  236,  237,  238,  239,  239,   30,   31}, SKINCOLOR_VOLCANIC,  9,  V_BROWNMAP,  true}, // SKINCOLOR_BRONZE
+	{"Sepia",     {  88,   84,   85,   86,  224,  226,  228,  230,  232,  235,  236,  237,  238,  239,   28,   28}, SKINCOLOR_ROSEWOOD,  5,  V_BROWNMAP,  true}, // SKINCOLOR_SEPIA
+	{"Ecru",      {  80,   83,   84,   85,   86,  242,  243,  245,  230,  232,  234,  236,  238,  239,   47,   47}, SKINCOLOR_MERCURY,   15, V_BROWNMAP,  true}, // SKINCOLOR_ECRU
+	{"Tan",       {0x51, 0x51, 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0xf5, 0xf5, 0xf9, 0xf9, 0xed, 0xed}, SKINCOLOR_BROWN,     12, V_BROWNMAP,  true}, // SKINCOLOR_TAN
+	{"Beige",     {0x54, 0x55, 0x56, 0x56, 0xf2, 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xed, 0xed}, SKINCOLOR_MOSS,      5,  V_BROWNMAP,  true}, // SKINCOLOR_BEIGE
+	{"Rosebush",  { 208,  216,  209,   85,   90,   91,   91,   92,  191,   93,   94,  107,  109,  110,  111,  111}, SKINCOLOR_EGGPLANT,  5,  V_GREENMAP,  true}, // SKINCOLOR_ROSEBUSH
+	{"Moss",      {0x58, 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f, 0x5f}, SKINCOLOR_BEIGE,     13, V_GREENMAP,  true}, // SKINCOLOR_MOSS
+	{"Azure",     {0x90, 0x90, 0x91, 0x91, 0xaa, 0xaa, 0xab, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf}, SKINCOLOR_PINK,      5,  V_AZUREMAP,  true}, // SKINCOLOR_AZURE
+	{"Eggplant",  {   4,   8,    11,   11,   16,  195,  195,  195,  196,  186,  187,  187,  254,  254,   30,   31}, SKINCOLOR_ROSEBUSH,  5,  V_PURPLEMAP, true}, // SKINCOLOR_EGGPLANT
+	{"Lavender",  {0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7}, SKINCOLOR_HEADLIGHT, 8,  V_PURPLEMAP, true}, // SKINCOLOR_LAVENDER
 
 	// Viv's vivid colours (toast 21/07/17)
 	// Tweaks & additions (Lach, sphere, Alice, MotorRoach 26/10/22)
 	{"Ruby",       {0xb0, 0xb0, 0xc9, 0xca, 0xcc, 0x26, 0x27, 0x28, 0x29, 0x2a, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfd}, SKINCOLOR_EMERALD,    10, V_REDMAP,     true}, // SKINCOLOR_RUBY
 	{"Cherry",     { 202,  203,  204,  205,  206,   40,   41,   42,   43,   44,  186,  187,   28,   29,   30,   31}, SKINCOLOR_MIDNIGHT,   10, V_REDMAP,     true}, // SKINCOLOR_CHERRY
 	{"Salmon",     {0xd0, 0xd0, 0xd1, 0xd2, 0x20, 0x21, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e}, SKINCOLOR_FOREST,     6,  V_REDMAP,     true}, // SKINCOLOR_SALMON
-	{"Pepper",     { 210,   32,   33,   34,   35,   35,   36,   37,   38,   39,   41,   43,   45,   45,   46,   47}, SKINCOLOR_MASTER,     8,  V_REDMAP,     true}, // SKINCOLOR_PEPPER
-	{"Red",        {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x47, 0x2e, 0x2f}, SKINCOLOR_GREEN,      10, V_REDMAP,     true}, // SKINCOLOR_RED
+	{"Pepper",     { 210,   32,   33,   34,   35,   35,   36,   37,   38,   39,   41,   43,   45,   45,   46,   47}, SKINCOLOR_GREEN,      10, V_REDMAP,     true}, // SKINCOLOR_PEPPER
+	{"Red",        {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x47, 0x2e, 0x2f}, SKINCOLOR_MASTER,     8,  V_REDMAP,     true}, // SKINCOLOR_RED
 	{"Crimson",    {0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x1f}, SKINCOLOR_ICY,        10, V_REDMAP,     true}, // SKINCOLOR_CRIMSON
 	{"Flame",      {0x31, 0x32, 0x33, 0x36, 0x22, 0x22, 0x25, 0x25, 0x25, 0xcd, 0xcf, 0xcf, 0xc5, 0xc5, 0xc7, 0xc7}, SKINCOLOR_PURPLE,     8,  V_REDMAP,     true}, // SKINCOLOR_FLAME
-	{"Garnet",     {   0,   83,   50,   53,   34,   35,   37,   38,   39,   40,   42,   44,   45,   46,   47,   47}, SKINCOLOR_AQUAMARINE, 8,  V_REDMAP,     true}, // SKINCOLOR_GARNET
+	{"Garnet",     {   0,   83,   50,   53,   34,   35,   37,   38,   39,   40,   42,   44,   45,   46,   47,   47}, SKINCOLOR_AQUAMARINE, 6,  V_REDMAP,     true}, // SKINCOLOR_GARNET
 	{"Ketchup",    {0x48, 0x49, 0x40, 0x33, 0x34, 0x36, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2b, 0x2c, 0x47, 0x2e, 0x2f}, SKINCOLOR_BOULDER,    8,  V_REDMAP,     true}, // SKINCOLOR_KETCHUP
 	{"Peachy",     {0xd0, 0x30, 0x31, 0x31, 0x32, 0x32, 0xdc, 0xdc, 0xdc, 0xd3, 0xd4, 0xd4, 0xcc, 0xcd, 0xce, 0xcf}, SKINCOLOR_TEAL,       7,  V_ROSYMAP,    true}, // SKINCOLOR_PEACHY
 	{"Quail",      {0xd8, 0xd9, 0xdb, 0xdc, 0xde, 0xdf, 0xd5, 0xd5, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0x1d, 0x1f}, SKINCOLOR_WAVE,       5,  V_BROWNMAP,   true}, // SKINCOLOR_QUAIL
-	{"Foundation", {  80,   81,   82,   84,  219,  221,  221,  212,  213,  214,  215,  195,  196,  186,  187,   30}, SKINCOLOR_DREAM,      9,  V_ORANGEMAP,  true}, // SKINCOLOR_FOUNDATION
+	{"Foundation", {  80,   81,   82,   84,  219,  221,  221,  212,  213,  214,  215,  195,  196,  186,  187,   30}, SKINCOLOR_DREAM,      6,  V_ORANGEMAP,  true}, // SKINCOLOR_FOUNDATION
 	{"Sunset",     {0x51, 0x52, 0x40, 0x40, 0x34, 0x36, 0xd5, 0xd5, 0xd6, 0xd7, 0xcf, 0xcf, 0xc6, 0xc6, 0xc7, 0xfe}, SKINCOLOR_SAPPHIRE,   5,  V_ORANGEMAP,  true}, // SKINCOLOR_SUNSET
 	{"Copper",     {0x58, 0x54, 0x40, 0x34, 0x35, 0x38, 0x3a, 0x3c, 0x3d, 0x2a, 0x2b, 0x2c, 0x2c, 0xba, 0xba, 0xbb}, SKINCOLOR_BLUEBELL,   5,  V_ORANGEMAP,  true}, // SKINCOLOR_COPPER
 	{"Apricot",    {0x00, 0xd8, 0xd9, 0xda, 0xdb, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e}, SKINCOLOR_CYAN,       4,  V_ORANGEMAP,  true}, // SKINCOLOR_APRICOT
 	{"Orange",     {  49,   50,   51,   52,   53,   54,   55,   57,   58,   59,   60,   42,   44,   45,   46,   46}, SKINCOLOR_BLUE,       4,  V_ORANGEMAP,  true}, // SKINCOLOR_ORANGE
-	{"Pumpkin",    {  51,   52,   53,   54,   56,   58,   59,   59,   61,   61,   63,   45,   46,   47,   47,   31}, SKINCOLOR_ARCTIC,     10, V_ORANGEMAP,  true}, // SKINCOLOR_PUMPKIN
+	{"Pumpkin",    {  51,   52,   53,   54,   56,   58,   59,   59,   61,   61,   63,   45,   46,   47,   47,   31}, SKINCOLOR_ARCTIC,     12, V_ORANGEMAP,  true}, // SKINCOLOR_PUMPKIN
 	{"Rust",       {0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3f, 0x2c, 0x2d, 0x47, 0x2e, 0x2f, 0x2f}, SKINCOLOR_YOGURT,     8,  V_ORANGEMAP,  true}, // SKINCOLOR_RUST
-	{"Gold",       {0x51, 0x51, 0x54, 0x54, 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x3f, 0x2d, 0x2e, 0x2f, 0x2f}, SKINCOLOR_LAVENDER,   10, V_YELLOWMAP,  true}, // SKINCOLOR_GOLD
-	{"Topaz",      {   0,   81,   83,   73,   74,   74,   65,   52,   53,   54,   56,   58,   60,   42,   43,   45}, SKINCOLOR_STONE     , 10, V_YELLOWMAP,  true}, // SKINCOLOR_TOPAZ
+	{"Gold",       {0x51, 0x51, 0x54, 0x54, 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x3f, 0x2d, 0x2e, 0x2f, 0x2f}, SKINCOLOR_MAUVE,      8,  V_YELLOWMAP,  true}, // SKINCOLOR_GOLD
+	{"Topaz",      {   0,   81,   83,   73,   74,   74,   65,   52,   53,   54,   56,   58,   60,   42,   43,   45}, SKINCOLOR_METEORITE,  10, V_YELLOWMAP,  true}, // SKINCOLOR_TOPAZ
 	{"Sandy",      {0x53, 0x40, 0x41, 0x42, 0x43, 0xe6, 0xe9, 0xe9, 0xea, 0xec, 0xec, 0xc6, 0xc6, 0xc7, 0xc7, 0xfe}, SKINCOLOR_SKY,        8,  V_YELLOWMAP,  true}, // SKINCOLOR_SANDY
-	{"Goldenrod",  {   0,   80,   81,   81,   83,   73,   73,   64,   65,   66,   67,   68,   69,   62,   44,   45}, SKINCOLOR_MAJESTY,    11, V_YELLOWMAP,  true}, // SKINCOLOR_GOLDENROD
+	{"Goldenrod",  {   0,   80,   81,   81,   83,   73,   73,   64,   65,   66,   67,   68,   69,   62,   44,   45}, SKINCOLOR_MAJESTY,    8,  V_YELLOWMAP,  true}, // SKINCOLOR_GOLDENROD
 	{"Yellow",     {0x52, 0x53, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4e, 0x4f, 0xed}, SKINCOLOR_CORNFLOWER, 8,  V_YELLOWMAP,  true}, // SKINCOLOR_YELLOW
 	{"Olive",      {0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0xe7, 0xe7, 0xe9, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7, 0xfd}, SKINCOLOR_DUSK,       3,  V_YELLOWMAP,  true}, // SKINCOLOR_OLIVE
 	{"Kiwi",       {  88,   89,  188,  189,  189,   76,   76,   67,   67,   68,   69,   70,   45,   46,   47,   47}, SKINCOLOR_MARINE,     9,  V_PERIDOTMAP, true}, // SKINCOLOR_KIWI
-	{"Lemon",      {   0,   80,   81,   83,   73,   73,   74,   74,   76,   76,  191,  191,   79,   79,  110,  111}, SKINCOLOR_FUCHSIA,    6,  V_YELLOWMAP,  true}, // SKINCOLOR_LEMON
+	{"Lemon",      {   0,   80,   81,   83,   73,   73,   74,   74,   76,   76,  191,  191,   79,   79,  110,  111}, SKINCOLOR_FUCHSIA,    8,  V_YELLOWMAP,  true}, // SKINCOLOR_LEMON
 	{"Lime",       {0x50, 0x51, 0x52, 0x53, 0x48, 0xbc, 0xbd, 0xbe, 0xbe, 0xbf, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_MAGENTA,    9,  V_PERIDOTMAP, true}, // SKINCOLOR_LIME
 	{"Peridot",    {0x58, 0x58, 0xbc, 0xbc, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0x5e, 0x5e, 0x5f, 0x5f, 0x77, 0x77}, SKINCOLOR_COBALT,     2,  V_PERIDOTMAP, true}, // SKINCOLOR_PERIDOT
 	{"Apple",      {0x49, 0x49, 0xbc, 0xbd, 0xbe, 0xbe, 0xbe, 0x67, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6d}, SKINCOLOR_RASPBERRY,  13, V_PERIDOTMAP, true}, // SKINCOLOR_APPLE
-	{"Chartreuse", {  80,   82,   72,   73,  188,  188,  113,  114,  114,  125,  126,  137,  138,  139,  253,  254}, SKINCOLOR_NOBLE,      12, V_PERIDOTMAP, true}, // SKINCOLOR_CHARTREUSE
-	{"Green",      {0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_RED,        6,  V_GREENMAP,   true}, // SKINCOLOR_GREEN
+	{"Chartreuse", {  80,   82,   72,   73,  188,  188,  113,  114,  114,  125,  126,  137,  138,  139,  253,  254}, SKINCOLOR_NOBLE,      9,  V_PERIDOTMAP, true}, // SKINCOLOR_CHARTREUSE
+	{"Green",      {0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_PEPPER,     8,  V_GREENMAP,   true}, // SKINCOLOR_GREEN
 	{"Forest",     {0x65, 0x66, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f}, SKINCOLOR_SALMON,     9,  V_GREENMAP,   true}, // SKINCOLOR_FOREST
 	{"Shamrock",   {0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77}, SKINCOLOR_CRYSTAL,    10, V_GREENMAP,   true}, // SKINCOLOR_SHAMROCK
-	{"Jade",       { 128,  120,  121,  122,  122,  113,  114,  114,  115,  116,  117,  118,  119,  110,  111,   30}, SKINCOLOR_TAFFY,      10, V_GREENMAP,   true}, // SKINCOLOR_JADE
-	{"Headlight",  {   0,   80,   81,   82,   73,   84,   64,   65,   91,   91,  124,  125,  126,  137,  138,  139}, SKINCOLOR_MAUVE,      8,  V_YELLOWMAP,  true}, // SKINCOLOR_HEADLIGHT
+	{"Jade",       { 128,  120,  121,  122,  122,  113,  114,  114,  115,  116,  117,  118,  119,  110,  111,   30}, SKINCOLOR_ROSY,       7,  V_GREENMAP,   true}, // SKINCOLOR_JADE
+	{"Headlight",  {   0,   80,   81,   82,   73,   84,   64,   65,   91,   91,  124,  125,  126,  137,  138,  139}, SKINCOLOR_LAVENDER,   10, V_YELLOWMAP,  true}, // SKINCOLOR_HEADLIGHT
 	{"Mint",       {0x00, 0x00, 0x58, 0x58, 0x59, 0x62, 0x62, 0x62, 0x64, 0x67, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a}, SKINCOLOR_VIOLET,     5,  V_GREENMAP,   true}, // SKINCOLOR_MINT
-	{"Master",     {   0,   80,   88,   96,  112,  113,   99,  100,  124,  125,  126,  117,  107,  118,  119,  111}, SKINCOLOR_PEPPER,     8,  V_GREENMAP,   true}, // SKINCOLOR_MASTER
-	{"Emerald",    {  80,   96,  112,  113,  114,  114,  125,  125,  126,  126,  137,  137,  138,  138,  139,  139}, SKINCOLOR_RUBY,       7,  V_GREENMAP,   true}, // SKINCOLOR_EMERALD
-	{"Bottle",     {   0,    1,    3,    4,    5,  140,  141,  141,  124,  125,  126,  127,  118,  119,  111,  111}, SKINCOLOR_LATTE,      12, V_AQUAMAP,    true}, // SKINCOLOR_BOTTLE
+	{"Master",     {   0,   80,   88,   96,  112,  113,   99,  100,  124,  125,  126,  117,  107,  118,  119,  111}, SKINCOLOR_RED,        6,  V_GREENMAP,   true}, // SKINCOLOR_MASTER
+	{"Emerald",    {  80,   96,  112,  113,  114,  114,  125,  125,  126,  126,  137,  137,  138,  138,  139,  139}, SKINCOLOR_RUBY,       9,  V_GREENMAP,   true}, // SKINCOLOR_EMERALD
+	{"Bottle",     {   0,    1,    3,    4,    5,  140,  141,  141,  124,  125,  126,  127,  118,  119,  111,  111}, SKINCOLOR_LATTE,      14, V_AQUAMAP,    true}, // SKINCOLOR_BOTTLE
 	{"Seafoam",    {0x01, 0x58, 0x59, 0x5a, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a, 0x8b, 0xfd, 0xfd}, SKINCOLOR_PLUM,       6,  V_AQUAMAP,    true}, // SKINCOLOR_SEAFOAM
 	{"Island",     {  96,   97,  113,  113,  114,  124,  142,  136,  136,  150,  151,  153,  168,  168,  169,  169}, SKINCOLOR_GALAXY,     7,  V_AQUAMAP,    true}, // SKINCOLOR_ISLAND
-	{"Aqua",       {0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x76, 0x77}, SKINCOLOR_ROSY,       7,  V_AQUAMAP,    true}, // SKINCOLOR_AQUA
+	{"Aqua",       {0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x76, 0x77}, SKINCOLOR_TAFFY,      10, V_AQUAMAP,    true}, // SKINCOLOR_AQUA
 	{"Teal",       {0x78, 0x78, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0x8a}, SKINCOLOR_PEACHY,     7,  V_SKYMAP,     true}, // SKINCOLOR_TEAL
 	{"Wave",       {0x00, 0x78, 0x78, 0x79, 0x8d, 0x87, 0x88, 0x89, 0x89, 0xae, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_QUAIL,      5,  V_SKYMAP,     true}, // SKINCOLOR_WAVE
 	{"Cyan",       {0x80, 0x81, 0xff, 0xff, 0x83, 0x83, 0x8d, 0x8d, 0x8d, 0x8e, 0x7e, 0x7f, 0x76, 0x76, 0x77, 0x6e}, SKINCOLOR_APRICOT,    6,  V_SKYMAP,     true}, // SKINCOLOR_CYAN
-	{"Turquoise",  {  0,   120,  121,  122,  123,  141,  141,  135,  136,  136,  150,  153,  155,  157,  159,  253}, SKINCOLOR_SANGRIA,    7,  V_SKYMAP,     true}, // SKINCOLOR_TURQUOISE
+	{"Turquoise",  {  0,   120,  121,  122,  123,  141,  141,  135,  136,  136,  150,  153,  155,  157,  159,  253}, SKINCOLOR_SANGRIA,    12, V_SKYMAP,     true}, // SKINCOLOR_TURQUOISE
 	{"Aquamarine", {   0,  120,  121,  131,  132,  133,  134,  134,  135,  135,  149,  149,  172,  173,  174,  175}, SKINCOLOR_GARNET,     8,  V_SKYMAP,     true}, // SKINCOLOR_AQUAMARINE
 	{"Sky",        {0x80, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x89, 0x8a, 0x8b}, SKINCOLOR_SANDY,      1,  V_SKYMAP,     true}, // SKINCOLOR_SKY
 	{"Marine",     { 144,  146,  147,  147,  148,  135,  136,  136,  137,  137,  127,  118,  119,  111,  111,  111}, SKINCOLOR_KIWI,       13, V_SKYMAP,     true}, // SKINCOLOR_MARINE
@@ -21662,7 +21662,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Icy",        {0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0x83, 0x83, 0x86, 0x87, 0x95, 0x95, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_CRIMSON,    0,  V_SKYMAP,     true}, // SKINCOLOR_ICY
 	{"Daybreak",   {  80,   81,   82,   72,   64,    9,   11,  171,  149,  150,  151,  153,  156,  157,  159,  253}, SKINCOLOR_EVENTIDE,   12, V_BLUEMAP,    true}, // SKINCOLOR_DAYBREAK
 	{"Sapphire",   {0x80, 0x82, 0x86, 0x87, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_SUNSET,     5,  V_SKYMAP,     true}, // SKINCOLOR_SAPPHIRE
-	{"Arctic",     {   0,    1,    3,    4,  146,  146,  147,  148,  148,  149,  150,  153,  156,  159,  253,  254}, SKINCOLOR_PUMPKIN,    10, V_BLUEMAP,    true}, // SKINCOLOR_ARCTIC
+	{"Arctic",     {   0,    1,    3,    4,  146,  146,  147,  148,  148,  149,  150,  153,  156,  159,  253,  254}, SKINCOLOR_PUMPKIN,    6,  V_BLUEMAP,    true}, // SKINCOLOR_ARCTIC
 	{"Cornflower", {0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x9a, 0x9c, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e}, SKINCOLOR_YELLOW,     4,  V_BLUEMAP,    true}, // SKINCOLOR_CORNFLOWER
 	{"Blue",       {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_ORANGE,     5,  V_BLUEMAP,    true}, // SKINCOLOR_BLUE
 	{"Cobalt",     { 145,  147,  149,  150,  151,  153,  154,  155,  156,  157,  158,  159,  253,  253,  254,  254}, SKINCOLOR_PERIDOT,    5,  V_BLUEMAP,    true}, // SKINCOLOR_COBALT
@@ -21670,11 +21670,11 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Galaxy",     { 160,  161,  162,  163,  164,  165,  166,  166,  154,  155,  156,  157,  159,  253,  254,   31}, SKINCOLOR_ISLAND,     7,  V_PURPLEMAP,  true}, // SKINCOLOR_GALAXY
 	{"Vapor",      {0x80, 0x81, 0x83, 0x86, 0x94, 0x94, 0xa3, 0xa3, 0xa4, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_LILAC,      4,  V_SKYMAP,     true}, // SKINCOLOR_VAPOR
 	{"Dusk",       {0x92, 0x93, 0x94, 0x94, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_OLIVE,      0,  V_BLUEMAP,    true}, // SKINCOLOR_DUSK
-	{"Majesty",    {   0,    1,  176,  160,  160,  161,  162,  162,  163,  172,  173,  174,  174,  175,  139,  139}, SKINCOLOR_GOLDENROD,  11, V_PURPLEMAP,  true}, // SKINCOLOR_MAJESTY
+	{"Majesty",    {   0,    1,  176,  160,  160,  161,  162,  162,  163,  172,  173,  174,  174,  175,  139,  139}, SKINCOLOR_GOLDENROD,  9,  V_PURPLEMAP,  true}, // SKINCOLOR_MAJESTY
 	{"Pastel",     {0x90, 0x90, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8}, SKINCOLOR_BUBBLEGUM,  9,  V_PURPLEMAP,  true}, // SKINCOLOR_PASTEL
 	{"Purple",     {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_FLAME,      7,  V_PURPLEMAP,  true}, // SKINCOLOR_PURPLE
 	{"Noble",      { 144,  146,  147,  148,  149,  164,  164,  165,  166,  185,  186,  186,  187,  187,   28,   29}, SKINCOLOR_CHARTREUSE, 12, V_PURPLEMAP,  true}, // SKINCOLOR_NOBLE
-	{"Fuchsia",    { 200,  201,  203,  204,  204,  183,  184,  184,  165,  166,  167,  168,  169,  159,  253,  254}, SKINCOLOR_LEMON,      6,  V_PURPLEMAP,  true}, // SKINCOLOR_FUCHSIA
+	{"Fuchsia",    { 200,  201,  203,  204,  204,  183,  184,  184,  165,  166,  167,  168,  169,  159,  253,  254}, SKINCOLOR_LEMON,      10, V_PURPLEMAP,  true}, // SKINCOLOR_FUCHSIA
 	{"Bubblegum",  {   0,  208,  208,  176,  177,  178,  179,  180,  181,  183,  165,  166,  167,  168,  169,  253}, SKINCOLOR_PASTEL,     8,  V_MAGENTAMAP, true}, // SKINCOLOR_BUBBLEGUM
 	{"Crystal",    { 252,  177,  179,  180,  181,  181,  182,  182,  183,  164,  166,  167,  167,  168,  169,  159}, SKINCOLOR_EMERALD,    8,  V_MAGENTAMAP, true}, // SKINCOLOR_CRYSTAL
 	{"Magenta",    {0xb3, 0xb3, 0xb4, 0xb5, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb}, SKINCOLOR_LIME,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_MAGENTA
@@ -21682,14 +21682,14 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Violet",     {0xd0, 0xd1, 0xd2, 0xca, 0xcc, 0xb8, 0xb9, 0xb9, 0xba, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfe, 0xfe}, SKINCOLOR_MINT,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_VIOLET
 	{"Royal",      { 208,  209,  192,  192,  192,  193,  193,  194,  194,  172,  173,  174,  175,  175,  139,  139}, SKINCOLOR_FANCY,      9,  V_PURPLEMAP,  true}, // SKINCOLOR_ROYAL
 	{"Lilac",      {0x00, 0xd0, 0xd1, 0xd2, 0xd3, 0xc1, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xfe, 0x1f}, SKINCOLOR_VAPOR,      4,  V_ROSYMAP,    true}, // SKINCOLOR_LILAC
-	{"Mauve",      { 176,  177,  178,  192,  193,  194,  195,  195,  196,  185,  185,  186,  186,  187,  187,  253}, SKINCOLOR_HEADLIGHT,  8,  V_PURPLEMAP,  true}, // SKINCOLOR_MAUVE
+	{"Mauve",      { 176,  177,  178,  192,  193,  194,  195,  195,  196,  185,  185,  186,  186,  187,  187,  253}, SKINCOLOR_GOLD,       4,  V_PURPLEMAP,  true}, // SKINCOLOR_MAUVE
 	{"Eventide",   {  51,   52,   53,   33,   34,  204,  183,  183,  184,  184,  166,  167,  168,  169,  253,  254}, SKINCOLOR_DAYBREAK,   13, V_MAGENTAMAP, true}, // SKINCOLOR_EVENTIDE
 	{"Plum",       {0xc8, 0xd3, 0xd5, 0xd6, 0xd7, 0xce, 0xcf, 0xb9, 0xb9, 0xba, 0xba, 0xa9, 0xa9, 0xa9, 0xfd, 0xfe}, SKINCOLOR_MINT,       7,  V_ROSYMAP,    true}, // SKINCOLOR_PLUM
 	{"Raspberry",  {0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xce, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfe, 0xfe}, SKINCOLOR_APPLE,      13, V_ROSYMAP,    true}, // SKINCOLOR_RASPBERRY
-	{"Taffy",      {   1,  176,  176,  177,  178,  179,  202,  203,  204,  204,  205,  206,  207,   44,   45,   46}, SKINCOLOR_JADE,       8,  V_ROSYMAP,    true}, // SKINCOLOR_TAFFY
-	{"Rosy",       {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_AQUA,       1,  V_ROSYMAP,    true}, // SKINCOLOR_ROSY
+	{"Taffy",      {   1,  176,  176,  177,  178,  179,  202,  203,  204,  204,  205,  206,  207,   44,   45,   46}, SKINCOLOR_AQUA,       1,  V_ROSYMAP,    true}, // SKINCOLOR_TAFFY
+	{"Rosy",       {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_JADE,       8,  V_ROSYMAP,    true}, // SKINCOLOR_ROSY
 	{"Fancy",      {   0,  208,   49,  210,  210,  202,  202,  203,  204,  204,  205,  206,  207,  207,  186,  186}, SKINCOLOR_ROYAL,      9,  V_ROSYMAP,    true}, // SKINCOLOR_FANCY
-	{"Sangria",    { 210,   32,   33,   34,   34,  215,  215,  207,  207,  185,  186,  186,  186,  169,  169,  253}, SKINCOLOR_TURQUOISE,  7,  V_ROSYMAP,    true}, // SKINCOLOR_SANGRIA
+	{"Sangria",    { 210,   32,   33,   34,   34,  215,  215,  207,  207,  185,  186,  186,  186,  169,  169,  253}, SKINCOLOR_TURQUOISE,  12, V_ROSYMAP,    true}, // SKINCOLOR_SANGRIA
 	{"Volcanic",   {  35,   38,   41,   42,   44,   46,   46,  169,  169,  159,  253,  254,   30,   30,   31,   31}, SKINCOLOR_BRONZE,     9,  V_REDMAP,     true}, // SKINCOLOR_VOLCANIC
 
 	// super
-- 
GitLab


From b97f0a8643dc59e9ffd78d73c848138d8882c06d Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 28 Mar 2023 20:10:53 +0200
Subject: [PATCH 152/518] Fix MT_NIGHTSCORE color cycling, using link colors

---
 src/p_inter.c  | 39 ++++++++-------------------------------
 src/p_mobj.c   |  2 +-
 src/st_stuff.c | 16 ++++++++--------
 src/st_stuff.h |  3 +++
 4 files changed, 20 insertions(+), 40 deletions(-)

diff --git a/src/p_inter.c b/src/p_inter.c
index 046a0a198e..c66c4e10e1 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -255,42 +255,19 @@ void P_DoNightsScore(player_t *player)
 		player->linktimer = nightslinktics;
 	}
 
-	if (player->linkcount < 10)
-	{
-		if (player->bonustime)
-		{
-			P_AddPlayerScore(player, player->linkcount*20);
-			P_SetMobjState(dummymo, dummymo->info->xdeathstate+player->linkcount-1);
-		}
-		else
-		{
-			P_AddPlayerScore(player, player->linkcount*10);
-			P_SetMobjState(dummymo, dummymo->info->spawnstate+player->linkcount-1);
-		}
-	}
-	else
-	{
-		if (player->bonustime)
-		{
-			P_AddPlayerScore(player, 200);
-			P_SetMobjState(dummymo, dummymo->info->xdeathstate+9);
-		}
-		else
-		{
-			P_AddPlayerScore(player, 100);
-			P_SetMobjState(dummymo, dummymo->info->spawnstate+9);
-		}
-	}
+	// Award 10-100 score, doubled if bonus time is active
+	P_AddPlayerScore(player, min(player->linkcount,10)*(player->bonustime ? 20 : 10));
+	P_SetMobjState(dummymo, (player->bonustime ? dummymo->info->xdeathstate : dummymo->info->spawnstate) + min(player->linkcount,10)-1);
 
-	// Hoops are the only things that should add to your drill meter
-	//player->drillmeter += TICRATE;
+	// Make objects slowly rise & scale up
 	dummymo->momz = FRACUNIT;
 	dummymo->fuse = 3*TICRATE;
-
-	// What?! NO, don't use the camera! Scale up instead!
-	//P_InstaThrust(dummymo, R_PointToAngle2(dummymo->x, dummymo->y, camera.x, camera.y), 3*FRACUNIT);
 	dummymo->scalespeed = FRACUNIT/25;
 	dummymo->destscale = 2*FRACUNIT;
+
+	// Add extra values used for color variety
+	dummymo->extravalue1 = player->linkcount-1;
+	dummymo->extravalue2 = ((player->linkcount-1 >= 300) ? (player->linkcount-1 >= 600) ? 2 : 1 : 0);
 }
 
 //
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 3eab29c095..a43afc9b15 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -9603,7 +9603,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
 	case MT_BOSSFLYPOINT:
 		return false;
 	case MT_NIGHTSCORE:
-		mobj->color = (UINT16)(leveltime % SKINCOLOR_WHITE);
+		mobj->color = linkColor[mobj->extravalue2][(leveltime + mobj->extravalue1) % NUMLINKCOLORS];
 		break;
 	case MT_JETFUME1:
 		if (!P_JetFume1Think(mobj))
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 484216e30d..3ce1cbfd8e 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -170,6 +170,14 @@ hudinfo_t hudinfo[NUMHUDITEMS] =
 static huddrawlist_h luahuddrawlist_game[2];
 static huddrawlist_h luahuddrawlist_titlecard;
 
+skincolornum_t linkColor[3][NUMLINKCOLORS] = {
+{SKINCOLOR_SHAMROCK, SKINCOLOR_AQUA, SKINCOLOR_SKY, SKINCOLOR_BLUE, SKINCOLOR_PURPLE, SKINCOLOR_MAGENTA,
+ SKINCOLOR_ROSY, SKINCOLOR_RED, SKINCOLOR_ORANGE, SKINCOLOR_GOLD, SKINCOLOR_YELLOW, SKINCOLOR_PERIDOT},
+{SKINCOLOR_EMERALD, SKINCOLOR_AQUAMARINE, SKINCOLOR_WAVE, SKINCOLOR_SAPPHIRE, SKINCOLOR_GALAXY, SKINCOLOR_CRYSTAL,
+ SKINCOLOR_TAFFY, SKINCOLOR_RUBY, SKINCOLOR_GARNET, SKINCOLOR_TOPAZ, SKINCOLOR_LEMON, SKINCOLOR_LIME},
+{SKINCOLOR_ISLAND, SKINCOLOR_TURQUOISE, SKINCOLOR_DREAM, SKINCOLOR_DAYBREAK, SKINCOLOR_VAPOR, SKINCOLOR_FUCHSIA,
+ SKINCOLOR_VIOLET, SKINCOLOR_EVENTIDE, SKINCOLOR_KETCHUP, SKINCOLOR_FOUNDATION, SKINCOLOR_HEADLIGHT, SKINCOLOR_CHARTREUSE}};
+
 //
 // STATUS BAR CODE
 //
@@ -1752,14 +1760,6 @@ static void ST_drawNightsRecords(void)
 }
 
 // NiGHTS link colors; 3 sets with increasingly fancy colors (1 to 299, 300 to 599, 600 and above)
-#define NUMLINKCOLORS 12
-static skincolornum_t linkColor[3][NUMLINKCOLORS] = {
-{SKINCOLOR_SHAMROCK, SKINCOLOR_AQUA, SKINCOLOR_SKY, SKINCOLOR_BLUE, SKINCOLOR_PURPLE, SKINCOLOR_MAGENTA,
- SKINCOLOR_ROSY, SKINCOLOR_RED, SKINCOLOR_ORANGE, SKINCOLOR_GOLD, SKINCOLOR_YELLOW, SKINCOLOR_PERIDOT},
-{SKINCOLOR_EMERALD, SKINCOLOR_AQUAMARINE, SKINCOLOR_WAVE, SKINCOLOR_SAPPHIRE, SKINCOLOR_GALAXY, SKINCOLOR_CRYSTAL,
- SKINCOLOR_TAFFY, SKINCOLOR_RUBY, SKINCOLOR_GARNET, SKINCOLOR_TOPAZ, SKINCOLOR_LEMON, SKINCOLOR_LIME},
-{SKINCOLOR_ISLAND, SKINCOLOR_TURQUOISE, SKINCOLOR_DREAM, SKINCOLOR_DAYBREAK, SKINCOLOR_VAPOR, SKINCOLOR_FUCHSIA,
- SKINCOLOR_VIOLET, SKINCOLOR_EVENTIDE, SKINCOLOR_KETCHUP, SKINCOLOR_FOUNDATION, SKINCOLOR_HEADLIGHT, SKINCOLOR_CHARTREUSE}};
 
 static void ST_drawNiGHTSLink(void)
 {
diff --git a/src/st_stuff.h b/src/st_stuff.h
index 68ac900f77..603be3c309 100644
--- a/src/st_stuff.h
+++ b/src/st_stuff.h
@@ -124,4 +124,7 @@ extern hudinfo_t hudinfo[NUMHUDITEMS];
 
 extern UINT16 objectsdrawn;
 
+#define NUMLINKCOLORS 12
+extern skincolornum_t linkColor[3][NUMLINKCOLORS];
+
 #endif
-- 
GitLab


From 1a956667a74c8619333076d68c94f177b5f5082f Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Wed, 29 Mar 2023 15:50:03 +0200
Subject: [PATCH 153/518] Small tweak to Bubblegum

---
 src/info.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/info.c b/src/info.c
index 5a304240d4..32b21b702b 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21675,7 +21675,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Purple",     {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_FLAME,      7,  V_PURPLEMAP,  true}, // SKINCOLOR_PURPLE
 	{"Noble",      { 144,  146,  147,  148,  149,  164,  164,  165,  166,  185,  186,  186,  187,  187,   28,   29}, SKINCOLOR_CHARTREUSE, 12, V_PURPLEMAP,  true}, // SKINCOLOR_NOBLE
 	{"Fuchsia",    { 200,  201,  203,  204,  204,  183,  184,  184,  165,  166,  167,  168,  169,  159,  253,  254}, SKINCOLOR_LEMON,      10, V_PURPLEMAP,  true}, // SKINCOLOR_FUCHSIA
-	{"Bubblegum",  {   0,  208,  208,  176,  177,  178,  179,  180,  181,  183,  165,  166,  167,  168,  169,  253}, SKINCOLOR_PASTEL,     8,  V_MAGENTAMAP, true}, // SKINCOLOR_BUBBLEGUM
+	{"Bubblegum",  {   0,  208,  208,  176,  177,  178,  179,  180,  181,  182,  164,  166,  167,  168,  169,  253}, SKINCOLOR_PASTEL,     8,  V_MAGENTAMAP, true}, // SKINCOLOR_BUBBLEGUM
 	{"Crystal",    { 252,  177,  179,  180,  181,  181,  182,  182,  183,  164,  166,  167,  167,  168,  169,  159}, SKINCOLOR_EMERALD,    8,  V_MAGENTAMAP, true}, // SKINCOLOR_CRYSTAL
 	{"Magenta",    {0xb3, 0xb3, 0xb4, 0xb5, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb}, SKINCOLOR_LIME,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_MAGENTA
 	{"Neon",       {0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb, 0xc7, 0xc7, 0x1d, 0x1d, 0x1e}, SKINCOLOR_CERULEAN,   2,  V_MAGENTAMAP, true}, // SKINCOLOR_NEON
-- 
GitLab


From 867a5e0d47180c068a5819e0b3e6d054e999d000 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 20 Jun 2023 21:48:26 +0200
Subject: [PATCH 154/518] Correct misplaced comment on NiGHTS link colors

---
 src/st_stuff.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/st_stuff.c b/src/st_stuff.c
index 3ce1cbfd8e..9cac145a70 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -170,6 +170,7 @@ hudinfo_t hudinfo[NUMHUDITEMS] =
 static huddrawlist_h luahuddrawlist_game[2];
 static huddrawlist_h luahuddrawlist_titlecard;
 
+// NiGHTS link colors; 3 sets with increasingly fancy colors (1 to 299, 300 to 599, 600 and above)
 skincolornum_t linkColor[3][NUMLINKCOLORS] = {
 {SKINCOLOR_SHAMROCK, SKINCOLOR_AQUA, SKINCOLOR_SKY, SKINCOLOR_BLUE, SKINCOLOR_PURPLE, SKINCOLOR_MAGENTA,
  SKINCOLOR_ROSY, SKINCOLOR_RED, SKINCOLOR_ORANGE, SKINCOLOR_GOLD, SKINCOLOR_YELLOW, SKINCOLOR_PERIDOT},
@@ -1759,8 +1760,6 @@ static void ST_drawNightsRecords(void)
 	}
 }
 
-// NiGHTS link colors; 3 sets with increasingly fancy colors (1 to 299, 300 to 599, 600 and above)
-
 static void ST_drawNiGHTSLink(void)
 {
 	static INT32 prevsel[2] = {0, 0}, prevtime[2] = {0, 0};
-- 
GitLab


From e88b3542c72516c94d9257c4c79852dca58e8791 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 22 Jun 2023 14:02:31 +0200
Subject: [PATCH 155/518] Update UZB/UDMF configuration files

---
 extras/conf/udb/Includes/SRB222_common.cfg   |  152 +-
 extras/conf/udb/Includes/SRB222_linedefs.cfg | 1810 +-------
 extras/conf/udb/Includes/SRB222_misc.cfg     |  358 +-
 extras/conf/udb/Includes/SRB222_sectors.cfg  |  107 -
 extras/conf/udb/Includes/SRB222_things.cfg   | 3933 +++---------------
 extras/conf/udb/SRB2_22Doom.cfg              |   32 -
 6 files changed, 755 insertions(+), 5637 deletions(-)
 delete mode 100644 extras/conf/udb/Includes/SRB222_sectors.cfg
 delete mode 100644 extras/conf/udb/SRB2_22Doom.cfg

diff --git a/extras/conf/udb/Includes/SRB222_common.cfg b/extras/conf/udb/Includes/SRB222_common.cfg
index 0ff044a6d3..9a574d6f23 100644
--- a/extras/conf/udb/Includes/SRB222_common.cfg
+++ b/extras/conf/udb/Includes/SRB222_common.cfg
@@ -15,7 +15,7 @@ common
 	ignoredextensions = "wad pk3 pk7 bak backup1 backup2 backup3 zip rar 7z";
 
 	// Default testing parameters
-	testparameters = "-file \"%AP\" \"%F\" -warp %L";
+	testparameters = "-folder \"%AF\" -file \"%AA\" \"%F\" -warp %L";
 	testshortpaths = true;
 
 	// Action special help
@@ -26,7 +26,7 @@ common
 	generalizedsectors = true;
 
 	// Maximum safe map size check (0 means skip check)
-	safeboundary = 1;
+	safeboundary = 0;
 
 	// Map boundaries. Map objects can only be placed within these boundaries
 	leftboundary = -32768;
@@ -40,6 +40,8 @@ common
 	defaultflatscale = 1.0f;
 	scaledtextureoffsets = true;
 
+	maxcolormapalpha = 25;
+
 	// Thing number for start position in 3D Mode
 	start3dmode = 3328;
 
@@ -68,137 +70,6 @@ common
 	}
 }
 
-mapformat_doom
-{
-	// The format interface handles the map data format
-	formatinterface = "DoomMapSetIO";
-
-	// Default nodebuilder configurations
-	defaultsavecompiler = "zennode_normal";
-	defaulttestcompiler = "zennode_fast";
-
-	/*
-	GAME DETECT PATTERN
-	Used to guess the game for which a WAD file is made.
-
-	1 = One of these lumps must exist
-	2 = None of these lumps must exist
-	3 = All of these lumps must exist
-	*/
-
-	gamedetect
-	{
-		EXTENDED = 2;
-
-
-		BEHAVIOR = 2;
-
-		E#M# = 2;
-
-		MAP?? = 1;
-	}
-
-	/*
-	MAP LUMP NAMES
-	Map lumps are loaded with the map as long as they are right after each other. When the editor
-	meets a lump which is not defined in this list it will ignore the map if not satisfied.
-	The order of items defines the order in which lumps will be written to WAD file on save.
-	To indicate the map header lump, use ~MAP
-
-	Legenda:
-	required = Lump is required to exist.
-	blindcopy = Lump will be copied along with the map blindly. (usefull for lumps Doom Builder doesn't use)
-	nodebuild = The nodebuilder generates this lump.
-	allowempty = The nodebuilder is allowed to leave this lump empty.
-	script = This lump is a text-based script. Specify the filename of the script configuration to use.
-	*/
-
-	maplumpnames
-	{
-		include("SRB222_misc.cfg", "doommaplumpnames");
-	}
-
-	// When this is set to true, sectors with the same tag will light up when a line is highlighted
-	linetagindicatesectors = true;
-
-	// Special linedefs
-	include("SRB222_misc.cfg", "speciallinedefs");
-
-	// Default flags for first new thing
-	defaultthingflags
-	{
-	}
-
-	// DEFAULT SECTOR BRIGHTNESS LEVELS
-	sectorbrightness
-	{
-		include("SRB222_misc.cfg", "sectorbrightness");
-	}
-
-	// SECTOR TYPES
-	sectortypes
-	{
-		include("SRB222_sectors.cfg", "sectortypes");
-	}
-
-	// GENERALISED SECTOR TYPES
-	gen_sectortypes
-	{
-		include("SRB222_sectors.cfg", "gen_sectortypes");
-	}
-
-	// LINEDEF FLAGS
-	linedefflags
-	{
-		include("SRB222_misc.cfg", "linedefflags");
-	}
-
-	// Linedef flags UDMF translation table
-	// This is needed for copy/paste and prefabs to work properly
-	// When the UDMF field name is prefixed with ! it is inverted
-	linedefflagstranslation
-	{
-		include("SRB222_misc.cfg", "linedefflagstranslation");
-	}
-
-	// LINEDEF ACTIVATIONS
-	linedefactivations
-	{
-	}
-
-	// LINEDEF TYPES
-	linedeftypes
-	{
-		include("SRB222_linedefs.cfg", "doom");
-	}
-
-	// THING FLAGS
-	thingflags
-	{
-		include("SRB222_misc.cfg", "thingflags");
-	}
-
-	// Thing flags UDMF translation table
-	// This is needed for copy/paste and prefabs to work properly
-	// When the UDMF field name is prefixed with ! it is inverted
-	thingflagstranslation
-	{
-		include("SRB222_misc.cfg", "thingflagstranslation");
-	}
-
-	// THING FLAGS ERROR MASK
-	// Mask for the thing flags which indicates the options
-	// that make the same thing appear in the same modes
-	thingflagsmask1 = 7;	// 1 + 2 + 4
-	thingflagsmask2 = 0;
-
-	// THING TYPES
-	thingtypes
-	{
-		include("SRB222_things.cfg", "doom");
-	}
-}
-
 mapformat_udmf
 {
 	// The format interface handles the map data format
@@ -222,9 +93,17 @@ mapformat_udmf
 	{
 		include("SRB222_misc.cfg", "universalfields");
 	}
+	
+	// Disable Doom-related modes that don't make sense for SRB2
+	soundsupport = false;
+	automapsupport = false;
 
 	// When this is set to true, sectors with the same tag will light up when a line is highlighted
 	linetagindicatesectors = false;
+	localsidedeftextureoffsets = false;
+	distinctfloorandceilingbrightness = true;
+	
+	planeequationsupport = true;
 
 	// Special linedefs
 	include("SRB222_misc.cfg", "speciallinedefs_udmf");
@@ -240,6 +119,11 @@ mapformat_udmf
 		include("SRB222_misc.cfg", "sectorflags");
 	}
 
+	sectorflagscategories
+	{
+		include("SRB222_misc.cfg", "sectorflagscategories");
+	}
+
 	// DEFAULT SECTOR BRIGHTNESS LEVELS
 	sectorbrightness
 	{
@@ -247,6 +131,7 @@ mapformat_udmf
 	}
 
 	damagetypes = "Generic Water Fire Lava Electric Spike DeathPitTilt DeathPitNoTilt Instakill SpecialStage";
+	triggerertypes = "Player AllPlayers Mobj";
 
 	// LINEDEF FLAGS
 	linedefflags
@@ -282,7 +167,6 @@ mapformat_udmf
 	// How to compare thing flags (for the stuck things error checker)
 	thingflagscompare
 	{
-		include("UDMF_misc.cfg", "thingflagscompare");
 	}
 
 	// THING TYPES
diff --git a/extras/conf/udb/Includes/SRB222_linedefs.cfg b/extras/conf/udb/Includes/SRB222_linedefs.cfg
index b526e84e7b..621b4abd53 100644
--- a/extras/conf/udb/Includes/SRB222_linedefs.cfg
+++ b/extras/conf/udb/Includes/SRB222_linedefs.cfg
@@ -1,1793 +1,3 @@
-doom
-{
-	misc
-	{
-		title = "Miscellaneous";
-
-		0
-		{
-			title = "None";
-			prefix = "(0)";
-		}
-		1
-		{
-			title = "Per-Sector Gravity";
-			prefix = "(1)";
-		}
-		5
-		{
-			title = "Camera Scanner <deprecated>";
-			prefix = "(5)";
-		}
-		7
-		{
-			title = "Sector Flat Alignment";
-			prefix = "(7)";
-		}
-		10
-		{
-			title = "Culling Plane";
-			prefix = "(10)";
-		}
-		13
-		{
-			title = "Heat Wave Effect";
-			prefix = "(13)";
-		}
-		40
-		{
-			title = "Visual Portal Between Tagged Linedefs";
-			prefix = "(40)";
-		}
-		41
-		{
-			title = "Horizon Effect";
-			prefix = "(41)";
-		}
-		50
-		{
-			title = "Instantly Lower Floor on Level Load";
-			prefix = "(50)";
-		}
-		51
-		{
-			title = "Instantly Raise Ceiling on Level Load";
-			prefix = "(51)";
-		}
-		63
-		{
-			title = "Fake Floor/Ceiling Planes";
-			prefix = "(63)";
-		}
-		540
-		{
-			title = "Floor Friction";
-			prefix = "(540)";
-		}
-	}
-
-	parameters
-	{
-		title = "Parameters";
-
-		2
-		{
-			title = "Custom Exit";
-			prefix = "(2)";
-		}
-		3
-		{
-			title = "Zoom Tube Parameters";
-			prefix = "(3)";
-		}
-		4
-		{
-			title = "Speed Pad Parameters";
-			prefix = "(4)";
-		}
-		8
-		{
-			title = "Special Sector Properties";
-			prefix = "(8)";
-		}
-		9
-		{
-			title = "Chain Parameters";
-			prefix = "(9)";
-		}
-		11
-		{
-			title = "Rope Hang Parameters";
-			prefix = "(11)";
-		}
-		12
-		{
-			title = "Rock Spawner Parameters";
-			prefix = "(12)";
-		}
-		14
-		{
-			title = "Bustable Block Parameters";
-			prefix = "(14)";
-		}
-		15
-		{
-			title = "Fan Particle Spawner Parameters";
-			prefix = "(15)";
-		}
-		16
-		{
-			title = "Minecart Parameters";
-			prefix = "(16)";
-		}
-		64
-		{
-			title = "Continuously Appearing/Disappearing FOF";
-			prefix = "(64)";
-		}
-		76
-		{
-			title = "Make FOF Bouncy";
-			prefix = "(76)";
-		}
-	}
-
-	polyobject
-	{
-		title = "PolyObject";
-
-		20
-		{
-			title = "First Line";
-			prefix = "(20)";
-		}
-		21
-		{
-			title = "Explicitly Include Line <disabled>";
-			prefix = "(21)";
-		}
-		22
-		{
-			title = "Parameters";
-			prefix = "(22)";
-		}
-		30
-		{
-			title = "Waving Flag";
-			prefix = "(30)";
-		}
-		31
-		{
-			title = "Displacement by Front Sector";
-			prefix = "(31)";
-		}
-		32
-		{
-			title = "Angular Displacement by Front Sector";
-			prefix = "(32)";
-		}
-	}
-
-	planemove
-	{
-		title = "Plane Movement";
-
-		52
-		{
-			title = "Continuously Falling Sector";
-			prefix = "(52)";
-		}
-		53
-		{
-			title = "Continuous Floor/Ceiling Mover";
-			prefix = "(53)";
-		}
-		54
-		{
-			title = "Continuous Floor Mover";
-			prefix = "(54)";
-		}
-		55
-		{
-			title = "Continuous Ceiling Mover";
-			prefix = "(55)";
-		}
-		56
-		{
-			title = "Continuous Two-Speed Floor/Ceiling Mover";
-			prefix = "(56)";
-		}
-		57
-		{
-			title = "Continuous Two-Speed Floor Mover";
-			prefix = "(57)";
-		}
-		58
-		{
-			title = "Continuous Two-Speed Ceiling Mover";
-			prefix = "(58)";
-		}
-		59
-		{
-			title = "Activate Moving Platform";
-			prefix = "(59)";
-		}
-		60
-		{
-			title = "Activate Moving Platform (Adjustable Speed)";
-			prefix = "(60)";
-		}
-		61
-		{
-			title = "Crusher (Ceiling to Floor)";
-			prefix = "(61)";
-		}
-		62
-		{
-			title = "Crusher (Floor to Ceiling)";
-			prefix = "(62)";
-		}
-		66
-		{
-			title = "Move Floor by Displacement";
-			prefix = "(66)";
-		}
-		67
-		{
-			title = "Move Ceiling by Displacement";
-			prefix = "(67)";
-		}
-		68
-		{
-			title = "Move Floor and Ceiling by Displacement";
-			prefix = "(68)";
-		}
-	}
-
-	fofsolid
-	{
-		title = "FOF (solid)";
-
-		100
-		{
-			title = "Solid, Opaque";
-			prefix = "(100)";
-		}
-		101
-		{
-			title = "Solid, Opaque, No Shadow";
-			prefix = "(101)";
-		}
-		102
-		{
-			title = "Solid, Translucent";
-			prefix = "(102)";
-		}
-		103
-		{
-			title = "Solid, Sides Only";
-			prefix = "(103)";
-		}
-		104
-		{
-			title = "Solid, No Sides";
-			prefix = "(104)";
-		}
-		105
-		{
-			title = "Solid, Invisible";
-			prefix = "(105)";
-		}
-		140
-		{
-			title = "Intangible from Bottom, Opaque";
-			prefix = "(140)";
-		}
-		141
-		{
-			title = "Intangible from Bottom, Translucent";
-			prefix = "(141)";
-		}
-		142
-		{
-			title = "Intangible from Bottom, Translucent, No Sides";
-			prefix = "(142)";
-		}
-		143
-		{
-			title = "Intangible from Top, Opaque";
-			prefix = "(143)";
-		}
-		144
-		{
-			title = "Intangible from Top, Translucent";
-			prefix = "(144)";
-		}
-		145
-		{
-			title = "Intangible from Top, Translucent, No Sides";
-			prefix = "(145)";
-		}
-		146
-		{
-			title = "Only Tangible from Sides";
-			prefix = "(146)";
-		}
-	}
-
-	fofintangible
-	{
-		title = "FOF (intangible)";
-
-		120
-		{
-			title = "Water, Opaque";
-			prefix = "(120)";
-		}
-		121
-		{
-			title = "Water, Translucent";
-			prefix = "(121)";
-		}
-		122
-		{
-			title = "Water, Opaque, No Sides";
-			prefix = "(122)";
-		}
-		123
-		{
-			title = "Water, Translucent, No Sides";
-			prefix = "(123)";
-		}
-		124
-		{
-			title = "Goo Water, Translucent";
-			prefix = "(124)";
-		}
-		125
-		{
-			title = "Goo Water, Translucent, No Sides";
-			prefix = "(125)";
-		}
-		220
-		{
-			title = "Intangible, Opaque";
-			prefix = "(220)";
-		}
-		221
-		{
-			title = "Intangible, Translucent";
-			prefix = "(221)";
-		}
-		222
-		{
-			title = "Intangible, Sides Only";
-			prefix = "(222)";
-		}
-		223
-		{
-			title = "Intangible, Invisible";
-			prefix = "(223)";
-		}
-	}
-
-	fofmoving
-	{
-		title = "FOF (moving)";
-
-		150
-		{
-			title = "Air Bobbing";
-			prefix = "(150)";
-		}
-		151
-		{
-			title = "Air Bobbing (Adjustable)";
-			prefix = "(151)";
-		}
-		152
-		{
-			title = "Reverse Air Bobbing (Adjustable)";
-			prefix = "(152)";
-		}
-		153
-		{
-			title = "Dynamically Sinking Platform";
-			prefix = "(153)";
-		}
-		160
-		{
-			title = "Water Bobbing";
-			prefix = "(160)";
-		}
-		190
-		{
-			title = "Rising Platform, Solid, Opaque";
-			prefix = "(190)";
-		}
-		191
-		{
-			title = "Rising Platform, Solid, Opaque, No Shadow";
-			prefix = "(191)";
-		}
-		192
-		{
-			title = "Rising Platform, Solid, Translucent";
-			prefix = "(192)";
-		}
-		193
-		{
-			title = "Rising Platform, Solid, Invisible";
-			prefix = "(193)";
-		}
-		194
-		{
-			title = "Rising Platform, Intangible from Bottom, Opaque";
-			prefix = "(194)";
-		}
-		195
-		{
-			title = "Rising Platform, Intangible from Bottom, Translucent";
-			prefix = "(195)";
-		}
-	}
-
-	fofcrumbling
-	{
-		title = "FOF (crumbling)";
-
-		170
-		{
-			title = "Crumbling, Respawn";
-			prefix = "(170)";
-		}
-		171
-		{
-			title = "Crumbling, No Respawn";
-			prefix = "(171)";
-		}
-		172
-		{
-			title = "Crumbling, Respawn, Intangible from Bottom";
-			prefix = "(172)";
-		}
-		173
-		{
-			title = "Crumbling, No Respawn, Intangible from Bottom";
-			prefix = "(173)";
-		}
-		174
-		{
-			title = "Crumbling, Respawn, Int. from Bottom, Translucent";
-			prefix = "(174)";
-		}
-		175
-		{
-			title = "Crumbling, No Respawn, Int. from Bottom, Translucent";
-			prefix = "(175)";
-		}
-		176
-		{
-			title = "Crumbling, Respawn, Floating, Bobbing";
-			prefix = "(176)";
-		}
-		177
-		{
-			title = "Crumbling, No Respawn, Floating, Bobbing";
-			prefix = "(177)";
-		}
-		178
-		{
-			title = "Crumbling, Respawn, Floating";
-			prefix = "(178)";
-		}
-		179
-		{
-			title = "Crumbling, No Respawn, Floating";
-			prefix = "(179)";
-		}
-		180
-		{
-			title = "Crumbling, Respawn, Air Bobbing";
-			prefix = "(180)";
-		}
-	}
-
-	fofspecial
-	{
-		title = "FOF (special)";
-
-		200
-		{
-			title = "Light Block";
-			prefix = "(200)";
-		}
-		201
-		{
-			title = "Half Light Block";
-			prefix = "(201)";
-		}
-		202
-		{
-			title = "Fog Block";
-			prefix = "(202)";
-		}
-		250
-		{
-			title = "Mario Block";
-			prefix = "(250)";
-		}
-		251
-		{
-			title = "Thwomp Block";
-			prefix = "(251)";
-		}
-		252
-		{
-			title = "Shatter Block";
-			prefix = "(252)";
-		}
-		253
-		{
-			title = "Shatter Block, Translucent";
-			prefix = "(253)";
-		}
-		254
-		{
-			title = "Bustable Block";
-			prefix = "(254)";
-		}
-		255
-		{
-			title = "Spin-Bustable Block";
-			prefix = "(255)";
-		}
-		256
-		{
-			title = "Spin-Bustable Block, Translucent";
-			prefix = "(256)";
-		}
-		257
-		{
-			title = "Quicksand";
-			prefix = "(257)";
-		}
-		258
-		{
-			title = "Laser";
-			prefix = "(258)";
-		}
-		259
-		{
-			title = "Custom FOF";
-			prefix = "(259)";
-		}
-	}
-
-	linedeftrigger
-	{
-		title = "Linedef Executor Trigger";
-
-		300
-		{
-			title = "Continuous";
-			prefix = "(300)";
-		}
-		301
-		{
-			title = "Each Time";
-			prefix = "(301)";
-		}
-		302
-		{
-			title = "Once";
-			prefix = "(302)";
-		}
-		303
-		{
-			title = "Ring Count - Continuous";
-			prefix = "(303)";
-		}
-		304
-		{
-			title = "Ring Count - Once";
-			prefix = "(304)";
-		}
-		305
-		{
-			title = "Character Ability - Continuous";
-			prefix = "(305)";
-		}
-		306
-		{
-			title = "Character Ability - Each Time";
-			prefix = "(306)";
-		}
-		307
-		{
-			title = "Character Ability - Once";
-			prefix = "(307)";
-		}
-		308
-		{
-			title = "Race Only - Once";
-			prefix = "(308)";
-		}
-		309
-		{
-			title = "CTF Red Team - Continuous";
-			prefix = "(309)";
-		}
-		310
-		{
-			title = "CTF Red Team - Each Time";
-			prefix = "(310)";
-		}
-		311
-		{
-			title = "CTF Blue Team - Continuous";
-			prefix = "(311)";
-		}
-		312
-		{
-			title = "CTF Blue Team - Each Time";
-			prefix = "(312)";
-		}
-		313
-		{
-			title = "No More Enemies - Once";
-			prefix = "(313)";
-		}
-		314
-		{
-			title = "Number of Pushables - Continuous";
-			prefix = "(314)";
-		}
-		315
-		{
-			title = "Number of Pushables - Once";
-			prefix = "(315)";
-		}
-		317
-		{
-			title = "Condition Set Trigger - Continuous";
-			prefix = "(317)";
-		}
-		318
-		{
-			title = "Condition Set Trigger - Once";
-			prefix = "(318)";
-		}
-		319
-		{
-			title = "Unlockable - Continuous";
-			prefix = "(319)";
-		}
-		320
-		{
-			title = "Unlockable - Once";
-			prefix = "(320)";
-		}
-		321
-		{
-			title = "Trigger After X Calls - Continuous";
-			prefix = "(321)";
-		}
-		322
-		{
-			title = "Trigger After X Calls - Each Time";
-			prefix = "(322)";
-		}
-		323
-		{
-			title = "NiGHTSerize - Each Time";
-			prefix = "(323)";
-		}
-		324
-		{
-			title = "NiGHTSerize - Once";
-			prefix = "(324)";
-		}
-		325
-		{
-			title = "De-NiGHTSerize - Each Time";
-			prefix = "(325)";
-		}
-		326
-		{
-			title = "De-NiGHTSerize - Once";
-			prefix = "(326)";
-		}
-		327
-		{
-			title = "NiGHTS Lap - Each Time";
-			prefix = "(327)";
-		}
-		328
-		{
-			title = "NiGHTS Lap - Once";
-			prefix = "(328)";
-		}
-		329
-		{
-			title = "Ideya Capture Touch - Each Time";
-			prefix = "(329)";
-		}
-		330
-		{
-			title = "Ideya Capture Touch - Once";
-			prefix = "(330)";
-		}
-		331
-		{
-			title = "Player Skin - Continuous";
-			flags64text = "[6] Disable for this skin";
-			prefix = "(331)";
-		}
-		332
-		{
-			title = "Player Skin - Each Time";
-			prefix = "(332)";
-		}
-		333
-		{
-			title = "Player Skin - Once";
-			prefix = "(333)";
-		}
-		334
-		{
-			title = "Object Dye - Continuous";
-			prefix = "(334)";
-		}
-		335
-		{
-			title = "Object Dye - Each Time";
-			prefix = "(335)";
-		}
-		336
-		{
-			title = "Object Dye - Once";
-			prefix = "(336)";
-		}
-		337
-		{
-			title = "Emerald Check - Continuous";
-			prefix = "(337)";
-		}
-		338
-		{
-			title = "Emerald Check - Each Time";
-			prefix = "(338)";
-		}
-		339
-		{
-			title = "Emerald Check - Once";
-			prefix = "(339)";
-		}
-		340
-		{
-			title = "NiGHTS Mare - Continuous";
-			prefix = "(340)";
-		}
-		341
-		{
-			title = "NiGHTS Mare - Each Time";
-			prefix = "(341)";
-		}
-		342
-		{
-			title = "NiGHTS Mare - Once";
-			prefix = "(342)";
-		}
-		343
-		{
-			title = "Gravity Check - Continuous";
-			prefix = "(343)";
-		}
-		344
-		{
-			title = "Gravity Check - Each Time";
-			prefix = "(344)";
-		}
-		345
-		{
-			title = "Gravity Check - Once";
-			prefix = "(345)";
-		}
-		399
-		{
-			title = "Level Load";
-			prefix = "(399)";
-		}
-	}
-
-	linedefexecsector
-	{
-		title = "Linedef Executor (sector)";
-
-		400
-		{
-			title = "Set Tagged Sector's Floor Height/Texture";
-			prefix = "(400)";
-		}
-		401
-		{
-			title = "Set Tagged Sector's Ceiling Height/Texture";
-			prefix = "(401)";
-		}
-		402
-		{
-			title = "Copy Light Level to Tagged Sectors";
-			prefix = "(402)";
-		}
-		408
-		{
-			title = "Set Tagged Sector's Flats";
-			prefix = "(408)";
-		}
-		409
-		{
-			title = "Change Tagged Sector's Tag";
-			prefix = "(409)";
-		}
-		410
-		{
-			title = "Change Front Sector's Tag";
-			prefix = "(410)";
-		}
-		416
-		{
-			title = "Start Adjustable Flickering Light";
-			prefix = "(416)";
-		}
-		417
-		{
-			title = "Start Adjustable Pulsating Light";
-			prefix = "(417)";
-		}
-		418
-		{
-			title = "Start Adjustable Blinking Light (unsynchronized)";
-			prefix = "(418)";
-		}
-		419
-		{
-			title = "Start Adjustable Blinking Light (synchronized)";
-			prefix = "(419)";
-		}
-		420
-		{
-			title = "Fade Light Level";
-			prefix = "(420)";
-		}
-		421
-		{
-			title = "Stop Lighting Effect";
-			prefix = "(421)";
-		}
-		435
-		{
-			title = "Change Plane Scroller Direction";
-			prefix = "(435)";
-		}
-		467
-		{
-			title = "Set Tagged Sector's Light Level";
-			prefix = "(467)";
-		}
-	}
-
-	linedefexecplane
-	{
-		title = "Linedef Executor (plane movement)";
-
-		403
-		{
-			title = "Move Tagged Sector's Floor";
-			prefix = "(403)";
-		}
-		404
-		{
-			title = "Move Tagged Sector's Ceiling";
-			prefix = "(404)";
-		}
-		405
-		{
-			title = "Move Floor According to Front Texture Offsets";
-			prefix = "(405)";
-		}
-		407
-		{
-			title = "Move Ceiling According to Front Texture Offsets";
-			prefix = "(407)";
-		}
-		411
-		{
-			title = "Stop Plane Movement";
-			prefix = "(411)";
-		}
-		428
-		{
-			title = "Start Platform Movement";
-			prefix = "(428)";
-		}
-		429
-		{
-			title = "Crush Ceiling Once";
-			prefix = "(429)";
-		}
-		430
-		{
-			title = "Crush Floor Once";
-			prefix = "(430)";
-		}
-		431
-		{
-			title = "Crush Floor and Ceiling Once";
-			prefix = "(431)";
-		}
-	}
-
-	linedefexecplayer
-	{
-		title = "Linedef Executor (player/object)";
-
-		412
-		{
-			title = "Teleporter";
-			prefix = "(412)";
-		}
-		425
-		{
-			title = "Change Object State";
-			prefix = "(425)";
-		}
-		426
-		{
-			title = "Stop Object";
-			prefix = "(426)";
-		}
-		427
-		{
-			title = "Award Score";
-			prefix = "(427)";
-		}
-		432
-		{
-			title = "Enable/Disable 2D Mode";
-			prefix = "(432)";
-		}
-		433
-		{
-			title = "Enable/Disable Gravity Flip";
-			prefix = "(433)";
-		}
-		434
-		{
-			title = "Award Power-Up";
-			prefix = "(434)";
-		}
-		437
-		{
-			title = "Disable Player Control";
-			prefix = "(437)";
-		}
-		438
-		{
-			title = "Change Object Size";
-			prefix = "(438)";
-		}
-		442
-		{
-			title = "Change Object Type State";
-			prefix = "(442)";
-		}
-		457
-		{
-			title = "Track Object's Angle";
-			prefix = "(457)";
-		}
-		458
-		{
-			title = "Stop Tracking Object's Angle";
-			prefix = "(458)";
-		}
-		460
-		{
-			title = "Award Rings";
-			prefix = "(460)";
-		}
-		461
-		{
-			title = "Spawn Object";
-			prefix = "(461)";
-		}
-		462
-		{
-			title = "Stop Timer/Exit Stage in Record Attack";
-			prefix = "(462)";
-		}
-		463
-		{
-			title = "Dye Object";
-			prefix = "(463)";
-		}
-		464
-		{
-			title = "Trigger Egg Capsule";
-			prefix = "(464)";
-		}
-		466
-		{
-			title = "Set Level Failure State";
-			prefix = "(466)";
-		}
-	}
-
-	linedefexecmisc
-	{
-		title = "Linedef Executor (misc.)";
-
-		413
-		{
-			title = "Change Music";
-			prefix = "(413)";
-		}
-		414
-		{
-			title = "Play Sound Effect";
-			prefix = "(414)";
-		}
-		415
-		{
-			title = "Run Script";
-			prefix = "(415)";
-		}
-		422
-		{
-			title = "Switch to Cut-Away View";
-			prefix = "(422)";
-		}
-		423
-		{
-			title = "Change Sky";
-			prefix = "(423)";
-		}
-		424
-		{
-			title = "Change Weather";
-			prefix = "(424)";
-		}
-		436
-		{
-			title = "Shatter FOF";
-			prefix = "(436)";
-		}
-		439
-		{
-			title = "Change Tagged Linedef's Textures";
-			prefix = "(439)";
-		}
-		440
-		{
-			title = "Start Metal Sonic Race";
-			prefix = "(440)";
-		}
-		441
-		{
-			title = "Condition Set Trigger";
-			prefix = "(441)";
-		}
-		443
-		{
-			title = "Call Lua Function";
-			prefix = "(443)";
-		}
-		444
-		{
-			title = "Earthquake";
-			prefix = "(444)";
-		}
-		445
-		{
-			title = "Make FOF Disappear/Reappear";
-			prefix = "(445)";
-		}
-		446
-		{
-			title = "Make FOF Crumble";
-			prefix = "(446)";
-		}
-		447
-		{
-			title = "Change Tagged Sector's Colormap";
-			prefix = "(447)";
-		}
-		448
-		{
-			title = "Change Skybox";
-			prefix = "(448)";
-		}
-		449
-		{
-			title = "Enable Bosses with Parameter";
-			prefix = "(449)";
-		}
-		450
-		{
-			title = "Execute Linedef Executor (specific tag)";
-			prefix = "(450)";
-		}
-		451
-		{
-			title = "Execute Linedef Executor (random tag in range)";
-			prefix = "(451)";
-		}
-		452
-		{
-			title = "Set FOF Translucency";
-			prefix = "(452)";
-		}
-		453
-		{
-			title = "Fade FOF";
-			prefix = "(453)";
-		}
-		454
-		{
-			title = "Stop Fading FOF";
-			prefix = "(454)";
-		}
-		455
-		{
-			title = "Fade Tagged Sector's Colormap";
-			prefix = "(455)";
-		}
-		456
-		{
-			title = "Stop Fading Tagged Sector's Colormap";
-			prefix = "(456)";
-		}
-		459
-		{
-			title = "Control Text Prompt";
-			prefix = "(459)";
-		}
-	}
-
-	linedefexecpoly
-	{
-		title = "Linedef Executor (polyobject)";
-
-		480
-		{
-			title = "Door Slide";
-			prefix = "(480)";
-		}
-		481
-		{
-			title = "Door Swing";
-			prefix = "(481)";
-		}
-		482
-		{
-			title = "Move";
-			prefix = "(482)";
-		}
-		483
-		{
-			title = "Move, Override";
-			prefix = "(483)";
-		}
-		484
-		{
-			title = "Rotate Right";
-			prefix = "(484)";
-		}
-		485
-		{
-			title = "Rotate Right, Override";
-			prefix = "(485)";
-		}
-		486
-		{
-			title = "Rotate Left";
-			prefix = "(486)";
-		}
-		487
-		{
-			title = "Rotate Left, Override";
-			prefix = "(487)";
-		}
-		488
-		{
-			title = "Move by Waypoints";
-			prefix = "(488)";
-		}
-		489
-		{
-			title = "Turn Invisible, Intangible";
-			prefix = "(489)";
-		}
-		490
-		{
-			title = "Turn Visible, Tangible";
-			prefix = "(490)";
-		}
-		491
-		{
-			title = "Set Translucency";
-			prefix = "(491)";
-		}
-		492
-		{
-			title = "Fade Translucency";
-			prefix = "(492)";
-		}
-	}
-
-	wallscroll
-	{
-		title = "Wall Scrolling";
-
-		500
-		{
-			title = "Scroll Wall Front Side Left";
-			prefix = "(500)";
-		}
-		501
-		{
-			title = "Scroll Wall Front Side Right";
-			prefix = "(501)";
-		}
-		502
-		{
-			title = "Scroll Wall According to Linedef";
-			prefix = "(502)";
-		}
-		503
-		{
-			title = "Scroll Wall According to Linedef (Accelerative)";
-			prefix = "(503)";
-		}
-		504
-		{
-			title = "Scroll Wall According to Linedef (Displacement)";
-			prefix = "(504)";
-		}
-		505
-		{
-			title = "Scroll Texture by Front Side Offsets";
-			prefix = "(505)";
-		}
-		506
-		{
-			title = "Scroll Texture by Back Side Offsets";
-			prefix = "(506)";
-		}
-	}
-
-	planescroll
-	{
-		title = "Plane Scrolling";
-
-		510
-		{
-			title = "Scroll Floor Texture";
-			prefix = "(510)";
-		}
-		511
-		{
-			title = "Scroll Floor Texture (Accelerative)";
-			prefix = "(511)";
-		}
-		512
-		{
-			title = "Scroll Floor Texture (Displacement)";
-			prefix = "(512)";
-		}
-		513
-		{
-			title = "Scroll Ceiling Texture";
-			prefix = "(513)";
-		}
-		514
-		{
-			title = "Scroll Ceiling Texture (Accelerative)";
-			prefix = "(514)";
-		}
-		515
-		{
-			title = "Scroll Ceiling Texture (Displacement)";
-			prefix = "(515)";
-		}
-		516
-		{
-			title = "Scroll Floor and Ceiling Texture";
-			prefix = "(516)";
-		}
-		517
-		{
-			title = "Scroll Floor and Ceiling Texture (Accelerative)";
-			prefix = "(517)";
-		}
-		518
-		{
-			title = "Scroll Floor and Ceiling Texture (Displacement)";
-			prefix = "(518)";
-		}
-		520
-		{
-			title = "Carry Objects on Floor";
-			prefix = "(520)";
-		}
-		521
-		{
-			title = "Carry Objects on Floor (Accelerative)";
-			prefix = "(521)";
-		}
-		522
-		{
-			title = "Carry Objects on Floor (Displacement)";
-			prefix = "(522)";
-		}
-		523
-		{
-			title = "Carry Objects on Ceiling";
-			prefix = "(523)";
-		}
-		524
-		{
-			title = "Carry Objects on Ceiling (Accelerative)";
-			prefix = "(524)";
-		}
-		525
-		{
-			title = "Carry Objects on Ceiling (Displacement)";
-			prefix = "(525)";
-		}
-		526
-		{
-			title = "Carry Objects on Floor and Ceiling";
-			prefix = "(526)";
-		}
-		527
-		{
-			title = "Carry Objects on Floor and Ceiling (Accelerative)";
-			prefix = "(527)";
-		}
-		528
-		{
-			title = "Carry Objects on Floor and Ceiling (Displacement)";
-			prefix = "(528)";
-		}
-		530
-		{
-			title = "Scroll Floor Texture and Carry Objects";
-			prefix = "(530)";
-		}
-		531
-		{
-			title = "Scroll Floor Texture and Carry Objects (Accelerative)";
-			prefix = "(531)";
-		}
-		532
-		{
-			title = "Scroll Floor Texture and Carry Objects (Displacement)";
-			prefix = "(532)";
-		}
-		533
-		{
-			title = "Scroll Ceiling Texture and Carry Objects";
-			prefix = "(533)";
-		}
-		534
-		{
-			title = "Scroll Ceiling Texture and Carry Objects (Accelerative)";
-			prefix = "(534)";
-		}
-		535
-		{
-			title = "Scroll Ceiling Texture and Carry Objects (Displacement)";
-			prefix = "(535)";
-		}
-		536
-		{
-			title = "Scroll Floor and Ceiling Texture and Carry Objects";
-			prefix = "(536)";
-		}
-		537
-		{
-			title = "Scroll Floor and Ceiling Texture and Carry Objects (Accelerative)";
-			prefix = "(537)";
-		}
-		538
-		{
-			title = "Scroll Floor and Ceiling Texture and Carry Objects (Displacement)";
-			prefix = "(538)";
-		}
-	}
-
-	pusher
-	{
-		title = "Pusher";
-
-		541
-		{
-			title = "Wind";
-			prefix = "(541)";
-		}
-		542
-		{
-			title = "Upwards Wind";
-			prefix = "(542)";
-		}
-		543
-		{
-			title = "Downwards Wind";
-			prefix = "(543)";
-		}
-		544
-		{
-			title = "Current";
-			prefix = "(544)";
-		}
-		545
-		{
-			title = "Upwards Current";
-			prefix = "(545)";
-		}
-		546
-		{
-			title = "Downwards Current";
-			prefix = "(546)";
-		}
-		547
-		{
-			title = "Push/Pull";
-			prefix = "(547)";
-		}
-	}
-
-	light
-	{
-		title = "Lighting";
-
-		600
-		{
-			title = "Floor Lighting";
-			prefix = "(600)";
-		}
-		601
-		{
-			title = "Ceiling Lighting";
-			prefix = "(601)";
-		}
-		602
-		{
-			title = "Adjustable Pulsating Light";
-			prefix = "(602)";
-		}
-		603
-		{
-			title = "Adjustable Flickering Light";
-			prefix = "(603)";
-		}
-		604
-		{
-			title = "Adjustable Blinking Light (unsynchronized)";
-			prefix = "(604)";
-		}
-		605
-		{
-			title = "Adjustable Blinking Light (synchronized)";
-			prefix = "(605)";
-		}
-		606
-		{
-			title = "Colormap";
-			prefix = "(606)";
-		}
-	}
-
-	slope
-	{
-		title = "Slope";
-
-		700
-		{
-			title = "Slope Frontside Floor";
-			prefix = "(700)";
-		}
-		701
-		{
-			title = "Slope Frontside Ceiling";
-			prefix = "(701)";
-		}
-		702
-		{
-			title = "Slope Frontside Floor and Ceiling";
-			prefix = "(702)";
-		}
-		703
-		{
-			title = "Slope Frontside Floor and Backside Ceiling";
-			prefix = "(703)";
-´		}
-		704
-		{
-			title = "Slope Frontside Floor by 3 Tagged Vertex Things";
-			prefix = "(704)";
-		}
-		705
-		{
-			title = "Slope Frontside Ceiling by 3 Tagged Vertex Things";
-			prefix = "(705)";
-		}
-		710
-		{
-			title = "Slope Backside Floor";
-			prefix = "(710)";
-		}
-		711
-		{
-			title = "Slope Backside Ceiling";
-			prefix = "(711)";
-		}
-		712
-		{
-			title = "Slope Backside Floor and Ceiling";
-			prefix = "(712)";
-		}
-		713
-		{
-			title = "Slope Backside Floor and Frontside Ceiling";
-			prefix = "(713)";
-		}
-		714
-		{
-			title = "Slope Backside Floor by 3 Tagged Vertex Things";
-			prefix = "(714)";
-		}
-		715
-		{
-			title = "Slope Backside Ceiling by 3 Tagged Vertex Things";
-			prefix = "(715)";
-		}
-		720
-		{
-			title = "Copy Frontside Floor Slope from Line Tag";
-			prefix = "(720)";
-		}
-		721
-		{
-			title = "Copy Frontside Ceiling Slope from Line Tag";
-			prefix = "(721)";
-		}
-		722
-		{
-			title = "Copy Frontside Floor and Ceiling Slope from Line Tag";
-			prefix = "(722)";
-		}
-		799
-		{
-			title = "Set Tagged Dynamic Slope Vertex to Front Sector Height";
-			prefix = "(799)";
-		}
-	}
-
-	transwall
-	{
-		title = "Translucent Wall";
-
-		900
-		{
-			title = "90% Opaque";
-			prefix = "(900)";
-		}
-		901
-		{
-			title = "80% Opaque";
-			prefix = "(901)";
-		}
-		902
-		{
-			title = "70% Opaque";
-			prefix = "(902)";
-		}
-		903
-		{
-			title = "60% Opaque";
-			prefix = "(903)";
-		}
-		904
-		{
-			title = "50% Opaque";
-			prefix = "(904)";
-		}
-		905
-		{
-			title = "40% Opaque";
-			prefix = "(905)";
-		}
-		906
-		{
-			title = "30% Opaque";
-			prefix = "(906)";
-		}
-		907
-		{
-			title = "20% Opaque";
-			prefix = "(907)";
-		}
-		908
-		{
-			title = "10% Opaque";
-			prefix = "(908)";
-		}
-		909
-		{
-			title = "Fog Wall";
-			prefix = "(909)";
-		}
-		910
-		{
-			title = "100% Additive";
-			prefix = "(910)";
-		}
-		911
-		{
-			title = "90% Additive";
-			prefix = "(911)";
-		}
-		912
-		{
-			title = "80% Additive";
-			prefix = "(912)";
-		}
-		913
-		{
-			title = "70% Additive";
-			prefix = "(913)";
-		}
-		914
-		{
-			title = "60% Additive";
-			prefix = "(914)";
-		}
-		915
-		{
-			title = "50% Additive";
-			prefix = "(915)";
-		}
-		916
-		{
-			title = "40% Additive";
-			prefix = "(916)";
-		}
-		917
-		{
-			title = "30% Additive";
-			prefix = "(917)";
-		}
-		918
-		{
-			title = "20% Additive";
-			prefix = "(918)";
-		}
-		919
-		{
-			title = "10% Additive";
-			prefix = "(919)";
-		}
-		920
-		{
-			title = "100% Subtractive";
-			prefix = "(920)";
-		}
-		921
-		{
-			title = "90% Subtractive";
-			prefix = "(921)";
-		}
-		922
-		{
-			title = "80% Subtractive";
-			prefix = "(922)";
-		}
-		923
-		{
-			title = "70% Subtractive";
-			prefix = "(923)";
-		}
-		924
-		{
-			title = "60% Subtractive";
-			prefix = "(924)";
-		}
-		925
-		{
-			title = "50% Subtractive";
-			prefix = "(925)";
-		}
-		926
-		{
-			title = "40% Subtractive";
-			prefix = "(926)";
-		}
-		927
-		{
-			title = "30% Subtractive";
-			prefix = "(927)";
-		}
-		928
-		{
-			title = "20% Subtractive";
-			prefix = "(928)";
-		}
-		929
-		{
-			title = "10% Subtractive";
-			prefix = "(929)";
-		}
-		930
-		{
-			title = "100% Reverse Subtractive";
-			prefix = "(930)";
-		}
-		931
-		{
-			title = "90% Reverse Subtractive";
-			prefix = "(931)";
-		}
-		932
-		{
-			title = "80% Reverse Subtractive";
-			prefix = "(932)";
-		}
-		933
-		{
-			title = "70% Reverse Subtractive";
-			prefix = "(933)";
-		}
-		934
-		{
-			title = "60% Reverse Subtractive";
-			prefix = "(934)";
-		}
-		935
-		{
-			title = "50% Reverse Subtractive";
-			prefix = "(935)";
-		}
-		936
-		{
-			title = "40% Reverse Subtractive";
-			prefix = "(936)";
-		}
-		937
-		{
-			title = "30% Reverse Subtractive";
-			prefix = "(937)";
-		}
-		938
-		{
-			title = "20% Reverse Subtractive";
-			prefix = "(938)";
-		}
-		939
-		{
-			title = "10% Reverse Subtractive";
-			prefix = "(939)";
-		}
-		940
-		{
-			title = "Modulate";
-			prefix = "(940)";
-		}
-	}
-}
-
 udmf
 {
 	misc
@@ -2502,6 +712,7 @@ udmf
 		{
 			title = "Solid";
 			prefix = "(100)";
+			id = "srb2_fofsolid";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2544,6 +755,7 @@ udmf
 		{
 			title = "Water";
 			prefix = "(120)";
+			id = "srb2_fofwater";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2580,6 +792,7 @@ udmf
 		{
 			title = "Air Bobbing";
 			prefix = "(150)";
+			id = "srb2_fofsolidopaque";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2606,6 +819,7 @@ udmf
 		{
 			title = "Water Bobbing";
 			prefix = "(160)";
+			id = "srb2_fofsolidopaque";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2617,6 +831,7 @@ udmf
 		{
 			title = "Crumbling";
 			prefix = "(170)";
+			id = "srb2_fofcrumbling";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2653,11 +868,12 @@ udmf
 				}
 			}
 		}
-
+		
 		190
 		{
 			title = "Rising";
 			prefix = "(190)";
+			id = "srb2_fofsolid";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2714,6 +930,7 @@ udmf
 		{
 			title = "Light Block";
 			prefix = "(200)";
+			id = "srb2_foflight";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2731,6 +948,7 @@ udmf
 		{
 			title = "Fog Block";
 			prefix = "(202)";
+			id = "srb2_foffog";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2742,6 +960,7 @@ udmf
 		{
 			title = "Intangible";
 			prefix = "(220)";
+			id = "srb2_fofintangible";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2778,6 +997,7 @@ udmf
 		{
 			title = "Intangible, Invisible";
 			prefix = "(223)";
+			id = "srb2_fofintangibleinvisible";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2789,6 +1009,7 @@ udmf
 		{
 			title = "Mario Block";
 			prefix = "(250)";
+			id = "srb2_fofsolidopaque";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2810,6 +1031,7 @@ udmf
 		{
 			title = "Thwomp Block";
 			prefix = "(251)";
+			id = "srb2_fofsolidopaque";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2834,6 +1056,7 @@ udmf
 		{
 			title = "Bustable Block";
 			prefix = "(254)";
+			id = "srb2_fofbustable";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2885,6 +1108,7 @@ udmf
 		{
 			title = "Quicksand";
 			prefix = "(257)";
+			id = "srb2_fofsolidopaque";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2910,6 +1134,7 @@ udmf
 		{
 			title = "Laser";
 			prefix = "(258)";
+			id = "srb2_foflaser";
 			arg0
 			{
 				title = "Target sector tag";
@@ -2942,6 +1167,7 @@ udmf
 		{
 			title = "Custom";
 			prefix = "(259)";
+			id = "srb2_fofcustom";
 			arg0
 			{
 				title = "Target sector tag";
@@ -5686,6 +3912,7 @@ udmf
 		{
 			title = "Create Vertex-Based Slope";
 			prefix = "(704)";
+			id = "srb2_vertexslope";
 			arg0
 			{
 				title = "Plane";
@@ -5729,6 +3956,7 @@ udmf
 		{
 			title = "Copy Slope";
 			prefix = "(720)";
+			id = "plane_copy";
 			arg0
 			{
 				title = "Front floor tag";
diff --git a/extras/conf/udb/Includes/SRB222_misc.cfg b/extras/conf/udb/Includes/SRB222_misc.cfg
index ed0488a3ff..e274fece69 100644
--- a/extras/conf/udb/Includes/SRB222_misc.cfg
+++ b/extras/conf/udb/Includes/SRB222_misc.cfg
@@ -1,24 +1,3 @@
-linedefflags
-{
-	1 = "[0] Impassable";
-	2 = "[1] Block Enemies";
-	4 = "[2] Double-Sided";
-	8 = "[3] Upper Unpegged";
-	16 = "[4] Lower Unpegged";
-	32 = "[5] Slope Skew (E1)";
-	64 = "[6] Not Climbable";
-	128 = "[7] No Midtexture Skew (E2)";
-	256 = "[8] Peg Midtexture (E3)";
-	512 = "[9] Solid Midtexture (E4)";
-	1024 = "[10] Repeat Midtexture (E5)";
-	2048 = "[11] Netgame Only";
-	4096 = "[12] No Netgame";
-	8192 = "[13] Effect 6";
-	16384 = "[14] Bouncy Wall";
-	32768 = "[15] Transfer Line";
-}
-
-
 // Linedef flags UDMF translation table
 // This is needed for copy/paste and prefabs to work properly
 // When the UDMF field name is prefixed with ! it is inverted
@@ -42,7 +21,6 @@ linedefflagstranslation
 	32768 = "transfer";
 }
 
-
 linedefflags_udmf
 {
 	blocking = "Impassable";
@@ -74,19 +52,13 @@ linedefrenderstyles
 
 sectorflags
 {
-	colormapfog = "Fog Planes in Colormap";
-	colormapfadesprites = "Fade Fullbright in Colormap";
-	colormapprotected = "Protected Colormap";
-	flipspecial_nofloor = "No Trigger on Floor Touch";
-	flipspecial_ceiling = "Trigger on Ceiling Touch";
-	triggerspecial_touch = "Trigger on Edge Touch";
-	triggerspecial_headbump = "Trigger on Headbump";
-	triggerline_plane = "Linedef Trigger Requires Plane Touch";
-	triggerline_mobj = "Non-Pushables Can Trigger Linedef";
 	invertprecip = "Invert Precipitation";
 	gravityflip = "Flip Objects in Reverse Gravity";
 	heatwave = "Heat Wave";
 	noclipcamera = "Intangible to the Camera";
+	colormapfog = "Fog Planes";
+	colormapfadesprites = "Fade Fullbright";
+	colormapprotected = "Protected from Tagging";
 	outerspace = "Space Countdown";
 	doublestepup = "Ramp Sector (double step-up/down)";
 	nostepdown = "Non-Ramp Sector (No step-down)";
@@ -104,23 +76,59 @@ sectorflags
 	zoomtubeend = "Zoom Tube End";
 	finishline = "Circuit Finish Line";
 	ropehang = "Rope Hang";
+	jumpflip = "Flip Gravity on Jump";
+	gravityoverride = "Make Reverse Gravity Temporary";
+	flipspecial_nofloor = "No Trigger on Floor Touch";
+	flipspecial_ceiling = "Trigger on Ceiling Touch";
+	triggerspecial_touch = "Trigger on Edge Touch";
+	triggerspecial_headbump = "Trigger on Headbump";
+	triggerline_plane = "Linedef Trigger Requires Plane Touch";
+	triggerline_mobj = "Non-Pushables Can Trigger Linedef";
 }
 
-thingflags
+sectorflagscategories
 {
-	1 = "[1] Extra";
-	2 = "[2] Flip";
-	4 = "[4] Special";
-	8 = "[8] Ambush";
+	invertprecip = "regular";
+	gravityflip = "regular";
+	heatwave = "regular";
+	noclipcamera = "regular";
+	colormapfog = "colormap";
+	colormapfadesprites = "colormap";
+	colormapprotected = "colormap";
+	outerspace = "special";
+	doublestepup = "special";
+	nostepdown = "special";
+	speedpad = "special";
+	starpostactivator = "special";
+	exit = "special";
+	specialstagepit = "special";
+	returnflag = "special";
+	redteambase = "special";
+	blueteambase = "special";
+	fan = "special";
+	supertransform = "special";
+	forcespin = "special";
+	zoomtubestart = "special";
+	zoomtubeend = "special";
+	finishline = "special";
+	ropehang = "special";
+	jumpflip = "special";
+	gravityoverride = "special";
+	flipspecial_nofloor = "trigger";
+	flipspecial_ceiling = "trigger";
+	triggerspecial_touch = "trigger";
+	triggerspecial_headbump = "trigger";
+	triggerline_plane = "trigger";
+	triggerline_mobj = "trigger";
 }
 
 // THING FLAGS
 thingflags_udmf
 {
 	flip = "Flip";
+	absolutez = "Absolute Z height";
 }
 
-
 // Thing flags UDMF translation table
 // This is needed for copy/paste and prefabs to work properly
 // When the UDMF field name is prefixed with ! it is inverted
@@ -130,9 +138,9 @@ thingflagstranslation
 	2 = "flip";
 	4 = "special";
 	8 = "ambush";
+	16 = "absolutez";
 }
 
-
 // DEFAULT SECTOR BRIGHTNESS LEVELS
 sectorbrightness
 {
@@ -171,6 +179,8 @@ sectorbrightness
 	0;
 }
 
+numbrightnesslevels = 32;
+
 /*
 TEXTURES AND FLAT SOURCES
 This tells Doom Builder where to find the information for textures
@@ -221,145 +231,18 @@ universalfields
 {
 	sector
 	{
-		lightalpha
-		{
-			type = 0;
-			default = 25;
-		}
-
-		fadealpha
-		{
-			type = 0;
-			default = 25;
-		}
-
-		fadestart
-		{
-			type = 0;
-			default = 0;
-		}
-
-		fadeend
-		{
-			type = 0;
-			default = 33;
-		}
-
-		foglighting
-		{
-			type = 3;
-			default = false;
-		}
-
-		friction
-		{
-			type = 1;
-			default = 0.90625;
-		}
-
-		triggertag
-		{
-			type = 15;
-			default = 0;
-		}
-
-		triggerer
-		{
-			type = 2;
-			default = "Player";
-		}
 	}
 
 	linedef
 	{
-		arg5
-		{
-			type = 0;
-			default = 0;
-		}
-		arg6
-		{
-			type = 0;
-			default = 0;
-		}
-		arg7
-		{
-			type = 0;
-			default = 0;
-		}
-		arg8
-		{
-			type = 0;
-			default = 0;
-		}
-		arg9
-		{
-			type = 0;
-			default = 0;
-		}
-		stringarg0
-		{
-			type = 2;
-			default = "";
-		}
-		stringarg1
-		{
-			type = 2;
-			default = "";
-		}
-		executordelay
-		{
-			type = 0;
-			default = 0;
-		}
 	}
 
 	sidedef
 	{
-		repeatcnt
-		{
-			type = 0;
-			default = 0;
-		}
 	}
 
 	thing
 	{
-		arg5
-		{
-			type = 0;
-			default = 0;
-		}
-		arg6
-		{
-			type = 0;
-			default = 0;
-		}
-		arg7
-		{
-			type = 0;
-			default = 0;
-		}
-		arg8
-		{
-			type = 0;
-			default = 0;
-		}
-		arg9
-		{
-			type = 0;
-			default = 0;
-		}
-		stringarg0
-		{
-			type = 2;
-			default = "";
-		}
-		stringarg1
-		{
-			type = 2;
-			default = "";
-		}
 	}
 }
 
@@ -378,87 +261,6 @@ allowempty = The nodebuilder is allowed to leave this lump empty.
 scriptbuild = This lump is a text-based script, which should be compiled using current script compiler;
 script = This lump is a text-based script. Specify the filename of the script configuration to use.
 */
-
-doommaplumpnames
-{
-	~MAP
-	{
-		required = true;
-		blindcopy = true;
-		nodebuild = false;
-	}
-
-	THINGS
-	{
-		required = true;
-		nodebuild = true;
-		allowempty = true;
-	}
-
-	LINEDEFS
-	{
-		required = true;
-		nodebuild = true;
-		allowempty = false;
-	}
-
-	SIDEDEFS
-	{
-		required = true;
-		nodebuild = true;
-		allowempty = false;
-	}
-
-	VERTEXES
-	{
-		required = true;
-		nodebuild = true;
-		allowempty = false;
-	}
-
-	SEGS
-	{
-		required = false;
-		nodebuild = true;
-		allowempty = false;
-	}
-
-	SSECTORS
-	{
-		required = false;
-		nodebuild = true;
-		allowempty = false;
-	}
-
-	NODES
-	{
-		required = false;
-		nodebuild = true;
-		allowempty = false;
-	}
-
-	SECTORS
-	{
-		required = true;
-		nodebuild = true;
-		allowempty = false;
-	}
-
-	REJECT
-	{
-		required = false;
-		nodebuild = true;
-		allowempty = false;
-	}
-
-	BLOCKMAP
-	{
-		required = false;
-		nodebuild = true;
-		allowempty = true;
-	}
-}
-
 udmfmaplumpnames
 {
 	ZNODES
@@ -682,48 +484,32 @@ thingsfilters
 
 	}
 
-
-	filter3
-	{
-		name = "Normal Gravity";
-		category = "";
-		type = -1;
-
-		fields
-		{
-			2 = false;
-		}
-
-	}
-
-
-	filter4
-	{
-		name = "Reverse Gravity";
-		category = "";
-		type = -1;
-
-		fields
-		{
-			2 = true;
-		}
-
-	}
+	//filter3
+	//{
+	//	name = "Normal Gravity";
+	//	category = "";
+	//	type = -1;
+	//
+	//	fields
+	//	{
+	//		2 = false;
+	//	}
+	//}
+
+	//filter4
+	//{
+	//	name = "Reverse Gravity";
+	//	category = "";
+	//	type = -1;
+	//
+	//	fields
+	//	{
+	//		2 = true;
+	//	}
+	//}
 }
 
 // Special linedefs
-speciallinedefs
-{
-	soundlinedefflag = 64;	// See linedefflags
-	singlesidedflag = 1;	// See linedefflags
-	doublesidedflag = 4;	// See linedefflags
-	impassableflag = 1;
-	upperunpeggedflag = 8;
-	lowerunpeggedflag = 16;
-	repeatmidtextureflag = 1024;
-	pegmidtextureflag = 256;
-}
-
 speciallinedefs_udmf
 {
 	soundlinedefflag = "noclimb";
@@ -734,6 +520,8 @@ speciallinedefs_udmf
 	lowerunpeggedflag = "dontpegbottom";
 	repeatmidtextureflag = "wrapmidtex";
 	pegmidtextureflag = "midpeg";
+	slopeskewflag = "skewtd";
+	nomidtextureskewflag = "noskew";
 }
 
 scriptlumpnames
diff --git a/extras/conf/udb/Includes/SRB222_sectors.cfg b/extras/conf/udb/Includes/SRB222_sectors.cfg
deleted file mode 100644
index 5b3ad4155c..0000000000
--- a/extras/conf/udb/Includes/SRB222_sectors.cfg
+++ /dev/null
@@ -1,107 +0,0 @@
-sectortypes
-{
-	0 = "Normal";
-	1 = "Damage";
-	2 = "Damage (Water)";
-	3 = "Damage (Fire)";
-	4 = "Damage (Electrical)";
-	5 = "Spikes";
-	6 = "Death Pit (Camera Tilt)";
-	7 = "Death Pit (No Camera Tilt)";
-	8 = "Instant Kill";
-	9 = "Ring Drainer (Floor Touch)";
-	10 = "Ring Drainer (Anywhere in Sector)";
-	11 = "Special Stage Damage";
-	12 = "Space Countdown";
-	13 = "Ramp Sector (double step-up/down)";
-	14 = "Non-Ramp Sector (no step-down)";
-	15 = "Bouncy FOF <deprecated>";
-	16 = "Trigger Line Ex. (Pushable Objects)";
-	32 = "Trigger Line Ex. (Anywhere, All Players)";
-	48 = "Trigger Line Ex. (Floor Touch, All Players)";
-	64 = "Trigger Line Ex. (Anywhere in Sector)";
-	80 = "Trigger Line Ex. (Floor Touch)";
-	96 = "Trigger Line Ex. (Emerald Check) <deprecated>";
-	112 = "Trigger Line Ex. (NiGHTS Mare) <deprecated>";
-	128 = "Check for Linedef Executor on FOFs";
-	144 = "Egg Capsule";
-	160 = "Special Stage Time/Spheres Parameters <deprecated>";
-	176 = "Custom Global Gravity <deprecated>";
-	1280 = "Speed Pad";
-	1536 = "Flip Gravity on Jump";
-	4096 = "Star Post Activator";
-	8192 = "Exit/Special Stage Pit/Return Flag";
-	12288 = "CTF Red Team Base";
-	16384 = "CTF Blue Team Base";
-	20480 = "Fan Sector";
-	24576 = "Super Sonic Transform";
-	28672 = "Force Spin";
-	32768 = "Zoom Tube Start";
-	36864 = "Zoom Tube End";
-	40960 = "Circuit Finish Line";
-	45056 = "Rope Hang";
-	49152 = "Intangible to the Camera";
-}
-
-gen_sectortypes
-{
-	first
-	{
-		0 = "Normal";
-		1 = "Damage";
-		2 = "Damage (Water)";
-		3 = "Damage (Fire)";
-		4 = "Damage (Electrical)";
-		5 = "Spikes";
-		6 = "Death Pit (Camera Tilt)";
-		7 = "Death Pit (No Camera Tilt)";
-		8 = "Instant Kill";
-		9 = "Ring Drainer (Floor Touch)";
-		10 = "Ring Drainer (Anywhere in Sector)";
-		11 = "Special Stage Damage";
-		12 = "Space Countdown";
-		13 = "Ramp Sector (double step-up/down)";
-		14 = "Non-Ramp Sector (no step-down)";
-		15 = "Bouncy FOF <deprecated>";
-	}
-
-	second
-	{
-		0 = "Normal";
-		16 = "Trigger Line Ex. (Pushable Objects)";
-		32 = "Trigger Line Ex. (Anywhere, All Players)";
-		48 = "Trigger Line Ex. (Floor Touch, All Players)";
-		64 = "Trigger Line Ex. (Anywhere in Sector)";
-		80 = "Trigger Line Ex. (Floor Touch)";
-		96 = "Trigger Line Ex. (Emerald Check) <deprecated>";
-		112 = "Trigger Line Ex. (NiGHTS Mare) <deprecated>";
-		128 = "Check for Linedef Executor on FOFs";
-		144 = "Egg Capsule";
-		160 = "Special Stage Time/Spheres Parameters <deprecated>";
-		176 = "Custom Global Gravity <deprecated>";
-	}
-
-	third
-	{
-		0 = "Normal";
-		1280 = "Speed Pad";
-		1536 = "Flip Gravity on Jump";
-	}
-
-	fourth
-	{
-		0 = "Normal";
-		4096 = "Star Post Activator";
-		8192 = "Exit/Special Stage Pit/Return Flag";
-		12288 = "CTF Red Team Base";
-		16384 = "CTF Blue Team Base";
-		20480 = "Fan Sector";
-		24576 = "Super Sonic Transform";
-		28672 = "Force Spin";
-		32768 = "Zoom Tube Start";
-		36864 = "Zoom Tube End";
-		40960 = "Circuit Finish Line";
-		45056 = "Rope Hang";
-		49152 = "Intangible to the Camera";
-	}
-}
diff --git a/extras/conf/udb/Includes/SRB222_things.cfg b/extras/conf/udb/Includes/SRB222_things.cfg
index b4508c91ea..df08e3ac50 100644
--- a/extras/conf/udb/Includes/SRB222_things.cfg
+++ b/extras/conf/udb/Includes/SRB222_things.cfg
@@ -3,3175 +3,8 @@
 // 8-Dark_Gray 9-Blue 10-Green 11-Cyan 12-Red 13-Magenta
 // 14-Yellow 15-White 16-Pink 17-Orange 18-Gold 19-Cream
 
-doom
-{
-	editor
-	{
-		color = 15; // White
-		arrow = 1;
-		title = "<Editor Things>";
-		error = -1;
-		width = 8;
-		height = 16;
-		sort = 1;
-
-		3328 = "3D Mode Start";
-	}
-
-	starts
-	{
-		color = 1; // Blue
-		arrow = 1;
-		title = "Player Starts";
-		width = 16;
-		height = 48;
-		sprite = "PLAYA0";
-
-		1
-		{
-			title = "Player 01 Start";
-			sprite = "PLAYA0";
-		}
-		2
-		{
-			title = "Player 02 Start";
-			sprite = "PLAYA0";
-		}
-		3
-		{
-			title = "Player 03 Start";
-			sprite = "PLAYA0";
-		}
-		4
-		{
-			title = "Player 04 Start";
-			sprite = "PLAYA0";
-		}
-		5
-		{
-			title = "Player 05 Start";
-			sprite = "PLAYA0";
-		}
-		6
-		{
-			title = "Player 06 Start";
-			sprite = "PLAYA0";
-		}
-		7
-		{
-			title = "Player 07 Start";
-			sprite = "PLAYA0";
-		}
-		8
-		{
-			title = "Player 08 Start";
-			sprite = "PLAYA0";
-		}
-		9
-		{
-			title = "Player 09 Start";
-			sprite = "PLAYA0";
-		}
-		10
-		{
-			title = "Player 10 Start";
-			sprite = "PLAYA0";
-		}
-		11
-		{
-			title = "Player 11 Start";
-			sprite = "PLAYA0";
-		}
-		12
-		{
-			title = "Player 12 Start";
-			sprite = "PLAYA0";
-		}
-		13
-		{
-			title = "Player 13 Start";
-			sprite = "PLAYA0";
-		}
-		14
-		{
-			title = "Player 14 Start";
-			sprite = "PLAYA0";
-		}
-		15
-		{
-			title = "Player 15 Start";
-			sprite = "PLAYA0";
-		}
-		16
-		{
-			title = "Player 16 Start";
-			sprite = "PLAYA0";
-		}
-		17
-		{
-			title = "Player 17 Start";
-			sprite = "PLAYA0";
-		}
-		18
-		{
-			title = "Player 18 Start";
-			sprite = "PLAYA0";
-		}
-		19
-		{
-			title = "Player 19 Start";
-			sprite = "PLAYA0";
-		}
-		20
-		{
-			title = "Player 20 Start";
-			sprite = "PLAYA0";
-		}
-		21
-		{
-			title = "Player 21 Start";
-			sprite = "PLAYA0";
-		}
-		22
-		{
-			title = "Player 22 Start";
-			sprite = "PLAYA0";
-		}
-		23
-		{
-			title = "Player 23 Start";
-			sprite = "PLAYA0";
-		}
-		24
-		{
-			title = "Player 24 Start";
-			sprite = "PLAYA0";
-		}
-		25
-		{
-			title = "Player 25 Start";
-			sprite = "PLAYA0";
-		}
-		26
-		{
-			title = "Player 26 Start";
-			sprite = "PLAYA0";
-		}
-		27
-		{
-			title = "Player 27 Start";
-			sprite = "PLAYA0";
-		}
-		28
-		{
-			title = "Player 28 Start";
-			sprite = "PLAYA0";
-		}
-		29
-		{
-			title = "Player 29 Start";
-			sprite = "PLAYA0";
-		}
-		30
-		{
-			title = "Player 30 Start";
-			sprite = "PLAYA0";
-		}
-		31
-		{
-			title = "Player 31 Start";
-			sprite = "PLAYA0";
-		}
-		32
-		{
-			title = "Player 32 Start";
-			sprite = "PLAYA0";
-		}
-		33
-		{
-			title = "Match Start";
-			sprite = "NDRNA2A8";
-		}
-		34
-		{
-			title = "CTF Red Team Start";
-			sprite = "SIGNG0";
-		}
-		35
-		{
-			title = "CTF Blue Team Start";
-			sprite = "SIGNE0";
-		}
-	}
-
-	enemies
-	{
-		color = 9; // Light_Blue
-		arrow = 1;
-		title = "Enemies";
-
-		100
-		{
-			title = "Crawla (Blue)";
-			sprite = "POSSA1";
-			width = 24;
-			height = 32;
-		}
-		101
-		{
-			title = "Crawla (Red)";
-			sprite = "SPOSA1";
-			width = 24;
-			height = 32;
-		}
-		102
-		{
-			title = "Stupid Dumb Unnamed RoboFish";
-			sprite = "FISHA0";
-			width = 8;
-			height = 28;
-		}
-		103
-		{
-			title = "Buzz (Gold)";
-			sprite = "BUZZA1";
-			width = 28;
-			height = 40;
-		}
-		104
-		{
-			title = "Buzz (Red)";
-			sprite = "RBUZA1";
-			width = 28;
-			height = 40;
-		}
-		108
-		{
-			title = "Deton";
-			sprite = "DETNA1";
-			width = 20;
-			height = 32;
-		}
-		110
-		{
-			title = "Turret";
-			sprite = "TRETA1";
-			width = 16;
-			height = 32;
-		}
-		111
-		{
-			title = "Pop-up Turret";
-			sprite = "TURRI1";
-			width = 12;
-			height = 64;
-		}
-		122
-		{
-			title = "Spring Shell (Green)";
-			sprite = "SSHLA1";
-			width = 24;
-			height = 40;
-		}
-		125
-		{
-			title = "Spring Shell (Yellow)";
-			sprite = "SSHLI1";
-			width = 24;
-			height = 40;
-		}
-		109
-		{
-			title = "Skim";
-			sprite = "SKIMA1";
-			width = 16;
-			height = 24;
-		}
-		113
-		{
-			title = "Jet Jaw";
-			sprite = "JJAWA3A7";
-			width = 12;
-			height = 20;
-		}
-		126
-		{
-			title = "Crushstacean";
-			sprite = "CRABA0";
-			width = 24;
-			height = 32;
-		}
-		138
-		{
-			title = "Banpyura";
-			sprite = "CR2BA0";
-			width = 24;
-			height = 32;
-		}
-		117
-		{
-			title = "Robo-Hood";
-			sprite = "ARCHA1";
-			width = 24;
-			height = 32;
-		}
-		118
-		{
-			title = "Lance-a-Bot";
-			sprite = "CBFSA1";
-			width = 32;
-			height = 72;
-		}
-		1113
-		{
-			title = "Suspicious Lance-a-Bot Statue";
-			sprite = "CBBSA1";
-			width = 32;
-			height = 72;
-		}
-		119
-		{
-			title = "Egg Guard";
-			sprite = "ESHIA1";
-			width = 16;
-			height = 48;
-		}
-		115
-		{
-			title = "Bird Aircraft Strike Hazard";
-			sprite = "VLTRF1";
-			width = 12;
-			height = 24;
-		}
-		120
-		{
-			title = "Green Snapper";
-			sprite = "GSNPA1";
-			width = 24;
-			height = 24;
-		}
-		121
-		{
-			title = "Minus";
-			sprite = "MNUSA0";
-			width = 24;
-			height = 32;
-		}
-		134
-		{
-			title = "Canarivore";
-			sprite = "CANAA0";
-			width = 12;
-			height = 80;
-			hangs = 1;
-		}
-		123
-		{
-			title = "Unidus";
-			sprite = "UNIDA1";
-			width = 18;
-			height = 36;
-		}
-		135
-		{
-			title = "Pterabyte Spawner";
-			sprite = "PTERA2A8";
-			width = 16;
-			height = 16;
-		}
-		136
-		{
-			title = "Pyre Fly";
-			sprite = "PYREA0";
-			width = 24;
-			height = 34;
-		}
-		137
-		{
-			title = "Dragonbomber";
-			sprite = "DRABA1";
-			width = 28;
-			height = 48;
-		}
-		105
-		{
-			title = "Jetty-Syn Bomber";
-			sprite = "JETBB1";
-			width = 20;
-			height = 50;
-		}
-		106
-		{
-			title = "Jetty-Syn Gunner";
-			sprite = "JETGB1";
-			width = 20;
-			height = 48;
-		}
-		112
-		{
-			title = "Spincushion";
-			sprite = "SHRPA1";
-			width = 16;
-			height = 24;
-		}
-		114
-		{
-			title = "Snailer";
-			sprite = "SNLRA3A7";
-			width = 24;
-			height = 48;
-		}
-		129
-		{
-			title = "Penguinator";
-			sprite = "PENGA1";
-			width = 24;
-			height = 32;
-		}
-		130
-		{
-			title = "Pophat";
-			sprite = "POPHA1";
-			width = 24;
-			height = 32;
-		}
-		107
-		{
-			title = "Crawla Commander";
-			sprite = "CCOMA1";
-			width = 16;
-			height = 32;
-		}
-		131
-		{
-			title = "Spinbobert";
-			sprite = "SBOBB0";
-			width = 32;
-			height = 32;
-		}
-		132
-		{
-			title = "Cacolantern";
-			sprite = "CACOA0";
-			width = 32;
-			height = 32;
-		}
-		133
-		{
-			title = "Hangster";
-			sprite = "HBATC1";
-			width = 24;
-			height = 24;
-			hangs = 1;
-		}
-		127
-		{
-			title = "Hive Elemental";
-			sprite = "HIVEA0";
-			width = 32;
-			height = 80;
-		}
-		128
-		{
-			title = "Bumblebore";
-			sprite = "BUMBA1";
-			width = 16;
-			height = 32;
-		}
-		124
-		{
-			title = "Buggle";
-			sprite = "BBUZA1";
-			width = 20;
-			height = 24;
-		}
-		116
-		{
-			title = "Pointy";
-			sprite = "PNTYA1";
-			width = 8;
-			height = 16;
-		}
-	}
-
-	bosses
-	{
-		color = 8; // Dark_Gray
-		arrow = 1;
-		title = "Bosses";
-
-		200
-		{
-			title = "Egg Mobile";
-			sprite = "EGGMA1";
-			width = 24;
-			height = 76;
-		}
-		201
-		{
-			title = "Egg Slimer";
-			sprite = "EGGNA1";
-			width = 24;
-			height = 76;
-		}
-		202
-		{
-			title = "Sea Egg";
-			sprite = "EGGOA1";
-			width = 32;
-			height = 116;
-		}
-		203
-		{
-			title = "Egg Colosseum";
-			sprite = "EGGPA1";
-			width = 24;
-			height = 76;
-		}
-		204
-		{
-			title = "Fang";
-			sprite = "FANGA1";
-			width = 24;
-			height = 60;
-		}
-		206
-		{
-			title = "Brak Eggman (Old)";
-			sprite = "BRAKB1";
-			width = 48;
-			height = 160;
-		}
-		207
-		{
-			title = "Metal Sonic (Race)";
-			sprite = "METLI1";
-			width = 16;
-			height = 48;
-		}
-		208
-		{
-			title = "Metal Sonic (Battle)";
-			sprite = "METLC1";
-			width = 16;
-			height = 48;
-		}
-		209
-		{
-			title = "Brak Eggman";
-			sprite = "BRAK01";
-			width = 48;
-			height = 160;
-		}
-		290
-		{
-			arrow = 0;
-			title = "Boss Escape Point";
-			width = 8;
-			height = 16;
-			sprite = "internal:eggmanend";
-		}
-		291
-		{
-			arrow = 0;
-			title = "Egg Capsule Center";
-			width = 8;
-			height = 16;
-			sprite = "internal:capsule";
-		}
-		292
-		{
-			arrow = 0;
-			title = "Boss Waypoint";
-			width = 8;
-			height = 16;
-			sprite = "internal:eggmanway";
-		}
-		293
-		{
-			title = "Metal Sonic Gather Point";
-			sprite = "internal:metal";
-			width = 8;
-			height = 16;
-		}
-		294
-		{
-			title = "Fang Waypoint";
-			sprite = "internal:eggmanway";
-			width = 8;
-			height = 16;
-		}
-	}
-
-	rings
-	{
-		color = 14; // Yellow
-		title = "Rings and Weapon Panels";
-		width = 24;
-		height = 24;
-		sprite = "RINGA0";
-
-		300
-		{
-			title = "Ring";
-			sprite = "RINGA0";
-			width = 16;
-		}
-		301
-		{
-			title = "Bounce Ring";
-			sprite = "internal:RNGBA0";
-		}
-		302
-		{
-			title = "Rail Ring";
-			sprite = "internal:RNGRA0";
-		}
-		303
-		{
-			title = "Infinity Ring";
-			sprite = "internal:RNGIA0";
-		}
-		304
-		{
-			title = "Automatic Ring";
-			sprite = "internal:RNGAA0";
-		}
-		305
-		{
-			title = "Explosion Ring";
-			sprite = "internal:RNGEA0";
-		}
-		306
-		{
-			title = "Scatter Ring";
-			sprite = "internal:RNGSA0";
-		}
-		307
-		{
-			title = "Grenade Ring";
-			sprite = "internal:RNGGA0";
-		}
-		308
-		{
-			title = "CTF Team Ring (Red)";
-			sprite = "internal:RRNGA0";
-			width = 16;
-		}
-		309
-		{
-			title = "CTF Team Ring (Blue)";
-			sprite = "internal:BRNGA0";
-			width = 16;
-		}
-		330
-		{
-			title = "Bounce Ring Panel";
-			sprite = "internal:PIKBA0";
-		}
-		331
-		{
-			title = "Rail Ring Panel";
-			sprite = "internal:PIKRA0";
-		}
-		332
-		{
-			title = "Automatic Ring Panel";
-			sprite = "internal:PIKAA0";
-		}
-		333
-		{
-			title = "Explosion Ring Panel";
-			sprite = "internal:PIKEA0";
-		}
-		334
-		{
-			title = "Scatter Ring Panel";
-			sprite = "internal:PIKSA0";
-		}
-		335
-		{
-			title = "Grenade Ring Panel";
-			sprite = "internal:PIKGA0";
-		}
-	}
-
-	collectibles
-	{
-		color = 10; // Light_Green
-		title = "Other Collectibles";
-		width = 16;
-		height = 32;
-		sort = 1;
-		sprite = "CEMGA0";
-
-		310
-		{
-			title = "CTF Red Flag";
-			sprite = "RFLGA0";
-			width = 24;
-			height = 64;
-		}
-		311
-		{
-			title = "CTF Blue Flag";
-			sprite = "BFLGA0";
-			width = 24;
-			height = 64;
-		}
-		312
-		{
-			title = "Emerald Token";
-			sprite = "TOKEA0";
-			width = 16;
-			height = 32;
-		}
-		313
-		{
-			title = "Chaos Emerald 1 (Green)";
-			sprite = "CEMGA0";
-		}
-		314
-		{
-			title = "Chaos Emerald 2 (Purple)";
-			sprite = "CEMGB0";
-		}
-		315
-		{
-			title = "Chaos Emerald 3 (Blue)";
-			sprite = "CEMGC0";
-		}
-		316
-		{
-			title = "Chaos Emerald 4 (Cyan)";
-			sprite = "CEMGD0";
-		}
-		317
-		{
-			title = "Chaos Emerald 5 (Orange)";
-			sprite = "CEMGE0";
-		}
-		318
-		{
-			title = "Chaos Emerald 6 (Red)";
-			sprite = "CEMGF0";
-		}
-		319
-		{
-			title = "Chaos Emerald 7 (Gray)";
-			sprite = "CEMGG0";
-		}
-		320
-		{
-			title = "Emerald Hunt Location";
-			sprite = "SHRDA0";
-		}
-		321
-		{
-			title = "Match Chaos Emerald Spawn";
-			sprite = "CEMGA0";
-		}
-		322
-		{
-			title = "Emblem";
-			sprite = "EMBMA0";
-			width = 16;
-			height = 30;
-		}
-	}
-
-	boxes
-	{
-		color = 7; // Gray
-		blocking = 2;
-		title = "Monitors";
-		width = 18;
-		height = 40;
-
-		400
-		{
-			title = "Super Ring (10 Rings)";
-			sprite = "TVRIA0";
-		}
-		401
-		{
-			title = "Pity Shield";
-			sprite = "TVPIA0";
-		}
-		402
-		{
-			title = "Attraction Shield";
-			sprite = "TVATA0";
-		}
-		403
-		{
-			title = "Force Shield";
-			sprite = "TVFOA0";
-		}
-		404
-		{
-			title = "Armageddon Shield";
-			sprite = "TVARA0";
-		}
-		405
-		{
-			title = "Whirlwind Shield";
-			sprite = "TVWWA0";
-		}
-		406
-		{
-			title = "Elemental Shield";
-			sprite = "TVELA0";
-		}
-		407
-		{
-			title = "Super Sneakers";
-			sprite = "TVSSA0";
-		}
-		408
-		{
-			title = "Invincibility";
-			sprite = "TVIVA0";
-		}
-		409
-		{
-			title = "Extra Life";
-			sprite = "TV1UA0";
-		}
-		410
-		{
-			title = "Eggman";
-			sprite = "TVEGA0";
-		}
-		411
-		{
-			title = "Teleporter";
-			sprite = "TVMXA0";
-		}
-		413
-		{
-			title = "Gravity Boots";
-			sprite = "TVGVA0";
-		}
-		414
-		{
-			title = "CTF Team Ring Monitor (Red)";
-			sprite = "TRRIA0";
-		}
-		415
-		{
-			title = "CTF Team Ring Monitor (Blue)";
-			sprite = "TBRIA0";
-		}
-		416
-		{
-			title = "Recycler";
-			sprite = "TVRCA0";
-		}
-		418
-		{
-			title = "Score (1,000 Points)";
-			sprite = "TV1KA0";
-		}
-		419
-		{
-			title = "Score (10,000 Points)";
-			sprite = "TVTKA0";
-		}
-		420
-		{
-			title = "Flame Shield";
-			sprite = "TVFLA0";
-		}
-		421
-		{
-			title = "Water Shield";
-			sprite = "TVBBA0";
-		}
-		422
-		{
-			title = "Lightning Shield";
-			sprite = "TVZPA0";
-		}
-	}
-
-	boxes2
-	{
-		color = 18; // Gold
-		blocking = 2;
-		title = "Monitors (Respawning)";
-		width = 20;
-		height = 44;
-
-		431
-		{
-			title = "Pity Shield (Respawn)";
-			sprite = "TVPIB0";
-		}
-		432
-		{
-			title = "Attraction Shield (Respawn)";
-			sprite = "TVATB0";
-		}
-		433
-		{
-			title = "Force Shield (Respawn)";
-			sprite = "TVFOB0";
-		}
-		434
-		{
-			title = "Armageddon Shield (Respawn)";
-			sprite = "TVARB0";
-		}
-		435
-		{
-			title = "Whirlwind Shield (Respawn)";
-			sprite = "TVWWB0";
-		}
-		436
-		{
-			title = "Elemental Shield (Respawn)";
-			sprite = "TVELB0";
-		}
-		437
-		{
-			title = "Super Sneakers (Respawn)";
-			sprite = "TVSSB0";
-		}
-		438
-		{
-			title = "Invincibility (Respawn)";
-			sprite = "TVIVB0";
-		}
-		440
-		{
-			title = "Eggman (Respawn)";
-			sprite = "TVEGB0";
-		}
-		443
-		{
-			title = "Gravity Boots (Respawn)";
-			sprite = "TVGVB0";
-		}
-		450
-		{
-			title = "Flame Shield (Respawn)";
-			sprite = "TVFLB0";
-		}
-		451
-		{
-			title = "Water Shield (Respawn)";
-			sprite = "TVBBB0";
-		}
-		452
-		{
-			title = "Lightning Shield (Respawn)";
-			sprite = "TVZPB0";
-		}
-	}
-
-	generic
-	{
-		color = 11; // Light_Cyan
-		title = "Generic Items & Hazards";
-
-		500
-		{
-			title = "Air Bubble Patch";
-			sprite = "BUBLE0";
-			width = 8;
-			height = 16;
-		}
-		501
-		{
-			title = "Signpost";
-			sprite = "SIGND0";
-			width = 8;
-			height = 32;
-		}
-		502
-		{
-			arrow = 1;
-			title = "Star Post";
-			sprite = "STPTA0M0";
-			width = 64;
-			height = 128;
-		}
-		520
-		{
-			title = "Bomb Sphere";
-			sprite = "SPHRD0";
-			width = 16;
-			height = 24;
-		}
-		521
-		{
-			title = "Spikeball";
-			sprite = "SPIKA0";
-			width = 12;
-			height = 8;
-		}
-		522
-		{
-			title = "Wall Spike";
-			sprite = "WSPKALAR";
-			width = 16;
-			height = 14;
-			arrow = 1;
-		}
-		523
-		{
-			title = "Spike";
-			sprite = "USPKA0";
-			width = 8;
-			height = 32;
-		}
-		1130
-		{
-			title = "Small Mace";
-			sprite = "SMCEA0";
-			width = 17;
-			height = 34;
-		}
-		1131
-		{
-			title = "Big Mace";
-			sprite = "BMCEA0";
-			width = 34;
-			height = 68;
-		}
-		1136
-		{
-			title = "Small Fireball";
-			sprite = "SFBRA0";
-			width = 17;
-			height = 34;
-		}
-		1137
-		{
-			title = "Large Fireball";
-			sprite = "BFBRA0";
-			width = 34;
-			height = 68;
-		}
-	}
-
-	springs
-	{
-		color = 12; // Light_Red
-		title = "Springs and Fans";
-		width = 20;
-		height = 16;
-		sprite = "RSPRD2";
-
-		540
-		{
-			title = "Fan";
-			sprite = "FANSA0D0";
-			width = 16;
-			height = 8;
-		}
-		541
-		{
-			title = "Gas Jet";
-			sprite = "STEMD0";
-			width = 32;
-		}
-		542
-		{
-			title = "Bumper";
-			sprite = "BUMPA0";
-			width = 32;
-			height = 64;
-		}
-		543
-		{
-			title = "Balloon";
-			sprite = "BLONA0";
-			width = 32;
-			height = 64;
-		}
-		550
-		{
-			title = "Yellow Spring";
-			sprite = "SPRYA0";
-		}
-		551
-		{
-			title = "Red Spring";
-			sprite = "SPRRA0";
-		}
-		552
-		{
-			title = "Blue Spring";
-			sprite = "SPRBA0";
-		}
-		555
-		{
-			arrow = 1;
-			title = "Diagonal Yellow Spring";
-			sprite = "YSPRD2";
-			width = 16;
-		}
-		556
-		{
-			arrow = 1;
-			title = "Diagonal Red Spring";
-			sprite = "RSPRD2";
-			width = 16;
-		}
-		557
-		{
-			arrow = 1;
-			title = "Diagonal Blue Spring";
-			sprite = "BSPRD2";
-			width = 16;
-		}
-		558
-		{
-			arrow = 1;
-			title = "Horizontal Yellow Spring";
-			sprite = "SSWYD2D8";
-			width = 16;
-			height = 32;
-		}
-		559
-		{
-			arrow = 1;
-			title = "Horizontal Red Spring";
-			sprite = "SSWRD2D8";
-			width = 16;
-			height = 32;
-		}
-		560
-		{
-			arrow = 1;
-			title = "Horizontal Blue Spring";
-			sprite = "SSWBD2D8";
-			width = 16;
-			height = 32;
-		}
-		1134
-		{
-			title = "Yellow Spring Ball";
-			sprite = "YSPBA0";
-			width = 17;
-			height = 34;
-		}
-		1135
-		{
-			title = "Red Spring Ball";
-			sprite = "RSPBA0";
-			width = 17;
-			height = 34;
-		}
-		544
-		{
-			arrow = 1;
-			title = "Yellow Boost Panel";
-			sprite = "BSTYA0";
-			width = 28;
-			height = 2;
-		}
-		545
-		{
-			arrow = 1;
-			title = "Red Boost Panel";
-			sprite = "BSTRA0";
-			width = 28;
-			height = 2;
-		}
-	}
-
-	patterns
-	{
-		color = 5; // Magenta
-		arrow = 1;
-		title = "Special Placement Patterns";
-		width = 16;
-		height = 384;
-		sprite = "RINGA0";
-
-		600
-		{
-			arrow = 0;
-			title = "5 Vertical Rings (Yellow Spring)";
-			sprite = "RINGA0";
-		}
-		601
-		{
-			arrow = 0;
-			title = "5 Vertical Rings (Red Spring)";
-			sprite = "RINGA0";
-			height = 1024;
-		}
-		602
-		{
-			title = "5 Diagonal Rings (Yellow Spring)";
-			sprite = "RINGA0";
-			height = 32;
-		}
-		603
-		{
-			title = "10 Diagonal Rings (Red Spring)";
-			sprite = "RINGA0";
-			height = 32;
-		}
-		604
-		{
-			title = "Circle of Rings";
-			sprite = "RINGA0";
-			width = 96;
-			height = 192;
-		}
-		605
-		{
-			title = "Circle of Rings (Big)";
-			sprite = "RINGA0";
-			width = 192;
-		}
-		606
-		{
-			title = "Circle of Blue Spheres";
-			sprite = "SPHRA0";
-			width = 96;
-			height = 192;
-		}
-		607
-		{
-			title = "Circle of Blue Spheres (Big)";
-			sprite = "SPHRA0";
-			width = 192;
-		}
-		608
-		{
-			title = "Circle of Rings and Spheres";
-			sprite = "SPHRA0";
-			width = 96;
-			height = 192;
-		}
-		609
-		{
-			title = "Circle of Rings and Spheres (Big)";
-			sprite = "SPHRA0";
-			width = 192;
-		}
-	}
-
-	invisible
-	{
-		color = 15; // White
-		title = "Misc. Invisible";
-		width = 0;
-		height = 0;
-		sprite = "UNKNA0";
-		sort = 1;
-		fixedsize = true;
-		blocking = 0;
-
-		700
-		{
-			title = "Water Ambience A (Large)";
-			sprite = "internal:ambiance";
-		}
-
-		701
-		{
-			title = "Water Ambience B (Large)";
-			sprite = "internal:ambiance";
-		}
-
-		702
-		{
-			title = "Water Ambience C (Medium)";
-			sprite = "internal:ambiance";
-		}
-
-		703
-		{
-			title = "Water Ambience D (Medium)";
-			sprite = "internal:ambiance";
-		}
-
-		704
-		{
-			title = "Water Ambience E (Small)";
-			sprite = "internal:ambiance";
-		}
-
-		705
-		{
-			title = "Water Ambience F (Small)";
-			sprite = "internal:ambiance";
-		}
-
-		706
-		{
-			title = "Water Ambience G (Extra Large)";
-			sprite = "internal:ambiance";
-		}
-
-		707
-		{
-			title = "Water Ambience H (Extra Large)";
-			sprite = "internal:ambiance";
-		}
-
-		708
-		{
-			title = "Disco Ambience";
-			sprite = "internal:ambiance";
-		}
-
-		709
-		{
-			title = "Volcano Ambience";
-			sprite = "internal:ambiance";
-		}
-
-		710
-		{
-			title = "Machine Ambience";
-			sprite = "internal:ambiance";
-		}
-
-		750
-		{
-			title = "Slope Vertex";
-			sprite = "internal:vertexslope";
-		}
-
-		751
-		{
-			arrow = 1;
-			title = "Teleport Destination";
-			sprite = "internal:tele";
-		}
-
-		752
-		{
-			arrow = 1;
-			title = "Alternate View Point";
-			sprite = "internal:view";
-		}
-
-		753
-		{
-			title = "Zoom Tube Waypoint";
-			sprite = "internal:zoom";
-		}
-
-		754
-		{
-			title = "Push Point";
-			sprite = "GWLGA0";
-		}
-		755
-		{
-			title = "Pull Point";
-			sprite = "GWLRA0";
-		}
-		756
-		{
-			title = "Blast Linedef Executor";
-			sprite = "TOADA0";
-			width = 32;
-			height = 16;
-		}
-		757
-		{
-			title = "Fan Particle Generator";
-			sprite = "PRTLA0";
-			width = 8;
-			height = 16;
-		}
-		758
-		{
-			title = "Object Angle Anchor";
-			sprite = "internal:view";
-		}
-		760
-		{
-			title = "PolyObject Anchor";
-			sprite = "internal:polyanchor";
-		}
-		761
-		{
-			title = "PolyObject Spawn Point";
-			sprite = "internal:polycenter";
-		}
-		762
-		{
-			title = "PolyObject Spawn Point (Crush)";
-			sprite = "internal:polycentercrush";
-		}
-		780
-		{
-			title = "Skybox View Point";
-			sprite = "internal:skyb";
-		}
-	}
-
-	greenflower
-	{
-		color = 10; // Green
-		title = "Greenflower";
-
-		800
-		{
-			title = "GFZ Flower";
-			sprite = "FWR1A0";
-			width = 16;
-			height = 40;
-		}
-		801
-		{
-			title = "Sunflower";
-			sprite = "FWR2A0";
-			width = 16;
-			height = 96;
-		}
-		802
-		{
-			title = "Budding Flower";
-			sprite = "FWR3A0";
-			width = 8;
-			height = 32;
-		}
-		803
-		{
-			title = "Blueberry Bush";
-			sprite = "BUS3A0";
-			width = 16;
-			height = 32;
-		}
-		804
-		{
-			title = "Berry Bush";
-			sprite = "BUS1A0";
-			width = 16;
-			height = 32;
-		}
-		805
-		{
-			title = "Bush";
-			sprite = "BUS2A0";
-			width = 16;
-			height = 32;
-		}
-		806
-		{
-			title = "GFZ Tree";
-			sprite = "TRE1A0";
-			width = 20;
-			height = 128;
-		}
-		807
-		{
-			title = "GFZ Berry Tree";
-			sprite = "TRE1B0";
-			width = 20;
-			height = 128;
-		}
-		808
-		{
-			title = "GFZ Cherry Tree";
-			sprite = "TRE1C0";
-			width = 20;
-			height = 128;
-		}
-		809
-		{
-			title = "Checkered Tree";
-			sprite = "TRE2A0";
-			width = 20;
-			height = 200;
-		}
-		810
-		{
-			title = "Checkered Tree (Sunset)";
-			sprite = "TRE2B0";
-			width = 20;
-			height = 200;
-		}
-		811
-		{
-			title = "Polygon Tree";
-			sprite = "TRE4A0";
-			width = 20;
-			height = 200;
-		}
-		812
-		{
-			title = "Bush Tree";
-			sprite = "TRE5A0";
-			width = 20;
-			height = 200;
-		}
-		813
-		{
-			title = "Red Bush Tree";
-			sprite = "TRE5B0";
-			width = 20;
-			height = 200;
-		}
-	}
-
-	technohill
-	{
-		color = 10; // Green
-		title = "Techno Hill";
-
-		900
-		{
-			title = "THZ Steam Flower";
-			sprite = "THZPA0";
-			width = 8;
-			height = 32;
-		}
-		901
-		{
-			title = "Alarm";
-			sprite = "ALRMA0";
-			width = 8;
-			height = 16;
-			hangs = 1;
-		}
-		902
-		{
-			title = "THZ Spin Flower (Red)";
-			sprite = "FWR5A0";
-			width = 16;
-			height = 64;
-		}
-		903
-		{
-			title = "THZ Spin Flower (Yellow)";
-			sprite = "FWR6A0";
-			width = 16;
-			height = 64;
-		}
-		904
-		{
-			arrow = 1;
-			title = "Whistlebush";
-			sprite = "THZTA0";
-			width = 16;
-			height = 64;
-		}
-	}
-
-	deepsea
-	{
-		color = 10; // Green
-		title = "Deep Sea";
-
-		1000
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Gargoyle";
-			sprite = "GARGA1";
-			width = 16;
-			height = 40;
-		}
-		1009
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Gargoyle (Big)";
-			sprite = "GARGB1";
-			width = 32;
-			height = 80;
-		}
-		1001
-		{
-			title = "Seaweed";
-			sprite = "SEWEA0";
-			width = 24;
-			height = 56;
-		}
-		1002
-		{
-			title = "Dripping Water";
-			sprite = "DRIPD0";
-			width = 8;
-			height = 16;
-			hangs = 1;
-		}
-		1003
-		{
-			title = "Coral (Green)";
-			sprite = "CORLA0";
-			width = 29;
-			height = 40;
-		}
-		1004
-		{
-			title = "Coral (Red)";
-			sprite = "CORLB0";
-			width = 30;
-			height = 53;
-		}
-		1005
-		{
-			title = "Coral (Orange)";
-			sprite = "CORLC0";
-			width = 28;
-			height = 41;
-		}
-		1006
-		{
-			title = "Blue Crystal";
-			sprite = "BCRYA1";
-			width = 8;
-			height = 16;
-		}
-		1007
-		{
-			title = "Kelp";
-			sprite = "KELPA0";
-			width = 16;
-			height = 292;
-		}
-		1008
-		{
-			title = "Stalagmite (DSZ1)";
-			sprite = "DSTGA0";
-			width = 8;
-			height = 116;
-		}
-		1010
-		{
-			arrow = 1;
-			title = "Light Beam";
-			sprite = "LIBEARAL";
-			width = 16;
-			height = 16;
-		}
-		1011
-		{
-			title = "Stalagmite (DSZ2)";
-			sprite = "DSTGA0";
-			width = 8;
-			height = 116;
-		}
-		1012
-		{
-			arrow = 1;
-			title = "Big Floating Mine";
-			width = 28;
-			height = 56;
-			sprite = "BMNEA1";
-		}
-		1013
-		{
-			title = "Animated Kelp";
-			sprite = "ALGAA0";
-			width = 48;
-			height = 120;
-		}
-		1014
-		{
-			title = "Large Coral (Brown)";
-			sprite = "CORLD0";
-			width = 56;
-			height = 112;
-		}
-		1015
-		{
-			title = "Large Coral (Beige)";
-			sprite = "CORLE0";
-			width = 56;
-			height = 112;
-		}
-	}
-
-	castleeggman
-	{
-		color = 10; // Green
-		title = "Castle Eggman";
-
-		1100
-		{
-			title = "Chain (Decorative)";
-			sprite = "CHANA0";
-			width = 4;
-			height = 128;
-			hangs = 1;
-		}
-		1101
-		{
-			title = "Torch";
-			sprite = "FLAMA0E0";
-			width = 8;
-			height = 32;
-		}
-		1102
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Eggman Statue";
-			sprite = "ESTAA1";
-			width = 32;
-			height = 240;
-		}
-		1103
-		{
-			title = "CEZ Flower";
-			sprite = "FWR4A0";
-			width = 16;
-			height = 40;
-		}
-		1104
-		{
-			title = "Mace Spawnpoint";
-			sprite = "SMCEA0";
-			width = 17;
-			height = 34;
-		}
-		1105
-		{
-			title = "Chain with Maces Spawnpoint";
-			sprite = "SMCEA0";
-			width = 17;
-			height = 34;
-		}
-		1106
-		{
-			title = "Chained Spring Spawnpoint";
-			sprite = "YSPBA0";
-			width = 17;
-			height = 34;
-		}
-		1107
-		{
-			title = "Chain Spawnpoint";
-			sprite = "BMCHA0";
-			width = 17;
-			height = 34;
-		}
-		1108
-		{
-			arrow = 1;
-			title = "Hidden Chain Spawnpoint";
-			sprite = "internal:chain3";
-			width = 17;
-			height = 34;
-		}
-		1109
-		{
-			title = "Firebar Spawnpoint";
-			sprite = "BFBRA0";
-			width = 17;
-			height = 34;
-		}
-		1110
-		{
-			title = "Custom Mace Spawnpoint";
-			sprite = "SMCEA0";
-			width = 17;
-			height = 34;
-		}
-		1111
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Crawla Statue";
-			sprite = "CSTAA1";
-			width = 16;
-			height = 40;
-		}
-		1112
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Lance-a-Bot Statue";
-			sprite = "CBBSA1";
-			width = 32;
-			height = 72;
-		}
-		1114
-		{
-			title = "Pine Tree";
-			sprite = "PINEA0";
-			width = 16;
-			height = 628;
-		}
-		1115
-		{
-			title = "CEZ Shrub (Small)";
-			sprite = "CEZBA0";
-			width = 16;
-			height = 24;
-		}
-		1116
-		{
-			title = "CEZ Shrub (Large)";
-			sprite = "CEZBB0";
-			width = 32;
-			height = 48;
-		}
-		1117
-		{
-			arrow = 1;
-			title = "Pole Banner (Red)";
-			sprite = "BANRA0";
-			width = 40;
-			height = 224;
-		}
-		1118
-		{
-			arrow = 1;
-			title = "Pole Banner (Blue)";
-			sprite = "BANRA0";
-			width = 40;
-			height = 224;
-		}
-		1119
-		{
-			title = "Candle";
-			sprite = "CNDLA0";
-			width = 8;
-			height = 48;
-		}
-		1120
-		{
-			title = "Candle Pricket";
-			sprite = "CNDLB0";
-			width = 8;
-			height = 176;
-		}
-		1121
-		{
-			title = "Flame Holder";
-			sprite = "FLMHA0";
-			width = 24;
-			height = 80;
-		}
-		1122
-		{
-			title = "Fire Torch";
-			sprite = "CTRCA0";
-			width = 16;
-			height = 80;
-		}
-		1123
-		{
-			title = "Cannonball Launcher";
-			sprite = "internal:cannonball";
-			width = 8;
-			height = 16;
-		}
-		1124
-		{
-			blocking = 2;
-			title = "Cannonball";
-			sprite = "CBLLA0";
-			width = 20;
-			height = 40;
-		}
-		1125
-		{
-			title = "Brambles";
-			sprite = "CABRALAR";
-			width = 48;
-			height = 32;
-		}
-		1126
-		{
-			title = "Invisible Lockon Object";
-			sprite = "LCKNC0";
-			width = 16;
-			height = 32;
-		}
-		1127
-		{
-			title = "Spectator Eggrobo";
-			sprite = "EGR1A1";
-			width = 20;
-			height = 72;
-		}
-		1128
-		{
-			arrow = 1;
-			title = "Waving Flag (Red)";
-			sprite = "CFLGA0";
-			width = 8;
-			height = 208;
-		}
-		1129
-		{
-			arrow = 1;
-			title = "Waving Flag (Blue)";
-			sprite = "CFLGA0";
-			width = 8;
-			height = 208;
-		}
-	}
-
-	aridcanyon
-	{
-		color = 10; // Green
-		title = "Arid Canyon";
-
-		1200
-		{
-			title = "Tumbleweed (Big)";
-			sprite = "BTBLA0";
-			width = 24;
-			height = 48;
-		}
-		1201
-		{
-			title = "Tumbleweed (Small)";
-			sprite = "STBLA0";
-			width = 12;
-			height = 24;
-		}
-		1202
-		{
-			arrow = 1;
-			title = "Rock Spawner";
-			sprite = "ROIAA0";
-			width = 8;
-			height = 16;
-		}
-		1203
-		{
-			title = "Tiny Red Flower Cactus";
-			sprite = "CACTA0";
-			width = 13;
-			height = 24;
-		}
-		1204
-		{
-			title = "Small Red Flower Cactus";
-			sprite = "CACTB0";
-			width = 15;
-			height = 52;
-		}
-		1205
-		{
-			title = "Tiny Blue Flower Cactus";
-			sprite = "CACTC0";
-			width = 13;
-			height = 24;
-		}
-		1206
-		{
-			title = "Small Blue Flower Cactus";
-			sprite = "CACTD0";
-			width = 15;
-			height = 52;
-		}
-		1207
-		{
-			title = "Prickly Pear";
-			sprite = "CACTE0";
-			width = 32;
-			height = 96;
-		}
-		1208
-		{
-			title = "Barrel Cactus";
-			sprite = "CACTF0";
-			width = 20;
-			height = 128;
-		}
-		1209
-		{
-			title = "Tall Barrel Cactus";
-			sprite = "CACTG0";
-			width = 24;
-			height = 224;
-		}
-		1210
-		{
-			title = "Armed Cactus";
-			sprite = "CACTH0";
-			width = 24;
-			height = 256;
-		}
-		1211
-		{
-			title = "Ball Cactus";
-			sprite = "CACTI0";
-			width = 48;
-			height = 96;
-		}
-		1212
-		{
-			title = "Caution Sign";
-			sprite = "WWSGAR";
-			width = 22;
-			height = 64;
-		}
-		1213
-		{
-			title = "Cacti Sign";
-			sprite = "WWS2AR";
-			width = 22;
-			height = 64;
-		}
-		1214
-		{
-			title = "Sharp Turn Sign";
-			sprite = "WWS3ALAR";
-			width = 16;
-			height = 192;
-		}
-		1215
-		{
-			title = "Mine Oil Lamp";
-			sprite = "OILLA0";
-			width = 22;
-			height = 64;
-			hangs = 1;
-		}
-		1216
-		{
-			title = "TNT Barrel";
-			sprite = "BARRA1";
-			width = 24;
-			height = 63;
-		}
-		1217
-		{
-			title = "TNT Proximity Shell";
-			sprite = "REMTA0";
-			width = 64;
-			height = 40;
-		}
-		1218
-		{
-			title = "Dust Devil";
-			sprite = "TAZDCR";
-			width = 80;
-			height = 416;
-		}
-		1219
-		{
-			title = "Minecart Spawner";
-			sprite = "MCRTCLFR";
-			width = 22;
-			height = 32;
-		}
-		1220
-		{
-			title = "Minecart Stopper";
-			sprite = "MCRTIR";
-			width = 32;
-			height = 32;
-		}
-		1221
-		{
-			title = "Minecart Saloon Door";
-			sprite = "SALDARAL";
-			width = 96;
-			height = 160;
-		}
-		1222
-		{
-			title = "Train Cameo Spawner";
-			sprite = "TRAEBRBL";
-			width = 28;
-			height = 32;
-		}
-		1223
-		{
-			title = "Train Dust Spawner";
-			sprite = "ADSTA0";
-			width = 4;
-			height = 4;
-		}
-		1224
-		{
-			title = "Train Steam Spawner";
-			sprite = "STEAA0";
-			width = 4;
-			height = 4;
-		}
-		1229
-		{
-			title = "Minecart Switch Point";
-			sprite = "internal:zoom";
-			width = 8;
-			height = 16;
-		}
-		1230
-		{
-			title = "Tiny Cactus";
-			sprite = "CACTJ0";
-			width = 13;
-			height = 28;
-		}
-		1231
-		{
-			title = "Small Cactus";
-			sprite = "CACTK0";
-			width = 15;
-			height = 60;
-		}
-	}
-
-	redvolcano
-	{
-		color = 10; // Green
-		title = "Red Volcano";
-
-		1300
-		{
-			arrow = 1;
-			title = "Flame Jet (Horizontal)";
-			sprite = "internal:flameh";
-			width = 16;
-			height = 40;
-		}
-		1301
-		{
-			title = "Flame Jet (Vertical)";
-			sprite = "internal:flamev";
-			width = 16;
-			height = 40;
-		}
-		1302
-		{
-			title = "Spinning Flame Jet (Counter-Clockwise)";
-			sprite = "internal:flame2";
-			width = 16;
-			height = 24;
-		}
-		1303
-		{
-			title = "Spinning Flame Jet (Clockwise)";
-			sprite = "internal:flame1";
-			width = 16;
-			height = 24;
-		}
-		1304
-		{
-			title = "Lavafall";
-			sprite = "LFALF0";
-			width = 30;
-			height = 32;
-		}
-		1305
-		{
-			title = "Rollout Rock";
-			sprite = "PUMIA1A5";
-			width = 30;
-			height = 60;
-		}
-		1306
-		{
-			title = "Big Fern";
-			sprite = "JPLAB0";
-			width = 32;
-			height = 48;
-		}
-		1307
-		{
-			title = "Jungle Palm";
-			sprite = "JPLAC0";
-			width = 32;
-			height = 48;
-		}
-		1308
-		{
-			title = "Torch Flower";
-			sprite = "TFLOA0";
-			width = 14;
-			height = 110;
-		}
-		1309
-		{
-			title = "RVZ1 Wall Vine (Long)";
-			sprite = "WVINALAR";
-			width = 1;
-			height = 288;
-		}
-		1310
-		{
-			title = "RVZ1 Wall Vine (Short)";
-			sprite = "WVINBLBR";
-			width = 1;
-			height = 288;
-		}
-	}
-
-	botanicserenity
-	{
-		color = 10; // Green
-		title = "Botanic Serenity";
-		width = 16;
-		height = 32;
-		sprite = "BSZ1A0";
-		1400
-		{
-			title = "Tall Flower (Red)";
-			sprite = "BSZ1A0";
-		}
-		1401
-		{
-			title = "Tall Flower (Purple)";
-			sprite = "BSZ1B0";
-		}
-		1402
-		{
-			title = "Tall Flower (Blue)";
-			sprite = "BSZ1C0";
-		}
-		1403
-		{
-			title = "Tall Flower (Cyan)";
-			sprite = "BSZ1D0";
-		}
-		1404
-		{
-			title = "Tall Flower (Yellow)";
-			sprite = "BSZ1E0";
-		}
-		1405
-		{
-			title = "Tall Flower (Orange)";
-			sprite = "BSZ1F0";
-		}
-		1410
-		{
-			title = "Medium Flower (Red)";
-			sprite = "BSZ2A0";
-		}
-		1411
-		{
-			title = "Medium Flower (Purple)";
-			sprite = "BSZ2B0";
-		}
-		1412
-		{
-			title = "Medium Flower (Blue)";
-			sprite = "BSZ2C0";
-		}
-		1413
-		{
-			title = "Medium Flower (Cyan)";
-			sprite = "BSZ2D0";
-		}
-		1414
-		{
-			title = "Medium Flower (Yellow)";
-			sprite = "BSZ2E0";
-		}
-		1415
-		{
-			title = "Medium Flower (Orange)";
-			sprite = "BSZ2F0";
-		}
-		1420
-		{
-			title = "Short Flower (Red)";
-			sprite = "BSZ3A0";
-		}
-		1421
-		{
-			title = "Short Flower (Purple)";
-			sprite = "BSZ3B0";
-		}
-		1422
-		{
-			title = "Short Flower (Blue)";
-			sprite = "BSZ3C0";
-		}
-		1423
-		{
-			title = "Short Flower (Cyan)";
-			sprite = "BSZ3D0";
-		}
-		1424
-		{
-			title = "Short Flower (Yellow)";
-			sprite = "BSZ3E0";
-		}
-		1425
-		{
-			title = "Short Flower (Orange)";
-			sprite = "BSZ3F0";
-		}
-		1430
-		{
-			title = "Tulip (Red)";
-			sprite = "BST1A0";
-		}
-		1431
-		{
-			title = "Tulip (Purple)";
-			sprite = "BST2A0";
-		}
-		1432
-		{
-			title = "Tulip (Blue)";
-			sprite = "BST3A0";
-		}
-		1433
-		{
-			title = "Tulip (Cyan)";
-			sprite = "BST4A0";
-		}
-		1434
-		{
-			title = "Tulip (Yellow)";
-			sprite = "BST5A0";
-		}
-		1435
-		{
-			title = "Tulip (Orange)";
-			sprite = "BST6A0";
-		}
-		1440
-		{
-			title = "Cluster (Red)";
-			sprite = "BSZ5A0";
-		}
-		1441
-		{
-			title = "Cluster (Purple)";
-			sprite = "BSZ5B0";
-		}
-		1442
-		{
-			title = "Cluster (Blue)";
-			sprite = "BSZ5C0";
-		}
-		1443
-		{
-			title = "Cluster (Cyan)";
-			sprite = "BSZ5D0";
-		}
-		1444
-		{
-			title = "Cluster (Yellow)";
-			sprite = "BSZ5E0";
-		}
-		1445
-		{
-			title = "Cluster (Orange)";
-			sprite = "BSZ5F0";
-		}
-		1450
-		{
-			title = "Bush (Red)";
-			sprite = "BSZ6A0";
-		}
-		1451
-		{
-			title = "Bush (Purple)";
-			sprite = "BSZ6B0";
-		}
-		1452
-		{
-			title = "Bush (Blue)";
-			sprite = "BSZ6C0";
-		}
-		1453
-		{
-			title = "Bush (Cyan)";
-			sprite = "BSZ6D0";
-		}
-		1454
-		{
-			title = "Bush (Yellow)";
-			sprite = "BSZ6E0";
-		}
-		1455
-		{
-			title = "Bush (Orange)";
-			sprite = "BSZ6F0";
-		}
-		1460
-		{
-			title = "Vine (Red)";
-			sprite = "BSZ7A0";
-		}
-		1461
-		{
-			title = "Vine (Purple)";
-			sprite = "BSZ7B0";
-		}
-		1462
-		{
-			title = "Vine (Blue)";
-			sprite = "BSZ7C0";
-		}
-		1463
-		{
-			title = "Vine (Cyan)";
-			sprite = "BSZ7D0";
-		}
-		1464
-		{
-			title = "Vine (Yellow)";
-			sprite = "BSZ7E0";
-		}
-		1465
-		{
-			title = "Vine (Orange)";
-			sprite = "BSZ7F0";
-		}
-		1470
-		{
-			title = "BSZ Shrub";
-			sprite = "BSZ8A0";
-		}
-		1471
-		{
-			title = "BSZ Clover";
-			sprite = "BSZ8B0";
-		}
-		1473
-		{
-			title = "Palm Tree (Big)";
-			width = 16;
-			height = 160;
-			sprite = "BSZ8D0";
-		}
-		1475
-		{
-			title = "Palm Tree (Small)";
-			width = 16;
-			height = 80;
-			sprite = "BSZ8F0";
-		}
-	}
-
-	azuretemple
-	{
-		color = 10; // Green
-		title = "Azure Temple";
-
-		1500
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Glaregoyle";
-			sprite = "BGARA1";
-			width = 16;
-			height = 40;
-		}
-		1501
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Glaregoyle (Up)";
-			sprite = "BGARA1";
-			width = 16;
-			height = 40;
-		}
-		1502
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Glaregoyle (Down)";
-			sprite = "BGARA1";
-			width = 16;
-			height = 40;
-		}
-		1503
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Glaregoyle (Long)";
-			sprite = "BGARA1";
-			width = 16;
-			height = 40;
-		}
-		1504
-		{
-			title = "ATZ Target";
-			sprite = "RCRYB0";
-			width = 24;
-			height = 32;
-		}
-		1505
-		{
-			title = "Green Flame";
-			sprite = "CFLMA0E0";
-			width = 8;
-			height = 32;
-		}
-		1506
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Blue Gargoyle";
-			sprite = "BGARD1";
-			width = 16;
-			height = 40;
-		}
-	}
-
-	dreamhill
-	{
-		color = 10; // Green
-		title = "Dream Hill";
-
-		1600
-		{
-			title = "Spring Tree";
-			sprite = "TRE6A0";
-			width = 16;
-			height = 32;
-		}
-		1601
-		{
-			title = "Shleep";
-			sprite = "SHLPA0";
-			width = 24;
-			height = 32;
-		}
-		1602
-		{
-			title = "Nightopian";
-			sprite = "NTPNA1";
-			width = 16;
-			height = 40;
-		}
-	}
-
-	nightstrk
-	{
-		color = 13; // Pink
-		title = "NiGHTS Track";
-		width = 8;
-		height = 4096;
-		sprite = "UNKNA0";
-
-		1700
-		{
-			title = "Axis";
-			sprite = "internal:axis1";
-			circle = 1;
-		}
-		1701
-		{
-			title = "Axis Transfer";
-			sprite = "internal:axis2";
-		}
-		1702
-		{
-			title = "Axis Transfer Line";
-			sprite = "internal:axis3";
-		}
-		1710
-		{
-			title = "Ideya Capture";
-			sprite = "CAPSA0";
-			width = 72;
-			height = 144;
-		}
-	}
-
-	nights
-	{
-		color = 13; // Pink
-		title = "NiGHTS Items";
-		width = 16;
-		height = 32;
-
-		1703
-		{
-			title = "Ideya Drone";
-			sprite = "NDRNA1";
-			width = 16;
-			height = 56;
-		}
-		1704
-		{
-			arrow = 1;
-			title = "NiGHTS Bumper";
-			sprite = "NBMPG3G7";
-			width = 32;
-			height = 64;
-		}
-		1705
-		{
-			arrow = 1;
-			title = "Hoop (Generic)";
-			sprite = "HOOPA0";
-			width = 80;
-			height = 160;
-		}
-		1706
-		{
-			title = "Blue Sphere";
-			sprite = "SPHRA0";
-			width = 16;
-			height = 24;
-		}
-		1707
-		{
-			title = "Super Paraloop";
-			sprite = "NPRUA0";
-		}
-		1708
-		{
-			title = "Drill Refill";
-			sprite = "NPRUB0";
-		}
-		1709
-		{
-			title = "Nightopian Helper";
-			sprite = "NPRUC0";
-		}
-		1711
-		{
-			title = "Extra Time";
-			sprite = "NPRUD0";
-		}
-		1712
-		{
-			title = "Link Freeze";
-			sprite = "NPRUE0";
-		}
-		1713
-		{
-			arrow = 1;
-			title = "Hoop (Customizable)";
-			sprite = "HOOPA0";
-			width = 80;
-			height = 160;
-		}
-		1714
-		{
-			title = "Ideya Anchor Point";
-			sprite = "internal:axis1";
-			width = 8;
-			height = 16;
-		}
-	}
-
-	mario
-	{
-		color = 6; // Brown
-		title = "Mario";
-
-		1800
-		{
-			title = "Coin";
-			sprite = "COINA0";
-			width = 16;
-			height = 24;
-		}
-		1801
-		{
-			arrow = 1;
-			title = "Goomba";
-			sprite = "GOOMA0";
-			width = 24;
-			height = 32;
-		}
-		1802
-		{
-			arrow = 1;
-			title = "Goomba (Blue)";
-			sprite = "BGOMA0";
-			width = 24;
-			height = 32;
-		}
-		1803
-		{
-			title = "Fire Flower";
-			sprite = "FFWRB0";
-			width = 16;
-			height = 32;
-		}
-		1804
-		{
-			title = "Koopa Shell";
-			sprite = "SHLLA1";
-			width = 16;
-			height = 20;
-		}
-		1805
-		{
-			title = "Puma (Jumping Fireball)";
-			sprite = "PUMAA0";
-			width = 8;
-			height = 16;
-		}
-		1806
-		{
-			title = "King Bowser";
-			sprite = "KOOPA0";
-			width = 16;
-			height = 48;
-		}
-		1807
-		{
-			title = "Axe";
-			sprite = "MAXEA0";
-			width = 8;
-			height = 16;
-		}
-		1808
-		{
-			title = "Bush (Short)";
-			sprite = "MUS1A0";
-			width = 16;
-			height = 32;
-		}
-		1809
-		{
-			title = "Bush (Tall)";
-			sprite = "MUS2A0";
-			width = 16;
-			height = 32;
-		}
-		1810
-		{
-			title = "Toad";
-			sprite = "TOADA0";
-			width = 8;
-			height = 32;
-		}
-	}
-
-	christmasdisco
-	{
-		color = 10; // Green
-		title = "Christmas & Disco";
-
-		1850
-		{
-			title = "Christmas Pole";
-			sprite = "XMS1A0";
-			width = 16;
-			height = 40;
-		}
-		1851
-		{
-			title = "Candy Cane";
-			sprite = "XMS2A0";
-			width = 8;
-			height = 32;
-		}
-		1852
-		{
-			blocking = 2;
-			title = "Snowman";
-			sprite = "XMS3A0";
-			width = 16;
-			height = 64;
-		}
-		1853
-		{
-			blocking = 2;
-			title = "Snowman (With Hat)";
-			sprite = "XMS3B0";
-			width = 16;
-			height = 80;
-		}
-		1854
-		{
-			title = "Lamp Post";
-			sprite = "XMS4A0";
-			width = 8;
-			height = 120;
-		}
-		1855
-		{
-			title = "Lamp Post (Snow)";
-			sprite = "XMS4B0";
-			width = 8;
-			height = 120;
-		}
-		1856
-		{
-			title = "Hanging Star";
-			sprite = "XMS5A0";
-			width = 4;
-			height = 80;
-			hangs = 1;
-		}
-		1857
-		{
-			title = "Berry Bush (Snow)";
-			sprite = "BUS1B0";
-			width = 16;
-			height = 32;
-		}
-		1858
-		{
-			title = "Bush (Snow)";
-			sprite = "BUS2B0";
-			width = 16;
-			height = 32;
-		}
-		1859
-		{
-			title = "Blueberry Bush (Snow)";
-			sprite = "BUS3B0";
-			width = 16;
-			height = 32;
-		}
-		1875
-		{
-			title = "Disco Ball";
-			sprite = "DBALA0";
-			width = 16;
-			height = 54;
-			hangs = 1;
-		}
-		1876
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Eggman Disco Statue";
-			sprite = "ESTAB1";
-			width = 20;
-			height = 96;
-		}
-	}
-
-	stalagmites
-	{
-		color = 10; // Green
-		title = "Stalagmites";
-		width = 16;
-		height = 40;
-
-		1900
-		{
-			title = "Brown Stalagmite (Tall)";
-			sprite = "STLGA0";
-			width = 16;
-			height = 40;
-		}
-		1901
-		{
-			title = "Brown Stalagmite";
-			sprite = "STLGB0";
-			width = 16;
-			height = 40;
-		}
-		1902
-		{
-			title = "Orange Stalagmite (Tall)";
-			sprite = "STLGC0";
-			width = 16;
-			height = 40;
-		}
-		1903
-		{
-			title = "Orange Stalagmite";
-			sprite = "STLGD0";
-			width = 16;
-			height = 40;
-		}
-		1904
-		{
-			title = "Red Stalagmite (Tall)";
-			sprite = "STLGE0";
-			width = 16;
-			height = 40;
-		}
-		1905
-		{
-			title = "Red Stalagmite";
-			sprite = "STLGF0";
-			width = 16;
-			height = 40;
-		}
-		1906
-		{
-			title = "Gray Stalagmite (Tall)";
-			sprite = "STLGG0";
-			width = 24;
-			height = 96;
-		}
-		1907
-		{
-			title = "Gray Stalagmite";
-			sprite = "STLGH0";
-			width = 16;
-			height = 40;
-		}
-		1908
-		{
-			title = "Blue Stalagmite (Tall)";
-			sprite = "STLGI0";
-			width = 16;
-			height = 40;
-		}
-		1909
-		{
-			title = "Blue Stalagmite";
-			sprite = "STLGJ0";
-			width = 16;
-			height = 40;
-		}
-	}
-
-	hauntedheights
-	{
-		color = 10; // Green
-		title = "Haunted Heights";
-
-		2000
-		{
-			title = "Smashing Spikeball";
-			sprite = "FMCEA0";
-			width = 18;
-			height = 28;
-		}
-		2001
-		{
-			title = "HHZ Grass";
-			sprite = "HHZMA0";
-			width = 16;
-			height = 40;
-		}
-		2002
-		{
-			title = "HHZ Tentacle 1";
-			sprite = "HHZMB0";
-			width = 16;
-			height = 40;
-		}
-		2003
-		{
-			title = "HHZ Tentacle 2";
-			sprite = "HHZMC0";
-			width = 16;
-			height = 40;
-		}
-		2004
-		{
-			title = "HHZ Stalagmite (Tall)";
-			sprite = "HHZME0";
-			width = 16;
-			height = 40;
-		}
-		2005
-		{
-			title = "HHZ Stalagmite (Short)";
-			sprite = "HHZMF0";
-			width = 16;
-			height = 40;
-		}
-		2006
-		{
-			title = "Jack-o'-lantern 1";
-			sprite = "PUMKA0";
-			width = 16;
-			height = 40;
-		}
-		2007
-		{
-			title = "Jack-o'-lantern 2";
-			sprite = "PUMKB0";
-			width = 16;
-			height = 40;
-		}
-		2008
-		{
-			title = "Jack-o'-lantern 3";
-			sprite = "PUMKC0";
-			width = 16;
-			height = 40;
-		}
-		2009
-		{
-			title = "Purple Mushroom";
-			sprite = "SHRMD0";
-			width = 16;
-			height = 48;
-		}
-		2010
-		{
-			title = "HHZ Tree";
-			sprite = "HHPLC0";
-			width = 12;
-			height = 40;
-		}
-	}
-
-	frozenhillside
-	{
-		color = 10; // Green
-		title = "Frozen Hillside";
-
-		2100
-		{
-			title = "Ice Shard (Small)";
-			sprite = "FHZIA0";
-			width = 8;
-			height = 32;
-		}
-		2101
-		{
-			title = "Ice Shard (Large)";
-			sprite = "FHZIB0";
-			width = 8;
-			height = 32;
-		}
-		2102
-		{
-			title = "Crystal Tree (Aqua)";
-			sprite = "TRE3A0";
-			width = 20;
-			height = 200;
-		}
-		2103
-		{
-			title = "Crystal Tree (Pink)";
-			sprite = "TRE3B0";
-			width = 20;
-			height = 200;
-		}
-		2104
-		{
-			title = "Amy Cameo";
-			sprite = "ROSYA1";
-			width = 16;
-			height = 48;
-		}
-		2105
-		{
-			title = "Mistletoe";
-			sprite = "XMS6A0";
-			width = 52;
-			height = 106;
-		}
-	}
-
-	tutorial
-	{
-		color = 10; // Green
-		title = "Tutorial";
-
-		799
-		{
-			title = "Tutorial Plant";
-			sprite = "TUPFH0";
-			width = 40;
-			height = 144;
-		}
-	}
-
-	flickies
-	{
-		color = 10; // Green
-		title = "Flickies";
-		width = 8;
-		height = 20;
-
-		2200
-		{
-			title = "Bluebird";
-			sprite = "FL01A1";
-		}
-		2201
-		{
-			title = "Rabbit";
-			sprite = "FL02A1";
-		}
-		2202
-		{
-			title = "Chicken";
-			sprite = "FL03A1";
-		}
-		2203
-		{
-			title = "Seal";
-			sprite = "FL04A1";
-		}
-		2204
-		{
-			title = "Pig";
-			sprite = "FL05A1";
-		}
-		2205
-		{
-			title = "Chipmunk";
-			sprite = "FL06A1";
-		}
-		2206
-		{
-			title = "Penguin";
-			sprite = "FL07A1";
-		}
-		2207
-		{
-			title = "Fish";
-			sprite = "FL08A1";
-		}
-		2208
-		{
-			title = "Ram";
-			sprite = "FL09A1";
-		}
-		2209
-		{
-			title = "Puffin";
-			sprite = "FL10A1";
-		}
-		2210
-		{
-			title = "Cow";
-			sprite = "FL11A1";
-		}
-		2211
-		{
-			title = "Rat";
-			sprite = "FL12A1";
-		}
-		2212
-		{
-			title = "Bear";
-			sprite = "FL13A1";
-		}
-		2213
-		{
-			title = "Dove";
-			sprite = "FL14A1";
-		}
-		2214
-		{
-			title = "Cat";
-			sprite = "FL15A1";
-		}
-		2215
-		{
-			title = "Canary";
-			sprite = "FL16A1";
-		}
-		2216
-		{
-			title = "Spider";
-			sprite = "FS01A1";
-		}
-		2217
-		{
-			title = "Bat";
-			sprite = "FS02A0";
-		}
-	}
-}
-
 udmf
 {
-	editor
-	{
-		color = 15; // White
-		arrow = 1;
-		title = "<Editor Things>";
-		error = -1;
-		width = 8;
-		height = 16;
-		sort = 1;
-
-		3328 = "3D Mode Start";
-	}
 
 	starts
 	{
@@ -3974,7 +807,7 @@ udmf
 
 	bosses
 	{
-		color = 8; // Dark_Gray
+		color = 4; // Dark Red
 		arrow = 1;
 		title = "Bosses";
 
@@ -4355,95 +1188,185 @@ udmf
 		width = 24;
 		height = 24;
 		sprite = "RINGA0";
-		arg0
-		{
-			title = "Float?";
-			type = 11;
-			enum = "yesno";
-		}
 
 		300
 		{
 			title = "Ring";
 			sprite = "RINGA0";
 			width = 16;
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		301
 		{
 			title = "Bounce Ring";
-			sprite = "internal:RNGBA0";
+			sprite = "RNGBA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		302
 		{
 			title = "Rail Ring";
-			sprite = "internal:RNGRA0";
+			sprite = "RNGRA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		303
 		{
 			title = "Infinity Ring";
-			sprite = "internal:RNGIA0";
+			sprite = "RNGIA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		304
 		{
 			title = "Automatic Ring";
-			sprite = "internal:RNGAA0";
+			sprite = "RNGAA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		305
 		{
 			title = "Explosion Ring";
-			sprite = "internal:RNGEA0";
+			sprite = "RNGEA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		306
 		{
 			title = "Scatter Ring";
-			sprite = "internal:RNGSA0";
+			sprite = "RNGSA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		307
 		{
 			title = "Grenade Ring";
-			sprite = "internal:RNGGA0";
+			sprite = "RNGGA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		308
 		{
 			title = "CTF Team Ring (Red)";
-			sprite = "internal:RRNGA0";
+			sprite = "internal:TRNGA0R";
 			width = 16;
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		309
 		{
 			title = "CTF Team Ring (Blue)";
-			sprite = "internal:BRNGA0";
+			sprite = "internal:TRNGA0B";
 			width = 16;
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		330
 		{
 			title = "Bounce Ring Panel";
-			sprite = "internal:PIKBA0";
+			sprite = "PIKBA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		331
 		{
 			title = "Rail Ring Panel";
-			sprite = "internal:PIKRA0";
+			sprite = "PIKRA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		332
 		{
 			title = "Automatic Ring Panel";
-			sprite = "internal:PIKAA0";
+			sprite = "PIKAA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		333
 		{
 			title = "Explosion Ring Panel";
-			sprite = "internal:PIKEA0";
+			sprite = "PIKEA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		334
 		{
 			title = "Scatter Ring Panel";
-			sprite = "internal:PIKSA0";
+			sprite = "PIKSA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		335
 		{
 			title = "Grenade Ring Panel";
-			sprite = "internal:PIKGA0";
+			sprite = "PIKGA0";
+			arg0
+			{
+				title = "Float?";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 	}
 
@@ -4562,16 +1485,16 @@ udmf
 		title = "Monitors";
 		width = 18;
 		height = 40;
-		arg0
-		{
-			title = "Death trigger tag";
-			type = 15;
-		}
 
 		400
 		{
 			title = "Super Ring (10 Rings)";
 			sprite = "TVRIA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4583,6 +1506,11 @@ udmf
 		{
 			title = "Pity Shield";
 			sprite = "TVPIA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4594,6 +1522,11 @@ udmf
 		{
 			title = "Attraction Shield";
 			sprite = "TVATA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4605,6 +1538,11 @@ udmf
 		{
 			title = "Force Shield";
 			sprite = "TVFOA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4616,6 +1554,11 @@ udmf
 		{
 			title = "Armageddon Shield";
 			sprite = "TVARA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4627,6 +1570,11 @@ udmf
 		{
 			title = "Whirlwind Shield";
 			sprite = "TVWWA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4638,6 +1586,11 @@ udmf
 		{
 			title = "Elemental Shield";
 			sprite = "TVELA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4649,6 +1602,11 @@ udmf
 		{
 			title = "Super Sneakers";
 			sprite = "TVSSA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4660,6 +1618,11 @@ udmf
 		{
 			title = "Invincibility";
 			sprite = "TVIVA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4671,6 +1634,11 @@ udmf
 		{
 			title = "Extra Life";
 			sprite = "TV1UA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4692,11 +1660,21 @@ udmf
 		{
 			title = "Eggman";
 			sprite = "TVEGA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		411
 		{
 			title = "Teleporter";
 			sprite = "TVMXA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4708,21 +1686,41 @@ udmf
 		{
 			title = "Gravity Boots";
 			sprite = "TVGVA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		414
 		{
 			title = "CTF Team Ring Monitor (Red)";
 			sprite = "TRRIA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		415
 		{
 			title = "CTF Team Ring Monitor (Blue)";
 			sprite = "TBRIA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		416
 		{
 			title = "Recycler";
 			sprite = "TVRCA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4734,16 +1732,31 @@ udmf
 		{
 			title = "Score (1,000 Points)";
 			sprite = "TV1KA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		419
 		{
 			title = "Score (10,000 Points)";
 			sprite = "TVTKA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		420
 		{
 			title = "Flame Shield";
 			sprite = "TVFLA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4755,6 +1768,11 @@ udmf
 		{
 			title = "Water Shield";
 			sprite = "TVBBA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4766,6 +1784,11 @@ udmf
 		{
 			title = "Lightning Shield";
 			sprite = "TVZPA0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 			arg1
 			{
 				title = "Respawn behavior";
@@ -4782,76 +1805,136 @@ udmf
 		title = "Monitors (Respawning)";
 		width = 20;
 		height = 44;
-		arg0
-		{
-			title = "Death trigger tag";
-			type = 15;
-		}
 
 		431
 		{
 			title = "Pity Shield (Respawn)";
 			sprite = "TVPIB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		432
 		{
 			title = "Attraction Shield (Respawn)";
 			sprite = "TVATB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		433
 		{
 			title = "Force Shield (Respawn)";
 			sprite = "TVFOB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		434
 		{
 			title = "Armageddon Shield (Respawn)";
 			sprite = "TVARB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		435
 		{
 			title = "Whirlwind Shield (Respawn)";
 			sprite = "TVWWB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		436
 		{
 			title = "Elemental Shield (Respawn)";
 			sprite = "TVELB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		437
 		{
 			title = "Super Sneakers (Respawn)";
 			sprite = "TVSSB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		438
 		{
 			title = "Invincibility (Respawn)";
 			sprite = "TVIVB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		440
 		{
 			title = "Eggman (Respawn)";
 			sprite = "TVEGB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		443
 		{
 			title = "Gravity Boots (Respawn)";
 			sprite = "TVGVB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		450
 		{
 			title = "Flame Shield (Respawn)";
 			sprite = "TVFLB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		451
 		{
 			title = "Water Shield (Respawn)";
 			sprite = "TVBBB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 		452
 		{
 			title = "Lightning Shield (Respawn)";
 			sprite = "TVZPB0";
+			arg0
+			{
+				title = "Death trigger tag";
+				type = 15;
+			}
 		}
 	}
 
@@ -5235,13 +2318,13 @@ udmf
 		{
 			arrow = 0;
 			title = "5 Vertical Rings (Yellow Spring)";
-			sprite = "RINGA0";
+			sprite = "internal:ringverticalyellow";
 		}
 		601
 		{
 			arrow = 0;
 			title = "5 Vertical Rings (Red Spring)";
-			sprite = "RINGA0";
+			sprite = "internal:ringverticalred";
 			height = 1024;
 		}
 		602
@@ -5259,41 +2342,47 @@ udmf
 		604
 		{
 			title = "Circle of Rings";
-			sprite = "RINGA0";
+			sprite = "internal:circlering";
 			width = 96;
 			height = 192;
+			centerhitbox = true;
 		}
 		605
 		{
 			title = "Circle of Rings (Big)";
-			sprite = "RINGA0";
+			sprite = "internal:circlebigring";
 			width = 192;
+			centerhitbox = true;
 		}
 		606
 		{
 			title = "Circle of Blue Spheres";
-			sprite = "SPHRA0";
+			sprite = "internal:circlesphere";
 			width = 96;
 			height = 192;
+			centerhitbox = true;
 		}
 		607
 		{
 			title = "Circle of Blue Spheres (Big)";
-			sprite = "SPHRA0";
+			sprite = "internal:circlebigsphere";
 			width = 192;
+			centerhitbox = true;
 		}
 		608
 		{
 			title = "Circle of Rings and Spheres";
-			sprite = "SPHRA0";
+			sprite = "internal:circleringsphere";
 			width = 96;
 			height = 192;
+			centerhitbox = true;
 		}
 		609
 		{
 			title = "Circle of Rings and Spheres (Big)";
-			sprite = "SPHRA0";
+			sprite = "internal:circlebigringsphere";
 			width = 192;
+			centerhitbox = true;
 		}
 		610
 		{
@@ -5322,6 +2411,7 @@ udmf
 			sprite = "RINGA0";
 			width = 96;
 			height = 192;
+			centerhitbox = true;
 			arg0
 			{
 				title = "Number of items";
@@ -5429,7 +2519,7 @@ udmf
 		756
 		{
 			title = "Blast Linedef Executor";
-			sprite = "TOADA0";
+			sprite = "internal:blastexec";
 			width = 32;
 			height = 16;
 			arg0
@@ -5441,7 +2531,7 @@ udmf
 		757
 		{
 			title = "Fan Particle Generator";
-			sprite = "PRTLA0";
+			sprite = "internal:fanparticles";
 			width = 8;
 			height = 16;
 			arg0
@@ -5490,11 +2580,6 @@ udmf
 			title = "PolyObject Spawn Point";
 			sprite = "internal:polycenter";
 		}
-		762
-		{
-			title = "PolyObject Spawn Point (Crush)";
-			sprite = "internal:polycentercrush";
-		}
 		780
 		{
 			title = "Skybox View Point";
@@ -5514,7 +2599,7 @@ udmf
 
 	greenflower
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Greenflower";
 
 		800
@@ -5619,7 +2704,7 @@ udmf
 
 	technohill
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Techno Hill";
 
 		900
@@ -5663,7 +2748,7 @@ udmf
 
 	deepsea
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Deep Sea";
 
 		1000
@@ -5823,7 +2908,7 @@ udmf
 
 	castleeggman
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Castle Eggman";
 
 		1100
@@ -6386,7 +3471,7 @@ udmf
 
 	aridcanyon
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Arid Canyon";
 
 		1200
@@ -6511,6 +3596,7 @@ udmf
 			sprite = "WWSGAR";
 			width = 22;
 			height = 64;
+			wallsprite = true;
 		}
 		1213
 		{
@@ -6518,6 +3604,7 @@ udmf
 			sprite = "WWS2AR";
 			width = 22;
 			height = 64;
+			wallsprite = true;
 		}
 		1214
 		{
@@ -6525,6 +3612,7 @@ udmf
 			sprite = "WWS3ALAR";
 			width = 16;
 			height = 192;
+			wallsprite = true;
 		}
 		1215
 		{
@@ -6644,7 +3732,7 @@ udmf
 
 	redvolcano
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Red Volcano";
 
 		1300
@@ -6789,7 +3877,7 @@ udmf
 
 	botanicserenity
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Botanic Serenity";
 		width = 16;
 		height = 32;
@@ -7032,7 +4120,7 @@ udmf
 
 	azuretemple
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Azure Temple";
 
 		1500
@@ -7144,7 +4232,7 @@ udmf
 
 	dreamhill
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Dream Hill";
 
 		1600
@@ -7178,8 +4266,8 @@ udmf
 
 	nightstrk
 	{
-		color = 13; // Pink
-		title = "NiGHTS Track";
+		color = 16; // Light Pink
+		title = "NiGHTS Track & Misc.";
 		width = 8;
 		height = 4096;
 		sprite = "UNKNA0";
@@ -7207,8 +4295,8 @@ udmf
 				type = 11;
 				enum
 				{
-					0 = "Clockwise";
-					1 = "Counterclockwise";
+					0 = "Counterclockwise";
+					1 = "Clockwise";
 				}
 			}
 		}
@@ -7238,30 +4326,6 @@ udmf
 				title = "Order";
 			}
 		}
-		1710
-		{
-			title = "Ideya Capture";
-			sprite = "CAPSA0";
-			width = 72;
-			height = 144;
-			arg0
-			{
-				title = "Mare";
-			}
-			arg1
-			{
-				title = "Required spheres";
-			}
-		}
-	}
-
-	nights
-	{
-		color = 13; // Pink
-		title = "NiGHTS Items";
-		width = 16;
-		height = 32;
-
 		1703
 		{
 			title = "Ideya Drone";
@@ -7294,11 +4358,46 @@ udmf
 			}
 			arg4
 			{
-				title = "Die upon time up?";
-				type = 11;
-				enum = "noyes";
+				title = "Die upon time up?";
+				type = 11;
+				enum = "noyes";
+			}
+		}
+		1710
+		{
+			title = "Ideya Capture";
+			sprite = "CAPSA0";
+			width = 72;
+			height = 144;
+			arg0
+			{
+				title = "Mare";
+			}
+			arg1
+			{
+				title = "Required spheres";
+			}
+		}
+		1714
+		{
+			title = "Ideya Anchor Point";
+			sprite = "internal:ideya";
+			width = 8;
+			height = 16;
+			arg0
+			{
+				title = "Mare";
 			}
 		}
+	}
+
+	nights
+	{
+		color = 13; // Pink
+		title = "NiGHTS Items";
+		width = 16;
+		height = 32;
+		
 		1704
 		{
 			arrow = 1;
@@ -7399,25 +4498,15 @@ udmf
 		{
 			arrow = 1;
 			title = "Hoop";
-			sprite = "HOOPA0";
+			sprite = "internal:nightshoop";
 			width = 80;
 			height = 160;
+			centerhitbox = true;
 			arg0
 			{
 				title = "Radius";
 			}
 		}
-		1714
-		{
-			title = "Ideya Anchor Point";
-			sprite = "internal:axis1";
-			width = 8;
-			height = 16;
-			arg0
-			{
-				title = "Mare";
-			}
-		}
 	}
 
 	mario
@@ -7528,7 +4617,7 @@ udmf
 
 	christmasdisco
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Christmas & Disco";
 
 		1850
@@ -7643,7 +4732,7 @@ udmf
 
 	stalagmites
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Stalagmites";
 		width = 16;
 		height = 40;
@@ -7722,7 +4811,7 @@ udmf
 
 	hauntedheights
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Haunted Heights";
 
 		2000
@@ -7828,7 +4917,7 @@ udmf
 
 	frozenhillside
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Frozen Hillside";
 
 		2100
@@ -7883,7 +4972,7 @@ udmf
 
 	tutorial
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Tutorial";
 
 		799
@@ -7901,65 +4990,170 @@ udmf
 
 	flickies
 	{
-		color = 10; // Green
+		color = 2; // Green
 		title = "Flickies";
 		width = 8;
 		height = 20;
-		arg0
-		{
-			title = "Radius";
-		}
-		arg1
-		{
-			title = "Flags";
-			type = 12;
-			enum
-			{
-				1 = "Move aimlessly";
-				2 = "No movement";
-				4 = "Hop";
-			}
-		}
 
 		2200
 		{
 			title = "Bluebird";
 			sprite = "FL01A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2201
 		{
 			title = "Rabbit";
 			sprite = "FL02A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2202
 		{
 			title = "Chicken";
 			sprite = "FL03A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2203
 		{
 			title = "Seal";
 			sprite = "FL04A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2204
 		{
 			title = "Pig";
 			sprite = "FL05A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2205
 		{
 			title = "Chipmunk";
 			sprite = "FL06A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2206
 		{
 			title = "Penguin";
 			sprite = "FL07A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2207
 		{
 			title = "Fish";
 			sprite = "FL08A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 			arg2
 			{
 				title = "Color";
@@ -7989,51 +5183,214 @@ udmf
 		{
 			title = "Ram";
 			sprite = "FL09A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2209
 		{
 			title = "Puffin";
 			sprite = "FL10A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2210
 		{
 			title = "Cow";
 			sprite = "FL11A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2211
 		{
 			title = "Rat";
 			sprite = "FL12A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2212
 		{
 			title = "Bear";
 			sprite = "FL13A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2213
 		{
 			title = "Dove";
 			sprite = "FL14A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2214
 		{
 			title = "Cat";
 			sprite = "FL15A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2215
 		{
 			title = "Canary";
 			sprite = "FL16A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2216
 		{
 			title = "Spider";
 			sprite = "FS01A1";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 		2217
 		{
 			title = "Bat";
 			sprite = "FS02A0";
+			arg0
+			{
+				title = "Radius";
+			}
+			arg1
+			{
+				title = "Flags";
+				type = 12;
+				enum
+				{
+					1 = "Move aimlessly";
+					2 = "No movement";
+					4 = "Hop";
+				}
+			}
 		}
 	}
-}
+	
+	editor
+	{
+		color = 15; // White
+		arrow = 1;
+		title = "3D Mode Start";
+		error = -1;
+		width = 8;
+		height = 16;
+		sort = 1;
+
+		3328 = "3D Mode Start";
+	}
+}
\ No newline at end of file
diff --git a/extras/conf/udb/SRB2_22Doom.cfg b/extras/conf/udb/SRB2_22Doom.cfg
deleted file mode 100644
index 9e733aa394..0000000000
--- a/extras/conf/udb/SRB2_22Doom.cfg
+++ /dev/null
@@ -1,32 +0,0 @@
-/************************************************************************\
-	Ultimate Doom Builder Game Configuration for Sonic Robo Blast 2 Version 2.2
-\************************************************************************/
-
-// This is required to prevent accidental use of a different configuration
-type = "Doom Builder 2 Game Configuration";
-
-// This is the title to show for this game
-game = "Sonic Robo Blast 2 - 2.2 (Doom format)";
-
-// This is the simplified game engine/sourceport name
-engine = "zdoom";
-
-// Settings common to all games and all map formats
-include("Includes\\SRB222_common.cfg", "common");
-
-// Settings common to Doom map format
-include("Includes\\SRB222_common.cfg", "mapformat_doom");
-
-include("Includes\\Game_SRB222.cfg");
-
-// Script lumps detection
-scriptlumpnames
-{
-	include("Includes\\SRB222_misc.cfg", "scriptlumpnames");
-}
-
-//Default things filters
-thingsfilters
-{
-	include("Includes\\SRB222_misc.cfg", "thingsfilters");
-}
\ No newline at end of file
-- 
GitLab


From b405f50caaa93dc0cfeb9de821cba003bda0a8a6 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Sat, 24 Jun 2023 19:35:57 +0200
Subject: [PATCH 156/518] Fix A_ConnectToGround's handling of scaled mobjs

---
 src/p_enemy.c | 41 ++++++++++++++++++++++-------------------
 1 file changed, 22 insertions(+), 19 deletions(-)

diff --git a/src/p_enemy.c b/src/p_enemy.c
index c7d87b88ad..87c2d7b9e5 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -12583,8 +12583,7 @@ void A_MineRange(mobj_t *actor)
 void A_ConnectToGround(mobj_t *actor)
 {
 	mobj_t *work;
-	fixed_t workz;
-	fixed_t workh;
+	fixed_t endz;
 	angle_t ang;
 	INT32 locvar1 = var1;
 	INT32 locvar2 = var2;
@@ -12595,38 +12594,42 @@ void A_ConnectToGround(mobj_t *actor)
 	if (actor->subsector->sector->ffloors)
 		P_AdjustMobjFloorZ_FFloors(actor, actor->subsector->sector, 2);
 
+	endz = actor->z;
 	if (actor->flags2 & MF2_OBJECTFLIP)
-		workz = (actor->z + actor->height) - actor->ceilingz;
+		actor->z = actor->ceilingz - actor->height; // Ensures perfect ceiling connection
 	else
-		workz = actor->floorz - actor->z;
+		actor->z = actor->floorz; // Ensures perfect floor connection
 
 	if (locvar2)
 	{
-		workh = FixedMul(mobjinfo[locvar2].height, actor->scale);
-		if (actor->flags2 & MF2_OBJECTFLIP)
-			workz += workh;
-		work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar2);
-		workz += workh;
-	}
+		work = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar2);
+		if (work)
+			work->old_z = work->z; // Don't copy old_z from the actor
 
-	if (!locvar1)
-		return;
+		actor->z += P_MobjFlip(actor) * FixedMul(mobjinfo[locvar2].height, actor->scale);
+	}
 
-	if (!(workh = FixedMul(mobjinfo[locvar1].height, actor->scale)))
+	if (!locvar1 || !mobjinfo[locvar1].height) // Can't tile the middle object?
+	{
+		actor->z = endz;
 		return;
+	}
 
 	ang = actor->angle + ANGLE_45;
-	while (workz < 0)
+	while ((actor->flags2 & MF2_OBJECTFLIP) ? (actor->z > endz) : (actor->z < endz))
 	{
-		work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar1);
+		work = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1);
 		if (work)
-			work->angle = ang;
+		{
+			work->angle = work->old_angle = ang;
+			work->old_z = work->z; // Don't copy old_z from the actor
+		}
+
 		ang += ANGLE_90;
-		workz += workh;
+		actor->z += P_MobjFlip(actor) * FixedMul(mobjinfo[locvar1].height, actor->scale);
 	}
 
-	if (workz != 0)
-		actor->z += P_MobjFlip(actor)*workz;
+	actor->old_z = actor->z; // Reset Z interpolation - the spawned objects intentionally don't have any Z interpolation either, after all
 }
 
 // Function: A_SpawnParticleRelative
-- 
GitLab


From 8c31d279cffd1f750ee8d3c082e72cd5be29baf9 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Sun, 11 Jun 2023 19:55:43 +0200
Subject: [PATCH 157/518] Implement per-texture offsets in UDMF

---
 extras/conf/udb/Includes/SRB222_common.cfg |  2 +-
 src/hardware/hw_main.c                     | 54 +++++++++++-----------
 src/lua_maplib.c                           | 48 +++++++++++++++++++
 src/p_maputl.c                             | 16 +++----
 src/p_setup.c                              | 29 ++++++++++++
 src/r_defs.h                               |  4 ++
 src/r_segs.c                               | 26 ++++++-----
 7 files changed, 132 insertions(+), 47 deletions(-)

diff --git a/extras/conf/udb/Includes/SRB222_common.cfg b/extras/conf/udb/Includes/SRB222_common.cfg
index 9a574d6f23..8f37aabaa4 100644
--- a/extras/conf/udb/Includes/SRB222_common.cfg
+++ b/extras/conf/udb/Includes/SRB222_common.cfg
@@ -100,7 +100,7 @@ mapformat_udmf
 
 	// When this is set to true, sectors with the same tag will light up when a line is highlighted
 	linetagindicatesectors = false;
-	localsidedeftextureoffsets = false;
+	localsidedeftextureoffsets = true;
 	distinctfloorandceilingbrightness = true;
 	
 	planeequationsupport = true;
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 897288da2a..c2390519ef 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -1153,7 +1153,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 			else
 				texturevpeg = gl_backsector->ceilingheight + textureheight[gl_toptexture] - gl_frontsector->ceilingheight;
 
-			texturevpeg += gl_sidedef->rowoffset;
+			texturevpeg += gl_sidedef->rowoffset + gl_sidedef->offsety_top;
 
 			// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
 			texturevpeg %= textureheight[gl_toptexture];
@@ -1162,8 +1162,8 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 
 			wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY;
 			wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_frontsector->ceilingheight - gl_backsector->ceilingheight) * grTex->scaleY;
-			wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
-			wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
+			wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_top) * grTex->scaleX;
+			wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_top) * grTex->scaleX;
 
 			// Adjust t value for sloped walls
 			if (!(gl_linedef->flags & ML_SKEWTD))
@@ -1213,7 +1213,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 			else
 				texturevpeg = gl_frontsector->floorheight - gl_backsector->floorheight;
 
-			texturevpeg += gl_sidedef->rowoffset;
+			texturevpeg += gl_sidedef->rowoffset + gl_sidedef->offsety_bot;
 
 			// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
 			texturevpeg %= textureheight[gl_bottomtexture];
@@ -1222,8 +1222,8 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 
 			wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY;
 			wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_backsector->floorheight - gl_frontsector->floorheight) * grTex->scaleY;
-			wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
-			wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
+			wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_bot) * grTex->scaleX;
+			wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_bot) * grTex->scaleX;
 
 			// Adjust t value for sloped walls
 			if (!(gl_linedef->flags & ML_SKEWTD))
@@ -1333,13 +1333,13 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 				// Peg it to the floor
 				if (gl_linedef->flags & ML_MIDPEG)
 				{
-					polybottom = max(front->floorheight, back->floorheight) + gl_sidedef->rowoffset;
+					polybottom = max(front->floorheight, back->floorheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
 					polytop = polybottom + midtexheight;
 				}
 				// Peg it to the ceiling
 				else
 				{
-					polytop = min(front->ceilingheight, back->ceilingheight) + gl_sidedef->rowoffset;
+					polytop = min(front->ceilingheight, back->ceilingheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
 					polybottom = polytop - midtexheight;
 				}
 
@@ -1350,9 +1350,9 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 			// Skew the texture, but peg it to the floor
 			else if (gl_linedef->flags & ML_MIDPEG)
 			{
-				polybottom = popenbottom + gl_sidedef->rowoffset;
+				polybottom = popenbottom + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
 				polytop = polybottom + midtexheight;
-				polybottomslope = popenbottomslope + gl_sidedef->rowoffset;
+				polybottomslope = popenbottomslope + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
 				polytopslope = polybottomslope + midtexheight;
 			}
 			// Skew it according to the ceiling's slope
@@ -1407,12 +1407,12 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 			// Left side
 			wallVerts[3].t = texturevpeg * grTex->scaleY;
 			wallVerts[0].t = (h - l + texturevpeg) * grTex->scaleY;
-			wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
+			wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
 
 			// Right side
 			wallVerts[2].t = texturevpegslope * grTex->scaleY;
 			wallVerts[1].t = (hS - lS + texturevpegslope) * grTex->scaleY;
-			wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
+			wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
 
 			// set top/bottom coords
 			// Take the texture peg into account, rather than changing the offsets past
@@ -1474,19 +1474,19 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 
 			// PEGGING
 			if ((gl_linedef->flags & (ML_DONTPEGBOTTOM|ML_NOSKEW)) == (ML_DONTPEGBOTTOM|ML_NOSKEW))
-				texturevpeg = gl_frontsector->floorheight + textureheight[gl_sidedef->midtexture] - gl_frontsector->ceilingheight + gl_sidedef->rowoffset;
+				texturevpeg = gl_frontsector->floorheight + textureheight[gl_sidedef->midtexture] - gl_frontsector->ceilingheight + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
 			else if (gl_linedef->flags & ML_DONTPEGBOTTOM)
-				texturevpeg = worldbottom + textureheight[gl_sidedef->midtexture] - worldtop + gl_sidedef->rowoffset;
+				texturevpeg = worldbottom + textureheight[gl_sidedef->midtexture] - worldtop + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
 			else
 				// top of texture at top
-				texturevpeg = gl_sidedef->rowoffset;
+				texturevpeg = gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
 
 			grTex = HWR_GetTexture(gl_midtexture);
 
 			wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY;
 			wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_frontsector->ceilingheight - gl_frontsector->floorheight) * grTex->scaleY;
-			wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
-			wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
+			wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
+			wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
 
 			// Texture correction for slopes
 			if (gl_linedef->flags & ML_NOSKEW) {
@@ -1634,13 +1634,13 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 					// -- Monster Iestyn 26/06/18
 					if (newline)
 					{
-						texturevpeg = sides[newline->sidenum[0]].rowoffset;
+						texturevpeg = sides[newline->sidenum[0]].rowoffset + sides[newline->sidenum[0]].offsety_mid;
 						attachtobottom = !!(newline->flags & ML_DONTPEGBOTTOM);
 						slopeskew = !!(newline->flags & ML_SKEWTD);
 					}
 					else
 					{
-						texturevpeg = sides[rover->master->sidenum[0]].rowoffset;
+						texturevpeg = sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid;
 						attachtobottom = !!(gl_linedef->flags & ML_DONTPEGBOTTOM);
 						slopeskew = !!(rover->master->flags & ML_SKEWTD);
 					}
@@ -1672,8 +1672,8 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 						}
 					}
 
-					wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
-					wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
+					wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
+					wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
 				}
 				if (rover->fofflags & FOF_FOG)
 				{
@@ -1785,17 +1785,17 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 
 					if (newline)
 					{
-						wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset) * grTex->scaleY;
-						wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset)) * grTex->scaleY;
+						wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset + sides[newline->sidenum[0]].offsety_mid) * grTex->scaleY;
+						wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset) + sides[newline->sidenum[0]].offsety_mid) * grTex->scaleY;
 					}
 					else
 					{
-						wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset) * grTex->scaleY;
-						wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset)) * grTex->scaleY;
+						wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid) * grTex->scaleY;
+						wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid)) * grTex->scaleY;
 					}
 
-					wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
-					wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
+					wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
+					wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
 				}
 
 				if (rover->fofflags & FOF_FOG)
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index dc477c81f5..f73b3c7d25 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -166,6 +166,12 @@ enum side_e {
 	side_valid = 0,
 	side_textureoffset,
 	side_rowoffset,
+	side_offsetx_top,
+	side_offsety_top,
+	side_offsetx_mid,
+	side_offsety_mid,
+	side_offsetx_bot,
+	side_offsety_bot,
 	side_toptexture,
 	side_bottomtexture,
 	side_midtexture,
@@ -180,6 +186,12 @@ static const char *const side_opt[] = {
 	"valid",
 	"textureoffset",
 	"rowoffset",
+	"offsetx_top",
+	"offsety_top",
+	"offsetx_mid",
+	"offsety_mid",
+	"offsetx_bot",
+	"offsety_bot",
 	"toptexture",
 	"bottomtexture",
 	"midtexture",
@@ -1097,6 +1109,24 @@ static int side_get(lua_State *L)
 	case side_rowoffset:
 		lua_pushfixed(L, side->rowoffset);
 		return 1;
+	case side_offsetx_top:
+		lua_pushfixed(L, side->offsetx_top);
+		return 1;
+	case side_offsety_top:
+		lua_pushfixed(L, side->offsety_top);
+		return 1;
+	case side_offsetx_mid:
+		lua_pushfixed(L, side->offsetx_mid);
+		return 1;
+	case side_offsety_mid:
+		lua_pushfixed(L, side->offsety_mid);
+		return 1;
+	case side_offsetx_bot:
+		lua_pushfixed(L, side->offsetx_bot);
+		return 1;
+	case side_offsety_bot:
+		lua_pushfixed(L, side->offsety_bot);
+		return 1;
 	case side_toptexture:
 		lua_pushinteger(L, side->toptexture);
 		return 1;
@@ -1154,6 +1184,24 @@ static int side_set(lua_State *L)
 	case side_rowoffset:
 		side->rowoffset = luaL_checkfixed(L, 3);
 		break;
+	case side_offsetx_top:
+		side->offsetx_top = luaL_checkfixed(L, 3);
+		break;
+	case side_offsety_top:
+		side->offsety_top = luaL_checkfixed(L, 3);
+		break;
+	case side_offsetx_mid:
+		side->offsetx_mid = luaL_checkfixed(L, 3);
+		break;
+	case side_offsety_mid:
+		side->offsety_mid = luaL_checkfixed(L, 3);
+		break;
+	case side_offsetx_bot:
+		side->offsetx_bot = luaL_checkfixed(L, 3);
+		break;
+	case side_offsety_bot:
+		side->offsety_bot = luaL_checkfixed(L, 3);
+		break;
 	case side_toptexture:
 		side->toptexture = luaL_checkinteger(L, 3);
 		break;
diff --git a/src/p_maputl.c b/src/p_maputl.c
index b6a3207308..e36d5fd724 100644
--- a/src/p_maputl.c
+++ b/src/p_maputl.c
@@ -509,26 +509,26 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
 				// on non-solid polyobjects should NEVER happen in the future
 				if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) {
 					if (linedef->flags & ML_WRAPMIDTEX && !side->repeatcnt) { // "infinite" repeat
-						texbottom = back->floorheight + side->rowoffset;
-						textop = back->ceilingheight + side->rowoffset;
+						texbottom = back->floorheight + side->rowoffset + side->offsety_mid;
+						textop = back->ceilingheight + side->rowoffset + side->offsety_mid;
 					} else if (linedef->flags & ML_MIDTEX) {
-						texbottom = back->floorheight + side->rowoffset;
+						texbottom = back->floorheight + side->rowoffset + side->offsety_mid;
 						textop = texbottom + texheight*(side->repeatcnt+1);
 					} else {
-						textop = back->ceilingheight + side->rowoffset;
+						textop = back->ceilingheight + side->rowoffset + side->offsety_mid;
 						texbottom = textop - texheight*(side->repeatcnt+1);
 					}
 				} else
 #endif
 				{
 					if (linedef->flags & ML_WRAPMIDTEX && !side->repeatcnt) { // "infinite" repeat
-						texbottom = openbottom + side->rowoffset;
-						textop = opentop + side->rowoffset;
+						texbottom = openbottom + side->rowoffset + side->offsety_mid;
+						textop = opentop + side->rowoffset + side->offsety_mid;
 					} else if (linedef->flags & ML_MIDPEG) {
-						texbottom = openbottom + side->rowoffset;
+						texbottom = openbottom + side->rowoffset + side->offsety_mid;
 						textop = texbottom + texheight*(side->repeatcnt+1);
 					} else {
-						textop = opentop + side->rowoffset;
+						textop = opentop + side->rowoffset + side->offsety_mid;
 						texbottom = textop - texheight*(side->repeatcnt+1);
 					}
 				}
diff --git a/src/p_setup.c b/src/p_setup.c
index 74645e8771..a10326986e 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1240,6 +1240,9 @@ static void P_LoadSidedefs(UINT8 *data)
 		}
 		sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
 
+		sd->offsetx_top = sd->offsetx_mid = sd->offsetx_bot = 0;
+		sd->offsety_top = sd->offsety_mid = sd->offsety_bot = 0;
+
 		P_SetSidedefSector(i, SHORT(msd->sector));
 
 		// Special info stored in texture fields!
@@ -1777,6 +1780,18 @@ static void ParseTextmapSidedefParameter(UINT32 i, const char *param, const char
 		sides[i].textureoffset = atol(val)<<FRACBITS;
 	else if (fastcmp(param, "offsety"))
 		sides[i].rowoffset = atol(val)<<FRACBITS;
+	else if (fastcmp(param, "offsetx_top"))
+		sides[i].offsetx_top = atol(val) << FRACBITS;
+	else if (fastcmp(param, "offsetx_mid"))
+		sides[i].offsetx_mid = atol(val) << FRACBITS;
+	else if (fastcmp(param, "offsetx_bottom"))
+		sides[i].offsetx_bot = atol(val) << FRACBITS;
+	else if (fastcmp(param, "offsety_top"))
+		sides[i].offsety_top = atol(val) << FRACBITS;
+	else if (fastcmp(param, "offsety_mid"))
+		sides[i].offsety_mid = atol(val) << FRACBITS;
+	else if (fastcmp(param, "offsety_bottom"))
+		sides[i].offsety_bot = atol(val) << FRACBITS;
 	else if (fastcmp(param, "texturetop"))
 		sides[i].toptexture = R_TextureNumForName(val);
 	else if (fastcmp(param, "texturebottom"))
@@ -2461,6 +2476,18 @@ static void P_WriteTextmap(void)
 			fprintf(f, "offsetx = %d;\n", wsides[i].textureoffset >> FRACBITS);
 		if (wsides[i].rowoffset != 0)
 			fprintf(f, "offsety = %d;\n", wsides[i].rowoffset >> FRACBITS);
+		if (wsides[i].offsetx_top != 0)
+			fprintf(f, "offsetx_top = %d;\n", wsides[i].offsetx_top >> FRACBITS);
+		if (wsides[i].offsety_top != 0)
+			fprintf(f, "offsety_top = %d;\n", wsides[i].offsety_top >> FRACBITS);
+		if (wsides[i].offsetx_mid != 0)
+			fprintf(f, "offsetx_mid = %d;\n", wsides[i].offsetx_mid >> FRACBITS);
+		if (wsides[i].offsety_mid != 0)
+			fprintf(f, "offsety_mid = %d;\n", wsides[i].offsety_mid >> FRACBITS);
+		if (wsides[i].offsetx_bot != 0)
+			fprintf(f, "offsetx_bottom = %d;\n", wsides[i].offsetx_bot >> FRACBITS);
+		if (wsides[i].offsety_bot != 0)
+			fprintf(f, "offsety_bottom = %d;\n", wsides[i].offsety_bot >> FRACBITS);
 		if (wsides[i].toptexture > 0 && wsides[i].toptexture < numtextures)
 			fprintf(f, "texturetop = \"%.*s\";\n", 8, textures[wsides[i].toptexture]->name);
 		if (wsides[i].bottomtexture > 0 && wsides[i].bottomtexture < numtextures)
@@ -2828,6 +2855,8 @@ static void P_LoadTextmap(void)
 		// Defaults.
 		sd->textureoffset = 0;
 		sd->rowoffset = 0;
+		sd->offsetx_top = sd->offsetx_mid = sd->offsetx_bot = 0;
+		sd->offsety_top = sd->offsety_mid = sd->offsety_bot = 0;
 		sd->toptexture = R_TextureNumForName("-");
 		sd->midtexture = R_TextureNumForName("-");
 		sd->bottomtexture = R_TextureNumForName("-");
diff --git a/src/r_defs.h b/src/r_defs.h
index 6d2b7d3d8a..963d655b17 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -559,6 +559,10 @@ typedef struct
 	// add this to the calculated texture top
 	fixed_t rowoffset;
 
+	// per-texture offsets for UDMF
+	fixed_t offsetx_top, offsetx_mid, offsetx_bot;
+	fixed_t offsety_top, offsety_mid, offsety_bot;
+
 	// Texture indices.
 	// We do not maintain names here.
 	INT32 toptexture, bottomtexture, midtexture;
diff --git a/src/r_segs.c b/src/r_segs.c
index 71fc9f9b2a..5acca9b170 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -49,6 +49,7 @@ fixed_t rw_distance;
 static INT32 rw_x, rw_stopx;
 static angle_t rw_centerangle;
 static fixed_t rw_offset;
+static fixed_t rw_offset_top, rw_offset_mid, rw_offset_bot;
 static fixed_t rw_offset2; // for splats
 static fixed_t rw_scale, rw_scalestep;
 static fixed_t rw_midtexturemid, rw_toptexturemid, rw_bottomtexturemid;
@@ -778,7 +779,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 
 	if (newline)
 	{
-		offsetvalue = sides[newline->sidenum[0]].rowoffset;
+		offsetvalue = sides[newline->sidenum[0]].rowoffset + sides[newline->sidenum[0]].offsety_mid;
 		if (newline->flags & ML_DONTPEGBOTTOM)
 		{
 			skewslope = *pfloor->b_slope; // skew using bottom slope
@@ -790,7 +791,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	}
 	else
 	{
-		offsetvalue = sides[pfloor->master->sidenum[0]].rowoffset;
+		offsetvalue = sides[pfloor->master->sidenum[0]].rowoffset + sides[pfloor->master->sidenum[0]].offsety_mid;
 		if (curline->linedef->flags & ML_DONTPEGBOTTOM)
 		{
 			skewslope = *pfloor->b_slope; // skew using bottom slope
@@ -1335,7 +1336,7 @@ static void R_RenderSegLoop (void)
 				dc_yl = yl;
 				dc_yh = yh;
 				dc_texturemid = rw_midtexturemid;
-				dc_source = R_GetColumn(midtexture,texturecolumn);
+				dc_source = R_GetColumn(midtexture,texturecolumn + (rw_offset_mid>>FRACBITS));
 				dc_texheight = textureheight[midtexture]>>FRACBITS;
 
 				//profile stuff ---------------------------------------------------------
@@ -1396,7 +1397,7 @@ static void R_RenderSegLoop (void)
 						dc_yl = yl;
 						dc_yh = mid;
 						dc_texturemid = rw_toptexturemid;
-						dc_source = R_GetColumn(toptexture,texturecolumn);
+						dc_source = R_GetColumn(toptexture,texturecolumn + (rw_offset_top>>FRACBITS));
 						dc_texheight = textureheight[toptexture]>>FRACBITS;
 						colfunc();
 						ceilingclip[rw_x] = (INT16)mid;
@@ -1433,7 +1434,7 @@ static void R_RenderSegLoop (void)
 						dc_yh = yh;
 						dc_texturemid = rw_bottomtexturemid;
 						dc_source = R_GetColumn(bottomtexture,
-							texturecolumn);
+							texturecolumn + (rw_offset_bot>>FRACBITS));
 						dc_texheight = textureheight[bottomtexture]>>FRACBITS;
 						colfunc();
 						floorclip[rw_x] = (INT16)mid;
@@ -1452,7 +1453,7 @@ static void R_RenderSegLoop (void)
 		{
 			// save texturecol
 			//  for backdrawing of masked mid texture
-			maskedtexturecol[rw_x] = (INT16)texturecolumn;
+			maskedtexturecol[rw_x] = (INT16)(texturecolumn + (rw_offset_mid>>FRACBITS));
 
 			if (maskedtextureheight != NULL) {
 				maskedtextureheight[rw_x] = (curline->linedef->flags & ML_MIDPEG) ?
@@ -1783,7 +1784,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 			rw_midtexturemid = worldtop;
 			rw_midtextureslide = ceilingfrontslide;
 		}
-		rw_midtexturemid += sidedef->rowoffset;
+		rw_midtexturemid += sidedef->rowoffset + sidedef->offsety_mid;
 
 		ds_p->silhouette = SIL_BOTH;
 		ds_p->sprtopclip = screenheightarray;
@@ -2022,8 +2023,8 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 			}
 		}
 
-		rw_toptexturemid += sidedef->rowoffset;
-		rw_bottomtexturemid += sidedef->rowoffset;
+		rw_toptexturemid += sidedef->rowoffset + sidedef->offsety_top;
+		rw_bottomtexturemid += sidedef->rowoffset + sidedef->offsety_bot;
 
 		// allocate space for masked texture tables
 		if (frontsector && backsector && !Tag_Compare(&frontsector->tags, &backsector->tags) && (backsector->ffloors || frontsector->ffloors))
@@ -2266,8 +2267,8 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 					rw_midtexturebackslide = ceilingbackslide;
 				}
 			}
-			rw_midtexturemid += sidedef->rowoffset;
-			rw_midtextureback += sidedef->rowoffset;
+			rw_midtexturemid += sidedef->rowoffset + sidedef->offsety_mid;
+			rw_midtextureback += sidedef->rowoffset + sidedef->offsety_mid;
 
 			maskedtexture = true;
 		}
@@ -2305,6 +2306,9 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 		/// don't use texture offset for splats
 		rw_offset2 = rw_offset + curline->offset;
 		rw_offset += sidedef->textureoffset + curline->offset;
+		rw_offset_top = sidedef->offsetx_top;
+		rw_offset_mid = sidedef->offsetx_mid;
+		rw_offset_bot = sidedef->offsetx_bot;
 		rw_centerangle = ANGLE_90 + viewangle - rw_normalangle;
 
 		// calculate light table
-- 
GitLab


From 2c66f2ce27fa097992fbc1e224eb7cfd2da4af69 Mon Sep 17 00:00:00 2001
From: katsy <katmint@live.com>
Date: Mon, 3 Jul 2023 16:27:07 -0500
Subject: [PATCH 158/518] Fix homing attack and ring attraction math dumbs

---
 src/p_enemy.c | 2 +-
 src/p_user.c  | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/p_enemy.c b/src/p_enemy.c
index fe062cffc6..df2b27af28 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -828,7 +828,7 @@ static boolean P_LookForShield(mobj_t *actor)
 			continue;
 
 		if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
-			&& (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale)))
+			&& (R_PointToDist2(0, 0, R_PointToDist2(0, 0, actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale)))
 		{
 			P_SetTarget(&actor->tracer, player->mo);
 
diff --git a/src/p_user.c b/src/p_user.c
index 38af2843b7..8bb5fb2e44 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -9217,7 +9217,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet)
 
 		{
 			fixed_t zdist = (player->mo->z + player->mo->height/2) - (mo->z + mo->height/2);
-			dist = P_AproxDistance(player->mo->x-mo->x, player->mo->y-mo->y);
+			dist = R_PointToDist2(0, 0, player->mo->x-mo->x, player->mo->y-mo->y);
 			if (bullet)
 			{
 				if ((R_PointToAngle2(0, 0, dist, zdist) + span) > span*2)
@@ -9234,7 +9234,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet)
 					continue;
 			}
 
-			dist = P_AproxDistance(dist, zdist);
+			dist = R_PointToDist2(0, 0, dist, zdist);
 			if (dist > maxdist)
 				continue; // out of range
 		}
-- 
GitLab


From beda6109b2cc4d903c7c4fbc4cdad8e9879cef76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Mon, 3 Jul 2023 23:30:49 +0200
Subject: [PATCH 159/518] Fix segfault when trying to set a read-only field on
 player_t

---
 src/lua_playerlib.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 1d06e081b1..f76ec16894 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -843,7 +843,7 @@ static int player_get(lua_State *L)
 	return 1;
 }
 
-#define NOSET luaL_error(L, LUA_QL("player_t") " field " LUA_QS " should not be set directly.", field)
+#define NOSET luaL_error(L, LUA_QL("player_t") " field " LUA_QS " should not be set directly.", player_opt[field])
 static int player_set(lua_State *L)
 {
 	player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@@ -1392,7 +1392,7 @@ static int power_len(lua_State *L)
 }
 
 #define NOFIELD luaL_error(L, LUA_QL("ticcmd_t") " has no field named " LUA_QS, field)
-#define NOSET luaL_error(L, LUA_QL("ticcmd_t") " field " LUA_QS " should not be set directly.", field)
+#define NOSET luaL_error(L, LUA_QL("ticcmd_t") " field " LUA_QS " should not be set directly.", ticcmd_opt[field])
 
 enum ticcmd_e
 {
-- 
GitLab


From 09b9adc8066a31926d9fdbad0e672bbb89e20435 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Tue, 4 Jul 2023 18:46:49 +0200
Subject: [PATCH 160/518] Fix Lua warning when accessing custom field on
 mobjinfo

---
 src/lua_infolib.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index fb07ccebb5..4d150347d9 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1168,7 +1168,7 @@ static int mobjinfo_fields_ref = LUA_NOREF;
 static int mobjinfo_get(lua_State *L)
 {
 	mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
-	enum mobjinfo_e field = luaL_checkoption(L, 2, mobjinfo_opt[0], mobjinfo_opt);
+	enum mobjinfo_e field = Lua_optoption(L, 2, mobjinfo_doomednum, mobjinfo_fields_ref);
 
 	I_Assert(info != NULL);
 	I_Assert(info >= mobjinfo);
-- 
GitLab


From ff7a24df09ac9517be430ea984104ce61242511b Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Wed, 5 Jul 2023 12:10:30 +0200
Subject: [PATCH 161/518] Rename Kiwi skincolor to Pear

---
 src/deh_tables.c | 2 +-
 src/doomdef.h    | 2 +-
 src/info.c       | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index d53680c19a..1614fbecbc 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4655,7 +4655,7 @@ const char *COLOR_ENUMS[] = {
 	"GOLDENROD",   	// SKINCOLOR_GOLDENROD,
 	"YELLOW",     	// SKINCOLOR_YELLOW,
 	"OLIVE",     	// SKINCOLOR_OLIVE,
-	"KIWI",     	// SKINCOLOR_KIWI,
+	"PEAR",     	// SKINCOLOR_PEAR,
 	"LEMON",     	// SKINCOLOR_LEMON,
 	"LIME",     	// SKINCOLOR_LIME,
 	"PERIDOT",     	// SKINCOLOR_PERIDOT,
diff --git a/src/doomdef.h b/src/doomdef.h
index 20b87f230e..84404d6edb 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -307,7 +307,7 @@ typedef enum
 	SKINCOLOR_GOLDENROD,
 	SKINCOLOR_YELLOW,
 	SKINCOLOR_OLIVE,
-	SKINCOLOR_KIWI,
+	SKINCOLOR_PEAR,
 	SKINCOLOR_LEMON,
 	SKINCOLOR_LIME,
 	SKINCOLOR_PERIDOT,
diff --git a/src/info.c b/src/info.c
index 32b21b702b..abcf4b4999 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21632,7 +21632,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Goldenrod",  {   0,   80,   81,   81,   83,   73,   73,   64,   65,   66,   67,   68,   69,   62,   44,   45}, SKINCOLOR_MAJESTY,    8,  V_YELLOWMAP,  true}, // SKINCOLOR_GOLDENROD
 	{"Yellow",     {0x52, 0x53, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4e, 0x4f, 0xed}, SKINCOLOR_CORNFLOWER, 8,  V_YELLOWMAP,  true}, // SKINCOLOR_YELLOW
 	{"Olive",      {0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0xe7, 0xe7, 0xe9, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7, 0xfd}, SKINCOLOR_DUSK,       3,  V_YELLOWMAP,  true}, // SKINCOLOR_OLIVE
-	{"Kiwi",       {  88,   89,  188,  189,  189,   76,   76,   67,   67,   68,   69,   70,   45,   46,   47,   47}, SKINCOLOR_MARINE,     9,  V_PERIDOTMAP, true}, // SKINCOLOR_KIWI
+	{"Pear",       {  88,   89,  188,  189,  189,   76,   76,   67,   67,   68,   69,   70,   45,   46,   47,   47}, SKINCOLOR_MARINE,     9,  V_PERIDOTMAP, true}, // SKINCOLOR_PEAR
 	{"Lemon",      {   0,   80,   81,   83,   73,   73,   74,   74,   76,   76,  191,  191,   79,   79,  110,  111}, SKINCOLOR_FUCHSIA,    8,  V_YELLOWMAP,  true}, // SKINCOLOR_LEMON
 	{"Lime",       {0x50, 0x51, 0x52, 0x53, 0x48, 0xbc, 0xbd, 0xbe, 0xbe, 0xbf, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_MAGENTA,    9,  V_PERIDOTMAP, true}, // SKINCOLOR_LIME
 	{"Peridot",    {0x58, 0x58, 0xbc, 0xbc, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0x5e, 0x5e, 0x5f, 0x5f, 0x77, 0x77}, SKINCOLOR_COBALT,     2,  V_PERIDOTMAP, true}, // SKINCOLOR_PERIDOT
@@ -21656,7 +21656,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Turquoise",  {  0,   120,  121,  122,  123,  141,  141,  135,  136,  136,  150,  153,  155,  157,  159,  253}, SKINCOLOR_SANGRIA,    12, V_SKYMAP,     true}, // SKINCOLOR_TURQUOISE
 	{"Aquamarine", {   0,  120,  121,  131,  132,  133,  134,  134,  135,  135,  149,  149,  172,  173,  174,  175}, SKINCOLOR_GARNET,     8,  V_SKYMAP,     true}, // SKINCOLOR_AQUAMARINE
 	{"Sky",        {0x80, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x89, 0x8a, 0x8b}, SKINCOLOR_SANDY,      1,  V_SKYMAP,     true}, // SKINCOLOR_SKY
-	{"Marine",     { 144,  146,  147,  147,  148,  135,  136,  136,  137,  137,  127,  118,  119,  111,  111,  111}, SKINCOLOR_KIWI,       13, V_SKYMAP,     true}, // SKINCOLOR_MARINE
+	{"Marine",     { 144,  146,  147,  147,  148,  135,  136,  136,  137,  137,  127,  118,  119,  111,  111,  111}, SKINCOLOR_PEAR,       13, V_SKYMAP,     true}, // SKINCOLOR_MARINE
 	{"Cerulean",   {0x85, 0x86, 0x87, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0xfd, 0xfd, 0xfd, 0x1f, 0x1f, 0x1f}, SKINCOLOR_NEON,       4,  V_SKYMAP,     true}, // SKINCOLOR_CERULEAN
 	{"Dream",      {  80,  208,  200,  200,  146,  146,  133,  134,  135,  136,  137,  138,  139,  139,  254,  254}, SKINCOLOR_FOUNDATION, 9,  V_SKYMAP,     true}, // SKINCOLOR_DREAM
 	{"Icy",        {0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0x83, 0x83, 0x86, 0x87, 0x95, 0x95, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_CRIMSON,    0,  V_SKYMAP,     true}, // SKINCOLOR_ICY
-- 
GitLab


From f261b9f0e118d6b6ac7216514423b06e888ab0e1 Mon Sep 17 00:00:00 2001
From: katsy <205-katsy@users.noreply.git.do.srb2.org>
Date: Wed, 5 Jul 2023 17:05:47 +0000
Subject: [PATCH 162/518] Add player->powers[pw_strong], refactor attacks and
 busting conditions, and allow breaking floors and ceilings separately

---
 src/d_player.h   | 35 +++++++++++++++++
 src/deh_tables.c | 28 +++++++++++++-
 src/p_inter.c    | 34 ++++++++---------
 src/p_map.c      | 92 +++++++++++++++++----------------------------
 src/p_saveg.c    |  2 +
 src/p_user.c     | 97 ++++++++++++++++++++++++++++++++----------------
 6 files changed, 180 insertions(+), 108 deletions(-)

diff --git a/src/d_player.h b/src/d_player.h
index 6df6689c5f..1b2c99743e 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -249,6 +249,38 @@ typedef enum
 	CR_FAN
 } carrytype_t; // pw_carry
 
+typedef enum
+{
+	STR_NONE = 0, // All strong powers can stack onto each other
+
+	// Attack powers
+	STR_ANIM = 0x1, // remove powers when leaving current animation
+	STR_PUNCH = 0x2, // frontal attack (knuckles glide)
+	STR_TAIL = 0x4, // rear attack
+	STR_STOMP = 0x8, // falling onto object (fang bounce)
+	STR_UPPER = 0x10, // moving upwards into object (tails fly)
+	STR_GUARD = 0x20, //protect against damage
+	STR_HEAVY = 0x40, // ignore vertical rebound
+	STR_DASH = 0x80, // special type for machine dashmode, automatically removes your powers when leaving dashmode
+
+	// Environment powers
+	STR_WALL = 0x100, // fof busting
+	STR_FLOOR = 0x200,
+	STR_CEILING = 0x400,
+	STR_SPRING = 0x800, // power up hit springs
+	STR_SPIKE = 0x1000, // break spikes
+
+	// Shortcuts
+	STR_ATTACK = STR_PUNCH|STR_TAIL|STR_STOMP|STR_UPPER,
+	STR_BUST = STR_WALL|STR_FLOOR|STR_CEILING,
+	STR_FLY = STR_ANIM|STR_UPPER,
+	STR_GLIDE = STR_ANIM|STR_PUNCH,
+	STR_TWINSPIN = STR_ANIM|STR_ATTACK|STR_BUST|STR_SPRING|STR_SPIKE,
+	STR_MELEE = STR_ANIM|STR_PUNCH|STR_HEAVY|STR_WALL|STR_FLOOR|STR_SPRING|STR_SPIKE,
+	STR_BOUNCE = STR_ANIM|STR_STOMP|STR_FLOOR,
+	STR_METAL = STR_DASH|STR_SPIKE
+} strongtype_t; // pw_strong
+
 // Player powers. (don't edit this comment)
 typedef enum
 {
@@ -293,6 +325,8 @@ typedef enum
 
 	pw_ignorelatch, // Don't grab onto CR_GENERIC, add 32768 (powers[pw_ignorelatch] & 1<<15) to avoid ALL not-NiGHTS CR_ types
 
+	pw_strong, // Additional properties for powerful attacks
+
 	NUMPOWERS
 } powertype_t;
 
@@ -405,6 +439,7 @@ typedef struct player_s
 
 	// playing animation.
 	panim_t panim;
+	UINT8 stronganim;
 
 	// For screen flashing (bright).
 	UINT16 flashcount;
diff --git a/src/deh_tables.c b/src/deh_tables.c
index 11d8b1a014..c7c53f650c 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4768,7 +4768,9 @@ const char *const POWERS_LIST[] = {
 
 	"JUSTLAUNCHED",
 
-	"IGNORELATCH"
+	"IGNORELATCH",
+
+	"STRONG"
 };
 
 const char *const HUDITEMS_LIST[] = {
@@ -5121,6 +5123,30 @@ struct int_const_s const INT_CONST[] = {
 	{"CR_DUSTDEVIL",CR_DUSTDEVIL},
 	{"CR_FAN",CR_FAN},
 
+	// Strong powers
+	{"STR_NONE",STR_NONE},
+	{"STR_ANIM",STR_ANIM},
+	{"STR_PUNCH",STR_PUNCH},
+	{"STR_TAIL",STR_TAIL},
+	{"STR_STOMP",STR_STOMP},
+	{"STR_UPPER",STR_UPPER},
+	{"STR_GUARD",STR_GUARD},
+	{"STR_HEAVY",STR_HEAVY},
+	{"STR_DASH",STR_DASH},
+	{"STR_WALL",STR_WALL},
+	{"STR_FLOOR",STR_FLOOR},
+	{"STR_CEILING",STR_CEILING},
+	{"STR_SPRING",STR_SPRING},
+	{"STR_SPIKE",STR_SPIKE},
+	{"STR_ATTACK",STR_ATTACK},
+	{"STR_BUST",STR_BUST},
+	{"STR_FLY",STR_FLY},
+	{"STR_GLIDE",STR_GLIDE},
+	{"STR_TWINSPIN",STR_TWINSPIN},
+	{"STR_MELEE",STR_MELEE},
+	{"STR_BOUNCE",STR_BOUNCE},
+	{"STR_METAL",STR_METAL},
+
 	// Ring weapons (ringweapons_t)
 	// Useful for A_GiveWeapon
 	{"RW_AUTO",RW_AUTO},
diff --git a/src/p_inter.c b/src/p_inter.c
index c230ce178a..cc8a1d2b91 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -465,23 +465,20 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			if (special->type == MT_PTERABYTE && special->target == player->mo && special->extravalue1 == 1)
 				return; // Can't hurt a Pterabyte if it's trying to pick you up
 
-			if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1))
+			if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1) && (!(player->powers[pw_strong] & STR_HEAVY)))
 			{
-				if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
+				fixed_t setmomz = -toucher->momz; // Store this, momz get changed by P_DoJump within P_DoBubbleBounce
+				
+				if (elementalpierce == 2) // Reset bubblewrap, part 1
+					P_DoBubbleBounce(player);
+				toucher->momz = setmomz;
+				if (elementalpierce == 2) // Reset bubblewrap, part 2
 				{
-					fixed_t setmomz = -toucher->momz; // Store this, momz get changed by P_DoJump within P_DoBubbleBounce
-
-					if (elementalpierce == 2) // Reset bubblewrap, part 1
-						P_DoBubbleBounce(player);
-					toucher->momz = setmomz;
-					if (elementalpierce == 2) // Reset bubblewrap, part 2
-					{
-						boolean underwater = toucher->eflags & MFE_UNDERWATER;
-
-						if (underwater)
-							toucher->momz /= 2;
-						toucher->momz -= (toucher->momz/(underwater ? 8 : 4)); // Cap the height!
-					}
+					boolean underwater = toucher->eflags & MFE_UNDERWATER;
+							
+					if (underwater)
+						toucher->momz /= 2;
+					toucher->momz -= (toucher->momz/(underwater ? 8 : 4)); // Cap the height!
 				}
 			}
 			if (player->pflags & PF_BOUNCING)
@@ -500,8 +497,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 					toucher->momx = 7*toucher->momx>>3;
 					toucher->momy = 7*toucher->momy>>3;
 				}
-				else if (player->dashmode >= DASHMODE_THRESHOLD && (player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE)
-					&& player->panim == PA_DASH)
+				else if ((player->powers[pw_strong] & STR_DASH) && player->panim == PA_DASH)
 					P_DoPlayerPain(player, special, special);
 			}
 			P_DamageMobj(special, toucher, toucher, 1, 0);
@@ -3213,7 +3209,7 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou
 		return false;
 
 	// Add pity.
-	if (!player->powers[pw_flashing] && !player->powers[pw_invulnerability] && !player->powers[pw_super]
+	if (!player->powers[pw_flashing] && !player->powers[pw_invulnerability] && !player->powers[pw_super] && !(player->powers[pw_strong] & STR_GUARD)
 	&& source->player->score > player->score)
 		player->pity++;
 
@@ -3683,7 +3679,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 			}
 			return false;
 		}
-		else if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super]) // ignore bouncing & such in invulnerability
+		else if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super] || (player->powers[pw_strong] & STR_GUARD)) // ignore bouncing & such in invulnerability
 		{
 			if (force
 			|| (inflictor && inflictor->flags & MF_MISSILE && inflictor->flags2 & MF2_SUPERFIRE)) // Super Sonic is stunned!
diff --git a/src/p_map.c b/src/p_map.c
index 2bb02503f4..3dc9134359 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -152,10 +152,8 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 	{
 		if (spring->info->painchance == 3)
 			;
-		else if (object->player->charability == CA_TWINSPIN && object->player->panim == PA_ABILITY)
+		else if (object->player->powers[pw_strong] & STR_SPRING)
 			strong = 1;
-		else if (object->player->charability2 == CA2_MELEE && object->player->panim == PA_ABILITY2)
-			strong = 2;
 	}
 
 	if (spring->info->painchance == -1) // Pinball bumper mode.
@@ -455,7 +453,8 @@ springstate:
 
 		if (strong)
 		{
-			P_TwinSpinRejuvenate(object->player, (strong == 1 ? object->player->thokitem : object->player->revitem));
+			if (object->player->charability == CA_TWINSPIN || object->player->charability2 == CA2_MELEE)
+				P_TwinSpinRejuvenate(object->player, (object->player->charability == CA_TWINSPIN ? object->player->thokitem : object->player->revitem));
 			S_StartSound(object, sfx_sprong); // strong spring. sprong.
 		}
 	}
@@ -796,43 +795,25 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		return true;
 	}
 
-	// SF_DASHMODE users destroy spikes and monitors, CA_TWINSPIN users and CA2_MELEE users destroy spikes.
-	if ((tmthing->player)
-		&& ((((tmthing->player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE)) && (tmthing->player->dashmode >= DASHMODE_THRESHOLD)
-		&& (thing->flags & (MF_MONITOR)
-		|| (thing->type == MT_SPIKE
-		|| thing->type == MT_WALLSPIKE)))
-	|| ((((tmthing->player->charability == CA_TWINSPIN) && (tmthing->player->panim == PA_ABILITY))
-	|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2))
-		&& (thing->type == MT_SPIKE
-		|| thing->type == MT_WALLSPIKE))))
-	{
-		if ((thing->flags & (MF_MONITOR)) && (thing->health <= 0 || !(thing->flags & MF_SHOOTABLE)))
-			return true;
-		blockdist = thing->radius + tmthing->radius;
-		if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
-			return true; // didn't hit it
-		// see if it went over / under
-		if (tmthing->z > thing->z + thing->height)
-			return true; // overhead
-		if (tmthing->z + tmthing->height < thing->z)
-			return true; // underneath
-		if (thing->type == MT_SPIKE
-		|| thing->type == MT_WALLSPIKE)
-		{
-			mobj_t *iter;
-			if (thing->flags & MF_SOLID)
-				S_StartSound(tmthing, thing->info->deathsound);
-			for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
-				if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
-					P_KillMobj(iter, tmthing, tmthing, 0);
-			return true;
-		}
-		else
-		{
-			if (P_DamageMobj(thing, tmthing, tmthing, 1, 0))
-				return true;
-		}
+	// STR_SPIKE users destroy spikes
+	if ((tmthing->player) && ((tmthing->player->powers[pw_strong] & STR_SPIKE) && (thing->type == MT_SPIKE || thing->type == MT_WALLSPIKE)))
+	{
+		mobj_t *iter;
+        	blockdist = thing->radius + tmthing->radius;
+        	if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
+            		return true; // didn't hit it
+        	// see if it went over / under
+        	if (tmthing->z > thing->z + thing->height)
+            		return true; // overhead
+        	if (tmthing->z + tmthing->height < thing->z)
+            		return true; // underneath
+
+		if (thing->flags & MF_SOLID)
+			S_StartSound(tmthing, thing->info->deathsound);
+		for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
+			if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
+				P_KillMobj(iter, tmthing, tmthing, 0);
+		return true;
 	}
 
 	// vectorise metal - done in a special case as at this point neither has the right flags for touching
@@ -1687,25 +1668,22 @@ static boolean PIT_CheckThing(mobj_t *thing)
 				// Going down? Then bounce back up.
 				if (P_DamageMobj(thing, tmthing, tmthing, 1, 0) // break the monitor
 				&& (flipval*(*momz) < 0) // monitor is on the floor and you're going down, or on the ceiling and you're going up
-				&& (elementalpierce != 1)) // you're not piercing through the monitor...
+				&& (elementalpierce != 1) && (!(player->powers[pw_strong] & STR_HEAVY))) // you're not piercing through the monitor...
 				{
-					if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
+					fixed_t setmomz = -*momz; // Store this, momz get changed by P_DoJump within P_DoBubbleBounce
+
+					if (elementalpierce == 2) // Reset bubblewrap, part 1
+						P_DoBubbleBounce(player);
+					*momz = setmomz; // Therefore, you should be thrust in the opposite direction, vertically.
+					if (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)
+						P_TwinSpinRejuvenate(player, player->thokitem);
+					if (elementalpierce == 2) // Reset bubblewrap, part 2
 					{
-						fixed_t setmomz = -*momz; // Store this, momz get changed by P_DoJump within P_DoBubbleBounce
-
-						if (elementalpierce == 2) // Reset bubblewrap, part 1
-							P_DoBubbleBounce(player);
-						*momz = setmomz; // Therefore, you should be thrust in the opposite direction, vertically.
-						if (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)
-							P_TwinSpinRejuvenate(player, player->thokitem);
-						if (elementalpierce == 2) // Reset bubblewrap, part 2
-						{
-							boolean underwater = tmthing->eflags & MFE_UNDERWATER;
+						boolean underwater = tmthing->eflags & MFE_UNDERWATER;
 
-							if (underwater)
-								*momz /= 2;
-							*momz -= (*momz/(underwater ? 8 : 4)); // Cap the height!
-						}
+						if (underwater)
+							*momz /= 2;
+						*momz -= (*momz/(underwater ? 8 : 4)); // Cap the height!
 					}
 				}
 				if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough.
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 07b3d8640e..b5907cfb8b 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -167,6 +167,7 @@ static void P_NetArchivePlayers(void)
 		WRITEUINT8(save_p, players[i].playerstate);
 		WRITEUINT32(save_p, players[i].pflags);
 		WRITEUINT8(save_p, players[i].panim);
+		WRITEUINT8(save_p, players[i].stronganim);
 		WRITEUINT8(save_p, players[i].spectator);
 
 		WRITEUINT16(save_p, players[i].flashpal);
@@ -394,6 +395,7 @@ static void P_NetUnArchivePlayers(void)
 		players[i].playerstate = READUINT8(save_p);
 		players[i].pflags = READUINT32(save_p);
 		players[i].panim = READUINT8(save_p);
+		players[i].stronganim = READUINT8(save_p);
 		players[i].spectator = READUINT8(save_p);
 
 		players[i].flashpal = READUINT16(save_p);
diff --git a/src/p_user.c b/src/p_user.c
index 2f522ad4b1..464cc521d3 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -988,6 +988,8 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
 	if (player->powers[pw_carry] == CR_ROPEHANG)
 		P_SetTarget(&player->mo->tracer, NULL);
 
+	player->powers[pw_strong] = STR_NONE;
+
 	{
 		angle_t ang;
 		fixed_t fallbackspeed;
@@ -1105,6 +1107,7 @@ void P_ResetPlayer(player_t *player)
 boolean P_PlayerCanDamage(player_t *player, mobj_t *thing)
 {
 	fixed_t bottomheight, topheight;
+	boolean allatk = ((player->powers[pw_strong] & STR_PUNCH) && (player->powers[pw_strong] & STR_TAIL) && (player->powers[pw_strong] & STR_STOMP) && (player->powers[pw_strong] & STR_UPPER));
 
 	if (!player->mo || player->spectator || !thing || P_MobjWasRemoved(thing))
 		return false;
@@ -1129,22 +1132,33 @@ boolean P_PlayerCanDamage(player_t *player, mobj_t *thing)
 
 	// Jumping.
 	if ((player->pflags & PF_JUMPED)
-	&& (!(player->pflags & PF_NOJUMPDAMAGE)
-		|| (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
+	&& (!(player->pflags & PF_NOJUMPDAMAGE)))
 		return true;
 
 	// Spinning.
 	if (player->pflags & PF_SPINNING)
 		return true;
 
-	if (player->dashmode >= DASHMODE_THRESHOLD && (player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE))
+	// Shield stomp.
+	if (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (player->pflags & PF_SHIELDABILITY))
+		return true;
+
+	// pw_strong checks below here
+
+	// Omnidirectional attacks.
+	if (allatk || (player->powers[pw_strong] & STR_DASH))
 		return true;
 
 	// From the front.
-	if (((player->pflags & PF_GLIDING) || (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
+	if ((player->powers[pw_strong] & STR_PUNCH)
 	&& (player->drawangle - R_PointToAngle2(player->mo->x - player->mo->momx, player->mo->y - player->mo->momy, thing->x, thing->y) +  + ANGLE_90) < ANGLE_180)
 		return true;
 
+	// From the back.
+	if ((player->powers[pw_strong] & STR_TAIL)
+	&& (player->drawangle - R_PointToAngle2(player->mo->x - player->mo->momx, player->mo->y - player->mo->momy, thing->x, thing->y) +  + ANGLE_90) >= ANGLE_180)
+		return true;
+
 	// From the top/bottom.
 	bottomheight = player->mo->z;
 	topheight = player->mo->z + player->mo->height;
@@ -1158,19 +1172,15 @@ boolean P_PlayerCanDamage(player_t *player, mobj_t *thing)
 
 	if (P_MobjFlip(player->mo)*(bottomheight - (thing->z + thing->height/2)) > 0)
 	{
-		if ((player->charflags & SF_STOMPDAMAGE || player->pflags & PF_BOUNCING) && (P_MobjFlip(player->mo)*(player->mo->momz - thing->momz) < 0))
+		if ((player->charflags & SF_STOMPDAMAGE || player->powers[pw_strong] & STR_STOMP) && (P_MobjFlip(player->mo)*(player->mo->momz - thing->momz) < 0))
 			return true;
 	}
 	else if (P_MobjFlip(player->mo)*(topheight - (thing->z + thing->height/2)) < 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))
+		if ((player->powers[pw_strong] & STR_UPPER) && (player->mo->sprite2 != SPR2_SWIM) && (P_MobjFlip(player->mo)*(player->mo->momz - thing->momz) > 0))
 			return true;
 	}
 
-	// Shield stomp.
-	if (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (player->pflags & PF_SHIELDABILITY))
-		return true;
-
 	return false;
 }
 
@@ -2324,6 +2334,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
 					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;
+					player->powers[pw_strong] = STR_MELEE;
 
 					// hearticles
 					if (type)
@@ -2513,17 +2524,13 @@ static boolean P_PlayerCanBust(player_t *player, ffloor_t *rover)
 			return true;
 
 		// Passive wall breaking
-		if (player->charflags & SF_CANBUSTWALLS)
+		if (player->charflags & SF_CANBUSTWALLS || player->powers[pw_strong] & (STR_WALL|STR_FLOOR|STR_CEILING|STR_DASH))
 			return true;
 
 		// Super
 		if (player->powers[pw_super])
 			return true;
 
-		// Dashmode
-		if ((player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE) && player->dashmode >= DASHMODE_THRESHOLD)
-			return true;
-
 		// NiGHTS drill
 		if (player->pflags & PF_DRILLING)
 			return true;
@@ -2534,21 +2541,11 @@ static boolean P_PlayerCanBust(player_t *player, ffloor_t *rover)
 
 		/* FALLTHRU */
 	case BT_STRONG: // Requires a "strong ability"
-		if (player->charflags & SF_CANBUSTWALLS)
-			return true;
-
-		if (player->pflags & PF_BOUNCING)
-			return true;
-
-		if (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)
-			return true;
-
-		if (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
+		if (player->charflags & SF_CANBUSTWALLS || player->powers[pw_strong] & (STR_WALL|STR_FLOOR|STR_CEILING))
 			return true;
 
 		break;
 	}
-
 	return false;
 }
 
@@ -2564,7 +2561,7 @@ static void P_CheckBustableBlocks(player_t *player)
 	oldx = player->mo->x;
 	oldy = player->mo->y;
 
-	if (!(player->pflags & PF_BOUNCING)) // Bouncers only get to break downwards, not sideways
+	if (!((player->powers[pw_strong] & (STR_FLOOR|STR_CEILING)) && (!(player->powers[pw_strong] & STR_WALL)) && (!(player->charflags & SF_CANBUSTWALLS)))) // Don't break sideways without wall powers
 	{
 		P_UnsetThingPosition(player->mo);
 		player->mo->x += player->mo->momx;
@@ -2591,8 +2588,24 @@ static void P_CheckBustableBlocks(player_t *player)
 			topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
 			bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
 
-			if (((player->charability == CA_TWINSPIN) && (player->panim == PA_ABILITY))
-			|| ((P_MobjFlip(player->mo)*player->mo->momz < 0) && (player->pflags & PF_BOUNCING || ((player->charability2 == CA2_MELEE) && (player->panim == PA_ABILITY2)))))
+			// Height checks
+			if (player->mo->eflags & MFE_VERTICALFLIP)
+			{
+				if ((player->powers[pw_strong] & STR_FLOOR) && (!(player->powers[pw_strong] & STR_CEILING)) && player->mo->z > topheight)
+					continue;
+
+				if ((player->powers[pw_strong] & STR_CEILING) && (!(player->powers[pw_strong] & STR_FLOOR)) && player->mo->z + player->mo->height < bottomheight)
+					continue;
+			}
+			else
+			{
+				if ((player->powers[pw_strong] & STR_FLOOR) && (!(player->powers[pw_strong] & STR_CEILING)) && player->mo->z < bottomheight)
+					continue;
+
+				if ((player->powers[pw_strong] & STR_CEILING) && (!(player->powers[pw_strong] & STR_FLOOR)) && player->mo->z + player->mo->height > topheight)
+					continue;
+			}
+			if (player->powers[pw_strong] & (STR_FLOOR|STR_CEILING))
 			{
 				topheight -= player->mo->momz;
 				bottomheight -= player->mo->momz;
@@ -2660,7 +2673,7 @@ static void P_CheckBustableBlocks(player_t *player)
 		}
 	}
 bustupdone:
-	if (!(player->pflags & PF_BOUNCING))
+	if (!((player->powers[pw_strong] & (STR_FLOOR|STR_CEILING)) && (!(player->powers[pw_strong] & STR_WALL)) && (!(player->charflags & SF_CANBUSTWALLS))))
 	{
 		P_UnsetThingPosition(player->mo);
 		player->mo->x = oldx;
@@ -4690,6 +4703,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
 						player->mo->momx += player->cmomx;
 						player->mo->momy += player->cmomy;
 						P_SetPlayerMobjState(player->mo, S_PLAY_MELEE);
+						player->powers[pw_strong] = STR_MELEE;
 						S_StartSound(player->mo, sfx_s3k42);
 					}
 					player->pflags |= PF_SPINDOWN;
@@ -4937,6 +4951,7 @@ static void P_DoTwinSpin(player_t *player)
 	S_StartSound(player->mo, sfx_s3k42);
 	player->mo->frame = 0;
 	P_SetPlayerMobjState(player->mo, S_PLAY_TWINSPIN);
+	player->powers[pw_strong] = STR_TWINSPIN;
 }
 
 //
@@ -5300,6 +5315,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 							player->pflags |= PF_THOKKED;
 						else
 							player->pflags |= (PF_THOKKED|PF_CANCARRY);
+						player->powers[pw_strong] = STR_FLY;
 					}
 					break;
 				case CA_GLIDEANDCLIMB:
@@ -5326,7 +5342,8 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 						P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
 						if (playerspeed < glidespeed)
 							P_Thrust(player->mo, player->mo->angle, glidespeed - playerspeed);
-						player->pflags &= ~(PF_SPINNING|PF_STARTDASH);
+						player->pflags &= ~(PF_JUMPED|PF_SPINNING|PF_STARTDASH);
+						player->powers[pw_strong] = STR_GLIDE;
 					}
 					break;
 				case CA_DOUBLEJUMP: // Double-Jump
@@ -5383,6 +5400,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 						P_SetPlayerMobjState(player->mo, S_PLAY_BOUNCE);
 						player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
 						player->pflags |= PF_THOKKED|PF_BOUNCING;
+						player->powers[pw_strong] = STR_BOUNCE;
 						player->mo->momx >>= 1;
 						player->mo->momy >>= 1;
 						player->mo->momz >>= 1;
@@ -12070,6 +12088,16 @@ void P_PlayerThink(player_t *player)
 	else
 		player->powers[pw_ignorelatch] = 0;
 
+	if (player->powers[pw_strong] & STR_ANIM)
+	{
+		if (!(player->stronganim))
+			player->stronganim = player->panim;
+		else if (player->panim != player->stronganim)
+			player->powers[pw_strong] = STR_NONE; 
+	}	
+	else if (player->stronganim)
+		player->stronganim = 0;
+			
 	//pw_super acts as a timer now
 	if (player->powers[pw_super]
 	&& (player->mo->state < &states[S_PLAY_SUPER_TRANS1]
@@ -12164,6 +12192,8 @@ void P_PlayerThink(player_t *player)
 			{
 				player->normalspeed = skins[player->skin].normalspeed; // Reset to default if not capable of entering dash mode.
 				player->jumpfactor = skins[player->skin].jumpfactor;
+				if (player->powers[pw_strong] & STR_DASH)
+					player->powers[pw_strong] = STR_NONE;
 			}
 		}
 		else if (P_IsObjectOnGround(player->mo)) // Activate dash mode if we're on the ground.
@@ -12173,6 +12203,9 @@ void P_PlayerThink(player_t *player)
 
 			if (player->jumpfactor < FixedMul(skins[player->skin].jumpfactor, 5*FRACUNIT/4)) // Boost jump height.
 				player->jumpfactor += FRACUNIT/300;
+
+			if ((player->charflags & SF_MACHINE) && (!(player->powers[pw_strong] == STR_METAL))) 
+					player->powers[pw_strong] = STR_METAL;
 		}
 
 		if (player->normalspeed >= skins[player->skin].normalspeed*2)
@@ -12190,6 +12223,8 @@ void P_PlayerThink(player_t *player)
 			player->normalspeed = skins[player->skin].normalspeed;
 			player->jumpfactor = skins[player->skin].jumpfactor;
 			S_StartSound(player->mo, sfx_kc65);
+			if (player->powers[pw_strong] & STR_DASH)
+				player->powers[pw_strong] = STR_NONE;
 		}
 		dashmode = 0;
 	}
-- 
GitLab


From 4342dacbdff30383d7e804cb2bb46d20045852c1 Mon Sep 17 00:00:00 2001
From: MIDIMan <miditheman2.0@gmail.com>
Date: Wed, 5 Jul 2023 14:21:53 -0400
Subject: [PATCH 163/518] Fix A_FishJump for custom objects in binary maps

---
 src/p_enemy.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/p_enemy.c b/src/p_enemy.c
index fe062cffc6..5e52993ccb 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -4886,7 +4886,9 @@ void A_FishJump(mobj_t *actor)
 			jumpval = locvar1;
 		else
 		{
-			if (actor->spawnpoint && actor->spawnpoint->args[0])
+			if (!udmf && actor->angle)
+				jumpval = AngleFixed(actor->angle)>>2;
+			else if (actor->spawnpoint && actor->spawnpoint->args[0])
 				jumpval = actor->spawnpoint->args[0] << (FRACBITS - 2);
 			else
 				jumpval = 44 << (FRACBITS - 2);
-- 
GitLab


From 8f75141c0779c6ec6e8ee91dc796341029f691e8 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 6 Jul 2023 12:38:02 +0200
Subject: [PATCH 164/518] Give drop shadows to weapon rings & panels

---
 src/p_mobj.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index a43afc9b15..44140042ea 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -10581,6 +10581,8 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing)
 
 			if (thing->flags & (MF_ENEMY|MF_BOSS))
 				return FRACUNIT;
+			else if (P_WeaponOrPanel(thing->type))
+				return 2*FRACUNIT/3;
 			else
 				return 0;
 	}
-- 
GitLab


From a4bcf4e2cefa850fa0996e19e0fdb6d502cbafad Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 6 Jul 2023 18:06:23 +0200
Subject: [PATCH 165/518] Do not consider 0 a valid executor tag for bosses

---
 src/p_enemy.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/p_enemy.c b/src/p_enemy.c
index fe062cffc6..e00a1a882e 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -3938,7 +3938,7 @@ static void P_DoBossVictory(mobj_t *mo)
 	}
 
 	// victory!
-	if (mo->spawnpoint)
+	if (mo->spawnpoint && mo->spawnpoint->args[3])
 		P_LinedefExecute(mo->spawnpoint->args[3], mo, NULL);
 
 	if (stoppedclock && modeattacking) // if you're just time attacking, skip making the capsule appear since you don't need to step on it anyways.
@@ -4157,7 +4157,7 @@ void A_BossDeath(mobj_t *mo)
 	if (LUA_CallAction(A_BOSSDEATH, mo))
 		return;
 
-	if (mo->spawnpoint)
+	if (mo->spawnpoint && mo->spawnpoint->args[2])
 		P_LinedefExecute(mo->spawnpoint->args[2], mo, NULL);
 	mo->health = 0;
 
@@ -7136,7 +7136,7 @@ void A_Boss1Chase(mobj_t *actor)
 		}
 		else
 		{
-			if (actor->spawnpoint)
+			if (actor->spawnpoint && actor->spawnpoint->args[4])
 				P_LinedefExecute(actor->spawnpoint->args[4], actor, NULL);
 			P_SetMobjState(actor, actor->info->raisestate);
 		}
-- 
GitLab


From 49528c15580f6c710ca8fd222d413fdb2caa13a6 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 6 Jul 2023 18:11:52 +0200
Subject: [PATCH 166/518] Remove generalized FOF type (linedef action 260)

---
 src/p_spec.c | 65 ----------------------------------------------------
 1 file changed, 65 deletions(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index 71ea145b9e..9195ef0199 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -6880,71 +6880,6 @@ void P_SpawnSpecials(boolean fromnetsave)
 				}
 				break;
 
-			case 260: // GZDoom-like 3D Floor.
-				{
-					UINT8 dtype = lines[i].args[1] & 3;
-					UINT8 dflags1 = lines[i].args[1] - dtype;
-					UINT8 dflags2 = lines[i].args[2];
-					UINT8 dopacity = lines[i].args[3];
-					boolean isfog = false;
-
-					if (dtype == 0)
-						dtype = 1;
-
-					ffloorflags = FOF_EXISTS;
-
-					if (dflags2 & 1) ffloorflags |= FOF_NOSHADE; // Disable light effects (Means no shadowcast)
-					if (dflags2 & 2) ffloorflags |= FOF_DOUBLESHADOW; // Restrict light inside (Means doubleshadow)
-					if (dflags2 & 4) isfog = true; // Fog effect (Explicitly render like a fog block)
-
-					if (dflags1 & 4) ffloorflags |= FOF_BOTHPLANES|FOF_ALLSIDES; // Render-inside
-					if (dflags1 & 16) ffloorflags |= FOF_INVERTSIDES|FOF_INVERTPLANES; // Invert visibility rules
-
-					// Fog block
-					if (isfog)
-						ffloorflags |= FOF_RENDERALL|FOF_CUTEXTRA|FOF_CUTSPRITES|FOF_BOTHPLANES|FOF_EXTRA|FOF_FOG|FOF_INVERTPLANES|FOF_ALLSIDES|FOF_INVERTSIDES;
-					else
-					{
-						ffloorflags |= FOF_RENDERALL;
-
-						// Solid
-						if (dtype == 1)
-							ffloorflags |= FOF_SOLID|FOF_CUTLEVEL;
-						// Water
-						else if (dtype == 2)
-							ffloorflags |= FOF_SWIMMABLE|FOF_CUTEXTRA|FOF_CUTSPRITES|FOF_EXTRA|FOF_RIPPLE;
-						// Intangible
-						else if (dtype == 3)
-							ffloorflags |= FOF_CUTEXTRA|FOF_CUTSPRITES|FOF_EXTRA;
-					}
-
-					// Non-opaque
-					if (dopacity < 255)
-					{
-						// Invisible
-						if (dopacity == 0)
-						{
-							// True invisible
-							if (ffloorflags & FOF_NOSHADE)
-								ffloorflags &= ~(FOF_RENDERALL|FOF_CUTEXTRA|FOF_CUTSPRITES|FOF_EXTRA|FOF_BOTHPLANES|FOF_ALLSIDES|FOF_CUTLEVEL);
-							// Shadow block
-							else
-							{
-								ffloorflags |= FOF_CUTSPRITES;
-								ffloorflags &= ~(FOF_RENDERALL|FOF_CUTEXTRA|FOF_EXTRA|FOF_BOTHPLANES|FOF_ALLSIDES|FOF_CUTLEVEL);
-							}
-						}
-						else
-						{
-							ffloorflags |= FOF_TRANSLUCENT|FOF_CUTEXTRA|FOF_EXTRA;
-							ffloorflags &= ~FOF_CUTLEVEL;
-						}
-					}
-
-					P_AddFakeFloorsByLine(i, dopacity, TMB_TRANSLUCENT, ffloorflags, secthinkers);
-				}
-				break;
-
 			case 300: // Trigger linedef executor
 			case 303: // Count rings
 			case 305: // Character ability
-- 
GitLab


From 29703f8d6ad7c4923bd088416b874373f2d74d1a Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 6 Jul 2023 18:26:30 +0200
Subject: [PATCH 167/518] Remove action 260 from the UDMF config

---
 extras/conf/udb/Includes/SRB222_linedefs.cfg | 46 --------------------
 1 file changed, 46 deletions(-)

diff --git a/extras/conf/udb/Includes/SRB222_linedefs.cfg b/extras/conf/udb/Includes/SRB222_linedefs.cfg
index 621b4abd53..6807786239 100644
--- a/extras/conf/udb/Includes/SRB222_linedefs.cfg
+++ b/extras/conf/udb/Includes/SRB222_linedefs.cfg
@@ -1221,52 +1221,6 @@ udmf
 				}
 			}
 		}
-		260
-		{
-			title = "Generalized 3D Floor";
-			prefix = "(260)";
-			id = "Sector_Set3dFloor";
-			requiresactivation = false;
-
-			arg0
-			{
-				title = "Target sector tag";
-				type = 13;
-			}
-			arg1
-			{
-				title = "Type";
-				type = 26;
-				default = 1;
-				enum
-				{
-					1 = "Solid";
-					2 = "Water";
-					3 = "Intangible";
-				}
-				flags
-				{
-					4 = "Render insides";
-					16 = "Only render insides";
-				}
-			}
-			arg2
-			{
-				title = "Flags";
-				type = 12;
-				enum
-				{
-					1 = "No shadow";
-					2 = "Double shadow";
-					4 = "Fog";
-				}
-			}
-			arg3
-			{
-				title = "Alpha";
-				default = 255;
-			}
-		}
 	}
 
 	linedeftrigger
-- 
GitLab


From 8d8257b4a642cda4061c50e4afca2b62f5490af0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Thu, 6 Jul 2023 19:12:29 +0200
Subject: [PATCH 168/518] Handle missing frames for sprite gracefully

---
 src/r_skins.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/r_skins.c b/src/r_skins.c
index 2c031ee851..db32273072 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -534,7 +534,7 @@ static void R_LoadSkinSprites(UINT16 wadnum, UINT16 *lump, UINT16 *lastlump, ski
 		R_AddSingleSpriteDef(spr2names[sprite2], &skin->sprites[sprite2], wadnum, *lump, *lastlump);
 
 	if (skin->sprites[0].numframes == 0)
-		I_Error("R_LoadSkinSprites: no frames found for sprite SPR2_%s\n", spr2names[0]);
+		CONS_Alert(CONS_ERROR, M_GetText("No frames found for sprite SPR2_%s\n"), spr2names[0]);
 }
 
 // returns whether found appropriate property
-- 
GitLab


From 372bb8b8fc10e2eee4f1ede2e6460c3cda6521c9 Mon Sep 17 00:00:00 2001
From: Ace Lite <lightacecz@gmail.com>
Date: Thu, 6 Jul 2023 19:27:32 +0000
Subject: [PATCH 169/518] [UDMF] Updated line sloping warning message

---
 src/p_slopes.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_slopes.c b/src/p_slopes.c
index 48a13a07d6..1f07b8f373 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -328,7 +328,7 @@ static void line_SpawnViaLine(const int linenum, const boolean spawnthinker)
 
 	if(!frontfloor && !backfloor && !frontceil && !backceil)
 	{
-		CONS_Printf("line_SpawnViaLine: Slope special with nothing to do.\n");
+		CONS_Printf("line_SpawnViaLine: Unused slope special with nothing to do on line number %i\n", linenum);
 		return;
 	}
 
-- 
GitLab


From ecf0c4b45a756039ccde6ffd1845c0232220952d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Fri, 7 Jul 2023 21:35:41 +0200
Subject: [PATCH 170/518] Fix segfault when calling P_RemoveMobj from
 MobjCollide hook

---
 src/p_map.c | 214 ++++++++++++++++++++++++++--------------------------
 1 file changed, 109 insertions(+), 105 deletions(-)

diff --git a/src/p_map.c b/src/p_map.c
index 0021fe450c..0221fbc11b 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -4005,131 +4005,135 @@ void P_BounceMove(mobj_t *mo)
 	slidemo = mo;
 	hitcount = 0;
 
-retry:
-	if (++hitcount == 3)
-		goto bounceback; // don't loop forever
-
-	if (mo->player)
-	{
-		mmomx = mo->player->rmomx;
-		mmomy = mo->player->rmomy;
-	}
-	else
+	do
 	{
-		mmomx = mo->momx;
-		mmomy = mo->momy;
-	}
+		if (++hitcount == 3)
+			goto bounceback; // don't loop forever
 
-	// trace along the three leading corners
-	if (mo->momx > 0)
-	{
-		leadx = mo->x + mo->radius;
-		trailx = mo->x - mo->radius;
-	}
-	else
-	{
-		leadx = mo->x - mo->radius;
-		trailx = mo->x + mo->radius;
-	}
+		if (mo->player)
+		{
+			mmomx = mo->player->rmomx;
+			mmomy = mo->player->rmomy;
+		}
+		else
+		{
+			mmomx = mo->momx;
+			mmomy = mo->momy;
+		}
 
-	if (mo->momy > 0)
-	{
-		leady = mo->y + mo->radius;
-		traily = mo->y - mo->radius;
-	}
-	else
-	{
-		leady = mo->y - mo->radius;
-		traily = mo->y + mo->radius;
-	}
+		// trace along the three leading corners
+		if (mo->momx > 0)
+		{
+			leadx = mo->x + mo->radius;
+			trailx = mo->x - mo->radius;
+		}
+		else
+		{
+			leadx = mo->x - mo->radius;
+			trailx = mo->x + mo->radius;
+		}
+
+		if (mo->momy > 0)
+		{
+			leady = mo->y + mo->radius;
+			traily = mo->y - mo->radius;
+		}
+		else
+		{
+			leady = mo->y - mo->radius;
+			traily = mo->y + mo->radius;
+		}
 
-	bestslidefrac = FRACUNIT + 1;
+		bestslidefrac = FRACUNIT + 1;
 
-	P_PathTraverse(leadx, leady, leadx + mmomx, leady + mmomy, PT_ADDLINES, PTR_SlideTraverse);
-	P_PathTraverse(trailx, leady, trailx + mmomx, leady + mmomy, PT_ADDLINES, PTR_SlideTraverse);
-	P_PathTraverse(leadx, traily, leadx + mmomx, traily + mmomy, PT_ADDLINES, PTR_SlideTraverse);
+		P_PathTraverse(leadx, leady, leadx + mmomx, leady + mmomy, PT_ADDLINES, PTR_SlideTraverse);
+		P_PathTraverse(trailx, leady, trailx + mmomx, leady + mmomy, PT_ADDLINES, PTR_SlideTraverse);
+		P_PathTraverse(leadx, traily, leadx + mmomx, traily + mmomy, PT_ADDLINES, PTR_SlideTraverse);
 
-	// move up to the wall
-	if (bestslidefrac == FRACUNIT + 1)
-	{
-		// the move must have hit the middle, so bounce straight back
-bounceback:
-		if (P_TryMove(mo, mo->x - mmomx, mo->y - mmomy, true))
+		// move up to the wall
+		if (bestslidefrac == FRACUNIT + 1)
 		{
-			mo->momx *= -1;
-			mo->momy *= -1;
-			mo->momx = FixedMul(mo->momx, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
-			mo->momy = FixedMul(mo->momy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
-
-			if (mo->player)
+			// the move must have hit the middle, so bounce straight back
+bounceback:
+			if (P_TryMove(mo, mo->x - mmomx, mo->y - mmomy, true))
 			{
-				mo->player->cmomx *= -1;
-				mo->player->cmomy *= -1;
-				mo->player->cmomx = FixedMul(mo->player->cmomx,
-					(FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
-				mo->player->cmomy = FixedMul(mo->player->cmomy,
-					(FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
+				mo->momx *= -1;
+				mo->momy *= -1;
+				mo->momx = FixedMul(mo->momx, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
+				mo->momy = FixedMul(mo->momy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
+
+				if (mo->player)
+				{
+					mo->player->cmomx *= -1;
+					mo->player->cmomy *= -1;
+					mo->player->cmomx = FixedMul(mo->player->cmomx,
+						(FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
+					mo->player->cmomy = FixedMul(mo->player->cmomy,
+						(FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
+				}
 			}
+			return;
 		}
-		return;
-	}
 
-	// fudge a bit to make sure it doesn't hit
-	bestslidefrac -= 0x800;
-	if (bestslidefrac > 0)
-	{
-		newx = FixedMul(mmomx, bestslidefrac);
-		newy = FixedMul(mmomy, bestslidefrac);
+		// fudge a bit to make sure it doesn't hit
+		bestslidefrac -= 0x800;
+		if (bestslidefrac > 0)
+		{
+			newx = FixedMul(mmomx, bestslidefrac);
+			newy = FixedMul(mmomy, bestslidefrac);
 
-		if (!P_TryMove(mo, mo->x + newx, mo->y + newy, true))
-			goto bounceback;
-	}
+			if (!P_TryMove(mo, mo->x + newx, mo->y + newy, true))
+			{
+				if (P_MobjWasRemoved(mo))
+					return;
+				goto bounceback;
+			}
+		}
 
-	// Now continue along the wall.
-	// First calculate remainder.
-	bestslidefrac = FRACUNIT - bestslidefrac;
+		// Now continue along the wall.
+		// First calculate remainder.
+		bestslidefrac = FRACUNIT - bestslidefrac;
 
-	if (bestslidefrac > FRACUNIT)
-		bestslidefrac = FRACUNIT;
+		if (bestslidefrac > FRACUNIT)
+			bestslidefrac = FRACUNIT;
 
-	if (bestslidefrac <= 0)
-		return;
+		if (bestslidefrac <= 0)
+			return;
 
-	if (mo->type == MT_SHELL)
-	{
-		tmxmove = mmomx;
-		tmymove = mmomy;
-	}
-	else if (mo->type == MT_THROWNBOUNCE)
-	{
-		tmxmove = FixedMul(mmomx, (FRACUNIT - (FRACUNIT>>6) - (FRACUNIT>>5)));
-		tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>6) - (FRACUNIT>>5)));
-	}
-	else if (mo->type == MT_THROWNGRENADE || mo->type == MT_CYBRAKDEMON_NAPALM_BOMB_LARGE)
-	{
-		// Quickly decay speed as it bounces
-		tmxmove = FixedDiv(mmomx, 2*FRACUNIT);
-		tmymove = FixedDiv(mmomy, 2*FRACUNIT);
-	}
-	else
-	{
-		tmxmove = FixedMul(mmomx, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
-		tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
-	}
+		if (mo->type == MT_SHELL)
+		{
+			tmxmove = mmomx;
+			tmymove = mmomy;
+		}
+		else if (mo->type == MT_THROWNBOUNCE)
+		{
+			tmxmove = FixedMul(mmomx, (FRACUNIT - (FRACUNIT>>6) - (FRACUNIT>>5)));
+			tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>6) - (FRACUNIT>>5)));
+		}
+		else if (mo->type == MT_THROWNGRENADE || mo->type == MT_CYBRAKDEMON_NAPALM_BOMB_LARGE)
+		{
+			// Quickly decay speed as it bounces
+			tmxmove = FixedDiv(mmomx, 2*FRACUNIT);
+			tmymove = FixedDiv(mmomy, 2*FRACUNIT);
+		}
+		else
+		{
+			tmxmove = FixedMul(mmomx, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
+			tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
+		}
 
-	P_HitBounceLine(bestslideline); // clip the moves
+		P_HitBounceLine(bestslideline); // clip the moves
 
-	mo->momx = tmxmove;
-	mo->momy = tmymove;
+		mo->momx = tmxmove;
+		mo->momy = tmymove;
 
-	if (mo->player)
-	{
-		mo->player->cmomx = tmxmove;
-		mo->player->cmomy = tmymove;
+		if (mo->player)
+		{
+			mo->player->cmomx = tmxmove;
+			mo->player->cmomy = tmymove;
+		}
 	}
-
-	if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove, true))
-		goto retry;
+	while (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove, true) && !P_MobjWasRemoved(mo));
 }
 
 //
-- 
GitLab


From 3ff9e908feac1349fefccf11a171f3e1c21d6a09 Mon Sep 17 00:00:00 2001
From: Tatsuru <44866610+TatsuruIKR@users.noreply.github.com>
Date: Fri, 7 Jul 2023 18:13:44 -0300
Subject: [PATCH 171/518] Signal handler minor refactor

---
 src/sdl/i_system.c | 76 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 55 insertions(+), 21 deletions(-)

diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 66eeffa30b..847ab2646f 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -325,56 +325,90 @@ static void write_backtrace(INT32 signal)
 static void I_ReportSignal(int num, int coredumped)
 {
 	//static char msg[] = "oh no! back to reality!\r\n";
-	const char *      sigmsg;
-	char msg[128];
+	const char *sigmsg, *sigttl;
+	char ttl[128];
 
 	switch (num)
 	{
 //	case SIGINT:
-//		sigmsg = "SIGINT - interrupted";
+//		sigttl = "SIGINT"
+//		sigmsg = "SRB2 was interrupted prematurely by the user.";
 //		break;
 	case SIGILL:
-		sigmsg = "SIGILL - illegal instruction - invalid function image";
+		sigmsg = "SRB2 has attempted to execute an illegal instruction and needs to close. %s";
+		sigttl = "SIGILL"; // illegal instruction - invalid function image
 		break;
 	case SIGFPE:
-		sigmsg = "SIGFPE - mathematical exception";
+		sigmsg = "SRB2 has encountered a mathematical exception and needs to close. %s";
+		sigttl = "SIGFPE"; // mathematical exception
 		break;
 	case SIGSEGV:
-		sigmsg = "SIGSEGV - segment violation";
+		sigmsg = "SRB2 has attempted to access a memory location that it shouldn't and needs to close. %s";
+		sigttl = "SIGSEGV"; // segment violation
 		break;
 //	case SIGTERM:
-//		sigmsg = "SIGTERM - Software termination signal from kill";
+//		sigmsg = "SRB2 was terminated by a kill signal.";
+//		sigttl = "SIGTERM"; // Software termination signal from kill
 //		break;
 //	case SIGBREAK:
-//		sigmsg = "SIGBREAK - Ctrl-Break sequence";
+//		sigmsg = "SRB2 was terminated by a Ctrl-Break sequence.";
+//		sigttl = "SIGBREAK" // Ctrl-Break sequence
 //		break;
 	case SIGABRT:
-		sigmsg = "SIGABRT - abnormal termination triggered by abort call";
+		sigmsg = "SRB2 was terminated by an abort signal. %s";
+		sigttl = "SIGABRT"; // abnormal termination triggered by abort call
 		break;
 	default:
-		sprintf(msg,"signal number %d", num);
+		sigmsg = "SRB2 was terminated by an unknown signal. %s";
+
+		sprintf(ttl, "number %d", num);
 		if (coredumped)
-			sigmsg = 0;
+			sigttl = 0;
 		else
-			sigmsg = msg;
+			sigttl = ttl;
 	}
 
 	if (coredumped)
 	{
-		if (sigmsg)
-			sprintf(msg, "%s (core dumped)", sigmsg);
+		if (sigttl)
+			sprintf(ttl, "%s (core dumped)", sigttl);
 		else
-			strcat(msg, " (core dumped)");
+			strcat(ttl, " (core dumped)");
 
-		sigmsg = msg;
+		sigttl = ttl;
 	}
 
-	I_OutputMsg("\nProcess killed by signal: %s\n\n", sigmsg);
+	sprintf(ttl, "Process killed by signal: %s", sigttl);
 
-	if (!M_CheckParm("-dedicated"))
-		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
-			"Process killed by signal",
-			sigmsg, NULL);
+	sigttl = ttl;
+
+	I_OutputMsg("\n%s\n\n", sigttl);
+
+	if (M_CheckParm("-dedicated"))
+		return;
+
+	const SDL_MessageBoxButtonData buttons[] = {
+		{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 0,		"OK" },
+		{ 										0, 1,  "Discord" },
+	};
+
+	const SDL_MessageBoxData messageboxdata = {
+		SDL_MESSAGEBOX_ERROR, /* .flags */
+		NULL, /* .window */
+		sigttl, /* .title */
+		va(sigmsg,
+			"\n\nTo help us figure out the cause, you can visit our official Discord server\nwhere you will find more instructions on how to submit a crash report.\n\nSorry for the inconvenience!"), /* .message */
+		SDL_arraysize(buttons), /* .numbuttons */
+		buttons, /* .buttons */
+		NULL /* .colorScheme */
+	};
+
+	int buttonid;
+
+	SDL_ShowMessageBox(&messageboxdata, &buttonid);
+
+	if (buttonid == 1)
+		SDL_OpenURL("https://www.srb2.org/discord");
 }
 
 #ifndef NEWSIGNALHANDLER
-- 
GitLab


From 0aa763df8543cf98eb2ca8da1d3a77e80f3a3119 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Sat, 8 Jul 2023 00:53:28 -0300
Subject: [PATCH 172/518] Do music fade callback on main thread

---
 src/sdl/mixer_sound.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index f13aaef5d6..0a39c7f286 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -108,6 +108,7 @@ static UINT32 fading_timer;
 static UINT32 fading_duration;
 static INT32 fading_id;
 static void (*fading_callback)(void);
+static boolean fading_do_callback;
 static boolean fading_nocleanup;
 
 #ifdef HAVE_GME
@@ -213,7 +214,10 @@ static void var_cleanup(void)
 	// HACK: See music_loop, where we want the fade timing to proceed after a non-looping
 	// song has stopped playing
 	if (!fading_nocleanup)
+	{
 		fading_callback = NULL;
+		fading_do_callback = false;
+	}
 	else
 		fading_nocleanup = false; // use it once, set it back immediately
 
@@ -330,6 +334,13 @@ void I_ShutdownSound(void)
 
 void I_UpdateSound(void)
 {
+	if (fading_do_callback)
+	{
+		if (fading_callback)
+			(*fading_callback)();
+		fading_callback = NULL;
+		fading_do_callback = false;
+	}
 }
 
 /// ------------------------
@@ -654,9 +665,8 @@ static UINT32 get_adjusted_position(UINT32 position)
 
 static void do_fading_callback(void)
 {
-	if (fading_callback)
-		(*fading_callback)();
-	fading_callback = NULL;
+	// TODO: Should I use a mutex here or something?
+	fading_do_callback = true;
 }
 
 /// ------------------------
-- 
GitLab


From 281e6012f3cf5b27dde2816ad1bacb4fe50c8634 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 8 Jul 2023 14:02:32 +0200
Subject: [PATCH 173/518] Fix segfault when exiting game with automap open

---
 src/d_netcmd.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index af44e53d63..361d320087 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -4609,6 +4609,7 @@ void Command_ExitGame_f(void)
 	botskin = 0;
 	cv_debug = 0;
 	emeralds = 0;
+	automapactive = false;
 	memset(&luabanks, 0, sizeof(luabanks));
 
 	if (dirmenu)
-- 
GitLab


From 8cc0feb21d41adebe9faef6cd5484d912e0c8e08 Mon Sep 17 00:00:00 2001
From: katsy <katmint@live.com>
Date: Sat, 8 Jul 2023 15:17:53 -0500
Subject: [PATCH 174/518] fix reading and writing of shieldscale and
 camerascale to record attack demos

---
 src/g_demo.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/g_demo.c b/src/g_demo.c
index 0403da16da..51001c6d0c 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -1493,8 +1493,8 @@ void G_BeginRecording(void)
 	WRITEUINT8(demo_p,player->acceleration);
 	WRITEFIXED(demo_p,player->height);
 	WRITEFIXED(demo_p,player->spinheight);
-	WRITEUINT8(demo_p,player->camerascale>>FRACBITS);
-	WRITEUINT8(demo_p,player->shieldscale>>FRACBITS);
+	WRITEFIXED(demo_p,player->camerascale);
+	WRITEFIXED(demo_p,player->shieldscale);
 
 	// Trying to convert it back to % causes demo desync due to precision loss.
 	// Don't do it.
@@ -1923,8 +1923,8 @@ void G_DoPlayDemo(char *defdemoname)
 	acceleration = READUINT8(demo_p);
 	height = (demoversion < 0x000e) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
 	spinheight = (demoversion < 0x000e) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
-	camerascale = (fixed_t)READUINT8(demo_p)<<FRACBITS;
-	shieldscale = (fixed_t)READUINT8(demo_p)<<FRACBITS;
+	camerascale = READFIXED(demo_p);
+	shieldscale = READFIXED(demo_p);
 	jumpfactor = READFIXED(demo_p);
 	followitem = READUINT32(demo_p);
 
-- 
GitLab


From fa02931f97c357ddac7773754f2dc43823436b19 Mon Sep 17 00:00:00 2001
From: katsy <katmint@live.com>
Date: Sat, 8 Jul 2023 16:14:37 -0500
Subject: [PATCH 175/518] actually, make all player stats precise

---
 src/g_demo.c | 41 ++++++++++++++++++++---------------------
 1 file changed, 20 insertions(+), 21 deletions(-)

diff --git a/src/g_demo.c b/src/g_demo.c
index 51001c6d0c..3dbbe6c47e 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -95,7 +95,7 @@ demoghost *ghosts = NULL;
 // DEMO RECORDING
 //
 
-#define DEMOVERSION 0x000f
+#define DEMOVERSION 0x0010
 #define DEMOHEADER  "\xF0" "SRB2Replay" "\x0F"
 
 #define DF_GHOST        0x01 // This demo contains ghost data too!
@@ -1483,11 +1483,11 @@ void G_BeginRecording(void)
 	// Stats
 	WRITEUINT8(demo_p,player->charability);
 	WRITEUINT8(demo_p,player->charability2);
-	WRITEUINT8(demo_p,player->actionspd>>FRACBITS);
-	WRITEUINT8(demo_p,player->mindash>>FRACBITS);
-	WRITEUINT8(demo_p,player->maxdash>>FRACBITS);
-	WRITEUINT8(demo_p,player->normalspeed>>FRACBITS);
-	WRITEUINT8(demo_p,player->runspeed>>FRACBITS);
+	WRITEFIXED(demo_p,player->actionspd);
+	WRITEFIXED(demo_p,player->mindash);
+	WRITEFIXED(demo_p,player->maxdash);
+	WRITEFIXED(demo_p,player->normalspeed);
+	WRITEFIXED(demo_p,player->runspeed);
 	WRITEUINT8(demo_p,player->thrustfactor);
 	WRITEUINT8(demo_p,player->accelstart);
 	WRITEUINT8(demo_p,player->acceleration);
@@ -1687,7 +1687,8 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
 	switch(oldversion) // demoversion
 	{
 	case DEMOVERSION: // latest always supported
-	case 0x000e: // The previous demoversions also supported
+	case 0x000f: // The previous demoversions also supported 
+	case 0x000e:
 	case 0x000d: // all that changed between then and now was longer color name
 	case 0x000c:
 		break;
@@ -1831,6 +1832,7 @@ void G_DoPlayDemo(char *defdemoname)
 	demoversion = READUINT16(demo_p);
 	switch(demoversion)
 	{
+	case 0x000f:
 	case 0x000d:
 	case 0x000e:
 	case DEMOVERSION: // latest always supported
@@ -1913,18 +1915,18 @@ void G_DoPlayDemo(char *defdemoname)
 
 	charability = READUINT8(demo_p);
 	charability2 = READUINT8(demo_p);
-	actionspd = (fixed_t)READUINT8(demo_p)<<FRACBITS;
-	mindash = (fixed_t)READUINT8(demo_p)<<FRACBITS;
-	maxdash = (fixed_t)READUINT8(demo_p)<<FRACBITS;
-	normalspeed = (fixed_t)READUINT8(demo_p)<<FRACBITS;
-	runspeed = (fixed_t)READUINT8(demo_p)<<FRACBITS;
+	actionspd = (demoversion < 0x0010) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
+	mindash = (demoversion < 0x0010) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
+	maxdash = (demoversion < 0x0010) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
+	normalspeed = (demoversion < 0x0010) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
+	runspeed = (demoversion < 0x0010) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
 	thrustfactor = READUINT8(demo_p);
 	accelstart = READUINT8(demo_p);
 	acceleration = READUINT8(demo_p);
 	height = (demoversion < 0x000e) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
 	spinheight = (demoversion < 0x000e) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
-	camerascale = READFIXED(demo_p);
-	shieldscale = READFIXED(demo_p);
+	camerascale = (demoversion < 0x0010) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
+	shieldscale = (demoversion < 0x0010) ? (fixed_t)READUINT8(demo_p)<<FRACBITS : READFIXED(demo_p);
 	jumpfactor = READFIXED(demo_p);
 	followitem = READUINT32(demo_p);
 
@@ -2085,6 +2087,7 @@ void G_AddGhost(char *defdemoname)
 	ghostversion = READUINT16(p);
 	switch(ghostversion)
 	{
+	case 0x000f:
 	case 0x000d:
 	case 0x000e:
 	case DEMOVERSION: // latest always supported
@@ -2161,17 +2164,12 @@ void G_AddGhost(char *defdemoname)
 	// Ghosts do not have a player structure to put this in.
 	p++; // charability
 	p++; // charability2
-	p++; // actionspd
-	p++; // mindash
-	p++; // maxdash
-	p++; // normalspeed
-	p++; // runspeed
+	p += (ghostversion < 0x0010) ? 5 : 5 * sizeof(fixed_t); // actionspd, mindash, maxdash, normalspeed, and runspeed
 	p++; // thrustfactor
 	p++; // accelstart
 	p++; // acceleration
 	p += (ghostversion < 0x000e) ? 2 : 2 * sizeof(fixed_t); // height and spinheight
-	p++; // camerascale
-	p++; // shieldscale
+	p += (ghostversion < 0x0010) ? 2 : 2 * sizeof(fixed_t); // camerascale and shieldscale
 	p += 4; // jumpfactor
 	p += 4; // followitem
 
@@ -2347,6 +2345,7 @@ void G_DoPlayMetal(void)
 	switch(metalversion)
 	{
 	case DEMOVERSION: // latest always supported
+	case 0x000f:
 	case 0x000e: // There are checks wheter the momentum is from older demo versions or not
 	case 0x000d: // all that changed between then and now was longer color name
 	case 0x000c:
-- 
GitLab


From 2ebd3fcca4b9c5935ca1a1346177ac8d3db79655 Mon Sep 17 00:00:00 2001
From: AJ Martinez <aj@worldsbe.st>
Date: Mon, 10 Jul 2023 15:40:20 -0700
Subject: [PATCH 176/518] Buffer ticcmds when receiving two on same tic

---
 src/d_clisrv.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index ca9f4a24e7..7d62272ff1 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -4565,12 +4565,17 @@ static void HandlePacketFromPlayer(SINT8 node)
 			/// \todo Use a separate cvar for that kind of timeout?
 			freezetimeout[node] = I_GetTime() + connectiontimeout;
 
+			// If we've alredy received a ticcmd for this tic, just submit it for the next one.
+			tic_t faketic = maketic;
+			if (!!(netcmds[maketic % BACKUPTICS][netconsole].angleturn & TICCMD_RECEIVED))
+				faketic++;
+
 			// Copy ticcmd
-			G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
+			G_MoveTiccmd(&netcmds[faketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
 
 			// Check ticcmd for "speed hacks"
-			if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
-				|| netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
+			if (netcmds[faketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[faketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
+				|| netcmds[faketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[faketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
 			{
 				CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole);
 				//D_Clearticcmd(k);
@@ -4582,9 +4587,10 @@ static void HandlePacketFromPlayer(SINT8 node)
 			// Splitscreen cmd
 			if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
 				&& nodetoplayer2[node] >= 0)
-				G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
+				G_MoveTiccmd(&netcmds[faketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
 					&netbuffer->u.client2pak.cmd2, 1);
 
+
 			// Check player consistancy during the level
 			if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL
 				&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
-- 
GitLab


From 576262f6c59d000efc7c9f48e860d3b74a2b1da9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Tue, 11 Jul 2023 16:37:45 +0200
Subject: [PATCH 177/518] Fix inaccuracies in FPS counter

---
 src/screen.c | 40 +++++++++-------------------------------
 1 file changed, 9 insertions(+), 31 deletions(-)

diff --git a/src/screen.c b/src/screen.c
index f8af4c504a..f4982111d0 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -462,11 +462,10 @@ double averageFPS = 0.0f;
 #define USE_FPS_SAMPLES
 
 #ifdef USE_FPS_SAMPLES
-#define FPS_SAMPLE_RATE (0.05) // How often to update FPS samples, in seconds
-#define NUM_FPS_SAMPLES (16) // Number of samples to store
+#define NUM_FPS_SAMPLES (32) // Number of samples to store
 
 static double fps_samples[NUM_FPS_SAMPLES];
-static double updateElapsed = 0.0;
+static int fps_index;
 #endif
 
 static boolean fps_init = false;
@@ -475,6 +474,7 @@ static precise_t fps_enter = 0;
 void SCR_CalculateFPS(void)
 {
 	precise_t fps_finish = 0;
+	int i;
 
 	double frameElapsed = 0.0;
 
@@ -489,35 +489,13 @@ void SCR_CalculateFPS(void)
 	fps_enter = fps_finish;
 
 #ifdef USE_FPS_SAMPLES
-	updateElapsed += frameElapsed;
+	fps_samples[fps_index] = frameElapsed / NUM_FPS_SAMPLES;
+	fps_index = (fps_index + 1) % NUM_FPS_SAMPLES;
 
-	if (updateElapsed >= FPS_SAMPLE_RATE)
-	{
-		static int sampleIndex = 0;
-		int i;
-
-		fps_samples[sampleIndex] = frameElapsed;
-
-		sampleIndex++;
-		if (sampleIndex >= NUM_FPS_SAMPLES)
-			sampleIndex = 0;
-
-		averageFPS = 0.0;
-		for (i = 0; i < NUM_FPS_SAMPLES; i++)
-		{
-			averageFPS += fps_samples[i];
-		}
-
-		if (averageFPS > 0.0)
-		{
-			averageFPS = 1.0 / (averageFPS / NUM_FPS_SAMPLES);
-		}
-	}
-
-	while (updateElapsed >= FPS_SAMPLE_RATE)
-	{
-		updateElapsed -= FPS_SAMPLE_RATE;
-	}
+	averageFPS = 0.0;
+	for (i = 0; i < NUM_FPS_SAMPLES; i++)
+		averageFPS += fps_samples[i];
+	averageFPS = 1.0 / averageFPS;
 #else
 	// Direct, unsampled counter.
 	averageFPS = 1.0 / frameElapsed;
-- 
GitLab


From 73ded4787c9c2c5d529917ab456510e9f32fa6a6 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 11 Jul 2023 19:00:47 +0200
Subject: [PATCH 178/518] Fix ammo/panel/emerald hitboxes, add shadows to
 thrown rings

---
 src/info.c   | 38 +++++++++++++++++++-------------------
 src/p_mobj.c | 25 +++++++++++++++++++++++--
 2 files changed, 42 insertions(+), 21 deletions(-)

diff --git a/src/info.c b/src/info.c
index abcf4b4999..eab6ac82a4 100644
--- a/src/info.c
+++ b/src/info.c
@@ -7194,7 +7194,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_cgot,       // deathsound
 		EMERALD1,       // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		24*FRACUNIT,    // height
 		0,              // display offset
 		16,             // mass
 		0,              // damage
@@ -7220,7 +7220,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_cgot,       // deathsound
 		EMERALD2,       // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		24*FRACUNIT,    // height
 		0,              // display offset
 		16,             // mass
 		0,              // damage
@@ -7246,7 +7246,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_cgot,       // deathsound
 		EMERALD3,       // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		24*FRACUNIT,    // height
 		0,              // display offset
 		16,             // mass
 		0,              // damage
@@ -7272,7 +7272,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_cgot,       // deathsound
 		EMERALD4,       // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		24*FRACUNIT,    // height
 		0,              // display offset
 		16,             // mass
 		0,              // damage
@@ -7298,7 +7298,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_cgot,       // deathsound
 		EMERALD5,       // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		24*FRACUNIT,    // height
 		0,              // display offset
 		16,             // mass
 		0,              // damage
@@ -7324,7 +7324,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_cgot,       // deathsound
 		EMERALD6,       // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		24*FRACUNIT,    // height
 		0,              // display offset
 		16,             // mass
 		0,              // damage
@@ -7350,7 +7350,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_cgot,       // deathsound
 		EMERALD7,       // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		24*FRACUNIT,    // height
 		0,              // display offset
 		16,             // mass
 		0,              // damage
@@ -18344,7 +18344,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_itemup,     // deathsound
 		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
+		16*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
 		pw_bouncering,  // mass
@@ -18371,7 +18371,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_itemup,     // deathsound
 		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
+		16*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
 		pw_railring,    // mass
@@ -18425,7 +18425,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_itemup,     // deathsound
 		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
+		16*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
 		pw_automaticring, // mass
@@ -18452,7 +18452,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_itemup,     // deathsound
 		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
+		16*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
 		pw_explosionring, // mass
@@ -18479,7 +18479,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_itemup,     // deathsound
 		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
+		16*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
 		pw_scatterring, // mass
@@ -18506,7 +18506,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // xdeathstate
 		sfx_itemup,     // deathsound
 		60*FRACUNIT,    // speed
-		24*FRACUNIT,    // radius
+		16*FRACUNIT,    // radius
 		24*FRACUNIT,    // height
 		0,              // display offset
 		pw_grenadering, // mass
@@ -18535,7 +18535,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_ncitem,     // deathsound
 		60*FRACUNIT,    // speed
 		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		pw_bouncering,  // mass
 		2*TICRATE,      // damage
@@ -18562,7 +18562,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_ncitem,     // deathsound
 		60*FRACUNIT,    // speed
 		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		pw_railring,    // mass
 		2*TICRATE,      // damage
@@ -18589,7 +18589,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_ncitem,     // deathsound
 		60*FRACUNIT,    // speed
 		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		pw_automaticring, // mass
 		2*TICRATE,      // damage
@@ -18616,7 +18616,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_ncitem,     // deathsound
 		60*FRACUNIT,    // speed
 		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		pw_explosionring, // mass
 		2*TICRATE,      // damage
@@ -18643,7 +18643,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_ncitem,     // deathsound
 		60*FRACUNIT,    // speed
 		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		pw_scatterring, // mass
 		2*TICRATE,      // damage
@@ -18670,7 +18670,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		sfx_ncitem,     // deathsound
 		60*FRACUNIT,    // speed
 		24*FRACUNIT,    // radius
-		24*FRACUNIT,    // height
+		40*FRACUNIT,    // height
 		0,              // display offset
 		pw_grenadering, // mass
 		2*TICRATE,      // damage
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 44140042ea..152513d1b2 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -10541,6 +10541,29 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing)
 		case MT_REDFLAG:
 		case MT_BLUEFLAG:
 
+		case MT_BOUNCERING:
+		case MT_AUTOMATICRING:
+		case MT_INFINITYRING:
+		case MT_RAILRING:
+		case MT_EXPLOSIONRING:
+		case MT_SCATTERRING:
+		case MT_GRENADERING:
+		
+		case MT_BOUNCEPICKUP:
+		case MT_RAILPICKUP:
+		case MT_AUTOPICKUP:
+		case MT_EXPLODEPICKUP:
+		case MT_SCATTERPICKUP:
+		case MT_GRENADEPICKUP:
+		
+		case MT_REDRING:
+		case MT_THROWNBOUNCE:
+		case MT_THROWNINFINITY:
+		case MT_THROWNAUTOMATIC:
+		case MT_THROWNSCATTER:
+		case MT_THROWNEXPLOSION:
+		case MT_THROWNGRENADE:
+
 		case MT_EMBLEM:
 
 		case MT_TOKEN:
@@ -10581,8 +10604,6 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing)
 
 			if (thing->flags & (MF_ENEMY|MF_BOSS))
 				return FRACUNIT;
-			else if (P_WeaponOrPanel(thing->type))
-				return 2*FRACUNIT/3;
 			else
 				return 0;
 	}
-- 
GitLab


From 464110ed814a4ed1f8a7b1d4ac7c3c4815728fe7 Mon Sep 17 00:00:00 2001
From: SteelT <steeltitanium1@gmail.com>
Date: Thu, 13 Jul 2023 01:02:15 -0400
Subject: [PATCH 179/518] Add CV_TrueFalse as possible value types for console
 variables

This also adds support for using true/false as value aliases for On/Off, Yes/No or 1/0
---
 src/command.c        | 15 +++++++++------
 src/command.h        |  1 +
 src/lua_consolelib.c |  4 +++-
 3 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/src/command.c b/src/command.c
index a34d9694a1..b03d2d8c1c 100644
--- a/src/command.c
+++ b/src/command.c
@@ -76,6 +76,7 @@ CV_PossibleValue_t CV_OnOff[] = {{0, "Off"}, {1, "On"}, {0, NULL}};
 CV_PossibleValue_t CV_YesNo[] = {{0, "No"}, {1, "Yes"}, {0, NULL}};
 CV_PossibleValue_t CV_Unsigned[] = {{0, "MIN"}, {999999999, "MAX"}, {0, NULL}};
 CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}};
+CV_PossibleValue_t CV_TrueFalse[] = {{0, "False"}, {1, "True"}, {0, NULL}};
 
 // Filter consvars by EXECVERSION
 // First implementation is 26 (2.1.21), so earlier configs default at 25 (2.1.20)
@@ -863,9 +864,11 @@ static void COM_Help_f(void)
 			{
 				CONS_Printf(" Possible values:\n");
 				if (cvar->PossibleValue == CV_YesNo)
-					CONS_Printf("  Yes or No (On or Off, 1 or 0)\n");
+					CONS_Printf("  Yes or No (On or Off, True or False, 1 or 0)\n");
 				else if (cvar->PossibleValue == CV_OnOff)
-					CONS_Printf("  On or Off (Yes or No, 1 or 0)\n");
+					CONS_Printf("  On or Off (Yes or No, True or False, 1 or 0)\n");
+				else if (cvar->PossibleValue == CV_TrueFalse)
+					CONS_Printf("  True or False (On or Off, Yes or No, 1 or 0)\n");
 				else if (cvar->PossibleValue == Color_cons_t)
 				{
 					for (i = 1; i < numskincolors; ++i)
@@ -1016,7 +1019,7 @@ static void COM_Toggle_f(void)
 	if (CV_Immutable(cvar))
 		return;
 
-	if (!(cvar->PossibleValue == CV_YesNo || cvar->PossibleValue == CV_OnOff))
+	if (!(cvar->PossibleValue == CV_YesNo || cvar->PossibleValue == CV_OnOff || cvar->PossibleValue == CV_TrueFalse))
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("%s is not a boolean value\n"), COM_Argv(1));
 		return;
@@ -1507,12 +1510,12 @@ static void Setvalue(consvar_t *var, const char *valstr, boolean stealth)
 						goto found;
 			}
 			// Not found ... but wait, there's hope!
-			if (var->PossibleValue == CV_OnOff || var->PossibleValue == CV_YesNo)
+			if (var->PossibleValue == CV_OnOff || var->PossibleValue == CV_YesNo || var->PossibleValue == CV_TrueFalse)
 			{
 				overrideval = -1;
-				if (!stricmp(valstr, "on") || !stricmp(valstr, "yes"))
+				if (!stricmp(valstr, "on") || !stricmp(valstr, "yes") || !stricmp(valstr, "true"))
 					overrideval = 1;
-				else if (!stricmp(valstr, "off") || !stricmp(valstr, "no"))
+				else if (!stricmp(valstr, "off") || !stricmp(valstr, "no") || !stricmp(valstr, "false"))
 					overrideval = 0;
 
 				if (overrideval != -1)
diff --git a/src/command.h b/src/command.h
index 69d1890d34..619d8c1dca 100644
--- a/src/command.h
+++ b/src/command.h
@@ -177,6 +177,7 @@ extern CV_PossibleValue_t CV_OnOff[];
 extern CV_PossibleValue_t CV_YesNo[];
 extern CV_PossibleValue_t CV_Unsigned[];
 extern CV_PossibleValue_t CV_Natural[];
+extern CV_PossibleValue_t CV_TrueFalse[];
 
 // Filter consvars by version
 extern consvar_t cv_execversion;
diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index 1d15b3b145..9e5d1e2ee0 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -357,7 +357,7 @@ static int lib_cvRegisterVar(lua_State *L)
 			if (lua_islightuserdata(L, 4))
 			{
 				CV_PossibleValue_t *pv = lua_touserdata(L, 4);
-				if (pv == CV_OnOff || pv == CV_YesNo || pv == CV_Unsigned || pv == CV_Natural)
+				if (pv == CV_OnOff || pv == CV_YesNo || pv == CV_Unsigned || pv == CV_Natural || pv == CV_TrueFalse)
 					cvar->PossibleValue = pv;
 				else
 					FIELDERROR("PossibleValue", "CV_PossibleValue_t expected, got unrecognised pointer")
@@ -652,6 +652,8 @@ int LUA_ConsoleLib(lua_State *L)
 	lua_setglobal(L, "CV_Unsigned");
 	lua_pushlightuserdata(L, CV_Natural);
 	lua_setglobal(L, "CV_Natural");
+	lua_pushlightuserdata(L, CV_TrueFalse);
+	lua_setglobal(L, "CV_TrueFalse");
 
 	// Set global functions
 	lua_pushvalue(L, LUA_GLOBALSINDEX);
-- 
GitLab


From 8755582901c70a8eff73084690d50da29d9c93cd Mon Sep 17 00:00:00 2001
From: AJ Martinez <aj@worldsbe.st>
Date: Fri, 14 Jul 2023 02:33:33 -0700
Subject: [PATCH 180/518] Don't submit future ticcmds if they could overwrite a
 needed ticcmd

---
 src/d_clisrv.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 7d62272ff1..2106191640 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -4567,7 +4567,8 @@ static void HandlePacketFromPlayer(SINT8 node)
 
 			// If we've alredy received a ticcmd for this tic, just submit it for the next one.
 			tic_t faketic = maketic;
-			if (!!(netcmds[maketic % BACKUPTICS][netconsole].angleturn & TICCMD_RECEIVED))
+			if ((!!(netcmds[maketic % BACKUPTICS][netconsole].angleturn & TICCMD_RECEIVED))
+				&& (maketic - firstticstosend < BACKUPTICS))
 				faketic++;
 
 			// Copy ticcmd
-- 
GitLab


From bfbbaf9f56a3622d9748251f09e8cd8d56ab3966 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Fri, 14 Jul 2023 13:25:09 +0200
Subject: [PATCH 181/518] Make sure all allocations are properly aligned

---
 src/d_netfil.c            |  2 +-
 src/lua_hudlib_drawlist.c |  6 +++---
 src/r_fps.c               | 17 ++++++-----------
 src/w_wad.c               |  2 +-
 src/z_zone.c              |  3 ++-
 src/z_zone.h              |  8 ++++----
 6 files changed, 17 insertions(+), 21 deletions(-)

diff --git a/src/d_netfil.c b/src/d_netfil.c
index e60af2c2c2..3fef756812 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -498,7 +498,7 @@ INT32 CL_CheckFiles(void)
 		CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename);
 
 		// Check in already loaded files
-		for (j = mainwads; wadfiles[j]; j++)
+		for (j = mainwads; j < numwadfiles; j++)
 		{
 			nameonly(strcpy(wadfilename, wadfiles[j]->filename));
 			if (!stricmp(wadfilename, fileneeded[i].filename) &&
diff --git a/src/lua_hudlib_drawlist.c b/src/lua_hudlib_drawlist.c
index f46f207c10..6f83094ac0 100644
--- a/src/lua_hudlib_drawlist.c
+++ b/src/lua_hudlib_drawlist.c
@@ -103,7 +103,7 @@ huddrawlist_h LUA_HUD_CreateDrawList(void)
 {
 	huddrawlist_h drawlist;
 
-	drawlist = (huddrawlist_h) Z_CallocAlign(sizeof(struct huddrawlist_s), PU_STATIC, NULL, 64);
+	drawlist = (huddrawlist_h) Z_Calloc(sizeof(struct huddrawlist_s), PU_STATIC, NULL);
 	drawlist->items = NULL;
 	drawlist->items_capacity = 0;
 	drawlist->items_len = 0;
@@ -160,7 +160,7 @@ static size_t AllocateDrawItem(huddrawlist_h list)
 	{
 		if (list->items_capacity == 0) list->items_capacity = 128;
 		else list->items_capacity *= 2;
-		list->items = (drawitem_t *) Z_ReallocAlign(list->items, sizeof(struct drawitem_s) * list->items_capacity, PU_STATIC, NULL, 64);
+		list->items = (drawitem_t *) Z_Realloc(list->items, sizeof(struct drawitem_s) * list->items_capacity, PU_STATIC, NULL);
 	}
 
 	return list->items_len++;
@@ -179,7 +179,7 @@ static const char *CopyString(huddrawlist_h list, const char* str)
 	{
 		if (list->strbuf_capacity == 0) list->strbuf_capacity = 256;
 		else list->strbuf_capacity *= 2;
-		list->strbuf = (char*) Z_ReallocAlign(list->strbuf, sizeof(char) * list->strbuf_capacity, PU_STATIC, NULL, 8);
+		list->strbuf = (char*) Z_Realloc(list->strbuf, sizeof(char) * list->strbuf_capacity, PU_STATIC, NULL);
 	}
 	const char *result = (const char *) &list->strbuf[list->strbuf_len];
 	strncpy(&list->strbuf[list->strbuf_len], str, lenstr + 1);
diff --git a/src/r_fps.c b/src/r_fps.c
index 2d30c9f019..6166f04080 100644
--- a/src/r_fps.c
+++ b/src/r_fps.c
@@ -369,12 +369,11 @@ static void AddInterpolator(levelinterpolator_t* interpolator)
 			levelinterpolators_size *= 2;
 		}
 
-		levelinterpolators = Z_ReallocAlign(
+		levelinterpolators = Z_Realloc(
 			(void*) levelinterpolators,
 			sizeof(levelinterpolator_t*) * levelinterpolators_size,
 			PU_LEVEL,
-			NULL,
-			sizeof(levelinterpolator_t*) * 8
+			NULL
 		);
 	}
 
@@ -384,11 +383,8 @@ static void AddInterpolator(levelinterpolator_t* interpolator)
 
 static levelinterpolator_t *CreateInterpolator(levelinterpolator_type_e type, thinker_t *thinker)
 {
-	levelinterpolator_t *ret = (levelinterpolator_t*) Z_CallocAlign(
-		sizeof(levelinterpolator_t),
-		PU_LEVEL,
-		NULL,
-		sizeof(levelinterpolator_t) * 8
+	levelinterpolator_t *ret = (levelinterpolator_t*) Z_Calloc(
+		sizeof(levelinterpolator_t), PU_LEVEL, NULL
 	);
 
 	ret->type = type;
@@ -703,12 +699,11 @@ void R_AddMobjInterpolator(mobj_t *mobj)
 			interpolated_mobjs_capacity *= 2;
 		}
 
-		interpolated_mobjs = Z_ReallocAlign(
+		interpolated_mobjs = Z_Realloc(
 			interpolated_mobjs,
 			sizeof(mobj_t *) * interpolated_mobjs_capacity,
 			PU_LEVEL,
-			NULL,
-			64
+			NULL
 		);
 	}
 
diff --git a/src/w_wad.c b/src/w_wad.c
index 456afef7b9..b13dc8cc1f 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -972,7 +972,7 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup)
 	// add the wadfile
 	//
 	CONS_Printf(M_GetText("Added file %s (%u lumps)\n"), filename, numlumps);
-	wadfiles = Z_Realloc(wadfiles, sizeof(wadfile_t) * (numwadfiles + 1), PU_STATIC, NULL);
+	wadfiles = Z_Realloc(wadfiles, sizeof(wadfile_t *) * (numwadfiles + 1), PU_STATIC, NULL);
 	wadfiles[numwadfiles] = wadfile;
 	numwadfiles++; // must come BEFORE W_LoadDehackedLumps, so any addfile called by COM_BufInsertText called by Lua doesn't overwrite what we just loaded
 
diff --git a/src/z_zone.c b/src/z_zone.c
index 11c4bcb2c7..d23c43b7b5 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -287,7 +287,8 @@ void *Z_Malloc2(size_t size, INT32 tag, void *user, INT32 alignbits,
 void *Z_MallocAlign(size_t size, INT32 tag, void *user, INT32 alignbits)
 #endif
 {
-	size_t extrabytes = (1<<alignbits) - 1;
+	I_Assert(alignbits >= 0 && alignbits < (INT32)(sizeof(size_t) * 8));
+	size_t extrabytes = ((size_t)1<<alignbits) - 1;
 	size_t padsize = 0;
 	memblock_t *block;
 	void *ptr;
diff --git a/src/z_zone.h b/src/z_zone.h
index c3cd4f0115..f00f577494 100644
--- a/src/z_zone.h
+++ b/src/z_zone.h
@@ -101,10 +101,10 @@ void *Z_CallocAlign(size_t size, INT32 tag, void *user, INT32 alignbits) FUNCALL
 void *Z_ReallocAlign(void *ptr, size_t size, INT32 tag, void *user, INT32 alignbits) FUNCALLOC(2);
 #endif
 
-// Alloc with no alignment
-#define Z_Malloc(s,t,u)    Z_MallocAlign(s, t, u, 0)
-#define Z_Calloc(s,t,u)    Z_CallocAlign(s, t, u, 0)
-#define Z_Realloc(p,s,t,u) Z_ReallocAlign(p, s, t, u, 0)
+// Alloc with standard alignment
+#define Z_Malloc(s,t,u)    Z_MallocAlign(s, t, u, sizeof(void *))
+#define Z_Calloc(s,t,u)    Z_CallocAlign(s, t, u, sizeof(void *))
+#define Z_Realloc(p,s,t,u) Z_ReallocAlign(p, s, t, u, sizeof(void *))
 
 // Free all memory by tag
 // these don't give line numbers for ZDEBUG currently though
-- 
GitLab


From 8b931da6f30414376d22181b558e176a5a408f7c Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <zwipzwapzapony@gmail.com>
Date: Fri, 14 Jul 2023 12:51:16 +0000
Subject: [PATCH 182/518] Draw hitboxes last in OpenGL

---
 src/hardware/hw_main.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index e7550fd6e8..bb05f2a6d6 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -4532,9 +4532,9 @@ static int CompareVisSprites(const void *p1, const void *p2)
 	int linkdraw1;
 	int linkdraw2;
 
-	// bbox doesn't need to be sorted
+	// draw bbox after everything else
 	if (spr1->bbox || spr2->bbox)
-		return 0;
+		return (spr1->bbox - spr2->bbox);
 
 	// check for precip first, because then sprX->mobj is actually a precipmobj_t and does not have flags2 or tracer
 	linkdraw1 = !spr1->precip && (spr1->mobj->flags2 & MF2_LINKDRAW) && spr1->mobj->tracer;
-- 
GitLab


From 34ce172a907778bc4c220dba02cd5ab3761ae761 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Fri, 14 Jul 2023 15:03:43 +0200
Subject: [PATCH 183/518] Allow toggling PF_NoDepthTest for OpenGL hitboxes

---
 src/d_netcmd.c         | 1 +
 src/hardware/hw_main.c | 4 ++--
 src/r_bbox.c           | 3 ++-
 src/screen.h           | 3 ++-
 4 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 0f859a569f..c3cd7daa87 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -874,6 +874,7 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_fullscreen);
 	CV_RegisterVar(&cv_renderview);
 	CV_RegisterVar(&cv_renderhitboxinterpolation);
+	CV_RegisterVar(&cv_renderhitboxgldepth);
 	CV_RegisterVar(&cv_renderhitbox);
 	CV_RegisterVar(&cv_renderer);
 	CV_RegisterVar(&cv_scr_depth);
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index bb05f2a6d6..194d68c547 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -4081,8 +4081,8 @@ static void HWR_DrawBoundingBox(gl_vissprite_t *vis)
 		v[017].y = v[020].y = v[021].y = v[025].y = v[026].y = v[027].y = vis->gzt; // top
 
 	Surf.PolyColor = V_GetColor(R_GetBoundingBoxColor(vis->mobj));
-
-	HWR_ProcessPolygon(&Surf, v, 24, PF_Modulated|PF_NoTexture|PF_WireFrame, SHADER_NONE, false);
+	
+	HWR_ProcessPolygon(&Surf, v, 24, (cv_renderhitboxgldepth.value ? 0 : PF_NoDepthTest)|PF_Modulated|PF_NoTexture|PF_WireFrame, SHADER_NONE, false);
 }
 
 // -----------------+
diff --git a/src/r_bbox.c b/src/r_bbox.c
index 7ed693fd0a..59d0893c4b 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -34,8 +34,9 @@ static CV_PossibleValue_t renderhitbox_cons_t[] = {
 	{RENDERHITBOX_RINGS, "Rings"},
 	{0}};
 
-consvar_t cv_renderhitbox = CVAR_INIT ("renderhitbox", "Off", 0, renderhitbox_cons_t, NULL);
+consvar_t cv_renderhitbox = CVAR_INIT ("renderhitbox", "Off", CV_CHEAT, renderhitbox_cons_t, NULL);
 consvar_t cv_renderhitboxinterpolation = CVAR_INIT ("renderhitbox_interpolation", "On", CV_SAVE, CV_OnOff, NULL);
+consvar_t cv_renderhitboxgldepth = CVAR_INIT ("renderhitbox_gldepth", "Off", CV_SAVE, CV_OnOff, NULL);
 
 struct bbox_col {
 	INT32 x;
diff --git a/src/screen.h b/src/screen.h
index b021a419dd..9222805fb4 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -199,7 +199,8 @@ extern CV_PossibleValue_t cv_renderer_t[];
 extern INT32 scr_bpp;
 extern UINT8 *scr_borderpatch; // patch used to fill the view borders
 
-extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_renderhitbox, cv_renderhitboxinterpolation, cv_fullscreen;
+extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen;
+extern consvar_t cv_renderhitbox, cv_renderhitboxinterpolation, cv_renderhitboxgldepth;
 // wait for page flipping to end or not
 extern consvar_t cv_vidwait;
 extern consvar_t cv_timescale;
-- 
GitLab


From 28849c31a8bed7854f1f28cdd37cb3fad0623287 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 15 Jul 2023 11:26:13 +0200
Subject: [PATCH 184/518] Update (U)ZB config with hitbox tweaks

---
 extras/conf/SRB2-22.cfg                    | 15 ++++++++++-----
 extras/conf/udb/Includes/SRB222_things.cfg | 21 ++++++++++++++++-----
 2 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index dd5cdb46b2..bcb6e62413 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -4367,7 +4367,6 @@ thingtypes
 	{
 		color = 14; // Yellow
 		title = "Rings and Weapon Panels";
-		width = 24;
 		height = 24;
 		flags8height = 24;
 		flags8text = "[8] Float";
@@ -4377,7 +4376,6 @@ thingtypes
 		{
 			title = "Ring";
 			sprite = "RINGA0";
-			width = 16;
 		}
 		301
 		{
@@ -4393,6 +4391,7 @@ thingtypes
 		{
 			title = "Infinity Ring";
 			sprite = "RNGIA0";
+			width = 24;
 		}
 		304
 		{
@@ -4418,43 +4417,48 @@ thingtypes
 		{
 			title = "CTF Team Ring (Red)";
 			sprite = "internal:TRNGA0R";
-			width = 16;
 		}
 		309
 		{
 			title = "CTF Team Ring (Blue)";
 			sprite = "internal:TRNGA0B";
-			width = 16;
 		}
 		330
 		{
 			title = "Bounce Ring Panel";
 			sprite = "PIKBA0";
+			width = 24;
+			height = 40;
 		}
 		331
 		{
 			title = "Rail Ring Panel";
 			sprite = "PIKRA0";
+			width = 24;
 		}
 		332
 		{
 			title = "Automatic Ring Panel";
 			sprite = "PIKAA0";
+			width = 24;
 		}
 		333
 		{
 			title = "Explosion Ring Panel";
 			sprite = "PIKEA0";
+			width = 24;
 		}
 		334
 		{
 			title = "Scatter Ring Panel";
 			sprite = "PIKSA0";
+			width = 24;
 		}
 		335
 		{
 			title = "Grenade Ring Panel";
 			sprite = "PIKGA0";
+			width = 24;
 		}
 	}
 
@@ -4463,7 +4467,7 @@ thingtypes
 		color = 10; // Light Green
 		title = "Other Collectibles";
 		width = 16;
-		height = 32;
+		height = 24;
 		sort = 1;
 		sprite = "CEMGA0";
 
@@ -4529,6 +4533,7 @@ thingtypes
 		{
 			title = "Emerald Hunt Location";
 			sprite = "SHRDA0";
+			height = 32;
 			flags8height = 24;
 			flags8text = "[8] Float";
 		}
diff --git a/extras/conf/udb/Includes/SRB222_things.cfg b/extras/conf/udb/Includes/SRB222_things.cfg
index df08e3ac50..9eb227974d 100644
--- a/extras/conf/udb/Includes/SRB222_things.cfg
+++ b/extras/conf/udb/Includes/SRB222_things.cfg
@@ -1185,7 +1185,7 @@ udmf
 	{
 		color = 14; // Yellow
 		title = "Rings and Weapon Panels";
-		width = 24;
+		width = 16;
 		height = 24;
 		sprite = "RINGA0";
 
@@ -1193,7 +1193,6 @@ udmf
 		{
 			title = "Ring";
 			sprite = "RINGA0";
-			width = 16;
 			arg0
 			{
 				title = "Float?";
@@ -1227,6 +1226,7 @@ udmf
 		{
 			title = "Infinity Ring";
 			sprite = "RNGIA0";
+			width = 24;
 			arg0
 			{
 				title = "Float?";
@@ -1282,7 +1282,6 @@ udmf
 		{
 			title = "CTF Team Ring (Red)";
 			sprite = "internal:TRNGA0R";
-			width = 16;
 			arg0
 			{
 				title = "Float?";
@@ -1294,7 +1293,6 @@ udmf
 		{
 			title = "CTF Team Ring (Blue)";
 			sprite = "internal:TRNGA0B";
-			width = 16;
 			arg0
 			{
 				title = "Float?";
@@ -1306,6 +1304,8 @@ udmf
 		{
 			title = "Bounce Ring Panel";
 			sprite = "PIKBA0";
+			width = 24;
+			height = 40;
 			arg0
 			{
 				title = "Float?";
@@ -1317,6 +1317,8 @@ udmf
 		{
 			title = "Rail Ring Panel";
 			sprite = "PIKRA0";
+			width = 24;
+			height = 40;
 			arg0
 			{
 				title = "Float?";
@@ -1328,6 +1330,8 @@ udmf
 		{
 			title = "Automatic Ring Panel";
 			sprite = "PIKAA0";
+			width = 24;
+			height = 40;
 			arg0
 			{
 				title = "Float?";
@@ -1339,6 +1343,8 @@ udmf
 		{
 			title = "Explosion Ring Panel";
 			sprite = "PIKEA0";
+			width = 24;
+			height = 40;
 			arg0
 			{
 				title = "Float?";
@@ -1350,6 +1356,8 @@ udmf
 		{
 			title = "Scatter Ring Panel";
 			sprite = "PIKSA0";
+			width = 24;
+			height = 40;
 			arg0
 			{
 				title = "Float?";
@@ -1361,6 +1369,8 @@ udmf
 		{
 			title = "Grenade Ring Panel";
 			sprite = "PIKGA0";
+			width = 24;
+			height = 40;
 			arg0
 			{
 				title = "Float?";
@@ -1375,7 +1385,7 @@ udmf
 		color = 10; // Light_Green
 		title = "Other Collectibles";
 		width = 16;
-		height = 32;
+		height = 24;
 		sort = 1;
 		sprite = "CEMGA0";
 
@@ -1445,6 +1455,7 @@ udmf
 		{
 			title = "Emerald Hunt Location";
 			sprite = "SHRDA0";
+			height = 32;
 			arg0
 			{
 				title = "Float?";
-- 
GitLab


From e3824b9f1b759c319df85d7c710bf76a70573c38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 15 Jul 2023 12:51:07 +0200
Subject: [PATCH 185/518] fixup! Fix inaccuracies in FPS counter

---
 src/screen.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/screen.c b/src/screen.c
index f4982111d0..b09ee81d28 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -462,10 +462,11 @@ double averageFPS = 0.0f;
 #define USE_FPS_SAMPLES
 
 #ifdef USE_FPS_SAMPLES
-#define NUM_FPS_SAMPLES (32) // Number of samples to store
+#define MAX_FRAME_TIME 0.05
+#define NUM_FPS_SAMPLES (16) // Number of samples to store
 
-static double fps_samples[NUM_FPS_SAMPLES];
-static int fps_index;
+static double total_frame_time = 0.0;
+static int frame_index;
 #endif
 
 static boolean fps_init = false;
@@ -474,7 +475,6 @@ static precise_t fps_enter = 0;
 void SCR_CalculateFPS(void)
 {
 	precise_t fps_finish = 0;
-	int i;
 
 	double frameElapsed = 0.0;
 
@@ -489,13 +489,13 @@ void SCR_CalculateFPS(void)
 	fps_enter = fps_finish;
 
 #ifdef USE_FPS_SAMPLES
-	fps_samples[fps_index] = frameElapsed / NUM_FPS_SAMPLES;
-	fps_index = (fps_index + 1) % NUM_FPS_SAMPLES;
-
-	averageFPS = 0.0;
-	for (i = 0; i < NUM_FPS_SAMPLES; i++)
-		averageFPS += fps_samples[i];
-	averageFPS = 1.0 / averageFPS;
+	total_frame_time += frameElapsed;
+	if (frame_index++ >= NUM_FPS_SAMPLES || total_frame_time >= MAX_FRAME_TIME)
+	{
+		averageFPS = 1.0 / (total_frame_time / frame_index);
+		total_frame_time = 0.0;
+		frame_index = 0;
+	}
 #else
 	// Direct, unsampled counter.
 	averageFPS = 1.0 / frameElapsed;
-- 
GitLab


From c64b242f012adbaa6d3e77070baebbf190313247 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 15 Jul 2023 13:07:55 +0200
Subject: [PATCH 186/518] Note to self: don't do busywork right after waking up
 from a short night of sleep

---
 extras/conf/SRB2-22.cfg | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index bcb6e62413..41ad998891 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -4435,30 +4435,35 @@ thingtypes
 			title = "Rail Ring Panel";
 			sprite = "PIKRA0";
 			width = 24;
+			height = 40;
 		}
 		332
 		{
 			title = "Automatic Ring Panel";
 			sprite = "PIKAA0";
 			width = 24;
+			height = 40;
 		}
 		333
 		{
 			title = "Explosion Ring Panel";
 			sprite = "PIKEA0";
 			width = 24;
+			height = 40;
 		}
 		334
 		{
 			title = "Scatter Ring Panel";
 			sprite = "PIKSA0";
 			width = 24;
+			height = 40;
 		}
 		335
 		{
 			title = "Grenade Ring Panel";
 			sprite = "PIKGA0";
 			width = 24;
+			height = 40;
 		}
 	}
 
-- 
GitLab


From 9a386111e285af2e9eea0293ff484f0375fd61a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sun, 16 Jul 2023 14:19:19 +0200
Subject: [PATCH 187/518] Fix crash when P_ZMovement calls from Lua removes the
 object

---
 src/lua_baselib.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 25fa38769a..0b0c93be4d 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1078,7 +1078,8 @@ static int lib_pZMovement(lua_State *L)
 	if (!actor)
 		return LUA_ErrInvalid(L, "mobj_t");
 	lua_pushboolean(L, P_ZMovement(actor));
-	P_CheckPosition(actor, actor->x, actor->y);
+	if (!P_MobjWasRemoved(actor))
+		P_CheckPosition(actor, actor->x, actor->y);
 	P_SetTarget(&tmthing, ptmthing);
 	return 1;
 }
@@ -1106,7 +1107,8 @@ static int lib_pSceneryZMovement(lua_State *L)
 	if (!actor)
 		return LUA_ErrInvalid(L, "mobj_t");
 	lua_pushboolean(L, P_SceneryZMovement(actor));
-	P_CheckPosition(actor, actor->x, actor->y);
+	if (!P_MobjWasRemoved(actor))
+		P_CheckPosition(actor, actor->x, actor->y);
 	P_SetTarget(&tmthing, ptmthing);
 	return 1;
 }
-- 
GitLab


From c55b2f29434ab6959873f5e455040d17d65838ba Mon Sep 17 00:00:00 2001
From: SteelT <steeltitanium1@gmail.com>
Date: Sun, 16 Jul 2023 13:56:42 -0400
Subject: [PATCH 188/518] Print list of existing alias commands

---
 src/command.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/command.c b/src/command.c
index b03d2d8c1c..2cb3d821c6 100644
--- a/src/command.c
+++ b/src/command.c
@@ -681,6 +681,17 @@ static void COM_ExecuteString(char *ptext)
 //                            SCRIPT COMMANDS
 // =========================================================================
 
+static void print_alias(void)
+{
+	cmdalias_t *a;
+
+	CONS_Printf("\x82""Current alias commands:\n");
+	for (a = com_alias; a; a = a->next)
+	{
+		CONS_Printf("%s : %s", a->name, a->value);
+	}
+}
+
 /** Creates a command name that replaces another command.
   */
 static void COM_Alias_f(void)
@@ -692,6 +703,7 @@ static void COM_Alias_f(void)
 	if (COM_Argc() < 3)
 	{
 		CONS_Printf(M_GetText("alias <name> <command>: create a shortcut command that executes other command(s)\n"));
+		print_alias();
 		return;
 	}
 
-- 
GitLab


From a90151570f3db849d93a41dd6812f2f42e9d1a67 Mon Sep 17 00:00:00 2001
From: SteelT <steeltitanium1@gmail.com>
Date: Sun, 16 Jul 2023 15:44:48 -0400
Subject: [PATCH 189/518] Replace the alias if an alias already exists

Reduces wasting memory by having multiple aliases with the same name
---
 src/command.c | 37 +++++++++++++++++++++++++++++--------
 1 file changed, 29 insertions(+), 8 deletions(-)

diff --git a/src/command.c b/src/command.c
index 2cb3d821c6..e1a43522da 100644
--- a/src/command.c
+++ b/src/command.c
@@ -692,11 +692,36 @@ static void print_alias(void)
 	}
 }
 
+static void add_alias(char *newname, char *newcmd)
+{
+	cmdalias_t *a;
+
+	// Check for existing aliases first
+	for (a = com_alias; a; a = a->next)
+	{
+		if (!stricmp(newname, a->name))
+		{
+			Z_Free(a->value); // Free old cmd 
+			a->value = newcmd;
+			return;
+		}
+	}
+
+	// No alias found, add it instead
+	a = ZZ_Alloc(sizeof *a);
+	a->next = com_alias;
+	com_alias = a;
+
+	a->name = newname;
+	a->value = newcmd;
+}
+
 /** Creates a command name that replaces another command.
   */
 static void COM_Alias_f(void)
 {
-	cmdalias_t *a;
+	char *name;
+	char *zcmd;
 	char cmd[1024];
 	size_t i, c;
 
@@ -707,11 +732,7 @@ static void COM_Alias_f(void)
 		return;
 	}
 
-	a = ZZ_Alloc(sizeof *a);
-	a->next = com_alias;
-	com_alias = a;
-
-	a->name = Z_StrDup(COM_Argv(1));
+	name = Z_StrDup(COM_Argv(1));
 
 	// copy the rest of the command line
 	cmd[0] = 0; // start out with a null string
@@ -723,8 +744,8 @@ static void COM_Alias_f(void)
 			strcat(cmd, " ");
 	}
 	strcat(cmd, "\n");
-
-	a->value = Z_StrDup(cmd);
+	zcmd = Z_StrDup(cmd);
+	add_alias(name, zcmd);
 }
 
 /** Prints a line of text to the console.
-- 
GitLab


From 1ced8acc6a3c301f638753cf404a29cdfecb4d54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sun, 16 Jul 2023 23:25:22 +0200
Subject: [PATCH 190/518] Fix softlocking as Sonic & Tails when Player 2 has no
 name

---
 src/d_clisrv.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 2106191640..05af083bd2 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1293,6 +1293,7 @@ static boolean CL_AskFileList(INT32 firstfile)
 static boolean CL_SendJoin(void)
 {
 	UINT8 localplayers = 1;
+	char const *player2name;
 	if (netgame)
 		CONS_Printf(M_GetText("Sending join request...\n"));
 	netbuffer->packettype = PT_CLIENTJOIN;
@@ -1309,9 +1310,11 @@ static boolean CL_SendJoin(void)
 	CleanupPlayerName(consoleplayer, cv_playername.zstring);
 	if (splitscreen)
 		CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */
+	// Avoid empty string on bots to avoid softlocking in singleplayer
+	player2name = botingame ? "a" : cv_playername2.zstring;
 
 	strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME);
-	strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME);
+	strncpy(netbuffer->u.clientcfg.names[1], player2name, MAXPLAYERNAME);
 
 	return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
 }
-- 
GitLab


From 80bf4d6c2d1cc413c41b0df69e2cf9334675377b Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 12 Jun 2023 22:34:42 -0400
Subject: [PATCH 191/518] Port SRB2Kart join on intermission fix

All gamestates besides GS_LEVEL are unsupported by the save game functions. This commit forces players joining during these gamestates into GS_WAITINGPLAYERS, which is a basic gamestate that just maintains the connection until we can enter the start of a new one. Also provides an extremely simple drawer for GS_WAITINGPLAYERS so the joining player knows what's going on.
---
 src/console.c  |  2 +-
 src/d_clisrv.c |  2 ++
 src/d_main.c   |  7 +++++++
 src/d_netcmd.c |  2 +-
 src/f_finale.c | 33 +++++++++++++++++++++++++++++++++
 src/f_finale.h |  4 ++++
 src/g_game.c   |  9 ++++++---
 src/p_saveg.c  |  6 +++++-
 8 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/src/console.c b/src/console.c
index 6d273f6207..119079464c 100644
--- a/src/console.c
+++ b/src/console.c
@@ -1889,7 +1889,7 @@ void CON_Drawer(void)
 		CON_DrawConsole();
 	else if (gamestate == GS_LEVEL
 	|| gamestate == GS_INTERMISSION || gamestate == GS_ENDING || gamestate == GS_CUTSCENE
-	|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION)
+	|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION || gamestate == GS_WAITINGPLAYERS)
 		CON_DrawHudlines();
 
 	Unlock_state();
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 2106191640..9b3187cbb7 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2600,6 +2600,8 @@ static void CL_ConnectToServer(void)
 	}
 	while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes))));
 
+	if (netgame)
+		F_StartWaitingPlayers();
 	DEBFILE(va("Synchronisation Finished\n"));
 
 	displayplayer = consoleplayer;
diff --git a/src/d_main.c b/src/d_main.c
index 5861f98865..2db4002580 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -458,6 +458,13 @@ static void D_Display(void)
 
 		case GS_WAITINGPLAYERS:
 			// The clientconnect drawer is independent...
+			if (netgame)
+			{
+				// I don't think HOM from nothing drawing is independent...
+				F_WaitingPlayersDrawer();
+				HU_Erase();
+				HU_Drawer();
+			}
 		case GS_DEDICATEDSERVER:
 		case GS_NULL:
 			break;
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index b23aaa5a0a..2cdf70843b 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -2174,7 +2174,7 @@ static void Command_Pause(void)
 
 	if (cv_pause.value || server || (IsPlayerAdmin(consoleplayer)))
 	{
-		if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) || (marathonmode && gamestate == GS_INTERMISSION))
+		if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_WAITINGPLAYERS) || (marathonmode && gamestate == GS_INTERMISSION))
 		{
 			CONS_Printf(M_GetText("You can't pause here.\n"));
 			return;
diff --git a/src/f_finale.c b/src/f_finale.c
index 299a6a054c..529244b5a6 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -4660,3 +4660,36 @@ void F_TextPromptTicker(void)
 			animtimer--;
 	}
 }
+
+// ================
+//  WAITINGPLAYERS
+// ================
+
+void F_StartWaitingPlayers(void)
+{
+	wipegamestate = GS_TITLESCREEN; // technically wiping from title screen
+	finalecount = 0;
+}
+
+void F_WaitingPlayersTicker(void)
+{
+	if (paused)
+		return;
+
+	finalecount++;
+
+	// dumb hack, only start the music on the 1st tick so if you instantly go into the map you aren't hearing a tic of music
+	if (finalecount == 2)
+		S_ChangeMusicInternal("_CHSEL", true);
+}
+
+void F_WaitingPlayersDrawer(void)
+{
+	const char *waittext1 = "You will join";
+	const char *waittext2 = "next level...";
+
+	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
+
+	V_DrawCreditString((160 - (V_CreditStringWidth(waittext1)>>1))<<FRACBITS, 48<<FRACBITS, 0, waittext1);
+	V_DrawCreditString((160 - (V_CreditStringWidth(waittext2)>>1))<<FRACBITS, 64<<FRACBITS, 0, waittext2);
+}
diff --git a/src/f_finale.h b/src/f_finale.h
index 7f53bfbad5..cb71775d05 100644
--- a/src/f_finale.h
+++ b/src/f_finale.h
@@ -74,6 +74,10 @@ void F_StartContinue(void);
 void F_ContinueTicker(void);
 void F_ContinueDrawer(void);
 
+void F_StartWaitingPlayers(void);
+void F_WaitingPlayersTicker(void);
+void F_WaitingPlayersDrawer(void);
+
 extern INT32 finalecount;
 extern INT32 titlescrollxspeed;
 extern INT32 titlescrollyspeed;
diff --git a/src/g_game.c b/src/g_game.c
index 47e670bfec..bcfe691051 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2437,14 +2437,17 @@ void G_Ticker(boolean run)
 		case GS_TITLESCREEN:
 			if (titlemapinaction)
 				P_Ticker(run);
-				// then intentionally fall through
-			/* FALLTHRU */
-		case GS_WAITINGPLAYERS:
 			if (run)
 				F_MenuPresTicker();
 			F_TitleScreenTicker(run);
 			break;
 
+		case GS_WAITINGPLAYERS:
+			if (netgame)
+				F_WaitingPlayersTicker();
+			HU_Ticker();
+			break;
+
 		case GS_DEDICATEDSERVER:
 		case GS_NULL:
 			break; // do nothing
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 40fd656386..62aa624fc1 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -4279,7 +4279,11 @@ static void P_NetArchiveMisc(boolean resending)
 	if (resending)
 		WRITEUINT32(save_p, gametic);
 	WRITEINT16(save_p, gamemap);
-	WRITEINT16(save_p, gamestate);
+
+	if (gamestate != GS_LEVEL)
+		WRITEINT16(save_p, GS_WAITINGPLAYERS); // nice hack to put people back into waitingplayers
+	else
+		WRITEINT16(save_p, gamestate);
 	WRITEINT16(save_p, gametype);
 
 	{
-- 
GitLab


From 4a735470b947550cd0a3aedeec9a35ed02f11dc8 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 17 Jul 2023 00:44:34 -0400
Subject: [PATCH 192/518] Even more SRB2Kart netcode improvement ports

- Prevent connection timeout during the waiting gamestate from the last commit.
- Keep client connections alive during fades / other internal loops.
- More consistently timeout clients when they reach the end of BACKUPTICS.
- Dedicated servers will not run any game logic if no nodes are sending packets to it, to reduce CPU usage when there is no one interacting with your server.
  - Unlike SRB2Kart, the amount of time is configurable with the "dedicatedidletime" console variable. Setting this to 0 will disable this feature.
- CL_SendClientCmd uses exact packet types instead of magic number offsets.
---
 src/d_clisrv.c | 254 ++++++++++++++++++++++++++++++++++++++++---------
 src/d_clisrv.h |   6 ++
 src/d_net.c    |   3 +
 src/d_netcmd.c |   1 +
 src/f_wipe.c   |   2 +
 src/g_game.c   |   1 +
 src/p_setup.c  |   1 +
 7 files changed, 225 insertions(+), 43 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 9b3187cbb7..13dab45390 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -120,6 +120,8 @@ UINT8 hu_redownloadinggamestate = 0;
 // true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks
 boolean hu_stopped = false;
 
+consvar_t cv_dedicatedidletime = CVAR_INIT ("dedicatedidletime", "10", CV_SAVE, CV_Unsigned, NULL);
+
 UINT8 adminpassmd5[16];
 boolean adminpasswordset = false;
 
@@ -4520,6 +4522,7 @@ static void HandlePacketFromPlayer(SINT8 node)
 		netconsole = 0;
 	else
 		netconsole = nodetoplayer[node];
+
 #ifdef PARANOIA
 	if (netconsole >= MAXPLAYERS)
 		I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
@@ -4558,15 +4561,20 @@ static void HandlePacketFromPlayer(SINT8 node)
 			// Update the nettics
 			nettics[node] = realend;
 
-			// Don't do anything for packets of type NODEKEEPALIVE?
-			if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
-				|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
+			// This should probably still timeout though, as the node should always have a player 1 number
+			if (netconsole == -1)
 				break;
 
 			// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
 			/// \todo Use a separate cvar for that kind of timeout?
 			freezetimeout[node] = I_GetTime() + connectiontimeout;
 
+			// Don't do anything for packets of type NODEKEEPALIVE?
+			// Sryder 2018/07/01: Update the freezetimeout still!
+			if (netbuffer->packettype == PT_NODEKEEPALIVE
+				|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
+				break;
+
 			// If we've alredy received a ticcmd for this tic, just submit it for the next one.
 			tic_t faketic = maketic;
 			if ((!!(netcmds[maketic % BACKUPTICS][netconsole].angleturn & TICCMD_RECEIVED))
@@ -4630,6 +4638,21 @@ static void HandlePacketFromPlayer(SINT8 node)
 				}
 			}
 			break;
+		case PT_BASICKEEPALIVE:
+			if (client)
+				break;
+
+			// This should probably still timeout though, as the node should always have a player 1 number
+			if (netconsole == -1)
+				break;
+
+			// If a client sends this it should mean they are done receiving the savegame
+			sendingsavegame[node] = false;
+
+			// As long as clients send keep alives, the server can keep running, so reset the timeout
+			/// \todo Use a separate cvar for that kind of timeout?
+			freezetimeout[node] = I_GetTime() + connectiontimeout;
+			break;
 		case PT_TEXTCMD2: // splitscreen special
 			netconsole = nodetoplayer2[node];
 			/* FALLTHRU */
@@ -5056,39 +5079,66 @@ static INT16 Consistancy(void)
 	return (INT16)(ret & 0xFFFF);
 }
 
+// confusing, but this DOESN'T send PT_NODEKEEPALIVE, it sends PT_BASICKEEPALIVE
+// used during wipes to tell the server that a node is still connected
+static void CL_SendClientKeepAlive(void)
+{
+	netbuffer->packettype = PT_BASICKEEPALIVE;
+
+	HSendPacket(servernode, false, 0, 0);
+}
+
+static void SV_SendServerKeepAlive(void)
+{
+	INT32 n;
+
+	for (n = 1; n < MAXNETNODES; n++)
+	{
+		if (nodeingame[n])
+		{
+			netbuffer->packettype = PT_BASICKEEPALIVE;
+			HSendPacket(n, false, 0, 0);
+		}
+	}
+}
+
 // send the client packet to the server
 static void CL_SendClientCmd(void)
 {
 	size_t packetsize = 0;
+	boolean mis = false;
 
 	netbuffer->packettype = PT_CLIENTCMD;
 
 	if (cl_packetmissed)
-		netbuffer->packettype++;
+	{
+		netbuffer->packettype = PT_CLIENTMIS;
+		mis = true;
+	}
+
 	netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX);
 	netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX);
 
 	if (gamestate == GS_WAITINGPLAYERS)
 	{
 		// Send PT_NODEKEEPALIVE packet
-		netbuffer->packettype += 4;
+		netbuffer->packettype = (mis ? PT_NODEKEEPALIVEMIS : PT_NODEKEEPALIVE);
 		packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16);
 		HSendPacket(servernode, false, 0, packetsize);
 	}
 	else if (gamestate != GS_NULL && (addedtogame || dedicated))
 	{
+		packetsize = sizeof (clientcmd_pak);
 		G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
 		netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
 
 		// Send a special packet with 2 cmd for splitscreen
 		if (splitscreen || botingame)
 		{
-			netbuffer->packettype += 2;
-			G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1);
+			netbuffer->packettype = (mis ? PT_CLIENT2MIS : PT_CLIENT2CMD);
 			packetsize = sizeof (client2cmd_pak);
+			G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1);
 		}
-		else
-			packetsize = sizeof (clientcmd_pak);
 
 		HSendPacket(servernode, false, 0, packetsize);
 	}
@@ -5099,7 +5149,7 @@ static void CL_SendClientCmd(void)
 		if (localtextcmd[0])
 		{
 			netbuffer->packettype = PT_TEXTCMD;
-			M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
+			M_Memcpy(netbuffer->u.textcmd, localtextcmd, localtextcmd[0]+1);
 			// All extra data have been sent
 			if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail...
 				localtextcmd[0] = 0;
@@ -5479,9 +5529,82 @@ static inline void PingUpdate(void)
 	pingmeasurecount = 1; //Reset count
 }
 
+static tic_t gametime = 0;
+
+static void UpdatePingTable(void)
+{
+	INT32 i;
+
+	if (server)
+	{
+		if (netgame && !(gametime % 35)) // update once per second.
+			PingUpdate();
+		// update node latency values so we can take an average later.
+		for (i = 0; i < MAXPLAYERS; i++)
+			if (playeringame[i] && playernode[i] != UINT8_MAX)
+				realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
+		pingmeasurecount++;
+	}
+}
+
+// Handle timeouts to prevent definitive freezes from happenning
+static void HandleNodeTimeouts(void)
+{
+	INT32 i;
+
+	if (server)
+	{
+		for (i = 1; i < MAXNETNODES; i++)
+			if (nodeingame[i] && freezetimeout[i] < I_GetTime())
+				Net_ConnectionTimeout(i);
+
+		// In case the cvar value was lowered
+		if (joindelay)
+			joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE);
+	}
+}
+
+// Keep the network alive while not advancing tics!
+void NetKeepAlive(void)
+{
+	tic_t nowtime;
+	INT32 realtics;
+
+	nowtime = I_GetTime();
+	realtics = nowtime - gametime;
+
+	// return if there's no time passed since the last call
+	if (realtics <= 0) // nothing new to update
+		return;
+
+	UpdatePingTable();
+
+	GetPackets();
+
+#ifdef MASTERSERVER
+	MasterClient_Ticker();
+#endif
+
+	if (client)
+	{
+		// send keep alive
+		CL_SendClientKeepAlive();
+		// No need to check for resynch because we aren't running any tics
+	}
+	else
+	{
+		SV_SendServerKeepAlive();
+	}
+
+	// No else because no tics are being run and we can't resynch during this
+
+	Net_AckTicker();
+	HandleNodeTimeouts();
+	FileSendTicker();
+}
+
 void NetUpdate(void)
 {
-	static tic_t gametime = 0;
 	static tic_t resptime = 0;
 	tic_t nowtime;
 	INT32 i;
@@ -5492,6 +5615,7 @@ void NetUpdate(void)
 
 	if (realtics <= 0) // nothing new to update
 		return;
+
 	if (realtics > 5)
 	{
 		if (server)
@@ -5500,19 +5624,72 @@ void NetUpdate(void)
 			realtics = 5;
 	}
 
-	gametime = nowtime;
-
-	if (server)
+	if (server && dedicated && gamestate == GS_LEVEL)
 	{
-		if (netgame && !(gametime % 35)) // update once per second.
-			PingUpdate();
-		// update node latency values so we can take an average later.
-		for (i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i] && playernode[i] != UINT8_MAX)
-				realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
-		pingmeasurecount++;
+		const tic_t dedicatedidletime = cv_dedicatedidletime.value * TICRATE;
+		static tic_t dedicatedidletimeprev = 0;
+		static tic_t dedicatedidle = 0;
+
+		if (dedicatedidletime > 0)
+		{
+			for (i = 1; i < MAXNETNODES; ++i)
+				if (nodeingame[i])
+				{
+					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;
 	}
 
+	gametime = nowtime;
+
+	UpdatePingTable();
+
 	if (client)
 		maketic = neededtic;
 
@@ -5543,24 +5720,25 @@ void NetUpdate(void)
 	}
 	else
 	{
-		if (!demoplayback)
+		if (!demoplayback && realtics > 0)
 		{
 			INT32 counts;
 
 			hu_redownloadinggamestate = false;
 
+			// Don't erase tics not acknowledged
+			counts = realtics;
+
 			firstticstosend = gametic;
 			for (i = 0; i < MAXNETNODES; i++)
-				if (nodeingame[i] && nettics[i] < firstticstosend)
-				{
+			{
+				if (!nodeingame[i])
+					continue;
+				if (nettics[i] < firstticstosend)
 					firstticstosend = nettics[i];
-
-					if (maketic + 1 >= nettics[i] + BACKUPTICS)
-						Net_ConnectionTimeout(i);
-				}
-
-			// Don't erase tics not acknowledged
-			counts = realtics;
+				if (maketic + counts >= nettics[i] + (BACKUPTICS - TICRATE))
+					Net_ConnectionTimeout(i);
+			}
 
 			if (maketic + counts >= firstticstosend + BACKUPTICS)
 				counts = firstticstosend+BACKUPTICS-maketic-1;
@@ -5578,20 +5756,10 @@ void NetUpdate(void)
 	}
 
 	Net_AckTicker();
-
-	// Handle timeouts to prevent definitive freezes from happenning
-	if (server)
-	{
-		for (i = 1; i < MAXNETNODES; i++)
-			if (nodeingame[i] && freezetimeout[i] < I_GetTime())
-				Net_ConnectionTimeout(i);
-
-		// In case the cvar value was lowered
-		if (joindelay)
-			joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE);
-	}
+	HandleNodeTimeouts();
 
 	nowtime /= NEWTICRATERATIO;
+
 	if (nowtime > resptime)
 	{
 		resptime = nowtime;
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index 04a7b5ba2f..49fb5fc1db 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -77,6 +77,8 @@ typedef enum
 	PT_ASKLUAFILE,     // Client telling the server they don't have the file
 	PT_HASLUAFILE,     // Client telling the server they have the file
 
+	PT_BASICKEEPALIVE,// Keep the network alive during wipes, as tics aren't advanced and NetUpdate isn't called
+
 	// Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
 
 	PT_CANFAIL,       // This is kind of a priority. Anything bigger than CANFAIL
@@ -398,6 +400,7 @@ extern tic_t servermaxping;
 extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_joindelay, cv_rejointimeout;
 extern consvar_t cv_resynchattempts, cv_blamecfail;
 extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
+extern consvar_t cv_dedicatedidletime;
 
 // Used in d_net, the only dependence
 tic_t ExpandTics(INT32 low, INT32 node);
@@ -412,6 +415,9 @@ void SendKick(UINT8 playernum, UINT8 msg);
 // Create any new ticcmds and broadcast to other players.
 void NetUpdate(void);
 
+// Maintain connections to nodes without timing them all out.
+void NetKeepAlive(void);
+
 void SV_StartSinglePlayerServer(void);
 boolean SV_SpawnServer(void);
 void SV_StopServer(void);
diff --git a/src/d_net.c b/src/d_net.c
index 768c9ac7eb..6d8c72942b 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -869,6 +869,9 @@ static void DebugPrintpacket(const char *header)
 				(UINT32)ExpandTics(netbuffer->u.clientpak.client_tic, doomcom->remotenode),
 				(UINT32)ExpandTics (netbuffer->u.clientpak.resendfrom, doomcom->remotenode));
 			break;
+		case PT_BASICKEEPALIVE:
+			fprintf(debugfile, "    wipetime\n");
+			break;
 		case PT_TEXTCMD:
 		case PT_TEXTCMD2:
 			fprintf(debugfile, "    length %d\n    ", netbuffer->u.textcmd[0]);
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 2cdf70843b..8fd207037b 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -599,6 +599,7 @@ void D_RegisterServerCommands(void)
 	CV_RegisterVar(&cv_joinnextround);
 	CV_RegisterVar(&cv_showjoinaddress);
 	CV_RegisterVar(&cv_blamecfail);
+	CV_RegisterVar(&cv_dedicatedidletime);
 #endif
 
 	COM_AddCommand("ping", Command_Ping_f, COM_LUA);
diff --git a/src/f_wipe.c b/src/f_wipe.c
index 6014fb7f94..4bcfb029b2 100644
--- a/src/f_wipe.c
+++ b/src/f_wipe.c
@@ -614,6 +614,8 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
 
 		if (moviemode)
 			M_SaveFrame();
+
+		NetKeepAlive(); // Update the network so we don't cause timeouts
 	}
 
 	WipeInAction = false;
diff --git a/src/g_game.c b/src/g_game.c
index bcfe691051..eea804c83b 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1915,6 +1915,7 @@ void G_PreLevelTitleCard(void)
 		ST_runTitleCard();
 		ST_preLevelTitleCardDrawer();
 		I_FinishUpdate(); // page flip or blit buffer
+		NetKeepAlive(); // Prevent timeouts
 
 		if (moviemode)
 			M_SaveFrame();
diff --git a/src/p_setup.c b/src/p_setup.c
index a10326986e..e4a46c0f4d 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -7436,6 +7436,7 @@ static void P_RunSpecialStageWipe(void)
 		lastwipetic = nowtime;
 		if (moviemode) // make sure we save frames for the white hold too
 			M_SaveFrame();
+		NetKeepAlive(); // Prevent timeout
 	}
 }
 
-- 
GitLab


From ff56d46827e9ff9ba71222248eda3483ac106cd6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Mon, 17 Jul 2023 16:41:22 +0200
Subject: [PATCH 193/518] fixup! Fix softlocking as Sonic & Tails when Player 2
 has no name

---
 src/d_clisrv.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 05af083bd2..9a8aedefac 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1311,7 +1311,10 @@ static boolean CL_SendJoin(void)
 	if (splitscreen)
 		CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */
 	// Avoid empty string on bots to avoid softlocking in singleplayer
-	player2name = botingame ? "a" : cv_playername2.zstring;
+	if (botingame)
+		player2name = strcmp(cv_playername.zstring, "Tails") == 0 ? "Tail" : "Tails";
+	else
+		player2name = cv_playername2.zstring;
 
 	strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME);
 	strncpy(netbuffer->u.clientcfg.names[1], player2name, MAXPLAYERNAME);
-- 
GitLab


From 8cf65e83018e02e4bcdea186082a9fc82f23e50c Mon Sep 17 00:00:00 2001
From: katsy <205-katsy@users.noreply.git.do.srb2.org>
Date: Wed, 19 Jul 2023 19:43:15 +0000
Subject: [PATCH 194/518] Fix inconsistency with title card patch coloring
 (color the title card patch used in the credits to skin)

---
 src/f_finale.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/src/f_finale.c b/src/f_finale.c
index 8dd03d44f5..2a551da7ee 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -1301,14 +1301,23 @@ void F_CreditDrawer(void)
 	UINT16 i;
 	INT16 zagpos = (timetonext - finalecount - animtimer) % 32;
 	fixed_t y = (80<<FRACBITS) - (animtimer<<FRACBITS>>1);
+	UINT8 colornum;
+	const UINT8 *colormap;
+
+	if (players[consoleplayer].skincolor)
+		colornum = players[consoleplayer].skincolor;
+	else
+		colornum = cv_playercolor.value;
+
+	colormap = R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE);
 
 	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
 
 	// Zig Zagz
-	V_DrawScaledPatch(-16,               zagpos,       V_SNAPTOLEFT,         W_CachePatchName("LTZIGZAG", PU_PATCH_LOWPRIORITY));
-	V_DrawScaledPatch(-16,               zagpos - 320, V_SNAPTOLEFT,         W_CachePatchName("LTZIGZAG", PU_PATCH_LOWPRIORITY));
-	V_DrawScaledPatch(BASEVIDWIDTH + 16, zagpos,       V_SNAPTORIGHT|V_FLIP, W_CachePatchName("LTZIGZAG", PU_PATCH_LOWPRIORITY));
-	V_DrawScaledPatch(BASEVIDWIDTH + 16, zagpos - 320, V_SNAPTORIGHT|V_FLIP, W_CachePatchName("LTZIGZAG", PU_PATCH_LOWPRIORITY));
+	V_DrawFixedPatch(-16*FRACUNIT, zagpos<<FRACBITS, FRACUNIT, V_SNAPTOLEFT, W_CachePatchName("LTZIGZAG", PU_PATCH_LOWPRIORITY), colormap);
+	V_DrawFixedPatch(-16*FRACUNIT, (zagpos - 320)<<FRACBITS, FRACUNIT, V_SNAPTOLEFT, W_CachePatchName("LTZIGZAG", PU_PATCH_LOWPRIORITY), colormap);
+	V_DrawFixedPatch((BASEVIDWIDTH + 16)*FRACUNIT, zagpos<<FRACBITS, FRACUNIT, V_SNAPTORIGHT|V_FLIP, W_CachePatchName("LTZIGZAG", PU_PATCH_LOWPRIORITY), colormap);
+	V_DrawFixedPatch((BASEVIDWIDTH + 16)*FRACUNIT, (zagpos - 320)<<FRACBITS, FRACUNIT, V_SNAPTORIGHT|V_FLIP, W_CachePatchName("LTZIGZAG", PU_PATCH_LOWPRIORITY), colormap);
 
 	// Draw background pictures first
 	for (i = 0; credits_pics[i].patch; i++)
-- 
GitLab


From cee5eb47b591b2b0a333198243e632607fb9585f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Wed, 19 Jul 2023 23:17:21 +0200
Subject: [PATCH 195/518] Clean up and simplify zone memory allocation

---
 src/z_zone.c | 225 +++++++++------------------------------------------
 1 file changed, 39 insertions(+), 186 deletions(-)

diff --git a/src/z_zone.c b/src/z_zone.c
index d23c43b7b5..5750f8ae01 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -51,27 +51,11 @@ static boolean Z_calloc = false;
 //#define ZDEBUG2
 #endif
 
-struct memblock_s;
-
-typedef struct
-{
-	struct memblock_s *block; // Describing this memory
-	UINT32 id; // Should be ZONEID
-} ATTRPACK memhdr_t;
-
-// Some code might want aligned memory. Assume it wants memory n bytes
-// aligned -- then we allocate n-1 extra bytes and return a pointer to
-// the first byte aligned as requested.
-// Thus, "real" is the pointer we get from malloc() and will free()
-// later, but "hdr" is where the memhdr_t starts.
-// For non-aligned allocations they will be the same.
 typedef struct memblock_s
 {
-	void *real;
-	memhdr_t *hdr;
-
 	void **user;
 	INT32 tag; // purgelevel
+	UINT32 id; // Should be ZONEID
 
 	size_t size; // including the header and blocks
 	size_t realsize; // size of real data only
@@ -82,7 +66,10 @@ typedef struct memblock_s
 #endif
 
 	struct memblock_s *next, *prev;
-} ATTRPACK memblock_t;
+} memblock_t;
+
+#define MEMORY(x) (void *)((uintptr_t)(x) + sizeof(memblock_t))
+#define MEMBLOCK(x) (memblock_t *)((uintptr_t)(x) - sizeof(memblock_t))
 
 // both the head and tail of the zone memory block list
 static memblock_t head;
@@ -128,64 +115,6 @@ void Z_Init(void)
 // Zone memory allocation
 // ----------------------
 
-/** Returns the corresponding memblock_t for a given memory block.
-  *
-  * \param ptr A pointer to allocated memory,
-  *             assumed to have been allocated with Z_Malloc/Z_Calloc.
-  * \param func A string containing the name of the function that called this,
-  *              to be printed if the function I_Errors
-  * \return A pointer to the memblock_t for the given memory.
-  * \sa Z_Free, Z_ReallocAlign
-  */
-#ifdef ZDEBUG
-#define Ptr2Memblock(s, f) Ptr2Memblock2(s, f, __FILE__, __LINE__)
-static memblock_t *Ptr2Memblock2(void *ptr, const char* func, const char *file, INT32 line)
-#else
-static memblock_t *Ptr2Memblock(void *ptr, const char* func)
-#endif
-{
-	memhdr_t *hdr;
-	memblock_t *block;
-
-	if (ptr == NULL)
-		return NULL;
-
-#ifdef ZDEBUG2
-	CONS_Printf("%s %s:%d\n", func, file, line);
-#endif
-
-	hdr = (memhdr_t *)((UINT8 *)ptr - sizeof *hdr);
-
-#ifdef VALGRIND_MAKE_MEM_DEFINED
-	VALGRIND_MAKE_MEM_DEFINED(hdr, sizeof *hdr);
-#endif
-
-#ifdef VALGRIND_MEMPOOL_EXISTS
-	if (!VALGRIND_MEMPOOL_EXISTS(hdr->block))
-	{
-#ifdef ZDEBUG
-		I_Error("%s: bad memblock from %s:%d", func, file, line);
-#else
-		I_Error("%s: bad memblock", func);
-#endif
-	}
-#endif
-	if (hdr->id != ZONEID)
-	{
-#ifdef ZDEBUG
-		I_Error("%s: wrong id from %s:%d", func, file, line);
-#else
-		I_Error("%s: wrong id", func);
-#endif
-	}
-	block = hdr->block;
-#ifdef VALGRIND_MAKE_MEM_NOACCESS
-	VALGRIND_MAKE_MEM_NOACCESS(hdr, sizeof *hdr);
-#endif
-	return block;
-
-}
-
 /** Frees allocated memory.
   *
   * \param ptr A pointer to allocated memory,
@@ -207,10 +136,14 @@ void Z_Free(void *ptr)
 	CONS_Debug(DBG_MEMORY, "Z_Free %s:%d\n", file, line);
 #endif
 
+	block = MEMBLOCK(ptr);
+#ifdef PARANOIA
+	if (block->id != ZONEID)
 #ifdef ZDEBUG
-	block = Ptr2Memblock2(ptr, "Z_Free", file, line);
+		I_Error("Z_Free at %s:%d: wrong id", file, line);
 #else
-	block = Ptr2Memblock(ptr, "Z_Free");
+		I_Error("Z_Free: wrong id");
+#endif
 #endif
 
 #ifdef ZDEBUG
@@ -229,8 +162,6 @@ void Z_Free(void *ptr)
 	if (block->user != NULL)
 		*block->user = NULL;
 
-	// Free the memory and get rid of the block.
-	free(block->real);
 #ifdef VALGRIND_DESTROY_MEMPOOL
 	VALGRIND_DESTROY_MEMPOOL(block);
 #endif
@@ -287,35 +218,17 @@ void *Z_Malloc2(size_t size, INT32 tag, void *user, INT32 alignbits,
 void *Z_MallocAlign(size_t size, INT32 tag, void *user, INT32 alignbits)
 #endif
 {
-	I_Assert(alignbits >= 0 && alignbits < (INT32)(sizeof(size_t) * 8));
-	size_t extrabytes = ((size_t)1<<alignbits) - 1;
-	size_t padsize = 0;
 	memblock_t *block;
 	void *ptr;
-	memhdr_t *hdr;
-	void *given;
-	size_t blocksize = extrabytes + sizeof *hdr + size;
+	(void)(alignbits); // no longer used, so silence warnings.
 
 #ifdef ZDEBUG2
 	CONS_Debug(DBG_MEMORY, "Z_Malloc %s:%d\n", file, line);
 #endif
 
-	if (blocksize < size)/* overflow check */
-		I_Error("You are allocating memory too large!");
-
-	block = xm(sizeof *block);
-#ifdef HAVE_VALGRIND
-	padsize += (1<<sizeof(size_t))*2;
-#endif
-	ptr = xm(blocksize + padsize*2);
-
-	// This horrible calculation makes sure that "given" is aligned
-	// properly.
-	given = (void *)((size_t)((UINT8 *)ptr + extrabytes + sizeof *hdr + padsize/2)
-		& ~extrabytes);
-
-	// The mem header lives 'sizeof (memhdr_t)' bytes before given.
-	hdr = (memhdr_t *)((UINT8 *)given - sizeof *hdr);
+	block = xm(sizeof (memblock_t) + size);
+	ptr = MEMORY(block);
+	I_Assert((intptr_t)ptr % sizeof (void *) == 0);
 
 #ifdef HAVE_VALGRIND
 	Z_calloc = false;
@@ -326,41 +239,31 @@ void *Z_MallocAlign(size_t size, INT32 tag, void *user, INT32 alignbits)
 	head.next = block;
 	block->next->prev = block;
 
-	block->real = ptr;
-	block->hdr = hdr;
 	block->tag = tag;
 	block->user = NULL;
 #ifdef ZDEBUG
 	block->ownerline = line;
 	block->ownerfile = file;
 #endif
-	block->size = blocksize;
+	block->size = sizeof (memblock_t) + size;
 	block->realsize = size;
 
 #ifdef VALGRIND_CREATE_MEMPOOL
-	VALGRIND_CREATE_MEMPOOL(block, padsize, Z_calloc);
+	VALGRIND_CREATE_MEMPOOL(block, size, Z_calloc);
 #endif
-//#ifdef VALGRIND_MEMPOOL_ALLOC
-//	VALGRIND_MEMPOOL_ALLOC(block, hdr, size + sizeof *hdr);
-//#endif
 
-	hdr->id = ZONEID;
-	hdr->block = block;
-
-#ifdef VALGRIND_MAKE_MEM_NOACCESS
-	VALGRIND_MAKE_MEM_NOACCESS(hdr, sizeof *hdr);
-#endif
+	block->id = ZONEID;
 
 	if (user != NULL)
 	{
 		block->user = user;
-		*(void **)user = given;
+		*(void **)user = ptr;
 	}
 	else if (tag >= PU_PURGELEVEL)
 		I_Error("Z_Malloc: attempted to allocate purgable block "
 			"(size %s) with no user", sizeu1(size));
 
-	return given;
+	return ptr;
 }
 
 /** The Z_CallocAlign function.
@@ -437,10 +340,14 @@ void *Z_ReallocAlign(void *ptr, size_t size, INT32 tag, void *user, INT32 alignb
 #endif
 	}
 
+	block = MEMBLOCK(ptr);
+#ifdef PARANOIA
+	if (block->id != ZONEID)
 #ifdef ZDEBUG
-	block = Ptr2Memblock2(ptr, "Z_Realloc", file, line);
+		I_Error("Z_ReallocAlign at %s:%d: wrong id", file, line);
 #else
-	block = Ptr2Memblock(ptr, "Z_Realloc");
+		I_Error("Z_ReallocAlign: wrong id");
+#endif
 #endif
 
 	if (block == NULL)
@@ -491,9 +398,8 @@ void Z_FreeTags(INT32 lowtag, INT32 hightag)
 	for (block = head.next; block != &head; block = next)
 	{
 		next = block->next; // get link before freeing
-
 		if (block->tag >= lowtag && block->tag <= hightag)
-			Z_Free((UINT8 *)block->hdr + sizeof *block->hdr);
+			Z_Free(MEMORY(block));
 	}
 }
 
@@ -516,7 +422,7 @@ void Z_IterateTags(INT32 lowtag, INT32 hightag, boolean (*iterfunc)(void *))
 
 		if (block->tag >= lowtag && block->tag <= hightag)
 		{
-			void *mem = (UINT8 *)block->hdr + sizeof *block->hdr;
+			void *mem = MEMORY(block);
 			boolean free = iterfunc(mem);
 			if (free)
 				Z_Free(mem);
@@ -561,15 +467,13 @@ void Z_CheckMemCleanup(void)
 void Z_CheckHeap(INT32 i)
 {
 	memblock_t *block;
-	memhdr_t *hdr;
 	UINT32 blocknumon = 0;
 	void *given;
 
 	for (block = head.next; block != &head; block = block->next)
 	{
 		blocknumon++;
-		hdr = block->hdr;
-		given = (UINT8 *)hdr + sizeof *hdr;
+		given = MEMORY(block);
 #ifdef ZDEBUG2
 		CONS_Debug(DBG_MEMORY, "block %u owned by %s:%d\n",
 			blocknumon, block->ownerfile, block->ownerline);
@@ -585,7 +489,7 @@ void Z_CheckHeap(INT32 i)
 #ifdef ZDEBUG
 				, block->ownerfile, block->ownerline
 #endif
-			        );
+				);
 		}
 #endif
 		if (block->user != NULL && *(block->user) != given)
@@ -598,7 +502,7 @@ void Z_CheckHeap(INT32 i)
 #ifdef ZDEBUG
 				, block->ownerfile, block->ownerline
 #endif
-			       );
+				);
 		}
 		if (block->next->prev != block)
 		{
@@ -610,7 +514,7 @@ void Z_CheckHeap(INT32 i)
 #ifdef ZDEBUG
 				, block->ownerfile, block->ownerline
 #endif
-			       );
+				);
 		}
 		if (block->prev->next != block)
 		{
@@ -622,25 +526,9 @@ void Z_CheckHeap(INT32 i)
 #ifdef ZDEBUG
 				, block->ownerfile, block->ownerline
 #endif
-			       );
+				);
 		}
-#ifdef VALGRIND_MAKE_MEM_DEFINED
-		VALGRIND_MAKE_MEM_DEFINED(hdr, sizeof *hdr);
-#endif
-		if (hdr->block != block)
-		{
-			I_Error("Z_CheckHeap %d: block %u"
-#ifdef ZDEBUG
-				"(owned by %s:%d)"
-#endif
-				" doesn't have linkback from allocated memory",
-				i, blocknumon
-#ifdef ZDEBUG
-				, block->ownerfile, block->ownerline
-#endif
-					);
-		}
-		if (hdr->id != ZONEID)
+		if (block->id != ZONEID)
 		{
 			I_Error("Z_CheckHeap %d: block %u"
 #ifdef ZDEBUG
@@ -650,11 +538,8 @@ void Z_CheckHeap(INT32 i)
 #ifdef ZDEBUG
 				, block->ownerfile, block->ownerline
 #endif
-					);
+				);
 		}
-#ifdef VALGRIND_MAKE_MEM_NOACCESS
-	VALGRIND_MAKE_MEM_NOACCESS(hdr, sizeof *hdr);
-#endif
 	}
 }
 
@@ -676,35 +561,14 @@ void Z_ChangeTag(void *ptr, INT32 tag)
 #endif
 {
 	memblock_t *block;
-	memhdr_t *hdr;
 
 	if (ptr == NULL)
 		return;
 
-	hdr = (memhdr_t *)((UINT8 *)ptr - sizeof *hdr);
-
-#ifdef VALGRIND_MAKE_MEM_DEFINED
-	VALGRIND_MAKE_MEM_DEFINED(hdr, sizeof *hdr);
-#endif
+	block = MEMBLOCK(ptr);
 
-#ifdef VALGRIND_MEMPOOL_EXISTS
-	if (!VALGRIND_MEMPOOL_EXISTS(hdr->block))
-	{
-#ifdef PARANOIA
-		I_Error("Z_CT at %s:%d: bad memblock", file, line);
-#else
-		I_Error("Z_CT: bad memblock");
-#endif
-	}
-#endif
 #ifdef PARANOIA
-	if (hdr->id != ZONEID) I_Error("Z_CT at %s:%d: wrong id", file, line);
-#endif
-
-	block = hdr->block;
-
-#ifdef VALGRIND_MAKE_MEM_NOACCESS
-	VALGRIND_MAKE_MEM_NOACCESS(hdr, sizeof *hdr);
+	if (block->id != ZONEID) I_Error("Z_ChangeTag at %s:%d: wrong id", file, line);
 #endif
 
 	if (tag >= PU_PURGELEVEL && block->user == NULL)
@@ -728,25 +592,14 @@ void Z_SetUser(void *ptr, void **newuser)
 #endif
 {
 	memblock_t *block;
-	memhdr_t *hdr;
 
 	if (ptr == NULL)
 		return;
 
-	hdr = (memhdr_t *)((UINT8 *)ptr - sizeof *hdr);
-
-#ifdef VALGRIND_MAKE_MEM_DEFINED
-	VALGRIND_MAKE_MEM_DEFINED(hdr, sizeof *hdr);
-#endif
+	block = MEMBLOCK(ptr);
 
 #ifdef PARANOIA
-	if (hdr->id != ZONEID) I_Error("Z_CT at %s:%d: wrong id", file, line);
-#endif
-
-	block = hdr->block;
-
-#ifdef VALGRIND_MAKE_MEM_NOACCESS
-	VALGRIND_MAKE_MEM_NOACCESS(hdr, sizeof *hdr);
+	if (block->id != ZONEID) I_Error("Z_SetUser at %s:%d: wrong id", file, line);
 #endif
 
 	if (block->tag >= PU_PURGELEVEL && newuser == NULL)
-- 
GitLab


From 36e6a2001a8a3f5ea3faf9add65a84ffc3ac08b9 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Thu, 20 Jul 2023 01:43:10 -0400
Subject: [PATCH 196/518] Paired down OS Lua library

Pretty much removed everything except for time operations. File operations steps on the toes of the IO library, and the others were useless or invasive. The only one I wasn't sure about was `os.tmpname()`, but I think that's something that could be implemented for IO library instead if necessary.

The main reason I want this is because os.time provides an OK way to initially seed RNG on game first boot. But it would also be cute to let Lua modders be able to re-implement Christmas mode / day&night cycles themselves :p
---
 src/blua/Sourcefile |   1 +
 src/blua/linit.c    |   1 +
 src/blua/loslib.c   | 167 ++++++++++++++++++++++++++++++++++++++++++++
 src/blua/lualib.h   |   3 +
 4 files changed, 172 insertions(+)
 create mode 100644 src/blua/loslib.c

diff --git a/src/blua/Sourcefile b/src/blua/Sourcefile
index f99c89c8df..dae9431098 100644
--- a/src/blua/Sourcefile
+++ b/src/blua/Sourcefile
@@ -23,3 +23,4 @@ lstring.c
 ltable.c
 ltm.c
 lvm.c
+loslib.c
diff --git a/src/blua/linit.c b/src/blua/linit.c
index d17390b20a..dcf05d9f2c 100644
--- a/src/blua/linit.c
+++ b/src/blua/linit.c
@@ -18,6 +18,7 @@ static const luaL_Reg lualibs[] = {
   {"", luaopen_base},
   {LUA_TABLIBNAME, luaopen_table},
   {LUA_IOLIBNAME, luaopen_io},
+  {LUA_OSLIBNAME, luaopen_os},
   {LUA_STRLIBNAME, luaopen_string},
   {NULL, NULL}
 };
diff --git a/src/blua/loslib.c b/src/blua/loslib.c
new file mode 100644
index 0000000000..ba2e1e3846
--- /dev/null
+++ b/src/blua/loslib.c
@@ -0,0 +1,167 @@
+/*
+** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $
+** Standard Operating System library
+** See Copyright Notice in lua.h
+*/
+
+
+#include <errno.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define loslib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+static int os_clock (lua_State *L) {
+  lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
+  return 1;
+}
+
+
+/*
+** {======================================================
+** Time/Date operations
+** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
+**   wday=%w+1, yday=%j, isdst=? }
+** =======================================================
+*/
+
+static void setfield (lua_State *L, const char *key, int value) {
+  lua_pushinteger(L, value);
+  lua_setfield(L, -2, key);
+}
+
+static void setboolfield (lua_State *L, const char *key, int value) {
+  if (value < 0)  /* undefined? */
+    return;  /* does not set field */
+  lua_pushboolean(L, value);
+  lua_setfield(L, -2, key);
+}
+
+static int getboolfield (lua_State *L, const char *key) {
+  int res;
+  lua_getfield(L, -1, key);
+  res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);
+  lua_pop(L, 1);
+  return res;
+}
+
+
+static int getfield (lua_State *L, const char *key, int d) {
+  int res;
+  lua_getfield(L, -1, key);
+  if (lua_isnumber(L, -1))
+    res = (int)lua_tointeger(L, -1);
+  else {
+    if (d < 0)
+      return luaL_error(L, "field " LUA_QS " missing in date table", key);
+    res = d;
+  }
+  lua_pop(L, 1);
+  return res;
+}
+
+
+static int os_date (lua_State *L) {
+  const char *s = luaL_optstring(L, 1, "%c");
+  time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
+  struct tm *stm;
+  if (*s == '!') {  /* UTC? */
+    stm = gmtime(&t);
+    s++;  /* skip `!' */
+  }
+  else
+    stm = localtime(&t);
+  if (stm == NULL)  /* invalid date? */
+    lua_pushnil(L);
+  else if (strcmp(s, "*t") == 0) {
+    lua_createtable(L, 0, 9);  /* 9 = number of fields */
+    setfield(L, "sec", stm->tm_sec);
+    setfield(L, "min", stm->tm_min);
+    setfield(L, "hour", stm->tm_hour);
+    setfield(L, "day", stm->tm_mday);
+    setfield(L, "month", stm->tm_mon+1);
+    setfield(L, "year", stm->tm_year+1900);
+    setfield(L, "wday", stm->tm_wday+1);
+    setfield(L, "yday", stm->tm_yday+1);
+    setboolfield(L, "isdst", stm->tm_isdst);
+  }
+  else {
+    char cc[3];
+    luaL_Buffer b;
+    cc[0] = '%'; cc[2] = '\0';
+    luaL_buffinit(L, &b);
+    for (; *s; s++) {
+      if (*s != '%' || *(s + 1) == '\0')  /* no conversion specifier? */
+        luaL_addchar(&b, *s);
+      else {
+        size_t reslen;
+        char buff[200];  /* should be big enough for any conversion result */
+        cc[1] = *(++s);
+        reslen = strftime(buff, sizeof(buff), cc, stm);
+        luaL_addlstring(&b, buff, reslen);
+      }
+    }
+    luaL_pushresult(&b);
+  }
+  return 1;
+}
+
+
+static int os_time (lua_State *L) {
+  time_t t;
+  if (lua_isnoneornil(L, 1))  /* called without args? */
+    t = time(NULL);  /* get current time */
+  else {
+    struct tm ts;
+    luaL_checktype(L, 1, LUA_TTABLE);
+    lua_settop(L, 1);  /* make sure table is at the top */
+    ts.tm_sec = getfield(L, "sec", 0);
+    ts.tm_min = getfield(L, "min", 0);
+    ts.tm_hour = getfield(L, "hour", 12);
+    ts.tm_mday = getfield(L, "day", -1);
+    ts.tm_mon = getfield(L, "month", -1) - 1;
+    ts.tm_year = getfield(L, "year", -1) - 1900;
+    ts.tm_isdst = getboolfield(L, "isdst");
+    t = mktime(&ts);
+  }
+  if (t == (time_t)(-1))
+    lua_pushnil(L);
+  else
+    lua_pushnumber(L, (lua_Number)t);
+  return 1;
+}
+
+
+static int os_difftime (lua_State *L) {
+  lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),
+                             (time_t)(luaL_optnumber(L, 2, 0))));
+  return 1;
+}
+
+/* }====================================================== */
+
+static const luaL_Reg syslib[] = {
+  {"clock",     os_clock},
+  {"date",      os_date},
+  {"difftime",  os_difftime},
+  {"time",      os_time},
+  {NULL, NULL}
+};
+
+/* }====================================================== */
+
+
+
+LUALIB_API int luaopen_os (lua_State *L) {
+  luaL_register(L, LUA_OSLIBNAME, syslib);
+  return 1;
+}
diff --git a/src/blua/lualib.h b/src/blua/lualib.h
index 4ea97edf3d..7127e4d77e 100644
--- a/src/blua/lualib.h
+++ b/src/blua/lualib.h
@@ -24,6 +24,9 @@ LUALIB_API int (luaopen_table) (lua_State *L);
 #define LUA_IOLIBNAME	"io"
 LUALIB_API int (luaopen_io) (lua_State *L);
 
+#define LUA_OSLIBNAME	"os"
+LUALIB_API int (luaopen_os) (lua_State *L);
+
 #define LUA_STRLIBNAME	"string"
 LUALIB_API int (luaopen_string) (lua_State *L);
 
-- 
GitLab


From dee24896205c4ed9ae18662bdf8c4aa4a9865770 Mon Sep 17 00:00:00 2001
From: toaster <rollerorbital@gmail.com>
Date: Thu, 20 Jul 2023 17:54:47 +0100
Subject: [PATCH 197/518] P_GetNodeType: Do not dereference invalid pointer for
 **nodedata parameter

---
 src/p_setup.c | 52 ++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 37 insertions(+), 15 deletions(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index a10326986e..3c83c6c911 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -3205,10 +3205,17 @@ static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata)
 	nodetype_t nodetype = NT_UNSUPPORTED;
 	char signature[4 + 1];
 
+	*nodedata = NULL;
+
 	if (udmf)
 	{
-		*nodedata = vres_Find(virt, "ZNODES")->data;
-		supported[NT_XGLN] = supported[NT_XGL3] = true;
+		virtlump_t *virtznodes = vres_Find(virt, "ZNODES");
+
+		if (virtznodes && virtznodes->size)
+		{
+			*nodedata = virtznodes->data;
+			supported[NT_XGLN] = supported[NT_XGL3] = true;
+		}
 	}
 	else
 	{
@@ -3217,24 +3224,39 @@ static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata)
 
 		if (virtsegs && virtsegs->size)
 		{
-			*nodedata = vres_Find(virt, "NODES")->data;
-			return NT_DOOM; // Traditional map format BSP tree.
-		}
-
-		virtssectors = vres_Find(virt, "SSECTORS");
-
-		if (virtssectors && virtssectors->size)
-		{ // Possibly GL nodes: NODES ignored, SSECTORS takes precedence as nodes lump, (It is confusing yeah) and has a signature.
-			*nodedata = virtssectors->data;
-			supported[NT_XGLN] = supported[NT_ZGLN] = supported[NT_XGL3] = true;
+			virtlump_t *virtnodes = vres_Find(virt, "NODES");
+			if (virtnodes && virtnodes->size)
+			{
+				*nodedata = virtnodes->data;
+				return NT_DOOM; // Traditional map format BSP tree.
+			}
 		}
 		else
-		{ // Possibly ZDoom extended nodes: SSECTORS is empty, NODES has a signature.
-			*nodedata = vres_Find(virt, "NODES")->data;
-			supported[NT_XNOD] = supported[NT_ZNOD] = true;
+		{
+			virtssectors = vres_Find(virt, "SSECTORS");
+
+			if (virtssectors && virtssectors->size)
+			{ // Possibly GL nodes: NODES ignored, SSECTORS takes precedence as nodes lump, (It is confusing yeah) and has a signature.
+				*nodedata = virtssectors->data;
+				supported[NT_XGLN] = supported[NT_ZGLN] = supported[NT_XGL3] = true;
+			}
+			else
+			{ // Possibly ZDoom extended nodes: SSECTORS is empty, NODES has a signature.
+				virtlump_t *virtnodes = vres_Find(virt, "NODES");
+				if (virtnodes && virtnodes->size)
+				{
+					*nodedata = virtnodes->data;
+					supported[NT_XNOD] = supported[NT_ZNOD] = true;
+				}
+			}
 		}
 	}
 
+	if (*nodedata == NULL)
+	{
+		I_Error("Level has no nodes (does your map have at least 2 sectors?)");
+	}
+
 	M_Memcpy(signature, *nodedata, 4);
 	signature[4] = '\0';
 	(*nodedata) += 4;
-- 
GitLab


From 66dd952fdd854800832aa563047c17e813a36c71 Mon Sep 17 00:00:00 2001
From: toaster <rollerorbital@gmail.com>
Date: Thu, 20 Jul 2023 19:42:21 +0100
Subject: [PATCH 198/518] Tidier conversion of string properties on linedefs to
 UDMF

Previously, many concatenated texture field strings were turned to text, then had that string run through get_number, then had it converted back into a string to become a stringarg.

Now, the concatenated string is copied directly to the relevant stringarg, with no intermediary steps.

This fixes an issue where a map with object or state properties would have "context drift" - breaking when the object or state list changed, due to differently ordered freeslots, or new hardcoded objects.

Affected types:
- doomednum 1110/Action 9 (Custom Mace/Chain)
- doomednum 700 and those immediately following (Ambient Sound)
- Action 4 and 414 (dash pad, play sound effect)
- Action 14 (bustable block parameters)
- Action 434 (Award Power)
- doomednum 757/Action 15 (fan particle generator)
- doomednum 1202 (bumpable hazard rock/apple spawner)
    - This one has undefined behaviour in the binary map format which was not previously forbidden. This undefined behaviour is EXTREMELY vulnerable to context drift, and so it's simply not worth creating a system to write numerical values into object types - we write an explicit name only for the intended range, and otherwise report the threat of context drift when converting.

In addition, to reduce duplicated zone memory, (sidedef_t).text and (linedef_t).text have been removed from all but the Lua API. In Lua, in binary maps, they point to the host line's stringargs - the line's text and a frontside's text will return stringargs[0], while a backside's text will return stringargs[1]. I've done my best to match the previous API as closely possible, to the point of reporting false nils if the line didn't previously have text available.

However, there are four linedef Actions for which the sidedef text will be different between builds containing and not containing this commit - 331, 332, 333 (the Character-specific linedef executors), and 443 (Call Lua Script), and only if the text is long enough to go over two lines. Given that both halves would be incomplete nonsense in this case, I'm willing to wager this minor point of discrepancy is not a breaking issue.
---
 src/lua_maplib.c |  35 +++++-
 src/p_setup.c    | 322 ++++++++++++++++++++++++++---------------------
 src/r_defs.h     |   3 -
 3 files changed, 208 insertions(+), 152 deletions(-)

diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index f73b3c7d25..904b4316b4 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -1032,8 +1032,24 @@ static int line_get(lua_State *L)
 		LUA_PushUserdata(L, line->polyobj, META_POLYOBJ);
 		return 1;
 	case line_text:
-		lua_pushstring(L, line->text);
-		return 1;
+		{
+			if (udmf)
+			{
+				LUA_Deprecated(L, "(linedef_t).text", "(linedef_t).stringargs");
+				lua_pushnil(L);
+				return 1;
+			}
+
+			if (line->special == 331 || line->special == 443)
+			{
+				// See P_ProcessLinedefsAfterSidedefs, P_ConvertBinaryLinedefTypes
+				lua_pushstring(L, line->stringargs[0]);
+			}
+			else
+				lua_pushnil(L);
+
+			return 1;
+		}
 	case line_callcount:
 		lua_pushinteger(L, line->callcount);
 		return 1;
@@ -1149,8 +1165,19 @@ static int side_get(lua_State *L)
 		lua_pushinteger(L, side->repeatcnt);
 		return 1;
 	case side_text:
-		lua_pushstring(L, side->text);
-		return 1;
+		{
+			if (udmf)
+			{
+				LUA_Deprecated(L, "(sidedef_t).text", "(sidedef_t).line.stringargs");
+				lua_pushnil(L);
+				return 1;
+			}
+
+			boolean isfrontside = side->line->sidenum[0] == side-sides;
+
+			lua_pushstring(L, side->line->stringargs[isfrontside ? 0 : 1]);
+			return 1;
+		}
 	}
 	return 0;
 }
diff --git a/src/p_setup.c b/src/p_setup.c
index 3c83c6c911..27ddb07757 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1103,7 +1103,6 @@ static void P_InitializeLinedef(line_t *ld)
 	ld->validcount = 0;
 	ld->polyobj = NULL;
 
-	ld->text = NULL;
 	ld->callcount = 0;
 
 	// cph 2006/09/30 - fix sidedef errors right away.
@@ -1207,10 +1206,108 @@ static void P_InitializeSidedef(side_t *sd)
 		sd->special = sd->line->special;
 	}
 
-	sd->text = NULL;
 	sd->colormap_data = NULL;
 }
 
+static boolean contextdrift = false;
+
+// In Ring Racers, this is just the reference implementation.
+// But in SRB2, backwards compatibility is a design goal!?
+// So we let the map function on load, but report that there's
+// an issue which could prevent saving it as a UDMF map.
+//
+// ... "context drift" is a term I learned from the story "Lena",
+// styled after a wikipedia article on a man whose brain scan
+// spawns an industry of digital brain emulation. It is a term
+// to describe that the understanding of the world MMAcevedo has
+// is rooted in a specific moment and time and will lose relevance
+// as societal norms, languages, and events change and are changed.
+// It is on the real wikipedia, but under the name "concept drift".
+// I am connecting the idea of the rooted-in-time brainstate with
+// the numerical evaluation of get_number(), the ground truth
+// CONTEXT for all these silly integers, that will trip up future
+// builds of SRB2 that have modified states and object lists.
+// Golden statues rotating in place of carousel rides is perhaps
+// not quite what qntm meant, but is a TEXTMAP, a human-readable
+// document for imparting a sense of place made/read by machines,
+// not its own language providing a vulnerable context?
+// ~toast 200723, see https://qntm.org/mmacevedo
+//
+static void P_WriteConstant(INT32 constant, char **target, const char *desc, UINT32 id)
+{
+	char buffer[12];
+	size_t len;
+
+	CONS_Alert(
+		CONS_WARNING, M_GetText(
+		"P_WriteConstant has been called for %s %d, "
+		"this level is vulnerable to context drift "
+		"if -writetextmap is used.\n"),
+		desc, id
+	);
+
+	contextdrift = true;
+
+	sprintf(buffer, "%d", constant);
+	len = strlen(buffer) + 1;
+	*target = Z_Malloc(len, PU_LEVEL, NULL);
+	M_Memcpy(*target, buffer, len);
+}
+
+static void P_WriteDuplicateText(const char *text, char **target)
+{
+	if (text == NULL || text[0] == '\0')
+		return;
+
+	size_t len = strlen(text) + 1;
+	*target = Z_Malloc(len, PU_LEVEL, NULL);
+	M_Memcpy(*target, text, len);
+}
+
+static void P_WriteSkincolor(INT32 constant, char **target)
+{
+	if (constant <= SKINCOLOR_NONE
+	|| constant >= (INT32)numskincolors)
+		return;
+
+	P_WriteDuplicateText(
+		va("SKINCOLOR_%s", skincolors[constant].name),
+		target
+	);
+}
+
+static void P_WriteSfx(INT32 constant, char **target)
+{
+	if (constant <= sfx_None
+	|| constant >= (INT32)sfxfree)
+		return;
+
+	P_WriteDuplicateText(
+		va("sfx_%s", S_sfx[constant].name),
+		target
+	);
+}
+
+static void P_WriteTics(INT32 tics, char **target)
+{
+	if (!tics)
+		return;
+
+	INT32 seconds = (tics / TICRATE);
+	tics %= TICRATE;
+
+	const char *text;
+
+	if (tics && !seconds)
+		text = va("%d", tics);
+	else if (seconds && !tics)
+		text = va("(%d*TICRATE)", seconds);
+	else
+		text = va("(%d*TICRATE)%s%d", seconds, (tics > 0) ? "+" : "", tics);
+
+	P_WriteDuplicateText(text, target);
+}
+
 static void P_LoadSidedefs(UINT8 *data)
 {
 	mapsidedef_t *msd = (mapsidedef_t*)data;
@@ -1259,9 +1356,13 @@ static void P_LoadSidedefs(UINT8 *data)
 
 			case 413: // Change music
 			{
+				sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
+
+				if (!isfrontside)
+					break;
+
 				char process[8+1];
 
-				sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
 				if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
 				{
 					M_Memcpy(process,msd->bottomtexture,8);
@@ -1276,48 +1377,39 @@ static void P_LoadSidedefs(UINT8 *data)
 					sd->midtexture = get_number(process);
 				}
 
-				sd->text = Z_Malloc(7, PU_LEVEL, NULL);
-				if (isfrontside && !(msd->toptexture[0] == '-' && msd->toptexture[1] == '\0'))
+				if (msd->toptexture[0] != '-' && msd->toptexture[1] != '\0')
 				{
+					sd->line->stringargs[0] = Z_Malloc(7, PU_LEVEL, NULL);
+
 					M_Memcpy(process,msd->toptexture,8);
 					process[8] = '\0';
 
 					// If they type in O_ or D_ and their music name, just shrug,
 					// then copy the rest instead.
 					if ((process[0] == 'O' || process[0] == 'D') && process[7])
-						M_Memcpy(sd->text, process+2, 6);
+						M_Memcpy(sd->line->stringargs[0], process+2, 6);
 					else // Assume it's a proper music name.
-						M_Memcpy(sd->text, process, 6);
-					sd->text[6] = 0;
+						M_Memcpy(sd->line->stringargs[0], process, 6);
+					sd->line->stringargs[0][6] = '\0';
 				}
-				else
-					sd->text[0] = 0;
-				break;
-			}
 
-			case 4: // Speed pad parameters
-			{
-				sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
-				if (msd->toptexture[0] != '-' || msd->toptexture[1] != '\0')
-				{
-					char process[8+1];
-					M_Memcpy(process,msd->toptexture,8);
-					process[8] = '\0';
-					sd->toptexture = get_number(process);
-				}
 				break;
 			}
 
+			case 4: // Speed pad parameters
 			case 414: // Play SFX
 			{
 				sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
+
+				if (!isfrontside)
+					break;
+
 				if (msd->toptexture[0] != '-' || msd->toptexture[1] != '\0')
 				{
 					char process[8 + 1];
 					M_Memcpy(process, msd->toptexture, 8);
 					process[8] = '\0';
-					sd->text = Z_Malloc(strlen(process) + 1, PU_LEVEL, NULL);
-					M_Memcpy(sd->text, process, strlen(process) + 1);
+					P_WriteDuplicateText(process, &sd->line->stringargs[0]);
 				}
 				break;
 			}
@@ -1326,21 +1418,13 @@ static void P_LoadSidedefs(UINT8 *data)
 			case 14: // Bustable block parameters
 			case 15: // Fan particle spawner parameters
 			{
-				char process[8*3+1];
-				memset(process,0,8*3+1);
-				sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
-				if (msd->toptexture[0] == '-' && msd->toptexture[1] == '\0')
+				if (msd->toptexture[7] == '\0' && strcasecmp(msd->toptexture, "MT_NULL") == 0)
+				{
+					// Don't bulk the conversion with irrelevant types
 					break;
-				else
-					M_Memcpy(process,msd->toptexture,8);
-				if (msd->midtexture[0] != '-' || msd->midtexture[1] != '\0')
-					M_Memcpy(process+strlen(process), msd->midtexture, 8);
-				if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
-					M_Memcpy(process+strlen(process), msd->bottomtexture, 8);
-				sd->toptexture = get_number(process);
-				break;
+				}
 			}
-
+			// FALLTHRU
 			case 331: // Trigger linedef executor: Skin - Continuous
 			case 332: // Trigger linedef executor: Skin - Each time
 			case 333: // Trigger linedef executor: Skin - Once
@@ -1366,8 +1450,11 @@ static void P_LoadSidedefs(UINT8 *data)
 					M_Memcpy(process+strlen(process), msd->midtexture, 8);
 				if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
 					M_Memcpy(process+strlen(process), msd->bottomtexture, 8);
-				sd->text = Z_Malloc(strlen(process)+1, PU_LEVEL, NULL);
-				M_Memcpy(sd->text, process, strlen(process)+1);
+
+				P_WriteDuplicateText(
+					process,
+					&sd->line->stringargs[(isfrontside) ? 0 : 1]
+				);
 				break;
 			}
 
@@ -2716,6 +2803,15 @@ static void P_WriteTextmap(void)
 	Z_Free(wlines);
 	Z_Free(wsides);
 	Z_Free(specialthings);
+
+	if (contextdrift)
+	{
+		CONS_Alert(
+			CONS_WARNING, M_GetText(
+			"Some elements of this level are vulnerable to context drift."
+			"Please check the console log for more information.\n")
+		);
+	}
 }
 
 /** Loads the textmap data, after obtaining the elements count and allocating their respective space.
@@ -2909,15 +3005,20 @@ static void P_ProcessLinedefsAfterSidedefs(void)
 		case 332: // Trigger linedef executor: Skin - Each time
 		case 333: // Trigger linedef executor: Skin - Once
 		case 443: // Calls a named Lua function
-			if (sides[ld->sidenum[0]].text)
+			if (ld->stringargs[0] && ld->stringargs[1])
 			{
-				size_t len = strlen(sides[ld->sidenum[0]].text) + 1;
-				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
-					len += strlen(sides[ld->sidenum[1]].text);
-				ld->text = Z_Malloc(len, PU_LEVEL, NULL);
-				M_Memcpy(ld->text, sides[ld->sidenum[0]].text, strlen(sides[ld->sidenum[0]].text) + 1);
-				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
-					M_Memcpy(ld->text + strlen(ld->text) + 1, sides[ld->sidenum[1]].text, strlen(sides[ld->sidenum[1]].text) + 1);
+				size_t len[2];
+				len[0] = strlen(ld->stringargs[0]);
+				len[1] = strlen(ld->stringargs[1]);
+
+				if (len[1])
+				{
+					ld->stringargs[0] = Z_Realloc(ld->stringargs[0], len[0] + len[1] + 1, PU_LEVEL, NULL);
+					M_Memcpy(ld->stringargs[0] + len[0] + 1, ld->stringargs[1], len[1] + 1);
+				}
+
+				Z_Free(ld->stringargs[1]);
+				ld->stringargs[1] = NULL;
 			}
 			break;
 		case 447: // Change colormap
@@ -4033,14 +4134,6 @@ static void P_AddBinaryMapTags(void)
 	}
 }
 
-static void P_WriteConstant(INT32 constant, char **target)
-{
-	char buffer[12];
-	sprintf(buffer, "%d", constant);
-	*target = Z_Malloc(strlen(buffer) + 1, PU_LEVEL, NULL);
-	M_Memcpy(*target, buffer, strlen(buffer) + 1);
-}
-
 static line_t *P_FindPointPushLine(taglist_t *list)
 {
 	INT32 i, l;
@@ -4183,7 +4276,6 @@ static void P_ConvertBinaryLinedefTypes(void)
 				lines[i].args[1] |= TMSP_NOTELEPORT;
 			if (lines[i].flags & ML_WRAPMIDTEX)
 				lines[i].args[1] |= TMSP_FORCESPIN;
-			P_WriteConstant(sides[lines[i].sidenum[0]].toptexture ? sides[lines[i].sidenum[0]].toptexture : sfx_spdpad, &lines[i].stringargs[0]);
 			break;
 		case 7: //Sector flat alignment
 			lines[i].args[0] = tag;
@@ -4269,8 +4361,6 @@ static void P_ConvertBinaryLinedefTypes(void)
 			lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
 			lines[i].args[1] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
 			lines[i].args[2] = !!(lines[i].flags & ML_SKEWTD);
-			if (sides[lines[i].sidenum[0]].toptexture)
-				P_WriteConstant(sides[lines[i].sidenum[0]].toptexture, &lines[i].stringargs[0]);
 			break;
 		case 16: //Minecart parameters
 			lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
@@ -4733,7 +4823,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 				lines[i].args[2] = 16;
 			}
 			if (lines[i].flags & ML_MIDSOLID)
-				P_WriteConstant(sides[lines[i].sidenum[0]].textureoffset >> FRACBITS, &lines[i].stringargs[0]);
+				P_WriteSfx(sides[lines[i].sidenum[0]].textureoffset >> FRACBITS, &lines[i].stringargs[0]);
 			break;
 		case 252: //FOF: Shatter block
 		case 253: //FOF: Shatter block, translucent
@@ -5000,11 +5090,6 @@ static void P_ConvertBinaryLinedefTypes(void)
 			else
 				lines[i].args[0] = TMT_CONTINUOUS;
 			lines[i].args[1] = !!(lines[i].flags & ML_NOCLIMB);
-			if (lines[i].text)
-			{
-				lines[i].stringargs[0] = Z_Malloc(strlen(lines[i].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[0], lines[i].text, strlen(lines[i].text) + 1);
-			}
 			lines[i].special = 331;
 			break;
 		case 334: // Object dye - continuous
@@ -5017,11 +5102,6 @@ static void P_ConvertBinaryLinedefTypes(void)
 			else
 				lines[i].args[0] = TMT_CONTINUOUS;
 			lines[i].args[1] = !!(lines[i].flags & ML_NOCLIMB);
-			if (sides[lines[i].sidenum[0]].text)
-			{
-				lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1);
-			}
 			lines[i].special = 334;
 			break;
 		case 337: //Emerald check - continuous
@@ -5168,11 +5248,6 @@ static void P_ConvertBinaryLinedefTypes(void)
 			lines[i].args[4] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : 0;
 			lines[i].args[5] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].rowoffset >> FRACBITS : -1;
 			lines[i].args[6] = sides[lines[i].sidenum[0]].bottomtexture;
-			if (sides[lines[i].sidenum[0]].text)
-			{
-				lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1);
-			}
 			break;
 		case 414: //Play sound effect
 			lines[i].args[2] = tag;
@@ -5212,11 +5287,6 @@ static void P_ConvertBinaryLinedefTypes(void)
 					lines[i].args[1] = TMSL_EVERYONE;
 				}
 			}
-			if (sides[lines[i].sidenum[0]].text)
-			{
-				lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1);
-			}
 			break;
 		case 415: //Run script
 		{
@@ -5307,13 +5377,6 @@ static void P_ConvertBinaryLinedefTypes(void)
 			lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
 			lines[i].args[1] = !!(lines[i].flags & ML_NOCLIMB);
 			break;
-		case 425: //Change object state
-			if (sides[lines[i].sidenum[0]].text)
-			{
-				lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1);
-			}
-			break;
 		case 426: //Stop object
 			lines[i].args[0] = !!(lines[i].flags & ML_NOCLIMB);
 			break;
@@ -5353,18 +5416,18 @@ static void P_ConvertBinaryLinedefTypes(void)
 			lines[i].args[2] = !!(lines[i].flags & ML_BLOCKMONSTERS);
 			break;
 		case 434: //Award power-up
-			if (sides[lines[i].sidenum[0]].text)
-			{
-				lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1);
-			}
-			if (lines[i].sidenum[1] != 0xffff && lines[i].flags & ML_BLOCKMONSTERS) // read power from back sidedef
+			if ((lines[i].flags & ML_BLOCKMONSTERS) == 0)
 			{
-				lines[i].stringargs[1] = Z_Malloc(strlen(sides[lines[i].sidenum[1]].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[1], sides[lines[i].sidenum[1]].text, strlen(sides[lines[i].sidenum[1]].text) + 1);
+				// do NOT read power from back sidedef if this flag is not present
+				if (lines[i].stringargs[1] != NULL)
+				{
+					Z_Free(lines[i].stringargs[1]);
+					lines[i].stringargs[1] = NULL;
+				}
+
+				// Instead... write the desired time!
+				P_WriteTics((lines[i].flags & ML_NOCLIMB) ? -1 : (sides[lines[i].sidenum[0]].textureoffset >> FRACBITS), &lines[i].stringargs[1]);
 			}
-			else
-				P_WriteConstant((lines[i].flags & ML_NOCLIMB) ? -1 : (sides[lines[i].sidenum[0]].textureoffset >> FRACBITS), &lines[i].stringargs[1]);
 			break;
 		case 435: //Change plane scroller direction
 			lines[i].args[0] = tag;
@@ -5392,30 +5455,10 @@ static void P_ConvertBinaryLinedefTypes(void)
 			break;
 		case 442: //Change object type state
 			lines[i].args[0] = tag;
-			if (sides[lines[i].sidenum[0]].text)
-			{
-				lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1);
-			}
-			if (lines[i].sidenum[1] == 0xffff)
-				lines[i].args[1] = 1;
-			else
-			{
-				lines[i].args[1] = 0;
-				if (sides[lines[i].sidenum[1]].text)
-				{
-					lines[i].stringargs[1] = Z_Malloc(strlen(sides[lines[i].sidenum[1]].text) + 1, PU_LEVEL, NULL);
-					M_Memcpy(lines[i].stringargs[1], sides[lines[i].sidenum[1]].text, strlen(sides[lines[i].sidenum[1]].text) + 1);
-				}
-			}
+			lines[i].args[3] = (lines[i].sidenum[1] == 0xffff) ? 1 : 0;
 			break;
 		case 443: //Call Lua function
-			if (lines[i].text)
-			{
-				lines[i].stringargs[0] = Z_Malloc(strlen(lines[i].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[0], lines[i].text, strlen(lines[i].text) + 1);
-			}
-			else
+			if (lines[i].stringargs[0] == NULL)
 				CONS_Alert(CONS_WARNING, "Linedef %s is missing the hook name of the Lua function to call! (This should be given in the front texture fields)\n", sizeu1(i));
 			break;
 		case 444: //Earthquake
@@ -5568,11 +5611,6 @@ static void P_ConvertBinaryLinedefTypes(void)
 			if (lines[i].flags & ML_MIDSOLID)
 				lines[i].args[2] |= TMP_FREEZETHINKERS;*/
 			lines[i].args[3] = (lines[i].sidenum[1] != 0xFFFF) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : tag;
-			if (sides[lines[i].sidenum[0]].text)
-			{
-				lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1);
-			}
 			break;
 		case 460: //Award rings
 			lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
@@ -5600,18 +5638,6 @@ static void P_ConvertBinaryLinedefTypes(void)
 			}
 			else
 				lines[i].args[4] = 0;
-			if (sides[lines[i].sidenum[0]].text)
-			{
-				lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1);
-			}
-			break;
-		case 463: //Dye object
-			if (sides[lines[i].sidenum[0]].text)
-			{
-				lines[i].stringargs[0] = Z_Malloc(strlen(sides[lines[i].sidenum[0]].text) + 1, PU_LEVEL, NULL);
-				M_Memcpy(lines[i].stringargs[0], sides[lines[i].sidenum[0]].text, strlen(sides[lines[i].sidenum[0]].text) + 1);
-			}
 			break;
 		case 464: //Trigger egg capsule
 			lines[i].args[0] = tag;
@@ -6440,7 +6466,7 @@ static void P_ConvertBinaryThingTypes(void)
 			break;
 		case 543: //Balloon
 			if (mapthings[i].angle > 0)
-				P_WriteConstant(((mapthings[i].angle - 1) % (numskincolors - 1)) + 1, &mapthings[i].stringargs[0]);
+				P_WriteSkincolor(((mapthings[i].angle - 1) % (numskincolors - 1)) + 1, &mapthings[i].stringargs[0]);
 			mapthings[i].args[0] = !!(mapthings[i].options & MTF_AMBUSH);
 			break;
 		case 555: //Diagonal yellow spring
@@ -6465,22 +6491,22 @@ static void P_ConvertBinaryThingTypes(void)
 		case 706: //Water ambience G
 		case 707: //Water ambience H
 			mapthings[i].args[0] = 35;
-			P_WriteConstant(sfx_amwtr1 + mapthings[i].type - 700, &mapthings[i].stringargs[0]);
+			P_WriteSfx(sfx_amwtr1 + mapthings[i].type - 700, &mapthings[i].stringargs[0]);
 			mapthings[i].type = 700;
 			break;
 		case 708: //Disco ambience
 			mapthings[i].args[0] = 512;
-			P_WriteConstant(sfx_ambint, &mapthings[i].stringargs[0]);
+			P_WriteSfx(sfx_ambint, &mapthings[i].stringargs[0]);
 			mapthings[i].type = 700;
 			break;
 		case 709: //Volcano ambience
 			mapthings[i].args[0] = 220;
-			P_WriteConstant(sfx_ambin2, &mapthings[i].stringargs[0]);
+			P_WriteSfx(sfx_ambin2, &mapthings[i].stringargs[0]);
 			mapthings[i].type = 700;
 			break;
 		case 710: //Machine ambience
 			mapthings[i].args[0] = 24;
-			P_WriteConstant(sfx_ambmac, &mapthings[i].stringargs[0]);
+			P_WriteSfx(sfx_ambmac, &mapthings[i].stringargs[0]);
 			mapthings[i].type = 700;
 			break;
 		case 750: //Slope vertex
@@ -6543,8 +6569,7 @@ static void P_ConvertBinaryThingTypes(void)
 			mapthings[i].args[3] = sides[lines[j].sidenum[0]].rowoffset >> FRACBITS;
 			mapthings[i].args[4] = lines[j].backsector ? sides[lines[j].sidenum[1]].textureoffset >> FRACBITS : 0;
 			mapthings[i].args[6] = mapthings[i].angle;
-			if (sides[lines[j].sidenum[0]].toptexture)
-				P_WriteConstant(sides[lines[j].sidenum[0]].toptexture, &mapthings[i].stringargs[0]);
+			P_WriteDuplicateText(lines[j].stringargs[0], &mapthings[i].stringargs[0]);
 			break;
 		}
 		case 762: //PolyObject spawn point (crush)
@@ -6633,8 +6658,8 @@ static void P_ConvertBinaryThingTypes(void)
 				mapthings[i].args[8] |= TMM_ALWAYSTHINK;
 			if (mapthings[i].type == 1110)
 			{
-				P_WriteConstant(sides[lines[j].sidenum[0]].toptexture, &mapthings[i].stringargs[0]);
-				P_WriteConstant(lines[j].backsector ? sides[lines[j].sidenum[1]].toptexture : MT_NULL, &mapthings[i].stringargs[1]);
+				P_WriteDuplicateText(lines[j].stringargs[0], &mapthings[i].stringargs[0]);
+				P_WriteDuplicateText(lines[j].stringargs[1], &mapthings[i].stringargs[1]);
 			}
 			break;
 		}
@@ -6675,7 +6700,13 @@ static void P_ConvertBinaryThingTypes(void)
 			mapthings[i].args[0] = P_AproxDistance(lines[j].dx, lines[j].dy) >> FRACBITS;
 			mapthings[i].args[1] = sides[lines[j].sidenum[0]].textureoffset >> FRACBITS;
 			mapthings[i].args[2] = !!(lines[j].flags & ML_NOCLIMB);
-			P_WriteConstant(MT_ROCKCRUMBLE1 + (sides[lines[j].sidenum[0]].rowoffset >> FRACBITS), &mapthings[i].stringargs[0]);
+			INT32 id = (sides[lines[j].sidenum[0]].rowoffset >> FRACBITS);
+			// Rather than introduce deh_tables.h as a dependency for literally one
+			// conversion, we just... recreate the string expected to be produced.
+			if (id > 0 && id < 16)
+				P_WriteDuplicateText(va("MT_ROCKCRUMBLE%d", id+1), &mapthings[i].stringargs[0]);
+			else
+				P_WriteConstant(MT_ROCKCRUMBLE1 + id, &mapthings[i].stringargs[0], "Mapthing", i);
 			break;
 		}
 		case 1221: //Minecart saloon door
@@ -6808,6 +6839,7 @@ static void P_ConvertBinaryLinedefFlags(void)
 //For maps in binary format, converts setup of specials to UDMF format.
 static void P_ConvertBinaryMap(void)
 {
+	contextdrift = false;
 	P_ConvertBinaryLinedefTypes();
 	P_ConvertBinarySectorTypes();
 	P_ConvertBinaryThingTypes();
diff --git a/src/r_defs.h b/src/r_defs.h
index 963d655b17..2c98ec3f35 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -547,7 +547,6 @@ typedef struct line_s
 	size_t validcount; // if == validcount, already checked
 	polyobj_t *polyobj; // Belongs to a polyobject?
 
-	char *text; // a concatenation of all front and back texture names, for linedef specials that require a string.
 	INT16 callcount; // no. of calls left before triggering, for the "X calls" linedef specials, defaults to 0
 } line_t;
 
@@ -576,8 +575,6 @@ typedef struct
 	INT16 special; // the special of the linedef this side belongs to
 	INT16 repeatcnt; // # of times to repeat midtexture
 
-	char *text; // a concatenation of all top, bottom, and mid texture names, for linedef specials that require a string.
-
 	extracolormap_t *colormap_data; // storage for colormaps; not applied to sectors.
 } side_t;
 
-- 
GitLab


From e914c19e8fe18339ff9688503eb5c3d5485b6f8d Mon Sep 17 00:00:00 2001
From: toaster <rollerorbital@gmail.com>
Date: Fri, 21 Jul 2023 14:41:22 +0100
Subject: [PATCH 199/518] P_WriteSfx: Work around the weird case sensitivity in
 internal instances of get_number by always providing an uppercase prefix on
 conversion

---
 src/p_setup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 27ddb07757..05c9572d51 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1283,7 +1283,7 @@ static void P_WriteSfx(INT32 constant, char **target)
 		return;
 
 	P_WriteDuplicateText(
-		va("sfx_%s", S_sfx[constant].name),
+		va("SFX_%s", S_sfx[constant].name),
 		target
 	);
 }
-- 
GitLab


From 69089e0858170fecad52461cc663400aa3d881fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 22 Jul 2023 22:47:52 +0200
Subject: [PATCH 200/518] Fix build errors when building without SDL

---
 src/dummy/i_video.c         | 2 ++
 src/hardware/Sourcefile     | 1 -
 src/hardware/hw_model.c     | 2 +-
 src/{hardware => }/u_list.c | 2 +-
 src/{hardware => }/u_list.h | 0
 5 files changed, 4 insertions(+), 3 deletions(-)
 rename src/{hardware => }/u_list.c (99%)
 rename src/{hardware => }/u_list.h (100%)

diff --git a/src/dummy/i_video.c b/src/dummy/i_video.c
index 3b0a12a328..bb796b6767 100644
--- a/src/dummy/i_video.c
+++ b/src/dummy/i_video.c
@@ -57,6 +57,8 @@ const char *VID_GetModeName(INT32 modenum)
 	return NULL;
 }
 
+UINT32 I_GetRefreshRate(void) { return 35; }
+
 void I_UpdateNoBlit(void){}
 
 void I_FinishUpdate(void){}
diff --git a/src/hardware/Sourcefile b/src/hardware/Sourcefile
index 1c05de76cc..6c374621d7 100644
--- a/src/hardware/Sourcefile
+++ b/src/hardware/Sourcefile
@@ -8,6 +8,5 @@ hw_cache.c
 hw_md2load.c
 hw_md3load.c
 hw_model.c
-u_list.c
 hw_batching.c
 r_opengl/r_opengl.c
diff --git a/src/hardware/hw_model.c b/src/hardware/hw_model.c
index 4ed03744bf..b69bce0e2d 100644
--- a/src/hardware/hw_model.c
+++ b/src/hardware/hw_model.c
@@ -15,7 +15,7 @@
 #include "hw_md2load.h"
 #include "hw_md3load.h"
 #include "hw_md2.h"
-#include "u_list.h"
+#include "../u_list.h"
 #include <string.h>
 
 static float PI = (3.1415926535897932384626433832795f);
diff --git a/src/hardware/u_list.c b/src/u_list.c
similarity index 99%
rename from src/hardware/u_list.c
rename to src/u_list.c
index dc49a74e7f..d5cfd5717a 100644
--- a/src/hardware/u_list.c
+++ b/src/u_list.c
@@ -8,7 +8,7 @@
 */
 
 #include "u_list.h"
-#include "../z_zone.h"
+#include "z_zone.h"
 
 // Utility for managing
 // structures in a linked
diff --git a/src/hardware/u_list.h b/src/u_list.h
similarity index 100%
rename from src/hardware/u_list.h
rename to src/u_list.h
-- 
GitLab


From d42d1e9a45ff9bc5ac4bdcefa1297f92c198e90b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 22 Jul 2023 22:56:17 +0200
Subject: [PATCH 201/518] fixup! Fix build errors when building without SDL

---
 src/Sourcefile | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/Sourcefile b/src/Sourcefile
index fb08c21713..7c53050005 100644
--- a/src/Sourcefile
+++ b/src/Sourcefile
@@ -83,6 +83,7 @@ i_tcp.c
 lzf.c
 vid_copy.s
 b_bot.c
+u_list.c
 lua_script.c
 lua_baselib.c
 lua_mathlib.c
-- 
GitLab


From 4bc7b2da1172f9a0142555c5c6d2ea5bdc7303cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 22 Jul 2023 22:59:00 +0200
Subject: [PATCH 202/518] fixup! fixup! Fix build errors when building without
 SDL

---
 src/d_netcmd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index b23aaa5a0a..0f14a49657 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -49,7 +49,7 @@
 #include "m_anigif.h"
 #include "md5.h"
 #include "m_perfstats.h"
-#include "hardware/u_list.h" // TODO: this should be a standard utility class
+#include "u_list.h"
 
 #ifdef NETGAME_DEVMODE
 #define CV_RESTRICT CV_NETVAR
-- 
GitLab


From 66f8908b03b68cc0d350b1556f49af3d6eb39ab6 Mon Sep 17 00:00:00 2001
From: SMS Alfredo <65426124+SMS-Alfredo@users.noreply.github.com>
Date: Sat, 22 Jul 2023 20:44:22 -0500
Subject: [PATCH 203/518] Reset Camera only if a new camera is set / Fix Lua
 jank with setting awayviewmobj or awayviewtics solo

---
 src/lua_playerlib.c | 27 ++++++++++++++++++++-------
 src/p_spec.c        |  8 ++++----
 2 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 1c3b4f46c2..4b4c9d30f3 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -1278,17 +1278,30 @@ static int player_set(lua_State *L)
 		mobj_t *mo = NULL;
 		if (!lua_isnil(L, 3))
 			mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
-		P_SetTarget(&plr->awayviewmobj, mo);
-		if (plr == &players[displayplayer])
-			P_ResetCamera(plr, &camera); // reset p1 camera on p1 getting an awayviewmobj
-		else if (splitscreen && plr == &players[secondarydisplayplayer])
-			P_ResetCamera(plr, &camera2);  // reset p2 camera on p2 getting an awayviewmobj
+		if (plr->awayviewmobj != mo) {
+			P_SetTarget(&plr->awayviewmobj, mo);
+			if (plr->awayviewtics) {
+				if (!plr->awayviewmobj)
+					plr->awayviewtics = 0; // can't have a NULL awayviewmobj with awayviewtics!
+				if (plr == &players[displayplayer])
+					P_ResetCamera(plr, &camera); // reset p1 camera on p1 getting an awayviewmobj
+				else if (splitscreen && plr == &players[secondarydisplayplayer])
+					P_ResetCamera(plr, &camera2);  // reset p2 camera on p2 getting an awayviewmobj
+			}
+		}
 		break;
 	}
 	case player_awayviewtics:
-		plr->awayviewtics = (INT32)luaL_checkinteger(L, 3);
-		if (plr->awayviewtics && !plr->awayviewmobj) // awayviewtics must ALWAYS have an awayviewmobj set!!
+		INT32 tics = (INT32)luaL_checkinteger(L, 3);
+		if (tics && !plr->awayviewmobj) // awayviewtics must ALWAYS have an awayviewmobj set!!
 			P_SetTarget(&plr->awayviewmobj, plr->mo); // but since the script might set awayviewmobj immediately AFTER setting awayviewtics, use player mobj as filler for now.
+		if ((tics && !plr->awayviewtics) || (!tics && plr->awayviewtics)) {
+			if (plr == &players[displayplayer])
+				P_ResetCamera(plr, &camera); // reset p1 camera on p1 transitioning to/from zero awayviewtics
+			else if (splitscreen && plr == &players[secondarydisplayplayer])
+				P_ResetCamera(plr, &camera2);  // reset p2 camera on p2 transitioning to/from zero awayviewtics
+		}
+		plr->awayviewtics = tics;
 		break;
 	case player_awayviewaiming:
 		plr->awayviewaiming = luaL_checkangle(L, 3);
diff --git a/src/p_spec.c b/src/p_spec.c
index 877b9c7bc3..33253e5219 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2661,10 +2661,8 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				// This is not revoked until overwritten; awayviewtics is ignored
 				if (titlemapinaction)
 					titlemapcameraref = altview;
-				else
-				{
+				else if (!mo->player->awayviewtics || mo->player->awayviewmobj != altview) {
 					P_SetTarget(&mo->player->awayviewmobj, altview);
-					mo->player->awayviewtics = line->args[1];
 					
 					if (mo->player == &players[displayplayer])
 						P_ResetCamera(mo->player, &camera); // reset p1 camera on p1 getting an awayviewmobj
@@ -2679,8 +2677,10 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				aim <<= 8;
 				if (titlemapinaction)
 					titlemapcameraref->cusval = (angle_t)aim;
-				else
+				else {
 					mo->player->awayviewaiming = (angle_t)aim;
+					mo->player->awayviewtics = line->args[1];
+				}
 			}
 			break;
 
-- 
GitLab


From 95cbee52c6b9293c03cb6c33007e73b2f1df7895 Mon Sep 17 00:00:00 2001
From: TehRealSalt <tehrealsalt@gmail.com>
Date: Tue, 28 May 2019 17:21:22 -0400
Subject: [PATCH 204/518] Model tilts

- Add modeltilt variable, for more manual control of the model tilting. By default this just copies standingslope, but doesn't get cleared in the air.
- Shadows & trailing bananas now tilt to match the ground they are on.
- Rocket Sneakers & afterimages now tilt to match the player's current orientation.
---
 src/hardware/hw_md2.c | 8 ++++----
 src/p_map.c           | 6 ++++++
 src/p_mobj.c          | 6 ++++++
 src/p_mobj.h          | 3 +++
 src/p_saveg.c         | 4 ++++
 src/p_slopes.c        | 6 ++++++
 src/p_user.c          | 4 ++++
 7 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 38e8e8fc42..16f25118f3 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1647,11 +1647,11 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 #ifdef USE_FTRANSFORM_ANGLEZ
 		// Slope rotation from Kart
 		p.anglez = 0.0f;
-		if (spr->mobj->standingslope)
+		if (spr->mobj->modeltilt)
 		{
-			fixed_t tempz = spr->mobj->standingslope->normal.z;
-			fixed_t tempy = spr->mobj->standingslope->normal.y;
-			fixed_t tempx = spr->mobj->standingslope->normal.x;
+			fixed_t tempz = spr->mobj->modeltilt->normal.z;
+			fixed_t tempy = spr->mobj->modeltilt->normal.y;
+			fixed_t tempx = spr->mobj->modeltilt->normal.x;
 			fixed_t tempangle = AngleFixed(R_PointToAngle2(0, 0, FixedSqrt(FixedMul(tempy, tempy) + FixedMul(tempz, tempz)), tempx));
 			p.anglez = FIXED_TO_FLOAT(tempangle);
 			tempangle = -AngleFixed(R_PointToAngle2(0, 0, tempz, tempy));
diff --git a/src/p_map.c b/src/p_map.c
index 0021fe450c..e486136ffe 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -2928,6 +2928,9 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 			if (thing->momz <= 0)
 			{
 				thing->standingslope = tmfloorslope;
+#ifdef HWRENDER
+				thing->modeltilt = thing->standingslope;
+#endif
 				if (thing->momz == 0 && thing->player && !startingonground)
 					P_PlayerHitFloor(thing->player, true);
 			}
@@ -2939,6 +2942,9 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 			if (thing->momz >= 0)
 			{
 				thing->standingslope = tmceilingslope;
+#ifdef HWRENDER
+				thing->modeltilt = thing->standingslope;
+#endif
 				if (thing->momz == 0 && thing->player && !startingonground)
 					P_PlayerHitFloor(thing->player, true);
 			}
diff --git a/src/p_mobj.c b/src/p_mobj.c
index a43afc9b15..cce79b496d 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -1985,6 +1985,9 @@ void P_XYMovement(mobj_t *mo)
 			// Now compare the Zs of the different quantizations
 			if (oldangle-newangle > ANG30 && oldangle-newangle < ANGLE_180) { // Allow for a bit of sticking - this value can be adjusted later
 				mo->standingslope = oldslope;
+#ifdef HWRENDER
+				mo->modeltilt = mo->standingslope;
+#endif
 				P_SlopeLaunch(mo);
 
 				//CONS_Printf("launched off of slope - ");
@@ -2557,6 +2560,9 @@ boolean P_ZMovement(mobj_t *mo)
 		if (((mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope) && (mo->type != MT_STEAM))
 		{
 			mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope;
+#ifdef HWRENDER
+			mo->modeltilt = mo->standingslope;
+#endif
 			P_ReverseQuantizeMomentumToSlope(&mom, mo->standingslope);
 		}
 
diff --git a/src/p_mobj.h b/src/p_mobj.h
index f573e90203..a17b94b87a 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -394,6 +394,9 @@ typedef struct mobj_s
 	INT32 cvmem;
 
 	struct pslope_s *standingslope; // The slope that the object is standing on (shouldn't need synced in savegames, right?)
+#ifdef HWRENDER
+	struct pslope_s *modeltilt; // Slope used for model tilting. Also is not synched, this is totally visual.
+#endif
 
 	boolean resetinterp; // if true, some fields should not be interpolated (see R_InterpolateMobjState implementation)
 	boolean colorized; // Whether the mobj uses the rainbow colormap
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 40fd656386..65c9eff640 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -2996,7 +2996,11 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 	if (diff2 & MD2_HPREV)
 		mobj->hprev = (mobj_t *)(size_t)READUINT32(save_p);
 	if (diff2 & MD2_SLOPE)
+	{
 		mobj->standingslope = P_SlopeById(READUINT16(save_p));
+#ifdef HWRENDER
+		mobj->modeltilt = mobj->standingslope;
+#endif
 	if (diff2 & MD2_COLORIZED)
 		mobj->colorized = READUINT8(save_p);
 	if (diff2 & MD2_MIRRORED)
diff --git a/src/p_slopes.c b/src/p_slopes.c
index 1f07b8f373..023ee46b98 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -899,6 +899,9 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope)
 		if (P_MobjFlip(thing)*(thing->momz) < 0) // falling, land on slope
 		{
 			thing->standingslope = slope;
+#ifdef HWRENDER
+			thing->modeltilt = thing->standingslope;
+#endif
 			if (!thing->player || !(thing->player->pflags & PF_BOUNCING))
 				thing->momz = -P_MobjFlip(thing);
 		}
@@ -915,6 +918,9 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope)
 		thing->momx = mom.x;
 		thing->momy = mom.y;
 		thing->standingslope = slope;
+#ifdef HWRENDER
+		thing->modeltilt = thing->standingslope;
+#endif
 		if (!thing->player || !(thing->player->pflags & PF_BOUNCING))
 			thing->momz = -P_MobjFlip(thing);
 	}
diff --git a/src/p_user.c b/src/p_user.c
index 1adf6bc3fe..f8cbf8fdbe 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -1995,6 +1995,10 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 
 	ghost->fuse = ghost->info->damage;
 	ghost->skin = mobj->skin;
+	ghost->standingslope = mobj->standingslope;
+#ifdef HWRENDER
+	ghost->modeltilt = mobj->modeltilt;
+#endif
 
 	if (mobj->flags2 & MF2_OBJECTFLIP)
 		ghost->flags |= MF2_OBJECTFLIP;
-- 
GitLab


From ae233b000ff7d859e0fcdf43cfc9df4d2ade904d Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Wed, 28 Oct 2020 18:36:26 -0400
Subject: [PATCH 205/518] Remove modeltilt, use roll & pitch instead

Broken currently, about half of the slopes you can go into have the wrong tilt
---
 src/hardware/hw_defs.h           |  7 +------
 src/hardware/hw_md2.c            | 20 ++++++++------------
 src/hardware/r_opengl/r_opengl.c |  2 --
 src/p_local.h                    |  1 +
 src/p_map.c                      | 10 ++++------
 src/p_mobj.c                     | 28 ++++++++++++++++++++++------
 src/p_mobj.h                     |  3 ---
 src/p_saveg.c                    |  4 ----
 src/p_slopes.c                   |  9 +++------
 src/p_user.c                     |  5 ++---
 10 files changed, 41 insertions(+), 48 deletions(-)

diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index 227fdf92b3..131d2caa39 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -94,8 +94,7 @@ typedef struct FVector
 //BP: transform order : scale(rotation_x(rotation_y(translation(v))))
 
 // Kart features
-//#define USE_FTRANSFORM_ANGLEZ
-//#define USE_FTRANSFORM_MIRROR
+#define USE_FTRANSFORM_MIRROR
 
 // Vanilla features
 #define USE_MODEL_NEXTFRAME
@@ -103,11 +102,7 @@ typedef struct FVector
 typedef struct
 {
 	FLOAT       x,y,z;           // position
-#ifdef USE_FTRANSFORM_ANGLEZ
 	FLOAT       anglex,angley,anglez;   // aimingangle / viewangle
-#else
-	FLOAT       anglex,angley;   // aimingangle / viewangle
-#endif
 	FLOAT       scalex,scaley,scalez;
 	FLOAT       fovxangle, fovyangle;
 	UINT8       splitscreen;
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 16f25118f3..cc7ef1e71b 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1642,22 +1642,18 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 				p.rollflip *= -1;
 		}
 
+		p.anglez = 0.0f;
 		p.anglex = 0.0f;
 
-#ifdef USE_FTRANSFORM_ANGLEZ
-		// Slope rotation from Kart
-		p.anglez = 0.0f;
-		if (spr->mobj->modeltilt)
+		if (spr->mobj->pitch)
 		{
-			fixed_t tempz = spr->mobj->modeltilt->normal.z;
-			fixed_t tempy = spr->mobj->modeltilt->normal.y;
-			fixed_t tempx = spr->mobj->modeltilt->normal.x;
-			fixed_t tempangle = AngleFixed(R_PointToAngle2(0, 0, FixedSqrt(FixedMul(tempy, tempy) + FixedMul(tempz, tempz)), tempx));
-			p.anglez = FIXED_TO_FLOAT(tempangle);
-			tempangle = -AngleFixed(R_PointToAngle2(0, 0, tempz, tempy));
-			p.anglex = FIXED_TO_FLOAT(tempangle);
+			p.anglez = FIXED_TO_FLOAT(-AngleFixed(spr->mobj->pitch));
+		}
+
+		if (spr->mobj->roll)
+		{
+			p.anglex = FIXED_TO_FLOAT(AngleFixed(spr->mobj->roll));
 		}
-#endif
 
 		// SRB2CBTODO: MD2 scaling support
 		finalscale *= FIXED_TO_FLOAT(interp.scale);
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 2fc1df8816..0ba32a27ec 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -2815,9 +2815,7 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float
 	if (hflipped)
 		scalez = -scalez;
 
-#ifdef USE_FTRANSFORM_ANGLEZ
 	pglRotatef(pos->anglez, 0.0f, 0.0f, -1.0f); // rotate by slope from Kart
-#endif
 	pglRotatef(pos->angley, 0.0f, -1.0f, 0.0f);
 	pglRotatef(pos->anglex, 1.0f, 0.0f, 0.0f);
 
diff --git a/src/p_local.h b/src/p_local.h
index 3c84d6fe2f..563e257d8f 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -537,5 +537,6 @@ void P_Thrust(mobj_t *mo, angle_t angle, fixed_t move);
 void P_DoSuperTransformation(player_t *player, boolean giverings);
 void P_ExplodeMissile(mobj_t *mo);
 void P_CheckGravity(mobj_t *mo, boolean affect);
+void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope);
 
 #endif // __P_LOCAL__
diff --git a/src/p_map.c b/src/p_map.c
index e486136ffe..4dffb8e10b 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -2928,9 +2928,8 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 			if (thing->momz <= 0)
 			{
 				thing->standingslope = tmfloorslope;
-#ifdef HWRENDER
-				thing->modeltilt = thing->standingslope;
-#endif
+				P_SetPitchRollFromSlope(thing, thing->standingslope);
+
 				if (thing->momz == 0 && thing->player && !startingonground)
 					P_PlayerHitFloor(thing->player, true);
 			}
@@ -2942,9 +2941,8 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 			if (thing->momz >= 0)
 			{
 				thing->standingslope = tmceilingslope;
-#ifdef HWRENDER
-				thing->modeltilt = thing->standingslope;
-#endif
+				P_SetPitchRollFromSlope(thing, thing->standingslope);
+
 				if (thing->momz == 0 && thing->player && !startingonground)
 					P_PlayerHitFloor(thing->player, true);
 			}
diff --git a/src/p_mobj.c b/src/p_mobj.c
index cce79b496d..9c6df4cf53 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -1551,6 +1551,26 @@ void P_CheckGravity(mobj_t *mo, boolean affect)
 	}
 }
 
+//
+// P_SetPitchRollFromSlope
+//
+void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope)
+{
+	if (slope)
+	{
+		fixed_t tempz = slope->normal.z;
+		fixed_t tempy = slope->normal.y;
+		fixed_t tempx = slope->normal.x;
+
+		mo->pitch = -R_PointToAngle2(0, 0, FixedSqrt(FixedMul(tempy, tempy) + FixedMul(tempz, tempz)), tempx);
+		mo->roll = -R_PointToAngle2(0, 0, tempz, tempy);
+	}
+	else
+	{
+		mo->pitch = mo->roll = 0;
+	}
+}
+
 #define STOPSPEED (FRACUNIT)
 
 //
@@ -1985,9 +2005,7 @@ void P_XYMovement(mobj_t *mo)
 			// Now compare the Zs of the different quantizations
 			if (oldangle-newangle > ANG30 && oldangle-newangle < ANGLE_180) { // Allow for a bit of sticking - this value can be adjusted later
 				mo->standingslope = oldslope;
-#ifdef HWRENDER
-				mo->modeltilt = mo->standingslope;
-#endif
+				P_SetPitchRollFromSlope(mo, mo->standingslope);
 				P_SlopeLaunch(mo);
 
 				//CONS_Printf("launched off of slope - ");
@@ -2560,9 +2578,7 @@ boolean P_ZMovement(mobj_t *mo)
 		if (((mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope) && (mo->type != MT_STEAM))
 		{
 			mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope;
-#ifdef HWRENDER
-			mo->modeltilt = mo->standingslope;
-#endif
+			P_SetPitchRollFromSlope(mo, mo->standingslope);
 			P_ReverseQuantizeMomentumToSlope(&mom, mo->standingslope);
 		}
 
diff --git a/src/p_mobj.h b/src/p_mobj.h
index a17b94b87a..f573e90203 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -394,9 +394,6 @@ typedef struct mobj_s
 	INT32 cvmem;
 
 	struct pslope_s *standingslope; // The slope that the object is standing on (shouldn't need synced in savegames, right?)
-#ifdef HWRENDER
-	struct pslope_s *modeltilt; // Slope used for model tilting. Also is not synched, this is totally visual.
-#endif
 
 	boolean resetinterp; // if true, some fields should not be interpolated (see R_InterpolateMobjState implementation)
 	boolean colorized; // Whether the mobj uses the rainbow colormap
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 65c9eff640..40fd656386 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -2996,11 +2996,7 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 	if (diff2 & MD2_HPREV)
 		mobj->hprev = (mobj_t *)(size_t)READUINT32(save_p);
 	if (diff2 & MD2_SLOPE)
-	{
 		mobj->standingslope = P_SlopeById(READUINT16(save_p));
-#ifdef HWRENDER
-		mobj->modeltilt = mobj->standingslope;
-#endif
 	if (diff2 & MD2_COLORIZED)
 		mobj->colorized = READUINT8(save_p);
 	if (diff2 & MD2_MIRRORED)
diff --git a/src/p_slopes.c b/src/p_slopes.c
index 023ee46b98..6aca116a58 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -899,9 +899,8 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope)
 		if (P_MobjFlip(thing)*(thing->momz) < 0) // falling, land on slope
 		{
 			thing->standingslope = slope;
-#ifdef HWRENDER
-			thing->modeltilt = thing->standingslope;
-#endif
+			P_SetPitchRollFromSlope(thing, slope);
+
 			if (!thing->player || !(thing->player->pflags & PF_BOUNCING))
 				thing->momz = -P_MobjFlip(thing);
 		}
@@ -918,9 +917,7 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope)
 		thing->momx = mom.x;
 		thing->momy = mom.y;
 		thing->standingslope = slope;
-#ifdef HWRENDER
-		thing->modeltilt = thing->standingslope;
-#endif
+		P_SetPitchRollFromSlope(thing, slope);
 		if (!thing->player || !(thing->player->pflags & PF_BOUNCING))
 			thing->momz = -P_MobjFlip(thing);
 	}
diff --git a/src/p_user.c b/src/p_user.c
index f8cbf8fdbe..74b5b7175f 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -1976,6 +1976,8 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 	ghost->colorized = mobj->colorized; // alternatively, "true" for sonic advance style colourisation
 
 	ghost->angle = (mobj->player ? mobj->player->drawangle : mobj->angle);
+	ghost->roll = mobj->roll;
+	ghost->pitch = mobj->pitch;
 	ghost->rollangle = mobj->rollangle;
 
 	ghost->sprite = mobj->sprite;
@@ -1996,9 +1998,6 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 	ghost->fuse = ghost->info->damage;
 	ghost->skin = mobj->skin;
 	ghost->standingslope = mobj->standingslope;
-#ifdef HWRENDER
-	ghost->modeltilt = mobj->modeltilt;
-#endif
 
 	if (mobj->flags2 & MF2_OBJECTFLIP)
 		ghost->flags |= MF2_OBJECTFLIP;
-- 
GitLab


From 5c23fdda246f412b2342e80a298c02518336c8fd Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Wed, 28 Oct 2020 21:05:58 -0400
Subject: [PATCH 206/518] Fix incorrect tilts

---
 src/hardware/hw_md2.c            | 14 ++------------
 src/hardware/r_opengl/r_opengl.c |  4 ++--
 src/p_mobj.c                     |  4 ++--
 3 files changed, 6 insertions(+), 16 deletions(-)

diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index cc7ef1e71b..83a9146cde 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1642,18 +1642,8 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 				p.rollflip *= -1;
 		}
 
-		p.anglez = 0.0f;
-		p.anglex = 0.0f;
-
-		if (spr->mobj->pitch)
-		{
-			p.anglez = FIXED_TO_FLOAT(-AngleFixed(spr->mobj->pitch));
-		}
-
-		if (spr->mobj->roll)
-		{
-			p.anglex = FIXED_TO_FLOAT(AngleFixed(spr->mobj->roll));
-		}
+		p.anglez = FIXED_TO_FLOAT(AngleFixed(spr->mobj->pitch));
+		p.anglex = FIXED_TO_FLOAT(AngleFixed(spr->mobj->roll));
 
 		// SRB2CBTODO: MD2 scaling support
 		finalscale *= FIXED_TO_FLOAT(interp.scale);
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 0ba32a27ec..f50479a718 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -2815,9 +2815,9 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float
 	if (hflipped)
 		scalez = -scalez;
 
-	pglRotatef(pos->anglez, 0.0f, 0.0f, -1.0f); // rotate by slope from Kart
-	pglRotatef(pos->angley, 0.0f, -1.0f, 0.0f);
+	pglRotatef(pos->anglez, 0.0f, 0.0f, -1.0f);
 	pglRotatef(pos->anglex, 1.0f, 0.0f, 0.0f);
+	pglRotatef(pos->angley, 0.0f, -1.0f, 0.0f);
 
 	if (pos->roll)
 	{
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 9c6df4cf53..9b22647c8a 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -1562,8 +1562,8 @@ void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope)
 		fixed_t tempy = slope->normal.y;
 		fixed_t tempx = slope->normal.x;
 
-		mo->pitch = -R_PointToAngle2(0, 0, FixedSqrt(FixedMul(tempy, tempy) + FixedMul(tempz, tempz)), tempx);
-		mo->roll = -R_PointToAngle2(0, 0, tempz, tempy);
+		mo->pitch = R_PointToAngle2(0, 0, FixedSqrt(FixedMul(tempy, tempy) + FixedMul(tempz, tempz)), tempx);
+		mo->roll = R_PointToAngle2(0, 0, tempz, tempy);
 	}
 	else
 	{
-- 
GitLab


From 7c2a2410426a5143b7879af0e19b5ab16f1e1d62 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Fri, 30 Oct 2020 02:15:49 -0400
Subject: [PATCH 207/518] Remove the need for setting rotation axis in
 spriteinfo for models

Now it just does it like Software
---
 src/deh_soc.c                    |  2 +-
 src/hardware/hw_defs.h           |  8 +-----
 src/hardware/hw_main.c           | 14 ++++++++---
 src/hardware/hw_md2.c            | 43 +++++++++++---------------------
 src/hardware/r_opengl/r_opengl.c | 26 ++-----------------
 src/lua_infolib.c                |  9 ++++---
 src/lua_mobjlib.c                | 16 +++++++-----
 src/p_enemy.c                    |  8 +++---
 src/p_inter.c                    |  2 +-
 src/p_mobj.c                     | 11 ++++++--
 src/p_mobj.h                     |  4 +--
 src/p_saveg.c                    | 14 +++++------
 src/p_user.c                     | 19 +++++++-------
 src/r_fps.c                      | 19 ++++++++++++++
 src/r_fps.h                      |  3 +++
 src/r_picformats.c               |  8 ------
 src/r_picformats.h               |  1 -
 src/r_splats.c                   |  2 +-
 src/r_things.c                   |  6 ++---
 19 files changed, 103 insertions(+), 112 deletions(-)

diff --git a/src/deh_soc.c b/src/deh_soc.c
index 8dd849daf4..2193cd875c 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -912,7 +912,7 @@ static void readspriteframe(MYFILE *f, spriteinfo_t *sprinfo, UINT8 frame)
 			else if (fastcmp(word, "YPIVOT"))
 				sprinfo->pivot[frame].y = value;
 			else if (fastcmp(word, "ROTAXIS"))
-				sprinfo->pivot[frame].rotaxis = value;
+				deh_warning("SpriteInfo: ROTAXIS is deprecated and will be removed.");
 			else
 			{
 				f->curpos = lastline;
diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index 131d2caa39..74c4ed7d2e 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -93,9 +93,6 @@ typedef struct FVector
 //Hurdler: Transform (coords + angles)
 //BP: transform order : scale(rotation_x(rotation_y(translation(v))))
 
-// Kart features
-#define USE_FTRANSFORM_MIRROR
-
 // Vanilla features
 #define USE_MODEL_NEXTFRAME
 
@@ -108,13 +105,10 @@ typedef struct
 	UINT8       splitscreen;
 	boolean     flip;            // screenflip
 	boolean     roll;
-	SINT8       rollflip;
 	FLOAT       rollangle; // done to not override USE_FTRANSFORM_ANGLEZ
-	UINT8       rotaxis;
 	FLOAT       centerx, centery;
-#ifdef USE_FTRANSFORM_MIRROR
+	FLOAT       rollx, rollz;
 	boolean     mirror;          // SRB2Kart: Encore Mode
-#endif
 	boolean     shearing;        // 14042019
 	float       viewaiming;      // 17052019
 } FTransform;
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 4e1b7975e4..bb2ce90015 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -4158,7 +4158,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 			angle = viewangle;
 
 		if (!spr->rotated)
-			angle += spr->mobj->rollangle;
+			angle += spr->mobj->spriteroll;
 
 		angle = -angle;
 		angle += ANGLE_90;
@@ -5245,17 +5245,17 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	spr_topoffset = spritecachedinfo[lumpoff].topoffset;
 
 #ifdef ROTSPRITE
-	if (thing->rollangle
+	if (interp.spriteroll
 	&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE)))
 	{
 		if (papersprite)
 		{
 			// a positive rollangle should should pitch papersprites upwards relative to their facing angle
-			rollangle = R_GetRollAngle(InvAngle(thing->rollangle));
+			rollangle = R_GetRollAngle(InvAngle(interp.spriteroll));
 		}
 		else
 		{
-			rollangle = R_GetRollAngle(thing->rollangle);
+			rollangle = R_GetRollAngle(interp.spriteroll);
 		}
 		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);
 
@@ -5913,6 +5913,8 @@ static void HWR_DrawSkyBackground(player_t *player)
 			fixed_t rol = AngleFixed(player->viewrollangle);
 			dometransform.rollangle = FIXED_TO_FLOAT(rol);
 			dometransform.roll = true;
+			dometransform.rollx = 1.0f;
+			dometransform.rollz = 0.0f;
 		}
 		dometransform.splitscreen = splitscreen;
 
@@ -6191,6 +6193,8 @@ void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player)
 		fixed_t rol = AngleFixed(player->viewrollangle);
 		atransform.rollangle = FIXED_TO_FLOAT(rol);
 		atransform.roll = true;
+		atransform.rollx = 1.0f;
+		atransform.rollz = 0.0f;
 	}
 	atransform.splitscreen = splitscreen;
 
@@ -6405,6 +6409,8 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
 		fixed_t rol = AngleFixed(player->viewrollangle);
 		atransform.rollangle = FIXED_TO_FLOAT(rol);
 		atransform.roll = true;
+		atransform.rollx = 1.0f;
+		atransform.rollz = 0.0f;
 	}
 	atransform.splitscreen = splitscreen;
 
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 83a9146cde..baacc2490a 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1346,8 +1346,6 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		const UINT8 hflip = (UINT8)(!(spr->mobj->mirrored) != !R_ThingHorizontallyFlipped(spr->mobj));
 		spritedef_t *sprdef;
 		spriteframe_t *sprframe;
-		spriteinfo_t *sprinfo;
-		angle_t ang;
 		INT32 mod;
 		float finalscale;
 		interpmobjstate_t interp;
@@ -1388,12 +1386,10 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		{
 			md2 = &md2_playermodels[(skin_t*)spr->mobj->skin-skins];
 			md2->skin = (skin_t*)spr->mobj->skin-skins;
-			sprinfo = &((skin_t *)spr->mobj->skin)->sprinfo[spr->mobj->sprite2];
 		}
 		else
 		{
 			md2 = &md2_models[spr->mobj->sprite];
-			sprinfo = &spriteinfo[spr->mobj->sprite];
 		}
 
 		// texture loading before model init, so it knows if sprite graphics are used, which
@@ -1615,43 +1611,32 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		}
 
 		p.rollangle = 0.0f;
-		p.rollflip = 1;
-		p.rotaxis = 0;
-		if (spr->mobj->rollangle)
+
+		if (interp.spriteroll)
 		{
-			fixed_t anglef = AngleFixed(spr->mobj->rollangle);
+			fixed_t camAngleDiff = AngleFixed(viewangle) - FLOAT_TO_FIXED(p.angley); // dumb reconversion back, I know
+			fixed_t anglef = AngleFixed(interp.spriteroll);
+
 			p.rollangle = FIXED_TO_FLOAT(anglef);
 			p.roll = true;
 
 			// rotation pivot
-			p.centerx = FIXED_TO_FLOAT(spr->mobj->radius/2);
-			p.centery = FIXED_TO_FLOAT(spr->mobj->height/(flip ? -2 : 2));
-
-			// rotation axis
-			if (sprinfo->available)
-				p.rotaxis = (UINT8)(sprinfo->pivot[(spr->mobj->frame & FF_FRAMEMASK)].rotaxis);
-
-			// for NiGHTS specifically but should work everywhere else
-			ang = R_PointToAngle (interp.x, interp.y) - interp.angle;
-			if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right
-				p.rollflip = 1;
-			else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left
-				p.rollflip = -1;
-
-			if (flip)
-				p.rollflip *= -1;
+			p.centerx = FIXED_TO_FLOAT(spr->mobj->radius / 2);
+			p.centery = FIXED_TO_FLOAT(spr->mobj->height / 2);
+
+			// rotation axes relative to camera
+			p.rollx = FIXED_TO_FLOAT(FINECOSINE(FixedAngle(camAngleDiff) >> ANGLETOFINESHIFT));
+			p.rollz = FIXED_TO_FLOAT(FINESINE(FixedAngle(camAngleDiff) >> ANGLETOFINESHIFT));
 		}
 
-		p.anglez = FIXED_TO_FLOAT(AngleFixed(spr->mobj->pitch));
-		p.anglex = FIXED_TO_FLOAT(AngleFixed(spr->mobj->roll));
+		p.anglez = FIXED_TO_FLOAT(AngleFixed(interp.pitch));
+		p.anglex = FIXED_TO_FLOAT(AngleFixed(interp.roll));
 
 		// SRB2CBTODO: MD2 scaling support
 		finalscale *= FIXED_TO_FLOAT(interp.scale);
 
 		p.flip = atransform.flip;
-#ifdef USE_FTRANSFORM_MIRROR
-		p.mirror = atransform.mirror; // from Kart
-#endif
+		p.mirror = atransform.mirror;
 
 		HWD.pfnSetShader(SHADER_MODEL);	// model shader
 		HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, hflip, &Surf);
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index f50479a718..d785b3cbf6 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -2782,7 +2782,6 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float
 	pglEnable(GL_CULL_FACE);
 	pglEnable(GL_NORMALIZE);
 
-#ifdef USE_FTRANSFORM_MIRROR
 	// flipped is if the object is vertically flipped
 	// hflipped is if the object is horizontally flipped
 	// pos->flip is if the screen is flipped vertically
@@ -2795,17 +2794,6 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float
 		else
 			pglCullFace(GL_BACK);
 	}
-#else
-	// pos->flip is if the screen is flipped too
-	if (flipped ^ hflipped ^ pos->flip) // If one or three of these are active, but not two, invert the model's culling
-	{
-		pglCullFace(GL_FRONT);
-	}
-	else
-	{
-		pglCullFace(GL_BACK);
-	}
-#endif
 
 	pglPushMatrix(); // should be the same as glLoadIdentity
 	//Hurdler: now it seems to work
@@ -2821,14 +2809,8 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float
 
 	if (pos->roll)
 	{
-		float roll = (1.0f * pos->rollflip);
 		pglTranslatef(pos->centerx, pos->centery, 0);
-		if (pos->rotaxis == 2) // Z
-			pglRotatef(pos->rollangle, 0.0f, 0.0f, roll);
-		else if (pos->rotaxis == 1) // Y
-			pglRotatef(pos->rollangle, 0.0f, roll, 0.0f);
-		else // X
-			pglRotatef(pos->rollangle, roll, 0.0f, 0.0f);
+		pglRotatef(pos->rollangle, pos->rollx, 0.0f, pos->rollz);
 		pglTranslatef(-pos->centerx, -pos->centery, 0);
 	}
 
@@ -3001,13 +2983,9 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform)
 	if (stransform)
 	{
 		used_fov = stransform->fovxangle;
-#ifdef USE_FTRANSFORM_MIRROR
-		// mirroring from Kart
 		if (stransform->mirror)
 			pglScalef(-stransform->scalex, stransform->scaley, -stransform->scalez);
-		else
-#endif
-		if (stransform->flip)
+		else if (stransform->flip)
 			pglScalef(stransform->scalex, -stransform->scaley, -stransform->scalez);
 		else
 			pglScalef(stransform->scalex, stransform->scaley, -stransform->scalez);
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 578878b21c..10baa3c672 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -319,7 +319,7 @@ static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, in
 				else if (ikey == 2 || (key && fastcmp(key, "y")))
 					pivot[idx].y = (INT32)value;
 				else if (ikey == 3 || (key && fastcmp(key, "rotaxis")))
-					pivot[idx].rotaxis = (UINT8)value;
+					LUA_UsageWarning(L, "\"rotaxis\" is deprecated and will be removed.")
 				else if (ikey == -1 && (key != NULL))
 					FIELDERROR("pivot key", va("invalid option %s", key));
 				okcool = 1;
@@ -576,7 +576,10 @@ static int framepivot_get(lua_State *L)
 	else if (fastcmp("y", field))
 		lua_pushinteger(L, framepivot->y);
 	else if (fastcmp("rotaxis", field))
-		lua_pushinteger(L, (UINT8)framepivot->rotaxis);
+	{
+		LUA_UsageWarning(L, "\"rotaxis\" is deprecated and will be removed.");
+		lua_pushinteger(L, 0);
+	}
 	else
 		return luaL_error(L, va("Field %s does not exist in spriteframepivot_t", field));
 
@@ -602,7 +605,7 @@ static int framepivot_set(lua_State *L)
 	else if (fastcmp("y", field))
 		framepivot->y = luaL_checkinteger(L, 3);
 	else if (fastcmp("rotaxis", field))
-		framepivot->rotaxis = luaL_checkinteger(L, 3);
+		LUA_UsageWarning(L, "\"rotaxis\" is deprecated and will be removed.")
 	else
 		return luaL_error(L, va("Field %s does not exist in spriteframepivot_t", field));
 
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 09d244c910..404af016a6 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -33,7 +33,8 @@ enum mobj_e {
 	mobj_angle,
 	mobj_pitch,
 	mobj_roll,
-	mobj_rollangle,
+	mobj_spriteroll,
+	mobj_rollangle, // backwards compat
 	mobj_sprite,
 	mobj_frame,
 	mobj_sprite2,
@@ -110,7 +111,8 @@ static const char *const mobj_opt[] = {
 	"angle",
 	"pitch",
 	"roll",
-	"rollangle",
+	"spriteroll",
+	"rollangle", // backwards compat
 	"sprite",
 	"frame",
 	"sprite2",
@@ -229,8 +231,9 @@ static int mobj_get(lua_State *L)
 	case mobj_roll:
 		lua_pushangle(L, mo->roll);
 		break;
-	case mobj_rollangle:
-		lua_pushangle(L, mo->rollangle);
+	case mobj_spriteroll:
+	case mobj_rollangle: // backwards compat
+		lua_pushangle(L, mo->spriteroll);
 		break;
 	case mobj_sprite:
 		lua_pushinteger(L, mo->sprite);
@@ -518,8 +521,9 @@ static int mobj_set(lua_State *L)
 	case mobj_roll:
 		mo->roll = luaL_checkangle(L, 3);
 		break;
-	case mobj_rollangle:
-		mo->rollangle = luaL_checkangle(L, 3);
+	case mobj_spriteroll:
+	case mobj_rollangle: // backwards compat
+		mo->spriteroll = luaL_checkangle(L, 3);
 		break;
 	case mobj_sprite:
 		mo->sprite = luaL_checkinteger(L, 3);
diff --git a/src/p_enemy.c b/src/p_enemy.c
index d9b44e603f..b40525b2d9 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -8687,10 +8687,10 @@ void A_RollAngle(mobj_t *actor)
 
 	// relative (default)
 	if (!locvar2)
-		actor->rollangle += angle;
+		actor->spriteroll += angle;
 	// absolute
 	else
-		actor->rollangle = angle;
+		actor->spriteroll = angle;
 }
 
 // Function: A_ChangeRollAngleRelative
@@ -8715,7 +8715,7 @@ void A_ChangeRollAngleRelative(mobj_t *actor)
 		I_Error("A_ChangeRollAngleRelative: var1 is greater than var2");
 #endif
 
-	actor->rollangle += FixedAngle(P_RandomRange(amin, amax));
+	actor->spriteroll += FixedAngle(P_RandomRange(amin, amax));
 }
 
 // Function: A_ChangeRollAngleAbsolute
@@ -8740,7 +8740,7 @@ void A_ChangeRollAngleAbsolute(mobj_t *actor)
 		I_Error("A_ChangeRollAngleAbsolute: var1 is greater than var2");
 #endif
 
-	actor->rollangle = FixedAngle(P_RandomRange(amin, amax));
+	actor->spriteroll = FixedAngle(P_RandomRange(amin, amax));
 }
 
 // Function: A_PlaySound
diff --git a/src/p_inter.c b/src/p_inter.c
index 8c57beac87..4d22ba3438 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -3117,7 +3117,7 @@ static void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
 		P_SetPlayerMobjState(target, S_PLAY_NIGHTS_STUN);
 		S_StartSound(target, sfx_nghurt);
 
-		player->mo->rollangle = 0;
+		player->mo->spriteroll = 0;
 
 		if (oldnightstime > 10*TICRATE
 			&& player->nightstime < 10*TICRATE)
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 9b22647c8a..9a889d5166 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -9868,9 +9868,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
 		break;
 	case MT_MINUS:
 		if (P_IsObjectOnGround(mobj))
-			mobj->rollangle = 0;
+			mobj->spriteroll = 0;
 		else
-			mobj->rollangle = R_PointToAngle2(0, 0, P_MobjFlip(mobj)*mobj->momz, (mobj->scale << 1) - min(abs(mobj->momz), mobj->scale << 1));
+			mobj->spriteroll = R_PointToAngle2(0, 0, P_MobjFlip(mobj)*mobj->momz, (mobj->scale << 1) - min(abs(mobj->momz), mobj->scale << 1));
 		break;
 	case MT_PUSH:
 		P_PointPushThink(mobj);
@@ -14167,6 +14167,13 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
 		newmobj->old_angle = mobj->old_angle;
 	}
 
+	newmobj->old_pitch2 = mobj->old_pitch2;
+	newmobj->old_pitch = mobj->old_pitch;
+	newmobj->old_roll2 = mobj->old_roll2;
+	newmobj->old_roll = mobj->old_roll;
+	newmobj->old_spriteroll2 = mobj->old_spriteroll2;
+	newmobj->old_spriteroll = mobj->old_spriteroll;
+
 	newmobj->old_scale2 = mobj->old_scale2;
 	newmobj->old_scale = mobj->old_scale;
 	newmobj->old_spritexscale = mobj->old_spritexscale;
diff --git a/src/p_mobj.h b/src/p_mobj.h
index f573e90203..6a5ab84c41 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -292,7 +292,7 @@ typedef struct mobj_s
 	angle_t angle, pitch, roll; // orientation
 	angle_t old_angle, old_pitch, old_roll; // orientation interpolation
 	angle_t old_angle2, old_pitch2, old_roll2;
-	angle_t rollangle;
+	angle_t spriteroll, old_spriteroll, old_spriteroll2;
 	spritenum_t sprite; // used to find patch_t and flip value
 	UINT32 frame; // frame number, plus bits see p_pspr.h
 	UINT8 sprite2; // player sprites
@@ -429,7 +429,7 @@ typedef struct precipmobj_s
 	angle_t angle, pitch, roll; // orientation
 	angle_t old_angle, old_pitch, old_roll; // orientation interpolation
 	angle_t old_angle2, old_pitch2, old_roll2;
-	angle_t rollangle;
+	angle_t spriteroll, old_spriteroll, old_spriteroll2;
 	spritenum_t sprite; // used to find patch_t and flip value
 	UINT32 frame; // frame number, plus bits see p_pspr.h
 	UINT8 sprite2; // player sprites
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 40fd656386..ce0b262a33 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1565,7 +1565,7 @@ typedef enum
 	MD2_SLOPE        = 1<<11,
 	MD2_COLORIZED    = 1<<12,
 	MD2_MIRRORED     = 1<<13,
-	MD2_ROLLANGLE    = 1<<14,
+	MD2_SPRITEROLL   = 1<<14,
 	MD2_SHADOWSCALE  = 1<<15,
 	MD2_RENDERFLAGS  = 1<<16,
 	MD2_BLENDMODE    = 1<<17,
@@ -1783,8 +1783,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		diff2 |= MD2_COLORIZED;
 	if (mobj->mirrored)
 		diff2 |= MD2_MIRRORED;
-	if (mobj->rollangle)
-		diff2 |= MD2_ROLLANGLE;
+	if (mobj->spriteroll)
+		diff2 |= MD2_SPRITEROLL;
 	if (mobj->shadowscale)
 		diff2 |= MD2_SHADOWSCALE;
 	if (mobj->renderflags)
@@ -1951,8 +1951,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		WRITEUINT8(save_p, mobj->colorized);
 	if (diff2 & MD2_MIRRORED)
 		WRITEUINT8(save_p, mobj->mirrored);
-	if (diff2 & MD2_ROLLANGLE)
-		WRITEANGLE(save_p, mobj->rollangle);
+	if (diff2 & MD2_SPRITEROLL)
+		WRITEANGLE(save_p, mobj->spriteroll);
 	if (diff2 & MD2_SHADOWSCALE)
 		WRITEFIXED(save_p, mobj->shadowscale);
 	if (diff2 & MD2_RENDERFLAGS)
@@ -3001,8 +3001,8 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		mobj->colorized = READUINT8(save_p);
 	if (diff2 & MD2_MIRRORED)
 		mobj->mirrored = READUINT8(save_p);
-	if (diff2 & MD2_ROLLANGLE)
-		mobj->rollangle = READANGLE(save_p);
+	if (diff2 & MD2_SPRITEROLL)
+		mobj->spriteroll = READANGLE(save_p);
 	if (diff2 & MD2_SHADOWSCALE)
 		mobj->shadowscale = READFIXED(save_p);
 	if (diff2 & MD2_RENDERFLAGS)
diff --git a/src/p_user.c b/src/p_user.c
index 74b5b7175f..16952f7750 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -676,7 +676,7 @@ static void P_DeNightserizePlayer(player_t *player)
 	player->marebonuslap = 0;
 	player->flyangle = 0;
 	player->anotherflyangle = 0;
-	player->mo->rollangle = 0;
+	player->mo->spriteroll = 0;
 
 	P_SetTarget(&player->mo->target, NULL);
 	P_SetTarget(&player->axis1, P_SetTarget(&player->axis2, NULL));
@@ -802,7 +802,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 	player->secondjump = 0;
 	player->flyangle = 0;
 	player->anotherflyangle = 0;
-	player->mo->rollangle = 0;
+	player->mo->spriteroll = 0;
 
 	player->powers[pw_shield] = SH_NONE;
 	player->powers[pw_super] = 0;
@@ -1978,7 +1978,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 	ghost->angle = (mobj->player ? mobj->player->drawangle : mobj->angle);
 	ghost->roll = mobj->roll;
 	ghost->pitch = mobj->pitch;
-	ghost->rollangle = mobj->rollangle;
+	ghost->spriteroll = mobj->spriteroll;
 
 	ghost->sprite = mobj->sprite;
 	ghost->sprite2 = mobj->sprite2;
@@ -2017,6 +2017,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 	ghost->old_angle = (mobj->player ? mobj->player->old_drawangle2 : mobj->old_angle2);
 	ghost->old_pitch = mobj->old_pitch2;
 	ghost->old_roll = mobj->old_roll2;
+	ghost->old_spriteroll = mobj->old_spriteroll2;
 
 	return ghost;
 }
@@ -6793,9 +6794,9 @@ static void P_DoNiGHTSCapsule(player_t *player)
 	{
 		if ((player->mo->state == &states[S_PLAY_NIGHTS_PULL])
 		&& (player->mo->sprite2 == SPR2_NPUL))
-			player->mo->rollangle -= ANG30;
+			player->mo->spriteroll -= ANG30;
 		else
-			player->mo->rollangle = 0;
+			player->mo->spriteroll = 0;
 	}
 
 	if (G_IsSpecialStage(gamemap))
@@ -7248,7 +7249,7 @@ static void P_NiGHTSMovement(player_t *player)
 		&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6])
 	{
 		player->mo->momx = player->mo->momy = player->mo->momz = 0;
-		player->mo->rollangle = 0;
+		player->mo->spriteroll = 0;
 		return;
 	}
 
@@ -7266,7 +7267,7 @@ static void P_NiGHTSMovement(player_t *player)
 		{
 			if (player->mo->state != &states[S_PLAY_NIGHTS_DRILL])
 				P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_DRILL);
-			player->mo->rollangle = ANGLE_90;
+			player->mo->spriteroll = ANGLE_90;
 		}
 		else
 #endif
@@ -7599,9 +7600,9 @@ static void P_NiGHTSMovement(player_t *player)
 		P_SetPlayerMobjState(player->mo, flystate);
 
 	if (player->charflags & SF_NONIGHTSROTATION)
-		player->mo->rollangle = 0;
+		player->mo->spriteroll = 0;
 	else
-		player->mo->rollangle = rollangle;
+		player->mo->spriteroll = rollangle;
 
 	P_SetPlayerAngle(player, player->mo->angle);
 
diff --git a/src/r_fps.c b/src/r_fps.c
index 6166f04080..e8f43f4997 100644
--- a/src/r_fps.c
+++ b/src/r_fps.c
@@ -294,6 +294,9 @@ void R_InterpolateMobjState(mobj_t *mobj, fixed_t frac, interpmobjstate_t *out)
 		out->scale = mobj->scale;
 		out->subsector = mobj->subsector;
 		out->angle = mobj->player ? mobj->player->drawangle : mobj->angle;
+		out->pitch = mobj->pitch;
+		out->roll = mobj->roll;
+		out->spriteroll = mobj->spriteroll;
 		out->spritexscale = mobj->spritexscale;
 		out->spriteyscale = mobj->spriteyscale;
 		out->spritexoffset = mobj->spritexoffset;
@@ -323,6 +326,10 @@ void R_InterpolateMobjState(mobj_t *mobj, fixed_t frac, interpmobjstate_t *out)
 	{
 		out->angle = mobj->resetinterp ? mobj->angle : R_LerpAngle(mobj->old_angle, mobj->angle, frac);
 	}
+
+	out->pitch = mobj->resetinterp ? mobj->pitch : R_LerpAngle(mobj->old_pitch, mobj->pitch, frac);
+	out->roll = mobj->resetinterp ? mobj->roll : R_LerpAngle(mobj->old_roll, mobj->roll, frac);
+	out->spriteroll = mobj->resetinterp ? mobj->spriteroll : R_LerpAngle(mobj->old_spriteroll, mobj->spriteroll, frac);
 }
 
 void R_InterpolatePrecipMobjState(precipmobj_t *mobj, fixed_t frac, interpmobjstate_t *out)
@@ -335,6 +342,9 @@ void R_InterpolatePrecipMobjState(precipmobj_t *mobj, fixed_t frac, interpmobjst
 		out->scale = FRACUNIT;
 		out->subsector = mobj->subsector;
 		out->angle = mobj->angle;
+		out->pitch = mobj->angle;
+		out->roll = mobj->roll;
+		out->spriteroll = mobj->spriteroll;
 		out->spritexscale = mobj->spritexscale;
 		out->spriteyscale = mobj->spriteyscale;
 		out->spritexoffset = mobj->spritexoffset;
@@ -354,6 +364,9 @@ void R_InterpolatePrecipMobjState(precipmobj_t *mobj, fixed_t frac, interpmobjst
 	out->subsector = R_PointInSubsector(out->x, out->y);
 
 	out->angle = R_LerpAngle(mobj->old_angle, mobj->angle, frac);
+	out->pitch = R_LerpAngle(mobj->old_pitch, mobj->pitch, frac);
+	out->roll = R_LerpAngle(mobj->old_roll, mobj->roll, frac);
+	out->spriteroll = R_LerpAngle(mobj->old_spriteroll, mobj->spriteroll, frac);
 }
 
 static void AddInterpolator(levelinterpolator_t* interpolator)
@@ -766,6 +779,7 @@ void R_ResetMobjInterpolationState(mobj_t *mobj)
 	mobj->old_angle2 = mobj->old_angle;
 	mobj->old_pitch2 = mobj->old_pitch;
 	mobj->old_roll2 = mobj->old_roll;
+	mobj->old_spriteroll2 = mobj->old_spriteroll;
 	mobj->old_scale2 = mobj->old_scale;
 	mobj->old_x = mobj->x;
 	mobj->old_y = mobj->y;
@@ -773,6 +787,7 @@ void R_ResetMobjInterpolationState(mobj_t *mobj)
 	mobj->old_angle = mobj->angle;
 	mobj->old_pitch = mobj->pitch;
 	mobj->old_roll = mobj->roll;
+	mobj->old_spriteroll = mobj->spriteroll;
 	mobj->old_scale = mobj->scale;
 	mobj->old_spritexscale = mobj->spritexscale;
 	mobj->old_spriteyscale = mobj->spriteyscale;
@@ -801,10 +816,14 @@ void R_ResetPrecipitationMobjInterpolationState(precipmobj_t *mobj)
 	mobj->old_angle2 = mobj->old_angle;
 	mobj->old_pitch2 = mobj->old_pitch;
 	mobj->old_roll2 = mobj->old_roll;
+	mobj->old_spriteroll2 = mobj->old_spriteroll;
 	mobj->old_x = mobj->x;
 	mobj->old_y = mobj->y;
 	mobj->old_z = mobj->z;
 	mobj->old_angle = mobj->angle;
+	mobj->old_pitch = mobj->pitch;
+	mobj->old_roll = mobj->roll;
+	mobj->old_spriteroll = mobj->spriteroll;
 	mobj->old_spritexscale = mobj->spritexscale;
 	mobj->old_spriteyscale = mobj->spriteyscale;
 	mobj->old_spritexoffset = mobj->spritexoffset;
diff --git a/src/r_fps.h b/src/r_fps.h
index 85c87a2f49..9a8bfa38ae 100644
--- a/src/r_fps.h
+++ b/src/r_fps.h
@@ -59,6 +59,9 @@ typedef struct {
 	fixed_t z;
 	subsector_t *subsector;
 	angle_t angle;
+	angle_t pitch;
+	angle_t roll;
+	angle_t spriteroll;
 	fixed_t scale;
 	fixed_t spritexscale;
 	fixed_t spriteyscale;
diff --git a/src/r_picformats.c b/src/r_picformats.c
index 9aea8c6c14..3e817f4a06 100644
--- a/src/r_picformats.c
+++ b/src/r_picformats.c
@@ -1407,7 +1407,6 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
 	UINT8 frameFrame = 0xFF;
 	INT16 frameXPivot = 0;
 	INT16 frameYPivot = 0;
-	rotaxis_t frameRotAxis = 0;
 
 	// Sprite identifier
 	sprinfoToken = M_GetToken(NULL);
@@ -1458,12 +1457,6 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
 				{
 					Z_Free(sprinfoToken);
 					sprinfoToken = M_GetToken(NULL);
-					if ((stricmp(sprinfoToken, "X")==0) || (stricmp(sprinfoToken, "XAXIS")==0) || (stricmp(sprinfoToken, "ROLL")==0))
-						frameRotAxis = ROTAXIS_X;
-					else if ((stricmp(sprinfoToken, "Y")==0) || (stricmp(sprinfoToken, "YAXIS")==0) || (stricmp(sprinfoToken, "PITCH")==0))
-						frameRotAxis = ROTAXIS_Y;
-					else if ((stricmp(sprinfoToken, "Z")==0) || (stricmp(sprinfoToken, "ZAXIS")==0) || (stricmp(sprinfoToken, "YAW")==0))
-						frameRotAxis = ROTAXIS_Z;
 				}
 				Z_Free(sprinfoToken);
 
@@ -1480,7 +1473,6 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
 	// set fields
 	info->pivot[frameFrame].x = frameXPivot;
 	info->pivot[frameFrame].y = frameYPivot;
-	info->pivot[frameFrame].rotaxis = frameRotAxis;
 }
 
 //
diff --git a/src/r_picformats.h b/src/r_picformats.h
index 4f96374600..4050a1b71d 100644
--- a/src/r_picformats.h
+++ b/src/r_picformats.h
@@ -95,7 +95,6 @@ typedef enum
 typedef struct
 {
 	INT32 x, y;
-	rotaxis_t rotaxis;
 } spriteframepivot_t;
 
 typedef struct
diff --git a/src/r_splats.c b/src/r_splats.c
index 72bfe74b3a..d182d628ba 100644
--- a/src/r_splats.c
+++ b/src/r_splats.c
@@ -191,7 +191,7 @@ void R_DrawFloorSplat(vissprite_t *spr)
 		splatangle = spr->viewpoint.angle;
 
 	if (!(spr->cut & SC_ISROTATED))
-		splatangle += mobj->rollangle;
+		splatangle += mobj->spriteroll;
 
 	splat.angle = -splatangle;
 	splat.angle += ANGLE_90;
diff --git a/src/r_things.c b/src/r_things.c
index 19b8ee83d0..eab8d13c27 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1743,17 +1743,17 @@ static void R_ProjectSprite(mobj_t *thing)
 	patch = W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE);
 
 #ifdef ROTSPRITE
-	if (thing->rollangle
+	if (interp.spriteroll
 	&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE)))
 	{
 		if (papersprite && ang >= ANGLE_180)
 		{
 			// Makes Software act much more sane like OpenGL
-			rollangle = R_GetRollAngle(InvAngle(thing->rollangle));
+			rollangle = R_GetRollAngle(InvAngle(interp.spriteroll));
 		}
 		else
 		{
-			rollangle = R_GetRollAngle(thing->rollangle);
+			rollangle = R_GetRollAngle(interp.spriteroll);
 		}
 
 		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);
-- 
GitLab


From b6129a6d42babaec67319e523db12af9d9e7b4a2 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Fri, 30 Oct 2020 15:54:58 -0400
Subject: [PATCH 208/518] Add R_SpriteRotationAngle function

Gets the rotation angle for the mobj's sprite. Meant for pitch & roll later, but that part is if'd out currently and just returns mobj->rollangle
---
 src/hardware/hw_main.c | 10 +++++++---
 src/r_patch.h          |  2 ++
 src/r_patchrotation.c  | 18 ++++++++++++++++++
 src/r_things.c         |  9 ++++++---
 4 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index bb2ce90015..8c1bc09e0a 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -5078,6 +5078,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 #ifdef ROTSPRITE
 	patch_t *rotsprite = NULL;
 	INT32 rollangle = 0;
+	angle_t spriterotangle = 0;
 #endif
 
 	// uncapped/interpolation
@@ -5245,18 +5246,21 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	spr_topoffset = spritecachedinfo[lumpoff].topoffset;
 
 #ifdef ROTSPRITE
-	if (interp.spriteroll
+	spriterotangle = R_SpriteRotationAngle(&interp);
+
+	if (spriterotangle != 0
 	&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE)))
 	{
 		if (papersprite)
 		{
 			// a positive rollangle should should pitch papersprites upwards relative to their facing angle
-			rollangle = R_GetRollAngle(InvAngle(interp.spriteroll));
+			rollangle = R_GetRollAngle(InvAngle(spriterotangle));
 		}
 		else
 		{
-			rollangle = R_GetRollAngle(interp.spriteroll);
+			rollangle = R_GetRollAngle(spriterotangle);
 		}
+
 		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);
 
 		if (rotsprite != NULL)
diff --git a/src/r_patch.h b/src/r_patch.h
index d2106a3900..27f7bddb6f 100644
--- a/src/r_patch.h
+++ b/src/r_patch.h
@@ -14,6 +14,7 @@
 
 #include "r_defs.h"
 #include "r_picformats.h"
+#include "r_fps.h"
 #include "doomdef.h"
 
 // Patch functions
@@ -38,6 +39,7 @@ patch_t *Patch_GetRotatedSprite(
 	size_t frame, size_t spriteangle,
 	boolean flip, boolean adjustfeet,
 	void *info, INT32 rotationangle);
+angle_t R_SpriteRotationAngle(interpmobjstate_t *interp);
 INT32 R_GetRollAngle(angle_t rollangle);
 #endif
 
diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c
index 3d3c6c5128..f9c4ec797d 100644
--- a/src/r_patchrotation.c
+++ b/src/r_patchrotation.c
@@ -13,11 +13,29 @@
 #include "r_things.h" // FEETADJUST
 #include "z_zone.h"
 #include "w_wad.h"
+#include "r_main.h" // R_PointToAngle
 
 #ifdef ROTSPRITE
 fixed_t rollcosang[ROTANGLES];
 fixed_t rollsinang[ROTANGLES];
 
+//
+// R_SpriteRotationAngle
+//
+// Gets the rollangle for the input object.
+//
+angle_t R_SpriteRotationAngle(interpmobjstate_t *interp)
+{
+	angle_t viewingAngle = R_PointToAngle(interp->x, interp->y);
+
+	fixed_t pitchMul = -FINESINE(viewingAngle >> ANGLETOFINESHIFT);
+	fixed_t rollMul = FINECOSINE(viewingAngle >> ANGLETOFINESHIFT);
+
+	angle_t rollOrPitch = FixedMul(interp->pitch, pitchMul) + FixedMul(interp->roll, rollMul);
+
+	return (rollOrPitch + interp->spriteroll);
+}
+
 INT32 R_GetRollAngle(angle_t rollangle)
 {
 	INT32 ra = AngleFixed(rollangle)>>FRACBITS;
diff --git a/src/r_things.c b/src/r_things.c
index eab8d13c27..248357e11f 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1594,6 +1594,7 @@ static void R_ProjectSprite(mobj_t *thing)
 #ifdef ROTSPRITE
 	patch_t *rotsprite = NULL;
 	INT32 rollangle = 0;
+	angle_t spriterotangle = 0;
 #endif
 
 	// uncapped/interpolation
@@ -1743,17 +1744,19 @@ static void R_ProjectSprite(mobj_t *thing)
 	patch = W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE);
 
 #ifdef ROTSPRITE
-	if (interp.spriteroll
+	spriterotangle = R_SpriteRotationAngle(&interp);
+
+	if (spriterotangle != 0
 	&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE)))
 	{
 		if (papersprite && ang >= ANGLE_180)
 		{
 			// Makes Software act much more sane like OpenGL
-			rollangle = R_GetRollAngle(InvAngle(interp.spriteroll));
+			rollangle = R_GetRollAngle(InvAngle(spriterotangle));
 		}
 		else
 		{
-			rollangle = R_GetRollAngle(interp.spriteroll);
+			rollangle = R_GetRollAngle(spriterotangle);
 		}
 
 		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);
-- 
GitLab


From 7db09b657b2735ea7b5655a6aa8df52f30dc913e Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Thu, 9 Dec 2021 22:13:56 -0800
Subject: [PATCH 209/518] Tilt models

---
 src/hardware/hw_md2.c | 29 ++++++++++++++++-------------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index baacc2490a..a20d808480 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1610,23 +1610,26 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 			p.angley = FIXED_TO_FLOAT(anglef);
 		}
 
-		p.rollangle = 0.0f;
-
-		if (interp.spriteroll)
 		{
-			fixed_t camAngleDiff = AngleFixed(viewangle) - FLOAT_TO_FIXED(p.angley); // dumb reconversion back, I know
-			fixed_t anglef = AngleFixed(interp.spriteroll);
+			fixed_t anglef = AngleFixed(R_SpriteRotationAngle(&interp));
+
+			p.rollangle = 0.0f;
 
-			p.rollangle = FIXED_TO_FLOAT(anglef);
-			p.roll = true;
+			if (anglef)
+			{
+				fixed_t camAngleDiff = AngleFixed(viewangle) - FLOAT_TO_FIXED(p.angley); // dumb reconversion back, I know
 
-			// rotation pivot
-			p.centerx = FIXED_TO_FLOAT(spr->mobj->radius / 2);
-			p.centery = FIXED_TO_FLOAT(spr->mobj->height / 2);
+				p.rollangle = FIXED_TO_FLOAT(anglef);
+				p.roll = true;
 
-			// rotation axes relative to camera
-			p.rollx = FIXED_TO_FLOAT(FINECOSINE(FixedAngle(camAngleDiff) >> ANGLETOFINESHIFT));
-			p.rollz = FIXED_TO_FLOAT(FINESINE(FixedAngle(camAngleDiff) >> ANGLETOFINESHIFT));
+				// rotation pivot
+				p.centerx = FIXED_TO_FLOAT(spr->mobj->radius / 2);
+				p.centery = FIXED_TO_FLOAT(spr->mobj->height / 2);
+
+				// rotation axes relative to camera
+				p.rollx = FIXED_TO_FLOAT(FINECOSINE(FixedAngle(camAngleDiff) >> ANGLETOFINESHIFT));
+				p.rollz = FIXED_TO_FLOAT(FINESINE(FixedAngle(camAngleDiff) >> ANGLETOFINESHIFT));
+			}
 		}
 
 		p.anglez = FIXED_TO_FLOAT(AngleFixed(interp.pitch));
-- 
GitLab


From c065029b01d6e3bfea4542b5f3443ad3115422fc Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Thu, 9 Dec 2021 22:41:35 -0800
Subject: [PATCH 210/518] Model stretching

---
 src/hardware/hw_drv.h            |  3 +--
 src/hardware/hw_md2.c            |  6 +++++-
 src/hardware/r_opengl/r_opengl.c | 15 ++++++++-------
 3 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h
index 426d2f283b..1c4cd99ab0 100644
--- a/src/hardware/hw_drv.h
+++ b/src/hardware/hw_drv.h
@@ -51,7 +51,7 @@ EXPORT void HWRAPI(ClearMipMapCache) (void);
 EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value);
 
 //Hurdler: added for new development
-EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface);
+EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float hscale, float vscale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface);
 EXPORT void HWRAPI(CreateModelVBOs) (model_t *model);
 EXPORT void HWRAPI(SetTransform) (FTransform *ptransform);
 EXPORT INT32 HWRAPI(GetTextureUsed) (void);
@@ -136,4 +136,3 @@ extern struct hwdriver_s hwdriver;
 #endif //not defined _CREATE_DLL_
 
 #endif //__HWR_DRV_H__
-
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index a20d808480..0f9195a753 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1642,7 +1642,11 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		p.mirror = atransform.mirror;
 
 		HWD.pfnSetShader(SHADER_MODEL);	// model shader
-		HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, hflip, &Surf);
+		{
+			float xs = finalscale * FIXED_TO_FLOAT(spr->mobj->spritexscale);
+			float ys = finalscale * FIXED_TO_FLOAT(spr->mobj->spriteyscale);
+			HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, xs, ys, flip, hflip, &Surf);
+		}
 	}
 
 	return true;
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index d785b3cbf6..71cb5ca703 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -2679,7 +2679,7 @@ EXPORT void HWRAPI(CreateModelVBOs) (model_t *model)
 
 #define BUFFER_OFFSET(i) ((void*)(i))
 
-static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface)
+static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float hscale, float vscale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface)
 {
 	static GLRGBAFloat poly = {0,0,0,0};
 	static GLRGBAFloat tint = {0,0,0,0};
@@ -2703,10 +2703,11 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float
 #endif
 
 	// Affect input model scaling
-	scale *= 0.5f;
-	scalex = scale;
-	scaley = scale;
-	scalez = scale;
+	hscale *= 0.5f;
+	vscale *= 0.5f;
+	scalex = hscale;
+	scaley = vscale;
+	scalez = hscale;
 
 	if (duration > 0.0 && tics >= 0.0) // don't interpolate if instantaneous or infinite in length
 	{
@@ -2964,9 +2965,9 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, float duration, float
 // -----------------+
 // HWRAPI DrawModel : Draw a model
 // -----------------+
-EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface)
+EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, float duration, float tics, INT32 nextFrameIndex, FTransform *pos, float hscale, float vscale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface)
 {
-	DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, scale, flipped, hflipped, Surface);
+	DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, hscale, vscale, flipped, hflipped, Surface);
 }
 
 // -----------------+
-- 
GitLab


From 6d9512d2905ff2ad5defd06de60e044ae5a79932 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Fri, 10 Dec 2021 00:32:46 -0800
Subject: [PATCH 211/518] Do not factor roll and pitch into model rollangle

Those transformations are applied separately so the model
tilts in 3d space.
---
 src/hardware/hw_md2.c |  2 +-
 src/r_patch.h         |  1 +
 src/r_patchrotation.c | 12 ++++++------
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 0f9195a753..096d24f528 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1611,7 +1611,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		}
 
 		{
-			fixed_t anglef = AngleFixed(R_SpriteRotationAngle(&interp));
+			fixed_t anglef = AngleFixed(R_ModelRotationAngle(&interp));
 
 			p.rollangle = 0.0f;
 
diff --git a/src/r_patch.h b/src/r_patch.h
index 27f7bddb6f..a0ab3e75ac 100644
--- a/src/r_patch.h
+++ b/src/r_patch.h
@@ -39,6 +39,7 @@ patch_t *Patch_GetRotatedSprite(
 	size_t frame, size_t spriteangle,
 	boolean flip, boolean adjustfeet,
 	void *info, INT32 rotationangle);
+angle_t R_ModelRotationAngle(interpmobjstate_t *interp);
 angle_t R_SpriteRotationAngle(interpmobjstate_t *interp);
 INT32 R_GetRollAngle(angle_t rollangle);
 #endif
diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c
index f9c4ec797d..a17b725d61 100644
--- a/src/r_patchrotation.c
+++ b/src/r_patchrotation.c
@@ -19,11 +19,11 @@
 fixed_t rollcosang[ROTANGLES];
 fixed_t rollsinang[ROTANGLES];
 
-//
-// R_SpriteRotationAngle
-//
-// Gets the rollangle for the input object.
-//
+angle_t R_ModelRotationAngle(interpmobjstate_t *interp)
+{
+	return interp->spriteroll;
+}
+
 angle_t R_SpriteRotationAngle(interpmobjstate_t *interp)
 {
 	angle_t viewingAngle = R_PointToAngle(interp->x, interp->y);
@@ -33,7 +33,7 @@ angle_t R_SpriteRotationAngle(interpmobjstate_t *interp)
 
 	angle_t rollOrPitch = FixedMul(interp->pitch, pitchMul) + FixedMul(interp->roll, rollMul);
 
-	return (rollOrPitch + interp->spriteroll);
+	return (rollOrPitch + R_ModelRotationAngle(interp));
 }
 
 INT32 R_GetRollAngle(angle_t rollangle)
-- 
GitLab


From a71a7f271036487eb608ef7196c0fe3b3575d321 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Tue, 25 Oct 2022 02:36:35 -0700
Subject: [PATCH 212/518] Support spritexoffset/spriteyoffset for 3D models

---
 src/hardware/hw_main.c |  2 +-
 src/hardware/hw_main.h |  1 +
 src/hardware/hw_md2.c  | 22 ++++++++++++++--------
 3 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 8c1bc09e0a..cb519877c0 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -140,7 +140,7 @@ static fixed_t dup_viewx, dup_viewy, dup_viewz;
 static angle_t dup_viewangle;
 
 static float gl_viewx, gl_viewy, gl_viewz;
-static float gl_viewsin, gl_viewcos;
+float gl_viewsin, gl_viewcos;
 
 // Maybe not necessary with the new T&L code (needs to be checked!)
 static float gl_viewludsin, gl_viewludcos; // look up down kik test
diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h
index 4a1b412fc6..9450ca2c56 100644
--- a/src/hardware/hw_main.h
+++ b/src/hardware/hw_main.h
@@ -115,6 +115,7 @@ extern float gl_viewwindowx, gl_basewindowcentery;
 // BP: big hack for a test in lighting ref : 1249753487AB
 extern fixed_t *hwbbox;
 extern FTransform atransform;
+extern float gl_viewsin, gl_viewcos;
 
 
 // Render stats
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 096d24f528..f0108969f9 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1347,7 +1347,6 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		spritedef_t *sprdef;
 		spriteframe_t *sprframe;
 		INT32 mod;
-		float finalscale;
 		interpmobjstate_t interp;
 
 		if (R_UsingFrameInterpolation() && !paused)
@@ -1451,7 +1450,6 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		}
 
 		//HWD.pfnSetBlend(blend); // This seems to actually break translucency?
-		finalscale = md2->scale;
 		//Hurdler: arf, I don't like that implementation at all... too much crappy
 
 		if (gpatch && hwrPatch && hwrPatch->mipmap->format) // else if meant that if a texture couldn't be loaded, it would just end up using something else's texture
@@ -1635,17 +1633,25 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		p.anglez = FIXED_TO_FLOAT(AngleFixed(interp.pitch));
 		p.anglex = FIXED_TO_FLOAT(AngleFixed(interp.roll));
 
-		// SRB2CBTODO: MD2 scaling support
-		finalscale *= FIXED_TO_FLOAT(interp.scale);
-
 		p.flip = atransform.flip;
 		p.mirror = atransform.mirror;
 
 		HWD.pfnSetShader(SHADER_MODEL);	// model shader
 		{
-			float xs = finalscale * FIXED_TO_FLOAT(spr->mobj->spritexscale);
-			float ys = finalscale * FIXED_TO_FLOAT(spr->mobj->spriteyscale);
-			HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, xs, ys, flip, hflip, &Surf);
+			float this_scale = FIXED_TO_FLOAT(interp.scale);
+
+			float xs = this_scale * FIXED_TO_FLOAT(interp.spritexscale);
+			float ys = this_scale * FIXED_TO_FLOAT(interp.spriteyscale);
+
+			float ox = xs * FIXED_TO_FLOAT(interp.spritexoffset);
+			float oy = ys * FIXED_TO_FLOAT(interp.spriteyoffset);
+
+			// offset perpendicular to the camera angle
+			p.x -= ox * gl_viewsin;
+			p.y += ox * gl_viewcos;
+			p.z += oy;
+
+			HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, md2->scale * xs, md2->scale * ys, flip, hflip, &Surf);
 		}
 	}
 
-- 
GitLab


From bc9e7c146126bdd65f79c8fb5f37930d41b2816e Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Tue, 25 Oct 2022 22:20:20 -0700
Subject: [PATCH 213/518] Disable sprite/model rotation on slopes for now

---
 src/hardware/hw_md2.c | 5 +++++
 src/p_mobj.c          | 5 +++++
 src/r_patchrotation.c | 4 ++++
 3 files changed, 14 insertions(+)

diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index f0108969f9..fca3af217e 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1630,8 +1630,13 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 			}
 		}
 
+#if 0
 		p.anglez = FIXED_TO_FLOAT(AngleFixed(interp.pitch));
 		p.anglex = FIXED_TO_FLOAT(AngleFixed(interp.roll));
+#else
+		p.anglez = 0.f;
+		p.anglex = 0.f;
+#endif
 
 		p.flip = atransform.flip;
 		p.mirror = atransform.mirror;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 9a889d5166..a25b5643bb 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -1556,6 +1556,7 @@ void P_CheckGravity(mobj_t *mo, boolean affect)
 //
 void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope)
 {
+#if 0
 	if (slope)
 	{
 		fixed_t tempz = slope->normal.z;
@@ -1569,6 +1570,10 @@ void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope)
 	{
 		mo->pitch = mo->roll = 0;
 	}
+#else
+	(void)mo;
+	(void)slope;
+#endif
 }
 
 #define STOPSPEED (FRACUNIT)
diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c
index a17b725d61..b0cbeaa429 100644
--- a/src/r_patchrotation.c
+++ b/src/r_patchrotation.c
@@ -26,6 +26,7 @@ angle_t R_ModelRotationAngle(interpmobjstate_t *interp)
 
 angle_t R_SpriteRotationAngle(interpmobjstate_t *interp)
 {
+#if 0
 	angle_t viewingAngle = R_PointToAngle(interp->x, interp->y);
 
 	fixed_t pitchMul = -FINESINE(viewingAngle >> ANGLETOFINESHIFT);
@@ -34,6 +35,9 @@ angle_t R_SpriteRotationAngle(interpmobjstate_t *interp)
 	angle_t rollOrPitch = FixedMul(interp->pitch, pitchMul) + FixedMul(interp->roll, rollMul);
 
 	return (rollOrPitch + R_ModelRotationAngle(interp));
+#else
+	return R_ModelRotationAngle(interp);
+#endif
 }
 
 INT32 R_GetRollAngle(angle_t rollangle)
-- 
GitLab


From 516e06bcba5fd9477ec951be63637eb7a0c65519 Mon Sep 17 00:00:00 2001
From: katsy <205-katsy@users.noreply.git.do.srb2.org>
Date: Mon, 24 Jul 2023 09:12:05 +0000
Subject: [PATCH 214/518] Autobackup old gamedatas (resolves #1031)

---
 src/g_game.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/g_game.c b/src/g_game.c
index b239800447..e188a772be 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -4372,6 +4372,17 @@ void G_LoadGameData(gamedata_t *data)
 		{
 			goto datacorrupt;
 		}
+
+		// make a backup of the old data
+		char currentfilename[64];
+		char backupfilename[69];
+		char bak[5];
+
+		strcpy(bak, ".bak");
+		strcpy(currentfilename, gamedatafilename);
+		STRBUFCPY(backupfilename, strcat(currentfilename, bak));
+
+		FIL_WriteFile(va(pandf, srb2home, backupfilename), savebuffer, length);
 	}
 	else
 #endif
-- 
GitLab


From a9d0630b15293ebdb403db9da979d40b1dac9373 Mon Sep 17 00:00:00 2001
From: Shane Ellis <cobaltbw@gmail.com>
Date: Thu, 8 Jul 2021 18:07:34 -0400
Subject: [PATCH 215/518] Add lives check to Team name HUD

---
 src/st_stuff.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/st_stuff.c b/src/st_stuff.c
index 9cac145a70..3e1638370e 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -884,7 +884,7 @@ static void ST_drawLivesArea(void)
 		}
 	}
 	// Team name
-	else if (G_GametypeHasTeams())
+	else if (G_GametypeHasTeams() && !(gametyperules & GTR_LIVES))
 	{
 		if (stplyr->ctfteam == 1)
 		{
-- 
GitLab


From 84e448eb63a3d805cc9ac98446461975b339378f Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Mon, 24 Jul 2023 06:09:50 -0400
Subject: [PATCH 216/518] Improve handling of team lives labels

The lives counter check is now always run, and team name is displayed in its place when its false.
---
 src/st_stuff.c | 67 ++++++++++++++++++++++++++++++++------------------
 1 file changed, 43 insertions(+), 24 deletions(-)

diff --git a/src/st_stuff.c b/src/st_stuff.c
index 3e1638370e..c6e6befc62 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -815,7 +815,7 @@ static inline void ST_drawRings(void)
 static void ST_drawLivesArea(void)
 {
 	INT32 v_colmap = V_YELLOWMAP, livescount;
-	boolean notgreyedout;
+	boolean notgreyedout = false;
 
 	if (!stplyr->skincolor)
 		return; // Just joined a server, skin isn't loaded yet!
@@ -868,39 +868,36 @@ static void ST_drawLivesArea(void)
 	if (metalrecording)
 	{
 		if (((2*leveltime)/TICRATE) & 1)
+		{
 			V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
 				hudinfo[HUD_LIVES].f|V_PERPLAYER|V_REDMAP|V_HUDTRANS, "REC");
+		}
 	}
 	// Spectator
 	else if (stplyr->spectator)
-		v_colmap = V_GRAYMAP;
-	// Tag
-	else if (gametyperules & GTR_TAG)
 	{
-		if (stplyr->pflags & PF_TAGIT)
-		{
-			V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "IT!");
-			v_colmap = V_ORANGEMAP;
-		}
+		v_colmap = V_GRAYMAP;
 	}
-	// Team name
-	else if (G_GametypeHasTeams() && !(gametyperules & GTR_LIVES))
+	else
 	{
-		if (stplyr->ctfteam == 1)
+		boolean candrawlives = false;
+
+		// Set the player's name color.
+		if (G_TagGametype() && (stplyr->pflags & PF_TAGIT))
 		{
-			V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "RED");
-			v_colmap = V_REDMAP;
+			v_colmap = V_ORANGEMAP;
 		}
-		else if (stplyr->ctfteam == 2)
+		else if (G_GametypeHasTeams())
 		{
-			V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "BLUE");
-			v_colmap = V_BLUEMAP;
+			if (stplyr->ctfteam == 1)
+			{
+				v_colmap = V_REDMAP;
+			}
+			else if (stplyr->ctfteam == 2)
+			{
+				v_colmap = V_BLUEMAP;
+			}
 		}
-	}
-	// Lives number
-	else
-	{
-		boolean candrawlives = true;
 
 		// Co-op and Competition, normal life counter
 		if (G_GametypeUsesLives())
@@ -936,12 +933,15 @@ static void ST_drawLivesArea(void)
 				livescount = (((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0) ? INFLIVES : stplyr->lives);
 				notgreyedout = true;
 			}
+
+			candrawlives = true;
 		}
 		// Infinity symbol (Race)
 		else if (G_PlatformGametype() && !(gametyperules & GTR_LIVES))
 		{
 			livescount = INFLIVES;
 			notgreyedout = true;
+			candrawlives = true;
 		}
 		// Otherwise nothing, sorry.
 		// Special Stages keep not showing lives,
@@ -950,8 +950,6 @@ static void ST_drawLivesArea(void)
 		// cannot show up because Special Stages
 		// still have the GTR_LIVES gametype rule
 		// by default.
-		else
-			candrawlives = false;
 
 		// Draw the lives counter here.
 		if (candrawlives)
@@ -959,8 +957,10 @@ static void ST_drawLivesArea(void)
 			// x
 			V_DrawScaledPatch(hudinfo[HUD_LIVES].x+22, hudinfo[HUD_LIVES].y+10, hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, stlivex);
 			if (livescount == INFLIVES)
+			{
 				V_DrawCharacter(hudinfo[HUD_LIVES].x+50, hudinfo[HUD_LIVES].y+8,
 					'\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false);
+			}
 			else
 			{
 				if (stplyr->playerstate == PST_DEAD && !(stplyr->spectator) && (livescount || stplyr->deadtimer < (TICRATE<<1)) && !(stplyr->pflags & PF_FINISHED))
@@ -971,6 +971,25 @@ static void ST_drawLivesArea(void)
 					hudinfo[HUD_LIVES].f|V_PERPLAYER|(notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF), va("%d",livescount));
 			}
 		}
+		else
+		{
+			// Draw team name instead of lives, if possible.
+			if (G_TagGametype() && (stplyr->pflags & PF_TAGIT))
+			{
+				V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "IT!");
+			}
+			else if (G_GametypeHasTeams())
+			{
+				if (stplyr->ctfteam == 1)
+				{
+					V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "RED");
+				}
+				else if (stplyr->ctfteam == 2)
+				{
+					V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "BLUE");
+				}
+			}
+		}
 #undef ST_drawLivesX
 	}
 
-- 
GitLab


From 86b4f9361001ea1b6ba7c2d0966932e4fd11473c Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Wed, 2 Mar 2022 23:47:15 +0100
Subject: [PATCH 217/518] Allow obtaining ring, time & grade emblems in regular
 SP gameplay.

---
 src/d_netcmd.c      |   2 +-
 src/d_player.h      |   3 +-
 src/g_game.c        | 330 ++++++++++++++++++++++++++------------------
 src/g_game.h        |   3 +-
 src/lua_playerlib.c |   8 ++
 src/m_cond.c        |   2 +-
 src/m_cond.h        |   2 +-
 src/p_saveg.c       |  61 ++++++--
 src/p_setup.c       |   2 +-
 src/p_user.c        |  12 +-
 10 files changed, 267 insertions(+), 158 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 90078f062e..52790b2ce2 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -2339,7 +2339,7 @@ static void Got_Clearscores(UINT8 **cp, INT32 playernum)
 	}
 
 	for (i = 0; i < MAXPLAYERS; i++)
-		players[i].score = 0;
+		players[i].score = players[i].recordscore = 0;
 
 	CONS_Printf(M_GetText("Scores have been reset by the server.\n"));
 }
diff --git a/src/d_player.h b/src/d_player.h
index f407c29e9b..7ad5b9f811 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -453,7 +453,8 @@ typedef struct player_s
 	INT32 skin;
 	UINT32 availabilities;
 
-	UINT32 score; // player score
+	UINT32 score; // player score (total)
+	UINT32 recordscore; // player score (per life / map)
 	fixed_t dashspeed; // dashing speed
 
 	fixed_t normalspeed; // Normal ground
diff --git a/src/g_game.c b/src/g_game.c
index 5717565032..81942f462d 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -519,140 +519,154 @@ UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare, gamedata_t *data)
 }
 
 // For easy adding of NiGHTS records
-void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare)
+void G_AddTempNightsRecords(player_t *player, UINT32 pscore, tic_t ptime, UINT8 mare)
 {
-	ntemprecords.score[mare] = pscore;
-	ntemprecords.grade[mare] = P_GetGrade(pscore, gamemap, mare - 1);
-	ntemprecords.time[mare] = ptime;
+	const UINT8 playerID = player - players;
+
+	I_Assert(player != NULL);
+
+	ntemprecords[playerID].score[mare] = pscore;
+	ntemprecords[playerID].grade[mare] = P_GetGrade(pscore, gamemap, mare - 1);
+	ntemprecords[playerID].time[mare] = ptime;
 
 	// Update nummares
 	// Note that mare "0" is overall, mare "1" is the first real mare
-	if (ntemprecords.nummares < mare)
-		ntemprecords.nummares = mare;
+	if (ntemprecords[playerID].nummares < mare)
+		ntemprecords[playerID].nummares = mare;
 }
 
 //
-// G_UpdateRecordReplays
+// G_SetMainRecords
 //
 // Update replay files/data, etc. for Record Attack
 // See G_SetNightsRecords for NiGHTS Attack.
 //
-static void G_UpdateRecordReplays(gamedata_t *data)
+static void G_SetMainRecords(gamedata_t *data, player_t *player)
 {
-	const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
-	char *gpath;
-	char lastdemo[256], bestdemo[256];
 	UINT8 earnedEmblems;
 
+	I_Assert(player != NULL);
+
 	// Record new best time
 	if (!data->mainrecords[gamemap-1])
 		G_AllocMainRecordData(gamemap-1, data);
 
-	if (players[consoleplayer].score > data->mainrecords[gamemap-1]->score)
-		data->mainrecords[gamemap-1]->score = players[consoleplayer].score;
+	if (player->recordscore > data->mainrecords[gamemap-1]->score)
+		data->mainrecords[gamemap-1]->score = player->recordscore;
 
-	if ((data->mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < data->mainrecords[gamemap-1]->time))
-		data->mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
+	if ((data->mainrecords[gamemap-1]->time == 0) || (player->realtime < data->mainrecords[gamemap-1]->time))
+		data->mainrecords[gamemap-1]->time = player->realtime;
 
-	if ((UINT16)(players[consoleplayer].rings) > data->mainrecords[gamemap-1]->rings)
-		data->mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
+	if ((UINT16)(player->rings) > data->mainrecords[gamemap-1]->rings)
+		data->mainrecords[gamemap-1]->rings = (UINT16)(player->rings);
 
-	// Save demo!
-	bestdemo[255] = '\0';
-	lastdemo[255] = '\0';
-	G_SetDemoTime(players[consoleplayer].realtime, players[consoleplayer].score, (UINT16)(players[consoleplayer].rings));
-	G_CheckDemoStatus();
+	if (modeattacking)
+	{
+		const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
+		char *gpath;
+		char lastdemo[256], bestdemo[256];
 
-	I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
-	I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
+		// Save demo!
+		bestdemo[255] = '\0';
+		lastdemo[255] = '\0';
+		G_SetDemoTime(player->realtime, player->recordscore, (UINT16)(player->rings));
+		G_CheckDemoStatus();
 
-	if ((gpath = malloc(glen)) == NULL)
-		I_Error("Out of memory for replay filepath\n");
+		I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
+		I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
 
-	sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
-	snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
+		if ((gpath = malloc(glen)) == NULL)
+			I_Error("Out of memory for replay filepath\n");
 
-	if (FIL_FileExists(lastdemo))
-	{
-		UINT8 *buf;
-		size_t len = FIL_ReadFile(lastdemo, &buf);
+		sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
+		snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
 
-		snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
-		if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
-		{ // Better time, save this demo.
-			if (FIL_FileExists(bestdemo))
-				remove(bestdemo);
-			FIL_WriteFile(bestdemo, buf, len);
-			CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
-		}
+		if (FIL_FileExists(lastdemo))
+		{
+			UINT8 *buf;
+			size_t len = FIL_ReadFile(lastdemo, &buf);
+
+			snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
+			if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
+			{ // Better time, save this demo.
+				if (FIL_FileExists(bestdemo))
+					remove(bestdemo);
+				FIL_WriteFile(bestdemo, buf, len);
+				CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
+			}
 
-		snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
-		if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
-		{ // Better score, save this demo.
-			if (FIL_FileExists(bestdemo))
-				remove(bestdemo);
-			FIL_WriteFile(bestdemo, buf, len);
-			CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
-		}
+			snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
+			if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
+			{ // Better score, save this demo.
+				if (FIL_FileExists(bestdemo))
+					remove(bestdemo);
+				FIL_WriteFile(bestdemo, buf, len);
+				CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
+			}
 
-		snprintf(bestdemo, 255, "%s-%s-rings-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
-		if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<2)))
-		{ // Better rings, save this demo.
-			if (FIL_FileExists(bestdemo))
-				remove(bestdemo);
-			FIL_WriteFile(bestdemo, buf, len);
-			CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW MOST RINGS!"), M_GetText("Saved replay as"), bestdemo);
-		}
+			snprintf(bestdemo, 255, "%s-%s-rings-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
+			if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<2)))
+			{ // Better rings, save this demo.
+				if (FIL_FileExists(bestdemo))
+					remove(bestdemo);
+				FIL_WriteFile(bestdemo, buf, len);
+				CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW MOST RINGS!"), M_GetText("Saved replay as"), bestdemo);
+			}
 
-		//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
+			//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
 
-		Z_Free(buf);
+			Z_Free(buf);
+		}
+		free(gpath);
 	}
-	free(gpath);
 
 	// Check emblems when level data is updated
 	if ((earnedEmblems = M_CheckLevelEmblems(data)))
+	{
 		CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
+	}
 
 	// Update timeattack menu's replay availability.
 	Nextmap_OnChange();
 }
 
-void G_SetNightsRecords(gamedata_t *data)
+static void G_SetNightsRecords(gamedata_t *data, player_t *player)
 {
-	INT32 i;
+	nightsdata_t *const ntemprecord = &ntemprecords[player - players];
 	UINT32 totalscore = 0;
 	tic_t totaltime = 0;
-	UINT8 earnedEmblems;
+	INT32 i;
 
-	const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
-	char *gpath;
-	char lastdemo[256], bestdemo[256];
+	UINT8 earnedEmblems;
 
-	if (!ntemprecords.nummares)
+	if (!ntemprecord->nummares)
+	{
 		return;
+	}
 
 	// Set overall
 	{
 		UINT8 totalrank = 0, realrank = 0;
 
-		for (i = 1; i <= ntemprecords.nummares; ++i)
+		for (i = 1; i <= ntemprecord->nummares; ++i)
 		{
-			totalscore += ntemprecords.score[i];
-			totalrank += ntemprecords.grade[i];
-			totaltime += ntemprecords.time[i];
+			totalscore += ntemprecord->score[i];
+			totalrank += ntemprecord->grade[i];
+			totaltime += ntemprecord->time[i];
 		}
 
 		// Determine overall grade
-		realrank = (UINT8)((FixedDiv((fixed_t)totalrank << FRACBITS, ntemprecords.nummares << FRACBITS) + (FRACUNIT/2)) >> FRACBITS);
+		realrank = (UINT8)((FixedDiv((fixed_t)totalrank << FRACBITS, ntemprecord->nummares << FRACBITS) + (FRACUNIT/2)) >> FRACBITS);
 
 		// You need ALL rainbow As to get a rainbow A overall
-		if (realrank == GRADE_S && (totalrank / ntemprecords.nummares) != GRADE_S)
+		if (realrank == GRADE_S && (totalrank / ntemprecord->nummares) != GRADE_S)
+		{
 			realrank = GRADE_A;
+		}
 
-		ntemprecords.score[0] = totalscore;
-		ntemprecords.grade[0] = realrank;
-		ntemprecords.time[0] = totaltime;
+		ntemprecord->score[0] = totalscore;
+		ntemprecord->grade[0] = realrank;
+		ntemprecord->time[0] = totaltime;
 	}
 
 	// Now take all temp records and put them in the actual records
@@ -660,71 +674,85 @@ void G_SetNightsRecords(gamedata_t *data)
 		nightsdata_t *maprecords;
 
 		if (!data->nightsrecords[gamemap-1])
+		{
 			G_AllocNightsRecordData(gamemap-1, data);
+		}
+
 		maprecords = data->nightsrecords[gamemap-1];
 
-		if (maprecords->nummares != ntemprecords.nummares)
-			maprecords->nummares = ntemprecords.nummares;
+		if (maprecords->nummares != ntemprecord->nummares)
+		{
+			maprecords->nummares = ntemprecord->nummares;
+		}
 
-		for (i = 0; i < ntemprecords.nummares + 1; ++i)
+		for (i = 0; i < ntemprecord->nummares + 1; ++i)
 		{
-			if (maprecords->score[i] < ntemprecords.score[i])
-				maprecords->score[i] = ntemprecords.score[i];
-			if (maprecords->grade[i] < ntemprecords.grade[i])
-				maprecords->grade[i] = ntemprecords.grade[i];
-			if (!maprecords->time[i] || maprecords->time[i] > ntemprecords.time[i])
-				maprecords->time[i] = ntemprecords.time[i];
+			if (maprecords->score[i] < ntemprecord->score[i])
+				maprecords->score[i] = ntemprecord->score[i];
+			if (maprecords->grade[i] < ntemprecord->grade[i])
+				maprecords->grade[i] = ntemprecord->grade[i];
+			if (!maprecords->time[i] || maprecords->time[i] > ntemprecord->time[i])
+				maprecords->time[i] = ntemprecord->time[i];
 		}
 	}
 
-	memset(&ntemprecords, 0, sizeof(nightsdata_t));
+	memset(&ntemprecords[player - players], 0, sizeof(nightsdata_t));
 
-	// Save demo!
-	bestdemo[255] = '\0';
-	lastdemo[255] = '\0';
-	G_SetDemoTime(totaltime, totalscore, 0);
-	G_CheckDemoStatus();
+	if (modeattacking)
+	{
+		const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
+		char *gpath;
+		char lastdemo[256], bestdemo[256];
 
-	I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
-	I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
+		// Save demo!
+		bestdemo[255] = '\0';
+		lastdemo[255] = '\0';
+		G_SetDemoTime(totaltime, totalscore, 0);
+		G_CheckDemoStatus();
 
-	if ((gpath = malloc(glen)) == NULL)
-		I_Error("Out of memory for replay filepath\n");
+		I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
+		I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
 
-	sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
-	snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
+		if ((gpath = malloc(glen)) == NULL)
+			I_Error("Out of memory for replay filepath\n");
 
-	if (FIL_FileExists(lastdemo))
-	{
-		UINT8 *buf;
-		size_t len = FIL_ReadFile(lastdemo, &buf);
+		sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
+		snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
 
-		snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);;
-		if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
-		{ // Better time, save this demo.
-			if (FIL_FileExists(bestdemo))
-				remove(bestdemo);
-			FIL_WriteFile(bestdemo, buf, len);
-			CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
-		}
+		if (FIL_FileExists(lastdemo))
+		{
+			UINT8 *buf;
+			size_t len = FIL_ReadFile(lastdemo, &buf);
+
+			snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);;
+			if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
+			{ // Better time, save this demo.
+				if (FIL_FileExists(bestdemo))
+					remove(bestdemo);
+				FIL_WriteFile(bestdemo, buf, len);
+				CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
+			}
 
-		snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
-		if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
-		{ // Better score, save this demo.
-			if (FIL_FileExists(bestdemo))
-				remove(bestdemo);
-			FIL_WriteFile(bestdemo, buf, len);
-			CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
-		}
+			snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
+			if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
+			{ // Better score, save this demo.
+				if (FIL_FileExists(bestdemo))
+					remove(bestdemo);
+				FIL_WriteFile(bestdemo, buf, len);
+				CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
+			}
 
-		//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
+			//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
 
-		Z_Free(buf);
+			Z_Free(buf);
+		}
+		free(gpath);
 	}
-	free(gpath);
 
 	if ((earnedEmblems = M_CheckLevelEmblems(data)))
+	{
 		CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for NiGHTS records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
+	}
 
 	// If the mare count changed, this will update the score display
 	Nextmap_OnChange();
@@ -2287,7 +2315,7 @@ void G_Ticker(boolean run)
 					p->lives = startinglivesbalance[0];
 					p->continues = 1;
 
-					p->score = 0;
+					p->score = p->recordscore = 0;
 
 					// The latter two should clear by themselves, but just in case
 					p->pflags &= ~(PF_TAGIT|PF_GAMETYPEOVER|PF_FULLSTASIS);
@@ -2490,6 +2518,7 @@ static inline void G_PlayerFinishLevel(INT32 player)
 
 	memset(p->powers, 0, sizeof (p->powers));
 	p->ringweapons = 0;
+	p->recordscore = 0;
 
 	p->mo->flags2 &= ~MF2_SHADOW; // cancel invisibility
 	P_FlashPal(p, 0, 0); // Resets
@@ -2513,6 +2542,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 {
 	player_t *p;
 	INT32 score;
+	INT32 recordscore;
 	INT32 lives;
 	INT32 continues;
 	fixed_t camerascale;
@@ -3165,6 +3195,7 @@ void G_DoReborn(INT32 playernum)
 			{
 				if (!playeringame[i])
 					continue;
+				players[i].recordscore = 0;
 				players[i].starpostscale = 0;
 				players[i].starpostangle = 0;
 				players[i].starposttime = 0;
@@ -3844,15 +3875,16 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
 //
 // G_UpdateVisited
 //
-static void G_UpdateVisited(gamedata_t *data, boolean silent)
+static void G_UpdateVisited(gamedata_t *data, player_t *player, boolean silent)
 {
-	boolean spec = G_IsSpecialStage(gamemap);
 	// Update visitation flags?
 	if (!demoplayback
 		&& G_CoopGametype() // Campaign mode
 		&& !stagefailed) // Did not fail the stage
 	{
 		UINT8 earnedEmblems;
+		UINT16 totalrings = 0;
+		INT32 i;
 
 		// Update visitation flags
 		data->mapvisited[gamemap-1] |= MV_BEATEN;
@@ -3861,36 +3893,62 @@ static void G_UpdateVisited(gamedata_t *data, boolean silent)
 		if (ultimatemode)
 			data->mapvisited[gamemap-1] |= MV_ULTIMATE;
 
+		for (i = 0; i < MAXPLAYERS; i++)
+		{
+			if (!playeringame[i])
+			{
+				continue;
+			}
+
+			totalrings += players[i].rings;
+		}
+
 		// may seem incorrect but IS possible in what the main game uses as mp special stages, and nummaprings will be -1 in NiGHTS
-		if (nummaprings > 0 && players[consoleplayer].rings >= nummaprings)
+		if (nummaprings > 0 && totalrings >= nummaprings)
 		{
 			data->mapvisited[gamemap-1] |= MV_PERFECT;
 			if (modeattacking)
 				data->mapvisited[gamemap-1] |= MV_PERFECTRA;
 		}
 
-		if (!spec)
+		if (!G_IsSpecialStage(gamemap))
 		{
 			// not available to special stages because they can only really be done in one order in an unmodified game, so impossible for first six and trivial for seventh
 			if (ALL7EMERALDS(emeralds))
 				data->mapvisited[gamemap-1] |= MV_ALLEMERALDS;
 		}
 
+		if ((earnedEmblems = M_CompletionEmblems(data)) && !silent)
+		{
+			CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
+		}
+
 		if (silent)
 		{
-			if (modeattacking)
-				M_CheckLevelEmblems(data);
+			M_CheckLevelEmblems(data);
 		}
 		else
 		{
-			if (modeattacking == ATTACKING_RECORD)
-				G_UpdateRecordReplays(data);
-			else if (modeattacking == ATTACKING_NIGHTS)
-				G_SetNightsRecords(data);
+			if (mapheaderinfo[gamemap-1]->menuflags & LF2_RECORDATTACK)
+				G_SetMainRecords(data, player);
+			else if (mapheaderinfo[gamemap-1]->menuflags & LF2_NIGHTSATTACK)
+				G_SetNightsRecords(data, player);
 		}
+	}
+}
 
-		if ((earnedEmblems = M_CompletionEmblems(data)) && !silent)
-			CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
+static void G_UpdateAllVisited(void)
+{
+	// Update server
+	G_UpdateVisited(serverGamedata, &players[serverplayer], true);
+
+	// Update client
+	G_UpdateVisited(clientGamedata, &players[consoleplayer], false);
+
+	if (splitscreen)
+	{
+		// Allow P2 to get emblems too, why not :)
+		G_UpdateVisited(clientGamedata, &players[secondarydisplayplayer], false);
 	}
 }
 
@@ -3914,6 +3972,9 @@ static boolean CanSaveLevel(INT32 mapnum)
 
 static void G_HandleSaveLevel(void)
 {
+	// Update records & emblems
+	G_UpdateAllVisited();
+
 	// do this before running the intermission or custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c
 	if (nextmap >= 1100-1)
 	{
@@ -4092,8 +4153,6 @@ static void G_DoCompleted(void)
 
 	if ((skipstats && !modeattacking) || (modeattacking && stagefailed) || (intertype == int_none))
 	{
-		G_UpdateVisited(serverGamedata, true);
-		G_UpdateVisited(clientGamedata, false);
 		G_HandleSaveLevel();
 		G_AfterIntermission();
 	}
@@ -4102,8 +4161,6 @@ static void G_DoCompleted(void)
 		G_SetGamestate(GS_INTERMISSION);
 		Y_StartIntermission();
 		Y_LoadIntermissionData();
-		G_UpdateVisited(serverGamedata, true);
-		G_UpdateVisited(clientGamedata, false);
 		G_HandleSaveLevel();
 	}
 }
@@ -4992,6 +5049,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
 			players[i].playerstate = PST_REBORN;
 			players[i].starpostscale = players[i].starpostangle = players[i].starpostnum = players[i].starposttime = 0;
 			players[i].starpostx = players[i].starposty = players[i].starpostz = 0;
+			players[i].recordscore = 0;
 
 			if (netgame || multiplayer)
 			{
diff --git a/src/g_game.h b/src/g_game.h
index 6cda7ca9cc..a8c285f79f 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -260,8 +260,7 @@ UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare, gamedata_t *data);
 tic_t G_GetBestNightsTime(INT16 map, UINT8 mare, gamedata_t *data);
 UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare, gamedata_t *data);
 
-void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare);
-void G_SetNightsRecords(gamedata_t *data);
+void G_AddTempNightsRecords(player_t *player, UINT32 pscore, tic_t ptime, UINT8 mare);
 
 FUNCMATH INT32 G_TicsToHours(tic_t tics);
 FUNCMATH INT32 G_TicsToMinutes(tic_t tics, boolean full);
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index c056be463e..055acfd800 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -114,6 +114,7 @@ enum player_e
 	player_skin,
 	player_availabilities,
 	player_score,
+	player_recordscore,
 	player_dashspeed,
 	player_normalspeed,
 	player_runspeed,
@@ -260,6 +261,7 @@ static const char *const player_opt[] = {
 	"skin",
 	"availabilities",
 	"score",
+	"recordscore",
 	"dashspeed",
 	"normalspeed",
 	"runspeed",
@@ -495,6 +497,9 @@ static int player_get(lua_State *L)
 	case player_score:
 		lua_pushinteger(L, plr->score);
 		break;
+	case player_recordscore:
+		lua_pushinteger(L, plr->recordscore);
+		break;
 	case player_dashspeed:
 		lua_pushfixed(L, plr->dashspeed);
 		break;
@@ -957,6 +962,9 @@ static int player_set(lua_State *L)
 	case player_score:
 		plr->score = (UINT32)luaL_checkinteger(L, 3);
 		break;
+	case player_recordscore:
+		plr->recordscore = (UINT32)luaL_checkinteger(L, 3);
+		break;
 	case player_dashspeed:
 		plr->dashspeed = luaL_checkfixed(L, 3);
 		break;
diff --git a/src/m_cond.c b/src/m_cond.c
index a54988f67a..b21778c697 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -45,7 +45,7 @@ INT32 numemblems = 0;
 INT32 numextraemblems = 0;
 
 // Temporary holding place for nights data for the current map
-nightsdata_t ntemprecords;
+nightsdata_t ntemprecords[MAXPLAYERS];
 
 // Create a new gamedata_t, for start-up
 gamedata_t *M_NewGameDataStruct(void)
diff --git a/src/m_cond.h b/src/m_cond.h
index 95c3e1ebeb..2be8d564be 100644
--- a/src/m_cond.h
+++ b/src/m_cond.h
@@ -181,7 +181,7 @@ typedef struct
 #define MV_MAX         63 // used in gamedata check, update whenever MV's are added
 
 // Temporary holding place for nights data for the current map
-extern nightsdata_t ntemprecords;
+extern nightsdata_t ntemprecords[MAXPLAYERS];
 
 // GAMEDATA STRUCTURE
 // Everything that would get saved in gamedata.dat
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 8eac911056..3565d01257 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -179,6 +179,7 @@ static void P_NetArchivePlayers(void)
 		WRITEINT32(save_p, players[i].skin);
 		WRITEUINT32(save_p, players[i].availabilities);
 		WRITEUINT32(save_p, players[i].score);
+		WRITEUINT32(save_p, players[i].recordscore);
 		WRITEFIXED(save_p, players[i].dashspeed);
 		WRITESINT8(save_p, players[i].lives);
 		WRITESINT8(save_p, players[i].continues);
@@ -407,6 +408,7 @@ static void P_NetUnArchivePlayers(void)
 		players[i].skin = READINT32(save_p);
 		players[i].availabilities = READUINT32(save_p);
 		players[i].score = READUINT32(save_p);
+		players[i].recordscore = READUINT32(save_p);
 		players[i].dashspeed = READFIXED(save_p); // dashing speed
 		players[i].lives = READSINT8(save_p);
 		players[i].continues = READSINT8(save_p); // continues that player has acquired
@@ -4342,8 +4344,6 @@ static void P_NetArchiveMisc(boolean resending)
 
 	WRITEUINT32(save_p, hidetime);
 
-	WRITEUINT32(save_p, unlocktriggers);
-
 	// Is it paused?
 	if (paused)
 		WRITEUINT8(save_p, 0x2f);
@@ -4442,8 +4442,6 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
 
 	hidetime = READUINT32(save_p);
 
-	unlocktriggers = READUINT32(save_p);
-
 	// Is it paused?
 	if (READUINT8(save_p) == 0x2f)
 		paused = true;
@@ -4451,7 +4449,6 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
 	return true;
 }
 
-
 static inline void P_NetArchiveEmblems(void)
 {
 	gamedata_t *data = serverGamedata;
@@ -4550,6 +4547,27 @@ static inline void P_NetArchiveEmblems(void)
 			WRITEUINT32(save_p, data->nightsrecords[i]->time[curmare]);
 		}
 	}
+
+	// Mid-map stuff
+	WRITEUINT32(save_p, unlocktriggers);
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!ntemprecords[i].nummares)
+		{
+			WRITEUINT8(save_p, 0);
+			continue;
+		}
+
+		WRITEUINT8(save_p, ntemprecords[i].nummares);
+
+		for (curmare = 0; curmare < (ntemprecords[i].nummares + 1); ++curmare)
+		{
+			WRITEUINT32(save_p, ntemprecords[i].score[curmare]);
+			WRITEUINT8(save_p, ntemprecords[i].grade[curmare]);
+			WRITEUINT32(save_p, ntemprecords[i].time[curmare]);
+		}
+	}
 }
 
 static inline void P_NetUnArchiveEmblems(void)
@@ -4569,9 +4587,9 @@ static inline void P_NetUnArchiveEmblems(void)
 	savemoddata = (boolean)READUINT8(save_p); // this one is actually necessary because savemoddata stays false otherwise for some reason.
 
 	if (numemblems != READINT32(save_p))
-		I_Error("numemblems mismatch");
+		I_Error("Bad $$$.sav dearchiving Emblems (numemblems mismatch)");
 	if (numextraemblems != READINT32(save_p))
-		I_Error("numextraemblems mismatch");
+		I_Error("Bad $$$.sav dearchiving Emblems (numextraemblems mismatch)");
 
 	// This shouldn't happen, but if something really fucked up happens and you transfer
 	// the SERVER player's gamedata over your own CLIENT gamedata,
@@ -4590,7 +4608,7 @@ static inline void P_NetUnArchiveEmblems(void)
 	// TODO put another cipher on these things? meh, I don't care...
 	for (i = 0; i < NUMMAPS; i++)
 		if ((data->mapvisited[i] = READUINT8(save_p)) > MV_MAX)
-			I_Error("Bad $$$.sav dearchiving Emblems");
+			I_Error("Bad $$$.sav dearchiving Emblems (invalid visit flags)");
 
 	// To save space, use one bit per collected/achieved/unlocked flag
 	for (i = 0; i < MAXEMBLEMS;)
@@ -4634,7 +4652,7 @@ static inline void P_NetUnArchiveEmblems(void)
 		recrings = READUINT16(save_p);
 
 		if (recrings > 10000 || recscore > MAXSCORE)
-			I_Error("Bad $$$.sav dearchiving Emblems");
+			I_Error("Bad $$$.sav dearchiving Emblems (invalid score)");
 
 		if (recscore || rectime || recrings)
 		{
@@ -4661,12 +4679,35 @@ static inline void P_NetUnArchiveEmblems(void)
 
 			if (data->nightsrecords[i]->grade[curmare] > GRADE_S)
 			{
-				I_Error("Bad $$$.sav dearchiving Emblems");
+				I_Error("Bad $$$.sav dearchiving Emblems (invalid grade)");
 			}
 		}
 
 		data->nightsrecords[i]->nummares = recmares;
 	}
+
+	// Mid-map stuff
+	unlocktriggers = READUINT32(save_p);
+
+	for (i = 0; i < MAXPLAYERS; ++i)
+	{
+		if ((recmares = READUINT8(save_p)) == 0)
+			continue;
+
+		for (curmare = 0; curmare < (recmares+1); ++curmare)
+		{
+			ntemprecords[i].score[curmare] = READUINT32(save_p);
+			ntemprecords[i].grade[curmare] = READUINT8(save_p);
+			ntemprecords[i].time[curmare] = (tic_t)READUINT32(save_p);
+
+			if (ntemprecords[i].grade[curmare] > GRADE_S)
+			{
+				I_Error("Bad $$$.sav dearchiving Emblems (invalid temp grade)");
+			}
+		}
+
+		ntemprecords[i].nummares = recmares;
+	}
 }
 
 static inline void P_ArchiveLuabanksAndConsistency(void)
diff --git a/src/p_setup.c b/src/p_setup.c
index bfb70751d0..051853017b 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -6985,7 +6985,7 @@ static void P_InitLevelSettings(void)
 	stagefailed = G_IsSpecialStage(gamemap);
 
 	// Reset temporary record data
-	memset(&ntemprecords, 0, sizeof(nightsdata_t));
+	memset(&ntemprecords, 0, sizeof(ntemprecords));
 
 	// earthquake camera
 	memset(&quake,0,sizeof(struct quake));
diff --git a/src/p_user.c b/src/p_user.c
index b89552673e..a0de50b8f0 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -882,8 +882,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 			}
 
 			// Add score to leaderboards now
-			if (!(netgame||multiplayer) && P_IsLocalPlayer(&players[i]))
-				G_AddTempNightsRecords(players[i].marescore, leveltime - player->marebegunat, players[i].mare + 1);
+			G_AddTempNightsRecords(player, players[i].marescore, leveltime - player->marebegunat, players[i].mare + 1);
 
 			// transfer scores anyway
 			players[i].totalmarescore += players[i].marescore;
@@ -909,8 +908,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 		player->finishedrings = (INT16)(player->rings);
 
 		// Add score to temp leaderboards
-		if (!(netgame||multiplayer) && P_IsLocalPlayer(player))
-			G_AddTempNightsRecords(player->marescore, leveltime - player->marebegunat, (UINT8)(oldmare + 1));
+		G_AddTempNightsRecords(player, player->marescore, leveltime - player->marebegunat, (UINT8)(oldmare + 1));
 
 		// Starting a new mare, transfer scores
 		player->totalmarescore += player->marescore;
@@ -1440,6 +1438,10 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
 	if (player->score > MAXSCORE)
 		player->score = MAXSCORE;
 
+	player->recordscore += amount;
+	if (player->recordscore > MAXSCORE)
+		player->recordscore = MAXSCORE;
+
 	// check for extra lives every 50000 pts
 	if (!ultimatemode && !modeattacking && player->score > oldscore && player->score % 50000 < amount && (gametyperules & GTR_LIVES))
 	{
@@ -11732,7 +11734,7 @@ void P_PlayerThink(player_t *player)
 	if (player->spectator)
 	{
 		if (!(gametyperules & GTR_CAMPAIGN))
-			player->score = 0;
+			player->score = player->recordscore = 0;
 	}
 	else if ((netgame || multiplayer) && player->lives <= 0 && !G_CoopGametype())
 	{
-- 
GitLab


From 8bddf0f097a2886a5edf642acca3aa9ab304626d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Mon, 24 Jul 2023 17:12:24 +0200
Subject: [PATCH 218/518] fixup! fixup! fixup! Fix build errors when building
 without SDL

---
 src/sdl/Srb2SDL-vc10.vcxproj | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index c20265ed17..0b95cd0b2e 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -259,7 +259,7 @@
     <ClInclude Include="..\hardware\hw_md2load.h" />
     <ClInclude Include="..\hardware\hw_md3load.h" />
     <ClInclude Include="..\hardware\hw_model.h" />
-    <ClInclude Include="..\hardware\u_list.h" />
+    <ClInclude Include="..\u_list.h" />
     <ClInclude Include="..\hu_stuff.h" />
     <ClInclude Include="..\info.h" />
     <ClInclude Include="..\i_addrinfo.h" />
@@ -424,7 +424,7 @@
     <ClCompile Include="..\hardware\hw_md3load.c" />
     <ClCompile Include="..\hardware\hw_model.c" />
     <ClCompile Include="..\hardware\r_opengl\r_opengl.c" />
-    <ClCompile Include="..\hardware\u_list.c" />
+    <ClCompile Include="..\u_list.c" />
     <ClCompile Include="..\hu_stuff.c" />
     <ClCompile Include="..\info.c" />
     <ClCompile Include="..\i_addrinfo.c">
@@ -556,4 +556,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
-- 
GitLab


From b36ca89e1646a370fd3be933ff066ddbf1fd60b8 Mon Sep 17 00:00:00 2001
From: Jisk <danielthesmartypants2017+jisk@gmail.com>
Date: Mon, 24 Jul 2023 18:00:31 +0000
Subject: [PATCH 219/518] Port player.ping from SRB2Kart

---
 src/lua_playerlib.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index f76ec16894..ae3c06d312 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -222,6 +222,7 @@ enum player_e
 	player_blocked,
 	player_jointime,
 	player_quittime,
+	player_ping,
 #ifdef HWRENDER
 	player_fovadd,
 #endif
@@ -368,6 +369,7 @@ static const char *const player_opt[] = {
 	"blocked",
 	"jointime",
 	"quittime",
+	"ping",
 #ifdef HWRENDER
 	"fovadd",
 #endif
@@ -819,6 +821,9 @@ static int player_get(lua_State *L)
 	case player_quittime:
 		lua_pushinteger(L, plr->quittime);
 		break;
+	case player_ping:
+		lua_pushinteger(L, playerpingtable[plr - players]);
+		break;
 #ifdef HWRENDER
 	case player_fovadd:
 		lua_pushfixed(L, plr->fovadd);
-- 
GitLab


From 4212035729c11197f2502b6f50f1eb4e7bf17b89 Mon Sep 17 00:00:00 2001
From: GoldenTails <milestailsprower101n2@gmail.com>
Date: Mon, 24 Jul 2023 21:44:53 -0500
Subject: [PATCH 220/518] Fix errors due to declaring variables in switch
 bodies

---
 src/lua_maplib.c    | 2 ++
 src/lua_mobjlib.c   | 2 ++
 src/lua_playerlib.c | 2 ++
 3 files changed, 6 insertions(+)

diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 568a99b68f..3d95cdb751 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -2822,6 +2822,7 @@ static int mapheaderinfo_get(lua_State *L)
 		break;
 	// TODO add support for reading numGradedMares and grades
 	default:
+	{
 		// Read custom vars now
 		// (note: don't include the "LUA." in your lua scripts!)
 		UINT8 j = 0;
@@ -2832,6 +2833,7 @@ static int mapheaderinfo_get(lua_State *L)
 		else
 			lua_pushnil(L);
 	}
+	}
 	return 1;
 }
 
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 404af016a6..851c569d27 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -1043,11 +1043,13 @@ static int mapthing_set(lua_State *L)
 			mt->z = (INT16)luaL_checkinteger(L, 3);
 			break;
 		case mapthing_extrainfo:
+		{
 			INT32 extrainfo = luaL_checkinteger(L, 3);
 			if (extrainfo & ~15)
 				return luaL_error(L, "mapthing_t extrainfo set %d out of range (%d - %d)", extrainfo, 0, 15);
 			mt->extrainfo = (UINT8)extrainfo;
 			break;
+		}
 		case mapthing_tag:
 			Tag_FSet(&mt->tags, (INT16)luaL_checkinteger(L, 3));
 			break;
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index f0fa2ed94b..510e481e66 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -1297,6 +1297,7 @@ static int player_set(lua_State *L)
 		break;
 	}
 	case player_awayviewtics:
+	{
 		INT32 tics = (INT32)luaL_checkinteger(L, 3);
 		if (tics && !plr->awayviewmobj) // awayviewtics must ALWAYS have an awayviewmobj set!!
 			P_SetTarget(&plr->awayviewmobj, plr->mo); // but since the script might set awayviewmobj immediately AFTER setting awayviewtics, use player mobj as filler for now.
@@ -1308,6 +1309,7 @@ static int player_set(lua_State *L)
 		}
 		plr->awayviewtics = tics;
 		break;
+	}
 	case player_awayviewaiming:
 		plr->awayviewaiming = luaL_checkangle(L, 3);
 		break;
-- 
GitLab


From 1a2f66acd9b14b4c64a6a4f6db049d097ac9c852 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Tue, 25 Jul 2023 06:47:32 -0400
Subject: [PATCH 221/518] Update .gitignore

---
 .gitignore | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index cd828dc116..2b4d9d2fbf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,4 +22,4 @@ Win32_LIB_ASM_Release
 /make
 /bin
 /build
-/build.*
+/build/*
-- 
GitLab


From 77576c3c5d526651074ec6b9f4c664671e0ab273 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Tue, 25 Jul 2023 08:40:36 -0400
Subject: [PATCH 222/518] Allow Record Attack in modified games

Replays now store & validate the add-on list before loading them. Custom characters can now be allowed in Record Attack.
---
 src/d_netcmd.c |  19 ++-
 src/f_finale.c |   1 +
 src/g_demo.c   | 387 ++++++++++++++++++++++++++++++++++++++++++++++---
 src/g_demo.h   |  22 +++
 src/m_menu.c   |  91 ++++++++++--
 5 files changed, 486 insertions(+), 34 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 72d020c22a..45a394eff5 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1643,9 +1643,14 @@ static void Command_Playdemo_f(void)
 {
 	char name[256];
 
-	if (COM_Argc() != 2)
+	if (COM_Argc() < 2)
 	{
-		CONS_Printf(M_GetText("playdemo <demoname>: playback a demo\n"));
+		CONS_Printf("playdemo <demoname> [-addfiles / -force]:\n");
+		CONS_Printf(M_GetText(
+					"Play back a demo file. The full path from your SRB2 directory must be given.\n\n"
+
+					"* With \"-addfiles\", any required files are added from a list contained within the demo file.\n"
+					"* With \"-force\", the demo is played even if the necessary files have not been added.\n"));
 		return;
 	}
 
@@ -1667,6 +1672,16 @@ static void Command_Playdemo_f(void)
 
 	CONS_Printf(M_GetText("Playing back demo '%s'.\n"), name);
 
+	demofileoverride = DFILE_OVERRIDE_NONE;
+	if (strcmp(COM_Argv(2), "-addfiles") == 0)
+	{
+		demofileoverride = DFILE_OVERRIDE_LOAD;
+	}
+	else if (strcmp(COM_Argv(2), "-force") == 0)
+	{
+		demofileoverride = DFILE_OVERRIDE_SKIP;
+	}
+
 	// Internal if no extension, external if one exists
 	// If external, convert the file name to a path in SRB2's home directory
 	if (FIL_CheckExtension(name))
diff --git a/src/f_finale.c b/src/f_finale.c
index e59b434720..d03795dc43 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -3509,6 +3509,7 @@ void F_TitleScreenTicker(boolean run)
 		}
 
 		titledemo = true;
+		demofileoverride = DFILE_OVERRIDE_NONE;
 		G_DoPlayDemo(dname);
 	}
 }
diff --git a/src/g_demo.c b/src/g_demo.c
index 0403da16da..230f6da2de 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -39,6 +39,7 @@
 #include "v_video.h"
 #include "lua_hook.h"
 #include "md5.h" // demo checksums
+#include "d_netfil.h" // G_CheckDemoExtraFiles
 
 boolean timingdemo; // if true, exit with report on completion
 boolean nodrawers; // for comparative timing purposes
@@ -49,6 +50,7 @@ static char demoname[64];
 boolean demorecording;
 boolean demoplayback;
 boolean titledemo; // Title Screen demo can be cancelled by any key
+demo_file_override_e demofileoverride;
 static UINT8 *demobuffer = NULL;
 static UINT8 *demo_p, *demotime_p;
 static UINT8 *demoend;
@@ -95,7 +97,7 @@ demoghost *ghosts = NULL;
 // DEMO RECORDING
 //
 
-#define DEMOVERSION 0x000f
+#define DEMOVERSION 0x0010
 #define DEMOHEADER  "\xF0" "SRB2Replay" "\x0F"
 
 #define DF_GHOST        0x01 // This demo contains ghost data too!
@@ -1413,6 +1415,10 @@ void G_BeginRecording(void)
 	char name[MAXCOLORNAME+1];
 	player_t *player = &players[consoleplayer];
 
+	char *filename;
+	UINT8 totalfiles;
+	UINT8 *m;
+
 	if (demo_p)
 		return;
 	memset(name,0,sizeof(name));
@@ -1435,23 +1441,43 @@ void G_BeginRecording(void)
 	M_Memcpy(demo_p, mapmd5, 16); demo_p += 16;
 
 	WRITEUINT8(demo_p,demoflags);
+
+	// file list
+	m = demo_p;/* file count */
+	demo_p += 1;
+
+	totalfiles = 0;
+	for (i = mainwads; ++i < numwadfiles; )
+	{
+		if (wadfiles[i]->important)
+		{
+			nameonly(( filename = va("%s", wadfiles[i]->filename) ));
+			WRITESTRINGL(demo_p, filename, MAX_WADPATH);
+			WRITEMEM(demo_p, wadfiles[i]->md5sum, 16);
+
+			totalfiles++;
+		}
+	}
+
+	WRITEUINT8(m, totalfiles);
+
 	switch ((demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
 	{
-	case ATTACKING_NONE: // 0
-		break;
-	case ATTACKING_RECORD: // 1
-		demotime_p = demo_p;
-		WRITEUINT32(demo_p,UINT32_MAX); // time
-		WRITEUINT32(demo_p,0); // score
-		WRITEUINT16(demo_p,0); // rings
-		break;
-	case ATTACKING_NIGHTS: // 2
-		demotime_p = demo_p;
-		WRITEUINT32(demo_p,UINT32_MAX); // time
-		WRITEUINT32(demo_p,0); // score
-		break;
-	default: // 3
-		break;
+		case ATTACKING_NONE: // 0
+			break;
+		case ATTACKING_RECORD: // 1
+			demotime_p = demo_p;
+			WRITEUINT32(demo_p,UINT32_MAX); // time
+			WRITEUINT32(demo_p,0); // score
+			WRITEUINT16(demo_p,0); // rings
+			break;
+		case ATTACKING_NIGHTS: // 2
+			demotime_p = demo_p;
+			WRITEUINT32(demo_p,UINT32_MAX); // time
+			WRITEUINT32(demo_p,0); // score
+			break;
+		default: // 3
+			break;
 	}
 
 	WRITEUINT32(demo_p,P_GetInitSeed());
@@ -1590,6 +1616,177 @@ void G_BeginMetal(void)
 	oldmetal.angle = mo->angle>>24;
 }
 
+static void G_LoadDemoExtraFiles(UINT8 **pp)
+{
+	UINT8 totalfiles;
+	char filename[MAX_WADPATH];
+	UINT8 md5sum[16];
+	filestatus_t ncs;
+	boolean toomany = false;
+	boolean alreadyloaded;
+	UINT8 i, j;
+
+	totalfiles = READUINT8((*pp));
+	for (i = 0; i < totalfiles; ++i)
+	{
+		if (toomany)
+			SKIPSTRING((*pp));
+		else
+		{
+			strlcpy(filename, (char *)(*pp), sizeof filename);
+			SKIPSTRING((*pp));
+		}
+		READMEM((*pp), md5sum, 16);
+
+		if (!toomany)
+		{
+			alreadyloaded = false;
+
+			for (j = 0; j < numwadfiles; ++j)
+			{
+				if (memcmp(md5sum, wadfiles[j]->md5sum, 16) == 0)
+				{
+					alreadyloaded = true;
+					break;
+				}
+			}
+
+			if (alreadyloaded)
+				continue;
+
+			if (numwadfiles >= MAX_WADFILES)
+				toomany = true;
+			else
+				ncs = findfile(filename, md5sum, false);
+
+			if (toomany)
+			{
+				CONS_Alert(CONS_WARNING, M_GetText("Too many files loaded to add anymore for demo playback\n"));
+				if (!CON_Ready())
+					M_StartMessage(M_GetText("There are too many files loaded to add this demo's addons.\n\nDemo playback may desync.\n\nPress ESC\n"), NULL, MM_NOTHING);
+			}
+			else if (ncs != FS_FOUND)
+			{
+				if (ncs == FS_NOTFOUND)
+					CONS_Alert(CONS_NOTICE, M_GetText("You do not have a copy of %s\n"), filename);
+				else if (ncs == FS_MD5SUMBAD)
+					CONS_Alert(CONS_NOTICE, M_GetText("Checksum mismatch on %s\n"), filename);
+				else
+					CONS_Alert(CONS_NOTICE, M_GetText("Unknown error finding file %s\n"), filename);
+
+				if (!CON_Ready())
+					M_StartMessage(M_GetText("There were errors trying to add this demo's addons. Check the console for more information.\n\nDemo playback may desync.\n\nPress ESC\n"), NULL, MM_NOTHING);
+			}
+			else
+			{
+				P_AddWadFile(filename);
+			}
+		}
+	}
+}
+
+static void G_SkipDemoExtraFiles(UINT8 **pp)
+{
+	UINT8 totalfiles;
+	UINT8 i;
+
+	if (demoversion < 0x0010)
+	{
+		// demo has no file list
+		return;
+	}
+
+	totalfiles = READUINT8((*pp));
+	for (i = 0; i < totalfiles; ++i)
+	{
+		SKIPSTRING((*pp));// file name
+		(*pp) += 16;// md5
+	}
+}
+
+// G_CheckDemoExtraFiles: checks if our loaded WAD list matches the demo's.
+// Enabling quick prevents filesystem checks to see if needed files are available to load.
+static UINT8 G_CheckDemoExtraFiles(UINT8 **pp, boolean quick)
+{
+	UINT8 totalfiles, filesloaded, nmusfilecount;
+	char filename[MAX_WADPATH];
+	UINT8 md5sum[16];
+	boolean toomany = false;
+	boolean alreadyloaded;
+	UINT8 i, j;
+	UINT8 error = DFILE_ERROR_NONE;
+
+	if (demoversion < 0x0010)
+	{
+		// demo has no file list
+		return DFILE_ERROR_NONE;
+	}
+
+	totalfiles = READUINT8((*pp));
+	filesloaded = 0;
+	for (i = 0; i < totalfiles; ++i)
+	{
+		if (toomany)
+			SKIPSTRING((*pp));
+		else
+		{
+			strlcpy(filename, (char *)(*pp), sizeof filename);
+			SKIPSTRING((*pp));
+		}
+		READMEM((*pp), md5sum, 16);
+
+		if (!toomany)
+		{
+			alreadyloaded = false;
+			nmusfilecount = 0;
+
+			for (j = 0; j < numwadfiles; ++j)
+			{
+				if (wadfiles[j]->important && j > mainwads)
+					nmusfilecount++;
+				else
+					continue;
+
+				if (memcmp(md5sum, wadfiles[j]->md5sum, 16) == 0)
+				{
+					alreadyloaded = true;
+
+					if (i != nmusfilecount-1 && error < DFILE_ERROR_OUTOFORDER)
+						error |= DFILE_ERROR_OUTOFORDER;
+
+					break;
+				}
+			}
+
+			if (alreadyloaded)
+			{
+				filesloaded++;
+				continue;
+			}
+
+			if (numwadfiles >= MAX_WADFILES)
+				error = DFILE_ERROR_CANNOTLOAD;
+			else if (!quick && findfile(filename, md5sum, false) != FS_FOUND)
+				error = DFILE_ERROR_CANNOTLOAD;
+			else if (error < DFILE_ERROR_INCOMPLETEOUTOFORDER)
+				error |= DFILE_ERROR_NOTLOADED;
+		} else
+			error = DFILE_ERROR_CANNOTLOAD;
+	}
+
+	// Get final file count
+	nmusfilecount = 0;
+
+	for (j = 0; j < numwadfiles; ++j)
+		if (wadfiles[j]->important && j > mainwads)
+			nmusfilecount++;
+
+	if (!error && filesloaded < nmusfilecount)
+		error = DFILE_ERROR_EXTRAFILES;
+
+	return error;
+}
+
 void G_SetDemoTime(UINT32 ptime, UINT32 pscore, UINT16 prings)
 {
 	if (!demorecording || !demotime_p)
@@ -1645,7 +1842,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
 	p += 2; // gamemap
 	p += 16; // map md5
 	flags = READUINT8(p); // demoflags
-
+	G_SkipDemoExtraFiles(&p);
 	aflags = flags & (DF_RECORDATTACK|DF_NIGHTSATTACK);
 	I_Assert(aflags);
 	if (flags & DF_RECORDATTACK)
@@ -1710,6 +1907,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
 		p += 2; // gamemap
 	p += 16; // mapmd5
 	flags = READUINT8(p);
+	G_SkipDemoExtraFiles(&p);
 	if (!(flags & aflags))
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("File '%s' not from same game mode. It will be overwritten.\n"), oldname);
@@ -1871,6 +2069,73 @@ void G_DoPlayDemo(char *defdemoname)
 	demo_p += 16; // mapmd5
 
 	demoflags = READUINT8(demo_p);
+
+	if (demoversion < 0x0010)
+	{
+		; // Don't do anything with files.
+	}
+	else if (titledemo)
+	{
+		// Titledemos should always play and ought to always be compatible with whatever wadlist is running.
+		G_SkipDemoExtraFiles(&demo_p);
+	}
+	else if (demofileoverride == DFILE_OVERRIDE_LOAD)
+	{
+		G_LoadDemoExtraFiles(&demo_p);
+	}
+	else if (demofileoverride == DFILE_OVERRIDE_SKIP)
+	{
+		G_SkipDemoExtraFiles(&demo_p);
+	}
+	else
+	{
+		UINT8 error = G_CheckDemoExtraFiles(&demo_p, false);
+
+		if (error)
+		{
+			switch (error)
+			{
+				case DFILE_ERROR_NOTLOADED:
+					snprintf(msg, 1024,
+						M_GetText("Required files for this demo are not loaded.\n\nUse\n\"playdemo %s -addfiles\"\nto load them and play the demo.\n"),
+					pdemoname);
+					break;
+
+				case DFILE_ERROR_OUTOFORDER:
+					snprintf(msg, 1024,
+						M_GetText("Required files for this demo are loaded out of order.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"),
+					pdemoname);
+					break;
+
+				case DFILE_ERROR_INCOMPLETEOUTOFORDER:
+					snprintf(msg, 1024,
+						M_GetText("Required files for this demo are not loaded, and some are out of order.\n\nUse\n\"playdemo %s -addfiles\"\nto load needed files and play the demo.\n"),
+					pdemoname);
+					break;
+
+				case DFILE_ERROR_CANNOTLOAD:
+					snprintf(msg, 1024,
+						M_GetText("Required files for this demo cannot be loaded.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"),
+					pdemoname);
+					break;
+
+				case DFILE_ERROR_EXTRAFILES:
+					snprintf(msg, 1024,
+						M_GetText("You have additional files loaded beyond the demo's file list.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"),
+					pdemoname);
+					break;
+			}
+
+			CONS_Alert(CONS_ERROR, "%s", msg);
+			M_StartMessage(msg, NULL, MM_NOTHING);
+			Z_Free(pdemoname);
+			Z_Free(demobuffer);
+			demoplayback = false;
+			titledemo = false;
+			return;
+		}
+	}
+
 	modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT;
 	CON_ToggleOff();
 
@@ -2026,6 +2291,91 @@ void G_DoPlayDemo(char *defdemoname)
 	demo_start = true;
 }
 
+//
+// Check if a replay can be loaded from the menu
+//
+UINT8 G_CheckDemoForError(char *defdemoname)
+{
+	lumpnum_t l;
+	char *n,*pdemoname;
+
+	n = defdemoname+strlen(defdemoname);
+	while (*n != '/' && *n != '\\' && n != defdemoname)
+		n--;
+	if (n != defdemoname)
+		n++;
+	pdemoname = ZZ_Alloc(strlen(n)+1);
+	strcpy(pdemoname,n);
+
+	// Internal if no extension, external if one exists
+	if (FIL_CheckExtension(defdemoname))
+	{
+		//FIL_DefaultExtension(defdemoname, ".lmp");
+		if (!FIL_ReadFile(defdemoname, &demobuffer))
+		{
+			return DFILE_ERROR_NOTDEMO;
+		}
+		demo_p = demobuffer;
+	}
+	// load demo resource from WAD
+	else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR)
+	{
+		return DFILE_ERROR_NOTDEMO;
+	}
+	else // it's an internal demo
+	{
+		demobuffer = demo_p = W_CacheLumpNum(l, PU_STATIC);
+	}
+
+	// read demo header
+	if (memcmp(demo_p, DEMOHEADER, 12))
+	{
+		return DFILE_ERROR_NOTDEMO;
+	}
+	demo_p += 12; // DEMOHEADER
+
+	demo_p++; // version
+	demo_p++; // subversion
+	demoversion = READUINT16(demo_p);
+	switch(demoversion)
+	{
+	case 0x000d:
+	case 0x000e:
+	case 0x000f:
+	case DEMOVERSION: // latest always supported
+		break;
+#ifdef OLD22DEMOCOMPAT
+	case 0x000c:
+		break;
+#endif
+	// too old, cannot support.
+	default:
+		return DFILE_ERROR_NOTDEMO;
+	}
+	demo_p += 16; // demo checksum
+	if (memcmp(demo_p, "PLAY", 4))
+	{
+		return DFILE_ERROR_NOTDEMO;
+	}
+	demo_p += 4; // "PLAY"
+	demo_p += 2; // gamemap
+	demo_p += 16; // mapmd5
+
+	demo_p++; // demoflags
+
+	// Don't do anything with files.
+	if (demoversion < 0x0010)
+	{
+		return DFILE_ERROR_NONE;
+	}
+	else if (titledemo)
+	{
+		return DFILE_ERROR_NONE;
+	}
+
+	return G_CheckDemoExtraFiles(&demo_p, true);
+}
+
 void G_AddGhost(char *defdemoname)
 {
 	INT32 i;
@@ -2130,6 +2480,9 @@ void G_AddGhost(char *defdemoname)
 		Z_Free(buffer);
 		return;
 	}
+
+	G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts.
+
 	switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
 	{
 	case ATTACKING_NONE: // 0
diff --git a/src/g_demo.h b/src/g_demo.h
index f25315a58c..383d2719b3 100644
--- a/src/g_demo.h
+++ b/src/g_demo.h
@@ -26,6 +26,15 @@
 extern boolean demoplayback, titledemo, demorecording, timingdemo;
 extern tic_t demostarttime;
 
+typedef enum
+{
+	DFILE_OVERRIDE_NONE = 0, // Show errors normally
+	DFILE_OVERRIDE_LOAD, // Forcefully load demo, add files beforehand
+	DFILE_OVERRIDE_SKIP, // Forcefully load demo, skip file list
+} demo_file_override_e;
+
+extern demo_file_override_e demofileoverride;
+
 // Quit after playing a demo from cmdline.
 extern boolean singledemo;
 extern boolean demo_start;
@@ -53,6 +62,18 @@ typedef enum
 	GHC_RETURNSKIN // ditto
 } ghostcolor_t;
 
+// G_CheckDemoExtraFiles: checks if our loaded WAD list matches the demo's.
+typedef enum
+{
+	DFILE_ERROR_NONE = 0, // No file error
+	DFILE_ERROR_NOTLOADED, // Files are not loaded, but can be without a restart.
+	DFILE_ERROR_OUTOFORDER, // Files are loaded, but out of order.
+	DFILE_ERROR_INCOMPLETEOUTOFORDER, // Some files are loaded out of order, but others are not.
+	DFILE_ERROR_CANNOTLOAD, // Files are missing and cannot be loaded.
+	DFILE_ERROR_EXTRAFILES, // Extra files outside of the replay's file list are loaded.
+	DFILE_ERROR_NOTDEMO = UINT8_MAX, // This replay isn't even a replay...
+} demo_file_error_e;
+
 // Record/playback tics
 void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum);
 void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum);
@@ -83,5 +104,6 @@ ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill);
 void G_StopDemo(void);
 boolean G_CheckDemoStatus(void);
 INT32 G_ConvertOldFrameFlags(INT32 frame);
+UINT8 G_CheckDemoForError(char *defdemoname);
 
 #endif // __G_DEMO__
diff --git a/src/m_menu.c b/src/m_menu.c
index 12003f945c..828fc1aee8 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3500,10 +3500,10 @@ boolean M_Responder(event_t *ev)
 #ifndef DEVELOP
 					// TODO: Replays are scary, so I left the remaining instances of this alone.
 					// It'd be nice to get rid of this once and for all though!
-					if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && (modifiedgame && !savemoddata) && !usedCheats)
+					if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && usedCheats)
 					{
 						S_StartSound(NULL, sfx_skid);
-						M_StartMessage(M_GetText("This cannot be done in a modified game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
+						M_StartMessage(M_GetText("This cannot be done in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
 						return true;
 					}
 #endif
@@ -10449,13 +10449,23 @@ static void M_ChooseTimeAttack(INT32 choice)
 	G_DeferedInitNew(false, G_BuildMapName(cv_nextmap.value), (UINT8)(cv_chooseskin.value-1), false, false);
 }
 
+static char ra_demoname[1024];
+
+static void M_StartTimeAttackReplay(INT32 choice)
+{
+	if (choice == 'y' || choice == KEY_ENTER)
+	{
+		M_ClearMenus(true);
+		modeattacking = ATTACKING_RECORD; // set modeattacking before G_DoPlayDemo so the map loader knows
+		G_DoPlayDemo(ra_demoname);
+	}
+}
+
 // Player has selected the "REPLAY" from the time attack screen
 static void M_ReplayTimeAttack(INT32 choice)
 {
 	const char *which;
-	char *demoname;
-	M_ClearMenus(true);
-	modeattacking = ATTACKING_RECORD; // set modeattacking before G_DoPlayDemo so the map loader knows
+	UINT8 error = DFILE_ERROR_NONE;
 
 	if (currentMenu == &SP_ReplayDef)
 	{
@@ -10475,11 +10485,15 @@ static void M_ReplayTimeAttack(INT32 choice)
 			break;
 		case 4: // guest
 			// srb2/replay/main/map01-guest.lmp
-			G_DoPlayDemo(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)));
-			return;
+			snprintf(ra_demoname, 1024, "%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value));
+			break;
+		}
+
+		if (choice != 4)
+		{
+			// srb2/replay/main/map01-sonic-time-best.lmp
+			snprintf(ra_demoname, 1024, "%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1].name, which);
 		}
-		// srb2/replay/main/map01-sonic-time-best.lmp
-		G_DoPlayDemo(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1].name, which));
 	}
 	else if (currentMenu == &SP_NightsReplayDef)
 	{
@@ -10495,19 +10509,66 @@ static void M_ReplayTimeAttack(INT32 choice)
 			which = "last";
 			break;
 		case 3: // guest
-			G_DoPlayDemo(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)));
-			return;
+			snprintf(ra_demoname, 1024, "%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value));
+			break;
 		}
 
-		demoname = va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1].name, which);
+		if (choice != 3)
+		{
+			snprintf(ra_demoname, 1024, "%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1].name, which);
 
 #ifdef OLDNREPLAYNAME // Check for old style named NiGHTS replay if a new style replay doesn't exist.
-		if (!FIL_FileExists(demoname))
-			demoname = va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), which);
+			if (!FIL_FileExists(ra_demoname))
+			{
+				snprintf(ra_demoname, 1024, "%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), which);
+			}
 #endif
+		}
+	}
 
-		G_DoPlayDemo(demoname);
+	demofileoverride = DFILE_OVERRIDE_NONE;
+	error = G_CheckDemoForError(ra_demoname);
+
+	if (error)
+	{
+		S_StartSound(NULL, sfx_skid);
+
+		switch (error)
+		{
+			case DFILE_ERROR_NOTDEMO:
+				M_StartMessage(M_GetText("An error occurred loading this replay.\n\n(Press a key)\n"), NULL, MM_NOTHING);
+				break;
+
+			case DFILE_ERROR_NOTLOADED:
+				demofileoverride = DFILE_OVERRIDE_LOAD;
+				M_StartMessage(M_GetText("Add-ons for this replay\nhave not been loaded.\n\nAttempt to load files?\n\n(Press 'Y' to confirm)\n"), M_StartTimeAttackReplay, MM_YESNO);
+				break;
+
+			case DFILE_ERROR_OUTOFORDER:
+				demofileoverride = DFILE_OVERRIDE_SKIP;
+				M_StartMessage(M_GetText("Add-ons for this replay\nwere loaded out of order.\n\nAttempt to playback anyway?\n\n(Press 'Y' to confirm)\n"), M_StartTimeAttackReplay, MM_YESNO);
+				break;
+
+			case DFILE_ERROR_INCOMPLETEOUTOFORDER:
+				demofileoverride = DFILE_OVERRIDE_LOAD;
+				M_StartMessage(M_GetText("Add-ons for this replay\nhave not been loaded,\nand some are in the wrong order.\n\nAttempt to load files?\n\n(Press 'Y' to confirm)\n"), M_StartTimeAttackReplay, MM_YESNO);
+				break;
+
+			case DFILE_ERROR_CANNOTLOAD:
+				demofileoverride = DFILE_OVERRIDE_SKIP;
+				M_StartMessage(M_GetText("Add-ons for this replay\ncould not be loaded.\n\nAttempt to playback anyway?\n\n(Press 'Y' to confirm)\n"), M_StartTimeAttackReplay, MM_YESNO);
+				break;
+
+			case DFILE_ERROR_EXTRAFILES:
+				demofileoverride = DFILE_OVERRIDE_SKIP;
+				M_StartMessage(M_GetText("You have more files loaded\nthan the replay does.\n\nAttempt to playback anyway?\n\n(Press 'Y' to confirm)\n"), M_StartTimeAttackReplay, MM_YESNO);
+				break;
+		}
+
+		return;
 	}
+
+	M_StartTimeAttackReplay(KEY_ENTER);
 }
 
 static void M_EraseGuest(INT32 choice)
-- 
GitLab


From f99cf91d3ddd08ec58ff099f588fd85819c3842f Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Tue, 25 Jul 2023 09:29:09 -0400
Subject: [PATCH 223/518] Promote file count to UINT16

---
 src/g_demo.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/g_demo.c b/src/g_demo.c
index 230f6da2de..8c6064c579 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -1416,7 +1416,7 @@ void G_BeginRecording(void)
 	player_t *player = &players[consoleplayer];
 
 	char *filename;
-	UINT8 totalfiles;
+	UINT16 totalfiles;
 	UINT8 *m;
 
 	if (demo_p)
@@ -1459,7 +1459,7 @@ void G_BeginRecording(void)
 		}
 	}
 
-	WRITEUINT8(m, totalfiles);
+	WRITEUINT16(m, totalfiles);
 
 	switch ((demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
 	{
@@ -1618,15 +1618,15 @@ void G_BeginMetal(void)
 
 static void G_LoadDemoExtraFiles(UINT8 **pp)
 {
-	UINT8 totalfiles;
+	UINT16 totalfiles;
 	char filename[MAX_WADPATH];
 	UINT8 md5sum[16];
 	filestatus_t ncs;
 	boolean toomany = false;
 	boolean alreadyloaded;
-	UINT8 i, j;
+	UINT16 i, j;
 
-	totalfiles = READUINT8((*pp));
+	totalfiles = READUINT16((*pp));
 	for (i = 0; i < totalfiles; ++i)
 	{
 		if (toomany)
@@ -1687,8 +1687,8 @@ static void G_LoadDemoExtraFiles(UINT8 **pp)
 
 static void G_SkipDemoExtraFiles(UINT8 **pp)
 {
-	UINT8 totalfiles;
-	UINT8 i;
+	UINT16 totalfiles;
+	UINT16 i;
 
 	if (demoversion < 0x0010)
 	{
@@ -1696,7 +1696,7 @@ static void G_SkipDemoExtraFiles(UINT8 **pp)
 		return;
 	}
 
-	totalfiles = READUINT8((*pp));
+	totalfiles = READUINT16((*pp));
 	for (i = 0; i < totalfiles; ++i)
 	{
 		SKIPSTRING((*pp));// file name
@@ -1708,12 +1708,12 @@ static void G_SkipDemoExtraFiles(UINT8 **pp)
 // Enabling quick prevents filesystem checks to see if needed files are available to load.
 static UINT8 G_CheckDemoExtraFiles(UINT8 **pp, boolean quick)
 {
-	UINT8 totalfiles, filesloaded, nmusfilecount;
+	UINT16 totalfiles, filesloaded, nmusfilecount;
 	char filename[MAX_WADPATH];
 	UINT8 md5sum[16];
 	boolean toomany = false;
 	boolean alreadyloaded;
-	UINT8 i, j;
+	UINT16 i, j;
 	UINT8 error = DFILE_ERROR_NONE;
 
 	if (demoversion < 0x0010)
@@ -1722,7 +1722,7 @@ static UINT8 G_CheckDemoExtraFiles(UINT8 **pp, boolean quick)
 		return DFILE_ERROR_NONE;
 	}
 
-	totalfiles = READUINT8((*pp));
+	totalfiles = READUINT16((*pp));
 	filesloaded = 0;
 	for (i = 0; i < totalfiles; ++i)
 	{
-- 
GitLab


From dded3522933aaa068f564c024096c97a174166c0 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Tue, 25 Jul 2023 09:34:55 -0400
Subject: [PATCH 224/518] Harsher replay menu

Don't allow loading demos with invalid file lists at all. If you insist on doing this, it has to be done from the command line now.
---
 src/g_demo.c |  2 +-
 src/m_menu.c | 12 ++++++++++++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/g_demo.c b/src/g_demo.c
index 8c6064c579..438d3dfc91 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -1444,7 +1444,7 @@ void G_BeginRecording(void)
 
 	// file list
 	m = demo_p;/* file count */
-	demo_p += 1;
+	demo_p += 2;
 
 	totalfiles = 0;
 	for (i = mainwads; ++i < numwadfiles; )
diff --git a/src/m_menu.c b/src/m_menu.c
index 828fc1aee8..21ba98dd2c 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -10545,23 +10545,35 @@ static void M_ReplayTimeAttack(INT32 choice)
 				break;
 
 			case DFILE_ERROR_OUTOFORDER:
+				/*
 				demofileoverride = DFILE_OVERRIDE_SKIP;
 				M_StartMessage(M_GetText("Add-ons for this replay\nwere loaded out of order.\n\nAttempt to playback anyway?\n\n(Press 'Y' to confirm)\n"), M_StartTimeAttackReplay, MM_YESNO);
+				*/
+				M_StartMessage(M_GetText("Add-ons for this replay\nwere loaded out of order.\n\n(Press a key)\n"), NULL, MM_NOTHING);
 				break;
 
 			case DFILE_ERROR_INCOMPLETEOUTOFORDER:
+				/*
 				demofileoverride = DFILE_OVERRIDE_LOAD;
 				M_StartMessage(M_GetText("Add-ons for this replay\nhave not been loaded,\nand some are in the wrong order.\n\nAttempt to load files?\n\n(Press 'Y' to confirm)\n"), M_StartTimeAttackReplay, MM_YESNO);
+				*/
+				M_StartMessage(M_GetText("Add-ons for this replay\nhave not been loaded,\nand some are in the wrong order.\n\n(Press a key)\n"), NULL, MM_NOTHING);
 				break;
 
 			case DFILE_ERROR_CANNOTLOAD:
+				/*
 				demofileoverride = DFILE_OVERRIDE_SKIP;
 				M_StartMessage(M_GetText("Add-ons for this replay\ncould not be loaded.\n\nAttempt to playback anyway?\n\n(Press 'Y' to confirm)\n"), M_StartTimeAttackReplay, MM_YESNO);
+				*/
+				M_StartMessage(M_GetText("Add-ons for this replay\ncould not be loaded.\n\n(Press a key)\n"), NULL, MM_NOTHING);
 				break;
 
 			case DFILE_ERROR_EXTRAFILES:
+				/*
 				demofileoverride = DFILE_OVERRIDE_SKIP;
 				M_StartMessage(M_GetText("You have more files loaded\nthan the replay does.\n\nAttempt to playback anyway?\n\n(Press 'Y' to confirm)\n"), M_StartTimeAttackReplay, MM_YESNO);
+				*/
+				M_StartMessage(M_GetText("You have more files loaded\nthan the replay does.\n\n(Press a key)\n"), NULL, MM_NOTHING);
 				break;
 		}
 
-- 
GitLab


From 0e2682d5908bf825d6125664144fe70ba20c9b9b Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Fri, 30 Jun 2023 00:09:20 +0200
Subject: [PATCH 225/518] Add "drawonlyforplayer" and "dontdrawforviewmobj"

---
 src/b_bot.c       |   3 +-
 src/g_game.c      |   1 +
 src/lua_baselib.c |   3 +-
 src/lua_mobjlib.c |  33 ++++++++++++
 src/p_mobj.c      |   6 +--
 src/p_mobj.h      |   2 +
 src/p_saveg.c     |  69 +++++++++++++++---------
 src/p_user.c      | 130 +++++++++++++++++++++++++++++++++-------------
 src/r_things.c    |  11 ++--
 9 files changed, 188 insertions(+), 70 deletions(-)

diff --git a/src/b_bot.c b/src/b_bot.c
index d1465f891d..57f7623042 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -631,7 +631,8 @@ void B_HandleFlightIndicator(player_t *player)
 	}
 
 	// otherwise, update its visibility
-	if (P_IsLocalPlayer(player->botleader))
+	tails->hnext->drawonlyforplayer = player->botleader; // Hide it from the other player in splitscreen, and yourself when spectating
+	if (P_IsLocalPlayer(player->botleader)) // Only display it on your own view. Don't display it for spectators
 		tails->hnext->flags2 &= ~MF2_DONTDRAW;
 	else
 		tails->hnext->flags2 |= MF2_DONTDRAW;
diff --git a/src/g_game.c b/src/g_game.c
index 15b1e10816..8264fbe6bb 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1441,6 +1441,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 			// I assume this is netgame-safe because gunslinger spawns this for only the local player...... *sweats intensely*
 			newtarget = P_SpawnMobj(ticcmd_ztargetfocus[forplayer]->x, ticcmd_ztargetfocus[forplayer]->y, ticcmd_ztargetfocus[forplayer]->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 			P_SetTarget(&newtarget->target, ticcmd_ztargetfocus[forplayer]);
+			newtarget->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
 
 			if (player->mo && P_AproxDistance(
 				player->mo->x - ticcmd_ztargetfocus[forplayer]->x,
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index ed5736fa7f..838e2f53ba 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -689,11 +689,12 @@ static int lib_pSpawnLockOn(lua_State *L)
 		return LUA_ErrInvalid(L, "player_t");
 	if (state >= NUMSTATES)
 		return luaL_error(L, "state %d out of range (0 - %d)", state, NUMSTATES-1);
-	if (P_IsLocalPlayer(player)) // Only display it on your own view.
+	if (P_IsLocalPlayer(player)) // Only display it on your own view. Don't display it for spectators
 	{
 		mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 		P_SetTarget(&visual->target, lockon);
 		visual->flags2 |= MF2_DONTDRAW;
+		visual->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
 		P_SetMobjStateNF(visual, state);
 	}
 	return 0;
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 851c569d27..fd8dcec925 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -44,6 +44,8 @@ enum mobj_e {
 	mobj_spritexoffset,
 	mobj_spriteyoffset,
 	mobj_floorspriteslope,
+	mobj_drawonlyforplayer,
+	mobj_dontdrawforviewmobj,
 	mobj_touching_sectorlist,
 	mobj_subsector,
 	mobj_floorz,
@@ -122,6 +124,8 @@ static const char *const mobj_opt[] = {
 	"spritexoffset",
 	"spriteyoffset",
 	"floorspriteslope",
+	"drawonlyforplayer",
+	"dontdrawforviewmobj",
 	"touching_sectorlist",
 	"subsector",
 	"floorz",
@@ -262,6 +266,17 @@ static int mobj_get(lua_State *L)
 	case mobj_floorspriteslope:
 		LUA_PushUserdata(L, mo->floorspriteslope, META_SLOPE);
 		break;
+	case mobj_drawonlyforplayer:
+		LUA_PushUserdata(L, mo->drawonlyforplayer, META_PLAYER);
+		break;
+	case mobj_dontdrawforviewmobj:
+		if (mo->dontdrawforviewmobj && P_MobjWasRemoved(mo->dontdrawforviewmobj))
+		{ // don't put invalid mobj back into Lua.
+			P_SetTarget(&mo->dontdrawforviewmobj, NULL);
+			return 0;
+		}
+		LUA_PushUserdata(L, mo->dontdrawforviewmobj, META_MOBJ);
+		break;
 	case mobj_touching_sectorlist:
 		return UNIMPLEMENTED;
 	case mobj_subsector:
@@ -551,6 +566,24 @@ static int mobj_set(lua_State *L)
 		break;
 	case mobj_floorspriteslope:
 		return NOSET;
+	case mobj_drawonlyforplayer:
+		if (lua_isnil(L, 3))
+			mo->drawonlyforplayer = NULL;
+		else
+		{
+			player_t *drawonlyforplayer = *((player_t **)luaL_checkudata(L, 3, META_PLAYER));
+			mo->drawonlyforplayer = drawonlyforplayer;
+		}
+		break;
+	case mobj_dontdrawforviewmobj:
+		if (lua_isnil(L, 3))
+			P_SetTarget(&mo->dontdrawforviewmobj, NULL);
+		else
+		{
+			mobj_t *dontdrawforviewmobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
+			P_SetTarget(&mo->dontdrawforviewmobj, dontdrawforviewmobj);
+		}
+		break;
 	case mobj_touching_sectorlist:
 		return UNIMPLEMENTED;
 	case mobj_subsector:
diff --git a/src/p_mobj.c b/src/p_mobj.c
index a25b5643bb..4ca59285f7 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -6882,6 +6882,7 @@ void P_RunOverlays(void)
 		mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP) | (mo->target->eflags & MFE_VERTICALFLIP);
 		mo->scale = mo->destscale = mo->target->scale;
 		mo->angle = (mo->target->player ? mo->target->player->drawangle : mo->target->angle) + mo->movedir;
+		P_SetTarget(&mo->dontdrawforviewmobj, mo->target->dontdrawforviewmobj); // Hide the overlay from the view that its target is hidden from - But don't copy drawonlyforplayer!
 
 		if (!(mo->state->frame & FF_ANIMATE))
 			zoffs = FixedMul(((signed)mo->state->var2)*FRACUNIT, mo->scale);
@@ -7951,11 +7952,6 @@ static void P_MobjSceneryThink(mobj_t *mobj)
 			return;
 		}
 
-		if (!camera.chase)
-			mobj->flags2 |= MF2_DONTDRAW;
-		else
-			mobj->flags2 &= ~MF2_DONTDRAW;
-
 		P_UnsetThingPosition(mobj);
 		{
 			fixed_t radius = FixedMul(10*mobj->info->speed, mobj->target->scale);
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 6a5ab84c41..9c598f59e3 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -334,6 +334,8 @@ typedef struct mobj_s
 	// Player and mobj sprites in multiplayer modes are modified
 	//  using an internal color lookup table for re-indexing.
 	UINT16 color; // This replaces MF_TRANSLATION. Use 0 for default (no translation).
+	struct player_s *drawonlyforplayer; // If set, hides the mobj for everyone except this player and their spectators
+	struct mobj_s *dontdrawforviewmobj; // If set, hides the mobj if dontdrawforviewmobj is the current camera (first-person player or awayviewmobj)
 
 	// Interaction info, by BLOCKMAP.
 	// Links in blocks (if needed).
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 76f1661ace..8f11e63e58 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1553,30 +1553,32 @@ typedef enum
 
 typedef enum
 {
-	MD2_CUSVAL      = 1,
-	MD2_CVMEM       = 1<<1,
-	MD2_SKIN        = 1<<2,
-	MD2_COLOR       = 1<<3,
-	MD2_SCALESPEED  = 1<<4,
-	MD2_EXTVAL1     = 1<<5,
-	MD2_EXTVAL2     = 1<<6,
-	MD2_HNEXT       = 1<<7,
-	MD2_HPREV       = 1<<8,
-	MD2_FLOORROVER  = 1<<9,
-	MD2_CEILINGROVER = 1<<10,
-	MD2_SLOPE        = 1<<11,
-	MD2_COLORIZED    = 1<<12,
-	MD2_MIRRORED     = 1<<13,
-	MD2_SPRITEROLL   = 1<<14,
-	MD2_SHADOWSCALE  = 1<<15,
-	MD2_RENDERFLAGS  = 1<<16,
-	MD2_BLENDMODE    = 1<<17,
-	MD2_SPRITEXSCALE = 1<<18,
-	MD2_SPRITEYSCALE = 1<<19,
-	MD2_SPRITEXOFFSET = 1<<20,
-	MD2_SPRITEYOFFSET = 1<<21,
-	MD2_FLOORSPRITESLOPE = 1<<22,
-	MD2_DISPOFFSET = 1<<23
+	MD2_CUSVAL              = 1,
+	MD2_CVMEM               = 1<<1,
+	MD2_SKIN                = 1<<2,
+	MD2_COLOR               = 1<<3,
+	MD2_SCALESPEED          = 1<<4,
+	MD2_EXTVAL1             = 1<<5,
+	MD2_EXTVAL2             = 1<<6,
+	MD2_HNEXT               = 1<<7,
+	MD2_HPREV               = 1<<8,
+	MD2_FLOORROVER          = 1<<9,
+	MD2_CEILINGROVER        = 1<<10,
+	MD2_SLOPE               = 1<<11,
+	MD2_COLORIZED           = 1<<12,
+	MD2_MIRRORED            = 1<<13,
+	MD2_SPRITEROLL          = 1<<14,
+	MD2_SHADOWSCALE         = 1<<15,
+	MD2_RENDERFLAGS         = 1<<16,
+	MD2_BLENDMODE           = 1<<17,
+	MD2_SPRITEXSCALE        = 1<<18,
+	MD2_SPRITEYSCALE        = 1<<19,
+	MD2_SPRITEXOFFSET       = 1<<20,
+	MD2_SPRITEYOFFSET       = 1<<21,
+	MD2_FLOORSPRITESLOPE    = 1<<22,
+	MD2_DISPOFFSET          = 1<<23,
+	MD2_DRAWONLYFORPLAYER   = 1<<24,
+	MD2_DONTDRAWFORVIEWMOBJ = 1<<25
 } mobj_diff2_t;
 
 typedef enum
@@ -1811,6 +1813,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		|| (slope->normal.z != FRACUNIT))
 			diff2 |= MD2_FLOORSPRITESLOPE;
 	}
+	if (mobj->drawonlyforplayer)
+		diff2 |= MD2_DRAWONLYFORPLAYER;
+	if (mobj->dontdrawforviewmobj)
+		diff2 |= MD2_DONTDRAWFORVIEWMOBJ;
 	if (mobj->dispoffset != mobj->info->dispoffset)
 		diff2 |= MD2_DISPOFFSET;
 
@@ -1988,6 +1994,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		WRITEFIXED(save_p, slope->normal.y);
 		WRITEFIXED(save_p, slope->normal.z);
 	}
+	if (diff2 & MD2_DRAWONLYFORPLAYER)
+		WRITEUINT8(save_p, mobj->drawonlyforplayer-players);
+	if (diff2 & MD2_DONTDRAWFORVIEWMOBJ)
+		WRITEUINT32(save_p, mobj->dontdrawforviewmobj->mobjnum);
 	if (diff2 & MD2_DISPOFFSET)
 		WRITEINT32(save_p, mobj->dispoffset);
 
@@ -3044,6 +3054,10 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		slope->normal.y = READFIXED(save_p);
 		slope->normal.z = READFIXED(save_p);
 	}
+	if (diff2 & MD2_DRAWONLYFORPLAYER)
+		mobj->drawonlyforplayer = &players[READUINT8(save_p)];
+	if (diff2 & MD2_DONTDRAWFORVIEWMOBJ)
+		mobj->dontdrawforviewmobj = (mobj_t *)(size_t)READUINT32(save_p);
 	if (diff2 & MD2_DISPOFFSET)
 		mobj->dispoffset = READINT32(save_p);
 	else
@@ -4063,6 +4077,13 @@ static void P_RelinkPointers(void)
 		if (mobj->type == MT_HOOP || mobj->type == MT_HOOPCOLLIDE || mobj->type == MT_HOOPCENTER)
 			continue;
 
+		if (mobj->dontdrawforviewmobj)
+		{
+			temp = (UINT32)(size_t)mobj->dontdrawforviewmobj;
+			mobj->dontdrawforviewmobj = NULL;
+			if (!P_SetTarget(&mobj->dontdrawforviewmobj, P_FindNewPosition(temp)))
+				CONS_Debug(DBG_GAMELOGIC, "dontdrawforviewmobj not found on %d\n", mobj->type);
+		}
 		if (mobj->tracer)
 		{
 			temp = (UINT32)(size_t)mobj->tracer;
diff --git a/src/p_user.c b/src/p_user.c
index 62dd2fcb24..889e887c14 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -398,6 +398,7 @@ void P_GiveFinishFlags(player_t *player)
 		angle += FixedAngle(120*FRACUNIT);
 
 		P_SetTarget(&flag->target, player->mo);
+		P_SetTarget(&flag->dontdrawforviewmobj, player->mo); // Hide the flag in first-person
 	}
 }
 
@@ -1840,6 +1841,7 @@ void P_SpawnShieldOrb(player_t *player)
 	shieldobj = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, orbtype);
 	shieldobj->flags2 |= MF2_SHIELD;
 	P_SetTarget(&shieldobj->target, player->mo);
+	P_SetTarget(&shieldobj->dontdrawforviewmobj, player->mo); // Hide the shield in first-person
 	if ((player->powers[pw_shield] & SH_NOSTACK) == SH_PINK)
 	{
 		shieldobj->color = SKINCOLOR_PINK;
@@ -1964,6 +1966,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 	mobj_t *ghost = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_GHOST);
 
 	P_SetTarget(&ghost->target, mobj);
+	P_SetTarget(&ghost->dontdrawforviewmobj, mobj); // Hide the ghost in first-person
 
 	P_SetScale(ghost, mobj->scale);
 	ghost->destscale = mobj->scale;
@@ -2009,6 +2012,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 		mobj_t *ghost2 = P_SpawnGhostMobj(mobj->player->followmobj);
 		P_SetTarget(&ghost2->tracer, ghost);
 		P_SetTarget(&ghost->tracer, ghost2);
+		P_SetTarget(&ghost2->dontdrawforviewmobj, mobj); // Hide the follow-ghost for the non-follow object
 		ghost2->flags2 |= (mobj->player->followmobj->flags2 & MF2_LINKDRAW);
 	}
 
@@ -3123,39 +3127,41 @@ static void P_DoBubbleBreath(player_t *player)
 //
 static void P_DoPlayerHeadSigns(player_t *player)
 {
+	mobj_t *sign = NULL;
+
 	if (G_TagGametype())
 	{
-		// If you're "IT", show a big "IT" over your head for others to see.
-		if (player->pflags & PF_TAGIT && !P_IsLocalPlayer(player))
+		// If you're "IT", show a big "IT" over your head for others to see, including spectators
+		// (and even yourself if you spectate someone else).
+		if (player->pflags & PF_TAGIT && (!P_IsLocalPlayer(player) || consoleplayer != displayplayer || splitscreen))
 		{
-			mobj_t* it = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TAG);
-			it->x = player->mo->x;
-			it->y = player->mo->y;
-			it->z = player->mo->z;
-			it->old_x = player->mo->old_x;
-			it->old_y = player->mo->old_y;
-			it->old_z = player->mo->old_z;
+			sign = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TAG);
+			sign->x = player->mo->x;
+			sign->y = player->mo->y;
+			sign->z = player->mo->z;
+			sign->old_x = player->mo->old_x;
+			sign->old_y = player->mo->old_y;
+			sign->old_z = player->mo->old_z;
 
 			if (!(player->mo->eflags & MFE_VERTICALFLIP))
 			{
-				it->z += player->mo->height;
-				it->old_z += player->mo->height;
+				sign->z += player->mo->height;
+				sign->old_z += player->mo->height;
 			}
 			else
 			{
-				it->z -= mobjinfo[MT_TAG].height;
-				it->old_z -= mobjinfo[MT_TAG].height;
+				sign->z -= mobjinfo[MT_TAG].height;
+				sign->old_z -= mobjinfo[MT_TAG].height;
 			}
 		}
 	}
 	else if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) // If you have the flag (duh).
 	{
-		// Spawn a got-flag message over the head of the player that
-		// has it (but not on your own screen if you have the flag).
-		if (splitscreen || player != &players[consoleplayer])
+		// Spawn a got-flag message over the head of the player that has it
+		// (but not on your own screen if you have the flag, unless you're spectating).
+		if (!P_IsLocalPlayer(player) || consoleplayer != displayplayer || splitscreen)
 		{
 			fixed_t zofs;
-			mobj_t *sign;
 			boolean player_is_flipped = (player->mo->eflags & MFE_VERTICALFLIP) > 0;
 
 			zofs = player->mo->momz;
@@ -3187,6 +3193,43 @@ static void P_DoPlayerHeadSigns(player_t *player)
 				sign->frame = 2|FF_FULLBRIGHT;
 		}
 	}
+
+	if (!P_MobjWasRemoved(sign) && splitscreen) // Hide the sign from yourself in splitscreen - In single-screen, it wouldn't get spawned if it shouldn't be visible
+	{
+		if (player == &players[displayplayer])
+			sign->drawonlyforplayer = &players[secondarydisplayplayer];
+		else
+			sign->drawonlyforplayer = &players[displayplayer];
+
+#ifdef QUADS
+		if (splitscreen > 1) // Can be seen by at least two local views, so we need an extra copy of the sign
+		{
+			UINT32 signframe = sign->frame; // Copy the flag frame
+			sign = P_SpawnMobjFromMobj(sign, 0, 0, 0, MT_TAG);
+			if (P_MobjWasRemoved(sign))
+				return;
+
+			sign->frame = signframe;
+			if (player == &players[displayplayer] || player == &players[secondarydisplayplayer])
+				sign->drawonlyforplayer = &players[thirddisplayplayer];
+			else
+				sign->drawonlyforplayer = &players[secondarydisplayplayer];
+
+			if (splitscreen > 2) // Can be seen by three local views
+			{
+				sign = P_SpawnMobjFromMobj(sign, 0, 0, 0, MT_TAG);
+				if (P_MobjWasRemoved(sign))
+					return;
+
+				sign->frame = signframe;
+				if (player != &players[fourthdisplayplayer])
+					sign->drawonlyforplayer = &players[fourthdisplayplayer];
+				else
+					sign->drawonlyforplayer = &players[thirddisplayplayer];
+			}
+		}
+#endif
+	}
 }
 
 //
@@ -4707,10 +4750,11 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
 						mobj_t *lockon = P_LookForEnemies(player, false, true);
 						if (lockon)
 						{
-							if (P_IsLocalPlayer(player)) // Only display it on your own view.
+							if (P_IsLocalPlayer(player)) // Only display it on your own view. Don't display it for spectators
 							{
 								mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 								P_SetTarget(&visual->target, lockon);
+								visual->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
 							}
 						}
 						if ((cmd->buttons & BT_SPIN) && !(player->pflags & PF_SPINDOWN))
@@ -5054,7 +5098,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
 		{
 			if ((lockonshield = P_LookForEnemies(player, false, false)))
 			{
-				if (P_IsLocalPlayer(player)) // Only display it on your own view.
+				if (P_IsLocalPlayer(player)) // Only display it on your own view. Don't display it for spectators
 				{
 					boolean dovis = true;
 					if (lockonshield == lockonthok)
@@ -5068,6 +5112,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
 					{
 						visual = P_SpawnMobj(lockonshield->x, lockonshield->y, lockonshield->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 						P_SetTarget(&visual->target, lockonshield);
+						visual->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
 						P_SetMobjStateNF(visual, visual->info->spawnstate+1);
 					}
 				}
@@ -5171,10 +5216,11 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 
 	if ((player->charability == CA_HOMINGTHOK) && !player->homing && (player->pflags & PF_JUMPED) && (!(player->pflags & PF_THOKKED) || (player->charflags & SF_MULTIABILITY)) && (lockonthok = P_LookForEnemies(player, true, false)))
 	{
-		if (P_IsLocalPlayer(player)) // Only display it on your own view.
+		if (P_IsLocalPlayer(player)) // Only display it on your own view. Don't display it for spectators
 		{
 			visual = P_SpawnMobj(lockonthok->x, lockonthok->y, lockonthok->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 			P_SetTarget(&visual->target, lockonthok);
+			visual->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
 		}
 	}
 
@@ -11084,17 +11130,30 @@ static void P_MinecartThink(player_t *player)
 
 			// Mark interpolation; the old positions need to be relative to the displacement from the minecart _after_ it's moved.
 			// This isn't quite correct (it captures the landing wobble) but it works well enough
+			// Additionally, hide other players' marks
 			if (detleft)
 			{
-				detleft->old_x = detleft->x - (minecart->old_x - minecart->old_x2);
-				detleft->old_y = detleft->y - (minecart->old_y - minecart->old_y2);
-				detleft->old_z = detleft->z - (minecart->old_z - minecart->old_z2);
+				if (P_IsLocalPlayer(player))
+				{
+					detleft->old_x = detleft->x - (minecart->old_x - minecart->old_x2);
+					detleft->old_y = detleft->y - (minecart->old_y - minecart->old_y2);
+					detleft->old_z = detleft->z - (minecart->old_z - minecart->old_z2);
+					detleft->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
+				}
+				else // Don't see others' marks when spectating others
+					P_RemoveMobj(detleft); // Lock-on markers are only spawned client-side, so this SHOULD be safe too...
 			}
 			if (detright)
 			{
-				detright->old_x = detright->x - (minecart->old_x - minecart->old_x2);
-				detright->old_y = detright->y - (minecart->old_y - minecart->old_y2);
-				detright->old_z = detright->z - (minecart->old_z - minecart->old_z2);
+				if (P_IsLocalPlayer(player))
+				{
+					detright->old_x = detright->x - (minecart->old_x - minecart->old_x2);
+					detright->old_y = detright->y - (minecart->old_y - minecart->old_y2);
+					detright->old_z = detright->z - (minecart->old_z - minecart->old_z2);
+					detright->drawonlyforplayer = player;
+				}
+				else
+					P_RemoveMobj(detleft);
 			}
 		}
 		else
@@ -11386,12 +11445,15 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
 
 			for (i = -1; i < 2; i += 2)
 			{
+				mobj_t *bubble;
 				offsetH = i*P_ReturnThrustX(fume, fume->movedir, radiusV);
 				offsetV = i*P_ReturnThrustY(fume, fume->movedir, radiusV);
 				x = mo->x + radiusX + FixedMul(offsetH, factorX);
 				y = mo->y + radiusY + FixedMul(offsetH, factorY);
 				z = mo->z + heightoffset + offsetV;
-				P_SpawnMobj(x, y, z, MT_SMALLBUBBLE)->scale = mo->scale >> 1;
+				bubble = P_SpawnMobj(x, y, z, MT_SMALLBUBBLE);
+				bubble->scale = mo->scale >> 1;
+				P_SetTarget(&bubble->dontdrawforviewmobj, mo); // Hide the bubble in first-person
 			}
 
 			fume->movefactor = 0;
@@ -11460,7 +11522,11 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
 
 	// If dashmode is high enough, spawn a trail
 	if (player->normalspeed >= skins[player->skin].normalspeed*2)
-		P_SpawnGhostMobj(fume);
+	{
+		mobj_t *ghost = P_SpawnGhostMobj(fume);
+		if (!P_MobjWasRemoved(ghost))
+			P_SetTarget(&ghost->dontdrawforviewmobj, mo); // Hide the trail in first-person
+	}
 }
 
 //
@@ -12104,14 +12170,6 @@ void P_PlayerThink(player_t *player)
 				gmobj->tracer->frame |= tr_trans70<<FF_TRANSSHIFT;
 			}
 		}
-
-		// Hide the mobj from our sights if we're the displayplayer and chasecam is off,
-		// or secondarydisplayplayer and chasecam2 is off.
-		// Why not just not spawn the mobj?  Well, I'd rather only flirt with
-		// consistency so much...
-		if ((player == &players[displayplayer] && !camera.chase)
-		|| (splitscreen && player == &players[secondarydisplayplayer] && !camera2.chase))
-			gmobj->flags2 |= MF2_DONTDRAW;
 	}
 #endif
 
diff --git a/src/r_things.c b/src/r_things.c
index 248357e11f..eee284d46f 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -3322,9 +3322,14 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
 boolean R_ThingVisible (mobj_t *thing)
 {
 	return (!(
-				thing->sprite == SPR_NULL ||
-				( thing->flags2 & (MF2_DONTDRAW) ) ||
-				(r_viewmobj && (thing == r_viewmobj || (r_viewmobj->player && r_viewmobj->player->followmobj == thing)))
+		(thing->sprite == SPR_NULL) || // Don't draw null-sprites
+		(thing->flags2 & MF2_DONTDRAW) || // Don't draw MF2_LINKDRAW objects
+		(thing->drawonlyforplayer && thing->drawonlyforplayer != viewplayer) || // Don't draw other players' personal objects
+		(r_viewmobj && (
+		  (r_viewmobj == thing) || // Don't draw first-person players or awayviewmobj objects
+		  (r_viewmobj->player && r_viewmobj->player->followmobj == thing) || // Don't draw first-person players' followmobj
+		  (r_viewmobj == thing->dontdrawforviewmobj) // Don't draw objects that are hidden for the current view
+		))
 	));
 }
 
-- 
GitLab


From 10a8f2d05ec81ab1f348b139a9452afb27accc27 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Tue, 25 Jul 2023 11:30:55 -0400
Subject: [PATCH 226/518] Fix some minor warnings

- Fix `lib_getSkinSprite` overshadowing `sprites` global
- Rename terrible result_e `ok` so it stops overshadowing crap
- Remove inlining from w_wad.c since it reached the inline limit (remember kids, your compiler knows better than you these days whenever or not to inline something)
- Remove unused variable in G_PlayerReborn
---
 src/g_game.c      |  1 -
 src/lua_skinlib.c |  4 ++--
 src/p_floor.c     |  8 ++++----
 src/p_spec.h      |  2 +-
 src/w_wad.c       | 10 +++++-----
 5 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/src/g_game.c b/src/g_game.c
index 8264fbe6bb..b8c4349985 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2547,7 +2547,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 {
 	player_t *p;
 	INT32 score;
-	INT32 recordscore;
 	INT32 lives;
 	INT32 continues;
 	fixed_t camerascale;
diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c
index b7890a6c71..13e0dd9871 100644
--- a/src/lua_skinlib.c
+++ b/src/lua_skinlib.c
@@ -334,13 +334,13 @@ static const char *const sprites_opt[] = {
 // skin.sprites[i] -> sprites[i]
 static int lib_getSkinSprite(lua_State *L)
 {
-	spritedef_t *sprites = *(spritedef_t **)luaL_checkudata(L, 1, META_SKINSPRITES);
+	spritedef_t *sksprites = *(spritedef_t **)luaL_checkudata(L, 1, META_SKINSPRITES);
 	playersprite_t i = luaL_checkinteger(L, 2);
 
 	if (i < 0 || i >= NUMPLAYERSPRITES*2)
 		return luaL_error(L, LUA_QL("skin_t") " field 'sprites' index %d out of range (0 - %d)", i, (NUMPLAYERSPRITES*2)-1);
 
-	LUA_PushUserdata(L, &sprites[i], META_SKINSPRITESLIST);
+	LUA_PushUserdata(L, &sksprites[i], META_SKINSPRITESLIST);
 	return 1;
 }
 
diff --git a/src/p_floor.c b/src/p_floor.c
index 9c24f58514..38f0c5a0fb 100644
--- a/src/p_floor.c
+++ b/src/p_floor.c
@@ -155,7 +155,7 @@ result_e T_MovePlane(sector_t *sector, fixed_t speed, fixed_t dest, boolean crus
 		}
 	}
 
-	return ok;
+	return planeok;
 }
 
 //
@@ -1128,7 +1128,7 @@ void T_ThwompSector(thwomp_t *thwomp)
 				thwomp->direction         // direction
 			);
 
-			if (res == ok || res == pastdest)
+			if (res == planeok || res == pastdest)
 				T_MovePlane
 				(
 					thwomp->sector,             // sector
@@ -1160,7 +1160,7 @@ void T_ThwompSector(thwomp_t *thwomp)
 				thwomp->direction // direction
 			);
 
-			if (res == ok || res == pastdest)
+			if (res == planeok || res == pastdest)
 				T_MovePlane
 				(
 					thwomp->sector,   // sector
@@ -1465,7 +1465,7 @@ void T_RaiseSector(raise_t *raise)
 		direction           // direction
 	);
 
-	if (res == ok || res == pastdest)
+	if (res == planeok || res == pastdest)
 		T_MovePlane
 		(
 			raise->sector,    // sector
diff --git a/src/p_spec.h b/src/p_spec.h
index c8701749f8..50ab6410f1 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -895,7 +895,7 @@ typedef struct
 
 typedef enum
 {
-	ok,
+	planeok,
 	crushed,
 	pastdest
 } result_e;
diff --git a/src/w_wad.c b/src/w_wad.c
index b13dc8cc1f..171eab4f31 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -201,7 +201,7 @@ FILE *W_OpenWadFile(const char **filename, boolean useerrors)
 }
 
 // Look for all DEHACKED and Lua scripts inside a PK3 archive.
-static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum, boolean mainfile)
+static void W_LoadDehackedLumpsPK3(UINT16 wadnum, boolean mainfile)
 {
 	UINT16 posStart, posEnd;
 
@@ -241,7 +241,7 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum, boolean mainfile)
 }
 
 // search for all DEHACKED lump in all wads and load it
-static inline void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile)
+static void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile)
 {
 	UINT16 lump;
 
@@ -305,7 +305,7 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile)
   * \param resblock resulting MD5 checksum
   * \return 0 if MD5 checksum was made, and is at resblock, 1 if error was found
   */
-static inline INT32 W_MakeFileMD5(const char *filename, void *resblock)
+static INT32 W_MakeFileMD5(const char *filename, void *resblock)
 {
 #ifdef NOMD5
 	(void)filename;
@@ -1956,7 +1956,7 @@ void *W_CacheLumpNumForce(lumpnum_t lumpnum, INT32 tag)
 // return false.
 //
 // no outside code uses the PWAD form, for now
-static inline boolean W_IsLumpCachedPWAD(UINT16 wad, UINT16 lump, void *ptr)
+static boolean W_IsLumpCachedPWAD(UINT16 wad, UINT16 lump, void *ptr)
 {
 	void *lcache;
 
@@ -1988,7 +1988,7 @@ boolean W_IsLumpCached(lumpnum_t lumpnum, void *ptr)
 // return false.
 //
 // no outside code uses the PWAD form, for now
-static inline boolean W_IsPatchCachedPWAD(UINT16 wad, UINT16 lump, void *ptr)
+static boolean W_IsPatchCachedPWAD(UINT16 wad, UINT16 lump, void *ptr)
 {
 	void *lcache;
 
-- 
GitLab


From c6c3cc3209833557a70933c87d25564a4ba39014 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Wed, 26 Jul 2023 00:06:52 -0300
Subject: [PATCH 227/518] Drawseg clipping optimized, from prboom-plus

Co-authored-by: Sally Coolatta <tehrealsalt@gmail.com>
---
 src/r_things.c | 202 +++++++++++++++++++++++++++++++++++++------------
 src/r_things.h |   1 -
 2 files changed, 154 insertions(+), 49 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index 248357e11f..5f0f83c5cd 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -80,6 +80,33 @@ static spriteframe_t sprtemp[64];
 static size_t maxframe;
 static const char *spritename;
 
+//
+// Clipping against drawsegs optimization, from prboom-plus
+//
+// TODO: This should be done with proper subsector pass through
+// sprites which would ideally remove the need to do it at all.
+// Unfortunately, SRB2's drawing loop has lots of annoying
+// changes from Doom for portals, which make it hard to implement.
+
+typedef struct drawseg_xrange_item_s
+{
+	INT16 x1, x2;
+	drawseg_t *user;
+} drawseg_xrange_item_t;
+
+typedef struct drawsegs_xrange_s
+{
+	drawseg_xrange_item_t *items;
+	INT32 count;
+} drawsegs_xrange_t;
+
+#define DS_RANGES_COUNT 3
+static drawsegs_xrange_t drawsegs_xranges[DS_RANGES_COUNT];
+
+static drawseg_xrange_item_t *drawsegs_xrange;
+static size_t drawsegs_xrange_size = 0;
+static INT32 drawsegs_xrange_count = 0;
+
 // ==========================================================================
 //
 // Sprite loading routines: support sprites in pwad, dehacked sprite renaming,
@@ -3136,7 +3163,7 @@ static void R_HeightSecClip(vissprite_t *spr, INT32 x1, INT32 x2)
 
 // R_ClipVisSprite
 // Clips vissprites without drawing, so that portals can work. -Red
-void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, drawseg_t* dsstart, portal_t* portal)
+static void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, portal_t* portal)
 {
 	drawseg_t *ds;
 	INT32		x;
@@ -3156,21 +3183,23 @@ void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, drawseg_t* dsstart, p
 	// Pointer check was originally nonportable
 	// and buggy, by going past LEFT end of array:
 
-	//    for (ds = ds_p-1; ds >= drawsegs; ds--)    old buggy code
-	for (ds = ds_p; ds-- > dsstart;)
+	// e6y: optimization
+	if (drawsegs_xrange_size)
 	{
-		// determine if the drawseg obscures the sprite
-		if (ds->x1 > x2 ||
-			ds->x2 < x1 ||
-			(!ds->silhouette
-			 && !ds->maskedtexturecol))
-		{
-			// does not cover sprite
-			continue;
-		}
+		const drawseg_xrange_item_t *last = &drawsegs_xrange[drawsegs_xrange_count - 1];
+		drawseg_xrange_item_t *curr = &drawsegs_xrange[-1];
 
-		if (ds->portalpass != 66)
+		while (++curr <= last)
 		{
+			// determine if the drawseg obscures the sprite
+			if (curr->x1 > x2 || curr->x2 < x1)
+			{
+				// does not cover sprite
+				continue;
+			}
+
+			ds = curr->user;
+
 			if (ds->portalpass > 0 && ds->portalpass <= portalrender)
 				continue; // is a portal
 
@@ -3195,43 +3224,43 @@ void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, drawseg_t* dsstart, p
 				// seg is behind sprite
 				continue;
 			}
-		}
 
-		r1 = ds->x1 < x1 ? x1 : ds->x1;
-		r2 = ds->x2 > x2 ? x2 : ds->x2;
+			r1 = ds->x1 < x1 ? x1 : ds->x1;
+			r2 = ds->x2 > x2 ? x2 : ds->x2;
 
-		// clip this piece of the sprite
-		silhouette = ds->silhouette;
+			// clip this piece of the sprite
+			silhouette = ds->silhouette;
 
-		if (spr->gz >= ds->bsilheight)
-			silhouette &= ~SIL_BOTTOM;
+			if (spr->gz >= ds->bsilheight)
+				silhouette &= ~SIL_BOTTOM;
 
-		if (spr->gzt <= ds->tsilheight)
-			silhouette &= ~SIL_TOP;
+			if (spr->gzt <= ds->tsilheight)
+				silhouette &= ~SIL_TOP;
 
-		if (silhouette == SIL_BOTTOM)
-		{
-			// bottom sil
-			for (x = r1; x <= r2; x++)
-				if (spr->clipbot[x] == -2)
-					spr->clipbot[x] = ds->sprbottomclip[x];
-		}
-		else if (silhouette == SIL_TOP)
-		{
-			// top sil
-			for (x = r1; x <= r2; x++)
-				if (spr->cliptop[x] == -2)
-					spr->cliptop[x] = ds->sprtopclip[x];
-		}
-		else if (silhouette == (SIL_TOP|SIL_BOTTOM))
-		{
-			// both
-			for (x = r1; x <= r2; x++)
+			if (silhouette == SIL_BOTTOM)
+			{
+				// bottom sil
+				for (x = r1; x <= r2; x++)
+					if (spr->clipbot[x] == -2)
+						spr->clipbot[x] = ds->sprbottomclip[x];
+			}
+			else if (silhouette == SIL_TOP)
 			{
-				if (spr->clipbot[x] == -2)
-					spr->clipbot[x] = ds->sprbottomclip[x];
-				if (spr->cliptop[x] == -2)
-					spr->cliptop[x] = ds->sprtopclip[x];
+				// top sil
+				for (x = r1; x <= r2; x++)
+					if (spr->cliptop[x] == -2)
+						spr->cliptop[x] = ds->sprtopclip[x];
+			}
+			else if (silhouette == (SIL_TOP|SIL_BOTTOM))
+			{
+				// both
+				for (x = r1; x <= r2; x++)
+				{
+					if (spr->clipbot[x] == -2)
+						spr->clipbot[x] = ds->sprbottomclip[x];
+					if (spr->cliptop[x] == -2)
+						spr->cliptop[x] = ds->sprtopclip[x];
+				}
 			}
 		}
 	}
@@ -3305,16 +3334,93 @@ void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, drawseg_t* dsstart, p
 
 void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
 {
+	const size_t maxdrawsegs = ds_p - drawsegs;
+	const INT32 cx = viewwidth / 2;
+	drawseg_t* ds;
+	INT32 i;
+
+	// e6y
+	// Reducing of cache misses in the following R_DrawSprite()
+	// Makes sense for scenes with huge amount of drawsegs.
+	// ~12% of speed improvement on epic.wad map05
+	for (i = 0; i < DS_RANGES_COUNT; i++)
+	{
+		drawsegs_xranges[i].count = 0;
+	}
+
+	if (visspritecount - clippedvissprites <= 0)
+	{
+		return;
+	}
+
+	if (drawsegs_xrange_size < maxdrawsegs)
+	{
+		drawsegs_xrange_size = 2 * maxdrawsegs;
+
+		for (i = 0; i < DS_RANGES_COUNT; i++)
+		{
+			drawsegs_xranges[i].items = Z_Realloc(
+				drawsegs_xranges[i].items,
+				drawsegs_xrange_size * sizeof(drawsegs_xranges[i].items[0]),
+				PU_STATIC, NULL
+			);
+		}
+	}
+
+	for (ds = ds_p; ds-- > dsstart;)
+	{
+		if (ds->silhouette || ds->maskedtexturecol)
+		{
+			drawsegs_xranges[0].items[drawsegs_xranges[0].count].x1 = ds->x1;
+			drawsegs_xranges[0].items[drawsegs_xranges[0].count].x2 = ds->x2;
+			drawsegs_xranges[0].items[drawsegs_xranges[0].count].user = ds;
+
+			// e6y: ~13% of speed improvement on sunder.wad map10
+			if (ds->x1 < cx)
+			{
+				drawsegs_xranges[1].items[drawsegs_xranges[1].count] =
+					drawsegs_xranges[0].items[drawsegs_xranges[0].count];
+				drawsegs_xranges[1].count++;
+			}
+
+			if (ds->x2 >= cx)
+			{
+				drawsegs_xranges[2].items[drawsegs_xranges[2].count] =
+					drawsegs_xranges[0].items[drawsegs_xranges[0].count];
+				drawsegs_xranges[2].count++;
+			}
+
+			drawsegs_xranges[0].count++;
+		}
+	}
+
 	for (; clippedvissprites < visspritecount; clippedvissprites++)
 	{
 		vissprite_t *spr = R_GetVisSprite(clippedvissprites);
 
-		if (!(spr->cut & SC_BBOX)) // Do not clip bounding boxes
+		if (spr->cut & SC_BBOX)
+			continue;
+
+		INT32 x1 = (spr->cut & SC_SPLAT) ? 0 : spr->x1;
+		INT32 x2 = (spr->cut & SC_SPLAT) ? viewwidth : spr->x2;
+
+		if (x2 < cx)
+		{
+			drawsegs_xrange = drawsegs_xranges[1].items;
+			drawsegs_xrange_count = drawsegs_xranges[1].count;
+		}
+		else if (x1 >= cx)
 		{
-			INT32 x1 = (spr->cut & SC_SPLAT) ? 0 : spr->x1;
-			INT32 x2 = (spr->cut & SC_SPLAT) ? viewwidth : spr->x2;
-			R_ClipVisSprite(spr, x1, x2, dsstart, portal);
+			drawsegs_xrange = drawsegs_xranges[2].items;
+			drawsegs_xrange_count = drawsegs_xranges[2].count;
 		}
+		else
+		{
+			drawsegs_xrange = drawsegs_xranges[0].items;
+			drawsegs_xrange_count = drawsegs_xranges[0].count;
+		}
+
+		R_ClipVisSprite(spr, x1, x2, portal);
 	}
 }
 
diff --git a/src/r_things.h b/src/r_things.h
index bb8a1e97b0..e110053635 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -222,7 +222,6 @@ typedef struct vissprite_s
 extern UINT32 visspritecount;
 
 void R_ClipSprites(drawseg_t* dsstart, portal_t* portal);
-void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, drawseg_t* dsstart, portal_t* portal);
 
 boolean R_SpriteIsFlashing(vissprite_t *vis);
 
-- 
GitLab


From e12288be3f3823e5e51c22adccd0ffbf74b7e944 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Wed, 26 Jul 2023 00:19:55 -0300
Subject: [PATCH 228/518] Move these two lines after calculating the slopes

---
 src/r_bbox.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/r_bbox.c b/src/r_bbox.c
index 59d0893c4b..4302d1b9ab 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -129,9 +129,6 @@ draw_bbox_row
 	x1 = a->x;
 	x2 = b->x;
 
-	if (x2 >= viewwidth)
-		x2 = viewwidth - 1;
-
 	if (x1 == x2 || x1 >= viewwidth || x2 < 0)
 		return;
 
@@ -159,6 +156,9 @@ draw_bbox_row
 		x1 = 0;
 	}
 
+	if (x2 >= viewwidth)
+		x2 = viewwidth - 1;
+
 	while (x1 < x2)
 	{
 		raster_bbox_seg(x1, y1, s1, bb->color);
-- 
GitLab


From 29f7ecd46051884e365022bf62ce5291d7c36a06 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Tue, 18 Jul 2023 09:01:29 -0400
Subject: [PATCH 229/518] Remove P_RandomFixed call when players are moving

"Add some extra randomization" is not how RNG works
---
 src/p_user.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/p_user.c b/src/p_user.c
index 889e887c14..dcb84451bd 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -11623,10 +11623,6 @@ void P_PlayerThink(player_t *player)
 
 	cmd = &player->cmd;
 
-	// Add some extra randomization.
-	if (cmd->forwardmove)
-		P_RandomFixed();
-
 #ifdef PARANOIA
 	if (player->playerstate == PST_REBORN)
 		I_Error("player %s is in PST_REBORN\n", sizeu1(playeri));
-- 
GitLab


From dc9346a45df2e5f9e7711846cd198c6d150e5537 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Wed, 26 Jul 2023 07:50:47 -0400
Subject: [PATCH 230/518] Add forwardmove rng backwards compat with demos

---
 src/g_demo.c | 3 +++
 src/g_demo.h | 1 +
 src/p_user.c | 7 +++++++
 3 files changed, 11 insertions(+)

diff --git a/src/g_demo.c b/src/g_demo.c
index a7b9098fa5..adb8e891d6 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -58,6 +58,7 @@ static UINT8 demoflags;
 static UINT16 demoversion;
 boolean singledemo; // quit after playing a demo from cmdline
 boolean demo_start; // don't start playing demo right away
+boolean demo_forwardmove_rng; // old demo backwards compatibility
 boolean demosynced = true; // console warning message
 
 boolean metalrecording; // recording as metal sonic
@@ -2028,6 +2029,7 @@ void G_DoPlayDemo(char *defdemoname)
 	version = READUINT8(demo_p);
 	subversion = READUINT8(demo_p);
 	demoversion = READUINT16(demo_p);
+	demo_forwardmove_rng = (demoversion < 0x0010);
 	switch(demoversion)
 	{
 	case 0x000f:
@@ -2339,6 +2341,7 @@ UINT8 G_CheckDemoForError(char *defdemoname)
 	demo_p++; // version
 	demo_p++; // subversion
 	demoversion = READUINT16(demo_p);
+	demo_forwardmove_rng = (demoversion < 0x0010);
 	switch(demoversion)
 	{
 	case 0x000d:
diff --git a/src/g_demo.h b/src/g_demo.h
index 383d2719b3..379c57428a 100644
--- a/src/g_demo.h
+++ b/src/g_demo.h
@@ -38,6 +38,7 @@ extern demo_file_override_e demofileoverride;
 // Quit after playing a demo from cmdline.
 extern boolean singledemo;
 extern boolean demo_start;
+extern boolean demo_forwardmove_rng;
 extern boolean demosynced;
 
 extern mobj_t *metalplayback;
diff --git a/src/p_user.c b/src/p_user.c
index dcb84451bd..1c2e4f7c9b 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -11623,6 +11623,13 @@ void P_PlayerThink(player_t *player)
 
 	cmd = &player->cmd;
 
+	if (demoplayback && demo_forwardmove_rng)
+	{
+		// Smelly demo backwards compatibility
+		if (cmd->forwardmove)
+			P_RandomFixed();
+	}
+
 #ifdef PARANOIA
 	if (player->playerstate == PST_REBORN)
 		I_Error("player %s is in PST_REBORN\n", sizeu1(playeri));
-- 
GitLab


From 0abe55619731b0c70e05d6aed996366814d04f6f Mon Sep 17 00:00:00 2001
From: Eidolon <furyhunter600@gmail.com>
Date: Sat, 5 Nov 2022 20:50:57 -0500
Subject: [PATCH 231/518] cmake: Enable C++ 17 and C11

---
 src/CMakeLists.txt | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2f4467a322..7ec229fe0d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,6 +5,8 @@ if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows" AND
 	target_link_options(SRB2SDL2 PRIVATE "-static")
 endif()
 
+target_compile_features(SRB2SDL2 PRIVATE c_std_11 cxx_std_17)
+
 # Core sources
 target_sourcefile(c)
 target_sources(SRB2SDL2 PRIVATE comptime.c md5.c config.h.in)
-- 
GitLab


From 763912700396b9d1eba495cd084317997b1232e1 Mon Sep 17 00:00:00 2001
From: Eidolon <furyhunter600@gmail.com>
Date: Sun, 6 Nov 2022 01:08:43 -0600
Subject: [PATCH 232/518] cmake: Add Catch2 unit testing

---
 CMakeLists.txt | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 915912af5e..568789a0de 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -58,6 +58,7 @@ option(
 	"Link dependencies using CMake's find_package and do not use internal builds"
 	${SRB2_CONFIG_SYSTEM_LIBRARIES_DEFAULT}
 )
+option(SRB2_CONFIG_ENABLE_TESTS "Build the test suite" ON)
 # This option isn't recommended for distribution builds and probably won't work (yet).
 cmake_dependent_option(
 	SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES
@@ -76,6 +77,25 @@ option(SRB2_CONFIG_ZDEBUG "Compile with ZDEBUG defined." OFF)
 option(SRB2_CONFIG_PROFILEMODE "Compile for profiling (GCC only)." OFF)
 set(SRB2_CONFIG_ASSET_DIRECTORY "" CACHE PATH "Path to directory that contains all asset files for the installer. If set, assets will be part of installation and cpack.")
 
+if(SRB2_CONFIG_ENABLE_TESTS)
+	# https://github.com/catchorg/Catch2
+	CPMAddPackage(
+		NAME Catch2
+		VERSION 3.1.1
+		GITHUB_REPOSITORY catchorg/Catch2
+		OPTIONS
+			"CATCH_INSTALL_DOCS OFF"
+	)
+	list(APPEND CMAKE_MODULE_PATH "${Catch2_SOURCE_DIR}/extras")
+	include(CTest)
+	include(Catch)
+	add_executable(srb2tests)
+	# To add tests, use target_sources to add individual test files to the target in subdirs.
+	target_link_libraries(srb2tests PRIVATE Catch2::Catch2 Catch2::Catch2WithMain)
+	target_compile_features(srb2tests PRIVATE c_std_11 cxx_std_17)
+	catch_discover_tests(srb2tests)
+endif()
+
 # Enable CCache
 # (Set USE_CCACHE=ON to use, CCACHE_OPTIONS for options)
 if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL Windows)
-- 
GitLab


From 2a8dbed3369afad767c23444fc55a635cd9afa26 Mon Sep 17 00:00:00 2001
From: Eidolon <furyhunter600@gmail.com>
Date: Sun, 13 Nov 2022 20:06:15 -0600
Subject: [PATCH 233/518] Redefine boolean for C++ compatibility

---
 src/CMakeLists.txt       |  1 +
 src/doomtype.h           | 60 +++++++++++++++++-----------------------
 src/tests/CMakeLists.txt |  3 ++
 src/tests/boolcompat.cpp |  8 ++++++
 4 files changed, 38 insertions(+), 34 deletions(-)
 create mode 100644 src/tests/CMakeLists.txt
 create mode 100644 src/tests/boolcompat.cpp

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7ec229fe0d..9352d55ef6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -291,6 +291,7 @@ if(SRB2_CONFIG_PROFILEMODE AND "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
 endif()
 
 add_subdirectory(sdl)
+add_subdirectory(tests)
 
 # strip debug symbols into separate file when using gcc.
 # to be consistent with Makefile, don't generate for OS X.
diff --git a/src/doomtype.h b/src/doomtype.h
index f6c236e20b..f834a3f4f8 100644
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -17,6 +17,10 @@
 #ifndef __DOOMTYPE__
 #define __DOOMTYPE__
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #ifdef _WIN32
 //#define WIN32_LEAN_AND_MEAN
 #define RPC_NO_WINDOWS_H
@@ -100,24 +104,6 @@ char *strcasestr(const char *in, const char *what);
 int startswith (const char *base, const char *tag);
 int endswith (const char *base, const char *tag);
 
-#if defined (macintosh) //|| defined (__APPLE__) //skip all boolean/Boolean crap
-	#define true 1
-	#define false 0
-	#define min(x,y) (((x)<(y)) ? (x) : (y))
-	#define max(x,y) (((x)>(y)) ? (x) : (y))
-
-#ifdef macintosh
-	#define stricmp strcmp
-	#define strnicmp strncmp
-#endif
-
-	#define boolean INT32
-
-	#ifndef O_BINARY
-	#define O_BINARY 0
-	#endif
-#endif //macintosh
-
 #if defined (_WIN32) || defined (__HAIKU__)
 #define HAVE_DOSSTR_FUNCS
 #endif
@@ -144,22 +130,24 @@ size_t strlcpy(char *dst, const char *src, size_t siz);
 
 /* Boolean type definition */
 
-// \note __BYTEBOOL__ used to be set above if "macintosh" was defined,
-// if macintosh's version of boolean type isn't needed anymore, then isn't this macro pointless now?
-#ifndef __BYTEBOOL__
-	#define __BYTEBOOL__
-
-	//faB: clean that up !!
-	#if defined( _MSC_VER)  && (_MSC_VER >= 1800) // MSVC 2013 and forward
-		#include "stdbool.h"
-	#elif defined (_WIN32)
-		#define false   FALSE           // use windows types
-		#define true    TRUE
-		#define boolean BOOL
-	#else
-		typedef enum {false, true} boolean;
-	#endif
-#endif // __BYTEBOOL__
+// Note: C++ bool and C99/C11 _Bool are NOT compatible.
+// Historically, boolean was win32 BOOL on Windows. For equivalence, it's now
+// int32_t. "true" and "false" are only declared for C code; in C++, conversion
+// between "bool" and "int32_t" takes over.
+#ifndef _WIN32
+typedef int32_t boolean;
+#else
+#define BOOL boolean
+#endif
+
+#ifndef __cplusplus
+#ifndef _WIN32
+enum {false = 0, true = 1};
+#else
+#define false FALSE
+#define true TRUE
+#endif
+#endif
 
 /* 7.18.2.1  Limits of exact-width integer types */
 
@@ -387,4 +375,8 @@ unset_bit_array (bitarray_t * const array, const int value)
 
 typedef UINT64 precise_t;
 
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
 #endif //__DOOMTYPE__
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
new file mode 100644
index 0000000000..28c4ce492f
--- /dev/null
+++ b/src/tests/CMakeLists.txt
@@ -0,0 +1,3 @@
+target_sources(srb2tests PRIVATE
+	boolcompat.cpp
+)
diff --git a/src/tests/boolcompat.cpp b/src/tests/boolcompat.cpp
new file mode 100644
index 0000000000..fee40cd36f
--- /dev/null
+++ b/src/tests/boolcompat.cpp
@@ -0,0 +1,8 @@
+#include <catch2/catch_test_macros.hpp>
+
+#include "../doomtype.h"
+
+TEST_CASE("C++ bool is convertible to doomtype.h boolean") {
+	REQUIRE(static_cast<boolean>(true) == 1);
+	REQUIRE(static_cast<boolean>(false) == 0);
+}
-- 
GitLab


From 12b6a7cabfdd298e7c23d1c5030e637d224e2288 Mon Sep 17 00:00:00 2001
From: Eidolon <furyhunter600@gmail.com>
Date: Sun, 13 Nov 2022 20:07:36 -0600
Subject: [PATCH 234/518] Don't preproc. define `inline` in C++

---
 src/doomtype.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/doomtype.h b/src/doomtype.h
index f834a3f4f8..e6da5e50c7 100644
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -82,7 +82,9 @@ typedef long ssize_t;
 #endif
 	#define strncasecmp             strnicmp
 	#define strcasecmp              stricmp
+#ifndef __cplusplus
 	#define inline                  __inline
+#endif
 #elif defined (__WATCOMC__)
 	#include <dos.h>
 	#include <sys\types.h>
-- 
GitLab


From 101cdea022167a6b5c7aefa2a1faa5e47d9168b4 Mon Sep 17 00:00:00 2001
From: Eidolon <furyhunter600@gmail.com>
Date: Sat, 5 Nov 2022 18:31:06 -0500
Subject: [PATCH 235/518] cmake: Add optional clang-tidy integration

clang-tidy performs static analysis over the compilation unit,
producing more comprehensive compile warnings than are normally
generated by the compiler. For example, it will trace branches in a
function to find the exact conditions in which a null dereference
can occur.

Note that this produces an absurd amount of warnings for our existing
C code, and significantly slows compile times. It is pretty effective
at its job though. I think it would be good to turn on by default for
upcoming C++ code.
---
 CMakeLists.txt                         |  3 +++
 cmake/Modules/clang-tidy-default.cmake | 21 +++++++++++++++++++++
 src/CMakeLists.txt                     | 10 ++++++++++
 3 files changed, 34 insertions(+)
 create mode 100644 cmake/Modules/clang-tidy-default.cmake

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 915912af5e..7fc050ea03 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -53,6 +53,9 @@ else()
 	set(SRB2_CONFIG_SYSTEM_LIBRARIES_DEFAULT OFF)
 endif()
 
+# Clang tidy options will be ignored if CMAKE_<LANG>_CLANG_TIDY are set.
+option(SRB2_CONFIG_ENABLE_CLANG_TIDY_C "Enable default clang-tidy check configuration for C" OFF)
+option(SRB2_CONFIG_ENABLE_CLANG_TIDY_CXX "Enable default clang-tidy check configuration for C++" OFF)
 option(
 	SRB2_CONFIG_SYSTEM_LIBRARIES
 	"Link dependencies using CMake's find_package and do not use internal builds"
diff --git a/cmake/Modules/clang-tidy-default.cmake b/cmake/Modules/clang-tidy-default.cmake
new file mode 100644
index 0000000000..2be3af10d9
--- /dev/null
+++ b/cmake/Modules/clang-tidy-default.cmake
@@ -0,0 +1,21 @@
+find_program(CLANG_TIDY clang-tidy)
+
+# Note: Apple Clang does not ship with clang tools. If you want clang-tidy on
+# macOS, it's best to install the Homebrew llvm bottle and set CLANG_TIDY
+# in your build directory. The llvm package is keg-only, so it will not
+# collide with Apple Clang.
+
+function(target_set_default_clang_tidy target lang checks)
+    if("${CLANG_TIDY}" STREQUAL "CLANG_TIDY-NOTFOUND")
+        return()
+    endif()
+
+    get_target_property(c_clang_tidy_prop SRB2SDL2 C_CLANG_TIDY)
+    if(NOT ("${c_clang_tidy_prop}" STREQUAL "c_clang_tidy_prop-NOTFOUND"))
+        return()
+    endif()
+
+    set_target_properties("${target}" PROPERTIES
+	    ${lang}_CLANG_TIDY "${CLANG_TIDY};-checks=${checks}"
+    )
+endfunction()
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2f4467a322..18c16eeb45 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,3 +1,5 @@
+include(clang-tidy-default)
+
 add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32)
 
 if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows" AND NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}" AND NOT "${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}")
@@ -329,3 +331,11 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL Windows AND NOT "${SRB2_CONFIG_INTERNAL_LIBRA
 		COMMENT "Copying runtime DLLs"
 	)
 endif()
+
+# Setup clang-tidy
+if(SRB2_CONFIG_ENABLE_CLANG_TIDY_C)
+	target_set_default_clang_tidy(SRB2SDL2 C "-*,clang-analyzer-*,-clang-analyzer-cplusplus-*")
+endif()
+if(SRB2_CONFIG_ENABLE_CLANG_TIDY_CXX)
+	target_set_default_clang_tidy(SRB2SDL2 CXX "-*,clang-analyzer-*,modernize-*")
+endif()
-- 
GitLab


From 9253a8cc549e862a9f997b18f060a9ecb1335b03 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Fri, 2 Jun 2023 18:49:37 +0200
Subject: [PATCH 236/518] Fix use-after-free when calling v.drawString in Lua

---
 src/lua_hudlib_drawlist.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/lua_hudlib_drawlist.c b/src/lua_hudlib_drawlist.c
index 6f83094ac0..c518ba5254 100644
--- a/src/lua_hudlib_drawlist.c
+++ b/src/lua_hudlib_drawlist.c
@@ -177,9 +177,18 @@ static const char *CopyString(huddrawlist_h list, const char* str)
 	lenstr = strlen(str);
 	if (list->strbuf_capacity <= list->strbuf_len + lenstr + 1)
 	{
+		const char *old_offset = list->strbuf;
+		size_t i;
 		if (list->strbuf_capacity == 0) list->strbuf_capacity = 256;
 		else list->strbuf_capacity *= 2;
 		list->strbuf = (char*) Z_Realloc(list->strbuf, sizeof(char) * list->strbuf_capacity, PU_STATIC, NULL);
+
+		// align the string pointers to make sure old pointers don't point towards invalid addresses
+		// this is necessary since Z_ReallocAlign might actually move the string buffer in memory
+		for (i = 0; i < list->items_len; i++)
+		{
+			list->items[i].str += list->strbuf - old_offset;
+		}
 	}
 	const char *result = (const char *) &list->strbuf[list->strbuf_len];
 	strncpy(&list->strbuf[list->strbuf_len], str, lenstr + 1);
-- 
GitLab


From 4bbf4c21c2cd81e402537509e68d05292d931e16 Mon Sep 17 00:00:00 2001
From: katsy <205-katsy@users.noreply.git.do.srb2.org>
Date: Wed, 26 Jul 2023 12:14:21 +0000
Subject: [PATCH 237/518] Warn in console instead of hard erroring when
 attempting to modify a vanilla skincolor (resolves #1046)

---
 src/lua_infolib.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 4d150347d9..1144648d00 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1709,7 +1709,7 @@ static int lib_setSkinColor(lua_State *L)
 		else if (i == 6 || (str && fastcmp(str,"accessible"))) {
 			boolean v = lua_toboolean(L, 3);
 			if (cnum < FIRSTSUPERCOLOR && v != skincolors[cnum].accessible)
-				return luaL_error(L, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
+				CONS_Alert(CONS_WARNING, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
 			else
 				info->accessible = v;
 		}
@@ -1804,7 +1804,7 @@ static int skincolor_set(lua_State *L)
 	else if (fastcmp(field,"accessible")) {
 		boolean v = lua_toboolean(L, 3);
 		if (cnum < FIRSTSUPERCOLOR && v != skincolors[cnum].accessible)
-			return luaL_error(L, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
+			CONS_Alert(CONS_WARNING, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
 		else
 			info->accessible = v;
 	} else
-- 
GitLab


From 9e5a8285089c671f827d52b8a30f9cc903445a0f Mon Sep 17 00:00:00 2001
From: tertu marybig <flameshadowxeroshin@gmail.com>
Date: Wed, 26 Jul 2023 14:53:01 +0000
Subject: [PATCH 238/518] New M_Random implementation

---
 src/android/i_system.c |  22 +++++
 src/d_main.c           |  12 +--
 src/dummy/i_system.c   |   5 ++
 src/i_system.h         |   4 +
 src/lua_hudlib.c       |   9 --
 src/m_random.c         | 192 ++++++++++++++++++++++++++++++++++++++---
 src/m_random.h         |   3 +
 src/sdl/i_system.c     |  38 ++++++++
 8 files changed, 259 insertions(+), 26 deletions(-)

diff --git a/src/android/i_system.c b/src/android/i_system.c
index ff8b88de53..9d798d452f 100644
--- a/src/android/i_system.c
+++ b/src/android/i_system.c
@@ -278,4 +278,26 @@ char *I_ClipboardPaste(void)
 
 void I_RegisterSysCommands(void) {}
 
+// This is identical to the SDL implementation.
+size_t I_GetRandomBytes(char *destination, size_t count)
+{
+  FILE *rndsource;
+  size_t actual_bytes;
+
+  if (!(rndsource = fopen("/dev/urandom", "r")))
+	  if (!(rndsource = fopen("/dev/random", "r")))
+		  actual_bytes = 0;
+
+  if (rndsource)
+  {
+	  actual_bytes = fread(destination, 1, count, rndsource);
+	  fclose(rndsource);
+  }
+
+  if (actual_bytes == 0)
+    I_OutputMsg("I_GetRandomBytes(): couldn't get any random bytes");
+
+  return actual_bytes;
+}
+
 #include "../sdl/dosstr.c"
diff --git a/src/d_main.c b/src/d_main.c
index 5861f98865..389cdd2db5 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -70,6 +70,7 @@
 #include "filesrch.h" // refreshdirmenu
 #include "g_input.h" // tutorial mode control scheming
 #include "m_perfstats.h"
+#include "m_random.h"
 
 #ifdef CMAKECONFIG
 #include "config.h"
@@ -1334,11 +1335,12 @@ void D_SRB2Main(void)
 	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));
-	rand();
-	rand();
-	rand();
+	// seed M_Random because it is necessary; seed P_Random for scripts that
+	// might want to use random numbers immediately at start
+	if (!M_RandomSeedFromOS())
+		M_RandomSeed((UINT32)time(NULL)); // less good but serviceable
+
+	P_SetRandSeed(M_RandomizedSeed());
 
 	if (M_CheckParm("-password") && M_IsNextParm())
 		D_SetPassword(M_GetNextParm());
diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c
index 8556c02486..125d2e8aec 100644
--- a/src/dummy/i_system.c
+++ b/src/dummy/i_system.c
@@ -180,6 +180,11 @@ const char *I_ClipboardPaste(void)
 	return NULL;
 }
 
+size_t I_GetRandomBytes(char *destination, size_t amount)
+{
+	return 0;
+}
+
 void I_RegisterSysCommands(void) {}
 
 void I_GetCursorPosition(INT32 *x, INT32 *y)
diff --git a/src/i_system.h b/src/i_system.h
index deea9f8a8d..834dd40914 100644
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -49,6 +49,10 @@ size_t I_GetFreeMem(size_t *total);
   */
 precise_t I_GetPreciseTime(void);
 
+/**	\brief	Fills a buffer with random data, returns amount of data obtained.
+  */
+size_t I_GetRandomBytes(char *destination, size_t count);
+
 /** \brief  Get the precision of precise_t in units per second. Invocations of
             this function for the program's duration MUST return the same value.
   */
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index c7f67e93ac..2e3bb9c683 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -1256,8 +1256,6 @@ static int libd_RandomKey(lua_State *L)
 	INT32 a = (INT32)luaL_checkinteger(L, 1);
 
 	HUDONLY
-	if (a > 65536)
-		LUA_UsageWarning(L, "v.RandomKey: range > 65536 is undefined behavior");
 	lua_pushinteger(L, M_RandomKey(a));
 	return 1;
 }
@@ -1268,13 +1266,6 @@ static int libd_RandomRange(lua_State *L)
 	INT32 b = (INT32)luaL_checkinteger(L, 2);
 
 	HUDONLY
-	if (b < a) {
-		INT32 c = a;
-		a = b;
-		b = c;
-	}
-	if ((b-a+1) > 65536)
-		LUA_UsageWarning(L, "v.RandomRange: range > 65536 is undefined behavior");
 	lua_pushinteger(L, M_RandomRange(a, b));
 	return 1;
 }
diff --git a/src/m_random.c b/src/m_random.c
index 8b5138b9c8..536fbfbbd1 100644
--- a/src/m_random.c
+++ b/src/m_random.c
@@ -3,6 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
+// Copyright (C) 2022-2023 by tertu marybig.
 // Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
@@ -14,11 +15,122 @@
 
 #include "doomdef.h"
 #include "doomtype.h"
+#include "i_system.h" // I_GetRandomBytes
 
 #include "m_random.h"
 #include "m_fixed.h"
 
-#include "m_cond.h" // totalplaytime
+// SFC32 random number generator implementation
+
+typedef struct rnstate_s {
+	UINT32 data[3];
+	UINT32 counter;
+} rnstate_t;
+
+/** Generate a raw uniform random number using a particular state.
+  *
+  * \param state The RNG state to use.
+  * \return A random UINT32.
+  */
+static inline UINT32 RandomState_Get32(rnstate_t *state) {
+	UINT32 result, b, c;
+
+	b = state->data[1];
+	c = state->data[2];
+	result = state->data[0] + b + state->counter++;
+
+	state->data[0] = b ^ (b >> 9);
+	state->data[1] = c * 9;
+	state->data[2] = ((c << 21) | (c >> 11)) + result;
+
+	return result;
+}
+
+/** Seed an SFC32 RNG state with up to 96 bits of seed data.
+  *
+  * \param state The RNG state to seed.
+  * \param seeds A pointer to up to 3 UINT32s to use as seed data.
+  * \param seed_count The number of seed words.
+  */
+static inline void RandomState_Seed(rnstate_t *state, UINT32 *seeds, size_t seed_count)
+{
+	size_t i;
+
+	state->counter = 1;
+
+	for(i = 0; i < 3; i++)
+	{
+		UINT32 seed_word;
+
+		if(i < seed_count)
+			seed_word = seeds[i];
+		else
+			seed_word = 0;
+
+		// For SFC32, seed data should be stored in the state in reverse order.
+		state->data[2-i] = seed_word;
+	}
+
+	for(i = 0; i < 16; i++)
+		RandomState_Get32(state);
+}
+
+/** Gets a uniform number in the range [0, limit).
+  * Technique is based on a combination of scaling and rejection sampling
+  * and is adapted from Daniel Lemire.
+  *
+  * \note Any UINT32 is a valid argument for limit.
+  *
+  * \param state The RNG state to use.
+  * \param limit The upper limit of the range.
+  * \return A UINT32 in the range [0, limit).
+  */
+static inline UINT32 RandomState_GetKey32(rnstate_t *state, const UINT32 limit)
+{
+	UINT32 raw_random, scaled_lower_word;
+	UINT64 scaled_random;
+
+	// This algorithm won't work correctly if passed a 0.
+	if (limit == 0) return 0;
+
+	raw_random = RandomState_Get32(state);
+	scaled_random = (UINT64)raw_random * (UINT64)limit;
+
+	/*The high bits of scaled_random now contain the number we want, but it is
+	possible, depending on the number we generated and the value of limit,
+	that there is bias in the result. The rest of this code is for ensuring
+	that does not happen.
+	*/
+	scaled_lower_word = (UINT32)scaled_random;
+
+	// If we're lucky, we can bail out now and avoid the division
+	if (scaled_lower_word < limit)
+	{
+		// Scale the limit to improve the chance of success.
+		// After this, the first result might turn out to be good enough.
+		UINT32 scaled_limit;
+		// An explanation for this trick: scaled_limit should be
+		// (UINT32_MAX+1)%range, but if that was computed directly the result
+		// would need to be computed as a UINT64. This trick allows it to be
+		// computed using 32-bit arithmetic.
+		scaled_limit = (-limit) % limit;
+
+		while (scaled_lower_word < scaled_limit)
+		{
+			raw_random = RandomState_Get32(state);
+			scaled_random = (UINT64)raw_random * (UINT64)limit;
+			scaled_lower_word = (UINT32)scaled_random;
+		}
+	}
+
+	return scaled_random >> 32;
+}
+
+// The default seed is the hexadecimal digits of pi, though it will be overwritten.
+static rnstate_t m_randomstate = {
+	.data = {0x4A3B6035U, 0x99555606U, 0x6F603421U},
+	.counter = 16
+};
 
 // ---------------------------
 // RNG functions (not synched)
@@ -31,13 +143,7 @@
   */
 fixed_t M_RandomFixed(void)
 {
-#if RAND_MAX < 65535
-	// Compensate for insufficient randomness.
-	fixed_t rndv = (rand()&1)<<15;
-	return rand()^rndv;
-#else
-	return (rand() & 0xFFFF);
-#endif
+	return RandomState_Get32(&m_randomstate) >> (32-FRACBITS);
 }
 
 /** Provides a random byte. Distribution is uniform.
@@ -47,7 +153,7 @@ fixed_t M_RandomFixed(void)
   */
 UINT8 M_RandomByte(void)
 {
-	return (rand() & 0xFF);
+	return RandomState_Get32(&m_randomstate) >> 24;
 }
 
 /** Provides a random integer for picking random elements from an array.
@@ -59,7 +165,22 @@ UINT8 M_RandomByte(void)
   */
 INT32 M_RandomKey(INT32 a)
 {
-	return (INT32)((rand()/((float)RAND_MAX+1.0f))*a);
+	boolean range_is_negative;
+	INT64 range;
+	INT32 random_result;
+
+	range = a;
+	range_is_negative = range < 0;
+
+	if(range_is_negative)
+		range = -range;
+
+	random_result = RandomState_GetKey32(&m_randomstate, (UINT32)range);
+
+	if(range_is_negative)
+		random_result = -random_result;
+
+	return random_result;
 }
 
 /** Provides a random integer in a given range.
@@ -72,7 +193,46 @@ INT32 M_RandomKey(INT32 a)
   */
 INT32 M_RandomRange(INT32 a, INT32 b)
 {
-	return (INT32)((rand()/((float)RAND_MAX+1.0f))*(b-a+1))+a;
+  	if (b < a)
+	{
+    	INT32 temp;
+
+		temp = a;
+		a = b;
+		b = temp;
+	}
+
+	const UINT32 spread = b-a+1;
+	return (INT32)((INT64)RandomState_GetKey32(&m_randomstate, spread) + a);
+}
+
+/** Attempts to seed the unsynched RNG from a good random number source
+  * provided by the operating system.
+  * \return true on success, false on failure.
+  */
+boolean M_RandomSeedFromOS(void)
+{
+	UINT32 complete_word_count;
+
+	union {
+		UINT32 words[3];
+		char bytes[sizeof(UINT32[3])];
+	} seed_data;
+
+	complete_word_count = I_GetRandomBytes((char *)&seed_data.bytes, sizeof(seed_data)) / sizeof(UINT32);
+
+	// If we get even 1 word of seed, it's fine, but any less probably is not fine.
+	if (complete_word_count == 0)
+		return false;
+
+	RandomState_Seed(&m_randomstate, (UINT32 *)&seed_data.words, complete_word_count);
+
+	return true;
+}
+
+void M_RandomSeed(UINT32 seed)
+{
+	RandomState_Seed(&m_randomstate, &seed, 1);
 }
 
 
@@ -246,10 +406,18 @@ void P_SetRandSeedD(const char *rfile, INT32 rline, UINT32 seed)
 }
 
 /** Gets a randomized seed for setting the random seed.
+  * This function will never return 0, as the current P_Random implementation
+  * cannot handle a zero seed. Any other seed is equally likely.
   *
   * \sa P_GetRandSeed
   */
 UINT32 M_RandomizedSeed(void)
 {
-	return ((serverGamedata->totalplaytime & 0xFFFF) << 16) | M_RandomFixed();
+	UINT32 seed;
+
+	do {
+		seed = RandomState_Get32(&m_randomstate);
+	} while(seed == 0);
+
+	return seed;
 }
diff --git a/src/m_random.h b/src/m_random.h
index 824287e27d..a7c07a46b5 100644
--- a/src/m_random.h
+++ b/src/m_random.h
@@ -3,6 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2012-2016 by Matthew "Kaito Sinclaire" Walsh.
+// Copyright (C) 2022-2023 by tertu marybig.
 // Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
@@ -29,6 +30,8 @@ fixed_t M_RandomFixed(void);
 UINT8   M_RandomByte(void);
 INT32   M_RandomKey(INT32 a);
 INT32   M_RandomRange(INT32 a, INT32 b);
+boolean M_RandomSeedFromOS(void);
+void    M_RandomSeed(UINT32 a);
 
 // PRNG functions
 #ifdef DEBUGRANDOM
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 847ab2646f..c21226ac3f 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -41,6 +41,12 @@ typedef DWORD (WINAPI *p_timeGetTime) (void);
 typedef UINT (WINAPI *p_timeEndPeriod) (UINT);
 typedef HANDLE (WINAPI *p_OpenFileMappingA) (DWORD, BOOL, LPCSTR);
 typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
+
+// This is for RtlGenRandom.
+#define SystemFunction036 NTAPI SystemFunction036
+#include <ntsecapi.h>
+#undef SystemFunction036
+
 #endif
 #include <stdio.h>
 #include <stdlib.h>
@@ -2776,6 +2782,38 @@ INT32 I_PutEnv(char *variable)
 #endif
 }
 
+size_t I_GetRandomBytes(char *destination, size_t count)
+{
+#if defined (__unix__) || defined (UNIXCOMMON) || defined(__APPLE__)
+	FILE *rndsource;
+	size_t actual_bytes;
+
+	if (!(rndsource = fopen("/dev/urandom", "r")))
+		if (!(rndsource = fopen("/dev/random", "r")))
+			actual_bytes = 0;
+
+	if (rndsource)
+	{
+		actual_bytes = fread(destination, 1, count, rndsource);
+		fclose(rndsource);
+	}
+
+	if (actual_bytes == 0)
+		I_OutputMsg("I_GetRandomBytes(): couldn't get any random bytes");
+
+	return actual_bytes;
+#elif defined (_WIN32)
+	if (RtlGenRandom(destination, count))
+		return count;
+
+	I_OutputMsg("I_GetRandomBytes(): couldn't get any random bytes");
+	return 0;
+#else
+	#warning SDL I_GetRandomBytes is not implemented on this platform.
+	return 0;
+#endif
+}
+
 INT32 I_ClipboardCopy(const char *data, size_t size)
 {
 	char storage[256];
-- 
GitLab


From 42bf107b71f157058ae0a57d40d2d85a0d5c52ae Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Thu, 15 Dec 2022 20:54:15 -0500
Subject: [PATCH 239/518] #define BOOL boolean -> #define boolean BOOL

---
 src/doomtype.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doomtype.h b/src/doomtype.h
index e6da5e50c7..c59e173813 100644
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -139,7 +139,7 @@ size_t strlcpy(char *dst, const char *src, size_t siz);
 #ifndef _WIN32
 typedef int32_t boolean;
 #else
-#define BOOL boolean
+#define boolean BOOL
 #endif
 
 #ifndef __cplusplus
-- 
GitLab


From 91e915b963456c1a26ea6951ba96aa4c54f0780c Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Wed, 26 Jul 2023 13:44:43 -0400
Subject: [PATCH 240/518] Fix recordscore not including intermission bonuses

---
 src/y_inter.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/y_inter.c b/src/y_inter.c
index 6e7d362a77..1b1f49e0b7 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -2065,6 +2065,9 @@ static void Y_AwardCoopBonuses(void)
 			players[i].score += localbonuses[j].points;
 			if (players[i].score > MAXSCORE)
 				players[i].score = MAXSCORE;
+			players[i].recordscore += localbonuses[j].points;
+			if (players[i].recordscore > MAXSCORE)
+				players[i].recordscore = MAXSCORE;
 		}
 
 		ptlives = min(
@@ -2121,6 +2124,10 @@ static void Y_AwardSpecialStageBonus(void)
 		players[i].score += localbonuses[1].points;
 		if (players[i].score > MAXSCORE)
 			players[i].score = MAXSCORE;
+		players[i].recordscore += localbonuses[0].points;
+		players[i].recordscore += localbonuses[1].points;
+		if (players[i].recordscore > MAXSCORE)
+			players[i].recordscore = MAXSCORE;
 
 		// grant extra lives right away since tally is faked
 		ptlives = min(
-- 
GitLab


From dc103330ac4b9f177307b7a25a178420803d1278 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Fri, 30 Dec 2022 20:17:26 -0800
Subject: [PATCH 241/518] Rename strcasestr to nongnu_strcasestr, macro
 strcasestr ifndef _GNU_SOURCE

Fix for GCC C++ compiler, which always defines _GNU_SOURCE.
---
 src/doomtype.h   | 5 ++++-
 src/strcasestr.c | 2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/doomtype.h b/src/doomtype.h
index c59e173813..617fd4d477 100644
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -100,7 +100,10 @@ typedef long ssize_t;
 	#define strnicmp(x,y,n) strncasecmp(x,y,n)
 #endif
 
-char *strcasestr(const char *in, const char *what);
+char *nongnu_strcasestr(const char *in, const char *what);
+#ifndef _GNU_SOURCE
+#define strcasestr nongnu_strcasestr
+#endif
 #define stristr strcasestr
 
 int startswith (const char *base, const char *tag);
diff --git a/src/strcasestr.c b/src/strcasestr.c
index 2796f11d52..37899a8425 100644
--- a/src/strcasestr.c
+++ b/src/strcasestr.c
@@ -61,7 +61,7 @@ swapp (char ***ppap, char ***ppbp, char **cpap, char **cpbp)
 }
 
 char *
-strcasestr (const char *s, const char *q)
+nongnu_strcasestr (const char *s, const char *q)
 {
 	size_t  qn;
 
-- 
GitLab


From 34f2594b4826a200ca50ecbf62652d5ffa181617 Mon Sep 17 00:00:00 2001
From: SteelT <steeltitanium1@gmail.com>
Date: Wed, 26 Jul 2023 14:09:48 -0400
Subject: [PATCH 242/518] Update Catch2 to latest version

This combined with the previous commit fixes building the test suite
---
 CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a21d22b34b..7d36c1fb63 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -84,7 +84,7 @@ if(SRB2_CONFIG_ENABLE_TESTS)
 	# https://github.com/catchorg/Catch2
 	CPMAddPackage(
 		NAME Catch2
-		VERSION 3.1.1
+		VERSION 3.4.0
 		GITHUB_REPOSITORY catchorg/Catch2
 		OPTIONS
 			"CATCH_INSTALL_DOCS OFF"
-- 
GitLab


From 7261db1eee9ee02fceb52c54265c233b0fff3ca5 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Mon, 20 Feb 2023 02:15:50 -0800
Subject: [PATCH 243/518] cmake: fix -DSRB2_CONFIG_ENABLE_TESTS=OFF build

---
 src/CMakeLists.txt | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f3ed96f892..b622373742 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -293,7 +293,9 @@ if(SRB2_CONFIG_PROFILEMODE AND "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
 endif()
 
 add_subdirectory(sdl)
-add_subdirectory(tests)
+if(SRB2_CONFIG_ENABLE_TESTS)
+	add_subdirectory(tests)
+endif()
 
 # strip debug symbols into separate file when using gcc.
 # to be consistent with Makefile, don't generate for OS X.
-- 
GitLab


From d01f25d91dff89f318801962f53b8bd966c13078 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Wed, 26 Jul 2023 23:18:07 +0200
Subject: [PATCH 244/518] Fix FreeBSD build errors

---
 src/Makefile.d/nix.mk |  2 +-
 src/sdl/i_system.c    | 43 ++++++++++---------------------------------
 2 files changed, 11 insertions(+), 34 deletions(-)

diff --git a/src/Makefile.d/nix.mk b/src/Makefile.d/nix.mk
index 767b64c12b..7287957716 100644
--- a/src/Makefile.d/nix.mk
+++ b/src/Makefile.d/nix.mk
@@ -29,7 +29,7 @@ endif
 # Tested by Steel, as of release 2.2.8.
 ifdef FREEBSD
 opts+=-I/usr/X11R6/include -DLINUX -DFREEBSD
-libs+=-L/usr/X11R6/lib -lipx -lkvm
+libs+=-L/usr/X11R6/lib -lkvm -lexecinfo
 endif
 
 # FIXME: UNTESTED
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index c21226ac3f..3616300c15 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -96,7 +96,7 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
 #include <kvm.h>
 #endif
 #include <nlist.h>
-#include <sys/vmmeter.h>
+#include <sys/sysctl.h>
 #endif
 #endif
 
@@ -3037,40 +3037,17 @@ static long get_entry(const char* name, const char* buf)
 size_t I_GetFreeMem(size_t *total)
 {
 #ifdef FREEBSD
-	struct vmmeter sum;
-	kvm_t *kd;
-	struct nlist namelist[] =
-	{
-#define X_SUM   0
-		{"_cnt"},
-		{NULL}
-	};
-	if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL)
-	{
-		if (total)
-			*total = 0L;
-		return 0;
-	}
-	if (kvm_nlist(kd, namelist) != 0)
-	{
-		kvm_close (kd);
-		if (total)
-			*total = 0L;
-		return 0;
-	}
-	if (kvm_read(kd, namelist[X_SUM].n_value, &sum,
-		sizeof (sum)) != sizeof (sum))
-	{
-		kvm_close(kd);
-		if (total)
-			*total = 0L;
-		return 0;
-	}
-	kvm_close(kd);
+	u_int v_free_count, v_page_size, v_page_count;
+	size_t size = sizeof(v_free_count);
+	sysctlbyname("vm.stat.vm.v_free_count", &v_free_count, &size, NULL, 0);
+	size_t size = sizeof(v_page_size);
+	sysctlbyname("vm.stat.vm.v_page_size", &v_page_size, &size, NULL, 0);
+	size_t size = sizeof(v_page_count);
+	sysctlbyname("vm.stat.vm.v_page_count", &v_page_count, &size, NULL, 0);
 
 	if (total)
-		*total = sum.v_page_count * sum.v_page_size;
-	return sum.v_free_count * sum.v_page_size;
+		*total = v_page_count * v_page_size;
+	return v_free_count * v_page_size;
 #elif defined (SOLARIS)
 	/* Just guess */
 	if (total)
-- 
GitLab


From 606797a5a4f6ec1f2af72639ac60f6744253895e Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 27 Jul 2023 14:41:02 +0200
Subject: [PATCH 245/518] Temporarily revert recent netcode changes

---
 src/d_clisrv.c   | 447 ++++++++---------------------------
 src/d_clisrv.h   |  10 +-
 src/d_net.c      |  31 +--
 src/d_net.h      |   2 +-
 src/d_netcmd.c   | 600 ++++++++++++++++++++++-------------------------
 src/d_netcmd.h   |  14 +-
 src/d_netfil.c   |   4 +-
 src/d_netfil.h   |   2 +-
 src/http-mserv.c |  30 +--
 src/i_addrinfo.c |   2 +-
 src/i_addrinfo.h |   2 +-
 src/i_net.h      |  13 +-
 src/i_tcp.c      |  73 ++----
 src/i_tcp.h      |   2 +-
 src/mserv.c      |  10 +-
 src/mserv.h      |   6 +-
 16 files changed, 441 insertions(+), 807 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index c66c8d798f..3091f33440 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -25,7 +25,8 @@
 #include "st_stuff.h"
 #include "hu_stuff.h"
 #include "keys.h"
-#include "g_input.h" // JOY1
+#include "g_input.h"
+#include "i_gamepad.h"
 #include "m_menu.h"
 #include "console.h"
 #include "d_netfil.h"
@@ -33,6 +34,7 @@
 #include "p_saveg.h"
 #include "z_zone.h"
 #include "p_local.h"
+#include "p_haptic.h"
 #include "m_misc.h"
 #include "am_map.h"
 #include "m_random.h"
@@ -49,7 +51,7 @@
 #include "m_perfstats.h"
 
 // aaaaaa
-#include "i_joy.h"
+#include "i_gamepad.h"
 
 #ifndef NONET
 // cl loading screen
@@ -120,8 +122,6 @@ UINT8 hu_redownloadinggamestate = 0;
 // true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks
 boolean hu_stopped = false;
 
-consvar_t cv_dedicatedidletime = CVAR_INIT ("dedicatedidletime", "10", CV_SAVE, CV_Unsigned, NULL);
-
 UINT8 adminpassmd5[16];
 boolean adminpasswordset = false;
 
@@ -655,22 +655,6 @@ static UINT8 Snake_GetOppositeDir(UINT8 dir)
 		return 12 + 5 - dir;
 }
 
-event_t *snakejoyevents[MAXEVENTS];
-UINT16 joyeventcount = 0;
-
-// I'm screaming the hack is clean - ashi
-static boolean Snake_Joy_Grabber(event_t *ev)
-{
-	if (ev->type == ev_joystick  && ev->key == 0)
-	{
-		snakejoyevents[joyeventcount] = ev;
-		joyeventcount++;
-		return true;
-	}
-	else
-		return false;
-}
-
 static void Snake_FindFreeSlot(UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady)
 {
 	UINT8 x, y;
@@ -697,19 +681,17 @@ static void Snake_Handle(void)
 	UINT8 x, y;
 	UINT8 oldx, oldy;
 	UINT16 i;
-	UINT16 j;
 	UINT16 joystate = 0;
-	static INT32 pjoyx = 0, pjoyy = 0;
 
 	// Handle retry
-	if (snake->gameover && (PLAYER1INPUTDOWN(GC_JUMP) || gamekeydown[KEY_ENTER]))
+	if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER]))
 	{
 		Snake_Initialise();
 		snake->pausepressed = true; // Avoid accidental pause on respawn
 	}
 
 	// Handle pause
-	if (PLAYER1INPUTDOWN(GC_PAUSE) || gamekeydown[KEY_ENTER])
+	if (G_PlayerInputDown(0, GC_PAUSE) || gamekeydown[KEY_ENTER])
 	{
 		if (!snake->pausepressed)
 			snake->paused = !snake->paused;
@@ -728,58 +710,23 @@ static void Snake_Handle(void)
 	oldx = snake->snakex[1];
 	oldy = snake->snakey[1];
 
-	// process the input events in here dear lord
-	for (j = 0; j < joyeventcount; j++)
-	{
-		event_t *ev = snakejoyevents[j];
-		const INT32 jdeadzone = (JOYAXISRANGE * cv_digitaldeadzone.value) / FRACUNIT;
-		if (ev->y != INT32_MAX)
-		{
-			if (Joystick.bGamepadStyle || abs(ev->y) > jdeadzone)
-			{
-				if (ev->y < 0 && pjoyy >= 0)
-					joystate = 1;
-				else if (ev->y > 0 && pjoyy <= 0)
-					joystate = 2;
-				pjoyy = ev->y;
-			}
-			else
-				pjoyy = 0;
-		}
-
-		if (ev->x != INT32_MAX)
-		{
-			if (Joystick.bGamepadStyle || abs(ev->x) > jdeadzone)
-			{
-				if (ev->x < 0 && pjoyx >= 0)
-					joystate = 3;
-				else if (ev->x > 0 && pjoyx <= 0)
-					joystate = 4;
-				pjoyx = ev->x;
-			}
-			else
-				pjoyx = 0;
-		}
-	}
-	joyeventcount = 0;
-
 	// Update direction
-	if (PLAYER1INPUTDOWN(GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3)
+	if (G_PlayerInputDown(0, GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3)
 	{
 		if (snake->snakelength < 2 || x <= oldx)
 			snake->snakedir[0] = 1;
 	}
-	else if (PLAYER1INPUTDOWN(GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4)
+	else if (G_PlayerInputDown(0, GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4)
 	{
 		if (snake->snakelength < 2 || x >= oldx)
 			snake->snakedir[0] = 2;
 	}
-	else if (PLAYER1INPUTDOWN(GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1)
+	else if (G_PlayerInputDown(0, GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1)
 	{
 		if (snake->snakelength < 2 || y <= oldy)
 			snake->snakedir[0] = 3;
 	}
-	else if (PLAYER1INPUTDOWN(GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2)
+	else if (G_PlayerInputDown(0, GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2)
 	{
 		if (snake->snakelength < 2 || y >= oldy)
 			snake->snakedir[0] = 4;
@@ -1295,7 +1242,6 @@ static boolean CL_AskFileList(INT32 firstfile)
 static boolean CL_SendJoin(void)
 {
 	UINT8 localplayers = 1;
-	char const *player2name;
 	if (netgame)
 		CONS_Printf(M_GetText("Sending join request...\n"));
 	netbuffer->packettype = PT_CLIENTJOIN;
@@ -1312,23 +1258,18 @@ static boolean CL_SendJoin(void)
 	CleanupPlayerName(consoleplayer, cv_playername.zstring);
 	if (splitscreen)
 		CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */
-	// Avoid empty string on bots to avoid softlocking in singleplayer
-	if (botingame)
-		player2name = strcmp(cv_playername.zstring, "Tails") == 0 ? "Tail" : "Tails";
-	else
-		player2name = cv_playername2.zstring;
 
 	strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME);
-	strncpy(netbuffer->u.clientcfg.names[1], player2name, MAXPLAYERNAME);
+	strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME);
 
 	return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
 }
 
 static INT32 FindRejoinerNum(SINT8 node)
 {
-	char addressbuffer[64];
+	char strippednodeaddress[64];
 	const char *nodeaddress;
-	const char *strippednodeaddress;
+	char *port;
 	INT32 i;
 
 	// Make sure there is no dead dress before proceeding to the stripping
@@ -1339,8 +1280,10 @@ static INT32 FindRejoinerNum(SINT8 node)
 		return -1;
 
 	// Strip the address of its port
-	strcpy(addressbuffer, nodeaddress);
-	strippednodeaddress = I_NetSplitAddress(addressbuffer, NULL);
+	strcpy(strippednodeaddress, nodeaddress);
+	port = strchr(strippednodeaddress, ':');
+	if (port)
+		*port = '\0';
 
 	// Check if any player matches the stripped address
 	for (i = 0; i < MAXPLAYERS; i++)
@@ -1383,9 +1326,8 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
 	netbuffer->u.serverinfo.time = (tic_t)LONG(servertime);
 	netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime);
 
-	// Exclude bots from both counts
-	netbuffer->u.serverinfo.numberofplayer = (UINT8)(D_NumPlayers() - D_NumBots());
-	netbuffer->u.serverinfo.maxplayer = (UINT8)(cv_maxplayers.value - D_NumBots());
+	netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
+	netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
 
 	netbuffer->u.serverinfo.refusereason = GetRefuseReason(node);
 
@@ -1512,7 +1454,6 @@ static boolean SV_SendServerConfig(INT32 node)
 	netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
 	netbuffer->u.servercfg.gametype = (UINT8)gametype;
 	netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
-	netbuffer->u.servercfg.usedCheats = (UINT8)usedCheats;
 
 	memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
 
@@ -1711,6 +1652,8 @@ static void CL_LoadReceivedSavegame(boolean reloading)
 	titledemo = false;
 	automapactive = false;
 
+	P_StopRumble(NULL);
+
 	// load a base level
 	if (P_LoadNetGame(reloading))
 	{
@@ -1997,9 +1940,10 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
 static void M_ConfirmConnect(event_t *ev)
 {
 #ifndef NONET
-	if (ev->type == ev_keydown)
+
+	if (ev->type == ev_keydown || ev->type == ev_gamepad_down)
 	{
-		if (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER || ev->key == KEY_JOY1)
+		if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A))
 		{
 			if (totalfilesrequestednum > 0)
 			{
@@ -2014,7 +1958,7 @@ static void M_ConfirmConnect(event_t *ev)
 
 			M_ClearMenus(true);
 		}
-		else if (ev->key == 'n' || ev->key == KEY_ESCAPE || ev->key == KEY_JOY1 + 3)
+		else if ((ev->type == ev_keydown && (ev->key == 'n' || ev->key == KEY_ESCAPE)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_B))
 		{
 			cl_mode = CL_ABORTED;
 			M_ClearMenus(true);
@@ -2448,14 +2392,11 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 			// my hand has been forced and I am dearly sorry for this awful hack :vomit:
 			for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
 			{
-#ifndef NONET
-				if (!Snake_Joy_Grabber(&events[eventtail]))
-#endif
-					G_MapEventsToControls(&events[eventtail]);
+				G_MapEventsToControls(&events[eventtail]);
 			}
 		}
 
-		if (gamekeydown[KEY_ESCAPE] || gamekeydown[KEY_JOY1+1] || cl_mode == CL_ABORTED)
+		if (gamekeydown[KEY_ESCAPE] || gamepads[0].buttons[GAMEPAD_BUTTON_B] || cl_mode == CL_ABORTED)
 		{
 			CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
 			M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
@@ -2493,7 +2434,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 		{
 			if (!snake)
 			{
-				F_MenuPresTicker(); // title sky
+				F_MenuPresTicker(true); // title sky
 				F_TitleScreenTicker(true);
 				F_TitleScreenDrawer();
 			}
@@ -2608,8 +2549,6 @@ static void CL_ConnectToServer(void)
 	}
 	while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes))));
 
-	if (netgame)
-		F_StartWaitingPlayers();
 	DEBFILE(va("Synchronisation Finished\n"));
 
 	displayplayer = consoleplayer;
@@ -3516,10 +3455,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}};
 consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL);
 
-consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
+consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
 consvar_t cv_joinnextround = CVAR_INIT ("joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); /// \todo not done
 static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}};
-consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, maxplayers_cons_t, NULL);
+consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL);
 static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}};
 consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL);
 static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}};
@@ -3547,22 +3486,22 @@ void D_ClientServerInit(void)
 		VERSION/100, VERSION%100, SUBVERSION));
 
 #ifndef NONET
-	COM_AddCommand("getplayernum", Command_GetPlayerNum, COM_LUA);
-	COM_AddCommand("kick", Command_Kick, COM_LUA);
-	COM_AddCommand("ban", Command_Ban, COM_LUA);
-	COM_AddCommand("banip", Command_BanIP, COM_LUA);
-	COM_AddCommand("clearbans", Command_ClearBans, COM_LUA);
-	COM_AddCommand("showbanlist", Command_ShowBan, COM_LUA);
-	COM_AddCommand("reloadbans", Command_ReloadBan, COM_LUA);
-	COM_AddCommand("connect", Command_connect, COM_LUA);
-	COM_AddCommand("nodes", Command_Nodes, COM_LUA);
-	COM_AddCommand("resendgamestate", Command_ResendGamestate, COM_LUA);
+	COM_AddCommand("getplayernum", Command_GetPlayerNum);
+	COM_AddCommand("kick", Command_Kick);
+	COM_AddCommand("ban", Command_Ban);
+	COM_AddCommand("banip", Command_BanIP);
+	COM_AddCommand("clearbans", Command_ClearBans);
+	COM_AddCommand("showbanlist", Command_ShowBan);
+	COM_AddCommand("reloadbans", Command_ReloadBan);
+	COM_AddCommand("connect", Command_connect);
+	COM_AddCommand("nodes", Command_Nodes);
+	COM_AddCommand("resendgamestate", Command_ResendGamestate);
 #ifdef PACKETDROP
-	COM_AddCommand("drop", Command_Drop, COM_LUA);
-	COM_AddCommand("droprate", Command_Droprate, COM_LUA);
+	COM_AddCommand("drop", Command_Drop);
+	COM_AddCommand("droprate", Command_Droprate);
 #endif
 #ifdef _DEBUG
-	COM_AddCommand("numnodes", Command_Numnodes, COM_LUA);
+	COM_AddCommand("numnodes", Command_Numnodes);
 #endif
 #endif
 
@@ -3653,9 +3592,6 @@ void SV_ResetServer(void)
 
 	CV_RevertNetVars();
 
-	// Ensure synched when creating a new server
-	M_CopyGameData(serverGamedata, clientGamedata);
-
 	DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
 }
 
@@ -3779,13 +3715,14 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 
 		if (server && I_GetNodeAddress)
 		{
-			char addressbuffer[64];
 			const char *address = I_GetNodeAddress(node);
+			char *port = NULL;
 			if (address) // MI: fix msvcrt.dll!_mbscat crash?
 			{
-				strcpy(addressbuffer, address);
-				strcpy(playeraddress[newplayernum],
-						I_NetSplitAddress(addressbuffer, NULL));
+				strcpy(playeraddress[newplayernum], address);
+				port = strchr(playeraddress[newplayernum], ':');
+				if (port)
+					*port = '\0';
 			}
 		}
 	}
@@ -4089,7 +4026,7 @@ ConnectionRefused (SINT8 node, INT32 rejoinernum)
 		{
 			return va(
 					"Maximum players reached: %d",
-					cv_maxplayers.value - D_NumBots());
+					cv_maxplayers.value);
 		}
 	}
 
@@ -4417,8 +4354,6 @@ static void HandlePacketFromAwayNode(SINT8 node)
 				maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
 				G_SetGametype(netbuffer->u.servercfg.gametype);
 				modifiedgame = netbuffer->u.servercfg.modifiedgame;
-				if (netbuffer->u.servercfg.usedCheats)
-					G_SetUsedCheats(true);
 				memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
 			}
 
@@ -4528,7 +4463,6 @@ static void HandlePacketFromPlayer(SINT8 node)
 		netconsole = 0;
 	else
 		netconsole = nodetoplayer[node];
-
 #ifdef PARANOIA
 	if (netconsole >= MAXPLAYERS)
 		I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
@@ -4567,32 +4501,21 @@ static void HandlePacketFromPlayer(SINT8 node)
 			// Update the nettics
 			nettics[node] = realend;
 
-			// This should probably still timeout though, as the node should always have a player 1 number
-			if (netconsole == -1)
+			// Don't do anything for packets of type NODEKEEPALIVE?
+			if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
+				|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
 				break;
 
 			// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
 			/// \todo Use a separate cvar for that kind of timeout?
 			freezetimeout[node] = I_GetTime() + connectiontimeout;
 
-			// Don't do anything for packets of type NODEKEEPALIVE?
-			// Sryder 2018/07/01: Update the freezetimeout still!
-			if (netbuffer->packettype == PT_NODEKEEPALIVE
-				|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
-				break;
-
-			// If we've alredy received a ticcmd for this tic, just submit it for the next one.
-			tic_t faketic = maketic;
-			if ((!!(netcmds[maketic % BACKUPTICS][netconsole].angleturn & TICCMD_RECEIVED))
-				&& (maketic - firstticstosend < BACKUPTICS))
-				faketic++;
-
 			// Copy ticcmd
-			G_MoveTiccmd(&netcmds[faketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
+			G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
 
 			// Check ticcmd for "speed hacks"
-			if (netcmds[faketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[faketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
-				|| netcmds[faketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[faketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
+			if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
+				|| netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
 			{
 				CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole);
 				//D_Clearticcmd(k);
@@ -4604,10 +4527,9 @@ static void HandlePacketFromPlayer(SINT8 node)
 			// Splitscreen cmd
 			if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
 				&& nodetoplayer2[node] >= 0)
-				G_MoveTiccmd(&netcmds[faketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
+				G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
 					&netbuffer->u.client2pak.cmd2, 1);
 
-
 			// Check player consistancy during the level
 			if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL
 				&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
@@ -4644,21 +4566,6 @@ static void HandlePacketFromPlayer(SINT8 node)
 				}
 			}
 			break;
-		case PT_BASICKEEPALIVE:
-			if (client)
-				break;
-
-			// This should probably still timeout though, as the node should always have a player 1 number
-			if (netconsole == -1)
-				break;
-
-			// If a client sends this it should mean they are done receiving the savegame
-			sendingsavegame[node] = false;
-
-			// As long as clients send keep alives, the server can keep running, so reset the timeout
-			/// \todo Use a separate cvar for that kind of timeout?
-			freezetimeout[node] = I_GetTime() + connectiontimeout;
-			break;
 		case PT_TEXTCMD2: // splitscreen special
 			netconsole = nodetoplayer2[node];
 			/* FALLTHRU */
@@ -5085,66 +4992,39 @@ static INT16 Consistancy(void)
 	return (INT16)(ret & 0xFFFF);
 }
 
-// confusing, but this DOESN'T send PT_NODEKEEPALIVE, it sends PT_BASICKEEPALIVE
-// used during wipes to tell the server that a node is still connected
-static void CL_SendClientKeepAlive(void)
-{
-	netbuffer->packettype = PT_BASICKEEPALIVE;
-
-	HSendPacket(servernode, false, 0, 0);
-}
-
-static void SV_SendServerKeepAlive(void)
-{
-	INT32 n;
-
-	for (n = 1; n < MAXNETNODES; n++)
-	{
-		if (nodeingame[n])
-		{
-			netbuffer->packettype = PT_BASICKEEPALIVE;
-			HSendPacket(n, false, 0, 0);
-		}
-	}
-}
-
 // send the client packet to the server
 static void CL_SendClientCmd(void)
 {
 	size_t packetsize = 0;
-	boolean mis = false;
 
 	netbuffer->packettype = PT_CLIENTCMD;
 
 	if (cl_packetmissed)
-	{
-		netbuffer->packettype = PT_CLIENTMIS;
-		mis = true;
-	}
-
+		netbuffer->packettype++;
 	netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX);
 	netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX);
 
 	if (gamestate == GS_WAITINGPLAYERS)
 	{
 		// Send PT_NODEKEEPALIVE packet
-		netbuffer->packettype = (mis ? PT_NODEKEEPALIVEMIS : PT_NODEKEEPALIVE);
+		netbuffer->packettype += 4;
 		packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16);
 		HSendPacket(servernode, false, 0, packetsize);
 	}
 	else if (gamestate != GS_NULL && (addedtogame || dedicated))
 	{
-		packetsize = sizeof (clientcmd_pak);
 		G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
 		netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
 
 		// Send a special packet with 2 cmd for splitscreen
 		if (splitscreen || botingame)
 		{
-			netbuffer->packettype = (mis ? PT_CLIENT2MIS : PT_CLIENT2CMD);
-			packetsize = sizeof (client2cmd_pak);
+			netbuffer->packettype += 2;
 			G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1);
+			packetsize = sizeof (client2cmd_pak);
 		}
+		else
+			packetsize = sizeof (clientcmd_pak);
 
 		HSendPacket(servernode, false, 0, packetsize);
 	}
@@ -5155,7 +5035,7 @@ static void CL_SendClientCmd(void)
 		if (localtextcmd[0])
 		{
 			netbuffer->packettype = PT_TEXTCMD;
-			M_Memcpy(netbuffer->u.textcmd, localtextcmd, localtextcmd[0]+1);
+			M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
 			// All extra data have been sent
 			if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail...
 				localtextcmd[0] = 0;
@@ -5299,7 +5179,7 @@ static void Local_Maketic(INT32 realtics)
 	                   // game responder calls HU_Responder, AM_Responder,
 	                   // and G_MapEventsToControls
 	if (!dedicated) rendergametic = gametic;
-	// translate inputs (keyboard/mouse/joystick) into game controls
+	// translate inputs (keyboard/mouse/gamepad) into game controls
 	G_BuildTiccmd(&localcmds, realtics, 1);
 	if (splitscreen || botingame)
 		G_BuildTiccmd(&localcmds2, realtics, 2);
@@ -5535,82 +5415,9 @@ static inline void PingUpdate(void)
 	pingmeasurecount = 1; //Reset count
 }
 
-static tic_t gametime = 0;
-
-static void UpdatePingTable(void)
-{
-	INT32 i;
-
-	if (server)
-	{
-		if (netgame && !(gametime % 35)) // update once per second.
-			PingUpdate();
-		// update node latency values so we can take an average later.
-		for (i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i] && playernode[i] != UINT8_MAX)
-				realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
-		pingmeasurecount++;
-	}
-}
-
-// Handle timeouts to prevent definitive freezes from happenning
-static void HandleNodeTimeouts(void)
-{
-	INT32 i;
-
-	if (server)
-	{
-		for (i = 1; i < MAXNETNODES; i++)
-			if (nodeingame[i] && freezetimeout[i] < I_GetTime())
-				Net_ConnectionTimeout(i);
-
-		// In case the cvar value was lowered
-		if (joindelay)
-			joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE);
-	}
-}
-
-// Keep the network alive while not advancing tics!
-void NetKeepAlive(void)
-{
-	tic_t nowtime;
-	INT32 realtics;
-
-	nowtime = I_GetTime();
-	realtics = nowtime - gametime;
-
-	// return if there's no time passed since the last call
-	if (realtics <= 0) // nothing new to update
-		return;
-
-	UpdatePingTable();
-
-	GetPackets();
-
-#ifdef MASTERSERVER
-	MasterClient_Ticker();
-#endif
-
-	if (client)
-	{
-		// send keep alive
-		CL_SendClientKeepAlive();
-		// No need to check for resynch because we aren't running any tics
-	}
-	else
-	{
-		SV_SendServerKeepAlive();
-	}
-
-	// No else because no tics are being run and we can't resynch during this
-
-	Net_AckTicker();
-	HandleNodeTimeouts();
-	FileSendTicker();
-}
-
 void NetUpdate(void)
 {
+	static tic_t gametime = 0;
 	static tic_t resptime = 0;
 	tic_t nowtime;
 	INT32 i;
@@ -5621,7 +5428,6 @@ void NetUpdate(void)
 
 	if (realtics <= 0) // nothing new to update
 		return;
-
 	if (realtics > 5)
 	{
 		if (server)
@@ -5630,71 +5436,18 @@ 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)
-		{
-			for (i = 1; i < MAXNETNODES; ++i)
-				if (nodeingame[i])
-				{
-					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;
-	}
-
 	gametime = nowtime;
 
-	UpdatePingTable();
+	if (server)
+	{
+		if (netgame && !(gametime % 35)) // update once per second.
+			PingUpdate();
+		// update node latency values so we can take an average later.
+		for (i = 0; i < MAXPLAYERS; i++)
+			if (playeringame[i] && playernode[i] != UINT8_MAX)
+				realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
+		pingmeasurecount++;
+	}
 
 	if (client)
 		maketic = neededtic;
@@ -5726,25 +5479,24 @@ void NetUpdate(void)
 	}
 	else
 	{
-		if (!demoplayback && realtics > 0)
+		if (!demoplayback)
 		{
 			INT32 counts;
 
 			hu_redownloadinggamestate = false;
 
-			// Don't erase tics not acknowledged
-			counts = realtics;
-
 			firstticstosend = gametic;
 			for (i = 0; i < MAXNETNODES; i++)
-			{
-				if (!nodeingame[i])
-					continue;
-				if (nettics[i] < firstticstosend)
+				if (nodeingame[i] && nettics[i] < firstticstosend)
+				{
 					firstticstosend = nettics[i];
-				if (maketic + counts >= nettics[i] + (BACKUPTICS - TICRATE))
-					Net_ConnectionTimeout(i);
-			}
+
+					if (maketic + 1 >= nettics[i] + BACKUPTICS)
+						Net_ConnectionTimeout(i);
+				}
+
+			// Don't erase tics not acknowledged
+			counts = realtics;
 
 			if (maketic + counts >= firstticstosend + BACKUPTICS)
 				counts = firstticstosend+BACKUPTICS-maketic-1;
@@ -5762,10 +5514,20 @@ void NetUpdate(void)
 	}
 
 	Net_AckTicker();
-	HandleNodeTimeouts();
 
-	nowtime /= NEWTICRATERATIO;
+	// Handle timeouts to prevent definitive freezes from happenning
+	if (server)
+	{
+		for (i = 1; i < MAXNETNODES; i++)
+			if (nodeingame[i] && freezetimeout[i] < I_GetTime())
+				Net_ConnectionTimeout(i);
 
+		// In case the cvar value was lowered
+		if (joindelay)
+			joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE);
+	}
+
+	nowtime /= NEWTICRATERATIO;
 	if (nowtime > resptime)
 	{
 		resptime = nowtime;
@@ -5796,19 +5558,6 @@ INT32 D_NumPlayers(void)
 	return num;
 }
 
-/** Similar to the above, but counts only bots.
-  * Purpose is to remove bots from both the player count and the
-  * max player count on the server view
-*/
-INT32 D_NumBots(void)
-{
-	INT32 num = 0, ix;
-	for (ix = 0; ix < MAXPLAYERS; ix++)
-		if (playeringame[ix] && players[ix].bot)
-			num++;
-	return num;
-}
-
 tic_t GetLag(INT32 node)
 {
 	return gametic - nettics[node];
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index 49fb5fc1db..e078641224 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -77,8 +77,6 @@ typedef enum
 	PT_ASKLUAFILE,     // Client telling the server they don't have the file
 	PT_HASLUAFILE,     // Client telling the server they have the file
 
-	PT_BASICKEEPALIVE,// Keep the network alive during wipes, as tics aren't advanced and NetUpdate isn't called
-
 	// Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
 
 	PT_CANFAIL,       // This is kind of a priority. Anything bigger than CANFAIL
@@ -160,7 +158,6 @@ typedef struct
 
 	UINT8 gametype;
 	UINT8 modifiedgame;
-	UINT8 usedCheats;
 
 	char server_context[8]; // Unique context id, generated at server startup.
 } ATTRPACK serverconfig_pak;
@@ -400,7 +397,6 @@ extern tic_t servermaxping;
 extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_joindelay, cv_rejointimeout;
 extern consvar_t cv_resynchattempts, cv_blamecfail;
 extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
-extern consvar_t cv_dedicatedidletime;
 
 // Used in d_net, the only dependence
 tic_t ExpandTics(INT32 low, INT32 node);
@@ -415,9 +411,6 @@ void SendKick(UINT8 playernum, UINT8 msg);
 // Create any new ticcmds and broadcast to other players.
 void NetUpdate(void);
 
-// Maintain connections to nodes without timing them all out.
-void NetKeepAlive(void);
-
 void SV_StartSinglePlayerServer(void);
 boolean SV_SpawnServer(void);
 void SV_StopServer(void);
@@ -454,7 +447,6 @@ extern char motd[254], server_context[8];
 extern UINT8 playernode[MAXPLAYERS];
 
 INT32 D_NumPlayers(void);
-INT32 D_NumBots(void);
 void D_ResetTiccmds(void);
 
 tic_t GetLag(INT32 node);
diff --git a/src/d_net.c b/src/d_net.c
index 6d8c72942b..a7e1eb16d6 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -869,9 +869,6 @@ static void DebugPrintpacket(const char *header)
 				(UINT32)ExpandTics(netbuffer->u.clientpak.client_tic, doomcom->remotenode),
 				(UINT32)ExpandTics (netbuffer->u.clientpak.resendfrom, doomcom->remotenode));
 			break;
-		case PT_BASICKEEPALIVE:
-			fprintf(debugfile, "    wipetime\n");
-			break;
 		case PT_TEXTCMD:
 		case PT_TEXTCMD2:
 			fprintf(debugfile, "    length %d\n    ", netbuffer->u.textcmd[0]);
@@ -1210,32 +1207,26 @@ static void Internal_FreeNodenum(INT32 nodenum)
 	(void)nodenum;
 }
 
-char *I_NetSplitAddress(char *host, char **port)
-{
-	boolean v4 = (strchr(host, '.') != NULL);
-
-	host = strtok(host, v4 ? ":" : "[]");
-
-	if (port)
-		*port = strtok(NULL, ":");
-
-	return host;
-}
-
 SINT8 I_NetMakeNode(const char *hostname)
 {
 	SINT8 newnode = -1;
 	if (I_NetMakeNodewPort)
 	{
 		char *localhostname = strdup(hostname);
-		char *port;
+		char  *t = localhostname;
+		const char *port;
 		if (!localhostname)
 			return newnode;
-
 		// retrieve portnum from address!
-		hostname = I_NetSplitAddress(localhostname, &port);
+		strtok(localhostname, ":");
+		port = strtok(NULL, ":");
+
+		// remove the port in the hostname as we've it already
+		while ((*t != ':') && (*t != '\0'))
+			t++;
+		*t = '\0';
 
-		newnode = I_NetMakeNodewPort(hostname, port);
+		newnode = I_NetMakeNodewPort(localhostname, port);
 		free(localhostname);
 	}
 	return newnode;
diff --git a/src/d_net.h b/src/d_net.h
index ddedbef4a8..5baa593a05 100644
--- a/src/d_net.h
+++ b/src/d_net.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 45a394eff5..5f02bc2dea 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -21,6 +21,7 @@
 #include "g_game.h"
 #include "hu_stuff.h"
 #include "g_input.h"
+#include "i_gamepad.h"
 #include "m_menu.h"
 #include "r_local.h"
 #include "r_skins.h"
@@ -49,7 +50,6 @@
 #include "m_anigif.h"
 #include "md5.h"
 #include "m_perfstats.h"
-#include "u_list.h"
 
 #ifdef NETGAME_DEVMODE
 #define CV_RESTRICT CV_NETVAR
@@ -182,14 +182,6 @@ static CV_PossibleValue_t mouse2port_cons_t[] = {{1, "COM1"}, {2, "COM2"}, {3, "
 	{0, NULL}};
 #endif
 
-#ifdef LJOYSTICK
-static CV_PossibleValue_t joyport_cons_t[] = {{1, "/dev/js0"}, {2, "/dev/js1"}, {3, "/dev/js2"},
-	{4, "/dev/js3"}, {0, NULL}};
-#else
-// accept whatever value - it is in fact the joystick device number
-#define usejoystick_cons_t NULL
-#endif
-
 static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}};
 
 static CV_PossibleValue_t startingliveslimit_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}};
@@ -203,37 +195,37 @@ static CV_PossibleValue_t matchboxes_cons_t[] = {{0, "Normal"}, {1, "Mystery"},
 static CV_PossibleValue_t chances_cons_t[] = {{0, "MIN"}, {9, "MAX"}, {0, NULL}};
 static CV_PossibleValue_t pause_cons_t[] = {{0, "Server"}, {1, "All"}, {0, NULL}};
 
-consvar_t cv_showinputjoy = CVAR_INIT ("showinputjoy", "Off", CV_ALLOWLUA, CV_OnOff, NULL);
+consvar_t cv_showinputjoy = CVAR_INIT ("showinputjoy", "Off", 0, CV_OnOff, NULL);
 
 #ifdef NETGAME_DEVMODE
 static consvar_t cv_fishcake = CVAR_INIT ("fishcake", "Off", CV_CALL|CV_NOSHOWHELP|CV_RESTRICT, CV_OnOff, Fishcake_OnChange);
 #endif
 static consvar_t cv_dummyconsvar = CVAR_INIT ("dummyconsvar", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, DummyConsvar_OnChange);
 
-consvar_t cv_restrictskinchange = CVAR_INIT ("restrictskinchange", "Yes", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, CV_YesNo, NULL);
-consvar_t cv_allowteamchange = CVAR_INIT ("allowteamchange", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
+consvar_t cv_restrictskinchange = CVAR_INIT ("restrictskinchange", "Yes", CV_SAVE|CV_NETVAR|CV_CHEAT, CV_YesNo, NULL);
+consvar_t cv_allowteamchange = CVAR_INIT ("allowteamchange", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL);
 
-consvar_t cv_startinglives = CVAR_INIT ("startinglives", "3", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, startingliveslimit_cons_t, NULL);
+consvar_t cv_startinglives = CVAR_INIT ("startinglives", "3", CV_SAVE|CV_NETVAR|CV_CHEAT, startingliveslimit_cons_t, NULL);
 
 static CV_PossibleValue_t respawntime_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "Off"}, {0, NULL}};
-consvar_t cv_respawntime = CVAR_INIT ("respawndelay", "3", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, respawntime_cons_t, NULL);
+consvar_t cv_respawntime = CVAR_INIT ("respawndelay", "3", CV_SAVE|CV_NETVAR|CV_CHEAT, respawntime_cons_t, NULL);
 
-consvar_t cv_competitionboxes = CVAR_INIT ("competitionboxes", "Mystery", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, competitionboxes_cons_t, NULL);
+consvar_t cv_competitionboxes = CVAR_INIT ("competitionboxes", "Mystery", CV_SAVE|CV_NETVAR|CV_CHEAT, competitionboxes_cons_t, NULL);
 
 static CV_PossibleValue_t seenames_cons_t[] = {{0, "Off"}, {1, "Colorless"}, {2, "Team"}, {3, "Ally/Foe"}, {0, NULL}};
-consvar_t cv_seenames = CVAR_INIT ("seenames", "Ally/Foe", CV_SAVE|CV_ALLOWLUA, seenames_cons_t, 0);
-consvar_t cv_allowseenames = CVAR_INIT ("allowseenames", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
+consvar_t cv_seenames = CVAR_INIT ("seenames", "Ally/Foe", CV_SAVE, seenames_cons_t, 0);
+consvar_t cv_allowseenames = CVAR_INIT ("allowseenames", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL);
 
 // names
 consvar_t cv_playername = CVAR_INIT ("name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange);
 consvar_t cv_playername2 = CVAR_INIT ("name2", "Tails", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name2_OnChange);
 // player colors
 UINT16 lastgoodcolor = SKINCOLOR_BLUE, lastgoodcolor2 = SKINCOLOR_BLUE;
-consvar_t cv_playercolor = CVAR_INIT ("color", "Blue", CV_CALL|CV_NOINIT|CV_ALLOWLUA, Color_cons_t, Color_OnChange);
-consvar_t cv_playercolor2 = CVAR_INIT ("color2", "Orange", CV_CALL|CV_NOINIT|CV_ALLOWLUA, Color_cons_t, Color2_OnChange);
+consvar_t cv_playercolor = CVAR_INIT ("color", "Blue", CV_CALL|CV_NOINIT, Color_cons_t, Color_OnChange);
+consvar_t cv_playercolor2 = CVAR_INIT ("color2", "Orange", CV_CALL|CV_NOINIT, Color_cons_t, Color2_OnChange);
 // player's skin, saved for commodity, when using a favorite skins wad..
-consvar_t cv_skin = CVAR_INIT ("skin", DEFAULTSKIN, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin_OnChange);
-consvar_t cv_skin2 = CVAR_INIT ("skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin2_OnChange);
+consvar_t cv_skin = CVAR_INIT ("skin", DEFAULTSKIN, CV_CALL|CV_NOINIT, NULL, Skin_OnChange);
+consvar_t cv_skin2 = CVAR_INIT ("skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT, NULL, Skin2_OnChange);
 
 // saved versions of the above six
 consvar_t cv_defaultplayercolor = CVAR_INIT ("defaultcolor", "Blue", CV_SAVE, Color_cons_t, NULL);
@@ -248,19 +240,61 @@ INT32 cv_debug;
 consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse);
 consvar_t cv_usemouse2 = CVAR_INIT ("use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2);
 
-consvar_t cv_usejoystick = CVAR_INIT ("use_gamepad", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick);
-consvar_t cv_usejoystick2 = CVAR_INIT ("use_gamepad2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick2);
-#if (defined (LJOYSTICK) || defined (HAVE_SDL))
-#ifdef LJOYSTICK
-consvar_t cv_joyport = CVAR_INIT ("padport", "/dev/js0", CV_SAVE, joyport_cons_t, NULL);
-consvar_t cv_joyport2 = CVAR_INIT ("padport2", "/dev/js0", CV_SAVE, joyport_cons_t, NULL); //Alam: for later
-#endif
-consvar_t cv_joyscale = CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale);
-consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2);
-#else
-consvar_t cv_joyscale = CVAR_INIT ("padscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save
-consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save
-#endif
+// We use cv_usegamepad.string as the USER-SET var
+// and cv_usegamepad.value as the INTERNAL var
+//
+// In practice, if cv_usegamepad.string == 0, this overrides
+// cv_usegamepad.value and always disables
+
+static void UseGamepad_OnChange(void)
+{
+	I_ChangeGamepad(0);
+}
+
+static void UseGamepad2_OnChange(void)
+{
+	I_ChangeGamepad(1);
+}
+
+consvar_t cv_usegamepad[2] = {
+	CVAR_INIT ("use_gamepad", "1", CV_SAVE|CV_CALL, NULL, UseGamepad_OnChange),
+	CVAR_INIT ("use_gamepad2", "2", CV_SAVE|CV_CALL, NULL, UseGamepad2_OnChange)
+};
+
+static void PadScale_OnChange(void)
+{
+	I_SetGamepadDigital(0, cv_gamepad_scale[0].value == 0);
+}
+
+static void PadScale2_OnChange(void)
+{
+	I_SetGamepadDigital(1, cv_gamepad_scale[1].value == 0);
+}
+
+consvar_t cv_gamepad_scale[2] = {
+	CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, PadScale_OnChange),
+	CVAR_INIT ("padscale2", "1", CV_SAVE|CV_CALL, NULL, PadScale2_OnChange)
+};
+
+static void PadRumble_OnChange(void)
+{
+	if (!cv_gamepad_rumble[0].value)
+		I_StopGamepadRumble(0);
+}
+
+static void PadRumble2_OnChange(void)
+{
+	if (!cv_gamepad_rumble[1].value)
+		I_StopGamepadRumble(1);
+}
+
+consvar_t cv_gamepad_rumble[2] = {
+	CVAR_INIT ("padrumble", "Off", CV_SAVE|CV_CALL, CV_OnOff, PadRumble_OnChange),
+	CVAR_INIT ("padrumble2", "Off", CV_SAVE|CV_CALL, CV_OnOff, PadRumble2_OnChange)
+};
+
+consvar_t cv_gamepad_autopause = CVAR_INIT ("pauseongamepaddisconnect", "On", CV_SAVE, CV_OnOff, NULL);
+
 #if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON)
 consvar_t cv_mouse2port = CVAR_INIT ("mouse2port", "/dev/gpmdata", CV_SAVE, mouse2port_cons_t, NULL);
 consvar_t cv_mouse2opt = CVAR_INIT ("mouse2opt", "0", CV_SAVE, NULL, NULL);
@@ -268,43 +302,43 @@ consvar_t cv_mouse2opt = CVAR_INIT ("mouse2opt", "0", CV_SAVE, NULL, NULL);
 consvar_t cv_mouse2port = CVAR_INIT ("mouse2port", "COM2", CV_SAVE, mouse2port_cons_t, NULL);
 #endif
 
-consvar_t cv_matchboxes = CVAR_INIT ("matchboxes", "Normal", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, matchboxes_cons_t, NULL);
-consvar_t cv_specialrings = CVAR_INIT ("specialrings", "On", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
-consvar_t cv_powerstones = CVAR_INIT ("powerstones", "On", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
-
-consvar_t cv_recycler =      CVAR_INIT ("tv_recycler",      "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-consvar_t cv_teleporters =   CVAR_INIT ("tv_teleporter",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-consvar_t cv_superring =     CVAR_INIT ("tv_superring",     "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-consvar_t cv_supersneakers = CVAR_INIT ("tv_supersneaker",  "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-consvar_t cv_invincibility = CVAR_INIT ("tv_invincibility", "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-consvar_t cv_jumpshield =    CVAR_INIT ("tv_jumpshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-consvar_t cv_watershield =   CVAR_INIT ("tv_watershield",   "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-consvar_t cv_ringshield =    CVAR_INIT ("tv_ringshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-consvar_t cv_forceshield =   CVAR_INIT ("tv_forceshield",   "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-consvar_t cv_bombshield =    CVAR_INIT ("tv_bombshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-consvar_t cv_1up =           CVAR_INIT ("tv_1up",           "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-consvar_t cv_eggmanbox =     CVAR_INIT ("tv_eggman",        "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
-
-consvar_t cv_ringslinger = CVAR_INIT ("ringslinger", "No", CV_NETVAR|CV_NOSHOWHELP|CV_CALL|CV_CHEAT|CV_ALLOWLUA, CV_YesNo, Ringslinger_OnChange);
-consvar_t cv_gravity = CVAR_INIT ("gravity", "0.5", CV_RESTRICT|CV_FLOAT|CV_CALL|CV_ALLOWLUA, NULL, Gravity_OnChange);
+consvar_t cv_matchboxes = CVAR_INIT ("matchboxes", "Normal", CV_SAVE|CV_NETVAR|CV_CHEAT, matchboxes_cons_t, NULL);
+consvar_t cv_specialrings = CVAR_INIT ("specialrings", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
+consvar_t cv_powerstones = CVAR_INIT ("powerstones", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
+
+consvar_t cv_recycler =      CVAR_INIT ("tv_recycler",      "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+consvar_t cv_teleporters =   CVAR_INIT ("tv_teleporter",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+consvar_t cv_superring =     CVAR_INIT ("tv_superring",     "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+consvar_t cv_supersneakers = CVAR_INIT ("tv_supersneaker",  "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+consvar_t cv_invincibility = CVAR_INIT ("tv_invincibility", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+consvar_t cv_jumpshield =    CVAR_INIT ("tv_jumpshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+consvar_t cv_watershield =   CVAR_INIT ("tv_watershield",   "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+consvar_t cv_ringshield =    CVAR_INIT ("tv_ringshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+consvar_t cv_forceshield =   CVAR_INIT ("tv_forceshield",   "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+consvar_t cv_bombshield =    CVAR_INIT ("tv_bombshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+consvar_t cv_1up =           CVAR_INIT ("tv_1up",           "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+consvar_t cv_eggmanbox =     CVAR_INIT ("tv_eggman",        "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
+
+consvar_t cv_ringslinger = CVAR_INIT ("ringslinger", "No", CV_NETVAR|CV_NOSHOWHELP|CV_CALL|CV_CHEAT, CV_YesNo, Ringslinger_OnChange);
+consvar_t cv_gravity = CVAR_INIT ("gravity", "0.5", CV_RESTRICT|CV_FLOAT|CV_CALL, NULL, Gravity_OnChange);
 
 consvar_t cv_soundtest = CVAR_INIT ("soundtest", "0", CV_CALL, NULL, SoundTest_OnChange);
 
 static CV_PossibleValue_t minitimelimit_cons_t[] = {{1, "MIN"}, {9999, "MAX"}, {0, NULL}};
-consvar_t cv_countdowntime = CVAR_INIT ("countdowntime", "60", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, minitimelimit_cons_t, NULL);
+consvar_t cv_countdowntime = CVAR_INIT ("countdowntime", "60", CV_SAVE|CV_NETVAR|CV_CHEAT, minitimelimit_cons_t, NULL);
 
-consvar_t cv_touchtag = CVAR_INIT ("touchtag", "Off", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
-consvar_t cv_hidetime = CVAR_INIT ("hidetime", "30", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, minitimelimit_cons_t, Hidetime_OnChange);
+consvar_t cv_touchtag = CVAR_INIT ("touchtag", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
+consvar_t cv_hidetime = CVAR_INIT ("hidetime", "30", CV_SAVE|CV_NETVAR|CV_CALL, minitimelimit_cons_t, Hidetime_OnChange);
 
-consvar_t cv_autobalance = CVAR_INIT ("autobalance", "Off", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, CV_OnOff, AutoBalance_OnChange);
-consvar_t cv_teamscramble = CVAR_INIT ("teamscramble", "Off", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, teamscramble_cons_t, TeamScramble_OnChange);
-consvar_t cv_scrambleonchange = CVAR_INIT ("scrambleonchange", "Off", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, teamscramble_cons_t, NULL);
+consvar_t cv_autobalance = CVAR_INIT ("autobalance", "Off", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, AutoBalance_OnChange);
+consvar_t cv_teamscramble = CVAR_INIT ("teamscramble", "Off", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, teamscramble_cons_t, TeamScramble_OnChange);
+consvar_t cv_scrambleonchange = CVAR_INIT ("scrambleonchange", "Off", CV_SAVE|CV_NETVAR, teamscramble_cons_t, NULL);
 
-consvar_t cv_friendlyfire = CVAR_INIT ("friendlyfire", "Off", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
-consvar_t cv_itemfinder = CVAR_INIT ("itemfinder", "Off", CV_CALL|CV_ALLOWLUA, CV_OnOff, ItemFinder_OnChange);
+consvar_t cv_friendlyfire = CVAR_INIT ("friendlyfire", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
+consvar_t cv_itemfinder = CVAR_INIT ("itemfinder", "Off", CV_CALL, CV_OnOff, ItemFinder_OnChange);
 
 // Scoring type options
-consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
+consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL);
 
 consvar_t cv_rollingdemos = CVAR_INIT ("rollingdemos", "On", CV_SAVE, CV_OnOff, NULL);
 
@@ -315,13 +349,13 @@ static CV_PossibleValue_t powerupdisplay_cons_t[] = {{0, "Never"}, {1, "First-pe
 consvar_t cv_powerupdisplay = CVAR_INIT ("powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL);
 
 static CV_PossibleValue_t pointlimit_cons_t[] = {{1, "MIN"}, {MAXSCORE, "MAX"}, {0, "None"}, {0, NULL}};
-consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, pointlimit_cons_t, PointLimit_OnChange);
+consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange);
 static CV_PossibleValue_t timelimit_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "None"}, {0, NULL}};
-consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, timelimit_cons_t, TimeLimit_OnChange);
+consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, TimeLimit_OnChange);
 static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, NULL}};
-consvar_t cv_numlaps = CVAR_INIT ("numlaps", "4", CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, numlaps_cons_t, NumLaps_OnChange);
+consvar_t cv_numlaps = CVAR_INIT ("numlaps", "4", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_cons_t, NumLaps_OnChange);
 static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, "Map default"}, {0, NULL}};
-consvar_t cv_basenumlaps = CVAR_INIT ("basenumlaps", "Map default", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT|CV_ALLOWLUA, basenumlaps_cons_t, BaseNumLaps_OnChange);
+consvar_t cv_basenumlaps = CVAR_INIT ("basenumlaps", "Map default", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, basenumlaps_cons_t, BaseNumLaps_OnChange);
 
 // Point and time limits for every gametype
 INT32 pointlimits[NUMGAMETYPES];
@@ -330,11 +364,11 @@ INT32 timelimits[NUMGAMETYPES];
 // log elemental hazards -- not a netvar, is local to current player
 consvar_t cv_hazardlog = CVAR_INIT ("hazardlog", "Yes", 0, CV_YesNo, NULL);
 
-consvar_t cv_forceskin = CVAR_INIT ("forceskin", "None", CV_NETVAR|CV_CALL|CV_CHEAT|CV_ALLOWLUA, NULL, ForceSkin_OnChange);
+consvar_t cv_forceskin = CVAR_INIT ("forceskin", "None", CV_NETVAR|CV_CALL|CV_CHEAT, NULL, ForceSkin_OnChange);
 consvar_t cv_downloading = CVAR_INIT ("downloading", "On", 0, CV_OnOff, NULL);
-consvar_t cv_allowexitlevel = CVAR_INIT ("allowexitlevel", "No", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
+consvar_t cv_allowexitlevel = CVAR_INIT ("allowexitlevel", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL);
 
-consvar_t cv_killingdead = CVAR_INIT ("killingdead", "Off", CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
+consvar_t cv_killingdead = CVAR_INIT ("killingdead", "Off", CV_NETVAR, CV_OnOff, NULL);
 
 consvar_t cv_netstat = CVAR_INIT ("netstat", "Off", 0, CV_OnOff, NULL); // show bandwidth statistics
 static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
@@ -352,26 +386,26 @@ consvar_t cv_showping = CVAR_INIT ("showping", "Warning", CV_SAVE, showping_cons
 
 // Intermission time Tails 04-19-2002
 static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}};
-consvar_t cv_inttime = CVAR_INIT ("inttime", "10", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, inttime_cons_t, NULL);
+consvar_t cv_inttime = CVAR_INIT ("inttime", "10", CV_SAVE|CV_NETVAR, inttime_cons_t, NULL);
 
 static CV_PossibleValue_t coopstarposts_cons_t[] = {{0, "Per-player"}, {1, "Shared"}, {2, "Teamwork"}, {0, NULL}};
-consvar_t cv_coopstarposts = CVAR_INIT ("coopstarposts", "Per-player", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, coopstarposts_cons_t, CoopStarposts_OnChange);
+consvar_t cv_coopstarposts = CVAR_INIT ("coopstarposts", "Per-player", CV_SAVE|CV_NETVAR|CV_CALL, coopstarposts_cons_t, CoopStarposts_OnChange);
 
 static CV_PossibleValue_t cooplives_cons_t[] = {{0, "Infinite"}, {1, "Per-player"}, {2, "Avoid Game Over"}, {3, "Single pool"}, {0, NULL}};
-consvar_t cv_cooplives = CVAR_INIT ("cooplives", "Avoid Game Over", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT|CV_ALLOWLUA, cooplives_cons_t, CoopLives_OnChange);
+consvar_t cv_cooplives = CVAR_INIT ("cooplives", "Avoid Game Over", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, cooplives_cons_t, CoopLives_OnChange);
 
 static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {0, NULL}};
-consvar_t cv_advancemap = CVAR_INIT ("advancemap", "Next", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, advancemap_cons_t, NULL);
+consvar_t cv_advancemap = CVAR_INIT ("advancemap", "Next", CV_SAVE|CV_NETVAR, advancemap_cons_t, NULL);
 
 static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "1/4"}, {2, "Half"}, {3, "3/4"}, {4, "All"}, {0, NULL}};
-consvar_t cv_playersforexit = CVAR_INIT ("playersforexit", "All", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, playersforexit_cons_t, NULL);
+consvar_t cv_playersforexit = CVAR_INIT ("playersforexit", "All", CV_SAVE|CV_NETVAR, playersforexit_cons_t, NULL);
 
-consvar_t cv_exitmove = CVAR_INIT ("exitmove", "On", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, CV_OnOff, ExitMove_OnChange);
+consvar_t cv_exitmove = CVAR_INIT ("exitmove", "On", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, ExitMove_OnChange);
 
-consvar_t cv_runscripts = CVAR_INIT ("runscripts", "Yes", CV_ALLOWLUA, CV_YesNo, NULL);
+consvar_t cv_runscripts = CVAR_INIT ("runscripts", "Yes", 0, CV_YesNo, NULL);
 
-consvar_t cv_pause = CVAR_INIT ("pausepermission", "Server", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, pause_cons_t, NULL);
-consvar_t cv_mute = CVAR_INIT ("mute", "Off", CV_NETVAR|CV_CALL|CV_ALLOWLUA, CV_OnOff, Mute_OnChange);
+consvar_t cv_pause = CVAR_INIT ("pausepermission", "Server", CV_SAVE|CV_NETVAR, pause_cons_t, NULL);
+consvar_t cv_mute = CVAR_INIT ("mute", "Off", CV_NETVAR|CV_CALL, CV_OnOff, Mute_OnChange);
 
 consvar_t cv_sleep = CVAR_INIT ("cpusleep", "1", CV_SAVE, sleeping_cons_t, NULL);
 
@@ -464,57 +498,57 @@ void D_RegisterServerCommands(void)
 	RegisterNetXCmd(XD_LUAFILE, Got_LuaFile);
 
 	// Remote Administration
-	COM_AddCommand("password", Command_Changepassword_f, COM_LUA);
-	COM_AddCommand("login", Command_Login_f, COM_LUA); // useful in dedicated to kick off remote admin
-	COM_AddCommand("promote", Command_Verify_f, COM_LUA);
+	COM_AddCommand("password", Command_Changepassword_f);
+	COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin
+	COM_AddCommand("promote", Command_Verify_f);
 	RegisterNetXCmd(XD_VERIFIED, Got_Verification);
-	COM_AddCommand("demote", Command_RemoveAdmin_f, COM_LUA);
+	COM_AddCommand("demote", Command_RemoveAdmin_f);
 	RegisterNetXCmd(XD_DEMOTED, Got_Removal);
 
-	COM_AddCommand("motd", Command_MotD_f, COM_LUA);
+	COM_AddCommand("motd", Command_MotD_f);
 	RegisterNetXCmd(XD_SETMOTD, Got_MotD_f); // For remote admin
 
 	RegisterNetXCmd(XD_TEAMCHANGE, Got_Teamchange);
-	COM_AddCommand("serverchangeteam", Command_ServerTeamChange_f, COM_LUA);
+	COM_AddCommand("serverchangeteam", Command_ServerTeamChange_f);
 
 	RegisterNetXCmd(XD_CLEARSCORES, Got_Clearscores);
-	COM_AddCommand("clearscores", Command_Clearscores_f, COM_LUA);
-	COM_AddCommand("map", Command_Map_f, COM_LUA);
+	COM_AddCommand("clearscores", Command_Clearscores_f);
+	COM_AddCommand("map", Command_Map_f);
 
-	COM_AddCommand("exitgame", Command_ExitGame_f, COM_LUA);
-	COM_AddCommand("retry", Command_Retry_f, COM_LUA);
-	COM_AddCommand("exitlevel", Command_ExitLevel_f, COM_LUA);
-	COM_AddCommand("showmap", Command_Showmap_f, COM_LUA);
-	COM_AddCommand("mapmd5", Command_Mapmd5_f, COM_LUA);
+	COM_AddCommand("exitgame", Command_ExitGame_f);
+	COM_AddCommand("retry", Command_Retry_f);
+	COM_AddCommand("exitlevel", Command_ExitLevel_f);
+	COM_AddCommand("showmap", Command_Showmap_f);
+	COM_AddCommand("mapmd5", Command_Mapmd5_f);
 
-	COM_AddCommand("addfolder", Command_Addfolder, COM_LUA);
-	COM_AddCommand("addfile", Command_Addfile, COM_LUA);
-	COM_AddCommand("listwad", Command_ListWADS_f, COM_LUA);
+	COM_AddCommand("addfolder", Command_Addfolder);
+	COM_AddCommand("addfile", Command_Addfile);
+	COM_AddCommand("listwad", Command_ListWADS_f);
 
-	COM_AddCommand("runsoc", Command_RunSOC, COM_LUA);
-	COM_AddCommand("pause", Command_Pause, COM_LUA);
-	COM_AddCommand("suicide", Command_Suicide, COM_LUA);
+	COM_AddCommand("runsoc", Command_RunSOC);
+	COM_AddCommand("pause", Command_Pause);
+	COM_AddCommand("suicide", Command_Suicide);
 
-	COM_AddCommand("gametype", Command_ShowGametype_f, COM_LUA);
-	COM_AddCommand("version", Command_Version_f, COM_LUA);
+	COM_AddCommand("gametype", Command_ShowGametype_f);
+	COM_AddCommand("version", Command_Version_f);
 #ifdef UPDATE_ALERT
-	COM_AddCommand("mod_details", Command_ModDetails_f, COM_LUA);
+	COM_AddCommand("mod_details", Command_ModDetails_f);
 #endif
-	COM_AddCommand("quit", Command_Quit_f, COM_LUA);
-
-	COM_AddCommand("saveconfig", Command_SaveConfig_f, 0);
-	COM_AddCommand("loadconfig", Command_LoadConfig_f, 0);
-	COM_AddCommand("changeconfig", Command_ChangeConfig_f, 0);
-	COM_AddCommand("isgamemodified", Command_Isgamemodified_f, COM_LUA); // test
-	COM_AddCommand("showscores", Command_ShowScores_f, COM_LUA);
-	COM_AddCommand("showtime", Command_ShowTime_f, COM_LUA);
-	COM_AddCommand("cheats", Command_Cheats_f, COM_LUA); // test
+	COM_AddCommand("quit", Command_Quit_f);
+
+	COM_AddCommand("saveconfig", Command_SaveConfig_f);
+	COM_AddCommand("loadconfig", Command_LoadConfig_f);
+	COM_AddCommand("changeconfig", Command_ChangeConfig_f);
+	COM_AddCommand("isgamemodified", Command_Isgamemodified_f); // test
+	COM_AddCommand("showscores", Command_ShowScores_f);
+	COM_AddCommand("showtime", Command_ShowTime_f);
+	COM_AddCommand("cheats", Command_Cheats_f); // test
 #ifdef _DEBUG
-	COM_AddCommand("togglemodified", Command_Togglemodified_f, COM_LUA);
-	COM_AddCommand("archivetest", Command_Archivetest_f, COM_LUA);
+	COM_AddCommand("togglemodified", Command_Togglemodified_f);
+	COM_AddCommand("archivetest", Command_Archivetest_f);
 #endif
 
-	COM_AddCommand("downloads", Command_Downloads_f, COM_LUA);
+	COM_AddCommand("downloads", Command_Downloads_f);
 
 	// for master server connection
 	AddMServCommands();
@@ -599,10 +633,9 @@ void D_RegisterServerCommands(void)
 	CV_RegisterVar(&cv_joinnextround);
 	CV_RegisterVar(&cv_showjoinaddress);
 	CV_RegisterVar(&cv_blamecfail);
-	CV_RegisterVar(&cv_dedicatedidletime);
 #endif
 
-	COM_AddCommand("ping", Command_Ping_f, COM_LUA);
+	COM_AddCommand("ping", Command_Ping_f);
 	CV_RegisterVar(&cv_nettimeout);
 	CV_RegisterVar(&cv_jointimeout);
 
@@ -614,10 +647,6 @@ void D_RegisterServerCommands(void)
 
 	CV_RegisterVar(&cv_allowseenames);
 
-	// Other filesrch.c consvars are defined in D_RegisterClientCommands
-	CV_RegisterVar(&cv_addons_option);
-	CV_RegisterVar(&cv_addons_folder);
-
 	CV_RegisterVar(&cv_dummyconsvar);
 }
 
@@ -650,25 +679,25 @@ void D_RegisterClientCommands(void)
 	if (dedicated)
 		return;
 
-	COM_AddCommand("numthinkers", Command_Numthinkers_f, COM_LUA);
-	COM_AddCommand("countmobjs", Command_CountMobjs_f, COM_LUA);
+	COM_AddCommand("numthinkers", Command_Numthinkers_f);
+	COM_AddCommand("countmobjs", Command_CountMobjs_f);
 
-	COM_AddCommand("changeteam", Command_Teamchange_f, COM_LUA);
-	COM_AddCommand("changeteam2", Command_Teamchange2_f, COM_LUA);
+	COM_AddCommand("changeteam", Command_Teamchange_f);
+	COM_AddCommand("changeteam2", Command_Teamchange2_f);
 
-	COM_AddCommand("playdemo", Command_Playdemo_f, 0);
-	COM_AddCommand("timedemo", Command_Timedemo_f, 0);
-	COM_AddCommand("stopdemo", Command_Stopdemo_f, COM_LUA);
-	COM_AddCommand("playintro", Command_Playintro_f, COM_LUA);
+	COM_AddCommand("playdemo", Command_Playdemo_f);
+	COM_AddCommand("timedemo", Command_Timedemo_f);
+	COM_AddCommand("stopdemo", Command_Stopdemo_f);
+	COM_AddCommand("playintro", Command_Playintro_f);
 
-	COM_AddCommand("resetcamera", Command_ResetCamera_f, COM_LUA);
+	COM_AddCommand("resetcamera", Command_ResetCamera_f);
 
-	COM_AddCommand("setcontrol", Command_Setcontrol_f, 0);
-	COM_AddCommand("setcontrol2", Command_Setcontrol2_f, 0);
+	COM_AddCommand("setcontrol", Command_Setcontrol_f);
+	COM_AddCommand("setcontrol2", Command_Setcontrol2_f);
 
-	COM_AddCommand("screenshot", M_ScreenShot, COM_LUA);
-	COM_AddCommand("startmovie", Command_StartMovie_f, COM_LUA);
-	COM_AddCommand("stopmovie", Command_StopMovie_f, COM_LUA);
+	COM_AddCommand("screenshot", M_ScreenShot);
+	COM_AddCommand("startmovie", Command_StartMovie_f);
+	COM_AddCommand("stopmovie", Command_StopMovie_f);
 
 	CV_RegisterVar(&cv_screenshot_option);
 	CV_RegisterVar(&cv_screenshot_folder);
@@ -730,7 +759,7 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_ghost_last);
 	CV_RegisterVar(&cv_ghost_guest);
 
-	COM_AddCommand("displayplayer", Command_Displayplayer_f, COM_LUA);
+	COM_AddCommand("displayplayer", Command_Displayplayer_f);
 
 	// FIXME: not to be here.. but needs be done for config loading
 	CV_RegisterVar(&cv_globalgamma);
@@ -777,30 +806,30 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_pauseifunfocused);
 
 	// g_input.c
-	CV_RegisterVar(&cv_sideaxis);
-	CV_RegisterVar(&cv_sideaxis2);
-	CV_RegisterVar(&cv_turnaxis);
-	CV_RegisterVar(&cv_turnaxis2);
-	CV_RegisterVar(&cv_moveaxis);
-	CV_RegisterVar(&cv_moveaxis2);
-	CV_RegisterVar(&cv_lookaxis);
-	CV_RegisterVar(&cv_lookaxis2);
-	CV_RegisterVar(&cv_jumpaxis);
-	CV_RegisterVar(&cv_jumpaxis2);
-	CV_RegisterVar(&cv_spinaxis);
-	CV_RegisterVar(&cv_spinaxis2);
-	CV_RegisterVar(&cv_fireaxis);
-	CV_RegisterVar(&cv_fireaxis2);
-	CV_RegisterVar(&cv_firenaxis);
-	CV_RegisterVar(&cv_firenaxis2);
-	CV_RegisterVar(&cv_deadzone);
-	CV_RegisterVar(&cv_deadzone2);
-	CV_RegisterVar(&cv_digitaldeadzone);
-	CV_RegisterVar(&cv_digitaldeadzone2);
+	CV_RegisterVar(&cv_sideaxis[0]);
+	CV_RegisterVar(&cv_sideaxis[1]);
+	CV_RegisterVar(&cv_turnaxis[0]);
+	CV_RegisterVar(&cv_turnaxis[1]);
+	CV_RegisterVar(&cv_moveaxis[0]);
+	CV_RegisterVar(&cv_moveaxis[1]);
+	CV_RegisterVar(&cv_lookaxis[0]);
+	CV_RegisterVar(&cv_lookaxis[1]);
+	CV_RegisterVar(&cv_jumpaxis[0]);
+	CV_RegisterVar(&cv_jumpaxis[1]);
+	CV_RegisterVar(&cv_spinaxis[0]);
+	CV_RegisterVar(&cv_spinaxis[1]);
+	CV_RegisterVar(&cv_fireaxis[0]);
+	CV_RegisterVar(&cv_fireaxis[1]);
+	CV_RegisterVar(&cv_firenaxis[0]);
+	CV_RegisterVar(&cv_firenaxis[1]);
+	CV_RegisterVar(&cv_deadzone[0]);
+	CV_RegisterVar(&cv_deadzone[1]);
+	CV_RegisterVar(&cv_digitaldeadzone[0]);
+	CV_RegisterVar(&cv_digitaldeadzone[1]);
 
 	// filesrch.c
-	//CV_RegisterVar(&cv_addons_option); // These two are now defined
-	//CV_RegisterVar(&cv_addons_folder); // in D_RegisterServerCommands
+	CV_RegisterVar(&cv_addons_option);
+	CV_RegisterVar(&cv_addons_folder);
 	CV_RegisterVar(&cv_addons_md5);
 	CV_RegisterVar(&cv_addons_showall);
 	CV_RegisterVar(&cv_addons_search_type);
@@ -825,14 +854,14 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_mousemove);
 	CV_RegisterVar(&cv_mousemove2);
 
-	CV_RegisterVar(&cv_usejoystick);
-	CV_RegisterVar(&cv_usejoystick2);
-#ifdef LJOYSTICK
-	CV_RegisterVar(&cv_joyport);
-	CV_RegisterVar(&cv_joyport2);
-#endif
-	CV_RegisterVar(&cv_joyscale);
-	CV_RegisterVar(&cv_joyscale2);
+	for (i = 0; i < 2; i++)
+	{
+		CV_RegisterVar(&cv_usegamepad[i]);
+		CV_RegisterVar(&cv_gamepad_scale[i]);
+		CV_RegisterVar(&cv_gamepad_rumble[i]);
+	}
+
+	CV_RegisterVar(&cv_gamepad_autopause);
 
 	// Analog Control
 	CV_RegisterVar(&cv_analog[0]);
@@ -874,15 +903,10 @@ void D_RegisterClientCommands(void)
 	// screen.c
 	CV_RegisterVar(&cv_fullscreen);
 	CV_RegisterVar(&cv_renderview);
-	CV_RegisterVar(&cv_renderhitboxinterpolation);
-	CV_RegisterVar(&cv_renderhitboxgldepth);
-	CV_RegisterVar(&cv_renderhitbox);
 	CV_RegisterVar(&cv_renderer);
 	CV_RegisterVar(&cv_scr_depth);
 	CV_RegisterVar(&cv_scr_width);
 	CV_RegisterVar(&cv_scr_height);
-	CV_RegisterVar(&cv_scr_width_w);
-	CV_RegisterVar(&cv_scr_height_w);
 
 	CV_RegisterVar(&cv_soundtest);
 
@@ -891,7 +915,7 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_ps_descriptor);
 
 	// ingame object placing
-	COM_AddCommand("objectplace", Command_ObjectPlace_f, COM_LUA);
+	COM_AddCommand("objectplace", Command_ObjectPlace_f);
 	//COM_AddCommand("writethings", Command_Writethings_f);
 	CV_RegisterVar(&cv_speed);
 	CV_RegisterVar(&cv_opflags);
@@ -903,32 +927,32 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_freedemocamera);
 
 	// add cheat commands
-	COM_AddCommand("noclip", Command_CheatNoClip_f, COM_LUA);
-	COM_AddCommand("god", Command_CheatGod_f, COM_LUA);
-	COM_AddCommand("notarget", Command_CheatNoTarget_f, COM_LUA);
-	COM_AddCommand("getallemeralds", Command_Getallemeralds_f, COM_LUA);
-	COM_AddCommand("resetemeralds", Command_Resetemeralds_f, COM_LUA);
-	COM_AddCommand("setrings", Command_Setrings_f, COM_LUA);
-	COM_AddCommand("setlives", Command_Setlives_f, COM_LUA);
-	COM_AddCommand("setcontinues", Command_Setcontinues_f, COM_LUA);
-	COM_AddCommand("devmode", Command_Devmode_f, COM_LUA);
-	COM_AddCommand("savecheckpoint", Command_Savecheckpoint_f, COM_LUA);
-	COM_AddCommand("scale", Command_Scale_f, COM_LUA);
-	COM_AddCommand("gravflip", Command_Gravflip_f, COM_LUA);
-	COM_AddCommand("hurtme", Command_Hurtme_f, COM_LUA);
-	COM_AddCommand("jumptoaxis", Command_JumpToAxis_f, COM_LUA);
-	COM_AddCommand("charability", Command_Charability_f, COM_LUA);
-	COM_AddCommand("charspeed", Command_Charspeed_f, COM_LUA);
-	COM_AddCommand("teleport", Command_Teleport_f, COM_LUA);
-	COM_AddCommand("rteleport", Command_RTeleport_f, COM_LUA);
-	COM_AddCommand("skynum", Command_Skynum_f, COM_LUA);
-	COM_AddCommand("weather", Command_Weather_f, COM_LUA);
-	COM_AddCommand("toggletwod", Command_Toggletwod_f, COM_LUA);
+	COM_AddCommand("noclip", Command_CheatNoClip_f);
+	COM_AddCommand("god", Command_CheatGod_f);
+	COM_AddCommand("notarget", Command_CheatNoTarget_f);
+	COM_AddCommand("getallemeralds", Command_Getallemeralds_f);
+	COM_AddCommand("resetemeralds", Command_Resetemeralds_f);
+	COM_AddCommand("setrings", Command_Setrings_f);
+	COM_AddCommand("setlives", Command_Setlives_f);
+	COM_AddCommand("setcontinues", Command_Setcontinues_f);
+	COM_AddCommand("devmode", Command_Devmode_f);
+	COM_AddCommand("savecheckpoint", Command_Savecheckpoint_f);
+	COM_AddCommand("scale", Command_Scale_f);
+	COM_AddCommand("gravflip", Command_Gravflip_f);
+	COM_AddCommand("hurtme", Command_Hurtme_f);
+	COM_AddCommand("jumptoaxis", Command_JumpToAxis_f);
+	COM_AddCommand("charability", Command_Charability_f);
+	COM_AddCommand("charspeed", Command_Charspeed_f);
+	COM_AddCommand("teleport", Command_Teleport_f);
+	COM_AddCommand("rteleport", Command_RTeleport_f);
+	COM_AddCommand("skynum", Command_Skynum_f);
+	COM_AddCommand("weather", Command_Weather_f);
+	COM_AddCommand("toggletwod", Command_Toggletwod_f);
 #ifdef _DEBUG
-	COM_AddCommand("causecfail", Command_CauseCfail_f, COM_LUA);
+	COM_AddCommand("causecfail", Command_CauseCfail_f);
 #endif
 #ifdef LUA_ALLOW_BYTECODE
-	COM_AddCommand("dumplua", Command_Dumplua_f, COM_LUA);
+	COM_AddCommand("dumplua", Command_Dumplua_f);
 #endif
 }
 
@@ -1643,14 +1667,9 @@ static void Command_Playdemo_f(void)
 {
 	char name[256];
 
-	if (COM_Argc() < 2)
+	if (COM_Argc() != 2)
 	{
-		CONS_Printf("playdemo <demoname> [-addfiles / -force]:\n");
-		CONS_Printf(M_GetText(
-					"Play back a demo file. The full path from your SRB2 directory must be given.\n\n"
-
-					"* With \"-addfiles\", any required files are added from a list contained within the demo file.\n"
-					"* With \"-force\", the demo is played even if the necessary files have not been added.\n"));
+		CONS_Printf(M_GetText("playdemo <demoname>: playback a demo\n"));
 		return;
 	}
 
@@ -1672,16 +1691,6 @@ static void Command_Playdemo_f(void)
 
 	CONS_Printf(M_GetText("Playing back demo '%s'.\n"), name);
 
-	demofileoverride = DFILE_OVERRIDE_NONE;
-	if (strcmp(COM_Argv(2), "-addfiles") == 0)
-	{
-		demofileoverride = DFILE_OVERRIDE_LOAD;
-	}
-	else if (strcmp(COM_Argv(2), "-force") == 0)
-	{
-		demofileoverride = DFILE_OVERRIDE_SKIP;
-	}
-
 	// Internal if no extension, external if one exists
 	// If external, convert the file name to a path in SRB2's home directory
 	if (FIL_CheckExtension(name))
@@ -1903,7 +1912,7 @@ static void Command_Map_f(void)
 	const char *gametypename;
 	boolean newresetplayers;
 
-	boolean wouldSetCheats;
+	boolean mustmodifygame;
 
 	INT32 newmapnum;
 
@@ -1924,11 +1933,11 @@ static void Command_Map_f(void)
 	option_gametype =   COM_CheckPartialParm("-g");
 	newresetplayers = ! COM_CheckParm("-noresetplayers");
 
-	wouldSetCheats =
-		!( netgame || multiplayer ) &&
-		!( usedCheats );
+	mustmodifygame =
+		!( netgame     || multiplayer ) &&
+		(!modifiedgame || savemoddata );
 
-	if (wouldSetCheats && !option_force)
+	if (mustmodifygame && !option_force)
 	{
 		/* May want to be more descriptive? */
 		CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
@@ -1982,9 +1991,9 @@ static void Command_Map_f(void)
 		return;
 	}
 
-	if (wouldSetCheats && option_force)
+	if (mustmodifygame && option_force)
 	{
-		G_SetUsedCheats(false);
+		G_SetGameModified(false);
 	}
 
 	// new gametype value
@@ -2057,7 +2066,7 @@ static void Command_Map_f(void)
 	// ... unless you're in a dedicated server.  Yes, technically this means you can view any level by
 	// running a dedicated server and joining it yourself, but that's better than making dedicated server's
 	// lives hell.
-	if (!dedicated && M_MapLocked(newmapnum, serverGamedata))
+	if (!dedicated && M_MapLocked(newmapnum))
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
 		Z_Free(realmapname);
@@ -2192,7 +2201,7 @@ static void Command_Pause(void)
 
 	if (cv_pause.value || server || (IsPlayerAdmin(consoleplayer)))
 	{
-		if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_WAITINGPLAYERS) || (marathonmode && gamestate == GS_INTERMISSION))
+		if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) || (marathonmode && gamestate == GS_INTERMISSION))
 		{
 			CONS_Printf(M_GetText("You can't pause here.\n"));
 			return;
@@ -2241,9 +2250,14 @@ static void Got_Pause(UINT8 **cp, INT32 playernum)
 		{
 			if (!menuactive || netgame)
 				S_PauseAudio();
+
+			P_PauseRumble(NULL);
 		}
 		else
+		{
 			S_ResumeAudio();
+			P_UnpauseRumble(NULL);
+		}
 	}
 
 	I_UpdateMouseGrab();
@@ -2355,7 +2369,7 @@ static void Got_Clearscores(UINT8 **cp, INT32 playernum)
 	}
 
 	for (i = 0; i < MAXPLAYERS; i++)
-		players[i].score = players[i].recordscore = 0;
+		players[i].score = 0;
 
 	CONS_Printf(M_GetText("Scores have been reset by the server.\n"));
 }
@@ -3298,69 +3312,6 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum)
 	G_SetGameModified(true);
 }
 
-// C++ would make this SO much simpler!
-typedef struct addedfile_s
-{
-	struct addedfile_s *next;
-	struct addedfile_s *prev;
-	char *value;
-} addedfile_t;
-
-static boolean AddedFileContains(addedfile_t *list, const char *value)
-{
-	addedfile_t *node;
-	for (node = list; node; node = node->next)
-	{
-		if (!strcmp(value, node->value))
-			return true;
-	}
-
-	return false;
-}
-
-static void AddedFilesAdd(addedfile_t **list, const char *value)
-{
-	addedfile_t *item = Z_Calloc(sizeof(addedfile_t), PU_STATIC, NULL);
-	item->value = Z_StrDup(value);
-	ListAdd(item, (listitem_t**)list);
-}
-
-static void AddedFilesRemove(void *pItem, addedfile_t **itemHead)
-{
-	addedfile_t *item = (addedfile_t *)pItem;
-
-	if (item == *itemHead) // Start of list
-	{
-		*itemHead = item->next;
-
-		if (*itemHead)
-			(*itemHead)->prev = NULL;
-	}
-	else if (item->next == NULL) // end of list
-	{
-		item->prev->next = NULL;
-	}
-	else // Somewhere in between
-	{
-		item->prev->next = item->next;
-		item->next->prev = item->prev;
-	}
-
-	Z_Free(item->value);
-	Z_Free(item);
-}
-
-static void AddedFilesClearList(addedfile_t **itemHead)
-{
-	addedfile_t *item;
-	addedfile_t *next;
-	for (item = *itemHead; item; item = next)
-	{
-		next = item->next;
-		AddedFilesRemove(item, itemHead);
-	}
-}
-
 /** Adds a pwad at runtime.
   * Searches for sounds, maps, music, new images.
   */
@@ -3369,7 +3320,8 @@ static void Command_Addfile(void)
 	size_t argc = COM_Argc(); // amount of arguments total
 	size_t curarg; // current argument index
 
-	addedfile_t *addedfiles = NULL; // list of filenames already processed
+	const char *addedfiles[argc]; // list of filenames already processed
+	size_t numfilesadded = 0; // the amount of filenames processed
 
 	if (argc < 2)
 	{
@@ -3384,14 +3336,25 @@ static void Command_Addfile(void)
 		char buf[256];
 		char *buf_p = buf;
 		INT32 i;
+		size_t ii;
 		int musiconly; // W_VerifyNMUSlumps isn't boolean
 		boolean fileadded = false;
 
 		fn = COM_Argv(curarg);
 
 		// For the amount of filenames previously processed...
-		fileadded = AddedFileContains(addedfiles, fn);
-		if (fileadded) // If this is one of them, don't try to add it.
+		for (ii = 0; ii < numfilesadded; ii++)
+		{
+			// If this is one of them, don't try to add it.
+			if (!strcmp(fn, addedfiles[ii]))
+			{
+				fileadded = true;
+				break;
+			}
+		}
+
+		// If we've added this one, skip to the next one.
+		if (fileadded)
 		{
 			CONS_Alert(CONS_WARNING, M_GetText("Already processed %s, skipping\n"), fn);
 			continue;
@@ -3400,16 +3363,13 @@ static void Command_Addfile(void)
 		// Disallow non-printing characters and semicolons.
 		for (i = 0; fn[i] != '\0'; i++)
 			if (!isprint(fn[i]) || fn[i] == ';')
-			{
-				AddedFilesClearList(&addedfiles);
 				return;
-			}
 
 		musiconly = W_VerifyNMUSlumps(fn, false);
 
 		if (musiconly == -1)
 		{
-			AddedFilesAdd(&addedfiles, fn);
+			addedfiles[numfilesadded++] = fn;
 			continue;
 		}
 
@@ -3428,7 +3388,7 @@ static void Command_Addfile(void)
 		if (!(netgame || multiplayer) || musiconly)
 		{
 			P_AddWadFile(fn);
-			AddedFilesAdd(&addedfiles, fn);
+			addedfiles[numfilesadded++] = fn;
 			continue;
 		}
 
@@ -3443,7 +3403,6 @@ static void Command_Addfile(void)
 		if (numwadfiles >= MAX_WADFILES)
 		{
 			CONS_Alert(CONS_ERROR, M_GetText("Too many files loaded to add %s\n"), fn);
-			AddedFilesClearList(&addedfiles);
 			return;
 		}
 
@@ -3483,15 +3442,13 @@ static void Command_Addfile(void)
 			WRITEMEM(buf_p, md5sum, 16);
 		}
 
-		AddedFilesAdd(&addedfiles, fn);
+		addedfiles[numfilesadded++] = fn;
 
 		if (IsPlayerAdmin(consoleplayer) && (!server)) // Request to add file
 			SendNetXCmd(XD_REQADDFILE, buf, buf_p - buf);
 		else
 			SendNetXCmd(XD_ADDFILE, buf, buf_p - buf);
 	}
-
-	AddedFilesClearList(&addedfiles);
 }
 
 static void Command_Addfolder(void)
@@ -3499,7 +3456,8 @@ static void Command_Addfolder(void)
 	size_t argc = COM_Argc(); // amount of arguments total
 	size_t curarg; // current argument index
 
-	addedfile_t *addedfolders = NULL; // list of filenames already processed
+	const char *addedfolders[argc]; // list of filenames already processed
+	size_t numfoldersadded = 0; // the amount of filenames processed
 
 	if (argc < 2)
 	{
@@ -3515,13 +3473,24 @@ static void Command_Addfolder(void)
 		char buf[256];
 		char *buf_p = buf;
 		INT32 i, stat;
+		size_t ii;
 		boolean folderadded = false;
 
 		fn = COM_Argv(curarg);
 
 		// For the amount of filenames previously processed...
-		folderadded = AddedFileContains(addedfolders, fn);
-		if (folderadded) // If we've added this one, skip to the next one.
+		for (ii = 0; ii < numfoldersadded; ii++)
+		{
+			// If this is one of them, don't try to add it.
+			if (!strcmp(fn, addedfolders[ii]))
+			{
+				folderadded = true;
+				break;
+			}
+		}
+
+		// If we've added this one, skip to the next one.
+		if (folderadded)
 		{
 			CONS_Alert(CONS_WARNING, M_GetText("Already processed %s, skipping\n"), fn);
 			continue;
@@ -3530,16 +3499,13 @@ static void Command_Addfolder(void)
 		// Disallow non-printing characters and semicolons.
 		for (i = 0; fn[i] != '\0'; i++)
 			if (!isprint(fn[i]) || fn[i] == ';')
-			{
-				AddedFilesClearList(&addedfolders);
 				return;
-			}
 
 		// Add file on your client directly if you aren't in a netgame.
 		if (!(netgame || multiplayer))
 		{
 			P_AddFolder(fn);
-			AddedFilesAdd(&addedfolders, fn);
+			addedfolders[numfoldersadded++] = fn;
 			continue;
 		}
 
@@ -3561,7 +3527,6 @@ static void Command_Addfolder(void)
 		if (numwadfiles >= MAX_WADFILES)
 		{
 			CONS_Alert(CONS_ERROR, M_GetText("Too many files loaded to add %s\n"), fn);
-			AddedFilesClearList(&addedfolders);
 			return;
 		}
 
@@ -3607,7 +3572,7 @@ static void Command_Addfolder(void)
 
 		Z_Free(fullpath);
 
-		AddedFilesAdd(&addedfolders, fn);
+		addedfolders[numfoldersadded++] = fn;
 
 		WRITESTRINGN(buf_p,p,240);
 
@@ -3966,12 +3931,18 @@ void ItemFinder_OnChange(void)
 	if (!cv_itemfinder.value)
 		return; // it's fine.
 
-	if (!M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata))
+	if (!M_SecretUnlocked(SECRET_ITEMFINDER))
 	{
 		CONS_Printf(M_GetText("You haven't earned this yet.\n"));
 		CV_StealthSetValue(&cv_itemfinder, 0);
 		return;
 	}
+	else if (netgame || multiplayer)
+	{
+		CONS_Printf(M_GetText("This only works in single player.\n"));
+		CV_StealthSetValue(&cv_itemfinder, 0);
+		return;
+	}
 }
 
 /** Deals with a pointlimit change by printing the change to the console.
@@ -4320,7 +4291,7 @@ void D_GameTypeChanged(INT32 lastgametype)
 
 static void Ringslinger_OnChange(void)
 {
-	if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !netgame && cv_ringslinger.value && !cv_debug)
+	if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && cv_ringslinger.value && !cv_debug)
 	{
 		CONS_Printf(M_GetText("You haven't earned this yet.\n"));
 		CV_StealthSetValue(&cv_ringslinger, 0);
@@ -4328,12 +4299,12 @@ static void Ringslinger_OnChange(void)
 	}
 
 	if (cv_ringslinger.value) // Only if it's been turned on
-		G_SetUsedCheats(false);
+		G_SetGameModified(multiplayer);
 }
 
 static void Gravity_OnChange(void)
 {
-	if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !netgame && !cv_debug
+	if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && !cv_debug
 		&& strcmp(cv_gravity.string, cv_gravity.defaultvalue))
 	{
 		CONS_Printf(M_GetText("You haven't earned this yet.\n"));
@@ -4349,7 +4320,7 @@ static void Gravity_OnChange(void)
 #endif
 
 	if (!CV_IsSetToDefault(&cv_gravity))
-		G_SetUsedCheats(false);
+		G_SetGameModified(multiplayer);
 	gravity = cv_gravity.value;
 }
 
@@ -4630,9 +4601,10 @@ void Command_ExitGame_f(void)
 	botskin = 0;
 	cv_debug = 0;
 	emeralds = 0;
-	automapactive = false;
 	memset(&luabanks, 0, sizeof(luabanks));
 
+	P_StopRumble(NULL);
+
 	if (dirmenu)
 		closefilemenu(true);
 
@@ -4666,7 +4638,7 @@ static void Fishcake_OnChange(void)
 	// so don't make modifiedgame always on!
 	if (cv_debug)
 	{
-		G_SetUsedCheats(false);
+		G_SetGameModified(multiplayer);
 	}
 
 	else if (cv_debug != cv_fishcake.value)
@@ -4683,11 +4655,11 @@ static void Fishcake_OnChange(void)
 static void Command_Isgamemodified_f(void)
 {
 	if (savemoddata)
-		CONS_Printf(M_GetText("modifiedgame is true, but you can save time data in this mod.\n"));
+		CONS_Printf(M_GetText("modifiedgame is true, but you can save emblem and time data in this mod.\n"));
 	else if (modifiedgame)
-		CONS_Printf(M_GetText("modifiedgame is true, time data can't be saved\n"));
+		CONS_Printf(M_GetText("modifiedgame is true, extras will not be unlocked\n"));
 	else
-		CONS_Printf(M_GetText("modifiedgame is false, you can save time data\n"));
+		CONS_Printf(M_GetText("modifiedgame is false, you can unlock extras\n"));
 }
 
 static void Command_Cheats_f(void)
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index 26bf4d5c6b..47f68a17e9 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -33,14 +33,10 @@ extern consvar_t cv_defaultskin2;
 
 extern consvar_t cv_seenames, cv_allowseenames;
 extern consvar_t cv_usemouse;
-extern consvar_t cv_usejoystick;
-extern consvar_t cv_usejoystick2;
-#ifdef LJOYSTICK
-extern consvar_t cv_joyport;
-extern consvar_t cv_joyport2;
-#endif
-extern consvar_t cv_joyscale;
-extern consvar_t cv_joyscale2;
+extern consvar_t cv_usegamepad[2];
+extern consvar_t cv_gamepad_scale[2];
+extern consvar_t cv_gamepad_rumble[2];
+extern consvar_t cv_gamepad_autopause;
 
 // splitscreen with second mouse
 extern consvar_t cv_mouse2port;
diff --git a/src/d_netfil.c b/src/d_netfil.c
index 3fef756812..edbef30bbf 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -498,7 +498,7 @@ INT32 CL_CheckFiles(void)
 		CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename);
 
 		// Check in already loaded files
-		for (j = mainwads; j < numwadfiles; j++)
+		for (j = mainwads; wadfiles[j]; j++)
 		{
 			nameonly(strcpy(wadfilename, wadfiles[j]->filename));
 			if (!stricmp(wadfilename, fileneeded[i].filename) &&
diff --git a/src/d_netfil.h b/src/d_netfil.h
index ecec976be8..f778a518ff 100644
--- a/src/d_netfil.h
+++ b/src/d_netfil.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/http-mserv.c b/src/http-mserv.c
index b7032e89a3..b0ef37fa16 100644
--- a/src/http-mserv.c
+++ b/src/http-mserv.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2023 by James R.
+// Copyright (C) 2020-2022 by James R.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -65,8 +65,6 @@ static I_mutex hms_api_mutex;
 
 static char *hms_server_token;
 
-static char hms_useragent[512];
-
 struct HMS_buffer
 {
 	CURL *curl;
@@ -83,22 +81,6 @@ Contact_error (void)
 	);
 }
 
-static void
-get_user_agent(char *buf, size_t len)
-{
-	if (snprintf(buf, len, "%s/%s (%s; %s; %i; %i) SRB2BASE/%i", SRB2APPLICATION, VERSIONSTRING, compbranch, comprevision,  MODID, MODVERSION, CODEBASE) < 0)
-		I_Error("http-mserv: get_user_agent failed");
-}
-
-static void
-init_user_agent_once(void)
-{
-	if (hms_useragent[0] != '\0')
-		return;
-	
-	get_user_agent(hms_useragent, 512);
-}
-
 static size_t
 HMS_on_read (char *s, size_t _1, size_t n, void *userdata)
 {
@@ -174,8 +156,6 @@ HMS_connect (const char *format, ...)
 	I_lock_mutex(&hms_api_mutex);
 #endif
 
-	init_user_agent_once();
-
 	seek = strlen(hms_api) + 1;/* + '/' */
 
 	va_start (ap, format);
@@ -216,18 +196,12 @@ HMS_connect (const char *format, ...)
 
 	curl_easy_setopt(curl, CURLOPT_URL, url);
 	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
-
-#ifndef NO_IPV6
-	if (M_CheckParm("-noipv6"))
-#endif
-		curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+	curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
 
 	curl_easy_setopt(curl, CURLOPT_TIMEOUT, cv_masterserver_timeout.value);
 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read);
 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer);
 
-	curl_easy_setopt(curl, CURLOPT_USERAGENT, hms_useragent);
-
 	curl_free(quack_token);
 	free(url);
 
diff --git a/src/i_addrinfo.c b/src/i_addrinfo.c
index 9efaff4dac..49aadf27d2 100644
--- a/src/i_addrinfo.c
+++ b/src/i_addrinfo.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2011-2023 by Sonic Team Junior.
+// Copyright (C) 2011-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_addrinfo.h b/src/i_addrinfo.h
index 79cfb05b25..592e693f4c 100644
--- a/src/i_addrinfo.h
+++ b/src/i_addrinfo.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2011-2023 by Sonic Team Junior.
+// Copyright (C) 2011-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_net.h b/src/i_net.h
index 12a07f183e..62b7528d59 100644
--- a/src/i_net.h
+++ b/src/i_net.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -109,17 +109,6 @@ extern boolean (*I_NetCanSend)(void);
 */
 extern void (*I_NetFreeNodenum)(INT32 nodenum);
 
-/**
-	\brief	split a string into address and port
-
-	\param	address	string to split
-
-	\param	port	double pointer to hold port component (optional)
-
-	\return	address component
-*/
-extern char *I_NetSplitAddress(char *address, char **port);
-
 /**	\brief	open a connection with specified address
 
 	\param	address	address to connect to
diff --git a/src/i_tcp.c b/src/i_tcp.c
index d95b381f4a..8838ba7257 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -340,14 +340,8 @@ static inline void I_UPnP_rem(const char *port, const char * servicetype)
 
 static const char *SOCK_AddrToStr(mysockaddr_t *sk)
 {
-	static char s[64]; // 255.255.255.255:65535 or
-	// [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+	static char s[64]; // 255.255.255.255:65535 or IPv6:65535
 #ifdef HAVE_NTOP
-#ifdef HAVE_IPV6
-	int v6 = (sk->any.sa_family == AF_INET6);
-#else
-	int v6 = 0;
-#endif
 	void *addr;
 
 	if(sk->any.sa_family == AF_INET)
@@ -361,21 +355,14 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk)
 
 	if(addr == NULL)
 		sprintf(s, "No address");
-	else if(inet_ntop(sk->any.sa_family, addr, &s[v6], sizeof (s) - v6) == NULL)
+	else if(inet_ntop(sk->any.sa_family, addr, s, sizeof (s)) == NULL)
 		sprintf(s, "Unknown family type, error #%u", errno);
 #ifdef HAVE_IPV6
-	else if(sk->any.sa_family == AF_INET6)
-	{
-		s[0] = '[';
-		strcat(s, "]");
-
-		if (sk->ip6.sin6_port != 0)
-			strcat(s, va(":%d", ntohs(sk->ip6.sin6_port)));
-	}
+	else if(sk->any.sa_family == AF_INET6 && sk->ip6.sin6_port != 0)
+		strcat(s, va(":%d", ntohs(sk->ip6.sin6_port)));
 #endif
 	else if(sk->any.sa_family == AF_INET  && sk->ip4.sin_port  != 0)
 		strcat(s, va(":%d", ntohs(sk->ip4.sin_port)));
-
 #else
 	if (sk->any.sa_family == AF_INET)
 	{
@@ -440,7 +427,7 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
 			&& (b->ip4.sin_port == 0 || (a->ip4.sin_port == b->ip4.sin_port));
 #ifdef HAVE_IPV6
 	else if (b->any.sa_family == AF_INET6)
-		return !memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, sizeof(b->ip6.sin6_addr))
+		return memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, sizeof(b->ip6.sin6_addr))
 			&& (b->ip6.sin6_port == 0 || (a->ip6.sin6_port == b->ip6.sin6_port));
 #endif
 	else
@@ -748,7 +735,8 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 	unsigned long trueval = true;
 #endif
 	mysockaddr_t straddr;
-	socklen_t len = sizeof(straddr);
+	struct sockaddr_in sin;
+	socklen_t len = sizeof(sin);
 
 	if (s == (SOCKET_TYPE)ERRSOCKET)
 		return (SOCKET_TYPE)ERRSOCKET;
@@ -766,12 +754,14 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 	}
 #endif
 
-	memcpy(&straddr, addr, addrlen);
+	straddr.any = *addr;
 	I_OutputMsg("Binding to %s\n", SOCK_AddrToStr(&straddr));
 
 	if (family == AF_INET)
 	{
-		if (straddr.ip4.sin_addr.s_addr == htonl(INADDR_ANY))
+		mysockaddr_t tmpaddr;
+		tmpaddr.any = *addr ;
+		if (tmpaddr.ip4.sin_addr.s_addr == htonl(INADDR_ANY))
 		{
 			opt = true;
 			opts = (socklen_t)sizeof(opt);
@@ -788,7 +778,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 #ifdef HAVE_IPV6
 	else if (family == AF_INET6)
 	{
-		if (memcmp(&straddr.ip6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL
+		if (memcmp(addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL
 		{
 			opt = true;
 			opts = (socklen_t)sizeof(opt);
@@ -798,7 +788,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 		// make it IPv6 ony
 		opt = true;
 		opts = (socklen_t)sizeof(opt);
-		if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, opts))
+		if (setsockopt(s, SOL_SOCKET, IPV6_V6ONLY, (char *)&opt, opts))
 		{
 			CONS_Alert(CONS_WARNING, M_GetText("Could not limit IPv6 bind\n")); // I do not care anymore
 		}
@@ -840,17 +830,10 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 			CONS_Printf(M_GetText("Network system buffer set to: %dKb\n"), opt>>10);
 	}
 
-	if (getsockname(s, &straddr.any, &len) == -1)
+	if (getsockname(s, (struct sockaddr *)&sin, &len) == -1)
 		CONS_Alert(CONS_WARNING, M_GetText("Failed to get port number\n"));
 	else
-	{
-		if (family == AF_INET)
-			current_port = (UINT16)ntohs(straddr.ip4.sin_port);
-#ifdef HAVE_IPV6
-		else if (family == AF_INET6)
-			current_port = (UINT16)ntohs(straddr.ip6.sin6_port);
-#endif
-	}
+		current_port = (UINT16)ntohs(sin.sin_port);
 
 	return s;
 }
@@ -861,7 +844,7 @@ static boolean UDP_Socket(void)
 	struct my_addrinfo *ai, *runp, hints;
 	int gaie;
 #ifdef HAVE_IPV6
-	const INT32 b_ipv6 = !M_CheckParm("-noipv6");
+	const INT32 b_ipv6 = M_CheckParm("-ipv6");
 #endif
 	const char *serv;
 
@@ -1173,7 +1156,6 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
 	SINT8 newnode = -1;
 	struct my_addrinfo *ai = NULL, *runp, hints;
 	int gaie;
-	size_t i;
 
 	 if (!port || !port[0])
 		port = DEFAULTPORT;
@@ -1201,24 +1183,13 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
 
 	while (runp != NULL)
 	{
-		// test ip address of server
-		for (i = 0; i < mysocketses; ++i)
+		// find ip of the server
+		if (sendto(mysockets[0], NULL, 0, 0, runp->ai_addr, runp->ai_addrlen) == 0)
 		{
-			/* sendto tests that there is a network to this
-				address */
-			if (runp->ai_addr->sa_family == myfamily[i] &&
-					sendto(mysockets[i], NULL, 0, 0,
-						runp->ai_addr, runp->ai_addrlen) == 0)
-			{
-				memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
-				break;
-			}
-		}
-
-		if (i < mysocketses)
-			runp = runp->ai_next;
-		else
+			memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
 			break;
+		}
+		runp = runp->ai_next;
 	}
 	I_freeaddrinfo(ai);
 	return newnode;
diff --git a/src/i_tcp.h b/src/i_tcp.h
index ae9983bf17..b6e5b92351 100644
--- a/src/i_tcp.h
+++ b/src/i_tcp.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/mserv.c b/src/mserv.c
index 62cda96e45..bff562c95f 100644
--- a/src/mserv.c
+++ b/src/mserv.c
@@ -1,8 +1,8 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
-// Copyright (C) 2020-2023 by James R.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by James R.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -62,7 +62,7 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = {
 };
 
 consvar_t cv_masterserver = CVAR_INIT ("masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange);
-consvar_t cv_servername = CVAR_INIT ("servername", "SRB2 server", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Update_parameters);
+consvar_t cv_servername = CVAR_INIT ("servername", "SRB2 server", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, NULL, Update_parameters);
 
 consvar_t cv_masterserver_update_rate = CVAR_INIT ("masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters);
 
@@ -97,8 +97,8 @@ void AddMServCommands(void)
 	CV_RegisterVar(&cv_masterserver_token);
 	CV_RegisterVar(&cv_servername);
 #ifdef MASTERSERVER
-	COM_AddCommand("listserv", Command_Listserv_f, 0);
-	COM_AddCommand("masterserver_update", Update_parameters, COM_LUA); // allows people to updates manually in case you were delisted by accident
+	COM_AddCommand("listserv", Command_Listserv_f);
+	COM_AddCommand("masterserver_update", Update_parameters); // allows people to updates manually in case you were delisted by accident
 #endif
 #endif
 }
diff --git a/src/mserv.h b/src/mserv.h
index 07253da856..23b26fbc54 100644
--- a/src/mserv.h
+++ b/src/mserv.h
@@ -1,8 +1,8 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
-// Copyright (C) 2020-2023 by James R.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 2020-2022 by James R.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -33,7 +33,7 @@ typedef union
 typedef struct
 {
 	msg_header_t header;
-	char ip[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
+	char ip[16];
 	char port[8];
 	char name[32];
 	INT32 room;
-- 
GitLab


From 77253157899492a2c7aab1967f7995b90c46fbbe Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Wed, 28 Dec 2022 16:12:05 +0100
Subject: [PATCH 246/518] Move snake minigame to its own files

---
 src/Sourcefile |   1 +
 src/d_clisrv.c | 525 +-----------------------------------------------
 src/snake.c    | 535 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/snake.h    |  20 ++
 4 files changed, 564 insertions(+), 517 deletions(-)
 create mode 100644 src/snake.c
 create mode 100644 src/snake.h

diff --git a/src/Sourcefile b/src/Sourcefile
index 7c53050005..c55752b095 100644
--- a/src/Sourcefile
+++ b/src/Sourcefile
@@ -84,6 +84,7 @@ lzf.c
 vid_copy.s
 b_bot.c
 u_list.c
+snake.c
 lua_script.c
 lua_baselib.c
 lua_mathlib.c
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 3091f33440..be10e4f9be 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -57,6 +57,7 @@
 // cl loading screen
 #include "v_video.h"
 #include "f_finale.h"
+#include "snake.h"
 #endif
 
 //
@@ -543,505 +544,7 @@ static cl_mode_t cl_mode = CL_SEARCHING;
 static UINT16 cl_lastcheckedfilecount = 0;	// used for full file list
 
 #ifndef NONET
-#define SNAKE_SPEED 5
-
-#define SNAKE_NUM_BLOCKS_X 20
-#define SNAKE_NUM_BLOCKS_Y 10
-#define SNAKE_BLOCK_SIZE 12
-#define SNAKE_BORDER_SIZE 12
-
-#define SNAKE_MAP_WIDTH  (SNAKE_NUM_BLOCKS_X * SNAKE_BLOCK_SIZE)
-#define SNAKE_MAP_HEIGHT (SNAKE_NUM_BLOCKS_Y * SNAKE_BLOCK_SIZE)
-
-#define SNAKE_LEFT_X ((BASEVIDWIDTH - SNAKE_MAP_WIDTH) / 2 - SNAKE_BORDER_SIZE)
-#define SNAKE_RIGHT_X (SNAKE_LEFT_X + SNAKE_MAP_WIDTH + SNAKE_BORDER_SIZE * 2 - 1)
-#define SNAKE_BOTTOM_Y (BASEVIDHEIGHT - 48)
-#define SNAKE_TOP_Y (SNAKE_BOTTOM_Y - SNAKE_MAP_HEIGHT - SNAKE_BORDER_SIZE * 2 + 1)
-
-enum snake_bonustype_s {
-	SNAKE_BONUS_NONE = 0,
-	SNAKE_BONUS_SLOW,
-	SNAKE_BONUS_FAST,
-	SNAKE_BONUS_GHOST,
-	SNAKE_BONUS_NUKE,
-	SNAKE_BONUS_SCISSORS,
-	SNAKE_BONUS_REVERSE,
-	SNAKE_BONUS_EGGMAN,
-	SNAKE_NUM_BONUSES,
-};
-
-static const char *snake_bonuspatches[] = {
-	NULL,
-	"DL_SLOW",
-	"TVSSC0",
-	"TVIVC0",
-	"TVARC0",
-	"DL_SCISSORS",
-	"TVRCC0",
-	"TVEGC0",
-};
-
-static const char *snake_backgrounds[] = {
-	"RVPUMICF",
-	"FRSTRCKF",
-	"TAR",
-	"MMFLRB4",
-	"RVDARKF1",
-	"RVZWALF1",
-	"RVZWALF4",
-	"RVZWALF5",
-	"RVZGRS02",
-	"RVZGRS04",
-};
-
-typedef struct snake_s
-{
-	boolean paused;
-	boolean pausepressed;
-	tic_t time;
-	tic_t nextupdate;
-	boolean gameover;
-	UINT8 background;
-
-	UINT16 snakelength;
-	enum snake_bonustype_s snakebonus;
-	tic_t snakebonustime;
-	UINT8 snakex[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
-	UINT8 snakey[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
-	UINT8 snakedir[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
-
-	UINT8 applex;
-	UINT8 appley;
-
-	enum snake_bonustype_s bonustype;
-	UINT8 bonusx;
-	UINT8 bonusy;
-} snake_t;
-
-static snake_t *snake = NULL;
-
-static void Snake_Initialise(void)
-{
-	if (!snake)
-		snake = malloc(sizeof(snake_t));
-
-	snake->paused = false;
-	snake->pausepressed = false;
-	snake->time = 0;
-	snake->nextupdate = SNAKE_SPEED;
-	snake->gameover = false;
-	snake->background = M_RandomKey(sizeof(snake_backgrounds) / sizeof(*snake_backgrounds));
-
-	snake->snakelength = 1;
-	snake->snakebonus = SNAKE_BONUS_NONE;
-	snake->snakex[0] = M_RandomKey(SNAKE_NUM_BLOCKS_X);
-	snake->snakey[0] = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
-	snake->snakedir[0] = 0;
-	snake->snakedir[1] = 0;
-
-	snake->applex = M_RandomKey(SNAKE_NUM_BLOCKS_X);
-	snake->appley = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
-
-	snake->bonustype = SNAKE_BONUS_NONE;
-}
-
-static UINT8 Snake_GetOppositeDir(UINT8 dir)
-{
-	if (dir == 1 || dir == 3)
-		return dir + 1;
-	else if (dir == 2 || dir == 4)
-		return dir - 1;
-	else
-		return 12 + 5 - dir;
-}
-
-static void Snake_FindFreeSlot(UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady)
-{
-	UINT8 x, y;
-	UINT16 i;
-
-	do
-	{
-		x = M_RandomKey(SNAKE_NUM_BLOCKS_X);
-		y = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
-
-		for (i = 0; i < snake->snakelength; i++)
-			if (x == snake->snakex[i] && y == snake->snakey[i])
-				break;
-	} while (i < snake->snakelength || (x == headx && y == heady)
-		|| (x == snake->applex && y == snake->appley)
-		|| (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy));
-
-	*freex = x;
-	*freey = y;
-}
-
-static void Snake_Handle(void)
-{
-	UINT8 x, y;
-	UINT8 oldx, oldy;
-	UINT16 i;
-	UINT16 joystate = 0;
-
-	// Handle retry
-	if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER]))
-	{
-		Snake_Initialise();
-		snake->pausepressed = true; // Avoid accidental pause on respawn
-	}
-
-	// Handle pause
-	if (G_PlayerInputDown(0, GC_PAUSE) || gamekeydown[KEY_ENTER])
-	{
-		if (!snake->pausepressed)
-			snake->paused = !snake->paused;
-		snake->pausepressed = true;
-	}
-	else
-		snake->pausepressed = false;
-
-	if (snake->paused)
-		return;
-
-	snake->time++;
-
-	x = snake->snakex[0];
-	y = snake->snakey[0];
-	oldx = snake->snakex[1];
-	oldy = snake->snakey[1];
-
-	// Update direction
-	if (G_PlayerInputDown(0, GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3)
-	{
-		if (snake->snakelength < 2 || x <= oldx)
-			snake->snakedir[0] = 1;
-	}
-	else if (G_PlayerInputDown(0, GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4)
-	{
-		if (snake->snakelength < 2 || x >= oldx)
-			snake->snakedir[0] = 2;
-	}
-	else if (G_PlayerInputDown(0, GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1)
-	{
-		if (snake->snakelength < 2 || y <= oldy)
-			snake->snakedir[0] = 3;
-	}
-	else if (G_PlayerInputDown(0, GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2)
-	{
-		if (snake->snakelength < 2 || y >= oldy)
-			snake->snakedir[0] = 4;
-	}
-
-	if (snake->snakebonustime)
-	{
-		snake->snakebonustime--;
-		if (!snake->snakebonustime)
-			snake->snakebonus = SNAKE_BONUS_NONE;
-	}
-
-	snake->nextupdate--;
-	if (snake->nextupdate)
-		return;
-	if (snake->snakebonus == SNAKE_BONUS_SLOW)
-		snake->nextupdate = SNAKE_SPEED * 2;
-	else if (snake->snakebonus == SNAKE_BONUS_FAST)
-		snake->nextupdate = SNAKE_SPEED * 2 / 3;
-	else
-		snake->nextupdate = SNAKE_SPEED;
-
-	if (snake->gameover)
-		return;
-
-	// Find new position
-	switch (snake->snakedir[0])
-	{
-		case 1:
-			if (x > 0)
-				x--;
-			else
-				snake->gameover = true;
-			break;
-		case 2:
-			if (x < SNAKE_NUM_BLOCKS_X - 1)
-				x++;
-			else
-				snake->gameover = true;
-			break;
-		case 3:
-			if (y > 0)
-				y--;
-			else
-				snake->gameover = true;
-			break;
-		case 4:
-			if (y < SNAKE_NUM_BLOCKS_Y - 1)
-				y++;
-			else
-				snake->gameover = true;
-			break;
-	}
-
-	// Check collision with snake
-	if (snake->snakebonus != SNAKE_BONUS_GHOST)
-		for (i = 1; i < snake->snakelength - 1; i++)
-			if (x == snake->snakex[i] && y == snake->snakey[i])
-			{
-				if (snake->snakebonus == SNAKE_BONUS_SCISSORS)
-				{
-					snake->snakebonus = SNAKE_BONUS_NONE;
-					snake->snakelength = i;
-					S_StartSound(NULL, sfx_adderr);
-				}
-				else
-					snake->gameover = true;
-			}
-
-	if (snake->gameover)
-	{
-		S_StartSound(NULL, sfx_lose);
-		return;
-	}
-
-	// Check collision with apple
-	if (x == snake->applex && y == snake->appley)
-	{
-		if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y)
-		{
-			snake->snakelength++;
-			snake->snakex  [snake->snakelength - 1] = snake->snakex  [snake->snakelength - 2];
-			snake->snakey  [snake->snakelength - 1] = snake->snakey  [snake->snakelength - 2];
-			snake->snakedir[snake->snakelength - 1] = snake->snakedir[snake->snakelength - 2];
-		}
-
-		// Spawn new apple
-		Snake_FindFreeSlot(&snake->applex, &snake->appley, x, y);
-
-		// Spawn new bonus
-		if (!(snake->snakelength % 5))
-		{
-			do
-			{
-				snake->bonustype = M_RandomKey(SNAKE_NUM_BONUSES - 1) + 1;
-			} while (snake->snakelength > SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y * 3 / 4
-				&& (snake->bonustype == SNAKE_BONUS_EGGMAN || snake->bonustype == SNAKE_BONUS_FAST || snake->bonustype == SNAKE_BONUS_REVERSE));
-
-			Snake_FindFreeSlot(&snake->bonusx, &snake->bonusy, x, y);
-		}
-
-		S_StartSound(NULL, sfx_s3k6b);
-	}
-
-	if (snake->snakelength > 1 && snake->snakedir[0])
-	{
-		UINT8 dir = snake->snakedir[0];
-
-		oldx = snake->snakex[1];
-		oldy = snake->snakey[1];
-
-		// Move
-		for (i = snake->snakelength - 1; i > 0; i--)
-		{
-			snake->snakex[i] = snake->snakex[i - 1];
-			snake->snakey[i] = snake->snakey[i - 1];
-			snake->snakedir[i] = snake->snakedir[i - 1];
-		}
-
-		// Handle corners
-		if      (x < oldx && dir == 3)
-			dir = 5;
-		else if (x > oldx && dir == 3)
-			dir = 6;
-		else if (x < oldx && dir == 4)
-			dir = 7;
-		else if (x > oldx && dir == 4)
-			dir = 8;
-		else if (y < oldy && dir == 1)
-			dir = 9;
-		else if (y < oldy && dir == 2)
-			dir = 10;
-		else if (y > oldy && dir == 1)
-			dir = 11;
-		else if (y > oldy && dir == 2)
-			dir = 12;
-		snake->snakedir[1] = dir;
-	}
-
-	snake->snakex[0] = x;
-	snake->snakey[0] = y;
-
-	// Check collision with bonus
-	if (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)
-	{
-		S_StartSound(NULL, sfx_ncchip);
-
-		switch (snake->bonustype)
-		{
-		case SNAKE_BONUS_SLOW:
-			snake->snakebonus = SNAKE_BONUS_SLOW;
-			snake->snakebonustime = 20 * TICRATE;
-			break;
-		case SNAKE_BONUS_FAST:
-			snake->snakebonus = SNAKE_BONUS_FAST;
-			snake->snakebonustime = 20 * TICRATE;
-			break;
-		case SNAKE_BONUS_GHOST:
-			snake->snakebonus = SNAKE_BONUS_GHOST;
-			snake->snakebonustime = 10 * TICRATE;
-			break;
-		case SNAKE_BONUS_NUKE:
-			for (i = 0; i < snake->snakelength; i++)
-			{
-				snake->snakex  [i] = snake->snakex  [0];
-				snake->snakey  [i] = snake->snakey  [0];
-				snake->snakedir[i] = snake->snakedir[0];
-			}
-
-			S_StartSound(NULL, sfx_bkpoof);
-			break;
-		case SNAKE_BONUS_SCISSORS:
-			snake->snakebonus = SNAKE_BONUS_SCISSORS;
-			snake->snakebonustime = 60 * TICRATE;
-			break;
-		case SNAKE_BONUS_REVERSE:
-			for (i = 0; i < (snake->snakelength + 1) / 2; i++)
-			{
-				UINT16 i2 = snake->snakelength - 1 - i;
-				UINT8 tmpx   = snake->snakex  [i];
-				UINT8 tmpy   = snake->snakey  [i];
-				UINT8 tmpdir = snake->snakedir[i];
-
-				// Swap first segment with last segment
-				snake->snakex  [i] = snake->snakex  [i2];
-				snake->snakey  [i] = snake->snakey  [i2];
-				snake->snakedir[i] = Snake_GetOppositeDir(snake->snakedir[i2]);
-				snake->snakex  [i2] = tmpx;
-				snake->snakey  [i2] = tmpy;
-				snake->snakedir[i2] = Snake_GetOppositeDir(tmpdir);
-			}
-
-			snake->snakedir[0] = 0;
-
-			S_StartSound(NULL, sfx_gravch);
-			break;
-		default:
-			if (snake->snakebonus != SNAKE_BONUS_GHOST)
-			{
-				snake->gameover = true;
-				S_StartSound(NULL, sfx_lose);
-			}
-		}
-
-		snake->bonustype = SNAKE_BONUS_NONE;
-	}
-}
-
-static void Snake_Draw(void)
-{
-	INT16 i;
-
-	// Background
-	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
-
-	V_DrawFlatFill(
-		SNAKE_LEFT_X + SNAKE_BORDER_SIZE,
-		SNAKE_TOP_Y  + SNAKE_BORDER_SIZE,
-		SNAKE_MAP_WIDTH,
-		SNAKE_MAP_HEIGHT,
-		W_GetNumForName(snake_backgrounds[snake->background])
-	);
-
-	// Borders
-	V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Top
-	V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_TOP_Y, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Right
-	V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE, SNAKE_TOP_Y + SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Bottom
-	V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y + SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Left
-
-	// Apple
-	V_DrawFixedPatch(
-		(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->applex * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
-		(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->appley * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
-		FRACUNIT / 4,
-		0,
-		W_CachePatchLongName("DL_APPLE", PU_HUDGFX),
-		NULL
-	);
-
-	// Bonus
-	if (snake->bonustype != SNAKE_BONUS_NONE)
-		V_DrawFixedPatch(
-			(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->bonusx * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2    ) * FRACUNIT,
-			(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->bonusy * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 + 4) * FRACUNIT,
-			FRACUNIT / 2,
-			0,
-			W_CachePatchLongName(snake_bonuspatches[snake->bonustype], PU_HUDGFX),
-			NULL
-		);
-
-	// Snake
-	if (!snake->gameover || snake->time % 8 < 8 / 2) // Blink if game over
-	{
-		for (i = snake->snakelength - 1; i >= 0; i--)
-		{
-			const char *patchname;
-			UINT8 dir = snake->snakedir[i];
-
-			if (i == 0) // Head
-			{
-				switch (dir)
-				{
-					case  1: patchname = "DL_SNAKEHEAD_L"; break;
-					case  2: patchname = "DL_SNAKEHEAD_R"; break;
-					case  3: patchname = "DL_SNAKEHEAD_T"; break;
-					case  4: patchname = "DL_SNAKEHEAD_B"; break;
-					default: patchname = "DL_SNAKEHEAD_M";
-				}
-			}
-			else // Body
-			{
-				switch (dir)
-				{
-					case  1: patchname = "DL_SNAKEBODY_L"; break;
-					case  2: patchname = "DL_SNAKEBODY_R"; break;
-					case  3: patchname = "DL_SNAKEBODY_T"; break;
-					case  4: patchname = "DL_SNAKEBODY_B"; break;
-					case  5: patchname = "DL_SNAKEBODY_LT"; break;
-					case  6: patchname = "DL_SNAKEBODY_RT"; break;
-					case  7: patchname = "DL_SNAKEBODY_LB"; break;
-					case  8: patchname = "DL_SNAKEBODY_RB"; break;
-					case  9: patchname = "DL_SNAKEBODY_TL"; break;
-					case 10: patchname = "DL_SNAKEBODY_TR"; break;
-					case 11: patchname = "DL_SNAKEBODY_BL"; break;
-					case 12: patchname = "DL_SNAKEBODY_BR"; break;
-					default: patchname = "DL_SNAKEBODY_B";
-				}
-			}
-
-			V_DrawFixedPatch(
-				(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->snakex[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
-				(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->snakey[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
-				i == 0 && dir == 0 ? FRACUNIT / 5 : FRACUNIT / 2,
-				snake->snakebonus == SNAKE_BONUS_GHOST ? V_TRANSLUCENT : 0,
-				W_CachePatchLongName(patchname, PU_HUDGFX),
-				NULL
-			);
-		}
-	}
-
-	// Length
-	V_DrawString(SNAKE_RIGHT_X + 4, SNAKE_TOP_Y, V_MONOSPACE, va("%u", snake->snakelength));
-
-	// Bonus
-	if (snake->snakebonus != SNAKE_BONUS_NONE
-	&& (snake->snakebonustime >= 3 * TICRATE || snake->time % 4 < 4 / 2))
-		V_DrawFixedPatch(
-			(SNAKE_RIGHT_X + 10) * FRACUNIT,
-			(SNAKE_TOP_Y + 24) * FRACUNIT,
-			FRACUNIT / 2,
-			0,
-			W_CachePatchLongName(snake_bonuspatches[snake->snakebonus], PU_HUDGFX),
-			NULL
-		);
-}
+static void *snake = NULL;
 
 static void CL_DrawConnectionStatusBox(void)
 {
@@ -1167,7 +670,7 @@ static inline void CL_DrawConnectionStatus(void)
 			char *filename;
 
 			if (snake)
-				Snake_Draw();
+				Snake_Draw(snake);
 
 			// Draw the bottom box.
 			CL_DrawConnectionStatusBox();
@@ -1213,7 +716,7 @@ static inline void CL_DrawConnectionStatus(void)
 		else
 		{
 			if (snake)
-				Snake_Draw();
+				Snake_Draw(snake);
 
 			CL_DrawConnectionStatusBox();
 			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP,
@@ -1950,7 +1453,7 @@ static void M_ConfirmConnect(event_t *ev)
 				if (CL_SendFileRequest())
 				{
 					cl_mode = CL_DOWNLOADFILES;
-					Snake_Initialise();
+					Snake_Allocate(&snake);
 				}
 			}
 			else
@@ -2300,13 +1803,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 			if (waitmore)
 				break; // exit the case
 
-#ifndef NONET
-			if (snake)
-			{
-				free(snake);
-				snake = NULL;
-			}
-#endif
+			Snake_Free(&snake);
 
 			cl_mode = CL_LOADFILES;
 			break;
@@ -2401,13 +1898,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 			CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
 			M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
 
-#ifndef NONET
-			if (snake)
-			{
-				free(snake);
-				snake = NULL;
-			}
-#endif
+			Snake_Free(&snake);
 
 			D_QuitNetGame();
 			CL_Reset();
@@ -2417,7 +1908,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 		}
 #ifndef NONET
 		else if (cl_mode == CL_DOWNLOADFILES && snake)
-			Snake_Handle();
+			Snake_Update(snake);
 #endif
 
 		if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME))
diff --git a/src/snake.c b/src/snake.c
new file mode 100644
index 0000000000..2c80adeb9f
--- /dev/null
+++ b/src/snake.c
@@ -0,0 +1,535 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2023-2023 by Louis-Antoine de Moulins de Rochefort.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  snake.c
+/// \brief Snake minigame for the download screen.
+
+#include "snake.h"
+#include "g_input.h"
+#include "m_random.h"
+#include "s_sound.h"
+#include "screen.h"
+#include "v_video.h"
+#include "w_wad.h"
+#include "z_zone.h"
+
+#define SNAKE_SPEED 5
+
+#define SNAKE_NUM_BLOCKS_X 20
+#define SNAKE_NUM_BLOCKS_Y 10
+#define SNAKE_BLOCK_SIZE 12
+#define SNAKE_BORDER_SIZE 12
+
+#define SNAKE_MAP_WIDTH  (SNAKE_NUM_BLOCKS_X * SNAKE_BLOCK_SIZE)
+#define SNAKE_MAP_HEIGHT (SNAKE_NUM_BLOCKS_Y * SNAKE_BLOCK_SIZE)
+
+#define SNAKE_LEFT_X ((BASEVIDWIDTH - SNAKE_MAP_WIDTH) / 2 - SNAKE_BORDER_SIZE)
+#define SNAKE_RIGHT_X (SNAKE_LEFT_X + SNAKE_MAP_WIDTH + SNAKE_BORDER_SIZE * 2 - 1)
+#define SNAKE_BOTTOM_Y (BASEVIDHEIGHT - 48)
+#define SNAKE_TOP_Y (SNAKE_BOTTOM_Y - SNAKE_MAP_HEIGHT - SNAKE_BORDER_SIZE * 2 + 1)
+
+enum snake_bonustype_s {
+	SNAKE_BONUS_NONE = 0,
+	SNAKE_BONUS_SLOW,
+	SNAKE_BONUS_FAST,
+	SNAKE_BONUS_GHOST,
+	SNAKE_BONUS_NUKE,
+	SNAKE_BONUS_SCISSORS,
+	SNAKE_BONUS_REVERSE,
+	SNAKE_BONUS_EGGMAN,
+	SNAKE_NUM_BONUSES,
+};
+
+typedef struct snake_s
+{
+	boolean paused;
+	boolean pausepressed;
+	tic_t time;
+	tic_t nextupdate;
+	boolean gameover;
+	UINT8 background;
+
+	UINT16 snakelength;
+	enum snake_bonustype_s snakebonus;
+	tic_t snakebonustime;
+	UINT8 snakex[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
+	UINT8 snakey[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
+	UINT8 snakedir[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
+
+	UINT8 applex;
+	UINT8 appley;
+
+	enum snake_bonustype_s bonustype;
+	UINT8 bonusx;
+	UINT8 bonusy;
+} snake_t;
+
+static const char *snake_bonuspatches[] = {
+	NULL,
+	"DL_SLOW",
+	"TVSSC0",
+	"TVIVC0",
+	"TVARC0",
+	"DL_SCISSORS",
+	"TVRCC0",
+	"TVEGC0",
+};
+
+static const char *snake_backgrounds[] = {
+	"RVPUMICF",
+	"FRSTRCKF",
+	"TAR",
+	"MMFLRB4",
+	"RVDARKF1",
+	"RVZWALF1",
+	"RVZWALF4",
+	"RVZWALF5",
+	"RVZGRS02",
+	"RVZGRS04",
+};
+
+static void Snake_Initialise(snake_t *snake)
+{
+	snake->paused = false;
+	snake->pausepressed = false;
+	snake->time = 0;
+	snake->nextupdate = SNAKE_SPEED;
+	snake->gameover = false;
+	snake->background = M_RandomKey(sizeof(snake_backgrounds) / sizeof(*snake_backgrounds));
+
+	snake->snakelength = 1;
+	snake->snakebonus = SNAKE_BONUS_NONE;
+	snake->snakex[0] = M_RandomKey(SNAKE_NUM_BLOCKS_X);
+	snake->snakey[0] = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
+	snake->snakedir[0] = 0;
+	snake->snakedir[1] = 0;
+
+	snake->applex = M_RandomKey(SNAKE_NUM_BLOCKS_X);
+	snake->appley = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
+
+	snake->bonustype = SNAKE_BONUS_NONE;
+}
+
+static UINT8 Snake_GetOppositeDir(UINT8 dir)
+{
+	if (dir == 1 || dir == 3)
+		return dir + 1;
+	else if (dir == 2 || dir == 4)
+		return dir - 1;
+	else
+		return 12 + 5 - dir;
+}
+
+static void Snake_FindFreeSlot(snake_t *snake, UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady)
+{
+	UINT8 x, y;
+	UINT16 i;
+
+	do
+	{
+		x = M_RandomKey(SNAKE_NUM_BLOCKS_X);
+		y = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
+
+		for (i = 0; i < snake->snakelength; i++)
+			if (x == snake->snakex[i] && y == snake->snakey[i])
+				break;
+	} while (i < snake->snakelength || (x == headx && y == heady)
+		|| (x == snake->applex && y == snake->appley)
+		|| (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy));
+
+	*freex = x;
+	*freey = y;
+}
+
+void Snake_Allocate(void **opaque)
+{
+	if (*opaque)
+		Snake_Free(opaque);
+	*opaque = malloc(sizeof(snake_t));
+	Snake_Initialise(*opaque);
+}
+
+void Snake_Update(void *opaque)
+{
+	UINT8 x, y;
+	UINT8 oldx, oldy;
+	UINT16 i;
+	UINT16 joystate = 0;
+
+	snake_t *snake = opaque;
+
+	// Handle retry
+	if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER]))
+	{
+		Snake_Initialise(snake);
+		snake->pausepressed = true; // Avoid accidental pause on respawn
+	}
+
+	// Handle pause
+	if (G_PlayerInputDown(0, GC_PAUSE) || gamekeydown[KEY_ENTER])
+	{
+		if (!snake->pausepressed)
+			snake->paused = !snake->paused;
+		snake->pausepressed = true;
+	}
+	else
+		snake->pausepressed = false;
+
+	if (snake->paused)
+		return;
+
+	snake->time++;
+
+	x = snake->snakex[0];
+	y = snake->snakey[0];
+	oldx = snake->snakex[1];
+	oldy = snake->snakey[1];
+
+	// Update direction
+	if (G_PlayerInputDown(0, GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3)
+	{
+		if (snake->snakelength < 2 || x <= oldx)
+			snake->snakedir[0] = 1;
+	}
+	else if (G_PlayerInputDown(0, GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4)
+	{
+		if (snake->snakelength < 2 || x >= oldx)
+			snake->snakedir[0] = 2;
+	}
+	else if (G_PlayerInputDown(0, GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1)
+	{
+		if (snake->snakelength < 2 || y <= oldy)
+			snake->snakedir[0] = 3;
+	}
+	else if (G_PlayerInputDown(0, GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2)
+	{
+		if (snake->snakelength < 2 || y >= oldy)
+			snake->snakedir[0] = 4;
+	}
+
+	if (snake->snakebonustime)
+	{
+		snake->snakebonustime--;
+		if (!snake->snakebonustime)
+			snake->snakebonus = SNAKE_BONUS_NONE;
+	}
+
+	snake->nextupdate--;
+	if (snake->nextupdate)
+		return;
+	if (snake->snakebonus == SNAKE_BONUS_SLOW)
+		snake->nextupdate = SNAKE_SPEED * 2;
+	else if (snake->snakebonus == SNAKE_BONUS_FAST)
+		snake->nextupdate = SNAKE_SPEED * 2 / 3;
+	else
+		snake->nextupdate = SNAKE_SPEED;
+
+	if (snake->gameover)
+		return;
+
+	// Find new position
+	switch (snake->snakedir[0])
+	{
+		case 1:
+			if (x > 0)
+				x--;
+			else
+				snake->gameover = true;
+			break;
+		case 2:
+			if (x < SNAKE_NUM_BLOCKS_X - 1)
+				x++;
+			else
+				snake->gameover = true;
+			break;
+		case 3:
+			if (y > 0)
+				y--;
+			else
+				snake->gameover = true;
+			break;
+		case 4:
+			if (y < SNAKE_NUM_BLOCKS_Y - 1)
+				y++;
+			else
+				snake->gameover = true;
+			break;
+	}
+
+	// Check collision with snake
+	if (snake->snakebonus != SNAKE_BONUS_GHOST)
+		for (i = 1; i < snake->snakelength - 1; i++)
+			if (x == snake->snakex[i] && y == snake->snakey[i])
+			{
+				if (snake->snakebonus == SNAKE_BONUS_SCISSORS)
+				{
+					snake->snakebonus = SNAKE_BONUS_NONE;
+					snake->snakelength = i;
+					S_StartSound(NULL, sfx_adderr);
+				}
+				else
+					snake->gameover = true;
+			}
+
+	if (snake->gameover)
+	{
+		S_StartSound(NULL, sfx_lose);
+		return;
+	}
+
+	// Check collision with apple
+	if (x == snake->applex && y == snake->appley)
+	{
+		if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y)
+		{
+			snake->snakelength++;
+			snake->snakex  [snake->snakelength - 1] = snake->snakex  [snake->snakelength - 2];
+			snake->snakey  [snake->snakelength - 1] = snake->snakey  [snake->snakelength - 2];
+			snake->snakedir[snake->snakelength - 1] = snake->snakedir[snake->snakelength - 2];
+		}
+
+		// Spawn new apple
+		Snake_FindFreeSlot(snake, &snake->applex, &snake->appley, x, y);
+
+		// Spawn new bonus
+		if (!(snake->snakelength % 5))
+		{
+			do
+			{
+				snake->bonustype = M_RandomKey(SNAKE_NUM_BONUSES - 1) + 1;
+			} while (snake->snakelength > SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y * 3 / 4
+				&& (snake->bonustype == SNAKE_BONUS_EGGMAN || snake->bonustype == SNAKE_BONUS_FAST || snake->bonustype == SNAKE_BONUS_REVERSE));
+
+			Snake_FindFreeSlot(snake, &snake->bonusx, &snake->bonusy, x, y);
+		}
+
+		S_StartSound(NULL, sfx_s3k6b);
+	}
+
+	if (snake->snakelength > 1 && snake->snakedir[0])
+	{
+		UINT8 dir = snake->snakedir[0];
+
+		oldx = snake->snakex[1];
+		oldy = snake->snakey[1];
+
+		// Move
+		for (i = snake->snakelength - 1; i > 0; i--)
+		{
+			snake->snakex[i] = snake->snakex[i - 1];
+			snake->snakey[i] = snake->snakey[i - 1];
+			snake->snakedir[i] = snake->snakedir[i - 1];
+		}
+
+		// Handle corners
+		if      (x < oldx && dir == 3)
+			dir = 5;
+		else if (x > oldx && dir == 3)
+			dir = 6;
+		else if (x < oldx && dir == 4)
+			dir = 7;
+		else if (x > oldx && dir == 4)
+			dir = 8;
+		else if (y < oldy && dir == 1)
+			dir = 9;
+		else if (y < oldy && dir == 2)
+			dir = 10;
+		else if (y > oldy && dir == 1)
+			dir = 11;
+		else if (y > oldy && dir == 2)
+			dir = 12;
+		snake->snakedir[1] = dir;
+	}
+
+	snake->snakex[0] = x;
+	snake->snakey[0] = y;
+
+	// Check collision with bonus
+	if (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)
+	{
+		S_StartSound(NULL, sfx_ncchip);
+
+		switch (snake->bonustype)
+		{
+		case SNAKE_BONUS_SLOW:
+			snake->snakebonus = SNAKE_BONUS_SLOW;
+			snake->snakebonustime = 20 * TICRATE;
+			break;
+		case SNAKE_BONUS_FAST:
+			snake->snakebonus = SNAKE_BONUS_FAST;
+			snake->snakebonustime = 20 * TICRATE;
+			break;
+		case SNAKE_BONUS_GHOST:
+			snake->snakebonus = SNAKE_BONUS_GHOST;
+			snake->snakebonustime = 10 * TICRATE;
+			break;
+		case SNAKE_BONUS_NUKE:
+			for (i = 0; i < snake->snakelength; i++)
+			{
+				snake->snakex  [i] = snake->snakex  [0];
+				snake->snakey  [i] = snake->snakey  [0];
+				snake->snakedir[i] = snake->snakedir[0];
+			}
+
+			S_StartSound(NULL, sfx_bkpoof);
+			break;
+		case SNAKE_BONUS_SCISSORS:
+			snake->snakebonus = SNAKE_BONUS_SCISSORS;
+			snake->snakebonustime = 60 * TICRATE;
+			break;
+		case SNAKE_BONUS_REVERSE:
+			for (i = 0; i < (snake->snakelength + 1) / 2; i++)
+			{
+				UINT16 i2 = snake->snakelength - 1 - i;
+				UINT8 tmpx   = snake->snakex  [i];
+				UINT8 tmpy   = snake->snakey  [i];
+				UINT8 tmpdir = snake->snakedir[i];
+
+				// Swap first segment with last segment
+				snake->snakex  [i] = snake->snakex  [i2];
+				snake->snakey  [i] = snake->snakey  [i2];
+				snake->snakedir[i] = Snake_GetOppositeDir(snake->snakedir[i2]);
+				snake->snakex  [i2] = tmpx;
+				snake->snakey  [i2] = tmpy;
+				snake->snakedir[i2] = Snake_GetOppositeDir(tmpdir);
+			}
+
+			snake->snakedir[0] = 0;
+
+			S_StartSound(NULL, sfx_gravch);
+			break;
+		default:
+			if (snake->snakebonus != SNAKE_BONUS_GHOST)
+			{
+				snake->gameover = true;
+				S_StartSound(NULL, sfx_lose);
+			}
+		}
+
+		snake->bonustype = SNAKE_BONUS_NONE;
+	}
+}
+
+void Snake_Draw(void *opaque)
+{
+	INT16 i;
+
+	snake_t *snake = opaque;
+
+	// Background
+	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
+
+	V_DrawFlatFill(
+		SNAKE_LEFT_X + SNAKE_BORDER_SIZE,
+		SNAKE_TOP_Y  + SNAKE_BORDER_SIZE,
+		SNAKE_MAP_WIDTH,
+		SNAKE_MAP_HEIGHT,
+		W_GetNumForName(snake_backgrounds[snake->background])
+	);
+
+	// Borders
+	V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Top
+	V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_TOP_Y, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Right
+	V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE, SNAKE_TOP_Y + SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Bottom
+	V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y + SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Left
+
+	// Apple
+	V_DrawFixedPatch(
+		(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->applex * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
+		(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->appley * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
+		FRACUNIT / 4,
+		0,
+		W_CachePatchLongName("DL_APPLE", PU_HUDGFX),
+		NULL
+	);
+
+	// Bonus
+	if (snake->bonustype != SNAKE_BONUS_NONE)
+		V_DrawFixedPatch(
+			(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->bonusx * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2    ) * FRACUNIT,
+			(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->bonusy * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 + 4) * FRACUNIT,
+			FRACUNIT / 2,
+			0,
+			W_CachePatchLongName(snake_bonuspatches[snake->bonustype], PU_HUDGFX),
+			NULL
+		);
+
+	// Snake
+	if (!snake->gameover || snake->time % 8 < 8 / 2) // Blink if game over
+	{
+		for (i = snake->snakelength - 1; i >= 0; i--)
+		{
+			const char *patchname;
+			UINT8 dir = snake->snakedir[i];
+
+			if (i == 0) // Head
+			{
+				switch (dir)
+				{
+					case  1: patchname = "DL_SNAKEHEAD_L"; break;
+					case  2: patchname = "DL_SNAKEHEAD_R"; break;
+					case  3: patchname = "DL_SNAKEHEAD_T"; break;
+					case  4: patchname = "DL_SNAKEHEAD_B"; break;
+					default: patchname = "DL_SNAKEHEAD_M";
+				}
+			}
+			else // Body
+			{
+				switch (dir)
+				{
+					case  1: patchname = "DL_SNAKEBODY_L"; break;
+					case  2: patchname = "DL_SNAKEBODY_R"; break;
+					case  3: patchname = "DL_SNAKEBODY_T"; break;
+					case  4: patchname = "DL_SNAKEBODY_B"; break;
+					case  5: patchname = "DL_SNAKEBODY_LT"; break;
+					case  6: patchname = "DL_SNAKEBODY_RT"; break;
+					case  7: patchname = "DL_SNAKEBODY_LB"; break;
+					case  8: patchname = "DL_SNAKEBODY_RB"; break;
+					case  9: patchname = "DL_SNAKEBODY_TL"; break;
+					case 10: patchname = "DL_SNAKEBODY_TR"; break;
+					case 11: patchname = "DL_SNAKEBODY_BL"; break;
+					case 12: patchname = "DL_SNAKEBODY_BR"; break;
+					default: patchname = "DL_SNAKEBODY_B";
+				}
+			}
+
+			V_DrawFixedPatch(
+				(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->snakex[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
+				(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->snakey[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
+				i == 0 && dir == 0 ? FRACUNIT / 5 : FRACUNIT / 2,
+				snake->snakebonus == SNAKE_BONUS_GHOST ? V_TRANSLUCENT : 0,
+				W_CachePatchLongName(patchname, PU_HUDGFX),
+				NULL
+			);
+		}
+	}
+
+	// Length
+	V_DrawString(SNAKE_RIGHT_X + 4, SNAKE_TOP_Y, V_MONOSPACE, va("%u", snake->snakelength));
+
+	// Bonus
+	if (snake->snakebonus != SNAKE_BONUS_NONE
+	&& (snake->snakebonustime >= 3 * TICRATE || snake->time % 4 < 4 / 2))
+		V_DrawFixedPatch(
+			(SNAKE_RIGHT_X + 10) * FRACUNIT,
+			(SNAKE_TOP_Y + 24) * FRACUNIT,
+			FRACUNIT / 2,
+			0,
+			W_CachePatchLongName(snake_bonuspatches[snake->snakebonus], PU_HUDGFX),
+			NULL
+		);
+}
+
+void Snake_Free(void **opaque)
+{
+	if (*opaque)
+	{
+		free(opaque);
+		*opaque = NULL;
+	}
+}
diff --git a/src/snake.h b/src/snake.h
new file mode 100644
index 0000000000..a3106bb0f0
--- /dev/null
+++ b/src/snake.h
@@ -0,0 +1,20 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2023-2023 by Louis-Antoine de Moulins de Rochefort.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  snake.h
+/// \brief Snake minigame for the download screen.
+
+#ifndef __SNAKE__
+#define __SNAKE__
+
+void Snake_Allocate(void **opaque);
+void Snake_Update(void *opaque);
+void Snake_Draw(void *opaque);
+void Snake_Free(void **opaque);
+
+#endif
-- 
GitLab


From 60f150a826e608d3fe57eaa67d44b160ecbc198e Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Wed, 28 Dec 2022 18:50:00 +0100
Subject: [PATCH 247/518] Remove snake_ prefix

---
 src/snake.c | 206 ++++++++++++++++++++++++++--------------------------
 1 file changed, 103 insertions(+), 103 deletions(-)

diff --git a/src/snake.c b/src/snake.c
index 2c80adeb9f..21e79401df 100644
--- a/src/snake.c
+++ b/src/snake.c
@@ -18,31 +18,31 @@
 #include "w_wad.h"
 #include "z_zone.h"
 
-#define SNAKE_SPEED 5
-
-#define SNAKE_NUM_BLOCKS_X 20
-#define SNAKE_NUM_BLOCKS_Y 10
-#define SNAKE_BLOCK_SIZE 12
-#define SNAKE_BORDER_SIZE 12
-
-#define SNAKE_MAP_WIDTH  (SNAKE_NUM_BLOCKS_X * SNAKE_BLOCK_SIZE)
-#define SNAKE_MAP_HEIGHT (SNAKE_NUM_BLOCKS_Y * SNAKE_BLOCK_SIZE)
-
-#define SNAKE_LEFT_X ((BASEVIDWIDTH - SNAKE_MAP_WIDTH) / 2 - SNAKE_BORDER_SIZE)
-#define SNAKE_RIGHT_X (SNAKE_LEFT_X + SNAKE_MAP_WIDTH + SNAKE_BORDER_SIZE * 2 - 1)
-#define SNAKE_BOTTOM_Y (BASEVIDHEIGHT - 48)
-#define SNAKE_TOP_Y (SNAKE_BOTTOM_Y - SNAKE_MAP_HEIGHT - SNAKE_BORDER_SIZE * 2 + 1)
-
-enum snake_bonustype_s {
-	SNAKE_BONUS_NONE = 0,
-	SNAKE_BONUS_SLOW,
-	SNAKE_BONUS_FAST,
-	SNAKE_BONUS_GHOST,
-	SNAKE_BONUS_NUKE,
-	SNAKE_BONUS_SCISSORS,
-	SNAKE_BONUS_REVERSE,
-	SNAKE_BONUS_EGGMAN,
-	SNAKE_NUM_BONUSES,
+#define SPEED 5
+
+#define NUM_BLOCKS_X 20
+#define NUM_BLOCKS_Y 10
+#define BLOCK_SIZE 12
+#define BORDER_SIZE 12
+
+#define MAP_WIDTH  (NUM_BLOCKS_X * BLOCK_SIZE)
+#define MAP_HEIGHT (NUM_BLOCKS_Y * BLOCK_SIZE)
+
+#define LEFT_X ((BASEVIDWIDTH - MAP_WIDTH) / 2 - BORDER_SIZE)
+#define RIGHT_X (LEFT_X + MAP_WIDTH + BORDER_SIZE * 2 - 1)
+#define BOTTOM_Y (BASEVIDHEIGHT - 48)
+#define TOP_Y (BOTTOM_Y - MAP_HEIGHT - BORDER_SIZE * 2 + 1)
+
+enum bonustype_s {
+	BONUS_NONE = 0,
+	BONUS_SLOW,
+	BONUS_FAST,
+	BONUS_GHOST,
+	BONUS_NUKE,
+	BONUS_SCISSORS,
+	BONUS_REVERSE,
+	BONUS_EGGMAN,
+	NUM_BONUSES,
 };
 
 typedef struct snake_s
@@ -55,21 +55,21 @@ typedef struct snake_s
 	UINT8 background;
 
 	UINT16 snakelength;
-	enum snake_bonustype_s snakebonus;
+	enum bonustype_s snakebonus;
 	tic_t snakebonustime;
-	UINT8 snakex[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
-	UINT8 snakey[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
-	UINT8 snakedir[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
+	UINT8 snakex[NUM_BLOCKS_X * NUM_BLOCKS_Y];
+	UINT8 snakey[NUM_BLOCKS_X * NUM_BLOCKS_Y];
+	UINT8 snakedir[NUM_BLOCKS_X * NUM_BLOCKS_Y];
 
 	UINT8 applex;
 	UINT8 appley;
 
-	enum snake_bonustype_s bonustype;
+	enum bonustype_s bonustype;
 	UINT8 bonusx;
 	UINT8 bonusy;
 } snake_t;
 
-static const char *snake_bonuspatches[] = {
+static const char *bonuspatches[] = {
 	NULL,
 	"DL_SLOW",
 	"TVSSC0",
@@ -80,7 +80,7 @@ static const char *snake_bonuspatches[] = {
 	"TVEGC0",
 };
 
-static const char *snake_backgrounds[] = {
+static const char *backgrounds[] = {
 	"RVPUMICF",
 	"FRSTRCKF",
 	"TAR",
@@ -93,29 +93,29 @@ static const char *snake_backgrounds[] = {
 	"RVZGRS04",
 };
 
-static void Snake_Initialise(snake_t *snake)
+static void Initialise(snake_t *snake)
 {
 	snake->paused = false;
 	snake->pausepressed = false;
 	snake->time = 0;
-	snake->nextupdate = SNAKE_SPEED;
+	snake->nextupdate = SPEED;
 	snake->gameover = false;
-	snake->background = M_RandomKey(sizeof(snake_backgrounds) / sizeof(*snake_backgrounds));
+	snake->background = M_RandomKey(sizeof(backgrounds) / sizeof(*backgrounds));
 
 	snake->snakelength = 1;
-	snake->snakebonus = SNAKE_BONUS_NONE;
-	snake->snakex[0] = M_RandomKey(SNAKE_NUM_BLOCKS_X);
-	snake->snakey[0] = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
+	snake->snakebonus = BONUS_NONE;
+	snake->snakex[0] = M_RandomKey(NUM_BLOCKS_X);
+	snake->snakey[0] = M_RandomKey(NUM_BLOCKS_Y);
 	snake->snakedir[0] = 0;
 	snake->snakedir[1] = 0;
 
-	snake->applex = M_RandomKey(SNAKE_NUM_BLOCKS_X);
-	snake->appley = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
+	snake->applex = M_RandomKey(NUM_BLOCKS_X);
+	snake->appley = M_RandomKey(NUM_BLOCKS_Y);
 
-	snake->bonustype = SNAKE_BONUS_NONE;
+	snake->bonustype = BONUS_NONE;
 }
 
-static UINT8 Snake_GetOppositeDir(UINT8 dir)
+static UINT8 GetOppositeDir(UINT8 dir)
 {
 	if (dir == 1 || dir == 3)
 		return dir + 1;
@@ -125,22 +125,22 @@ static UINT8 Snake_GetOppositeDir(UINT8 dir)
 		return 12 + 5 - dir;
 }
 
-static void Snake_FindFreeSlot(snake_t *snake, UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady)
+static void FindFreeSlot(snake_t *snake, UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady)
 {
 	UINT8 x, y;
 	UINT16 i;
 
 	do
 	{
-		x = M_RandomKey(SNAKE_NUM_BLOCKS_X);
-		y = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
+		x = M_RandomKey(NUM_BLOCKS_X);
+		y = M_RandomKey(NUM_BLOCKS_Y);
 
 		for (i = 0; i < snake->snakelength; i++)
 			if (x == snake->snakex[i] && y == snake->snakey[i])
 				break;
 	} while (i < snake->snakelength || (x == headx && y == heady)
 		|| (x == snake->applex && y == snake->appley)
-		|| (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy));
+		|| (snake->bonustype != BONUS_NONE && x == snake->bonusx && y == snake->bonusy));
 
 	*freex = x;
 	*freey = y;
@@ -151,7 +151,7 @@ void Snake_Allocate(void **opaque)
 	if (*opaque)
 		Snake_Free(opaque);
 	*opaque = malloc(sizeof(snake_t));
-	Snake_Initialise(*opaque);
+	Initialise(*opaque);
 }
 
 void Snake_Update(void *opaque)
@@ -166,7 +166,7 @@ void Snake_Update(void *opaque)
 	// Handle retry
 	if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER]))
 	{
-		Snake_Initialise(snake);
+		Initialise(snake);
 		snake->pausepressed = true; // Avoid accidental pause on respawn
 	}
 
@@ -216,18 +216,18 @@ void Snake_Update(void *opaque)
 	{
 		snake->snakebonustime--;
 		if (!snake->snakebonustime)
-			snake->snakebonus = SNAKE_BONUS_NONE;
+			snake->snakebonus = BONUS_NONE;
 	}
 
 	snake->nextupdate--;
 	if (snake->nextupdate)
 		return;
-	if (snake->snakebonus == SNAKE_BONUS_SLOW)
-		snake->nextupdate = SNAKE_SPEED * 2;
-	else if (snake->snakebonus == SNAKE_BONUS_FAST)
-		snake->nextupdate = SNAKE_SPEED * 2 / 3;
+	if (snake->snakebonus == BONUS_SLOW)
+		snake->nextupdate = SPEED * 2;
+	else if (snake->snakebonus == BONUS_FAST)
+		snake->nextupdate = SPEED * 2 / 3;
 	else
-		snake->nextupdate = SNAKE_SPEED;
+		snake->nextupdate = SPEED;
 
 	if (snake->gameover)
 		return;
@@ -242,7 +242,7 @@ void Snake_Update(void *opaque)
 				snake->gameover = true;
 			break;
 		case 2:
-			if (x < SNAKE_NUM_BLOCKS_X - 1)
+			if (x < NUM_BLOCKS_X - 1)
 				x++;
 			else
 				snake->gameover = true;
@@ -254,7 +254,7 @@ void Snake_Update(void *opaque)
 				snake->gameover = true;
 			break;
 		case 4:
-			if (y < SNAKE_NUM_BLOCKS_Y - 1)
+			if (y < NUM_BLOCKS_Y - 1)
 				y++;
 			else
 				snake->gameover = true;
@@ -262,13 +262,13 @@ void Snake_Update(void *opaque)
 	}
 
 	// Check collision with snake
-	if (snake->snakebonus != SNAKE_BONUS_GHOST)
+	if (snake->snakebonus != BONUS_GHOST)
 		for (i = 1; i < snake->snakelength - 1; i++)
 			if (x == snake->snakex[i] && y == snake->snakey[i])
 			{
-				if (snake->snakebonus == SNAKE_BONUS_SCISSORS)
+				if (snake->snakebonus == BONUS_SCISSORS)
 				{
-					snake->snakebonus = SNAKE_BONUS_NONE;
+					snake->snakebonus = BONUS_NONE;
 					snake->snakelength = i;
 					S_StartSound(NULL, sfx_adderr);
 				}
@@ -285,7 +285,7 @@ void Snake_Update(void *opaque)
 	// Check collision with apple
 	if (x == snake->applex && y == snake->appley)
 	{
-		if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y)
+		if (snake->snakelength + 3 < NUM_BLOCKS_X * NUM_BLOCKS_Y)
 		{
 			snake->snakelength++;
 			snake->snakex  [snake->snakelength - 1] = snake->snakex  [snake->snakelength - 2];
@@ -294,18 +294,18 @@ void Snake_Update(void *opaque)
 		}
 
 		// Spawn new apple
-		Snake_FindFreeSlot(snake, &snake->applex, &snake->appley, x, y);
+		FindFreeSlot(snake, &snake->applex, &snake->appley, x, y);
 
 		// Spawn new bonus
 		if (!(snake->snakelength % 5))
 		{
 			do
 			{
-				snake->bonustype = M_RandomKey(SNAKE_NUM_BONUSES - 1) + 1;
-			} while (snake->snakelength > SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y * 3 / 4
-				&& (snake->bonustype == SNAKE_BONUS_EGGMAN || snake->bonustype == SNAKE_BONUS_FAST || snake->bonustype == SNAKE_BONUS_REVERSE));
+				snake->bonustype = M_RandomKey(NUM_BONUSES - 1) + 1;
+			} while (snake->snakelength > NUM_BLOCKS_X * NUM_BLOCKS_Y * 3 / 4
+				&& (snake->bonustype == BONUS_EGGMAN || snake->bonustype == BONUS_FAST || snake->bonustype == BONUS_REVERSE));
 
-			Snake_FindFreeSlot(snake, &snake->bonusx, &snake->bonusy, x, y);
+			FindFreeSlot(snake, &snake->bonusx, &snake->bonusy, x, y);
 		}
 
 		S_StartSound(NULL, sfx_s3k6b);
@@ -350,25 +350,25 @@ void Snake_Update(void *opaque)
 	snake->snakey[0] = y;
 
 	// Check collision with bonus
-	if (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)
+	if (snake->bonustype != BONUS_NONE && x == snake->bonusx && y == snake->bonusy)
 	{
 		S_StartSound(NULL, sfx_ncchip);
 
 		switch (snake->bonustype)
 		{
-		case SNAKE_BONUS_SLOW:
-			snake->snakebonus = SNAKE_BONUS_SLOW;
+		case BONUS_SLOW:
+			snake->snakebonus = BONUS_SLOW;
 			snake->snakebonustime = 20 * TICRATE;
 			break;
-		case SNAKE_BONUS_FAST:
-			snake->snakebonus = SNAKE_BONUS_FAST;
+		case BONUS_FAST:
+			snake->snakebonus = BONUS_FAST;
 			snake->snakebonustime = 20 * TICRATE;
 			break;
-		case SNAKE_BONUS_GHOST:
-			snake->snakebonus = SNAKE_BONUS_GHOST;
+		case BONUS_GHOST:
+			snake->snakebonus = BONUS_GHOST;
 			snake->snakebonustime = 10 * TICRATE;
 			break;
-		case SNAKE_BONUS_NUKE:
+		case BONUS_NUKE:
 			for (i = 0; i < snake->snakelength; i++)
 			{
 				snake->snakex  [i] = snake->snakex  [0];
@@ -378,11 +378,11 @@ void Snake_Update(void *opaque)
 
 			S_StartSound(NULL, sfx_bkpoof);
 			break;
-		case SNAKE_BONUS_SCISSORS:
-			snake->snakebonus = SNAKE_BONUS_SCISSORS;
+		case BONUS_SCISSORS:
+			snake->snakebonus = BONUS_SCISSORS;
 			snake->snakebonustime = 60 * TICRATE;
 			break;
-		case SNAKE_BONUS_REVERSE:
+		case BONUS_REVERSE:
 			for (i = 0; i < (snake->snakelength + 1) / 2; i++)
 			{
 				UINT16 i2 = snake->snakelength - 1 - i;
@@ -393,10 +393,10 @@ void Snake_Update(void *opaque)
 				// Swap first segment with last segment
 				snake->snakex  [i] = snake->snakex  [i2];
 				snake->snakey  [i] = snake->snakey  [i2];
-				snake->snakedir[i] = Snake_GetOppositeDir(snake->snakedir[i2]);
+				snake->snakedir[i] = GetOppositeDir(snake->snakedir[i2]);
 				snake->snakex  [i2] = tmpx;
 				snake->snakey  [i2] = tmpy;
-				snake->snakedir[i2] = Snake_GetOppositeDir(tmpdir);
+				snake->snakedir[i2] = GetOppositeDir(tmpdir);
 			}
 
 			snake->snakedir[0] = 0;
@@ -404,14 +404,14 @@ void Snake_Update(void *opaque)
 			S_StartSound(NULL, sfx_gravch);
 			break;
 		default:
-			if (snake->snakebonus != SNAKE_BONUS_GHOST)
+			if (snake->snakebonus != BONUS_GHOST)
 			{
 				snake->gameover = true;
 				S_StartSound(NULL, sfx_lose);
 			}
 		}
 
-		snake->bonustype = SNAKE_BONUS_NONE;
+		snake->bonustype = BONUS_NONE;
 	}
 }
 
@@ -425,23 +425,23 @@ void Snake_Draw(void *opaque)
 	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
 
 	V_DrawFlatFill(
-		SNAKE_LEFT_X + SNAKE_BORDER_SIZE,
-		SNAKE_TOP_Y  + SNAKE_BORDER_SIZE,
-		SNAKE_MAP_WIDTH,
-		SNAKE_MAP_HEIGHT,
-		W_GetNumForName(snake_backgrounds[snake->background])
+		LEFT_X + BORDER_SIZE,
+		TOP_Y  + BORDER_SIZE,
+		MAP_WIDTH,
+		MAP_HEIGHT,
+		W_GetNumForName(backgrounds[snake->background])
 	);
 
 	// Borders
-	V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Top
-	V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_TOP_Y, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Right
-	V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE, SNAKE_TOP_Y + SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Bottom
-	V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y + SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Left
+	V_DrawFill(LEFT_X, TOP_Y, BORDER_SIZE + MAP_WIDTH, BORDER_SIZE, 242); // Top
+	V_DrawFill(LEFT_X + BORDER_SIZE + MAP_WIDTH, TOP_Y, BORDER_SIZE, BORDER_SIZE + MAP_HEIGHT, 242); // Right
+	V_DrawFill(LEFT_X + BORDER_SIZE, TOP_Y + BORDER_SIZE + MAP_HEIGHT, BORDER_SIZE + MAP_WIDTH, BORDER_SIZE, 242); // Bottom
+	V_DrawFill(LEFT_X, TOP_Y + BORDER_SIZE, BORDER_SIZE, BORDER_SIZE + MAP_HEIGHT, 242); // Left
 
 	// Apple
 	V_DrawFixedPatch(
-		(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->applex * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
-		(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->appley * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
+		(LEFT_X + BORDER_SIZE + snake->applex * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT,
+		(TOP_Y  + BORDER_SIZE + snake->appley * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT,
 		FRACUNIT / 4,
 		0,
 		W_CachePatchLongName("DL_APPLE", PU_HUDGFX),
@@ -449,13 +449,13 @@ void Snake_Draw(void *opaque)
 	);
 
 	// Bonus
-	if (snake->bonustype != SNAKE_BONUS_NONE)
+	if (snake->bonustype != BONUS_NONE)
 		V_DrawFixedPatch(
-			(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->bonusx * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2    ) * FRACUNIT,
-			(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->bonusy * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 + 4) * FRACUNIT,
+			(LEFT_X + BORDER_SIZE + snake->bonusx * BLOCK_SIZE + BLOCK_SIZE / 2    ) * FRACUNIT,
+			(TOP_Y  + BORDER_SIZE + snake->bonusy * BLOCK_SIZE + BLOCK_SIZE / 2 + 4) * FRACUNIT,
 			FRACUNIT / 2,
 			0,
-			W_CachePatchLongName(snake_bonuspatches[snake->bonustype], PU_HUDGFX),
+			W_CachePatchLongName(bonuspatches[snake->bonustype], PU_HUDGFX),
 			NULL
 		);
 
@@ -499,10 +499,10 @@ void Snake_Draw(void *opaque)
 			}
 
 			V_DrawFixedPatch(
-				(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->snakex[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
-				(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->snakey[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
+				(LEFT_X + BORDER_SIZE + snake->snakex[i] * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT,
+				(TOP_Y  + BORDER_SIZE + snake->snakey[i] * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT,
 				i == 0 && dir == 0 ? FRACUNIT / 5 : FRACUNIT / 2,
-				snake->snakebonus == SNAKE_BONUS_GHOST ? V_TRANSLUCENT : 0,
+				snake->snakebonus == BONUS_GHOST ? V_TRANSLUCENT : 0,
 				W_CachePatchLongName(patchname, PU_HUDGFX),
 				NULL
 			);
@@ -510,17 +510,17 @@ void Snake_Draw(void *opaque)
 	}
 
 	// Length
-	V_DrawString(SNAKE_RIGHT_X + 4, SNAKE_TOP_Y, V_MONOSPACE, va("%u", snake->snakelength));
+	V_DrawString(RIGHT_X + 4, TOP_Y, V_MONOSPACE, va("%u", snake->snakelength));
 
 	// Bonus
-	if (snake->snakebonus != SNAKE_BONUS_NONE
+	if (snake->snakebonus != BONUS_NONE
 	&& (snake->snakebonustime >= 3 * TICRATE || snake->time % 4 < 4 / 2))
 		V_DrawFixedPatch(
-			(SNAKE_RIGHT_X + 10) * FRACUNIT,
-			(SNAKE_TOP_Y + 24) * FRACUNIT,
+			(RIGHT_X + 10) * FRACUNIT,
+			(TOP_Y + 24) * FRACUNIT,
 			FRACUNIT / 2,
 			0,
-			W_CachePatchLongName(snake_bonuspatches[snake->snakebonus], PU_HUDGFX),
+			W_CachePatchLongName(bonuspatches[snake->snakebonus], PU_HUDGFX),
 			NULL
 		);
 }
-- 
GitLab


From 27b500ce9aa021493a85ba46824960a1f24f06c7 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 29 Dec 2022 10:23:12 +0100
Subject: [PATCH 248/518] Split packet handling switch into functions

---
 src/d_clisrv.c | 1083 ++++++++++++++++++++++++------------------------
 src/d_netfil.c |   49 ++-
 src/d_netfil.h |    8 +-
 3 files changed, 591 insertions(+), 549 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index be10e4f9be..d3f6854c74 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -3677,642 +3677,657 @@ static void HandleServerInfo(SINT8 node)
 }
 #endif
 
-static void PT_WillResendGamestate(void)
+// Helper function for packets that should only be sent by the server
+// If it is NOT from the server, bail out and close the connection!
+static boolean ServerOnly(SINT8 node)
 {
-#ifndef NONET
-	char tmpsave[256];
+	if (node == servernode)
+		return false;
 
-	if (server || cl_redownloadinggamestate)
-		return;
+	Net_CloseConnection(node);
+	return true;
+}
 
-	// Send back a PT_CANRECEIVEGAMESTATE packet to the server
-	// so they know they can start sending the game state
-	netbuffer->packettype = PT_CANRECEIVEGAMESTATE;
-	if (!HSendPacket(servernode, true, 0, 0))
-		return;
+static void PT_AskInfoViaMS(SINT8 node)
+{
+	Net_CloseConnection(node);
+}
 
-	CONS_Printf(M_GetText("Reloading game state...\n"));
+static void PT_TellFilesNeeded(SINT8 node)
+{
+	if (server && serverrunning)
+	{
+		UINT8 *p;
+		INT32 firstfile = netbuffer->u.filesneedednum;
 
-	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
+		netbuffer->packettype = PT_MOREFILESNEEDED;
+		netbuffer->u.filesneededcfg.first = firstfile;
+		netbuffer->u.filesneededcfg.more = 0;
 
-	// Don't get a corrupt savegame error because tmpsave already exists
-	if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
-		I_Error("Can't delete %s\n", tmpsave);
+		p = PutFileNeeded(firstfile);
 
-	CL_PrepareDownloadSaveGame(tmpsave);
+		HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
+	}
+	else // Shouldn't get this if you aren't the server...?
+		Net_CloseConnection(node);
+}
 
-	cl_redownloadinggamestate = true;
-#endif
+static void PT_MoreFilesNeeded(SINT8 node)
+{
+	if (server && serverrunning)
+	{ // But wait I thought I'm the server?
+		Net_CloseConnection(node);
+		return;
+	}
+	if (ServerOnly(node))
+		return;
+	if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum)
+	{
+		D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first);
+		if (!netbuffer->u.filesneededcfg.more)
+			cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list
+	}
 }
 
-static void PT_CanReceiveGamestate(SINT8 node)
+static void PT_AskInfo(SINT8 node)
 {
-#ifndef NONET
-	if (client || sendingsavegame[node])
+	if (server && serverrunning)
+	{
+		SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time));
+		SV_SendPlayerInfo(node); // Send extra info
+	}
+	Net_CloseConnection(node);
+}
+
+// Negative response of client join request
+static void PT_ServerRefuse(SINT8 node)
+{
+	if (server && serverrunning)
+	{ // But wait I thought I'm the server?
+		Net_CloseConnection(node);
 		return;
+	}
+	if (ServerOnly(node))
+		return;
+	if (cl_mode == CL_WAITJOINRESPONSE)
+	{
+		// Save the reason so it can be displayed after quitting the netgame
+		char *reason = strdup(netbuffer->u.serverrefuse.reason);
+		if (!reason)
+			I_Error("Out of memory!\n");
 
-	CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]);
+		if (strstr(reason, "Maximum players reached"))
+		{
+			serverisfull = true;
+			//Special timeout for when refusing due to player cap. The client will wait 3 seconds between join requests when waiting for a slot, so we need this to be much longer
+			//We set it back to the value of cv_nettimeout.value in CL_Reset
+			connectiontimeout = NEWTICRATE*7;
+			cl_mode = CL_ASKJOIN;
+			free(reason);
+			return;
+		}
 
-	SV_SendSaveGame(node, true); // Resend a complete game state
-	resendingsavegame[node] = true;
-#else
-	(void)node;
-#endif
+		M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
+			reason), NULL, MM_NOTHING);
+
+		D_QuitNetGame();
+		CL_Reset();
+		D_StartTitle();
+
+		free(reason);
+
+		// Will be reset by caller. Signals refusal.
+		cl_mode = CL_ABORTED;
+	}
 }
 
-/** Handles a packet received from a node that isn't in game
-  *
-  * \param node The packet sender
-  * \todo Choose a better name, as the packet can also come from the server apparently?
-  * \sa HandlePacketFromPlayer
-  * \sa GetPackets
-  *
-  */
-static void HandlePacketFromAwayNode(SINT8 node)
+// Positive response of client join request
+static void PT_ServerCFG(SINT8 node)
 {
-	if (node != servernode)
-		DEBFILE(va("Received packet from unknown host %d\n", node));
+	if (server && serverrunning && node != servernode)
+	{ // but wait I thought I'm the server?
+		Net_CloseConnection(node);
+		return;
+	}
+	if (ServerOnly(node))
+		return;
+	/// \note how would this happen? and is it doing the right thing if it does?
+	if (cl_mode != CL_WAITJOINRESPONSE)
+		return;
 
-// macro for packets that should only be sent by the server
-// if it is NOT from the server, bail out and close the connection!
-#define SERVERONLY \
-			if (node != servernode) \
-			{ \
-				Net_CloseConnection(node); \
-				break; \
-			}
-	switch (netbuffer->packettype)
+	if (client)
 	{
-		case PT_ASKINFOVIAMS:
-			Net_CloseConnection(node);
-			break;
-
-		case PT_TELLFILESNEEDED:
-			if (server && serverrunning)
-			{
-				UINT8 *p;
-				INT32 firstfile = netbuffer->u.filesneedednum;
+		maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
+		G_SetGametype(netbuffer->u.servercfg.gametype);
+		modifiedgame = netbuffer->u.servercfg.modifiedgame;
+		memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
+	}
 
-				netbuffer->packettype = PT_MOREFILESNEEDED;
-				netbuffer->u.filesneededcfg.first = firstfile;
-				netbuffer->u.filesneededcfg.more = 0;
+	nodeingame[(UINT8)servernode] = true;
+	serverplayer = netbuffer->u.servercfg.serverplayer;
+	doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
+	mynode = netbuffer->u.servercfg.clientnode;
+	if (serverplayer >= 0)
+		playernode[(UINT8)serverplayer] = servernode;
 
-				p = PutFileNeeded(firstfile);
+	if (netgame)
+#ifndef NONET
+		CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n"));
+#else
+		CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n"));
+#endif
+	DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode));
 
-				HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
-			}
-			else // Shouldn't get this if you aren't the server...?
-				Net_CloseConnection(node);
-			break;
+#ifndef NONET
+	/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
+	///       Shouldn't them be downloaded even at intermission time?
+	///       Also, according to HandleConnect, the server will send the savegame even during intermission...
+	if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* ||
+		netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
+		cl_mode = CL_DOWNLOADSAVEGAME;
+	else
+#endif
+		cl_mode = CL_CONNECTED;
+}
 
-		case PT_MOREFILESNEEDED:
-			if (server && serverrunning)
-			{ // But wait I thought I'm the server?
-				Net_CloseConnection(node);
-				break;
-			}
-			SERVERONLY
-			if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum)
-			{
-				D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first);
-				if (!netbuffer->u.filesneededcfg.more)
-					cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list
-			}
-			break;
+static void PT_ClientCmd(SINT8 node, INT32 netconsole)
+{
+	tic_t realend, realstart;
 
-		case PT_ASKINFO:
-			if (server && serverrunning)
-			{
-				SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time));
-				SV_SendPlayerInfo(node); // Send extra info
-			}
-			Net_CloseConnection(node);
-			break;
+	if (client)
+		return;
 
-		case PT_SERVERREFUSE: // Negative response of client join request
-			if (server && serverrunning)
-			{ // But wait I thought I'm the server?
-				Net_CloseConnection(node);
-				break;
-			}
-			SERVERONLY
-			if (cl_mode == CL_WAITJOINRESPONSE)
-			{
-				// Save the reason so it can be displayed after quitting the netgame
-				char *reason = strdup(netbuffer->u.serverrefuse.reason);
-				if (!reason)
-					I_Error("Out of memory!\n");
+	// To save bytes, only the low byte of tic numbers are sent
+	// Use ExpandTics to figure out what the rest of the bytes are
+	realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node);
+	realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node);
 
-				if (strstr(reason, "Maximum players reached"))
-				{
-					serverisfull = true;
-					//Special timeout for when refusing due to player cap. The client will wait 3 seconds between join requests when waiting for a slot, so we need this to be much longer
-					//We set it back to the value of cv_nettimeout.value in CL_Reset
-					connectiontimeout = NEWTICRATE*7;
-					cl_mode = CL_ASKJOIN;
-					free(reason);
-					break;
-				}
+	if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
+		|| netbuffer->packettype == PT_NODEKEEPALIVEMIS
+		|| supposedtics[node] < realend)
+	{
+		supposedtics[node] = realend;
+	}
+	// Discard out of order packet
+	if (nettics[node] > realend)
+	{
+		DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node]));
+		return;
+	}
 
-				M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
-					reason), NULL, MM_NOTHING);
+	// Update the nettics
+	nettics[node] = realend;
 
-				D_QuitNetGame();
-				CL_Reset();
-				D_StartTitle();
+	// Don't do anything for packets of type NODEKEEPALIVE?
+	if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
+		|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
+		return;
 
-				free(reason);
+	// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
+	/// \todo Use a separate cvar for that kind of timeout?
+	freezetimeout[node] = I_GetTime() + connectiontimeout;
 
-				// Will be reset by caller. Signals refusal.
-				cl_mode = CL_ABORTED;
-			}
-			break;
+	// Copy ticcmd
+	G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
 
-		case PT_SERVERCFG: // Positive response of client join request
-		{
-			if (server && serverrunning && node != servernode)
-			{ // but wait I thought I'm the server?
-				Net_CloseConnection(node);
-				break;
-			}
-			SERVERONLY
-			/// \note how would this happen? and is it doing the right thing if it does?
-			if (cl_mode != CL_WAITJOINRESPONSE)
-				break;
-
-			if (client)
-			{
-				maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
-				G_SetGametype(netbuffer->u.servercfg.gametype);
-				modifiedgame = netbuffer->u.servercfg.modifiedgame;
-				memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
-			}
+	// Check ticcmd for "speed hacks"
+	if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
+		|| netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
+	{
+		CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole);
+		//D_Clearticcmd(k);
 
-			nodeingame[(UINT8)servernode] = true;
-			serverplayer = netbuffer->u.servercfg.serverplayer;
-			doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
-			mynode = netbuffer->u.servercfg.clientnode;
-			if (serverplayer >= 0)
-				playernode[(UINT8)serverplayer] = servernode;
+		SendKick(netconsole, KICK_MSG_CON_FAIL);
+		return;
+	}
 
-			if (netgame)
-#ifndef NONET
-				CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n"));
-#else
-				CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n"));
-#endif
-			DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode));
+	// Splitscreen cmd
+	if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
+		&& nodetoplayer2[node] >= 0)
+		G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
+			&netbuffer->u.client2pak.cmd2, 1);
 
+	// Check player consistancy during the level
+	if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL
+		&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
 #ifndef NONET
-			/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
-			///       Shouldn't them be downloaded even at intermission time?
-			///       Also, according to HandleConnect, the server will send the savegame even during intermission...
-			if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* ||
-				netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
-				cl_mode = CL_DOWNLOADSAVEGAME;
-			else
+		&& !SV_ResendingSavegameToAnyone()
 #endif
-				cl_mode = CL_CONNECTED;
-			break;
+		&& !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime())
+	{
+		if (cv_resynchattempts.value)
+		{
+			// Tell the client we are about to resend them the gamestate
+			netbuffer->packettype = PT_WILLRESENDGAMESTATE;
+			HSendPacket(node, true, 0, 0);
+
+			resendingsavegame[node] = true;
+
+			if (cv_blamecfail.value)
+				CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
+					netconsole+1, player_names[netconsole],
+					consistancy[realstart%BACKUPTICS],
+					SHORT(netbuffer->u.clientpak.consistancy));
+			DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n",
+				netconsole, realstart, consistancy[realstart%BACKUPTICS],
+				SHORT(netbuffer->u.clientpak.consistancy)));
+			return;
+		}
+		else
+		{
+			SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
+			DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
+				netconsole, realstart, consistancy[realstart%BACKUPTICS],
+				SHORT(netbuffer->u.clientpak.consistancy)));
+			return;
 		}
+	}
+}
 
-		// Handled in d_netfil.c
-		case PT_FILEFRAGMENT:
-			if (server)
-			{ // But wait I thought I'm the server?
-				Net_CloseConnection(node);
-				break;
-			}
-			SERVERONLY
-			PT_FileFragment();
-			break;
+static void PT_TextCmd(SINT8 node, INT32 netconsole)
+{
+	if (client)
+		return;
 
-		case PT_FILEACK:
-			if (server)
-				PT_FileAck();
-			break;
+	// splitscreen special
+	if (netbuffer->packettype == PT_TEXTCMD2)
+		netconsole = nodetoplayer2[node];
 
-		case PT_FILERECEIVED:
-			if (server)
-				PT_FileReceived();
-			break;
+	if (netconsole < 0 || netconsole >= MAXPLAYERS)
+		Net_UnAcknowledgePacket(node);
+	else
+	{
+		size_t j;
+		tic_t tic = maketic;
+		UINT8 *textcmd;
 
-		case PT_REQUESTFILE:
-			if (server)
-			{
-				if (!cv_downloading.value || !PT_RequestFile(node))
-					Net_CloseConnection(node); // close connection if one of the requested files could not be sent, or you disabled downloading anyway
-			}
-			else
-				Net_CloseConnection(node); // nope
-			break;
+		// ignore if the textcmd has a reported size of zero
+		// this shouldn't be sent at all
+		if (!netbuffer->u.textcmd[0])
+		{
+			DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n",
+				node, netconsole));
+			Net_UnAcknowledgePacket(node);
+			return;
+		}
 
-		case PT_NODETIMEOUT:
-		case PT_CLIENTQUIT:
-			if (server)
-				Net_CloseConnection(node);
-			break;
+		// ignore if the textcmd size var is actually larger than it should be
+		// BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength
+		if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1)
+		{
+			DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n",
+			netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1),
+				node, netconsole));
+			Net_UnAcknowledgePacket(node);
+			return;
+		}
 
-		case PT_CLIENTCMD:
-			break; // This is not an "unknown packet"
+		// check if tic that we are making isn't too large else we cannot send it :(
+		// doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time
+		j = software_MAXPACKETLENGTH
+			- (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE
+			+ (doomcom->numslots+1)*sizeof(ticcmd_t));
 
-		case PT_SERVERTICS:
-			// Do not remove my own server (we have just get a out of order packet)
-			if (node == servernode)
-				break;
-			/* FALLTHRU */
+		// search a tic that have enougth space in the ticcmd
+		while ((textcmd = D_GetExistingTextcmd(tic, netconsole)),
+			(TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD)
+			&& tic < firstticstosend + BACKUPTICS)
+			tic++;
 
-		default:
-			DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
-			Net_CloseConnection(node);
-			break; // Ignore it
+		if (tic >= firstticstosend + BACKUPTICS)
+		{
+			DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, "
+				"tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)),
+				maketic, firstticstosend, node, netconsole));
+			Net_UnAcknowledgePacket(node);
+			return;
+		}
+
+		// Make sure we have a buffer
+		if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole);
 
+		DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n",
+			tic, textcmd[0]+1, netconsole, firstticstosend, maketic));
+
+		M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
+		textcmd[0] += (UINT8)netbuffer->u.textcmd[0];
 	}
-#undef SERVERONLY
 }
 
-/** Handles a packet received from a node that is in game
-  *
-  * \param node The packet sender
-  * \todo Choose a better name
-  * \sa HandlePacketFromAwayNode
-  * \sa GetPackets
-  *
-  */
-static void HandlePacketFromPlayer(SINT8 node)
+static void PT_Login(SINT8 node, INT32 netconsole)
 {
-	INT32 netconsole;
-	tic_t realend, realstart;
-	UINT8 *pak, *txtpak, numtxtpak;
+	(void)node;
+
+	if (client)
+		return;
+
 #ifndef NOMD5
 	UINT8 finalmd5[16];/* Well, it's the cool thing to do? */
-#endif
 
-	txtpak = NULL;
+	if (doomcom->datalength < 16)/* ignore partial sends */
+		return;
 
-	if (dedicated && node == 0)
-		netconsole = 0;
+	if (!adminpasswordset)
+	{
+		CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]);
+		return;
+	}
+
+	// Do the final pass to compare with the sent md5
+	D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5);
+
+	if (!memcmp(netbuffer->u.md5sum, finalmd5, 16))
+	{
+		CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]);
+		COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately
+	}
 	else
-		netconsole = nodetoplayer[node];
-#ifdef PARANOIA
-	if (netconsole >= MAXPLAYERS)
-		I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
+		CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]);
+#else
+	(void)netconsole;
 #endif
+}
 
-	switch (netbuffer->packettype)
-	{
-// -------------------------------------------- SERVER RECEIVE ----------
-		case PT_CLIENTCMD:
-		case PT_CLIENT2CMD:
-		case PT_CLIENTMIS:
-		case PT_CLIENT2MIS:
-		case PT_NODEKEEPALIVE:
-		case PT_NODEKEEPALIVEMIS:
-			if (client)
-				break;
+static void PT_ClientQuit(SINT8 node, INT32 netconsole)
+{
+	if (client)
+		return;
 
-			// To save bytes, only the low byte of tic numbers are sent
-			// Use ExpandTics to figure out what the rest of the bytes are
-			realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node);
-			realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node);
+	if (!nodeingame[node])
+	{
+		Net_CloseConnection(node);
+		return;
+	}
 
-			if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
-				|| netbuffer->packettype == PT_NODEKEEPALIVEMIS
-				|| supposedtics[node] < realend)
-			{
-				supposedtics[node] = realend;
-			}
-			// Discard out of order packet
-			if (nettics[node] > realend)
-			{
-				DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node]));
-				break;
-			}
+	// nodeingame will be put false in the execution of kick command
+	// this allow to send some packets to the quitting client to have their ack back
+	nodewaiting[node] = 0;
+	if (netconsole != -1 && playeringame[netconsole])
+	{
+		UINT8 kickmsg;
 
-			// Update the nettics
-			nettics[node] = realend;
+		if (netbuffer->packettype == PT_NODETIMEOUT)
+			kickmsg = KICK_MSG_TIMEOUT;
+		else
+			kickmsg = KICK_MSG_PLAYER_QUIT;
+		kickmsg |= KICK_MSG_KEEP_BODY;
 
-			// Don't do anything for packets of type NODEKEEPALIVE?
-			if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
-				|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
-				break;
+		SendKick(netconsole, kickmsg);
+		nodetoplayer[node] = -1;
 
-			// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
-			/// \todo Use a separate cvar for that kind of timeout?
-			freezetimeout[node] = I_GetTime() + connectiontimeout;
+		if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0
+			&& playeringame[(UINT8)nodetoplayer2[node]])
+		{
+			SendKick(nodetoplayer2[node], kickmsg);
+			nodetoplayer2[node] = -1;
+		}
+	}
+	Net_CloseConnection(node);
+	nodeingame[node] = false;
+}
 
-			// Copy ticcmd
-			G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
+static void PT_CanReceiveGamestate(SINT8 node)
+{
+#ifndef NONET
+	if (client || sendingsavegame[node])
+		return;
 
-			// Check ticcmd for "speed hacks"
-			if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
-				|| netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
-			{
-				CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole);
-				//D_Clearticcmd(k);
+	CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]);
 
-				SendKick(netconsole, KICK_MSG_CON_FAIL);
-				break;
-			}
+	SV_SendSaveGame(node, true); // Resend a complete game state
+	resendingsavegame[node] = true;
+#else
+	(void)node;
+#endif
+}
 
-			// Splitscreen cmd
-			if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
-				&& nodetoplayer2[node] >= 0)
-				G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
-					&netbuffer->u.client2pak.cmd2, 1);
+static void PT_AskLuaFile(SINT8 node)
+{
+	if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED)
+		AddLuaFileToSendQueue(node, luafiletransfers->realfilename);
+}
 
-			// Check player consistancy during the level
-			if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL
-				&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
-#ifndef NONET
-				&& !SV_ResendingSavegameToAnyone()
-#endif
-				&& !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime())
-			{
-				if (cv_resynchattempts.value)
-				{
-					// Tell the client we are about to resend them the gamestate
-					netbuffer->packettype = PT_WILLRESENDGAMESTATE;
-					HSendPacket(node, true, 0, 0);
-
-					resendingsavegame[node] = true;
-
-					if (cv_blamecfail.value)
-						CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
-							netconsole+1, player_names[netconsole],
-							consistancy[realstart%BACKUPTICS],
-							SHORT(netbuffer->u.clientpak.consistancy));
-					DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n",
-						netconsole, realstart, consistancy[realstart%BACKUPTICS],
-						SHORT(netbuffer->u.clientpak.consistancy)));
-					break;
-				}
-				else
-				{
-					SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
-					DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
-						netconsole, realstart, consistancy[realstart%BACKUPTICS],
-						SHORT(netbuffer->u.clientpak.consistancy)));
-					break;
-				}
-			}
-			break;
-		case PT_TEXTCMD2: // splitscreen special
-			netconsole = nodetoplayer2[node];
-			/* FALLTHRU */
-		case PT_TEXTCMD:
-			if (client)
-				break;
+static void PT_HasLuaFile(SINT8 node)
+{
+	if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING)
+		SV_HandleLuaFileSent(node);
+}
 
-			if (netconsole < 0 || netconsole >= MAXPLAYERS)
-				Net_UnAcknowledgePacket(node);
-			else
-			{
-				size_t j;
-				tic_t tic = maketic;
-				UINT8 *textcmd;
+static void PT_ReceivedGamestate(SINT8 node)
+{
+	sendingsavegame[node] = false;
+	resendingsavegame[node] = false;
+	savegameresendcooldown[node] = I_GetTime() + 5 * TICRATE;
+}
 
-				// ignore if the textcmd has a reported size of zero
-				// this shouldn't be sent at all
-				if (!netbuffer->u.textcmd[0])
-				{
-					DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n",
-						node, netconsole));
-					Net_UnAcknowledgePacket(node);
-					break;
-				}
+static void PT_ServerTics(SINT8 node, INT32 netconsole)
+{
+	UINT8 *pak, *txtpak, numtxtpak;
+	tic_t realend, realstart;
 
-				// ignore if the textcmd size var is actually larger than it should be
-				// BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength
-				if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1)
-				{
-					DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n",
-					netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1),
-						node, netconsole));
-					Net_UnAcknowledgePacket(node);
-					break;
-				}
+	if (!nodeingame[node])
+	{
+		// Do not remove my own server (we have just get a out of order packet)
+		if (node != servernode)
+		{
+			DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
+			Net_CloseConnection(node);
+		}
+		return;
+	}
 
-				// check if tic that we are making isn't too large else we cannot send it :(
-				// doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time
-				j = software_MAXPACKETLENGTH
-					- (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE
-					+ (doomcom->numslots+1)*sizeof(ticcmd_t));
+	// Only accept PT_SERVERTICS from the server.
+	if (node != servernode)
+	{
+		CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node);
+		if (server)
+			SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
+		return;
+	}
 
-				// search a tic that have enougth space in the ticcmd
-				while ((textcmd = D_GetExistingTextcmd(tic, netconsole)),
-					(TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD)
-					&& tic < firstticstosend + BACKUPTICS)
-					tic++;
+	realstart = netbuffer->u.serverpak.starttic;
+	realend = realstart + netbuffer->u.serverpak.numtics;
 
-				if (tic >= firstticstosend + BACKUPTICS)
-				{
-					DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, "
-						"tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)),
-						maketic, firstticstosend, node, netconsole));
-					Net_UnAcknowledgePacket(node);
-					break;
-				}
+	txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots
+		* netbuffer->u.serverpak.numtics];
 
-				// Make sure we have a buffer
-				if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole);
+	if (realend > gametic + CLIENTBACKUPTICS)
+		realend = gametic + CLIENTBACKUPTICS;
+	cl_packetmissed = realstart > neededtic;
 
-				DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n",
-					tic, textcmd[0]+1, netconsole, firstticstosend, maketic));
+	if (realstart <= neededtic && realend > neededtic)
+	{
+		tic_t i, j;
+		pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
 
-				M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
-				textcmd[0] += (UINT8)netbuffer->u.textcmd[0];
-			}
-			break;
-		case PT_LOGIN:
-			if (client)
-				break;
+		for (i = realstart; i < realend; i++)
+		{
+			// clear first
+			D_Clearticcmd(i);
 
-#ifndef NOMD5
-			if (doomcom->datalength < 16)/* ignore partial sends */
-				break;
+			// copy the tics
+			pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
+				netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
 
-			if (!adminpasswordset)
+			// copy the textcmds
+			numtxtpak = *txtpak++;
+			for (j = 0; j < numtxtpak; j++)
 			{
-				CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]);
-				break;
+				INT32 k = *txtpak++; // playernum
+				const size_t txtsize = txtpak[0]+1;
+
+				if (i >= gametic) // Don't copy old net commands
+					M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize);
+				txtpak += txtsize;
 			}
+		}
 
-			// Do the final pass to compare with the sent md5
-			D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5);
+		neededtic = realend;
+	}
+	else
+	{
+		DEBFILE(va("frame not in bound: %u\n", neededtic));
+		/*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart)
+			I_Error("Received an out of order PT_SERVERTICS packet!\n"
+					"Got tics %d-%d, needed tic %d\n\n"
+					"Please report this crash on the Master Board,\n"
+					"IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/
+	}
+}
 
-			if (!memcmp(netbuffer->u.md5sum, finalmd5, 16))
-			{
-				CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]);
-				COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately
-			}
-			else
-				CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]);
-#endif
-			break;
-		case PT_NODETIMEOUT:
-		case PT_CLIENTQUIT:
-			if (client)
-				break;
+static void PT_Ping(SINT8 node, INT32 netconsole)
+{
+	// Only accept PT_PING from the server.
+	if (node != servernode)
+	{
+		CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node);
+		if (server)
+			SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
+		return;
+	}
 
-			// nodeingame will be put false in the execution of kick command
-			// this allow to send some packets to the quitting client to have their ack back
-			nodewaiting[node] = 0;
-			if (netconsole != -1 && playeringame[netconsole])
-			{
-				UINT8 kickmsg;
+	//Update client ping table from the server.
+	if (client)
+	{
+		UINT8 i;
+		for (i = 0; i < MAXPLAYERS; i++)
+			if (playeringame[i])
+				playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i];
 
-				if (netbuffer->packettype == PT_NODETIMEOUT)
-					kickmsg = KICK_MSG_TIMEOUT;
-				else
-					kickmsg = KICK_MSG_PLAYER_QUIT;
-				kickmsg |= KICK_MSG_KEEP_BODY;
+		servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS];
+	}
+}
 
-				SendKick(netconsole, kickmsg);
-				nodetoplayer[node] = -1;
+static void PT_WillResendGamestate(SINT8 node)
+{
+	(void)node;
 
-				if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0
-					&& playeringame[(UINT8)nodetoplayer2[node]])
-				{
-					SendKick(nodetoplayer2[node], kickmsg);
-					nodetoplayer2[node] = -1;
-				}
-			}
-			Net_CloseConnection(node);
-			nodeingame[node] = false;
-			break;
-		case PT_CANRECEIVEGAMESTATE:
-			PT_CanReceiveGamestate(node);
-			break;
-		case PT_ASKLUAFILE:
-			if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED)
-				AddLuaFileToSendQueue(node, luafiletransfers->realfilename);
-			break;
-		case PT_HASLUAFILE:
-			if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING)
-				SV_HandleLuaFileSent(node);
-			break;
-		case PT_RECEIVEDGAMESTATE:
-			sendingsavegame[node] = false;
-			resendingsavegame[node] = false;
-			savegameresendcooldown[node] = I_GetTime() + 5 * TICRATE;
-			break;
-// -------------------------------------------- CLIENT RECEIVE ----------
-		case PT_SERVERTICS:
-			// Only accept PT_SERVERTICS from the server.
-			if (node != servernode)
-			{
-				CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node);
-				if (server)
-					SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
-				break;
-			}
+#ifndef NONET
+	char tmpsave[256];
 
-			realstart = netbuffer->u.serverpak.starttic;
-			realend = realstart + netbuffer->u.serverpak.numtics;
+	if (server || cl_redownloadinggamestate)
+		return;
 
-			if (!txtpak)
-				txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots
-					* netbuffer->u.serverpak.numtics];
+	// Send back a PT_CANRECEIVEGAMESTATE packet to the server
+	// so they know they can start sending the game state
+	netbuffer->packettype = PT_CANRECEIVEGAMESTATE;
+	if (!HSendPacket(servernode, true, 0, 0))
+		return;
 
-			if (realend > gametic + CLIENTBACKUPTICS)
-				realend = gametic + CLIENTBACKUPTICS;
-			cl_packetmissed = realstart > neededtic;
+	CONS_Printf(M_GetText("Reloading game state...\n"));
 
-			if (realstart <= neededtic && realend > neededtic)
-			{
-				tic_t i, j;
-				pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
+	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
 
-				for (i = realstart; i < realend; i++)
-				{
-					// clear first
-					D_Clearticcmd(i);
+	// Don't get a corrupt savegame error because tmpsave already exists
+	if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
+		I_Error("Can't delete %s\n", tmpsave);
 
-					// copy the tics
-					pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
-						netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
+	CL_PrepareDownloadSaveGame(tmpsave);
 
-					// copy the textcmds
-					numtxtpak = *txtpak++;
-					for (j = 0; j < numtxtpak; j++)
-					{
-						INT32 k = *txtpak++; // playernum
-						const size_t txtsize = txtpak[0]+1;
+	cl_redownloadinggamestate = true;
+#endif
+}
 
-						if (i >= gametic) // Don't copy old net commands
-							M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize);
-						txtpak += txtsize;
-					}
-				}
+static void PT_SendingLuaFile(SINT8 node)
+{
+	(void)node;
 
-				neededtic = realend;
-			}
-			else
-			{
-				DEBFILE(va("frame not in bound: %u\n", neededtic));
-				/*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart)
-					I_Error("Received an out of order PT_SERVERTICS packet!\n"
-							"Got tics %d-%d, needed tic %d\n\n"
-							"Please report this crash on the Master Board,\n"
-							"IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/
-			}
-			break;
-		case PT_PING:
-			// Only accept PT_PING from the server.
-			if (node != servernode)
-			{
-				CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node);
-				if (server)
-					SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
-				break;
-			}
+	if (client)
+		CL_PrepareDownloadLuaFile();
+}
 
-			//Update client ping table from the server.
-			if (client)
-			{
-				UINT8 i;
-				for (i = 0; i < MAXPLAYERS; i++)
-					if (playeringame[i])
-						playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i];
+/** Handles a packet received from a node that isn't in game
+  *
+  * \param node The packet sender
+  * \todo Choose a better name, as the packet can also come from the server apparently?
+  * \sa HandlePacketFromPlayer
+  * \sa GetPackets
+  *
+  */
+static void HandlePacketFromAwayNode(SINT8 node)
+{
+	if (node != servernode)
+		DEBFILE(va("Received packet from unknown host %d\n", node));
 
-				servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS];
-			}
+	switch (netbuffer->packettype)
+	{
+		case PT_ASKINFOVIAMS   : PT_AskInfoViaMS   (node    ); break;
+		case PT_TELLFILESNEEDED: PT_TellFilesNeeded(node    ); break;
+		case PT_MOREFILESNEEDED: PT_MoreFilesNeeded(node    ); break;
+		case PT_ASKINFO        : PT_AskInfo        (node    ); break;
+		case PT_SERVERREFUSE   : PT_ServerRefuse   (node    ); break;
+		case PT_SERVERCFG      : PT_ServerCFG      (node    ); break;
+		case PT_FILEFRAGMENT   : PT_FileFragment   (node, -1); break;
+		case PT_FILEACK        : PT_FileAck        (node    ); break;
+		case PT_FILERECEIVED   : PT_FileReceived   (node    ); break;
+		case PT_REQUESTFILE    : PT_RequestFile    (node    ); break;
+		case PT_NODETIMEOUT    : PT_ClientQuit     (node, -1); break;
+		case PT_CLIENTQUIT     : PT_ClientQuit     (node, -1); break;
+		case PT_SERVERTICS     : PT_ServerTics     (node, -1); break;
+		case PT_CLIENTCMD      :                               break; // This is not an "unknown packet"
 
+		default:
+			DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
+			Net_CloseConnection(node);
+	}
+}
+
+/** Handles a packet received from a node that is in game
+  *
+  * \param node The packet sender
+  * \todo Choose a better name
+  * \sa HandlePacketFromAwayNode
+  * \sa GetPackets
+  *
+  */
+static void HandlePacketFromPlayer(SINT8 node)
+{
+	INT32 netconsole;
+
+	if (dedicated && node == 0)
+		netconsole = 0;
+	else
+		netconsole = nodetoplayer[node];
+#ifdef PARANOIA
+	if (netconsole >= MAXPLAYERS)
+		I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
+#endif
+
+	switch (netbuffer->packettype)
+	{
+		// SERVER RECEIVE
+		case PT_CLIENTCMD:
+		case PT_CLIENT2CMD:
+		case PT_CLIENTMIS:
+		case PT_CLIENT2MIS:
+		case PT_NODEKEEPALIVE:
+		case PT_NODEKEEPALIVEMIS:
+			PT_ClientCmd(node, netconsole);
 			break;
-		case PT_SERVERCFG:
-			break;
-		case PT_FILEFRAGMENT:
-			// Only accept PT_FILEFRAGMENT from the server.
-			if (node != servernode)
-			{
-				CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node);
-				if (server)
-					SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
-				break;
-			}
-			if (client)
-				PT_FileFragment();
-			break;
-		case PT_FILEACK:
-			if (server)
-				PT_FileAck();
-			break;
-		case PT_FILERECEIVED:
-			if (server)
-				PT_FileReceived();
-			break;
-		case PT_WILLRESENDGAMESTATE:
-			PT_WillResendGamestate();
-			break;
-		case PT_SENDINGLUAFILE:
-			if (client)
-				CL_PrepareDownloadLuaFile();
-			break;
+		case PT_TEXTCMD            : PT_TextCmd            (node, netconsole); break;
+		case PT_TEXTCMD2           : PT_TextCmd            (node, netconsole); break;
+		case PT_LOGIN              : PT_Login              (node, netconsole); break;
+		case PT_NODETIMEOUT        : PT_ClientQuit         (node, netconsole); break;
+		case PT_CLIENTQUIT         : PT_ClientQuit         (node, netconsole); break;
+		case PT_CANRECEIVEGAMESTATE: PT_CanReceiveGamestate(node            ); break;
+		case PT_ASKLUAFILE         : PT_AskLuaFile         (node            ); break;
+		case PT_HASLUAFILE         : PT_HasLuaFile         (node            ); break;
+		case PT_RECEIVEDGAMESTATE  : PT_ReceivedGamestate  (node            ); break;
+
+		// CLIENT RECEIVE
+		case PT_SERVERTICS         : PT_ServerTics         (node, netconsole); break;
+		case PT_PING               : PT_Ping               (node, netconsole); break;
+		case PT_FILEFRAGMENT       : PT_FileFragment       (node, netconsole); break;
+		case PT_FILEACK            : PT_FileAck            (node            ); break;
+		case PT_FILERECEIVED       : PT_FileReceived       (node            ); break;
+		case PT_WILLRESENDGAMESTATE: PT_WillResendGamestate(node            ); break;
+		case PT_SENDINGLUAFILE     : PT_SendingLuaFile     (node            ); break;
+		case PT_SERVERCFG          :                                           break;
+
 		default:
 			DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n",
 				netbuffer->packettype, node));
-	} // end switch
+	}
 }
 
 /**	Handles all received packets, if any
diff --git a/src/d_netfil.c b/src/d_netfil.c
index edbef30bbf..7c9ed54d86 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -413,12 +413,17 @@ boolean CL_SendFileRequest(void)
 }
 
 // get request filepak and put it on the send queue
-// returns false if a requested file was not found or cannot be sent
-boolean PT_RequestFile(INT32 node)
+void PT_RequestFile(SINT8 node)
 {
 	UINT8 *p = netbuffer->u.textcmd;
 	UINT8 id;
 
+	if (client || !cv_downloading.value)
+	{
+		Net_CloseConnection(node); // close connection if you are not the server or disabled downloading
+		return;
+	}
+
 	while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow
 	{
 		id = READUINT8(p);
@@ -428,11 +433,12 @@ boolean PT_RequestFile(INT32 node)
 		if (!AddFileToSendQueue(node, id))
 		{
 			SV_AbortSendFiles(node);
-			return false; // don't read the rest of the files
+			Net_CloseConnection(node); // close connection if one of the requested files could not be sent
+			return; // don't read the rest of the files
 		}
 	}
 
-	return true; // no problems with any files
+	return; // no problems with any files
 }
 
 /** Checks if the files needed aren't already loaded or on the disk
@@ -1137,13 +1143,15 @@ void FileSendTicker(void)
 	}
 }
 
-void PT_FileAck(void)
+void PT_FileAck(SINT8 node)
 {
 	fileack_pak *packet = &netbuffer->u.fileack;
-	INT32 node = doomcom->remotenode;
 	filetran_t *trans = &transfer[node];
 	INT32 i, j;
 
+	if (client)
+		return;
+
 	// Wrong file id? Ignore it, it's probably a late packet
 	if (!(trans->txlist && packet->fileid == trans->txlist->fileid))
 		return;
@@ -1190,12 +1198,12 @@ void PT_FileAck(void)
 	}
 }
 
-void PT_FileReceived(void)
+void PT_FileReceived(SINT8 node)
 {
-	filetx_t *trans = transfer[doomcom->remotenode].txlist;
+	filetx_t *trans = transfer[node].txlist;
 
-	if (trans && netbuffer->u.filereceived == trans->fileid)
-		SV_EndFileSend(doomcom->remotenode);
+	if (server && trans && netbuffer->u.filereceived == trans->fileid)
+		SV_EndFileSend(node);
 }
 
 static void SendAckPacket(fileack_pak *packet, UINT8 fileid)
@@ -1281,8 +1289,27 @@ void FileReceiveTicker(void)
 	}
 }
 
-void PT_FileFragment(void)
+void PT_FileFragment(SINT8 node, INT32 netconsole)
 {
+	if (nodeingame[node])
+	{
+		// Only accept PT_FILEFRAGMENT from the server.
+		if (node != servernode)
+		{
+			CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node);
+			if (server)
+				SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
+			return;
+		}
+		if (server)
+			return;
+	}
+	else if (server || node != servernode)
+	{
+		Net_CloseConnection(node);
+		return;
+	}
+
 	INT32 filenum = netbuffer->u.filetxpak.fileid;
 	fileneeded_t *file = &fileneeded[filenum];
 	UINT32 fragmentpos = LONG(netbuffer->u.filetxpak.position);
diff --git a/src/d_netfil.h b/src/d_netfil.h
index f778a518ff..5c7f4ef49f 100644
--- a/src/d_netfil.h
+++ b/src/d_netfil.h
@@ -90,16 +90,16 @@ void AddRamToSendQueue(INT32 node, void *data, size_t size, freemethod_t freemet
 	UINT8 fileid);
 
 void FileSendTicker(void);
-void PT_FileAck(void);
-void PT_FileReceived(void);
+void PT_FileAck(SINT8 node);
+void PT_FileReceived(SINT8 node);
 boolean SendingFile(INT32 node);
 
 void FileReceiveTicker(void);
-void PT_FileFragment(void);
+void PT_FileFragment(SINT8 node, INT32 netconsole);
 
 boolean CL_CheckDownloadable(void);
 boolean CL_SendFileRequest(void);
-boolean PT_RequestFile(INT32 node);
+void PT_RequestFile(SINT8 node);
 
 typedef enum
 {
-- 
GitLab


From 4c0705008e46051bdae26dd1e3cdbbd148ccf33a Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 29 Dec 2022 15:52:45 +0100
Subject: [PATCH 249/518] Merge node arrays into a structure

---
 src/d_clisrv.c | 202 ++++++++++++++++++++++++-------------------------
 src/d_net.h    |  23 +++++-
 src/d_netfil.c |   4 +-
 src/i_tcp.c    |  14 ++--
 4 files changed, 125 insertions(+), 118 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index d3f6854c74..e311bf15a3 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -84,6 +84,8 @@ boolean serverrunning = false;
 INT32 serverplayer = 0;
 char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support)
 
+netnode_t netnodes[MAXNETNODES];
+
 // Server specific vars
 UINT8 playernode[MAXPLAYERS];
 char playeraddress[MAXPLAYERS][64];
@@ -91,10 +93,6 @@ char playeraddress[MAXPLAYERS][64];
 // Minimum timeout for sending the savegame
 // The actual timeout will be longer depending on the savegame length
 tic_t jointimeout = (10*TICRATE);
-static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame?
-static boolean resendingsavegame[MAXNETNODES]; // Are we resending the savegame?
-static tic_t savegameresendcooldown[MAXNETNODES]; // How long before we can resend again?
-static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout?
 
 // Incremented by cv_joindelay when a client joins, decremented each tic.
 // If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled.
@@ -103,14 +101,8 @@ static tic_t joindelay = 0;
 UINT16 pingmeasurecount = 1;
 UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone.
 UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values.
-SINT8 nodetoplayer[MAXNETNODES];
-SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
-UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen
-boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
 tic_t servermaxping = 800; // server's max ping. Defaults to 800
-static tic_t nettics[MAXNETNODES]; // what tic the client have received
-static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet
-static UINT8 nodewaiting[MAXNETNODES];
+
 static tic_t firstticstosend; // min of the nettics
 static tic_t tictoclear = 0; // optimize d_clearticcmd
 static tic_t maketic;
@@ -218,14 +210,14 @@ tic_t ExpandTics(INT32 low, INT32 node)
 {
 	INT32 delta;
 
-	delta = low - (nettics[node] & UINT8_MAX);
+	delta = low - (netnodes[node].tic & UINT8_MAX);
 
 	if (delta >= -64 && delta <= 64)
-		return (nettics[node] & ~UINT8_MAX) + low;
+		return (netnodes[node].tic & ~UINT8_MAX) + low;
 	else if (delta > 64)
-		return (nettics[node] & ~UINT8_MAX) - 256 + low;
+		return (netnodes[node].tic & ~UINT8_MAX) - 256 + low;
 	else //if (delta < -64)
-		return (nettics[node] & ~UINT8_MAX) + 256 + low;
+		return (netnodes[node].tic & ~UINT8_MAX) + 256 + low;
 }
 
 // -----------------------------------------------------------------
@@ -999,7 +991,7 @@ static boolean SV_ResendingSavegameToAnyone(void)
 	INT32 i;
 
 	for (i = 0; i < MAXNETNODES; i++)
-		if (resendingsavegame[i])
+		if (netnodes[i].resendingsavegame)
 			return true;
 	return false;
 }
@@ -1068,8 +1060,8 @@ static void SV_SendSaveGame(INT32 node, boolean resending)
 	save_p = NULL;
 
 	// Remember when we started sending the savegame so we can handle timeouts
-	sendingsavegame[node] = true;
-	freezetimeout[node] = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte
+	netnodes[node].sendingsavegame = true;
+	netnodes[node].freezetimeout = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte
 }
 
 #ifdef DUMPCONSISTENCY
@@ -2034,7 +2026,7 @@ static void CL_ConnectToServer(void)
 		{
 			pnumnodes = 0;
 			for (i = 0; i < MAXNETNODES; i++)
-				if (nodeingame[i])
+				if (netnodes[i].ingame)
 					pnumnodes++;
 		}
 	}
@@ -2316,10 +2308,10 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
 	if (server && !demoplayback && playernode[playernum] != UINT8_MAX)
 	{
 		INT32 node = playernode[playernum];
-		playerpernode[node]--;
-		if (playerpernode[node] <= 0)
+		netnodes[node].numplayers--;
+		if (netnodes[node].numplayers <= 0)
 		{
-			nodeingame[node] = false;
+			netnodes[node].ingame = false;
 			Net_CloseConnection(node);
 			ResetNode(node);
 		}
@@ -2431,7 +2423,7 @@ void CL_Reset(void)
 
 	if (servernode > 0 && servernode < MAXNETNODES)
 	{
-		nodeingame[(UINT8)servernode] = false;
+		netnodes[(UINT8)servernode].ingame = false;
 		Net_CloseConnection(servernode);
 	}
 	D_CloseConnection(); // netgame = false
@@ -2674,7 +2666,7 @@ static void Command_Kick(void)
 		// Special case if we are trying to kick a player who is downloading the game state:
 		// trigger a timeout instead of kicking them, because a kick would only
 		// take effect after they have finished downloading
-		if (server && playernode[pn] != UINT8_MAX && sendingsavegame[playernode[pn]])
+		if (server && playernode[pn] != UINT8_MAX && netnodes[playernode[pn]].sendingsavegame)
 		{
 			Net_ConnectionTimeout(playernode[pn]);
 			return;
@@ -2763,8 +2755,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 
 	// Is playernum authorized to make this kick?
 	if (playernum != serverplayer && !IsPlayerAdmin(playernum)
-		&& !(playernode[playernum] != UINT8_MAX && playerpernode[playernode[playernum]] == 2
-		&& nodetoplayer2[playernode[playernum]] == pnum))
+		&& !(playernode[playernum] != UINT8_MAX && netnodes[playernode[playernum]].numplayers == 2
+		&& netnodes[playernode[playernum]].player2 == pnum))
 	{
 		// We received a kick command from someone who isn't the
 		// server or admin, and who isn't in splitscreen removing
@@ -2802,10 +2794,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 			"\n"
 			"If you think this is a bug, please report it, including all of the details above.\n",
 				playernum, pnum,
-				playernode[playernum], playerpernode[playernode[playernum]],
-				nodetoplayer2[playernode[playernum]],
-				playernode[pnum], playerpernode[playernode[pnum]],
-				nodetoplayer2[playernode[pnum]]);
+				playernode[playernum], netnodes[playernode[playernum]].numplayers,
+				netnodes[playernode[playernum]].player2,
+				playernode[pnum], netnodes[playernode[pnum]].numplayers,
+				netnodes[playernode[pnum]].player2);
 */
 		pnum = playernum;
 		msg = KICK_MSG_CON_FAIL;
@@ -2926,10 +2918,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 		if (server && !demoplayback && playernode[pnum] != UINT8_MAX)
 		{
 			INT32 node = playernode[pnum];
-			playerpernode[node]--;
-			if (playerpernode[node] <= 0)
+			netnodes[node].numplayers--;
+			if (netnodes[node].numplayers <= 0)
 			{
-				nodeingame[node] = false;
+				netnodes[node].ingame = false;
 				Net_CloseConnection(node);
 				ResetNode(node);
 			}
@@ -3017,19 +3009,19 @@ void D_ClientServerInit(void)
 
 static void ResetNode(INT32 node)
 {
-	nodeingame[node] = false;
-	nodewaiting[node] = 0;
+	netnodes[node].ingame = false;
+	netnodes[node].numplayerswaiting = 0;
 
-	nettics[node] = gametic;
-	supposedtics[node] = gametic;
+	netnodes[node].tic = gametic;
+	netnodes[node].supposedtic = gametic;
 
-	nodetoplayer[node] = -1;
-	nodetoplayer2[node] = -1;
-	playerpernode[node] = 0;
+	netnodes[node].player = -1;
+	netnodes[node].player2 = -1;
+	netnodes[node].numplayers = 0;
 
-	sendingsavegame[node] = false;
-	resendingsavegame[node] = false;
-	savegameresendcooldown[node] = 0;
+	netnodes[node].sendingsavegame = false;
+	netnodes[node].resendingsavegame = false;
+	netnodes[node].savegameresendcooldown = 0;
 }
 
 void SV_ResetServer(void)
@@ -3067,7 +3059,7 @@ void SV_ResetServer(void)
 
 	if (dedicated)
 	{
-		nodeingame[0] = true;
+		netnodes[0].ingame = true;
 		serverplayer = 0;
 	}
 	else
@@ -3130,14 +3122,14 @@ void D_QuitNetGame(void)
 
 		netbuffer->packettype = PT_SERVERSHUTDOWN;
 		for (i = 0; i < MAXNETNODES; i++)
-			if (nodeingame[i])
+			if (netnodes[i].ingame)
 				HSendPacket(i, true, 0, 0);
 #ifdef MASTERSERVER
 		if (serverrunning && ms_RoomId > 0)
 			UnregisterServer();
 #endif
 	}
-	else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode])
+	else if (servernode > 0 && servernode < MAXNETNODES && netnodes[(UINT8)servernode].ingame)
 	{
 		netbuffer->packettype = PT_CLIENTQUIT;
 		HSendPacket(servernode, true, 0, 0);
@@ -3161,12 +3153,12 @@ void D_QuitNetGame(void)
 // Adds a node to the game (player will follow at map change or at savegame....)
 static inline void SV_AddNode(INT32 node)
 {
-	nettics[node] = gametic;
-	supposedtics[node] = gametic;
+	netnodes[node].tic = gametic;
+	netnodes[node].supposedtic = gametic;
 	// little hack because the server connects to itself and puts
 	// nodeingame when connected not here
 	if (node)
-		nodeingame[node] = true;
+		netnodes[node].ingame = true;
 }
 
 // Xcmd XD_ADDPLAYER
@@ -3311,7 +3303,7 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2)
 	for (node = 0; node < MAXNETNODES; node++)
 	{
 		// splitscreen can allow 2 player in one node
-		for (; nodewaiting[node] > 0; nodewaiting[node]--)
+		for (; netnodes[node].numplayerswaiting > 0; netnodes[node].numplayerswaiting--)
 		{
 			newplayer = true;
 
@@ -3325,7 +3317,7 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2)
 					if (playeringame[newplayernum])
 						continue;
 					for (n = 0; n < MAXNETNODES; n++)
-						if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum)
+						if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum)
 							break;
 					if (n == MAXNETNODES)
 						break;
@@ -3341,18 +3333,18 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2)
 			p = buf + 2;
 			buf[0] = (UINT8)node;
 			buf[1] = newplayernum;
-			if (playerpernode[node] < 1)
+			if (netnodes[node].numplayers < 1)
 			{
-				nodetoplayer[node] = newplayernum;
+				netnodes[node].player = newplayernum;
 				WRITESTRINGN(p, name, MAXPLAYERNAME);
 			}
 			else
 			{
-				nodetoplayer2[node] = newplayernum;
+				netnodes[node].player2 = newplayernum;
 				buf[1] |= 0x80;
 				WRITESTRINGN(p, name2, MAXPLAYERNAME);
 			}
-			playerpernode[node]++;
+			netnodes[node].numplayers++;
 
 			SendNetXCmd(XD_ADDPLAYER, &buf, p - buf);
 
@@ -3571,7 +3563,7 @@ static void HandleConnect(SINT8 node)
 		boolean newnode = false;
 #endif
 
-		for (i = 0; i < netbuffer->u.clientcfg.localplayers - playerpernode[node]; i++)
+		for (i = 0; i < netbuffer->u.clientcfg.localplayers - netnodes[node].numplayers; i++)
 		{
 			strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1);
 			if (!EnsurePlayerNameIsGood(names[i], rejoinernum))
@@ -3582,8 +3574,8 @@ static void HandleConnect(SINT8 node)
 		}
 
 		// client authorised to join
-		nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]);
-		if (!nodeingame[node])
+		netnodes[node].numplayerswaiting = (UINT8)(netbuffer->u.clientcfg.localplayers - netnodes[node].numplayers);
+		if (!netnodes[node].ingame)
 		{
 			gamestate_t backupstate = gamestate;
 #ifndef NONET
@@ -3608,7 +3600,7 @@ static void HandleConnect(SINT8 node)
 			DEBFILE("new node joined\n");
 		}
 #ifndef NONET
-		if (nodewaiting[node])
+		if (netnodes[node].numplayerswaiting)
 		{
 			if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode)
 			{
@@ -3803,7 +3795,7 @@ static void PT_ServerCFG(SINT8 node)
 		memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
 	}
 
-	nodeingame[(UINT8)servernode] = true;
+	netnodes[(UINT8)servernode].ingame = true;
 	serverplayer = netbuffer->u.servercfg.serverplayer;
 	doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
 	mynode = netbuffer->u.servercfg.clientnode;
@@ -3844,19 +3836,19 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole)
 
 	if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
 		|| netbuffer->packettype == PT_NODEKEEPALIVEMIS
-		|| supposedtics[node] < realend)
+		|| netnodes[node].supposedtic < realend)
 	{
-		supposedtics[node] = realend;
+		netnodes[node].supposedtic = realend;
 	}
 	// Discard out of order packet
-	if (nettics[node] > realend)
+	if (netnodes[node].tic > realend)
 	{
-		DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node]));
+		DEBFILE(va("out of order ticcmd discarded nettics = %u\n", netnodes[node].tic));
 		return;
 	}
 
 	// Update the nettics
-	nettics[node] = realend;
+	netnodes[node].tic = realend;
 
 	// Don't do anything for packets of type NODEKEEPALIVE?
 	if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
@@ -3865,7 +3857,7 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole)
 
 	// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
 	/// \todo Use a separate cvar for that kind of timeout?
-	freezetimeout[node] = I_GetTime() + connectiontimeout;
+	netnodes[node].freezetimeout = I_GetTime() + connectiontimeout;
 
 	// Copy ticcmd
 	G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
@@ -3883,8 +3875,8 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole)
 
 	// Splitscreen cmd
 	if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
-		&& nodetoplayer2[node] >= 0)
-		G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
+		&& netnodes[node].player2 >= 0)
+		G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2],
 			&netbuffer->u.client2pak.cmd2, 1);
 
 	// Check player consistancy during the level
@@ -3893,7 +3885,7 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole)
 #ifndef NONET
 		&& !SV_ResendingSavegameToAnyone()
 #endif
-		&& !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime())
+		&& !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime())
 	{
 		if (cv_resynchattempts.value)
 		{
@@ -3901,7 +3893,7 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole)
 			netbuffer->packettype = PT_WILLRESENDGAMESTATE;
 			HSendPacket(node, true, 0, 0);
 
-			resendingsavegame[node] = true;
+			netnodes[node].resendingsavegame = true;
 
 			if (cv_blamecfail.value)
 				CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
@@ -3931,7 +3923,7 @@ static void PT_TextCmd(SINT8 node, INT32 netconsole)
 
 	// splitscreen special
 	if (netbuffer->packettype == PT_TEXTCMD2)
-		netconsole = nodetoplayer2[node];
+		netconsole = netnodes[node].player2;
 
 	if (netconsole < 0 || netconsole >= MAXPLAYERS)
 		Net_UnAcknowledgePacket(node);
@@ -4033,7 +4025,7 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole)
 	if (client)
 		return;
 
-	if (!nodeingame[node])
+	if (!netnodes[node].ingame)
 	{
 		Net_CloseConnection(node);
 		return;
@@ -4041,7 +4033,7 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole)
 
 	// nodeingame will be put false in the execution of kick command
 	// this allow to send some packets to the quitting client to have their ack back
-	nodewaiting[node] = 0;
+	netnodes[node].numplayerswaiting = 0;
 	if (netconsole != -1 && playeringame[netconsole])
 	{
 		UINT8 kickmsg;
@@ -4053,29 +4045,29 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole)
 		kickmsg |= KICK_MSG_KEEP_BODY;
 
 		SendKick(netconsole, kickmsg);
-		nodetoplayer[node] = -1;
+		netnodes[node].player = -1;
 
-		if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0
-			&& playeringame[(UINT8)nodetoplayer2[node]])
+		if (netnodes[node].player2 != -1 && netnodes[node].player2 >= 0
+			&& playeringame[(UINT8)netnodes[node].player2])
 		{
-			SendKick(nodetoplayer2[node], kickmsg);
-			nodetoplayer2[node] = -1;
+			SendKick(netnodes[node].player2, kickmsg);
+			netnodes[node].player2 = -1;
 		}
 	}
 	Net_CloseConnection(node);
-	nodeingame[node] = false;
+	netnodes[node].ingame = false;
 }
 
 static void PT_CanReceiveGamestate(SINT8 node)
 {
 #ifndef NONET
-	if (client || sendingsavegame[node])
+	if (client || netnodes[node].sendingsavegame)
 		return;
 
-	CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]);
+	CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[netnodes[node].player]);
 
 	SV_SendSaveGame(node, true); // Resend a complete game state
-	resendingsavegame[node] = true;
+	netnodes[node].resendingsavegame = true;
 #else
 	(void)node;
 #endif
@@ -4095,9 +4087,9 @@ static void PT_HasLuaFile(SINT8 node)
 
 static void PT_ReceivedGamestate(SINT8 node)
 {
-	sendingsavegame[node] = false;
-	resendingsavegame[node] = false;
-	savegameresendcooldown[node] = I_GetTime() + 5 * TICRATE;
+	netnodes[node].sendingsavegame = false;
+	netnodes[node].resendingsavegame = false;
+	netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE;
 }
 
 static void PT_ServerTics(SINT8 node, INT32 netconsole)
@@ -4105,7 +4097,7 @@ static void PT_ServerTics(SINT8 node, INT32 netconsole)
 	UINT8 *pak, *txtpak, numtxtpak;
 	tic_t realend, realstart;
 
-	if (!nodeingame[node])
+	if (!netnodes[node].ingame)
 	{
 		// Do not remove my own server (we have just get a out of order packet)
 		if (node != servernode)
@@ -4287,7 +4279,7 @@ static void HandlePacketFromPlayer(SINT8 node)
 	if (dedicated && node == 0)
 		netconsole = 0;
 	else
-		netconsole = nodetoplayer[node];
+		netconsole = netnodes[node].player;
 #ifdef PARANOIA
 	if (netconsole >= MAXPLAYERS)
 		I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
@@ -4376,7 +4368,7 @@ static void GetPackets(void)
 			continue; // We do nothing with PLAYERINFO, that's for the MS browser.
 
 		// Packet received from someone already playing
-		if (nodeingame[node])
+		if (netnodes[node].ingame)
 			HandlePacketFromPlayer(node);
 		// Packet received from someone not playing
 		else
@@ -4572,13 +4564,13 @@ static void SV_SendTics(void)
 
 	// send to all client but not to me
 	// for each node create a packet with x tics and send it
-	// x is computed using supposedtics[n], max packet size and maketic
+	// x is computed using netnodes[n].supposedtic, max packet size and maketic
 	for (n = 1; n < MAXNETNODES; n++)
-		if (nodeingame[n])
+		if (netnodes[n].ingame)
 		{
-			// assert supposedtics[n]>=nettics[n]
-			realfirsttic = supposedtics[n];
-			lasttictosend = min(maketic, nettics[n] + CLIENTBACKUPTICS);
+			// assert netnodes[n].supposedtic>=netnodes[n].tic
+			realfirsttic = netnodes[n].supposedtic;
+			lasttictosend = min(maketic, netnodes[n].tic + CLIENTBACKUPTICS);
 
 			if (realfirsttic >= lasttictosend)
 			{
@@ -4587,8 +4579,8 @@ static void SV_SendTics(void)
 				// packet detection work when we have received packet with firsttic > neededtic
 				// (getpacket servertics case)
 				DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n",
-					n, maketic, supposedtics[n], nettics[n]));
-				realfirsttic = nettics[n];
+					n, maketic, netnodes[n].supposedtic, netnodes[n].tic));
+				realfirsttic = netnodes[n].tic;
 				if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3)
 					// all tic are ok
 					continue;
@@ -4666,13 +4658,13 @@ static void SV_SendTics(void)
 			HSendPacket(n, false, 0, packsize);
 			// when tic are too large, only one tic is sent so don't go backward!
 			if (lasttictosend-doomcom->extratics > realfirsttic)
-				supposedtics[n] = lasttictosend-doomcom->extratics;
+				netnodes[n].supposedtic = lasttictosend-doomcom->extratics;
 			else
-				supposedtics[n] = lasttictosend;
-			if (supposedtics[n] < nettics[n]) supposedtics[n] = nettics[n];
+				netnodes[n].supposedtic = lasttictosend;
+			if (netnodes[n].supposedtic < netnodes[n].tic) netnodes[n].supposedtic = netnodes[n].tic;
 		}
 	// node 0 is me!
-	supposedtics[0] = maketic;
+	netnodes[0].supposedtic = maketic;
 }
 
 //
@@ -4915,7 +4907,7 @@ static inline void PingUpdate(void)
 
 	//send out our ping packets
 	for (i = 0; i < MAXNETNODES; i++)
-		if (nodeingame[i])
+		if (netnodes[i].ingame)
 			HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1));
 
 	pingmeasurecount = 1; //Reset count
@@ -4993,11 +4985,11 @@ void NetUpdate(void)
 
 			firstticstosend = gametic;
 			for (i = 0; i < MAXNETNODES; i++)
-				if (nodeingame[i] && nettics[i] < firstticstosend)
+				if (netnodes[i].ingame && netnodes[i].tic < firstticstosend)
 				{
-					firstticstosend = nettics[i];
+					firstticstosend = netnodes[i].tic;
 
-					if (maketic + 1 >= nettics[i] + BACKUPTICS)
+					if (maketic + 1 >= netnodes[i].tic + BACKUPTICS)
 						Net_ConnectionTimeout(i);
 				}
 
@@ -5025,7 +5017,7 @@ void NetUpdate(void)
 	if (server)
 	{
 		for (i = 1; i < MAXNETNODES; i++)
-			if (nodeingame[i] && freezetimeout[i] < I_GetTime())
+			if (netnodes[i].ingame && netnodes[i].freezetimeout < I_GetTime())
 				Net_ConnectionTimeout(i);
 
 		// In case the cvar value was lowered
@@ -5066,7 +5058,7 @@ INT32 D_NumPlayers(void)
 
 tic_t GetLag(INT32 node)
 {
-	return gametic - nettics[node];
+	return gametic - netnodes[node].tic;
 }
 
 void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest)
diff --git a/src/d_net.h b/src/d_net.h
index 5baa593a05..9eee53894d 100644
--- a/src/d_net.h
+++ b/src/d_net.h
@@ -37,10 +37,25 @@ boolean Net_GetNetStat(void);
 extern INT32 getbytes;
 extern INT64 sendbytes; // Realtime updated
 
-extern SINT8 nodetoplayer[MAXNETNODES];
-extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen)
-extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen
-extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game
+typedef struct netnode_s
+{
+	boolean ingame; // set false as nodes leave game
+	tic_t freezetimeout; // Until when can this node freeze the server before getting a timeout?
+
+	SINT8 player;
+	SINT8 player2; // say the numplayer for this node if any (splitscreen)
+	UINT8 numplayers; // used specialy for scplitscreen
+	UINT8 numplayerswaiting;
+
+	tic_t tic; // what tic the client have received
+	tic_t supposedtic; // nettics prevision for smaller packet
+
+	boolean sendingsavegame; // Are we sending the savegame?
+	boolean resendingsavegame; // Are we resending the savegame?
+	tic_t savegameresendcooldown; // How long before we can resend again?
+} netnode_t;
+
+extern netnode_t netnodes[MAXNETNODES];
 
 extern boolean serverrunning;
 
diff --git a/src/d_netfil.c b/src/d_netfil.c
index 7c9ed54d86..1fee1df81a 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -667,7 +667,7 @@ void SV_PrepareSendLuaFile(void)
 
 	// Set status to "waiting" for everyone
 	for (i = 0; i < MAXNETNODES; i++)
-		luafiletransfers->nodestatus[i] = (nodeingame[i] ? LFTNS_WAITING : LFTNS_NONE);
+		luafiletransfers->nodestatus[i] = (netnodes[i].ingame ? LFTNS_WAITING : LFTNS_NONE);
 
 	if (FIL_ReadFileOK(luafiletransfers->realfilename))
 	{
@@ -1291,7 +1291,7 @@ void FileReceiveTicker(void)
 
 void PT_FileFragment(SINT8 node, INT32 netconsole)
 {
-	if (nodeingame[node])
+	if (netnodes[node].ingame)
 	{
 		// Only accept PT_FILEFRAGMENT from the server.
 		if (node != servernode)
diff --git a/src/i_tcp.c b/src/i_tcp.c
index 8838ba7257..00aebddd36 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -449,7 +449,7 @@ static void cleanupnodes(void)
 
 	// Why can't I start at zero?
 	for (j = 1; j < MAXNETNODES; j++)
-		if (!(nodeingame[j] || SendingFile(j)))
+		if (!(netnodes[j].ingame || SendingFile(j)))
 			nodeconnected[j] = false;
 }
 
@@ -473,7 +473,7 @@ static SINT8 getfreenode(void)
 	  */
 	/*I_Error("No more free nodes!!1!11!11!!1111\n");
 	for (j = 1; j < MAXNETNODES; j++)
-		if (!nodeingame[j])
+		if (!netnodes[j].ingame)
 			return j;*/
 
 	return -1;
@@ -488,24 +488,24 @@ void Command_Numnodes(void)
 
 	for (i = 1; i < MAXNETNODES; i++)
 	{
-		if (!(nodeconnected[i] || nodeingame[i]))
+		if (!(nodeconnected[i] || netnodes[i].ingame))
 			continue;
 
 		if (nodeconnected[i])
 			connected++;
-		if (nodeingame[i])
+		if (netnodes[i].ingame)
 			ingame++;
 
 		CONS_Printf("%2d - ", i);
-		if (nodetoplayer[i] != -1)
-			CONS_Printf("player %.2d", nodetoplayer[i]);
+		if (netnodes[i].player != -1)
+			CONS_Printf("player %.2d", netnodes[i].player);
 		else
 			CONS_Printf("         ");
 		if (nodeconnected[i])
 			CONS_Printf(" - connected");
 		else
 			CONS_Printf(" -          ");
-		if (nodeingame[i])
+		if (netnodes[i].ingame)
 			CONS_Printf(" - ingame");
 		else
 			CONS_Printf(" -       ");
-- 
GitLab


From f1117095b76a6cb8d85c6ea809bd47ddaa64f914 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 29 Dec 2022 20:45:56 +0100
Subject: [PATCH 250/518] Cleanup ResetNode

---
 src/d_clisrv.c | 24 ++++++------------------
 1 file changed, 6 insertions(+), 18 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index e311bf15a3..3ab0df1d0e 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2278,7 +2278,12 @@ static void Command_connect(void)
 }
 #endif
 
-static void ResetNode(INT32 node);
+static void ResetNode(INT32 node)
+{
+	memset(&netnodes[node], 0, sizeof(*netnodes));
+	netnodes[node].player = -1;
+	netnodes[node].player2 = -1;
+}
 
 //
 // CL_ClearPlayer
@@ -3007,23 +3012,6 @@ void D_ClientServerInit(void)
 		SV_SpawnServer();
 }
 
-static void ResetNode(INT32 node)
-{
-	netnodes[node].ingame = false;
-	netnodes[node].numplayerswaiting = 0;
-
-	netnodes[node].tic = gametic;
-	netnodes[node].supposedtic = gametic;
-
-	netnodes[node].player = -1;
-	netnodes[node].player2 = -1;
-	netnodes[node].numplayers = 0;
-
-	netnodes[node].sendingsavegame = false;
-	netnodes[node].resendingsavegame = false;
-	netnodes[node].savegameresendcooldown = 0;
-}
-
 void SV_ResetServer(void)
 {
 	INT32 i;
-- 
GitLab


From ecacb14fc19fd312ca049a9df666c8b4c30f4274 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 29 Dec 2022 20:48:17 +0100
Subject: [PATCH 251/518] Move player-node unlinking to a function

---
 src/d_clisrv.c | 48 ++++++++++++++++++++++--------------------------
 1 file changed, 22 insertions(+), 26 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 3ab0df1d0e..1fee20a3f7 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2298,6 +2298,24 @@ void CL_ClearPlayer(INT32 playernum)
 	memset(playeraddress[playernum], 0, sizeof(*playeraddress));
 }
 
+static void UnlinkPlayerFromNode(INT32 playernum)
+{
+	INT32 node = playernode[playernum];
+
+	if (node == UINT8_MAX)
+		return;
+
+	playernode[playernum] = UINT8_MAX;
+
+	netnodes[node].numplayers--;
+	if (netnodes[node].numplayers <= 0)
+	{
+		netnodes[node].ingame = false;
+		Net_CloseConnection(node);
+		ResetNode(node);
+	}
+}
+
 //
 // CL_RemovePlayer
 //
@@ -2310,17 +2328,8 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
 	if (!playeringame[playernum])
 		return;
 
-	if (server && !demoplayback && playernode[playernum] != UINT8_MAX)
-	{
-		INT32 node = playernode[playernum];
-		netnodes[node].numplayers--;
-		if (netnodes[node].numplayers <= 0)
-		{
-			netnodes[node].ingame = false;
-			Net_CloseConnection(node);
-			ResetNode(node);
-		}
-	}
+	if (server)
+		UnlinkPlayerFromNode(playernum);
 
 	if (gametyperules & GTR_TEAMFLAGS)
 		P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you!
@@ -2392,7 +2401,6 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
 
 	// remove avatar of player
 	playeringame[playernum] = false;
-	playernode[playernum] = UINT8_MAX;
 	while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1)
 		doomcom->numslots--;
 
@@ -2920,20 +2928,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 	}
 	else if (keepbody)
 	{
-		if (server && !demoplayback && playernode[pnum] != UINT8_MAX)
-		{
-			INT32 node = playernode[pnum];
-			netnodes[node].numplayers--;
-			if (netnodes[node].numplayers <= 0)
-			{
-				netnodes[node].ingame = false;
-				Net_CloseConnection(node);
-				ResetNode(node);
-			}
-		}
-
-		playernode[pnum] = UINT8_MAX;
-
+		if (server)
+			UnlinkPlayerFromNode(pnum);
 		players[pnum].quittime = 1;
 	}
 	else
-- 
GitLab


From 2de6b623c09c33cca862b553ce925c8340cd5a4a Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 29 Dec 2022 21:28:53 +0100
Subject: [PATCH 252/518] Move sphere redistribution to its own function

---
 src/d_clisrv.c | 106 ++++++++++++++++++++++++++-----------------------
 1 file changed, 56 insertions(+), 50 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 1fee20a3f7..eee820db2d 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2316,6 +2316,61 @@ static void UnlinkPlayerFromNode(INT32 playernum)
 	}
 }
 
+// If in a special stage, redistribute the player's
+// spheres across the remaining players.
+// I feel like this shouldn't even be in this file at all, but well.
+static void RedistributeSpecialStageSpheres(INT32 playernum)
+{
+	INT32 i, count, sincrement, spheres, rincrement, rings;
+
+	if (!G_IsSpecialStage(gamemap))
+		return;
+
+	for (i = 0, count = 0; i < MAXPLAYERS; i++)
+	{
+		if (playeringame[i])
+			count++;
+	}
+
+	count--;
+	sincrement = spheres = players[playernum].spheres;
+	rincrement = rings = players[playernum].rings;
+
+	if (count)
+	{
+		sincrement /= count;
+		rincrement /= count;
+	}
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (playeringame[i] && i != playernum)
+		{
+			if (spheres < 2*sincrement)
+			{
+				P_GivePlayerSpheres(&players[i], spheres);
+				spheres = 0;
+			}
+			else
+			{
+				P_GivePlayerSpheres(&players[i], sincrement);
+				spheres -= sincrement;
+			}
+
+			if (rings < 2*rincrement)
+			{
+				P_GivePlayerRings(&players[i], rings);
+				rings = 0;
+			}
+			else
+			{
+				P_GivePlayerRings(&players[i], rincrement);
+				rings -= rincrement;
+			}
+		}
+	}
+}
+
 //
 // CL_RemovePlayer
 //
@@ -2334,56 +2389,7 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
 	if (gametyperules & GTR_TEAMFLAGS)
 		P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you!
 
-	// If in a special stage, redistribute the player's spheres across
-	// the remaining players.
-	if (G_IsSpecialStage(gamemap))
-	{
-		INT32 i, count, sincrement, spheres, rincrement, rings;
-
-		for (i = 0, count = 0; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i])
-				count++;
-		}
-
-		count--;
-		sincrement = spheres = players[playernum].spheres;
-		rincrement = rings = players[playernum].rings;
-
-		if (count)
-		{
-			sincrement /= count;
-			rincrement /= count;
-		}
-
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i] && i != playernum)
-			{
-				if (spheres < 2*sincrement)
-				{
-					P_GivePlayerSpheres(&players[i], spheres);
-					spheres = 0;
-				}
-				else
-				{
-					P_GivePlayerSpheres(&players[i], sincrement);
-					spheres -= sincrement;
-				}
-
-				if (rings < 2*rincrement)
-				{
-					P_GivePlayerRings(&players[i], rings);
-					rings = 0;
-				}
-				else
-				{
-					P_GivePlayerRings(&players[i], rincrement);
-					rings -= rincrement;
-				}
-			}
-		}
-	}
+	RedistributeSpecialStageSpheres(playernum);
 
 	LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting
 
-- 
GitLab


From 0053eaac009a1a675fcf57a7c67f488faf0f1882 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 29 Dec 2022 22:16:08 +0100
Subject: [PATCH 253/518] Fix sphere redistribution

---
 src/d_clisrv.c | 54 +++++++++++++++++++++++++-------------------------
 1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index eee820db2d..432001b80a 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2333,39 +2333,39 @@ static void RedistributeSpecialStageSpheres(INT32 playernum)
 	}
 
 	count--;
-	sincrement = spheres = players[playernum].spheres;
-	rincrement = rings = players[playernum].rings;
+	spheres = players[playernum].spheres;
+	rings = players[playernum].rings;
 
-	if (count)
+	while (count && (spheres || rings))
 	{
-		sincrement /= count;
-		rincrement /= count;
-	}
+		sincrement = max(spheres / count, 1);
+		rincrement = max(rings / count, 1);
 
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (playeringame[i] && i != playernum)
+		for (i = 0; i < MAXPLAYERS; i++)
 		{
-			if (spheres < 2*sincrement)
+			if (playeringame[i] && i != playernum)
 			{
-				P_GivePlayerSpheres(&players[i], spheres);
-				spheres = 0;
-			}
-			else
-			{
-				P_GivePlayerSpheres(&players[i], sincrement);
-				spheres -= sincrement;
-			}
+				if (spheres < sincrement)
+				{
+					P_GivePlayerSpheres(&players[i], spheres);
+					spheres = 0;
+				}
+				else
+				{
+					P_GivePlayerSpheres(&players[i], sincrement);
+					spheres -= sincrement;
+				}
 
-			if (rings < 2*rincrement)
-			{
-				P_GivePlayerRings(&players[i], rings);
-				rings = 0;
-			}
-			else
-			{
-				P_GivePlayerRings(&players[i], rincrement);
-				rings -= rincrement;
+				if (rings < rincrement)
+				{
+					P_GivePlayerRings(&players[i], rings);
+					rings = 0;
+				}
+				else
+				{
+					P_GivePlayerRings(&players[i], rincrement);
+					rings -= rincrement;
+				}
 			}
 		}
 	}
-- 
GitLab


From 394e925c16c7bab4fc8906046b3bf23a1ceee823 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 29 Dec 2022 22:30:26 +0100
Subject: [PATCH 254/518] Cleanup sphere redistribution

---
 src/d_clisrv.c | 55 ++++++++++++++++----------------------------------
 1 file changed, 17 insertions(+), 38 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 432001b80a..1b344a0de9 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2321,52 +2321,31 @@ static void UnlinkPlayerFromNode(INT32 playernum)
 // I feel like this shouldn't even be in this file at all, but well.
 static void RedistributeSpecialStageSpheres(INT32 playernum)
 {
-	INT32 i, count, sincrement, spheres, rincrement, rings;
-
-	if (!G_IsSpecialStage(gamemap))
+	if (!G_IsSpecialStage(gamemap) || D_NumPlayers() <= 1)
 		return;
 
-	for (i = 0, count = 0; i < MAXPLAYERS; i++)
-	{
-		if (playeringame[i])
-			count++;
-	}
-
-	count--;
-	spheres = players[playernum].spheres;
-	rings = players[playernum].rings;
+	INT32 count = D_NumPlayers() - 1;
+	INT32 spheres = players[playernum].spheres;
+	INT32 rings = players[playernum].rings;
 
-	while (count && (spheres || rings))
+	while (spheres || rings)
 	{
-		sincrement = max(spheres / count, 1);
-		rincrement = max(rings / count, 1);
+		INT32 sincrement = max(spheres / count, 1);
+		INT32 rincrement = max(rings / count, 1);
 
+		INT32 i, n;
 		for (i = 0; i < MAXPLAYERS; i++)
 		{
-			if (playeringame[i] && i != playernum)
-			{
-				if (spheres < sincrement)
-				{
-					P_GivePlayerSpheres(&players[i], spheres);
-					spheres = 0;
-				}
-				else
-				{
-					P_GivePlayerSpheres(&players[i], sincrement);
-					spheres -= sincrement;
-				}
+			if (!playeringame[i] || i == playernum)
+				continue;
 
-				if (rings < rincrement)
-				{
-					P_GivePlayerRings(&players[i], rings);
-					rings = 0;
-				}
-				else
-				{
-					P_GivePlayerRings(&players[i], rincrement);
-					rings -= rincrement;
-				}
-			}
+			n = min(spheres, sincrement);
+			P_GivePlayerSpheres(&players[i], n);
+			spheres -= n;
+
+			n = min(rings, rincrement);
+			P_GivePlayerRings(&players[i], n);
+			rings -= n;
 		}
 	}
 }
-- 
GitLab


From 160db6383c1f04075491a0522196e9edd7b7d2c7 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 30 Dec 2022 00:24:06 +0100
Subject: [PATCH 255/518] Remove useless return value for SV_AddWaitingPlayers

---
 src/d_clisrv.c | 12 +++---------
 src/d_clisrv.h |  2 +-
 src/d_netcmd.c |  3 +--
 3 files changed, 5 insertions(+), 12 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 1b344a0de9..0c6960236c 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -3262,9 +3262,9 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 		LUA_HookInt(newplayernum, HOOK(PlayerJoin));
 }
 
-static boolean SV_AddWaitingPlayers(const char *name, const char *name2)
+static void SV_AddWaitingPlayers(const char *name, const char *name2)
 {
-	INT32 node, n, newplayer = false;
+	INT32 node, n;
 	UINT8 buf[2 + MAXPLAYERNAME];
 	UINT8 *p;
 	INT32 newplayernum;
@@ -3274,8 +3274,6 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2)
 		// splitscreen can allow 2 player in one node
 		for (; netnodes[node].numplayerswaiting > 0; netnodes[node].numplayerswaiting--)
 		{
-			newplayer = true;
-
 			newplayernum = FindRejoinerNum(node);
 			if (newplayernum == -1)
 			{
@@ -3320,8 +3318,6 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2)
 			DEBFILE(va("Server added player %d node %d\n", newplayernum, node));
 		}
 	}
-
-	return newplayer;
 }
 
 void CL_AddSplitscreenPlayer(void)
@@ -3344,7 +3340,7 @@ boolean Playing(void)
 	return (server && serverrunning) || (client && cl_mode == CL_CONNECTED);
 }
 
-boolean SV_SpawnServer(void)
+void SV_SpawnServer(void)
 {
 	if (demoplayback)
 		G_StopDemo(); // reset engine parameter
@@ -3371,8 +3367,6 @@ boolean SV_SpawnServer(void)
 			CL_ConnectToServer();
 		else doomcom->numslots = 1;
 	}
-
-	return SV_AddWaitingPlayers(cv_playername.zstring, cv_playername2.zstring);
 }
 
 void SV_StopServer(void)
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index e078641224..b3ee95c0cc 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -412,7 +412,7 @@ void SendKick(UINT8 playernum, UINT8 msg);
 void NetUpdate(void);
 
 void SV_StartSinglePlayerServer(void);
-boolean SV_SpawnServer(void);
+void SV_SpawnServer(void);
 void SV_StopServer(void);
 void SV_ResetServer(void);
 void CL_AddSplitscreenPlayer(void);
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 5f02bc2dea..243501ba4c 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1850,8 +1850,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
 		// reset players if there is a new one
 		if (!IsPlayerAdmin(consoleplayer))
 		{
-			if (SV_SpawnServer())
-				buf[0] &= ~(1<<1);
+			SV_SpawnServer();
 			if (!Playing()) // you failed to start a server somehow, so cancel the map change
 				return;
 		}
-- 
GitLab


From 6ab9b9ae8a29fda62129650c757bd9e2d72a2e64 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 30 Dec 2022 01:48:26 +0100
Subject: [PATCH 256/518] Remove useless condition

---
 src/d_clisrv.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 0c6960236c..bc09925391 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -3563,17 +3563,14 @@ static void HandleConnect(SINT8 node)
 			DEBFILE("new node joined\n");
 		}
 #ifndef NONET
-		if (netnodes[node].numplayerswaiting)
+		if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode)
 		{
-			if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode)
-			{
-				SV_SendSaveGame(node, false); // send a complete game state
-				DEBFILE("send savegame\n");
-			}
-			SV_AddWaitingPlayers(names[0], names[1]);
-			joindelay += cv_joindelay.value * TICRATE;
-			player_joining = true;
+			SV_SendSaveGame(node, false); // send a complete game state
+			DEBFILE("send savegame\n");
 		}
+		SV_AddWaitingPlayers(names[0], names[1]);
+		joindelay += cv_joindelay.value * TICRATE;
+		player_joining = true;
 #endif
 	}
 }
-- 
GitLab


From f501c4b25072a063141649353f86bfbada89d83e Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 30 Dec 2022 02:46:59 +0100
Subject: [PATCH 257/518] Remove unneeded numplayerswaiting field

---
 src/d_clisrv.c | 92 ++++++++++++++++++++++++--------------------------
 src/d_net.h    |  1 -
 2 files changed, 44 insertions(+), 49 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index bc09925391..7aafdf539f 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -3262,62 +3262,54 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 		LUA_HookInt(newplayernum, HOOK(PlayerJoin));
 }
 
-static void SV_AddWaitingPlayers(const char *name, const char *name2)
+static void SV_AddPlayer(SINT8 node, const char *name)
 {
-	INT32 node, n;
+	INT32 n;
 	UINT8 buf[2 + MAXPLAYERNAME];
 	UINT8 *p;
 	INT32 newplayernum;
 
-	for (node = 0; node < MAXNETNODES; node++)
+	newplayernum = FindRejoinerNum(node);
+	if (newplayernum == -1)
 	{
-		// splitscreen can allow 2 player in one node
-		for (; netnodes[node].numplayerswaiting > 0; netnodes[node].numplayerswaiting--)
+		// search for a free playernum
+		// we can't use playeringame since it is not updated here
+		for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++)
 		{
-			newplayernum = FindRejoinerNum(node);
-			if (newplayernum == -1)
-			{
-				// search for a free playernum
-				// we can't use playeringame since it is not updated here
-				for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++)
-				{
-					if (playeringame[newplayernum])
-						continue;
-					for (n = 0; n < MAXNETNODES; n++)
-						if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum)
-							break;
-					if (n == MAXNETNODES)
-						break;
-				}
-			}
+			if (playeringame[newplayernum])
+				continue;
+			for (n = 0; n < MAXNETNODES; n++)
+				if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum)
+					break;
+			if (n == MAXNETNODES)
+				break;
+		}
+	}
 
-			// should never happen since we check the playernum
-			// before accepting the join
-			I_Assert(newplayernum < MAXPLAYERS);
+	// should never happen since we check the playernum
+	// before accepting the join
+	I_Assert(newplayernum < MAXPLAYERS);
 
-			playernode[newplayernum] = (UINT8)node;
+	playernode[newplayernum] = (UINT8)node;
 
-			p = buf + 2;
-			buf[0] = (UINT8)node;
-			buf[1] = newplayernum;
-			if (netnodes[node].numplayers < 1)
-			{
-				netnodes[node].player = newplayernum;
-				WRITESTRINGN(p, name, MAXPLAYERNAME);
-			}
-			else
-			{
-				netnodes[node].player2 = newplayernum;
-				buf[1] |= 0x80;
-				WRITESTRINGN(p, name2, MAXPLAYERNAME);
-			}
-			netnodes[node].numplayers++;
+	p = buf + 2;
+	buf[0] = (UINT8)node;
+	buf[1] = newplayernum;
+	if (netnodes[node].numplayers < 1)
+	{
+		netnodes[node].player = newplayernum;
+	}
+	else
+	{
+		netnodes[node].player2 = newplayernum;
+		buf[1] |= 0x80;
+	}
+	WRITESTRINGN(p, name, MAXPLAYERNAME);
+	netnodes[node].numplayers++;
 
-			SendNetXCmd(XD_ADDPLAYER, &buf, p - buf);
+	SendNetXCmd(XD_ADDPLAYER, &buf, p - buf);
 
-			DEBFILE(va("Server added player %d node %d\n", newplayernum, node));
-		}
-	}
+	DEBFILE(va("Server added player %d node %d\n", newplayernum, node));
 }
 
 void CL_AddSplitscreenPlayer(void)
@@ -3522,11 +3514,12 @@ static void HandleConnect(SINT8 node)
 		SV_SendRefuse(node, refuse);
 	else
 	{
+		INT32 numplayers = netbuffer->u.clientcfg.localplayers;
 #ifndef NONET
 		boolean newnode = false;
 #endif
 
-		for (i = 0; i < netbuffer->u.clientcfg.localplayers - netnodes[node].numplayers; i++)
+		for (i = 0; i < numplayers; i++)
 		{
 			strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1);
 			if (!EnsurePlayerNameIsGood(names[i], rejoinernum))
@@ -3537,7 +3530,6 @@ static void HandleConnect(SINT8 node)
 		}
 
 		// client authorised to join
-		netnodes[node].numplayerswaiting = (UINT8)(netbuffer->u.clientcfg.localplayers - netnodes[node].numplayers);
 		if (!netnodes[node].ingame)
 		{
 			gamestate_t backupstate = gamestate;
@@ -3568,7 +3560,12 @@ static void HandleConnect(SINT8 node)
 			SV_SendSaveGame(node, false); // send a complete game state
 			DEBFILE("send savegame\n");
 		}
-		SV_AddWaitingPlayers(names[0], names[1]);
+
+		// Splitscreen can allow 2 players in one node
+		SV_AddPlayer(node, names[0]);
+		if (numplayers > 1)
+			SV_AddPlayer(node, names[1]);
+
 		joindelay += cv_joindelay.value * TICRATE;
 		player_joining = true;
 #endif
@@ -3993,7 +3990,6 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole)
 
 	// nodeingame will be put false in the execution of kick command
 	// this allow to send some packets to the quitting client to have their ack back
-	netnodes[node].numplayerswaiting = 0;
 	if (netconsole != -1 && playeringame[netconsole])
 	{
 		UINT8 kickmsg;
diff --git a/src/d_net.h b/src/d_net.h
index 9eee53894d..c6c9e2894c 100644
--- a/src/d_net.h
+++ b/src/d_net.h
@@ -45,7 +45,6 @@ typedef struct netnode_s
 	SINT8 player;
 	SINT8 player2; // say the numplayer for this node if any (splitscreen)
 	UINT8 numplayers; // used specialy for scplitscreen
-	UINT8 numplayerswaiting;
 
 	tic_t tic; // what tic the client have received
 	tic_t supposedtic; // nettics prevision for smaller packet
-- 
GitLab


From b66353932d80f27ba780ac474061a8c1cc841708 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 30 Dec 2022 13:10:01 +0100
Subject: [PATCH 258/518] Remove broken joinnextround console variable

---
 src/d_clisrv.c | 6 ------
 src/d_clisrv.h | 2 +-
 src/d_netcmd.c | 1 -
 3 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 7aafdf539f..31a19001ad 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2925,7 +2925,6 @@ static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, N
 consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL);
 
 consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
-consvar_t cv_joinnextround = CVAR_INIT ("joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); /// \todo not done
 static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}};
 consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL);
 static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}};
@@ -3532,17 +3531,13 @@ static void HandleConnect(SINT8 node)
 		// client authorised to join
 		if (!netnodes[node].ingame)
 		{
-			gamestate_t backupstate = gamestate;
 #ifndef NONET
 			newnode = true;
 #endif
 			SV_AddNode(node);
 
-			if (cv_joinnextround.value && gameaction == ga_nothing)
-				G_SetGamestate(GS_WAITINGPLAYERS);
 			if (!SV_SendServerConfig(node))
 			{
-				G_SetGamestate(backupstate);
 				/// \note Shouldn't SV_SendRefuse be called before ResetNode?
 				ResetNode(node);
 				SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
@@ -3551,7 +3546,6 @@ static void HandleConnect(SINT8 node)
 			}
 			//if (gamestate != GS_LEVEL) // GS_INTERMISSION, etc?
 			//	SV_SendPlayerConfigs(node); // send bare minimum player info
-			G_SetGamestate(backupstate);
 			DEBFILE("new node joined\n");
 		}
 #ifndef NONET
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index b3ee95c0cc..99d8599240 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -394,7 +394,7 @@ extern UINT32 realpingtable[MAXPLAYERS];
 extern UINT32 playerpingtable[MAXPLAYERS];
 extern tic_t servermaxping;
 
-extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_joindelay, cv_rejointimeout;
+extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_maxplayers, cv_joindelay, cv_rejointimeout;
 extern consvar_t cv_resynchattempts, cv_blamecfail;
 extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
 
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 243501ba4c..64f478ff7a 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -630,7 +630,6 @@ void D_RegisterServerCommands(void)
 	CV_RegisterVar(&cv_downloadspeed);
 #ifndef NONET
 	CV_RegisterVar(&cv_allownewplayer);
-	CV_RegisterVar(&cv_joinnextround);
 	CV_RegisterVar(&cv_showjoinaddress);
 	CV_RegisterVar(&cv_blamecfail);
 #endif
-- 
GitLab


From d816e4d111499a820f09df3fbc9acaa57a1859bc Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 30 Dec 2022 14:01:03 +0100
Subject: [PATCH 259/518] Cleanup HandleConnect

---
 src/d_clisrv.c | 80 +++++++++++++++++++++-----------------------------
 1 file changed, 33 insertions(+), 47 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 31a19001ad..699c372d71 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -3501,69 +3501,55 @@ ConnectionRefused (SINT8 node, INT32 rejoinernum)
 static void HandleConnect(SINT8 node)
 {
 	char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
+	INT32 numplayers = netbuffer->u.clientcfg.localplayers;
 	INT32 rejoinernum;
 	INT32 i;
-	const char *refuse;
 
 	rejoinernum = FindRejoinerNum(node);
 
-	refuse = ConnectionRefused(node, rejoinernum);
-
+	const char *refuse = ConnectionRefused(node, rejoinernum);
 	if (refuse)
-		SV_SendRefuse(node, refuse);
-	else
 	{
-		INT32 numplayers = netbuffer->u.clientcfg.localplayers;
-#ifndef NONET
-		boolean newnode = false;
-#endif
+		SV_SendRefuse(node, refuse);
+		return;
+	}
 
-		for (i = 0; i < numplayers; i++)
+	for (i = 0; i < numplayers; i++)
+	{
+		strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1);
+		if (!EnsurePlayerNameIsGood(names[i], rejoinernum))
 		{
-			strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1);
-			if (!EnsurePlayerNameIsGood(names[i], rejoinernum))
-			{
-				SV_SendRefuse(node, "Bad player name");
-				return;
-			}
+			SV_SendRefuse(node, "Bad player name");
+			return;
 		}
+	}
 
-		// client authorised to join
-		if (!netnodes[node].ingame)
-		{
-#ifndef NONET
-			newnode = true;
-#endif
-			SV_AddNode(node);
+	SV_AddNode(node);
+
+	if (!SV_SendServerConfig(node))
+	{
+		/// \note Shouldn't SV_SendRefuse be called before ResetNode?
+		ResetNode(node);
+		SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
+		/// \todo fix this !!!
+		return; // restart the while
+	}
+	DEBFILE("new node joined\n");
 
-			if (!SV_SendServerConfig(node))
-			{
-				/// \note Shouldn't SV_SendRefuse be called before ResetNode?
-				ResetNode(node);
-				SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
-				/// \todo fix this !!!
-				return; // restart the while
-			}
-			//if (gamestate != GS_LEVEL) // GS_INTERMISSION, etc?
-			//	SV_SendPlayerConfigs(node); // send bare minimum player info
-			DEBFILE("new node joined\n");
-		}
 #ifndef NONET
-		if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode)
-		{
-			SV_SendSaveGame(node, false); // send a complete game state
-			DEBFILE("send savegame\n");
-		}
+	if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)
+	{
+		SV_SendSaveGame(node, false); // send a complete game state
+		DEBFILE("send savegame\n");
+	}
 
-		// Splitscreen can allow 2 players in one node
-		SV_AddPlayer(node, names[0]);
-		if (numplayers > 1)
-			SV_AddPlayer(node, names[1]);
+	// Splitscreen can allow 2 players in one node
+	for (i = 0; i < numplayers; i++)
+		SV_AddPlayer(node, names[i]);
 
-		joindelay += cv_joindelay.value * TICRATE;
-		player_joining = true;
+	joindelay += cv_joindelay.value * TICRATE;
+	player_joining = true;
 #endif
-	}
 }
 
 /** Called when a PT_SERVERSHUTDOWN packet is received
-- 
GitLab


From 7b96efc17964972753bc38ca310dcd64732d9ca9 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 30 Dec 2022 14:01:37 +0100
Subject: [PATCH 260/518] Rename ConnectionRefused to be more descriptive

---
 src/d_clisrv.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 699c372d71..d7d02f9bbe 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -3422,7 +3422,7 @@ static size_t TotalTextCmdPerTic(tic_t tic)
 }
 
 static const char *
-ConnectionRefused (SINT8 node, INT32 rejoinernum)
+GetRefuseMessage (SINT8 node, INT32 rejoinernum)
 {
 	clientconfig_pak *cc = &netbuffer->u.clientcfg;
 
@@ -3507,7 +3507,7 @@ static void HandleConnect(SINT8 node)
 
 	rejoinernum = FindRejoinerNum(node);
 
-	const char *refuse = ConnectionRefused(node, rejoinernum);
+	const char *refuse = GetRefuseMessage(node, rejoinernum);
 	if (refuse)
 	{
 		SV_SendRefuse(node, refuse);
-- 
GitLab


From 1f4c71739ab57fbd035bd1f13d178b981a2836f4 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 30 Dec 2022 14:03:32 +0100
Subject: [PATCH 261/518] Ignore duplicate PT_CLIENTJOIN packets

---
 src/d_clisrv.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index d7d02f9bbe..b25e34c566 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -3505,6 +3505,10 @@ static void HandleConnect(SINT8 node)
 	INT32 rejoinernum;
 	INT32 i;
 
+	// Ignore duplicate packets
+	if (netnodes[node].ingame)
+		return;
+
 	rejoinernum = FindRejoinerNum(node);
 
 	const char *refuse = GetRefuseMessage(node, rejoinernum);
-- 
GitLab


From 07bc9222737a026859bc99d56b598c8973a66bb9 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 30 Dec 2022 15:07:57 +0100
Subject: [PATCH 262/518] Delete netcode

Just kidding, I just deleted NONET
---
 src/Makefile               |   1 -
 src/Makefile.d/features.mk |   4 +-
 src/Makefile.d/win32.mk    |   2 -
 src/d_clisrv.c             |  85 +------------
 src/d_clisrv.h             |   2 -
 src/d_net.c                |  48 --------
 src/d_net.h                |   2 -
 src/d_netcmd.c             |   2 -
 src/d_netfil.c             |   6 -
 src/d_netfil.h             |   2 -
 src/deh_tables.c           |   2 +-
 src/doomdef.h              |   3 +-
 src/hu_stuff.c             |  31 -----
 src/i_tcp.c                | 238 ++++++++++++++-----------------------
 src/m_menu.c               |  57 +--------
 src/m_menu.h               |   2 +-
 src/mserv.c                |   6 -
 src/sdl/i_system.c         |   4 -
 18 files changed, 97 insertions(+), 400 deletions(-)

diff --git a/src/Makefile b/src/Makefile
index 36b1a7efab..7fe7f8990f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -64,7 +64,6 @@
 #
 # Netplay incompatible
 # --------------------
-# NONET=1 - Disable online capability.
 # NOMD5=1 - Disable MD5 checksum (validation tool).
 # NOPOSTPROCESSING=1 - ?
 # MOBJCONSISTANCY=1 - ??
diff --git a/src/Makefile.d/features.mk b/src/Makefile.d/features.mk
index 8ba33383bb..9acbb4d862 100644
--- a/src/Makefile.d/features.mk
+++ b/src/Makefile.d/features.mk
@@ -3,7 +3,7 @@
 #
 
 passthru_opts+=\
-	NONET NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\
+	NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\
 	MOBJCONSISTANCY PACKETDROP ZDEBUG\
 	HAVE_MINIUPNPC\
 
@@ -46,13 +46,11 @@ sources+=apng.c
 endif
 endif
 
-ifndef NONET
 ifndef NOCURL
 CURLCONFIG?=curl-config
 $(eval $(call Configure,CURL,$(CURLCONFIG)))
 opts+=-DHAVE_CURL
 endif
-endif
 
 ifdef HAVE_MINIUPNPC
 libs+=-lminiupnpc
diff --git a/src/Makefile.d/win32.mk b/src/Makefile.d/win32.mk
index 0e48ed6835..e7269e1e76 100644
--- a/src/Makefile.d/win32.mk
+++ b/src/Makefile.d/win32.mk
@@ -35,12 +35,10 @@ libs+=-lws2_32
 endif
 endif
 
-ifndef NONET
 ifndef MINGW64 # miniupnc is broken with MINGW64
 opts+=-I../libs -DSTATIC_MINIUPNPC
 libs+=-L../libs/miniupnpc/mingw$(32) -lws2_32 -liphlpapi
 endif
-endif
 
 ifndef MINGW64
 32=32
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index b25e34c566..cb1ccd5398 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -53,12 +53,10 @@
 // aaaaaa
 #include "i_gamepad.h"
 
-#ifndef NONET
 // cl loading screen
 #include "v_video.h"
 #include "f_finale.h"
 #include "snake.h"
-#endif
 
 //
 // NETWORKING
@@ -535,7 +533,6 @@ static cl_mode_t cl_mode = CL_SEARCHING;
 
 static UINT16 cl_lastcheckedfilecount = 0;	// used for full file list
 
-#ifndef NONET
 static void *snake = NULL;
 
 static void CL_DrawConnectionStatusBox(void)
@@ -716,7 +713,6 @@ static inline void CL_DrawConnectionStatus(void)
 		}
 	}
 }
-#endif
 
 static boolean CL_AskFileList(INT32 firstfile)
 {
@@ -983,7 +979,6 @@ static boolean SV_SendServerConfig(INT32 node)
 	return waspacketsent;
 }
 
-#ifndef NONET
 #define SAVEGAMESIZE (768*1024)
 
 static boolean SV_ResendingSavegameToAnyone(void)
@@ -1210,9 +1205,7 @@ static void CL_ReloadReceivedSavegame(void)
 
 	CONS_Printf(M_GetText("Game state reloaded\n"));
 }
-#endif
 
-#ifndef NONET
 static void SendAskInfo(INT32 node)
 {
 	const tic_t asktime = I_GetTime();
@@ -1430,12 +1423,8 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
 #endif/*MASTERSERVER*/
 }
 
-#endif // ifndef NONET
-
 static void M_ConfirmConnect(event_t *ev)
 {
-#ifndef NONET
-
 	if (ev->type == ev_keydown || ev->type == ev_gamepad_down)
 	{
 		if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A))
@@ -1459,9 +1448,6 @@ static void M_ConfirmConnect(event_t *ev)
 			M_ClearMenus(true);
 		}
 	}
-#else
-	(void)ev;
-#endif
 }
 
 static boolean CL_FinishedFileList(void)
@@ -1541,7 +1527,6 @@ static boolean CL_FinishedFileList(void)
 			return false;
 		}
 
-#ifndef NONET
 		downloadcompletednum = 0;
 		downloadcompletedsize = 0;
 		totalfilesrequestednum = 0;
@@ -1561,7 +1546,6 @@ static boolean CL_FinishedFileList(void)
 			downloadsize = Z_StrDup(va("%uM",totalfilesrequestedsize>>20));
 		else
 			downloadsize = Z_StrDup(va("%uK",totalfilesrequestedsize>>10));
-#endif
 
 		if (serverisfull)
 			M_StartMessage(va(M_GetText(
@@ -1586,7 +1570,6 @@ static boolean CL_FinishedFileList(void)
 	return true;
 }
 
-#ifndef NONET
 static const char * InvalidServerReason (serverinfo_pak *info)
 {
 #define EOT "\nPress ESC\n"
@@ -1650,7 +1633,6 @@ static const char * InvalidServerReason (serverinfo_pak *info)
 
 #undef EOT
 }
-#endif // ifndef NONET
 
 /** Called by CL_ServerConnectionTicker
   *
@@ -1662,7 +1644,6 @@ static const char * InvalidServerReason (serverinfo_pak *info)
   */
 static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
 {
-#ifndef NONET
 	INT32 i;
 
 	// serverlist is updated by GetPacket function
@@ -1732,11 +1713,6 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
 		SendAskInfo(servernode);
 		*asksent = I_GetTime();
 	}
-#else
-	(void)asksent;
-	// No netgames, so we skip this state.
-	cl_mode = CL_ASKJOIN;
-#endif // ifndef NONET/else
 
 	return true;
 }
@@ -1756,10 +1732,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 	boolean waitmore;
 	INT32 i;
 
-#ifdef NONET
-	(void)tmpsave;
-#endif
-
 	switch (cl_mode)
 	{
 		case CL_SEARCHING:
@@ -1824,12 +1796,12 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 				), NULL, MM_NOTHING);
 				return false;
 			}
-#ifndef NONET
+
 			// prepare structures to save the file
 			// WARNING: this can be useless in case of server not in GS_LEVEL
 			// but since the network layer doesn't provide ordered packets...
 			CL_PrepareDownloadSaveGame(tmpsave);
-#endif
+
 			if (I_GetTime() >= *asksent && CL_SendJoin())
 			{
 				*asksent = I_GetTime() + NEWTICRATE*3;
@@ -1842,7 +1814,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 				cl_mode = CL_ASKJOIN;
 			}
 			break;
-#ifndef NONET
 		case CL_DOWNLOADSAVEGAME:
 			// At this state, the first (and only) needed file is the gamestate
 			if (fileneeded[0].status == FS_FOUND)
@@ -1853,7 +1824,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 			} // don't break case continue to CL_CONNECTED
 			else
 				break;
-#endif
 
 		case CL_CONNECTED:
 		case CL_CONFIRMCONNECT: //logic is handled by M_ConfirmConnect
@@ -1898,10 +1868,8 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 			memset(gamekeydown, 0, NUMKEYS);
 			return false;
 		}
-#ifndef NONET
 		else if (cl_mode == CL_DOWNLOADFILES && snake)
 			Snake_Update(snake);
-#endif
 
 		if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME))
 			FileReceiveTicker();
@@ -1912,7 +1880,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 		//FileSendTicker();
 		*oldtic = I_GetTime();
 
-#ifndef NONET
 		if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED)
 		{
 			if (!snake)
@@ -1935,10 +1902,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 			S_UpdateSounds();
 			S_UpdateClosedCaptions();
 		}
-#else
-		CON_Drawer();
-		I_UpdateNoVsync();
-#endif
 	}
 	else
 	{
@@ -1958,22 +1921,18 @@ static void CL_ConnectToServer(void)
 {
 	INT32 pnumnodes, nodewaited = doomcom->numnodes, i;
 	tic_t oldtic;
-#ifndef NONET
 	tic_t asksent;
 	char tmpsave[256];
 
 	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
 
 	lastfilenum = -1;
-#endif
 
 	cl_mode = CL_SEARCHING;
 
-#ifndef NONET
 	// Don't get a corrupt savegame error because tmpsave already exists
 	if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
 		I_Error("Can't delete %s\n", tmpsave);
-#endif
 
 	if (netgame)
 	{
@@ -1994,7 +1953,6 @@ static void CL_ConnectToServer(void)
 	pnumnodes = 1;
 	oldtic = I_GetTime() - 1;
 
-#ifndef NONET
 	asksent = (tic_t) - TICRATE;
 	firstconnectattempttime = I_GetTime();
 
@@ -2010,16 +1968,11 @@ static void CL_ConnectToServer(void)
 		 serverlist[i].info.version%100, serverlist[i].info.subversion);
 	}
 	SL_ClearServerList(servernode);
-#endif
 
 	do
 	{
 		// If the connection was aborted for some reason, leave
-#ifndef NONET
 		if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent))
-#else
-		if (!CL_ServerConnectionTicker((char*)NULL, &oldtic, (tic_t *)NULL))
-#endif
 			return;
 
 		if (server)
@@ -2037,7 +1990,6 @@ static void CL_ConnectToServer(void)
 	displayplayer = consoleplayer;
 }
 
-#ifndef NONET
 typedef struct banreason_s
 {
 	char *reason;
@@ -2276,7 +2228,6 @@ static void Command_connect(void)
 	botskin = 0;
 	CL_ConnectToServer();
 }
-#endif
 
 static void ResetNode(INT32 node)
 {
@@ -2437,10 +2388,8 @@ void CL_Reset(void)
 	FreeFileNeeded();
 	fileneedednum = 0;
 
-#ifndef NONET
 	totalfilesrequestednum = 0;
 	totalfilesrequestedsize = 0;
-#endif
 	firstconnectattempttime = 0;
 	serverisfull = false;
 	connectiontimeout = (tic_t)cv_nettimeout.value; //reset this temporary hack
@@ -2448,7 +2397,6 @@ void CL_Reset(void)
 	// D_StartTitle should get done now, but the calling function will handle it
 }
 
-#ifndef NONET
 static void Command_GetPlayerNum(void)
 {
 	INT32 i;
@@ -2726,7 +2674,6 @@ static void Command_ResendGamestate(void)
 		return;
 	}
 }
-#endif
 
 static void Got_KickCmd(UINT8 **p, INT32 playernum)
 {
@@ -2810,10 +2757,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 	{
 		if (I_Ban && !I_Ban(playernode[(INT32)pnum]))
 			CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
-#ifndef NONET
 		else
 			Ban_Add(reason);
-#endif
 	}
 
 	switch (msg)
@@ -2953,7 +2898,6 @@ void D_ClientServerInit(void)
 	DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n",
 		VERSION/100, VERSION%100, SUBVERSION));
 
-#ifndef NONET
 	COM_AddCommand("getplayernum", Command_GetPlayerNum);
 	COM_AddCommand("kick", Command_Kick);
 	COM_AddCommand("ban", Command_Ban);
@@ -2970,17 +2914,14 @@ void D_ClientServerInit(void)
 #endif
 #ifdef _DEBUG
 	COM_AddCommand("numnodes", Command_Numnodes);
-#endif
 #endif
 
 	RegisterNetXCmd(XD_KICK, Got_KickCmd);
 	RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer);
-#ifndef NONET
 #ifdef DUMPCONSISTENCY
 	CV_RegisterVar(&cv_dumpconsistency);
 #endif
 	Ban_Load_File(false);
-#endif
 
 	gametic = 0;
 	localgametic = 0;
@@ -3540,7 +3481,6 @@ static void HandleConnect(SINT8 node)
 	}
 	DEBFILE("new node joined\n");
 
-#ifndef NONET
 	if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)
 	{
 		SV_SendSaveGame(node, false); // send a complete game state
@@ -3553,7 +3493,6 @@ static void HandleConnect(SINT8 node)
 
 	joindelay += cv_joindelay.value * TICRATE;
 	player_joining = true;
-#endif
 }
 
 /** Called when a PT_SERVERSHUTDOWN packet is received
@@ -3586,7 +3525,6 @@ static void HandleTimeout(SINT8 node)
 	M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
 }
 
-#ifndef NONET
 /** Called when a PT_SERVERINFO packet is received
   *
   * \param node The packet sender
@@ -3608,7 +3546,6 @@ static void HandleServerInfo(SINT8 node)
 
 	SL_InsertServer(&netbuffer->u.serverinfo, node);
 }
-#endif
 
 // Helper function for packets that should only be sent by the server
 // If it is NOT from the server, bail out and close the connection!
@@ -3744,14 +3681,9 @@ static void PT_ServerCFG(SINT8 node)
 		playernode[(UINT8)serverplayer] = servernode;
 
 	if (netgame)
-#ifndef NONET
 		CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n"));
-#else
-		CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n"));
-#endif
 	DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode));
 
-#ifndef NONET
 	/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
 	///       Shouldn't them be downloaded even at intermission time?
 	///       Also, according to HandleConnect, the server will send the savegame even during intermission...
@@ -3759,7 +3691,6 @@ static void PT_ServerCFG(SINT8 node)
 		netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
 		cl_mode = CL_DOWNLOADSAVEGAME;
 	else
-#endif
 		cl_mode = CL_CONNECTED;
 }
 
@@ -3823,9 +3754,7 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole)
 	// Check player consistancy during the level
 	if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL
 		&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
-#ifndef NONET
 		&& !SV_ResendingSavegameToAnyone()
-#endif
 		&& !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime())
 	{
 		if (cv_resynchattempts.value)
@@ -4000,7 +3929,6 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole)
 
 static void PT_CanReceiveGamestate(SINT8 node)
 {
-#ifndef NONET
 	if (client || netnodes[node].sendingsavegame)
 		return;
 
@@ -4008,9 +3936,6 @@ static void PT_CanReceiveGamestate(SINT8 node)
 
 	SV_SendSaveGame(node, true); // Resend a complete game state
 	netnodes[node].resendingsavegame = true;
-#else
-	(void)node;
-#endif
 }
 
 static void PT_AskLuaFile(SINT8 node)
@@ -4134,7 +4059,6 @@ static void PT_WillResendGamestate(SINT8 node)
 {
 	(void)node;
 
-#ifndef NONET
 	char tmpsave[256];
 
 	if (server || cl_redownloadinggamestate)
@@ -4157,7 +4081,6 @@ static void PT_WillResendGamestate(SINT8 node)
 	CL_PrepareDownloadSaveGame(tmpsave);
 
 	cl_redownloadinggamestate = true;
-#endif
 }
 
 static void PT_SendingLuaFile(SINT8 node)
@@ -4296,13 +4219,11 @@ static void GetPackets(void)
 			}
 		}
 
-#ifndef NONET
 		if (netbuffer->packettype == PT_SERVERINFO)
 		{
 			HandleServerInfo(node);
 			continue;
 		}
-#endif
 
 		if (netbuffer->packettype == PT_PLAYERINFO)
 			continue; // We do nothing with PLAYERINFO, that's for the MS browser.
@@ -4906,11 +4827,9 @@ void NetUpdate(void)
 
 	if (client)
 	{
-#ifndef NONET
 		// If the client just finished redownloading the game state, load it
 		if (cl_redownloadinggamestate && fileneeded[0].status == FS_FOUND)
 			CL_ReloadReceivedSavegame();
-#endif
 
 		CL_SendClientCmd(); // Send tic cmd
 		hu_redownloadinggamestate = cl_redownloadinggamestate;
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index 99d8599240..0d6add13a3 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -437,11 +437,9 @@ boolean TryRunTics(tic_t realtic);
 /*boolean AddLmpExtradata(UINT8 **demo_p, INT32 playernum);
 void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum);*/
 
-#ifndef NONET
 // translate a playername in a player number return -1 if not found and
 // print a error message in the console
 SINT8 nametonum(const char *name);
-#endif
 
 extern char motd[254], server_context[8];
 extern UINT8 playernode[MAXPLAYERS];
diff --git a/src/d_net.c b/src/d_net.c
index a7e1eb16d6..9e3759d32c 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -138,7 +138,6 @@ boolean Net_GetNetStat(void)
 #define URGENTFREESLOTNUM 10
 #define ACKTOSENDTIMEOUT (TICRATE/11)
 
-#ifndef NONET
 typedef struct
 {
 	UINT8 acknum;
@@ -152,7 +151,6 @@ typedef struct
 		doomdata_t data;
 	} pak;
 } ackpak_t;
-#endif
 
 typedef enum
 {
@@ -160,10 +158,8 @@ typedef enum
 	NF_TIMEOUT = 2, // Flag is set when the node got a timeout
 } node_flags_t;
 
-#ifndef NONET
 // Table of packets that were not acknowleged can be resent (the sender window)
 static ackpak_t ackpak[MAXACKPACKETS];
-#endif
 
 typedef struct
 {
@@ -191,7 +187,6 @@ typedef struct
 static node_t nodes[MAXNETNODES];
 #define NODETIMEOUT 14
 
-#ifndef NONET
 // return <0 if a < b (mod 256)
 //         0 if a = n (mod 256)
 //        >0 if a > b (mod 256)
@@ -426,21 +421,15 @@ static boolean Processackpak(void)
 	}
 	return goodpacket;
 }
-#endif
 
 // send special packet with only ack on it
 void Net_SendAcks(INT32 node)
 {
-#ifdef NONET
-	(void)node;
-#else
 	netbuffer->packettype = PT_NOTHING;
 	M_Memcpy(netbuffer->u.textcmd, nodes[node].acktosend, MAXACKTOSEND);
 	HSendPacket(node, false, 0, MAXACKTOSEND);
-#endif
 }
 
-#ifndef NONET
 static void GotAcks(void)
 {
 	INT32 i, j;
@@ -463,7 +452,6 @@ static void GotAcks(void)
 						}
 				}
 }
-#endif
 
 void Net_ConnectionTimeout(INT32 node)
 {
@@ -489,7 +477,6 @@ void Net_ConnectionTimeout(INT32 node)
 // Resend the data if needed
 void Net_AckTicker(void)
 {
-#ifndef NONET
 	INT32 i;
 
 	for (i = 0; i < MAXACKPACKETS; i++)
@@ -536,16 +523,12 @@ void Net_AckTicker(void)
 			}
 		}
 	}
-#endif
 }
 
 // Remove last packet received ack before resending the ackreturn
 // (the higher layer doesn't have room, or something else ....)
 void Net_UnAcknowledgePacket(INT32 node)
 {
-#ifdef NONET
-	(void)node;
-#else
 	INT32 hm1 = (nodes[node].acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND;
 	DEBFILE(va("UnAcknowledge node %d\n", node));
 	if (!node)
@@ -577,10 +560,8 @@ void Net_UnAcknowledgePacket(INT32 node)
 		if (!nodes[node].firstacktosend)
 			nodes[node].firstacktosend = 1;
 	}
-#endif
 }
 
-#ifndef NONET
 /** Checks if all acks have been received
   *
   * \return True if all acks have been received
@@ -596,7 +577,6 @@ static boolean Net_AllAcksReceived(void)
 
 	return true;
 }
-#endif
 
 /** Waits for all ackreturns
   *
@@ -605,9 +585,6 @@ static boolean Net_AllAcksReceived(void)
   */
 void Net_WaitAllAckReceived(UINT32 timeout)
 {
-#ifdef NONET
-	(void)timeout;
-#else
 	tic_t tictac = I_GetTime();
 	timeout = tictac + timeout*NEWTICRATE;
 
@@ -623,7 +600,6 @@ void Net_WaitAllAckReceived(UINT32 timeout)
 		HGetPacket();
 		Net_AckTicker();
 	}
-#endif
 }
 
 static void InitNode(node_t *node)
@@ -639,10 +615,8 @@ static void InitAck(void)
 {
 	INT32 i;
 
-#ifndef NONET
 	for (i = 0; i < MAXACKPACKETS; i++)
 		ackpak[i].acknum = 0;
-#endif
 
 	for (i = 0; i < MAXNETNODES; i++)
 		InitNode(&nodes[i]);
@@ -655,9 +629,6 @@ static void InitAck(void)
   */
 void Net_AbortPacketType(UINT8 packettype)
 {
-#ifdef NONET
-	(void)packettype;
-#else
 	INT32 i;
 	for (i = 0; i < MAXACKPACKETS; i++)
 		if (ackpak[i].acknum && (ackpak[i].pak.data.packettype == packettype
@@ -665,7 +636,6 @@ void Net_AbortPacketType(UINT8 packettype)
 		{
 			ackpak[i].acknum = 0;
 		}
-#endif
 }
 
 // -----------------------------------------------------------------
@@ -675,9 +645,6 @@ void Net_AbortPacketType(UINT8 packettype)
 // remove a node, clear all ack from this node and reset askret
 void Net_CloseConnection(INT32 node)
 {
-#ifdef NONET
-	(void)node;
-#else
 	INT32 i;
 	boolean forceclose = (node & FORCECLOSE) != 0;
 
@@ -722,10 +689,8 @@ void Net_CloseConnection(INT32 node)
 	if (server)
 		SV_AbortLuaFileTransfer(node);
 	I_NetFreeNodenum(node);
-#endif
 }
 
-#ifndef NONET
 //
 // Checksum
 //
@@ -741,7 +706,6 @@ static UINT32 NetbufferChecksum(void)
 
 	return LONG(c);
 }
-#endif
 
 #ifdef DEBUGFILE
 
@@ -983,14 +947,12 @@ void Command_Droprate(void)
 	packetdroprate = droprate;
 }
 
-#ifndef NONET
 static boolean ShouldDropPacket(void)
 {
 	return (packetdropquantity[netbuffer->packettype])
 		|| (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100;
 }
 #endif
-#endif
 
 //
 // HSendPacket
@@ -1025,11 +987,6 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
 	if (!netgame)
 		I_Error("Tried to transmit to another node");
 
-#ifdef NONET
-	(void)node;
-	(void)reliable;
-	(void)acknum;
-#else
 	// do this before GetFreeAcknum because this function backups
 	// the current packet
 	doomcom->remotenode = (INT16)node;
@@ -1090,8 +1047,6 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
 	}
 #endif
 
-#endif // ndef NONET
-
 	return true;
 }
 
@@ -1125,8 +1080,6 @@ boolean HGetPacket(void)
 	if (!netgame)
 		return false;
 
-#ifndef NONET
-
 	while(true)
 	{
 		//nodejustjoined = I_NetGet();
@@ -1186,7 +1139,6 @@ boolean HGetPacket(void)
 		}
 		break;
 	}
-#endif // ndef NONET
 
 	return true;
 }
diff --git a/src/d_net.h b/src/d_net.h
index c6c9e2894c..803b49d740 100644
--- a/src/d_net.h
+++ b/src/d_net.h
@@ -66,9 +66,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum,
 	size_t packetlength);
 boolean HGetPacket(void);
 void D_SetDoomcom(void);
-#ifndef NONET
 void D_SaveBan(void);
-#endif
 boolean D_CheckNetGame(void);
 void D_CloseConnection(void);
 void Net_UnAcknowledgePacket(INT32 node);
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 64f478ff7a..69eed90de9 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -628,11 +628,9 @@ void D_RegisterServerCommands(void)
 	CV_RegisterVar(&cv_maxsend);
 	CV_RegisterVar(&cv_noticedownload);
 	CV_RegisterVar(&cv_downloadspeed);
-#ifndef NONET
 	CV_RegisterVar(&cv_allownewplayer);
 	CV_RegisterVar(&cv_showjoinaddress);
 	CV_RegisterVar(&cv_blamecfail);
-#endif
 
 	COM_AddCommand("ping", Command_Ping_f);
 	CV_RegisterVar(&cv_nettimeout);
diff --git a/src/d_netfil.c b/src/d_netfil.c
index 1fee1df81a..bf3952cc92 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -103,14 +103,12 @@ typedef struct
 } pauseddownload_t;
 static pauseddownload_t *pauseddownload = NULL;
 
-#ifndef NONET
 // for cl loading screen
 INT32 lastfilenum = -1;
 INT32 downloadcompletednum = 0;
 UINT32 downloadcompletedsize = 0;
 INT32 totalfilesrequestednum = 0;
 UINT32 totalfilesrequestedsize = 0;
-#endif
 
 luafiletransfer_t *luafiletransfers = NULL;
 boolean waitingforluafiletransfer = false;
@@ -250,9 +248,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi
 
 void CL_PrepareDownloadSaveGame(const char *tmpsave)
 {
-#ifndef NONET
 	lastfilenum = -1;
-#endif
 
 	FreeFileNeeded();
 	AllocFileNeeded(1);
@@ -1466,9 +1462,7 @@ void PT_FileFragment(SINT8 node, INT32 netconsole)
 		I_Error("Received a file not requested (file id: %d, file status: %s)\n", filenum, s);
 	}
 
-#ifndef NONET
 	lastfilenum = filenum;
-#endif
 }
 
 /** \brief Checks if a node is downloading a file
diff --git a/src/d_netfil.h b/src/d_netfil.h
index 5c7f4ef49f..ec53be5879 100644
--- a/src/d_netfil.h
+++ b/src/d_netfil.h
@@ -70,13 +70,11 @@ extern INT32 fileneedednum;
 extern fileneeded_t *fileneeded;
 extern char downloaddir[512];
 
-#ifndef NONET
 extern INT32 lastfilenum;
 extern INT32 downloadcompletednum;
 extern UINT32 downloadcompletedsize;
 extern INT32 totalfilesrequestednum;
 extern UINT32 totalfilesrequestedsize;
-#endif
 
 void AllocFileNeeded(INT32 size);
 void FreeFileNeeded(void);
diff --git a/src/deh_tables.c b/src/deh_tables.c
index 6a815b6ea5..5607142e51 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4879,7 +4879,7 @@ const char *const MENUTYPES_LIST[] = {
 	"MP_SERVER",
 	"MP_CONNECT",
 	"MP_ROOM",
-	"MP_PLAYERSETUP", // MP_PlayerSetupDef shared with SPLITSCREEN if #defined NONET
+	"MP_PLAYERSETUP",
 	"MP_SERVER_OPTIONS",
 
 	// Options
diff --git a/src/doomdef.h b/src/doomdef.h
index 84404d6edb..22cafa1909 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -56,7 +56,6 @@
 #endif
 
 #ifdef _WINDOWS
-#define NONET
 #if !defined (HWRENDER) && !defined (NOHW)
 #define HWRENDER
 #endif
@@ -707,7 +706,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
 /// Maintain compatibility with older 2.2 demos
 #define OLD22DEMOCOMPAT
 
-#if defined (HAVE_CURL) && ! defined (NONET)
+#ifdef HAVE_CURL
 #define MASTERSERVER
 #else
 #undef UPDATE_ALERT
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 091e2b2fba..c423f29053 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -175,14 +175,12 @@ static huddrawlist_h luahuddrawlist_scores;
 
 static tic_t resynch_ticker = 0;
 
-#ifndef NONET
 // just after
 static void Command_Say_f(void);
 static void Command_Sayto_f(void);
 static void Command_Sayteam_f(void);
 static void Command_CSay_f(void);
 static void Got_Saycmd(UINT8 **p, INT32 playernum);
-#endif
 
 void HU_LoadGraphics(void)
 {
@@ -327,13 +325,11 @@ void HU_LoadGraphics(void)
 //
 void HU_Init(void)
 {
-#ifndef NONET
 	COM_AddCommand("say", Command_Say_f, COM_LUA);
 	COM_AddCommand("sayto", Command_Sayto_f, COM_LUA);
 	COM_AddCommand("sayteam", Command_Sayteam_f, COM_LUA);
 	COM_AddCommand("csay", Command_CSay_f, COM_LUA);
 	RegisterNetXCmd(XD_SAY, Got_Saycmd);
-#endif
 
 	// set shift translation table
 	shiftxform = english_shiftxform;
@@ -363,8 +359,6 @@ void HU_Start(void)
 //                            EXECUTION
 //======================================================================
 
-#ifndef NONET
-
 // EVERY CHANGE IN THIS SCRIPT IS LOL XD! BY VINCYTM
 
 static UINT32 chat_nummsg_log = 0;
@@ -412,11 +406,9 @@ static void HU_removeChatText_Log(void)
 	}
 	chat_nummsg_log--; // lost 1 msg.
 }
-#endif
 
 void HU_AddChatText(const char *text, boolean playsound)
 {
-#ifndef NONET
 	if (playsound && cv_consolechat.value != 2) // Don't play the sound if we're using hidden chat.
 		S_StartSound(NULL, sfx_radio);
 	// reguardless of our preferences, put all of this in the chat buffer in case we decide to change from oldchat mid-game.
@@ -438,14 +430,8 @@ void HU_AddChatText(const char *text, boolean playsound)
 		CONS_Printf("%s\n", text);
 	else			// if we aren't, still save the message to log.txt
 		CON_LogMessage(va("%s\n", text));
-#else
-	(void)playsound;
-	CONS_Printf("%s\n", text);
-#endif
 }
 
-#ifndef NONET
-
 /** Runs a say command, sending an ::XD_SAY message.
   * A say command consists of a signed 8-bit integer for the target, an
   * unsigned 8-bit flag variable, and then the message itself.
@@ -865,8 +851,6 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
 #endif
 }
 
-#endif
-
 //
 //
 void HU_Ticker(void)
@@ -882,7 +866,6 @@ void HU_Ticker(void)
 	else
 		hu_showscores = false;
 
-#ifndef NONET
 	if (chat_on)
 	{
 		// count down the scroll timer.
@@ -910,7 +893,6 @@ void HU_Ticker(void)
 				HU_removeChatText_Mini();
 		}
 	}
-#endif
 
 	if (cechotimer > 0) --cechotimer;
 
@@ -918,8 +900,6 @@ void HU_Ticker(void)
 		resynch_ticker++;
 }
 
-#ifndef NONET
-
 static boolean teamtalk = false;
 static boolean justscrolleddown;
 static boolean justscrolledup;
@@ -1027,8 +1007,6 @@ static void HU_sendChatMessage(void)
 	}
 }
 
-#endif
-
 void HU_clearChatChars(void)
 {
 	memset(w_chat, '\0', sizeof(w_chat));
@@ -1043,9 +1021,7 @@ void HU_clearChatChars(void)
 //
 boolean HU_Responder(event_t *ev)
 {
-#ifndef NONET
 	INT32 c=0;
-#endif
 
 	if (ev->type != ev_keydown)
 		return false;
@@ -1072,7 +1048,6 @@ boolean HU_Responder(event_t *ev)
 			return false;
 	}*/	//We don't actually care about that unless we get splitscreen netgames. :V
 
-#ifndef NONET
 	c = (INT32)ev->key;
 
 	if (!chat_on)
@@ -1222,7 +1197,6 @@ boolean HU_Responder(event_t *ev)
 
 		return true;
 	}
-#endif
 
 	return false;
 }
@@ -1232,8 +1206,6 @@ boolean HU_Responder(event_t *ev)
 //                         HEADS UP DRAWING
 //======================================================================
 
-#ifndef NONET
-
 // Precompile a wordwrapped string to any given width.
 // This is a muuuch better method than V_WORDWRAP.
 // again stolen and modified a bit from video.c, don't mind me, will need to rearrange this one day.
@@ -1813,7 +1785,6 @@ static void HU_DrawChat_Old(void)
 	if (hu_tick < 4)
 		V_DrawCharacter(HU_INPUTX + c, y, '_' | cv_constextsize.value |V_NOSCALESTART|t, true);
 }
-#endif
 
 // Draw crosshairs at the exact center of the view.
 // In splitscreen, crosshairs are stretched vertically to compensate for V_PERPLAYER squishing them.
@@ -1953,7 +1924,6 @@ static void HU_DrawDemoInfo(void)
 //
 void HU_Drawer(void)
 {
-#ifndef NONET
 	// draw chat string plus cursor
 	if (chat_on)
 	{
@@ -1970,7 +1940,6 @@ void HU_Drawer(void)
 		if (!OLDCHAT && cv_consolechat.value < 2 && netgame) // Don't display minimized chat if you set the mode to Window (Hidden)
 			HU_drawMiniChat(); // draw messages in a cool fashion.
 	}
-#endif
 
 	if (cechotimer)
 		HU_DrawCEcho();
diff --git a/src/i_tcp.c b/src/i_tcp.c
index 00aebddd36..ec3d1ae93a 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -38,96 +38,87 @@
 
 #include "doomdef.h"
 
-#if defined (NOMD5) && !defined (NONET)
-	//#define NONET
+#ifdef USE_WINSOCK1
+	#include <winsock.h>
+#else
+	#ifndef USE_WINSOCK
+		#include <arpa/inet.h>
+		#ifdef __APPLE_CC__
+			#ifndef _BSD_SOCKLEN_T_
+				#define _BSD_SOCKLEN_T_
+			#endif //_BSD_SOCKLEN_T_
+		#endif //__APPLE_CC__
+		#include <sys/socket.h>
+		#include <netinet/in.h>
+		#include <netdb.h>
+		#include <sys/ioctl.h>
+	#endif //normal BSD API
+
+	#include <errno.h>
+	#include <time.h>
+
+	#if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON)
+		#include <sys/time.h>
+	#endif // UNIXCOMMON
 #endif
 
-#ifdef NONET
-	#undef HAVE_MINIUPNPC
-#else
-	#ifdef USE_WINSOCK1
-		#include <winsock.h>
-	#else
-		#ifndef USE_WINSOCK
-			#include <arpa/inet.h>
-			#ifdef __APPLE_CC__
-				#ifndef _BSD_SOCKLEN_T_
-					#define _BSD_SOCKLEN_T_
-				#endif //_BSD_SOCKLEN_T_
-			#endif //__APPLE_CC__
-			#include <sys/socket.h>
-			#include <netinet/in.h>
-			#include <netdb.h>
-			#include <sys/ioctl.h>
-		#endif //normal BSD API
-
-		#include <errno.h>
-		#include <time.h>
-
-		#if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON)
-			#include <sys/time.h>
-		#endif // UNIXCOMMON
+#ifdef USE_WINSOCK
+	// some undefined under win32
+	#undef errno
+	//#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right?
+	#define errno h_errno // some very strange things happen when not using h_error?!?
+	#ifdef EWOULDBLOCK
+	#undef EWOULDBLOCK
 	#endif
-
-	#ifdef USE_WINSOCK
-		// some undefined under win32
-		#undef errno
-		//#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right?
-		#define errno h_errno // some very strange things happen when not using h_error?!?
-		#ifdef EWOULDBLOCK
-		#undef EWOULDBLOCK
-		#endif
-		#define EWOULDBLOCK WSAEWOULDBLOCK
-		#ifdef EMSGSIZE
-		#undef EMSGSIZE
-		#endif
-		#define EMSGSIZE WSAEMSGSIZE
-		#ifdef ECONNREFUSED
-		#undef ECONNREFUSED
-		#endif
-		#define ECONNREFUSED WSAECONNREFUSED
-		#ifdef ETIMEDOUT
-		#undef ETIMEDOUT
-		#endif
-		#define ETIMEDOUT WSAETIMEDOUT
-		#ifndef IOC_VENDOR
-		#define IOC_VENDOR 0x18000000
-		#endif
-		#ifndef _WSAIOW
-		#define _WSAIOW(x,y) (IOC_IN|(x)|(y))
-		#endif
-		#ifndef SIO_UDP_CONNRESET
-		#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
-		#endif
-		#ifndef AI_ADDRCONFIG
-		#define AI_ADDRCONFIG 0x00000400
-		#endif
-		#ifndef STATUS_INVALID_PARAMETER
-		#define STATUS_INVALID_PARAMETER 0xC000000D
-		#endif
-	#endif // USE_WINSOCK
-
-	typedef union
-	{
-		struct sockaddr     any;
-		struct sockaddr_in  ip4;
-	#ifdef HAVE_IPV6
-		struct sockaddr_in6 ip6;
+	#define EWOULDBLOCK WSAEWOULDBLOCK
+	#ifdef EMSGSIZE
+	#undef EMSGSIZE
+	#endif
+	#define EMSGSIZE WSAEMSGSIZE
+	#ifdef ECONNREFUSED
+	#undef ECONNREFUSED
+	#endif
+	#define ECONNREFUSED WSAECONNREFUSED
+	#ifdef ETIMEDOUT
+	#undef ETIMEDOUT
+	#endif
+	#define ETIMEDOUT WSAETIMEDOUT
+	#ifndef IOC_VENDOR
+	#define IOC_VENDOR 0x18000000
+	#endif
+	#ifndef _WSAIOW
+	#define _WSAIOW(x,y) (IOC_IN|(x)|(y))
+	#endif
+	#ifndef SIO_UDP_CONNRESET
+	#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
+	#endif
+	#ifndef AI_ADDRCONFIG
+	#define AI_ADDRCONFIG 0x00000400
+	#endif
+	#ifndef STATUS_INVALID_PARAMETER
+	#define STATUS_INVALID_PARAMETER 0xC000000D
 	#endif
-	} mysockaddr_t;
+#endif // USE_WINSOCK
 
-	#ifdef HAVE_MINIUPNPC
-		#ifdef STATIC_MINIUPNPC
-			#define STATICLIB
-		#endif
-		#include "miniupnpc/miniwget.h"
-		#include "miniupnpc/miniupnpc.h"
-		#include "miniupnpc/upnpcommands.h"
-		#undef STATICLIB
-		static UINT8 UPNP_support = TRUE;
-	#endif // HAVE_MINIUPNC
+typedef union
+{
+	struct sockaddr     any;
+	struct sockaddr_in  ip4;
+#ifdef HAVE_IPV6
+	struct sockaddr_in6 ip6;
+#endif
+} mysockaddr_t;
 
-#endif // !NONET
+#ifdef HAVE_MINIUPNPC
+	#ifdef STATIC_MINIUPNPC
+		#define STATICLIB
+	#endif
+	#include "miniupnpc/miniwget.h"
+	#include "miniupnpc/miniupnpc.h"
+	#include "miniupnpc/upnpcommands.h"
+	#undef STATICLIB
+	static UINT8 UPNP_support = TRUE;
+#endif // HAVE_MINIUPNC
 
 #define MAXBANS 100
 
@@ -151,7 +142,7 @@
 #define SELECTTEST
 #define DEFAULTPORT "5029"
 
-#if defined (USE_WINSOCK) && !defined (NONET)
+#ifdef USE_WINSOCK
 	typedef SOCKET SOCKET_TYPE;
 	#define ERRSOCKET (SOCKET_ERROR)
 #else
@@ -163,22 +154,20 @@
 	#define ERRSOCKET (-1)
 #endif
 
-#ifndef NONET
-	// define socklen_t in DOS/Windows if it is not already defined
-	#ifdef USE_WINSOCK1
-		typedef int socklen_t;
-	#endif
-	static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET};
-	static size_t mysocketses = 0;
-	static int myfamily[MAXNETNODES+1] = {0};
-	static SOCKET_TYPE nodesocket[MAXNETNODES+1] = {ERRSOCKET};
-	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];
+// define socklen_t in DOS/Windows if it is not already defined
+#ifdef USE_WINSOCK1
+	typedef int socklen_t;
 #endif
+static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET};
+static size_t mysocketses = 0;
+static int myfamily[MAXNETNODES+1] = {0};
+static SOCKET_TYPE nodesocket[MAXNETNODES+1] = {ERRSOCKET};
+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 size_t numbans = 0;
 static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1?
@@ -187,7 +176,6 @@ static boolean init_tcp_driver = false;
 static const char *serverport_name = DEFAULTPORT;
 static const char *clientport_name;/* any port */
 
-#ifndef NONET
 #ifdef USE_WINSOCK
 // stupid microsoft makes things complicated
 static char *get_WSAErrorStr(int e)
@@ -374,47 +362,33 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk)
 #endif
 	return s;
 }
-#endif
 
 static const char *SOCK_GetNodeAddress(INT32 node)
 {
 	if (node == 0)
 		return "self";
-#ifdef NONET
-	return NULL;
-#else
 	if (!nodeconnected[node])
 		return NULL;
 	return SOCK_AddrToStr(&clientaddress[node]);
-#endif
 }
 
 static const char *SOCK_GetBanAddress(size_t ban)
 {
 	if (ban >= numbans)
 		return NULL;
-#ifdef NONET
-	return NULL;
-#else
 	return SOCK_AddrToStr(&banned[ban]);
-#endif
 }
 
 static const char *SOCK_GetBanMask(size_t ban)
 {
-#ifdef NONET
-	(void)ban;
-#else
 	static char s[16]; //255.255.255.255 netmask? no, just CDIR for only
 	if (ban >= numbans)
 		return NULL;
 	if (sprintf(s,"%d",bannedmask[ban]) > 0)
 		return s;
-#endif
 	return NULL;
 }
 
-#ifndef NONET
 static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
 {
 	UINT32 bitmask = INADDR_NONE;
@@ -518,9 +492,7 @@ void Command_Numnodes(void)
 				connected, ingame);
 }
 #endif
-#endif
 
-#ifndef NONET
 // Returns true if a packet was received from a new node, false in all other cases
 static boolean SOCK_Get(void)
 {
@@ -583,10 +555,8 @@ static boolean SOCK_Get(void)
 	doomcom->remotenode = -1; // no packet
 	return false;
 }
-#endif
 
 // check if we can send (do not go over the buffer)
-#ifndef NONET
 
 static fd_set masterset;
 
@@ -636,9 +606,7 @@ static boolean SOCK_CanGet(void)
 	return false;
 }
 #endif
-#endif
 
-#ifndef NONET
 static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr)
 {
 	socklen_t d4 = (socklen_t)sizeof(struct sockaddr_in);
@@ -701,9 +669,7 @@ static void SOCK_Send(void)
 				SOCK_GetNodeAddress(doomcom->remotenode), e, strerror(e));
 	}
 }
-#endif
 
-#ifndef NONET
 static void SOCK_FreeNodenum(INT32 numnode)
 {
 	// can't disconnect from self :)
@@ -718,12 +684,10 @@ static void SOCK_FreeNodenum(INT32 numnode)
 	// put invalid address
 	memset(&clientaddress[numnode], 0, sizeof (clientaddress[numnode]));
 }
-#endif
 
 //
 // UDPsocket
 //
-#ifndef NONET
 
 // allocate a socket
 static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen)
@@ -1044,12 +1008,10 @@ static boolean UDP_Socket(void)
 
 	return true;
 }
-#endif
 
 boolean I_InitTcpDriver(void)
 {
 	boolean tcp_was_up = init_tcp_driver;
-#ifndef NONET
 	if (!init_tcp_driver)
 	{
 #ifdef USE_WINSOCK
@@ -1104,7 +1066,7 @@ boolean I_InitTcpDriver(void)
 #endif
 		init_tcp_driver = true;
 	}
-#endif
+
 	if (!tcp_was_up && init_tcp_driver)
 	{
 		I_AddExitFunc(I_ShutdownTcpDriver);
@@ -1118,7 +1080,6 @@ boolean I_InitTcpDriver(void)
 	return init_tcp_driver;
 }
 
-#ifndef NONET
 static void SOCK_CloseSocket(void)
 {
 	size_t i;
@@ -1133,11 +1094,9 @@ static void SOCK_CloseSocket(void)
 		mysockets[i] = ERRSOCKET;
 	}
 }
-#endif
 
 void I_ShutdownTcpDriver(void)
 {
-#ifndef NONET
 	SOCK_CloseSocket();
 
 	CONS_Printf("I_ShutdownTcpDriver: ");
@@ -1147,10 +1106,8 @@ void I_ShutdownTcpDriver(void)
 #endif
 	CONS_Printf("shut down\n");
 	init_tcp_driver = false;
-#endif
 }
 
-#ifndef NONET
 static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
 {
 	SINT8 newnode = -1;
@@ -1194,11 +1151,9 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
 	I_freeaddrinfo(ai);
 	return newnode;
 }
-#endif
 
 static boolean SOCK_OpenSocket(void)
 {
-#ifndef NONET
 	size_t i;
 
 	memset(clientaddress, 0, sizeof (clientaddress));
@@ -1222,18 +1177,12 @@ static boolean SOCK_OpenSocket(void)
 	// build the socket but close it first
 	SOCK_CloseSocket();
 	return UDP_Socket();
-#else
-	return false;
-#endif
 }
 
 static boolean SOCK_Ban(INT32 node)
 {
 	if (node > MAXNETNODES)
 		return false;
-#ifdef NONET
-	return false;
-#else
 	if (numbans == MAXBANS)
 		return false;
 
@@ -1252,16 +1201,10 @@ static boolean SOCK_Ban(INT32 node)
 #endif
 	numbans++;
 	return true;
-#endif
 }
 
 static boolean SOCK_SetBanAddress(const char *address, const char *mask)
 {
-#ifdef NONET
-	(void)address;
-	(void)mask;
-	return false;
-#else
 	struct my_addrinfo *ai, *runp, hints;
 	int gaie;
 
@@ -1306,7 +1249,6 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
 	I_freeaddrinfo(ai);
 
 	return true;
-#endif
 }
 
 static void SOCK_ClearBans(void)
diff --git a/src/m_menu.c b/src/m_menu.c
index 21ba98dd2c..3e1d30feb3 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -149,9 +149,7 @@ levellist_mode_t levellistmode = LLM_CREATESERVER;
 UINT8 maplistoption = 0;
 
 static char joystickInfo[MAX_JOYSTICKS+1][29];
-#ifndef NONET
 static UINT32 serverlistpage;
-#endif
 
 static UINT8 numsaves = 0;
 static saveinfo_t* savegameinfo = NULL; // Extra info about the save games.
@@ -190,10 +188,8 @@ static void M_GoBack(INT32 choice);
 static void M_StopMessage(INT32 choice);
 static boolean stopstopmessage = false;
 
-#ifndef NONET
 static void M_HandleServerPage(INT32 choice);
 static void M_RoomMenu(INT32 choice);
-#endif
 
 // Prototyping is fun, innit?
 // ==========================================================================
@@ -296,7 +292,6 @@ static void M_SetupMultiPlayer2(INT32 choice);
 static void M_StartSplitServerMenu(INT32 choice);
 static void M_StartServer(INT32 choice);
 static void M_ServerOptions(INT32 choice);
-#ifndef NONET
 static void M_StartServerMenu(INT32 choice);
 static void M_ConnectMenu(INT32 choice);
 static void M_ConnectMenuModChecks(INT32 choice);
@@ -304,7 +299,6 @@ static void M_Refresh(INT32 choice);
 static void M_Connect(INT32 choice);
 static void M_ChooseRoom(INT32 choice);
 menu_t MP_MainDef;
-#endif
 
 // Options
 // Split into multiple parts due to size
@@ -382,11 +376,9 @@ static void M_DrawVideoMode(void);
 static void M_DrawColorMenu(void);
 static void M_DrawScreenshotMenu(void);
 static void M_DrawMonitorToggles(void);
-#ifndef NONET
 static void M_DrawConnectMenu(void);
 static void M_DrawMPMainMenu(void);
 static void M_DrawRoomMenu(void);
-#endif
 static void M_DrawJoystick(void);
 static void M_DrawSetupMultiPlayerMenu(void);
 static void M_DrawColorRamp(INT32 x, INT32 y, INT32 w, INT32 h, skincolor_t color);
@@ -401,10 +393,8 @@ static void M_HandleImageDef(INT32 choice);
 static void M_HandleLoadSave(INT32 choice);
 static void M_HandleLevelStats(INT32 choice);
 static void M_HandlePlaystyleMenu(INT32 choice);
-#ifndef NONET
 static boolean M_CancelConnect(void);
 static void M_HandleConnectIP(INT32 choice);
-#endif
 static void M_HandleSetupMultiPlayer(INT32 choice);
 static void M_HandleVideoMode(INT32 choice);
 
@@ -503,11 +493,7 @@ consvar_t cv_dummyloadless = CVAR_INIT ("dummyloadless", "In-game", CV_HIDEN, lo
 static menuitem_t MainMenu[] =
 {
 	{IT_STRING|IT_CALL,    NULL, "1  Player",   M_SinglePlayerMenu,      76},
-#ifndef NONET
 	{IT_STRING|IT_SUBMENU, NULL, "Multiplayer", &MP_MainDef,             84},
-#else
-	{IT_STRING|IT_CALL,    NULL, "Multiplayer", M_StartSplitServerMenu,  84},
-#endif
 	{IT_STRING|IT_CALL,    NULL, "Extras",      M_SecretsMenu,           92},
 	{IT_CALL   |IT_STRING, NULL, "Addons",      M_Addons,               100},
 	{IT_STRING|IT_CALL,    NULL, "Options",     M_Options,              108},
@@ -930,16 +916,10 @@ static menuitem_t SP_PlayerMenu[] =
 static menuitem_t MP_SplitServerMenu[] =
 {
 	{IT_STRING|IT_CALL,              NULL, "Select Gametype/Level...", M_MapChange,         100},
-#ifdef NONET // In order to keep player setup accessible.
-	{IT_STRING|IT_CALL,              NULL, "Player 1 setup...",        M_SetupMultiPlayer,  110},
-	{IT_STRING|IT_CALL,              NULL, "Player 2 setup...",        M_SetupMultiPlayer2, 120},
-#endif
 	{IT_STRING|IT_CALL,              NULL, "More Options...",          M_ServerOptions,     130},
 	{IT_WHITESTRING|IT_CALL,         NULL, "Start",                    M_StartServer,       140},
 };
 
-#ifndef NONET
-
 static menuitem_t MP_MainMenu[] =
 {
 	{IT_HEADER, NULL, "Join a game", NULL, 0},
@@ -1026,8 +1006,6 @@ menuitem_t MP_RoomMenu[] =
 	{IT_DISABLED,         NULL, "",               M_ChooseRoom, 162},
 };
 
-#endif
-
 static menuitem_t MP_PlayerSetupMenu[] =
 {
 	{IT_KEYHANDLER, NULL, "", M_HandleSetupMultiPlayer, 0}, // name
@@ -1586,14 +1564,12 @@ enum
 static menuitem_t OP_ServerOptionsMenu[] =
 {
 	{IT_HEADER, NULL, "General", NULL, 0},
-#ifndef NONET
 	{IT_STRING | IT_CVAR | IT_CV_STRING,
 	                         NULL, "Server name",                      &cv_servername,           7},
 	{IT_STRING | IT_CVAR,    NULL, "Max Players",                      &cv_maxplayers,          21},
 	{IT_STRING | IT_CVAR,    NULL, "Allow Add-on Downloading",         &cv_downloading,         26},
 	{IT_STRING | IT_CVAR,    NULL, "Allow players to join",            &cv_allownewplayer,      31},
 	{IT_STRING | IT_CVAR,    NULL, "Minutes for reconnecting",         &cv_rejointimeout,       36},
-#endif
 	{IT_STRING | IT_CVAR,    NULL, "Map progression",                  &cv_advancemap,          41},
 	{IT_STRING | IT_CVAR,    NULL, "Intermission Timer",               &cv_inttime,             46},
 
@@ -1632,7 +1608,6 @@ static menuitem_t OP_ServerOptionsMenu[] =
 	{IT_STRING | IT_CVAR,    NULL, "Autobalance sizes",                &cv_autobalance,        216},
 	{IT_STRING | IT_CVAR,    NULL, "Scramble on Map Change",           &cv_scrambleonchange,   221},
 
-#ifndef NONET
 	{IT_HEADER, NULL, "Advanced", NULL, 230},
 	{IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Master server",        &cv_masterserver,       236},
 
@@ -1640,7 +1615,6 @@ static menuitem_t OP_ServerOptionsMenu[] =
 	{IT_STRING | IT_CVAR,    NULL, "Attempts to resynchronise",        &cv_resynchattempts,    256},
 
 	{IT_STRING | IT_CVAR,    NULL, "Show IP Address of Joiners",       &cv_showjoinaddress,    261},
-#endif
 };
 
 static menuitem_t OP_MonitorToggleMenu[] =
@@ -1954,11 +1928,7 @@ menu_t MP_SplitServerDef =
 	MTREE2(MN_MP_MAIN, MN_MP_SPLITSCREEN),
 	"M_MULTI",
 	sizeof (MP_SplitServerMenu)/sizeof (menuitem_t),
-#ifndef NONET
 	&MP_MainDef,
-#else
-	&MainDef,
-#endif
 	MP_SplitServerMenu,
 	M_DrawServerMenu,
 	27, 30 - 50,
@@ -1966,8 +1936,6 @@ menu_t MP_SplitServerDef =
 	NULL
 };
 
-#ifndef NONET
-
 menu_t MP_MainDef =
 {
 	MN_MP_MAIN,
@@ -2019,15 +1987,10 @@ menu_t MP_RoomDef =
 	0,
 	NULL
 };
-#endif
 
 menu_t MP_PlayerSetupDef =
 {
-#ifdef NONET
-	MTREE2(MN_MP_MAIN, MN_MP_PLAYERSETUP),
-#else
 	MTREE3(MN_MP_MAIN, MN_MP_SPLITSCREEN, MN_MP_PLAYERSETUP),
-#endif
 	"M_SPLAYR",
 	sizeof (MP_PlayerSetupMenu)/sizeof (menuitem_t),
 	&MainDef, // doesn't matter
@@ -3956,9 +3919,7 @@ void M_Init(void)
 		OP_JoystickSetMenu[i].itemaction = M_AssignJoystick;
 	}
 
-#ifndef NONET
 	CV_RegisterVar(&cv_serversort);
-#endif
 }
 
 void M_InitCharacterTables(void)
@@ -11110,7 +11071,6 @@ static void M_EndGame(INT32 choice)
 
 #define S_LINEY(n) currentMenu->y + SERVERHEADERHEIGHT + (n * SERVERLINEHEIGHT)
 
-#ifndef NONET
 static UINT32 localservercount;
 
 static void M_HandleServerPage(INT32 choice)
@@ -11382,11 +11342,9 @@ static int ServerListEntryComparator_modified(const void *entry1, const void *en
 	// Default to strcmp.
 	return strcmp(sa->info.servername, sb->info.servername);
 }
-#endif
 
 void M_SortServerList(void)
 {
-#ifndef NONET
 	switch(cv_serversort.value)
 	{
 	case 0:		// Ping.
@@ -11408,10 +11366,8 @@ void M_SortServerList(void)
 		qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename);
 		break;
 	}
-#endif
 }
 
-#ifndef NONET
 #ifdef UPDATE_ALERT
 static boolean M_CheckMODVersion(int id)
 {
@@ -11610,7 +11566,6 @@ static void M_ChooseRoom(INT32 choice)
 	if (currentMenu == &MP_ConnectDef)
 		M_Refresh(0);
 }
-#endif //NONET
 
 //===========================================================================
 // Start Server Menu
@@ -11658,7 +11613,6 @@ static void M_DrawServerMenu(void)
 {
 	M_DrawGenericMenu();
 
-#ifndef NONET
 	// Room name
 	if (currentMenu == &MP_ServerDef)
 	{
@@ -11670,15 +11624,10 @@ static void M_DrawServerMenu(void)
 			V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey,
 			                         V_YELLOWMAP, room_list[menuRoomIndex].name);
 	}
-#endif
 
 	if (cv_nextmap.value)
 	{
-#ifndef NONET
 #define imgheight MP_ServerMenu[mp_server_levelgt].alphaKey
-#else
-#define imgheight 100
-#endif
 		patch_t *PictureOfLevel;
 		lumpnum_t lumpnum;
 		char headerstr[40];
@@ -11730,7 +11679,6 @@ static void M_ServerOptions(INT32 choice)
 {
 	(void)choice;
 
-#ifndef NONET
 	if ((splitscreen && !netgame) || currentMenu == &MP_SplitServerDef)
 	{
 		OP_ServerOptionsMenu[ 1].status = IT_GRAYEDOUT; // Server name
@@ -11751,7 +11699,6 @@ static void M_ServerOptions(INT32 choice)
 		OP_ServerOptionsMenu[37].status = IT_STRING | IT_CVAR;
 		OP_ServerOptionsMenu[38].status = IT_STRING | IT_CVAR;
 	}
-#endif
 
 	/* Disable fading because of different menu head. */
 	if (currentMenu == &OP_MainDef)/* from Options menu */
@@ -11763,7 +11710,6 @@ static void M_ServerOptions(INT32 choice)
 	M_SetupNextMenu(&OP_ServerOptionsDef);
 }
 
-#ifndef NONET
 static void M_StartServerMenu(INT32 choice)
 {
 	(void)choice;
@@ -12030,7 +11976,6 @@ static void M_HandleConnectIP(INT32 choice)
 			M_ClearMenus(true);
 	}
 }
-#endif //!NONET
 
 // ========================
 // MULTIPLAYER PLAYER SETUP
@@ -12544,7 +12489,7 @@ static void M_SetupMultiPlayer2(INT32 choice)
 
 	multi_frame = 0;
 	multi_tics = 4*FRACUNIT;
-	
+
 	strcpy (setupm_name, cv_playername2.string);
 
 	// set for splitscreen secondary player
diff --git a/src/m_menu.h b/src/m_menu.h
index c925c7f49c..e91b910137 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -74,7 +74,7 @@ typedef enum
 	MN_MP_SERVER,
 	MN_MP_CONNECT,
 	MN_MP_ROOM,
-	MN_MP_PLAYERSETUP, // MP_PlayerSetupDef shared with SPLITSCREEN if #defined NONET
+	MN_MP_PLAYERSETUP,
 	MN_MP_SERVER_OPTIONS,
 
 	// Options
diff --git a/src/mserv.c b/src/mserv.c
index bff562c95f..90091d2415 100644
--- a/src/mserv.c
+++ b/src/mserv.c
@@ -45,9 +45,7 @@ static I_cond  MSCond;
 #  define Unlock_state()
 #endif/*HAVE_THREADS*/
 
-#ifndef NONET
 static void Command_Listserv_f(void);
-#endif
 
 #endif/*MASTERSERVER*/
 
@@ -89,7 +87,6 @@ msg_rooms_t room_list[NUM_LIST_ROOMS+1]; // +1 for easy test
   */
 void AddMServCommands(void)
 {
-#ifndef NONET
 	CV_RegisterVar(&cv_masterserver);
 	CV_RegisterVar(&cv_masterserver_update_rate);
 	CV_RegisterVar(&cv_masterserver_timeout);
@@ -100,7 +97,6 @@ void AddMServCommands(void)
 	COM_AddCommand("listserv", Command_Listserv_f);
 	COM_AddCommand("masterserver_update", Update_parameters); // allows people to updates manually in case you were delisted by accident
 #endif
-#endif
 }
 
 #ifdef MASTERSERVER
@@ -189,7 +185,6 @@ void GetMODVersion_Console(void)
 }
 #endif
 
-#ifndef NONET
 /** Gets a list of game servers. Called from console.
   */
 static void Command_Listserv_f(void)
@@ -200,7 +195,6 @@ static void Command_Listserv_f(void)
 		HMS_list_servers();
 	}
 }
-#endif
 
 static void
 Finish_registration (void)
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index c21226ac3f..ba4dd7985e 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -2389,9 +2389,7 @@ void I_Quit(void)
 	SDLforceUngrabMouse();
 	quiting = SDL_FALSE;
 	M_SaveConfig(NULL); //save game config, cvars..
-#ifndef NONET
 	D_SaveBan(); // save the ban list
-#endif
 	G_SaveGameData(clientGamedata); // Tails 12-08-2002
 	//added:16-02-98: when recording a demo, should exit using 'q' key,
 	//        but sometimes we forget and use 'F10'.. so save here too.
@@ -2506,9 +2504,7 @@ void I_Error(const char *error, ...)
 	// ---
 
 	M_SaveConfig(NULL); // save game config, cvars..
-#ifndef NONET
 	D_SaveBan(); // save the ban list
-#endif
 	G_SaveGameData(clientGamedata); // Tails 12-08-2002
 
 	// Shutdown. Here might be other errors.
-- 
GitLab


From 0b17e3c4180d5d8e6f8002c2a3cadfeaf7b25aff Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 31 Dec 2022 14:10:19 +0100
Subject: [PATCH 263/518] Move netcode files to a new folder

---
 src/Makefile                   |  1 +
 src/Sourcefile                 |  7 ----
 src/android/i_net.c            |  2 +-
 src/blua/liolib.c              |  2 +-
 src/command.c                  |  4 +-
 src/d_main.c                   |  6 +--
 src/deh_soc.c                  |  2 +-
 src/deh_soc.h                  |  2 +-
 src/doomstat.h                 |  2 +-
 src/dummy/i_net.c              |  2 +-
 src/f_finale.c                 |  2 +-
 src/filesrch.c                 |  2 +-
 src/filesrch.h                 |  2 +-
 src/g_demo.c                   |  2 +-
 src/g_game.c                   |  2 +-
 src/g_input.c                  |  2 +-
 src/hardware/hw_main.c         |  2 +-
 src/hu_stuff.c                 |  2 +-
 src/i_time.c                   |  2 +-
 src/lua_baselib.c              |  4 +-
 src/lua_hooklib.c              |  2 +-
 src/lua_script.c               |  2 +-
 src/m_cheat.c                  |  2 +-
 src/m_menu.c                   |  6 +--
 src/m_menu.h                   |  2 +-
 src/m_perfstats.c              |  2 +-
 src/netcode/Sourcefile         |  7 ++++
 src/{ => netcode}/d_clisrv.c   | 68 +++++++++++++++++-----------------
 src/{ => netcode}/d_clisrv.h   |  6 +--
 src/{ => netcode}/d_net.c      | 16 ++++----
 src/{ => netcode}/d_net.h      |  0
 src/{ => netcode}/d_netcmd.c   | 68 +++++++++++++++++-----------------
 src/{ => netcode}/d_netcmd.h   |  2 +-
 src/{ => netcode}/d_netfil.c   | 30 +++++++--------
 src/{ => netcode}/d_netfil.h   |  2 +-
 src/{ => netcode}/http-mserv.c | 10 ++---
 src/{ => netcode}/i_addrinfo.c |  0
 src/{ => netcode}/i_addrinfo.h |  0
 src/{ => netcode}/i_net.h      |  4 +-
 src/{ => netcode}/i_tcp.c      |  8 ++--
 src/{ => netcode}/i_tcp.h      |  0
 src/{ => netcode}/mserv.c      | 12 +++---
 src/{ => netcode}/mserv.h      |  2 +-
 src/p_ceilng.c                 |  2 +-
 src/p_lights.c                 |  2 +-
 src/p_user.c                   |  2 +-
 src/r_segs.c                   |  2 +-
 src/r_things.c                 |  2 +-
 src/screen.c                   |  2 +-
 src/sdl/i_net.c                |  6 +--
 src/sdl/i_system.c             |  4 +-
 src/sdl/i_ttf.c                |  2 +-
 src/w_wad.c                    |  4 +-
 src/y_inter.c                  |  2 +-
 54 files changed, 167 insertions(+), 166 deletions(-)
 create mode 100644 src/netcode/Sourcefile
 rename src/{ => netcode}/d_clisrv.c (99%)
 mode change 100755 => 100644
 rename src/{ => netcode}/d_clisrv.h (99%)
 rename src/{ => netcode}/d_net.c (99%)
 rename src/{ => netcode}/d_net.h (100%)
 rename src/{ => netcode}/d_netcmd.c (99%)
 rename src/{ => netcode}/d_netcmd.h (99%)
 rename src/{ => netcode}/d_netfil.c (99%)
 rename src/{ => netcode}/d_netfil.h (99%)
 rename src/{ => netcode}/http-mserv.c (98%)
 rename src/{ => netcode}/i_addrinfo.c (100%)
 rename src/{ => netcode}/i_addrinfo.h (100%)
 rename src/{ => netcode}/i_net.h (98%)
 rename src/{ => netcode}/i_tcp.c (99%)
 rename src/{ => netcode}/i_tcp.h (100%)
 rename src/{ => netcode}/mserv.c (98%)
 rename src/{ => netcode}/mserv.h (99%)

diff --git a/src/Makefile b/src/Makefile
index 7fe7f8990f..1968e995b8 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -207,6 +207,7 @@ objdir:=$(makedir)/objs
 sources+=\
 	$(call List,Sourcefile)\
 	$(call List,blua/Sourcefile)\
+	$(call List,netcode/Sourcefile)\
 
 depends:=$(basename $(filter %.c %.s,$(sources)))
 objects:=$(basename $(filter %.c %.s %.nas,$(sources)))
diff --git a/src/Sourcefile b/src/Sourcefile
index c55752b095..0046733677 100644
--- a/src/Sourcefile
+++ b/src/Sourcefile
@@ -1,9 +1,5 @@
 string.c
 d_main.c
-d_clisrv.c
-d_net.c
-d_netfil.c
-d_netcmd.c
 dehacked.c
 deh_soc.c
 deh_lua.c
@@ -77,9 +73,6 @@ s_sound.c
 sounds.c
 w_wad.c
 filesrch.c
-mserv.c
-http-mserv.c
-i_tcp.c
 lzf.c
 vid_copy.s
 b_bot.c
diff --git a/src/android/i_net.c b/src/android/i_net.c
index f6e642022e..4c30dc767a 100644
--- a/src/android/i_net.c
+++ b/src/android/i_net.c
@@ -1,4 +1,4 @@
-#include "../i_net.h"
+#include "../netcode/i_net.h"
 
 boolean I_InitNetwork(void)
 {
diff --git a/src/blua/liolib.c b/src/blua/liolib.c
index 545f9c144c..00e31e965e 100644
--- a/src/blua/liolib.c
+++ b/src/blua/liolib.c
@@ -19,7 +19,7 @@
 #include "lualib.h"
 #include "../i_system.h"
 #include "../g_game.h"
-#include "../d_netfil.h"
+#include "../netcode/d_netfil.h"
 #include "../lua_libs.h"
 #include "../byteptr.h"
 #include "../lua_script.h"
diff --git a/src/command.c b/src/command.c
index e1a43522da..0256365f38 100644
--- a/src/command.c
+++ b/src/command.c
@@ -28,11 +28,11 @@
 #include "byteptr.h"
 #include "p_saveg.h"
 #include "g_game.h" // for player_names
-#include "d_netcmd.h"
+#include "netcode/d_netcmd.h"
 #include "hu_stuff.h"
 #include "p_setup.h"
 #include "lua_script.h"
-#include "d_netfil.h" // findfile
+#include "netcode/d_netfil.h" // findfile
 #include "r_data.h" // Color_cons_t
 #include "d_main.h" // D_IsPathAllowed
 
diff --git a/src/d_main.c b/src/d_main.c
index b7b7f6616d..a4238c8b19 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -34,7 +34,7 @@
 #include "doomdef.h"
 #include "am_map.h"
 #include "console.h"
-#include "d_net.h"
+#include "netcode/d_net.h"
 #include "f_finale.h"
 #include "g_game.h"
 #include "hu_stuff.h"
@@ -56,11 +56,11 @@
 #include "w_wad.h"
 #include "z_zone.h"
 #include "d_main.h"
-#include "d_netfil.h"
+#include "netcode/d_netfil.h"
 #include "m_cheat.h"
 #include "y_inter.h"
 #include "p_local.h" // chasecam
-#include "mserv.h" // ms_RoomId
+#include "netcode/mserv.h" // ms_RoomId
 #include "m_misc.h" // screenshot functionality
 #include "deh_tables.h" // Dehacked list test
 #include "m_cond.h" // condition initialization
diff --git a/src/deh_soc.c b/src/deh_soc.c
index 2193cd875c..59eb0a9bd9 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -34,7 +34,7 @@
 #include "r_sky.h"
 #include "fastcmp.h"
 #include "lua_script.h" // Reluctantly included for LUA_EvalMath
-#include "d_clisrv.h"
+#include "netcode/d_clisrv.h"
 
 #ifdef HWRENDER
 #include "hardware/hw_light.h"
diff --git a/src/deh_soc.h b/src/deh_soc.h
index 0cab545f68..0293901337 100644
--- a/src/deh_soc.h
+++ b/src/deh_soc.h
@@ -35,7 +35,7 @@
 #include "r_sky.h"
 #include "fastcmp.h"
 #include "lua_script.h" // Reluctantly included for LUA_EvalMath
-#include "d_clisrv.h"
+#include "netcode/d_clisrv.h"
 
 #ifdef HWRENDER
 #include "hardware/hw_light.h"
diff --git a/src/doomstat.h b/src/doomstat.h
index a812cc304f..27889726e8 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -633,7 +633,7 @@ extern boolean singletics;
 // Netgame stuff
 // =============
 
-#include "d_clisrv.h"
+#include "netcode/d_clisrv.h"
 
 extern consvar_t cv_timetic; // display high resolution timer
 extern consvar_t cv_powerupdisplay; // display powerups
diff --git a/src/dummy/i_net.c b/src/dummy/i_net.c
index f6e642022e..4c30dc767a 100644
--- a/src/dummy/i_net.c
+++ b/src/dummy/i_net.c
@@ -1,4 +1,4 @@
-#include "../i_net.h"
+#include "../netcode/i_net.h"
 
 boolean I_InitNetwork(void)
 {
diff --git a/src/f_finale.c b/src/f_finale.c
index d03795dc43..82c2a4f96d 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -14,7 +14,7 @@
 #include "doomdef.h"
 #include "doomstat.h"
 #include "d_main.h"
-#include "d_netcmd.h"
+#include "netcode/d_netcmd.h"
 #include "f_finale.h"
 #include "g_game.h"
 #include "hu_stuff.h"
diff --git a/src/filesrch.c b/src/filesrch.c
index 9ee64f5ba6..313f286e1f 100644
--- a/src/filesrch.c
+++ b/src/filesrch.c
@@ -26,7 +26,7 @@
 #include <string.h>
 
 #include "filesrch.h"
-#include "d_netfil.h"
+#include "netcode/d_netfil.h"
 #include "m_misc.h"
 #include "z_zone.h"
 #include "m_menu.h" // Addons_option_Onchange
diff --git a/src/filesrch.h b/src/filesrch.h
index 59ef5269b1..a934c48d61 100644
--- a/src/filesrch.h
+++ b/src/filesrch.h
@@ -5,7 +5,7 @@
 #define __FILESRCH_H__
 
 #include "doomdef.h"
-#include "d_netfil.h"
+#include "netcode/d_netfil.h"
 #include "m_menu.h" // MAXSTRINGLENGTH
 #include "w_wad.h"
 
diff --git a/src/g_demo.c b/src/g_demo.c
index adb8e891d6..dea80e7930 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -15,7 +15,7 @@
 #include "console.h"
 #include "d_main.h"
 #include "d_player.h"
-#include "d_clisrv.h"
+#include "netcode/d_clisrv.h"
 #include "p_setup.h"
 #include "i_time.h"
 #include "i_system.h"
diff --git a/src/g_game.c b/src/g_game.c
index b8c4349985..f47aa21835 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -15,7 +15,7 @@
 #include "console.h"
 #include "d_main.h"
 #include "d_player.h"
-#include "d_clisrv.h"
+#include "netcode/d_clisrv.h"
 #include "f_finale.h"
 #include "p_setup.h"
 #include "p_saveg.h"
diff --git a/src/g_input.c b/src/g_input.c
index 826dcecbdf..fa30c1984f 100644
--- a/src/g_input.c
+++ b/src/g_input.c
@@ -16,7 +16,7 @@
 #include "g_input.h"
 #include "keys.h"
 #include "hu_stuff.h" // need HUFONT start & end
-#include "d_net.h"
+#include "netcode/d_net.h"
 #include "console.h"
 
 #define MAXMOUSESENSITIVITY 100 // sensitivity steps
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 36ff86abd7..b14a168a17 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -29,7 +29,7 @@
 #include "../r_patch.h"
 #include "../r_picformats.h"
 #include "../r_bsp.h"
-#include "../d_clisrv.h"
+#include "../netcode/d_clisrv.h"
 #include "../w_wad.h"
 #include "../z_zone.h"
 #include "../r_splats.h"
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index c423f29053..33ef8a404a 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -19,7 +19,7 @@
 #include "m_cond.h" // emblems
 #include "m_misc.h" // word jumping
 
-#include "d_clisrv.h"
+#include "netcode/d_clisrv.h"
 
 #include "g_game.h"
 #include "g_input.h"
diff --git a/src/i_time.c b/src/i_time.c
index 2a22503f1b..fae26abede 100644
--- a/src/i_time.c
+++ b/src/i_time.c
@@ -17,7 +17,7 @@
 
 #include "command.h"
 #include "doomtype.h"
-#include "d_netcmd.h"
+#include "netcode/d_netcmd.h"
 #include "m_fixed.h"
 #include "i_system.h"
 
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index bf3fd1decc..42e37e55e9 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -26,11 +26,11 @@
 #include "y_inter.h"
 #include "hu_stuff.h"	// HU_AddChatText
 #include "console.h"
-#include "d_netcmd.h" // IsPlayerAdmin
+#include "netcode/d_netcmd.h" // IsPlayerAdmin
 #include "m_menu.h" // Player Setup menu color stuff
 #include "m_misc.h" // M_MapNumber
 #include "b_bot.h" // B_UpdateBotleader
-#include "d_clisrv.h" // CL_RemovePlayer
+#include "netcode/d_clisrv.h" // CL_RemovePlayer
 #include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision
 
 #include "lua_script.h"
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 039a9677f4..0fc25ee6c9 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -24,7 +24,7 @@
 #include "lua_hud.h" // hud_running errors
 
 #include "m_perfstats.h"
-#include "d_netcmd.h" // for cv_perfstats
+#include "netcode/d_netcmd.h" // for cv_perfstats
 #include "i_system.h" // I_GetPreciseTime
 
 /* =========================================================================
diff --git a/src/lua_script.c b/src/lua_script.c
index a8263ea5fe..ed5adbe3fd 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -28,7 +28,7 @@
 #include "p_slopes.h" // for P_SlopeById and slopelist
 #include "p_polyobj.h" // polyobj_t, PolyObjects
 #ifdef LUA_ALLOW_BYTECODE
-#include "d_netfil.h" // for LUA_DumpFile
+#include "netcode/d_netfil.h" // for LUA_DumpFile
 #endif
 
 #include "lua_script.h"
diff --git a/src/m_cheat.c b/src/m_cheat.c
index e370335f83..69b3b643c3 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -19,7 +19,7 @@
 #include "r_local.h"
 #include "p_local.h"
 #include "p_setup.h"
-#include "d_net.h"
+#include "netcode/d_net.h"
 
 #include "m_cheat.h"
 #include "m_menu.h"
diff --git a/src/m_menu.c b/src/m_menu.c
index 3e1d30feb3..72e50de696 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -20,7 +20,7 @@
 
 #include "doomdef.h"
 #include "d_main.h"
-#include "d_netcmd.h"
+#include "netcode/d_netcmd.h"
 #include "console.h"
 #include "r_fps.h"
 #include "r_local.h"
@@ -53,8 +53,8 @@
 #include "hardware/hw_main.h"
 #endif
 
-#include "d_net.h"
-#include "mserv.h"
+#include "netcode/d_net.h"
+#include "netcode/mserv.h"
 #include "m_misc.h"
 #include "m_anigif.h"
 #include "byteptr.h"
diff --git a/src/m_menu.h b/src/m_menu.h
index e91b910137..b8fe3b8089 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -20,7 +20,7 @@
 #include "command.h"
 #include "f_finale.h" // for ttmode_enum
 #include "i_threads.h"
-#include "mserv.h"
+#include "netcode/mserv.h"
 #include "r_things.h" // for SKINNAMESIZE
 
 // Compatibility with old-style named NiGHTS replay files.
diff --git a/src/m_perfstats.c b/src/m_perfstats.c
index 17e026b3e1..1511859324 100644
--- a/src/m_perfstats.c
+++ b/src/m_perfstats.c
@@ -12,7 +12,7 @@
 #include "m_perfstats.h"
 #include "v_video.h"
 #include "i_video.h"
-#include "d_netcmd.h"
+#include "netcode/d_netcmd.h"
 #include "r_main.h"
 #include "i_system.h"
 #include "z_zone.h"
diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile
new file mode 100644
index 0000000000..9087917d04
--- /dev/null
+++ b/src/netcode/Sourcefile
@@ -0,0 +1,7 @@
+d_clisrv.c
+d_net.c
+d_netcmd.c
+d_netfil.c
+http-mserv.c
+i_tcp.c
+mserv.c
diff --git a/src/d_clisrv.c b/src/netcode/d_clisrv.c
old mode 100755
new mode 100644
similarity index 99%
rename from src/d_clisrv.c
rename to src/netcode/d_clisrv.c
index cb1ccd5398..9b11e23ff0
--- a/src/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -15,48 +15,48 @@
 #include <unistd.h> //for unlink
 #endif
 
-#include "i_time.h"
+#include "../i_time.h"
 #include "i_net.h"
-#include "i_system.h"
-#include "i_video.h"
+#include "../i_system.h"
+#include "../i_video.h"
 #include "d_net.h"
-#include "d_main.h"
-#include "g_game.h"
-#include "st_stuff.h"
-#include "hu_stuff.h"
-#include "keys.h"
-#include "g_input.h"
-#include "i_gamepad.h"
-#include "m_menu.h"
-#include "console.h"
+#include "../d_main.h"
+#include "../g_game.h"
+#include "../st_stuff.h"
+#include "../hu_stuff.h"
+#include "../keys.h"
+#include "../g_input.h"
+#include "../i_gamepad.h"
+#include "../m_menu.h"
+#include "../console.h"
 #include "d_netfil.h"
-#include "byteptr.h"
-#include "p_saveg.h"
-#include "z_zone.h"
-#include "p_local.h"
-#include "p_haptic.h"
-#include "m_misc.h"
-#include "am_map.h"
-#include "m_random.h"
+#include "../byteptr.h"
+#include "../p_saveg.h"
+#include "../z_zone.h"
+#include "../p_local.h"
+#include "../p_haptic.h"
+#include "../m_misc.h"
+#include "../am_map.h"
+#include "../m_random.h"
 #include "mserv.h"
-#include "y_inter.h"
-#include "r_local.h"
-#include "m_argv.h"
-#include "p_setup.h"
-#include "lzf.h"
-#include "lua_script.h"
-#include "lua_hook.h"
-#include "lua_libs.h"
-#include "md5.h"
-#include "m_perfstats.h"
+#include "../y_inter.h"
+#include "../r_local.h"
+#include "../m_argv.h"
+#include "../p_setup.h"
+#include "../lzf.h"
+#include "../lua_script.h"
+#include "../lua_hook.h"
+#include "../lua_libs.h"
+#include "../md5.h"
+#include "../m_perfstats.h"
 
 // aaaaaa
-#include "i_gamepad.h"
+#include "../i_gamepad.h"
 
 // cl loading screen
-#include "v_video.h"
-#include "f_finale.h"
-#include "snake.h"
+#include "../v_video.h"
+#include "../f_finale.h"
+#include "../snake.h"
 
 //
 // NETWORKING
diff --git a/src/d_clisrv.h b/src/netcode/d_clisrv.h
similarity index 99%
rename from src/d_clisrv.h
rename to src/netcode/d_clisrv.h
index 0d6add13a3..50b86e9f01 100644
--- a/src/d_clisrv.h
+++ b/src/netcode/d_clisrv.h
@@ -13,12 +13,12 @@
 #ifndef __D_CLISRV__
 #define __D_CLISRV__
 
-#include "d_ticcmd.h"
+#include "../d_ticcmd.h"
 #include "d_net.h"
 #include "d_netcmd.h"
 #include "d_net.h"
-#include "tables.h"
-#include "d_player.h"
+#include "../tables.h"
+#include "../d_player.h"
 #include "mserv.h"
 
 /*
diff --git a/src/d_net.c b/src/netcode/d_net.c
similarity index 99%
rename from src/d_net.c
rename to src/netcode/d_net.c
index 9e3759d32c..ae0605001a 100644
--- a/src/d_net.c
+++ b/src/netcode/d_net.c
@@ -16,19 +16,19 @@
 ///        This protocol uses a mix of "goback n" and "selective repeat" implementation
 ///        The NOTHING packet is sent when connection is idle to acknowledge packets
 
-#include "doomdef.h"
-#include "g_game.h"
-#include "i_time.h"
+#include "../doomdef.h"
+#include "../g_game.h"
+#include "../i_time.h"
 #include "i_net.h"
-#include "i_system.h"
-#include "m_argv.h"
+#include "../i_system.h"
+#include "../m_argv.h"
 #include "d_net.h"
-#include "w_wad.h"
+#include "../w_wad.h"
 #include "d_netfil.h"
 #include "d_clisrv.h"
-#include "z_zone.h"
+#include "../z_zone.h"
 #include "i_tcp.h"
-#include "d_main.h" // srb2home
+#include "../d_main.h" // srb2home
 
 //
 // NETWORKING
diff --git a/src/d_net.h b/src/netcode/d_net.h
similarity index 100%
rename from src/d_net.h
rename to src/netcode/d_net.h
diff --git a/src/d_netcmd.c b/src/netcode/d_netcmd.c
similarity index 99%
rename from src/d_netcmd.c
rename to src/netcode/d_netcmd.c
index 69eed90de9..ed310805dc 100644
--- a/src/d_netcmd.c
+++ b/src/netcode/d_netcmd.c
@@ -12,44 +12,44 @@
 ///        commands are executed through the command buffer
 ///	       like console commands, other miscellaneous commands (at the end)
 
-#include "doomdef.h"
-
-#include "console.h"
-#include "command.h"
-#include "i_time.h"
-#include "i_system.h"
-#include "g_game.h"
-#include "hu_stuff.h"
-#include "g_input.h"
-#include "i_gamepad.h"
-#include "m_menu.h"
-#include "r_local.h"
-#include "r_skins.h"
-#include "p_local.h"
-#include "p_setup.h"
-#include "s_sound.h"
-#include "i_sound.h"
-#include "m_misc.h"
-#include "am_map.h"
-#include "byteptr.h"
+#include "../doomdef.h"
+
+#include "../console.h"
+#include "../command.h"
+#include "../i_time.h"
+#include "../i_system.h"
+#include "../g_game.h"
+#include "../hu_stuff.h"
+#include "../g_input.h"
+#include "../i_gamepad.h"
+#include "../m_menu.h"
+#include "../r_local.h"
+#include "../r_skins.h"
+#include "../p_local.h"
+#include "../p_setup.h"
+#include "../s_sound.h"
+#include "../i_sound.h"
+#include "../m_misc.h"
+#include "../am_map.h"
+#include "../byteptr.h"
 #include "d_netfil.h"
-#include "p_spec.h"
-#include "m_cheat.h"
+#include "../p_spec.h"
+#include "../m_cheat.h"
 #include "d_clisrv.h"
 #include "d_net.h"
-#include "v_video.h"
-#include "d_main.h"
-#include "m_random.h"
-#include "f_finale.h"
-#include "filesrch.h"
+#include "../v_video.h"
+#include "../d_main.h"
+#include "../m_random.h"
+#include "../f_finale.h"
+#include "../filesrch.h"
 #include "mserv.h"
-#include "z_zone.h"
-#include "lua_script.h"
-#include "lua_hook.h"
-#include "m_cond.h"
-#include "m_anigif.h"
-#include "md5.h"
-#include "m_perfstats.h"
+#include "../z_zone.h"
+#include "../lua_script.h"
+#include "../lua_hook.h"
+#include "../m_cond.h"
+#include "../m_anigif.h"
+#include "../md5.h"
+#include "../m_perfstats.h"
 
 #ifdef NETGAME_DEVMODE
 #define CV_RESTRICT CV_NETVAR
diff --git a/src/d_netcmd.h b/src/netcode/d_netcmd.h
similarity index 99%
rename from src/d_netcmd.h
rename to src/netcode/d_netcmd.h
index 47f68a17e9..797a686a78 100644
--- a/src/d_netcmd.h
+++ b/src/netcode/d_netcmd.h
@@ -15,7 +15,7 @@
 #ifndef __D_NETCMD__
 #define __D_NETCMD__
 
-#include "command.h"
+#include "../command.h"
 
 // console vars
 extern consvar_t cv_playername;
diff --git a/src/d_netfil.c b/src/netcode/d_netfil.c
similarity index 99%
rename from src/d_netfil.c
rename to src/netcode/d_netfil.c
index bf3952cc92..80fa068529 100644
--- a/src/d_netfil.c
+++ b/src/netcode/d_netfil.c
@@ -31,24 +31,24 @@
 #include <sys/utime.h>
 #endif
 
-#include "doomdef.h"
-#include "doomstat.h"
-#include "d_main.h"
-#include "g_game.h"
-#include "i_time.h"
+#include "../doomdef.h"
+#include "../doomstat.h"
+#include "../d_main.h"
+#include "../g_game.h"
+#include "../i_time.h"
 #include "i_net.h"
-#include "i_system.h"
-#include "m_argv.h"
+#include "../i_system.h"
+#include "../m_argv.h"
 #include "d_net.h"
-#include "w_wad.h"
+#include "../w_wad.h"
 #include "d_netfil.h"
-#include "z_zone.h"
-#include "byteptr.h"
-#include "p_setup.h"
-#include "m_misc.h"
-#include "m_menu.h"
-#include "md5.h"
-#include "filesrch.h"
+#include "../z_zone.h"
+#include "../byteptr.h"
+#include "../p_setup.h"
+#include "../m_misc.h"
+#include "../m_menu.h"
+#include "../md5.h"
+#include "../filesrch.h"
 
 #include <errno.h>
 
diff --git a/src/d_netfil.h b/src/netcode/d_netfil.h
similarity index 99%
rename from src/d_netfil.h
rename to src/netcode/d_netfil.h
index ec53be5879..732efcd5ec 100644
--- a/src/d_netfil.h
+++ b/src/netcode/d_netfil.h
@@ -15,7 +15,7 @@
 
 #include "d_net.h"
 #include "d_clisrv.h"
-#include "w_wad.h"
+#include "../w_wad.h"
 
 typedef enum
 {
diff --git a/src/http-mserv.c b/src/netcode/http-mserv.c
similarity index 98%
rename from src/http-mserv.c
rename to src/netcode/http-mserv.c
index b0ef37fa16..72dc1cafb2 100644
--- a/src/http-mserv.c
+++ b/src/netcode/http-mserv.c
@@ -18,14 +18,14 @@ Documentation available here.
 #include <curl/curl.h>
 #endif
 
-#include "doomdef.h"
+#include "../doomdef.h"
 #include "d_clisrv.h"
-#include "command.h"
-#include "m_argv.h"
-#include "m_menu.h"
+#include "../command.h"
+#include "../m_argv.h"
+#include "../m_menu.h"
 #include "mserv.h"
 #include "i_tcp.h"/* for current_port */
-#include "i_threads.h"
+#include "../i_threads.h"
 
 /* reasonable default I guess?? */
 #define DEFAULT_BUFFER_SIZE (4096)
diff --git a/src/i_addrinfo.c b/src/netcode/i_addrinfo.c
similarity index 100%
rename from src/i_addrinfo.c
rename to src/netcode/i_addrinfo.c
diff --git a/src/i_addrinfo.h b/src/netcode/i_addrinfo.h
similarity index 100%
rename from src/i_addrinfo.h
rename to src/netcode/i_addrinfo.h
diff --git a/src/i_net.h b/src/netcode/i_net.h
similarity index 98%
rename from src/i_net.h
rename to src/netcode/i_net.h
index 62b7528d59..66126d0503 100644
--- a/src/i_net.h
+++ b/src/netcode/i_net.h
@@ -18,8 +18,8 @@
 #pragma interface
 #endif
 
-#include "doomdef.h"
-#include "command.h"
+#include "../doomdef.h"
+#include "../command.h"
 
 /// \brief program net id
 #define DOOMCOM_ID (INT32)0x12345678l
diff --git a/src/i_tcp.c b/src/netcode/i_tcp.c
similarity index 99%
rename from src/i_tcp.c
rename to src/netcode/i_tcp.c
index ec3d1ae93a..6baba6275d 100644
--- a/src/i_tcp.c
+++ b/src/netcode/i_tcp.c
@@ -36,7 +36,7 @@
 	#include <ws2tcpip.h>
 #endif
 
-#include "doomdef.h"
+#include "../doomdef.h"
 
 #ifdef USE_WINSOCK1
 	#include <winsock.h>
@@ -122,14 +122,14 @@ typedef union
 
 #define MAXBANS 100
 
-#include "i_system.h"
+#include "../i_system.h"
 #include "i_net.h"
 #include "d_net.h"
 #include "d_netfil.h"
 #include "i_tcp.h"
-#include "m_argv.h"
+#include "../m_argv.h"
 
-#include "doomstat.h"
+#include "../doomstat.h"
 
 // win32
 #ifdef USE_WINSOCK
diff --git a/src/i_tcp.h b/src/netcode/i_tcp.h
similarity index 100%
rename from src/i_tcp.h
rename to src/netcode/i_tcp.h
diff --git a/src/mserv.c b/src/netcode/mserv.c
similarity index 98%
rename from src/mserv.c
rename to src/netcode/mserv.c
index 90091d2415..78301f4d98 100644
--- a/src/mserv.c
+++ b/src/netcode/mserv.c
@@ -15,13 +15,13 @@
 #include <time.h>
 #endif
 
-#include "doomstat.h"
-#include "doomdef.h"
-#include "command.h"
-#include "i_threads.h"
+#include "../doomstat.h"
+#include "../doomdef.h"
+#include "../command.h"
+#include "../i_threads.h"
 #include "mserv.h"
-#include "m_menu.h"
-#include "z_zone.h"
+#include "../m_menu.h"
+#include "../z_zone.h"
 
 #ifdef MASTERSERVER
 
diff --git a/src/mserv.h b/src/netcode/mserv.h
similarity index 99%
rename from src/mserv.h
rename to src/netcode/mserv.h
index 23b26fbc54..7fdf3ed1b9 100644
--- a/src/mserv.h
+++ b/src/netcode/mserv.h
@@ -14,7 +14,7 @@
 #ifndef _MSERV_H_
 #define _MSERV_H_
 
-#include "i_threads.h"
+#include "../i_threads.h"
 
 // lowered from 32 due to menu changes
 #define NUM_LIST_ROOMS 16
diff --git a/src/p_ceilng.c b/src/p_ceilng.c
index 98e9313625..61430d73f1 100644
--- a/src/p_ceilng.c
+++ b/src/p_ceilng.c
@@ -17,7 +17,7 @@
 #include "r_main.h"
 #include "s_sound.h"
 #include "z_zone.h"
-#include "d_netcmd.h"
+#include "netcode/d_netcmd.h"
 
 // ==========================================================================
 //                              CEILINGS
diff --git a/src/p_lights.c b/src/p_lights.c
index 4b6a3673b5..971165e88e 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -17,7 +17,7 @@
 #include "r_state.h"
 #include "z_zone.h"
 #include "m_random.h"
-#include "d_netcmd.h"
+#include "netcode/d_netcmd.h"
 
 /** Removes any active lighting effects in a sector.
   *
diff --git a/src/p_user.c b/src/p_user.c
index 1c2e4f7c9b..6bbefcb199 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -17,7 +17,7 @@
 #include "doomdef.h"
 #include "i_system.h"
 #include "d_event.h"
-#include "d_net.h"
+#include "netcode/d_net.h"
 #include "g_game.h"
 #include "p_local.h"
 #include "r_fps.h"
diff --git a/src/r_segs.c b/src/r_segs.c
index facab62ab7..00ee4fd6d3 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -20,7 +20,7 @@
 
 #include "w_wad.h"
 #include "z_zone.h"
-#include "d_netcmd.h"
+#include "netcode/d_netcmd.h"
 #include "m_misc.h"
 #include "p_local.h" // Camera...
 #include "p_slopes.h"
diff --git a/src/r_things.c b/src/r_things.c
index 90b80dda8f..642f3a8e2f 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -34,7 +34,7 @@
 #include "p_tick.h"
 #include "p_local.h"
 #include "p_slopes.h"
-#include "d_netfil.h" // blargh. for nameonly().
+#include "netcode/d_netfil.h" // blargh. for nameonly().
 #include "m_cheat.h" // objectplace
 #ifdef HWRENDER
 #include "hardware/hw_md2.h"
diff --git a/src/screen.c b/src/screen.c
index fe5b399958..05bbcfabb0 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -27,7 +27,7 @@
 #include "hu_stuff.h"
 #include "z_zone.h"
 #include "d_main.h"
-#include "d_clisrv.h"
+#include "netcode/d_clisrv.h"
 #include "f_finale.h"
 #include "y_inter.h" // usebuffer
 #include "i_sound.h" // closed captions
diff --git a/src/sdl/i_net.c b/src/sdl/i_net.c
index ee4a34c13d..515a855684 100644
--- a/src/sdl/i_net.c
+++ b/src/sdl/i_net.c
@@ -21,16 +21,16 @@
 
 #include "../i_system.h"
 #include "../d_event.h"
-#include "../d_net.h"
+#include "../netcode/d_net.h"
 #include "../m_argv.h"
 
 #include "../doomstat.h"
 
-#include "../i_net.h"
+#include "../netcode/i_net.h"
 
 #include "../z_zone.h"
 
-#include "../i_tcp.h"
+#include "../netcode/i_tcp.h"
 
 #ifdef HAVE_SDL
 
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index ba4dd7985e..50ceae34c2 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -193,7 +193,7 @@ static char returnWadPath[256];
 #include "../i_system.h"
 #include "../i_threads.h"
 #include "../screen.h" //vid.WndParent
-#include "../d_net.h"
+#include "../netcode/d_net.h"
 #include "../g_game.h"
 #include "../filesrch.h"
 #include "endtxt.h"
@@ -214,7 +214,7 @@ static char returnWadPath[256];
 
 #if !defined(NOMUMBLE) && defined(HAVE_MUMBLE)
 // Mumble context string
-#include "../d_clisrv.h"
+#include "../netcode/d_clisrv.h"
 #include "../byteptr.h"
 #endif
 
diff --git a/src/sdl/i_ttf.c b/src/sdl/i_ttf.c
index f2cd497eec..1f838e9b46 100644
--- a/src/sdl/i_ttf.c
+++ b/src/sdl/i_ttf.c
@@ -21,7 +21,7 @@
 #include "SDL_ttf.h"
 #include "../doomdef.h"
 #include "../doomstat.h"
-#include "../d_netfil.h"
+#include "../netcode/d_netfil.h"
 #include "../filesrch.h"
 #include "i_ttf.h"
 
diff --git a/src/w_wad.c b/src/w_wad.c
index 171eab4f31..23d84c3362 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -51,8 +51,8 @@
 #include "filesrch.h"
 
 #include "d_main.h"
-#include "d_netfil.h"
-#include "d_clisrv.h"
+#include "netcode/d_netfil.h"
+#include "netcode/d_clisrv.h"
 #include "dehacked.h"
 #include "r_defs.h"
 #include "r_data.h"
diff --git a/src/y_inter.c b/src/y_inter.c
index 1b1f49e0b7..c8bd06d91c 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -15,7 +15,7 @@
 #include "f_finale.h"
 #include "g_game.h"
 #include "hu_stuff.h"
-#include "i_net.h"
+#include "netcode/i_net.h"
 #include "i_video.h"
 #include "p_tick.h"
 #include "r_defs.h"
-- 
GitLab


From e090f0e4006e329fc6faf7d66cb508fab7e6be5a Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 31 Dec 2022 21:37:35 +0100
Subject: [PATCH 264/518] Move client connection handling to a new file

---
 src/m_menu.c                    |    1 +
 src/netcode/Sourcefile          |    1 +
 src/netcode/client_connection.c | 1181 ++++++++++++++++++++++++++++++
 src/netcode/client_connection.h |   61 ++
 src/netcode/d_clisrv.c          | 1184 +------------------------------
 src/netcode/d_clisrv.h          |   16 +-
 src/netcode/http-mserv.c        |    1 +
 src/netcode/mserv.c             |    1 +
 8 files changed, 1256 insertions(+), 1190 deletions(-)
 create mode 100644 src/netcode/client_connection.c
 create mode 100644 src/netcode/client_connection.h

diff --git a/src/m_menu.c b/src/m_menu.c
index 72e50de696..f0691c17fe 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -55,6 +55,7 @@
 
 #include "netcode/d_net.h"
 #include "netcode/mserv.h"
+#include "netcode/client_connection.h"
 #include "m_misc.h"
 #include "m_anigif.h"
 #include "byteptr.h"
diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile
index 9087917d04..c11b3d6c1f 100644
--- a/src/netcode/Sourcefile
+++ b/src/netcode/Sourcefile
@@ -1,4 +1,5 @@
 d_clisrv.c
+client_connection.c
 d_net.c
 d_netcmd.c
 d_netfil.c
diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c
new file mode 100644
index 0000000000..bfaea2e81e
--- /dev/null
+++ b/src/netcode/client_connection.c
@@ -0,0 +1,1181 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  client_connection.h
+/// \brief Client connection handling
+
+#include "client_connection.h"
+#include "d_clisrv.h"
+#include "d_netfil.h"
+#include "../d_main.h"
+#include "../f_finale.h"
+#include "../g_game.h"
+#include "../i_gamepad.h"
+#include "i_net.h"
+#include "../i_system.h"
+#include "../i_time.h"
+#include "../i_video.h"
+#include "../m_menu.h"
+#include "../m_misc.h"
+#include "../snake.h"
+#include "../s_sound.h"
+#include "../v_video.h"
+#include "../y_inter.h"
+#include "../z_zone.h"
+#include "../doomtype.h"
+#include "../doomstat.h"
+
+cl_mode_t cl_mode = CL_SEARCHING;
+static UINT16 cl_lastcheckedfilecount = 0;	// used for full file list
+boolean serverisfull = false; //lets us be aware if the server was full after we check files, but before downloading, so we can ask if the user still wants to download or not
+tic_t firstconnectattempttime = 0;
+UINT8 mynode; // my address pointofview server
+static void *snake = NULL;
+
+static void CL_DrawConnectionStatusBox(void)
+{
+	M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1);
+	if (cl_mode != CL_CONFIRMCONNECT)
+		V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort");
+}
+
+//
+// CL_DrawConnectionStatus
+//
+// Keep the local client informed of our status.
+//
+static inline void CL_DrawConnectionStatus(void)
+{
+	INT32 ccstime = I_GetTime();
+
+	// Draw background fade
+	V_DrawFadeScreen(0xFF00, 16); // force default
+
+	if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_LOADFILES)
+	{
+		INT32 i, animtime = ((ccstime / 4) & 15) + 16;
+		UINT8 palstart;
+		const char *cltext;
+
+		// Draw the bottom box.
+		CL_DrawConnectionStatusBox();
+
+		if (cl_mode == CL_SEARCHING)
+			palstart = 32; // Red
+		else if (cl_mode == CL_CONFIRMCONNECT)
+			palstart = 48; // Orange
+		else
+			palstart = 96; // Green
+
+		if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1))
+			for (i = 0; i < 16; ++i) // 15 pal entries total.
+				V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15));
+
+		switch (cl_mode)
+		{
+			case CL_DOWNLOADSAVEGAME:
+				if (fileneeded && lastfilenum != -1)
+				{
+					UINT32 currentsize = fileneeded[lastfilenum].currentsize;
+					UINT32 totalsize = fileneeded[lastfilenum].totalsize;
+					INT32 dldlength;
+
+					cltext = M_GetText("Downloading game state...");
+					Net_GetNetStat();
+
+					dldlength = (INT32)((currentsize/(double)totalsize) * 256);
+					if (dldlength > 256)
+						dldlength = 256;
+					V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111);
+					V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96);
+
+					V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
+						va(" %4uK/%4uK",currentsize>>10,totalsize>>10));
+
+					V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
+						va("%3.1fK/s ", ((double)getbps)/1024));
+				}
+				else
+					cltext = M_GetText("Waiting to download game state...");
+				break;
+			case CL_ASKFULLFILELIST:
+			case CL_CHECKFILES:
+				cltext = M_GetText("Checking server addon list...");
+				break;
+			case CL_CONFIRMCONNECT:
+				cltext = "";
+				break;
+			case CL_LOADFILES:
+				cltext = M_GetText("Loading server addons...");
+				break;
+			case CL_ASKJOIN:
+			case CL_WAITJOINRESPONSE:
+				if (serverisfull)
+					cltext = M_GetText("Server full, waiting for a slot...");
+				else
+					cltext = M_GetText("Requesting to join...");
+				break;
+			default:
+				cltext = M_GetText("Connecting to server...");
+				break;
+		}
+		V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, cltext);
+	}
+	else
+	{
+		if (cl_mode == CL_LOADFILES)
+		{
+			INT32 totalfileslength;
+			INT32 loadcompletednum = 0;
+			INT32 i;
+
+			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort");
+
+			//ima just count files here
+			if (fileneeded)
+			{
+				for (i = 0; i < fileneedednum; i++)
+					if (fileneeded[i].status == FS_OPEN)
+						loadcompletednum++;
+			}
+
+			// Loading progress
+			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, "Loading server addons...");
+			totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum)) * 256);
+			M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1);
+			V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111);
+			V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, totalfileslength, 8, 96);
+			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
+				va(" %2u/%2u Files",loadcompletednum,fileneedednum));
+		}
+		else if (lastfilenum != -1)
+		{
+			INT32 dldlength;
+			static char tempname[28];
+			fileneeded_t *file;
+			char *filename;
+
+			if (snake)
+				Snake_Draw(snake);
+
+			// Draw the bottom box.
+			CL_DrawConnectionStatusBox();
+
+			if (fileneeded)
+			{
+				file = &fileneeded[lastfilenum];
+				filename = file->filename;
+			}
+			else
+				return;
+
+			Net_GetNetStat();
+			dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256);
+			if (dldlength > 256)
+				dldlength = 256;
+			V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111);
+			V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96);
+
+			memset(tempname, 0, sizeof(tempname));
+			// offset filename to just the name only part
+			filename += strlen(filename) - nameonlylength(filename);
+
+			if (strlen(filename) > sizeof(tempname)-1) // too long to display fully
+			{
+				size_t endhalfpos = strlen(filename)-10;
+				// display as first 14 chars + ... + last 10 chars
+				// which should add up to 27 if our math(s) is correct
+				snprintf(tempname, sizeof(tempname), "%.14s...%.10s", filename, filename+endhalfpos);
+			}
+			else // we can copy the whole thing in safely
+			{
+				strncpy(tempname, filename, sizeof(tempname)-1);
+			}
+
+			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP,
+				va(M_GetText("Downloading \"%s\""), tempname));
+			V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
+				va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10));
+			V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
+				va("%3.1fK/s ", ((double)getbps)/1024));
+		}
+		else
+		{
+			if (snake)
+				Snake_Draw(snake);
+
+			CL_DrawConnectionStatusBox();
+			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP,
+				M_GetText("Waiting to download files..."));
+		}
+	}
+}
+
+static boolean CL_AskFileList(INT32 firstfile)
+{
+	netbuffer->packettype = PT_TELLFILESNEEDED;
+	netbuffer->u.filesneedednum = firstfile;
+
+	return HSendPacket(servernode, false, 0, sizeof (INT32));
+}
+
+/** Sends a special packet to declare how many players in local
+  * Used only in arbitratrenetstart()
+  * Sends a PT_CLIENTJOIN packet to the server
+  *
+  * \return True if the packet was successfully sent
+  * \todo Improve the description...
+  *
+  */
+boolean CL_SendJoin(void)
+{
+	UINT8 localplayers = 1;
+	if (netgame)
+		CONS_Printf(M_GetText("Sending join request...\n"));
+	netbuffer->packettype = PT_CLIENTJOIN;
+
+	netbuffer->u.clientcfg.modversion = MODVERSION;
+	strncpy(netbuffer->u.clientcfg.application,
+			SRB2APPLICATION,
+			sizeof netbuffer->u.clientcfg.application);
+
+	if (splitscreen || botingame)
+		localplayers++;
+	netbuffer->u.clientcfg.localplayers = localplayers;
+
+	CleanupPlayerName(consoleplayer, cv_playername.zstring);
+	if (splitscreen)
+		CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */
+
+	strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME);
+	strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME);
+
+	return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
+}
+
+static void SendAskInfo(INT32 node)
+{
+	const tic_t asktime = I_GetTime();
+	netbuffer->packettype = PT_ASKINFO;
+	netbuffer->u.askinfo.version = VERSION;
+	netbuffer->u.askinfo.time = (tic_t)LONG(asktime);
+
+	// Even if this never arrives due to the host being firewalled, we've
+	// now allowed traffic from the host to us in, so once the MS relays
+	// our address to the host, it'll be able to speak to us.
+	HSendPacket(node, false, 0, sizeof (askinfo_pak));
+}
+
+serverelem_t serverlist[MAXSERVERLIST];
+UINT32 serverlistcount = 0;
+
+#define FORCECLOSE 0x8000
+
+static void SL_ClearServerList(INT32 connectedserver)
+{
+	UINT32 i;
+
+	for (i = 0; i < serverlistcount; i++)
+		if (connectedserver != serverlist[i].node)
+		{
+			Net_CloseConnection(serverlist[i].node|FORCECLOSE);
+			serverlist[i].node = 0;
+		}
+	serverlistcount = 0;
+}
+
+static UINT32 SL_SearchServer(INT32 node)
+{
+	UINT32 i;
+	for (i = 0; i < serverlistcount; i++)
+		if (serverlist[i].node == node)
+			return i;
+
+	return UINT32_MAX;
+}
+
+static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
+{
+	UINT32 i;
+
+	// search if not already on it
+	i = SL_SearchServer(node);
+	if (i == UINT32_MAX)
+	{
+		// not found add it
+		if (serverlistcount >= MAXSERVERLIST)
+			return; // list full
+
+		/* check it later if connecting to this one */
+		if (node != servernode)
+		{
+			if (info->_255 != 255)
+				return;/* old packet format */
+
+			if (info->packetversion != PACKETVERSION)
+				return;/* old new packet format */
+
+			if (info->version != VERSION)
+				return; // Not same version.
+
+			if (info->subversion != SUBVERSION)
+				return; // Close, but no cigar.
+
+			if (strcmp(info->application, SRB2APPLICATION))
+				return;/* that's a different mod */
+		}
+
+		i = serverlistcount++;
+	}
+
+	serverlist[i].info = *info;
+	serverlist[i].node = node;
+
+	// resort server list
+	M_SortServerList();
+}
+
+#if defined (MASTERSERVER) && defined (HAVE_THREADS)
+struct Fetch_servers_ctx
+{
+	int room;
+	int id;
+};
+
+static void
+Fetch_servers_thread (struct Fetch_servers_ctx *ctx)
+{
+	msg_server_t *server_list;
+
+	server_list = GetShortServersList(ctx->room, ctx->id);
+
+	if (server_list)
+	{
+		I_lock_mutex(&ms_QueryId_mutex);
+		{
+			if (ctx->id != ms_QueryId)
+			{
+				free(server_list);
+				server_list = NULL;
+			}
+		}
+		I_unlock_mutex(ms_QueryId_mutex);
+
+		if (server_list)
+		{
+			I_lock_mutex(&m_menu_mutex);
+			{
+				if (m_waiting_mode == M_WAITING_SERVERS)
+					m_waiting_mode = M_NOT_WAITING;
+			}
+			I_unlock_mutex(m_menu_mutex);
+
+			I_lock_mutex(&ms_ServerList_mutex);
+			{
+				ms_ServerList = server_list;
+			}
+			I_unlock_mutex(ms_ServerList_mutex);
+		}
+	}
+
+	free(ctx);
+}
+#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/
+
+void CL_QueryServerList (msg_server_t *server_list)
+{
+	INT32 i;
+
+	for (i = 0; server_list[i].header.buffer[0]; i++)
+	{
+		// Make sure MS version matches our own, to
+		// thwart nefarious servers who lie to the MS.
+
+		/* lol bruh, that version COMES from the servers */
+		//if (strcmp(version, server_list[i].version) == 0)
+		{
+			INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port);
+			if (node == -1)
+				break; // no more node free
+			SendAskInfo(node);
+			// Force close the connection so that servers can't eat
+			// up nodes forever if we never get a reply back from them
+			// (usually when they've not forwarded their ports).
+			//
+			// Don't worry, we'll get in contact with the working
+			// servers again when they send SERVERINFO to us later!
+			//
+			// (Note: as a side effect this probably means every
+			// server in the list will probably be using the same node (e.g. node 1),
+			// not that it matters which nodes they use when
+			// the connections are closed afterwards anyway)
+			// -- Monster Iestyn 12/11/18
+			Net_CloseConnection(node|FORCECLOSE);
+		}
+	}
+}
+
+void CL_UpdateServerList(boolean internetsearch, INT32 room)
+{
+	(void)internetsearch;
+	(void)room;
+
+	SL_ClearServerList(0);
+
+	if (!netgame && I_NetOpenSocket)
+	{
+		if (I_NetOpenSocket())
+		{
+			netgame = true;
+			multiplayer = true;
+		}
+	}
+
+	// search for local servers
+	if (netgame)
+		SendAskInfo(BROADCASTADDR);
+
+#ifdef MASTERSERVER
+	if (internetsearch)
+	{
+#ifdef HAVE_THREADS
+		struct Fetch_servers_ctx *ctx;
+
+		ctx = malloc(sizeof *ctx);
+
+		/* This called from M_Refresh so I don't use a mutex */
+		m_waiting_mode = M_WAITING_SERVERS;
+
+		I_lock_mutex(&ms_QueryId_mutex);
+		{
+			ctx->id = ms_QueryId;
+		}
+		I_unlock_mutex(ms_QueryId_mutex);
+
+		ctx->room = room;
+
+		I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx);
+#else
+		msg_server_t *server_list;
+
+		server_list = GetShortServersList(room, 0);
+
+		if (server_list)
+		{
+			CL_QueryServerList(server_list);
+			free(server_list);
+		}
+#endif
+	}
+#endif/*MASTERSERVER*/
+}
+
+static void M_ConfirmConnect(event_t *ev)
+{
+	if (ev->type == ev_keydown || ev->type == ev_gamepad_down)
+	{
+		if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A))
+		{
+			if (totalfilesrequestednum > 0)
+			{
+				if (CL_SendFileRequest())
+				{
+					cl_mode = CL_DOWNLOADFILES;
+					Snake_Allocate(&snake);
+				}
+			}
+			else
+				cl_mode = CL_LOADFILES;
+
+			M_ClearMenus(true);
+		}
+		else if ((ev->type == ev_keydown && (ev->key == 'n' || ev->key == KEY_ESCAPE)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_B))
+		{
+			cl_mode = CL_ABORTED;
+			M_ClearMenus(true);
+		}
+	}
+}
+
+static boolean CL_FinishedFileList(void)
+{
+	INT32 i;
+	char *downloadsize = NULL;
+
+	//CONS_Printf(M_GetText("Checking files...\n"));
+	i = CL_CheckFiles();
+	if (i == 4) // still checking ...
+	{
+		return true;
+	}
+	else if (i == 3) // too many files
+	{
+		D_QuitNetGame();
+		CL_Reset();
+		D_StartTitle();
+		M_StartMessage(M_GetText(
+			"You have too many WAD files loaded\n"
+			"to add ones the server is using.\n"
+			"Please restart SRB2 before connecting.\n\n"
+			"Press ESC\n"
+		), NULL, MM_NOTHING);
+		return false;
+	}
+	else if (i == 2) // cannot join for some reason
+	{
+		D_QuitNetGame();
+		CL_Reset();
+		D_StartTitle();
+		M_StartMessage(M_GetText(
+			"You have the wrong addons loaded.\n\n"
+			"To play on this server, restart\n"
+			"the game and don't load any addons.\n"
+			"SRB2 will automatically add\n"
+			"everything you need when you join.\n\n"
+			"Press ESC\n"
+		), NULL, MM_NOTHING);
+		return false;
+	}
+	else if (i == 1)
+	{
+		if (serverisfull)
+		{
+			M_StartMessage(M_GetText(
+				"This server is full!\n"
+				"\n"
+				"You may load server addons (if any), and wait for a slot.\n"
+				"\n"
+				"Press ENTER to continue\nor ESC to cancel.\n\n"
+			), M_ConfirmConnect, MM_EVENTHANDLER);
+			cl_mode = CL_CONFIRMCONNECT;
+			curfadevalue = 0;
+		}
+		else
+			cl_mode = CL_LOADFILES;
+	}
+	else
+	{
+		// must download something
+		// can we, though?
+		if (!CL_CheckDownloadable()) // nope!
+		{
+			D_QuitNetGame();
+			CL_Reset();
+			D_StartTitle();
+			M_StartMessage(M_GetText(
+				"An error occured when trying to\n"
+				"download missing addons.\n"
+				"(This is almost always a problem\n"
+				"with the server, not your game.)\n\n"
+				"See the console or log file\n"
+				"for additional details.\n\n"
+				"Press ESC\n"
+			), NULL, MM_NOTHING);
+			return false;
+		}
+
+		downloadcompletednum = 0;
+		downloadcompletedsize = 0;
+		totalfilesrequestednum = 0;
+		totalfilesrequestedsize = 0;
+
+		if (fileneeded == NULL)
+			I_Error("CL_FinishedFileList: fileneeded == NULL");
+
+		for (i = 0; i < fileneedednum; i++)
+			if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD)
+			{
+				totalfilesrequestednum++;
+				totalfilesrequestedsize += fileneeded[i].totalsize;
+			}
+
+		if (totalfilesrequestedsize>>20 >= 100)
+			downloadsize = Z_StrDup(va("%uM",totalfilesrequestedsize>>20));
+		else
+			downloadsize = Z_StrDup(va("%uK",totalfilesrequestedsize>>10));
+
+		if (serverisfull)
+			M_StartMessage(va(M_GetText(
+				"This server is full!\n"
+				"Download of %s additional content\nis required to join.\n"
+				"\n"
+				"You may download, load server addons,\nand wait for a slot.\n"
+				"\n"
+				"Press ENTER to continue\nor ESC to cancel.\n"
+			), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER);
+		else
+			M_StartMessage(va(M_GetText(
+				"Download of %s additional content\nis required to join.\n"
+				"\n"
+				"Press ENTER to continue\nor ESC to cancel.\n"
+			), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER);
+
+		Z_Free(downloadsize);
+		cl_mode = CL_CONFIRMCONNECT;
+		curfadevalue = 0;
+	}
+	return true;
+}
+
+static const char * InvalidServerReason (serverinfo_pak *info)
+{
+#define EOT "\nPress ESC\n"
+
+	/* magic number for new packet format */
+	if (info->_255 != 255)
+	{
+		return
+			"Outdated server (version unknown).\n" EOT;
+	}
+
+	if (strncmp(info->application, SRB2APPLICATION, sizeof
+				info->application))
+	{
+		return va(
+				"%s cannot connect\n"
+				"to %s servers.\n" EOT,
+				SRB2APPLICATION,
+				info->application);
+	}
+
+	if (
+			info->packetversion != PACKETVERSION ||
+			info->version != VERSION ||
+			info->subversion != SUBVERSION
+	){
+		return va(
+				"Incompatible %s versions.\n"
+				"(server version %d.%d.%d)\n" EOT,
+				SRB2APPLICATION,
+				info->version / 100,
+				info->version % 100,
+				info->subversion);
+	}
+
+	switch (info->refusereason)
+	{
+		case REFUSE_BANNED:
+			return
+				"You have been banned\n"
+				"from the server.\n" EOT;
+		case REFUSE_JOINS_DISABLED:
+			return
+				"The server is not accepting\n"
+				"joins for the moment.\n" EOT;
+		case REFUSE_SLOTS_FULL:
+			return va(
+					"Maximum players reached: %d\n" EOT,
+					info->maxplayer);
+		default:
+			if (info->refusereason)
+			{
+				return
+					"You can't join.\n"
+					"I don't know why,\n"
+					"but you can't join.\n" EOT;
+			}
+	}
+
+	return NULL;
+
+#undef EOT
+}
+
+/** Called by CL_ServerConnectionTicker
+  *
+  * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit.
+  * \return False if the connection was aborted
+  * \sa CL_ServerConnectionTicker
+  * \sa CL_ConnectToServer
+  *
+  */
+static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
+{
+	INT32 i;
+
+	// serverlist is updated by GetPacket function
+	if (serverlistcount > 0)
+	{
+		// this can be a responce to our broadcast request
+		if (servernode == -1 || servernode >= MAXNETNODES)
+		{
+			i = 0;
+			servernode = serverlist[i].node;
+			CONS_Printf(M_GetText("Found, "));
+		}
+		else
+		{
+			i = SL_SearchServer(servernode);
+			if (i < 0)
+				return true;
+		}
+
+		if (client)
+		{
+			serverinfo_pak *info = &serverlist[i].info;
+
+			if (info->refusereason == REFUSE_SLOTS_FULL)
+				serverisfull = true;
+			else
+			{
+				const char *reason = InvalidServerReason(info);
+
+				// Quit here rather than downloading files
+				// and being refused later.
+				if (reason)
+				{
+					char *message = Z_StrDup(reason);
+					D_QuitNetGame();
+					CL_Reset();
+					D_StartTitle();
+					M_StartMessage(message, NULL, MM_NOTHING);
+					Z_Free(message);
+					return false;
+				}
+			}
+
+			D_ParseFileneeded(info->fileneedednum, info->fileneeded, 0);
+
+			if (info->flags & SV_LOTSOFADDONS)
+			{
+				cl_mode = CL_ASKFULLFILELIST;
+				cl_lastcheckedfilecount = 0;
+				return true;
+			}
+
+			cl_mode = CL_CHECKFILES;
+		}
+		else
+		{
+			cl_mode = CL_ASKJOIN; // files need not be checked for the server.
+			*asksent = 0;
+		}
+
+		return true;
+	}
+
+	// Ask the info to the server (askinfo packet)
+	if (*asksent + NEWTICRATE < I_GetTime())
+	{
+		SendAskInfo(servernode);
+		*asksent = I_GetTime();
+	}
+
+	return true;
+}
+
+/** Called by CL_ConnectToServer
+  *
+  * \param tmpsave The name of the gamestate file???
+  * \param oldtic Used for knowing when to poll events and redraw
+  * \param asksent ???
+  * \return False if the connection was aborted
+  * \sa CL_ServerConnectionSearchTicker
+  * \sa CL_ConnectToServer
+  *
+  */
+static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent)
+{
+	boolean waitmore;
+	INT32 i;
+
+	switch (cl_mode)
+	{
+		case CL_SEARCHING:
+			if (!CL_ServerConnectionSearchTicker(asksent))
+				return false;
+			break;
+
+		case CL_ASKFULLFILELIST:
+			if (cl_lastcheckedfilecount == UINT16_MAX) // All files retrieved
+				cl_mode = CL_CHECKFILES;
+			else if (fileneedednum != cl_lastcheckedfilecount || I_GetTime() >= *asksent)
+			{
+				if (CL_AskFileList(fileneedednum))
+				{
+					cl_lastcheckedfilecount = fileneedednum;
+					*asksent = I_GetTime() + NEWTICRATE;
+				}
+			}
+			break;
+		case CL_CHECKFILES:
+			if (!CL_FinishedFileList())
+				return false;
+			break;
+		case CL_DOWNLOADFILES:
+			waitmore = false;
+			for (i = 0; i < fileneedednum; i++)
+				if (fileneeded[i].status == FS_DOWNLOADING
+					|| fileneeded[i].status == FS_REQUESTED)
+				{
+					waitmore = true;
+					break;
+				}
+			if (waitmore)
+				break; // exit the case
+
+			Snake_Free(&snake);
+
+			cl_mode = CL_LOADFILES;
+			break;
+		case CL_LOADFILES:
+			if (CL_LoadServerFiles())
+			{
+				FreeFileNeeded();
+				*asksent = 0; //This ensure the first join ask is right away
+				firstconnectattempttime = I_GetTime();
+				cl_mode = CL_ASKJOIN;
+			}
+			break;
+		case CL_ASKJOIN:
+			if (firstconnectattempttime + NEWTICRATE*300 < I_GetTime() && !server)
+			{
+				CONS_Printf(M_GetText("5 minute wait time exceeded.\n"));
+				CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
+				D_QuitNetGame();
+				CL_Reset();
+				D_StartTitle();
+				M_StartMessage(M_GetText(
+					"5 minute wait time exceeded.\n"
+					"You may retry connection.\n"
+					"\n"
+					"Press ESC\n"
+				), NULL, MM_NOTHING);
+				return false;
+			}
+
+			// prepare structures to save the file
+			// WARNING: this can be useless in case of server not in GS_LEVEL
+			// but since the network layer doesn't provide ordered packets...
+			CL_PrepareDownloadSaveGame(tmpsave);
+
+			if (I_GetTime() >= *asksent && CL_SendJoin())
+			{
+				*asksent = I_GetTime() + NEWTICRATE*3;
+				cl_mode = CL_WAITJOINRESPONSE;
+			}
+			break;
+		case CL_WAITJOINRESPONSE:
+			if (I_GetTime() >= *asksent)
+			{
+				cl_mode = CL_ASKJOIN;
+			}
+			break;
+		case CL_DOWNLOADSAVEGAME:
+			// At this state, the first (and only) needed file is the gamestate
+			if (fileneeded[0].status == FS_FOUND)
+			{
+				// Gamestate is now handled within CL_LoadReceivedSavegame()
+				CL_LoadReceivedSavegame(false);
+				cl_mode = CL_CONNECTED;
+			} // don't break case continue to CL_CONNECTED
+			else
+				break;
+
+		case CL_CONNECTED:
+		case CL_CONFIRMCONNECT: //logic is handled by M_ConfirmConnect
+		default:
+			break;
+
+		// Connection closed by cancel, timeout or refusal.
+		case CL_ABORTED:
+			cl_mode = CL_SEARCHING;
+			return false;
+	}
+
+	GetPackets();
+	Net_AckTicker();
+
+	// Call it only once by tic
+	if (*oldtic != I_GetTime())
+	{
+		I_OsPolling();
+
+		if (cl_mode == CL_CONFIRMCONNECT)
+			D_ProcessEvents(); //needed for menu system to receive inputs
+		else
+		{
+			// my hand has been forced and I am dearly sorry for this awful hack :vomit:
+			for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
+			{
+				G_MapEventsToControls(&events[eventtail]);
+			}
+		}
+
+		if (gamekeydown[KEY_ESCAPE] || gamepads[0].buttons[GAMEPAD_BUTTON_B] || cl_mode == CL_ABORTED)
+		{
+			CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
+			M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
+
+			Snake_Free(&snake);
+
+			D_QuitNetGame();
+			CL_Reset();
+			D_StartTitle();
+			memset(gamekeydown, 0, NUMKEYS);
+			return false;
+		}
+		else if (cl_mode == CL_DOWNLOADFILES && snake)
+			Snake_Update(snake);
+
+		if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME))
+			FileReceiveTicker();
+
+		// why are these here? this is for servers, we're a client
+		//if (key == 's' && server)
+		//	doomcom->numnodes = (INT16)pnumnodes;
+		//FileSendTicker();
+		*oldtic = I_GetTime();
+
+		if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED)
+		{
+			if (!snake)
+			{
+				F_MenuPresTicker(true); // title sky
+				F_TitleScreenTicker(true);
+				F_TitleScreenDrawer();
+			}
+			CL_DrawConnectionStatus();
+#ifdef HAVE_THREADS
+			I_lock_mutex(&m_menu_mutex);
+#endif
+			M_Drawer(); //Needed for drawing messageboxes on the connection screen
+#ifdef HAVE_THREADS
+			I_unlock_mutex(m_menu_mutex);
+#endif
+			I_UpdateNoVsync(); // page flip or blit buffer
+			if (moviemode)
+				M_SaveFrame();
+			S_UpdateSounds();
+			S_UpdateClosedCaptions();
+		}
+	}
+	else
+	{
+		I_Sleep(cv_sleep.value);
+		I_UpdateTime(cv_timescale.value);
+	}
+
+	return true;
+}
+
+#define TMPSAVENAME "$$$.sav"
+
+/** Use adaptive send using net_bandwidth and stat.sendbytes
+  *
+  * \todo Better description...
+  *
+  */
+void CL_ConnectToServer(void)
+{
+	INT32 pnumnodes, nodewaited = doomcom->numnodes, i;
+	tic_t oldtic;
+	tic_t asksent;
+	char tmpsave[256];
+
+	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
+
+	lastfilenum = -1;
+
+	cl_mode = CL_SEARCHING;
+
+	// Don't get a corrupt savegame error because tmpsave already exists
+	if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
+		I_Error("Can't delete %s\n", tmpsave);
+
+	if (netgame)
+	{
+		if (servernode < 0 || servernode >= MAXNETNODES)
+			CONS_Printf(M_GetText("Searching for a server...\n"));
+		else
+			CONS_Printf(M_GetText("Contacting the server...\n"));
+	}
+
+	if (gamestate == GS_INTERMISSION)
+		Y_EndIntermission(); // clean up intermission graphics etc
+
+	DEBFILE(va("waiting %d nodes\n", doomcom->numnodes));
+	G_SetGamestate(GS_WAITINGPLAYERS);
+	wipegamestate = GS_WAITINGPLAYERS;
+
+	ClearAdminPlayers();
+	pnumnodes = 1;
+	oldtic = I_GetTime() - 1;
+
+	asksent = (tic_t) - TICRATE;
+	firstconnectattempttime = I_GetTime();
+
+	i = SL_SearchServer(servernode);
+
+	if (i != -1)
+	{
+		char *gametypestr = serverlist[i].info.gametypename;
+		CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername);
+		gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0';
+		CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr);
+		CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100,
+		 serverlist[i].info.version%100, serverlist[i].info.subversion);
+	}
+	SL_ClearServerList(servernode);
+
+	do
+	{
+		// If the connection was aborted for some reason, leave
+		if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent))
+			return;
+
+		if (server)
+		{
+			pnumnodes = 0;
+			for (i = 0; i < MAXNETNODES; i++)
+				if (netnodes[i].ingame)
+					pnumnodes++;
+		}
+	}
+	while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes))));
+
+	DEBFILE(va("Synchronisation Finished\n"));
+
+	displayplayer = consoleplayer;
+}
+
+/** Called when a PT_SERVERINFO packet is received
+  *
+  * \param node The packet sender
+  * \note What happens if the packet comes from a client or something like that?
+  *
+  */
+void HandleServerInfo(SINT8 node)
+{
+	// compute ping in ms
+	const tic_t ticnow = I_GetTime();
+	const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time);
+	const tic_t ticdiff = (ticnow - ticthen)*1000/NEWTICRATE;
+	netbuffer->u.serverinfo.time = (tic_t)LONG(ticdiff);
+	netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0;
+	netbuffer->u.serverinfo.application
+		[sizeof netbuffer->u.serverinfo.application - 1] = '\0';
+	netbuffer->u.serverinfo.gametypename
+		[sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0';
+
+	SL_InsertServer(&netbuffer->u.serverinfo, node);
+}
+
+// Helper function for packets that should only be sent by the server
+// If it is NOT from the server, bail out and close the connection!
+static boolean ServerOnly(SINT8 node)
+{
+	if (node == servernode)
+		return false;
+
+	Net_CloseConnection(node);
+	return true;
+}
+
+void PT_MoreFilesNeeded(SINT8 node)
+{
+	if (server && serverrunning)
+	{ // But wait I thought I'm the server?
+		Net_CloseConnection(node);
+		return;
+	}
+	if (ServerOnly(node))
+		return;
+	if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum)
+	{
+		D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first);
+		if (!netbuffer->u.filesneededcfg.more)
+			cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list
+	}
+}
+
+// Negative response of client join request
+void PT_ServerRefuse(SINT8 node)
+{
+	if (server && serverrunning)
+	{ // But wait I thought I'm the server?
+		Net_CloseConnection(node);
+		return;
+	}
+	if (ServerOnly(node))
+		return;
+	if (cl_mode == CL_WAITJOINRESPONSE)
+	{
+		// Save the reason so it can be displayed after quitting the netgame
+		char *reason = strdup(netbuffer->u.serverrefuse.reason);
+		if (!reason)
+			I_Error("Out of memory!\n");
+
+		if (strstr(reason, "Maximum players reached"))
+		{
+			serverisfull = true;
+			//Special timeout for when refusing due to player cap. The client will wait 3 seconds between join requests when waiting for a slot, so we need this to be much longer
+			//We set it back to the value of cv_nettimeout.value in CL_Reset
+			connectiontimeout = NEWTICRATE*7;
+			cl_mode = CL_ASKJOIN;
+			free(reason);
+			return;
+		}
+
+		M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
+			reason), NULL, MM_NOTHING);
+
+		D_QuitNetGame();
+		CL_Reset();
+		D_StartTitle();
+
+		free(reason);
+
+		// Will be reset by caller. Signals refusal.
+		cl_mode = CL_ABORTED;
+	}
+}
+
+// Positive response of client join request
+void PT_ServerCFG(SINT8 node)
+{
+	if (server && serverrunning && node != servernode)
+	{ // but wait I thought I'm the server?
+		Net_CloseConnection(node);
+		return;
+	}
+	if (ServerOnly(node))
+		return;
+	/// \note how would this happen? and is it doing the right thing if it does?
+	if (cl_mode != CL_WAITJOINRESPONSE)
+		return;
+
+	if (client)
+	{
+		maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
+		G_SetGametype(netbuffer->u.servercfg.gametype);
+		modifiedgame = netbuffer->u.servercfg.modifiedgame;
+		memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
+	}
+
+	netnodes[(UINT8)servernode].ingame = true;
+	serverplayer = netbuffer->u.servercfg.serverplayer;
+	doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
+	mynode = netbuffer->u.servercfg.clientnode;
+	if (serverplayer >= 0)
+		playernode[(UINT8)serverplayer] = servernode;
+
+	if (netgame)
+		CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n"));
+	DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode));
+
+	/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
+	///       Shouldn't them be downloaded even at intermission time?
+	///       Also, according to HandleConnect, the server will send the savegame even during intermission...
+	if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* ||
+		netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
+		cl_mode = CL_DOWNLOADSAVEGAME;
+	else
+		cl_mode = CL_CONNECTED;
+}
diff --git a/src/netcode/client_connection.h b/src/netcode/client_connection.h
new file mode 100644
index 0000000000..a76411ba64
--- /dev/null
+++ b/src/netcode/client_connection.h
@@ -0,0 +1,61 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  client_connection.h
+/// \brief Client connection handling
+
+#ifndef __D_CLIENT_CONNECTION__
+#define __D_CLIENT_CONNECTION__
+
+#include "../doomtype.h"
+#include "d_clisrv.h"
+
+#define MAXSERVERLIST (MAXNETNODES-1)
+
+typedef struct
+{
+	SINT8 node;
+	serverinfo_pak info;
+} serverelem_t;
+
+typedef enum
+{
+	CL_SEARCHING,
+	CL_CHECKFILES,
+	CL_DOWNLOADFILES,
+	CL_ASKJOIN,
+	CL_LOADFILES,
+	CL_WAITJOINRESPONSE,
+	CL_DOWNLOADSAVEGAME,
+	CL_CONNECTED,
+	CL_ABORTED,
+	CL_ASKFULLFILELIST,
+	CL_CONFIRMCONNECT
+} cl_mode_t;
+
+extern serverelem_t serverlist[MAXSERVERLIST];
+extern UINT32 serverlistcount;
+
+extern cl_mode_t cl_mode;
+extern boolean serverisfull; //lets us be aware if the server was full after we check files, but before downloading, so we can ask if the user still wants to download or not
+extern tic_t firstconnectattempttime;
+extern UINT8 mynode; // my address pointofview server
+
+void CL_QueryServerList(msg_server_t *list);
+void CL_UpdateServerList(boolean internetsearch, INT32 room);
+
+void CL_ConnectToServer(void);
+boolean CL_SendJoin(void);
+
+void HandleServerInfo(SINT8 node);
+void PT_MoreFilesNeeded(SINT8 node);
+void PT_ServerRefuse(SINT8 node);
+void PT_ServerCFG(SINT8 node);
+
+#endif
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 9b11e23ff0..f7a9260e1c 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -49,14 +49,7 @@
 #include "../lua_libs.h"
 #include "../md5.h"
 #include "../m_perfstats.h"
-
-// aaaaaa
-#include "../i_gamepad.h"
-
-// cl loading screen
-#include "../v_video.h"
-#include "../f_finale.h"
-#include "../snake.h"
+#include "client_connection.h"
 
 //
 // NETWORKING
@@ -103,7 +96,7 @@ tic_t servermaxping = 800; // server's max ping. Defaults to 800
 
 static tic_t firstticstosend; // min of the nettics
 static tic_t tictoclear = 0; // optimize d_clearticcmd
-static tic_t maketic;
+tic_t maketic;
 
 static INT16 consistancy[BACKUPTICS];
 
@@ -121,21 +114,17 @@ static ticcmd_t localcmds;
 static ticcmd_t localcmds2;
 static boolean cl_packetmissed;
 // here it is for the secondary local player (splitscreen)
-static UINT8 mynode; // my address pointofview server
 static boolean cl_redownloadinggamestate = false;
 
 static UINT8 localtextcmd[MAXTEXTCMD];
 static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen
-static tic_t neededtic;
+tic_t neededtic;
 SINT8 servernode = 0; // the number of the server node
 
 /// \brief do we accept new players?
 /// \todo WORK!
 boolean acceptnewnode = true;
 
-static boolean serverisfull = false; //lets us be aware if the server was full after we check files, but before downloading, so we can ask if the user still wants to download or not
-static tic_t firstconnectattempttime = 0;
-
 // engine
 
 // Must be a power of two
@@ -512,250 +501,6 @@ void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum)
 
 static INT16 Consistancy(void);
 
-typedef enum
-{
-	CL_SEARCHING,
-	CL_CHECKFILES,
-	CL_DOWNLOADFILES,
-	CL_ASKJOIN,
-	CL_LOADFILES,
-	CL_WAITJOINRESPONSE,
-	CL_DOWNLOADSAVEGAME,
-	CL_CONNECTED,
-	CL_ABORTED,
-	CL_ASKFULLFILELIST,
-	CL_CONFIRMCONNECT
-} cl_mode_t;
-
-static void GetPackets(void);
-
-static cl_mode_t cl_mode = CL_SEARCHING;
-
-static UINT16 cl_lastcheckedfilecount = 0;	// used for full file list
-
-static void *snake = NULL;
-
-static void CL_DrawConnectionStatusBox(void)
-{
-	M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1);
-	if (cl_mode != CL_CONFIRMCONNECT)
-		V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort");
-}
-
-//
-// CL_DrawConnectionStatus
-//
-// Keep the local client informed of our status.
-//
-static inline void CL_DrawConnectionStatus(void)
-{
-	INT32 ccstime = I_GetTime();
-
-	// Draw background fade
-	V_DrawFadeScreen(0xFF00, 16); // force default
-
-	if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_LOADFILES)
-	{
-		INT32 i, animtime = ((ccstime / 4) & 15) + 16;
-		UINT8 palstart;
-		const char *cltext;
-
-		// Draw the bottom box.
-		CL_DrawConnectionStatusBox();
-
-		if (cl_mode == CL_SEARCHING)
-			palstart = 32; // Red
-		else if (cl_mode == CL_CONFIRMCONNECT)
-			palstart = 48; // Orange
-		else
-			palstart = 96; // Green
-
-		if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1))
-			for (i = 0; i < 16; ++i) // 15 pal entries total.
-				V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15));
-
-		switch (cl_mode)
-		{
-			case CL_DOWNLOADSAVEGAME:
-				if (fileneeded && lastfilenum != -1)
-				{
-					UINT32 currentsize = fileneeded[lastfilenum].currentsize;
-					UINT32 totalsize = fileneeded[lastfilenum].totalsize;
-					INT32 dldlength;
-
-					cltext = M_GetText("Downloading game state...");
-					Net_GetNetStat();
-
-					dldlength = (INT32)((currentsize/(double)totalsize) * 256);
-					if (dldlength > 256)
-						dldlength = 256;
-					V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111);
-					V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96);
-
-					V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
-						va(" %4uK/%4uK",currentsize>>10,totalsize>>10));
-
-					V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
-						va("%3.1fK/s ", ((double)getbps)/1024));
-				}
-				else
-					cltext = M_GetText("Waiting to download game state...");
-				break;
-			case CL_ASKFULLFILELIST:
-			case CL_CHECKFILES:
-				cltext = M_GetText("Checking server addon list...");
-				break;
-			case CL_CONFIRMCONNECT:
-				cltext = "";
-				break;
-			case CL_LOADFILES:
-				cltext = M_GetText("Loading server addons...");
-				break;
-			case CL_ASKJOIN:
-			case CL_WAITJOINRESPONSE:
-				if (serverisfull)
-					cltext = M_GetText("Server full, waiting for a slot...");
-				else
-					cltext = M_GetText("Requesting to join...");
-				break;
-			default:
-				cltext = M_GetText("Connecting to server...");
-				break;
-		}
-		V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, cltext);
-	}
-	else
-	{
-		if (cl_mode == CL_LOADFILES)
-		{
-			INT32 totalfileslength;
-			INT32 loadcompletednum = 0;
-			INT32 i;
-
-			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort");
-
-			//ima just count files here
-			if (fileneeded)
-			{
-				for (i = 0; i < fileneedednum; i++)
-					if (fileneeded[i].status == FS_OPEN)
-						loadcompletednum++;
-			}
-
-			// Loading progress
-			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, "Loading server addons...");
-			totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum)) * 256);
-			M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1);
-			V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111);
-			V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, totalfileslength, 8, 96);
-			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
-				va(" %2u/%2u Files",loadcompletednum,fileneedednum));
-		}
-		else if (lastfilenum != -1)
-		{
-			INT32 dldlength;
-			static char tempname[28];
-			fileneeded_t *file;
-			char *filename;
-
-			if (snake)
-				Snake_Draw(snake);
-
-			// Draw the bottom box.
-			CL_DrawConnectionStatusBox();
-
-			if (fileneeded)
-			{
-				file = &fileneeded[lastfilenum];
-				filename = file->filename;
-			}
-			else
-				return;
-
-			Net_GetNetStat();
-			dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256);
-			if (dldlength > 256)
-				dldlength = 256;
-			V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111);
-			V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96);
-
-			memset(tempname, 0, sizeof(tempname));
-			// offset filename to just the name only part
-			filename += strlen(filename) - nameonlylength(filename);
-
-			if (strlen(filename) > sizeof(tempname)-1) // too long to display fully
-			{
-				size_t endhalfpos = strlen(filename)-10;
-				// display as first 14 chars + ... + last 10 chars
-				// which should add up to 27 if our math(s) is correct
-				snprintf(tempname, sizeof(tempname), "%.14s...%.10s", filename, filename+endhalfpos);
-			}
-			else // we can copy the whole thing in safely
-			{
-				strncpy(tempname, filename, sizeof(tempname)-1);
-			}
-
-			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP,
-				va(M_GetText("Downloading \"%s\""), tempname));
-			V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
-				va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10));
-			V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
-				va("%3.1fK/s ", ((double)getbps)/1024));
-		}
-		else
-		{
-			if (snake)
-				Snake_Draw(snake);
-
-			CL_DrawConnectionStatusBox();
-			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP,
-				M_GetText("Waiting to download files..."));
-		}
-	}
-}
-
-static boolean CL_AskFileList(INT32 firstfile)
-{
-	netbuffer->packettype = PT_TELLFILESNEEDED;
-	netbuffer->u.filesneedednum = firstfile;
-
-	return HSendPacket(servernode, false, 0, sizeof (INT32));
-}
-
-/** Sends a special packet to declare how many players in local
-  * Used only in arbitratrenetstart()
-  * Sends a PT_CLIENTJOIN packet to the server
-  *
-  * \return True if the packet was successfully sent
-  * \todo Improve the description...
-  *
-  */
-static boolean CL_SendJoin(void)
-{
-	UINT8 localplayers = 1;
-	if (netgame)
-		CONS_Printf(M_GetText("Sending join request...\n"));
-	netbuffer->packettype = PT_CLIENTJOIN;
-
-	netbuffer->u.clientcfg.modversion = MODVERSION;
-	strncpy(netbuffer->u.clientcfg.application,
-			SRB2APPLICATION,
-			sizeof netbuffer->u.clientcfg.application);
-
-	if (splitscreen || botingame)
-		localplayers++;
-	netbuffer->u.clientcfg.localplayers = localplayers;
-
-	CleanupPlayerName(consoleplayer, cv_playername.zstring);
-	if (splitscreen)
-		CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */
-
-	strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME);
-	strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME);
-
-	return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
-}
-
 static INT32 FindRejoinerNum(SINT8 node)
 {
 	char strippednodeaddress[64];
@@ -1105,7 +850,7 @@ static void SV_SavedGame(void)
 #define TMPSAVENAME "$$$.sav"
 
 
-static void CL_LoadReceivedSavegame(boolean reloading)
+void CL_LoadReceivedSavegame(boolean reloading)
 {
 	UINT8 *savebuffer = NULL;
 	size_t length, decompressedlen;
@@ -1206,790 +951,6 @@ static void CL_ReloadReceivedSavegame(void)
 	CONS_Printf(M_GetText("Game state reloaded\n"));
 }
 
-static void SendAskInfo(INT32 node)
-{
-	const tic_t asktime = I_GetTime();
-	netbuffer->packettype = PT_ASKINFO;
-	netbuffer->u.askinfo.version = VERSION;
-	netbuffer->u.askinfo.time = (tic_t)LONG(asktime);
-
-	// Even if this never arrives due to the host being firewalled, we've
-	// now allowed traffic from the host to us in, so once the MS relays
-	// our address to the host, it'll be able to speak to us.
-	HSendPacket(node, false, 0, sizeof (askinfo_pak));
-}
-
-serverelem_t serverlist[MAXSERVERLIST];
-UINT32 serverlistcount = 0;
-
-#define FORCECLOSE 0x8000
-
-static void SL_ClearServerList(INT32 connectedserver)
-{
-	UINT32 i;
-
-	for (i = 0; i < serverlistcount; i++)
-		if (connectedserver != serverlist[i].node)
-		{
-			Net_CloseConnection(serverlist[i].node|FORCECLOSE);
-			serverlist[i].node = 0;
-		}
-	serverlistcount = 0;
-}
-
-static UINT32 SL_SearchServer(INT32 node)
-{
-	UINT32 i;
-	for (i = 0; i < serverlistcount; i++)
-		if (serverlist[i].node == node)
-			return i;
-
-	return UINT32_MAX;
-}
-
-static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
-{
-	UINT32 i;
-
-	// search if not already on it
-	i = SL_SearchServer(node);
-	if (i == UINT32_MAX)
-	{
-		// not found add it
-		if (serverlistcount >= MAXSERVERLIST)
-			return; // list full
-
-		/* check it later if connecting to this one */
-		if (node != servernode)
-		{
-			if (info->_255 != 255)
-				return;/* old packet format */
-
-			if (info->packetversion != PACKETVERSION)
-				return;/* old new packet format */
-
-			if (info->version != VERSION)
-				return; // Not same version.
-
-			if (info->subversion != SUBVERSION)
-				return; // Close, but no cigar.
-
-			if (strcmp(info->application, SRB2APPLICATION))
-				return;/* that's a different mod */
-		}
-
-		i = serverlistcount++;
-	}
-
-	serverlist[i].info = *info;
-	serverlist[i].node = node;
-
-	// resort server list
-	M_SortServerList();
-}
-
-#if defined (MASTERSERVER) && defined (HAVE_THREADS)
-struct Fetch_servers_ctx
-{
-	int room;
-	int id;
-};
-
-static void
-Fetch_servers_thread (struct Fetch_servers_ctx *ctx)
-{
-	msg_server_t *server_list;
-
-	server_list = GetShortServersList(ctx->room, ctx->id);
-
-	if (server_list)
-	{
-		I_lock_mutex(&ms_QueryId_mutex);
-		{
-			if (ctx->id != ms_QueryId)
-			{
-				free(server_list);
-				server_list = NULL;
-			}
-		}
-		I_unlock_mutex(ms_QueryId_mutex);
-
-		if (server_list)
-		{
-			I_lock_mutex(&m_menu_mutex);
-			{
-				if (m_waiting_mode == M_WAITING_SERVERS)
-					m_waiting_mode = M_NOT_WAITING;
-			}
-			I_unlock_mutex(m_menu_mutex);
-
-			I_lock_mutex(&ms_ServerList_mutex);
-			{
-				ms_ServerList = server_list;
-			}
-			I_unlock_mutex(ms_ServerList_mutex);
-		}
-	}
-
-	free(ctx);
-}
-#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/
-
-void CL_QueryServerList (msg_server_t *server_list)
-{
-	INT32 i;
-
-	for (i = 0; server_list[i].header.buffer[0]; i++)
-	{
-		// Make sure MS version matches our own, to
-		// thwart nefarious servers who lie to the MS.
-
-		/* lol bruh, that version COMES from the servers */
-		//if (strcmp(version, server_list[i].version) == 0)
-		{
-			INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port);
-			if (node == -1)
-				break; // no more node free
-			SendAskInfo(node);
-			// Force close the connection so that servers can't eat
-			// up nodes forever if we never get a reply back from them
-			// (usually when they've not forwarded their ports).
-			//
-			// Don't worry, we'll get in contact with the working
-			// servers again when they send SERVERINFO to us later!
-			//
-			// (Note: as a side effect this probably means every
-			// server in the list will probably be using the same node (e.g. node 1),
-			// not that it matters which nodes they use when
-			// the connections are closed afterwards anyway)
-			// -- Monster Iestyn 12/11/18
-			Net_CloseConnection(node|FORCECLOSE);
-		}
-	}
-}
-
-void CL_UpdateServerList(boolean internetsearch, INT32 room)
-{
-	(void)internetsearch;
-	(void)room;
-
-	SL_ClearServerList(0);
-
-	if (!netgame && I_NetOpenSocket)
-	{
-		if (I_NetOpenSocket())
-		{
-			netgame = true;
-			multiplayer = true;
-		}
-	}
-
-	// search for local servers
-	if (netgame)
-		SendAskInfo(BROADCASTADDR);
-
-#ifdef MASTERSERVER
-	if (internetsearch)
-	{
-#ifdef HAVE_THREADS
-		struct Fetch_servers_ctx *ctx;
-
-		ctx = malloc(sizeof *ctx);
-
-		/* This called from M_Refresh so I don't use a mutex */
-		m_waiting_mode = M_WAITING_SERVERS;
-
-		I_lock_mutex(&ms_QueryId_mutex);
-		{
-			ctx->id = ms_QueryId;
-		}
-		I_unlock_mutex(ms_QueryId_mutex);
-
-		ctx->room = room;
-
-		I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx);
-#else
-		msg_server_t *server_list;
-
-		server_list = GetShortServersList(room, 0);
-
-		if (server_list)
-		{
-			CL_QueryServerList(server_list);
-			free(server_list);
-		}
-#endif
-	}
-#endif/*MASTERSERVER*/
-}
-
-static void M_ConfirmConnect(event_t *ev)
-{
-	if (ev->type == ev_keydown || ev->type == ev_gamepad_down)
-	{
-		if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A))
-		{
-			if (totalfilesrequestednum > 0)
-			{
-				if (CL_SendFileRequest())
-				{
-					cl_mode = CL_DOWNLOADFILES;
-					Snake_Allocate(&snake);
-				}
-			}
-			else
-				cl_mode = CL_LOADFILES;
-
-			M_ClearMenus(true);
-		}
-		else if ((ev->type == ev_keydown && (ev->key == 'n' || ev->key == KEY_ESCAPE)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_B))
-		{
-			cl_mode = CL_ABORTED;
-			M_ClearMenus(true);
-		}
-	}
-}
-
-static boolean CL_FinishedFileList(void)
-{
-	INT32 i;
-	char *downloadsize = NULL;
-
-	//CONS_Printf(M_GetText("Checking files...\n"));
-	i = CL_CheckFiles();
-	if (i == 4) // still checking ...
-	{
-		return true;
-	}
-	else if (i == 3) // too many files
-	{
-		D_QuitNetGame();
-		CL_Reset();
-		D_StartTitle();
-		M_StartMessage(M_GetText(
-			"You have too many WAD files loaded\n"
-			"to add ones the server is using.\n"
-			"Please restart SRB2 before connecting.\n\n"
-			"Press ESC\n"
-		), NULL, MM_NOTHING);
-		return false;
-	}
-	else if (i == 2) // cannot join for some reason
-	{
-		D_QuitNetGame();
-		CL_Reset();
-		D_StartTitle();
-		M_StartMessage(M_GetText(
-			"You have the wrong addons loaded.\n\n"
-			"To play on this server, restart\n"
-			"the game and don't load any addons.\n"
-			"SRB2 will automatically add\n"
-			"everything you need when you join.\n\n"
-			"Press ESC\n"
-		), NULL, MM_NOTHING);
-		return false;
-	}
-	else if (i == 1)
-	{
-		if (serverisfull)
-		{
-			M_StartMessage(M_GetText(
-				"This server is full!\n"
-				"\n"
-				"You may load server addons (if any), and wait for a slot.\n"
-				"\n"
-				"Press ENTER to continue\nor ESC to cancel.\n\n"
-			), M_ConfirmConnect, MM_EVENTHANDLER);
-			cl_mode = CL_CONFIRMCONNECT;
-			curfadevalue = 0;
-		}
-		else
-			cl_mode = CL_LOADFILES;
-	}
-	else
-	{
-		// must download something
-		// can we, though?
-		if (!CL_CheckDownloadable()) // nope!
-		{
-			D_QuitNetGame();
-			CL_Reset();
-			D_StartTitle();
-			M_StartMessage(M_GetText(
-				"An error occured when trying to\n"
-				"download missing addons.\n"
-				"(This is almost always a problem\n"
-				"with the server, not your game.)\n\n"
-				"See the console or log file\n"
-				"for additional details.\n\n"
-				"Press ESC\n"
-			), NULL, MM_NOTHING);
-			return false;
-		}
-
-		downloadcompletednum = 0;
-		downloadcompletedsize = 0;
-		totalfilesrequestednum = 0;
-		totalfilesrequestedsize = 0;
-
-		if (fileneeded == NULL)
-			I_Error("CL_FinishedFileList: fileneeded == NULL");
-
-		for (i = 0; i < fileneedednum; i++)
-			if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD)
-			{
-				totalfilesrequestednum++;
-				totalfilesrequestedsize += fileneeded[i].totalsize;
-			}
-
-		if (totalfilesrequestedsize>>20 >= 100)
-			downloadsize = Z_StrDup(va("%uM",totalfilesrequestedsize>>20));
-		else
-			downloadsize = Z_StrDup(va("%uK",totalfilesrequestedsize>>10));
-
-		if (serverisfull)
-			M_StartMessage(va(M_GetText(
-				"This server is full!\n"
-				"Download of %s additional content\nis required to join.\n"
-				"\n"
-				"You may download, load server addons,\nand wait for a slot.\n"
-				"\n"
-				"Press ENTER to continue\nor ESC to cancel.\n"
-			), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER);
-		else
-			M_StartMessage(va(M_GetText(
-				"Download of %s additional content\nis required to join.\n"
-				"\n"
-				"Press ENTER to continue\nor ESC to cancel.\n"
-			), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER);
-
-		Z_Free(downloadsize);
-		cl_mode = CL_CONFIRMCONNECT;
-		curfadevalue = 0;
-	}
-	return true;
-}
-
-static const char * InvalidServerReason (serverinfo_pak *info)
-{
-#define EOT "\nPress ESC\n"
-
-	/* magic number for new packet format */
-	if (info->_255 != 255)
-	{
-		return
-			"Outdated server (version unknown).\n" EOT;
-	}
-
-	if (strncmp(info->application, SRB2APPLICATION, sizeof
-				info->application))
-	{
-		return va(
-				"%s cannot connect\n"
-				"to %s servers.\n" EOT,
-				SRB2APPLICATION,
-				info->application);
-	}
-
-	if (
-			info->packetversion != PACKETVERSION ||
-			info->version != VERSION ||
-			info->subversion != SUBVERSION
-	){
-		return va(
-				"Incompatible %s versions.\n"
-				"(server version %d.%d.%d)\n" EOT,
-				SRB2APPLICATION,
-				info->version / 100,
-				info->version % 100,
-				info->subversion);
-	}
-
-	switch (info->refusereason)
-	{
-		case REFUSE_BANNED:
-			return
-				"You have been banned\n"
-				"from the server.\n" EOT;
-		case REFUSE_JOINS_DISABLED:
-			return
-				"The server is not accepting\n"
-				"joins for the moment.\n" EOT;
-		case REFUSE_SLOTS_FULL:
-			return va(
-					"Maximum players reached: %d\n" EOT,
-					info->maxplayer);
-		default:
-			if (info->refusereason)
-			{
-				return
-					"You can't join.\n"
-					"I don't know why,\n"
-					"but you can't join.\n" EOT;
-			}
-	}
-
-	return NULL;
-
-#undef EOT
-}
-
-/** Called by CL_ServerConnectionTicker
-  *
-  * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit.
-  * \return False if the connection was aborted
-  * \sa CL_ServerConnectionTicker
-  * \sa CL_ConnectToServer
-  *
-  */
-static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
-{
-	INT32 i;
-
-	// serverlist is updated by GetPacket function
-	if (serverlistcount > 0)
-	{
-		// this can be a responce to our broadcast request
-		if (servernode == -1 || servernode >= MAXNETNODES)
-		{
-			i = 0;
-			servernode = serverlist[i].node;
-			CONS_Printf(M_GetText("Found, "));
-		}
-		else
-		{
-			i = SL_SearchServer(servernode);
-			if (i < 0)
-				return true;
-		}
-
-		if (client)
-		{
-			serverinfo_pak *info = &serverlist[i].info;
-
-			if (info->refusereason == REFUSE_SLOTS_FULL)
-				serverisfull = true;
-			else
-			{
-				const char *reason = InvalidServerReason(info);
-
-				// Quit here rather than downloading files
-				// and being refused later.
-				if (reason)
-				{
-					char *message = Z_StrDup(reason);
-					D_QuitNetGame();
-					CL_Reset();
-					D_StartTitle();
-					M_StartMessage(message, NULL, MM_NOTHING);
-					Z_Free(message);
-					return false;
-				}
-			}
-
-			D_ParseFileneeded(info->fileneedednum, info->fileneeded, 0);
-
-			if (info->flags & SV_LOTSOFADDONS)
-			{
-				cl_mode = CL_ASKFULLFILELIST;
-				cl_lastcheckedfilecount = 0;
-				return true;
-			}
-
-			cl_mode = CL_CHECKFILES;
-		}
-		else
-		{
-			cl_mode = CL_ASKJOIN; // files need not be checked for the server.
-			*asksent = 0;
-		}
-
-		return true;
-	}
-
-	// Ask the info to the server (askinfo packet)
-	if (*asksent + NEWTICRATE < I_GetTime())
-	{
-		SendAskInfo(servernode);
-		*asksent = I_GetTime();
-	}
-
-	return true;
-}
-
-/** Called by CL_ConnectToServer
-  *
-  * \param tmpsave The name of the gamestate file???
-  * \param oldtic Used for knowing when to poll events and redraw
-  * \param asksent ???
-  * \return False if the connection was aborted
-  * \sa CL_ServerConnectionSearchTicker
-  * \sa CL_ConnectToServer
-  *
-  */
-static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent)
-{
-	boolean waitmore;
-	INT32 i;
-
-	switch (cl_mode)
-	{
-		case CL_SEARCHING:
-			if (!CL_ServerConnectionSearchTicker(asksent))
-				return false;
-			break;
-
-		case CL_ASKFULLFILELIST:
-			if (cl_lastcheckedfilecount == UINT16_MAX) // All files retrieved
-				cl_mode = CL_CHECKFILES;
-			else if (fileneedednum != cl_lastcheckedfilecount || I_GetTime() >= *asksent)
-			{
-				if (CL_AskFileList(fileneedednum))
-				{
-					cl_lastcheckedfilecount = fileneedednum;
-					*asksent = I_GetTime() + NEWTICRATE;
-				}
-			}
-			break;
-		case CL_CHECKFILES:
-			if (!CL_FinishedFileList())
-				return false;
-			break;
-		case CL_DOWNLOADFILES:
-			waitmore = false;
-			for (i = 0; i < fileneedednum; i++)
-				if (fileneeded[i].status == FS_DOWNLOADING
-					|| fileneeded[i].status == FS_REQUESTED)
-				{
-					waitmore = true;
-					break;
-				}
-			if (waitmore)
-				break; // exit the case
-
-			Snake_Free(&snake);
-
-			cl_mode = CL_LOADFILES;
-			break;
-		case CL_LOADFILES:
-			if (CL_LoadServerFiles())
-			{
-				FreeFileNeeded();
-				*asksent = 0; //This ensure the first join ask is right away
-				firstconnectattempttime = I_GetTime();
-				cl_mode = CL_ASKJOIN;
-			}
-			break;
-		case CL_ASKJOIN:
-			if (firstconnectattempttime + NEWTICRATE*300 < I_GetTime() && !server)
-			{
-				CONS_Printf(M_GetText("5 minute wait time exceeded.\n"));
-				CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
-				D_QuitNetGame();
-				CL_Reset();
-				D_StartTitle();
-				M_StartMessage(M_GetText(
-					"5 minute wait time exceeded.\n"
-					"You may retry connection.\n"
-					"\n"
-					"Press ESC\n"
-				), NULL, MM_NOTHING);
-				return false;
-			}
-
-			// prepare structures to save the file
-			// WARNING: this can be useless in case of server not in GS_LEVEL
-			// but since the network layer doesn't provide ordered packets...
-			CL_PrepareDownloadSaveGame(tmpsave);
-
-			if (I_GetTime() >= *asksent && CL_SendJoin())
-			{
-				*asksent = I_GetTime() + NEWTICRATE*3;
-				cl_mode = CL_WAITJOINRESPONSE;
-			}
-			break;
-		case CL_WAITJOINRESPONSE:
-			if (I_GetTime() >= *asksent)
-			{
-				cl_mode = CL_ASKJOIN;
-			}
-			break;
-		case CL_DOWNLOADSAVEGAME:
-			// At this state, the first (and only) needed file is the gamestate
-			if (fileneeded[0].status == FS_FOUND)
-			{
-				// Gamestate is now handled within CL_LoadReceivedSavegame()
-				CL_LoadReceivedSavegame(false);
-				cl_mode = CL_CONNECTED;
-			} // don't break case continue to CL_CONNECTED
-			else
-				break;
-
-		case CL_CONNECTED:
-		case CL_CONFIRMCONNECT: //logic is handled by M_ConfirmConnect
-		default:
-			break;
-
-		// Connection closed by cancel, timeout or refusal.
-		case CL_ABORTED:
-			cl_mode = CL_SEARCHING;
-			return false;
-	}
-
-	GetPackets();
-	Net_AckTicker();
-
-	// Call it only once by tic
-	if (*oldtic != I_GetTime())
-	{
-		I_OsPolling();
-
-		if (cl_mode == CL_CONFIRMCONNECT)
-			D_ProcessEvents(); //needed for menu system to receive inputs
-		else
-		{
-			// my hand has been forced and I am dearly sorry for this awful hack :vomit:
-			for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
-			{
-				G_MapEventsToControls(&events[eventtail]);
-			}
-		}
-
-		if (gamekeydown[KEY_ESCAPE] || gamepads[0].buttons[GAMEPAD_BUTTON_B] || cl_mode == CL_ABORTED)
-		{
-			CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
-			M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
-
-			Snake_Free(&snake);
-
-			D_QuitNetGame();
-			CL_Reset();
-			D_StartTitle();
-			memset(gamekeydown, 0, NUMKEYS);
-			return false;
-		}
-		else if (cl_mode == CL_DOWNLOADFILES && snake)
-			Snake_Update(snake);
-
-		if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME))
-			FileReceiveTicker();
-
-		// why are these here? this is for servers, we're a client
-		//if (key == 's' && server)
-		//	doomcom->numnodes = (INT16)pnumnodes;
-		//FileSendTicker();
-		*oldtic = I_GetTime();
-
-		if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED)
-		{
-			if (!snake)
-			{
-				F_MenuPresTicker(true); // title sky
-				F_TitleScreenTicker(true);
-				F_TitleScreenDrawer();
-			}
-			CL_DrawConnectionStatus();
-#ifdef HAVE_THREADS
-			I_lock_mutex(&m_menu_mutex);
-#endif
-			M_Drawer(); //Needed for drawing messageboxes on the connection screen
-#ifdef HAVE_THREADS
-			I_unlock_mutex(m_menu_mutex);
-#endif
-			I_UpdateNoVsync(); // page flip or blit buffer
-			if (moviemode)
-				M_SaveFrame();
-			S_UpdateSounds();
-			S_UpdateClosedCaptions();
-		}
-	}
-	else
-	{
-		I_Sleep(cv_sleep.value);
-		I_UpdateTime(cv_timescale.value);
-	}
-
-	return true;
-}
-
-/** Use adaptive send using net_bandwidth and stat.sendbytes
-  *
-  * \todo Better description...
-  *
-  */
-static void CL_ConnectToServer(void)
-{
-	INT32 pnumnodes, nodewaited = doomcom->numnodes, i;
-	tic_t oldtic;
-	tic_t asksent;
-	char tmpsave[256];
-
-	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
-
-	lastfilenum = -1;
-
-	cl_mode = CL_SEARCHING;
-
-	// Don't get a corrupt savegame error because tmpsave already exists
-	if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
-		I_Error("Can't delete %s\n", tmpsave);
-
-	if (netgame)
-	{
-		if (servernode < 0 || servernode >= MAXNETNODES)
-			CONS_Printf(M_GetText("Searching for a server...\n"));
-		else
-			CONS_Printf(M_GetText("Contacting the server...\n"));
-	}
-
-	if (gamestate == GS_INTERMISSION)
-		Y_EndIntermission(); // clean up intermission graphics etc
-
-	DEBFILE(va("waiting %d nodes\n", doomcom->numnodes));
-	G_SetGamestate(GS_WAITINGPLAYERS);
-	wipegamestate = GS_WAITINGPLAYERS;
-
-	ClearAdminPlayers();
-	pnumnodes = 1;
-	oldtic = I_GetTime() - 1;
-
-	asksent = (tic_t) - TICRATE;
-	firstconnectattempttime = I_GetTime();
-
-	i = SL_SearchServer(servernode);
-
-	if (i != -1)
-	{
-		char *gametypestr = serverlist[i].info.gametypename;
-		CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername);
-		gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0';
-		CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr);
-		CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100,
-		 serverlist[i].info.version%100, serverlist[i].info.subversion);
-	}
-	SL_ClearServerList(servernode);
-
-	do
-	{
-		// If the connection was aborted for some reason, leave
-		if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent))
-			return;
-
-		if (server)
-		{
-			pnumnodes = 0;
-			for (i = 0; i < MAXNETNODES; i++)
-				if (netnodes[i].ingame)
-					pnumnodes++;
-		}
-	}
-	while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes))));
-
-	DEBFILE(va("Synchronisation Finished\n"));
-
-	displayplayer = consoleplayer;
-}
-
 typedef struct banreason_s
 {
 	char *reason;
@@ -3525,39 +2486,6 @@ static void HandleTimeout(SINT8 node)
 	M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
 }
 
-/** Called when a PT_SERVERINFO packet is received
-  *
-  * \param node The packet sender
-  * \note What happens if the packet comes from a client or something like that?
-  *
-  */
-static void HandleServerInfo(SINT8 node)
-{
-	// compute ping in ms
-	const tic_t ticnow = I_GetTime();
-	const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time);
-	const tic_t ticdiff = (ticnow - ticthen)*1000/NEWTICRATE;
-	netbuffer->u.serverinfo.time = (tic_t)LONG(ticdiff);
-	netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0;
-	netbuffer->u.serverinfo.application
-		[sizeof netbuffer->u.serverinfo.application - 1] = '\0';
-	netbuffer->u.serverinfo.gametypename
-		[sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0';
-
-	SL_InsertServer(&netbuffer->u.serverinfo, node);
-}
-
-// Helper function for packets that should only be sent by the server
-// If it is NOT from the server, bail out and close the connection!
-static boolean ServerOnly(SINT8 node)
-{
-	if (node == servernode)
-		return false;
-
-	Net_CloseConnection(node);
-	return true;
-}
-
 static void PT_AskInfoViaMS(SINT8 node)
 {
 	Net_CloseConnection(node);
@@ -3582,23 +2510,6 @@ static void PT_TellFilesNeeded(SINT8 node)
 		Net_CloseConnection(node);
 }
 
-static void PT_MoreFilesNeeded(SINT8 node)
-{
-	if (server && serverrunning)
-	{ // But wait I thought I'm the server?
-		Net_CloseConnection(node);
-		return;
-	}
-	if (ServerOnly(node))
-		return;
-	if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum)
-	{
-		D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first);
-		if (!netbuffer->u.filesneededcfg.more)
-			cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list
-	}
-}
-
 static void PT_AskInfo(SINT8 node)
 {
 	if (server && serverrunning)
@@ -3609,91 +2520,6 @@ static void PT_AskInfo(SINT8 node)
 	Net_CloseConnection(node);
 }
 
-// Negative response of client join request
-static void PT_ServerRefuse(SINT8 node)
-{
-	if (server && serverrunning)
-	{ // But wait I thought I'm the server?
-		Net_CloseConnection(node);
-		return;
-	}
-	if (ServerOnly(node))
-		return;
-	if (cl_mode == CL_WAITJOINRESPONSE)
-	{
-		// Save the reason so it can be displayed after quitting the netgame
-		char *reason = strdup(netbuffer->u.serverrefuse.reason);
-		if (!reason)
-			I_Error("Out of memory!\n");
-
-		if (strstr(reason, "Maximum players reached"))
-		{
-			serverisfull = true;
-			//Special timeout for when refusing due to player cap. The client will wait 3 seconds between join requests when waiting for a slot, so we need this to be much longer
-			//We set it back to the value of cv_nettimeout.value in CL_Reset
-			connectiontimeout = NEWTICRATE*7;
-			cl_mode = CL_ASKJOIN;
-			free(reason);
-			return;
-		}
-
-		M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
-			reason), NULL, MM_NOTHING);
-
-		D_QuitNetGame();
-		CL_Reset();
-		D_StartTitle();
-
-		free(reason);
-
-		// Will be reset by caller. Signals refusal.
-		cl_mode = CL_ABORTED;
-	}
-}
-
-// Positive response of client join request
-static void PT_ServerCFG(SINT8 node)
-{
-	if (server && serverrunning && node != servernode)
-	{ // but wait I thought I'm the server?
-		Net_CloseConnection(node);
-		return;
-	}
-	if (ServerOnly(node))
-		return;
-	/// \note how would this happen? and is it doing the right thing if it does?
-	if (cl_mode != CL_WAITJOINRESPONSE)
-		return;
-
-	if (client)
-	{
-		maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
-		G_SetGametype(netbuffer->u.servercfg.gametype);
-		modifiedgame = netbuffer->u.servercfg.modifiedgame;
-		memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
-	}
-
-	netnodes[(UINT8)servernode].ingame = true;
-	serverplayer = netbuffer->u.servercfg.serverplayer;
-	doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
-	mynode = netbuffer->u.servercfg.clientnode;
-	if (serverplayer >= 0)
-		playernode[(UINT8)serverplayer] = servernode;
-
-	if (netgame)
-		CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n"));
-	DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode));
-
-	/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
-	///       Shouldn't them be downloaded even at intermission time?
-	///       Also, according to HandleConnect, the server will send the savegame even during intermission...
-	if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* ||
-		netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
-		cl_mode = CL_DOWNLOADSAVEGAME;
-	else
-		cl_mode = CL_CONNECTED;
-}
-
 static void PT_ClientCmd(SINT8 node, INT32 netconsole)
 {
 	tic_t realend, realstart;
@@ -4190,7 +3016,7 @@ static void HandlePacketFromPlayer(SINT8 node)
   * \todo Add details to this description (lol)
   *
   */
-static void GetPackets(void)
+void GetPackets(void)
 {
 	SINT8 node; // The packet sender
 
diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h
index 50b86e9f01..3b0690a799 100644
--- a/src/netcode/d_clisrv.h
+++ b/src/netcode/d_clisrv.h
@@ -332,15 +332,6 @@ typedef struct
 #pragma pack()
 #endif
 
-#define MAXSERVERLIST (MAXNETNODES-1)
-typedef struct
-{
-	SINT8 node;
-	serverinfo_pak info;
-} serverelem_t;
-
-extern serverelem_t serverlist[MAXSERVERLIST];
-extern UINT32 serverlistcount;
 extern INT32 mapchangepending;
 
 // Points inside doomcom
@@ -385,6 +376,8 @@ extern boolean dedicated; // For dedicated server
 extern UINT16 software_MAXPACKETLENGTH;
 extern boolean acceptnewnode;
 extern SINT8 servernode;
+extern tic_t maketic;
+extern tic_t neededtic;
 
 void Command_Ping_f(void);
 extern tic_t connectiontimeout;
@@ -411,6 +404,8 @@ void SendKick(UINT8 playernum, UINT8 msg);
 // Create any new ticcmds and broadcast to other players.
 void NetUpdate(void);
 
+void GetPackets(void);
+
 void SV_StartSinglePlayerServer(void);
 void SV_SpawnServer(void);
 void SV_StopServer(void);
@@ -419,9 +414,8 @@ void CL_AddSplitscreenPlayer(void);
 void CL_RemoveSplitscreenPlayer(void);
 void CL_Reset(void);
 void CL_ClearPlayer(INT32 playernum);
-void CL_QueryServerList(msg_server_t *list);
-void CL_UpdateServerList(boolean internetsearch, INT32 room);
 void CL_RemovePlayer(INT32 playernum, kickreason_t reason);
+void CL_LoadReceivedSavegame(boolean reloading);
 // Is there a game running
 boolean Playing(void);
 
diff --git a/src/netcode/http-mserv.c b/src/netcode/http-mserv.c
index 72dc1cafb2..68e46b52a1 100644
--- a/src/netcode/http-mserv.c
+++ b/src/netcode/http-mserv.c
@@ -20,6 +20,7 @@ Documentation available here.
 
 #include "../doomdef.h"
 #include "d_clisrv.h"
+#include "client_connection.h"
 #include "../command.h"
 #include "../m_argv.h"
 #include "../m_menu.h"
diff --git a/src/netcode/mserv.c b/src/netcode/mserv.c
index 78301f4d98..f603d78e5b 100644
--- a/src/netcode/mserv.c
+++ b/src/netcode/mserv.c
@@ -20,6 +20,7 @@
 #include "../command.h"
 #include "../i_threads.h"
 #include "mserv.h"
+#include "client_connection.h"
 #include "../m_menu.h"
 #include "../z_zone.h"
 
-- 
GitLab


From fc41dd78f46f5683612a5e33f922d94881d2b13e Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sun, 1 Jan 2023 11:45:09 +0100
Subject: [PATCH 265/518] Move server-side connection handling to a new file

---
 src/netcode/Sourcefile          |   1 +
 src/netcode/d_clisrv.c          | 475 +-----------------------------
 src/netcode/d_clisrv.h          |   3 +-
 src/netcode/d_netcmd.c          |   1 +
 src/netcode/server_connection.c | 497 ++++++++++++++++++++++++++++++++
 src/netcode/server_connection.h |  29 ++
 6 files changed, 533 insertions(+), 473 deletions(-)
 create mode 100644 src/netcode/server_connection.c
 create mode 100644 src/netcode/server_connection.h

diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile
index c11b3d6c1f..4177166a45 100644
--- a/src/netcode/Sourcefile
+++ b/src/netcode/Sourcefile
@@ -1,4 +1,5 @@
 d_clisrv.c
+server_connection.c
 client_connection.c
 d_net.c
 d_netcmd.c
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index f7a9260e1c..c1264768e0 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -49,6 +49,7 @@
 #include "../lua_libs.h"
 #include "../md5.h"
 #include "../m_perfstats.h"
+#include "server_connection.h"
 #include "client_connection.h"
 
 //
@@ -79,15 +80,6 @@ netnode_t netnodes[MAXNETNODES];
 
 // Server specific vars
 UINT8 playernode[MAXPLAYERS];
-char playeraddress[MAXPLAYERS][64];
-
-// Minimum timeout for sending the savegame
-// The actual timeout will be longer depending on the savegame length
-tic_t jointimeout = (10*TICRATE);
-
-// Incremented by cv_joindelay when a client joins, decremented each tic.
-// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled.
-static tic_t joindelay = 0;
 
 UINT16 pingmeasurecount = 1;
 UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone.
@@ -100,7 +92,6 @@ tic_t maketic;
 
 static INT16 consistancy[BACKUPTICS];
 
-static UINT8 player_joining = false;
 UINT8 hu_redownloadinggamestate = 0;
 
 // true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks
@@ -501,229 +492,6 @@ void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum)
 
 static INT16 Consistancy(void);
 
-static INT32 FindRejoinerNum(SINT8 node)
-{
-	char strippednodeaddress[64];
-	const char *nodeaddress;
-	char *port;
-	INT32 i;
-
-	// Make sure there is no dead dress before proceeding to the stripping
-	if (!I_GetNodeAddress)
-		return -1;
-	nodeaddress = I_GetNodeAddress(node);
-	if (!nodeaddress)
-		return -1;
-
-	// Strip the address of its port
-	strcpy(strippednodeaddress, nodeaddress);
-	port = strchr(strippednodeaddress, ':');
-	if (port)
-		*port = '\0';
-
-	// Check if any player matches the stripped address
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX
-		&& !strcmp(playeraddress[i], strippednodeaddress))
-			return i;
-	}
-
-	return -1;
-}
-
-static UINT8
-GetRefuseReason (INT32 node)
-{
-	if (!node || FindRejoinerNum(node) != -1)
-		return 0;
-	else if (bannednode && bannednode[node])
-		return REFUSE_BANNED;
-	else if (!cv_allownewplayer.value)
-		return REFUSE_JOINS_DISABLED;
-	else if (D_NumPlayers() >= cv_maxplayers.value)
-		return REFUSE_SLOTS_FULL;
-	else
-		return 0;
-}
-
-static void SV_SendServerInfo(INT32 node, tic_t servertime)
-{
-	UINT8 *p;
-
-	netbuffer->packettype = PT_SERVERINFO;
-	netbuffer->u.serverinfo._255 = 255;
-	netbuffer->u.serverinfo.packetversion = PACKETVERSION;
-	netbuffer->u.serverinfo.version = VERSION;
-	netbuffer->u.serverinfo.subversion = SUBVERSION;
-	strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION,
-			sizeof netbuffer->u.serverinfo.application);
-	// return back the time value so client can compute their ping
-	netbuffer->u.serverinfo.time = (tic_t)LONG(servertime);
-	netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime);
-
-	netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
-	netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
-
-	netbuffer->u.serverinfo.refusereason = GetRefuseReason(node);
-
-	strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype],
-			sizeof netbuffer->u.serverinfo.gametypename);
-	netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
-	netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
-	netbuffer->u.serverinfo.flags = (dedicated ? SV_DEDICATED : 0);
-	strncpy(netbuffer->u.serverinfo.servername, cv_servername.string,
-		MAXSERVERNAME);
-	strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7);
-
-	M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16);
-
-	memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle);
-
-	if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl)
-	{
-		char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle;
-		while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0')
-		{
-			if (!(*read & 0x80))
-			{
-				*writ = toupper(*read);
-				writ++;
-			}
-			read++;
-		}
-		*writ = '\0';
-		//strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33);
-	}
-	else
-		strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32);
-
-	if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
-		netbuffer->u.serverinfo.iszone = 1;
-	else
-		netbuffer->u.serverinfo.iszone = 0;
-
-	if (mapheaderinfo[gamemap-1])
-		netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum;
-
-	p = PutFileNeeded(0);
-
-	HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
-}
-
-static void SV_SendPlayerInfo(INT32 node)
-{
-	UINT8 i;
-	netbuffer->packettype = PT_PLAYERINFO;
-
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (!playeringame[i])
-		{
-			netbuffer->u.playerinfo[i].num = 255; // This slot is empty.
-			continue;
-		}
-
-		netbuffer->u.playerinfo[i].num = i;
-		strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1);
-		netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0';
-
-		//fetch IP address
-		//No, don't do that, you fuckface.
-		memset(netbuffer->u.playerinfo[i].address, 0, 4);
-
-		if (G_GametypeHasTeams())
-		{
-			if (!players[i].ctfteam)
-				netbuffer->u.playerinfo[i].team = 255;
-			else
-				netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam;
-		}
-		else
-		{
-			if (players[i].spectator)
-				netbuffer->u.playerinfo[i].team = 255;
-			else
-				netbuffer->u.playerinfo[i].team = 0;
-		}
-
-		netbuffer->u.playerinfo[i].score = LONG(players[i].score);
-		netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE));
-		netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin
-#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself
-		% 3
-#endif
-		);
-
-		// Extra data
-		netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor;
-
-		if (players[i].pflags & PF_TAGIT)
-			netbuffer->u.playerinfo[i].data |= 0x20;
-
-		if (players[i].gotflag)
-			netbuffer->u.playerinfo[i].data |= 0x40;
-
-		if (players[i].powers[pw_super])
-			netbuffer->u.playerinfo[i].data |= 0x80;
-	}
-
-	HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS);
-}
-
-/** Sends a PT_SERVERCFG packet
-  *
-  * \param node The destination
-  * \return True if the packet was successfully sent
-  *
-  */
-static boolean SV_SendServerConfig(INT32 node)
-{
-	boolean waspacketsent;
-
-	netbuffer->packettype = PT_SERVERCFG;
-
-	netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer;
-	netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots);
-	netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic);
-	netbuffer->u.servercfg.clientnode = (UINT8)node;
-	netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
-	netbuffer->u.servercfg.gametype = (UINT8)gametype;
-	netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
-
-	memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
-
-	{
-		const size_t len = sizeof (serverconfig_pak);
-
-#ifdef DEBUGFILE
-		if (debugfile)
-		{
-			fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n",
-				sizeu1(len), node);
-		}
-#endif
-
-		waspacketsent = HSendPacket(node, true, 0, len);
-	}
-
-#ifdef DEBUGFILE
-	if (debugfile)
-	{
-		if (waspacketsent)
-		{
-			fprintf(debugfile, "ServerConfig Packet was sent\n");
-		}
-		else
-		{
-			fprintf(debugfile, "ServerConfig Packet could not be sent right now\n");
-		}
-	}
-#endif
-
-	return waspacketsent;
-}
-
 #define SAVEGAMESIZE (768*1024)
 
 static boolean SV_ResendingSavegameToAnyone(void)
@@ -736,7 +504,7 @@ static boolean SV_ResendingSavegameToAnyone(void)
 	return false;
 }
 
-static void SV_SendSaveGame(INT32 node, boolean resending)
+void SV_SendSaveGame(INT32 node, boolean resending)
 {
 	size_t length, compressedlen;
 	UINT8 *savebuffer;
@@ -1190,7 +958,7 @@ static void Command_connect(void)
 	CL_ConnectToServer();
 }
 
-static void ResetNode(INT32 node)
+void ResetNode(INT32 node)
 {
 	memset(&netnodes[node], 0, sizeof(*netnodes));
 	netnodes[node].player = -1;
@@ -2020,17 +1788,6 @@ void D_QuitNetGame(void)
 #endif
 }
 
-// Adds a node to the game (player will follow at map change or at savegame....)
-static inline void SV_AddNode(INT32 node)
-{
-	netnodes[node].tic = gametic;
-	netnodes[node].supposedtic = gametic;
-	// little hack because the server connects to itself and puts
-	// nodeingame when connected not here
-	if (node)
-		netnodes[node].ingame = true;
-}
-
 // Xcmd XD_ADDPLAYER
 static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 {
@@ -2163,56 +1920,6 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 		LUA_HookInt(newplayernum, HOOK(PlayerJoin));
 }
 
-static void SV_AddPlayer(SINT8 node, const char *name)
-{
-	INT32 n;
-	UINT8 buf[2 + MAXPLAYERNAME];
-	UINT8 *p;
-	INT32 newplayernum;
-
-	newplayernum = FindRejoinerNum(node);
-	if (newplayernum == -1)
-	{
-		// search for a free playernum
-		// we can't use playeringame since it is not updated here
-		for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++)
-		{
-			if (playeringame[newplayernum])
-				continue;
-			for (n = 0; n < MAXNETNODES; n++)
-				if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum)
-					break;
-			if (n == MAXNETNODES)
-				break;
-		}
-	}
-
-	// should never happen since we check the playernum
-	// before accepting the join
-	I_Assert(newplayernum < MAXPLAYERS);
-
-	playernode[newplayernum] = (UINT8)node;
-
-	p = buf + 2;
-	buf[0] = (UINT8)node;
-	buf[1] = newplayernum;
-	if (netnodes[node].numplayers < 1)
-	{
-		netnodes[node].player = newplayernum;
-	}
-	else
-	{
-		netnodes[node].player2 = newplayernum;
-		buf[1] |= 0x80;
-	}
-	WRITESTRINGN(p, name, MAXPLAYERNAME);
-	netnodes[node].numplayers++;
-
-	SendNetXCmd(XD_ADDPLAYER, &buf, p - buf);
-
-	DEBFILE(va("Server added player %d node %d\n", newplayernum, node));
-}
-
 void CL_AddSplitscreenPlayer(void)
 {
 	if (cl_mode == CL_CONNECTED)
@@ -2298,15 +2005,6 @@ void SV_StartSinglePlayerServer(void)
 		multiplayer = true;
 }
 
-static void SV_SendRefuse(INT32 node, const char *reason)
-{
-	strcpy(netbuffer->u.serverrefuse.reason, reason);
-
-	netbuffer->packettype = PT_SERVERREFUSE;
-	HSendPacket(node, true, 0, strlen(netbuffer->u.serverrefuse.reason) + 1);
-	Net_CloseConnection(node);
-}
-
 // used at txtcmds received to check packetsize bound
 static size_t TotalTextCmdPerTic(tic_t tic)
 {
@@ -2323,139 +2021,6 @@ static size_t TotalTextCmdPerTic(tic_t tic)
 	return total;
 }
 
-static const char *
-GetRefuseMessage (SINT8 node, INT32 rejoinernum)
-{
-	clientconfig_pak *cc = &netbuffer->u.clientcfg;
-
-	boolean rejoining = (rejoinernum != -1);
-
-	if (!node)/* server connecting to itself */
-		return NULL;
-
-	if (
-			cc->modversion != MODVERSION ||
-			strncmp(cc->application, SRB2APPLICATION,
-				sizeof cc->application)
-	){
-		return/* this is probably client's fault */
-			"Incompatible.";
-	}
-	else if (bannednode && bannednode[node])
-	{
-		return
-			"You have been banned\n"
-			"from the server.";
-	}
-	else if (cc->localplayers != 1)
-	{
-		return
-			"Wrong player count.";
-	}
-
-	if (!rejoining)
-	{
-		if (!cv_allownewplayer.value)
-		{
-			return
-				"The server is not accepting\n"
-				"joins for the moment.";
-		}
-		else if (D_NumPlayers() >= cv_maxplayers.value)
-		{
-			return va(
-					"Maximum players reached: %d",
-					cv_maxplayers.value);
-		}
-	}
-
-	if (luafiletransfers)
-	{
-		return
-			"The serveris broadcasting a file\n"
-			"requested by a Lua script.\n"
-			"Please wait a bit and then\n"
-			"try rejoining.";
-	}
-
-	if (netgame)
-	{
-		const tic_t th = 2 * cv_joindelay.value * TICRATE;
-
-		if (joindelay > th)
-		{
-			return va(
-					"Too many people are connecting.\n"
-					"Please wait %d seconds and then\n"
-					"try rejoining.",
-					(joindelay - th) / TICRATE);
-		}
-	}
-
-	return NULL;
-}
-
-/** Called when a PT_CLIENTJOIN packet is received
-  *
-  * \param node The packet sender
-  *
-  */
-static void HandleConnect(SINT8 node)
-{
-	char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
-	INT32 numplayers = netbuffer->u.clientcfg.localplayers;
-	INT32 rejoinernum;
-	INT32 i;
-
-	// Ignore duplicate packets
-	if (netnodes[node].ingame)
-		return;
-
-	rejoinernum = FindRejoinerNum(node);
-
-	const char *refuse = GetRefuseMessage(node, rejoinernum);
-	if (refuse)
-	{
-		SV_SendRefuse(node, refuse);
-		return;
-	}
-
-	for (i = 0; i < numplayers; i++)
-	{
-		strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1);
-		if (!EnsurePlayerNameIsGood(names[i], rejoinernum))
-		{
-			SV_SendRefuse(node, "Bad player name");
-			return;
-		}
-	}
-
-	SV_AddNode(node);
-
-	if (!SV_SendServerConfig(node))
-	{
-		/// \note Shouldn't SV_SendRefuse be called before ResetNode?
-		ResetNode(node);
-		SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
-		/// \todo fix this !!!
-		return; // restart the while
-	}
-	DEBFILE("new node joined\n");
-
-	if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)
-	{
-		SV_SendSaveGame(node, false); // send a complete game state
-		DEBFILE("send savegame\n");
-	}
-
-	// Splitscreen can allow 2 players in one node
-	for (i = 0; i < numplayers; i++)
-		SV_AddPlayer(node, names[i]);
-
-	joindelay += cv_joindelay.value * TICRATE;
-	player_joining = true;
-}
-
 /** Called when a PT_SERVERSHUTDOWN packet is received
   *
   * \param node The packet sender (should be the server)
@@ -2486,40 +2051,6 @@ static void HandleTimeout(SINT8 node)
 	M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
 }
 
-static void PT_AskInfoViaMS(SINT8 node)
-{
-	Net_CloseConnection(node);
-}
-
-static void PT_TellFilesNeeded(SINT8 node)
-{
-	if (server && serverrunning)
-	{
-		UINT8 *p;
-		INT32 firstfile = netbuffer->u.filesneedednum;
-
-		netbuffer->packettype = PT_MOREFILESNEEDED;
-		netbuffer->u.filesneededcfg.first = firstfile;
-		netbuffer->u.filesneededcfg.more = 0;
-
-		p = PutFileNeeded(firstfile);
-
-		HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
-	}
-	else // Shouldn't get this if you aren't the server...?
-		Net_CloseConnection(node);
-}
-
-static void PT_AskInfo(SINT8 node)
-{
-	if (server && serverrunning)
-	{
-		SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time));
-		SV_SendPlayerInfo(node); // Send extra info
-	}
-	Net_CloseConnection(node);
-}
-
 static void PT_ClientCmd(SINT8 node, INT32 netconsole)
 {
 	tic_t realend, realstart;
diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h
index 3b0690a799..b8da645baa 100644
--- a/src/netcode/d_clisrv.h
+++ b/src/netcode/d_clisrv.h
@@ -381,7 +381,6 @@ extern tic_t neededtic;
 
 void Command_Ping_f(void);
 extern tic_t connectiontimeout;
-extern tic_t jointimeout;
 extern UINT16 pingmeasurecount;
 extern UINT32 realpingtable[MAXPLAYERS];
 extern UINT32 playerpingtable[MAXPLAYERS];
@@ -405,11 +404,13 @@ void SendKick(UINT8 playernum, UINT8 msg);
 void NetUpdate(void);
 
 void GetPackets(void);
+void ResetNode(INT32 node);
 
 void SV_StartSinglePlayerServer(void);
 void SV_SpawnServer(void);
 void SV_StopServer(void);
 void SV_ResetServer(void);
+void SV_SendSaveGame(INT32 node, boolean resending);
 void CL_AddSplitscreenPlayer(void);
 void CL_RemoveSplitscreenPlayer(void);
 void CL_Reset(void);
diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c
index ed310805dc..fe4cf22bd1 100644
--- a/src/netcode/d_netcmd.c
+++ b/src/netcode/d_netcmd.c
@@ -36,6 +36,7 @@
 #include "../p_spec.h"
 #include "../m_cheat.h"
 #include "d_clisrv.h"
+#include "server_connection.h"
 #include "d_net.h"
 #include "../v_video.h"
 #include "../d_main.h"
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
new file mode 100644
index 0000000000..9e397b0f31
--- /dev/null
+++ b/src/netcode/server_connection.c
@@ -0,0 +1,497 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  server_connection.c
+/// \brief Server-side part of connection handling
+
+#include "server_connection.h"
+#include "i_net.h"
+#include "d_clisrv.h"
+#include "d_netfil.h"
+#include "mserv.h"
+#include "../byteptr.h"
+#include "../g_game.h"
+#include "../g_state.h"
+#include "../p_setup.h"
+#include "../p_tick.h"
+#include "../doomstat.h"
+
+// Minimum timeout for sending the savegame
+// The actual timeout will be longer depending on the savegame length
+tic_t jointimeout = (10*TICRATE);
+
+// Incremented by cv_joindelay when a client joins, decremented each tic.
+// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled.
+tic_t joindelay = 0;
+
+// Minimum timeout for sending the savegame
+// The actual timeout will be longer depending on the savegame length
+char playeraddress[MAXPLAYERS][64];
+
+UINT8 player_joining = false;
+
+static INT32 FindRejoinerNum(SINT8 node)
+{
+	char strippednodeaddress[64];
+	const char *nodeaddress;
+	char *port;
+	INT32 i;
+
+	// Make sure there is no dead dress before proceeding to the stripping
+	if (!I_GetNodeAddress)
+		return -1;
+	nodeaddress = I_GetNodeAddress(node);
+	if (!nodeaddress)
+		return -1;
+
+	// Strip the address of its port
+	strcpy(strippednodeaddress, nodeaddress);
+	port = strchr(strippednodeaddress, ':');
+	if (port)
+		*port = '\0';
+
+	// Check if any player matches the stripped address
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX
+		&& !strcmp(playeraddress[i], strippednodeaddress))
+			return i;
+	}
+
+	return -1;
+}
+
+static UINT8
+GetRefuseReason (INT32 node)
+{
+	if (!node || FindRejoinerNum(node) != -1)
+		return 0;
+	else if (bannednode && bannednode[node])
+		return REFUSE_BANNED;
+	else if (!cv_allownewplayer.value)
+		return REFUSE_JOINS_DISABLED;
+	else if (D_NumPlayers() >= cv_maxplayers.value)
+		return REFUSE_SLOTS_FULL;
+	else
+		return 0;
+}
+
+static void SV_SendServerInfo(INT32 node, tic_t servertime)
+{
+	UINT8 *p;
+
+	netbuffer->packettype = PT_SERVERINFO;
+	netbuffer->u.serverinfo._255 = 255;
+	netbuffer->u.serverinfo.packetversion = PACKETVERSION;
+	netbuffer->u.serverinfo.version = VERSION;
+	netbuffer->u.serverinfo.subversion = SUBVERSION;
+	strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION,
+			sizeof netbuffer->u.serverinfo.application);
+	// return back the time value so client can compute their ping
+	netbuffer->u.serverinfo.time = (tic_t)LONG(servertime);
+	netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime);
+
+	netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
+	netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
+
+	netbuffer->u.serverinfo.refusereason = GetRefuseReason(node);
+
+	strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype],
+			sizeof netbuffer->u.serverinfo.gametypename);
+	netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
+	netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
+	netbuffer->u.serverinfo.flags = (dedicated ? SV_DEDICATED : 0);
+	strncpy(netbuffer->u.serverinfo.servername, cv_servername.string,
+		MAXSERVERNAME);
+	strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7);
+
+	M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16);
+
+	memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle);
+
+	if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl)
+	{
+		char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle;
+		while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0')
+		{
+			if (!(*read & 0x80))
+			{
+				*writ = toupper(*read);
+				writ++;
+			}
+			read++;
+		}
+		*writ = '\0';
+		//strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33);
+	}
+	else
+		strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32);
+
+	if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
+		netbuffer->u.serverinfo.iszone = 1;
+	else
+		netbuffer->u.serverinfo.iszone = 0;
+
+	if (mapheaderinfo[gamemap-1])
+		netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum;
+
+	p = PutFileNeeded(0);
+
+	HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
+}
+
+static void SV_SendPlayerInfo(INT32 node)
+{
+	UINT8 i;
+	netbuffer->packettype = PT_PLAYERINFO;
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i])
+		{
+			netbuffer->u.playerinfo[i].num = 255; // This slot is empty.
+			continue;
+		}
+
+		netbuffer->u.playerinfo[i].num = i;
+		strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1);
+		netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0';
+
+		//fetch IP address
+		//No, don't do that, you fuckface.
+		memset(netbuffer->u.playerinfo[i].address, 0, 4);
+
+		if (G_GametypeHasTeams())
+		{
+			if (!players[i].ctfteam)
+				netbuffer->u.playerinfo[i].team = 255;
+			else
+				netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam;
+		}
+		else
+		{
+			if (players[i].spectator)
+				netbuffer->u.playerinfo[i].team = 255;
+			else
+				netbuffer->u.playerinfo[i].team = 0;
+		}
+
+		netbuffer->u.playerinfo[i].score = LONG(players[i].score);
+		netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE));
+		netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin
+#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself
+		% 3
+#endif
+		);
+
+		// Extra data
+		netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor;
+
+		if (players[i].pflags & PF_TAGIT)
+			netbuffer->u.playerinfo[i].data |= 0x20;
+
+		if (players[i].gotflag)
+			netbuffer->u.playerinfo[i].data |= 0x40;
+
+		if (players[i].powers[pw_super])
+			netbuffer->u.playerinfo[i].data |= 0x80;
+	}
+
+	HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS);
+}
+
+/** Sends a PT_SERVERCFG packet
+  *
+  * \param node The destination
+  * \return True if the packet was successfully sent
+  *
+  */
+static boolean SV_SendServerConfig(INT32 node)
+{
+	boolean waspacketsent;
+
+	netbuffer->packettype = PT_SERVERCFG;
+
+	netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer;
+	netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots);
+	netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic);
+	netbuffer->u.servercfg.clientnode = (UINT8)node;
+	netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
+	netbuffer->u.servercfg.gametype = (UINT8)gametype;
+	netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
+
+	memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
+
+	{
+		const size_t len = sizeof (serverconfig_pak);
+
+#ifdef DEBUGFILE
+		if (debugfile)
+		{
+			fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n",
+				sizeu1(len), node);
+		}
+#endif
+
+		waspacketsent = HSendPacket(node, true, 0, len);
+	}
+
+#ifdef DEBUGFILE
+	if (debugfile)
+	{
+		if (waspacketsent)
+		{
+			fprintf(debugfile, "ServerConfig Packet was sent\n");
+		}
+		else
+		{
+			fprintf(debugfile, "ServerConfig Packet could not be sent right now\n");
+		}
+	}
+#endif
+
+	return waspacketsent;
+}
+
+// Adds a node to the game (player will follow at map change or at savegame....)
+static inline void SV_AddNode(INT32 node)
+{
+	netnodes[node].tic = gametic;
+	netnodes[node].supposedtic = gametic;
+	// little hack because the server connects to itself and puts
+	// nodeingame when connected not here
+	if (node)
+		netnodes[node].ingame = true;
+}
+
+static void SV_AddPlayer(SINT8 node, const char *name)
+{
+	INT32 n;
+	UINT8 buf[2 + MAXPLAYERNAME];
+	UINT8 *p;
+	INT32 newplayernum;
+
+	newplayernum = FindRejoinerNum(node);
+	if (newplayernum == -1)
+	{
+		// search for a free playernum
+		// we can't use playeringame since it is not updated here
+		for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++)
+		{
+			if (playeringame[newplayernum])
+				continue;
+			for (n = 0; n < MAXNETNODES; n++)
+				if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum)
+					break;
+			if (n == MAXNETNODES)
+				break;
+		}
+	}
+
+	// should never happen since we check the playernum
+	// before accepting the join
+	I_Assert(newplayernum < MAXPLAYERS);
+
+	playernode[newplayernum] = (UINT8)node;
+
+	p = buf + 2;
+	buf[0] = (UINT8)node;
+	buf[1] = newplayernum;
+	if (netnodes[node].numplayers < 1)
+	{
+		netnodes[node].player = newplayernum;
+	}
+	else
+	{
+		netnodes[node].player2 = newplayernum;
+		buf[1] |= 0x80;
+	}
+	WRITESTRINGN(p, name, MAXPLAYERNAME);
+	netnodes[node].numplayers++;
+
+	SendNetXCmd(XD_ADDPLAYER, &buf, p - buf);
+
+	DEBFILE(va("Server added player %d node %d\n", newplayernum, node));
+}
+
+static void SV_SendRefuse(INT32 node, const char *reason)
+{
+	strcpy(netbuffer->u.serverrefuse.reason, reason);
+
+	netbuffer->packettype = PT_SERVERREFUSE;
+	HSendPacket(node, true, 0, strlen(netbuffer->u.serverrefuse.reason) + 1);
+	Net_CloseConnection(node);
+}
+
+static const char *
+GetRefuseMessage (SINT8 node, INT32 rejoinernum)
+{
+	clientconfig_pak *cc = &netbuffer->u.clientcfg;
+
+	boolean rejoining = (rejoinernum != -1);
+
+	if (!node)/* server connecting to itself */
+		return NULL;
+
+	if (
+			cc->modversion != MODVERSION ||
+			strncmp(cc->application, SRB2APPLICATION,
+				sizeof cc->application)
+	){
+		return/* this is probably client's fault */
+			"Incompatible.";
+	}
+	else if (bannednode && bannednode[node])
+	{
+		return
+			"You have been banned\n"
+			"from the server.";
+	}
+	else if (cc->localplayers != 1)
+	{
+		return
+			"Wrong player count.";
+	}
+
+	if (!rejoining)
+	{
+		if (!cv_allownewplayer.value)
+		{
+			return
+				"The server is not accepting\n"
+				"joins for the moment.";
+		}
+		else if (D_NumPlayers() >= cv_maxplayers.value)
+		{
+			return va(
+					"Maximum players reached: %d",
+					cv_maxplayers.value);
+		}
+	}
+
+	if (luafiletransfers)
+	{
+		return
+			"The serveris broadcasting a file\n"
+			"requested by a Lua script.\n"
+			"Please wait a bit and then\n"
+			"try rejoining.";
+	}
+
+	if (netgame)
+	{
+		const tic_t th = 2 * cv_joindelay.value * TICRATE;
+
+		if (joindelay > th)
+		{
+			return va(
+					"Too many people are connecting.\n"
+					"Please wait %d seconds and then\n"
+					"try rejoining.",
+					(joindelay - th) / TICRATE);
+		}
+	}
+
+	return NULL;
+}
+
+/** Called when a PT_CLIENTJOIN packet is received
+  *
+  * \param node The packet sender
+  *
+  */
+void HandleConnect(SINT8 node)
+{
+	char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
+	INT32 numplayers = netbuffer->u.clientcfg.localplayers;
+	INT32 rejoinernum;
+	INT32 i;
+
+	// Ignore duplicate packets
+	if (netnodes[node].ingame)
+		return;
+
+	rejoinernum = FindRejoinerNum(node);
+
+	const char *refuse = GetRefuseMessage(node, rejoinernum);
+	if (refuse)
+	{
+		SV_SendRefuse(node, refuse);
+		return;
+	}
+
+	for (i = 0; i < numplayers; i++)
+	{
+		strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1);
+		if (!EnsurePlayerNameIsGood(names[i], rejoinernum))
+		{
+			SV_SendRefuse(node, "Bad player name");
+			return;
+		}
+	}
+
+	SV_AddNode(node);
+
+	if (!SV_SendServerConfig(node))
+	{
+		/// \note Shouldn't SV_SendRefuse be called before ResetNode?
+		ResetNode(node);
+		SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
+		/// \todo fix this !!!
+		return; // restart the while
+	}
+	DEBFILE("new node joined\n");
+
+	if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)
+	{
+		SV_SendSaveGame(node, false); // send a complete game state
+		DEBFILE("send savegame\n");
+	}
+
+	// Splitscreen can allow 2 players in one node
+	for (i = 0; i < numplayers; i++)
+		SV_AddPlayer(node, names[i]);
+
+	joindelay += cv_joindelay.value * TICRATE;
+	player_joining = true;
+}
+
+void PT_AskInfoViaMS(SINT8 node)
+{
+	Net_CloseConnection(node);
+}
+
+void PT_TellFilesNeeded(SINT8 node)
+{
+	if (server && serverrunning)
+	{
+		UINT8 *p;
+		INT32 firstfile = netbuffer->u.filesneedednum;
+
+		netbuffer->packettype = PT_MOREFILESNEEDED;
+		netbuffer->u.filesneededcfg.first = firstfile;
+		netbuffer->u.filesneededcfg.more = 0;
+
+		p = PutFileNeeded(firstfile);
+
+		HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
+	}
+	else // Shouldn't get this if you aren't the server...?
+		Net_CloseConnection(node);
+}
+
+void PT_AskInfo(SINT8 node)
+{
+	if (server && serverrunning)
+	{
+		SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time));
+		SV_SendPlayerInfo(node); // Send extra info
+	}
+	Net_CloseConnection(node);
+}
diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h
new file mode 100644
index 0000000000..4204db2e6c
--- /dev/null
+++ b/src/netcode/server_connection.h
@@ -0,0 +1,29 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  server_connection.h
+/// \brief Server-side part of connection handling
+
+#ifndef __D_SERVER_CONNECTION__
+#define __D_SERVER_CONNECTION__
+
+#include "../doomdef.h"
+#include "../doomtype.h"
+
+void HandleConnect(SINT8 node);
+void PT_AskInfoViaMS(SINT8 node);
+void PT_TellFilesNeeded(SINT8 node);
+void PT_AskInfo(SINT8 node);
+
+extern tic_t jointimeout;
+extern tic_t joindelay;
+extern char playeraddress[MAXPLAYERS][64];
+extern UINT8 player_joining;
+
+#endif
-- 
GitLab


From 0ade3ae0bc020e3ca932f98e977c78301dcbaa0d Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 5 Jan 2023 22:51:17 +0100
Subject: [PATCH 266/518] Move tic and net command handling to new files

---
 src/blua/liolib.c               |   1 +
 src/command.c                   |   1 +
 src/g_game.c                    |   1 +
 src/hu_stuff.c                  |   1 +
 src/lua_consolelib.c            |   1 +
 src/netcode/Sourcefile          |   2 +
 src/netcode/d_clisrv.c          | 878 +-------------------------------
 src/netcode/d_clisrv.h          |  12 +-
 src/netcode/d_net.c             |   1 +
 src/netcode/d_netcmd.c          |   1 +
 src/netcode/d_netfil.c          |   1 +
 src/netcode/net_command.c       | 315 ++++++++++++
 src/netcode/net_command.h       |  62 +++
 src/netcode/server_connection.c |   1 +
 src/netcode/tic_command.c       | 504 ++++++++++++++++++
 src/netcode/tic_command.h       |  44 ++
 src/p_inter.c                   |   1 +
 src/p_mobj.c                    |   1 +
 src/p_setup.c                   |   2 +
 src/p_tick.c                    |   1 +
 src/p_user.c                    |   1 +
 21 files changed, 955 insertions(+), 877 deletions(-)
 create mode 100644 src/netcode/net_command.c
 create mode 100644 src/netcode/net_command.h
 create mode 100644 src/netcode/tic_command.c
 create mode 100644 src/netcode/tic_command.h

diff --git a/src/blua/liolib.c b/src/blua/liolib.c
index 00e31e965e..6b44e2eb8d 100644
--- a/src/blua/liolib.c
+++ b/src/blua/liolib.c
@@ -20,6 +20,7 @@
 #include "../i_system.h"
 #include "../g_game.h"
 #include "../netcode/d_netfil.h"
+#include "../netcode/net_command.h"
 #include "../lua_libs.h"
 #include "../byteptr.h"
 #include "../lua_script.h"
diff --git a/src/command.c b/src/command.c
index 0256365f38..e0deff8e1c 100644
--- a/src/command.c
+++ b/src/command.c
@@ -29,6 +29,7 @@
 #include "p_saveg.h"
 #include "g_game.h" // for player_names
 #include "netcode/d_netcmd.h"
+#include "netcode/net_command.h"
 #include "hu_stuff.h"
 #include "p_setup.h"
 #include "lua_script.h"
diff --git a/src/g_game.c b/src/g_game.c
index f47aa21835..70d249a2f0 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -16,6 +16,7 @@
 #include "d_main.h"
 #include "d_player.h"
 #include "netcode/d_clisrv.h"
+#include "netcode/net_command.h"
 #include "f_finale.h"
 #include "p_setup.h"
 #include "p_saveg.h"
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 33ef8a404a..1e5e47df69 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -20,6 +20,7 @@
 #include "m_misc.h" // word jumping
 
 #include "netcode/d_clisrv.h"
+#include "netcode/net_command.h"
 
 #include "g_game.h"
 #include "g_input.h"
diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index 9e5d1e2ee0..831dceba5b 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -16,6 +16,7 @@
 #include "g_game.h"
 #include "byteptr.h"
 #include "z_zone.h"
+#include "netcode/net_command.h"
 
 #include "lua_script.h"
 #include "lua_libs.h"
diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile
index 4177166a45..1039b218a6 100644
--- a/src/netcode/Sourcefile
+++ b/src/netcode/Sourcefile
@@ -1,6 +1,8 @@
 d_clisrv.c
 server_connection.c
 client_connection.c
+tic_command.c
+net_command.c
 d_net.c
 d_netcmd.c
 d_netfil.c
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index c1264768e0..06e6da7512 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -51,6 +51,8 @@
 #include "../m_perfstats.h"
 #include "server_connection.h"
 #include "client_connection.h"
+#include "tic_command.h"
+#include "net_command.h"
 
 //
 // NETWORKING
@@ -86,11 +88,9 @@ UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will
 UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values.
 tic_t servermaxping = 800; // server's max ping. Defaults to 800
 
-static tic_t firstticstosend; // min of the nettics
-static tic_t tictoclear = 0; // optimize d_clearticcmd
 tic_t maketic;
 
-static INT16 consistancy[BACKUPTICS];
+INT16 consistancy[BACKUPTICS];
 
 UINT8 hu_redownloadinggamestate = 0;
 
@@ -101,14 +101,9 @@ UINT8 adminpassmd5[16];
 boolean adminpasswordset = false;
 
 // Client specific
-static ticcmd_t localcmds;
-static ticcmd_t localcmds2;
-static boolean cl_packetmissed;
 // here it is for the secondary local player (splitscreen)
 static boolean cl_redownloadinggamestate = false;
 
-static UINT8 localtextcmd[MAXTEXTCMD];
-static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen
 tic_t neededtic;
 SINT8 servernode = 0; // the number of the server node
 
@@ -116,385 +111,19 @@ SINT8 servernode = 0; // the number of the server node
 /// \todo WORK!
 boolean acceptnewnode = true;
 
-// engine
-
-// Must be a power of two
-#define TEXTCMD_HASH_SIZE 4
-
-typedef struct textcmdplayer_s
-{
-	INT32 playernum;
-	UINT8 cmd[MAXTEXTCMD];
-	struct textcmdplayer_s *next;
-} textcmdplayer_t;
-
-typedef struct textcmdtic_s
-{
-	tic_t tic;
-	textcmdplayer_t *playercmds[TEXTCMD_HASH_SIZE];
-	struct textcmdtic_s *next;
-} textcmdtic_t;
-
-ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS];
-static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL};
-
-
 consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
 
 static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}};
 consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL);
 
-static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n)
-{
-	const size_t d = n / sizeof(ticcmd_t);
-	const size_t r = n % sizeof(ticcmd_t);
-	UINT8 *ret = dest;
-
-	if (r)
-		M_Memcpy(dest, src, n);
-	else if (d)
-		G_MoveTiccmd(dest, src, d);
-	return ret+n;
-}
-
-static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n)
-{
-	const size_t d = n / sizeof(ticcmd_t);
-	const size_t r = n % sizeof(ticcmd_t);
-	UINT8 *ret = src;
-
-	if (r)
-		M_Memcpy(dest, src, n);
-	else if (d)
-		G_MoveTiccmd(dest, src, d);
-	return ret+n;
-}
-
-
-
 // Some software don't support largest packet
 // (original sersetup, not exactely, but the probability of sending a packet
 // of 512 bytes is like 0.1)
 UINT16 software_MAXPACKETLENGTH;
 
-/** Guesses the full value of a tic from its lowest byte, for a specific node
-  *
-  * \param low The lowest byte of the tic value
-  * \param node The node to deduce the tic for
-  * \return The full tic value
-  *
-  */
-tic_t ExpandTics(INT32 low, INT32 node)
-{
-	INT32 delta;
-
-	delta = low - (netnodes[node].tic & UINT8_MAX);
-
-	if (delta >= -64 && delta <= 64)
-		return (netnodes[node].tic & ~UINT8_MAX) + low;
-	else if (delta > 64)
-		return (netnodes[node].tic & ~UINT8_MAX) - 256 + low;
-	else //if (delta < -64)
-		return (netnodes[node].tic & ~UINT8_MAX) + 256 + low;
-}
-
-// -----------------------------------------------------------------
-// Some extra data function for handle textcmd buffer
-// -----------------------------------------------------------------
-
-static void (*listnetxcmd[MAXNETXCMD])(UINT8 **p, INT32 playernum);
-
-void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum))
-{
-#ifdef PARANOIA
-	if (id >= MAXNETXCMD)
-		I_Error("Command id %d too big", id);
-	if (listnetxcmd[id] != 0)
-		I_Error("Command id %d already used", id);
-#endif
-	listnetxcmd[id] = cmd_f;
-}
-
-void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam)
-{
-	if (localtextcmd[0]+2+nparam > MAXTEXTCMD)
-	{
-		// for future reference: if (cv_debug) != debug disabled.
-		CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[0], sizeu1(nparam));
-		return;
-	}
-	localtextcmd[0]++;
-	localtextcmd[localtextcmd[0]] = (UINT8)id;
-	if (param && nparam)
-	{
-		M_Memcpy(&localtextcmd[localtextcmd[0]+1], param, nparam);
-		localtextcmd[0] = (UINT8)(localtextcmd[0] + (UINT8)nparam);
-	}
-}
-
-// splitscreen player
-void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam)
-{
-	if (localtextcmd2[0]+2+nparam > MAXTEXTCMD)
-	{
-		I_Error("No more place in the buffer for netcmd %d\n",id);
-		return;
-	}
-	localtextcmd2[0]++;
-	localtextcmd2[localtextcmd2[0]] = (UINT8)id;
-	if (param && nparam)
-	{
-		M_Memcpy(&localtextcmd2[localtextcmd2[0]+1], param, nparam);
-		localtextcmd2[0] = (UINT8)(localtextcmd2[0] + (UINT8)nparam);
-	}
-}
-
-UINT8 GetFreeXCmdSize(void)
-{
-	// -1 for the size and another -1 for the ID.
-	return (UINT8)(localtextcmd[0] - 2);
-}
-
-// Frees all textcmd memory for the specified tic
-static void D_FreeTextcmd(tic_t tic)
-{
-	textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
-	textcmdtic_t *textcmdtic = *tctprev;
-
-	while (textcmdtic && textcmdtic->tic != tic)
-	{
-		tctprev = &textcmdtic->next;
-		textcmdtic = textcmdtic->next;
-	}
-
-	if (textcmdtic)
-	{
-		INT32 i;
-
-		// Remove this tic from the list.
-		*tctprev = textcmdtic->next;
-
-		// Free all players.
-		for (i = 0; i < TEXTCMD_HASH_SIZE; i++)
-		{
-			textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i];
-
-			while (textcmdplayer)
-			{
-				textcmdplayer_t *tcpnext = textcmdplayer->next;
-				Z_Free(textcmdplayer);
-				textcmdplayer = tcpnext;
-			}
-		}
-
-		// Free this tic's own memory.
-		Z_Free(textcmdtic);
-	}
-}
-
-// Gets the buffer for the specified ticcmd, or NULL if there isn't one
-static UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum)
-{
-	textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
-	while (textcmdtic && textcmdtic->tic != tic) textcmdtic = textcmdtic->next;
-
-	// Do we have an entry for the tic? If so, look for player.
-	if (textcmdtic)
-	{
-		textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)];
-		while (textcmdplayer && textcmdplayer->playernum != playernum) textcmdplayer = textcmdplayer->next;
-
-		if (textcmdplayer) return textcmdplayer->cmd;
-	}
-
-	return NULL;
-}
-
-// Gets the buffer for the specified ticcmd, creating one if necessary
-static UINT8* D_GetTextcmd(tic_t tic, INT32 playernum)
-{
-	textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
-	textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
-	textcmdplayer_t *textcmdplayer, **tcpprev;
-
-	// Look for the tic.
-	while (textcmdtic && textcmdtic->tic != tic)
-	{
-		tctprev = &textcmdtic->next;
-		textcmdtic = textcmdtic->next;
-	}
-
-	// If we don't have an entry for the tic, make it.
-	if (!textcmdtic)
-	{
-		textcmdtic = *tctprev = Z_Calloc(sizeof (textcmdtic_t), PU_STATIC, NULL);
-		textcmdtic->tic = tic;
-	}
-
-	tcpprev = &textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)];
-	textcmdplayer = *tcpprev;
-
-	// Look for the player.
-	while (textcmdplayer && textcmdplayer->playernum != playernum)
-	{
-		tcpprev = &textcmdplayer->next;
-		textcmdplayer = textcmdplayer->next;
-	}
-
-	// If we don't have an entry for the player, make it.
-	if (!textcmdplayer)
-	{
-		textcmdplayer = *tcpprev = Z_Calloc(sizeof (textcmdplayer_t), PU_STATIC, NULL);
-		textcmdplayer->playernum = playernum;
-	}
-
-	return textcmdplayer->cmd;
-}
-
-static void ExtraDataTicker(void)
-{
-	INT32 i;
-
-	for (i = 0; i < MAXPLAYERS; i++)
-		if (playeringame[i] || i == 0)
-		{
-			UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i);
-
-			if (bufferstart)
-			{
-				UINT8 *curpos = bufferstart;
-				UINT8 *bufferend = &curpos[curpos[0]+1];
-
-				curpos++;
-				while (curpos < bufferend)
-				{
-					if (*curpos < MAXNETXCMD && listnetxcmd[*curpos])
-					{
-						const UINT8 id = *curpos;
-						curpos++;
-						DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i));
-						(listnetxcmd[id])(&curpos, i);
-						DEBFILE("done\n");
-					}
-					else
-					{
-						if (server)
-						{
-							SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
-							DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic));
-						}
-						CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]);
-						break;
-					}
-				}
-			}
-		}
-
-	// If you are a client, you can safely forget the net commands for this tic
-	// If you are the server, you need to remember them until every client has been acknowledged,
-	// because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it
-	if (client)
-		D_FreeTextcmd(gametic);
-}
-
-static void D_Clearticcmd(tic_t tic)
-{
-	INT32 i;
-
-	D_FreeTextcmd(tic);
-
-	for (i = 0; i < MAXPLAYERS; i++)
-		netcmds[tic%BACKUPTICS][i].angleturn = 0;
-
-	DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS));
-}
-
-void D_ResetTiccmds(void)
-{
-	INT32 i;
-
-	memset(&localcmds, 0, sizeof(ticcmd_t));
-	memset(&localcmds2, 0, sizeof(ticcmd_t));
-
-	// Reset the net command list
-	for (i = 0; i < TEXTCMD_HASH_SIZE; i++)
-		while (textcmds[i])
-			D_Clearticcmd(textcmds[i]->tic);
-}
-
-void SendKick(UINT8 playernum, UINT8 msg)
-{
-	UINT8 buf[2];
-
-	if (!(server && cv_rejointimeout.value))
-		msg &= ~KICK_MSG_KEEP_BODY;
-
-	buf[0] = playernum;
-	buf[1] = msg;
-	SendNetXCmd(XD_KICK, &buf, 2);
-}
-
-// -----------------------------------------------------------------
-// end of extra data function
-// -----------------------------------------------------------------
-
-// -----------------------------------------------------------------
-// extra data function for lmps
-// -----------------------------------------------------------------
-
-// if extradatabit is set, after the ziped tic you find this:
-//
-//   type   |  description
-// ---------+--------------
-//   byte   | size of the extradata
-//   byte   | the extradata (xd) bits: see XD_...
-//            with this byte you know what parameter folow
-// if (xd & XDNAMEANDCOLOR)
-//   byte   | color
-//   char[MAXPLAYERNAME] | name of the player
-// endif
-// if (xd & XD_WEAPON_PREF)
-//   byte   | original weapon switch: boolean, true if use the old
-//          | weapon switch methode
-//   char[NUMWEAPONS] | the weapon switch priority
-//   byte   | autoaim: true if use the old autoaim system
-// endif
-/*boolean AddLmpExtradata(UINT8 **demo_point, INT32 playernum)
-{
-	UINT8 *textcmd = D_GetExistingTextcmd(gametic, playernum);
-
-	if (!textcmd)
-		return false;
-
-	M_Memcpy(*demo_point, textcmd, textcmd[0]+1);
-	*demo_point += textcmd[0]+1;
-	return true;
-}
-
-void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum)
-{
-	UINT8 nextra;
-	UINT8 *textcmd;
-
-	if (!demo_pointer)
-		return;
-
-	textcmd = D_GetTextcmd(gametic, playernum);
-	nextra = **demo_pointer;
-	M_Memcpy(textcmd, *demo_pointer, nextra + 1);
-	// increment demo pointer
-	*demo_pointer += nextra + 1;
-}*/
-
-// -----------------------------------------------------------------
-// end extra data function for lmps
-// -----------------------------------------------------------------
-
-static INT16 Consistancy(void);
-
 #define SAVEGAMESIZE (768*1024)
 
-static boolean SV_ResendingSavegameToAnyone(void)
+boolean SV_ResendingSavegameToAnyone(void)
 {
 	INT32 i;
 
@@ -2005,22 +1634,6 @@ void SV_StartSinglePlayerServer(void)
 		multiplayer = true;
 }
 
-// used at txtcmds received to check packetsize bound
-static size_t TotalTextCmdPerTic(tic_t tic)
-{
-	INT32 i;
-	size_t total = 1; // num of textcmds in the tic (ntextcmd byte)
-
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		UINT8 *textcmd = D_GetExistingTextcmd(tic, i);
-		if ((!i || playeringame[i]) && textcmd)
-			total += 2 + textcmd[0]; // "+2" for size and playernum
-	}
-
-	return total;
-}
-
 /** Called when a PT_SERVERSHUTDOWN packet is received
   *
   * \param node The packet sender (should be the server)
@@ -2051,168 +1664,6 @@ static void HandleTimeout(SINT8 node)
 	M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
 }
 
-static void PT_ClientCmd(SINT8 node, INT32 netconsole)
-{
-	tic_t realend, realstart;
-
-	if (client)
-		return;
-
-	// To save bytes, only the low byte of tic numbers are sent
-	// Use ExpandTics to figure out what the rest of the bytes are
-	realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node);
-	realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node);
-
-	if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
-		|| netbuffer->packettype == PT_NODEKEEPALIVEMIS
-		|| netnodes[node].supposedtic < realend)
-	{
-		netnodes[node].supposedtic = realend;
-	}
-	// Discard out of order packet
-	if (netnodes[node].tic > realend)
-	{
-		DEBFILE(va("out of order ticcmd discarded nettics = %u\n", netnodes[node].tic));
-		return;
-	}
-
-	// Update the nettics
-	netnodes[node].tic = realend;
-
-	// Don't do anything for packets of type NODEKEEPALIVE?
-	if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
-		|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
-		return;
-
-	// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
-	/// \todo Use a separate cvar for that kind of timeout?
-	netnodes[node].freezetimeout = I_GetTime() + connectiontimeout;
-
-	// Copy ticcmd
-	G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
-
-	// Check ticcmd for "speed hacks"
-	if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
-		|| netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
-	{
-		CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole);
-		//D_Clearticcmd(k);
-
-		SendKick(netconsole, KICK_MSG_CON_FAIL);
-		return;
-	}
-
-	// Splitscreen cmd
-	if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
-		&& netnodes[node].player2 >= 0)
-		G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2],
-			&netbuffer->u.client2pak.cmd2, 1);
-
-	// Check player consistancy during the level
-	if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL
-		&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
-		&& !SV_ResendingSavegameToAnyone()
-		&& !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime())
-	{
-		if (cv_resynchattempts.value)
-		{
-			// Tell the client we are about to resend them the gamestate
-			netbuffer->packettype = PT_WILLRESENDGAMESTATE;
-			HSendPacket(node, true, 0, 0);
-
-			netnodes[node].resendingsavegame = true;
-
-			if (cv_blamecfail.value)
-				CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
-					netconsole+1, player_names[netconsole],
-					consistancy[realstart%BACKUPTICS],
-					SHORT(netbuffer->u.clientpak.consistancy));
-			DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n",
-				netconsole, realstart, consistancy[realstart%BACKUPTICS],
-				SHORT(netbuffer->u.clientpak.consistancy)));
-			return;
-		}
-		else
-		{
-			SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
-			DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
-				netconsole, realstart, consistancy[realstart%BACKUPTICS],
-				SHORT(netbuffer->u.clientpak.consistancy)));
-			return;
-		}
-	}
-}
-
-static void PT_TextCmd(SINT8 node, INT32 netconsole)
-{
-	if (client)
-		return;
-
-	// splitscreen special
-	if (netbuffer->packettype == PT_TEXTCMD2)
-		netconsole = netnodes[node].player2;
-
-	if (netconsole < 0 || netconsole >= MAXPLAYERS)
-		Net_UnAcknowledgePacket(node);
-	else
-	{
-		size_t j;
-		tic_t tic = maketic;
-		UINT8 *textcmd;
-
-		// ignore if the textcmd has a reported size of zero
-		// this shouldn't be sent at all
-		if (!netbuffer->u.textcmd[0])
-		{
-			DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n",
-				node, netconsole));
-			Net_UnAcknowledgePacket(node);
-			return;
-		}
-
-		// ignore if the textcmd size var is actually larger than it should be
-		// BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength
-		if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1)
-		{
-			DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n",
-			netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1),
-				node, netconsole));
-			Net_UnAcknowledgePacket(node);
-			return;
-		}
-
-		// check if tic that we are making isn't too large else we cannot send it :(
-		// doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time
-		j = software_MAXPACKETLENGTH
-			- (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE
-			+ (doomcom->numslots+1)*sizeof(ticcmd_t));
-
-		// search a tic that have enougth space in the ticcmd
-		while ((textcmd = D_GetExistingTextcmd(tic, netconsole)),
-			(TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD)
-			&& tic < firstticstosend + BACKUPTICS)
-			tic++;
-
-		if (tic >= firstticstosend + BACKUPTICS)
-		{
-			DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, "
-				"tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)),
-				maketic, firstticstosend, node, netconsole));
-			Net_UnAcknowledgePacket(node);
-			return;
-		}
-
-		// Make sure we have a buffer
-		if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole);
-
-		DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n",
-			tic, textcmd[0]+1, netconsole, firstticstosend, maketic));
-
-		M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
-		textcmd[0] += (UINT8)netbuffer->u.textcmd[0];
-	}
-}
-
 static void PT_Login(SINT8 node, INT32 netconsole)
 {
 	(void)node;
@@ -2314,81 +1765,6 @@ static void PT_ReceivedGamestate(SINT8 node)
 	netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE;
 }
 
-static void PT_ServerTics(SINT8 node, INT32 netconsole)
-{
-	UINT8 *pak, *txtpak, numtxtpak;
-	tic_t realend, realstart;
-
-	if (!netnodes[node].ingame)
-	{
-		// Do not remove my own server (we have just get a out of order packet)
-		if (node != servernode)
-		{
-			DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
-			Net_CloseConnection(node);
-		}
-		return;
-	}
-
-	// Only accept PT_SERVERTICS from the server.
-	if (node != servernode)
-	{
-		CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node);
-		if (server)
-			SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
-		return;
-	}
-
-	realstart = netbuffer->u.serverpak.starttic;
-	realend = realstart + netbuffer->u.serverpak.numtics;
-
-	txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots
-		* netbuffer->u.serverpak.numtics];
-
-	if (realend > gametic + CLIENTBACKUPTICS)
-		realend = gametic + CLIENTBACKUPTICS;
-	cl_packetmissed = realstart > neededtic;
-
-	if (realstart <= neededtic && realend > neededtic)
-	{
-		tic_t i, j;
-		pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
-
-		for (i = realstart; i < realend; i++)
-		{
-			// clear first
-			D_Clearticcmd(i);
-
-			// copy the tics
-			pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
-				netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
-
-			// copy the textcmds
-			numtxtpak = *txtpak++;
-			for (j = 0; j < numtxtpak; j++)
-			{
-				INT32 k = *txtpak++; // playernum
-				const size_t txtsize = txtpak[0]+1;
-
-				if (i >= gametic) // Don't copy old net commands
-					M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize);
-				txtpak += txtsize;
-			}
-		}
-
-		neededtic = realend;
-	}
-	else
-	{
-		DEBFILE(va("frame not in bound: %u\n", neededtic));
-		/*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart)
-			I_Error("Received an out of order PT_SERVERTICS packet!\n"
-					"Got tics %d-%d, needed tic %d\n\n"
-					"Please report this crash on the Master Board,\n"
-					"IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/
-	}
-}
-
 static void PT_Ping(SINT8 node, INT32 netconsole)
 {
 	// Only accept PT_PING from the server.
@@ -2602,7 +1978,7 @@ void GetPackets(void)
 // 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)
+INT16 Consistancy(void)
 {
 	INT32 i;
 	UINT32 ret = 0;
@@ -2708,239 +2084,11 @@ static INT16 Consistancy(void)
 	return (INT16)(ret & 0xFFFF);
 }
 
-// send the client packet to the server
-static void CL_SendClientCmd(void)
-{
-	size_t packetsize = 0;
-
-	netbuffer->packettype = PT_CLIENTCMD;
-
-	if (cl_packetmissed)
-		netbuffer->packettype++;
-	netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX);
-	netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX);
-
-	if (gamestate == GS_WAITINGPLAYERS)
-	{
-		// Send PT_NODEKEEPALIVE packet
-		netbuffer->packettype += 4;
-		packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16);
-		HSendPacket(servernode, false, 0, packetsize);
-	}
-	else if (gamestate != GS_NULL && (addedtogame || dedicated))
-	{
-		G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
-		netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
-
-		// Send a special packet with 2 cmd for splitscreen
-		if (splitscreen || botingame)
-		{
-			netbuffer->packettype += 2;
-			G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1);
-			packetsize = sizeof (client2cmd_pak);
-		}
-		else
-			packetsize = sizeof (clientcmd_pak);
-
-		HSendPacket(servernode, false, 0, packetsize);
-	}
-
-	if (cl_mode == CL_CONNECTED || dedicated)
-	{
-		// Send extra data if needed
-		if (localtextcmd[0])
-		{
-			netbuffer->packettype = PT_TEXTCMD;
-			M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
-			// All extra data have been sent
-			if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail...
-				localtextcmd[0] = 0;
-		}
-
-		// Send extra data if needed for player 2 (splitscreen)
-		if (localtextcmd2[0])
-		{
-			netbuffer->packettype = PT_TEXTCMD2;
-			M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1);
-			// All extra data have been sent
-			if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail...
-				localtextcmd2[0] = 0;
-		}
-	}
-}
-
-// send the server packet
-// send tic from firstticstosend to maketic-1
-static void SV_SendTics(void)
-{
-	tic_t realfirsttic, lasttictosend, i;
-	UINT32 n;
-	INT32 j;
-	size_t packsize;
-	UINT8 *bufpos;
-	UINT8 *ntextcmd;
-
-	// send to all client but not to me
-	// for each node create a packet with x tics and send it
-	// x is computed using netnodes[n].supposedtic, max packet size and maketic
-	for (n = 1; n < MAXNETNODES; n++)
-		if (netnodes[n].ingame)
-		{
-			// assert netnodes[n].supposedtic>=netnodes[n].tic
-			realfirsttic = netnodes[n].supposedtic;
-			lasttictosend = min(maketic, netnodes[n].tic + CLIENTBACKUPTICS);
-
-			if (realfirsttic >= lasttictosend)
-			{
-				// well we have sent all tics we will so use extrabandwidth
-				// to resent packet that are supposed lost (this is necessary since lost
-				// packet detection work when we have received packet with firsttic > neededtic
-				// (getpacket servertics case)
-				DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n",
-					n, maketic, netnodes[n].supposedtic, netnodes[n].tic));
-				realfirsttic = netnodes[n].tic;
-				if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3)
-					// all tic are ok
-					continue;
-				DEBFILE(va("Sent %d anyway\n", realfirsttic));
-			}
-			if (realfirsttic < firstticstosend)
-				realfirsttic = firstticstosend;
-
-			// compute the length of the packet and cut it if too large
-			packsize = BASESERVERTICSSIZE;
-			for (i = realfirsttic; i < lasttictosend; i++)
-			{
-				packsize += sizeof (ticcmd_t) * doomcom->numslots;
-				packsize += TotalTextCmdPerTic(i);
-
-				if (packsize > software_MAXPACKETLENGTH)
-				{
-					DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n",
-						sizeu1(packsize), i, realfirsttic, lasttictosend));
-					lasttictosend = i;
-
-					// too bad: too much player have send extradata and there is too
-					//          much data in one tic.
-					// To avoid it put the data on the next tic. (see getpacket
-					// textcmd case) but when numplayer changes the computation can be different
-					if (lasttictosend == realfirsttic)
-					{
-						if (packsize > MAXPACKETLENGTH)
-							I_Error("Too many players: can't send %s data for %d players to node %d\n"
-							        "Well sorry nobody is perfect....\n",
-							        sizeu1(packsize), doomcom->numslots, n);
-						else
-						{
-							lasttictosend++; // send it anyway!
-							DEBFILE("sending it anyway\n");
-						}
-					}
-					break;
-				}
-			}
-
-			// Send the tics
-			netbuffer->packettype = PT_SERVERTICS;
-			netbuffer->u.serverpak.starttic = realfirsttic;
-			netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic);
-			netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots);
-			bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds;
-
-			for (i = realfirsttic; i < lasttictosend; i++)
-			{
-				bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t));
-			}
-
-			// add textcmds
-			for (i = realfirsttic; i < lasttictosend; i++)
-			{
-				ntextcmd = bufpos++;
-				*ntextcmd = 0;
-				for (j = 0; j < MAXPLAYERS; j++)
-				{
-					UINT8 *textcmd = D_GetExistingTextcmd(i, j);
-					INT32 size = textcmd ? textcmd[0] : 0;
-
-					if ((!j || playeringame[j]) && size)
-					{
-						(*ntextcmd)++;
-						WRITEUINT8(bufpos, j);
-						M_Memcpy(bufpos, textcmd, size + 1);
-						bufpos += size + 1;
-					}
-				}
-			}
-			packsize = bufpos - (UINT8 *)&(netbuffer->u);
-
-			HSendPacket(n, false, 0, packsize);
-			// when tic are too large, only one tic is sent so don't go backward!
-			if (lasttictosend-doomcom->extratics > realfirsttic)
-				netnodes[n].supposedtic = lasttictosend-doomcom->extratics;
-			else
-				netnodes[n].supposedtic = lasttictosend;
-			if (netnodes[n].supposedtic < netnodes[n].tic) netnodes[n].supposedtic = netnodes[n].tic;
-		}
-	// node 0 is me!
-	netnodes[0].supposedtic = maketic;
-}
-
-//
-// TryRunTics
-//
-static void Local_Maketic(INT32 realtics)
-{
-	I_OsPolling(); // I_Getevent
-	D_ProcessEvents(); // menu responder, cons responder,
-	                   // game responder calls HU_Responder, AM_Responder,
-	                   // and G_MapEventsToControls
-	if (!dedicated) rendergametic = gametic;
-	// translate inputs (keyboard/mouse/gamepad) into game controls
-	G_BuildTiccmd(&localcmds, realtics, 1);
-	if (splitscreen || botingame)
-		G_BuildTiccmd(&localcmds2, realtics, 2);
-
-	localcmds.angleturn |= TICCMD_RECEIVED;
-	localcmds2.angleturn |= TICCMD_RECEIVED;
-}
-
-// create missed tic
-static void SV_Maketic(void)
-{
-	INT32 i;
-
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (!playeringame[i])
-			continue;
-
-		// We didn't receive this tic
-		if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0)
-		{
-			ticcmd_t *    ticcmd = &netcmds[(maketic    ) % BACKUPTICS][i];
-			ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i];
-
-			if (players[i].quittime)
-			{
-				// Copy the angle/aiming from the previous tic
-				// and empty the other inputs
-				memset(ticcmd, 0, sizeof(netcmds[0][0]));
-				ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED;
-				ticcmd->aiming = prevticcmd->aiming;
-			}
-			else
-			{
-				DEBFILE(va("MISS tic%4d for player %d\n", maketic, i));
-				// Copy the input from the previous tic
-				*ticcmd = *prevticcmd;
-				ticcmd->angleturn &= ~TICCMD_RECEIVED;
-			}
-		}
-	}
-
-	// all tic are now proceed make the next
-	maketic++;
-}
+/*
+Ping Update except better:
+We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
+If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
+*/
 
 boolean TryRunTics(tic_t realtics)
 {
@@ -3049,12 +2197,6 @@ boolean TryRunTics(tic_t realtics)
 	return ticking;
 }
 
-/*
-Ping Update except better:
-We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
-If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
-*/
-
 static INT32 pingtimeout[MAXPLAYERS];
 
 static inline void PingUpdate(void)
diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h
index b8da645baa..8b73170da1 100644
--- a/src/netcode/d_clisrv.h
+++ b/src/netcode/d_clisrv.h
@@ -378,6 +378,7 @@ extern boolean acceptnewnode;
 extern SINT8 servernode;
 extern tic_t maketic;
 extern tic_t neededtic;
+extern INT16 consistancy[BACKUPTICS];
 
 void Command_Ping_f(void);
 extern tic_t connectiontimeout;
@@ -391,20 +392,15 @@ extern consvar_t cv_resynchattempts, cv_blamecfail;
 extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
 
 // Used in d_net, the only dependence
-tic_t ExpandTics(INT32 low, INT32 node);
 void D_ClientServerInit(void);
 
-// Initialise the other field
-void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum));
-void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam);
-void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player
-void SendKick(UINT8 playernum, UINT8 msg);
-
 // Create any new ticcmds and broadcast to other players.
 void NetUpdate(void);
 
 void GetPackets(void);
 void ResetNode(INT32 node);
+INT16 Consistancy(void);
+boolean SV_ResendingSavegameToAnyone(void);
 
 void SV_StartSinglePlayerServer(void);
 void SV_SpawnServer(void);
@@ -440,10 +436,8 @@ extern char motd[254], server_context[8];
 extern UINT8 playernode[MAXPLAYERS];
 
 INT32 D_NumPlayers(void);
-void D_ResetTiccmds(void);
 
 tic_t GetLag(INT32 node);
-UINT8 GetFreeXCmdSize(void);
 
 void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest);
 
diff --git a/src/netcode/d_net.c b/src/netcode/d_net.c
index ae0605001a..9723422824 100644
--- a/src/netcode/d_net.c
+++ b/src/netcode/d_net.c
@@ -26,6 +26,7 @@
 #include "../w_wad.h"
 #include "d_netfil.h"
 #include "d_clisrv.h"
+#include "tic_command.h"
 #include "../z_zone.h"
 #include "i_tcp.h"
 #include "../d_main.h" // srb2home
diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c
index fe4cf22bd1..0d17855105 100644
--- a/src/netcode/d_netcmd.c
+++ b/src/netcode/d_netcmd.c
@@ -37,6 +37,7 @@
 #include "../m_cheat.h"
 #include "d_clisrv.h"
 #include "server_connection.h"
+#include "net_command.h"
 #include "d_net.h"
 #include "../v_video.h"
 #include "../d_main.h"
diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c
index 80fa068529..e581be2ace 100644
--- a/src/netcode/d_netfil.c
+++ b/src/netcode/d_netfil.c
@@ -42,6 +42,7 @@
 #include "d_net.h"
 #include "../w_wad.h"
 #include "d_netfil.h"
+#include "net_command.h"
 #include "../z_zone.h"
 #include "../byteptr.h"
 #include "../p_setup.h"
diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c
new file mode 100644
index 0000000000..eeb479d21f
--- /dev/null
+++ b/src/netcode/net_command.c
@@ -0,0 +1,315 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  net_command.c
+/// \brief Net command handling
+
+#include "net_command.h"
+#include "tic_command.h"
+#include "d_clisrv.h"
+#include "i_net.h"
+#include "../g_game.h"
+#include "../z_zone.h"
+#include "../doomtype.h"
+
+textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL};
+UINT8 localtextcmd[MAXTEXTCMD];
+UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen
+static void (*listnetxcmd[MAXNETXCMD])(UINT8 **p, INT32 playernum);
+
+void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum))
+{
+#ifdef PARANOIA
+	if (id >= MAXNETXCMD)
+		I_Error("Command id %d too big", id);
+	if (listnetxcmd[id] != 0)
+		I_Error("Command id %d already used", id);
+#endif
+	listnetxcmd[id] = cmd_f;
+}
+
+void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam)
+{
+	if (localtextcmd[0]+2+nparam > MAXTEXTCMD)
+	{
+		// for future reference: if (cv_debug) != debug disabled.
+		CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[0], sizeu1(nparam));
+		return;
+	}
+	localtextcmd[0]++;
+	localtextcmd[localtextcmd[0]] = (UINT8)id;
+	if (param && nparam)
+	{
+		M_Memcpy(&localtextcmd[localtextcmd[0]+1], param, nparam);
+		localtextcmd[0] = (UINT8)(localtextcmd[0] + (UINT8)nparam);
+	}
+}
+
+// splitscreen player
+void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam)
+{
+	if (localtextcmd2[0]+2+nparam > MAXTEXTCMD)
+	{
+		I_Error("No more place in the buffer for netcmd %d\n",id);
+		return;
+	}
+	localtextcmd2[0]++;
+	localtextcmd2[localtextcmd2[0]] = (UINT8)id;
+	if (param && nparam)
+	{
+		M_Memcpy(&localtextcmd2[localtextcmd2[0]+1], param, nparam);
+		localtextcmd2[0] = (UINT8)(localtextcmd2[0] + (UINT8)nparam);
+	}
+}
+
+UINT8 GetFreeXCmdSize(void)
+{
+	// -1 for the size and another -1 for the ID.
+	return (UINT8)(localtextcmd[0] - 2);
+}
+
+// Frees all textcmd memory for the specified tic
+void D_FreeTextcmd(tic_t tic)
+{
+	textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
+	textcmdtic_t *textcmdtic = *tctprev;
+
+	while (textcmdtic && textcmdtic->tic != tic)
+	{
+		tctprev = &textcmdtic->next;
+		textcmdtic = textcmdtic->next;
+	}
+
+	if (textcmdtic)
+	{
+		INT32 i;
+
+		// Remove this tic from the list.
+		*tctprev = textcmdtic->next;
+
+		// Free all players.
+		for (i = 0; i < TEXTCMD_HASH_SIZE; i++)
+		{
+			textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i];
+
+			while (textcmdplayer)
+			{
+				textcmdplayer_t *tcpnext = textcmdplayer->next;
+				Z_Free(textcmdplayer);
+				textcmdplayer = tcpnext;
+			}
+		}
+
+		// Free this tic's own memory.
+		Z_Free(textcmdtic);
+	}
+}
+
+// Gets the buffer for the specified ticcmd, or NULL if there isn't one
+UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum)
+{
+	textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
+	while (textcmdtic && textcmdtic->tic != tic) textcmdtic = textcmdtic->next;
+
+	// Do we have an entry for the tic? If so, look for player.
+	if (textcmdtic)
+	{
+		textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)];
+		while (textcmdplayer && textcmdplayer->playernum != playernum) textcmdplayer = textcmdplayer->next;
+
+		if (textcmdplayer) return textcmdplayer->cmd;
+	}
+
+	return NULL;
+}
+
+// Gets the buffer for the specified ticcmd, creating one if necessary
+UINT8* D_GetTextcmd(tic_t tic, INT32 playernum)
+{
+	textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
+	textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
+	textcmdplayer_t *textcmdplayer, **tcpprev;
+
+	// Look for the tic.
+	while (textcmdtic && textcmdtic->tic != tic)
+	{
+		tctprev = &textcmdtic->next;
+		textcmdtic = textcmdtic->next;
+	}
+
+	// If we don't have an entry for the tic, make it.
+	if (!textcmdtic)
+	{
+		textcmdtic = *tctprev = Z_Calloc(sizeof (textcmdtic_t), PU_STATIC, NULL);
+		textcmdtic->tic = tic;
+	}
+
+	tcpprev = &textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)];
+	textcmdplayer = *tcpprev;
+
+	// Look for the player.
+	while (textcmdplayer && textcmdplayer->playernum != playernum)
+	{
+		tcpprev = &textcmdplayer->next;
+		textcmdplayer = textcmdplayer->next;
+	}
+
+	// If we don't have an entry for the player, make it.
+	if (!textcmdplayer)
+	{
+		textcmdplayer = *tcpprev = Z_Calloc(sizeof (textcmdplayer_t), PU_STATIC, NULL);
+		textcmdplayer->playernum = playernum;
+	}
+
+	return textcmdplayer->cmd;
+}
+
+void ExtraDataTicker(void)
+{
+	INT32 i;
+
+	for (i = 0; i < MAXPLAYERS; i++)
+		if (playeringame[i] || i == 0)
+		{
+			UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i);
+
+			if (bufferstart)
+			{
+				UINT8 *curpos = bufferstart;
+				UINT8 *bufferend = &curpos[curpos[0]+1];
+
+				curpos++;
+				while (curpos < bufferend)
+				{
+					if (*curpos < MAXNETXCMD && listnetxcmd[*curpos])
+					{
+						const UINT8 id = *curpos;
+						curpos++;
+						DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i));
+						(listnetxcmd[id])(&curpos, i);
+						DEBFILE("done\n");
+					}
+					else
+					{
+						if (server)
+						{
+							SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
+							DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic));
+						}
+						CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]);
+						break;
+					}
+				}
+			}
+		}
+
+	// If you are a client, you can safely forget the net commands for this tic
+	// If you are the server, you need to remember them until every client has been acknowledged,
+	// because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it
+	if (client)
+		D_FreeTextcmd(gametic);
+}
+
+// used at txtcmds received to check packetsize bound
+size_t TotalTextCmdPerTic(tic_t tic)
+{
+	INT32 i;
+	size_t total = 1; // num of textcmds in the tic (ntextcmd byte)
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		UINT8 *textcmd = D_GetExistingTextcmd(tic, i);
+		if ((!i || playeringame[i]) && textcmd)
+			total += 2 + textcmd[0]; // "+2" for size and playernum
+	}
+
+	return total;
+}
+
+void PT_TextCmd(SINT8 node, INT32 netconsole)
+{
+	if (client)
+		return;
+
+	// splitscreen special
+	if (netbuffer->packettype == PT_TEXTCMD2)
+		netconsole = netnodes[node].player2;
+
+	if (netconsole < 0 || netconsole >= MAXPLAYERS)
+		Net_UnAcknowledgePacket(node);
+	else
+	{
+		size_t j;
+		tic_t tic = maketic;
+		UINT8 *textcmd;
+
+		// ignore if the textcmd has a reported size of zero
+		// this shouldn't be sent at all
+		if (!netbuffer->u.textcmd[0])
+		{
+			DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n",
+				node, netconsole));
+			Net_UnAcknowledgePacket(node);
+			return;
+		}
+
+		// ignore if the textcmd size var is actually larger than it should be
+		// BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength
+		if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1)
+		{
+			DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n",
+			netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1),
+				node, netconsole));
+			Net_UnAcknowledgePacket(node);
+			return;
+		}
+
+		// check if tic that we are making isn't too large else we cannot send it :(
+		// doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time
+		j = software_MAXPACKETLENGTH
+			- (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE
+			+ (doomcom->numslots+1)*sizeof(ticcmd_t));
+
+		// search a tic that have enougth space in the ticcmd
+		while ((textcmd = D_GetExistingTextcmd(tic, netconsole)),
+			(TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD)
+			&& tic < firstticstosend + BACKUPTICS)
+			tic++;
+
+		if (tic >= firstticstosend + BACKUPTICS)
+		{
+			DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, "
+				"tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)),
+				maketic, firstticstosend, node, netconsole));
+			Net_UnAcknowledgePacket(node);
+			return;
+		}
+
+		// Make sure we have a buffer
+		if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole);
+
+		DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n",
+			tic, textcmd[0]+1, netconsole, firstticstosend, maketic));
+
+		M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
+		textcmd[0] += (UINT8)netbuffer->u.textcmd[0];
+	}
+}
+
+void SendKick(UINT8 playernum, UINT8 msg)
+{
+	UINT8 buf[2];
+
+	if (!(server && cv_rejointimeout.value))
+		msg &= ~KICK_MSG_KEEP_BODY;
+
+	buf[0] = playernum;
+	buf[1] = msg;
+	SendNetXCmd(XD_KICK, &buf, 2);
+}
diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h
new file mode 100644
index 0000000000..1fc5035c81
--- /dev/null
+++ b/src/netcode/net_command.h
@@ -0,0 +1,62 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  net_command.h
+/// \brief Net command handling
+
+#ifndef __D_NET_COMMAND__
+#define __D_NET_COMMAND__
+
+#include "d_clisrv.h"
+#include "../doomtype.h"
+
+// Must be a power of two
+#define TEXTCMD_HASH_SIZE 4
+
+typedef struct textcmdplayer_s
+{
+	INT32 playernum;
+	UINT8 cmd[MAXTEXTCMD];
+	struct textcmdplayer_s *next;
+} textcmdplayer_t;
+
+typedef struct textcmdtic_s
+{
+	tic_t tic;
+	textcmdplayer_t *playercmds[TEXTCMD_HASH_SIZE];
+	struct textcmdtic_s *next;
+} textcmdtic_t;
+
+extern textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE];
+
+extern UINT8 localtextcmd[MAXTEXTCMD];
+extern UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen
+
+void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum));
+void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam);
+void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player
+
+UINT8 GetFreeXCmdSize(void);
+void D_FreeTextcmd(tic_t tic);
+
+// Gets the buffer for the specified ticcmd, or NULL if there isn't one
+UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum);
+
+// Gets the buffer for the specified ticcmd, creating one if necessary
+UINT8* D_GetTextcmd(tic_t tic, INT32 playernum);
+
+void ExtraDataTicker(void);
+
+// used at txtcmds received to check packetsize bound
+size_t TotalTextCmdPerTic(tic_t tic);
+
+void PT_TextCmd(SINT8 node, INT32 netconsole);
+void SendKick(UINT8 playernum, UINT8 msg);
+
+#endif
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
index 9e397b0f31..b776db4228 100644
--- a/src/netcode/server_connection.c
+++ b/src/netcode/server_connection.c
@@ -15,6 +15,7 @@
 #include "d_clisrv.h"
 #include "d_netfil.h"
 #include "mserv.h"
+#include "net_command.h"
 #include "../byteptr.h"
 #include "../g_game.h"
 #include "../g_state.h"
diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
new file mode 100644
index 0000000000..e7df895ffb
--- /dev/null
+++ b/src/netcode/tic_command.c
@@ -0,0 +1,504 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  tic_command.c
+/// \brief Tic command handling
+
+#include "tic_command.h"
+#include "d_clisrv.h"
+#include "net_command.h"
+#include "client_connection.h"
+#include "i_net.h"
+#include "../d_main.h"
+#include "../g_game.h"
+#include "../i_system.h"
+#include "../i_time.h"
+#include "../byteptr.h"
+#include "../doomstat.h"
+#include "../doomtype.h"
+
+tic_t firstticstosend; // min of the nettics
+tic_t tictoclear = 0; // optimize d_clearticcmd
+ticcmd_t localcmds;
+ticcmd_t localcmds2;
+boolean cl_packetmissed;
+ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS];
+
+static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n)
+{
+	const size_t d = n / sizeof(ticcmd_t);
+	const size_t r = n % sizeof(ticcmd_t);
+	UINT8 *ret = dest;
+
+	if (r)
+		M_Memcpy(dest, src, n);
+	else if (d)
+		G_MoveTiccmd(dest, src, d);
+	return ret+n;
+}
+
+static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n)
+{
+	const size_t d = n / sizeof(ticcmd_t);
+	const size_t r = n % sizeof(ticcmd_t);
+	UINT8 *ret = src;
+
+	if (r)
+		M_Memcpy(dest, src, n);
+	else if (d)
+		G_MoveTiccmd(dest, src, d);
+	return ret+n;
+}
+
+/** Guesses the full value of a tic from its lowest byte, for a specific node
+  *
+  * \param low The lowest byte of the tic value
+  * \param node The node to deduce the tic for
+  * \return The full tic value
+  *
+  */
+tic_t ExpandTics(INT32 low, INT32 node)
+{
+	INT32 delta;
+
+	delta = low - (netnodes[node].tic & UINT8_MAX);
+
+	if (delta >= -64 && delta <= 64)
+		return (netnodes[node].tic & ~UINT8_MAX) + low;
+	else if (delta > 64)
+		return (netnodes[node].tic & ~UINT8_MAX) - 256 + low;
+	else //if (delta < -64)
+		return (netnodes[node].tic & ~UINT8_MAX) + 256 + low;
+}
+
+void D_Clearticcmd(tic_t tic)
+{
+	INT32 i;
+
+	D_FreeTextcmd(tic);
+
+	for (i = 0; i < MAXPLAYERS; i++)
+		netcmds[tic%BACKUPTICS][i].angleturn = 0;
+
+	DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS));
+}
+
+void D_ResetTiccmds(void)
+{
+	INT32 i;
+
+	memset(&localcmds, 0, sizeof(ticcmd_t));
+	memset(&localcmds2, 0, sizeof(ticcmd_t));
+
+	// Reset the net command list
+	for (i = 0; i < TEXTCMD_HASH_SIZE; i++)
+		while (textcmds[i])
+			D_Clearticcmd(textcmds[i]->tic);
+}
+
+void PT_ClientCmd(SINT8 node, INT32 netconsole)
+{
+	tic_t realend, realstart;
+
+	if (client)
+		return;
+
+	// To save bytes, only the low byte of tic numbers are sent
+	// Use ExpandTics to figure out what the rest of the bytes are
+	realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node);
+	realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node);
+
+	if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
+		|| netbuffer->packettype == PT_NODEKEEPALIVEMIS
+		|| netnodes[node].supposedtic < realend)
+	{
+		netnodes[node].supposedtic = realend;
+	}
+	// Discard out of order packet
+	if (netnodes[node].tic > realend)
+	{
+		DEBFILE(va("out of order ticcmd discarded nettics = %u\n", netnodes[node].tic));
+		return;
+	}
+
+	// Update the nettics
+	netnodes[node].tic = realend;
+
+	// Don't do anything for packets of type NODEKEEPALIVE?
+	if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
+		|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
+		return;
+
+	// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
+	/// \todo Use a separate cvar for that kind of timeout?
+	netnodes[node].freezetimeout = I_GetTime() + connectiontimeout;
+
+	// Copy ticcmd
+	G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
+
+	// Check ticcmd for "speed hacks"
+	if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
+		|| netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
+	{
+		CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole);
+		//D_Clearticcmd(k);
+
+		SendKick(netconsole, KICK_MSG_CON_FAIL);
+		return;
+	}
+
+	// Splitscreen cmd
+	if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
+		&& netnodes[node].player2 >= 0)
+		G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2],
+			&netbuffer->u.client2pak.cmd2, 1);
+
+	// Check player consistancy during the level
+	if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL
+		&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
+		&& !SV_ResendingSavegameToAnyone()
+		&& !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime())
+	{
+		if (cv_resynchattempts.value)
+		{
+			// Tell the client we are about to resend them the gamestate
+			netbuffer->packettype = PT_WILLRESENDGAMESTATE;
+			HSendPacket(node, true, 0, 0);
+
+			netnodes[node].resendingsavegame = true;
+
+			if (cv_blamecfail.value)
+				CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
+					netconsole+1, player_names[netconsole],
+					consistancy[realstart%BACKUPTICS],
+					SHORT(netbuffer->u.clientpak.consistancy));
+			DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n",
+				netconsole, realstart, consistancy[realstart%BACKUPTICS],
+				SHORT(netbuffer->u.clientpak.consistancy)));
+			return;
+		}
+		else
+		{
+			SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
+			DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
+				netconsole, realstart, consistancy[realstart%BACKUPTICS],
+				SHORT(netbuffer->u.clientpak.consistancy)));
+			return;
+		}
+	}
+}
+
+void PT_ServerTics(SINT8 node, INT32 netconsole)
+{
+	UINT8 *pak, *txtpak, numtxtpak;
+	tic_t realend, realstart;
+
+	if (!netnodes[node].ingame)
+	{
+		// Do not remove my own server (we have just get a out of order packet)
+		if (node != servernode)
+		{
+			DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
+			Net_CloseConnection(node);
+		}
+		return;
+	}
+
+	// Only accept PT_SERVERTICS from the server.
+	if (node != servernode)
+	{
+		CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node);
+		if (server)
+			SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
+		return;
+	}
+
+	realstart = netbuffer->u.serverpak.starttic;
+	realend = realstart + netbuffer->u.serverpak.numtics;
+
+	txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots
+		* netbuffer->u.serverpak.numtics];
+
+	if (realend > gametic + CLIENTBACKUPTICS)
+		realend = gametic + CLIENTBACKUPTICS;
+	cl_packetmissed = realstart > neededtic;
+
+	if (realstart <= neededtic && realend > neededtic)
+	{
+		tic_t i, j;
+		pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
+
+		for (i = realstart; i < realend; i++)
+		{
+			// clear first
+			D_Clearticcmd(i);
+
+			// copy the tics
+			pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
+				netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
+
+			// copy the textcmds
+			numtxtpak = *txtpak++;
+			for (j = 0; j < numtxtpak; j++)
+			{
+				INT32 k = *txtpak++; // playernum
+				const size_t txtsize = txtpak[0]+1;
+
+				if (i >= gametic) // Don't copy old net commands
+					M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize);
+				txtpak += txtsize;
+			}
+		}
+
+		neededtic = realend;
+	}
+	else
+	{
+		DEBFILE(va("frame not in bound: %u\n", neededtic));
+		/*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart)
+			I_Error("Received an out of order PT_SERVERTICS packet!\n"
+					"Got tics %d-%d, needed tic %d\n\n"
+					"Please report this crash on the Master Board,\n"
+					"IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/
+	}
+}
+
+// send the client packet to the server
+void CL_SendClientCmd(void)
+{
+	size_t packetsize = 0;
+
+	netbuffer->packettype = PT_CLIENTCMD;
+
+	if (cl_packetmissed)
+		netbuffer->packettype++;
+	netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX);
+	netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX);
+
+	if (gamestate == GS_WAITINGPLAYERS)
+	{
+		// Send PT_NODEKEEPALIVE packet
+		netbuffer->packettype += 4;
+		packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16);
+		HSendPacket(servernode, false, 0, packetsize);
+	}
+	else if (gamestate != GS_NULL && (addedtogame || dedicated))
+	{
+		G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
+		netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
+
+		// Send a special packet with 2 cmd for splitscreen
+		if (splitscreen || botingame)
+		{
+			netbuffer->packettype += 2;
+			G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1);
+			packetsize = sizeof (client2cmd_pak);
+		}
+		else
+			packetsize = sizeof (clientcmd_pak);
+
+		HSendPacket(servernode, false, 0, packetsize);
+	}
+
+	if (cl_mode == CL_CONNECTED || dedicated)
+	{
+		// Send extra data if needed
+		if (localtextcmd[0])
+		{
+			netbuffer->packettype = PT_TEXTCMD;
+			M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
+			// All extra data have been sent
+			if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail...
+				localtextcmd[0] = 0;
+		}
+
+		// Send extra data if needed for player 2 (splitscreen)
+		if (localtextcmd2[0])
+		{
+			netbuffer->packettype = PT_TEXTCMD2;
+			M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1);
+			// All extra data have been sent
+			if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail...
+				localtextcmd2[0] = 0;
+		}
+	}
+}
+
+// send the server packet
+// send tic from firstticstosend to maketic-1
+void SV_SendTics(void)
+{
+	tic_t realfirsttic, lasttictosend, i;
+	UINT32 n;
+	INT32 j;
+	size_t packsize;
+	UINT8 *bufpos;
+	UINT8 *ntextcmd;
+
+	// send to all client but not to me
+	// for each node create a packet with x tics and send it
+	// x is computed using netnodes[n].supposedtic, max packet size and maketic
+	for (n = 1; n < MAXNETNODES; n++)
+		if (netnodes[n].ingame)
+		{
+			// assert netnodes[n].supposedtic>=netnodes[n].tic
+			realfirsttic = netnodes[n].supposedtic;
+			lasttictosend = min(maketic, netnodes[n].tic + CLIENTBACKUPTICS);
+
+			if (realfirsttic >= lasttictosend)
+			{
+				// well we have sent all tics we will so use extrabandwidth
+				// to resent packet that are supposed lost (this is necessary since lost
+				// packet detection work when we have received packet with firsttic > neededtic
+				// (getpacket servertics case)
+				DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n",
+					n, maketic, netnodes[n].supposedtic, netnodes[n].tic));
+				realfirsttic = netnodes[n].tic;
+				if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3)
+					// all tic are ok
+					continue;
+				DEBFILE(va("Sent %d anyway\n", realfirsttic));
+			}
+			if (realfirsttic < firstticstosend)
+				realfirsttic = firstticstosend;
+
+			// compute the length of the packet and cut it if too large
+			packsize = BASESERVERTICSSIZE;
+			for (i = realfirsttic; i < lasttictosend; i++)
+			{
+				packsize += sizeof (ticcmd_t) * doomcom->numslots;
+				packsize += TotalTextCmdPerTic(i);
+
+				if (packsize > software_MAXPACKETLENGTH)
+				{
+					DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n",
+						sizeu1(packsize), i, realfirsttic, lasttictosend));
+					lasttictosend = i;
+
+					// too bad: too much player have send extradata and there is too
+					//          much data in one tic.
+					// To avoid it put the data on the next tic. (see getpacket
+					// textcmd case) but when numplayer changes the computation can be different
+					if (lasttictosend == realfirsttic)
+					{
+						if (packsize > MAXPACKETLENGTH)
+							I_Error("Too many players: can't send %s data for %d players to node %d\n"
+							        "Well sorry nobody is perfect....\n",
+							        sizeu1(packsize), doomcom->numslots, n);
+						else
+						{
+							lasttictosend++; // send it anyway!
+							DEBFILE("sending it anyway\n");
+						}
+					}
+					break;
+				}
+			}
+
+			// Send the tics
+			netbuffer->packettype = PT_SERVERTICS;
+			netbuffer->u.serverpak.starttic = realfirsttic;
+			netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic);
+			netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots);
+			bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds;
+
+			for (i = realfirsttic; i < lasttictosend; i++)
+			{
+				bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t));
+			}
+
+			// add textcmds
+			for (i = realfirsttic; i < lasttictosend; i++)
+			{
+				ntextcmd = bufpos++;
+				*ntextcmd = 0;
+				for (j = 0; j < MAXPLAYERS; j++)
+				{
+					UINT8 *textcmd = D_GetExistingTextcmd(i, j);
+					INT32 size = textcmd ? textcmd[0] : 0;
+
+					if ((!j || playeringame[j]) && size)
+					{
+						(*ntextcmd)++;
+						WRITEUINT8(bufpos, j);
+						M_Memcpy(bufpos, textcmd, size + 1);
+						bufpos += size + 1;
+					}
+				}
+			}
+			packsize = bufpos - (UINT8 *)&(netbuffer->u);
+
+			HSendPacket(n, false, 0, packsize);
+			// when tic are too large, only one tic is sent so don't go backward!
+			if (lasttictosend-doomcom->extratics > realfirsttic)
+				netnodes[n].supposedtic = lasttictosend-doomcom->extratics;
+			else
+				netnodes[n].supposedtic = lasttictosend;
+			if (netnodes[n].supposedtic < netnodes[n].tic) netnodes[n].supposedtic = netnodes[n].tic;
+		}
+	// node 0 is me!
+	netnodes[0].supposedtic = maketic;
+}
+
+//
+// TryRunTics
+//
+void Local_Maketic(INT32 realtics)
+{
+	I_OsPolling(); // I_Getevent
+	D_ProcessEvents(); // menu responder, cons responder,
+	                   // game responder calls HU_Responder, AM_Responder,
+	                   // and G_MapEventsToControls
+	if (!dedicated) rendergametic = gametic;
+	// translate inputs (keyboard/mouse/gamepad) into game controls
+	G_BuildTiccmd(&localcmds, realtics, 1);
+	if (splitscreen || botingame)
+		G_BuildTiccmd(&localcmds2, realtics, 2);
+
+	localcmds.angleturn |= TICCMD_RECEIVED;
+	localcmds2.angleturn |= TICCMD_RECEIVED;
+}
+
+// create missed tic
+void SV_Maketic(void)
+{
+	INT32 i;
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i])
+			continue;
+
+		// We didn't receive this tic
+		if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0)
+		{
+			ticcmd_t *    ticcmd = &netcmds[(maketic    ) % BACKUPTICS][i];
+			ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i];
+
+			if (players[i].quittime)
+			{
+				// Copy the angle/aiming from the previous tic
+				// and empty the other inputs
+				memset(ticcmd, 0, sizeof(netcmds[0][0]));
+				ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED;
+				ticcmd->aiming = prevticcmd->aiming;
+			}
+			else
+			{
+				DEBFILE(va("MISS tic%4d for player %d\n", maketic, i));
+				// Copy the input from the previous tic
+				*ticcmd = *prevticcmd;
+				ticcmd->angleturn &= ~TICCMD_RECEIVED;
+			}
+		}
+	}
+
+	// all tic are now proceed make the next
+	maketic++;
+}
diff --git a/src/netcode/tic_command.h b/src/netcode/tic_command.h
new file mode 100644
index 0000000000..d19f4c1aac
--- /dev/null
+++ b/src/netcode/tic_command.h
@@ -0,0 +1,44 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  tic_command.h
+/// \brief Tic command handling
+
+#ifndef __D_TIC_COMMAND__
+#define __D_TIC_COMMAND__
+
+#include "d_clisrv.h"
+#include "../d_ticcmd.h"
+#include "../doomdef.h"
+#include "../doomtype.h"
+
+extern tic_t firstticstosend; // min of the nettics
+extern tic_t tictoclear; // optimize d_clearticcmd
+
+extern ticcmd_t localcmds;
+extern ticcmd_t localcmds2;
+extern boolean cl_packetmissed;
+
+extern ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS];
+
+tic_t ExpandTics(INT32 low, INT32 node);
+void D_Clearticcmd(tic_t tic);
+void D_ResetTiccmds(void);
+
+void PT_ClientCmd(SINT8 node, INT32 netconsole);
+void PT_ServerTics(SINT8 node, INT32 netconsole);
+
+// send the client packet to the server
+void CL_SendClientCmd(void);
+
+void SV_SendTics(void);
+void Local_Maketic(INT32 realtics);
+void SV_Maketic(void);
+
+#endif
diff --git a/src/p_inter.c b/src/p_inter.c
index 4d22ba3438..490065a1fe 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -28,6 +28,7 @@
 #include "m_misc.h"
 #include "v_video.h" // video flags for CEchos
 #include "f_finale.h"
+#include "netcode/net_command.h"
 
 // CTF player names
 #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 4ca59285f7..807368890e 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -36,6 +36,7 @@
 #include "p_slopes.h"
 #include "f_finale.h"
 #include "m_cond.h"
+#include "netcode/net_command.h"
 
 static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
 consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL);
diff --git a/src/p_setup.c b/src/p_setup.c
index 175ab3328b..4dc37d6674 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -87,6 +87,8 @@
 
 #include "taglist.h"
 
+#include "netcode/net_command.h"
+
 //
 // Map MD5, calculated on level load.
 // Sent to clients in PT_SERVERINFO.
diff --git a/src/p_tick.c b/src/p_tick.c
index b1fd367ed9..4b724e3331 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -26,6 +26,7 @@
 #include "r_main.h"
 #include "r_fps.h"
 #include "i_video.h" // rendermode
+#include "netcode/net_command.h"
 
 // Object place
 #include "m_cheat.h"
diff --git a/src/p_user.c b/src/p_user.c
index 6bbefcb199..c8657f8c59 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -18,6 +18,7 @@
 #include "i_system.h"
 #include "d_event.h"
 #include "netcode/d_net.h"
+#include "netcode/net_command.h"
 #include "g_game.h"
 #include "p_local.h"
 #include "r_fps.h"
-- 
GitLab


From 1a7f42d172e3ec4c68003988436b4c15ef23e765 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 5 Jan 2023 23:48:44 +0100
Subject: [PATCH 267/518] Declare data exchanged through the network to a new
 file

---
 src/netcode/d_clisrv.c |   1 +
 src/netcode/d_clisrv.h | 313 +-------------------------------------
 src/netcode/d_net.h    |   2 +
 src/netcode/protocol.h | 335 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 339 insertions(+), 312 deletions(-)
 create mode 100644 src/netcode/protocol.h

diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 06e6da7512..b834c92f5f 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -53,6 +53,7 @@
 #include "client_connection.h"
 #include "tic_command.h"
 #include "net_command.h"
+#include "protocol.h"
 
 //
 // NETWORKING
diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h
index 8b73170da1..691a2787f8 100644
--- a/src/netcode/d_clisrv.h
+++ b/src/netcode/d_clisrv.h
@@ -13,6 +13,7 @@
 #ifndef __D_CLISRV__
 #define __D_CLISRV__
 
+#include "protocol.h"
 #include "../d_ticcmd.h"
 #include "d_net.h"
 #include "d_netcmd.h"
@@ -21,85 +22,7 @@
 #include "../d_player.h"
 #include "mserv.h"
 
-/*
-The 'packet version' is used to distinguish packet
-formats. This version is independent of VERSION and
-SUBVERSION. Different applications may follow different
-packet versions.
-
-If you change the struct or the meaning of a field
-therein, increment this number.
-*/
-#define PACKETVERSION 4
-
-// Network play related stuff.
-// There is a data struct that stores network
-//  communication related stuff, and another
-//  one that defines the actual packets to
-//  be transmitted.
-
-// Networking and tick handling related.
-#define BACKUPTICS 1024
 #define CLIENTBACKUPTICS 32
-#define MAXTEXTCMD 256
-//
-// Packet structure
-//
-typedef enum
-{
-	PT_NOTHING,       // To send a nop through the network. ^_~
-	PT_SERVERCFG,     // Server config used in start game
-	                  // (must stay 1 for backwards compatibility).
-	                  // This is a positive response to a CLIENTJOIN request.
-	PT_CLIENTCMD,     // Ticcmd of the client.
-	PT_CLIENTMIS,     // Same as above with but saying resend from.
-	PT_CLIENT2CMD,    // 2 cmds in the packet for splitscreen.
-	PT_CLIENT2MIS,    // Same as above with but saying resend from
-	PT_NODEKEEPALIVE, // Same but without ticcmd and consistancy
-	PT_NODEKEEPALIVEMIS,
-	PT_SERVERTICS,    // All cmds for the tic.
-	PT_SERVERREFUSE,  // Server refuses joiner (reason inside).
-	PT_SERVERSHUTDOWN,
-	PT_CLIENTQUIT,    // Client closes the connection.
-
-	PT_ASKINFO,       // Anyone can ask info of the server.
-	PT_SERVERINFO,    // Send game & server info (gamespy).
-	PT_PLAYERINFO,    // Send information for players in game (gamespy).
-	PT_REQUESTFILE,   // Client requests a file transfer
-	PT_ASKINFOVIAMS,  // Packet from the MS requesting info be sent to new client.
-	                  // If this ID changes, update masterserver definition.
-
-	PT_WILLRESENDGAMESTATE, // Hey Client, I am about to resend you the gamestate!
-	PT_CANRECEIVEGAMESTATE, // Okay Server, I'm ready to receive it, you can go ahead.
-	PT_RECEIVEDGAMESTATE,   // Thank you Server, I am ready to play again!
-
-	PT_SENDINGLUAFILE, // Server telling a client Lua needs to open a file
-	PT_ASKLUAFILE,     // Client telling the server they don't have the file
-	PT_HASLUAFILE,     // Client telling the server they have the file
-
-	// Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
-
-	PT_CANFAIL,       // This is kind of a priority. Anything bigger than CANFAIL
-	                  // allows HSendPacket(*, true, *, *) to return false.
-	                  // In addition, this packet can't occupy all the available slots.
-
-	PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file.
-	PT_FILEACK,
-	PT_FILERECEIVED,
-
-	PT_TEXTCMD,       // Extra text commands from the client.
-	PT_TEXTCMD2,      // Splitscreen text commands.
-	PT_CLIENTJOIN,    // Client wants to join; used in start game.
-	PT_NODETIMEOUT,   // Packet sent to self if the connection times out.
-
-	PT_LOGIN,         // Login attempt from the client.
-
-	PT_TELLFILESNEEDED, // Client, to server: "what other files do I need starting from this number?"
-	PT_MOREFILESNEEDED, // Server, to client: "you need these (+ more on top of those)"
-
-	PT_PING,          // Packet sent to tell clients the other client's latency to server.
-	NUMPACKETTYPE
-} packettype_t;
 
 #ifdef PACKETDROP
 void Command_Drop(void);
@@ -109,229 +32,6 @@ void Command_Droprate(void);
 void Command_Numnodes(void);
 #endif
 
-#if defined(_MSC_VER)
-#pragma pack(1)
-#endif
-
-// Client to server packet
-typedef struct
-{
-	UINT8 client_tic;
-	UINT8 resendfrom;
-	INT16 consistancy;
-	ticcmd_t cmd;
-} ATTRPACK clientcmd_pak;
-
-// Splitscreen packet
-// WARNING: must have the same format of clientcmd_pak, for more easy use
-typedef struct
-{
-	UINT8 client_tic;
-	UINT8 resendfrom;
-	INT16 consistancy;
-	ticcmd_t cmd, cmd2;
-} ATTRPACK client2cmd_pak;
-
-#ifdef _MSC_VER
-#pragma warning(disable :  4200)
-#endif
-
-// Server to client packet
-// this packet is too large
-typedef struct
-{
-	tic_t starttic;
-	UINT8 numtics;
-	UINT8 numslots; // "Slots filled": Highest player number in use plus one.
-	ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large
-} ATTRPACK servertics_pak;
-
-typedef struct
-{
-	// Server launch stuffs
-	UINT8 serverplayer;
-	UINT8 totalslotnum; // "Slots": highest player number in use plus one.
-
-	tic_t gametic;
-	UINT8 clientnode;
-	UINT8 gamestate;
-
-	UINT8 gametype;
-	UINT8 modifiedgame;
-
-	char server_context[8]; // Unique context id, generated at server startup.
-} ATTRPACK serverconfig_pak;
-
-typedef struct
-{
-	UINT8 fileid;
-	UINT32 filesize;
-	UINT8 iteration;
-	UINT32 position;
-	UINT16 size;
-	UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH
-} ATTRPACK filetx_pak;
-
-typedef struct
-{
-	UINT32 start;
-	UINT32 acks;
-} ATTRPACK fileacksegment_t;
-
-typedef struct
-{
-	UINT8 fileid;
-	UINT8 iteration;
-	UINT8 numsegments;
-	fileacksegment_t segments[0];
-} ATTRPACK fileack_pak;
-
-#ifdef _MSC_VER
-#pragma warning(default : 4200)
-#endif
-
-#define MAXAPPLICATION 16
-
-typedef struct
-{
-	UINT8 modversion;
-	char application[MAXAPPLICATION];
-	UINT8 localplayers;
-	UINT8 mode;
-	char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME];
-} ATTRPACK clientconfig_pak;
-
-#define SV_DEDICATED    0x40 // server is dedicated
-#define SV_LOTSOFADDONS 0x20 // flag used to ask for full file list in d_netfil
-
-enum {
-	REFUSE_JOINS_DISABLED = 1,
-	REFUSE_SLOTS_FULL,
-	REFUSE_BANNED,
-};
-
-#define MAXSERVERNAME 32
-#define MAXFILENEEDED 915
-// This packet is too large
-typedef struct
-{
-	/*
-	In the old packet, 'version' is the first field. Now that field is set
-	to 255 always, so older versions won't be confused with the new
-	versions or vice-versa.
-	*/
-	UINT8 _255;
-	UINT8 packetversion;
-	char  application[MAXAPPLICATION];
-	UINT8 version;
-	UINT8 subversion;
-	UINT8 numberofplayer;
-	UINT8 maxplayer;
-	UINT8 refusereason; // 0: joinable, REFUSE enum
-	char gametypename[24];
-	UINT8 modifiedgame;
-	UINT8 cheatsenabled;
-	UINT8 flags;
-	UINT8 fileneedednum;
-	tic_t time;
-	tic_t leveltime;
-	char servername[MAXSERVERNAME];
-	char mapname[8];
-	char maptitle[33];
-	unsigned char mapmd5[16];
-	UINT8 actnum;
-	UINT8 iszone;
-	UINT8 fileneeded[MAXFILENEEDED]; // is filled with writexxx (byteptr.h)
-} ATTRPACK serverinfo_pak;
-
-typedef struct
-{
-	char reason[255];
-} ATTRPACK serverrefuse_pak;
-
-typedef struct
-{
-	UINT8 version;
-	tic_t time; // used for ping evaluation
-} ATTRPACK askinfo_pak;
-
-typedef struct
-{
-	char clientaddr[22];
-	tic_t time; // used for ping evaluation
-} ATTRPACK msaskinfo_pak;
-
-// Shorter player information for external use.
-typedef struct
-{
-	UINT8 num;
-	char name[MAXPLAYERNAME+1];
-	UINT8 address[4]; // sending another string would run us up against MAXPACKETLENGTH
-	UINT8 team;
-	UINT8 skin;
-	UINT8 data; // Color is first four bits, hasflag, isit and issuper have one bit each, the last is unused.
-	UINT32 score;
-	UINT16 timeinserver; // In seconds.
-} ATTRPACK plrinfo;
-
-// Shortest player information for join during intermission.
-typedef struct
-{
-	char name[MAXPLAYERNAME+1];
-	UINT8 skin;
-	UINT16 color;
-	UINT32 pflags;
-	UINT32 score;
-	UINT8 ctfteam;
-} ATTRPACK plrconfig;
-
-typedef struct
-{
-	INT32 first;
-	UINT8 num;
-	UINT8 more;
-	UINT8 files[MAXFILENEEDED]; // is filled with writexxx (byteptr.h)
-} ATTRPACK filesneededconfig_pak;
-
-//
-// Network packet data
-//
-typedef struct
-{
-	UINT32 checksum;
-	UINT8 ack; // If not zero the node asks for acknowledgement, the receiver must resend the ack
-	UINT8 ackreturn; // The return of the ack number
-
-	UINT8 packettype;
-	UINT8 reserved; // Padding
-	union
-	{
-		clientcmd_pak clientpak;            //         144 bytes
-		client2cmd_pak client2pak;          //         200 bytes
-		servertics_pak serverpak;           //      132495 bytes (more around 360, no?)
-		serverconfig_pak servercfg;         //         773 bytes
-		UINT8 textcmd[MAXTEXTCMD+1];        //       66049 bytes (wut??? 64k??? More like 257 bytes...)
-		filetx_pak filetxpak;               //         139 bytes
-		fileack_pak fileack;
-		UINT8 filereceived;
-		clientconfig_pak clientcfg;         //         136 bytes
-		UINT8 md5sum[16];
-		serverinfo_pak serverinfo;          //        1024 bytes
-		serverrefuse_pak serverrefuse;      //       65025 bytes (somehow I feel like those values are garbage...)
-		askinfo_pak askinfo;                //          61 bytes
-		msaskinfo_pak msaskinfo;            //          22 bytes
-		plrinfo playerinfo[MAXPLAYERS];     //         576 bytes(?)
-		plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?)
-		INT32 filesneedednum;               //           4 bytes
-		filesneededconfig_pak filesneededcfg; //       ??? bytes
-		UINT32 pingtable[MAXPLAYERS+1];     //          68 bytes
-	} u; // This is needed to pack diff packet types data together
-} ATTRPACK doomdata_t;
-
-#if defined(_MSC_VER)
-#pragma pack()
-#endif
-
 extern INT32 mapchangepending;
 
 // Points inside doomcom
@@ -341,19 +41,8 @@ extern consvar_t cv_showjoinaddress;
 extern consvar_t cv_playbackspeed;
 
 #define BASEPACKETSIZE      offsetof(doomdata_t, u)
-#define FILETXHEADER        offsetof(filetx_pak, data)
 #define BASESERVERTICSSIZE  offsetof(doomdata_t, u.serverpak.cmds[0])
 
-#define KICK_MSG_GO_AWAY     1
-#define KICK_MSG_CON_FAIL    2
-#define KICK_MSG_PLAYER_QUIT 3
-#define KICK_MSG_TIMEOUT     4
-#define KICK_MSG_BANNED      5
-#define KICK_MSG_PING_HIGH   6
-#define KICK_MSG_CUSTOM_KICK 7
-#define KICK_MSG_CUSTOM_BAN  8
-#define KICK_MSG_KEEP_BODY   0x80
-
 typedef enum
 {
 	KR_KICK          = 1, //Kicked by server
diff --git a/src/netcode/d_net.h b/src/netcode/d_net.h
index 803b49d740..857c3463c9 100644
--- a/src/netcode/d_net.h
+++ b/src/netcode/d_net.h
@@ -18,6 +18,8 @@
 #ifndef __D_NET__
 #define __D_NET__
 
+#include "../doomtype.h"
+
 // Max computers in a game
 // 127 is probably as high as this can go, because
 // SINT8 is used for nodes sometimes >:(
diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h
new file mode 100644
index 0000000000..566d10d8cb
--- /dev/null
+++ b/src/netcode/protocol.h
@@ -0,0 +1,335 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  protocol.h
+/// \brief Data exchanged through the network
+
+#ifndef __PROTOCOL__
+#define __PROTOCOL__
+
+#include "d_net.h"
+#include "../d_ticcmd.h"
+#include "../doomdef.h"
+
+/*
+The 'packet version' is used to distinguish packet
+formats. This version is independent of VERSION and
+SUBVERSION. Different applications may follow different
+packet versions.
+
+If you change the struct or the meaning of a field
+therein, increment this number.
+*/
+#define PACKETVERSION 4
+
+// Network play related stuff.
+// There is a data struct that stores network
+//  communication related stuff, and another
+//  one that defines the actual packets to
+//  be transmitted.
+
+#define BACKUPTICS 1024
+#define MAXTEXTCMD 256
+
+//
+// Packet structure
+//
+typedef enum
+{
+	PT_NOTHING,       // To send a nop through the network. ^_~
+	PT_SERVERCFG,     // Server config used in start game
+	                  // (must stay 1 for backwards compatibility).
+	                  // This is a positive response to a CLIENTJOIN request.
+	PT_CLIENTCMD,     // Ticcmd of the client.
+	PT_CLIENTMIS,     // Same as above with but saying resend from.
+	PT_CLIENT2CMD,    // 2 cmds in the packet for splitscreen.
+	PT_CLIENT2MIS,    // Same as above with but saying resend from
+	PT_NODEKEEPALIVE, // Same but without ticcmd and consistancy
+	PT_NODEKEEPALIVEMIS,
+	PT_SERVERTICS,    // All cmds for the tic.
+	PT_SERVERREFUSE,  // Server refuses joiner (reason inside).
+	PT_SERVERSHUTDOWN,
+	PT_CLIENTQUIT,    // Client closes the connection.
+
+	PT_ASKINFO,       // Anyone can ask info of the server.
+	PT_SERVERINFO,    // Send game & server info (gamespy).
+	PT_PLAYERINFO,    // Send information for players in game (gamespy).
+	PT_REQUESTFILE,   // Client requests a file transfer
+	PT_ASKINFOVIAMS,  // Packet from the MS requesting info be sent to new client.
+	                  // If this ID changes, update masterserver definition.
+
+	PT_WILLRESENDGAMESTATE, // Hey Client, I am about to resend you the gamestate!
+	PT_CANRECEIVEGAMESTATE, // Okay Server, I'm ready to receive it, you can go ahead.
+	PT_RECEIVEDGAMESTATE,   // Thank you Server, I am ready to play again!
+
+	PT_SENDINGLUAFILE, // Server telling a client Lua needs to open a file
+	PT_ASKLUAFILE,     // Client telling the server they don't have the file
+	PT_HASLUAFILE,     // Client telling the server they have the file
+
+	// Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
+
+	PT_CANFAIL,       // This is kind of a priority. Anything bigger than CANFAIL
+	                  // allows HSendPacket(*, true, *, *) to return false.
+	                  // In addition, this packet can't occupy all the available slots.
+
+	PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file.
+	PT_FILEACK,
+	PT_FILERECEIVED,
+
+	PT_TEXTCMD,       // Extra text commands from the client.
+	PT_TEXTCMD2,      // Splitscreen text commands.
+	PT_CLIENTJOIN,    // Client wants to join; used in start game.
+	PT_NODETIMEOUT,   // Packet sent to self if the connection times out.
+
+	PT_LOGIN,         // Login attempt from the client.
+
+	PT_TELLFILESNEEDED, // Client, to server: "what other files do I need starting from this number?"
+	PT_MOREFILESNEEDED, // Server, to client: "you need these (+ more on top of those)"
+
+	PT_PING,          // Packet sent to tell clients the other client's latency to server.
+	NUMPACKETTYPE
+} packettype_t;
+
+#if defined(_MSC_VER)
+#pragma pack(1)
+#endif
+
+// Client to server packet
+typedef struct
+{
+	UINT8 client_tic;
+	UINT8 resendfrom;
+	INT16 consistancy;
+	ticcmd_t cmd;
+} ATTRPACK clientcmd_pak;
+
+// Splitscreen packet
+// WARNING: must have the same format of clientcmd_pak, for more easy use
+typedef struct
+{
+	UINT8 client_tic;
+	UINT8 resendfrom;
+	INT16 consistancy;
+	ticcmd_t cmd, cmd2;
+} ATTRPACK client2cmd_pak;
+
+#ifdef _MSC_VER
+#pragma warning(disable :  4200)
+#endif
+
+// Server to client packet
+// this packet is too large
+typedef struct
+{
+	tic_t starttic;
+	UINT8 numtics;
+	UINT8 numslots; // "Slots filled": Highest player number in use plus one.
+	ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large
+} ATTRPACK servertics_pak;
+
+typedef struct
+{
+	// Server launch stuffs
+	UINT8 serverplayer;
+	UINT8 totalslotnum; // "Slots": highest player number in use plus one.
+
+	tic_t gametic;
+	UINT8 clientnode;
+	UINT8 gamestate;
+
+	UINT8 gametype;
+	UINT8 modifiedgame;
+
+	char server_context[8]; // Unique context id, generated at server startup.
+} ATTRPACK serverconfig_pak;
+
+typedef struct
+{
+	UINT8 fileid;
+	UINT32 filesize;
+	UINT8 iteration;
+	UINT32 position;
+	UINT16 size;
+	UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH
+} ATTRPACK filetx_pak;
+
+typedef struct
+{
+	UINT32 start;
+	UINT32 acks;
+} ATTRPACK fileacksegment_t;
+
+typedef struct
+{
+	UINT8 fileid;
+	UINT8 iteration;
+	UINT8 numsegments;
+	fileacksegment_t segments[0];
+} ATTRPACK fileack_pak;
+
+#ifdef _MSC_VER
+#pragma warning(default : 4200)
+#endif
+
+#define MAXAPPLICATION 16
+
+typedef struct
+{
+	UINT8 modversion;
+	char application[MAXAPPLICATION];
+	UINT8 localplayers;
+	UINT8 mode;
+	char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME];
+} ATTRPACK clientconfig_pak;
+
+#define SV_DEDICATED    0x40 // server is dedicated
+#define SV_LOTSOFADDONS 0x20 // flag used to ask for full file list in d_netfil
+
+enum {
+	REFUSE_JOINS_DISABLED = 1,
+	REFUSE_SLOTS_FULL,
+	REFUSE_BANNED,
+};
+
+#define MAXSERVERNAME 32
+#define MAXFILENEEDED 915
+
+// This packet is too large
+typedef struct
+{
+	/*
+	In the old packet, 'version' is the first field. Now that field is set
+	to 255 always, so older versions won't be confused with the new
+	versions or vice-versa.
+	*/
+	UINT8 _255;
+	UINT8 packetversion;
+	char  application[MAXAPPLICATION];
+	UINT8 version;
+	UINT8 subversion;
+	UINT8 numberofplayer;
+	UINT8 maxplayer;
+	UINT8 refusereason; // 0: joinable, REFUSE enum
+	char gametypename[24];
+	UINT8 modifiedgame;
+	UINT8 cheatsenabled;
+	UINT8 flags;
+	UINT8 fileneedednum;
+	tic_t time;
+	tic_t leveltime;
+	char servername[MAXSERVERNAME];
+	char mapname[8];
+	char maptitle[33];
+	unsigned char mapmd5[16];
+	UINT8 actnum;
+	UINT8 iszone;
+	UINT8 fileneeded[MAXFILENEEDED]; // is filled with writexxx (byteptr.h)
+} ATTRPACK serverinfo_pak;
+
+typedef struct
+{
+	char reason[255];
+} ATTRPACK serverrefuse_pak;
+
+typedef struct
+{
+	UINT8 version;
+	tic_t time; // used for ping evaluation
+} ATTRPACK askinfo_pak;
+
+typedef struct
+{
+	char clientaddr[22];
+	tic_t time; // used for ping evaluation
+} ATTRPACK msaskinfo_pak;
+
+// Shorter player information for external use.
+typedef struct
+{
+	UINT8 num;
+	char name[MAXPLAYERNAME+1];
+	UINT8 address[4]; // sending another string would run us up against MAXPACKETLENGTH
+	UINT8 team;
+	UINT8 skin;
+	UINT8 data; // Color is first four bits, hasflag, isit and issuper have one bit each, the last is unused.
+	UINT32 score;
+	UINT16 timeinserver; // In seconds.
+} ATTRPACK plrinfo;
+
+// Shortest player information for join during intermission.
+typedef struct
+{
+	char name[MAXPLAYERNAME+1];
+	UINT8 skin;
+	UINT16 color;
+	UINT32 pflags;
+	UINT32 score;
+	UINT8 ctfteam;
+} ATTRPACK plrconfig;
+
+typedef struct
+{
+	INT32 first;
+	UINT8 num;
+	UINT8 more;
+	UINT8 files[MAXFILENEEDED]; // is filled with writexxx (byteptr.h)
+} ATTRPACK filesneededconfig_pak;
+
+//
+// Network packet data
+//
+typedef struct
+{
+	UINT32 checksum;
+	UINT8 ack; // If not zero the node asks for acknowledgement, the receiver must resend the ack
+	UINT8 ackreturn; // The return of the ack number
+
+	UINT8 packettype;
+	UINT8 reserved; // Padding
+	union
+	{
+		clientcmd_pak clientpak;            //         144 bytes
+		client2cmd_pak client2pak;          //         200 bytes
+		servertics_pak serverpak;           //      132495 bytes (more around 360, no?)
+		serverconfig_pak servercfg;         //         773 bytes
+		UINT8 textcmd[MAXTEXTCMD+1];        //       66049 bytes (wut??? 64k??? More like 257 bytes...)
+		filetx_pak filetxpak;               //         139 bytes
+		fileack_pak fileack;
+		UINT8 filereceived;
+		clientconfig_pak clientcfg;         //         136 bytes
+		UINT8 md5sum[16];
+		serverinfo_pak serverinfo;          //        1024 bytes
+		serverrefuse_pak serverrefuse;      //       65025 bytes (somehow I feel like those values are garbage...)
+		askinfo_pak askinfo;                //          61 bytes
+		msaskinfo_pak msaskinfo;            //          22 bytes
+		plrinfo playerinfo[MAXPLAYERS];     //         576 bytes(?)
+		plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?)
+		INT32 filesneedednum;               //           4 bytes
+		filesneededconfig_pak filesneededcfg; //       ??? bytes
+		UINT32 pingtable[MAXPLAYERS+1];     //          68 bytes
+	} u; // This is needed to pack diff packet types data together
+} ATTRPACK doomdata_t;
+
+#if defined(_MSC_VER)
+#pragma pack()
+#endif
+
+#define FILETXHEADER        offsetof(filetx_pak, data)
+
+#define KICK_MSG_GO_AWAY     1
+#define KICK_MSG_CON_FAIL    2
+#define KICK_MSG_PLAYER_QUIT 3
+#define KICK_MSG_TIMEOUT     4
+#define KICK_MSG_BANNED      5
+#define KICK_MSG_PING_HIGH   6
+#define KICK_MSG_CUSTOM_KICK 7
+#define KICK_MSG_CUSTOM_BAN  8
+#define KICK_MSG_KEEP_BODY   0x80
+
+#endif
-- 
GitLab


From de46eef068b5fa241c2340edf88a6ff24c74c24c Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 7 Jan 2023 13:01:48 +0100
Subject: [PATCH 268/518] Move gamestate handling to a new file

---
 src/hu_stuff.c                  |   1 +
 src/netcode/Sourcefile          |   1 +
 src/netcode/d_clisrv.c          | 308 +----------------------------
 src/netcode/d_clisrv.h          |   5 -
 src/netcode/gamestate.c         | 341 ++++++++++++++++++++++++++++++++
 src/netcode/gamestate.h         |  31 +++
 src/netcode/net_command.c       |   1 +
 src/netcode/server_connection.c |   1 +
 src/netcode/tic_command.c       |   1 +
 9 files changed, 378 insertions(+), 312 deletions(-)
 create mode 100644 src/netcode/gamestate.c
 create mode 100644 src/netcode/gamestate.h

diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 1e5e47df69..dc4dfe4b76 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -21,6 +21,7 @@
 
 #include "netcode/d_clisrv.h"
 #include "netcode/net_command.h"
+#include "netcode/gamestate.h"
 
 #include "g_game.h"
 #include "g_input.h"
diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile
index 1039b218a6..c590503678 100644
--- a/src/netcode/Sourcefile
+++ b/src/netcode/Sourcefile
@@ -3,6 +3,7 @@ server_connection.c
 client_connection.c
 tic_command.c
 net_command.c
+gamestate.c
 d_net.c
 d_netcmd.c
 d_netfil.c
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index b834c92f5f..8ba10f6ea6 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -53,6 +53,7 @@
 #include "client_connection.h"
 #include "tic_command.h"
 #include "net_command.h"
+#include "gamestate.h"
 #include "protocol.h"
 
 //
@@ -93,18 +94,12 @@ tic_t maketic;
 
 INT16 consistancy[BACKUPTICS];
 
-UINT8 hu_redownloadinggamestate = 0;
-
 // true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks
 boolean hu_stopped = false;
 
 UINT8 adminpassmd5[16];
 boolean adminpasswordset = false;
 
-// Client specific
-// here it is for the secondary local player (splitscreen)
-static boolean cl_redownloadinggamestate = false;
-
 tic_t neededtic;
 SINT8 servernode = 0; // the number of the server node
 
@@ -122,233 +117,6 @@ consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_c
 // of 512 bytes is like 0.1)
 UINT16 software_MAXPACKETLENGTH;
 
-#define SAVEGAMESIZE (768*1024)
-
-boolean SV_ResendingSavegameToAnyone(void)
-{
-	INT32 i;
-
-	for (i = 0; i < MAXNETNODES; i++)
-		if (netnodes[i].resendingsavegame)
-			return true;
-	return false;
-}
-
-void SV_SendSaveGame(INT32 node, boolean resending)
-{
-	size_t length, compressedlen;
-	UINT8 *savebuffer;
-	UINT8 *compressedsave;
-	UINT8 *buffertosend;
-
-	// first save it in a malloced buffer
-	savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
-	if (!savebuffer)
-	{
-		CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
-		return;
-	}
-
-	// Leave room for the uncompressed length.
-	save_p = savebuffer + sizeof(UINT32);
-
-	P_SaveNetGame(resending);
-
-	length = save_p - savebuffer;
-	if (length > SAVEGAMESIZE)
-	{
-		free(savebuffer);
-		save_p = NULL;
-		I_Error("Savegame buffer overrun");
-	}
-
-	// Allocate space for compressed save: one byte fewer than for the
-	// uncompressed data to ensure that the compression is worthwhile.
-	compressedsave = malloc(length - 1);
-	if (!compressedsave)
-	{
-		CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
-		return;
-	}
-
-	// Attempt to compress it.
-	if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1)))
-	{
-		// Compressing succeeded; send compressed data
-
-		free(savebuffer);
-
-		// State that we're compressed.
-		buffertosend = compressedsave;
-		WRITEUINT32(compressedsave, length - sizeof(UINT32));
-		length = compressedlen + sizeof(UINT32);
-	}
-	else
-	{
-		// Compression failed to make it smaller; send original
-
-		free(compressedsave);
-
-		// State that we're not compressed
-		buffertosend = savebuffer;
-		WRITEUINT32(savebuffer, 0);
-	}
-
-	AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0);
-	save_p = NULL;
-
-	// Remember when we started sending the savegame so we can handle timeouts
-	netnodes[node].sendingsavegame = true;
-	netnodes[node].freezetimeout = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte
-}
-
-#ifdef DUMPCONSISTENCY
-#define TMPSAVENAME "badmath.sav"
-static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
-
-static void SV_SavedGame(void)
-{
-	size_t length;
-	UINT8 *savebuffer;
-	char tmpsave[256];
-
-	if (!cv_dumpconsistency.value)
-		return;
-
-	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
-
-	// first save it in a malloced buffer
-	save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
-	if (!save_p)
-	{
-		CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
-		return;
-	}
-
-	P_SaveNetGame(false);
-
-	length = save_p - savebuffer;
-	if (length > SAVEGAMESIZE)
-	{
-		free(savebuffer);
-		save_p = NULL;
-		I_Error("Savegame buffer overrun");
-	}
-
-	// then save it!
-	if (!FIL_WriteFile(tmpsave, savebuffer, length))
-		CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave);
-
-	free(savebuffer);
-	save_p = NULL;
-}
-
-#undef  TMPSAVENAME
-#endif
-#define TMPSAVENAME "$$$.sav"
-
-
-void CL_LoadReceivedSavegame(boolean reloading)
-{
-	UINT8 *savebuffer = NULL;
-	size_t length, decompressedlen;
-	char tmpsave[256];
-
-	FreeFileNeeded();
-
-	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
-
-	length = FIL_ReadFile(tmpsave, &savebuffer);
-
-	CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length));
-	if (!length)
-	{
-		I_Error("Can't read savegame sent");
-		return;
-	}
-
-	save_p = savebuffer;
-
-	// Decompress saved game if necessary.
-	decompressedlen = READUINT32(save_p);
-	if(decompressedlen > 0)
-	{
-		UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL);
-		lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen);
-		Z_Free(savebuffer);
-		save_p = savebuffer = decompressedbuffer;
-	}
-
-	paused = false;
-	demoplayback = false;
-	titlemapinaction = TITLEMAP_OFF;
-	titledemo = false;
-	automapactive = false;
-
-	P_StopRumble(NULL);
-
-	// load a base level
-	if (P_LoadNetGame(reloading))
-	{
-		const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum;
-		CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap));
-		if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, ""))
-		{
-			CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl);
-			if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
-				CONS_Printf(M_GetText(" Zone"));
-			if (actnum > 0)
-				CONS_Printf(" %2d", actnum);
-		}
-		CONS_Printf("\"\n");
-	}
-
-	// done
-	Z_Free(savebuffer);
-	save_p = NULL;
-	if (unlink(tmpsave) == -1)
-		CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
-	consistancy[gametic%BACKUPTICS] = Consistancy();
-	CON_ToggleOff();
-
-	// Tell the server we have received and reloaded the gamestate
-	// so they know they can resume the game
-	netbuffer->packettype = PT_RECEIVEDGAMESTATE;
-	HSendPacket(servernode, true, 0, 0);
-}
-
-static void CL_ReloadReceivedSavegame(void)
-{
-	INT32 i;
-
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		LUA_InvalidatePlayer(&players[i]);
-		sprintf(player_names[i], "Player %d", i + 1);
-	}
-
-	CL_LoadReceivedSavegame(true);
-
-	if (neededtic < gametic)
-		neededtic = gametic;
-	maketic = neededtic;
-
-	ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn;
-	P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16));
-	if (splitscreen)
-	{
-		ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn;
-		P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16));
-	}
-
-	camera.subsector = R_PointInSubsector(camera.x, camera.y);
-	camera2.subsector = R_PointInSubsector(camera2.x, camera2.y);
-
-	cl_redownloadinggamestate = false;
-
-	CONS_Printf(M_GetText("Game state reloaded\n"));
-}
-
 typedef struct banreason_s
 {
 	char *reason;
@@ -1006,34 +774,6 @@ static void Command_Kick(void)
 		CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
 }
 
-static void Command_ResendGamestate(void)
-{
-	SINT8 playernum;
-
-	if (COM_Argc() == 1)
-	{
-		CONS_Printf(M_GetText("resendgamestate <playername/playernum>: resend the game state to a player\n"));
-		return;
-	}
-	else if (client)
-	{
-		CONS_Printf(M_GetText("Only the server can use this.\n"));
-		return;
-	}
-
-	playernum = nametonum(COM_Argv(1));
-	if (playernum == -1 || playernum == 0)
-		return;
-
-	// Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on
-	netbuffer->packettype = PT_WILLRESENDGAMESTATE;
-	if (!HSendPacket(playernode[playernum], true, 0, 0))
-	{
-		CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n"));
-		return;
-	}
-}
-
 static void Got_KickCmd(UINT8 **p, INT32 playernum)
 {
 	INT32 pnum, msg;
@@ -1736,17 +1476,6 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole)
 	netnodes[node].ingame = false;
 }
 
-static void PT_CanReceiveGamestate(SINT8 node)
-{
-	if (client || netnodes[node].sendingsavegame)
-		return;
-
-	CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[netnodes[node].player]);
-
-	SV_SendSaveGame(node, true); // Resend a complete game state
-	netnodes[node].resendingsavegame = true;
-}
-
 static void PT_AskLuaFile(SINT8 node)
 {
 	if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED)
@@ -1759,13 +1488,6 @@ static void PT_HasLuaFile(SINT8 node)
 		SV_HandleLuaFileSent(node);
 }
 
-static void PT_ReceivedGamestate(SINT8 node)
-{
-	netnodes[node].sendingsavegame = false;
-	netnodes[node].resendingsavegame = false;
-	netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE;
-}
-
 static void PT_Ping(SINT8 node, INT32 netconsole)
 {
 	// Only accept PT_PING from the server.
@@ -1789,34 +1511,6 @@ static void PT_Ping(SINT8 node, INT32 netconsole)
 	}
 }
 
-static void PT_WillResendGamestate(SINT8 node)
-{
-	(void)node;
-
-	char tmpsave[256];
-
-	if (server || cl_redownloadinggamestate)
-		return;
-
-	// Send back a PT_CANRECEIVEGAMESTATE packet to the server
-	// so they know they can start sending the game state
-	netbuffer->packettype = PT_CANRECEIVEGAMESTATE;
-	if (!HSendPacket(servernode, true, 0, 0))
-		return;
-
-	CONS_Printf(M_GetText("Reloading game state...\n"));
-
-	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
-
-	// Don't get a corrupt savegame error because tmpsave already exists
-	if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
-		I_Error("Can't delete %s\n", tmpsave);
-
-	CL_PrepareDownloadSaveGame(tmpsave);
-
-	cl_redownloadinggamestate = true;
-}
-
 static void PT_SendingLuaFile(SINT8 node)
 {
 	(void)node;
diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h
index 691a2787f8..d5bafe750d 100644
--- a/src/netcode/d_clisrv.h
+++ b/src/netcode/d_clisrv.h
@@ -89,19 +89,16 @@ void NetUpdate(void);
 void GetPackets(void);
 void ResetNode(INT32 node);
 INT16 Consistancy(void);
-boolean SV_ResendingSavegameToAnyone(void);
 
 void SV_StartSinglePlayerServer(void);
 void SV_SpawnServer(void);
 void SV_StopServer(void);
 void SV_ResetServer(void);
-void SV_SendSaveGame(INT32 node, boolean resending);
 void CL_AddSplitscreenPlayer(void);
 void CL_RemoveSplitscreenPlayer(void);
 void CL_Reset(void);
 void CL_ClearPlayer(INT32 playernum);
 void CL_RemovePlayer(INT32 playernum, kickreason_t reason);
-void CL_LoadReceivedSavegame(boolean reloading);
 // Is there a game running
 boolean Playing(void);
 
@@ -130,8 +127,6 @@ tic_t GetLag(INT32 node);
 
 void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest);
 
-extern UINT8 hu_redownloadinggamestate;
-
 extern UINT8 adminpassmd5[16];
 extern boolean adminpasswordset;
 
diff --git a/src/netcode/gamestate.c b/src/netcode/gamestate.c
new file mode 100644
index 0000000000..4a3e9f3af8
--- /dev/null
+++ b/src/netcode/gamestate.c
@@ -0,0 +1,341 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  gamestate.c
+/// \brief Gamestate (re)sending
+
+#include "d_clisrv.h"
+#include "d_netfil.h"
+#include "gamestate.h"
+#include "i_net.h"
+#include "protocol.h"
+#include "server_connection.h"
+#include "../am_map.h"
+#include "../byteptr.h"
+#include "../console.h"
+#include "../d_main.h"
+#include "../doomstat.h"
+#include "../doomtype.h"
+#include "../f_finale.h"
+#include "../g_demo.h"
+#include "../g_game.h"
+#include "../i_time.h"
+#include "../lua_script.h"
+#include "../lzf.h"
+#include "../m_misc.h"
+#include "../p_haptic.h"
+#include "../p_local.h"
+#include "../p_saveg.h"
+#include "../r_main.h"
+#include "../tables.h"
+#include "../z_zone.h"
+
+#define SAVEGAMESIZE (768*1024)
+
+UINT8 hu_redownloadinggamestate = 0;
+boolean cl_redownloadinggamestate = false;
+
+boolean SV_ResendingSavegameToAnyone(void)
+{
+	INT32 i;
+
+	for (i = 0; i < MAXNETNODES; i++)
+		if (netnodes[i].resendingsavegame)
+			return true;
+	return false;
+}
+
+void SV_SendSaveGame(INT32 node, boolean resending)
+{
+	size_t length, compressedlen;
+	UINT8 *savebuffer;
+	UINT8 *compressedsave;
+	UINT8 *buffertosend;
+
+	// first save it in a malloced buffer
+	savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
+	if (!savebuffer)
+	{
+		CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
+		return;
+	}
+
+	// Leave room for the uncompressed length.
+	save_p = savebuffer + sizeof(UINT32);
+
+	P_SaveNetGame(resending);
+
+	length = save_p - savebuffer;
+	if (length > SAVEGAMESIZE)
+	{
+		free(savebuffer);
+		save_p = NULL;
+		I_Error("Savegame buffer overrun");
+	}
+
+	// Allocate space for compressed save: one byte fewer than for the
+	// uncompressed data to ensure that the compression is worthwhile.
+	compressedsave = malloc(length - 1);
+	if (!compressedsave)
+	{
+		CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
+		return;
+	}
+
+	// Attempt to compress it.
+	if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1)))
+	{
+		// Compressing succeeded; send compressed data
+
+		free(savebuffer);
+
+		// State that we're compressed.
+		buffertosend = compressedsave;
+		WRITEUINT32(compressedsave, length - sizeof(UINT32));
+		length = compressedlen + sizeof(UINT32);
+	}
+	else
+	{
+		// Compression failed to make it smaller; send original
+
+		free(compressedsave);
+
+		// State that we're not compressed
+		buffertosend = savebuffer;
+		WRITEUINT32(savebuffer, 0);
+	}
+
+	AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0);
+	save_p = NULL;
+
+	// Remember when we started sending the savegame so we can handle timeouts
+	netnodes[node].sendingsavegame = true;
+	netnodes[node].freezetimeout = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte
+}
+
+#ifdef DUMPCONSISTENCY
+#define TMPSAVENAME "badmath.sav"
+static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
+
+void SV_SavedGame(void)
+{
+	size_t length;
+	UINT8 *savebuffer;
+	char tmpsave[256];
+
+	if (!cv_dumpconsistency.value)
+		return;
+
+	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
+
+	// first save it in a malloced buffer
+	save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
+	if (!save_p)
+	{
+		CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
+		return;
+	}
+
+	P_SaveNetGame(false);
+
+	length = save_p - savebuffer;
+	if (length > SAVEGAMESIZE)
+	{
+		free(savebuffer);
+		save_p = NULL;
+		I_Error("Savegame buffer overrun");
+	}
+
+	// then save it!
+	if (!FIL_WriteFile(tmpsave, savebuffer, length))
+		CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave);
+
+	free(savebuffer);
+	save_p = NULL;
+}
+
+#undef  TMPSAVENAME
+#endif
+#define TMPSAVENAME "$$$.sav"
+
+
+void CL_LoadReceivedSavegame(boolean reloading)
+{
+	UINT8 *savebuffer = NULL;
+	size_t length, decompressedlen;
+	char tmpsave[256];
+
+	FreeFileNeeded();
+
+	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
+
+	length = FIL_ReadFile(tmpsave, &savebuffer);
+
+	CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length));
+	if (!length)
+	{
+		I_Error("Can't read savegame sent");
+		return;
+	}
+
+	save_p = savebuffer;
+
+	// Decompress saved game if necessary.
+	decompressedlen = READUINT32(save_p);
+	if(decompressedlen > 0)
+	{
+		UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL);
+		lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen);
+		Z_Free(savebuffer);
+		save_p = savebuffer = decompressedbuffer;
+	}
+
+	paused = false;
+	demoplayback = false;
+	titlemapinaction = TITLEMAP_OFF;
+	titledemo = false;
+	automapactive = false;
+
+	P_StopRumble(NULL);
+
+	// load a base level
+	if (P_LoadNetGame(reloading))
+	{
+		const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum;
+		CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap));
+		if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, ""))
+		{
+			CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl);
+			if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
+				CONS_Printf(M_GetText(" Zone"));
+			if (actnum > 0)
+				CONS_Printf(" %2d", actnum);
+		}
+		CONS_Printf("\"\n");
+	}
+
+	// done
+	Z_Free(savebuffer);
+	save_p = NULL;
+	if (unlink(tmpsave) == -1)
+		CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
+	consistancy[gametic%BACKUPTICS] = Consistancy();
+	CON_ToggleOff();
+
+	// Tell the server we have received and reloaded the gamestate
+	// so they know they can resume the game
+	netbuffer->packettype = PT_RECEIVEDGAMESTATE;
+	HSendPacket(servernode, true, 0, 0);
+}
+
+void CL_ReloadReceivedSavegame(void)
+{
+	INT32 i;
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		LUA_InvalidatePlayer(&players[i]);
+		sprintf(player_names[i], "Player %d", i + 1);
+	}
+
+	CL_LoadReceivedSavegame(true);
+
+	if (neededtic < gametic)
+		neededtic = gametic;
+	maketic = neededtic;
+
+	ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn;
+	P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16));
+	if (splitscreen)
+	{
+		ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn;
+		P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16));
+	}
+
+	camera.subsector = R_PointInSubsector(camera.x, camera.y);
+	camera2.subsector = R_PointInSubsector(camera2.x, camera2.y);
+
+	cl_redownloadinggamestate = false;
+
+	CONS_Printf(M_GetText("Game state reloaded\n"));
+}
+
+void Command_ResendGamestate(void)
+{
+	SINT8 playernum;
+
+	if (COM_Argc() == 1)
+	{
+		CONS_Printf(M_GetText("resendgamestate <playername/playernum>: resend the game state to a player\n"));
+		return;
+	}
+	else if (client)
+	{
+		CONS_Printf(M_GetText("Only the server can use this.\n"));
+		return;
+	}
+
+	playernum = nametonum(COM_Argv(1));
+	if (playernum == -1 || playernum == 0)
+		return;
+
+	// Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on
+	netbuffer->packettype = PT_WILLRESENDGAMESTATE;
+	if (!HSendPacket(playernode[playernum], true, 0, 0))
+	{
+		CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n"));
+		return;
+	}
+}
+
+void PT_CanReceiveGamestate(SINT8 node)
+{
+	if (client || netnodes[node].sendingsavegame)
+		return;
+
+	CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[netnodes[node].player]);
+
+	SV_SendSaveGame(node, true); // Resend a complete game state
+	netnodes[node].resendingsavegame = true;
+}
+
+void PT_ReceivedGamestate(SINT8 node)
+{
+	netnodes[node].sendingsavegame = false;
+	netnodes[node].resendingsavegame = false;
+	netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE;
+}
+
+void PT_WillResendGamestate(SINT8 node)
+{
+	(void)node;
+
+	char tmpsave[256];
+
+	if (server || cl_redownloadinggamestate)
+		return;
+
+	// Send back a PT_CANRECEIVEGAMESTATE packet to the server
+	// so they know they can start sending the game state
+	netbuffer->packettype = PT_CANRECEIVEGAMESTATE;
+	if (!HSendPacket(servernode, true, 0, 0))
+		return;
+
+	CONS_Printf(M_GetText("Reloading game state...\n"));
+
+	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
+
+	// Don't get a corrupt savegame error because tmpsave already exists
+	if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
+		I_Error("Can't delete %s\n", tmpsave);
+
+	CL_PrepareDownloadSaveGame(tmpsave);
+
+	cl_redownloadinggamestate = true;
+}
diff --git a/src/netcode/gamestate.h b/src/netcode/gamestate.h
new file mode 100644
index 0000000000..9d27797722
--- /dev/null
+++ b/src/netcode/gamestate.h
@@ -0,0 +1,31 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  gamestate.h
+/// \brief Gamestate (re)sending
+
+#ifndef __GAMESTATE__
+#define __GAMESTATE__
+
+#include "../doomtype.h"
+
+extern UINT8 hu_redownloadinggamestate;
+extern boolean cl_redownloadinggamestate;
+
+boolean SV_ResendingSavegameToAnyone(void);
+void SV_SendSaveGame(INT32 node, boolean resending);
+void SV_SavedGame(void);
+void CL_LoadReceivedSavegame(boolean reloading);
+void CL_ReloadReceivedSavegame(void);
+void Command_ResendGamestate(void);
+void PT_CanReceiveGamestate(SINT8 node);
+void PT_ReceivedGamestate(SINT8 node);
+void PT_WillResendGamestate(SINT8 node);
+
+#endif
diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c
index eeb479d21f..6f9f3f79ad 100644
--- a/src/netcode/net_command.c
+++ b/src/netcode/net_command.c
@@ -12,6 +12,7 @@
 
 #include "net_command.h"
 #include "tic_command.h"
+#include "gamestate.h"
 #include "d_clisrv.h"
 #include "i_net.h"
 #include "../g_game.h"
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
index b776db4228..28d97c0524 100644
--- a/src/netcode/server_connection.c
+++ b/src/netcode/server_connection.c
@@ -16,6 +16,7 @@
 #include "d_netfil.h"
 #include "mserv.h"
 #include "net_command.h"
+#include "gamestate.h"
 #include "../byteptr.h"
 #include "../g_game.h"
 #include "../g_state.h"
diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
index e7df895ffb..180d54e69c 100644
--- a/src/netcode/tic_command.c
+++ b/src/netcode/tic_command.c
@@ -14,6 +14,7 @@
 #include "d_clisrv.h"
 #include "net_command.h"
 #include "client_connection.h"
+#include "gamestate.h"
 #include "i_net.h"
 #include "../d_main.h"
 #include "../g_game.h"
-- 
GitLab


From 91f9f53cfc45503767e3fa7f2724c3151bab91eb Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 7 Jan 2023 13:37:33 +0100
Subject: [PATCH 269/518] Remove unused stuff

---
 src/netcode/d_clisrv.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 8ba10f6ea6..36116b9315 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -69,13 +69,9 @@
 //   firstticstosend is used to optimize a condition
 // Normally maketic >= gametic > 0
 
-#define PREDICTIONQUEUE BACKUPTICS
-#define PREDICTIONMASK (PREDICTIONQUEUE-1)
 #define MAX_REASONLENGTH 30
 
 boolean server = true; // true or false but !server == client
-#define client (!server)
-boolean nodownload = false;
 boolean serverrunning = false;
 INT32 serverplayer = 0;
 char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support)
-- 
GitLab


From d841bfb23647a2f5e97c863b5cb729fcde87fedd Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 7 Jan 2023 15:57:04 +0100
Subject: [PATCH 270/518] Move netcode console variables to appropriate files

---
 src/m_menu.c                    |  1 +
 src/netcode/client_connection.c |  1 +
 src/netcode/d_clisrv.c          | 40 +++++++++------------------------
 src/netcode/d_clisrv.h          |  7 +-----
 src/netcode/d_netfil.c          |  9 ++++++++
 src/netcode/d_netfil.h          |  2 ++
 src/netcode/net_command.c       |  1 +
 src/netcode/server_connection.c | 14 ++++++++++++
 src/netcode/server_connection.h |  2 ++
 src/p_tick.c                    |  1 +
 10 files changed, 43 insertions(+), 35 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index f0691c17fe..6a8d59f48c 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -55,6 +55,7 @@
 
 #include "netcode/d_net.h"
 #include "netcode/mserv.h"
+#include "netcode/server_connection.h"
 #include "netcode/client_connection.h"
 #include "m_misc.h"
 #include "m_anigif.h"
diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c
index bfaea2e81e..a00157a500 100644
--- a/src/netcode/client_connection.c
+++ b/src/netcode/client_connection.c
@@ -11,6 +11,7 @@
 /// \brief Client connection handling
 
 #include "client_connection.h"
+#include "gamestate.h"
 #include "d_clisrv.h"
 #include "d_netfil.h"
 #include "../d_main.h"
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 36116b9315..0b1c487d86 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -103,11 +103,6 @@ SINT8 servernode = 0; // the number of the server node
 /// \todo WORK!
 boolean acceptnewnode = true;
 
-consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
-
-static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}};
-consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL);
-
 // Some software don't support largest packet
 // (original sersetup, not exactely, but the probability of sending a packet
 // of 512 bytes is like 0.1)
@@ -123,6 +118,17 @@ typedef struct banreason_s
 static banreason_t *reasontail = NULL; //last entry, use prev
 static banreason_t *reasonhead = NULL; //1st entry, use next
 
+static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}};
+consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL);
+
+static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}};
+consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL);
+
+consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
+
+static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}};
+consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL);
+
 static void Command_ShowBan(void) //Print out ban list
 {
 	size_t i;
@@ -961,30 +967,6 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 		CL_RemovePlayer(pnum, kickreason);
 }
 
-static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}};
-consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL);
-
-consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
-static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}};
-consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL);
-static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}};
-consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL);
-static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}};
-consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "2", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL);
-
-static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}};
-consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL);
-consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
-
-// max file size to send to a player (in kilobytes)
-static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {204800, "MAX"}, {0, NULL}};
-consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL);
-consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
-
-// Speed of file downloading (in packets per tic)
-static CV_PossibleValue_t downloadspeed_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}};
-consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL);
-
 static void Got_AddPlayer(UINT8 **p, INT32 playernum);
 
 // called one time at init
diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h
index d5bafe750d..9cb3ef3e81 100644
--- a/src/netcode/d_clisrv.h
+++ b/src/netcode/d_clisrv.h
@@ -37,9 +37,6 @@ extern INT32 mapchangepending;
 // Points inside doomcom
 extern doomdata_t *netbuffer;
 
-extern consvar_t cv_showjoinaddress;
-extern consvar_t cv_playbackspeed;
-
 #define BASEPACKETSIZE      offsetof(doomdata_t, u)
 #define BASESERVERTICSSIZE  offsetof(doomdata_t, u.serverpak.cmds[0])
 
@@ -76,9 +73,7 @@ extern UINT32 realpingtable[MAXPLAYERS];
 extern UINT32 playerpingtable[MAXPLAYERS];
 extern tic_t servermaxping;
 
-extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_maxplayers, cv_joindelay, cv_rejointimeout;
-extern consvar_t cv_resynchattempts, cv_blamecfail;
-extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
+extern consvar_t cv_netticbuffer,  cv_resynchattempts, cv_blamecfail, cv_playbackspeed;
 
 // Used in d_net, the only dependence
 void D_ClientServerInit(void);
diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c
index e581be2ace..205abf713e 100644
--- a/src/netcode/d_netfil.c
+++ b/src/netcode/d_netfil.c
@@ -116,6 +116,15 @@ boolean waitingforluafiletransfer = false;
 boolean waitingforluafilecommand = false;
 char luafiledir[256 + 16] = "luafiles";
 
+// max file size to send to a player (in kilobytes)
+static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {204800, "MAX"}, {0, NULL}};
+consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL);
+
+consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
+
+// Speed of file downloading (in packets per tic)
+static CV_PossibleValue_t downloadspeed_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}};
+consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL);
 
 static UINT16 GetWadNumFromFileNeededId(UINT8 id)
 {
diff --git a/src/netcode/d_netfil.h b/src/netcode/d_netfil.h
index 732efcd5ec..850e24d490 100644
--- a/src/netcode/d_netfil.h
+++ b/src/netcode/d_netfil.h
@@ -76,6 +76,8 @@ extern UINT32 downloadcompletedsize;
 extern INT32 totalfilesrequestednum;
 extern UINT32 totalfilesrequestedsize;
 
+extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
+
 void AllocFileNeeded(INT32 size);
 void FreeFileNeeded(void);
 UINT8 *PutFileNeeded(UINT16 firstfile);
diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c
index 6f9f3f79ad..95a49a9592 100644
--- a/src/netcode/net_command.c
+++ b/src/netcode/net_command.c
@@ -13,6 +13,7 @@
 #include "net_command.h"
 #include "tic_command.h"
 #include "gamestate.h"
+#include "server_connection.h"
 #include "d_clisrv.h"
 #include "i_net.h"
 #include "../g_game.h"
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
index 28d97c0524..ce11924752 100644
--- a/src/netcode/server_connection.c
+++ b/src/netcode/server_connection.c
@@ -22,6 +22,7 @@
 #include "../g_state.h"
 #include "../p_setup.h"
 #include "../p_tick.h"
+#include "../command.h"
 #include "../doomstat.h"
 
 // Minimum timeout for sending the savegame
@@ -38,6 +39,19 @@ char playeraddress[MAXPLAYERS][64];
 
 UINT8 player_joining = false;
 
+consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
+
+consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
+
+static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}};
+consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL);
+
+static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}};
+consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL);
+
+static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}};
+consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "2", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL);
+
 static INT32 FindRejoinerNum(SINT8 node)
 {
 	char strippednodeaddress[64];
diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h
index 4204db2e6c..a40bd4603f 100644
--- a/src/netcode/server_connection.h
+++ b/src/netcode/server_connection.h
@@ -13,6 +13,7 @@
 #ifndef __D_SERVER_CONNECTION__
 #define __D_SERVER_CONNECTION__
 
+#include "../command.h"
 #include "../doomdef.h"
 #include "../doomtype.h"
 
@@ -25,5 +26,6 @@ extern tic_t jointimeout;
 extern tic_t joindelay;
 extern char playeraddress[MAXPLAYERS][64];
 extern UINT8 player_joining;
+extern consvar_t cv_showjoinaddress, cv_allownewplayer, cv_maxplayers, cv_joindelay, cv_rejointimeout;
 
 #endif
diff --git a/src/p_tick.c b/src/p_tick.c
index 4b724e3331..2473d5facb 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -27,6 +27,7 @@
 #include "r_fps.h"
 #include "i_video.h" // rendermode
 #include "netcode/net_command.h"
+#include "netcode/server_connection.h"
 
 // Object place
 #include "m_cheat.h"
-- 
GitLab


From 332a0f6b9322c07a5ec36616b621e365a2569c29 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 7 Jan 2023 15:57:17 +0100
Subject: [PATCH 271/518] Add missing PT_ prefix to a few packet handlers

---
 src/netcode/client_connection.c |  4 ++--
 src/netcode/client_connection.h |  2 +-
 src/netcode/d_clisrv.c          | 12 ++++++------
 src/netcode/server_connection.c |  2 +-
 src/netcode/server_connection.h |  2 +-
 5 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c
index a00157a500..bed445a644 100644
--- a/src/netcode/client_connection.c
+++ b/src/netcode/client_connection.c
@@ -1052,7 +1052,7 @@ void CL_ConnectToServer(void)
   * \note What happens if the packet comes from a client or something like that?
   *
   */
-void HandleServerInfo(SINT8 node)
+void PT_ServerInfo(SINT8 node)
 {
 	// compute ping in ms
 	const tic_t ticnow = I_GetTime();
@@ -1173,7 +1173,7 @@ void PT_ServerCFG(SINT8 node)
 
 	/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
 	///       Shouldn't them be downloaded even at intermission time?
-	///       Also, according to HandleConnect, the server will send the savegame even during intermission...
+	///       Also, according to PT_Connect, the server will send the savegame even during intermission...
 	if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* ||
 		netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
 		cl_mode = CL_DOWNLOADSAVEGAME;
diff --git a/src/netcode/client_connection.h b/src/netcode/client_connection.h
index a76411ba64..74cff61fff 100644
--- a/src/netcode/client_connection.h
+++ b/src/netcode/client_connection.h
@@ -53,7 +53,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room);
 void CL_ConnectToServer(void);
 boolean CL_SendJoin(void);
 
-void HandleServerInfo(SINT8 node);
+void PT_ServerInfo(SINT8 node);
 void PT_MoreFilesNeeded(SINT8 node);
 void PT_ServerRefuse(SINT8 node);
 void PT_ServerCFG(SINT8 node);
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 0b1c487d86..4589c83d3e 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -1358,7 +1358,7 @@ void SV_StartSinglePlayerServer(void)
   * \param node The packet sender (should be the server)
   *
   */
-static void HandleShutdown(SINT8 node)
+static void PT_Shutdown(SINT8 node)
 {
 	(void)node;
 	LUA_HookBool(false, HOOK(GameQuit));
@@ -1373,7 +1373,7 @@ static void HandleShutdown(SINT8 node)
   * \param node The packet sender (should be the server)
   *
   */
-static void HandleTimeout(SINT8 node)
+static void PT_Timeout(SINT8 node)
 {
 	(void)node;
 	LUA_HookBool(false, HOOK(GameQuit));
@@ -1608,26 +1608,26 @@ void GetPackets(void)
 
 		if (netbuffer->packettype == PT_CLIENTJOIN && server)
 		{
-			HandleConnect(node);
+			PT_Connect(node);
 			continue;
 		}
 		if (node == servernode && client && cl_mode != CL_SEARCHING)
 		{
 			if (netbuffer->packettype == PT_SERVERSHUTDOWN)
 			{
-				HandleShutdown(node);
+				PT_Shutdown(node);
 				continue;
 			}
 			if (netbuffer->packettype == PT_NODETIMEOUT)
 			{
-				HandleTimeout(node);
+				PT_Timeout(node);
 				continue;
 			}
 		}
 
 		if (netbuffer->packettype == PT_SERVERINFO)
 		{
-			HandleServerInfo(node);
+			PT_ServerInfo(node);
 			continue;
 		}
 
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
index ce11924752..e37c459510 100644
--- a/src/netcode/server_connection.c
+++ b/src/netcode/server_connection.c
@@ -422,7 +422,7 @@ GetRefuseMessage (SINT8 node, INT32 rejoinernum)
   * \param node The packet sender
   *
   */
-void HandleConnect(SINT8 node)
+void PT_Connect(SINT8 node)
 {
 	char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
 	INT32 numplayers = netbuffer->u.clientcfg.localplayers;
diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h
index a40bd4603f..585c9e428e 100644
--- a/src/netcode/server_connection.h
+++ b/src/netcode/server_connection.h
@@ -17,7 +17,7 @@
 #include "../doomdef.h"
 #include "../doomtype.h"
 
-void HandleConnect(SINT8 node);
+void PT_Connect(SINT8 node);
 void PT_AskInfoViaMS(SINT8 node);
 void PT_TellFilesNeeded(SINT8 node);
 void PT_AskInfo(SINT8 node);
-- 
GitLab


From 39556e8af74d2d34976159b7654a9af684152d14 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 7 Jan 2023 16:14:41 +0100
Subject: [PATCH 272/518] Add missing _pak suffix to a few packet structures

---
 src/netcode/protocol.h          | 8 ++++----
 src/netcode/server_connection.c | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h
index 566d10d8cb..1eba9286b9 100644
--- a/src/netcode/protocol.h
+++ b/src/netcode/protocol.h
@@ -260,7 +260,7 @@ typedef struct
 	UINT8 data; // Color is first four bits, hasflag, isit and issuper have one bit each, the last is unused.
 	UINT32 score;
 	UINT16 timeinserver; // In seconds.
-} ATTRPACK plrinfo;
+} ATTRPACK plrinfo_pak;
 
 // Shortest player information for join during intermission.
 typedef struct
@@ -271,7 +271,7 @@ typedef struct
 	UINT32 pflags;
 	UINT32 score;
 	UINT8 ctfteam;
-} ATTRPACK plrconfig;
+} ATTRPACK plrconfig_pak;
 
 typedef struct
 {
@@ -308,8 +308,8 @@ typedef struct
 		serverrefuse_pak serverrefuse;      //       65025 bytes (somehow I feel like those values are garbage...)
 		askinfo_pak askinfo;                //          61 bytes
 		msaskinfo_pak msaskinfo;            //          22 bytes
-		plrinfo playerinfo[MAXPLAYERS];     //         576 bytes(?)
-		plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?)
+		plrinfo_pak playerinfo[MAXPLAYERS];     //         576 bytes(?)
+		plrconfig_pak playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?)
 		INT32 filesneedednum;               //           4 bytes
 		filesneededconfig_pak filesneededcfg; //       ??? bytes
 		UINT32 pingtable[MAXPLAYERS+1];     //          68 bytes
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
index e37c459510..924aa82d6c 100644
--- a/src/netcode/server_connection.c
+++ b/src/netcode/server_connection.c
@@ -219,7 +219,7 @@ static void SV_SendPlayerInfo(INT32 node)
 			netbuffer->u.playerinfo[i].data |= 0x80;
 	}
 
-	HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS);
+	HSendPacket(node, false, 0, sizeof(plrinfo_pak) * MAXPLAYERS);
 }
 
 /** Sends a PT_SERVERCFG packet
-- 
GitLab


From 94761d3a5505d79d6f7ebdf96e7fbf88991bff47 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sun, 8 Jan 2023 00:43:18 +0100
Subject: [PATCH 273/518] Remove outdated or misleading comments

---
 src/netcode/client_connection.c | 11 ++---------
 src/netcode/d_clisrv.c          | 14 ++------------
 src/netcode/protocol.h          | 32 ++++++++++++++++----------------
 src/netcode/server_connection.c |  2 +-
 src/netcode/tic_command.c       |  8 --------
 5 files changed, 21 insertions(+), 46 deletions(-)

diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c
index bed445a644..1adc3a65ea 100644
--- a/src/netcode/client_connection.c
+++ b/src/netcode/client_connection.c
@@ -36,7 +36,7 @@ cl_mode_t cl_mode = CL_SEARCHING;
 static UINT16 cl_lastcheckedfilecount = 0;	// used for full file list
 boolean serverisfull = false; //lets us be aware if the server was full after we check files, but before downloading, so we can ask if the user still wants to download or not
 tic_t firstconnectattempttime = 0;
-UINT8 mynode; // my address pointofview server
+UINT8 mynode;
 static void *snake = NULL;
 
 static void CL_DrawConnectionStatusBox(void)
@@ -226,12 +226,9 @@ static boolean CL_AskFileList(INT32 firstfile)
 	return HSendPacket(servernode, false, 0, sizeof (INT32));
 }
 
-/** Sends a special packet to declare how many players in local
-  * Used only in arbitratrenetstart()
-  * Sends a PT_CLIENTJOIN packet to the server
+/** Sends a PT_CLIENTJOIN packet to the server
   *
   * \return True if the packet was successfully sent
-  * \todo Improve the description...
   *
   */
 boolean CL_SendJoin(void)
@@ -928,10 +925,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 		if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME))
 			FileReceiveTicker();
 
-		// why are these here? this is for servers, we're a client
-		//if (key == 's' && server)
-		//	doomcom->numnodes = (INT16)pnumnodes;
-		//FileSendTicker();
 		*oldtic = I_GetTime();
 
 		if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED)
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 4589c83d3e..eef9bdfe52 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -99,13 +99,8 @@ boolean adminpasswordset = false;
 tic_t neededtic;
 SINT8 servernode = 0; // the number of the server node
 
-/// \brief do we accept new players?
-/// \todo WORK!
 boolean acceptnewnode = true;
 
-// Some software don't support largest packet
-// (original sersetup, not exactely, but the probability of sending a packet
-// of 512 bytes is like 0.1)
 UINT16 software_MAXPACKETLENGTH;
 
 typedef struct banreason_s
@@ -300,9 +295,6 @@ static void Command_connect(void)
 		return;
 	}
 
-	// modified game check: no longer handled
-	// we don't request a restart unless the filelist differs
-
 	server = false;
 /*
 	if (!stricmp(COM_Argv(1), "self"))
@@ -1768,7 +1760,7 @@ boolean TryRunTics(tic_t realtics)
 	boolean ticking;
 
 	// the machine has lagged but it is not so bad
-	if (realtics > TICRATE/7) // FIXME: consistency failure!!
+	if (realtics > TICRATE/7)
 	{
 		if (server)
 			realtics = 1;
@@ -1802,8 +1794,6 @@ boolean TryRunTics(tic_t realtics)
 #ifdef DEBUGFILE
 	if (debugfile && (realtics || neededtic > gametic))
 	{
-		//SoM: 3/30/2000: Need long INT32 in the format string for args 4 & 5.
-		//Shut up stupid warning!
 		fprintf(debugfile, "------------ Tryruntic: REAL:%d NEED:%d GAME:%d LOAD: %d\n",
 			realtics, neededtic, gametic, debugload);
 		debugload = 100000;
@@ -1983,7 +1973,7 @@ void NetUpdate(void)
 	if (client)
 		maketic = neededtic;
 
-	Local_Maketic(realtics); // make local tic, and call menu?
+	Local_Maketic(realtics);
 
 	if (server)
 		CL_SendClientCmd(); // send it
diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h
index 1eba9286b9..cbeb37f36c 100644
--- a/src/netcode/protocol.h
+++ b/src/netcode/protocol.h
@@ -294,25 +294,25 @@ typedef struct
 	UINT8 reserved; // Padding
 	union
 	{
-		clientcmd_pak clientpak;            //         144 bytes
-		client2cmd_pak client2pak;          //         200 bytes
-		servertics_pak serverpak;           //      132495 bytes (more around 360, no?)
-		serverconfig_pak servercfg;         //         773 bytes
-		UINT8 textcmd[MAXTEXTCMD+1];        //       66049 bytes (wut??? 64k??? More like 257 bytes...)
-		filetx_pak filetxpak;               //         139 bytes
+		clientcmd_pak clientpak;
+		client2cmd_pak client2pak;
+		servertics_pak serverpak;
+		serverconfig_pak servercfg;
+		UINT8 textcmd[MAXTEXTCMD+1];
+		filetx_pak filetxpak;
 		fileack_pak fileack;
 		UINT8 filereceived;
-		clientconfig_pak clientcfg;         //         136 bytes
+		clientconfig_pak clientcfg;
 		UINT8 md5sum[16];
-		serverinfo_pak serverinfo;          //        1024 bytes
-		serverrefuse_pak serverrefuse;      //       65025 bytes (somehow I feel like those values are garbage...)
-		askinfo_pak askinfo;                //          61 bytes
-		msaskinfo_pak msaskinfo;            //          22 bytes
-		plrinfo_pak playerinfo[MAXPLAYERS];     //         576 bytes(?)
-		plrconfig_pak playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?)
-		INT32 filesneedednum;               //           4 bytes
-		filesneededconfig_pak filesneededcfg; //       ??? bytes
-		UINT32 pingtable[MAXPLAYERS+1];     //          68 bytes
+		serverinfo_pak serverinfo;
+		serverrefuse_pak serverrefuse;
+		askinfo_pak askinfo;
+		msaskinfo_pak msaskinfo;
+		plrinfo_pak playerinfo[MAXPLAYERS];
+		plrconfig_pak playerconfig[MAXPLAYERS];
+		INT32 filesneedednum;
+		filesneededconfig_pak filesneededcfg;
+		UINT32 pingtable[MAXPLAYERS+1];
 	} u; // This is needed to pack diff packet types data together
 } ATTRPACK doomdata_t;
 
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
index 924aa82d6c..f2b0306e64 100644
--- a/src/netcode/server_connection.c
+++ b/src/netcode/server_connection.c
@@ -460,7 +460,7 @@ void PT_Connect(SINT8 node)
 		ResetNode(node);
 		SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
 		/// \todo fix this !!!
-		return; // restart the while
+		return;
 	}
 	DEBFILE("new node joined\n");
 
diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
index 180d54e69c..bab0fad0a6 100644
--- a/src/netcode/tic_command.c
+++ b/src/netcode/tic_command.c
@@ -262,11 +262,6 @@ void PT_ServerTics(SINT8 node, INT32 netconsole)
 	else
 	{
 		DEBFILE(va("frame not in bound: %u\n", neededtic));
-		/*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart)
-			I_Error("Received an out of order PT_SERVERTICS packet!\n"
-					"Got tics %d-%d, needed tic %d\n\n"
-					"Please report this crash on the Master Board,\n"
-					"IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/
 	}
 }
 
@@ -447,9 +442,6 @@ void SV_SendTics(void)
 	netnodes[0].supposedtic = maketic;
 }
 
-//
-// TryRunTics
-//
 void Local_Maketic(INT32 realtics)
 {
 	I_OsPolling(); // I_Getevent
-- 
GitLab


From cb634402a843e6542d00c851fda814d9f2df9a23 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sun, 8 Jan 2023 00:46:12 +0100
Subject: [PATCH 274/518] Rename packet handlers to match their associated PT_
 constants

---
 src/netcode/client_connection.c | 2 +-
 src/netcode/d_clisrv.c          | 4 ++--
 src/netcode/server_connection.c | 2 +-
 src/netcode/server_connection.h | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c
index 1adc3a65ea..ff20a6fc74 100644
--- a/src/netcode/client_connection.c
+++ b/src/netcode/client_connection.c
@@ -1166,7 +1166,7 @@ void PT_ServerCFG(SINT8 node)
 
 	/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
 	///       Shouldn't them be downloaded even at intermission time?
-	///       Also, according to PT_Connect, the server will send the savegame even during intermission...
+	///       Also, according to PT_ClientJoin, the server will send the savegame even during intermission...
 	if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* ||
 		netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
 		cl_mode = CL_DOWNLOADSAVEGAME;
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index eef9bdfe52..7fc64b4561 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -1350,7 +1350,7 @@ void SV_StartSinglePlayerServer(void)
   * \param node The packet sender (should be the server)
   *
   */
-static void PT_Shutdown(SINT8 node)
+static void PT_ServerShutdown(SINT8 node)
 {
 	(void)node;
 	LUA_HookBool(false, HOOK(GameQuit));
@@ -1365,7 +1365,7 @@ static void PT_Shutdown(SINT8 node)
   * \param node The packet sender (should be the server)
   *
   */
-static void PT_Timeout(SINT8 node)
+static void PT_NodeTimeout(SINT8 node)
 {
 	(void)node;
 	LUA_HookBool(false, HOOK(GameQuit));
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
index f2b0306e64..3e7d46ef65 100644
--- a/src/netcode/server_connection.c
+++ b/src/netcode/server_connection.c
@@ -422,7 +422,7 @@ GetRefuseMessage (SINT8 node, INT32 rejoinernum)
   * \param node The packet sender
   *
   */
-void PT_Connect(SINT8 node)
+void PT_ClientJoin(SINT8 node)
 {
 	char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
 	INT32 numplayers = netbuffer->u.clientcfg.localplayers;
diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h
index 585c9e428e..96f7566baa 100644
--- a/src/netcode/server_connection.h
+++ b/src/netcode/server_connection.h
@@ -17,7 +17,7 @@
 #include "../doomdef.h"
 #include "../doomtype.h"
 
-void PT_Connect(SINT8 node);
+void PT_ClientJoin(SINT8 node);
 void PT_AskInfoViaMS(SINT8 node);
 void PT_TellFilesNeeded(SINT8 node);
 void PT_AskInfo(SINT8 node);
-- 
GitLab


From 5d5e2c78e4e116b40de4bfb81b2d409b4211c189 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sun, 8 Jan 2023 00:47:33 +0100
Subject: [PATCH 275/518] Call all packet handlers from the same place

---
 src/netcode/d_clisrv.c          | 41 +++++++++++----------------------
 src/netcode/server_connection.c |  2 +-
 2 files changed, 14 insertions(+), 29 deletions(-)

diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 7fc64b4561..c004eeede0 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -1352,6 +1352,9 @@ void SV_StartSinglePlayerServer(void)
   */
 static void PT_ServerShutdown(SINT8 node)
 {
+	if (node != servernode || server || cl_mode == CL_SEARCHING)
+		return;
+
 	(void)node;
 	LUA_HookBool(false, HOOK(GameQuit));
 	D_QuitNetGame();
@@ -1412,7 +1415,11 @@ static void PT_Login(SINT8 node, INT32 netconsole)
 static void PT_ClientQuit(SINT8 node, INT32 netconsole)
 {
 	if (client)
+	{
+		if (node == servernode && cl_mode != CL_SEARCHING && netbuffer->packettype == PT_NODETIMEOUT)
+			PT_NodeTimeout(node);
 		return;
+	}
 
 	if (!netnodes[node].ingame)
 	{
@@ -1505,6 +1512,7 @@ static void HandlePacketFromAwayNode(SINT8 node)
 	switch (netbuffer->packettype)
 	{
 		case PT_ASKINFOVIAMS   : PT_AskInfoViaMS   (node    ); break;
+		case PT_SERVERINFO     : PT_ServerInfo     (node    ); break;
 		case PT_TELLFILESNEEDED: PT_TellFilesNeeded(node    ); break;
 		case PT_MOREFILESNEEDED: PT_MoreFilesNeeded(node    ); break;
 		case PT_ASKINFO        : PT_AskInfo        (node    ); break;
@@ -1517,6 +1525,8 @@ static void HandlePacketFromAwayNode(SINT8 node)
 		case PT_NODETIMEOUT    : PT_ClientQuit     (node, -1); break;
 		case PT_CLIENTQUIT     : PT_ClientQuit     (node, -1); break;
 		case PT_SERVERTICS     : PT_ServerTics     (node, -1); break;
+		case PT_CLIENTJOIN     : PT_ClientJoin     (node    ); break;
+		case PT_SERVERSHUTDOWN : PT_ServerShutdown (node    ); break;
 		case PT_CLIENTCMD      :                               break; // This is not an "unknown packet"
 
 		default:
@@ -1566,6 +1576,7 @@ static void HandlePacketFromPlayer(SINT8 node)
 		case PT_ASKLUAFILE         : PT_AskLuaFile         (node            ); break;
 		case PT_HASLUAFILE         : PT_HasLuaFile         (node            ); break;
 		case PT_RECEIVEDGAMESTATE  : PT_ReceivedGamestate  (node            ); break;
+		case PT_SERVERINFO         : PT_ServerInfo         (node            ); break;
 
 		// CLIENT RECEIVE
 		case PT_SERVERTICS         : PT_ServerTics         (node, netconsole); break;
@@ -1575,7 +1586,9 @@ static void HandlePacketFromPlayer(SINT8 node)
 		case PT_FILERECEIVED       : PT_FileReceived       (node            ); break;
 		case PT_WILLRESENDGAMESTATE: PT_WillResendGamestate(node            ); break;
 		case PT_SENDINGLUAFILE     : PT_SendingLuaFile     (node            ); break;
+		case PT_SERVERSHUTDOWN     : PT_ServerShutdown     (node            ); break;
 		case PT_SERVERCFG          :                                           break;
+		case PT_CLIENTJOIN         :                                           break;
 
 		default:
 			DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n",
@@ -1598,34 +1611,6 @@ void GetPackets(void)
 	{
 		node = (SINT8)doomcom->remotenode;
 
-		if (netbuffer->packettype == PT_CLIENTJOIN && server)
-		{
-			PT_Connect(node);
-			continue;
-		}
-		if (node == servernode && client && cl_mode != CL_SEARCHING)
-		{
-			if (netbuffer->packettype == PT_SERVERSHUTDOWN)
-			{
-				PT_Shutdown(node);
-				continue;
-			}
-			if (netbuffer->packettype == PT_NODETIMEOUT)
-			{
-				PT_Timeout(node);
-				continue;
-			}
-		}
-
-		if (netbuffer->packettype == PT_SERVERINFO)
-		{
-			PT_ServerInfo(node);
-			continue;
-		}
-
-		if (netbuffer->packettype == PT_PLAYERINFO)
-			continue; // We do nothing with PLAYERINFO, that's for the MS browser.
-
 		// Packet received from someone already playing
 		if (netnodes[node].ingame)
 			HandlePacketFromPlayer(node);
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
index 3e7d46ef65..9b41da9201 100644
--- a/src/netcode/server_connection.c
+++ b/src/netcode/server_connection.c
@@ -430,7 +430,7 @@ void PT_ClientJoin(SINT8 node)
 	INT32 i;
 
 	// Ignore duplicate packets
-	if (netnodes[node].ingame)
+	if (client || netnodes[node].ingame)
 		return;
 
 	rejoinernum = FindRejoinerNum(node);
-- 
GitLab


From 2bd2b728bf530b65de3c12cbb9a7f75e79d94c5f Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sun, 8 Jan 2023 13:27:08 +0100
Subject: [PATCH 276/518] Remove useless variable

---
 src/netcode/d_clisrv.c          | 9 ---------
 src/netcode/server_connection.c | 3 ---
 src/netcode/server_connection.h | 1 -
 3 files changed, 13 deletions(-)

diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index c004eeede0..ecd7c9bbe8 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -1605,8 +1605,6 @@ void GetPackets(void)
 {
 	SINT8 node; // The packet sender
 
-	player_joining = false;
-
 	while (HGetPacket())
 	{
 		node = (SINT8)doomcom->remotenode;
@@ -1793,13 +1791,6 @@ boolean TryRunTics(tic_t realtics)
 			hu_stopped = false;
 	}
 
-	if (player_joining)
-	{
-		if (realtics)
-			hu_stopped = true;
-		return false;
-	}
-
 	if (ticking)
 	{
 		if (advancedemo)
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
index 9b41da9201..ed0e28309a 100644
--- a/src/netcode/server_connection.c
+++ b/src/netcode/server_connection.c
@@ -37,8 +37,6 @@ tic_t joindelay = 0;
 // The actual timeout will be longer depending on the savegame length
 char playeraddress[MAXPLAYERS][64];
 
-UINT8 player_joining = false;
-
 consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
 
 consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
@@ -475,7 +473,6 @@ void PT_ClientJoin(SINT8 node)
 		SV_AddPlayer(node, names[i]);
 
 	joindelay += cv_joindelay.value * TICRATE;
-	player_joining = true;
 }
 
 void PT_AskInfoViaMS(SINT8 node)
diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h
index 96f7566baa..7481d0eb56 100644
--- a/src/netcode/server_connection.h
+++ b/src/netcode/server_connection.h
@@ -25,7 +25,6 @@ void PT_AskInfo(SINT8 node);
 extern tic_t jointimeout;
 extern tic_t joindelay;
 extern char playeraddress[MAXPLAYERS][64];
-extern UINT8 player_joining;
 extern consvar_t cv_showjoinaddress, cv_allownewplayer, cv_maxplayers, cv_joindelay, cv_rejointimeout;
 
 #endif
-- 
GitLab


From b42336c23254e795e351aad18a4ef83989f987a1 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sun, 8 Jan 2023 16:28:41 +0100
Subject: [PATCH 277/518] Simplify convoluted code

---
 src/netcode/d_clisrv.c | 38 ++++++++++++--------------------------
 1 file changed, 12 insertions(+), 26 deletions(-)

diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index ecd7c9bbe8..5bb2c4600a 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -1421,15 +1421,9 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole)
 		return;
 	}
 
-	if (!netnodes[node].ingame)
-	{
-		Net_CloseConnection(node);
-		return;
-	}
-
 	// nodeingame will be put false in the execution of kick command
 	// this allow to send some packets to the quitting client to have their ack back
-	if (netconsole != -1 && playeringame[netconsole])
+	if (netnodes[node].ingame && netconsole != -1 && playeringame[netconsole])
 	{
 		UINT8 kickmsg;
 
@@ -1440,17 +1434,18 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole)
 		kickmsg |= KICK_MSG_KEEP_BODY;
 
 		SendKick(netconsole, kickmsg);
-		netnodes[node].player = -1;
 
 		if (netnodes[node].player2 != -1 && netnodes[node].player2 >= 0
 			&& playeringame[(UINT8)netnodes[node].player2])
 		{
 			SendKick(netnodes[node].player2, kickmsg);
-			netnodes[node].player2 = -1;
 		}
 	}
+
 	Net_CloseConnection(node);
 	netnodes[node].ingame = false;
+	netnodes[node].player = -1;
+	netnodes[node].player2 = -1;
 }
 
 static void PT_AskLuaFile(SINT8 node)
@@ -1603,11 +1598,9 @@ static void HandlePacketFromPlayer(SINT8 node)
   */
 void GetPackets(void)
 {
-	SINT8 node; // The packet sender
-
 	while (HGetPacket())
 	{
-		node = (SINT8)doomcom->remotenode;
+		SINT8 node = doomcom->remotenode;
 
 		// Packet received from someone already playing
 		if (netnodes[node].ingame)
@@ -1740,8 +1733,6 @@ If they're not lagging, decrement the timer by 1. Of course, reset all of this i
 
 boolean TryRunTics(tic_t realtics)
 {
-	boolean ticking;
-
 	// the machine has lagged but it is not so bad
 	if (realtics > TICRATE/7)
 	{
@@ -1783,16 +1774,11 @@ boolean TryRunTics(tic_t realtics)
 	}
 #endif
 
-	ticking = neededtic > gametic;
-
-	if (ticking)
+	if (neededtic > gametic)
 	{
 		if (realtics)
 			hu_stopped = false;
-	}
 
-	if (ticking)
-	{
 		if (advancedemo)
 		{
 			if (timedemo_quit)
@@ -1826,16 +1812,19 @@ boolean TryRunTics(tic_t realtics)
 				if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value)
 					break;
 			}
+
+		return true;
 	}
 	else
 	{
 		if (realtics)
 			hu_stopped = true;
-	}
 
-	return ticking;
+		return false;
+	}
 }
 
+
 static INT32 pingtimeout[MAXPLAYERS];
 
 static inline void PingUpdate(void)
@@ -1976,8 +1965,6 @@ void NetUpdate(void)
 	{
 		if (!demoplayback)
 		{
-			INT32 counts;
-
 			hu_redownloadinggamestate = false;
 
 			firstticstosend = gametic;
@@ -1991,8 +1978,7 @@ void NetUpdate(void)
 				}
 
 			// Don't erase tics not acknowledged
-			counts = realtics;
-
+			INT32 counts = realtics;
 			if (maketic + counts >= firstticstosend + BACKUPTICS)
 				counts = firstticstosend+BACKUPTICS-maketic-1;
 
-- 
GitLab


From 2f9a2ebc86f6252526f5186793c81933d71f9293 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sun, 8 Jan 2023 16:33:21 +0100
Subject: [PATCH 278/518] Move comment to an appropriate place

---
 src/netcode/d_clisrv.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 5bb2c4600a..b0d534b843 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -1725,12 +1725,6 @@ INT16 Consistancy(void)
 	return (INT16)(ret & 0xFFFF);
 }
 
-/*
-Ping Update except better:
-We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
-If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
-*/
-
 boolean TryRunTics(tic_t realtics)
 {
 	// the machine has lagged but it is not so bad
@@ -1824,6 +1818,11 @@ boolean TryRunTics(tic_t realtics)
 	}
 }
 
+/*
+Ping Update except better:
+We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
+If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
+*/
 
 static INT32 pingtimeout[MAXPLAYERS];
 
-- 
GitLab


From 35dac244ef1a0727ba54a3dbdb6dd797220fdbcd Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Mon, 9 Jan 2023 21:39:33 +0100
Subject: [PATCH 279/518] Fix clients not being able to join servers

---
 src/netcode/d_clisrv.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index b0d534b843..6680c7e42f 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -1523,6 +1523,7 @@ static void HandlePacketFromAwayNode(SINT8 node)
 		case PT_CLIENTJOIN     : PT_ClientJoin     (node    ); break;
 		case PT_SERVERSHUTDOWN : PT_ServerShutdown (node    ); break;
 		case PT_CLIENTCMD      :                               break; // This is not an "unknown packet"
+		case PT_PLAYERINFO     :                               break; // This is not an "unknown packet"
 
 		default:
 			DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
-- 
GitLab


From 456c6b1a0012c8b04881ba9f7042869e111c9471 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Mon, 9 Jan 2023 22:14:58 +0100
Subject: [PATCH 280/518] Remove reboundstore timeout hack

---
 src/netcode/d_clisrv.c    | 50 ++++++++-------------------------------
 src/netcode/d_clisrv.h    |  1 +
 src/netcode/d_net.c       | 19 +++++----------
 src/netcode/net_command.c | 10 ++++++++
 src/netcode/net_command.h |  1 +
 src/netcode/protocol.h    |  1 -
 6 files changed, 28 insertions(+), 54 deletions(-)

diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 6680c7e42f..81d7e55435 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -479,6 +479,15 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
 		P_CheckRacers();
 }
 
+void CL_HandleTimeout(void)
+{
+	LUA_HookBool(false, HOOK(GameQuit));
+	D_QuitNetGame();
+	CL_Reset();
+	D_StartTitle();
+	M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
+}
+
 void CL_Reset(void)
 {
 	if (metalrecording)
@@ -1363,21 +1372,6 @@ static void PT_ServerShutdown(SINT8 node)
 	M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING);
 }
 
-/** Called when a PT_NODETIMEOUT packet is received
-  *
-  * \param node The packet sender (should be the server)
-  *
-  */
-static void PT_NodeTimeout(SINT8 node)
-{
-	(void)node;
-	LUA_HookBool(false, HOOK(GameQuit));
-	D_QuitNetGame();
-	CL_Reset();
-	D_StartTitle();
-	M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
-}
-
 static void PT_Login(SINT8 node, INT32 netconsole)
 {
 	(void)node;
@@ -1415,32 +1409,10 @@ static void PT_Login(SINT8 node, INT32 netconsole)
 static void PT_ClientQuit(SINT8 node, INT32 netconsole)
 {
 	if (client)
-	{
-		if (node == servernode && cl_mode != CL_SEARCHING && netbuffer->packettype == PT_NODETIMEOUT)
-			PT_NodeTimeout(node);
 		return;
-	}
 
-	// nodeingame will be put false in the execution of kick command
-	// this allow to send some packets to the quitting client to have their ack back
 	if (netnodes[node].ingame && netconsole != -1 && playeringame[netconsole])
-	{
-		UINT8 kickmsg;
-
-		if (netbuffer->packettype == PT_NODETIMEOUT)
-			kickmsg = KICK_MSG_TIMEOUT;
-		else
-			kickmsg = KICK_MSG_PLAYER_QUIT;
-		kickmsg |= KICK_MSG_KEEP_BODY;
-
-		SendKick(netconsole, kickmsg);
-
-		if (netnodes[node].player2 != -1 && netnodes[node].player2 >= 0
-			&& playeringame[(UINT8)netnodes[node].player2])
-		{
-			SendKick(netnodes[node].player2, kickmsg);
-		}
-	}
+		SendKicksForNode(node, KICK_MSG_PLAYER_QUIT | KICK_MSG_KEEP_BODY);
 
 	Net_CloseConnection(node);
 	netnodes[node].ingame = false;
@@ -1517,7 +1489,6 @@ static void HandlePacketFromAwayNode(SINT8 node)
 		case PT_FILEACK        : PT_FileAck        (node    ); break;
 		case PT_FILERECEIVED   : PT_FileReceived   (node    ); break;
 		case PT_REQUESTFILE    : PT_RequestFile    (node    ); break;
-		case PT_NODETIMEOUT    : PT_ClientQuit     (node, -1); break;
 		case PT_CLIENTQUIT     : PT_ClientQuit     (node, -1); break;
 		case PT_SERVERTICS     : PT_ServerTics     (node, -1); break;
 		case PT_CLIENTJOIN     : PT_ClientJoin     (node    ); break;
@@ -1566,7 +1537,6 @@ static void HandlePacketFromPlayer(SINT8 node)
 		case PT_TEXTCMD            : PT_TextCmd            (node, netconsole); break;
 		case PT_TEXTCMD2           : PT_TextCmd            (node, netconsole); break;
 		case PT_LOGIN              : PT_Login              (node, netconsole); break;
-		case PT_NODETIMEOUT        : PT_ClientQuit         (node, netconsole); break;
 		case PT_CLIENTQUIT         : PT_ClientQuit         (node, netconsole); break;
 		case PT_CANRECEIVEGAMESTATE: PT_CanReceiveGamestate(node            ); break;
 		case PT_ASKLUAFILE         : PT_AskLuaFile         (node            ); break;
diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h
index 9cb3ef3e81..0abd638ce9 100644
--- a/src/netcode/d_clisrv.h
+++ b/src/netcode/d_clisrv.h
@@ -94,6 +94,7 @@ void CL_RemoveSplitscreenPlayer(void);
 void CL_Reset(void);
 void CL_ClearPlayer(INT32 playernum);
 void CL_RemovePlayer(INT32 playernum, kickreason_t reason);
+void CL_HandleTimeout(void);
 // Is there a game running
 boolean Playing(void);
 
diff --git a/src/netcode/d_net.c b/src/netcode/d_net.c
index 9723422824..3c16392d01 100644
--- a/src/netcode/d_net.c
+++ b/src/netcode/d_net.c
@@ -27,6 +27,7 @@
 #include "d_netfil.h"
 #include "d_clisrv.h"
 #include "tic_command.h"
+#include "net_command.h"
 #include "../z_zone.h"
 #include "i_tcp.h"
 #include "../d_main.h" // srb2home
@@ -461,14 +462,10 @@ void Net_ConnectionTimeout(INT32 node)
 		return;
 	nodes[node].flags |= NF_TIMEOUT;
 
-	// Send a very special packet to self (hack the reboundstore queue)
-	// Main code will handle it
-	reboundstore[rebound_head].packettype = PT_NODETIMEOUT;
-	reboundstore[rebound_head].ack = 0;
-	reboundstore[rebound_head].ackreturn = 0;
-	reboundstore[rebound_head].u.textcmd[0] = (UINT8)node;
-	reboundsize[rebound_head] = (INT16)(BASEPACKETSIZE + 1);
-	rebound_head = (rebound_head+1) % MAXREBOUND;
+	if (server)
+		SendKicksForNode(node, KICK_MSG_TIMEOUT | KICK_MSG_KEEP_BODY);
+	else
+		CL_HandleTimeout();
 
 	// Do not redo it quickly (if we do not close connection it is
 	// for a good reason!)
@@ -782,7 +779,6 @@ static const char *packettypename[NUMPACKETTYPE] =
 	"TEXTCMD",
 	"TEXTCMD2",
 	"CLIENTJOIN",
-	"NODETIMEOUT",
 	"LOGIN",
 	"TELLFILESNEEDED",
 	"MOREFILESNEEDED",
@@ -1065,10 +1061,7 @@ boolean HGetPacket(void)
 	{
 		M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]);
 		doomcom->datalength = reboundsize[rebound_tail];
-		if (netbuffer->packettype == PT_NODETIMEOUT)
-			doomcom->remotenode = netbuffer->u.textcmd[0];
-		else
-			doomcom->remotenode = 0;
+		doomcom->remotenode = 0;
 
 		rebound_tail = (rebound_tail+1) % MAXREBOUND;
 #ifdef DEBUGFILE
diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c
index 95a49a9592..13477d42d5 100644
--- a/src/netcode/net_command.c
+++ b/src/netcode/net_command.c
@@ -315,3 +315,13 @@ void SendKick(UINT8 playernum, UINT8 msg)
 	buf[1] = msg;
 	SendNetXCmd(XD_KICK, &buf, 2);
 }
+
+void SendKicksForNode(SINT8 node, UINT8 msg)
+{
+	if (!netnodes[node].ingame)
+		return;
+
+	for (INT32 playernum = netnodes[node].player; playernum != -1; playernum = netnodes[node].player2)
+		if (playernum != -1 && playeringame[playernum])
+			SendKick(playernum, msg);
+}
diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h
index 1fc5035c81..f1b4b2f3f8 100644
--- a/src/netcode/net_command.h
+++ b/src/netcode/net_command.h
@@ -58,5 +58,6 @@ size_t TotalTextCmdPerTic(tic_t tic);
 
 void PT_TextCmd(SINT8 node, INT32 netconsole);
 void SendKick(UINT8 playernum, UINT8 msg);
+void SendKicksForNode(SINT8 node, UINT8 msg);
 
 #endif
diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h
index cbeb37f36c..374cc5bffd 100644
--- a/src/netcode/protocol.h
+++ b/src/netcode/protocol.h
@@ -85,7 +85,6 @@ typedef enum
 	PT_TEXTCMD,       // Extra text commands from the client.
 	PT_TEXTCMD2,      // Splitscreen text commands.
 	PT_CLIENTJOIN,    // Client wants to join; used in start game.
-	PT_NODETIMEOUT,   // Packet sent to self if the connection times out.
 
 	PT_LOGIN,         // Login attempt from the client.
 
-- 
GitLab


From 79356fe6280d95828c27479301425410156edc11 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 12 Jan 2023 19:40:31 +0100
Subject: [PATCH 281/518] Move netgame commands to a new file

---
 src/netcode/Sourcefile |   1 +
 src/netcode/commands.c | 489 +++++++++++++++++++++++++++++++++++++++++
 src/netcode/commands.h |  33 +++
 src/netcode/d_clisrv.c | 460 +-------------------------------------
 src/netcode/d_net.h    |   1 -
 5 files changed, 524 insertions(+), 460 deletions(-)
 create mode 100644 src/netcode/commands.c
 create mode 100644 src/netcode/commands.h

diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile
index c590503678..7c0354714f 100644
--- a/src/netcode/Sourcefile
+++ b/src/netcode/Sourcefile
@@ -4,6 +4,7 @@ client_connection.c
 tic_command.c
 net_command.c
 gamestate.c
+commands.c
 d_net.c
 d_netcmd.c
 d_netfil.c
diff --git a/src/netcode/commands.c b/src/netcode/commands.c
new file mode 100644
index 0000000000..834e1c6668
--- /dev/null
+++ b/src/netcode/commands.c
@@ -0,0 +1,489 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  commands.c
+/// \brief Various netgame commands, such as kick and ban
+
+#include "commands.h"
+#include "d_clisrv.h"
+#include "client_connection.h"
+#include "net_command.h"
+#include "d_netcmd.h"
+#include "d_net.h"
+#include "i_net.h"
+#include "protocol.h"
+#include "../byteptr.h"
+#include "../d_main.h"
+#include "../g_game.h"
+#include "../w_wad.h"
+#include "../z_zone.h"
+#include "../doomstat.h"
+#include "../doomdef.h"
+#include "../r_local.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+typedef struct banreason_s
+{
+	char *reason;
+	struct banreason_s *prev; //-1
+	struct banreason_s *next; //+1
+} banreason_t;
+
+static banreason_t *reasontail = NULL; //last entry, use prev
+static banreason_t *reasonhead = NULL; //1st entry, use next
+
+void Ban_Add(const char *reason)
+{
+	banreason_t *reasonlist = malloc(sizeof(*reasonlist));
+
+	if (!reasonlist)
+		return;
+	if (!reason)
+		reason = "NA";
+
+	reasonlist->next = NULL;
+	reasonlist->reason = Z_StrDup(reason);
+	if ((reasonlist->prev = reasontail) == NULL)
+		reasonhead = reasonlist;
+	else
+		reasontail->next = reasonlist;
+	reasontail = reasonlist;
+}
+
+static void Ban_Clear(void)
+{
+	banreason_t *temp;
+
+	I_ClearBans();
+
+	reasontail = NULL;
+
+	while (reasonhead)
+	{
+		temp = reasonhead->next;
+		Z_Free(reasonhead->reason);
+		free(reasonhead);
+		reasonhead = temp;
+	}
+}
+
+void Ban_Load_File(boolean warning)
+{
+	FILE *f;
+	size_t i;
+	const char *address, *mask;
+	char buffer[MAX_WADPATH];
+
+	if (!I_ClearBans)
+		return;
+
+	f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r");
+
+	if (!f)
+	{
+		if (warning)
+			CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n"));
+		return;
+	}
+
+	Ban_Clear();
+
+	for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++)
+	{
+		address = strtok(buffer, " \t\r\n");
+		mask = strtok(NULL, " \t\r\n");
+
+		I_SetBanAddress(address, mask);
+
+		Ban_Add(strtok(NULL, "\r\n"));
+	}
+
+	fclose(f);
+}
+
+void D_SaveBan(void)
+{
+	FILE *f;
+	size_t i;
+	banreason_t *reasonlist = reasonhead;
+	const char *address, *mask;
+	const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt");
+
+	if (!reasonhead)
+	{
+		remove(path);
+		return;
+	}
+
+	f = fopen(path, "w");
+
+	if (!f)
+	{
+		CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n"));
+		return;
+	}
+
+	for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
+	{
+		if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
+			fprintf(f, "%s 0", address);
+		else
+			fprintf(f, "%s %s", address, mask);
+
+		if (reasonlist && reasonlist->reason)
+			fprintf(f, " %s\n", reasonlist->reason);
+		else
+			fprintf(f, " %s\n", "NA");
+
+		if (reasonlist) reasonlist = reasonlist->next;
+	}
+
+	fclose(f);
+}
+
+void Command_ShowBan(void) //Print out ban list
+{
+	size_t i;
+	const char *address, *mask;
+	banreason_t *reasonlist = reasonhead;
+
+	if (I_GetBanAddress)
+		CONS_Printf(M_GetText("Ban List:\n"));
+	else
+		return;
+
+	for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
+	{
+		if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
+			CONS_Printf("%s: %s ", sizeu1(i+1), address);
+		else
+			CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask);
+
+		if (reasonlist && reasonlist->reason)
+			CONS_Printf("(%s)\n", reasonlist->reason);
+		else
+			CONS_Printf("\n");
+
+		if (reasonlist) reasonlist = reasonlist->next;
+	}
+
+	if (i == 0 && !address)
+		CONS_Printf(M_GetText("(empty)\n"));
+}
+
+void Command_ClearBans(void)
+{
+	if (!I_ClearBans)
+		return;
+
+	Ban_Clear();
+	D_SaveBan();
+}
+
+void Command_Ban(void)
+{
+	if (COM_Argc() < 2)
+	{
+		CONS_Printf(M_GetText("Ban <playername/playernum> <reason>: ban and kick a player\n"));
+		return;
+	}
+
+	if (!netgame) // Don't kick Tails in splitscreen!
+	{
+		CONS_Printf(M_GetText("This only works in a netgame.\n"));
+		return;
+	}
+
+	if (server || IsPlayerAdmin(consoleplayer))
+	{
+		UINT8 buf[3 + MAX_REASONLENGTH];
+		UINT8 *p = buf;
+		const SINT8 pn = nametonum(COM_Argv(1));
+		const INT32 node = playernode[(INT32)pn];
+
+		if (pn == -1 || pn == 0)
+			return;
+
+		WRITEUINT8(p, pn);
+
+		if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now
+		{
+			CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
+			WRITEUINT8(p, KICK_MSG_GO_AWAY);
+			SendNetXCmd(XD_KICK, &buf, 2);
+		}
+		else
+		{
+			if (server) // only the server is allowed to do this right now
+			{
+				Ban_Add(COM_Argv(2));
+				D_SaveBan(); // save the ban list
+			}
+
+			if (COM_Argc() == 2)
+			{
+				WRITEUINT8(p, KICK_MSG_BANNED);
+				SendNetXCmd(XD_KICK, &buf, 2);
+			}
+			else
+			{
+				size_t i, j = COM_Argc();
+				char message[MAX_REASONLENGTH];
+
+				//Steal from the motd code so you don't have to put the reason in quotes.
+				strlcpy(message, COM_Argv(2), sizeof message);
+				for (i = 3; i < j; i++)
+				{
+					strlcat(message, " ", sizeof message);
+					strlcat(message, COM_Argv(i), sizeof message);
+				}
+
+				WRITEUINT8(p, KICK_MSG_CUSTOM_BAN);
+				WRITESTRINGN(p, message, MAX_REASONLENGTH);
+				SendNetXCmd(XD_KICK, &buf, p - buf);
+			}
+		}
+	}
+	else
+		CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
+
+}
+
+void Command_BanIP(void)
+{
+	if (COM_Argc() < 2)
+	{
+		CONS_Printf(M_GetText("banip <ip> <reason>: ban an ip address\n"));
+		return;
+	}
+
+	if (server) // Only the server can use this, otherwise does nothing.
+	{
+		const char *address = (COM_Argv(1));
+		const char *reason;
+
+		if (COM_Argc() == 2)
+			reason = NULL;
+		else
+			reason = COM_Argv(2);
+
+
+		if (I_SetBanAddress && I_SetBanAddress(address, NULL))
+		{
+			if (reason)
+				CONS_Printf("Banned IP address %s for: %s\n", address, reason);
+			else
+				CONS_Printf("Banned IP address %s\n", address);
+
+			Ban_Add(reason);
+			D_SaveBan();
+		}
+		else
+		{
+			return;
+		}
+	}
+}
+
+void Command_ReloadBan(void)  //recheck ban.txt
+{
+	Ban_Load_File(true);
+}
+
+void Command_Kick(void)
+{
+	if (COM_Argc() < 2)
+	{
+		CONS_Printf(M_GetText("kick <playername/playernum> <reason>: kick a player\n"));
+		return;
+	}
+
+	if (!netgame) // Don't kick Tails in splitscreen!
+	{
+		CONS_Printf(M_GetText("This only works in a netgame.\n"));
+		return;
+	}
+
+	if (server || IsPlayerAdmin(consoleplayer))
+	{
+		UINT8 buf[3 + MAX_REASONLENGTH];
+		UINT8 *p = buf;
+		const SINT8 pn = nametonum(COM_Argv(1));
+
+		if (pn == -1 || pn == 0)
+			return;
+
+		// Special case if we are trying to kick a player who is downloading the game state:
+		// trigger a timeout instead of kicking them, because a kick would only
+		// take effect after they have finished downloading
+		if (server && playernode[pn] != UINT8_MAX && netnodes[playernode[pn]].sendingsavegame)
+		{
+			Net_ConnectionTimeout(playernode[pn]);
+			return;
+		}
+
+		WRITESINT8(p, pn);
+
+		if (COM_Argc() == 2)
+		{
+			WRITEUINT8(p, KICK_MSG_GO_AWAY);
+			SendNetXCmd(XD_KICK, &buf, 2);
+		}
+		else
+		{
+			size_t i, j = COM_Argc();
+			char message[MAX_REASONLENGTH];
+
+			//Steal from the motd code so you don't have to put the reason in quotes.
+			strlcpy(message, COM_Argv(2), sizeof message);
+			for (i = 3; i < j; i++)
+			{
+				strlcat(message, " ", sizeof message);
+				strlcat(message, COM_Argv(i), sizeof message);
+			}
+
+			WRITEUINT8(p, KICK_MSG_CUSTOM_KICK);
+			WRITESTRINGN(p, message, MAX_REASONLENGTH);
+			SendNetXCmd(XD_KICK, &buf, p - buf);
+		}
+	}
+	else
+		CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
+}
+
+void Command_connect(void)
+{
+	if (COM_Argc() < 2 || *COM_Argv(1) == 0)
+	{
+		CONS_Printf(M_GetText(
+			"Connect <serveraddress> (port): connect to a server\n"
+			"Connect ANY: connect to the first lan server found\n"
+			//"Connect SELF: connect to your own server.\n"
+			));
+		return;
+	}
+
+	if (Playing() || titledemo)
+	{
+		CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n"));
+		return;
+	}
+
+	server = false;
+/*
+	if (!stricmp(COM_Argv(1), "self"))
+	{
+		servernode = 0;
+		server = true;
+		/// \bug should be but...
+		//SV_SpawnServer();
+	}
+	else
+*/
+	{
+		// used in menu to connect to a server in the list
+		if (netgame && !stricmp(COM_Argv(1), "node"))
+		{
+			servernode = (SINT8)atoi(COM_Argv(2));
+		}
+		else if (netgame)
+		{
+			CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n"));
+			return;
+		}
+		else if (I_NetOpenSocket)
+		{
+			I_NetOpenSocket();
+			netgame = true;
+			multiplayer = true;
+
+			if (!stricmp(COM_Argv(1), "any"))
+				servernode = BROADCASTADDR;
+			else if (I_NetMakeNodewPort)
+			{
+				if (COM_Argc() >= 3) // address AND port
+					servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2));
+				else // address only, or address:port
+					servernode = I_NetMakeNode(COM_Argv(1));
+			}
+			else
+			{
+				CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n"));
+				D_CloseConnection();
+				return;
+			}
+		}
+		else
+			CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n"));
+	}
+
+	splitscreen = false;
+	SplitScreen_OnChange();
+	botingame = false;
+	botskin = 0;
+	CL_ConnectToServer();
+}
+
+void Command_GetPlayerNum(void)
+{
+	INT32 i;
+
+	for (i = 0; i < MAXPLAYERS; i++)
+		if (playeringame[i])
+		{
+			if (serverplayer == i)
+				CONS_Printf(M_GetText("num:%2d  node:%2d  %s\n"), i, playernode[i], player_names[i]);
+			else
+				CONS_Printf(M_GetText("\x82num:%2d  node:%2d  %s\n"), i, playernode[i], player_names[i]);
+		}
+}
+
+/** Lists all players and their player numbers.
+  *
+  * \sa Command_GetPlayerNum
+  */
+void Command_Nodes(void)
+{
+	INT32 i;
+	size_t maxlen = 0;
+	const char *address;
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		const size_t plen = strlen(player_names[i]);
+		if (playeringame[i] && plen > maxlen)
+			maxlen = plen;
+	}
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (playeringame[i])
+		{
+			CONS_Printf("%.2u: %*s", i, (int)maxlen, player_names[i]);
+
+			if (playernode[i] != UINT8_MAX)
+			{
+				CONS_Printf(" - node %.2d", playernode[i]);
+				if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL)
+					CONS_Printf(" - %s", address);
+			}
+
+			if (IsPlayerAdmin(i))
+				CONS_Printf(M_GetText(" (verified admin)"));
+
+			if (players[i].spectator)
+				CONS_Printf(M_GetText(" (spectator)"));
+
+			CONS_Printf("\n");
+		}
+	}
+}
diff --git a/src/netcode/commands.h b/src/netcode/commands.h
new file mode 100644
index 0000000000..5ff4d1cae6
--- /dev/null
+++ b/src/netcode/commands.h
@@ -0,0 +1,33 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2022 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  commands.h
+/// \brief Various netgame commands, such as kick and ban
+
+#ifndef __COMMANDS__
+#define __COMMANDS__
+
+#include "../doomdef.h"
+
+#define MAX_REASONLENGTH 30
+
+void Ban_Add(const char *reason);
+void D_SaveBan(void);
+void Ban_Load_File(boolean warning);
+void Command_ShowBan(void);
+void Command_ClearBans(void);
+void Command_Ban(void);
+void Command_BanIP(void);
+void Command_ReloadBan(void);
+void Command_Kick(void);
+void Command_connect(void);
+void Command_GetPlayerNum(void);
+void Command_Nodes(void);
+
+#endif
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 81d7e55435..506de8c028 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -54,6 +54,7 @@
 #include "tic_command.h"
 #include "net_command.h"
 #include "gamestate.h"
+#include "commands.h"
 #include "protocol.h"
 
 //
@@ -69,8 +70,6 @@
 //   firstticstosend is used to optimize a condition
 // Normally maketic >= gametic > 0
 
-#define MAX_REASONLENGTH 30
-
 boolean server = true; // true or false but !server == client
 boolean serverrunning = false;
 INT32 serverplayer = 0;
@@ -103,16 +102,6 @@ boolean acceptnewnode = true;
 
 UINT16 software_MAXPACKETLENGTH;
 
-typedef struct banreason_s
-{
-	char *reason;
-	struct banreason_s *prev; //-1
-	struct banreason_s *next; //+1
-} banreason_t;
-
-static banreason_t *reasontail = NULL; //last entry, use prev
-static banreason_t *reasonhead = NULL; //1st entry, use next
-
 static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}};
 consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL);
 
@@ -124,232 +113,6 @@ consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_
 static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}};
 consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL);
 
-static void Command_ShowBan(void) //Print out ban list
-{
-	size_t i;
-	const char *address, *mask;
-	banreason_t *reasonlist = reasonhead;
-
-	if (I_GetBanAddress)
-		CONS_Printf(M_GetText("Ban List:\n"));
-	else
-		return;
-
-	for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
-	{
-		if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
-			CONS_Printf("%s: %s ", sizeu1(i+1), address);
-		else
-			CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask);
-
-		if (reasonlist && reasonlist->reason)
-			CONS_Printf("(%s)\n", reasonlist->reason);
-		else
-			CONS_Printf("\n");
-
-		if (reasonlist) reasonlist = reasonlist->next;
-	}
-
-	if (i == 0 && !address)
-		CONS_Printf(M_GetText("(empty)\n"));
-}
-
-void D_SaveBan(void)
-{
-	FILE *f;
-	size_t i;
-	banreason_t *reasonlist = reasonhead;
-	const char *address, *mask;
-	const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt");
-
-	if (!reasonhead)
-	{
-		remove(path);
-		return;
-	}
-
-	f = fopen(path, "w");
-
-	if (!f)
-	{
-		CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n"));
-		return;
-	}
-
-	for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
-	{
-		if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
-			fprintf(f, "%s 0", address);
-		else
-			fprintf(f, "%s %s", address, mask);
-
-		if (reasonlist && reasonlist->reason)
-			fprintf(f, " %s\n", reasonlist->reason);
-		else
-			fprintf(f, " %s\n", "NA");
-
-		if (reasonlist) reasonlist = reasonlist->next;
-	}
-
-	fclose(f);
-}
-
-static void Ban_Add(const char *reason)
-{
-	banreason_t *reasonlist = malloc(sizeof(*reasonlist));
-
-	if (!reasonlist)
-		return;
-	if (!reason)
-		reason = "NA";
-
-	reasonlist->next = NULL;
-	reasonlist->reason = Z_StrDup(reason);
-	if ((reasonlist->prev = reasontail) == NULL)
-		reasonhead = reasonlist;
-	else
-		reasontail->next = reasonlist;
-	reasontail = reasonlist;
-}
-
-static void Ban_Clear(void)
-{
-	banreason_t *temp;
-
-	I_ClearBans();
-
-	reasontail = NULL;
-
-	while (reasonhead)
-	{
-		temp = reasonhead->next;
-		Z_Free(reasonhead->reason);
-		free(reasonhead);
-		reasonhead = temp;
-	}
-}
-
-static void Command_ClearBans(void)
-{
-	if (!I_ClearBans)
-		return;
-
-	Ban_Clear();
-	D_SaveBan();
-}
-
-static void Ban_Load_File(boolean warning)
-{
-	FILE *f;
-	size_t i;
-	const char *address, *mask;
-	char buffer[MAX_WADPATH];
-
-	if (!I_ClearBans)
-		return;
-
-	f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r");
-
-	if (!f)
-	{
-		if (warning)
-			CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n"));
-		return;
-	}
-
-	Ban_Clear();
-
-	for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++)
-	{
-		address = strtok(buffer, " \t\r\n");
-		mask = strtok(NULL, " \t\r\n");
-
-		I_SetBanAddress(address, mask);
-
-		Ban_Add(strtok(NULL, "\r\n"));
-	}
-
-	fclose(f);
-}
-
-static void Command_ReloadBan(void)  //recheck ban.txt
-{
-	Ban_Load_File(true);
-}
-
-static void Command_connect(void)
-{
-	if (COM_Argc() < 2 || *COM_Argv(1) == 0)
-	{
-		CONS_Printf(M_GetText(
-			"Connect <serveraddress> (port): connect to a server\n"
-			"Connect ANY: connect to the first lan server found\n"
-			//"Connect SELF: connect to your own server.\n"
-			));
-		return;
-	}
-
-	if (Playing() || titledemo)
-	{
-		CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n"));
-		return;
-	}
-
-	server = false;
-/*
-	if (!stricmp(COM_Argv(1), "self"))
-	{
-		servernode = 0;
-		server = true;
-		/// \bug should be but...
-		//SV_SpawnServer();
-	}
-	else
-*/
-	{
-		// used in menu to connect to a server in the list
-		if (netgame && !stricmp(COM_Argv(1), "node"))
-		{
-			servernode = (SINT8)atoi(COM_Argv(2));
-		}
-		else if (netgame)
-		{
-			CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n"));
-			return;
-		}
-		else if (I_NetOpenSocket)
-		{
-			I_NetOpenSocket();
-			netgame = true;
-			multiplayer = true;
-
-			if (!stricmp(COM_Argv(1), "any"))
-				servernode = BROADCASTADDR;
-			else if (I_NetMakeNodewPort)
-			{
-				if (COM_Argc() >= 3) // address AND port
-					servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2));
-				else // address only, or address:port
-					servernode = I_NetMakeNode(COM_Argv(1));
-			}
-			else
-			{
-				CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n"));
-				D_CloseConnection();
-				return;
-			}
-		}
-		else
-			CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n"));
-	}
-
-	splitscreen = false;
-	SplitScreen_OnChange();
-	botingame = false;
-	botskin = 0;
-	CL_ConnectToServer();
-}
-
 void ResetNode(INT32 node)
 {
 	memset(&netnodes[node], 0, sizeof(*netnodes));
@@ -527,20 +290,6 @@ void CL_Reset(void)
 	// D_StartTitle should get done now, but the calling function will handle it
 }
 
-static void Command_GetPlayerNum(void)
-{
-	INT32 i;
-
-	for (i = 0; i < MAXPLAYERS; i++)
-		if (playeringame[i])
-		{
-			if (serverplayer == i)
-				CONS_Printf(M_GetText("num:%2d  node:%2d  %s\n"), i, playernode[i], player_names[i]);
-			else
-				CONS_Printf(M_GetText("\x82num:%2d  node:%2d  %s\n"), i, playernode[i], player_names[i]);
-		}
-}
-
 SINT8 nametonum(const char *name)
 {
 	INT32 playernum, i;
@@ -570,213 +319,6 @@ SINT8 nametonum(const char *name)
 	return -1;
 }
 
-/** Lists all players and their player numbers.
-  *
-  * \sa Command_GetPlayerNum
-  */
-static void Command_Nodes(void)
-{
-	INT32 i;
-	size_t maxlen = 0;
-	const char *address;
-
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		const size_t plen = strlen(player_names[i]);
-		if (playeringame[i] && plen > maxlen)
-			maxlen = plen;
-	}
-
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (playeringame[i])
-		{
-			CONS_Printf("%.2u: %*s", i, (int)maxlen, player_names[i]);
-
-			if (playernode[i] != UINT8_MAX)
-			{
-				CONS_Printf(" - node %.2d", playernode[i]);
-				if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL)
-					CONS_Printf(" - %s", address);
-			}
-
-			if (IsPlayerAdmin(i))
-				CONS_Printf(M_GetText(" (verified admin)"));
-
-			if (players[i].spectator)
-				CONS_Printf(M_GetText(" (spectator)"));
-
-			CONS_Printf("\n");
-		}
-	}
-}
-
-static void Command_Ban(void)
-{
-	if (COM_Argc() < 2)
-	{
-		CONS_Printf(M_GetText("Ban <playername/playernum> <reason>: ban and kick a player\n"));
-		return;
-	}
-
-	if (!netgame) // Don't kick Tails in splitscreen!
-	{
-		CONS_Printf(M_GetText("This only works in a netgame.\n"));
-		return;
-	}
-
-	if (server || IsPlayerAdmin(consoleplayer))
-	{
-		UINT8 buf[3 + MAX_REASONLENGTH];
-		UINT8 *p = buf;
-		const SINT8 pn = nametonum(COM_Argv(1));
-		const INT32 node = playernode[(INT32)pn];
-
-		if (pn == -1 || pn == 0)
-			return;
-
-		WRITEUINT8(p, pn);
-
-		if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now
-		{
-			CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
-			WRITEUINT8(p, KICK_MSG_GO_AWAY);
-			SendNetXCmd(XD_KICK, &buf, 2);
-		}
-		else
-		{
-			if (server) // only the server is allowed to do this right now
-			{
-				Ban_Add(COM_Argv(2));
-				D_SaveBan(); // save the ban list
-			}
-
-			if (COM_Argc() == 2)
-			{
-				WRITEUINT8(p, KICK_MSG_BANNED);
-				SendNetXCmd(XD_KICK, &buf, 2);
-			}
-			else
-			{
-				size_t i, j = COM_Argc();
-				char message[MAX_REASONLENGTH];
-
-				//Steal from the motd code so you don't have to put the reason in quotes.
-				strlcpy(message, COM_Argv(2), sizeof message);
-				for (i = 3; i < j; i++)
-				{
-					strlcat(message, " ", sizeof message);
-					strlcat(message, COM_Argv(i), sizeof message);
-				}
-
-				WRITEUINT8(p, KICK_MSG_CUSTOM_BAN);
-				WRITESTRINGN(p, message, MAX_REASONLENGTH);
-				SendNetXCmd(XD_KICK, &buf, p - buf);
-			}
-		}
-	}
-	else
-		CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
-
-}
-
-static void Command_BanIP(void)
-{
-	if (COM_Argc() < 2)
-	{
-		CONS_Printf(M_GetText("banip <ip> <reason>: ban an ip address\n"));
-		return;
-	}
-
-	if (server) // Only the server can use this, otherwise does nothing.
-	{
-		const char *address = (COM_Argv(1));
-		const char *reason;
-
-		if (COM_Argc() == 2)
-			reason = NULL;
-		else
-			reason = COM_Argv(2);
-
-
-		if (I_SetBanAddress && I_SetBanAddress(address, NULL))
-		{
-			if (reason)
-				CONS_Printf("Banned IP address %s for: %s\n", address, reason);
-			else
-				CONS_Printf("Banned IP address %s\n", address);
-
-			Ban_Add(reason);
-			D_SaveBan();
-		}
-		else
-		{
-			return;
-		}
-	}
-}
-
-static void Command_Kick(void)
-{
-	if (COM_Argc() < 2)
-	{
-		CONS_Printf(M_GetText("kick <playername/playernum> <reason>: kick a player\n"));
-		return;
-	}
-
-	if (!netgame) // Don't kick Tails in splitscreen!
-	{
-		CONS_Printf(M_GetText("This only works in a netgame.\n"));
-		return;
-	}
-
-	if (server || IsPlayerAdmin(consoleplayer))
-	{
-		UINT8 buf[3 + MAX_REASONLENGTH];
-		UINT8 *p = buf;
-		const SINT8 pn = nametonum(COM_Argv(1));
-
-		if (pn == -1 || pn == 0)
-			return;
-
-		// Special case if we are trying to kick a player who is downloading the game state:
-		// trigger a timeout instead of kicking them, because a kick would only
-		// take effect after they have finished downloading
-		if (server && playernode[pn] != UINT8_MAX && netnodes[playernode[pn]].sendingsavegame)
-		{
-			Net_ConnectionTimeout(playernode[pn]);
-			return;
-		}
-
-		WRITESINT8(p, pn);
-
-		if (COM_Argc() == 2)
-		{
-			WRITEUINT8(p, KICK_MSG_GO_AWAY);
-			SendNetXCmd(XD_KICK, &buf, 2);
-		}
-		else
-		{
-			size_t i, j = COM_Argc();
-			char message[MAX_REASONLENGTH];
-
-			//Steal from the motd code so you don't have to put the reason in quotes.
-			strlcpy(message, COM_Argv(2), sizeof message);
-			for (i = 3; i < j; i++)
-			{
-				strlcat(message, " ", sizeof message);
-				strlcat(message, COM_Argv(i), sizeof message);
-			}
-
-			WRITEUINT8(p, KICK_MSG_CUSTOM_KICK);
-			WRITESTRINGN(p, message, MAX_REASONLENGTH);
-			SendNetXCmd(XD_KICK, &buf, p - buf);
-		}
-	}
-	else
-		CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
-}
-
 static void Got_KickCmd(UINT8 **p, INT32 playernum)
 {
 	INT32 pnum, msg;
diff --git a/src/netcode/d_net.h b/src/netcode/d_net.h
index 857c3463c9..039f5b3b48 100644
--- a/src/netcode/d_net.h
+++ b/src/netcode/d_net.h
@@ -68,7 +68,6 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum,
 	size_t packetlength);
 boolean HGetPacket(void);
 void D_SetDoomcom(void);
-void D_SaveBan(void);
 boolean D_CheckNetGame(void);
 void D_CloseConnection(void);
 void Net_UnAcknowledgePacket(INT32 node);
-- 
GitLab


From 2c6bb7172678ac54ccbdd2eabc0cd0c9a00ea726 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 13 Jan 2023 22:05:57 +0100
Subject: [PATCH 282/518] Reorder functions in d_clisrv.c

---
 src/netcode/d_clisrv.c | 1489 ++++++++++++++++++++--------------------
 1 file changed, 743 insertions(+), 746 deletions(-)

diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 506de8c028..1658856b68 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -83,6 +83,7 @@ UINT8 playernode[MAXPLAYERS];
 UINT16 pingmeasurecount = 1;
 UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone.
 UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values.
+static INT32 pingtimeout[MAXPLAYERS];
 tic_t servermaxping = 800; // server's max ping. Defaults to 800
 
 tic_t maketic;
@@ -120,137 +121,6 @@ void ResetNode(INT32 node)
 	netnodes[node].player2 = -1;
 }
 
-//
-// CL_ClearPlayer
-//
-// Clears the player data so that a future client can use this slot
-//
-void CL_ClearPlayer(INT32 playernum)
-{
-	if (players[playernum].mo)
-		P_RemoveMobj(players[playernum].mo);
-	memset(&players[playernum], 0, sizeof (player_t));
-	memset(playeraddress[playernum], 0, sizeof(*playeraddress));
-}
-
-static void UnlinkPlayerFromNode(INT32 playernum)
-{
-	INT32 node = playernode[playernum];
-
-	if (node == UINT8_MAX)
-		return;
-
-	playernode[playernum] = UINT8_MAX;
-
-	netnodes[node].numplayers--;
-	if (netnodes[node].numplayers <= 0)
-	{
-		netnodes[node].ingame = false;
-		Net_CloseConnection(node);
-		ResetNode(node);
-	}
-}
-
-// If in a special stage, redistribute the player's
-// spheres across the remaining players.
-// I feel like this shouldn't even be in this file at all, but well.
-static void RedistributeSpecialStageSpheres(INT32 playernum)
-{
-	if (!G_IsSpecialStage(gamemap) || D_NumPlayers() <= 1)
-		return;
-
-	INT32 count = D_NumPlayers() - 1;
-	INT32 spheres = players[playernum].spheres;
-	INT32 rings = players[playernum].rings;
-
-	while (spheres || rings)
-	{
-		INT32 sincrement = max(spheres / count, 1);
-		INT32 rincrement = max(rings / count, 1);
-
-		INT32 i, n;
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (!playeringame[i] || i == playernum)
-				continue;
-
-			n = min(spheres, sincrement);
-			P_GivePlayerSpheres(&players[i], n);
-			spheres -= n;
-
-			n = min(rings, rincrement);
-			P_GivePlayerRings(&players[i], n);
-			rings -= n;
-		}
-	}
-}
-
-//
-// CL_RemovePlayer
-//
-// Removes a player from the current game
-//
-void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
-{
-	// Sanity check: exceptional cases (i.e. c-fails) can cause multiple
-	// kick commands to be issued for the same player.
-	if (!playeringame[playernum])
-		return;
-
-	if (server)
-		UnlinkPlayerFromNode(playernum);
-
-	if (gametyperules & GTR_TEAMFLAGS)
-		P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you!
-
-	RedistributeSpecialStageSpheres(playernum);
-
-	LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting
-
-	// don't look through someone's view who isn't there
-	if (playernum == displayplayer)
-	{
-		// Call ViewpointSwitch hooks here.
-		// The viewpoint was forcibly changed.
-		LUA_HookViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true);
-		displayplayer = consoleplayer;
-	}
-
-	// Reset player data
-	CL_ClearPlayer(playernum);
-
-	// remove avatar of player
-	playeringame[playernum] = false;
-	while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1)
-		doomcom->numslots--;
-
-	// Reset the name
-	sprintf(player_names[playernum], "Player %d", playernum+1);
-
-	player_name_changes[playernum] = 0;
-
-	if (IsPlayerAdmin(playernum))
-	{
-		RemoveAdminPlayer(playernum); // don't stay admin after you're gone
-	}
-
-	LUA_InvalidatePlayer(&players[playernum]);
-
-	if (G_TagGametype()) //Check if you still have a game. Location flexible. =P
-		P_CheckSurvivors();
-	else if (gametyperules & GTR_RACE)
-		P_CheckRacers();
-}
-
-void CL_HandleTimeout(void)
-{
-	LUA_HookBool(false, HOOK(GameQuit));
-	D_QuitNetGame();
-	CL_Reset();
-	D_StartTitle();
-	M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
-}
-
 void CL_Reset(void)
 {
 	if (metalrecording)
@@ -290,93 +160,241 @@ void CL_Reset(void)
 	// D_StartTitle should get done now, but the calling function will handle it
 }
 
-SINT8 nametonum(const char *name)
+//
+// CL_ClearPlayer
+//
+// Clears the player data so that a future client can use this slot
+//
+void CL_ClearPlayer(INT32 playernum)
 {
-	INT32 playernum, i;
-
-	if (!strcmp(name, "0"))
-		return 0;
-
-	playernum = (SINT8)atoi(name);
-
-	if (playernum < 0 || playernum >= MAXPLAYERS)
-		return -1;
-
-	if (playernum)
-	{
-		if (playeringame[playernum])
-			return (SINT8)playernum;
-		else
-			return -1;
-	}
-
-	for (i = 0; i < MAXPLAYERS; i++)
-		if (playeringame[i] && !stricmp(player_names[i], name))
-			return (SINT8)i;
-
-	CONS_Printf(M_GetText("There is no player named \"%s\"\n"), name);
-
-	return -1;
+	if (players[playernum].mo)
+		P_RemoveMobj(players[playernum].mo);
+	memset(&players[playernum], 0, sizeof (player_t));
+	memset(playeraddress[playernum], 0, sizeof(*playeraddress));
 }
 
-static void Got_KickCmd(UINT8 **p, INT32 playernum)
+// Xcmd XD_ADDPLAYER
+static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 {
-	INT32 pnum, msg;
-	char buf[3 + MAX_REASONLENGTH];
-	char *reason = buf;
-	kickreason_t kickreason = KR_KICK;
-	boolean keepbody;
-
-	pnum = READUINT8(*p);
-	msg = READUINT8(*p);
-	keepbody = (msg & KICK_MSG_KEEP_BODY) != 0;
-	msg &= ~KICK_MSG_KEEP_BODY;
+	INT16 node, newplayernum;
+	boolean splitscreenplayer;
+	boolean rejoined;
+	player_t *newplayer;
 
-	if (pnum == serverplayer && IsPlayerAdmin(playernum))
+	if (playernum != serverplayer && !IsPlayerAdmin(playernum))
 	{
-		CONS_Printf(M_GetText("Server is being shut down remotely. Goodbye!\n"));
-
+		// protect against hacked/buggy client
+		CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]);
 		if (server)
-			COM_BufAddText("quit\n");
-
+			SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
 		return;
 	}
 
-	// Is playernum authorized to make this kick?
-	if (playernum != serverplayer && !IsPlayerAdmin(playernum)
-		&& !(playernode[playernum] != UINT8_MAX && netnodes[playernode[playernum]].numplayers == 2
-		&& netnodes[playernode[playernum]].player2 == pnum))
+	node = READUINT8(*p);
+	newplayernum = READUINT8(*p);
+	splitscreenplayer = newplayernum & 0x80;
+	newplayernum &= ~0x80;
+
+	rejoined = playeringame[newplayernum];
+
+	if (!rejoined)
 	{
-		// We received a kick command from someone who isn't the
-		// server or admin, and who isn't in splitscreen removing
-		// player 2. Thus, it must be someone with a modified
-		// binary, trying to kick someone but without having
-		// authorization.
+		// Clear player before joining, lest some things get set incorrectly
+		// HACK: don't do this for splitscreen, it relies on preset values
+		if (!splitscreen && !botingame)
+			CL_ClearPlayer(newplayernum);
+		playeringame[newplayernum] = true;
+		G_AddPlayer(newplayernum);
+		if (newplayernum+1 > doomcom->numslots)
+			doomcom->numslots = (INT16)(newplayernum+1);
 
-		// We deal with this by changing the kick reason to
-		// "consistency failure" and kicking the offending user
-		// instead.
+		if (server && I_GetNodeAddress)
+		{
+			const char *address = I_GetNodeAddress(node);
+			char *port = NULL;
+			if (address) // MI: fix msvcrt.dll!_mbscat crash?
+			{
+				strcpy(playeraddress[newplayernum], address);
+				port = strchr(playeraddress[newplayernum], ':');
+				if (port)
+					*port = '\0';
+			}
+		}
+	}
 
-		// Note: Splitscreen in netgames is broken because of
-		// this. Only the server has any idea of which players
-		// are using splitscreen on the same computer, so
-		// clients cannot always determine if a kick is
-		// legitimate.
+	newplayer = &players[newplayernum];
 
-		CONS_Alert(CONS_WARNING, M_GetText("Illegal kick command received from %s for player %d\n"), player_names[playernum], pnum);
+	newplayer->jointime = 0;
+	newplayer->quittime = 0;
 
-		// In debug, print a longer message with more details.
-		// TODO Callum: Should we translate this?
-/*
-		CONS_Debug(DBG_NETPLAY,
-			"So, you must be asking, why is this an illegal kick?\n"
-			"Well, let's take a look at the facts, shall we?\n"
-			"\n"
-			"playernum (this is the guy who did it), he's %d.\n"
-			"pnum (the guy he's trying to kick) is %d.\n"
-			"playernum's node is %d.\n"
-			"That node has %d players.\n"
-			"Player 2 on that node is %d.\n"
+	READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
+
+	// the server is creating my player
+	if (node == mynode)
+	{
+		playernode[newplayernum] = 0; // for information only
+		if (!splitscreenplayer)
+		{
+			consoleplayer = newplayernum;
+			displayplayer = newplayernum;
+			secondarydisplayplayer = newplayernum;
+			DEBFILE("spawning me\n");
+			ticcmd_oldangleturn[0] = newplayer->oldrelangleturn;
+		}
+		else
+		{
+			secondarydisplayplayer = newplayernum;
+			DEBFILE("spawning my brother\n");
+			if (botingame)
+				newplayer->bot = 1;
+			ticcmd_oldangleturn[1] = newplayer->oldrelangleturn;
+		}
+		P_ForceLocalAngle(newplayer, (angle_t)(newplayer->angleturn << 16));
+		D_SendPlayerConfig();
+		addedtogame = true;
+
+		if (rejoined)
+		{
+			if (newplayer->mo)
+			{
+				newplayer->viewheight = 41*newplayer->height/48;
+
+				if (newplayer->mo->eflags & MFE_VERTICALFLIP)
+					newplayer->viewz = newplayer->mo->z + newplayer->mo->height - newplayer->viewheight;
+				else
+					newplayer->viewz = newplayer->mo->z + newplayer->viewheight;
+			}
+
+			// wake up the status bar
+			ST_Start();
+			// wake up the heads up text
+			HU_Start();
+
+			if (camera.chase && !splitscreenplayer)
+				P_ResetCamera(newplayer, &camera);
+			if (camera2.chase && splitscreenplayer)
+				P_ResetCamera(newplayer, &camera2);
+		}
+	}
+
+	if (netgame)
+	{
+		char joinmsg[256];
+
+		if (rejoined)
+			strcpy(joinmsg, M_GetText("\x82*%s has rejoined the game (player %d)"));
+		else
+			strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)"));
+		strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum));
+
+		// Merge join notification + IP to avoid clogging console/chat
+		if (server && cv_showjoinaddress.value && I_GetNodeAddress)
+		{
+			const char *address = I_GetNodeAddress(node);
+			if (address)
+				strcat(joinmsg, va(" (%s)", address));
+		}
+
+		HU_AddChatText(joinmsg, false);
+	}
+
+	if (server && multiplayer && motd[0] != '\0')
+		COM_BufAddText(va("sayto %d %s\n", newplayernum, motd));
+
+	if (!rejoined)
+		LUA_HookInt(newplayernum, HOOK(PlayerJoin));
+}
+
+static void UnlinkPlayerFromNode(INT32 playernum)
+{
+	INT32 node = playernode[playernum];
+
+	if (node == UINT8_MAX)
+		return;
+
+	playernode[playernum] = UINT8_MAX;
+
+	netnodes[node].numplayers--;
+	if (netnodes[node].numplayers <= 0)
+	{
+		netnodes[node].ingame = false;
+		Net_CloseConnection(node);
+		ResetNode(node);
+	}
+}
+
+static void PT_ClientQuit(SINT8 node, INT32 netconsole)
+{
+	if (client)
+		return;
+
+	if (netnodes[node].ingame && netconsole != -1 && playeringame[netconsole])
+		SendKicksForNode(node, KICK_MSG_PLAYER_QUIT | KICK_MSG_KEEP_BODY);
+
+	Net_CloseConnection(node);
+	netnodes[node].ingame = false;
+	netnodes[node].player = -1;
+	netnodes[node].player2 = -1;
+}
+
+static void Got_KickCmd(UINT8 **p, INT32 playernum)
+{
+	INT32 pnum, msg;
+	char buf[3 + MAX_REASONLENGTH];
+	char *reason = buf;
+	kickreason_t kickreason = KR_KICK;
+	boolean keepbody;
+
+	pnum = READUINT8(*p);
+	msg = READUINT8(*p);
+	keepbody = (msg & KICK_MSG_KEEP_BODY) != 0;
+	msg &= ~KICK_MSG_KEEP_BODY;
+
+	if (pnum == serverplayer && IsPlayerAdmin(playernum))
+	{
+		CONS_Printf(M_GetText("Server is being shut down remotely. Goodbye!\n"));
+
+		if (server)
+			COM_BufAddText("quit\n");
+
+		return;
+	}
+
+	// Is playernum authorized to make this kick?
+	if (playernum != serverplayer && !IsPlayerAdmin(playernum)
+		&& !(playernode[playernum] != UINT8_MAX && netnodes[playernode[playernum]].numplayers == 2
+		&& netnodes[playernode[playernum]].player2 == pnum))
+	{
+		// We received a kick command from someone who isn't the
+		// server or admin, and who isn't in splitscreen removing
+		// player 2. Thus, it must be someone with a modified
+		// binary, trying to kick someone but without having
+		// authorization.
+
+		// We deal with this by changing the kick reason to
+		// "consistency failure" and kicking the offending user
+		// instead.
+
+		// Note: Splitscreen in netgames is broken because of
+		// this. Only the server has any idea of which players
+		// are using splitscreen on the same computer, so
+		// clients cannot always determine if a kick is
+		// legitimate.
+
+		CONS_Alert(CONS_WARNING, M_GetText("Illegal kick command received from %s for player %d\n"), player_names[playernum], pnum);
+
+		// In debug, print a longer message with more details.
+		// TODO Callum: Should we translate this?
+/*
+		CONS_Debug(DBG_NETPLAY,
+			"So, you must be asking, why is this an illegal kick?\n"
+			"Well, let's take a look at the facts, shall we?\n"
+			"\n"
+			"playernum (this is the guy who did it), he's %d.\n"
+			"pnum (the guy he's trying to kick) is %d.\n"
+			"playernum's node is %d.\n"
+			"That node has %d players.\n"
+			"Player 2 on that node is %d.\n"
 			"pnum's node is %d.\n"
 			"That node has %d players.\n"
 			"Player 2 on that node is %d.\n"
@@ -510,116 +528,95 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 		CL_RemovePlayer(pnum, kickreason);
 }
 
-static void Got_AddPlayer(UINT8 **p, INT32 playernum);
-
-// called one time at init
-void D_ClientServerInit(void)
+// If in a special stage, redistribute the player's
+// spheres across the remaining players.
+// I feel like this shouldn't even be in this file at all, but well.
+static void RedistributeSpecialStageSpheres(INT32 playernum)
 {
-	DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n",
-		VERSION/100, VERSION%100, SUBVERSION));
+	if (!G_IsSpecialStage(gamemap) || D_NumPlayers() <= 1)
+		return;
 
-	COM_AddCommand("getplayernum", Command_GetPlayerNum);
-	COM_AddCommand("kick", Command_Kick);
-	COM_AddCommand("ban", Command_Ban);
-	COM_AddCommand("banip", Command_BanIP);
-	COM_AddCommand("clearbans", Command_ClearBans);
-	COM_AddCommand("showbanlist", Command_ShowBan);
-	COM_AddCommand("reloadbans", Command_ReloadBan);
-	COM_AddCommand("connect", Command_connect);
-	COM_AddCommand("nodes", Command_Nodes);
-	COM_AddCommand("resendgamestate", Command_ResendGamestate);
-#ifdef PACKETDROP
-	COM_AddCommand("drop", Command_Drop);
-	COM_AddCommand("droprate", Command_Droprate);
-#endif
-#ifdef _DEBUG
-	COM_AddCommand("numnodes", Command_Numnodes);
-#endif
+	INT32 count = D_NumPlayers() - 1;
+	INT32 spheres = players[playernum].spheres;
+	INT32 rings = players[playernum].rings;
 
-	RegisterNetXCmd(XD_KICK, Got_KickCmd);
-	RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer);
-#ifdef DUMPCONSISTENCY
-	CV_RegisterVar(&cv_dumpconsistency);
-#endif
-	Ban_Load_File(false);
+	while (spheres || rings)
+	{
+		INT32 sincrement = max(spheres / count, 1);
+		INT32 rincrement = max(rings / count, 1);
 
-	gametic = 0;
-	localgametic = 0;
+		INT32 i, n;
+		for (i = 0; i < MAXPLAYERS; i++)
+		{
+			if (!playeringame[i] || i == playernum)
+				continue;
 
-	// do not send anything before the real begin
-	SV_StopServer();
-	SV_ResetServer();
-	if (dedicated)
-		SV_SpawnServer();
+			n = min(spheres, sincrement);
+			P_GivePlayerSpheres(&players[i], n);
+			spheres -= n;
+
+			n = min(rings, rincrement);
+			P_GivePlayerRings(&players[i], n);
+			rings -= n;
+		}
+	}
 }
 
-void SV_ResetServer(void)
+//
+// CL_RemovePlayer
+//
+// Removes a player from the current game
+//
+void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
 {
-	INT32 i;
-
-	// +1 because this command will be executed in com_executebuffer in
-	// tryruntic so gametic will be incremented, anyway maketic > gametic
-	// is not an issue
-
-	maketic = gametic + 1;
-	neededtic = maketic;
-	tictoclear = maketic;
-
-	joindelay = 0;
+	// Sanity check: exceptional cases (i.e. c-fails) can cause multiple
+	// kick commands to be issued for the same player.
+	if (!playeringame[playernum])
+		return;
 
-	for (i = 0; i < MAXNETNODES; i++)
-		ResetNode(i);
+	if (server)
+		UnlinkPlayerFromNode(playernum);
 
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		LUA_InvalidatePlayer(&players[i]);
-		playeringame[i] = false;
-		playernode[i] = UINT8_MAX;
-		memset(playeraddress[i], 0, sizeof(*playeraddress));
-		sprintf(player_names[i], "Player %d", i + 1);
-		adminplayers[i] = -1; // Populate the entire adminplayers array with -1.
-	}
+	if (gametyperules & GTR_TEAMFLAGS)
+		P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you!
 
-	memset(player_name_changes, 0, sizeof player_name_changes);
+	RedistributeSpecialStageSpheres(playernum);
 
-	mynode = 0;
-	cl_packetmissed = false;
-	cl_redownloadinggamestate = false;
+	LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting
 
-	if (dedicated)
+	// don't look through someone's view who isn't there
+	if (playernum == displayplayer)
 	{
-		netnodes[0].ingame = true;
-		serverplayer = 0;
+		// Call ViewpointSwitch hooks here.
+		// The viewpoint was forcibly changed.
+		LUA_HookViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true);
+		displayplayer = consoleplayer;
 	}
-	else
-		serverplayer = consoleplayer;
-
-	if (server)
-		servernode = 0;
 
-	doomcom->numslots = 0;
+	// Reset player data
+	CL_ClearPlayer(playernum);
 
-	// clear server_context
-	memset(server_context, '-', 8);
+	// remove avatar of player
+	playeringame[playernum] = false;
+	while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1)
+		doomcom->numslots--;
 
-	CV_RevertNetVars();
+	// Reset the name
+	sprintf(player_names[playernum], "Player %d", playernum+1);
 
-	DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
-}
+	player_name_changes[playernum] = 0;
 
-static inline void SV_GenContext(void)
-{
-	UINT8 i;
-	// generate server_context, as exactly 8 bytes of randomly mixed A-Z and a-z
-	// (hopefully M_Random is initialized!! if not this will be awfully silly!)
-	for (i = 0; i < 8; i++)
+	if (IsPlayerAdmin(playernum))
 	{
-		const char a = M_RandomKey(26*2);
-		if (a < 26) // uppercase
-			server_context[i] = 'A'+a;
-		else // lowercase
-			server_context[i] = 'a'+(a-26);
+		RemoveAdminPlayer(playernum); // don't stay admin after you're gone
 	}
+
+	LUA_InvalidatePlayer(&players[playernum]);
+
+	if (G_TagGametype()) //Check if you still have a game. Location flexible. =P
+		P_CheckSurvivors();
+	else if (gametyperules & GTR_RACE)
+		P_CheckRacers();
 }
 
 //
@@ -633,182 +630,59 @@ void D_QuitNetGame(void)
 	I_UpdateMouseGrab();
 
 	if (!netgame || !netbuffer)
-		return;
-
-	DEBFILE("===========================================================================\n"
-	        "                  Quitting Game, closing connection\n"
-	        "===========================================================================\n");
-
-	// abort send/receive of files
-	CloseNetFile();
-	RemoveAllLuaFileTransfers();
-	waitingforluafiletransfer = false;
-	waitingforluafilecommand = false;
-
-	if (server)
-	{
-		INT32 i;
-
-		netbuffer->packettype = PT_SERVERSHUTDOWN;
-		for (i = 0; i < MAXNETNODES; i++)
-			if (netnodes[i].ingame)
-				HSendPacket(i, true, 0, 0);
-#ifdef MASTERSERVER
-		if (serverrunning && ms_RoomId > 0)
-			UnregisterServer();
-#endif
-	}
-	else if (servernode > 0 && servernode < MAXNETNODES && netnodes[(UINT8)servernode].ingame)
-	{
-		netbuffer->packettype = PT_CLIENTQUIT;
-		HSendPacket(servernode, true, 0, 0);
-	}
-
-	D_CloseConnection();
-	ClearAdminPlayers();
-
-	DEBFILE("===========================================================================\n"
-	        "                         Log finish\n"
-	        "===========================================================================\n");
-#ifdef DEBUGFILE
-	if (debugfile)
-	{
-		fclose(debugfile);
-		debugfile = NULL;
-	}
-#endif
-}
-
-// Xcmd XD_ADDPLAYER
-static void Got_AddPlayer(UINT8 **p, INT32 playernum)
-{
-	INT16 node, newplayernum;
-	boolean splitscreenplayer;
-	boolean rejoined;
-	player_t *newplayer;
-
-	if (playernum != serverplayer && !IsPlayerAdmin(playernum))
-	{
-		// protect against hacked/buggy client
-		CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]);
-		if (server)
-			SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
-		return;
-	}
-
-	node = READUINT8(*p);
-	newplayernum = READUINT8(*p);
-	splitscreenplayer = newplayernum & 0x80;
-	newplayernum &= ~0x80;
-
-	rejoined = playeringame[newplayernum];
-
-	if (!rejoined)
-	{
-		// Clear player before joining, lest some things get set incorrectly
-		// HACK: don't do this for splitscreen, it relies on preset values
-		if (!splitscreen && !botingame)
-			CL_ClearPlayer(newplayernum);
-		playeringame[newplayernum] = true;
-		G_AddPlayer(newplayernum);
-		if (newplayernum+1 > doomcom->numslots)
-			doomcom->numslots = (INT16)(newplayernum+1);
-
-		if (server && I_GetNodeAddress)
-		{
-			const char *address = I_GetNodeAddress(node);
-			char *port = NULL;
-			if (address) // MI: fix msvcrt.dll!_mbscat crash?
-			{
-				strcpy(playeraddress[newplayernum], address);
-				port = strchr(playeraddress[newplayernum], ':');
-				if (port)
-					*port = '\0';
-			}
-		}
-	}
-
-	newplayer = &players[newplayernum];
-
-	newplayer->jointime = 0;
-	newplayer->quittime = 0;
-
-	READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
-
-	// the server is creating my player
-	if (node == mynode)
-	{
-		playernode[newplayernum] = 0; // for information only
-		if (!splitscreenplayer)
-		{
-			consoleplayer = newplayernum;
-			displayplayer = newplayernum;
-			secondarydisplayplayer = newplayernum;
-			DEBFILE("spawning me\n");
-			ticcmd_oldangleturn[0] = newplayer->oldrelangleturn;
-		}
-		else
-		{
-			secondarydisplayplayer = newplayernum;
-			DEBFILE("spawning my brother\n");
-			if (botingame)
-				newplayer->bot = 1;
-			ticcmd_oldangleturn[1] = newplayer->oldrelangleturn;
-		}
-		P_ForceLocalAngle(newplayer, (angle_t)(newplayer->angleturn << 16));
-		D_SendPlayerConfig();
-		addedtogame = true;
-
-		if (rejoined)
-		{
-			if (newplayer->mo)
-			{
-				newplayer->viewheight = 41*newplayer->height/48;
-
-				if (newplayer->mo->eflags & MFE_VERTICALFLIP)
-					newplayer->viewz = newplayer->mo->z + newplayer->mo->height - newplayer->viewheight;
-				else
-					newplayer->viewz = newplayer->mo->z + newplayer->viewheight;
-			}
-
-			// wake up the status bar
-			ST_Start();
-			// wake up the heads up text
-			HU_Start();
+		return;
 
-			if (camera.chase && !splitscreenplayer)
-				P_ResetCamera(newplayer, &camera);
-			if (camera2.chase && splitscreenplayer)
-				P_ResetCamera(newplayer, &camera2);
-		}
-	}
+	DEBFILE("===========================================================================\n"
+	        "                  Quitting Game, closing connection\n"
+	        "===========================================================================\n");
 
-	if (netgame)
+	// abort send/receive of files
+	CloseNetFile();
+	RemoveAllLuaFileTransfers();
+	waitingforluafiletransfer = false;
+	waitingforluafilecommand = false;
+
+	if (server)
 	{
-		char joinmsg[256];
+		INT32 i;
 
-		if (rejoined)
-			strcpy(joinmsg, M_GetText("\x82*%s has rejoined the game (player %d)"));
-		else
-			strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)"));
-		strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum));
+		netbuffer->packettype = PT_SERVERSHUTDOWN;
+		for (i = 0; i < MAXNETNODES; i++)
+			if (netnodes[i].ingame)
+				HSendPacket(i, true, 0, 0);
+#ifdef MASTERSERVER
+		if (serverrunning && ms_RoomId > 0)
+			UnregisterServer();
+#endif
+	}
+	else if (servernode > 0 && servernode < MAXNETNODES && netnodes[(UINT8)servernode].ingame)
+	{
+		netbuffer->packettype = PT_CLIENTQUIT;
+		HSendPacket(servernode, true, 0, 0);
+	}
 
-		// Merge join notification + IP to avoid clogging console/chat
-		if (server && cv_showjoinaddress.value && I_GetNodeAddress)
-		{
-			const char *address = I_GetNodeAddress(node);
-			if (address)
-				strcat(joinmsg, va(" (%s)", address));
-		}
+	D_CloseConnection();
+	ClearAdminPlayers();
 
-		HU_AddChatText(joinmsg, false);
+	DEBFILE("===========================================================================\n"
+	        "                         Log finish\n"
+	        "===========================================================================\n");
+#ifdef DEBUGFILE
+	if (debugfile)
+	{
+		fclose(debugfile);
+		debugfile = NULL;
 	}
+#endif
+}
 
-	if (server && multiplayer && motd[0] != '\0')
-		COM_BufAddText(va("sayto %d %s\n", newplayernum, motd));
-
-	if (!rejoined)
-		LUA_HookInt(newplayernum, HOOK(PlayerJoin));
+void CL_HandleTimeout(void)
+{
+	LUA_HookBool(false, HOOK(GameQuit));
+	D_QuitNetGame();
+	CL_Reset();
+	D_StartTitle();
+	M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
 }
 
 void CL_AddSplitscreenPlayer(void)
@@ -825,10 +699,73 @@ void CL_RemoveSplitscreenPlayer(void)
 	SendKick(secondarydisplayplayer, KICK_MSG_PLAYER_QUIT);
 }
 
-// is there a game running
-boolean Playing(void)
+void SV_ResetServer(void)
 {
-	return (server && serverrunning) || (client && cl_mode == CL_CONNECTED);
+	INT32 i;
+
+	// +1 because this command will be executed in com_executebuffer in
+	// tryruntic so gametic will be incremented, anyway maketic > gametic
+	// is not an issue
+
+	maketic = gametic + 1;
+	neededtic = maketic;
+	tictoclear = maketic;
+
+	joindelay = 0;
+
+	for (i = 0; i < MAXNETNODES; i++)
+		ResetNode(i);
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		LUA_InvalidatePlayer(&players[i]);
+		playeringame[i] = false;
+		playernode[i] = UINT8_MAX;
+		memset(playeraddress[i], 0, sizeof(*playeraddress));
+		sprintf(player_names[i], "Player %d", i + 1);
+		adminplayers[i] = -1; // Populate the entire adminplayers array with -1.
+	}
+
+	memset(player_name_changes, 0, sizeof player_name_changes);
+
+	mynode = 0;
+	cl_packetmissed = false;
+	cl_redownloadinggamestate = false;
+
+	if (dedicated)
+	{
+		netnodes[0].ingame = true;
+		serverplayer = 0;
+	}
+	else
+		serverplayer = consoleplayer;
+
+	if (server)
+		servernode = 0;
+
+	doomcom->numslots = 0;
+
+	// clear server_context
+	memset(server_context, '-', 8);
+
+	CV_RevertNetVars();
+
+	DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
+}
+
+static inline void SV_GenContext(void)
+{
+	UINT8 i;
+	// generate server_context, as exactly 8 bytes of randomly mixed A-Z and a-z
+	// (hopefully M_Random is initialized!! if not this will be awfully silly!)
+	for (i = 0; i < 8; i++)
+	{
+		const char a = M_RandomKey(26*2);
+		if (a < 26) // uppercase
+			server_context[i] = 'A'+a;
+		else // lowercase
+			server_context[i] = 'a'+(a-26);
+	}
 }
 
 void SV_SpawnServer(void)
@@ -860,6 +797,21 @@ void SV_SpawnServer(void)
 	}
 }
 
+// called at singleplayer start and stopdemo
+void SV_StartSinglePlayerServer(void)
+{
+	server = true;
+	netgame = false;
+	multiplayer = false;
+	G_SetGametype(GT_COOP);
+
+	// no more tic the game with this settings!
+	SV_StopServer();
+
+	if (splitscreen)
+		multiplayer = true;
+}
+
 void SV_StopServer(void)
 {
 	tic_t i;
@@ -881,21 +833,6 @@ void SV_StopServer(void)
 	serverrunning = false;
 }
 
-// called at singleplayer start and stopdemo
-void SV_StartSinglePlayerServer(void)
-{
-	server = true;
-	netgame = false;
-	multiplayer = false;
-	G_SetGametype(GT_COOP);
-
-	// no more tic the game with this settings!
-	SV_StopServer();
-
-	if (splitscreen)
-		multiplayer = true;
-}
-
 /** Called when a PT_SERVERSHUTDOWN packet is received
   *
   * \param node The packet sender (should be the server)
@@ -933,45 +870,119 @@ static void PT_Login(SINT8 node, INT32 netconsole)
 		return;
 	}
 
-	// Do the final pass to compare with the sent md5
-	D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5);
-
-	if (!memcmp(netbuffer->u.md5sum, finalmd5, 16))
+	// Do the final pass to compare with the sent md5
+	D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5);
+
+	if (!memcmp(netbuffer->u.md5sum, finalmd5, 16))
+	{
+		CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]);
+		COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately
+	}
+	else
+		CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]);
+#else
+	(void)netconsole;
+#endif
+}
+
+static void PT_AskLuaFile(SINT8 node)
+{
+	if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED)
+		AddLuaFileToSendQueue(node, luafiletransfers->realfilename);
+}
+
+static void PT_HasLuaFile(SINT8 node)
+{
+	if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING)
+		SV_HandleLuaFileSent(node);
+}
+
+static void PT_SendingLuaFile(SINT8 node)
+{
+	(void)node;
+
+	if (client)
+		CL_PrepareDownloadLuaFile();
+}
+
+/*
+Ping Update except better:
+We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
+If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
+*/
+
+static inline void PingUpdate(void)
+{
+	INT32 i;
+	boolean laggers[MAXPLAYERS];
+	UINT8 numlaggers = 0;
+	memset(laggers, 0, sizeof(boolean) * MAXPLAYERS);
+
+	netbuffer->packettype = PT_PING;
+
+	//check for ping limit breakage.
+	if (cv_maxping.value)
+	{
+		for (i = 1; i < MAXPLAYERS; i++)
+		{
+			if (playeringame[i] && !players[i].quittime
+			&& (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value))
+			{
+				if (players[i].jointime > 30 * TICRATE)
+					laggers[i] = true;
+				numlaggers++;
+			}
+			else
+				pingtimeout[i] = 0;
+		}
+
+		//kick lagging players... unless everyone but the server's ping sucks.
+		//in that case, it is probably the server's fault.
+		if (numlaggers < D_NumPlayers() - 1)
+		{
+			for (i = 1; i < MAXPLAYERS; i++)
+			{
+				if (playeringame[i] && laggers[i])
+				{
+					pingtimeout[i]++;
+					// ok your net has been bad for too long, you deserve to die.
+					if (pingtimeout[i] > cv_pingtimeout.value)
+					{
+						pingtimeout[i] = 0;
+						SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY);
+					}
+				}
+				/*
+					you aren't lagging,
+					but you aren't free yet.
+					In case you'll keep spiking,
+					we just make the timer go back down. (Very unstable net must still get kicked).
+				*/
+				else
+					pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1);
+			}
+		}
+	}
+
+	//make the ping packet and clear server data for next one
+	for (i = 0; i < MAXPLAYERS; i++)
 	{
-		CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]);
-		COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately
+		netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount;
+		//server takes a snapshot of the real ping for display.
+		//otherwise, pings fluctuate a lot and would be odd to look at.
+		playerpingtable[i] = realpingtable[i] / pingmeasurecount;
+		realpingtable[i] = 0; //Reset each as we go.
 	}
-	else
-		CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]);
-#else
-	(void)netconsole;
-#endif
-}
-
-static void PT_ClientQuit(SINT8 node, INT32 netconsole)
-{
-	if (client)
-		return;
 
-	if (netnodes[node].ingame && netconsole != -1 && playeringame[netconsole])
-		SendKicksForNode(node, KICK_MSG_PLAYER_QUIT | KICK_MSG_KEEP_BODY);
-
-	Net_CloseConnection(node);
-	netnodes[node].ingame = false;
-	netnodes[node].player = -1;
-	netnodes[node].player2 = -1;
-}
+	// send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked.
+	netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value;
 
-static void PT_AskLuaFile(SINT8 node)
-{
-	if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED)
-		AddLuaFileToSendQueue(node, luafiletransfers->realfilename);
-}
+	//send out our ping packets
+	for (i = 0; i < MAXNETNODES; i++)
+		if (netnodes[i].ingame)
+			HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1));
 
-static void PT_HasLuaFile(SINT8 node)
-{
-	if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING)
-		SV_HandleLuaFileSent(node);
+	pingmeasurecount = 1; //Reset count
 }
 
 static void PT_Ping(SINT8 node, INT32 netconsole)
@@ -997,14 +1008,6 @@ static void PT_Ping(SINT8 node, INT32 netconsole)
 	}
 }
 
-static void PT_SendingLuaFile(SINT8 node)
-{
-	(void)node;
-
-	if (client)
-		CL_PrepareDownloadLuaFile();
-}
-
 /** Handles a packet received from a node that isn't in game
   *
   * \param node The packet sender
@@ -1124,120 +1127,6 @@ void GetPackets(void)
 	}
 }
 
-//
-// NetUpdate
-// 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.
-//
-INT16 Consistancy(void)
-{
-	INT32 i;
-	UINT32 ret = 0;
-#ifdef MOBJCONSISTANCY
-	thinker_t *th;
-	mobj_t *mo;
-#endif
-
-	DEBFILE(va("TIC %u ", gametic));
-
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (!playeringame[i])
-			ret ^= 0xCCCC;
-		else if (!players[i].mo);
-		else
-		{
-			ret += players[i].mo->x;
-			ret -= players[i].mo->y;
-			ret += players[i].powers[pw_shield];
-			ret *= i+1;
-		}
-	}
-	// I give up
-	// Coop desynching enemies is painful
-	if (!G_PlatformGametype())
-		ret += P_GetRandSeed();
-
-#ifdef MOBJCONSISTANCY
-	if (gamestate == GS_LEVEL)
-	{
-		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
-		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
-				continue;
-
-			mo = (mobj_t *)th;
-
-			if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY))
-			{
-				ret -= mo->type;
-				ret += mo->x;
-				ret -= mo->y;
-				ret += mo->z;
-				ret -= mo->momx;
-				ret += mo->momy;
-				ret -= mo->momz;
-				ret += mo->angle;
-				ret -= mo->flags;
-				ret += mo->flags2;
-				ret -= mo->eflags;
-				if (mo->target)
-				{
-					ret += mo->target->type;
-					ret -= mo->target->x;
-					ret += mo->target->y;
-					ret -= mo->target->z;
-					ret += mo->target->momx;
-					ret -= mo->target->momy;
-					ret += mo->target->momz;
-					ret -= mo->target->angle;
-					ret += mo->target->flags;
-					ret -= mo->target->flags2;
-					ret += mo->target->eflags;
-					ret -= mo->target->state - states;
-					ret += mo->target->tics;
-					ret -= mo->target->sprite;
-					ret += mo->target->frame;
-				}
-				else
-					ret ^= 0x3333;
-				if (mo->tracer && mo->tracer->type != MT_OVERLAY)
-				{
-					ret += mo->tracer->type;
-					ret -= mo->tracer->x;
-					ret += mo->tracer->y;
-					ret -= mo->tracer->z;
-					ret += mo->tracer->momx;
-					ret -= mo->tracer->momy;
-					ret += mo->tracer->momz;
-					ret -= mo->tracer->angle;
-					ret += mo->tracer->flags;
-					ret -= mo->tracer->flags2;
-					ret += mo->tracer->eflags;
-					ret -= mo->tracer->state - states;
-					ret += mo->tracer->tics;
-					ret -= mo->tracer->sprite;
-					ret += mo->tracer->frame;
-				}
-				else
-					ret ^= 0xAAAA;
-				ret -= mo->state - states;
-				ret += mo->tics;
-				ret -= mo->sprite;
-				ret += mo->frame;
-			}
-		}
-	}
-#endif
-
-	DEBFILE(va("Consistancy = %u\n", (ret & 0xFFFF)));
-
-	return (INT16)(ret & 0xFFFF);
-}
-
 boolean TryRunTics(tic_t realtics)
 {
 	// the machine has lagged but it is not so bad
@@ -1291,126 +1180,44 @@ boolean TryRunTics(tic_t realtics)
 			if (timedemo_quit)
 				COM_ImmedExecute("quit");
 			else
-				D_StartTitle();
-		}
-		else
-			// run the count * tics
-			while (neededtic > gametic)
-			{
-				boolean update_stats = !(paused || P_AutoPause());
-
-				DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic));
-
-				if (update_stats)
-					PS_START_TIMING(ps_tictime);
-
-				G_Ticker((gametic % NEWTICRATERATIO) == 0);
-				ExtraDataTicker();
-				gametic++;
-				consistancy[gametic%BACKUPTICS] = Consistancy();
-
-				if (update_stats)
-				{
-					PS_STOP_TIMING(ps_tictime);
-					PS_UpdateTickStats();
-				}
-
-				// Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame.
-				if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value)
-					break;
-			}
-
-		return true;
-	}
-	else
-	{
-		if (realtics)
-			hu_stopped = true;
-
-		return false;
-	}
-}
-
-/*
-Ping Update except better:
-We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
-If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
-*/
-
-static INT32 pingtimeout[MAXPLAYERS];
-
-static inline void PingUpdate(void)
-{
-	INT32 i;
-	boolean laggers[MAXPLAYERS];
-	UINT8 numlaggers = 0;
-	memset(laggers, 0, sizeof(boolean) * MAXPLAYERS);
-
-	netbuffer->packettype = PT_PING;
-
-	//check for ping limit breakage.
-	if (cv_maxping.value)
-	{
-		for (i = 1; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i] && !players[i].quittime
-			&& (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value))
-			{
-				if (players[i].jointime > 30 * TICRATE)
-					laggers[i] = true;
-				numlaggers++;
-			}
-			else
-				pingtimeout[i] = 0;
-		}
-
-		//kick lagging players... unless everyone but the server's ping sucks.
-		//in that case, it is probably the server's fault.
-		if (numlaggers < D_NumPlayers() - 1)
-		{
-			for (i = 1; i < MAXPLAYERS; i++)
+				D_StartTitle();
+		}
+		else
+			// run the count * tics
+			while (neededtic > gametic)
 			{
-				if (playeringame[i] && laggers[i])
+				boolean update_stats = !(paused || P_AutoPause());
+
+				DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic));
+
+				if (update_stats)
+					PS_START_TIMING(ps_tictime);
+
+				G_Ticker((gametic % NEWTICRATERATIO) == 0);
+				ExtraDataTicker();
+				gametic++;
+				consistancy[gametic%BACKUPTICS] = Consistancy();
+
+				if (update_stats)
 				{
-					pingtimeout[i]++;
-					// ok your net has been bad for too long, you deserve to die.
-					if (pingtimeout[i] > cv_pingtimeout.value)
-					{
-						pingtimeout[i] = 0;
-						SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY);
-					}
+					PS_STOP_TIMING(ps_tictime);
+					PS_UpdateTickStats();
 				}
-				/*
-					you aren't lagging,
-					but you aren't free yet.
-					In case you'll keep spiking,
-					we just make the timer go back down. (Very unstable net must still get kicked).
-				*/
-				else
-					pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1);
+
+				// Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame.
+				if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value)
+					break;
 			}
-		}
-	}
 
-	//make the ping packet and clear server data for next one
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount;
-		//server takes a snapshot of the real ping for display.
-		//otherwise, pings fluctuate a lot and would be odd to look at.
-		playerpingtable[i] = realpingtable[i] / pingmeasurecount;
-		realpingtable[i] = 0; //Reset each as we go.
+		return true;
 	}
+	else
+	{
+		if (realtics)
+			hu_stopped = true;
 
-	// send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked.
-	netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value;
-
-	//send out our ping packets
-	for (i = 0; i < MAXNETNODES; i++)
-		if (netnodes[i].ingame)
-			HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1));
-
-	pingmeasurecount = 1; //Reset count
+		return false;
+	}
 }
 
 void NetUpdate(void)
@@ -1537,6 +1344,82 @@ void NetUpdate(void)
 	FileSendTicker();
 }
 
+// called one time at init
+void D_ClientServerInit(void)
+{
+	DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n",
+		VERSION/100, VERSION%100, SUBVERSION));
+
+	COM_AddCommand("getplayernum", Command_GetPlayerNum);
+	COM_AddCommand("kick", Command_Kick);
+	COM_AddCommand("ban", Command_Ban);
+	COM_AddCommand("banip", Command_BanIP);
+	COM_AddCommand("clearbans", Command_ClearBans);
+	COM_AddCommand("showbanlist", Command_ShowBan);
+	COM_AddCommand("reloadbans", Command_ReloadBan);
+	COM_AddCommand("connect", Command_connect);
+	COM_AddCommand("nodes", Command_Nodes);
+	COM_AddCommand("resendgamestate", Command_ResendGamestate);
+#ifdef PACKETDROP
+	COM_AddCommand("drop", Command_Drop);
+	COM_AddCommand("droprate", Command_Droprate);
+#endif
+#ifdef _DEBUG
+	COM_AddCommand("numnodes", Command_Numnodes);
+#endif
+
+	RegisterNetXCmd(XD_KICK, Got_KickCmd);
+	RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer);
+#ifdef DUMPCONSISTENCY
+	CV_RegisterVar(&cv_dumpconsistency);
+#endif
+	Ban_Load_File(false);
+
+	gametic = 0;
+	localgametic = 0;
+
+	// do not send anything before the real begin
+	SV_StopServer();
+	SV_ResetServer();
+	if (dedicated)
+		SV_SpawnServer();
+}
+
+SINT8 nametonum(const char *name)
+{
+	INT32 playernum, i;
+
+	if (!strcmp(name, "0"))
+		return 0;
+
+	playernum = (SINT8)atoi(name);
+
+	if (playernum < 0 || playernum >= MAXPLAYERS)
+		return -1;
+
+	if (playernum)
+	{
+		if (playeringame[playernum])
+			return (SINT8)playernum;
+		else
+			return -1;
+	}
+
+	for (i = 0; i < MAXPLAYERS; i++)
+		if (playeringame[i] && !stricmp(player_names[i], name))
+			return (SINT8)i;
+
+	CONS_Printf(M_GetText("There is no player named \"%s\"\n"), name);
+
+	return -1;
+}
+
+// is there a game running
+boolean Playing(void)
+{
+	return (server && serverrunning) || (client && cl_mode == CL_CONNECTED);
+}
+
 /** Returns the number of players playing.
   * \return Number of players. Can be zero if we're running a ::dedicated
   *         server.
@@ -1551,6 +1434,120 @@ INT32 D_NumPlayers(void)
 	return num;
 }
 
+//
+// NetUpdate
+// 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.
+//
+INT16 Consistancy(void)
+{
+	INT32 i;
+	UINT32 ret = 0;
+#ifdef MOBJCONSISTANCY
+	thinker_t *th;
+	mobj_t *mo;
+#endif
+
+	DEBFILE(va("TIC %u ", gametic));
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i])
+			ret ^= 0xCCCC;
+		else if (!players[i].mo);
+		else
+		{
+			ret += players[i].mo->x;
+			ret -= players[i].mo->y;
+			ret += players[i].powers[pw_shield];
+			ret *= i+1;
+		}
+	}
+	// I give up
+	// Coop desynching enemies is painful
+	if (!G_PlatformGametype())
+		ret += P_GetRandSeed();
+
+#ifdef MOBJCONSISTANCY
+	if (gamestate == GS_LEVEL)
+	{
+		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
+		{
+			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+				continue;
+
+			mo = (mobj_t *)th;
+
+			if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY))
+			{
+				ret -= mo->type;
+				ret += mo->x;
+				ret -= mo->y;
+				ret += mo->z;
+				ret -= mo->momx;
+				ret += mo->momy;
+				ret -= mo->momz;
+				ret += mo->angle;
+				ret -= mo->flags;
+				ret += mo->flags2;
+				ret -= mo->eflags;
+				if (mo->target)
+				{
+					ret += mo->target->type;
+					ret -= mo->target->x;
+					ret += mo->target->y;
+					ret -= mo->target->z;
+					ret += mo->target->momx;
+					ret -= mo->target->momy;
+					ret += mo->target->momz;
+					ret -= mo->target->angle;
+					ret += mo->target->flags;
+					ret -= mo->target->flags2;
+					ret += mo->target->eflags;
+					ret -= mo->target->state - states;
+					ret += mo->target->tics;
+					ret -= mo->target->sprite;
+					ret += mo->target->frame;
+				}
+				else
+					ret ^= 0x3333;
+				if (mo->tracer && mo->tracer->type != MT_OVERLAY)
+				{
+					ret += mo->tracer->type;
+					ret -= mo->tracer->x;
+					ret += mo->tracer->y;
+					ret -= mo->tracer->z;
+					ret += mo->tracer->momx;
+					ret -= mo->tracer->momy;
+					ret += mo->tracer->momz;
+					ret -= mo->tracer->angle;
+					ret += mo->tracer->flags;
+					ret -= mo->tracer->flags2;
+					ret += mo->tracer->eflags;
+					ret -= mo->tracer->state - states;
+					ret += mo->tracer->tics;
+					ret -= mo->tracer->sprite;
+					ret += mo->tracer->frame;
+				}
+				else
+					ret ^= 0xAAAA;
+				ret -= mo->state - states;
+				ret += mo->tics;
+				ret -= mo->sprite;
+				ret += mo->frame;
+			}
+		}
+	}
+#endif
+
+	DEBFILE(va("Consistancy = %u\n", (ret & 0xFFFF)));
+
+	return (INT16)(ret & 0xFFFF);
+}
+
 tic_t GetLag(INT32 node)
 {
 	return gametic - netnodes[node].tic;
-- 
GitLab


From 684382fc5e18de501b88ab292b1a7c81bbc78843 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 13 Jan 2023 22:28:03 +0100
Subject: [PATCH 283/518] Add missing include directive

---
 src/sdl/i_system.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 50ceae34c2..38769e6676 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -194,6 +194,7 @@ static char returnWadPath[256];
 #include "../i_threads.h"
 #include "../screen.h" //vid.WndParent
 #include "../netcode/d_net.h"
+#include "../netcode/commands.h"
 #include "../g_game.h"
 #include "../filesrch.h"
 #include "endtxt.h"
-- 
GitLab


From 715893ad25ba2c5077de8c60b9516edd822da954 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 14 Jan 2023 14:49:33 +0100
Subject: [PATCH 284/518] Split PT_ClientCmd into functions

---
 src/netcode/tic_command.c | 95 +++++++++++++++++++++------------------
 1 file changed, 51 insertions(+), 44 deletions(-)

diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
index bab0fad0a6..417a2fdfdf 100644
--- a/src/netcode/tic_command.c
+++ b/src/netcode/tic_command.c
@@ -103,6 +103,55 @@ void D_ResetTiccmds(void)
 			D_Clearticcmd(textcmds[i]->tic);
 }
 
+// Check ticcmd for "speed hacks"
+static void CheckTiccmdHacks(INT32 playernum)
+{
+	ticcmd_t *cmd = &netcmds[maketic%BACKUPTICS][playernum];
+	if (cmd->forwardmove > MAXPLMOVE || cmd->forwardmove < -MAXPLMOVE
+		|| cmd->sidemove > MAXPLMOVE || cmd->sidemove < -MAXPLMOVE)
+	{
+		CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), playernum);
+		SendKick(playernum, KICK_MSG_CON_FAIL);
+	}
+}
+
+// Check player consistancy during the level
+static void CheckConsistancy(SINT8 nodenum, tic_t tic)
+{
+	netnode_t *node = &netnodes[nodenum];
+	INT16 neededconsistancy = consistancy[tic%BACKUPTICS];
+	INT16 clientconsistancy = SHORT(netbuffer->u.clientpak.consistancy);
+
+	if (tic > gametic || tic + BACKUPTICS - 1 <= gametic || gamestate != GS_LEVEL
+		|| neededconsistancy == clientconsistancy || SV_ResendingSavegameToAnyone()
+		|| node->resendingsavegame || node->savegameresendcooldown > I_GetTime())
+		return;
+
+	if (cv_resynchattempts.value)
+	{
+		// Tell the client we are about to resend them the gamestate
+		netbuffer->packettype = PT_WILLRESENDGAMESTATE;
+		HSendPacket(nodenum, true, 0, 0);
+
+		node->resendingsavegame = true;
+
+		if (cv_blamecfail.value)
+			CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
+				node->player+1, player_names[node->player],
+				neededconsistancy, clientconsistancy);
+
+		DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n",
+			node->player, tic, neededconsistancy, clientconsistancy));
+	}
+	else
+	{
+		SendKick(node->player, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
+
+		DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
+			node->player, tic, neededconsistancy, clientconsistancy));
+	}
+}
+
 void PT_ClientCmd(SINT8 node, INT32 netconsole)
 {
 	tic_t realend, realstart;
@@ -143,56 +192,14 @@ void PT_ClientCmd(SINT8 node, INT32 netconsole)
 	// Copy ticcmd
 	G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
 
-	// Check ticcmd for "speed hacks"
-	if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
-		|| netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
-	{
-		CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole);
-		//D_Clearticcmd(k);
-
-		SendKick(netconsole, KICK_MSG_CON_FAIL);
-		return;
-	}
-
 	// Splitscreen cmd
 	if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
 		&& netnodes[node].player2 >= 0)
 		G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2],
 			&netbuffer->u.client2pak.cmd2, 1);
 
-	// Check player consistancy during the level
-	if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL
-		&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
-		&& !SV_ResendingSavegameToAnyone()
-		&& !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime())
-	{
-		if (cv_resynchattempts.value)
-		{
-			// Tell the client we are about to resend them the gamestate
-			netbuffer->packettype = PT_WILLRESENDGAMESTATE;
-			HSendPacket(node, true, 0, 0);
-
-			netnodes[node].resendingsavegame = true;
-
-			if (cv_blamecfail.value)
-				CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
-					netconsole+1, player_names[netconsole],
-					consistancy[realstart%BACKUPTICS],
-					SHORT(netbuffer->u.clientpak.consistancy));
-			DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n",
-				netconsole, realstart, consistancy[realstart%BACKUPTICS],
-				SHORT(netbuffer->u.clientpak.consistancy)));
-			return;
-		}
-		else
-		{
-			SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
-			DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
-				netconsole, realstart, consistancy[realstart%BACKUPTICS],
-				SHORT(netbuffer->u.clientpak.consistancy)));
-			return;
-		}
-	}
+	CheckTiccmdHacks(netconsole);
+	CheckConsistancy(node, realstart);
 }
 
 void PT_ServerTics(SINT8 node, INT32 netconsole)
-- 
GitLab


From 2639dc176df55f14a5fa1310554d4d768da2d8c3 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 14 Jan 2023 14:52:13 +0100
Subject: [PATCH 285/518] Move net command copying to a new function

---
 src/netcode/net_command.c | 20 ++++++++++++++++++++
 src/netcode/net_command.h |  1 +
 src/netcode/tic_command.c | 21 +++------------------
 3 files changed, 24 insertions(+), 18 deletions(-)

diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c
index 13477d42d5..227b786605 100644
--- a/src/netcode/net_command.c
+++ b/src/netcode/net_command.c
@@ -304,6 +304,26 @@ void PT_TextCmd(SINT8 node, INT32 netconsole)
 	}
 }
 
+void SV_CopyNetCommandsToServerPacket(tic_t tic)
+{
+	servertics_pak *packet = &netbuffer->u.serverpak;
+	UINT8 *cmds = (UINT8*)&packet->cmds[packet->numslots * packet->numtics];
+	UINT8 numcmds;
+
+	numcmds = *cmds++;
+
+	for (UINT32 i = 0; i < numcmds; i++)
+	{
+		INT32 playernum = *cmds++; // playernum
+		size_t size = cmds[0]+1;
+
+		if (tic >= gametic) // Don't copy old net commands
+			M_Memcpy(D_GetTextcmd(tic, playernum), cmds, size);
+		cmds += size;
+	}
+}
+
+void CL_SendNetCommands(void)
 void SendKick(UINT8 playernum, UINT8 msg)
 {
 	UINT8 buf[2];
diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h
index f1b4b2f3f8..a9447ed7a1 100644
--- a/src/netcode/net_command.h
+++ b/src/netcode/net_command.h
@@ -57,6 +57,7 @@ void ExtraDataTicker(void);
 size_t TotalTextCmdPerTic(tic_t tic);
 
 void PT_TextCmd(SINT8 node, INT32 netconsole);
+void SV_CopyNetCommandsToServerPacket(tic_t tic);
 void SendKick(UINT8 playernum, UINT8 msg);
 void SendKicksForNode(SINT8 node, UINT8 msg);
 
diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
index 417a2fdfdf..2eb3b10aca 100644
--- a/src/netcode/tic_command.c
+++ b/src/netcode/tic_command.c
@@ -204,7 +204,6 @@ void PT_ClientCmd(SINT8 node, INT32 netconsole)
 
 void PT_ServerTics(SINT8 node, INT32 netconsole)
 {
-	UINT8 *pak, *txtpak, numtxtpak;
 	tic_t realend, realstart;
 
 	if (!netnodes[node].ingame)
@@ -230,19 +229,15 @@ void PT_ServerTics(SINT8 node, INT32 netconsole)
 	realstart = netbuffer->u.serverpak.starttic;
 	realend = realstart + netbuffer->u.serverpak.numtics;
 
-	txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots
-		* netbuffer->u.serverpak.numtics];
-
 	if (realend > gametic + CLIENTBACKUPTICS)
 		realend = gametic + CLIENTBACKUPTICS;
 	cl_packetmissed = realstart > neededtic;
 
 	if (realstart <= neededtic && realend > neededtic)
 	{
-		tic_t i, j;
-		pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
+		UINT8 *pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
 
-		for (i = realstart; i < realend; i++)
+		for (tic_t i = realstart; i < realend; i++)
 		{
 			// clear first
 			D_Clearticcmd(i);
@@ -251,17 +246,7 @@ void PT_ServerTics(SINT8 node, INT32 netconsole)
 			pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
 				netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
 
-			// copy the textcmds
-			numtxtpak = *txtpak++;
-			for (j = 0; j < numtxtpak; j++)
-			{
-				INT32 k = *txtpak++; // playernum
-				const size_t txtsize = txtpak[0]+1;
-
-				if (i >= gametic) // Don't copy old net commands
-					M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize);
-				txtpak += txtsize;
-			}
+			SV_CopyNetCommandsToServerPacket(i);
 		}
 
 		neededtic = realend;
-- 
GitLab


From e4c403408b1b3a306b316c135a91e0121c44a33e Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 14 Jan 2023 14:53:27 +0100
Subject: [PATCH 286/518] Move net command sending to a new function

---
 src/netcode/net_command.c | 22 ++++++++++++++++++++++
 src/netcode/net_command.h |  1 +
 src/netcode/tic_command.c | 22 +---------------------
 3 files changed, 24 insertions(+), 21 deletions(-)

diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c
index 227b786605..9e089a1a26 100644
--- a/src/netcode/net_command.c
+++ b/src/netcode/net_command.c
@@ -324,6 +324,28 @@ void SV_CopyNetCommandsToServerPacket(tic_t tic)
 }
 
 void CL_SendNetCommands(void)
+{
+	// Send extra data if needed
+	if (localtextcmd[0])
+	{
+		netbuffer->packettype = PT_TEXTCMD;
+		M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
+		// All extra data have been sent
+		if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail...
+			localtextcmd[0] = 0;
+	}
+
+	// Send extra data if needed for player 2 (splitscreen)
+	if (localtextcmd2[0])
+	{
+		netbuffer->packettype = PT_TEXTCMD2;
+		M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1);
+		// All extra data have been sent
+		if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail...
+			localtextcmd2[0] = 0;
+	}
+}
+
 void SendKick(UINT8 playernum, UINT8 msg)
 {
 	UINT8 buf[2];
diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h
index a9447ed7a1..3a433ebe46 100644
--- a/src/netcode/net_command.h
+++ b/src/netcode/net_command.h
@@ -58,6 +58,7 @@ size_t TotalTextCmdPerTic(tic_t tic);
 
 void PT_TextCmd(SINT8 node, INT32 netconsole);
 void SV_CopyNetCommandsToServerPacket(tic_t tic);
+void CL_SendNetCommands(void);
 void SendKick(UINT8 playernum, UINT8 msg);
 void SendKicksForNode(SINT8 node, UINT8 msg);
 
diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
index 2eb3b10aca..76d46451f7 100644
--- a/src/netcode/tic_command.c
+++ b/src/netcode/tic_command.c
@@ -295,27 +295,7 @@ void CL_SendClientCmd(void)
 	}
 
 	if (cl_mode == CL_CONNECTED || dedicated)
-	{
-		// Send extra data if needed
-		if (localtextcmd[0])
-		{
-			netbuffer->packettype = PT_TEXTCMD;
-			M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
-			// All extra data have been sent
-			if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail...
-				localtextcmd[0] = 0;
-		}
-
-		// Send extra data if needed for player 2 (splitscreen)
-		if (localtextcmd2[0])
-		{
-			netbuffer->packettype = PT_TEXTCMD2;
-			M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1);
-			// All extra data have been sent
-			if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail...
-				localtextcmd2[0] = 0;
-		}
-	}
+		CL_SendNetCommands();
 }
 
 // send the server packet
-- 
GitLab


From a69204b609376532eacb255199e628d791904fa5 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 14 Jan 2023 15:14:09 +0100
Subject: [PATCH 287/518] Fix function name

---
 src/netcode/net_command.c | 2 +-
 src/netcode/net_command.h | 2 +-
 src/netcode/tic_command.c | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c
index 9e089a1a26..98c578b797 100644
--- a/src/netcode/net_command.c
+++ b/src/netcode/net_command.c
@@ -304,7 +304,7 @@ void PT_TextCmd(SINT8 node, INT32 netconsole)
 	}
 }
 
-void SV_CopyNetCommandsToServerPacket(tic_t tic)
+void CL_CopyNetCommandsFromServerPacket(tic_t tic)
 {
 	servertics_pak *packet = &netbuffer->u.serverpak;
 	UINT8 *cmds = (UINT8*)&packet->cmds[packet->numslots * packet->numtics];
diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h
index 3a433ebe46..0e8339ef7c 100644
--- a/src/netcode/net_command.h
+++ b/src/netcode/net_command.h
@@ -57,7 +57,7 @@ void ExtraDataTicker(void);
 size_t TotalTextCmdPerTic(tic_t tic);
 
 void PT_TextCmd(SINT8 node, INT32 netconsole);
-void SV_CopyNetCommandsToServerPacket(tic_t tic);
+void CL_CopyNetCommandsFromServerPacket(tic_t tic);
 void CL_SendNetCommands(void);
 void SendKick(UINT8 playernum, UINT8 msg);
 void SendKicksForNode(SINT8 node, UINT8 msg);
diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
index 76d46451f7..6b237ed614 100644
--- a/src/netcode/tic_command.c
+++ b/src/netcode/tic_command.c
@@ -246,7 +246,7 @@ void PT_ServerTics(SINT8 node, INT32 netconsole)
 			pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
 				netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
 
-			SV_CopyNetCommandsToServerPacket(i);
+			CL_CopyNetCommandsFromServerPacket(i);
 		}
 
 		neededtic = realend;
-- 
GitLab


From 8df0debd541a253a2161267bc77cba1823184852 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 14 Jan 2023 19:01:36 +0100
Subject: [PATCH 288/518] Split SV_SendTics into functions

---
 src/netcode/net_command.c | 21 +++++++++
 src/netcode/net_command.h |  1 +
 src/netcode/tic_command.c | 95 ++++++++++++++++++---------------------
 3 files changed, 66 insertions(+), 51 deletions(-)

diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c
index 98c578b797..11b351af73 100644
--- a/src/netcode/net_command.c
+++ b/src/netcode/net_command.c
@@ -304,6 +304,27 @@ void PT_TextCmd(SINT8 node, INT32 netconsole)
 	}
 }
 
+void SV_WriteNetCommandsForTic(tic_t tic, UINT8 **buf)
+{
+	UINT8 *numcmds;
+
+	numcmds = (*buf)++;
+	*numcmds = 0;
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
+	{
+		UINT8 *cmd = D_GetExistingTextcmd(tic, i);
+		INT32 size = cmd ? cmd[0] : 0;
+
+		if ((!i || playeringame[i]) && size)
+		{
+			(*numcmds)++;
+			WRITEUINT8(*buf, i);
+			M_Memcpy(*buf, cmd, size + 1);
+			*buf += size + 1;
+		}
+	}
+}
+
 void CL_CopyNetCommandsFromServerPacket(tic_t tic)
 {
 	servertics_pak *packet = &netbuffer->u.serverpak;
diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h
index 0e8339ef7c..cc26aeb0ef 100644
--- a/src/netcode/net_command.h
+++ b/src/netcode/net_command.h
@@ -57,6 +57,7 @@ void ExtraDataTicker(void);
 size_t TotalTextCmdPerTic(tic_t tic);
 
 void PT_TextCmd(SINT8 node, INT32 netconsole);
+void SV_WriteNetCommandsForTic(tic_t tic, UINT8 **buf);
 void CL_CopyNetCommandsFromServerPacket(tic_t tic);
 void CL_SendNetCommands(void);
 void SendKick(UINT8 playernum, UINT8 msg);
diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
index 6b237ed614..bf8c5d21d4 100644
--- a/src/netcode/tic_command.c
+++ b/src/netcode/tic_command.c
@@ -298,16 +298,56 @@ void CL_SendClientCmd(void)
 		CL_SendNetCommands();
 }
 
+// PT_SERVERTICS packets can grow too large for a single UDP packet,
+// So this checks how many tics worth of data can be sent in one packet.
+// The rest can be sent later, usually the next tic.
+static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t lasttic)
+{
+	size_t size = BASESERVERTICSSIZE;
+	tic_t tic;
+
+	for (tic = firsttic; tic < lasttic; tic++)
+	{
+		size += sizeof (ticcmd_t) * doomcom->numslots;
+		size += TotalTextCmdPerTic(tic);
+
+		if (size > software_MAXPACKETLENGTH)
+		{
+			DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n",
+				sizeu1(size), tic, firsttic, lasttic));
+			lasttic = tic;
+
+			// too bad: too much player have send extradata and there is too
+			//          much data in one tic.
+			// To avoid it put the data on the next tic. (see getpacket
+			// textcmd case) but when numplayer changes the computation can be different
+			if (lasttic == firsttic)
+			{
+				if (size > MAXPACKETLENGTH)
+					I_Error("Too many players: can't send %s data for %d players to node %d\n"
+							"Well sorry nobody is perfect....\n",
+							sizeu1(size), doomcom->numslots, nodenum);
+				else
+				{
+					lasttic++; // send it anyway!
+					DEBFILE("sending it anyway\n");
+				}
+			}
+			break;
+		}
+	}
+
+	return lasttic - firsttic;
+}
+
 // send the server packet
 // send tic from firstticstosend to maketic-1
 void SV_SendTics(void)
 {
 	tic_t realfirsttic, lasttictosend, i;
 	UINT32 n;
-	INT32 j;
 	size_t packsize;
 	UINT8 *bufpos;
-	UINT8 *ntextcmd;
 
 	// send to all client but not to me
 	// for each node create a packet with x tics and send it
@@ -336,38 +376,7 @@ void SV_SendTics(void)
 			if (realfirsttic < firstticstosend)
 				realfirsttic = firstticstosend;
 
-			// compute the length of the packet and cut it if too large
-			packsize = BASESERVERTICSSIZE;
-			for (i = realfirsttic; i < lasttictosend; i++)
-			{
-				packsize += sizeof (ticcmd_t) * doomcom->numslots;
-				packsize += TotalTextCmdPerTic(i);
-
-				if (packsize > software_MAXPACKETLENGTH)
-				{
-					DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n",
-						sizeu1(packsize), i, realfirsttic, lasttictosend));
-					lasttictosend = i;
-
-					// too bad: too much player have send extradata and there is too
-					//          much data in one tic.
-					// To avoid it put the data on the next tic. (see getpacket
-					// textcmd case) but when numplayer changes the computation can be different
-					if (lasttictosend == realfirsttic)
-					{
-						if (packsize > MAXPACKETLENGTH)
-							I_Error("Too many players: can't send %s data for %d players to node %d\n"
-							        "Well sorry nobody is perfect....\n",
-							        sizeu1(packsize), doomcom->numslots, n);
-						else
-						{
-							lasttictosend++; // send it anyway!
-							DEBFILE("sending it anyway\n");
-						}
-					}
-					break;
-				}
-			}
+			lasttictosend = realfirsttic + SV_CalculateNumTicsForPacket(n, realfirsttic, lasttictosend);
 
 			// Send the tics
 			netbuffer->packettype = PT_SERVERTICS;
@@ -383,23 +392,7 @@ void SV_SendTics(void)
 
 			// add textcmds
 			for (i = realfirsttic; i < lasttictosend; i++)
-			{
-				ntextcmd = bufpos++;
-				*ntextcmd = 0;
-				for (j = 0; j < MAXPLAYERS; j++)
-				{
-					UINT8 *textcmd = D_GetExistingTextcmd(i, j);
-					INT32 size = textcmd ? textcmd[0] : 0;
-
-					if ((!j || playeringame[j]) && size)
-					{
-						(*ntextcmd)++;
-						WRITEUINT8(bufpos, j);
-						M_Memcpy(bufpos, textcmd, size + 1);
-						bufpos += size + 1;
-					}
-				}
-			}
+				SV_WriteNetCommandsForTic();
 			packsize = bufpos - (UINT8 *)&(netbuffer->u);
 
 			HSendPacket(n, false, 0, packsize);
-- 
GitLab


From a54155e2cccacd6a8025485088d1c7ba0fcf3efa Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 14 Jan 2023 20:02:06 +0100
Subject: [PATCH 289/518] Cleanup

---
 src/netcode/client_connection.c | 21 ++++------
 src/netcode/commands.c          | 23 +++++------
 src/netcode/d_clisrv.c          | 57 ++++++++++----------------
 src/netcode/d_net.c             | 66 ++++++++++++------------------
 src/netcode/d_netfil.c          | 71 ++++++++++++---------------------
 src/netcode/gamestate.c         |  8 +---
 src/netcode/i_tcp.c             | 34 ++++++----------
 src/netcode/net_command.c       | 11 ++---
 src/netcode/server_connection.c | 11 ++---
 src/netcode/tic_command.c       | 56 +++++++++++---------------
 10 files changed, 132 insertions(+), 226 deletions(-)

diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c
index ff20a6fc74..faa91b6d7c 100644
--- a/src/netcode/client_connection.c
+++ b/src/netcode/client_connection.c
@@ -60,7 +60,7 @@ static inline void CL_DrawConnectionStatus(void)
 
 	if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_LOADFILES)
 	{
-		INT32 i, animtime = ((ccstime / 4) & 15) + 16;
+		INT32 animtime = ((ccstime / 4) & 15) + 16;
 		UINT8 palstart;
 		const char *cltext;
 
@@ -75,7 +75,7 @@ static inline void CL_DrawConnectionStatus(void)
 			palstart = 96; // Green
 
 		if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1))
-			for (i = 0; i < 16; ++i) // 15 pal entries total.
+			for (INT32 i = 0; i < 16; ++i) // 15 pal entries total.
 				V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15));
 
 		switch (cl_mode)
@@ -134,14 +134,13 @@ static inline void CL_DrawConnectionStatus(void)
 		{
 			INT32 totalfileslength;
 			INT32 loadcompletednum = 0;
-			INT32 i;
 
 			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort");
 
 			//ima just count files here
 			if (fileneeded)
 			{
-				for (i = 0; i < fileneedednum; i++)
+				for (INT32 i = 0; i < fileneedednum; i++)
 					if (fileneeded[i].status == FS_OPEN)
 						loadcompletednum++;
 			}
@@ -277,9 +276,7 @@ UINT32 serverlistcount = 0;
 
 static void SL_ClearServerList(INT32 connectedserver)
 {
-	UINT32 i;
-
-	for (i = 0; i < serverlistcount; i++)
+	for (UINT32 i = 0; i < serverlistcount; i++)
 		if (connectedserver != serverlist[i].node)
 		{
 			Net_CloseConnection(serverlist[i].node|FORCECLOSE);
@@ -290,8 +287,7 @@ static void SL_ClearServerList(INT32 connectedserver)
 
 static UINT32 SL_SearchServer(INT32 node)
 {
-	UINT32 i;
-	for (i = 0; i < serverlistcount; i++)
+	for (UINT32 i = 0; i < serverlistcount; i++)
 		if (serverlist[i].node == node)
 			return i;
 
@@ -388,9 +384,7 @@ Fetch_servers_thread (struct Fetch_servers_ctx *ctx)
 
 void CL_QueryServerList (msg_server_t *server_list)
 {
-	INT32 i;
-
-	for (i = 0; server_list[i].header.buffer[0]; i++)
+	for (INT32 i = 0; server_list[i].header.buffer[0]; i++)
 	{
 		// Make sure MS version matches our own, to
 		// thwart nefarious servers who lie to the MS.
@@ -781,7 +775,6 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
 static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent)
 {
 	boolean waitmore;
-	INT32 i;
 
 	switch (cl_mode)
 	{
@@ -808,7 +801,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 			break;
 		case CL_DOWNLOADFILES:
 			waitmore = false;
-			for (i = 0; i < fileneedednum; i++)
+			for (INT32 i = 0; i < fileneedednum; i++)
 				if (fileneeded[i].status == FS_DOWNLOADING
 					|| fileneeded[i].status == FS_REQUESTED)
 				{
diff --git a/src/netcode/commands.c b/src/netcode/commands.c
index 834e1c6668..4d9a48b6bc 100644
--- a/src/netcode/commands.c
+++ b/src/netcode/commands.c
@@ -79,7 +79,6 @@ static void Ban_Clear(void)
 void Ban_Load_File(boolean warning)
 {
 	FILE *f;
-	size_t i;
 	const char *address, *mask;
 	char buffer[MAX_WADPATH];
 
@@ -97,7 +96,7 @@ void Ban_Load_File(boolean warning)
 
 	Ban_Clear();
 
-	for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++)
+	for (size_t i=0; fgets(buffer, (int)sizeof(buffer), f); i++)
 	{
 		address = strtok(buffer, " \t\r\n");
 		mask = strtok(NULL, " \t\r\n");
@@ -113,7 +112,6 @@ void Ban_Load_File(boolean warning)
 void D_SaveBan(void)
 {
 	FILE *f;
-	size_t i;
 	banreason_t *reasonlist = reasonhead;
 	const char *address, *mask;
 	const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt");
@@ -132,7 +130,7 @@ void D_SaveBan(void)
 		return;
 	}
 
-	for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
+	for (size_t i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
 	{
 		if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
 			fprintf(f, "%s 0", address);
@@ -236,12 +234,12 @@ void Command_Ban(void)
 			}
 			else
 			{
-				size_t i, j = COM_Argc();
+				size_t j = COM_Argc();
 				char message[MAX_REASONLENGTH];
 
 				//Steal from the motd code so you don't have to put the reason in quotes.
 				strlcpy(message, COM_Argv(2), sizeof message);
-				for (i = 3; i < j; i++)
+				for (size_t i = 3; i < j; i++)
 				{
 					strlcat(message, " ", sizeof message);
 					strlcat(message, COM_Argv(i), sizeof message);
@@ -340,12 +338,12 @@ void Command_Kick(void)
 		}
 		else
 		{
-			size_t i, j = COM_Argc();
+			size_t j = COM_Argc();
 			char message[MAX_REASONLENGTH];
 
 			//Steal from the motd code so you don't have to put the reason in quotes.
 			strlcpy(message, COM_Argv(2), sizeof message);
-			for (i = 3; i < j; i++)
+			for (size_t i = 3; i < j; i++)
 			{
 				strlcat(message, " ", sizeof message);
 				strlcat(message, COM_Argv(i), sizeof message);
@@ -435,9 +433,7 @@ void Command_connect(void)
 
 void Command_GetPlayerNum(void)
 {
-	INT32 i;
-
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 		if (playeringame[i])
 		{
 			if (serverplayer == i)
@@ -453,18 +449,17 @@ void Command_GetPlayerNum(void)
   */
 void Command_Nodes(void)
 {
-	INT32 i;
 	size_t maxlen = 0;
 	const char *address;
 
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 	{
 		const size_t plen = strlen(player_names[i]);
 		if (playeringame[i] && plen > maxlen)
 			maxlen = plen;
 	}
 
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 	{
 		if (playeringame[i])
 		{
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 1658856b68..105526dd28 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -440,11 +440,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 
 			if (M_CheckParm("-consisdump")) // Helps debugging some problems
 			{
-				INT32 i;
-
 				CONS_Printf(M_GetText("Player kicked is #%d, dumping consistency...\n"), pnum);
 
-				for (i = 0; i < MAXPLAYERS; i++)
+				for (INT32 i = 0; i < MAXPLAYERS; i++)
 				{
 					if (!playeringame[i])
 						continue;
@@ -545,8 +543,8 @@ static void RedistributeSpecialStageSpheres(INT32 playernum)
 		INT32 sincrement = max(spheres / count, 1);
 		INT32 rincrement = max(rings / count, 1);
 
-		INT32 i, n;
-		for (i = 0; i < MAXPLAYERS; i++)
+		INT32 n;
+		for (INT32 i = 0; i < MAXPLAYERS; i++)
 		{
 			if (!playeringame[i] || i == playernum)
 				continue;
@@ -644,10 +642,8 @@ void D_QuitNetGame(void)
 
 	if (server)
 	{
-		INT32 i;
-
 		netbuffer->packettype = PT_SERVERSHUTDOWN;
-		for (i = 0; i < MAXNETNODES; i++)
+		for (INT32 i = 0; i < MAXNETNODES; i++)
 			if (netnodes[i].ingame)
 				HSendPacket(i, true, 0, 0);
 #ifdef MASTERSERVER
@@ -701,8 +697,6 @@ void CL_RemoveSplitscreenPlayer(void)
 
 void SV_ResetServer(void)
 {
-	INT32 i;
-
 	// +1 because this command will be executed in com_executebuffer in
 	// tryruntic so gametic will be incremented, anyway maketic > gametic
 	// is not an issue
@@ -713,10 +707,10 @@ void SV_ResetServer(void)
 
 	joindelay = 0;
 
-	for (i = 0; i < MAXNETNODES; i++)
+	for (INT32 i = 0; i < MAXNETNODES; i++)
 		ResetNode(i);
 
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 	{
 		LUA_InvalidatePlayer(&players[i]);
 		playeringame[i] = false;
@@ -755,10 +749,9 @@ void SV_ResetServer(void)
 
 static inline void SV_GenContext(void)
 {
-	UINT8 i;
 	// generate server_context, as exactly 8 bytes of randomly mixed A-Z and a-z
 	// (hopefully M_Random is initialized!! if not this will be awfully silly!)
-	for (i = 0; i < 8; i++)
+	for (UINT8 i = 0; i < 8; i++)
 	{
 		const char a = M_RandomKey(26*2);
 		if (a < 26) // uppercase
@@ -814,8 +807,6 @@ void SV_StartSinglePlayerServer(void)
 
 void SV_StopServer(void)
 {
-	tic_t i;
-
 	if (gamestate == GS_INTERMISSION)
 		Y_EndIntermission();
 	gamestate = wipegamestate = GS_NULL;
@@ -823,7 +814,7 @@ void SV_StopServer(void)
 	localtextcmd[0] = 0;
 	localtextcmd2[0] = 0;
 
-	for (i = firstticstosend; i < firstticstosend + BACKUPTICS; i++)
+	for (tic_t i = firstticstosend; i < firstticstosend + BACKUPTICS; i++)
 		D_Clearticcmd(i);
 
 	consoleplayer = 0;
@@ -913,7 +904,6 @@ If they're not lagging, decrement the timer by 1. Of course, reset all of this i
 
 static inline void PingUpdate(void)
 {
-	INT32 i;
 	boolean laggers[MAXPLAYERS];
 	UINT8 numlaggers = 0;
 	memset(laggers, 0, sizeof(boolean) * MAXPLAYERS);
@@ -923,7 +913,7 @@ static inline void PingUpdate(void)
 	//check for ping limit breakage.
 	if (cv_maxping.value)
 	{
-		for (i = 1; i < MAXPLAYERS; i++)
+		for (INT32 i = 1; i < MAXPLAYERS; i++)
 		{
 			if (playeringame[i] && !players[i].quittime
 			&& (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value))
@@ -940,7 +930,7 @@ static inline void PingUpdate(void)
 		//in that case, it is probably the server's fault.
 		if (numlaggers < D_NumPlayers() - 1)
 		{
-			for (i = 1; i < MAXPLAYERS; i++)
+			for (INT32 i = 1; i < MAXPLAYERS; i++)
 			{
 				if (playeringame[i] && laggers[i])
 				{
@@ -965,7 +955,7 @@ static inline void PingUpdate(void)
 	}
 
 	//make the ping packet and clear server data for next one
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 	{
 		netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount;
 		//server takes a snapshot of the real ping for display.
@@ -978,7 +968,7 @@ static inline void PingUpdate(void)
 	netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value;
 
 	//send out our ping packets
-	for (i = 0; i < MAXNETNODES; i++)
+	for (INT32 i = 0; i < MAXNETNODES; i++)
 		if (netnodes[i].ingame)
 			HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1));
 
@@ -999,8 +989,7 @@ static void PT_Ping(SINT8 node, INT32 netconsole)
 	//Update client ping table from the server.
 	if (client)
 	{
-		UINT8 i;
-		for (i = 0; i < MAXPLAYERS; i++)
+		for (INT32 i = 0; i < MAXPLAYERS; i++)
 			if (playeringame[i])
 				playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i];
 
@@ -1225,7 +1214,6 @@ void NetUpdate(void)
 	static tic_t gametime = 0;
 	static tic_t resptime = 0;
 	tic_t nowtime;
-	INT32 i;
 	INT32 realtics;
 
 	nowtime = I_GetTime();
@@ -1248,7 +1236,7 @@ void NetUpdate(void)
 		if (netgame && !(gametime % 35)) // update once per second.
 			PingUpdate();
 		// update node latency values so we can take an average later.
-		for (i = 0; i < MAXPLAYERS; i++)
+		for (INT32 i = 0; i < MAXPLAYERS; i++)
 			if (playeringame[i] && playernode[i] != UINT8_MAX)
 				realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
 		pingmeasurecount++;
@@ -1287,7 +1275,7 @@ void NetUpdate(void)
 			hu_redownloadinggamestate = false;
 
 			firstticstosend = gametic;
-			for (i = 0; i < MAXNETNODES; i++)
+			for (INT32 i = 0; i < MAXNETNODES; i++)
 				if (netnodes[i].ingame && netnodes[i].tic < firstticstosend)
 				{
 					firstticstosend = netnodes[i].tic;
@@ -1301,7 +1289,7 @@ void NetUpdate(void)
 			if (maketic + counts >= firstticstosend + BACKUPTICS)
 				counts = firstticstosend+BACKUPTICS-maketic-1;
 
-			for (i = 0; i < counts; i++)
+			for (INT32 i = 0; i < counts; i++)
 				SV_Maketic(); // Create missed tics and increment maketic
 
 			for (; tictoclear < firstticstosend; tictoclear++) // Clear only when acknowledged
@@ -1318,7 +1306,7 @@ void NetUpdate(void)
 	// Handle timeouts to prevent definitive freezes from happenning
 	if (server)
 	{
-		for (i = 1; i < MAXNETNODES; i++)
+		for (INT32 i = 1; i < MAXNETNODES; i++)
 			if (netnodes[i].ingame && netnodes[i].freezetimeout < I_GetTime())
 				Net_ConnectionTimeout(i);
 
@@ -1387,7 +1375,7 @@ void D_ClientServerInit(void)
 
 SINT8 nametonum(const char *name)
 {
-	INT32 playernum, i;
+	INT32 playernum;
 
 	if (!strcmp(name, "0"))
 		return 0;
@@ -1405,7 +1393,7 @@ SINT8 nametonum(const char *name)
 			return -1;
 	}
 
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 		if (playeringame[i] && !stricmp(player_names[i], name))
 			return (SINT8)i;
 
@@ -1427,8 +1415,8 @@ boolean Playing(void)
   */
 INT32 D_NumPlayers(void)
 {
-	INT32 num = 0, ix;
-	for (ix = 0; ix < MAXPLAYERS; ix++)
+	INT32 num = 0;
+	for (INT32 ix = 0; ix < MAXPLAYERS; ix++)
 		if (playeringame[ix])
 			num++;
 	return num;
@@ -1444,7 +1432,6 @@ INT32 D_NumPlayers(void)
 //
 INT16 Consistancy(void)
 {
-	INT32 i;
 	UINT32 ret = 0;
 #ifdef MOBJCONSISTANCY
 	thinker_t *th;
@@ -1453,7 +1440,7 @@ INT16 Consistancy(void)
 
 	DEBFILE(va("TIC %u ", gametic));
 
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 	{
 		if (!playeringame[i])
 			ret ^= 0xCCCC;
diff --git a/src/netcode/d_net.c b/src/netcode/d_net.c
index 3c16392d01..a4b0778e36 100644
--- a/src/netcode/d_net.c
+++ b/src/netcode/d_net.c
@@ -211,7 +211,7 @@ FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b)
 static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
 {
 	node_t *node = &nodes[doomcom->remotenode];
-	INT32 i, numfreeslot = 0;
+	INT32 numfreeslot = 0;
 
 	if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0)
 	{
@@ -219,7 +219,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
 		return false;
 	}
 
-	for (i = 0; i < MAXACKPACKETS; i++)
+	for (INT32 i = 0; i < MAXACKPACKETS; i++)
 		if (!ackpak[i].acknum)
 		{
 			// For low priority packets, make sure to let freeslots so urgent packets can be sent
@@ -276,10 +276,10 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
   */
 INT32 Net_GetFreeAcks(boolean urgent)
 {
-	INT32 i, numfreeslot = 0;
+	INT32 numfreeslot = 0;
 	INT32 n = 0; // Number of free acks found
 
-	for (i = 0; i < MAXACKPACKETS; i++)
+	for (INT32 i = 0; i < MAXACKPACKETS; i++)
 		if (!ackpak[i].acknum)
 		{
 			// For low priority packets, make sure to let freeslots so urgent packets can be sent
@@ -315,7 +315,6 @@ static void RemoveAck(INT32 i)
 // We have got a packet, proceed the ack request and ack return
 static boolean Processackpak(void)
 {
-	INT32 i;
 	boolean goodpacket = true;
 	node_t *node = &nodes[doomcom->remotenode];
 
@@ -324,7 +323,7 @@ static boolean Processackpak(void)
 	{
 		node->remotefirstack = netbuffer->ackreturn;
 		// Search the ackbuffer and free it
-		for (i = 0; i < MAXACKPACKETS; i++)
+		for (INT32 i = 0; i < MAXACKPACKETS; i++)
 			if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes
 				&& cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0)
 			{
@@ -346,7 +345,7 @@ static boolean Processackpak(void)
 		else
 		{
 			// Check if it is not already in the queue
-			for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND)
+			for (INT32 i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND)
 				if (node->acktosend[i] == ack)
 				{
 					DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack));
@@ -374,7 +373,7 @@ static boolean Processackpak(void)
 					while (change)
 					{
 						change = false;
-						for (i = node->acktosend_tail; i != node->acktosend_head;
+						for (INT32 i = node->acktosend_tail; i != node->acktosend_head;
 							i = (i+1) % MAXACKTOSEND)
 						{
 							if (cmpack(node->acktosend[i], nextfirstack) <= 0)
@@ -434,11 +433,9 @@ void Net_SendAcks(INT32 node)
 
 static void GotAcks(void)
 {
-	INT32 i, j;
-
-	for (j = 0; j < MAXACKTOSEND; j++)
+	for (INT32 j = 0; j < MAXACKTOSEND; j++)
 		if (netbuffer->u.textcmd[j])
-			for (i = 0; i < MAXACKPACKETS; i++)
+			for (INT32 i = 0; i < MAXACKPACKETS; i++)
 				if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode)
 				{
 					if (ackpak[i].acknum == netbuffer->u.textcmd[j])
@@ -475,9 +472,8 @@ void Net_ConnectionTimeout(INT32 node)
 // Resend the data if needed
 void Net_AckTicker(void)
 {
-	INT32 i;
 
-	for (i = 0; i < MAXACKPACKETS; i++)
+	for (INT32 i = 0; i < MAXACKPACKETS; i++)
 	{
 		const INT32 nodei = ackpak[i].destinationnode;
 		node_t *node = &nodes[nodei];
@@ -504,7 +500,7 @@ void Net_AckTicker(void)
 		}
 	}
 
-	for (i = 1; i < MAXNETNODES; i++)
+	for (INT32 i = 1; i < MAXNETNODES; i++)
 	{
 		// This is something like node open flag
 		if (nodes[i].firstacktosend)
@@ -567,9 +563,7 @@ void Net_UnAcknowledgePacket(INT32 node)
   */
 static boolean Net_AllAcksReceived(void)
 {
-	INT32 i;
-
-	for (i = 0; i < MAXACKPACKETS; i++)
+	for (INT32 i = 0; i < MAXACKPACKETS; i++)
 		if (ackpak[i].acknum)
 			return false;
 
@@ -611,12 +605,10 @@ static void InitNode(node_t *node)
 
 static void InitAck(void)
 {
-	INT32 i;
-
-	for (i = 0; i < MAXACKPACKETS; i++)
+	for (INT32 i = 0; i < MAXACKPACKETS; i++)
 		ackpak[i].acknum = 0;
 
-	for (i = 0; i < MAXNETNODES; i++)
+	for (INT32 i = 0; i < MAXNETNODES; i++)
 		InitNode(&nodes[i]);
 }
 
@@ -627,8 +619,7 @@ static void InitAck(void)
   */
 void Net_AbortPacketType(UINT8 packettype)
 {
-	INT32 i;
-	for (i = 0; i < MAXACKPACKETS; i++)
+	for (INT32 i = 0; i < MAXACKPACKETS; i++)
 		if (ackpak[i].acknum && (ackpak[i].pak.data.packettype == packettype
 			|| packettype == UINT8_MAX))
 		{
@@ -643,7 +634,6 @@ void Net_AbortPacketType(UINT8 packettype)
 // remove a node, clear all ack from this node and reset askret
 void Net_CloseConnection(INT32 node)
 {
-	INT32 i;
 	boolean forceclose = (node & FORCECLOSE) != 0;
 
 	if (node == -1)
@@ -673,7 +663,7 @@ void Net_CloseConnection(INT32 node)
 	}
 
 	// check if we are waiting for an ack from this node
-	for (i = 0; i < MAXACKPACKETS; i++)
+	for (INT32 i = 0; i < MAXACKPACKETS; i++)
 		if (ackpak[i].acknum && ackpak[i].destinationnode == node)
 		{
 			if (!forceclose)
@@ -697,9 +687,8 @@ static UINT32 NetbufferChecksum(void)
 	UINT32 c = 0x1234567;
 	const INT32 l = doomcom->datalength - 4;
 	const UINT8 *buf = (UINT8 *)netbuffer + 4;
-	INT32 i;
 
-	for (i = 0; i < l; i++, buf++)
+	for (INT32 i = 0; i < l; i++, buf++)
 		c += (*buf) * (i+1);
 
 	return LONG(c);
@@ -710,9 +699,8 @@ static UINT32 NetbufferChecksum(void)
 static void fprintfstring(char *s, size_t len)
 {
 	INT32 mode = 0;
-	size_t i;
 
-	for (i = 0; i < len; i++)
+	for (size_t i = 0; i < len; i++)
 		if (s[i] < 32)
 		{
 			if (!mode)
@@ -879,7 +867,6 @@ void Command_Drop(void)
 {
 	INT32 packetquantity;
 	const char *packetname;
-	size_t i;
 
 	if (COM_Argc() < 2)
 	{
@@ -909,11 +896,11 @@ void Command_Drop(void)
 	packetname = COM_Argv(1);
 
 	if (!(stricmp(packetname, "all") && stricmp(packetname, "any")))
-		for (i = 0; i < NUMPACKETTYPE; i++)
+		for (size_t i = 0; i < NUMPACKETTYPE; i++)
 			packetdropquantity[i] = packetquantity;
 	else
 	{
-		for (i = 0; i < NUMPACKETTYPE; i++)
+		for (size_t i = 0; i < NUMPACKETTYPE; i++)
 			if (!stricmp(packetname, packettypename[i]))
 			{
 				packetdropquantity[i] = packetquantity;
@@ -1336,13 +1323,12 @@ void Command_Ping_f(void)
 	int name_width = 0;
 	int   ms_width = 0;
 
-	int n;
-	INT32 i;
-
 	pingc = 0;
-	for (i = 1; i < MAXPLAYERS; ++i)
+	for (INT32 i = 1; i < MAXPLAYERS; ++i)
 		if (playeringame[i])
 	{
+		int n;
+
 		n = strlen(player_names[i]);
 		if (n > name_width)
 			name_width = n;
@@ -1362,7 +1348,7 @@ void Command_Ping_f(void)
 
 	qsort(pingv, pingc, sizeof (struct pingcell), &pingcellcmp);
 
-	for (i = 0; i < pingc; ++i)
+	for (INT32 i = 0; i < pingc; ++i)
 	{
 		CONS_Printf("%02d : %-*s %*d ms\n",
 				pingv[i].num,
@@ -1378,15 +1364,13 @@ void Command_Ping_f(void)
 
 void D_CloseConnection(void)
 {
-	INT32 i;
-
 	if (netgame)
 	{
 		// wait the ackreturn with timout of 5 Sec
 		Net_WaitAllAckReceived(5);
 
 		// close all connection
-		for (i = 0; i < MAXNETNODES; i++)
+		for (INT32 i = 0; i < MAXNETNODES; i++)
 			Net_CloseConnection(i|FORCECLOSE);
 
 		InitAck();
diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c
index 205abf713e..10b7359adc 100644
--- a/src/netcode/d_netfil.c
+++ b/src/netcode/d_netfil.c
@@ -128,9 +128,7 @@ consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR
 
 static UINT16 GetWadNumFromFileNeededId(UINT8 id)
 {
-	UINT16 wadnum;
-
-	for (wadnum = mainwads; wadnum < numwadfiles; wadnum++)
+	for (UINT16 wadnum = mainwads; wadnum < numwadfiles; wadnum++)
 	{
 		if (!wadfiles[wadnum]->important)
 			continue;
@@ -150,14 +148,13 @@ static UINT16 GetWadNumFromFileNeededId(UINT8 id)
   */
 UINT8 *PutFileNeeded(UINT16 firstfile)
 {
-	size_t i;
 	UINT8 count = 0;
 	UINT8 *p_start = netbuffer->packettype == PT_MOREFILESNEEDED ? netbuffer->u.filesneededcfg.files : netbuffer->u.serverinfo.fileneeded;
 	UINT8 *p = p_start;
 	char wadfilename[MAX_WADPATH] = "";
 	UINT8 filestatus, folder;
 
-	for (i = mainwads; i < numwadfiles; i++) //mainwads, otherwise we start on the first mainwad
+	for (size_t i = mainwads; i < numwadfiles; i++) //mainwads, otherwise we start on the first mainwad
 	{
 		// If it has only music/sound lumps, don't put it in the list
 		if (!wadfiles[i]->important)
@@ -232,7 +229,6 @@ void FreeFileNeeded(void)
   */
 void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 firstfile)
 {
-	INT32 i;
 	UINT8 *p;
 	UINT8 filestatus;
 
@@ -241,7 +237,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi
 
 	AllocFileNeeded(fileneedednum);
 
-	for (i = firstfile; i < fileneedednum; i++)
+	for (INT32 i = firstfile; i < fileneedednum; i++)
 	{
 		fileneeded[i].type = FILENEEDED_WAD;
 		fileneeded[i].status = FS_NOTCHECKED; // We haven't even started looking for the file yet
@@ -281,9 +277,9 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave)
   */
 boolean CL_CheckDownloadable(void)
 {
-	UINT8 i,dlstatus = 0;
+	UINT8 dlstatus = 0;
 
-	for (i = 0; i < fileneedednum; i++)
+	for (UINT8 i = 0; i < fileneedednum; i++)
 		if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN)
 		{
 			if (fileneeded[i].willsend == 1)
@@ -304,7 +300,7 @@ boolean CL_CheckDownloadable(void)
 
 	// not downloadable, put reason in console
 	CONS_Alert(CONS_NOTICE, M_GetText("You need additional files to connect to this server:\n"));
-	for (i = 0; i < fileneedednum; i++)
+	for (UINT8 i = 0; i < fileneedednum; i++)
 		if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN)
 		{
 			CONS_Printf(" * \"%s\" (%dK)", fileneeded[i].filename, fileneeded[i].totalsize >> 10);
@@ -374,14 +370,13 @@ void CL_AbortDownloadResume(void)
 boolean CL_SendFileRequest(void)
 {
 	char *p;
-	INT32 i;
 	INT64 totalfreespaceneeded = 0, availablefreespace;
 
 #ifdef PARANOIA
 	if (M_CheckParm("-nodownload"))
 		I_Error("Attempted to download files in -nodownload mode");
 
-	for (i = 0; i < fileneedednum; i++)
+	for (INT32 i = 0; i < fileneedednum; i++)
 		if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN
 			&& (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2))
 		{
@@ -391,7 +386,7 @@ boolean CL_SendFileRequest(void)
 
 	netbuffer->packettype = PT_REQUESTFILE;
 	p = (char *)netbuffer->u.textcmd;
-	for (i = 0; i < fileneedednum; i++)
+	for (INT32 i = 0; i < fileneedednum; i++)
 		if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD))
 		{
 			totalfreespaceneeded += fileneeded[i].totalsize;
@@ -422,7 +417,6 @@ boolean CL_SendFileRequest(void)
 void PT_RequestFile(SINT8 node)
 {
 	UINT8 *p = netbuffer->u.textcmd;
-	UINT8 id;
 
 	if (client || !cv_downloading.value)
 	{
@@ -432,7 +426,7 @@ void PT_RequestFile(SINT8 node)
 
 	while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow
 	{
-		id = READUINT8(p);
+		UINT8 id = READUINT8(p);
 		if (id == 0xFF)
 			break;
 
@@ -543,9 +537,7 @@ INT32 CL_CheckFiles(void)
 // Load it now
 boolean CL_LoadServerFiles(void)
 {
-	INT32 i;
-
-	for (i = 0; i < fileneedednum; i++)
+	for (INT32 i = 0; i < fileneedednum; i++)
 	{
 		if (fileneeded[i].status == FS_OPEN)
 			continue; // Already loaded
@@ -641,11 +633,10 @@ void AddLuaFileTransfer(const char *filename, const char *mode)
 
 static void SV_PrepareSendLuaFileToNextNode(void)
 {
-	INT32 i;
 	UINT8 success = 1;
 
     // Find a client to send the file to
-	for (i = 1; i < MAXNETNODES; i++)
+	for (INT32 i = 1; i < MAXNETNODES; i++)
 		if (luafiletransfers->nodestatus[i] == LFTNS_WAITING) // Node waiting
 		{
 			// Tell the client we're about to send them the file
@@ -667,12 +658,11 @@ static void SV_PrepareSendLuaFileToNextNode(void)
 void SV_PrepareSendLuaFile(void)
 {
 	char *binfilename;
-	INT32 i;
 
 	luafiletransfers->ongoing = true;
 
 	// Set status to "waiting" for everyone
-	for (i = 0; i < MAXNETNODES; i++)
+	for (INT32 i = 0; i < MAXNETNODES; i++)
 		luafiletransfers->nodestatus[i] = (netnodes[i].ingame ? LFTNS_WAITING : LFTNS_NONE);
 
 	if (FIL_ReadFileOK(luafiletransfers->realfilename))
@@ -1153,7 +1143,6 @@ void PT_FileAck(SINT8 node)
 {
 	fileack_pak *packet = &netbuffer->u.fileack;
 	filetran_t *trans = &transfer[node];
-	INT32 i, j;
 
 	if (client)
 		return;
@@ -1175,11 +1164,11 @@ void PT_FileAck(SINT8 node)
 			trans->dontsenduntil = 0;
 	}
 
-	for (i = 0; i < packet->numsegments; i++)
+	for (INT32 i = 0; i < packet->numsegments; i++)
 	{
 		fileacksegment_t *segment = &packet->segments[i];
 
-		for (j = 0; j < 32; j++)
+		for (INT32 j = 0; j < 32; j++)
 			if (LONG(segment->acks) & (1 << j))
 			{
 				if (LONG(segment->start) * FILEFRAGMENTSIZE >= trans->txlist->size)
@@ -1215,13 +1204,12 @@ void PT_FileReceived(SINT8 node)
 static void SendAckPacket(fileack_pak *packet, UINT8 fileid)
 {
 	size_t packetsize;
-	INT32 i;
 
 	packetsize = sizeof(*packet) + packet->numsegments * sizeof(*packet->segments);
 
 	// Finalise the packet
 	packet->fileid = fileid;
-	for (i = 0; i < packet->numsegments; i++)
+	for (INT32 i = 0; i < packet->numsegments; i++)
 	{
 		packet->segments[i].start = LONG(packet->segments[i].start);
 		packet->segments[i].acks = LONG(packet->segments[i].acks);
@@ -1261,9 +1249,7 @@ static void AddFragmentToAckPacket(fileack_pak *packet, UINT8 iteration, UINT32
 
 void FileReceiveTicker(void)
 {
-	INT32 i;
-
-	for (i = 0; i < fileneedednum; i++)
+	for (INT32 i = 0; i < fileneedednum; i++)
 	{
 		fileneeded_t *file = &fileneeded[i];
 
@@ -1277,8 +1263,7 @@ void FileReceiveTicker(void)
 			if (file->ackresendposition != UINT32_MAX && file->status == FS_DOWNLOADING)
 			{
 				// Acknowledge ~70 MB/s, whichs means the client sends ~18 KB/s
-				INT32 j;
-				for (j = 0; j < 2048; j++)
+				for (INT32 j = 0; j < 2048; j++)
 				{
 					if (file->receivedfragments[file->ackresendposition])
 						AddFragmentToAckPacket(file->ackpacket, file->iteration, file->ackresendposition, i);
@@ -1500,15 +1485,14 @@ void SV_AbortSendFiles(INT32 node)
 
 void CloseNetFile(void)
 {
-	INT32 i;
 	// Is sending?
-	for (i = 0; i < MAXNETNODES; i++)
+	for (INT32 i = 0; i < MAXNETNODES; i++)
 		SV_AbortSendFiles(i);
 
 	// Receiving a file?
 	if (fileneeded)
 	{
-		for (i = 0; i < fileneedednum; i++)
+		for (INT32 i = 0; i < fileneedednum; i++)
 			if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file)
 			{
 				fclose(fileneeded[i].file);
@@ -1541,9 +1525,7 @@ void CloseNetFile(void)
 
 void Command_Downloads_f(void)
 {
-	INT32 node;
-
-	for (node = 0; node < MAXNETNODES; node++)
+	for (INT32 node = 0; node < MAXNETNODES; node++)
 		if (transfer[node].txlist
 		&& transfer[node].txlist->ram == SF_FILE) // Node is downloading a file?
 		{
@@ -1577,14 +1559,11 @@ void Command_Downloads_f(void)
 
 void nameonly(char *s)
 {
-	size_t j, len;
-	void *ns;
-
-	for (j = strlen(s); j != (size_t)-1; j--)
+	for (size_t j = strlen(s); j != (size_t)-1; j--)
 		if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/'))
 		{
-			ns = &(s[j+1]);
-			len = strlen(ns);
+			void *ns = &(s[j+1]);
+			size_t len = strlen(ns);
 #if 0
 			M_Memcpy(s, ns, len+1);
 #else
@@ -1597,9 +1576,9 @@ void nameonly(char *s)
 // Returns the length in characters of the last element of a path.
 size_t nameonlylength(const char *s)
 {
-	size_t j, len = strlen(s);
+	size_t len = strlen(s);
 
-	for (j = len; j != (size_t)-1; j--)
+	for (size_t j = len; j != (size_t)-1; j--)
 		if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/'))
 			return len - j - 1;
 
diff --git a/src/netcode/gamestate.c b/src/netcode/gamestate.c
index 4a3e9f3af8..d284164c05 100644
--- a/src/netcode/gamestate.c
+++ b/src/netcode/gamestate.c
@@ -43,9 +43,7 @@ boolean cl_redownloadinggamestate = false;
 
 boolean SV_ResendingSavegameToAnyone(void)
 {
-	INT32 i;
-
-	for (i = 0; i < MAXNETNODES; i++)
+	for (INT32 i = 0; i < MAXNETNODES; i++)
 		if (netnodes[i].resendingsavegame)
 			return true;
 	return false;
@@ -236,9 +234,7 @@ void CL_LoadReceivedSavegame(boolean reloading)
 
 void CL_ReloadReceivedSavegame(void)
 {
-	INT32 i;
-
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 	{
 		LUA_InvalidatePlayer(&players[i]);
 		sprintf(player_names[i], "Player %d", i + 1);
diff --git a/src/netcode/i_tcp.c b/src/netcode/i_tcp.c
index 6baba6275d..bd950c3559 100644
--- a/src/netcode/i_tcp.c
+++ b/src/netcode/i_tcp.c
@@ -416,24 +416,20 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
   */
 static void cleanupnodes(void)
 {
-	SINT8 j;
-
 	if (!Playing())
 		return;
 
 	// Why can't I start at zero?
-	for (j = 1; j < MAXNETNODES; j++)
+	for (SINT8 j = 1; j < MAXNETNODES; j++)
 		if (!(netnodes[j].ingame || SendingFile(j)))
 			nodeconnected[j] = false;
 }
 
 static SINT8 getfreenode(void)
 {
-	SINT8 j;
-
 	cleanupnodes();
 
-	for (j = 0; j < MAXNETNODES; j++)
+	for (SINT8 j = 0; j < MAXNETNODES; j++)
 		if (!nodeconnected[j])
 		{
 			nodeconnected[j] = true;
@@ -446,7 +442,7 @@ static SINT8 getfreenode(void)
 	  *          downloading a needed wad, but it's better than not letting anyone join...
 	  */
 	/*I_Error("No more free nodes!!1!11!11!!1111\n");
-	for (j = 1; j < MAXNETNODES; j++)
+	for (SINT8 j = 1; j < MAXNETNODES; j++)
 		if (!netnodes[j].ingame)
 			return j;*/
 
@@ -458,9 +454,8 @@ void Command_Numnodes(void)
 {
 	INT32 connected = 0;
 	INT32 ingame = 0;
-	INT32 i;
 
-	for (i = 1; i < MAXNETNODES; i++)
+	for (INT32 i = 1; i < MAXNETNODES; i++)
 	{
 		if (!(nodeconnected[i] || netnodes[i].ingame))
 			continue;
@@ -496,13 +491,13 @@ void Command_Numnodes(void)
 // Returns true if a packet was received from a new node, false in all other cases
 static boolean SOCK_Get(void)
 {
-	size_t i, n;
+	size_t i;
 	int j;
 	ssize_t c;
 	mysockaddr_t fromaddress;
 	socklen_t fromlen;
 
-	for (n = 0; n < mysocketses; n++)
+	for (size_t n = 0; n < mysocketses; n++)
 	{
 		fromlen = (socklen_t)sizeof(fromaddress);
 		c = recvfrom(mysockets[n], (char *)&doomcom->data, MAXPACKETLENGTH, 0,
@@ -563,10 +558,9 @@ static fd_set masterset;
 #ifdef SELECTTEST
 static boolean FD_CPY(fd_set *src, fd_set *dst, SOCKET_TYPE *fd, size_t len)
 {
-	size_t i;
 	boolean testset = false;
 	FD_ZERO(dst);
-	for (i = 0; i < len;i++)
+	for (size_t i = 0; i < len;i++)
 	{
 		if(fd[i] != (SOCKET_TYPE)ERRSOCKET &&
 		   FD_ISSET(fd[i], src) && !FD_ISSET(fd[i], dst)) // no checking for dups
@@ -630,16 +624,15 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr
 static void SOCK_Send(void)
 {
 	ssize_t c = ERRSOCKET;
-	size_t i, j;
 
 	if (!nodeconnected[doomcom->remotenode])
 		return;
 
 	if (doomcom->remotenode == BROADCASTADDR)
 	{
-		for (i = 0; i < mysocketses; i++)
+		for (size_t i = 0; i < mysocketses; i++)
 		{
-			for (j = 0; j < broadcastaddresses; j++)
+			for (size_t j = 0; j < broadcastaddresses; j++)
 			{
 				if (myfamily[i] == broadcastaddress[j].any.sa_family)
 					SOCK_SendToAddr(mysockets[i], &broadcastaddress[j]);
@@ -649,7 +642,7 @@ static void SOCK_Send(void)
 	}
 	else if (nodesocket[doomcom->remotenode] == (SOCKET_TYPE)ERRSOCKET)
 	{
-		for (i = 0; i < mysocketses; i++)
+		for (size_t i = 0; i < mysocketses; i++)
 		{
 			if (myfamily[i] == clientaddress[doomcom->remotenode].any.sa_family)
 				SOCK_SendToAddr(mysockets[i], &clientaddress[doomcom->remotenode]);
@@ -1082,8 +1075,7 @@ boolean I_InitTcpDriver(void)
 
 static void SOCK_CloseSocket(void)
 {
-	size_t i;
-	for (i=0; i < MAXNETNODES+1; i++)
+	for (size_t i=0; i < MAXNETNODES+1; i++)
 	{
 		if (mysockets[i] != (SOCKET_TYPE)ERRSOCKET
 		 && FD_ISSET(mysockets[i], &masterset))
@@ -1154,12 +1146,10 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
 
 static boolean SOCK_OpenSocket(void)
 {
-	size_t i;
-
 	memset(clientaddress, 0, sizeof (clientaddress));
 
 	nodeconnected[0] = true; // always connected to self
-	for (i = 1; i < MAXNETNODES; i++)
+	for (size_t i = 1; i < MAXNETNODES; i++)
 		nodeconnected[i] = false;
 	nodeconnected[BROADCASTADDR] = true;
 	I_NetSend = SOCK_Send;
diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c
index 11b351af73..887b7fa2a4 100644
--- a/src/netcode/net_command.c
+++ b/src/netcode/net_command.c
@@ -90,13 +90,11 @@ void D_FreeTextcmd(tic_t tic)
 
 	if (textcmdtic)
 	{
-		INT32 i;
-
 		// Remove this tic from the list.
 		*tctprev = textcmdtic->next;
 
 		// Free all players.
-		for (i = 0; i < TEXTCMD_HASH_SIZE; i++)
+		for (INT32 i = 0; i < TEXTCMD_HASH_SIZE; i++)
 		{
 			textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i];
 
@@ -174,9 +172,7 @@ UINT8* D_GetTextcmd(tic_t tic, INT32 playernum)
 
 void ExtraDataTicker(void)
 {
-	INT32 i;
-
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 		if (playeringame[i] || i == 0)
 		{
 			UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i);
@@ -221,10 +217,9 @@ void ExtraDataTicker(void)
 // used at txtcmds received to check packetsize bound
 size_t TotalTextCmdPerTic(tic_t tic)
 {
-	INT32 i;
 	size_t total = 1; // num of textcmds in the tic (ntextcmd byte)
 
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 	{
 		UINT8 *textcmd = D_GetExistingTextcmd(tic, i);
 		if ((!i || playeringame[i]) && textcmd)
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
index ed0e28309a..f8ec3c7bd5 100644
--- a/src/netcode/server_connection.c
+++ b/src/netcode/server_connection.c
@@ -55,7 +55,6 @@ static INT32 FindRejoinerNum(SINT8 node)
 	char strippednodeaddress[64];
 	const char *nodeaddress;
 	char *port;
-	INT32 i;
 
 	// Make sure there is no dead dress before proceeding to the stripping
 	if (!I_GetNodeAddress)
@@ -71,7 +70,7 @@ static INT32 FindRejoinerNum(SINT8 node)
 		*port = '\0';
 
 	// Check if any player matches the stripped address
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 	{
 		if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX
 		&& !strcmp(playeraddress[i], strippednodeaddress))
@@ -162,10 +161,9 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
 
 static void SV_SendPlayerInfo(INT32 node)
 {
-	UINT8 i;
 	netbuffer->packettype = PT_PLAYERINFO;
 
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (UINT8 i = 0; i < MAXPLAYERS; i++)
 	{
 		if (!playeringame[i])
 		{
@@ -425,7 +423,6 @@ void PT_ClientJoin(SINT8 node)
 	char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
 	INT32 numplayers = netbuffer->u.clientcfg.localplayers;
 	INT32 rejoinernum;
-	INT32 i;
 
 	// Ignore duplicate packets
 	if (client || netnodes[node].ingame)
@@ -440,7 +437,7 @@ void PT_ClientJoin(SINT8 node)
 		return;
 	}
 
-	for (i = 0; i < numplayers; i++)
+	for (INT32 i = 0; i < numplayers; i++)
 	{
 		strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1);
 		if (!EnsurePlayerNameIsGood(names[i], rejoinernum))
@@ -469,7 +466,7 @@ void PT_ClientJoin(SINT8 node)
 	}
 
 	// Splitscreen can allow 2 players in one node
-	for (i = 0; i < numplayers; i++)
+	for (INT32 i = 0; i < numplayers; i++)
 		SV_AddPlayer(node, names[i]);
 
 	joindelay += cv_joindelay.value * TICRATE;
diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
index bf8c5d21d4..befe8a5cd3 100644
--- a/src/netcode/tic_command.c
+++ b/src/netcode/tic_command.c
@@ -80,11 +80,9 @@ tic_t ExpandTics(INT32 low, INT32 node)
 
 void D_Clearticcmd(tic_t tic)
 {
-	INT32 i;
-
 	D_FreeTextcmd(tic);
 
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 		netcmds[tic%BACKUPTICS][i].angleturn = 0;
 
 	DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS));
@@ -92,13 +90,11 @@ void D_Clearticcmd(tic_t tic)
 
 void D_ResetTiccmds(void)
 {
-	INT32 i;
-
 	memset(&localcmds, 0, sizeof(ticcmd_t));
 	memset(&localcmds2, 0, sizeof(ticcmd_t));
 
 	// Reset the net command list
-	for (i = 0; i < TEXTCMD_HASH_SIZE; i++)
+	for (INT32 i = 0; i < TEXTCMD_HASH_SIZE; i++)
 		while (textcmds[i])
 			D_Clearticcmd(textcmds[i]->tic);
 }
@@ -229,8 +225,7 @@ void PT_ServerTics(SINT8 node, INT32 netconsole)
 	realstart = netbuffer->u.serverpak.starttic;
 	realend = realstart + netbuffer->u.serverpak.numtics;
 
-	if (realend > gametic + CLIENTBACKUPTICS)
-		realend = gametic + CLIENTBACKUPTICS;
+	realend = min(realend, gametic + CLIENTBACKUPTICS);
 	cl_packetmissed = realstart > neededtic;
 
 	if (realstart <= neededtic && realend > neededtic)
@@ -304,9 +299,8 @@ void CL_SendClientCmd(void)
 static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t lasttic)
 {
 	size_t size = BASESERVERTICSSIZE;
-	tic_t tic;
 
-	for (tic = firsttic; tic < lasttic; tic++)
+	for (tic_t tic = firsttic; tic < lasttic; tic++)
 	{
 		size += sizeof (ticcmd_t) * doomcom->numslots;
 		size += TotalTextCmdPerTic(tic);
@@ -344,15 +338,12 @@ static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t l
 // send tic from firstticstosend to maketic-1
 void SV_SendTics(void)
 {
-	tic_t realfirsttic, lasttictosend, i;
-	UINT32 n;
-	size_t packsize;
-	UINT8 *bufpos;
+	tic_t realfirsttic, lasttictosend;
 
 	// send to all client but not to me
 	// for each node create a packet with x tics and send it
 	// x is computed using netnodes[n].supposedtic, max packet size and maketic
-	for (n = 1; n < MAXNETNODES; n++)
+	for (INT32 n = 1; n < MAXNETNODES; n++)
 		if (netnodes[n].ingame)
 		{
 			// assert netnodes[n].supposedtic>=netnodes[n].tic
@@ -367,42 +358,42 @@ void SV_SendTics(void)
 				// (getpacket servertics case)
 				DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n",
 					n, maketic, netnodes[n].supposedtic, netnodes[n].tic));
+
 				realfirsttic = netnodes[n].tic;
+
 				if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3)
 					// all tic are ok
 					continue;
+
 				DEBFILE(va("Sent %d anyway\n", realfirsttic));
 			}
-			if (realfirsttic < firstticstosend)
-				realfirsttic = firstticstosend;
+			realfirsttic = max(realfirsttic, firstticstosend);
 
 			lasttictosend = realfirsttic + SV_CalculateNumTicsForPacket(n, realfirsttic, lasttictosend);
 
-			// Send the tics
+			// Prepare the packet header
 			netbuffer->packettype = PT_SERVERTICS;
 			netbuffer->u.serverpak.starttic = realfirsttic;
 			netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic);
 			netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots);
-			bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds;
 
-			for (i = realfirsttic; i < lasttictosend; i++)
-			{
+			// Fill and send the packet
+			UINT8 *bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds;
+			for (tic_t i = realfirsttic; i < lasttictosend; i++)
 				bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t));
-			}
-
-			// add textcmds
-			for (i = realfirsttic; i < lasttictosend; i++)
-				SV_WriteNetCommandsForTic();
-			packsize = bufpos - (UINT8 *)&(netbuffer->u);
-
+			for (tic_t i = realfirsttic; i < lasttictosend; i++)
+				SV_WriteNetCommandsForTic(i, &bufpos);
+			size_t packsize = bufpos - (UINT8 *)&(netbuffer->u);
 			HSendPacket(n, false, 0, packsize);
+
 			// when tic are too large, only one tic is sent so don't go backward!
 			if (lasttictosend-doomcom->extratics > realfirsttic)
 				netnodes[n].supposedtic = lasttictosend-doomcom->extratics;
 			else
 				netnodes[n].supposedtic = lasttictosend;
-			if (netnodes[n].supposedtic < netnodes[n].tic) netnodes[n].supposedtic = netnodes[n].tic;
+			netnodes[n].supposedtic = max(netnodes[n].supposedtic, netnodes[n].tic);
 		}
+
 	// node 0 is me!
 	netnodes[0].supposedtic = maketic;
 }
@@ -413,7 +404,8 @@ void Local_Maketic(INT32 realtics)
 	D_ProcessEvents(); // menu responder, cons responder,
 	                   // game responder calls HU_Responder, AM_Responder,
 	                   // and G_MapEventsToControls
-	if (!dedicated) rendergametic = gametic;
+	if (!dedicated)
+		rendergametic = gametic;
 	// translate inputs (keyboard/mouse/gamepad) into game controls
 	G_BuildTiccmd(&localcmds, realtics, 1);
 	if (splitscreen || botingame)
@@ -426,9 +418,7 @@ void Local_Maketic(INT32 realtics)
 // create missed tic
 void SV_Maketic(void)
 {
-	INT32 i;
-
-	for (i = 0; i < MAXPLAYERS; i++)
+	for (INT32 i = 0; i < MAXPLAYERS; i++)
 	{
 		if (!playeringame[i])
 			continue;
-- 
GitLab


From 69af7e1ea486d946557004eb308508ac1a56fb79 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sat, 14 Jan 2023 20:02:20 +0100
Subject: [PATCH 290/518] Add missing include directive

---
 src/netcode/net_command.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c
index 887b7fa2a4..efc8bd0efc 100644
--- a/src/netcode/net_command.c
+++ b/src/netcode/net_command.c
@@ -16,6 +16,7 @@
 #include "server_connection.h"
 #include "d_clisrv.h"
 #include "i_net.h"
+#include "../byteptr.h"
 #include "../g_game.h"
 #include "../z_zone.h"
 #include "../doomtype.h"
-- 
GitLab


From 6f536835194bee1dfbd495ba658cf9ab3ca29ef3 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sun, 15 Jan 2023 13:08:31 +0100
Subject: [PATCH 291/518] Cleanup

---
 src/netcode/gamestate.c   |  3 +--
 src/netcode/tic_command.c | 43 +++++++++++++++++++++------------------
 src/netcode/tic_command.h |  2 +-
 3 files changed, 25 insertions(+), 23 deletions(-)

diff --git a/src/netcode/gamestate.c b/src/netcode/gamestate.c
index d284164c05..c1ceb95b5d 100644
--- a/src/netcode/gamestate.c
+++ b/src/netcode/gamestate.c
@@ -242,8 +242,7 @@ void CL_ReloadReceivedSavegame(void)
 
 	CL_LoadReceivedSavegame(true);
 
-	if (neededtic < gametic)
-		neededtic = gametic;
+	neededtic = max(neededtic, gametic);
 	maketic = neededtic;
 
 	ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn;
diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
index befe8a5cd3..0fbf87c0c1 100644
--- a/src/netcode/tic_command.c
+++ b/src/netcode/tic_command.c
@@ -148,8 +148,9 @@ static void CheckConsistancy(SINT8 nodenum, tic_t tic)
 	}
 }
 
-void PT_ClientCmd(SINT8 node, INT32 netconsole)
+void PT_ClientCmd(SINT8 nodenum, INT32 netconsole)
 {
+	netnode_t *node = &netnodes[nodenum];
 	tic_t realend, realstart;
 
 	if (client)
@@ -157,24 +158,24 @@ void PT_ClientCmd(SINT8 node, INT32 netconsole)
 
 	// To save bytes, only the low byte of tic numbers are sent
 	// Use ExpandTics to figure out what the rest of the bytes are
-	realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node);
-	realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node);
+	realstart = ExpandTics(netbuffer->u.clientpak.client_tic, nodenum);
+	realend = ExpandTics(netbuffer->u.clientpak.resendfrom, nodenum);
 
 	if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
 		|| netbuffer->packettype == PT_NODEKEEPALIVEMIS
-		|| netnodes[node].supposedtic < realend)
+		|| node->supposedtic < realend)
 	{
-		netnodes[node].supposedtic = realend;
+		node->supposedtic = realend;
 	}
 	// Discard out of order packet
-	if (netnodes[node].tic > realend)
+	if (node->tic > realend)
 	{
-		DEBFILE(va("out of order ticcmd discarded nettics = %u\n", netnodes[node].tic));
+		DEBFILE(va("out of order ticcmd discarded nettics = %u\n", node->tic));
 		return;
 	}
 
 	// Update the nettics
-	netnodes[node].tic = realend;
+	node->tic = realend;
 
 	// Don't do anything for packets of type NODEKEEPALIVE?
 	if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
@@ -183,19 +184,19 @@ void PT_ClientCmd(SINT8 node, INT32 netconsole)
 
 	// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
 	/// \todo Use a separate cvar for that kind of timeout?
-	netnodes[node].freezetimeout = I_GetTime() + connectiontimeout;
+	node->freezetimeout = I_GetTime() + connectiontimeout;
 
 	// Copy ticcmd
 	G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
 
 	// Splitscreen cmd
 	if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
-		&& netnodes[node].player2 >= 0)
-		G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2],
+		&& node->player2 >= 0)
+		G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)node->player2],
 			&netbuffer->u.client2pak.cmd2, 1);
 
 	CheckTiccmdHacks(netconsole);
-	CheckConsistancy(node, realstart);
+	CheckConsistancy(nodenum, realstart);
 }
 
 void PT_ServerTics(SINT8 node, INT32 netconsole)
@@ -346,9 +347,11 @@ void SV_SendTics(void)
 	for (INT32 n = 1; n < MAXNETNODES; n++)
 		if (netnodes[n].ingame)
 		{
-			// assert netnodes[n].supposedtic>=netnodes[n].tic
-			realfirsttic = netnodes[n].supposedtic;
-			lasttictosend = min(maketic, netnodes[n].tic + CLIENTBACKUPTICS);
+			netnode_t *node = netnodes[n];
+
+			// assert node->supposedtic>=node->tic
+			realfirsttic = node->supposedtic;
+			lasttictosend = min(maketic, node->tic + CLIENTBACKUPTICS);
 
 			if (realfirsttic >= lasttictosend)
 			{
@@ -357,9 +360,9 @@ void SV_SendTics(void)
 				// packet detection work when we have received packet with firsttic > neededtic
 				// (getpacket servertics case)
 				DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n",
-					n, maketic, netnodes[n].supposedtic, netnodes[n].tic));
+					n, maketic, node->supposedtic, node->tic));
 
-				realfirsttic = netnodes[n].tic;
+				realfirsttic = node->tic;
 
 				if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3)
 					// all tic are ok
@@ -388,10 +391,10 @@ void SV_SendTics(void)
 
 			// when tic are too large, only one tic is sent so don't go backward!
 			if (lasttictosend-doomcom->extratics > realfirsttic)
-				netnodes[n].supposedtic = lasttictosend-doomcom->extratics;
+				node->supposedtic = lasttictosend-doomcom->extratics;
 			else
-				netnodes[n].supposedtic = lasttictosend;
-			netnodes[n].supposedtic = max(netnodes[n].supposedtic, netnodes[n].tic);
+				node->supposedtic = lasttictosend;
+			node->supposedtic = max(node->supposedtic, node->tic);
 		}
 
 	// node 0 is me!
diff --git a/src/netcode/tic_command.h b/src/netcode/tic_command.h
index d19f4c1aac..289750fb30 100644
--- a/src/netcode/tic_command.h
+++ b/src/netcode/tic_command.h
@@ -31,7 +31,7 @@ tic_t ExpandTics(INT32 low, INT32 node);
 void D_Clearticcmd(tic_t tic);
 void D_ResetTiccmds(void);
 
-void PT_ClientCmd(SINT8 node, INT32 netconsole);
+void PT_ClientCmd(SINT8 nodenum, INT32 netconsole);
 void PT_ServerTics(SINT8 node, INT32 netconsole);
 
 // send the client packet to the server
-- 
GitLab


From 7f4e82e7d9e1fdb5f01b3381c0f70d3c70eea7dd Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sun, 15 Jan 2023 13:10:23 +0100
Subject: [PATCH 292/518] Cleanup comments

---
 src/netcode/client_connection.c | 41 ++++++++++++++-------------------
 src/netcode/d_clisrv.c          | 11 ++++-----
 src/netcode/protocol.h          |  3 +--
 src/netcode/tic_command.c       | 35 ++++++++++++++--------------
 4 files changed, 39 insertions(+), 51 deletions(-)

diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c
index faa91b6d7c..d363d7d5ad 100644
--- a/src/netcode/client_connection.c
+++ b/src/netcode/client_connection.c
@@ -34,7 +34,7 @@
 
 cl_mode_t cl_mode = CL_SEARCHING;
 static UINT16 cl_lastcheckedfilecount = 0;	// used for full file list
-boolean serverisfull = false; //lets us be aware if the server was full after we check files, but before downloading, so we can ask if the user still wants to download or not
+boolean serverisfull = false; // lets us be aware if the server was full after we check files, but before downloading, so we can ask if the user still wants to download or not
 tic_t firstconnectattempttime = 0;
 UINT8 mynode;
 static void *snake = NULL;
@@ -137,7 +137,7 @@ static inline void CL_DrawConnectionStatus(void)
 
 			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort");
 
-			//ima just count files here
+			// ima just count files here
 			if (fileneeded)
 			{
 				for (INT32 i = 0; i < fileneedednum; i++)
@@ -248,7 +248,7 @@ boolean CL_SendJoin(void)
 
 	CleanupPlayerName(consoleplayer, cv_playername.zstring);
 	if (splitscreen)
-		CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */
+		CleanupPlayerName(1, cv_playername2.zstring); // 1 is a HACK? oh no
 
 	strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME);
 	strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME);
@@ -306,14 +306,14 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
 		if (serverlistcount >= MAXSERVERLIST)
 			return; // list full
 
-		/* check it later if connecting to this one */
+		// check it later if connecting to this one
 		if (node != servernode)
 		{
 			if (info->_255 != 255)
-				return;/* old packet format */
+				return; // Old packet format
 
 			if (info->packetversion != PACKETVERSION)
-				return;/* old new packet format */
+				return; // Old new packet format
 
 			if (info->version != VERSION)
 				return; // Not same version.
@@ -322,7 +322,7 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
 				return; // Close, but no cigar.
 
 			if (strcmp(info->application, SRB2APPLICATION))
-				return;/* that's a different mod */
+				return; // That's a different mod
 		}
 
 		i = serverlistcount++;
@@ -380,7 +380,7 @@ Fetch_servers_thread (struct Fetch_servers_ctx *ctx)
 
 	free(ctx);
 }
-#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/
+#endif // defined (MASTERSERVER) && defined (HAVE_THREADS)
 
 void CL_QueryServerList (msg_server_t *server_list)
 {
@@ -389,7 +389,7 @@ void CL_QueryServerList (msg_server_t *server_list)
 		// Make sure MS version matches our own, to
 		// thwart nefarious servers who lie to the MS.
 
-		/* lol bruh, that version COMES from the servers */
+		// lol bruh, that version COMES from the servers
 		//if (strcmp(version, server_list[i].version) == 0)
 		{
 			INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port);
@@ -441,7 +441,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
 
 		ctx = malloc(sizeof *ctx);
 
-		/* This called from M_Refresh so I don't use a mutex */
+		// This called from M_Refresh so I don't use a mutex
 		m_waiting_mode = M_WAITING_SERVERS;
 
 		I_lock_mutex(&ms_QueryId_mutex);
@@ -465,7 +465,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
 		}
 #endif
 	}
-#endif/*MASTERSERVER*/
+#endif // MASTERSERVER
 }
 
 static void M_ConfirmConnect(event_t *ev)
@@ -619,7 +619,7 @@ static const char * InvalidServerReason (serverinfo_pak *info)
 {
 #define EOT "\nPress ESC\n"
 
-	/* magic number for new packet format */
+	// Magic number for new packet format
 	if (info->_255 != 255)
 	{
 		return
@@ -691,10 +691,10 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
 {
 	INT32 i;
 
-	// serverlist is updated by GetPacket function
+	// serverlist is updated by GetPackets
 	if (serverlistcount > 0)
 	{
-		// this can be a responce to our broadcast request
+		// This can be a response to our broadcast request
 		if (servernode == -1 || servernode >= MAXNETNODES)
 		{
 			i = 0;
@@ -819,7 +819,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 			if (CL_LoadServerFiles())
 			{
 				FreeFileNeeded();
-				*asksent = 0; //This ensure the first join ask is right away
+				*asksent = 0; // This ensures the first join request is right away
 				firstconnectattempttime = I_GetTime();
 				cl_mode = CL_ASKJOIN;
 			}
@@ -841,9 +841,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 				return false;
 			}
 
-			// prepare structures to save the file
-			// WARNING: this can be useless in case of server not in GS_LEVEL
-			// but since the network layer doesn't provide ordered packets...
+			// Prepare structures to save the file
 			CL_PrepareDownloadSaveGame(tmpsave);
 
 			if (I_GetTime() >= *asksent && CL_SendJoin())
@@ -954,11 +952,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 
 #define TMPSAVENAME "$$$.sav"
 
-/** Use adaptive send using net_bandwidth and stat.sendbytes
-  *
-  * \todo Better description...
-  *
-  */
 void CL_ConnectToServer(void)
 {
 	INT32 pnumnodes, nodewaited = doomcom->numnodes, i;
@@ -1158,7 +1151,7 @@ void PT_ServerCFG(SINT8 node)
 	DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode));
 
 	/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
-	///       Shouldn't them be downloaded even at intermission time?
+	///       Shouldn't they be downloaded even at intermission time?
 	///       Also, according to PT_ClientJoin, the server will send the savegame even during intermission...
 	if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* ||
 		netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 105526dd28..18eae580cd 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -1252,8 +1252,8 @@ void NetUpdate(void)
 
 	GetPackets(); // get packet from client or from server
 
-	// client send the command after a receive of the server
-	// the server send before because in single player is beter
+	// The client sends the command after receiving from the server
+	// The server sends it before because this is better in single player
 
 #ifdef MASTERSERVER
 	MasterClient_Ticker(); // Acking the Master Server
@@ -1402,7 +1402,7 @@ SINT8 nametonum(const char *name)
 	return -1;
 }
 
-// is there a game running
+// Is there a game running?
 boolean Playing(void)
 {
 	return (server && serverrunning) || (client && cl_mode == CL_CONNECTED);
@@ -1423,11 +1423,8 @@ INT32 D_NumPlayers(void)
 }
 
 //
-// NetUpdate
-// Builds ticcmds for console player,
-// sends out a packet
+// Consistancy
 //
-// no more use random generator, because at very first tic isn't yet synchronized
 // Note: It is called consistAncy on purpose.
 //
 INT16 Consistancy(void)
diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h
index 374cc5bffd..9866e4c5a5 100644
--- a/src/netcode/protocol.h
+++ b/src/netcode/protocol.h
@@ -123,13 +123,12 @@ typedef struct
 #endif
 
 // Server to client packet
-// this packet is too large
 typedef struct
 {
 	tic_t starttic;
 	UINT8 numtics;
 	UINT8 numslots; // "Slots filled": Highest player number in use plus one.
-	ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large
+	ticcmd_t cmds[45];
 } ATTRPACK servertics_pak;
 
 typedef struct
diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
index 0fbf87c0c1..620a10f7a2 100644
--- a/src/netcode/tic_command.c
+++ b/src/netcode/tic_command.c
@@ -24,8 +24,8 @@
 #include "../doomstat.h"
 #include "../doomtype.h"
 
-tic_t firstticstosend; // min of the nettics
-tic_t tictoclear = 0; // optimize d_clearticcmd
+tic_t firstticstosend; // Smallest netnode.tic
+tic_t tictoclear = 0; // Optimize D_ClearTiccmd
 ticcmd_t localcmds;
 ticcmd_t localcmds2;
 boolean cl_packetmissed;
@@ -312,10 +312,9 @@ static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t l
 				sizeu1(size), tic, firsttic, lasttic));
 			lasttic = tic;
 
-			// too bad: too much player have send extradata and there is too
-			//          much data in one tic.
-			// To avoid it put the data on the next tic. (see getpacket
-			// textcmd case) but when numplayer changes the computation can be different
+			// Too bad: too many players have sent extra data
+			// and there is too much data for a single tic.
+			// To avoid that, keep the data for the next tic (see PT_TEXTCMD).
 			if (lasttic == firsttic)
 			{
 				if (size > MAXPACKETLENGTH)
@@ -335,15 +334,15 @@ static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t l
 	return lasttic - firsttic;
 }
 
-// send the server packet
-// send tic from firstticstosend to maketic-1
+// Sends the server packet
+// Sends tic/net commands from firstticstosend to maketic-1
 void SV_SendTics(void)
 {
 	tic_t realfirsttic, lasttictosend;
 
-	// send to all client but not to me
-	// for each node create a packet with x tics and send it
-	// x is computed using netnodes[n].supposedtic, max packet size and maketic
+	// Send to all clients except yourself
+	// For each node, create a packet with x tics and send it
+	// x is computed using node.supposedtic, max packet size and maketic
 	for (INT32 n = 1; n < MAXNETNODES; n++)
 		if (netnodes[n].ingame)
 		{
@@ -355,17 +354,17 @@ void SV_SendTics(void)
 
 			if (realfirsttic >= lasttictosend)
 			{
-				// well we have sent all tics we will so use extrabandwidth
-				// to resent packet that are supposed lost (this is necessary since lost
-				// packet detection work when we have received packet with firsttic > neededtic
-				// (getpacket servertics case)
+				// Well, we have sent all the tics, so we will use extra bandwidth
+				// to resend packets that are supposed lost.
+				// This is necessary since lost packet detection
+				// works when we receive a packet with firsttic > neededtic (PT_SERVERTICS)
 				DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n",
 					n, maketic, node->supposedtic, node->tic));
 
 				realfirsttic = node->tic;
 
 				if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3)
-					// all tic are ok
+					// All tics are Ok
 					continue;
 
 				DEBFILE(va("Sent %d anyway\n", realfirsttic));
@@ -389,7 +388,7 @@ void SV_SendTics(void)
 			size_t packsize = bufpos - (UINT8 *)&(netbuffer->u);
 			HSendPacket(n, false, 0, packsize);
 
-			// when tic are too large, only one tic is sent so don't go backward!
+			// When tics are too large, only one tic is sent so don't go backwards!
 			if (lasttictosend-doomcom->extratics > realfirsttic)
 				node->supposedtic = lasttictosend-doomcom->extratics;
 			else
@@ -450,6 +449,6 @@ void SV_Maketic(void)
 		}
 	}
 
-	// all tic are now proceed make the next
+	// All tics have been processed, make the next
 	maketic++;
 }
-- 
GitLab


From 97311dc5b00ab232a6e898c085c20d2f6bdbfa3b Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Thu, 27 Jul 2023 19:15:35 -0300
Subject: [PATCH 293/518] Use separate table for maskedtexturecol

---
 src/r_defs.h  |   4 +-
 src/r_main.c  |   1 +
 src/r_plane.c |   6 -
 src/r_plane.h |   3 -
 src/r_segs.c  | 647 +++++++++++++++++++++++++-------------------------
 src/r_segs.h  |   1 +
 6 files changed, 324 insertions(+), 338 deletions(-)

diff --git a/src/r_defs.h b/src/r_defs.h
index a9b9a4a083..dfd2d6d708 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -757,12 +757,12 @@ typedef struct drawseg_s
 	// Pointers to lists for sprite clipping, all three adjusted so [x1] is first value.
 	INT16 *sprtopclip;
 	INT16 *sprbottomclip;
-	INT16 *maskedtexturecol;
+	fixed_t *maskedtexturecol;
 
 	struct visplane_s *ffloorplanes[MAXFFLOORS];
 	INT32 numffloorplanes;
 	struct ffloor_s *thicksides[MAXFFLOORS];
-	INT16 *thicksidecol;
+	fixed_t *thicksidecol;
 	INT32 numthicksides;
 	fixed_t frontscale[MAXVIDWIDTH];
 
diff --git a/src/r_main.c b/src/r_main.c
index 55bb9c4ffe..952171405c 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -1479,6 +1479,7 @@ void R_RenderPlayerView(player_t *player)
 		R_ClearClipSegs();
 	}
 	R_ClearDrawSegs();
+	R_ClearSegTables();
 	R_ClearSprites();
 	Portal_InitList();
 
diff --git a/src/r_plane.c b/src/r_plane.c
index c568484b6e..29ce26b292 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -53,10 +53,6 @@ INT32 numffloors;
 #define visplane_hash(picnum,lightlevel,height) \
   ((unsigned)((picnum)*3+(lightlevel)+(height)*7) & VISPLANEHASHMASK)
 
-//SoM: 3/23/2000: Use boom opening limit removal
-size_t maxopenings;
-INT16 *openings, *lastopening; /// \todo free leak
-
 //
 // Clip values are the solid pixel bounding the range.
 //  floorclip starts out SCREENHEIGHT
@@ -366,8 +362,6 @@ void R_ClearPlanes(void)
 		freehead = &(*freehead)->next;
 	}
 
-	lastopening = openings;
-
 	// texture calculation
 	memset(cachedheight, 0, sizeof (cachedheight));
 }
diff --git a/src/r_plane.h b/src/r_plane.h
index 9870a43e26..917e8b041b 100644
--- a/src/r_plane.h
+++ b/src/r_plane.h
@@ -60,9 +60,6 @@ extern visplane_t *floorplane;
 extern visplane_t *ceilingplane;
 
 // Visplane related.
-extern INT16 *lastopening, *openings;
-extern size_t maxopenings;
-
 extern INT16 floorclip[MAXVIDWIDTH], ceilingclip[MAXVIDWIDTH];
 extern fixed_t frontscale[MAXVIDWIDTH], yslopetab[MAXVIDHEIGHT*16];
 extern fixed_t cachedheight[MAXVIDHEIGHT];
diff --git a/src/r_segs.c b/src/r_segs.c
index facab62ab7..9ee3bcfec6 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -71,9 +71,22 @@ static fixed_t topfrac, topstep;
 static fixed_t bottomfrac, bottomstep;
 
 static lighttable_t **walllights;
-static INT16 *maskedtexturecol;
+static fixed_t *maskedtexturecol;
 static fixed_t *maskedtextureheight = NULL;
 
+//SoM: 3/23/2000: Use boom opening limit removal
+static size_t numopenings;
+static INT16 *openings, *lastopening;
+
+static size_t texturecolumntablesize;
+static fixed_t *texturecolumntable, *curtexturecolumntable;
+
+void R_ClearSegTables(void)
+{
+	lastopening = openings;
+	curtexturecolumntable = texturecolumntable;
+}
+
 // ==========================================================================
 // R_RenderMaskedSegRange
 // ==========================================================================
@@ -350,170 +363,115 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 				dc_texturemid += (textureheight[texnum])*times + textureheight[texnum];
 			else
 				dc_texturemid -= (textureheight[texnum])*times;
-			// calculate lighting
-			if (maskedtexturecol[dc_x] != INT16_MAX)
+
+			// Check for overflows first
+			overflow_test = (INT64)centeryfrac - (((INT64)dc_texturemid*spryscale)>>FRACBITS);
+			if (overflow_test < 0) overflow_test = -overflow_test;
+			if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL)
 			{
-				// Check for overflows first
-				overflow_test = (INT64)centeryfrac - (((INT64)dc_texturemid*spryscale)>>FRACBITS);
-				if (overflow_test < 0) overflow_test = -overflow_test;
-				if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL)
+				// Eh, no, go away, don't waste our time
+				if (dc_numlights)
 				{
-					// Eh, no, go away, don't waste our time
-					if (dc_numlights)
+					for (i = 0; i < dc_numlights; i++)
 					{
-						for (i = 0; i < dc_numlights; i++)
-						{
-							rlight = &dc_lightlist[i];
-							rlight->height += rlight->heightstep;
-						}
+						rlight = &dc_lightlist[i];
+						rlight->height += rlight->heightstep;
 					}
-					spryscale += rw_scalestep;
-					continue;
 				}
+				spryscale += rw_scalestep;
+				continue;
+			}
 
-				if (dc_numlights)
-				{
-					lighttable_t **xwalllights;
-
-					sprbotscreen = INT32_MAX;
-					sprtopscreen = windowtop = (centeryfrac - FixedMul(dc_texturemid, spryscale));
-
-					realbot = windowbottom = FixedMul(textureheight[texnum], spryscale) + sprtopscreen;
-					dc_iscale = 0xffffffffu / (unsigned)spryscale;
+			// calculate lighting
+			if (dc_numlights)
+			{
+				lighttable_t **xwalllights;
 
-					// draw the texture
-					col = (column_t *)((UINT8 *)R_GetColumn(texnum, maskedtexturecol[dc_x]) - 3);
+				sprbotscreen = INT32_MAX;
+				sprtopscreen = windowtop = (centeryfrac - FixedMul(dc_texturemid, spryscale));
 
-					for (i = 0; i < dc_numlights; i++)
-					{
-						rlight = &dc_lightlist[i];
+				realbot = windowbottom = FixedMul(textureheight[texnum], spryscale) + sprtopscreen;
+				dc_iscale = 0xffffffffu / (unsigned)spryscale;
 
-						if ((rlight->flags & FOF_NOSHADE))
-							continue;
+				// draw the texture
+				col = (column_t *)((UINT8 *)R_GetColumn(texnum, (maskedtexturecol[dc_x] >> FRACBITS)) - 3);
 
-						if (rlight->lightnum < 0)
-							xwalllights = scalelight[0];
-						else if (rlight->lightnum >= LIGHTLEVELS)
-							xwalllights = scalelight[LIGHTLEVELS-1];
-						else
-							xwalllights = scalelight[rlight->lightnum];
+				for (i = 0; i < dc_numlights; i++)
+				{
+					rlight = &dc_lightlist[i];
 
-						pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
+					if ((rlight->flags & FOF_NOSHADE))
+						continue;
 
-						if (pindex >= MAXLIGHTSCALE)
-							pindex = MAXLIGHTSCALE - 1;
+					if (rlight->lightnum < 0)
+						xwalllights = scalelight[0];
+					else if (rlight->lightnum >= LIGHTLEVELS)
+						xwalllights = scalelight[LIGHTLEVELS-1];
+					else
+						xwalllights = scalelight[rlight->lightnum];
 
-						if (rlight->extra_colormap)
-							rlight->rcolormap = rlight->extra_colormap->colormap + (xwalllights[pindex] - colormaps);
-						else
-							rlight->rcolormap = xwalllights[pindex];
+					pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
 
-						height = rlight->height;
-						rlight->height += rlight->heightstep;
+					if (pindex >= MAXLIGHTSCALE)
+						pindex = MAXLIGHTSCALE - 1;
 
-						if (height <= windowtop)
-						{
-							dc_colormap = rlight->rcolormap;
-							continue;
-						}
+					if (rlight->extra_colormap)
+						rlight->rcolormap = rlight->extra_colormap->colormap + (xwalllights[pindex] - colormaps);
+					else
+						rlight->rcolormap = xwalllights[pindex];
 
-						windowbottom = height;
-						if (windowbottom >= realbot)
-						{
-							windowbottom = realbot;
-							colfunc_2s(col);
-							for (i++; i < dc_numlights; i++)
-							{
-								rlight = &dc_lightlist[i];
-								rlight->height += rlight->heightstep;
-							}
+					height = rlight->height;
+					rlight->height += rlight->heightstep;
 
-							continue;
-						}
-						colfunc_2s(col);
-						windowtop = windowbottom + 1;
+					if (height <= windowtop)
+					{
 						dc_colormap = rlight->rcolormap;
+						continue;
 					}
-					windowbottom = realbot;
-					if (windowtop < windowbottom)
+
+					windowbottom = height;
+					if (windowbottom >= realbot)
+					{
+						windowbottom = realbot;
 						colfunc_2s(col);
+						for (i++; i < dc_numlights; i++)
+						{
+							rlight = &dc_lightlist[i];
+							rlight->height += rlight->heightstep;
+						}
 
-					spryscale += rw_scalestep;
-					continue;
+						continue;
+					}
+					colfunc_2s(col);
+					windowtop = windowbottom + 1;
+					dc_colormap = rlight->rcolormap;
 				}
+				windowbottom = realbot;
+				if (windowtop < windowbottom)
+					colfunc_2s(col);
 
-				// calculate lighting
-				pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
-
-				if (pindex >= MAXLIGHTSCALE)
-					pindex = MAXLIGHTSCALE - 1;
-
-				dc_colormap = walllights[pindex];
-
-				if (frontsector->extra_colormap)
-					dc_colormap = frontsector->extra_colormap->colormap + (dc_colormap - colormaps);
-
-				sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
-				dc_iscale = 0xffffffffu / (unsigned)spryscale;
-
-				// draw the texture
-				col = (column_t *)((UINT8 *)R_GetColumn(texnum, maskedtexturecol[dc_x]) - 3);
-
-#if 0 // Disabling this allows inside edges to render below the planes, for until the clipping is fixed to work right when POs are near the camera. -Red
-				if (curline->dontrenderme && curline->polyseg && (curline->polyseg->flags & POF_RENDERPLANES))
-				{
-					fixed_t my_topscreen;
-					fixed_t my_bottomscreen;
-					fixed_t my_yl, my_yh;
+				spryscale += rw_scalestep;
+				continue;
+			}
 
-					my_topscreen = sprtopscreen + spryscale*col->topdelta;
-					my_bottomscreen = sprbotscreen == INT32_MAX ? my_topscreen + spryscale*col->length
-					                                         : sprbotscreen + spryscale*col->length;
+			// calculate lighting
+			pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
 
-					my_yl = (my_topscreen+FRACUNIT-1)>>FRACBITS;
-					my_yh = (my_bottomscreen-1)>>FRACBITS;
-	//				CONS_Debug(DBG_RENDER, "my_topscreen: %d\nmy_bottomscreen: %d\nmy_yl: %d\nmy_yh: %d\n", my_topscreen, my_bottomscreen, my_yl, my_yh);
+			if (pindex >= MAXLIGHTSCALE)
+				pindex = MAXLIGHTSCALE - 1;
 
-					if (numffloors)
-					{
-						INT32 top = my_yl;
-						INT32 bottom = my_yh;
+			dc_colormap = walllights[pindex];
 
-						for (i = 0; i < numffloors; i++)
-						{
-							if (!ffloor[i].polyobj || ffloor[i].polyobj != curline->polyseg)
-								continue;
+			if (frontsector->extra_colormap)
+				dc_colormap = frontsector->extra_colormap->colormap + (dc_colormap - colormaps);
 
-							if (ffloor[i].height < viewz)
-							{
-								INT32 top_w = ffloor[i].plane->top[dc_x];
+			sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
+			dc_iscale = 0xffffffffu / (unsigned)spryscale;
 
-	//							CONS_Debug(DBG_RENDER, "Leveltime : %d\n", leveltime);
-	//							CONS_Debug(DBG_RENDER, "Top is %d, top_w is %d\n", top, top_w);
-								if (top_w < top)
-								{
-									ffloor[i].plane->top[dc_x] = (INT16)top;
-									ffloor[i].plane->picnum = 0;
-								}
-	//							CONS_Debug(DBG_RENDER, "top_w is now %d\n", ffloor[i].plane->top[dc_x]);
-							}
-							else if (ffloor[i].height > viewz)
-							{
-								INT32 bottom_w = ffloor[i].plane->bottom[dc_x];
+			// draw the texture
+			col = (column_t *)((UINT8 *)R_GetColumn(texnum, (maskedtexturecol[dc_x] >> FRACBITS)) - 3);
+			colfunc_2s(col);
 
-								if (bottom_w > bottom)
-								{
-									ffloor[i].plane->bottom[dc_x] = (INT16)bottom;
-									ffloor[i].plane->picnum = 0;
-								}
-							}
-						}
-					}
-				}
-				else
-#endif
-					colfunc_2s(col);
-			}
 			spryscale += rw_scalestep;
 		}
 	}
@@ -857,183 +815,182 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	// draw the columns
 	for (dc_x = x1; dc_x <= x2; dc_x++)
 	{
-		if (maskedtexturecol[dc_x] != INT16_MAX)
+		// skew FOF walls
+		if (ffloortextureslide)
 		{
-			if (ffloortextureslide) { // skew FOF walls
-				if (oldx != -1)
-					dc_texturemid += FixedMul(ffloortextureslide, (maskedtexturecol[oldx]-maskedtexturecol[dc_x])<<FRACBITS);
-				oldx = dc_x;
-			}
-			// Calculate bounds
-			// clamp the values if necessary to avoid overflows and rendering glitches caused by them
+			if (oldx != -1)
+				dc_texturemid += FixedMul(ffloortextureslide, maskedtexturecol[oldx]-maskedtexturecol[dc_x]);
+			oldx = dc_x;
+		}
 
-			if      (top_frac > (INT64)CLAMPMAX) sprtopscreen = windowtop = CLAMPMAX;
-			else if (top_frac > (INT64)CLAMPMIN) sprtopscreen = windowtop = (fixed_t)top_frac;
-			else                                 sprtopscreen = windowtop = CLAMPMIN;
-			if      (bottom_frac > (INT64)CLAMPMAX) sprbotscreen = windowbottom = CLAMPMAX;
-			else if (bottom_frac > (INT64)CLAMPMIN) sprbotscreen = windowbottom = (fixed_t)bottom_frac;
-			else                                    sprbotscreen = windowbottom = CLAMPMIN;
+		// Calculate bounds
+		// clamp the values if necessary to avoid overflows and rendering glitches caused by them
+		if      (top_frac > (INT64)CLAMPMAX) sprtopscreen = windowtop = CLAMPMAX;
+		else if (top_frac > (INT64)CLAMPMIN) sprtopscreen = windowtop = (fixed_t)top_frac;
+		else                                 sprtopscreen = windowtop = CLAMPMIN;
+		if      (bottom_frac > (INT64)CLAMPMAX) sprbotscreen = windowbottom = CLAMPMAX;
+		else if (bottom_frac > (INT64)CLAMPMIN) sprbotscreen = windowbottom = (fixed_t)bottom_frac;
+		else                                    sprbotscreen = windowbottom = CLAMPMIN;
 
-			top_frac += top_step;
-			bottom_frac += bottom_step;
+		top_frac += top_step;
+		bottom_frac += bottom_step;
 
-			// SoM: If column is out of range, why bother with it??
-			if (windowbottom < topbounds || windowtop > bottombounds)
+		// SoM: If column is out of range, why bother with it??
+		if (windowbottom < topbounds || windowtop > bottombounds)
+		{
+			if (dc_numlights)
 			{
-				if (dc_numlights)
+				for (i = 0; i < dc_numlights; i++)
 				{
-					for (i = 0; i < dc_numlights; i++)
-					{
-						rlight = &dc_lightlist[i];
-						rlight->height += rlight->heightstep;
-						if (rlight->flags & FOF_CUTLEVEL)
-							rlight->botheight += rlight->botheightstep;
-					}
+					rlight = &dc_lightlist[i];
+					rlight->height += rlight->heightstep;
+					if (rlight->flags & FOF_CUTLEVEL)
+						rlight->botheight += rlight->botheightstep;
 				}
-				spryscale += rw_scalestep;
-				continue;
 			}
+			spryscale += rw_scalestep;
+			continue;
+		}
 
-			dc_iscale = 0xffffffffu / (unsigned)spryscale;
+		dc_iscale = 0xffffffffu / (unsigned)spryscale;
 
-			// Get data for the column
-			col = (column_t *)((UINT8 *)R_GetColumn(texnum,maskedtexturecol[dc_x]) - 3);
+		// Get data for the column
+		col = (column_t *)((UINT8 *)R_GetColumn(texnum, (maskedtexturecol[dc_x] >> FRACBITS)) - 3);
 
-			// SoM: New code does not rely on R_DrawColumnShadowed_8 which
-			// will (hopefully) put less strain on the stack.
-			if (dc_numlights)
-			{
-				lighttable_t **xwalllights;
-				fixed_t height;
-				fixed_t bheight = 0;
-				INT32 solid = 0;
-				INT32 lighteffect = 0;
+		// SoM: New code does not rely on R_DrawColumnShadowed_8 which
+		// will (hopefully) put less strain on the stack.
+		if (dc_numlights)
+		{
+			lighttable_t **xwalllights;
+			fixed_t height;
+			fixed_t bheight = 0;
+			INT32 solid = 0;
+			INT32 lighteffect = 0;
 
-				for (i = 0; i < dc_numlights; i++)
+			for (i = 0; i < dc_numlights; i++)
+			{
+				// Check if the current light effects the colormap/lightlevel
+				rlight = &dc_lightlist[i];
+				lighteffect = !(dc_lightlist[i].flags & FOF_NOSHADE);
+				if (lighteffect)
 				{
-					// Check if the current light effects the colormap/lightlevel
-					rlight = &dc_lightlist[i];
-					lighteffect = !(dc_lightlist[i].flags & FOF_NOSHADE);
-					if (lighteffect)
-					{
-						lightnum = rlight->lightnum;
-
-						if (lightnum < 0)
-							xwalllights = scalelight[0];
-						else if (lightnum >= LIGHTLEVELS)
-							xwalllights = scalelight[LIGHTLEVELS-1];
-						else
-							xwalllights = scalelight[lightnum];
-
-						pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
+					lightnum = rlight->lightnum;
 
-						if (pindex >= MAXLIGHTSCALE)
-							pindex = MAXLIGHTSCALE-1;
+					if (lightnum < 0)
+						xwalllights = scalelight[0];
+					else if (lightnum >= LIGHTLEVELS)
+						xwalllights = scalelight[LIGHTLEVELS-1];
+					else
+						xwalllights = scalelight[lightnum];
 
-						if (pfloor->fofflags & FOF_FOG)
-						{
-							if (pfloor->master->frontsector->extra_colormap)
-								rlight->rcolormap = pfloor->master->frontsector->extra_colormap->colormap + (xwalllights[pindex] - colormaps);
-							else
-								rlight->rcolormap = xwalllights[pindex];
-						}
-						else
-						{
-							if (rlight->extra_colormap)
-								rlight->rcolormap = rlight->extra_colormap->colormap + (xwalllights[pindex] - colormaps);
-							else
-								rlight->rcolormap = xwalllights[pindex];
-						}
-					}
+					pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
 
-					solid = 0; // don't carry over solid-cutting flag from the previous light
+					if (pindex >= MAXLIGHTSCALE)
+						pindex = MAXLIGHTSCALE-1;
 
-					// Check if the current light can cut the current 3D floor.
-					if (rlight->flags & FOF_CUTSOLIDS && !(pfloor->fofflags & FOF_EXTRA))
-						solid = 1;
-					else if (rlight->flags & FOF_CUTEXTRA && pfloor->fofflags & FOF_EXTRA)
+					if (pfloor->fofflags & FOF_FOG)
 					{
-						if (rlight->flags & FOF_EXTRA)
-						{
-							// The light is from an extra 3D floor... Check the flags so
-							// there are no undesired cuts.
-							if ((rlight->flags & (FOF_FOG|FOF_SWIMMABLE)) == (pfloor->fofflags & (FOF_FOG|FOF_SWIMMABLE)))
-								solid = 1;
-						}
+						if (pfloor->master->frontsector->extra_colormap)
+							rlight->rcolormap = pfloor->master->frontsector->extra_colormap->colormap + (xwalllights[pindex] - colormaps);
 						else
-							solid = 1;
+							rlight->rcolormap = xwalllights[pindex];
 					}
 					else
-						solid = 0;
-
-					height = rlight->height;
-					rlight->height += rlight->heightstep;
-
-					if (solid)
 					{
-						bheight = rlight->botheight - (FRACUNIT >> 1);
-						rlight->botheight += rlight->botheightstep;
+						if (rlight->extra_colormap)
+							rlight->rcolormap = rlight->extra_colormap->colormap + (xwalllights[pindex] - colormaps);
+						else
+							rlight->rcolormap = xwalllights[pindex];
 					}
+				}
 
-					if (height <= windowtop)
-					{
-						if (lighteffect)
-							dc_colormap = rlight->rcolormap;
-						if (solid && windowtop < bheight)
-							windowtop = bheight;
-						continue;
-					}
+				solid = 0; // don't carry over solid-cutting flag from the previous light
 
-					windowbottom = height;
-					if (windowbottom >= sprbotscreen)
+				// Check if the current light can cut the current 3D floor.
+				if (rlight->flags & FOF_CUTSOLIDS && !(pfloor->fofflags & FOF_EXTRA))
+					solid = 1;
+				else if (rlight->flags & FOF_CUTEXTRA && pfloor->fofflags & FOF_EXTRA)
+				{
+					if (rlight->flags & FOF_EXTRA)
 					{
-						windowbottom = sprbotscreen;
-						// draw the texture
-						colfunc_2s (col);
-						for (i++; i < dc_numlights; i++)
-						{
-							rlight = &dc_lightlist[i];
-							rlight->height += rlight->heightstep;
-							if (rlight->flags & FOF_CUTLEVEL)
-								rlight->botheight += rlight->botheightstep;
-						}
-						continue;
+						// The light is from an extra 3D floor... Check the flags so
+						// there are no undesired cuts.
+						if ((rlight->flags & (FOF_FOG|FOF_SWIMMABLE)) == (pfloor->fofflags & (FOF_FOG|FOF_SWIMMABLE)))
+							solid = 1;
 					}
-					// draw the texture
-					colfunc_2s (col);
-					if (solid)
-						windowtop = bheight;
 					else
-						windowtop = windowbottom + 1;
+						solid = 1;
+				}
+				else
+					solid = 0;
+
+				height = rlight->height;
+				rlight->height += rlight->heightstep;
+
+				if (solid)
+				{
+					bheight = rlight->botheight - (FRACUNIT >> 1);
+					rlight->botheight += rlight->botheightstep;
+				}
+
+				if (height <= windowtop)
+				{
 					if (lighteffect)
 						dc_colormap = rlight->rcolormap;
+					if (solid && windowtop < bheight)
+						windowtop = bheight;
+					continue;
 				}
-				windowbottom = sprbotscreen;
-				// draw the texture, if there is any space left
-				if (windowtop < windowbottom)
-					colfunc_2s (col);
 
-				spryscale += rw_scalestep;
-				continue;
+				windowbottom = height;
+				if (windowbottom >= sprbotscreen)
+				{
+					windowbottom = sprbotscreen;
+					// draw the texture
+					colfunc_2s (col);
+					for (i++; i < dc_numlights; i++)
+					{
+						rlight = &dc_lightlist[i];
+						rlight->height += rlight->heightstep;
+						if (rlight->flags & FOF_CUTLEVEL)
+							rlight->botheight += rlight->botheightstep;
+					}
+					continue;
+				}
+				// draw the texture
+				colfunc_2s (col);
+				if (solid)
+					windowtop = bheight;
+				else
+					windowtop = windowbottom + 1;
+				if (lighteffect)
+					dc_colormap = rlight->rcolormap;
 			}
+			windowbottom = sprbotscreen;
+			// draw the texture, if there is any space left
+			if (windowtop < windowbottom)
+				colfunc_2s (col);
 
-			// calculate lighting
-			pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
+			spryscale += rw_scalestep;
+			continue;
+		}
 
-			if (pindex >= MAXLIGHTSCALE)
-				pindex = MAXLIGHTSCALE - 1;
+		// calculate lighting
+		pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT;
 
-			dc_colormap = walllights[pindex];
+		if (pindex >= MAXLIGHTSCALE)
+			pindex = MAXLIGHTSCALE - 1;
 
-			if (pfloor->fofflags & FOF_FOG && pfloor->master->frontsector->extra_colormap)
-				dc_colormap = pfloor->master->frontsector->extra_colormap->colormap + (dc_colormap - colormaps);
-			else if (frontsector->extra_colormap)
-				dc_colormap = frontsector->extra_colormap->colormap + (dc_colormap - colormaps);
+		dc_colormap = walllights[pindex];
 
-			// draw the texture
-			colfunc_2s (col);
-			spryscale += rw_scalestep;
-		}
+		if (pfloor->fofflags & FOF_FOG && pfloor->master->frontsector->extra_colormap)
+			dc_colormap = pfloor->master->frontsector->extra_colormap->colormap + (dc_colormap - colormaps);
+		else if (frontsector->extra_colormap)
+			dc_colormap = frontsector->extra_colormap->colormap + (dc_colormap - colormaps);
+
+		// draw the texture
+		colfunc_2s (col);
+		spryscale += rw_scalestep;
 	}
 	colfunc = colfuncs[BASEDRAWFUNC];
 
@@ -1270,7 +1227,7 @@ static void R_RenderSegLoop (void)
 		}
 		oldtexturecolumn = texturecolumn;
 
-		texturecolumn >>= FRACBITS;
+		INT32 itexturecolumn = texturecolumn >> FRACBITS;
 
 		// texturecolumn and lighting are independent of wall tiers
 		if (segtextured)
@@ -1336,7 +1293,7 @@ static void R_RenderSegLoop (void)
 				dc_yl = yl;
 				dc_yh = yh;
 				dc_texturemid = rw_midtexturemid;
-				dc_source = R_GetColumn(midtexture,texturecolumn + (rw_offset_mid>>FRACBITS));
+				dc_source = R_GetColumn(midtexture, itexturecolumn + (rw_offset_mid>>FRACBITS));
 				dc_texheight = textureheight[midtexture]>>FRACBITS;
 
 				//profile stuff ---------------------------------------------------------
@@ -1397,7 +1354,7 @@ static void R_RenderSegLoop (void)
 						dc_yl = yl;
 						dc_yh = mid;
 						dc_texturemid = rw_toptexturemid;
-						dc_source = R_GetColumn(toptexture,texturecolumn + (rw_offset_top>>FRACBITS));
+						dc_source = R_GetColumn(toptexture, itexturecolumn + (rw_offset_top>>FRACBITS));
 						dc_texheight = textureheight[toptexture]>>FRACBITS;
 						colfunc();
 						ceilingclip[rw_x] = (INT16)mid;
@@ -1433,8 +1390,7 @@ static void R_RenderSegLoop (void)
 						dc_yl = mid;
 						dc_yh = yh;
 						dc_texturemid = rw_bottomtexturemid;
-						dc_source = R_GetColumn(bottomtexture,
-							texturecolumn + (rw_offset_bot>>FRACBITS));
+						dc_source = R_GetColumn(bottomtexture, itexturecolumn + (rw_offset_bot>>FRACBITS));
 						dc_texheight = textureheight[bottomtexture]>>FRACBITS;
 						colfunc();
 						floorclip[rw_x] = (INT16)mid;
@@ -1453,7 +1409,7 @@ static void R_RenderSegLoop (void)
 		{
 			// save texturecol
 			//  for backdrawing of masked mid texture
-			maskedtexturecol[rw_x] = (INT16)(texturecolumn + (rw_offset_mid>>FRACBITS));
+			maskedtexturecol[rw_x] = texturecolumn + rw_offset_mid;
 
 			if (maskedtextureheight != NULL) {
 				maskedtextureheight[rw_x] = (curline->linedef->flags & ML_MIDPEG) ?
@@ -1512,6 +1468,67 @@ static INT64 R_CalcSegDist(seg_t* seg, INT64 x2, INT64 y2)
 	}
 }
 
+//SoM: Code to remove limits on openings.
+static void R_AllocClippingTables(size_t range)
+{
+	size_t pos = lastopening - openings;
+	size_t need = range * 2; // for both sprtopclip and sprbottomclip
+
+	if (pos + need < numopenings)
+		return;
+
+	INT16 *oldopenings = openings;
+	INT16 *oldlast = lastopening;
+
+	if (numopenings == 0)
+		numopenings = 16384;
+
+	numopenings += need;
+	openings = Z_Realloc(openings, numopenings * sizeof (*openings), PU_STATIC, NULL);
+	lastopening = openings + pos;
+
+	// borrowed fix from *cough* zdoom *cough*
+	// [RH] We also need to adjust the openings pointers that
+	//    were already stored in drawsegs.
+	for (drawseg_t *ds = drawsegs; ds < ds_p; ds++)
+	{
+		// Check if it's in range of the openings
+		if (ds->sprtopclip + ds->x1 >= oldopenings && ds->sprtopclip + ds->x1 <= oldlast)
+			ds->sprtopclip = (ds->sprtopclip - oldopenings) + openings;
+		if (ds->sprbottomclip + ds->x1 >= oldopenings && ds->sprbottomclip + ds->x1 <= oldlast)
+			ds->sprbottomclip = (ds->sprbottomclip - oldopenings) + openings;
+	}
+}
+
+static void R_AllocTextureColumnTables(size_t range)
+{
+	size_t pos = curtexturecolumntable - texturecolumntable;
+
+	// For both tables, we reserve exactly an amount of memory that's equivalent to
+	// how many columns the seg will take on the entire screen (think about it)
+	if (pos + range < texturecolumntablesize)
+		return;
+
+	fixed_t *oldtable = texturecolumntable;
+	fixed_t *oldlast = curtexturecolumntable;
+
+	if (texturecolumntablesize == 0)
+		texturecolumntablesize = 16384;
+
+	texturecolumntablesize += range;
+	texturecolumntable = Z_Realloc(texturecolumntable, texturecolumntablesize * sizeof (*texturecolumntable), PU_STATIC, NULL);
+	curtexturecolumntable = texturecolumntable + pos;
+
+	for (drawseg_t *ds = drawsegs; ds < ds_p; ds++)
+	{
+		// Check if it's in range of the tables
+		if (ds->maskedtexturecol + ds->x1 >= oldtable && ds->maskedtexturecol + ds->x1 <= oldlast)
+			ds->maskedtexturecol = (ds->maskedtexturecol - oldtable) + texturecolumntable;
+		if (ds->thicksidecol + ds->x1 >= oldtable && ds->thicksidecol + ds->x1 <= oldlast)
+			ds->thicksidecol = (ds->thicksidecol - oldtable) + texturecolumntable;
+	}
+}
+
 //
 // R_StoreWallRange
 // A wall segment will be drawn
@@ -1580,37 +1597,6 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 	ds_p->curline = curline;
 	rw_stopx = stop+1;
 
-	//SoM: Code to remove limits on openings.
-	{
-		size_t pos = lastopening - openings;
-		size_t need = (rw_stopx - start)*4 + pos;
-		if (need > maxopenings)
-		{
-			drawseg_t *ds;  //needed for fix from *cough* zdoom *cough*
-			INT16 *oldopenings = openings;
-			INT16 *oldlast = lastopening;
-
-			do
-				maxopenings = maxopenings ? maxopenings*2 : 16384;
-			while (need > maxopenings);
-			openings = Z_Realloc(openings, maxopenings * sizeof (*openings), PU_STATIC, NULL);
-			lastopening = openings + pos;
-
-			// borrowed fix from *cough* zdoom *cough*
-			// [RH] We also need to adjust the openings pointers that
-			//    were already stored in drawsegs.
-			for (ds = drawsegs; ds < ds_p; ds++)
-			{
-#define ADJUST(p) if (ds->p + ds->x1 >= oldopenings && ds->p + ds->x1 <= oldlast) ds->p = ds->p - oldopenings + openings;
-				ADJUST(maskedtexturecol);
-				ADJUST(sprtopclip);
-				ADJUST(sprbottomclip);
-				ADJUST(thicksidecol);
-#undef ADJUST
-			}
-		}
-	}  // end of code to remove limits on openings
-
 	// calculate scale at both ends and step
 	ds_p->scale1 = rw_scale = R_ScaleFromGlobalAngle(viewangle + xtoviewangle[start]);
 
@@ -2026,6 +2012,8 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 		rw_toptexturemid += sidedef->rowoffset + sidedef->offsety_top;
 		rw_bottomtexturemid += sidedef->rowoffset + sidedef->offsety_bot;
 
+		R_AllocTextureColumnTables(rw_stopx - start);
+
 		// allocate space for masked texture tables
 		if (frontsector && backsector && !Tag_Compare(&frontsector->tags, &backsector->tags) && (backsector->ffloors || frontsector->ffloors))
 		{
@@ -2040,8 +2028,8 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 			//markceiling = markfloor = true;
 			maskedtexture = true;
 
-			ds_p->thicksidecol = maskedtexturecol = lastopening - rw_x;
-			lastopening += rw_stopx - rw_x;
+			ds_p->thicksidecol = maskedtexturecol = curtexturecolumntable - rw_x;
+			curtexturecolumntable += rw_stopx - rw_x;
 
 			lowcut = max(worldbottom, worldlow) + viewz;
 			highcut = min(worldtop, worldhigh) + viewz;
@@ -2224,8 +2212,8 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 			// masked midtexture
 			if (!ds_p->thicksidecol)
 			{
-				ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x;
-				lastopening += rw_stopx - rw_x;
+				ds_p->maskedtexturecol = maskedtexturecol = curtexturecolumntable - rw_x;
+				curtexturecolumntable += rw_stopx - rw_x;
 			}
 			else
 				ds_p->maskedtexturecol = ds_p->thicksidecol;
@@ -2737,29 +2725,34 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 		ds_p->portalpass = 0;
 
 	// save sprite clipping info
-	if (((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip)
+	if (maskedtexture || (ds_p->silhouette & (SIL_TOP | SIL_BOTTOM)))
 	{
-		M_Memcpy(lastopening, ceilingclip+start, 2*(rw_stopx - start));
-		ds_p->sprtopclip = lastopening - start;
-		lastopening += rw_stopx - start;
-	}
+		R_AllocClippingTables(rw_stopx - start);
 
-	if (((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && !ds_p->sprbottomclip)
-	{
-		M_Memcpy(lastopening, floorclip + start, 2*(rw_stopx-start));
-		ds_p->sprbottomclip = lastopening - start;
-		lastopening += rw_stopx - start;
+		if (((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip)
+		{
+			M_Memcpy(lastopening, ceilingclip + start, 2*(rw_stopx - start));
+			ds_p->sprtopclip = lastopening - start;
+			lastopening += rw_stopx - start;
+		}
+
+		if (((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && !ds_p->sprbottomclip)
+		{
+			M_Memcpy(lastopening, floorclip + start, 2*(rw_stopx - start));
+			ds_p->sprbottomclip = lastopening - start;
+			lastopening += rw_stopx - start;
+		}
 	}
 
 	if (maskedtexture && !(ds_p->silhouette & SIL_TOP))
 	{
 		ds_p->silhouette |= SIL_TOP;
-		ds_p->tsilheight = (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) ? INT32_MIN: INT32_MAX;
+		ds_p->tsilheight = (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) ? INT32_MIN : INT32_MAX;
 	}
 	if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM))
 	{
 		ds_p->silhouette |= SIL_BOTTOM;
-		ds_p->bsilheight = (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) ? INT32_MAX: INT32_MIN;
+		ds_p->bsilheight = (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) ? INT32_MAX : INT32_MIN;
 	}
 	ds_p++;
 }
diff --git a/src/r_segs.h b/src/r_segs.h
index 09c68b27e9..cad0146748 100644
--- a/src/r_segs.h
+++ b/src/r_segs.h
@@ -22,5 +22,6 @@ transnum_t R_GetLinedefTransTable(fixed_t alpha);
 void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2);
 void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pffloor);
 void R_StoreWallRange(INT32 start, INT32 stop);
+void R_ClearSegTables(void);
 
 #endif
-- 
GitLab


From 7634a960314d5a576656cc4300a74caf889f8d7f Mon Sep 17 00:00:00 2001
From: katsy <katmint@live.com>
Date: Thu, 27 Jul 2023 23:58:53 -0500
Subject: [PATCH 294/518] delete ASM code, remove NASM

---
 SRB2.cbp                                      |   18 -
 SRB2_common.props                             |    3 -
 Srb2.dev                                      |   52 +-
 appveyor.yml                                  |    8 -
 cmake/Modules/CMakeASM_YASMInformation.cmake  |   46 -
 .../CMakeDetermineASM_YASMCompiler.cmake      |   27 -
 cmake/Modules/CMakeTestASM_YASMCompiler.cmake |   23 -
 src/Android.mk                                |    2 +-
 src/CMakeLists.txt                            |   35 -
 src/Makefile                                  |   23 -
 src/Makefile.d/features.mk                    |    7 -
 src/Makefile.d/nix.mk                         |    5 -
 src/Makefile.d/platform.mk                    |    1 -
 src/Makefile.d/sdl.mk                         |    7 -
 src/Makefile.d/win32.mk                       |    2 -
 src/Sourcefile                                |    1 -
 src/asm_defs.inc                              |   43 -
 src/d_netcmd.c                                |    5 -
 src/p5prof.h                                  |  278 ---
 src/r_draw.c                                  |    2 -
 src/r_draw.h                                  |   12 -
 src/r_splats.c                                |   12 -
 src/screen.c                                  |   27 -
 src/sdl/CMakeLists.txt                        |   17 -
 src/sdl/MakeCYG.cfg                           |    1 -
 src/sdl/i_main.c                              |   34 -
 src/tmap.nas                                  |  957 ----------
 src/tmap.s                                    | 1587 -----------------
 src/tmap_asm.s                                |  322 ----
 src/tmap_mmx.nas                              |  674 -------
 src/tmap_vc.nas                               |   48 -
 src/v_video.c                                 |   10 -
 src/vid_copy.s                                |   61 -
 tools/anglechk.c                              |    1 -
 34 files changed, 2 insertions(+), 4349 deletions(-)
 delete mode 100644 cmake/Modules/CMakeASM_YASMInformation.cmake
 delete mode 100644 cmake/Modules/CMakeDetermineASM_YASMCompiler.cmake
 delete mode 100644 cmake/Modules/CMakeTestASM_YASMCompiler.cmake
 delete mode 100644 src/asm_defs.inc
 delete mode 100644 src/p5prof.h
 delete mode 100644 src/tmap.nas
 delete mode 100644 src/tmap.s
 delete mode 100644 src/tmap_asm.s
 delete mode 100644 src/tmap_mmx.nas
 delete mode 100644 src/tmap_vc.nas
 delete mode 100644 src/vid_copy.s

diff --git a/SRB2.cbp b/SRB2.cbp
index 2a1eb87b85..9e887bf859 100644
--- a/SRB2.cbp
+++ b/SRB2.cbp
@@ -1992,24 +1992,6 @@ HW3SOUND for 3D hardware sound  support
 			<Option compilerVar="CC" />
 		</Unit>
 		<Unit filename="src/v_video.h" />
-		<Unit filename="src/vid_copy.s">
-			<Option compilerVar="CC" />
-			<Option compiler="avrgcc" use="1" buildCommand="$compiler $options -x assembler-with-cpp -c $file -o $object" />
-			<Option compiler="gnu_gcc_compiler_for_mingw32" use="1" buildCommand="$compiler $options -x assembler-with-cpp -c $file -o $object" />
-			<Option compiler="gnu_gcc_compiler_for_mingw64" use="1" buildCommand="$compiler $options -x assembler-with-cpp -c $file -o $object" />
-			<Option compiler="armelfgcc" use="1" buildCommand="$compiler $options -x assembler-with-cpp -c $file -o $object" />
-			<Option compiler="tricoregcc" use="1" buildCommand="$compiler $options -x assembler-with-cpp -c $file -o $object" />
-			<Option compiler="ppcgcc" use="1" buildCommand="$compiler $options -x assembler-with-cpp -c $file -o $object" />
-			<Option compiler="gcc" use="1" buildCommand="$compiler $options -x assembler-with-cpp -c $file -o $object" />
-			<Option target="Debug Native/SDL" />
-			<Option target="Release Native/SDL" />
-			<Option target="Debug Linux/SDL" />
-			<Option target="Release Linux/SDL" />
-			<Option target="Debug Mingw/SDL" />
-			<Option target="Release Mingw/SDL" />
-			<Option target="Debug Mingw/DirectX" />
-			<Option target="Release Mingw/DirectX" />
-		</Unit>
 		<Unit filename="src/w_wad.c">
 			<Option compilerVar="CC" />
 		</Unit>
diff --git a/SRB2_common.props b/SRB2_common.props
index 0f80ceb174..6a0d53484f 100644
--- a/SRB2_common.props
+++ b/SRB2_common.props
@@ -25,9 +25,6 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(PlatformTarget)'=='x86'">
-    <ClCompile>
-      <PreprocessorDefinitions>USEASM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-    </ClCompile>
     <Link>
       <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
     </Link>
diff --git a/Srb2.dev b/Srb2.dev
index 21683e7c3c..8bd36cf490 100644
--- a/Srb2.dev
+++ b/Srb2.dev
@@ -5,7 +5,7 @@ Ver=3
 IsCpp=0
 Type=0
 UnitCount=279
-Folders=A_Asm,B_Bot,BLUA,D_Doom,F_Frame,G_Game,H_Hud,Hw_Hardware,Hw_Hardware/r_opengl,I_Interface,I_Interface/Dummy,I_Interface/SDL,I_Interface/Win32,LUA,M_Misc,P_Play,R_Rend,S_Sounds,W_Wad
+Folders=B_Bot,BLUA,D_Doom,F_Frame,G_Game,H_Hud,Hw_Hardware,Hw_Hardware/r_opengl,I_Interface,I_Interface/Dummy,I_Interface/SDL,I_Interface/Win32,LUA,M_Misc,P_Play,R_Rend,S_Sounds,W_Wad
 CommandLine=
 CompilerSettings=00000000000100000111e1
 PchHead=-1
@@ -1473,36 +1473,6 @@ Priority=1000
 OverrideBuildCmd=0
 BuildCmd=
 
-[Unit149]
-FileName=src\tmap.nas
-Folder=A_Asm
-Compile=0
-CompileCpp=0
-Link=0
-Priority=1000
-OverrideBuildCmd=1
-BuildCmd=nasm.exe -g -o $@ -f win32 src/tmap.nas
-
-[Unit150]
-FileName=src\asm_defs.inc
-Folder=A_Asm
-Compile=0
-CompileCpp=0
-Link=0
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit151]
-FileName=src\vid_copy.s
-Folder=A_Asm
-Compile=1
-CompileCpp=0
-Link=1
-Priority=1000
-OverrideBuildCmd=1
-BuildCmd=$(CC) $(CFLAGS) -x assembler-with-cpp -c src/vid_copy.s -o $@
-
 [Unit152]
 FileName=src\y_inter.h
 Folder=H_Hud
@@ -1543,26 +1513,6 @@ Priority=1000
 OverrideBuildCmd=0
 BuildCmd=
 
-[Unit156]
-FileName=src\p5prof.h
-Folder=A_Asm
-Compile=1
-CompileCpp=0
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit157]
-FileName=src\tmap_mmx.nas
-Folder=A_Asm
-Compile=0
-CompileCpp=0
-Link=0
-Priority=1000
-OverrideBuildCmd=1
-BuildCmd=nasm.exe -g -o $@ -f win32 src/tmap_mmx.nas
-
 [Unit159]
 FileName=src\lzf.h
 Folder=W_Wad
diff --git a/appveyor.yml b/appveyor.yml
index e3348d35cb..9770cb37df 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -7,8 +7,6 @@ environment:
  # c:\mingw-w64 i686 has gcc 6.3.0, so use c:\msys64 7.3.0 instead
  MINGW_SDK: c:\msys64\mingw32
  CFLAGS: -Wno-implicit-fallthrough
- NASM_ZIP: nasm-2.12.01
- NASM_URL: http://www.nasm.us/pub/nasm/releasebuilds/2.12.01/win64/nasm-2.12.01-win64.zip
  UPX_ZIP: upx391w
  UPX_URL: http://upx.sourceforge.net/download/upx391w.zip
  CCACHE_EXE: ccache.exe
@@ -40,17 +38,12 @@ environment:
  ASSET_CLEAN: 0
 
 cache:
-- nasm-2.12.01.zip
 - upx391w.zip
 - ccache.exe
 - C:\Users\appveyor\.ccache
 - C:\Users\appveyor\srb2_cache
 
 install:
-- if not exist "%NASM_ZIP%.zip" appveyor DownloadFile "%NASM_URL%" -FileName "%NASM_ZIP%.zip"
-- 7z x -y "%NASM_ZIP%.zip" -o%TMP% >null
-- robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs "%TMP%\%NASM_ZIP%" "%MINGW_SDK%\bin" nasm.exe || exit 0
-
 - if not exist "%UPX_ZIP%.zip" appveyor DownloadFile "%UPX_URL%" -FileName "%UPX_ZIP%.zip"
 - 7z x -y "%UPX_ZIP%.zip" -o%TMP% >null
 - robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs "%TMP%\%UPX_ZIP%" "%MINGW_SDK%\bin" upx.exe || exit 0
@@ -65,7 +58,6 @@ configuration:
 before_build:
 - set "Path=%MINGW_SDK%\bin;%Path%"
 - mingw32-make --version
-- nasm -v
 - if not [%NOUPX%] == [1] ( upx -V )
 - ccache -V
 - ccache -s
diff --git a/cmake/Modules/CMakeASM_YASMInformation.cmake b/cmake/Modules/CMakeASM_YASMInformation.cmake
deleted file mode 100644
index 1765180853..0000000000
--- a/cmake/Modules/CMakeASM_YASMInformation.cmake
+++ /dev/null
@@ -1,46 +0,0 @@
-
-#=============================================================================
-# Copyright 2010 Kitware, Inc.
-#
-# Distributed under the OSI-approved BSD License (the "License");
-# see accompanying file Copyright.txt for details.
-#
-# This software is distributed WITHOUT ANY WARRANTY; without even the
-# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the License for more information.
-#=============================================================================
-# (To distribute this file outside of CMake, substitute the full
-#  License text for the above reference.)
-
-# support for the yasm assembler
-
-set(CMAKE_ASM_YASM_SOURCE_FILE_EXTENSIONS nasm yasm asm)
-
-if(NOT CMAKE_ASM_YASM_OBJECT_FORMAT)
-  if(WIN32)
-    if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
-      set(CMAKE_ASM_YASM_OBJECT_FORMAT win64)
-    else()
-      set(CMAKE_ASM_YASM_OBJECT_FORMAT win32)
-    endif()
-  elseif(APPLE)
-    if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
-      set(CMAKE_ASM_YASM_OBJECT_FORMAT macho64)
-    else()
-      set(CMAKE_ASM_YASM_OBJECT_FORMAT macho)
-    endif()
-  else()
-    if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
-      set(CMAKE_ASM_YASM_OBJECT_FORMAT elf64)
-    else()
-      set(CMAKE_ASM_YASM_OBJECT_FORMAT elf)
-    endif()
-  endif()
-endif()
-
-set(CMAKE_ASM_YASM_COMPILE_OBJECT "<CMAKE_ASM_YASM_COMPILER> <FLAGS> -f ${CMAKE_ASM_YASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
-
-# Load the generic ASMInformation file:
-set(ASM_DIALECT "_YASM")
-include(CMakeASMInformation)
-set(ASM_DIALECT)
diff --git a/cmake/Modules/CMakeDetermineASM_YASMCompiler.cmake b/cmake/Modules/CMakeDetermineASM_YASMCompiler.cmake
deleted file mode 100644
index a5e7c9e580..0000000000
--- a/cmake/Modules/CMakeDetermineASM_YASMCompiler.cmake
+++ /dev/null
@@ -1,27 +0,0 @@
-
-#=============================================================================
-# Copyright 2010 Kitware, Inc.
-#
-# Distributed under the OSI-approved BSD License (the "License");
-# see accompanying file Copyright.txt for details.
-#
-# This software is distributed WITHOUT ANY WARRANTY; without even the
-# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the License for more information.
-#=============================================================================
-# (To distribute this file outside of CMake, substitute the full
-#  License text for the above reference.)
-
-# Find the nasm assembler. yasm (http://www.tortall.net/projects/yasm/) is nasm compatible
-
-set(CMAKE_ASM_YASM_COMPILER_LIST nasm yasm)
-
-if(NOT CMAKE_ASM_YASM_COMPILER)
-  find_program(CMAKE_ASM_YASM_COMPILER yasm
-    "$ENV{ProgramFiles}/YASM")
-endif()
-
-# Load the generic DetermineASM compiler file with the DIALECT set properly:
-set(ASM_DIALECT "_YASM")
-include(CMakeDetermineASMCompiler)
-set(ASM_DIALECT)
diff --git a/cmake/Modules/CMakeTestASM_YASMCompiler.cmake b/cmake/Modules/CMakeTestASM_YASMCompiler.cmake
deleted file mode 100644
index 745f7125c4..0000000000
--- a/cmake/Modules/CMakeTestASM_YASMCompiler.cmake
+++ /dev/null
@@ -1,23 +0,0 @@
-
-#=============================================================================
-# Copyright 2010 Kitware, Inc.
-#
-# Distributed under the OSI-approved BSD License (the "License");
-# see accompanying file Copyright.txt for details.
-#
-# This software is distributed WITHOUT ANY WARRANTY; without even the
-# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the License for more information.
-#=============================================================================
-# (To distribute this file outside of CMake, substitute the full
-#  License text for the above reference.)
-
-# This file is used by EnableLanguage in cmGlobalGenerator to
-# determine that the selected ASM_NASM "compiler" works.
-# For assembler this can only check whether the compiler has been found,
-# because otherwise there would have to be a separate assembler source file
-# for each assembler on every architecture.
-
-set(ASM_DIALECT "_YASM")
-include(CMakeTestASMCompiler)
-set(ASM_DIALECT)
diff --git a/src/Android.mk b/src/Android.mk
index a461da2242..035d488877 100644
--- a/src/Android.mk
+++ b/src/Android.mk
@@ -76,7 +76,7 @@ LOCAL_SRC_FILES :=      am_map.c \
                         android/i_system.c \
                         android/i_video.c
 
-LOCAL_CFLAGS += -DPLATFORM_ANDROID -DNONX86 -DLINUX -DDEBUGMODE -DNOASM -DNOPIX -DUNIXCOMMON -DNOTERMIOS
+LOCAL_CFLAGS += -DPLATFORM_ANDROID -DNONX86 -DLINUX -DDEBUGMODE -DNOPIX -DUNIXCOMMON -DNOTERMIOS
 
 LOCAL_MODULE := libsrb2
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b622373742..8cd0310137 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -13,15 +13,7 @@ target_compile_features(SRB2SDL2 PRIVATE c_std_11 cxx_std_17)
 target_sourcefile(c)
 target_sources(SRB2SDL2 PRIVATE comptime.c md5.c config.h.in)
 
-set(SRB2_ASM_SOURCES vid_copy.s)
-
-set(SRB2_NASM_SOURCES tmap_mmx.nas tmap.nas)
-
 ### Configuration
-set(SRB2_CONFIG_USEASM OFF CACHE BOOL
-	"Enable NASM tmap implementation for software mode speedup.")
-set(SRB2_CONFIG_YASM OFF CACHE BOOL
-	"Use YASM in place of NASM.")
 set(SRB2_CONFIG_DEV_BUILD OFF CACHE BOOL
 	"Compile a development build of SRB2.")
 
@@ -78,33 +70,6 @@ if("${SRB2_CONFIG_HWRENDER}")
 	endif()
 endif()
 
-if(${SRB2_CONFIG_USEASM})
-	#SRB2_ASM_FLAGS can be used to pass flags to either nasm or yasm.
-	if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
-		set(SRB2_ASM_FLAGS "-DLINUX ${SRB2_ASM_FLAGS}")
-	endif()
-
-	if(${SRB2_CONFIG_YASM})
-		set(CMAKE_ASM_YASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_YASM_SOURCE_FILE_EXTENSIONS} nas)
-		set(CMAKE_ASM_YASM_FLAGS "${SRB2_ASM_FLAGS}" CACHE STRING "Flags used by the assembler during all build types.")
-		enable_language(ASM_YASM)
-	else()
-		set(CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS} nas)
-		set(CMAKE_ASM_NASM_FLAGS "${SRB2_ASM_FLAGS}" CACHE STRING "Flags used by the assembler during all build types.")
-		enable_language(ASM_NASM)
-	endif()
-
-	set(SRB2_USEASM ON)
-	target_compile_definitions(SRB2SDL2 PRIVATE -DUSEASM)
-	target_compile_options(SRB2SDL2 PRIVATE -msse3 -mfpmath=sse)
-
-	target_sources(SRB2SDL2 PRIVATE ${SRB2_ASM_SOURCES}
-		${SRB2_NASM_SOURCES})
-else()
-	set(SRB2_USEASM OFF)
-	target_compile_definitions(SRB2SDL2 PRIVATE -DNONX86 -DNORUSEASM)
-endif()
-
 # Targets
 
 # If using CCACHE, then force it.
diff --git a/src/Makefile b/src/Makefile
index 36b1a7efab..92f4c0c66c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -47,8 +47,6 @@
 # HAVE_MINIUPNPC=1 - Enable automated port forwarding.
 #                    Already enabled by default for 32-bit
 #                    Windows.
-# NOASM=1 - Disable hand optimized assembly code for the
-#           Software renderer.
 # NOPNG=1 - Disable PNG graphics support. (TODO: double
 #           check netplay compatible.)
 # NOCURL=1 - Disable libcurl--HTTP capability.
@@ -88,7 +86,6 @@
 #        executable.
 # WINDOWSHELL=1 - Use Windows commands.
 # PREFIX= - Prefix to many commands, for cross compiling.
-# YASM=1 - Use Yasm instead of NASM assembler.
 # STABS=1 - ?
 # ECHO=1 - Print out each command in the build process.
 # NOECHOFILENAMES=1 - Don't print out each that is being
@@ -148,22 +145,6 @@ OBJCOPY:=$(call Prefix,objcopy)
 OBJDUMP:=$(call Prefix,objdump)
 WINDRES:=$(call Prefix,windres)
 
-ifdef YASM
-NASM?=yasm
-else
-NASM?=nasm
-endif
-
-ifdef YASM
-ifdef STABS
-NASMOPTS?=-g stabs
-else
-NASMOPTS?=-g dwarf2
-endif
-else
-NASMOPTS?=-g
-endif
-
 GZIP?=gzip
 GZIP_OPTS?=-9 -f -n
 ifdef WINDOWSHELL
@@ -187,8 +168,6 @@ makedir:=../make
 opts:=-DCOMPVERSION -g
 libs:=
 
-nasm_format:=
-
 # This is a list of variables names, of which if defined,
 # also defines the name as a macro to the compiler.
 passthru_opts:=
@@ -316,7 +295,6 @@ endif
 
 LD:=$(CC)
 cc:=$(cc) $(opts)
-nasm=$(NASM) $(NASMOPTS) -f $(nasm_format)
 ifdef UPX
 upx=$(UPX) $(UPX_OPTS)
 endif
@@ -393,7 +371,6 @@ $(objdir)/%.$(1) : %.$(2) | $$$$(@D)/
 endef
 
 $(eval $(call _recipe,o,c,$(cc) -c -o $$@ $$<))
-$(eval $(call _recipe,o,nas,$(nasm) -o $$@ $$<))
 $(eval $(call _recipe,o,s,$(cc) $(asflags) -c -o $$@ $$<))
 $(eval $(call _recipe,res,rc,$(windres) -i $$< -o $$@))
 
diff --git a/src/Makefile.d/features.mk b/src/Makefile.d/features.mk
index 8ba33383bb..1787f94cb8 100644
--- a/src/Makefile.d/features.mk
+++ b/src/Makefile.d/features.mk
@@ -18,13 +18,6 @@ opts+=-DHWRENDER
 sources+=$(call List,hardware/Sourcefile)
 endif
 
-ifndef NOASM
-ifndef NONX86
-sources+=tmap.nas tmap_mmx.nas
-opts+=-DUSEASM
-endif
-endif
-
 ifndef NOMD5
 sources+=md5.c
 endif
diff --git a/src/Makefile.d/nix.mk b/src/Makefile.d/nix.mk
index 767b64c12b..aa2e96df7e 100644
--- a/src/Makefile.d/nix.mk
+++ b/src/Makefile.d/nix.mk
@@ -9,10 +9,6 @@ opts+=-DUNIXCOMMON -DLUA_USE_POSIX
 # instead of addresses
 libs+=-lm -rdynamic
 
-ifndef nasm_format
-nasm_format:=elf -DLINUX
-endif
-
 ifndef NOHW
 opts+=-I/usr/X11R6/include
 libs+=-L/usr/X11R6/lib
@@ -35,7 +31,6 @@ endif
 # FIXME: UNTESTED
 #ifdef SOLARIS
 #NOIPX=1
-#NOASM=1
 #opts+=-I/usr/local/include -I/opt/sfw/include \
 #		-DSOLARIS -DINADDR_NONE=INADDR_ANY -DBSD_COMP
 #libs+=-L/opt/sfw/lib -lsocket -lnsl
diff --git a/src/Makefile.d/platform.mk b/src/Makefile.d/platform.mk
index c5ac71a20a..d19143e4cf 100644
--- a/src/Makefile.d/platform.mk
+++ b/src/Makefile.d/platform.mk
@@ -39,7 +39,6 @@ else ifdef SOLARIS # FIXME: UNTESTED
 UNIX=1
 platform=solaris
 else ifdef CYGWIN32 # FIXME: UNTESTED
-nasm_format=win32
 platform=cygwin
 else ifdef MINGW
 ifdef MINGW64
diff --git a/src/Makefile.d/sdl.mk b/src/Makefile.d/sdl.mk
index 99ca624e69..a1bfa33038 100644
--- a/src/Makefile.d/sdl.mk
+++ b/src/Makefile.d/sdl.mk
@@ -56,13 +56,6 @@ SDL_LDFLAGS?=$(shell $(SDL_CONFIG) \
 $(eval $(call Propogate_flags,SDL))
 endif
 
-# use the x86 asm code
-ifndef CYGWIN32
-ifndef NOASM
-USEASM=1
-endif
-endif
-
 ifdef MINGW
 ifndef NOSDLMAIN
 SDLMAIN=1
diff --git a/src/Makefile.d/win32.mk b/src/Makefile.d/win32.mk
index 0e48ed6835..73a3d9e453 100644
--- a/src/Makefile.d/win32.mk
+++ b/src/Makefile.d/win32.mk
@@ -17,8 +17,6 @@ sources+=win32/Srb2win.rc
 opts+=-DSTDC_HEADERS
 libs+=-ladvapi32 -lkernel32 -lmsvcrt -luser32
 
-nasm_format:=win32
-
 SDL?=1
 
 ifndef NOHW
diff --git a/src/Sourcefile b/src/Sourcefile
index 7c53050005..f2b408c665 100644
--- a/src/Sourcefile
+++ b/src/Sourcefile
@@ -81,7 +81,6 @@ mserv.c
 http-mserv.c
 i_tcp.c
 lzf.c
-vid_copy.s
 b_bot.c
 u_list.c
 lua_script.c
diff --git a/src/asm_defs.inc b/src/asm_defs.inc
deleted file mode 100644
index 48f8da0d8f..0000000000
--- a/src/asm_defs.inc
+++ /dev/null
@@ -1,43 +0,0 @@
-// SONIC ROBO BLAST 2
-//-----------------------------------------------------------------------------
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
-//
-// This program is free software distributed under the
-// terms of the GNU General Public License, version 2.
-// See the 'LICENSE' file for more details.
-//-----------------------------------------------------------------------------
-/// \file  asm_defs.inc
-/// \brief must match the C structures
-
-#ifndef __ASM_DEFS__
-#define __ASM_DEFS__
-
-// this makes variables more noticable,
-// and make the label match with C code
-
-// Linux, unlike DOS, has no "_" 19990119 by Kin
-// and nasm needs .data code segs under linux 20010210 by metzgermeister
-// FIXME: nasm ignores these settings, so I put the macros into the makefile
-#ifdef __ELF__
-#define C(label) label
-#define CODE_SEG .data
-#else
-#define C(label) _##label
-#define CODE_SEG .text
-#endif
-
-/* This is a more readable way to access the arguments passed from C code   */
-/* PLEASE NOTE: it is supposed that all arguments passed from C code are    */
-/*              32bit integer (INT32, long, and most *pointers)               */
-#define ARG1      8(%ebp)
-#define ARG2      12(%ebp)
-#define ARG3      16(%ebp)
-#define ARG4      20(%ebp)
-#define ARG5      24(%ebp)
-#define ARG6      28(%ebp)
-#define ARG7      32(%ebp)
-#define ARG8      36(%ebp)
-#define ARG9      40(%ebp)      //(c)tm ... Allegro by Shawn Hargreaves.
-
-#endif
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 45a394eff5..1b987bcf13 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -3893,11 +3893,6 @@ static void Command_Version_f(void)
 	else // 16-bit? 128-bit?
 		CONS_Printf("Bits Unknown ");
 
-	// No ASM?
-#ifdef NOASM
-	CONS_Printf("\x85" "NOASM " "\x80");
-#endif
-
 	// Debug build
 #ifdef _DEBUG
 	CONS_Printf("\x85" "DEBUG " "\x80");
diff --git a/src/p5prof.h b/src/p5prof.h
deleted file mode 100644
index a9ed3965e9..0000000000
--- a/src/p5prof.h
+++ /dev/null
@@ -1,278 +0,0 @@
-/*********************************************************
- *
- * File:  p5prof.h
- * By:    Kevin Baca
- *
- * MODIFIED BY Fab SO THAT RDMSR(...) WRITES EDX : EAX TO A LONG LONG
- * (WHICH MEANS WRITE THE LOW DWORD FIRST)
- *
- * Now in yer code do:
- *   INT64 count,total;
- *
- *   ...
- *   RDMSR(0x10,&count);        //inner loop count
- *   total += count;
- *   ...
- *
- *   printf("0x%x %x", (INT32)total, *((INT32 *)&total+1));
- *   //                  HIGH        LOW
- *
- *********************************************************/
-/**\file
-	\brief  This file provides macros to profile your code.
-
- Here's how they work...
-
- As you may or may not know, the Pentium class of
- processors provides extremely fine grained profiling
- capabilities through the use of what are called
- Machine Specific Registers (MSRs). These registers
- can provide information about almost any aspect of
- CPU performance down to a single cycle.
-
- The MSRs of interest for profiling are specified by
- indices 0x10, 0x11, 0x12, and 0x13.  Here is a brief
- description of each of these registers:
-
- MSR 0x10
-    This register is simple a cycle counter.
-
- MSR 0x11
-    This register controls what type of profiling data
- will be gathered.
-
- MSRs 0x12 and 0x13
-    These registers gather the profiling data specified in
- MSR 0x11.
-
- Each MSR is 64 bits wide.  For the Pentium processor,
- only the lower 32 bits of MSR 0x11 are valid.  Bits 0-15
- specify what data will be gathered in MSR 0x12.  Bits 16-31
- specify what data will be gathered in MSR 0x13.  Both sets
- of bits have the same format:
-
- Bits 0-5 specify which hardware event will be tracked.
- Bit 6, if set, indicates events will be tracked in
- rings 0-2.
- Bit 7, if set, indicates events will be tracked in
- ring 3.
- Bit 8, if set, indicates cycles should be counted for
- the specified event.  If clear, it indicates the
- number of events should be counted.
-
- Two instructions are provided for manupulating the MSRs.
- RDMSR (Read Machine Specific Register) and WRMSR
- (Write Machine Specific Register).  These opcodes were
- originally undocumented and therefore most assemblers don't
- recognize them.  Their byte codes are provided in the
- macros below.
-
- RDMSR takes the MSR index in ecx and the profiling criteria
- in edx : eax.
-
- WRMSR takes the MSR index in ecx and returns the profile data
- in edx : eax.
-
- Two profiling registers limits profiling capability to
- gathering only two types of information.  The register
- usage can, however, be combined in interesting ways.
- For example, you can set one register to gather the
- number of a specific type of event while the other gathers
- the number of cycles for the same event.  Or you can
- gather the number of two separate events while using
- MSR 0x10 to gather the number of cycles.
-
- The enumerated list provides somewhat readable labels for
- the types of events that can be tracked.
-
- For more information, get ahold of appendix H from the
- Intel Pentium programmer's manual (I don't remember the
- order number) or go to
- http://green.kaist.ac.kr/jwhahn/art3.htm.
- That's an article by Terje Mathisen where I got most of
- my information.
-
- You may use this code however you wish.  I hope it's
- useful and I hope I got everything right.
-
- -Kevin
-
- kbaca@skygames.com
-
-*/
-
-#ifdef __GNUC__
-
-#define RDTSC(_dst) \
-__asm__("
-     .byte 0x0F,0x31
-     movl %%edx,(%%edi)
-     movl %%eax,4(%%edi)"\
-: : "D" (_dst) : "eax", "edx", "edi")
-
-// the old code... swapped it
-//     movl %%edx,(%%edi)
-//     movl %%eax,4(%%edi)"
-#define RDMSR(_msri, _msrd) \
-__asm__("
-     .byte 0x0F,0x32
-     movl %%eax,(%%edi)
-     movl %%edx,4(%%edi)"\
-: : "c" (_msri), "D" (_msrd) : "eax", "ecx", "edx", "edi")
-
-#define WRMSR(_msri, _msrd) \
-__asm__("
-     xorl %%edx,%%edx
-     .byte 0x0F,0x30"\
-: : "c" (_msri), "a" (_msrd) : "eax", "ecx", "edx")
-
-#define RDMSR_0x12_0x13(_msr12, _msr13) \
-__asm__("
-     movl $0x12,%%ecx
-     .byte 0x0F,0x32
-     movl %%edx,(%%edi)
-     movl %%eax,4(%%edi)
-     movl $0x13,%%ecx
-     .byte 0x0F,0x32
-     movl %%edx,(%%esi)
-     movl %%eax,4(%%esi)"\
-: : "D" (_msr12), "S" (_msr13) : "eax", "ecx", "edx", "edi")
-
-#define ZERO_MSR_0x12_0x13() \
-__asm__("
-     xorl %%edx,%%edx
-     xorl %%eax,%%eax
-     movl $0x12,%%ecx
-     .byte 0x0F,0x30
-     movl $0x13,%%ecx
-     .byte 0x0F,0x30"\
-: : : "eax", "ecx", "edx")
-
-#elif defined (__WATCOMC__)
-
-extern void RDTSC(UINT32 *dst);
-#pragma aux RDTSC =\
-   "db 0x0F,0x31"\
-   "mov [edi],edx"\
-   "mov [4+edi],eax"\
-   parm [edi]\
-   modify [eax edx edi];
-
-extern void RDMSR(UINT32 msri, UINT32 *msrd);
-#pragma aux RDMSR =\
-   "db 0x0F,0x32"\
-   "mov [edi],edx"\
-   "mov [4+edi],eax"\
-   parm [ecx] [edi]\
-   modify [eax ecx edx edi];
-
-extern void WRMSR(UINT32 msri, UINT32 msrd);
-#pragma aux WRMSR =\
-   "xor edx,edx"\
-   "db 0x0F,0x30"\
-   parm [ecx] [eax]\
-   modify [eax ecx edx];
-
-extern void RDMSR_0x12_0x13(UINT32 *msr12, UINT32 *msr13);
-#pragma aux RDMSR_0x12_0x13 =\
-   "mov ecx,0x12"\
-   "db 0x0F,0x32"\
-   "mov [edi],edx"\
-   "mov [4+edi],eax"\
-   "mov ecx,0x13"\
-   "db 0x0F,0x32"\
-   "mov [esi],edx"\
-   "mov [4+esi],eax"\
-   parm [edi] [esi]\
-   modify [eax ecx edx edi esi];
-
-extern void ZERO_MSR_0x12_0x13(void);
-#pragma aux ZERO_MSR_0x12_0x13 =\
-   "xor edx,edx"\
-   "xor eax,eax"\
-   "mov ecx,0x12"\
-   "db 0x0F,0x30"\
-   "mov ecx,0x13"\
-   "db 0x0F,0x30"\
-   modify [eax ecx edx];
-
-#endif
-
-typedef enum
-{
-   DataRead,
-     DataWrite,
-     DataTLBMiss,
-     DataReadMiss,
-     DataWriteMiss,
-     WriteHitEM,
-     DataCacheLinesWritten,
-     DataCacheSnoops,
-     DataCacheSnoopHit,
-     MemAccessBothPipes,
-     BankConflict,
-     MisalignedDataRef,
-     CodeRead,
-     CodeTLBMiss,
-     CodeCacheMiss,
-     SegRegLoad,
-     RESERVED0,
-     RESERVED1,
-     Branch,
-     BTBHit,
-     TakenBranchOrBTBHit,
-     PipelineFlush,
-     InstructionsExeced,
-     InstructionsExecedVPipe,
-     BusUtilizationClocks,
-     PipelineStalledWriteBackup,
-     PipelineStalledDateMemRead,
-     PipeLineStalledWriteEM,
-     LockedBusCycle,
-     IOReadOrWriteCycle,
-     NonCacheableMemRef,
-     AGI,
-     RESERVED2,
-     RESERVED3,
-     FPOperation,
-     Breakpoint0Match,
-     Breakpoint1Match,
-     Breakpoint2Match,
-     Breakpoint3Match,
-     HWInterrupt,
-     DataReadOrWrite,
-     DataReadOrWriteMiss
-};
-
-#define PROF_CYCLES (0x100)
-#define PROF_EVENTS (0x000)
-#define RING_012    (0x40)
-#define RING_3      (0x80)
-#define RING_0123   (RING_012 | RING_3)
-
-/*void ProfSetProfiles(UINT32 msr12, UINT32 msr13);*/
-#define ProfSetProfiles(_msr12, _msr13)\
-{\
-   UINT32 prof;\
-\
-   prof = (_msr12) | ((_msr13) << 16);\
-   WRMSR(0x11, prof);\
-}
-
-/*void ProfBeginProfiles(void);*/
-#define ProfBeginProfiles()\
-   ZERO_MSR_0x12_0x13();
-
-/*void ProfGetProfiles(UINT32 msr12[2], UINT32 msr13[2]);*/
-#define ProfGetProfiles(_msr12, _msr13)\
-   RDMSR_0x12_0x13(_msr12, _msr13);
-
-/*void ProfZeroTimer(void);*/
-#define ProfZeroTimer()\
-   WRMSR(0x10, 0);
-
-/*void ProfReadTimer(UINT32 timer[2]);*/
-#define ProfReadTimer(timer)\
-   RDMSR(0x10, timer);
-
-/*EOF*/
diff --git a/src/r_draw.c b/src/r_draw.c
index b0467e4f72..df9e1a4608 100644
--- a/src/r_draw.c
+++ b/src/r_draw.c
@@ -179,8 +179,6 @@ CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1];
 void R_InitTranslucencyTables(void)
 {
 	// Load here the transparency lookup tables 'TRANSx0'
-	// NOTE: the TRANSx0 resources MUST BE aligned on 64k for the asm
-	// optimised code (in other words, transtables pointer low word is 0)
 	transtables = Z_MallocAlign(NUMTRANSTABLES*0x10000, PU_STATIC,
 		NULL, 16);
 
diff --git a/src/r_draw.h b/src/r_draw.h
index ea03a8e3d5..0103ed8278 100644
--- a/src/r_draw.h
+++ b/src/r_draw.h
@@ -225,18 +225,6 @@ void R_DrawTiltedTransSolidColorSpan_8(void);
 void R_DrawWaterSolidColorSpan_8(void);
 void R_DrawTiltedWaterSolidColorSpan_8(void);
 
-#ifdef USEASM
-void ASMCALL R_DrawColumn_8_ASM(void);
-void ASMCALL R_DrawShadeColumn_8_ASM(void);
-void ASMCALL R_DrawTranslucentColumn_8_ASM(void);
-void ASMCALL R_Draw2sMultiPatchColumn_8_ASM(void);
-
-void ASMCALL R_DrawColumn_8_MMX(void);
-
-void ASMCALL R_Draw2sMultiPatchColumn_8_MMX(void);
-void ASMCALL R_DrawSpan_8_MMX(void);
-#endif
-
 // ------------------
 // 16bpp DRAWING CODE
 // ------------------
diff --git a/src/r_splats.c b/src/r_splats.c
index d182d628ba..737b6d110a 100644
--- a/src/r_splats.c
+++ b/src/r_splats.c
@@ -31,20 +31,8 @@ static void prepare_rastertab(void);
 
 static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, vissprite_t *vis);
 
-#ifdef USEASM
-void ASMCALL rasterize_segment_tex_asm(INT32 x1, INT32 y1, INT32 x2, INT32 y2, INT32 tv1, INT32 tv2, INT32 tc, INT32 dir);
-#endif
-
 static void rasterize_segment_tex(INT32 x1, INT32 y1, INT32 x2, INT32 y2, INT32 tv1, INT32 tv2, INT32 tc, INT32 dir)
 {
-#ifdef USEASM
-	if (R_ASM)
-	{
-		rasterize_segment_tex_asm(x1, y1, x2, y2, tv1, tv2, tc, dir);
-		return;
-	}
-	else
-#endif
 	{
 		fixed_t xs, xe, count;
 		fixed_t dx0, dx1;
diff --git a/src/screen.c b/src/screen.c
index fe5b399958..417e793bde 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -44,10 +44,6 @@
 // SRB2Kart
 #include "r_fps.h" // R_GetFramerateCap
 
-#if defined (USEASM) && !defined (NORUSEASM)//&& (!defined (_MSC_VER) || (_MSC_VER <= 1200))
-#define RUSEASM //MSC.NET can't patch itself
-#endif
-
 // --------------------------------------------
 // assembly or c drawer routines for 8bpp/16bpp
 // --------------------------------------------
@@ -102,7 +98,6 @@ UINT8 *scr_borderpatch; // flat used to fill the reduced view borders set at ST_
 //  Short and Tall sky drawer, for the current color mode
 void (*walldrawerfunc)(void);
 
-boolean R_ASM = true;
 boolean R_486 = false;
 boolean R_586 = false;
 boolean R_MMX = false;
@@ -169,26 +164,6 @@ void SCR_SetDrawFuncs(void)
 		spanfuncs_npo2[SPANDRAWFUNC_WATER] = R_DrawWaterSpan_NPO2_8;
 		spanfuncs_npo2[SPANDRAWFUNC_TILTEDWATER] = R_DrawTiltedWaterSpan_NPO2_8;
 
-#ifdef RUSEASM
-		if (R_ASM)
-		{
-			if (R_MMX)
-			{
-				colfuncs[BASEDRAWFUNC] = R_DrawColumn_8_MMX;
-				//colfuncs[COLDRAWFUNC_SHADE] = R_DrawShadeColumn_8_ASM;
-				//colfuncs[COLDRAWFUNC_FUZZY] = R_DrawTranslucentColumn_8_ASM;
-				colfuncs[COLDRAWFUNC_TWOSMULTIPATCH] = R_Draw2sMultiPatchColumn_8_MMX;
-				spanfuncs[BASEDRAWFUNC] = R_DrawSpan_8_MMX;
-			}
-			else
-			{
-				colfuncs[BASEDRAWFUNC] = R_DrawColumn_8_ASM;
-				//colfuncs[COLDRAWFUNC_SHADE] = R_DrawShadeColumn_8_ASM;
-				//colfuncs[COLDRAWFUNC_FUZZY] = R_DrawTranslucentColumn_8_ASM;
-				colfuncs[COLDRAWFUNC_TWOSMULTIPATCH] = R_Draw2sMultiPatchColumn_8_ASM;
-			}
-		}
-#endif
 	}
 /*	else if (vid.bpp > 1)
 	{
@@ -271,8 +246,6 @@ void SCR_Startup(void)
 		CONS_Printf("CPU Info: 486: %i, 586: %i, MMX: %i, 3DNow: %i, MMXExt: %i, SSE2: %i\n", R_486, R_586, R_MMX, R_3DNow, R_MMXExt, R_SSE2);
 	}
 
-	if (M_CheckParm("-noASM"))
-		R_ASM = false;
 	if (M_CheckParm("-486"))
 		R_486 = true;
 	if (M_CheckParm("-586"))
diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt
index be540b778b..aab83ca284 100644
--- a/src/sdl/CMakeLists.txt
+++ b/src/sdl/CMakeLists.txt
@@ -8,11 +8,6 @@ target_sources(SRB2SDL2 PRIVATE ogl_sdl.c)
 
 target_sources(SRB2SDL2 PRIVATE i_threads.c)
 
-if(${SRB2_USEASM})
-	set_source_files_properties(${SRB2_ASM_SOURCES} PROPERTIES LANGUAGE C)
-	set_source_files_properties(${SRB2_ASM_SOURCES} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp")
-endif()
-
 if("${CMAKE_SYSTEM_NAME}" MATCHES Windows)
 	target_sources(SRB2SDL2 PRIVATE
 		../win32/win_dbg.c
@@ -68,18 +63,6 @@ if("${CMAKE_SYSTEM_NAME}" MATCHES Linux)
 	target_link_libraries(SRB2SDL2 PRIVATE m rt)
 endif()
 
-if(${SRB2_USEASM})
-	if(${SRB2_CONFIG_YASM})
-		set(ASM_ASSEMBLER_TEMP ${CMAKE_ASM_YASM_COMPILER})
-		set(ASM_ASSEMBLER_OBJFORMAT ${CMAKE_ASM_YASM_OBJECT_FORMAT})
-		set_source_files_properties(${SRB2_NASM_SOURCES} LANGUAGE ASM_YASM)
-	else()
-		set(ASM_ASSEMBLER_TEMP ${CMAKE_ASM_NASM_COMPILER})
-		set(ASM_ASSEMBLER_OBJFORMAT ${CMAKE_ASM_NASM_OBJECT_FORMAT})
-		set_source_files_properties(${SRB2_NASM_SOURCES} LANGUAGE ASM_NASM)
-	endif()
-endif()
-
 if("${CMAKE_SYSTEM_NAME}" MATCHES Windows)
 	target_link_libraries(SRB2SDL2 PRIVATE
 		ws2_32
diff --git a/src/sdl/MakeCYG.cfg b/src/sdl/MakeCYG.cfg
index 5907579c1b..b78316b001 100644
--- a/src/sdl/MakeCYG.cfg
+++ b/src/sdl/MakeCYG.cfg
@@ -7,7 +7,6 @@
 
 	NOHW=1
 	NOHS=1
-	NOASM=1
 
 	OPTS+=-DLINUX
 
diff --git a/src/sdl/i_main.c b/src/sdl/i_main.c
index 1dee379c0d..3eeacd8356 100644
--- a/src/sdl/i_main.c
+++ b/src/sdl/i_main.c
@@ -70,39 +70,6 @@ char logfilename[1024];
 typedef BOOL (WINAPI *p_IsDebuggerPresent)(VOID);
 #endif
 
-#if defined (_WIN32)
-static inline VOID MakeCodeWritable(VOID)
-{
-#ifdef USEASM // Disable write-protection of code segment
-	DWORD OldRights;
-	const DWORD NewRights = PAGE_EXECUTE_READWRITE;
-	PBYTE pBaseOfImage = (PBYTE)GetModuleHandle(NULL);
-	PIMAGE_DOS_HEADER dosH =(PIMAGE_DOS_HEADER)pBaseOfImage;
-	PIMAGE_NT_HEADERS ntH = (PIMAGE_NT_HEADERS)(pBaseOfImage + dosH->e_lfanew);
-	PIMAGE_OPTIONAL_HEADER oH = (PIMAGE_OPTIONAL_HEADER)
-		((PBYTE)ntH + sizeof (IMAGE_NT_SIGNATURE) + sizeof (IMAGE_FILE_HEADER));
-	LPVOID pA = pBaseOfImage+oH->BaseOfCode;
-	SIZE_T pS = oH->SizeOfCode;
-#if 1 // try to find the text section
-	PIMAGE_SECTION_HEADER ntS = IMAGE_FIRST_SECTION (ntH);
-	WORD s;
-	for (s = 0; s < ntH->FileHeader.NumberOfSections; s++)
-	{
-		if (memcmp (ntS[s].Name, ".text\0\0", 8) == 0)
-		{
-			pA = pBaseOfImage+ntS[s].VirtualAddress;
-			pS = ntS[s].Misc.VirtualSize;
-			break;
-		}
-	}
-#endif
-
-	if (!VirtualProtect(pA,pS,NewRights,&OldRights))
-		I_Error("Could not make code writable\n");
-#endif
-}
-#endif
-
 #ifdef LOGMESSAGES
 static void InitLogging(void)
 {
@@ -243,7 +210,6 @@ int main(int argc, char **argv)
 #ifndef __MINGW32__
 	prevExceptionFilter = SetUnhandledExceptionFilter(RecordExceptionInfo);
 #endif
-	MakeCodeWritable();
 #endif
 
 	// startup SRB2
diff --git a/src/tmap.nas b/src/tmap.nas
deleted file mode 100644
index 85091cbd5d..0000000000
--- a/src/tmap.nas
+++ /dev/null
@@ -1,957 +0,0 @@
-;; SONIC ROBO BLAST 2
-;;-----------------------------------------------------------------------------
-;; Copyright (C) 1998-2000 by DooM Legacy Team.
-;; Copyright (C) 1999-2023 by Sonic Team Junior.
-;;
-;; This program is free software distributed under the
-;; terms of the GNU General Public License, version 2.
-;; See the 'LICENSE' file for more details.
-;;-----------------------------------------------------------------------------
-;; FILE:
-;;      tmap.nas
-;; DESCRIPTION:
-;;      Assembler optimised rendering code for software mode.
-;;      Draw wall columns.
-
-
-[BITS 32]
-
-%define FRACBITS 16
-%define TRANSPARENTPIXEL 255
-
-%ifdef LINUX
-%macro cextern 1
-[extern %1]
-%endmacro
-
-%macro cglobal 1
-[global %1]
-%endmacro
-
-%else
-%macro cextern 1
-%define %1 _%1
-[extern %1]
-%endmacro
-
-%macro cglobal 1
-%define %1 _%1
-[global %1]
-%endmacro
-
-%endif
-
-
-; The viddef_s structure. We only need the width field.
-struc viddef_s
-        resb 12
-.width: resb 4
-        resb 44
-endstruc
-
-;; externs
-;; columns
-cextern dc_x
-cextern dc_yl
-cextern dc_yh
-cextern ylookup
-cextern columnofs
-cextern dc_source
-cextern dc_texturemid
-cextern dc_texheight
-cextern dc_iscale
-cextern dc_hires
-cextern centery
-cextern centeryfrac
-cextern dc_colormap
-cextern dc_transmap
-cextern colormaps
-cextern vid
-cextern topleft
-
-; DELME
-cextern R_DrawColumn_8
-
-; polygon edge rasterizer
-cextern prastertab
-
-[SECTION .data]
-
-;;.align        4
-loopcount       dd      0
-pixelcount      dd      0
-tystep          dd      0
-
-[SECTION .text]
-
-;;----------------------------------------------------------------------
-;;
-;; R_DrawColumn : 8bpp column drawer
-;;
-;; New  optimised version 10-01-1998 by D.Fabrice and P.Boris
-;; Revised by G. Dick July 2010 to support the intervening twelve years'
-;; worth of changes to the renderer. Since I only vaguely know what I'm
-;; doing, this is probably rather suboptimal. Help appreciated!
-;;
-;;----------------------------------------------------------------------
-;; fracstep, vid.width in memory
-;; eax = accumulator
-;; ebx = colormap
-;; ecx = count
-;; edx = heightmask
-;; esi = source
-;; edi = dest
-;; ebp = frac
-;;----------------------------------------------------------------------
-
-cglobal R_DrawColumn_8_ASM
-;       align   16
-R_DrawColumn_8_ASM:
-        push    ebp                     ;; preserve caller's stack frame pointer
-        push    esi                     ;; preserve register variables
-        push    edi
-        push    ebx
-;;
-;; dest = ylookup[dc_yl] + columnofs[dc_x];
-;;
-        mov     ebp,[dc_yl]
-        mov     edi,[ylookup+ebp*4]
-        mov     ebx,[dc_x]
-        add     edi,[columnofs+ebx*4]  ;; edi = dest
-;;
-;; pixelcount = yh - yl + 1
-;;
-        mov     ecx,[dc_yh]
-        add     ecx,1
-        sub     ecx,ebp                 ;; pixel count
-        jle     near .done              ;; nothing to scale
-;;
-;; fracstep = dc_iscale;	// But we just use [dc_iscale]
-;; frac = (dc_texturemid + FixedMul((dc_yl << FRACBITS) - centeryfrac, fracstep));
-;;
-        mov     eax,ebp                 ;; dc_yl
-        shl     eax,FRACBITS
-        sub     eax,[centeryfrac]
-        imul    dword [dc_iscale]
-        shrd    eax,edx,FRACBITS
-        add     eax,[dc_texturemid]
-        mov     ebp,eax                 ;; ebp = frac
-
-        mov     ebx,[dc_colormap]
-
-        mov     esi,[dc_source]
-;;
-;; if (dc_hires) frac = 0;
-;;
-        test    byte [dc_hires],0x01
-        jz      .texheightcheck
-        xor     ebp,ebp
-
-;;
-;; Check for power of two
-;;
-.texheightcheck:
-        mov     edx,[dc_texheight]
-        sub     edx,1                   ;; edx = heightmask
-        test    edx,[dc_texheight]
-        jnz     .notpowertwo
-
-        test    ecx,0x01                ;; Test for odd no. pixels
-        jnz     .odd
-
-;;
-;; Texture height is a power of two, so we get modular arithmetic by
-;; masking
-;;
-.powertwo:
-        mov     eax,ebp                 ;; eax = frac
-        sar     eax,FRACBITS            ;; Integer part
-        and     eax,edx                 ;; eax &= heightmask
-        movzx   eax,byte [esi + eax]    ;; eax = texel
-        add     ebp,[dc_iscale]         ;; frac += fracstep
-        movzx   eax,byte [ebx+eax]      ;; Map through colormap
-        mov     [edi],al                ;; Write pixel
-                                        ;; dest += vid.width
-        add     edi,[vid + viddef_s.width]
-
-.odd:
-        mov     eax,ebp                 ;; eax = frac
-        sar     eax,FRACBITS            ;; Integer part
-        and     eax,edx                 ;; eax &= heightmask
-        movzx   eax,byte [esi + eax]    ;; eax = texel
-        add     ebp,[dc_iscale]         ;; frac += fracstep
-        movzx   eax,byte [ebx+eax]      ;; Map through colormap
-        mov     [edi],al                ;; Write pixel
-                                        ;; dest += vid.width
-        add     edi,[vid + viddef_s.width]
-
-
-        sub     ecx,2                   ;; count -= 2
-        jg      .powertwo
-
-        jmp     .done
-
-.notpowertwo:
-        add     edx,1
-        shl     edx,FRACBITS
-        test    ebp,ebp
-        jns     .notpowtwoloop
-
-.makefracpos:
-        add     ebp,edx                 ;; frac is negative; make it positive
-        js      .makefracpos
-
-.notpowtwoloop:
-        cmp     ebp,edx                 ;; Reduce mod height
-        jl      .writenonpowtwo
-        sub     ebp,edx
-        jmp     .notpowtwoloop
-
-.writenonpowtwo:
-        mov     eax,ebp                 ;; eax = frac
-        sar     eax,FRACBITS            ;; Integer part.
-        mov     bl,[esi + eax]          ;; ebx = colormap + texel
-        add     ebp,[dc_iscale]         ;; frac += fracstep
-        movzx   eax,byte [ebx]          ;; Map through colormap
-        mov     [edi],al                ;; Write pixel
-                                        ;; dest += vid.width
-        add     edi,[vid + viddef_s.width]
-
-        sub     ecx,1
-        jnz     .notpowtwoloop
-
-;;
-
-.done:
-        pop     ebx                     ;; restore register variables
-        pop     edi
-        pop     esi
-        pop     ebp                     ;; restore caller's stack frame pointer
-        ret
-
-
-;;----------------------------------------------------------------------
-;;
-;; R_Draw2sMultiPatchColumn : Like R_DrawColumn, but omits transparent
-;;                            pixels.
-;;
-;; New  optimised version 10-01-1998 by D.Fabrice and P.Boris
-;; Revised by G. Dick July 2010 to support the intervening twelve years'
-;; worth of changes to the renderer. Since I only vaguely know what I'm
-;; doing, this is probably rather suboptimal. Help appreciated!
-;;
-;;----------------------------------------------------------------------
-;; fracstep, vid.width in memory
-;; eax = accumulator
-;; ebx = colormap
-;; ecx = count
-;; edx = heightmask
-;; esi = source
-;; edi = dest
-;; ebp = frac
-;;----------------------------------------------------------------------
-
-cglobal R_Draw2sMultiPatchColumn_8_ASM
-;       align   16
-R_Draw2sMultiPatchColumn_8_ASM:
-        push    ebp                     ;; preserve caller's stack frame pointer
-        push    esi                     ;; preserve register variables
-        push    edi
-        push    ebx
-;;
-;; dest = ylookup[dc_yl] + columnofs[dc_x];
-;;
-        mov     ebp,[dc_yl]
-        mov     edi,[ylookup+ebp*4]
-        mov     ebx,[dc_x]
-        add     edi,[columnofs+ebx*4]  ;; edi = dest
-;;
-;; pixelcount = yh - yl + 1
-;;
-        mov     ecx,[dc_yh]
-        add     ecx,1
-        sub     ecx,ebp                 ;; pixel count
-        jle     near .done              ;; nothing to scale
-;;
-;; fracstep = dc_iscale;	// But we just use [dc_iscale]
-;; frac = (dc_texturemid + FixedMul((dc_yl << FRACBITS) - centeryfrac, fracstep));
-;;
-        mov     eax,ebp                 ;; dc_yl
-        shl     eax,FRACBITS
-        sub     eax,[centeryfrac]
-        imul    dword [dc_iscale]
-        shrd    eax,edx,FRACBITS
-        add     eax,[dc_texturemid]
-        mov     ebp,eax                 ;; ebp = frac
-
-        mov     ebx,[dc_colormap]
-
-        mov     esi,[dc_source]
-;;
-;; if (dc_hires) frac = 0;
-;;
-        test    byte [dc_hires],0x01
-        jz      .texheightcheck
-        xor     ebp,ebp
-
-;;
-;; Check for power of two
-;;
-.texheightcheck:
-        mov     edx,[dc_texheight]
-        sub     edx,1                   ;; edx = heightmask
-        test    edx,[dc_texheight]
-        jnz     .notpowertwo
-
-        test    ecx,0x01                ;; Test for odd no. pixels
-        jnz     .odd
-
-;;
-;; Texture height is a power of two, so we get modular arithmetic by
-;; masking
-;;
-.powertwo:
-        mov     eax,ebp                 ;; eax = frac
-        sar     eax,FRACBITS            ;; Integer part
-        and     eax,edx                 ;; eax &= heightmask
-        movzx   eax,byte [esi + eax]    ;; eax = texel
-        add     ebp,[dc_iscale]         ;; frac += fracstep
-        cmp     al,TRANSPARENTPIXEL     ;; Is pixel transparent?
-        je      .nextpowtwoeven         ;; If so, advance.
-        movzx   eax,byte [ebx+eax]      ;; Map through colormap
-        mov	    [edi],al                ;; Write pixel
-.nextpowtwoeven:
-                                        ;; dest += vid.width
-        add     edi,[vid + viddef_s.width]
-
-.odd:
-        mov     eax,ebp                 ;; eax = frac
-        sar     eax,FRACBITS            ;; Integer part
-        and     eax,edx                 ;; eax &= heightmask
-        movzx   eax,byte [esi + eax]    ;; eax = texel
-        add     ebp,[dc_iscale]         ;; frac += fracstep
-        cmp     al,TRANSPARENTPIXEL     ;; Is pixel transparent?
-        je      .nextpowtwoodd          ;; If so, advance.
-        movzx   eax,byte [ebx+eax]      ;; Map through colormap
-        mov     [edi],al                ;; Write pixel
-.nextpowtwoodd:
-                                        ;; dest += vid.width
-        add     edi,[vid + viddef_s.width]
-
-
-        sub     ecx,2                   ;; count -= 2
-        jg      .powertwo
-
-        jmp     .done
-
-.notpowertwo:
-        add     edx,1
-        shl     edx,FRACBITS
-        test    ebp,ebp
-        jns     .notpowtwoloop
-
-.makefracpos:
-        add     ebp,edx                 ;; frac is negative; make it positive
-        js      .makefracpos
-
-.notpowtwoloop:
-        cmp     ebp,edx                 ;; Reduce mod height
-        jl      .writenonpowtwo
-        sub     ebp,edx
-        jmp     .notpowtwoloop
-
-.writenonpowtwo:
-        mov     eax,ebp                 ;; eax = frac
-        sar     eax,FRACBITS            ;; Integer part.
-        mov     bl,[esi + eax]          ;; ebx = colormap + texel
-        add     ebp,[dc_iscale]         ;; frac += fracstep
-        cmp     bl,TRANSPARENTPIXEL     ;; Is pixel transparent?
-        je      .nextnonpowtwo          ;; If so, advance.
-        movzx   eax,byte [ebx]          ;; Map through colormap
-        mov     [edi],al                ;; Write pixel
-.nextnonpowtwo:
-                                        ;; dest += vid.width
-        add     edi,[vid + viddef_s.width]
-
-        sub     ecx,1
-        jnz     .notpowtwoloop
-
-;;
-
-.done:
-        pop     ebx                     ;; restore register variables
-        pop     edi
-        pop     esi
-        pop     ebp                     ;; restore caller's stack frame pointer
-        ret
-
-;;----------------------------------------------------------------------
-;; R_DrawTranslucentColumnA_8
-;;
-;; Vertical column texture drawer, with transparency. Replaces Doom2's
-;; 'fuzz' effect, which was not so beautiful.
-;; Transparency is always impressive in some way, don't know why...
-;;----------------------------------------------------------------------
-
-cglobal R_DrawTranslucentColumn_8_ASM
-R_DrawTranslucentColumn_8_ASM:
-        push    ebp                     ;; preserve caller's stack frame pointer
-        push    esi                     ;; preserve register variables
-        push    edi
-        push    ebx
-;;
-;; dest = ylookup[dc_yl] + columnofs[dc_x];
-;;
-        mov     ebp,[dc_yl]
-        mov     ebx,ebp
-        mov     edi,[ylookup+ebx*4]
-        mov     ebx,[dc_x]
-        add     edi,[columnofs+ebx*4]   ;; edi = dest
-;;
-;; pixelcount = yh - yl + 1
-;;
-        mov     eax,[dc_yh]
-        inc     eax
-        sub     eax,ebp                 ;; pixel count
-        mov     [pixelcount],eax        ;; save for final pixel
-        jle     near    vtdone         ;; nothing to scale
-;;
-;; frac = dc_texturemid - (centery-dc_yl)*fracstep;
-;;
-        mov     ecx,[dc_iscale]        ;; fracstep
-        mov     eax,[centery]
-        sub     eax,ebp
-        imul    eax,ecx
-        mov     edx,[dc_texturemid]
-        sub     edx,eax
-        mov     ebx,edx
-
-        shr     ebx,16                  ;; frac int.
-        and     ebx,0x7f
-        shl     edx,16                  ;; y frac up
-
-        mov     ebp,ecx
-        shl     ebp,16                  ;; fracstep f. up
-        shr     ecx,16                  ;; fracstep i. ->cl
-        and     cl,0x7f
-        push    cx
-        mov     ecx,edx
-        pop     cx
-        mov     edx,[dc_colormap]
-        mov     esi,[dc_source]
-;;
-;; lets rock :) !
-;;
-        mov     eax,[pixelcount]
-        shr     eax,0x2
-        test    byte [pixelcount],0x3
-        mov     ch,al                   ;; quad count
-        mov     eax,[dc_transmap]
-        je      vt4quadloop
-;;
-;;  do un-even pixel
-;;
-        test    byte [pixelcount],0x1
-        je      trf2
-
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        add     ecx,ebp
-        adc     bl,cl
-        mov     al,[edi]                ;; fetch dest  : index into colormap
-        and     bl,0x7f
-        mov     dl,[eax]
-        mov     dl,[edx]
-        mov     [edi],dl
-pf:     add     edi,0x12345678
-;;
-;;  do two non-quad-aligned pixels
-;;
-trf2:    test    byte [pixelcount],0x2
-        je      trf3
-
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        add     ecx,ebp
-        adc     bl,cl
-        mov     al,[edi]                ;; fetch dest  : index into colormap
-        and     bl,0x7f
-        mov     dl,[eax]
-        mov     dl,[edx]
-        mov     [edi],dl
-pg:     add     edi,0x12345678
-
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        add     ecx,ebp
-        adc     bl,cl
-        mov     al,[edi]                ;; fetch dest  : index into colormap
-        and     bl,0x7f
-        mov     dl,[eax]
-        mov     dl,[edx]
-        mov     [edi],dl
-ph:     add     edi,0x12345678
-;;
-;;  test if there was at least 4 pixels
-;;
-trf3:   test    ch,0xff                 ;; test quad count
-        je near vtdone
-
-;;
-;; ebp : ystep frac. upper 24 bits
-;; edx : y     frac. upper 24 bits
-;; ebx : y     i.    lower 7 bits,  masked for index
-;; ecx : ch = counter, cl = y step i.
-;; eax : colormap aligned 256
-;; esi : source texture column
-;; edi : dest screen
-;;
-vt4quadloop:
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        mov     [tystep],ebp
-pi:     add     edi,0x12345678
-        mov     al,[edi]                ;; fetch dest  : index into colormap
-pj:     sub     edi,0x12345678
-        mov     ebp,edi
-pk:     sub     edi,0x12345678
-        jmp short inloop
-align 4
-vtquadloop:
-        add     ecx,[tystep]
-        adc     bl,cl
-q1:     add     ebp,0x23456789
-        and     bl,0x7f
-        mov     dl,[eax]
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        mov     dl,[edx]
-        mov     [edi],dl
-        mov     al,[ebp]                ;; fetch dest   : index into colormap
-inloop:
-        add     ecx,[tystep]
-        adc     bl,cl
-q2:     add     edi,0x23456789
-        and     bl,0x7f
-        mov     dl,[eax]
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        mov     dl,[edx]
-        mov     [ebp+0x0],dl
-        mov     al,[edi]                ;; fetch dest   : index into colormap
-
-        add     ecx,[tystep]
-        adc     bl,cl
-q3:     add     ebp,0x23456789
-        and     bl,0x7f
-        mov     dl,[eax]
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        mov     dl,[edx]
-        mov     [edi],dl
-        mov     al,[ebp]                ;; fetch dest   : index into colormap
-
-        add     ecx,[tystep]
-        adc     bl,cl
-q4:     add     edi,0x23456789
-        and     bl,0x7f
-        mov     dl,[eax]
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        mov     dl,[edx]
-        mov     [ebp],dl
-        mov     al,[edi]                ;; fetch dest   : index into colormap
-
-        dec     ch
-        jne     vtquadloop
-vtdone:
-        pop     ebx
-        pop     edi
-        pop     esi
-        pop     ebp
-        ret
-
-;;----------------------------------------------------------------------
-;; R_DrawShadeColumn
-;;
-;;   for smoke..etc.. test.
-;;----------------------------------------------------------------------
-cglobal R_DrawShadeColumn_8_ASM
-R_DrawShadeColumn_8_ASM:
-        push    ebp                     ;; preserve caller's stack frame pointer
-        push    esi                     ;; preserve register variables
-        push    edi
-        push    ebx
-
-;;
-;; dest = ylookup[dc_yl] + columnofs[dc_x];
-;;
-        mov     ebp,[dc_yl]
-        mov     ebx,ebp
-        mov     edi,[ylookup+ebx*4]
-        mov     ebx,[dc_x]
-        add     edi,[columnofs+ebx*4]  ;; edi = dest
-;;
-;; pixelcount = yh - yl + 1
-;;
-        mov     eax,[dc_yh]
-        inc     eax
-        sub     eax,ebp                 ;; pixel count
-        mov     [pixelcount],eax       ;; save for final pixel
-        jle near shdone                ;; nothing to scale
-;;
-;; frac = dc_texturemid - (centery-dc_yl)*fracstep;
-;;
-        mov     ecx,[dc_iscale]        ;; fracstep
-        mov     eax,[centery]
-        sub     eax,ebp
-        imul    eax,ecx
-        mov     edx,[dc_texturemid]
-        sub     edx,eax
-        mov     ebx,edx
-        shr     ebx,16                  ;; frac int.
-        and     ebx,byte +0x7f
-        shl     edx,16                  ;; y frac up
-
-        mov     ebp,ecx
-        shl     ebp,16                  ;; fracstep f. up
-        shr     ecx,16                  ;; fracstep i. ->cl
-        and     cl,0x7f
-
-        mov     esi,[dc_source]
-;;
-;; lets rock :) !
-;;
-        mov     eax,[pixelcount]
-        mov     dh,al
-        shr     eax,2
-        mov     ch,al                   ;; quad count
-        mov     eax,[colormaps]
-        test    dh,3
-        je      sh4quadloop
-;;
-;;  do un-even pixel
-;;
-        test    dh,0x1
-        je      shf2
-
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        add     edx,ebp
-        adc     bl,cl
-        mov     al,[edi]                ;; fetch dest  : index into colormap
-        and     bl,0x7f
-        mov     dl,[eax]
-        mov     [edi],dl
-pl:     add     edi,0x12345678
-;;
-;;  do two non-quad-aligned pixels
-;;
-shf2:
-        test    dh,0x2
-        je      shf3
-
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        add     edx,ebp
-        adc     bl,cl
-        mov     al,[edi]                ;; fetch dest  : index into colormap
-        and     bl,0x7f
-        mov     dl,[eax]
-        mov     [edi],dl
-pm:     add     edi,0x12345678
-
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        add     edx,ebp
-        adc     bl,cl
-        mov     al,[edi]                ;; fetch dest  : index into colormap
-        and     bl,0x7f
-        mov     dl,[eax]
-        mov     [edi],dl
-pn:     add     edi,0x12345678
-;;
-;;  test if there was at least 4 pixels
-;;
-shf3:
-        test    ch,0xff                 ;; test quad count
-        je near shdone
-
-;;
-;; ebp : ystep frac. upper 24 bits
-;; edx : y     frac. upper 24 bits
-;; ebx : y     i.    lower 7 bits,  masked for index
-;; ecx : ch = counter, cl = y step i.
-;; eax : colormap aligned 256
-;; esi : source texture column
-;; edi : dest screen
-;;
-sh4quadloop:
-        mov     dh,0x7f                 ;; prep mask
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        mov     [tystep],ebp
-po:     add     edi,0x12345678
-        mov     al,[edi]                ;; fetch dest  : index into colormap
-pp:     sub     edi,0x12345678
-        mov     ebp,edi
-pq:     sub     edi,0x12345678
-        jmp short shinloop
-
-align  4
-shquadloop:
-        add     edx,[tystep]
-        adc     bl,cl
-        and     bl,dh
-q5:     add     ebp,0x12345678
-        mov     dl,[eax]
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        mov     [edi],dl
-        mov     al,[ebp]                ;; fetch dest : index into colormap
-shinloop:
-        add     edx,[tystep]
-        adc     bl,cl
-        and     bl,dh
-q6:     add     edi,0x12345678
-        mov     dl,[eax]
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        mov     [ebp],dl
-        mov     al,[edi]                ;; fetch dest : index into colormap
-
-        add     edx,[tystep]
-        adc     bl,cl
-        and     bl,dh
-q7:     add     ebp,0x12345678
-        mov     dl,[eax]
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        mov     [edi],dl
-        mov     al,[ebp]                ;; fetch dest : index into colormap
-
-        add     edx,[tystep]
-        adc     bl,cl
-        and     bl,dh
-q8:     add     edi,0x12345678
-        mov     dl,[eax]
-        mov     ah,[esi+ebx]            ;; fetch texel : colormap number
-        mov     [ebp],dl
-        mov     al,[edi]                ;; fetch dest : index into colormap
-
-        dec     ch
-        jne     shquadloop
-
-shdone:
-        pop     ebx                     ;; restore register variables
-        pop     edi
-        pop     esi
-        pop     ebp                     ;; restore caller's stack frame pointer
-        ret
-
-
-;; ========================================================================
-;;  Rasterization of the segments of a LINEAR polygne textur of manire.
-;;  It is thus a question of interpolating coordinate them at the edges of texture in
-;;  the time that the X-coordinates minx/maxx for each line.
-;;  the argument ' dir' indicates which edges of texture are Interpol?:
-;;    0:  segments associs at edge TOP? and BOTTOM? (constant TY)
-;;    1:  segments associs at the LEFT and RIGHT edge (constant TX)
-;; ========================================================================
-;;
-;;  void   rasterize_segment_tex( LONG x1, LONG y1, LONG x2, LONG y2, LONG tv1, LONG tv2, LONG tc, LONG dir );
-;;                                   ARG1     ARG2     ARG3     ARG4      ARG5      ARG6     ARG7       ARG8
-;;
-;;  Pour dir = 0, (tv1,tv2) = (tX1,tX2), tc = tY, en effet TY est constant.
-;;
-;;  Pour dir = 1, (tv1,tv2) = (tY1,tY2), tc = tX, en effet TX est constant.
-;;
-;;
-;;  Uses:  extern struct rastery *_rastertab;
-;;
-
-MINX            EQU    0
-MAXX            EQU    4
-TX1             EQU    8
-TY1             EQU    12
-TX2             EQU    16
-TY2             EQU    20
-RASTERY_SIZEOF  EQU    24
-
-cglobal rasterize_segment_tex_asm
-rasterize_segment_tex_asm:
-        push    ebp
-        mov     ebp,esp
-
-        sub     esp,byte +0x8           ;; allocate the local variables
-
-        push    ebx
-        push    esi
-        push    edi
-        o16 mov ax,es
-        push    eax
-
-;;        #define DX       [ebp-4]
-;;        #define TD       [ebp-8]
-
-        mov     eax,[ebp+0xc]           ;; y1
-        mov     ebx,[ebp+0x14]          ;; y2
-        cmp     ebx,eax
-        je near .L_finished             ;; special (y1==y2) segment horizontal, exit!
-
-        jg near .L_rasterize_right
-
-;;rasterize_left:       ;; one rasterize a segment LEFT of the polygne
-
-        mov     ecx,eax
-        sub     ecx,ebx
-        inc     ecx                     ;; y1-y2+1
-
-        mov     eax,RASTERY_SIZEOF
-        mul     ebx                     ;; * y2
-        mov     esi,[prastertab]
-        add     esi,eax                 ;; point into rastertab[y2]
-
-        mov     eax,[ebp+0x8]           ;; ARG1
-        sub     eax,[ebp+0x10]          ;; ARG3
-        shl     eax,0x10                ;;     ((x1-x2)<<PRE) ...
-        cdq
-        idiv    ecx                     ;; dx =     ...        / (y1-y2+1)
-        mov     [ebp-0x4],eax           ;; DX
-
-        mov     eax,[ebp+0x18]          ;; ARG5
-        sub     eax,[ebp+0x1c]          ;; ARG6
-        shl     eax,0x10
-        cdq
-        idiv    ecx                     ;;      tdx =((tx1-tx2)<<PRE) / (y1-y2+1)
-        mov     [ebp-0x8],eax           ;; idem tdy =((ty1-ty2)<<PRE) / (y1-y2+1)
-
-        mov     eax,[ebp+0x10]          ;; ARG3
-        shl     eax,0x10                ;; x = x2<<PRE
-
-        mov     ebx,[ebp+0x1c]          ;; ARG6
-        shl     ebx,0x10                ;; tx = tx2<<PRE    d0
-                                        ;; ty = ty2<<PRE    d1
-        mov     edx,[ebp+0x20]          ;; ARG7
-        shl     edx,0x10                ;; ty = ty<<PRE     d0
-                                        ;; tx = tx<<PRE     d1
-        push    ebp
-        mov     edi,[ebp-0x4]           ;; DX
-        cmp     dword [ebp+0x24],byte +0x0      ;; ARG8   direction ?
-
-        mov     ebp,[ebp-0x8]           ;; TD
-        je      .L_rleft_h_loop
-;;
-;; TY varies, TX is constant
-;;
-.L_rleft_v_loop:
-        mov     [esi+MINX],eax           ;; rastertab[y].minx = x
-          add     ebx,ebp
-        mov     [esi+TX1],edx           ;;             .tx1  = tx
-          add     eax,edi
-        mov     [esi+TY1],ebx           ;;             .ty1  = ty
-
-        ;;addl    DX, %eax        // x     += dx
-        ;;addl    TD, %ebx        // ty    += tdy
-
-        add     esi,RASTERY_SIZEOF      ;; next raster line into rastertab[]
-        dec     ecx
-        jne     .L_rleft_v_loop
-        pop     ebp
-        jmp     .L_finished
-;;
-;; TX varies, TY is constant
-;;
-.L_rleft_h_loop:
-        mov     [esi+MINX],eax           ;; rastertab[y].minx = x
-          add     eax,edi
-        mov     [esi+TX1],ebx           ;;             .tx1  = tx
-          add     ebx,ebp
-        mov     [esi+TY1],edx           ;;             .ty1  = ty
-
-        ;;addl    DX, %eax        // x     += dx
-        ;;addl    TD, %ebx        // tx    += tdx
-
-        add     esi,RASTERY_SIZEOF      ;; next raster line into rastertab[]
-        dec     ecx
-        jne     .L_rleft_h_loop
-        pop     ebp
-        jmp     .L_finished
-;;
-;; one rasterize a segment LINE of the polygne
-;;
-.L_rasterize_right:
-        mov     ecx,ebx
-        sub     ecx,eax
-        inc     ecx                     ;; y2-y1+1
-
-        mov     ebx,RASTERY_SIZEOF
-        mul     ebx                     ;;   * y1
-        mov     esi,[prastertab]
-        add     esi,eax                 ;;  point into rastertab[y1]
-
-        mov     eax,[ebp+0x10]          ;; ARG3
-        sub     eax,[ebp+0x8]           ;; ARG1
-        shl     eax,0x10                ;; ((x2-x1)<<PRE) ...
-        cdq
-        idiv    ecx                     ;;  dx =     ...        / (y2-y1+1)
-        mov     [ebp-0x4],eax           ;; DX
-
-        mov     eax,[ebp+0x1c]          ;; ARG6
-        sub     eax,[ebp+0x18]          ;; ARG5
-        shl     eax,0x10
-        cdq
-        idiv    ecx                     ;;       tdx =((tx2-tx1)<<PRE) / (y2-y1+1)
-        mov     [ebp-0x8],eax           ;;  idem tdy =((ty2-ty1)<<PRE) / (y2-y1+1)
-
-        mov     eax,[ebp+0x8]           ;; ARG1
-        shl     eax,0x10                ;; x  = x1<<PRE
-
-        mov     ebx,[ebp+0x18]          ;; ARG5
-        shl     ebx,0x10                ;; tx = tx1<<PRE    d0
-                                        ;; ty = ty1<<PRE    d1
-        mov     edx,[ebp+0x20]          ;; ARG7
-        shl     edx,0x10                ;; ty = ty<<PRE     d0
-                                        ;; tx = tx<<PRE     d1
-        push    ebp
-        mov     edi,[ebp-0x4]           ;; DX
-
-        cmp     dword [ebp+0x24], 0     ;; direction ?
-
-         mov     ebp,[ebp-0x8]          ;; TD
-        je      .L_rright_h_loop
-;;
-;; TY varies, TX is constant
-;;
-.L_rright_v_loop:
-
-        mov     [esi+MAXX],eax           ;; rastertab[y].maxx = x
-          add     ebx,ebp
-        mov     [esi+TX2],edx          ;;             .tx2  = tx
-          add     eax,edi
-        mov     [esi+TY2],ebx          ;;             .ty2  = ty
-
-        ;;addl    DX, %eax        // x     += dx
-        ;;addl    TD, %ebx        // ty    += tdy
-
-        add     esi,RASTERY_SIZEOF
-        dec     ecx
-        jne     .L_rright_v_loop
-
-        pop     ebp
-
-        jmp     short .L_finished
-;;
-;; TX varies, TY is constant
-;;
-.L_rright_h_loop:
-        mov     [esi+MAXX],eax           ;; rastertab[y].maxx = x
-          add     eax,edi
-        mov     [esi+TX2],ebx          ;;             .tx2  = tx
-          add     ebx,ebp
-        mov     [esi+TY2],edx          ;;             .ty2  = ty
-
-        ;;addl    DX, %eax        // x     += dx
-        ;;addl    TD, %ebx        // tx    += tdx
-
-        add     esi,RASTERY_SIZEOF
-        dec     ecx
-        jne     .L_rright_h_loop
-
-        pop     ebp
-
-.L_finished:
-        pop     eax
-        o16 mov es,ax
-        pop     edi
-        pop     esi
-        pop     ebx
-
-        mov     esp,ebp
-        pop     ebp
-        ret
diff --git a/src/tmap.s b/src/tmap.s
deleted file mode 100644
index d98d82e25c..0000000000
--- a/src/tmap.s
+++ /dev/null
@@ -1,1587 +0,0 @@
-// SONIC ROBO BLAST 2
-//-----------------------------------------------------------------------------
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
-//
-// This program is free software distributed under the
-// terms of the GNU General Public License, version 2.
-// See the 'LICENSE' file for more details.
-//-----------------------------------------------------------------------------
-/// \file  tmap.s
-/// \brief optimised drawing routines for span/column rendering
-
-// structures, must match the C structures!
-#include "asm_defs.inc"
-
-// Rappel: seuls EAX, ECX, EDX peuvent �tre �cras�s librement.
-//         il faut sauver esi,edi, cd...gs
-
-/* Attention aux comparaisons!                                              */
-/*                                                                          */
-/*      Intel_compare:                                                      */
-/*                                                                          */
-/*              cmp     A,B                     // A-B , set flags          */
-/*              jg      A_greater_than_B                                    */
-/*                                                                          */
-/*      AT&T_compare:                                                       */
-/*                                                                          */
-/*              cmp     A,B                     // B-A , set flags          */
-/*              jg      B_greater_than_A                                    */
-/*                                                                          */
-/*        (soustrait l'op�rande source DE l'op�rande destination,           */
-/*         comme sur Motorola! )                                            */
-
-// RAPPEL: Intel
-//         SECTION:[BASE+INDEX*SCALE+DISP]
-// devient SECTION:DISP(BASE,INDEX,SCALE)
-
-//----------------------------------------------------------------------
-//
-// R_DrawColumn
-//
-//   New optimised version 10-01-1998 by D.Fabrice and P.Boris
-//   TO DO: optimise it much farther... should take at most 3 cycles/pix
-//          once it's fixed, add code to patch the offsets so that it
-//          works in every screen width.
-//
-//----------------------------------------------------------------------
-
-    .data
-#ifdef LINUX
-    .align 2
-#else
-    .align 4
-#endif
-C(loopcount):   .long   0
-C(pixelcount):  .long   0
-C(tystep):      .long   0
-
-C(vidwidth):    .long   0       //use this one out of the inner loops
-                                //so you don't need to patch everywhere...
-
-#ifdef USEASM
-#if !defined( LINUX)
-    .text
-#endif
-.globl C(ASM_PatchRowBytes)
-C(ASM_PatchRowBytes):
-    pushl   %ebp
-    movl    %esp, %ebp      // assure l'"adressabilit� du stack"
-
-    movl    ARG1, %edx         // read first arg
-    movl    %edx, C(vidwidth)
-
-    // 1 * vidwidth
-    movl    %edx,p1+2
-    movl    %edx,w1+2   //water
-    movl    %edx,p1b+2  //sky
-
-    movl    %edx,p5+2
-      movl    %edx,sh5+2        //smokie test
-
-    // 2 * vidwidth
-    addl    ARG1,%edx
-
-    movl    %edx,p2+2
-    movl    %edx,w2+2   //water
-    movl    %edx,p2b+2  //sky
-
-    movl    %edx,p6+2
-    movl    %edx,p7+2
-    movl    %edx,p8+2
-    movl    %edx,p9+2
-      movl    %edx,sh6+2         //smokie test
-      movl    %edx,sh7+2
-      movl    %edx,sh8+2
-      movl    %edx,sh9+2
-
-    // 3 * vidwidth
-    addl    ARG1,%edx
-
-    movl    %edx,p3+2
-    movl    %edx,w3+2   //water
-    movl    %edx,p3b+2  //sky
-
-    // 4 * vidwidth
-    addl    ARG1,%edx
-
-    movl    %edx,p4+2
-    movl    %edx,w4+2   //water
-    movl    %edx,p4b+2  //sky
-
-    popl    %ebp
-    ret
-
-
-#ifdef LINUX
-    .align 2
-#else
-    .align 5
-#endif
-.globl C(R_DrawColumn_8)
-C(R_DrawColumn_8):
-    pushl   %ebp                // preserve caller's stack frame pointer
-    pushl   %esi                // preserve register variables
-    pushl   %edi
-    pushl   %ebx
-
-//
-// dest = ylookup[dc_yl] + columnofs[dc_x];
-//
-    movl     C(dc_yl),%ebp
-    movl     %ebp,%ebx
-    movl     C(ylookup)(,%ebx,4),%edi
-    movl     C(dc_x),%ebx
-    addl     C(columnofs)(,%ebx,4),%edi  // edi = dest
-
-//
-// pixelcount = yh - yl + 1
-//
-    movl     C(dc_yh),%eax
-    incl     %eax
-    subl     %ebp,%eax                   // pixel count
-    movl     %eax,C(pixelcount)          // save for final pixel
-    jle      vdone                       // nothing to scale
-
-//
-// frac = dc_texturemid - (centery-dc_yl)*fracstep;
-//
-    movl     C(dc_iscale),%ecx           // fracstep
-    movl     C(centery),%eax
-    subl     %ebp,%eax
-    imul     %ecx,%eax
-    movl     C(dc_texturemid),%edx
-    subl     %eax,%edx
-     movl     %edx,%ebx
-     shrl     $16,%ebx          // frac int.
-     andl     $0x0000007f,%ebx
-     shll     $16,%edx          // y frac up
-
-     movl     %ecx,%ebp
-     shll     $16,%ebp          // fracstep f. up
-     shrl     $16,%ecx          // fracstep i. ->cl
-     andb     $0x7f,%cl
-
-    movl     C(dc_source),%esi
-
-//
-// lets rock :) !
-//
-    movl    C(pixelcount),%eax
-    movb    %al,%dh
-    shrl    $2,%eax
-    movb    %al,%ch             // quad count
-    movl    C(dc_colormap),%eax
-    testb   $3,%dh
-    jz      v4quadloop
-
-//
-//  do un-even pixel
-//
-    testb   $1,%dh
-    jz      2f
-
-    movb    (%esi,%ebx),%al     // prep un-even loops
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-    andb    $0x7f,%bl            // mask 0-127 texture index
-     movb    %dl,(%edi)           // output pixel
-    addl    C(vidwidth),%edi
-
-//
-//  do two non-quad-aligned pixels
-//
-2:
-    testb   $2,%dh
-    jz      3f
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-    andb    $0x7f,%bl            // mask 0-127 texture index
-     movb    %dl,(%edi)           // output pixel
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-    andb    $0x7f,%bl            // mask 0-127 texture index
-    addl    C(vidwidth),%edi
-     movb    %dl,(%edi)           // output pixel
-
-    addl    C(vidwidth),%edi
-
-//
-//  test if there was at least 4 pixels
-//
-3:
-    testb   $0xFF,%ch           // test quad count
-    jz      vdone
-
-//
-// ebp : ystep frac. upper 24 bits
-// edx : y     frac. upper 24 bits
-// ebx : y     i.    lower 7 bits,  masked for index
-// ecx : ch = counter, cl = y step i.
-// eax : colormap aligned 256
-// esi : source texture column
-// edi : dest screen
-//
-v4quadloop:
-    movb    $0x7f,%dh           // prep mask
-//    .align  4
-vquadloop:
-    movb    (%esi,%ebx),%al     // prep loop
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-    movb    %dl,(%edi)           // output pixel
-     andb    $0x7f,%bl            // mask 0-127 texture index
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%eax),%dl
-p1:    movb    %dl,0x12345678(%edi)
-     andb    $0x7f,%bl
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%eax),%dl
-p2:    movb    %dl,2*0x12345678(%edi)
-     andb    $0x7f,%bl
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%eax),%dl
-p3:    movb    %dl,3*0x12345678(%edi)
-     andb    $0x7f,%bl
-
-p4:    addl    $4*0x12345678,%edi
-
-    decb   %ch
-     jnz    vquadloop
-
-vdone:
-    popl    %ebx                // restore register variables
-    popl    %edi
-    popl    %esi
-    popl    %ebp                // restore caller's stack frame pointer
-    ret
-
-#ifdef HORIZONTALDRAW
-// --------------------------------------------------------------------------
-// Horizontal Column Drawer Optimisation
-// --------------------------------------------------------------------------
-
-#ifdef LINUX
-    .align 2
-#else
-    .align 5
-#endif
-.globl C(R_DrawHColumn_8)
-C(R_DrawHColumn_8):
-    pushl   %ebp
-    pushl   %esi
-    pushl   %edi
-    pushl   %ebx
-
-//
-// dest = yhlookup[dc_x] + hcolumnofs[dc_yl];
-//
-    movl    C(dc_x),%ebx
-    movl    C(yhlookup)(,%ebx,4),%edi
-    movl    C(dc_yl),%ebp
-    movl    %ebp,%ebx
-    addl    C(hcolumnofs)(,%ebx,4),%edi  // edi = dest
-
-//
-// pixelcount = yh - yl + 1
-//
-    movl     C(dc_yh),%eax
-    incl     %eax
-    subl     %ebp,%eax                   // pixel count
-    movl     %eax,C(pixelcount)          // save for final pixel
-    jle      vhdone                      // nothing to scale
-
-//
-// frac = dc_texturemid - (centery-dc_yl)*fracstep;
-//
-    movl     C(dc_iscale),%ecx           // fracstep
-    movl     C(centery),%eax
-    subl     %ebp,%eax
-    imul     %ecx,%eax
-    movl     C(dc_texturemid),%edx
-    subl     %eax,%edx
-     movl     %edx,%ebx
-     shrl     $16,%ebx          // frac int.
-     andl     $0x0000007f,%ebx
-     shll     $16,%edx          // y frac up
-
-     movl     %ecx,%ebp
-     shll     $16,%ebp          // fracstep f. up
-     shrl     $16,%ecx          // fracstep i. ->cl
-     andb     $0x7f,%cl
-
-    movl     C(dc_source),%esi
-
-//
-// lets rock :) !
-//
-
-    movl    C(pixelcount),%eax
-    movb    %al,%dh
-    shrl    $2,%eax
-    movb    %al,%ch     // quad count
-
-    testb   %ch, %ch
-    jz      vhnearlydone
-
-    movl    C(dc_colormap),%eax
-    decl    %edi                  //-----
-
-vhloop:
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     andb    $0x7f,%bl
-    incl    %edi                 //-----
-     movb    (%eax),%dh
-    movb    %dh,(%edi)           //-----
-
-     movb    (%esi,%ebx),%al      // fetch source texel
-    addl    %ebp,%edx
-     incl    %edi                //-----
-    adcb    %cl,%bl
-     movb    (%eax),%dl
-    andb    $0x7f,%bl
-     movb    %dl,(%edi)          //-----
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-//    shll    $16,%edx
-     andb    $0x7f,%bl
-    incl    %edi                //-----
-     movb    (%eax),%dh
-    movb    %dh,(%edi)          //-----
-
-     movb    (%esi,%ebx),%al      // fetch source texel
-    addl    %ebp,%edx
-     incl    %edi               //-----
-    adcb    %cl,%bl
-     movb    (%eax),%dl
-    andb    $0x7f,%bl
-     movb    %dl,(%edi)
-//     movl    %edx,(%edi)
-//    addl    $4,%edi
-
-    decb   %ch
-     jnz    vhloop
-
-vhnearlydone:
-//    movl    C(pixelcount)
-
-vhdone:
-    popl    %ebx
-    popl    %edi
-    popl    %esi
-    popl    %ebp
-    ret
-
-
-// --------------------------------------------------------------------------
-// Rotate a buffer 90 degree in clockwise order after horiz.col. draws
-// --------------------------------------------------------------------------
-
-#ifdef LINUX
-    .align 2
-#else
-    .align 5
-#endif
-.globl C(R_RotateBuffer)
-C(R_RotateBuffer):
-    pushl   %ebp
-    pushl   %esi
-    pushl   %edi
-    pushl   %ebx
-
-
-    movl    C(dc_source),%esi
-    movl    C(dc_colormap),%edi
-
-
-    movb    (%esi),%ah
-     addl    $200,%esi
-    movb    (%ebx),%al
-     addl    $200,%ebx
-    bswap    %eax
-    movb    (%esi),%ah
-     addl    $200,%esi
-    movb    (%ebx),%al
-     addl    $200,%ebx
-    movl    %eax,(%edi)
-     addl    $4,%edi
-
-
-    popl    %ebx
-    popl    %edi
-    popl    %esi
-    popl    %ebp
-    ret
-#endif
-
-//----------------------------------------------------------------------
-//13-02-98:
-//   R_DrawSkyColumn : same as R_DrawColumn but:
-//
-//            - wrap around 256 instead of 127.
-//   this is needed because we have a higher texture for mouselook,
-//   we need at least 200 lines for the sky.
-//
-//   NOTE: the sky should never wrap, so it could use a faster method.
-//         for the moment, we'll still use a wrapping method...
-//
-//  IT S JUST A QUICK CUT N PASTE, WAS NOT OPTIMISED AS IT SHOULD BE !!!
-//
-//----------------------------------------------------------------------
-
-#ifdef LINUX
-    .align 2
-#else
-    .align 5
-#endif
-.globl C(R_DrawSkyColumn_8)
-C(R_DrawSkyColumn_8):
-    pushl   %ebp
-    pushl   %esi
-    pushl   %edi
-    pushl   %ebx
-
-//
-// dest = ylookup[dc_yl] + columnofs[dc_x];
-//
-    movl     C(dc_yl),%ebp
-    movl     %ebp,%ebx
-    movl     C(ylookup)(,%ebx,4),%edi
-    movl     C(dc_x),%ebx
-    addl     C(columnofs)(,%ebx,4),%edi  // edi = dest
-
-//
-// pixelcount = yh - yl + 1
-//
-    movl     C(dc_yh),%eax
-    incl     %eax
-    subl     %ebp,%eax                   // pixel count
-    movl     %eax,C(pixelcount)          // save for final pixel
-    jle      vskydone                       // nothing to scale
-
-//
-// frac = dc_texturemid - (centery-dc_yl)*fracstep;
-//
-    movl     C(dc_iscale),%ecx           // fracstep
-    movl     C(centery),%eax
-    subl     %ebp,%eax
-    imul     %ecx,%eax
-    movl     C(dc_texturemid),%edx
-    subl     %eax,%edx
-     movl     %edx,%ebx
-     shrl     $16,%ebx          // frac int.
-     andl     $0x000000ff,%ebx
-     shll     $16,%edx          // y frac up
-
-     movl     %ecx,%ebp
-     shll     $16,%ebp          // fracstep f. up
-     shrl     $16,%ecx          // fracstep i. ->cl
-
-    movl     C(dc_source),%esi
-
-//
-// lets rock :) !
-//
-    movl    C(pixelcount),%eax
-    movb    %al,%dh
-    shrl    $2,%eax
-    movb    %al,%ch             // quad count
-    movl    C(dc_colormap),%eax
-    testb   $3,%dh
-    jz      v4skyquadloop
-
-//
-//  do un-even pixel
-//
-    testb   $1,%dh
-    jz      2f
-
-    movb    (%esi,%ebx),%al     // prep un-even loops
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-     movb    %dl,(%edi)           // output pixel
-    addl    C(vidwidth),%edi
-
-//
-//  do two non-quad-aligned pixels
-//
-2:
-    testb   $2,%dh
-    jz      3f
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-     movb    %dl,(%edi)           // output pixel
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-    addl    C(vidwidth),%edi
-     movb    %dl,(%edi)           // output pixel
-
-    addl    C(vidwidth),%edi
-
-//
-//  test if there was at least 4 pixels
-//
-3:
-    testb   $0xFF,%ch           // test quad count
-    jz      vskydone
-
-//
-// ebp : ystep frac. upper 24 bits
-// edx : y     frac. upper 24 bits
-// ebx : y     i.    lower 7 bits,  masked for index
-// ecx : ch = counter, cl = y step i.
-// eax : colormap aligned 256
-// esi : source texture column
-// edi : dest screen
-//
-v4skyquadloop:
-//    .align  4
-vskyquadloop:
-    movb    (%esi,%ebx),%al     // prep loop
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-    movb    %dl,(%edi)           // output pixel
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%eax),%dl
-p1b:    movb    %dl,0x12345678(%edi)
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%eax),%dl
-p2b:    movb    %dl,2*0x12345678(%edi)
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%eax),%dl
-p3b:    movb    %dl,3*0x12345678(%edi)
-
-p4b:    addl    $4*0x12345678,%edi
-
-    decb   %ch
-     jnz    vskyquadloop
-
-vskydone:
-    popl    %ebx                // restore register variables
-    popl    %edi
-    popl    %esi
-    popl    %ebp                // restore caller's stack frame pointer
-    ret
-
-
-
-//----------------------------------------------------------------------
-//
-// R_DrawSpan
-//
-// Horizontal texture mapping
-//
-//----------------------------------------------------------------------
-
-    .data
-
-ystep:          .long   0
-xstep:          .long   0
-C(texwidth):    .long   64      // texture width
-#if !defined( LINUX)
-    .text
-#endif
-#ifdef LINUX
-    .align 2
-#else
-    .align 4
-#endif
-.globl C(R_DrawSpan_8)
-C(R_DrawSpan_8):
-    pushl   %ebp                // preserve caller's stack frame pointer
-    pushl   %esi                // preserve register variables
-    pushl   %edi
-    pushl   %ebx
-
-
-//
-// find loop count
-//
-    movl    C(ds_x2),%eax
-    incl    %eax
-    subl    C(ds_x1),%eax               // pixel count
-    movl    %eax,C(pixelcount)          // save for final pixel
-    js      hdone                       // nothing to scale
-    shrl    $1,%eax                     // double pixel count
-    movl    %eax,C(loopcount)
-
-//
-// build composite position
-//
-    movl    C(ds_xfrac),%ebp
-    shll    $10,%ebp
-    andl    $0x0ffff0000,%ebp
-    movl    C(ds_yfrac),%eax
-    shrl    $6,%eax
-    andl    $0x0ffff,%eax
-    movl    C(ds_y),%edi
-    orl     %eax,%ebp
-
-    movl    C(ds_source),%esi
-
-//
-// calculate screen dest
-//
-
-    movl    C(ylookup)(,%edi,4),%edi
-    movl    C(ds_x1),%eax
-    addl    C(columnofs)(,%eax,4),%edi
-
-//
-// build composite step
-//
-    movl    C(ds_xstep),%ebx
-    shll    $10,%ebx
-    andl    $0x0ffff0000,%ebx
-    movl    C(ds_ystep),%eax
-    shrl    $6,%eax
-    andl    $0x0ffff,%eax
-    orl     %eax,%ebx
-
-    //movl        %eax,OFFSET hpatch1+2        // convice tasm to modify code...
-    movl    %ebx,hpatch1+2
-    //movl        %eax,OFFSET hpatch2+2        // convice tasm to modify code...
-    movl    %ebx,hpatch2+2
-    movl    %esi,hpatch3+2
-    movl    %esi,hpatch4+2
-// %eax      aligned colormap
-// %ebx      aligned colormap
-// %ecx,%edx  scratch
-// %esi      virtual source
-// %edi      moving destination pointer
-// %ebp      frac
-    movl    C(ds_colormap),%eax
-//    shld    $22,%ebp,%ecx           // begin calculating third pixel (y units)
-//    shld    $6,%ebp,%ecx            // begin calculating third pixel (x units)
-     movl    %ebp,%ecx
-    addl    %ebx,%ebp               // advance frac pointer
-     shrw    $10,%cx
-     roll    $6,%ecx
-    andl    $4095,%ecx              // finish calculation for third pixel
-//    shld    $22,%ebp,%edx           // begin calculating fourth pixel (y units)
-//    shld    $6,%ebp,%edx            // begin calculating fourth pixel (x units)
-     movl    %ebp,%edx
-     shrw    $10,%dx
-     roll    $6,%edx
-    addl    %ebx,%ebp               // advance frac pointer
-    andl    $4095,%edx              // finish calculation for fourth pixel
-    movl    %eax,%ebx
-    movb    (%esi,%ecx),%al         // get first pixel
-    movb    (%esi,%edx),%bl         // get second pixel
-    testl   $0x0fffffffe,C(pixelcount)
-    movb    (%eax),%dl             // color translate first pixel
-
-//    jnz hdoubleloop             // at least two pixels to map
-//    jmp hchecklast
-
-//    movw $0xf0f0,%dx //see visplanes start
-
-    jz      hchecklast
-    movb    (%ebx),%dh              // color translate second pixel
-    movl    C(loopcount),%esi
-//    .align  4
-hdoubleloop:
-//    shld    $22,%ebp,%ecx        // begin calculating third pixel (y units)
-//    shld    $6,%ebp,%ecx         // begin calculating third pixel (x units)
-    movl    %ebp,%ecx
-    shrw    $10,%cx
-    roll    $6,%ecx
-hpatch1:
-    addl    $0x012345678,%ebp    // advance frac pointer
-    movw    %dx,(%edi)           // write first pixel
-    andl    $4095,%ecx           // finish calculation for third pixel
-//    shld    $22,%ebp,%edx        // begin calculating fourth pixel (y units)
-//    shld    $6,%ebp,%edx         // begin calculating fourth pixel (x units)
-    movl    %ebp,%edx
-    shrw    $10,%dx
-    roll    $6,%edx
-hpatch3:
-    movb    0x012345678(%ecx),%al      // get third pixel
-//    movb    %bl,1(%edi)          // write second pixel
-    andl    $4095,%edx           // finish calculation for fourth pixel
-hpatch2:
-    addl    $0x012345678,%ebp    // advance frac pointer
-hpatch4:
-    movb    0x012345678(%edx),%bl      // get fourth pixel
-    movb    (%eax),%dl           // color translate third pixel
-    addl    $2,%edi              // advance to third pixel destination
-    decl    %esi                 // done with loop?
-    movb    (%ebx),%dh           // color translate fourth pixel
-    jnz hdoubleloop
-
-// check for final pixel
-hchecklast:
-    testl   $1,C(pixelcount)
-    jz      hdone
-    movb    %dl,(%edi)           // write final pixel
-
-hdone:
-    popl    %ebx                 // restore register variables
-    popl    %edi
-    popl    %esi
-    popl    %ebp                 // restore caller's stack frame pointer
-    ret
-
-
-//.endif
-
-
-//----------------------------------------------------------------------
-// R_DrawTransColumn
-//
-// Vertical column texture drawer, with transparency. Replaces Doom2's
-// 'fuzz' effect, which was not so beautiful.
-// Transparency is always impressive in some way, don't know why...
-//----------------------------------------------------------------------
-
-#ifdef LINUX
-    .align 2
-#else
-    .align 5
-#endif
-
-.globl C(R_DrawTranslucentColumn_8)
-C(R_DrawTranslucentColumn_8):
-    pushl   %ebp                // preserve caller's stack frame pointer
-    pushl   %esi                // preserve register variables
-    pushl   %edi
-    pushl   %ebx
-
-//
-// dest = ylookup[dc_yl] + columnofs[dc_x];
-//
-    movl     C(dc_yl),%ebp
-    movl     %ebp,%ebx
-    movl     C(ylookup)(,%ebx,4),%edi
-    movl     C(dc_x),%ebx
-    addl     C(columnofs)(,%ebx,4),%edi  // edi = dest
-
-//
-// pixelcount = yh - yl + 1
-//
-    movl     C(dc_yh),%eax
-    incl     %eax
-    subl     %ebp,%eax                   // pixel count
-    movl     %eax,C(pixelcount)          // save for final pixel
-    jle      vtdone                       // nothing to scale
-
-//
-// frac = dc_texturemid - (centery-dc_yl)*fracstep;
-//
-    movl     C(dc_iscale),%ecx           // fracstep
-    movl     C(centery),%eax
-    subl     %ebp,%eax
-    imul     %ecx,%eax
-    movl     C(dc_texturemid),%edx
-    subl     %eax,%edx
-    movl     %edx,%ebx
-
-    shrl     $16,%ebx          // frac int.
-    andl     $0x0000007f,%ebx
-    shll     $16,%edx          // y frac up
-
-    movl     %ecx,%ebp
-    shll     $16,%ebp          // fracstep f. up
-    shrl     $16,%ecx          // fracstep i. ->cl
-    andb     $0x7f,%cl
-    pushw    %cx
-    movl     %edx,%ecx
-    popw     %cx
-    movl     C(dc_colormap),%edx
-    movl     C(dc_source),%esi
-
-//
-// lets rock :) !
-//
-    movl    C(pixelcount),%eax
-    shrl    $2,%eax
-    testb   $0x03,C(pixelcount)
-    movb    %al,%ch             // quad count
-    movl    C(dc_transmap),%eax
-    jz      vt4quadloop
-//
-//  do un-even pixel
-//
-    testb   $1,C(pixelcount)
-    jz      2f
-
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-     addl    %ebp,%ecx
-    adcb    %cl,%bl
-     movb    (%edi),%al           // fetch dest  : index into colormap
-    andb    $0x7f,%bl
-     movb    (%eax),%dl
-    movb    (%edx), %dl          // use colormap now !
-    movb    %dl,(%edi)
-     addl    C(vidwidth),%edi
-//
-//  do two non-quad-aligned pixels
-//
-2:
-    testb   $2,C(pixelcount)
-    jz      3f
-
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-     addl    %ebp,%ecx
-    adcb    %cl,%bl
-     movb    (%edi),%al           // fetch dest  : index into colormap
-    andb    $0x7f,%bl
-     movb    (%eax),%dl
-    movb    (%edx), %dl          // use colormap now !
-    movb    %dl,(%edi)
-     addl    C(vidwidth),%edi
-
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-     addl    %ebp,%ecx
-    adcb    %cl,%bl
-     movb    (%edi),%al           // fetch dest  : index into colormap
-    andb    $0x7f,%bl
-     movb    (%eax),%dl
-    movb    (%edx), %dl          // use colormap now !
-    movb    %dl,(%edi)
-     addl    C(vidwidth),%edi
-
-//
-//  test if there was at least 4 pixels
-//
-3:
-    testb   $0xFF,%ch           // test quad count
-    jz      vtdone
-
-//
-// tystep : ystep frac. upper 24 bits
-// edx : upper 24 bit : colomap
-//  dl : tmp pixel to write
-// ebx : y     i.    lower 7 bits,  masked for index
-// ecx : y     frac. upper 16 bits
-// ecx : ch = counter, cl = y step i.
-// eax : transmap aligned 65535 (upper 16 bit)
-//  ah : background pixel (from the screen buffer)
-//  al : foreground pixel (from the texture)
-// esi : source texture column
-// ebp,edi : dest screen
-//
-vt4quadloop:
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-p5: movb    0x12345678(%edi),%al           // fetch dest  : index into colormap
-
-    movl    %ebp,C(tystep)
-    movl    %edi,%ebp
-    subl    C(vidwidth),%edi
-    jmp inloop
-//    .align  4
-vtquadloop:
-    addl    C(tystep),%ecx
-    adcb    %cl,%bl
-p6: addl    $2*0x12345678,%ebp
-    andb    $0x7f,%bl
-    movb    (%eax),%dl
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-    movb    (%edx), %dl          // use colormap now !
-    movb    %dl,(%edi)
-    movb    (%ebp),%al           // fetch dest  : index into colormap
-inloop:
-    addl    C(tystep),%ecx
-    adcb    %cl,%bl
-p7: addl    $2*0x12345678,%edi
-    andb    $0x7f,%bl
-    movb    (%eax),%dl
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-    movb    (%edx), %dl          // use colormap now !
-    movb    %dl,(%ebp)
-    movb    (%edi),%al           // fetch dest  : index into colormap
-
-    addl    C(tystep),%ecx
-    adcb    %cl,%bl
-p8: addl    $2*0x12345678,%ebp
-    andb    $0x7f,%bl
-    movb    (%eax),%dl
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-    movb    (%edx), %dl          // use colormap now !
-    movb    %dl,(%edi)
-    movb    (%ebp),%al           // fetch dest  : index into colormap
-
-    addl    C(tystep),%ecx
-    adcb    %cl,%bl
-p9: addl    $2*0x12345678,%edi
-    andb    $0x7f,%bl
-    movb    (%eax),%dl
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-    movb    (%edx), %dl          // use colormap now !
-    movb    %dl,(%ebp)
-    movb    (%edi),%al           // fetch dest  : index into colormap
-
-    decb   %ch
-     jnz    vtquadloop
-
-vtdone:
-    popl    %ebx                // restore register variables
-    popl    %edi
-    popl    %esi
-    popl    %ebp                // restore caller's stack frame pointer
-    ret
-
-#endif // ifdef USEASM
-
-
-
-//----------------------------------------------------------------------
-// R_DrawShadeColumn
-//
-//   for smoke..etc.. test.
-//----------------------------------------------------------------------
-
-#ifdef LINUX
-    .align 2
-#else
-    .align 5
-#endif
-.globl C(R_DrawShadeColumn_8)
-C(R_DrawShadeColumn_8):
-    pushl   %ebp                // preserve caller's stack frame pointer
-    pushl   %esi                // preserve register variables
-    pushl   %edi
-    pushl   %ebx
-
-//
-// dest = ylookup[dc_yl] + columnofs[dc_x];
-//
-    movl     C(dc_yl),%ebp
-    movl     %ebp,%ebx
-    movl     C(ylookup)(,%ebx,4),%edi
-    movl     C(dc_x),%ebx
-    addl     C(columnofs)(,%ebx,4),%edi  // edi = dest
-
-//
-// pixelcount = yh - yl + 1
-//
-    movl     C(dc_yh),%eax
-    incl     %eax
-    subl     %ebp,%eax                   // pixel count
-    movl     %eax,C(pixelcount)          // save for final pixel
-    jle      shdone                       // nothing to scale
-
-//
-// frac = dc_texturemid - (centery-dc_yl)*fracstep;
-//
-    movl     C(dc_iscale),%ecx           // fracstep
-    movl     C(centery),%eax
-    subl     %ebp,%eax
-    imul     %ecx,%eax
-    movl     C(dc_texturemid),%edx
-    subl     %eax,%edx
-     movl     %edx,%ebx
-     shrl     $16,%ebx          // frac int.
-     andl     $0x0000007f,%ebx
-     shll     $16,%edx          // y frac up
-
-     movl     %ecx,%ebp
-     shll     $16,%ebp          // fracstep f. up
-     shrl     $16,%ecx          // fracstep i. ->cl
-     andb     $0x7f,%cl
-
-    movl     C(dc_source),%esi
-
-//
-// lets rock :) !
-//
-    movl    C(pixelcount),%eax
-    movb    %al,%dh
-    shrl    $2,%eax
-    movb    %al,%ch             // quad count
-    movl    C(colormaps),%eax
-    testb   $0x03,%dh
-    jz      sh4quadloop
-
-//
-//  do un-even pixel
-//
-    testb   $1,%dh
-    jz      2f
-
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%edi),%al           // fetch dest  : index into colormap
-    andb    $0x7f,%bl
-     movb    (%eax),%dl
-    movb    %dl,(%edi)
-     addl    C(vidwidth),%edi
-
-//
-//  do two non-quad-aligned pixels
-//
-2:
-    testb   $2,%dh
-    jz      3f
-
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%edi),%al           // fetch dest  : index into colormap
-    andb    $0x7f,%bl
-     movb    (%eax),%dl
-    movb    %dl,(%edi)
-     addl    C(vidwidth),%edi
-
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%edi),%al           // fetch dest  : index into colormap
-    andb    $0x7f,%bl
-     movb    (%eax),%dl
-    movb    %dl,(%edi)
-     addl    C(vidwidth),%edi
-
-//
-//  test if there was at least 4 pixels
-//
-3:
-    testb   $0xFF,%ch           // test quad count
-    jz      shdone
-
-//
-// ebp : ystep frac. upper 24 bits
-// edx : y     frac. upper 24 bits
-// ebx : y     i.    lower 7 bits,  masked for index
-// ecx : ch = counter, cl = y step i.
-// eax : colormap aligned 256
-// esi : source texture column
-// edi : dest screen
-//
-sh4quadloop:
-    movb    $0x7f,%dh           // prep mask
-
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-sh5:    movb    0x12345678(%edi),%al           // fetch dest  : index into colormap
-
-    movl    %ebp,C(tystep)
-    movl    %edi,%ebp
-    subl    C(vidwidth),%edi
-    jmp shinloop
-//    .align  4
-shquadloop:
-    addl    C(tystep),%edx
-    adcb    %cl,%bl
-    andb    %dh,%bl
-sh6:    addl    $2*0x12345678,%ebp
-    movb    (%eax),%dl
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-    movb    %dl,(%edi)
-    movb    (%ebp),%al           // fetch dest  : index into colormap
-shinloop:
-    addl    C(tystep),%edx
-    adcb    %cl,%bl
-    andb    %dh,%bl
-sh7:    addl    $2*0x12345678,%edi
-    movb    (%eax),%dl
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-    movb    %dl,(%ebp)
-    movb    (%edi),%al           // fetch dest  : index into colormap
-
-    addl    C(tystep),%edx
-    adcb    %cl,%bl
-    andb    %dh,%bl
-sh8:    addl    $2*0x12345678,%ebp
-    movb    (%eax),%dl
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-    movb    %dl,(%edi)
-    movb    (%ebp),%al           // fetch dest  : index into colormap
-
-    addl    C(tystep),%edx
-    adcb    %cl,%bl
-    andb    %dh,%bl
-sh9:    addl    $2*0x12345678,%edi
-    movb    (%eax),%dl
-    movb    (%esi,%ebx),%ah      // fetch texel : colormap number
-    movb    %dl,(%ebp)
-    movb    (%edi),%al           // fetch dest  : index into colormap
-
-    decb   %ch
-     jnz    shquadloop
-
-shdone:
-    popl    %ebx                // restore register variables
-    popl    %edi
-    popl    %esi
-    popl    %ebp                // restore caller's stack frame pointer
-    ret
-
-
-
-//----------------------------------------------------------------------
-//
-//  R_DrawWaterColumn : basically it's just a copy of R_DrawColumn,
-//                      but it uses dc_colormap from dc_yl to dc_yw-1
-//                      then it uses dc_wcolormap from dc_yw to dc_yh
-//
-//  Thus, the 'underwater' part of the walls is remapped to 'water-like'
-//  colors.
-//
-//----------------------------------------------------------------------
-
-#ifdef LINUX
-    .align 2
-#else
-    .align 5
-#endif
-.globl C(R_DrawWaterColumn)
-C(R_DrawWaterColumn):
-    pushl   %ebp                // preserve caller's stack frame pointer
-    pushl   %esi                // preserve register variables
-    pushl   %edi
-    pushl   %ebx
-
-//
-// dest = ylookup[dc_yl] + columnofs[dc_x];
-//
-    movl     C(dc_yl),%ebp
-    movl     %ebp,%ebx
-    movl     C(ylookup)(,%ebx,4),%edi
-    movl     C(dc_x),%ebx
-    addl     C(columnofs)(,%ebx,4),%edi  // edi = dest
-
-//
-// pixelcount = yh - yl + 1
-//
-    movl     C(dc_yh),%eax
-    incl     %eax
-    subl     %ebp,%eax                   // pixel count
-    movl     %eax,C(pixelcount)          // save for final pixel
-    jle      wdone                       // nothing to scale
-
-//
-// frac = dc_texturemid - (centery-dc_yl)*fracstep;
-//
-    movl     C(dc_iscale),%ecx           // fracstep
-    movl     C(centery),%eax
-    subl     %ebp,%eax
-    imul     %ecx,%eax
-    movl     C(dc_texturemid),%edx
-    subl     %eax,%edx
-     movl     %edx,%ebx
-     shrl     $16,%ebx          // frac int.
-     andl     $0x0000007f,%ebx
-     shll     $16,%edx          // y frac up
-
-     movl     %ecx,%ebp
-     shll     $16,%ebp          // fracstep f. up
-     shrl     $16,%ecx          // fracstep i. ->cl
-     andb     $0x7f,%cl
-
-    movl     C(dc_source),%esi
-
-//
-// lets rock :) !
-//
-    movl    C(pixelcount),%eax
-    movb    %al,%dh
-    shrl    $2,%eax
-    movb    %al,%ch             // quad count
-    movl    C(dc_wcolormap),%eax
-    testb   $3,%dh
-    jz      w4quadloop
-
-//
-//  do un-even pixel
-//
-    testb   $1,%dh
-    jz      2f
-
-    movb    (%esi,%ebx),%al     // prep un-even loops
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-    andb    $0x7f,%bl            // mask 0-127 texture index
-     movb    %dl,(%edi)           // output pixel
-    addl    C(vidwidth),%edi
-
-//
-//  do two non-quad-aligned pixels
-//
-2:
-    testb   $2,%dh
-    jz      3f
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-    andb    $0x7f,%bl            // mask 0-127 texture index
-     movb    %dl,(%edi)           // output pixel
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-    andb    $0x7f,%bl            // mask 0-127 texture index
-    addl    C(vidwidth),%edi
-     movb    %dl,(%edi)           // output pixel
-
-    addl    C(vidwidth),%edi
-
-//
-//  test if there was at least 4 pixels
-//
-3:
-    testb   $0xFF,%ch           // test quad count
-    jz      wdone
-
-//
-// ebp : ystep frac. upper 24 bits
-// edx : y     frac. upper 24 bits
-// ebx : y     i.    lower 7 bits,  masked for index
-// ecx : ch = counter, cl = y step i.
-// eax : colormap aligned 256
-// esi : source texture column
-// edi : dest screen
-//
-w4quadloop:
-    movb    $0x7f,%dh           // prep mask
-//    .align  4
-wquadloop:
-    movb    (%esi,%ebx),%al     // prep loop
-     addl    %ebp,%edx            // ypos f += ystep f
-    adcb    %cl,%bl              // ypos i += ystep i
-     movb    (%eax),%dl           // colormap texel
-    movb    %dl,(%edi)           // output pixel
-     andb    $0x7f,%bl            // mask 0-127 texture index
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%eax),%dl
-w1:    movb    %dl,0x12345678(%edi)
-     andb    $0x7f,%bl
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%eax),%dl
-w2:    movb    %dl,2*0x12345678(%edi)
-     andb    $0x7f,%bl
-
-    movb    (%esi,%ebx),%al      // fetch source texel
-     addl    %ebp,%edx
-    adcb    %cl,%bl
-     movb    (%eax),%dl
-w3:    movb    %dl,3*0x12345678(%edi)
-     andb    $0x7f,%bl
-
-w4:    addl    $4*0x12345678,%edi
-
-    decb   %ch
-     jnz    wquadloop
-
-wdone:
-    popl    %ebx                // restore register variables
-    popl    %edi
-    popl    %esi
-    popl    %ebp                // restore caller's stack frame pointer
-    ret
-
-
-
-
-
-
-
-//----------------------------------------------------------------------
-//
-//  R_DrawSpanNoWrap
-//
-//      Horizontal texture mapping, does not remap colors,
-//      neither needs to wrap around the source texture.
-//
-//      Thus, a special optimisation can be used...
-//
-//----------------------------------------------------------------------
-
-    .data
-
-advancetable:   .long   0, 0
-#if !defined( LINUX)
-    .text
-#endif
-#ifdef LINUX
-    .align 2
-#else
-    .align 4
-#endif
-.globl C(R_DrawSpanNoWrap)
-C(R_DrawSpanNoWrap):
-    pushl   %ebp                // preserve caller's stack frame pointer
-    pushl   %esi                // preserve register variables
-    pushl   %edi
-    pushl   %ebx
-
-//
-// find loop count
-//
-
-    movl    C(ds_x2),%eax
-    incl    %eax
-    subl    C(ds_x1),%eax               // pixel count
-    movl    %eax,C(pixelcount)          // save for final pixel
-    jle     htvdone                       // nothing to scale
-//    shrl    $1,%eax                     // double pixel count
-//    movl    %eax,C(loopcount)
-
-//
-// calculate screen dest
-//
-
-    movl    C(ds_y),%edi        //full destination start address
-
-//
-// set up advancetable
-//
-
-    movl    C(ds_xstep),%ebp
-    movl    C(ds_ystep),%ecx
-    movl    %ecx,%eax
-    movl    %ebp,%edx
-    sarl    $16,%edx            // xstep >>= 16;
-    movl    C(vidwidth),%ebx
-    sarl    $16,%eax            // ystep >>= 16;
-    jz      0f
-    imull   %ebx,%eax           // (ystep >> 16) * texwidth;
-0:
-    addl    %edx,%eax           // add in xstep
-                                // (ystep >> 16) * texwidth + (xstep >> 16);
-
-    movl    %eax,advancetable+4 // advance base in y
-    addl    %ebx,%eax           // ((ystep >> 16) + 1) * texwidth +
-                                //  (xstep >> 16);
-    movl    %eax,advancetable   // advance extra in y
-
-    shll    $16,%ebp            // left-justify xstep fractional part
-    movl    %ebp,xstep
-    shll    $16,%ecx            // left-justify ystep fractional part
-    movl    %ecx,ystep
-
-//
-// calculate the texture starting address
-//
-    movl    C(ds_source),%esi       // texture source
-
-     movl    C(ds_yfrac),%eax
-     movl    %eax,%edx
-     sarl    $16,%eax
-    movl    C(ds_xfrac),%ecx
-     imull   %ebx,%eax               // (yfrac >> 16) * texwidth
-    movl    %ecx,%ebx
-    sarl    $16,%ecx
-    movl    %ecx,%ebp
-     addl    %eax,%ebp               // source = (xfrac >> 16) +
-                                    //           ((yfrac >> 16) * texwidth);
-
-//
-//  esi : texture source
-//  edi : screen dest
-//  eax : colormap aligned on 256 boundary, hehehe...
-//  ebx : xfrac << 16
-//  ecx : used in loop, contains either 0 or -1, *4, offset into advancetable
-//  edx : yfrac << 16
-//  ebp : offset into texture
-//
-
-    shll    $16,%edx             // yfrac upper word, lower byte will be used
-    movl    C(ds_colormap),%eax
-    shll    $16,%ebx             // xfrac upper word, lower unused
-
-    movl    C(pixelcount),%ecx
-    shrl    $2,%ecx
-    movb    %cl,%dh             // quad pixels count
-
-    movl    C(pixelcount),%ecx
-    andl    $3,%ecx
-    jz      htvquadloop         // pixelcount is multiple of 4
-    decl    %ecx
-    jz      1f
-    decl    %ecx
-    jz      2f
-
-//
-//  do one to three pixels first
-//
-    addl    ystep,%edx          // yfrac += ystep
-   sbbl    %ecx,%ecx           // turn carry into 0 or -1 if set
-    movb    (%esi,%ebp),%al          // get texture pixel
-   addl    xstep,%ebx           // xfrac += xstep
-//    movb    (%eax),%dl           // pixel goes through colormap
-   adcl    advancetable+4(,%ecx,4),%ebp       // advance source
-    movb    %al,(%edi)           // write pixel dest
-
-   incl    %edi
-
-2:
-    addl    ystep,%edx          // yfrac += ystep
-   sbbl    %ecx,%ecx           // turn carry into 0 or -1 if set
-    movb    (%esi,%ebp),%al          // get texture pixel
-   addl    xstep,%ebx           // xfrac += xstep
-//    movb    (%eax),%dl           // pixel goes through colormap
-   adcl    advancetable+4(,%ecx,4),%ebp       // advance source
-    movb    %al,(%edi)           // write pixel dest
-
-   incl    %edi
-
-1:
-    addl    ystep,%edx          // yfrac += ystep
-   sbbl    %ecx,%ecx           // turn carry into 0 or -1 if set
-    movb    (%esi,%ebp),%al          // get texture pixel
-   addl    xstep,%ebx           // xfrac += xstep
-//    movb    (%eax),%dl           // pixel goes through colormap
-   adcl    advancetable+4(,%ecx,4),%ebp       // advance source
-    movb    %al,(%edi)           // write pixel dest
-
-   incl    %edi
-
-//
-//  test if there was at least 4 pixels
-//
-    testb   $0xFF,%dh
-    jz      htvdone
-
-//
-//  two pixels per loop
-// U
-//  V
-htvquadloop:
-    addl    ystep,%edx             // yfrac += ystep
-   sbbl    %ecx,%ecx               // turn carry into 0 or -1 if set
-    movb    (%esi,%ebp),%al        // get texture pixel
-   addl    xstep,%ebx              // xfrac += xstep
-//    movb    (%eax),%dl             // pixel goes through colormap
-   adcl    advancetable+4(,%ecx,4),%ebp       // advance source
-    movb    %al,(%edi)             // write pixel dest
-
-    addl    ystep,%edx
-   sbbl    %ecx,%ecx
-    movb    (%esi,%ebp),%al
-   addl    xstep,%ebx
-//    movb    (%eax),%dl
-   adcl    advancetable+4(,%ecx,4),%ebp
-    movb    %al,1(%edi)
-
-    addl    ystep,%edx
-   sbbl    %ecx,%ecx
-    movb    (%esi,%ebp),%al
-   addl    xstep,%ebx
-//    movb    (%eax),%dl
-   adcl    advancetable+4(,%ecx,4),%ebp
-    movb    %al,2(%edi)
-
-    addl    ystep,%edx
-   sbbl    %ecx,%ecx
-    movb    (%esi,%ebp),%al
-   addl    xstep,%ebx
-//    movb    (%eax),%dl
-   adcl    advancetable+4(,%ecx,4),%ebp
-    movb    %al,3(%edi)
-
-   addl    $4, %edi
-    incl    %ecx    //dummy
-
-   decb   %dh
-    jnz    htvquadloop          // paire dans V-pipe
-
-htvdone:
-    popl    %ebx                // restore register variables
-    popl    %edi
-    popl    %esi
-    popl    %ebp                // restore caller's stack frame pointer
-    ret
-
-
-//.endif
-
-#ifdef HORIZONTALDRAW
-// void R_RotateBuffere (void)
-
-#ifdef LINUX
-    .align 2
-#else
-    .align 4
-#endif
-.globl C(R_RotateBufferasm)
-C(R_RotateBufferasm):
-    pushl   %ebp                // preserve caller's stack frame pointer
-    pushl   %esi                // preserve register variables
-    pushl   %edi
-    pushl   %ebx
-
-    movl    C(dc_source),%esi
-    movl    C(dc_colormap),%edi
-
-    movl    $200,%edx
-ra2:
-    movl    $40,%ecx
-ra:
-    movb    -2*200(%esi),%al
-    movb    -6*200(%esi),%bl
-    movb    -3*200(%esi),%ah
-    movb    -7*200(%esi),%bh
-    shll    $16,%eax
-    shll    $16,%ebx
-    movb    (%esi),%al
-    movb    -4*200(%esi),%bl
-    movb    -1*200(%esi),%ah
-    movb    -5*200(%esi),%bh
-    movl    %eax,(%edi)
-    subl    $8*200,%esi
-    movl    %ebx,4(%edi)
-    addl    $8,%edi
-    decl    %ecx
-    jnz     ra
-
-    addl    $320*200+1,%esi      //32*480 passe a la ligne suivante
-//    addl    320-32,%edi
-
-    decl    %edx
-    jnz     ra2
-
-    pop   %ebp                // preserve caller's stack frame pointer
-    pop   %esi                // preserve register variables
-    pop   %edi
-    pop   %ebx
-    ret
-#endif
diff --git a/src/tmap_asm.s b/src/tmap_asm.s
deleted file mode 100644
index d8967178cd..0000000000
--- a/src/tmap_asm.s
+++ /dev/null
@@ -1,322 +0,0 @@
-// SONIC ROBO BLAST 2
-//-----------------------------------------------------------------------------
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
-//
-// This program is free software distributed under the
-// terms of the GNU General Public License, version 2.
-// See the 'LICENSE' file for more details.
-//-----------------------------------------------------------------------------
-/// \file  tmap_asm.s
-/// \brief ???
-
-//.comm _dc_colormap,4
-//.comm _dc_x,4
-//.comm _dc_yl,4
-//.comm _dc_yh,4
-//.comm _dc_iscale,4
-//.comm _dc_texturemid,4
-//.comm _dc_source,4
-//.comm _ylookup,4
-//.comm _columnofs,4
-//.comm _loopcount,4
-//.comm _pixelcount,4
-.data
-_pixelcount:
-.long 0x00000000
-_loopcount:
-.long 0x00000000
-.align 8
-_mmxcomm:
-.long 0x00000000
-.text
-
-        .align 4
-.globl _R_DrawColumn8_NOMMX
-_R_DrawColumn8_NOMMX:
-   pushl %ebp
-   pushl %esi
-   pushl %edi
-   pushl %ebx
-	movl _dc_yl,%edx
-	movl _dc_yh,%eax
-	subl %edx,%eax
-	leal 1(%eax),%ebx
-	testl %ebx,%ebx
-	jle rdc8ndone
-	movl _dc_x,%eax
-        movl _ylookup, %edi
-	movl (%edi,%edx,4),%esi
-	movl _columnofs, %edi
-	addl (%edi,%eax,4),%esi
-	movl _dc_iscale,%edi
-	movl %edx,%eax
-	imull %edi,%eax
-	movl _dc_texturemid,%ecx
-	addl %eax,%ecx
-
-	movl _dc_source,%ebp
-   xorl %edx, %edx
-   subl $0x12345678, %esi
-.globl rdc8nwidth1
-rdc8nwidth1:
-	.align 4,0x90
-rdc8nloop:
-	movl %ecx,%eax
-	shrl $16,%eax
-	addl %edi,%ecx
-	andl $127,%eax
-	addl $0x12345678,%esi
-.globl rdc8nwidth2
-rdc8nwidth2:
-	movb (%eax,%ebp),%dl
-	movl _dc_colormap,%eax
-	movb (%eax,%edx),%al
-	movb %al,(%esi)
-	decl %ebx
-	jne rdc8nloop
-rdc8ndone:
-   popl %ebx
-   popl %edi
-   popl %esi
-   popl %ebp
-   ret
-
-//
-// Optimised specifically for P54C/P55C (aka Pentium with/without MMX)
-// By ES 1998/08/01
-//
-
-.globl _R_DrawColumn_8_Pentium
-_R_DrawColumn_8_Pentium:
-	pushl %ebp
-        pushl %ebx
-	pushl %esi
-        pushl %edi
-	movl _dc_yl,%eax        // Top pixel
-	movl _dc_yh,%ebx        // Bottom pixel
-        movl _ylookup, %edi
-	movl (%edi,%ebx,4),%ecx
-	subl %eax,%ebx          // ebx=number of pixels-1
-	jl rdc8pdone            // no pixel to draw, done
-	jnz rdc8pmany
-	movl _dc_x,%edx         // Special case: only one pixel
-        movl _columnofs, %edi
-	addl (%edi,%edx,4),%ecx // dest pixel at (%ecx)
-	movl _dc_iscale,%esi
-	imull %esi,%eax
-	movl _dc_texturemid,%edi
-	addl %eax,%edi          // texture index in edi
-	movl _dc_colormap,%edx
-   	shrl $16, %edi
-   	movl _dc_source,%ebp
-	andl $127,%edi
-	movb (%edi,%ebp),%dl    // read texture pixel
-	movb (%edx),%al	        // lookup for light
-	movb %al,0(%ecx) 	// write it
-	jmp rdc8pdone		// done!
-.align 4, 0x90
-rdc8pmany:			// draw >1 pixel
-	movl _dc_x,%edx
-        movl _columnofs, %edi
-	movl (%edi,%edx,4),%edx
-	leal 0x12345678(%edx, %ecx), %edi  // edi = two pixels above bottom
-.globl rdc8pwidth5
-rdc8pwidth5:  // DeadBeef = -2*SCREENWIDTH
-        movl _dc_iscale,%edx	// edx = fracstep
-	imull %edx,%eax
-   	shll $9, %edx           // fixme: Should get 7.25 fix as input
-	movl _dc_texturemid,%ecx
-	addl %eax,%ecx          // ecx = frac
-	movl _dc_colormap,%eax  // eax = lighting/special effects LUT
-   	shll $9, %ecx
-   	movl _dc_source,%esi    // esi = source ptr
-
-	imull $0x12345678, %ebx // ebx = negative offset to pixel
-.globl rdc8pwidth6
-rdc8pwidth6:  // DeadBeef = -SCREENWIDTH
-
-// Begin the calculation of the two first pixels
-        leal (%ecx, %edx), %ebp
-	shrl $25, %ecx
-	movb (%esi, %ecx), %al
-	leal (%edx, %ebp), %ecx
-	shrl $25, %ebp
-        movb (%eax), %dl
-
-// The main loop
-rdc8ploop:
-	movb (%esi,%ebp), %al		// load 1
-        leal (%ecx, %edx), %ebp         // calc frac 3
-
-	shrl $25, %ecx                  // shift frac 2
-        movb %dl, 0x12345678(%edi, %ebx)// store 0
-.globl rdc8pwidth1
-rdc8pwidth1:  // DeadBeef = 2*SCREENWIDTH
-
-        movb (%eax), %al                // lookup 1
-
-        movb %al, 0x12345678(%edi, %ebx)// store 1
-.globl rdc8pwidth2
-rdc8pwidth2:  // DeadBeef = 3*SCREENWIDTH
-        movb (%esi, %ecx), %al          // load 2
-
-        leal (%ebp, %edx), %ecx         // calc frac 4
-
-        shrl $25, %ebp                  // shift frac 3
-        movb (%eax), %dl                // lookup 2
-
-        addl $0x12345678, %ebx          // counter
-.globl rdc8pwidth3
-rdc8pwidth3:  // DeadBeef = 2*SCREENWIDTH
-        jl rdc8ploop                    // loop
-
-// End of loop. Write extra pixel or just exit.
-        jnz rdc8pdone
-        movb %dl, 0x12345678(%edi, %ebx)// Write odd pixel
-.globl rdc8pwidth4
-rdc8pwidth4:  // DeadBeef = 2*SCREENWIDTH
-
-rdc8pdone:
-
-        popl %edi
-	popl %esi
-        popl %ebx
-	popl %ebp
-        ret
-
-//
-// MMX asm version, optimised for K6
-// By ES 1998/07/05
-//
-
-.globl _R_DrawColumn_8_K6_MMX
-_R_DrawColumn_8_K6_MMX:
-	pushl %ebp
-        pushl %ebx
-	pushl %esi
-        pushl %edi
-
-        movl %esp, %eax // Push 8 or 12, so that (%esp) gets aligned by 8
-        andl $7,%eax
-        addl $8,%eax
-        movl %eax, _mmxcomm // Temp storage in mmxcomm: (%esp) is used instead
-        subl %eax,%esp
-
-	movl _dc_yl,%edx        // Top pixel
-	movl _dc_yh,%ebx        // Bottom pixel
-        movl _ylookup, %edi
-	movl (%edi,%ebx,4),%ecx
-	subl %edx,%ebx         // ebx=number of pixels-1
-	jl 0x12345678            // no pixel to draw, done
-.globl rdc8moffs1
-rdc8moffs1:
-	jnz rdc8mmany
-	movl _dc_x,%eax         // Special case: only one pixel
-        movl _columnofs, %edi
-	addl (%edi,%eax,4),%ecx  // dest pixel at (%ecx)
-	movl _dc_iscale,%esi
-	imull %esi,%edx
-	movl _dc_texturemid,%edi
-	addl %edx,%edi         // texture index in edi
-	movl _dc_colormap,%edx
-   	shrl $16, %edi
-   	movl _dc_source,%ebp
-	andl $127,%edi
-	movb (%edi,%ebp),%dl  // read texture pixel
-	movb (%edx),%al	 // lookup for light
-	movb %al,0(%ecx) 	 // write it
-	jmp rdc8mdone		 // done!
-.globl rdc8moffs2
-rdc8moffs2:
-.align 4, 0x90
-rdc8mmany:			 // draw >1 pixel
-	movl _dc_x,%eax
-        movl _columnofs, %edi
-	movl (%edi,%eax,4),%eax
-	leal 0x12345678(%eax, %ecx), %esi  // esi = two pixels above bottom
-.globl rdc8mwidth3
-rdc8mwidth3:  // DeadBeef = -2*SCREENWIDTH
-        movl _dc_iscale,%ecx	 // ecx = fracstep
-	imull %ecx,%edx
-   	shll $9, %ecx           // fixme: Should get 7.25 fix as input
-	movl _dc_texturemid,%eax
-	addl %edx,%eax         // eax = frac
-	movl _dc_colormap,%edx  // edx = lighting/special effects LUT
-   	shll $9, %eax
-	leal (%ecx, %ecx), %edi
-   	movl _dc_source,%ebp    // ebp = source ptr
-	movl %edi, 0(%esp)     // Start moving frac and fracstep to MMX regs
-
-	imull $0x12345678, %ebx  // ebx = negative offset to pixel
-.globl rdc8mwidth5
-rdc8mwidth5:  // DeadBeef = -SCREENWIDTH
-
-	movl %edi, 4(%esp)
-	leal (%eax, %ecx), %edi
-	movq 0(%esp), %mm1     // fracstep:fracstep in mm1
-	movl %eax, 0(%esp)
-	shrl $25, %eax
-	movl %edi, 4(%esp)
-	movzbl (%ebp, %eax), %eax
-	movq 0(%esp), %mm0     // frac:frac in mm0
-
-	paddd %mm1, %mm0
-	shrl $25, %edi
-	movq %mm0, %mm2
-	psrld $25, %mm2         // texture index in mm2
-	paddd %mm1, %mm0
-	movq %mm2, 0(%esp)
-
-.globl rdc8mloop
-rdc8mloop:                      		// The main loop
-	movq %mm0, %mm2                    // move 4-5 to temp reg
-	movzbl (%ebp, %edi), %edi 		// read 1
-
-	psrld $25, %mm2 			// shift 4-5
-	movb (%edx,%eax), %cl 		// lookup 0
-
-	movl 0(%esp), %eax 			// load 2
-	addl $0x12345678, %ebx 		// counter
-.globl rdc8mwidth2
-rdc8mwidth2:  // DeadBeef = 2*SCREENWIDTH
-
-	movb %cl, (%esi, %ebx)		// write 0
-	movb (%edx,%edi), %ch 		// lookup 1
-
-	movb %ch, 0x12345678(%esi, %ebx) 	// write 1
-.globl rdc8mwidth1
-rdc8mwidth1:  // DeadBeef = SCREENWIDTH
-	movl 4(%esp), %edi			// load 3
-
-	paddd %mm1, %mm0 			// frac 6-7
-	movzbl (%ebp, %eax), %eax 		// lookup 2
-
-	movq %mm2, 0(%esp) 		     // store texture index 4-5
-	jl rdc8mloop
-
-	jnz rdc8mno_odd
-	movb (%edx,%eax), %cl  // write the last odd pixel
-	movb %cl, 0x12345678(%esi)
-.globl rdc8mwidth4
-rdc8mwidth4:  // DeadBeef = 2*SCREENWIDTH
-rdc8mno_odd:
-
-.globl rdc8mdone
-rdc8mdone:
-        emms
-
-        addl _mmxcomm, %esp
-        popl %edi
-	popl %esi
-        popl %ebx
-	popl %ebp
-        ret
-
-// Need some extra space to align run-time
-.globl R_DrawColumn_8_K6_MMX_end
-R_DrawColumn_8_K6_MMX_end:
-nop;nop;nop;nop;nop;nop;nop;nop;
-nop;nop;nop;nop;nop;nop;nop;nop;
-nop;nop;nop;nop;nop;nop;nop;nop;
-nop;nop;nop;nop;nop;nop;nop;
diff --git a/src/tmap_mmx.nas b/src/tmap_mmx.nas
deleted file mode 100644
index a45667e23d..0000000000
--- a/src/tmap_mmx.nas
+++ /dev/null
@@ -1,674 +0,0 @@
-;; SONIC ROBO BLAST 2
-;;-----------------------------------------------------------------------------
-;; Copyright (C) 1998-2000 by DOSDOOM.
-;; Copyright (C) 2010-2023 by Sonic Team Junior.
-;;
-;; This program is free software distributed under the
-;; terms of the GNU General Public License, version 2.
-;; See the 'LICENSE' file for more details.
-;;-----------------------------------------------------------------------------
-;; FILE:
-;;      tmap_mmx.nas
-;; DESCRIPTION:
-;;      Assembler optimised rendering code for software mode, using SIMD
-;;      instructions.
-;;      Draw wall columns.
-
-
-[BITS 32]
-
-%define FRACBITS 16
-%define TRANSPARENTPIXEL 255
-
-%ifdef LINUX
-%macro cextern 1
-[extern %1]
-%endmacro
-
-%macro cglobal 1
-[global %1]
-%endmacro
-
-%else
-%macro cextern 1
-%define %1 _%1
-[extern %1]
-%endmacro
-
-%macro cglobal 1
-%define %1 _%1
-[global %1]
-%endmacro
-
-%endif
-
-
-; The viddef_s structure. We only need the width field.
-struc viddef_s
-		resb 12
-.width: resb 4
-		resb 44
-endstruc
-
-
-;; externs
-;; columns
-cextern dc_colormap
-cextern dc_x
-cextern dc_yl
-cextern dc_yh
-cextern dc_iscale
-cextern dc_texturemid
-cextern dc_texheight
-cextern dc_source
-cextern dc_hires
-cextern centery
-cextern centeryfrac
-cextern dc_transmap
-
-cextern R_DrawColumn_8_ASM
-cextern R_Draw2sMultiPatchColumn_8_ASM
-
-;; spans
-cextern nflatshiftup
-cextern nflatxshift
-cextern nflatyshift
-cextern nflatmask
-cextern ds_xfrac
-cextern ds_yfrac
-cextern ds_xstep
-cextern ds_ystep
-cextern ds_x1
-cextern ds_x2
-cextern ds_y
-cextern ds_source
-cextern ds_colormap
-
-cextern ylookup
-cextern columnofs
-cextern vid
-
-[SECTION .data]
-
-nflatmask64		dq		0
-
-
-[SECTION .text]
-
-;;----------------------------------------------------------------------
-;;
-;; R_DrawColumn : 8bpp column drawer
-;;
-;; MMX column drawer.
-;;
-;;----------------------------------------------------------------------
-;; eax = accumulator
-;; ebx = colormap
-;; ecx = count
-;; edx = accumulator
-;; esi = source
-;; edi = dest
-;; ebp = vid.width
-;; mm0 = accumulator
-;; mm1 = heightmask, twice
-;; mm2 = 2 * fracstep, twice
-;; mm3 = pair of consecutive fracs
-;;----------------------------------------------------------------------
-
-
-cglobal R_DrawColumn_8_MMX
-R_DrawColumn_8_MMX:
-		push		ebp						;; preserve caller's stack frame pointer
-		push		esi						;; preserve register variables
-		push		edi
-		push		ebx
-
-;;
-;; Our algorithm requires that the texture height be a power of two.
-;; If not, fall back to the non-MMX drawer.
-;;
-.texheightcheck:
-		mov			edx, [dc_texheight]
-		sub			edx, 1					;; edx = heightmask
-		test		edx, [dc_texheight]
-		jnz			near .usenonMMX
-
-		mov			ebp, edx				;; Keep a copy of heightmask in a
-											;; GPR for the time being.
-
-;;
-;; Fill mm1 with heightmask
-;;
-		movd		mm1, edx				;; low dword = heightmask
-		punpckldq	mm1, mm1				;; copy low dword to high dword
-
-;;
-;; dest = ylookup[dc_yl] + columnofs[dc_x];
-;;
-		mov			eax, [dc_yl]
-		mov			edi, [ylookup+eax*4]
-		mov			ebx, [dc_x]
-		add			edi, [columnofs+ebx*4]	;; edi = dest
-
-
-;;
-;; pixelcount = yh - yl + 1
-;;
-		mov			ecx, [dc_yh]
-		add			ecx, 1
-		sub			ecx, eax				;; pixel count
-		jle			near .done				;; nothing to scale
-
-;;
-;; fracstep = dc_iscale;
-;;
-		movd		mm2, [dc_iscale]		;; fracstep in low dword
-		punpckldq	mm2, mm2				;; copy to high dword
-
-		mov			ebx, [dc_colormap]
-		mov			esi, [dc_source]
-
-;;
-;; frac = (dc_texturemid + FixedMul((dc_yl << FRACBITS) - centeryfrac, fracstep));
-;;
-											;; eax == dc_yl already
-		shl			eax, FRACBITS
-		sub			eax, [centeryfrac]
-		imul		dword [dc_iscale]
-		shrd		eax, edx, FRACBITS
-		add			eax, [dc_texturemid]
-
-;;
-;; if (dc_hires) frac = 0;
-;;
-		test		byte [dc_hires], 0x01
-		jz			.mod2
-		xor			eax, eax
-
-
-;;
-;; Do mod-2 pixel.
-;;
-.mod2:
-		test		ecx, 1
-		jz			.pairprepare
-		mov			edx, eax				;; edx = frac
-		add			eax, [dc_iscale]		;; eax += fracstep
-		sar			edx, FRACBITS
-		and			edx, ebp				;; edx &= heightmask
-		movzx		edx, byte [esi + edx]
-		movzx		edx, byte [ebx + edx]
-		mov			[edi], dl
-
-		add			edi, [vid + viddef_s.width]
-		sub			ecx, 1
-		jz			.done
-
-.pairprepare:
-;;
-;; Prepare for the main loop.
-;;
-		movd		mm3, eax				;; Low dword = frac
-		movq		mm4, mm3				;; Copy to intermediate register
-		paddd		mm4, mm2				;; dwords of mm4 += fracstep
-		punpckldq	mm3, mm4				;; Low dword = first frac, high = second
-		pslld		mm2, 1					;; fracstep *= 2
-
-;;
-;; ebp = vid.width
-;;
-		mov			ebp, [vid + viddef_s.width]
-
-		align		16
-.pairloop:
-		movq		mm0, mm3				;; 3B 1u.
-		psrad		mm0, FRACBITS			;; 4B 1u.
-		pand		mm0, mm1				;; 3B 1u. frac &= heightmask
-		paddd		mm3, mm2				;; 3B 1u. frac += fracstep
-
-		movd		eax, mm0				;; 3B 1u. Get first frac
-;; IFETCH boundary
-		movzx		eax, byte [esi + eax]	;; 4B 1u. Texture map
-		movzx		eax, byte [ebx + eax]	;; 4B 1u. Colormap
-
-		punpckhdq	mm0, mm0				;; 3B 1(2)u. low dword = high dword
-		movd		edx, mm0				;; 3B 1u. Get second frac
-		mov			[edi], al				;; 2B 1(2)u. First pixel
-;; IFETCH boundary
-
-		movzx		edx, byte [esi + edx]	;; 4B 1u. Texture map
-		movzx		edx, byte [ebx + edx]	;; 4B 1u. Colormap
-		mov			[edi + 1*ebp], dl		;; 3B 1(2)u. Second pixel
-
-		lea			edi, [edi + 2*ebp]		;; 3B 1u. edi += 2 * vid.width
-;; IFETCH boundary
-		sub			ecx, 2					;; 3B 1u. count -= 2
-		jnz			.pairloop				;; 2B 1u. if(count != 0) goto .pairloop
-
-
-.done:
-;;
-;; Clear MMX state, or else FPU operations will go badly awry.
-;;
-		emms
-
-		pop			ebx
-		pop			edi
-		pop			esi
-		pop			ebp
-		ret
-
-.usenonMMX:
-		call		R_DrawColumn_8_ASM
-		jmp			.done
-
-
-;;----------------------------------------------------------------------
-;;
-;; R_Draw2sMultiPatchColumn : Like R_DrawColumn, but omits transparent
-;;                            pixels.
-;;
-;; MMX column drawer.
-;;
-;;----------------------------------------------------------------------
-;; eax = accumulator
-;; ebx = colormap
-;; ecx = count
-;; edx = accumulator
-;; esi = source
-;; edi = dest
-;; ebp = vid.width
-;; mm0 = accumulator
-;; mm1 = heightmask, twice
-;; mm2 = 2 * fracstep, twice
-;; mm3 = pair of consecutive fracs
-;;----------------------------------------------------------------------
-
-
-cglobal R_Draw2sMultiPatchColumn_8_MMX
-R_Draw2sMultiPatchColumn_8_MMX:
-		push		ebp						;; preserve caller's stack frame pointer
-		push		esi						;; preserve register variables
-		push		edi
-		push		ebx
-
-;;
-;; Our algorithm requires that the texture height be a power of two.
-;; If not, fall back to the non-MMX drawer.
-;;
-.texheightcheck:
-		mov			edx, [dc_texheight]
-		sub			edx, 1					;; edx = heightmask
-		test		edx, [dc_texheight]
-		jnz			near .usenonMMX
-
-		mov			ebp, edx				;; Keep a copy of heightmask in a
-											;; GPR for the time being.
-
-;;
-;; Fill mm1 with heightmask
-;;
-		movd		mm1, edx				;; low dword = heightmask
-		punpckldq	mm1, mm1				;; copy low dword to high dword
-
-;;
-;; dest = ylookup[dc_yl] + columnofs[dc_x];
-;;
-		mov			eax, [dc_yl]
-		mov			edi, [ylookup+eax*4]
-		mov			ebx, [dc_x]
-		add			edi, [columnofs+ebx*4]	;; edi = dest
-
-
-;;
-;; pixelcount = yh - yl + 1
-;;
-		mov			ecx, [dc_yh]
-		add			ecx, 1
-		sub			ecx, eax				;; pixel count
-		jle			near .done				;; nothing to scale
-;;
-;; fracstep = dc_iscale;
-;;
-		movd		mm2, [dc_iscale]		;; fracstep in low dword
-		punpckldq	mm2, mm2				;; copy to high dword
-
-		mov			ebx, [dc_colormap]
-		mov			esi, [dc_source]
-
-;;
-;; frac = (dc_texturemid + FixedMul((dc_yl << FRACBITS) - centeryfrac, fracstep));
-;;
-											;; eax == dc_yl already
-		shl			eax, FRACBITS
-		sub			eax, [centeryfrac]
-		imul		dword [dc_iscale]
-		shrd		eax, edx, FRACBITS
-		add			eax, [dc_texturemid]
-
-;;
-;; if (dc_hires) frac = 0;
-;;
-		test		byte [dc_hires], 0x01
-		jz			.mod2
-		xor			eax, eax
-
-
-;;
-;; Do mod-2 pixel.
-;;
-.mod2:
-		test		ecx, 1
-		jz			.pairprepare
-		mov			edx, eax				;; edx = frac
-		add			eax, [dc_iscale]		;; eax += fracstep
-		sar			edx, FRACBITS
-		and			edx, ebp				;; edx &= heightmask
-		movzx		edx, byte [esi + edx]
-		cmp			dl, TRANSPARENTPIXEL
-		je			.nextmod2
-		movzx		edx, byte [ebx + edx]
-		mov			[edi], dl
-
-.nextmod2:
-		add			edi, [vid + viddef_s.width]
-		sub			ecx, 1
-		jz			.done
-
-.pairprepare:
-;;
-;; Prepare for the main loop.
-;;
-		movd		mm3, eax				;; Low dword = frac
-		movq		mm4, mm3				;; Copy to intermediate register
-		paddd		mm4, mm2				;; dwords of mm4 += fracstep
-		punpckldq	mm3, mm4				;; Low dword = first frac, high = second
-		pslld		mm2, 1					;; fracstep *= 2
-
-;;
-;; ebp = vid.width
-;;
-		mov			ebp, [vid + viddef_s.width]
-
-		align		16
-.pairloop:
-		movq		mm0, mm3				;; 3B 1u.
-		psrad		mm0, FRACBITS			;; 4B 1u.
-		pand		mm0, mm1				;; 3B 1u. frac &= heightmask
-		paddd		mm3, mm2				;; 3B 1u. frac += fracstep
-
-		movd		eax, mm0				;; 3B 1u. Get first frac
-;; IFETCH boundary
-		movzx		eax, byte [esi + eax]	;; 4B 1u. Texture map
-		punpckhdq	mm0, mm0				;; 3B 1(2)u. low dword = high dword
-		movd		edx, mm0				;; 3B 1u. Get second frac
-		cmp			al, TRANSPARENTPIXEL	;; 2B 1u.
-		je			.secondinpair			;; 2B 1u.
-;; IFETCH boundary
-		movzx		eax, byte [ebx + eax]	;; 4B 1u. Colormap
-		mov			[edi], al				;; 2B 1(2)u. First pixel
-
-.secondinpair:
-		movzx		edx, byte [esi + edx]	;; 4B 1u. Texture map
-		cmp			dl, TRANSPARENTPIXEL	;; 2B 1u.
-		je			.nextpair				;; 2B 1u.
-;; IFETCH boundary
-		movzx		edx, byte [ebx + edx]	;; 4B 1u. Colormap
-		mov			[edi + 1*ebp], dl		;; 3B 1(2)u. Second pixel
-
-.nextpair:
-		lea			edi, [edi + 2*ebp]		;; 3B 1u. edi += 2 * vid.width
-		sub			ecx, 2					;; 3B 1u. count -= 2
-		jnz			.pairloop				;; 2B 1u. if(count != 0) goto .pairloop
-
-
-.done:
-;;
-;; Clear MMX state, or else FPU operations will go badly awry.
-;;
-		emms
-
-		pop			ebx
-		pop			edi
-		pop			esi
-		pop			ebp
-		ret
-
-.usenonMMX:
-		call		R_Draw2sMultiPatchColumn_8_ASM
-		jmp			.done
-
-
-;;----------------------------------------------------------------------
-;;
-;; R_DrawSpan : 8bpp span drawer
-;;
-;; MMX span drawer.
-;;
-;;----------------------------------------------------------------------
-;; eax = accumulator
-;; ebx = colormap
-;; ecx = count
-;; edx = accumulator
-;; esi = source
-;; edi = dest
-;; ebp = two pixels
-;; mm0 = accumulator
-;; mm1 = xposition
-;; mm2 = yposition
-;; mm3 = 2 * xstep
-;; mm4 = 2 * ystep
-;; mm5 = nflatxshift
-;; mm6 = nflatyshift
-;; mm7 = accumulator
-;;----------------------------------------------------------------------
-
-cglobal R_DrawSpan_8_MMX
-R_DrawSpan_8_MMX:
-		push		ebp						;; preserve caller's stack frame pointer
-		push		esi						;; preserve register variables
-		push		edi
-		push		ebx
-
-;;
-;; esi = ds_source
-;; ebx = ds_colormap
-;;
-		mov			esi, [ds_source]
-		mov			ebx, [ds_colormap]
-
-;;
-;; edi = ylookup[ds_y] + columnofs[ds_x1]
-;;
-		mov			eax, [ds_y]
-		mov			edi, [ylookup + eax*4]
-		mov			edx, [ds_x1]
-		add			edi, [columnofs + edx*4]
-
-;;
-;; ecx = ds_x2 - ds_x1 + 1
-;;
-		mov			ecx, [ds_x2]
-		sub			ecx, edx
-		add			ecx, 1
-
-;;
-;; Needed for fracs and steps
-;;
-		movd		mm7, [nflatshiftup]
-
-;;
-;; mm3 = xstep
-;;
-		movd		mm3, [ds_xstep]
-		pslld		mm3, mm7
-		punpckldq	mm3, mm3
-
-;;
-;; mm4 = ystep
-;;
-		movd		mm4, [ds_ystep]
-		pslld		mm4, mm7
-		punpckldq	mm4, mm4
-
-;;
-;; mm1 = pair of consecutive xpositions
-;;
-		movd		mm1, [ds_xfrac]
-		pslld		mm1, mm7
-		movq		mm6, mm1
-		paddd		mm6, mm3
-		punpckldq	mm1, mm6
-
-;;
-;; mm2 = pair of consecutive ypositions
-;;
-		movd		mm2, [ds_yfrac]
-		pslld		mm2, mm7
-		movq		mm6, mm2
-		paddd		mm6, mm4
-		punpckldq	mm2, mm6
-
-;;
-;; mm5 = nflatxshift
-;; mm6 = nflatyshift
-;;
-		movd		mm5, [nflatxshift]
-		movd		mm6, [nflatyshift]
-
-;;
-;; Mask is in memory due to lack of registers.
-;;
-		mov			eax, [nflatmask]
-		mov			[nflatmask64], eax
-		mov			[nflatmask64 + 4], eax
-
-
-;;
-;; Go until we reach a dword boundary.
-;;
-.unaligned:
-		test		edi, 3
-		jz			.alignedprep
-.stragglers:
-		cmp			ecx, 0
-		je			.done					;; If ecx == 0, we're finished.
-
-;;
-;; eax = ((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)
-;;
-		movq		mm0, mm1				;; mm0 = xposition
-		movq		mm7, mm2				;; mm7 = yposition
-		paddd		mm1, mm3				;; xposition += xstep (once!)
-		paddd		mm2, mm4				;; yposition += ystep (once!)
-		psrld		mm0, mm5				;; shift
-		psrld		mm7, mm6				;; shift
-		pand		mm7, [nflatmask64]		;; mask
-		por			mm0, mm7				;; or x and y together
-
-		movd		eax, mm0				;; eax = index of first pixel
-		movzx		eax, byte [esi + eax]	;; al = source[eax]
-		movzx		eax, byte [ebx + eax]	;; al = colormap[al]
-
-		mov			[edi], al
-		add			edi, 1
-
-		sub			ecx, 1
-		jmp			.unaligned
-
-
-.alignedprep:
-;;
-;; We can double the steps now.
-;;
-		pslld		mm3, 1
-		pslld		mm4, 1
-
-
-;;
-;; Generate chunks of four pixels.
-;;
-.alignedloop:
-
-;;
-;; Make sure we have at least four pixels.
-;;
-		cmp			ecx, 4
-		jl			.prestragglers
-
-;;
-;; First two pixels.
-;;
-		movq		mm0, mm1				;; mm0 = xposition
-		movq		mm7, mm2				;; mm7 = yposition
-		paddd		mm1, mm3				;; xposition += xstep
-		paddd		mm2, mm4				;; yposition += ystep
-		psrld		mm0, mm5				;; shift
-		psrld		mm7, mm6				;; shift
-		pand		mm7, [nflatmask64]		;; mask
-		por			mm0, mm7				;; or x and y together
-
-		movd		eax, mm0				;; eax = index of first pixel
-		movzx		eax, byte [esi + eax]	;; al = source[eax]
-		movzx		ebp, byte [ebx + eax]	;; ebp = colormap[al]
-
-		punpckhdq	mm0, mm0				;; both dwords = high dword
-		movd		eax, mm0				;; eax = index of second pixel
-		movzx		eax, byte [esi + eax]	;; al = source[eax]
-		movzx		eax, byte [ebx + eax]	;; al = colormap[al]
-		shl			eax, 8					;; get pixel in right byte
-		or			ebp, eax				;; put pixel in ebp
-
-;;
-;; Next two pixels.
-;;
-		movq		mm0, mm1				;; mm0 = xposition
-		movq		mm7, mm2				;; mm7 = yposition
-		paddd		mm1, mm3				;; xposition += xstep
-		paddd		mm2, mm4				;; yposition += ystep
-		psrld		mm0, mm5				;; shift
-		psrld		mm7, mm6				;; shift
-		pand		mm7, [nflatmask64]		;; mask
-		por			mm0, mm7				;; or x and y together
-
-		movd		eax, mm0				;; eax = index of third pixel
-		movzx		eax, byte [esi + eax]	;; al = source[eax]
-		movzx		eax, byte [ebx + eax]	;; al = colormap[al]
-		shl			eax, 16					;; get pixel in right byte
-		or			ebp, eax				;; put pixel in ebp
-
-		punpckhdq	mm0, mm0				;; both dwords = high dword
-		movd		eax, mm0				;; eax = index of second pixel
-		movzx		eax, byte [esi + eax]	;; al = source[eax]
-		movzx		eax, byte [ebx + eax]	;; al = colormap[al]
-		shl			eax, 24					;; get pixel in right byte
-		or			ebp, eax				;; put pixel in ebp
-
-;;
-;; Write pixels.
-;;
-		mov			[edi], ebp
-		add			edi, 4
-
-		sub			ecx, 4
-		jmp			.alignedloop
-
-.prestragglers:
-;;
-;; Back to one step at a time.
-;;
-		psrad		mm3, 1
-		psrad		mm4, 1
-		jmp			.stragglers
-
-.done:
-;;
-;; Clear MMX state, or else FPU operations will go badly awry.
-;;
-		emms
-
-		pop			ebx
-		pop			edi
-		pop			esi
-		pop			ebp
-		ret
diff --git a/src/tmap_vc.nas b/src/tmap_vc.nas
deleted file mode 100644
index c85cf70035..0000000000
--- a/src/tmap_vc.nas
+++ /dev/null
@@ -1,48 +0,0 @@
-;; SONIC ROBO BLAST 2
-;;-----------------------------------------------------------------------------
-;; Copyright (C) 1998-2000 by DooM Legacy Team.
-;; Copyright (C) 1999-2023 by Sonic Team Junior.
-;;
-;; This program is free software distributed under the
-;; terms of the GNU General Public License, version 2.
-;; See the 'LICENSE' file for more details.
-;;-----------------------------------------------------------------------------
-;; FILE:
-;;      tmap_vc.nas
-;; DESCRIPTION:
-;;      Assembler optimised math code for Visual C++.
-
-
-[BITS 32]
-
-%macro cglobal 1
-%define %1 _%1
-[global %1]
-%endmacro
-
-[SECTION .text write]
-
-;----------------------------------------------------------------------------
-;fixed_t FixedMul (fixed_t a, fixed_t b)
-;----------------------------------------------------------------------------
-cglobal FixedMul
-;       align   16
-FixedMul:
-        mov     eax,[esp+4]
-        imul    dword [esp+8]
-        shrd    eax,edx,16
-        ret
-
-;----------------------------------------------------------------------------
-;fixed_t FixedDiv2 (fixed_t a, fixed_t b);
-;----------------------------------------------------------------------------
-cglobal FixedDiv2
-;       align   16
-FixedDiv2:
-        mov     eax,[esp+4]
-        mov     edx,eax                 ;; these two instructions allow the next
-        sar     edx,31                  ;; two to pair, on the Pentium processor.
-        shld    edx,eax,16
-        sal     eax,16
-        idiv    dword [esp+8]
-        ret
diff --git a/src/v_video.c b/src/v_video.c
index 461a5e3bc7..3f958b286c 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -447,12 +447,6 @@ static void CV_palette_OnChange(void)
 	V_SetPalette(0);
 }
 
-#if defined (__GNUC__) && defined (__i386__) && !defined (NOASM) && !defined (__APPLE__) && !defined (NORUSEASM)
-void VID_BlitLinearScreen_ASM(const UINT8 *srcptr, UINT8 *destptr, INT32 width, INT32 height, size_t srcrowbytes,
-	size_t destrowbytes);
-#define HAVE_VIDCOPY
-#endif
-
 static void CV_constextsize_OnChange(void)
 {
 	if (!con_refresh)
@@ -466,9 +460,6 @@ static void CV_constextsize_OnChange(void)
 void VID_BlitLinearScreen(const UINT8 *srcptr, UINT8 *destptr, INT32 width, INT32 height, size_t srcrowbytes,
 	size_t destrowbytes)
 {
-#ifdef HAVE_VIDCOPY
-    VID_BlitLinearScreen_ASM(srcptr,destptr,width,height,srcrowbytes,destrowbytes);
-#else
 	if (srcrowbytes == destrowbytes)
 		M_Memcpy(destptr, srcptr, srcrowbytes * height);
 	else
@@ -481,7 +472,6 @@ void VID_BlitLinearScreen(const UINT8 *srcptr, UINT8 *destptr, INT32 width, INT3
 			srcptr += srcrowbytes;
 		}
 	}
-#endif
 }
 
 static UINT8 hudplusalpha[11]  = { 10,  8,  6,  4,  2,  0,  0,  0,  0,  0,  0};
diff --git a/src/vid_copy.s b/src/vid_copy.s
deleted file mode 100644
index 1473a3856f..0000000000
--- a/src/vid_copy.s
+++ /dev/null
@@ -1,61 +0,0 @@
-// SONIC ROBO BLAST 2
-//-----------------------------------------------------------------------------
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2023 by Sonic Team Junior.
-//
-// This program is free software distributed under the
-// terms of the GNU General Public License, version 2.
-// See the 'LICENSE' file for more details.
-//-----------------------------------------------------------------------------
-/// \file  vid_copy.s
-/// \brief code for updating the linear frame buffer screen.
-
-#include "asm_defs.inc"           // structures, must match the C structures!
-
-// DJGPPv2 is as fast as this one, but then someone may compile with a less
-// good version of DJGPP than mine, so this little asm will do the trick!
-
-#define srcptr          4+16
-#define destptr         8+16
-#define width           12+16
-#define height          16+16
-#define srcrowbytes     20+16
-#define destrowbytes    24+16
-
-// VID_BlitLinearScreen( src, dest, width, height, srcwidth, destwidth );
-//         width is given as BYTES
-
-#ifdef __i386__
-
-.globl C(VID_BlitLinearScreen_ASM)
-C(VID_BlitLinearScreen_ASM):
-    pushl   %ebp                // preserve caller's stack frame
-    pushl   %edi
-    pushl   %esi                // preserve register variables
-    pushl   %ebx
-
-    cld
-    movl    srcptr(%esp),%esi
-    movl    destptr(%esp),%edi
-    movl    width(%esp),%ebx
-    movl    srcrowbytes(%esp),%eax
-    subl    %ebx,%eax
-    movl    destrowbytes(%esp),%edx
-    subl    %ebx,%edx
-    shrl    $2,%ebx
-    movl    height(%esp),%ebp
-LLRowLoop:
-    movl    %ebx,%ecx
-    rep/movsl   (%esi),(%edi)
-    addl    %eax,%esi
-    addl    %edx,%edi
-    decl    %ebp
-    jnz     LLRowLoop
-
-    popl    %ebx                // restore register variables
-    popl    %esi
-    popl    %edi
-    popl    %ebp                // restore the caller's stack frame
-
-    ret
-#endif
diff --git a/tools/anglechk.c b/tools/anglechk.c
index 4a67069bf7..7f56abff7e 100644
--- a/tools/anglechk.c
+++ b/tools/anglechk.c
@@ -22,7 +22,6 @@
 #ifdef _MSC_VER
 #include <assert.h>
 #endif
-#define NOASM
 #include "../src/tables.h"
 #define NO_M
 #include "../src/m_fixed.c"
-- 
GitLab


From 723a148089449417915ad682bcaaee7a2da945ea Mon Sep 17 00:00:00 2001
From: tertu marybig <flameshadowxeroshin@gmail.com>
Date: Fri, 28 Jul 2023 11:38:59 +0000
Subject: [PATCH 295/518] Remove FixedMul and FixedDiv2 asm implementations

---
 src/m_fixed.c |  44 ----------------
 src/m_fixed.h | 136 ++++++++------------------------------------------
 2 files changed, 22 insertions(+), 158 deletions(-)

diff --git a/src/m_fixed.c b/src/m_fixed.c
index d40ccd98e3..315ac0bde5 100644
--- a/src/m_fixed.c
+++ b/src/m_fixed.c
@@ -21,50 +21,6 @@
 #include "doomdef.h"
 #include "m_fixed.h"
 
-#ifdef __USE_C_FIXEDMUL__
-
-/**	\brief	The FixedMul function
-
-	\param	a	fixed_t number
-	\param	b	fixed_t number
-
-	\return	a*b>>FRACBITS
-
-*/
-fixed_t FixedMul(fixed_t a, fixed_t b)
-{
-	// Need to cast to unsigned before shifting to avoid undefined behaviour
-	// for negative integers
-	return (fixed_t)(((UINT64)((INT64)a * b)) >> FRACBITS);
-}
-
-#endif //__USE_C_FIXEDMUL__
-
-#ifdef __USE_C_FIXEDDIV__
-/**	\brief	The FixedDiv2 function
-
-	\param	a	fixed_t number
-	\param	b	fixed_t number
-
-	\return	a/b * FRACUNIT
-
-*/
-fixed_t FixedDiv2(fixed_t a, fixed_t b)
-{
-	INT64 ret;
-
-	if (b == 0)
-		I_Error("FixedDiv: divide by zero");
-
-	ret = (((INT64)a * FRACUNIT)) / b;
-
-	if ((ret > INT32_MAX) || (ret < INT32_MIN))
-		I_Error("FixedDiv: divide by zero");
-	return (fixed_t)ret;
-}
-
-#endif // __USE_C_FIXEDDIV__
-
 fixed_t FixedSqrt(fixed_t x)
 {
 #ifdef HAVE_SQRT
diff --git a/src/m_fixed.h b/src/m_fixed.h
index 1cf2f00d1e..83b1710f77 100644
--- a/src/m_fixed.h
+++ b/src/m_fixed.h
@@ -53,127 +53,35 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FloatToFixed(float f)
 #define FIXED_TO_FLOAT(x) FixedToFloat(x) // (((float)(x)) / ((float)FRACUNIT))
 #define FLOAT_TO_FIXED(f) FloatToFixed(f) // (fixed_t)((f) * ((float)FRACUNIT))
 
+/**	\brief	The FixedMul function
 
-#if defined (__WATCOMC__) && FRACBITS == 16
-	#pragma aux FixedMul =  \
-		"imul ebx",         \
-		"shrd eax,edx,16"   \
-		parm    [eax] [ebx] \
-		value   [eax]       \
-		modify exact [eax edx]
-
-	#pragma aux FixedDiv2 = \
-		"cdq",              \
-		"shld edx,eax,16",  \
-		"sal eax,16",       \
-		"idiv ebx"          \
-		parm    [eax] [ebx] \
-		value   [eax]       \
-		modify exact [eax edx]
-#elif defined (__GNUC__) && defined (__i386__) && !defined (NOASM)
-	// i386 linux, cygwin or mingw
-	FUNCMATH FUNCINLINE static inline fixed_t FixedMul(fixed_t a, fixed_t b) // asm
-	{
-		fixed_t ret;
-		asm
-		(
-			 "imull %2;"           // a*b
-			 "shrdl %3,%%edx,%0;"  // shift logical right FRACBITS bits
-			:"=a" (ret)            // eax is always the result and the first operand (%0,%1)
-			:"0" (a), "r" (b)      // and %2 is what we use imull on with what in %1
-			, "I" (FRACBITS)       // %3 holds FRACBITS (normally 16)
-			:"cc", "%edx"         // edx and condition codes clobbered
-		);
-		return ret;
-	}
+	\param	a	fixed_t number
+	\param	b	fixed_t number
 
-	FUNCMATH FUNCINLINE static inline fixed_t FixedDiv2(fixed_t a, fixed_t b)
-	{
-		fixed_t ret;
-		asm
-		(
-			  "movl  %1,%%edx;"    // these two instructions allow the next two to pair, on the Pentium processor.
-			  "sarl  $31,%%edx;"   // shift arithmetic right 31 on EDX
-			  "shldl %3,%1,%%edx;" // DP shift logical left FRACBITS on EDX
-			  "sall  %3,%0;"       // shift arithmetic left FRACBITS on EAX
-			  "idivl %2;"          // EDX/b = EAX
-			: "=a" (ret)
-			: "0" (a), "r" (b)
-			, "I" (FRACBITS)
-			: "%edx"
-		);
-		return ret;
-	}
-#elif defined (__GNUC__) && defined (__arm__) && !defined(__thumb__) && !defined(NOASM) //ARMv4 ASM
-	FUNCMATH FUNCINLINE static inline fixed_t FixedMul(fixed_t a, fixed_t b) // let abuse smull
-	{
-		fixed_t ret;
-		asm
-		(
-			  "smull %[lo], r1, %[a], %[b];"
-			  "mov %[lo], %[lo], lsr %3;"
-			  "orr %[lo], %[lo], r1, lsl %3;"
-			: [lo] "=&r" (ret) // rhi, rlo and rm must be distinct registers
-			: [a] "r" (a), [b] "r" (b)
-			, "i" (FRACBITS)
-			: "r1"
-		);
-		return ret;
-	}
+	\return	a*b>>FRACBITS
 
-	#define __USE_C_FIXEDDIV__ // no double or asm div in ARM land
-#elif defined (__GNUC__) && defined (__ppc__) && !defined(NOASM) && 0 // WII: PPC CPU
-	FUNCMATH FUNCINLINE static inline fixed_t FixedMul(fixed_t a, fixed_t b) // asm
-	{
-		fixed_t ret, hi, lo;
-		asm
-		(
-			  "mullw %0, %2, %3;"
-			  "mulhw %1, %2, %3"
-			: "=r" (hi), "=r" (lo)
-			: "r" (a), "r" (b)
-			, "I" (FRACBITS)
-		);
-		ret = (INT64)((hi>>FRACBITS)+lo)<<FRACBITS;
-		return ret;
-	}
+*/
+FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedMul(fixed_t a, fixed_t b)
+{
+	// Need to cast to unsigned before shifting to avoid undefined behaviour
+	// for negative integers
+	return (fixed_t)(((UINT64)((INT64)a * b)) >> FRACBITS);
+}
 
-	#define __USE_C_FIXEDDIV__// Alam: I am lazy
-#elif defined (__GNUC__) && defined (__mips__) && !defined(NOASM) && 0 // PSP: MIPS CPU
-	FUNCMATH FUNCINLINE static inline fixed_t FixedMul(fixed_t a, fixed_t b) // asm
-	{
-		fixed_t ret;
-		asm
-		(
-			  "mult %3, %4;"    // a*b=h<32+l
-			: "=r" (ret), "=l" (a), "=h" (b) //todo: abuse shr opcode
-			: "0" (a), "r" (b)
-			, "I" (FRACBITS)
-			//: "+l", "+h"
-		);
-		ret = (INT64)((a>>FRACBITS)+b)<<FRACBITS;
-		return ret;
-	}
+/**	\brief	The FixedDiv2 function
 
-	#define __USE_C_FIXEDDIV__ // no 64b asm div in MIPS land
-#elif defined (__GNUC__) && defined (__sh__) && 0 // DC: SH4 CPU
-#elif defined (__GNUC__) && defined (__m68k__) && 0 // DEAD: Motorola 6800 CPU
-#elif defined (_MSC_VER) && defined(USEASM) && FRACBITS == 16
-	// Microsoft Visual C++ (no asm inline)
-	fixed_t __cdecl FixedMul(fixed_t a, fixed_t b);
-	fixed_t __cdecl FixedDiv2(fixed_t a, fixed_t b);
-#else
-	#define __USE_C_FIXEDMUL__
-	#define __USE_C_FIXEDDIV__
-#endif
+	\param	a	fixed_t number
+	\param	b	fixed_t number
 
-#ifdef __USE_C_FIXEDMUL__
-FUNCMATH fixed_t FixedMul(fixed_t a, fixed_t b);
-#endif
+	\return	a/b * FRACUNIT
 
-#ifdef __USE_C_FIXEDDIV__
-FUNCMATH fixed_t FixedDiv2(fixed_t a, fixed_t b);
-#endif
+*/
+FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedDiv2(fixed_t a, fixed_t b)
+{
+	// This does not check for division overflow or division by 0!
+	// That is the caller's responsibility.
+	return (fixed_t)(((INT64)a * FRACUNIT) / b);
+}
 
 /**	\brief	The FixedInt function
 
-- 
GitLab


From 0cca1a77ffce233539c5f7c5f310de5ee5034ed7 Mon Sep 17 00:00:00 2001
From: katsy <katmint@live.com>
Date: Fri, 28 Jul 2023 11:26:02 -0500
Subject: [PATCH 296/518] remove NASM from readme and circleci

---
 .circleci/config.yml | 2 +-
 README.md            | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 711be39d76..3faca372cd 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -50,7 +50,7 @@ jobs:
             - v1-SRB2-APT
       - run:
           name: Install SDK
-          command: apt-get -o Dir::Cache="/root/.cache/apt" -qq -y --no-install-recommends install git build-essential nasm libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 libcurl4-openssl-dev:i386 libopenmpt-dev:i386 gettext ccache wget gcc-multilib upx openssh-client
+          command: apt-get -o Dir::Cache="/root/.cache/apt" -qq -y --no-install-recommends install git build-essential libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 libcurl4-openssl-dev:i386 libopenmpt-dev:i386 gettext ccache wget gcc-multilib upx openssh-client
       - run:
           name: make md5sum
           command: find /root/.cache/apt/archives -type f -print0 | sort -z | xargs -r0 md5sum > /root/.cache/apt_archives.md5
diff --git a/README.md b/README.md
index 49a3cc36d1..56ff2d02d2 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,6 @@
 [Sonic Robo Blast 2](https://srb2.org/) is a 3D Sonic the Hedgehog fangame based on a modified version of [Doom Legacy](http://doomlegacy.sourceforge.net/).
 
 ## Dependencies
-- NASM (x86 builds only)
 - SDL2 (Linux/OS X only)
 - SDL2-Mixer (Linux/OS X only)
 - libupnp (Linux/OS X only)
-- 
GitLab


From f4d944e49a21e5560ca252f0c00e6b4a79e1fee3 Mon Sep 17 00:00:00 2001
From: MIDIMan <miditheman2.0@gmail.com>
Date: Sat, 29 Jul 2023 20:45:10 -0400
Subject: [PATCH 297/518] Make HWR_AllowModel check for SPR2_SIGN instead of
 S_PLAY_SIGN

---
 src/hardware/hw_md2.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index fca3af217e..4ce3b38f6b 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1146,7 +1146,7 @@ static void HWR_GetBlendedTexture(patch_t *patch, patch_t *blendpatch, INT32 ski
 static boolean HWR_AllowModel(mobj_t *mobj)
 {
 	// Signpost overlay. Not needed.
-	if (mobj->state-states == S_PLAY_SIGN)
+	if (mobj->sprite2 == SPR2_SIGN)
 		return false;
 
 	// Otherwise, render the model.
-- 
GitLab


From 70ec89682e9928444b3452e5082f20c57c73197f Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Sun, 30 Jul 2023 14:26:17 +0200
Subject: [PATCH 298/518] Fix minor off-by-one error in faketic calculation

---
 src/d_clisrv.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index c66c8d798f..83482b527c 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -4584,7 +4584,7 @@ static void HandlePacketFromPlayer(SINT8 node)
 			// If we've alredy received a ticcmd for this tic, just submit it for the next one.
 			tic_t faketic = maketic;
 			if ((!!(netcmds[maketic % BACKUPTICS][netconsole].angleturn & TICCMD_RECEIVED))
-				&& (maketic - firstticstosend < BACKUPTICS))
+				&& (maketic - firstticstosend < BACKUPTICS - 1))
 				faketic++;
 
 			// Copy ticcmd
-- 
GitLab


From 28ef38faad193925d21491e3243a4339e7a0e344 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Mon, 31 Jul 2023 15:32:22 +0200
Subject: [PATCH 299/518] Reallocate wadfiles when adding folders

---
 src/w_wad.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/w_wad.c b/src/w_wad.c
index 171eab4f31..c8880f6934 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1150,6 +1150,7 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup)
 	Z_Calloc(numlumps * sizeof (*wadfile->patchcache), PU_STATIC, &wadfile->patchcache);
 
 	CONS_Printf(M_GetText("Added folder %s (%u files, %u folders)\n"), fn, numlumps, foldercount);
+	wadfiles = Z_Realloc(wadfiles, sizeof(wadfile_t *) * (numwadfiles + 1), PU_STATIC, NULL);
 	wadfiles[numwadfiles] = wadfile;
 	numwadfiles++;
 
-- 
GitLab


From d20ca3d919127b8d3cf850af3d68fba1cd597579 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Mon, 31 Jul 2023 19:44:50 +0100
Subject: [PATCH 300/518] reset emeralds and luabanks in G_InitNew, not
 Got_Mapcmd

---
 src/d_netcmd.c | 6 ------
 src/g_game.c   | 6 ++++++
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 1b987bcf13..6cf5191472 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -2146,12 +2146,6 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
 	if (demoplayback && !timingdemo)
 		precache = false;
 
-	if (resetplayer && !FLS)
-	{
-		emeralds = 0;
-		memset(&luabanks, 0, sizeof(luabanks));
-	}
-
 	if (modeattacking)
 	{
 		SetPlayerSkinByNum(0, cv_chooseskin.value-1);
diff --git a/src/g_game.c b/src/g_game.c
index b8c4349985..a1fd2e6ec6 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -5048,6 +5048,12 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
 		numgameovers = tokenlist = token = sstimer = redscore = bluescore = lastmap = 0;
 		countdown = countdown2 = exitfadestarted = 0;
 
+		if (!FLS)
+		{
+			emeralds = 0;
+			memset(&luabanks, 0, sizeof(luabanks));
+		}
+
 		for (i = 0; i < MAXPLAYERS; i++)
 		{
 			players[i].playerstate = PST_REBORN;
-- 
GitLab


From 77d70aaa1bbf687c301c1da09a3e1bec6c2b5a74 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Mon, 31 Jul 2023 20:06:28 +0100
Subject: [PATCH 301/518] relocate savedata code from P_LoadLevel to G_InitNew
 as well

(this doesn't fix !1064 yet, this just makes the code a bit tidier)
---
 src/g_game.c  | 14 ++++++++++++++
 src/p_setup.c | 12 ------------
 2 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/src/g_game.c b/src/g_game.c
index a1fd2e6ec6..f4e84512be 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -5061,6 +5061,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
 			players[i].starpostx = players[i].starposty = players[i].starpostz = 0;
 			players[i].recordscore = 0;
 
+			// default lives, continues and score
 			if (netgame || multiplayer)
 			{
 				if (!FLS || (players[i].lives < 1))
@@ -5120,6 +5121,19 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
 	automapactive = false;
 	imcontinuing = false;
 
+	// fetch saved data if available
+	if (savedata.lives > 0)
+	{
+		numgameovers = savedata.numgameovers;
+		players[consoleplayer].continues = savedata.continues;
+		players[consoleplayer].lives = savedata.lives;
+		players[consoleplayer].score = savedata.score;
+		if ((botingame = ((botskin = savedata.botskin) != 0)))
+			botcolor = skins[botskin-1].prefcolor;
+		emeralds = savedata.emeralds;
+		savedata.lives = 0;
+	}
+
 	if ((gametyperules & GTR_CUTSCENES) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking && !(marathonmode & MA_NOCUTSCENES)) // Start a custom cutscene.
 		F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer, FLS);
 	else
diff --git a/src/p_setup.c b/src/p_setup.c
index 175ab3328b..aac02417d4 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -7795,18 +7795,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 	R_InitMobjInterpolators();
 	P_InitCachedActions();
 
-	if (!fromnetsave && savedata.lives > 0)
-	{
-		numgameovers = savedata.numgameovers;
-		players[consoleplayer].continues = savedata.continues;
-		players[consoleplayer].lives = savedata.lives;
-		players[consoleplayer].score = savedata.score;
-		if ((botingame = ((botskin = savedata.botskin) != 0)))
-			botcolor = skins[botskin-1].prefcolor;
-		emeralds = savedata.emeralds;
-		savedata.lives = 0;
-	}
-
 	// internal game map
 	maplumpname = G_BuildMapName(gamemap);
 	lastloadedmaplumpnum = W_CheckNumForMap(maplumpname);
-- 
GitLab


From ddc5cc6e48342654dcb46d98d92b7fd1a9b55ee4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Mon, 31 Jul 2023 22:51:50 +0200
Subject: [PATCH 302/518] Fix wrong SSF_* values in Lua due to missing entries

---
 src/deh_tables.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index 6a815b6ea5..80f4e5b1a2 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4551,6 +4551,7 @@ const char *const MSF_LIST[] = {
 const char *const SSF_LIST[] = {
 	"OUTERSPACE",
 	"DOUBLESTEPUP",
+	"NOSTEPDOWN",
 	"WINDCURRENT",
 	"CONVEYOR",
 	"SPEEDPAD",
@@ -4567,6 +4568,8 @@ const char *const SSF_LIST[] = {
 	"ZOOMTUBEEND",
 	"FINISHLINE",
 	"ROPEHANG",
+	"JUMPFLIP",
+	"GRAVITYOVERRIDE",
 	NULL
 };
 
-- 
GitLab


From fb00b65f41be4c34f713242825683e5830c6c5b9 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Mon, 31 Jul 2023 23:36:07 +0100
Subject: [PATCH 303/518] ...you know what, just call G_LoadGame for
 SP_PauseLevelSelectDef in M_LevelSelectWarp too

---
 src/m_menu.c | 22 +++-------------------
 1 file changed, 3 insertions(+), 19 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 21ba98dd2c..e10bb75472 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -7100,9 +7100,6 @@ static void M_DestroyRobots(INT32 choice)
 
 static void M_LevelSelectWarp(INT32 choice)
 {
-	boolean fromloadgame = (currentMenu == &SP_LevelSelectDef);
-	boolean frompause = (currentMenu == &SP_PauseLevelSelectDef);
-
 	(void)choice;
 
 	if (W_CheckNumForName(G_BuildMapName(cv_nextmap.value)) == LUMPERROR)
@@ -7114,25 +7111,12 @@ static void M_LevelSelectWarp(INT32 choice)
 	startmap = (INT16)(cv_nextmap.value);
 	fromlevelselect = true;
 
-	if (fromloadgame)
-		G_LoadGame((UINT32)cursaveslot, startmap);
+	if (currentMenu == &SP_LevelSelectDef || currentMenu == &SP_PauseLevelSelectDef)
+		G_LoadGame((UINT32)cursaveslot, startmap); // reload from SP save data: this is needed to keep score/lives/continues from reverting to defaults
 	else
 	{
 		cursaveslot = 0;
-
-		if (frompause)
-		{
-			M_ClearMenus(true);
-
-			G_DeferedInitNew(false, G_BuildMapName(startmap), cv_skin.value, false, fromlevelselect); // Not sure about using cv_skin here, but it seems fine in testing.
-			COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this
-
-			if (levelselect.rows)
-				Z_Free(levelselect.rows);
-			levelselect.rows = NULL;
-		}
-		else
-			M_SetupChoosePlayer(0);
+		M_SetupChoosePlayer(0);
 	}
 }
 
-- 
GitLab


From 26583a9845249f6c72ec1a784ba13d764a84e67d Mon Sep 17 00:00:00 2001
From: Sal <tehrealsalt@gmail.com>
Date: Tue, 1 Aug 2023 02:29:45 +0000
Subject: [PATCH 304/518] Ring Racers CMake + Debug build improvements

---
 CMakeLists.txt                   |  54 +++-
 CMakePresets.json                |  29 ++
 alias-bootstrap.sh               |  29 ++
 cmake/Comptime.cmake             |  32 ++
 cmake/Modules/GitUtilities.cmake |  62 ++--
 src/CMakeLists.txt               | 147 ++++++++-
 src/Makefile                     |   2 +
 src/blua/CMakeLists.txt          |  29 +-
 src/comptime.c                   |  10 +
 src/config.h.in                  |  14 +-
 src/d_main.c                     |  17 +
 src/d_netcmd.c                   |   2 +-
 src/d_think.h                    |   7 +
 src/doomdef.h                    |  19 +-
 src/g_game.c                     |   2 +-
 src/hardware/CMakeLists.txt      |  15 +-
 src/http-mserv.c                 |   2 +-
 src/lua_infolib.c                |   4 -
 src/m_cond.c                     |   2 +-
 src/p_mobj.c                     |  17 +-
 src/p_saveg.c                    |   4 +-
 src/p_tick.c                     | 120 ++++++-
 src/p_tick.h                     |  15 +-
 src/sdl/CMakeLists.txt           |  19 +-
 src/sdl/i_system.c               |  16 +-
 src/z_zone.h                     |   1 +
 thirdparty/CMakeLists.txt        | 522 +------------------------------
 thirdparty/cpm-curl.cmake        |  35 +++
 thirdparty/cpm-libgme.cmake      |  16 +
 thirdparty/cpm-openmpt.cmake     | 289 +++++++++++++++++
 thirdparty/cpm-png.cmake         |  69 ++++
 thirdparty/cpm-sdl2-mixer.cmake  |  22 ++
 thirdparty/cpm-sdl2.cmake        |  13 +
 thirdparty/cpm-zlib.cmake        |  53 ++++
 34 files changed, 1076 insertions(+), 613 deletions(-)
 create mode 100644 CMakePresets.json
 create mode 100755 alias-bootstrap.sh
 create mode 100644 cmake/Comptime.cmake
 create mode 100644 thirdparty/cpm-curl.cmake
 create mode 100644 thirdparty/cpm-libgme.cmake
 create mode 100644 thirdparty/cpm-openmpt.cmake
 create mode 100644 thirdparty/cpm-png.cmake
 create mode 100644 thirdparty/cpm-sdl2-mixer.cmake
 create mode 100644 thirdparty/cpm-sdl2.cmake
 create mode 100644 thirdparty/cpm-zlib.cmake

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7d36c1fb63..80a3bdcd67 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -131,7 +131,11 @@ if("${SRB2_CONFIG_SYSTEM_LIBRARIES}")
 	find_package(SDL2_mixer REQUIRED)
 	find_package(CURL REQUIRED)
 	find_package(OPENMPT REQUIRED)
-	find_package(GME REQUIRED)
+
+	# libgme defaults to "Nuked" YM2612 emulator, which is
+	# very SLOW. The system library probably uses the
+	# default so just always build it.
+	#find_package(GME REQUIRED)
 endif()
 
 if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})
@@ -142,13 +146,6 @@ if ((${SRB2_USE_CCACHE}) AND (${CMAKE_C_COMPILER} MATCHES "clang"))
 	message(WARNING "Using clang and CCache: You may want to set environment variable CCACHE_CPP2=yes to prevent include errors during compile.")
 endif()
 
-# Add sources from Sourcefile
-function(target_sourcefile type)
-	file(STRINGS Sourcefile list
-		REGEX "[-0-9A-Za-z_]+\.${type}")
-	target_sources(SRB2SDL2 PRIVATE ${list})
-endfunction()
-
 # bitness check
 set(SRB2_SYSTEM_BITS 0)
 if(CMAKE_SIZEOF_VOID_P EQUAL 8)
@@ -167,7 +164,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
 set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
 
 # Set EXE names so the assets CMakeLists can refer to its target
-set(SRB2_SDL2_EXE_NAME srb2 CACHE STRING "Executable binary output name")
+set(SRB2_SDL2_EXE_NAME "" CACHE STRING "Override executable binary output name")
+set(SRB2_SDL2_EXE_SUFFIX "" CACHE STRING "Optional executable suffix, separated by an underscore")
 
 include_directories(${CMAKE_CURRENT_BINARY_DIR}/src)
 
@@ -175,11 +173,37 @@ add_subdirectory(src)
 add_subdirectory(assets)
 
 
-## config.h generation
 set(GIT_EXECUTABLE "git" CACHE FILEPATH "Path to git binary")
 include(GitUtilities)
-git_latest_commit(SRB2_COMP_COMMIT "${CMAKE_SOURCE_DIR}")
-git_current_branch(SRB2_GIT_BRANCH "${CMAKE_SOURCE_DIR}")
-set(SRB2_COMP_BRANCH "${SRB2_GIT_BRANCH}")
-set(SRB2_COMP_REVISION "${SRB2_COMP_COMMIT}")
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/config.h)
+
+if("${SRB2_SDL2_EXE_NAME}" STREQUAL "")
+	# cause a reconfigure if the branch changes
+	get_git_dir(SRB2_GIT_DIR)
+	configure_file("${SRB2_GIT_DIR}/HEAD" HEAD COPYONLY)
+
+	git_current_branch(SRB2_GIT_REVISION)
+
+	if("${SRB2_GIT_REVISION}" STREQUAL "")
+		# use abbreviated commit hash if on detached HEAD
+		git_latest_commit(SRB2_GIT_REVISION)
+	endif()
+
+	if("${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
+		list(APPEND EXE_NAME_PARTS "srb2win")
+	elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
+		list(APPEND EXE_NAME_PARTS "lsdlsrb2")
+	else()
+		list(APPEND EXE_NAME_PARTS "srb2")
+	endif()
+
+	if(NOT "${SRB2_GIT_REVISION}" STREQUAL "master")
+		list(APPEND EXE_NAME_PARTS ${SRB2_GIT_REVISION})
+	endif()
+else()
+	list(APPEND EXE_NAME_PARTS ${SRB2_SDL2_EXE_NAME})
+endif()
+
+list(APPEND EXE_NAME_PARTS ${SRB2_SDL2_EXE_SUFFIX})
+
+list(JOIN EXE_NAME_PARTS "_" EXE_NAME)
+set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME ${EXE_NAME})
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 0000000000..7713bb3851
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,29 @@
+{
+	"version": 3,
+	"configurePresets": [
+		{
+			"name": "default",
+			"description": "Build using default generator",
+			"binaryDir": "build",
+			"cacheVariables": {
+				"CMAKE_C_FLAGS": "-fdiagnostics-color",
+				"CMAKE_CXX_FLAGS": "-fdiagnostics-color",
+				"CMAKE_BUILD_TYPE": "RelWithDebInfo"
+			}
+		},
+		{
+			"name": "debug",
+			"description": "Build for development (no optimizations)",
+			"inherits": "default",
+			"cacheVariables": {
+				"CMAKE_BUILD_TYPE": "Debug"
+			}
+		}
+	],
+	"buildPresets": [
+		{
+			"name": "default",
+			"configurePreset": "default"
+		}
+	]
+}
diff --git a/alias-bootstrap.sh b/alias-bootstrap.sh
new file mode 100755
index 0000000000..f1a6ac4ed9
--- /dev/null
+++ b/alias-bootstrap.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env sh
+
+# All these commands can be run from anywhere in the git
+# tree, not just the top level.
+
+# Usage: git cmake
+#
+# Same usage as standard CMake command.
+#
+git config 'alias.cmake' '!cmake'
+
+# Usage: git build <build preset> [options]
+# Usage: git build [options]
+#
+# In the second usage, when no preset is given, the
+# "default" build preset is used.
+#
+# Available options can be found by running:
+#
+#     git cmake --build
+#
+git config 'alias.build' '!p="${1##-*}"; [ "$p" ] && shift; git cmake --build --preset "${p:-default}"'
+
+# Usage: git crossmake
+#
+# Shortcut to i686-w64-mingw32-cmake (CMake cross
+# compiler)
+#
+git config 'alias.crossmake' '!i686-w64-mingw32-cmake'
diff --git a/cmake/Comptime.cmake b/cmake/Comptime.cmake
new file mode 100644
index 0000000000..8388aed9ec
--- /dev/null
+++ b/cmake/Comptime.cmake
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
+
+set(CMAKE_BINARY_DIR "${BINARY_DIR}")
+set(CMAKE_CURRENT_BINARY_DIR "${BINARY_DIR}")
+
+# Set up CMAKE path
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
+
+include(GitUtilities)
+
+git_current_branch(SRB2_COMP_BRANCH)
+git_working_tree_dirty(SRB2_COMP_UNCOMMITTED)
+
+git_latest_commit(SRB2_COMP_REVISION)
+git_subject(subject)
+string(REGEX REPLACE "([\"\\])" "\\\\\\1" SRB2_COMP_NOTE "${subject}")
+
+if("${CMAKE_BUILD_TYPE}" STREQUAL "")
+	set(CMAKE_BUILD_TYPE None)
+endif()
+
+# These build types enable optimizations of some kind by default.
+set(optimized_build_types "MINSIZEREL;RELEASE;RELWITHDEBINFO")
+
+string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type)
+if("${build_type}" IN_LIST optimized_build_types)
+	set(SRB2_COMP_OPTIMIZED TRUE)
+else()
+	set(SRB2_COMP_OPTIMIZED FALSE)
+endif()
+
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/src/config.h")
diff --git a/cmake/Modules/GitUtilities.cmake b/cmake/Modules/GitUtilities.cmake
index d29e6b509d..586c7b433f 100644
--- a/cmake/Modules/GitUtilities.cmake
+++ b/cmake/Modules/GitUtilities.cmake
@@ -6,38 +6,54 @@ endif()
 
 set(__GitUtilities ON)
 
-function(git_describe variable path)
-	execute_process(COMMAND "${GIT_EXECUTABLE}" "describe"
-		WORKING_DIRECTORY "${path}"
-		RESULT_VARIABLE result
+macro(_git_command)
+	execute_process(
+		COMMAND "${GIT_EXECUTABLE}" ${ARGN}
+		WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
 		OUTPUT_VARIABLE output
 		ERROR_QUIET
 		OUTPUT_STRIP_TRAILING_WHITESPACE
 	)
+endmacro()
 
+macro(_git_easy_command)
+	_git_command(${ARGN})
 	set(${variable} "${output}" PARENT_SCOPE)
-endfunction()
+endmacro()
 
-function(git_current_branch variable path)
-	execute_process(COMMAND ${GIT_EXECUTABLE} "symbolic-ref" "--short" "HEAD"
-		WORKING_DIRECTORY "${path}"
-		RESULT_VARIABLE result
-		OUTPUT_VARIABLE output
-		ERROR_QUIET
-		OUTPUT_STRIP_TRAILING_WHITESPACE
-	)
+function(git_current_branch variable)
+	_git_command(symbolic-ref -q --short HEAD)
+
+	# If a detached head, a ref could still be resolved.
+	if("${output}" STREQUAL "")
+		_git_command(describe --all --exact-match)
+
+		# Get the ref, in the form heads/master or
+		# remotes/origin/master so isolate the final part.
+		string(REGEX REPLACE ".*/" "" output "${output}")
+	endif()
 
 	set(${variable} "${output}" PARENT_SCOPE)
 endfunction()
 
-function(git_latest_commit variable path)
-	execute_process(COMMAND ${GIT_EXECUTABLE} "rev-parse" "--short" "HEAD"
-		WORKING_DIRECTORY "${path}"
-		RESULT_VARIABLE result
-		OUTPUT_VARIABLE output
-		ERROR_QUIET
-		OUTPUT_STRIP_TRAILING_WHITESPACE
-	)
+function(git_latest_commit variable)
+	_git_easy_command(rev-parse --short HEAD)
+endfunction()
 
-	set(${variable} "${output}" PARENT_SCOPE)
-endfunction()
\ No newline at end of file
+function(git_working_tree_dirty variable)
+	_git_command(status --porcelain -uno)
+
+	if(output STREQUAL "")
+		set(${variable} FALSE PARENT_SCOPE)
+	else()
+		set(${variable} TRUE PARENT_SCOPE)
+	endif()
+endfunction()
+
+function(git_subject variable)
+	_git_easy_command(log -1 --format=%s)
+endfunction()
+
+function(get_git_dir variable)
+	_git_easy_command(rev-parse --git-dir)
+endfunction()
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8cd0310137..b926b3b7a3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,18 +1,149 @@
 include(clang-tidy-default)
 
-add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32)
+add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
+	comptime.c
+	md5.c
+	config.h.in
+	string.c
+	d_main.c
+	d_clisrv.c
+	d_net.c
+	d_netfil.c
+	d_netcmd.c
+	dehacked.c
+	deh_soc.c
+	deh_lua.c
+	deh_tables.c
+	z_zone.c
+	f_finale.c
+	f_wipe.c
+	g_demo.c
+	g_game.c
+	g_input.c
+	am_map.c
+	command.c
+	console.c
+	hu_stuff.c
+	i_time.c
+	y_inter.c
+	st_stuff.c
+	m_aatree.c
+	m_anigif.c
+	m_argv.c
+	m_bbox.c
+	m_cheat.c
+	m_cond.c
+	m_easing.c
+	m_fixed.c
+	m_menu.c
+	m_misc.c
+	m_perfstats.c
+	m_random.c
+	m_queue.c
+	info.c
+	p_ceilng.c
+	p_enemy.c
+	p_floor.c
+	p_inter.c
+	p_lights.c
+	p_map.c
+	p_maputl.c
+	p_mobj.c
+	p_polyobj.c
+	p_saveg.c
+	p_setup.c
+	p_sight.c
+	p_spec.c
+	p_telept.c
+	p_tick.c
+	p_user.c
+	p_slopes.c
+	tables.c
+	r_bsp.c
+	r_data.c
+	r_draw.c
+	r_fps.c
+	r_main.c
+	r_plane.c
+	r_segs.c
+	r_skins.c
+	r_sky.c
+	r_splats.c
+	r_things.c
+	r_bbox.c
+	r_textures.c
+	r_patch.c
+	r_patchrotation.c
+	r_picformats.c
+	r_portal.c
+	screen.c
+	taglist.c
+	v_video.c
+	s_sound.c
+	sounds.c
+	w_wad.c
+	filesrch.c
+	mserv.c
+	http-mserv.c
+	i_tcp.c
+	lzf.c
+	b_bot.c
+	u_list.c
+	lua_script.c
+	lua_baselib.c
+	lua_mathlib.c
+	lua_hooklib.c
+	lua_consolelib.c
+	lua_infolib.c
+	lua_mobjlib.c
+	lua_playerlib.c
+	lua_skinlib.c
+	lua_thinkerlib.c
+	lua_maplib.c
+	lua_taglib.c
+	lua_polyobjlib.c
+	lua_blockmaplib.c
+	lua_hudlib.c
+	lua_hudlib_drawlist.c
+	lua_inputlib.c
+)
+
+# This updates the modification time for comptime.c at the
+# end of building so when the build system is ran next time,
+# that file gets flagged. comptime.c will always be rebuilt.
+#
+# This begs the question, why always rebuild comptime.c?
+# Some things like the git commit must be checked each time
+# the program is built. But the build system determines which
+# files should be rebuilt before anything else. So
+# comptime.c, which only needs to be rebuilt based on
+# information known at build time, must be told to rebuild
+# before that information can be ascertained.
+add_custom_command(
+	TARGET SRB2SDL2
+	POST_BUILD
+	COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${CMAKE_CURRENT_SOURCE_DIR}/comptime.c
+)
 
-if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows" AND NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}" AND NOT "${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}")
-	# On MinGW with internal libraries, link the standard library statically
-	target_link_options(SRB2SDL2 PRIVATE "-static")
+# config.h is generated by this command. It should be done at
+# build time for accurate git information and before anything
+# that needs it, obviously.
+add_custom_target(_SRB2_reconf ALL
+	COMMAND ${CMAKE_COMMAND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DBINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/.. -P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Comptime.cmake
+	WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.."
+)
+add_dependencies(SRB2SDL2 _SRB2_reconf)
+
+if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows")
+	target_link_options(SRB2SDL2 PRIVATE "-Wl,--disable-dynamicbase")
+	if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}" AND NOT "${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}")
+		# On MinGW with internal libraries, link the standard library statically
+		target_link_options(SRB2SDL2 PRIVATE "-static")
+	endif()
 endif()
 
 target_compile_features(SRB2SDL2 PRIVATE c_std_11 cxx_std_17)
 
-# Core sources
-target_sourcefile(c)
-target_sources(SRB2SDL2 PRIVATE comptime.c md5.c config.h.in)
-
 ### Configuration
 set(SRB2_CONFIG_DEV_BUILD OFF CACHE BOOL
 	"Compile a development build of SRB2.")
diff --git a/src/Makefile b/src/Makefile
index 92f4c0c66c..e1d9cb384f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -391,3 +391,5 @@ ifdef WINDOWSHELL
 else
 	@:
 endif
+
+$(warning The handwritten GNU Makefile for SRB2 is deprecated, and may be removed in the future. Please consider switching to CMake.)
diff --git a/src/blua/CMakeLists.txt b/src/blua/CMakeLists.txt
index 4e9c67d2f3..892bf534ad 100644
--- a/src/blua/CMakeLists.txt
+++ b/src/blua/CMakeLists.txt
@@ -1 +1,28 @@
-target_sourcefile(c)
+target_sources(SRB2SDL2 PRIVATE
+	lapi.c
+	lbaselib.c
+	ldo.c
+	lfunc.c
+	linit.c
+	liolib.c
+	llex.c
+	lmem.c
+	lobject.c
+	lstate.c
+	lstrlib.c
+	ltablib.c
+	lundump.c
+	lzio.c
+	lauxlib.c
+	lcode.c
+	ldebug.c
+	ldump.c
+	lgc.c
+	lopcodes.c
+	lparser.c
+	lstring.c
+	ltable.c
+	ltm.c
+	lvm.c
+	loslib.c
+)
diff --git a/src/comptime.c b/src/comptime.c
index 398eda0743..386b53f469 100644
--- a/src/comptime.c
+++ b/src/comptime.c
@@ -11,6 +11,9 @@
 #include "config.h"
 const char *compbranch = SRB2_COMP_BRANCH;
 const char *comprevision = SRB2_COMP_REVISION;
+const char *compnote = SRB2_COMP_NOTE;
+const char *comptype = CMAKE_BUILD_TYPE;
+const int compoptimized = SRB2_COMP_OPTIMIZED;
 
 #elif (defined(COMPVERSION))
 #include "comptime.h"
@@ -21,5 +24,12 @@ const char *comprevision = "illegal";
 
 #endif
 
+const int compuncommitted =
+#if (defined(COMPVERSION_UNCOMMITTED))
+1;
+#else
+0;
+#endif
+
 const char *compdate = __DATE__;
 const char *comptime = __TIME__;
diff --git a/src/config.h.in b/src/config.h.in
index 3d6d983756..0ca0f26e90 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -11,8 +11,18 @@
 
 #ifdef CMAKECONFIG
 
-#define SRB2_COMP_REVISION    "${SRB2_COMP_REVISION}"
-#define SRB2_COMP_BRANCH      "${SRB2_COMP_BRANCH}"
+#define SRB2_COMP_REVISION       "${SRB2_COMP_REVISION}"
+#define SRB2_COMP_BRANCH         "${SRB2_COMP_BRANCH}"
+#define SRB2_COMP_NOTE           "${SRB2_COMP_NOTE}"
+// This is done with configure_file instead of defines in order to avoid
+// recompiling the whole target whenever the working directory state changes
+#cmakedefine SRB2_COMP_UNCOMMITTED
+#ifdef SRB2_COMP_UNCOMMITTED
+#define COMPVERSION_UNCOMMITTED
+#endif
+
+#define CMAKE_BUILD_TYPE         "${CMAKE_BUILD_TYPE}"
+#cmakedefine01 SRB2_COMP_OPTIMIZED
 
 #endif
 
diff --git a/src/d_main.c b/src/d_main.c
index b7b7f6616d..22a6762554 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -71,6 +71,7 @@
 #include "g_input.h" // tutorial mode control scheming
 #include "m_perfstats.h"
 #include "m_random.h"
+#include "command.h"
 
 #ifdef CMAKECONFIG
 #include "config.h"
@@ -1216,6 +1217,15 @@ D_ConvertVersionNumbers (void)
 #endif
 }
 
+static void Command_assert(void)
+{
+#if !defined(NDEBUG) || defined(PARANOIA)
+	CONS_Printf("Yes, assertions are enabled.\n");
+#else
+	CONS_Printf("No, assertions are NOT enabled.\n");
+#endif
+}
+
 //
 // D_SRB2Main
 //
@@ -1229,6 +1239,11 @@ void D_SRB2Main(void)
 	/* break the version string into version numbers, for netplay */
 	D_ConvertVersionNumbers();
 
+	if (!strcmp(compbranch, ""))
+	{
+		compbranch = "detached HEAD";
+	}
+
 	// Print GPL notice for our console users (Linux)
 	CONS_Printf(
 	"\n\nSonic Robo Blast 2\n"
@@ -1365,6 +1380,8 @@ void D_SRB2Main(void)
 	// Do this up here so that WADs loaded through the command line can use ExecCfg
 	COM_Init();
 
+	COM_AddCommand("assert", Command_assert, COM_LUA);
+
 	// Add any files specified on the command line with
 	// "-file <file>" or "-folder <folder>" to the add-on list
 	if (!((M_GetUrlProtocolArg() || M_CheckParm("-connect")) && !M_CheckParm("-server")))
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 1b987bcf13..b243ab8492 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -3859,7 +3859,7 @@ static void Command_ListWADS_f(void)
 static void Command_Version_f(void)
 {
 #ifdef DEVELOP
-	CONS_Printf("Sonic Robo Blast 2 %s-%s (%s %s) ", compbranch, comprevision, compdate, comptime);
+	CONS_Printf("Sonic Robo Blast 2 %s %s %s (%s %s) ", compbranch, comprevision, compnote, compdate, comptime);
 #else
 	CONS_Printf("Sonic Robo Blast 2 %s (%s %s %s %s) ", VERSIONSTRING, compdate, comptime, comprevision, compbranch);
 #endif
diff --git a/src/d_think.h b/src/d_think.h
index bdb5db3f54..efc1589bf6 100644
--- a/src/d_think.h
+++ b/src/d_think.h
@@ -17,6 +17,8 @@
 #ifndef __D_THINK__
 #define __D_THINK__
 
+#include "doomdef.h"
+
 #ifdef __GNUG__
 #pragma interface
 #endif
@@ -49,6 +51,11 @@ typedef struct thinker_s
 	// killough 11/98: count of how many other objects reference
 	// this one using pointers. Used for garbage collection.
 	INT32 references;
+
+#ifdef PARANOIA
+	INT32 debug_mobjtype;
+	tic_t debug_time;
+#endif
 } thinker_t;
 
 #endif
diff --git a/src/doomdef.h b/src/doomdef.h
index 84404d6edb..f82b0a4cd4 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -108,6 +108,14 @@ FILE *fopenfile(const char*, const char*);
 
 //#define NOMD5
 
+// If you don't disable ALL debug first, you get ALL debug enabled
+#if !defined (NDEBUG)
+#define PACKETDROP
+#define PARANOIA
+#define RANGECHECK
+#define ZDEBUG
+#endif
+
 // Uncheck this to compile debugging code
 //#define RANGECHECK
 //#ifndef PARANOIA
@@ -637,7 +645,16 @@ UINT32 quickncasehash (const char *p, size_t n)
 #define PUNCTUATION "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
 
 // Compile date and time and revision.
-extern const char *compdate, *comptime, *comprevision, *compbranch;
+extern const char
+	*compdate,
+	*comptime,
+	*comprevision,
+	*compbranch,
+	*compnote,
+	*comptype;
+extern int
+	compuncommitted,
+	compoptimized;
 
 // Disabled code and code under testing
 // None of these that are disabled in the normal build are guaranteed to work perfectly
diff --git a/src/g_game.c b/src/g_game.c
index b8c4349985..de2e33ff71 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -4266,7 +4266,7 @@ static void G_DoContinued(void)
 {
 	player_t *pl = &players[consoleplayer];
 	I_Assert(!netgame && !multiplayer);
-	I_Assert(pl->continues > 0);
+	//I_Assert(pl->continues > 0);
 
 	if (pl->continues)
 		pl->continues--;
diff --git a/src/hardware/CMakeLists.txt b/src/hardware/CMakeLists.txt
index 4e9c67d2f3..e7819aba97 100644
--- a/src/hardware/CMakeLists.txt
+++ b/src/hardware/CMakeLists.txt
@@ -1 +1,14 @@
-target_sourcefile(c)
+target_sources(SRB2SDL2 PRIVATE
+	hw_bsp.c
+	hw_draw.c
+	hw_light.c
+	hw_main.c
+	hw_clip.c
+	hw_md2.c
+	hw_cache.c
+	hw_md2load.c
+	hw_md3load.c
+	hw_model.c
+	hw_batching.c
+	r_opengl/r_opengl.c
+)
diff --git a/src/http-mserv.c b/src/http-mserv.c
index b7032e89a3..df9a71a5c5 100644
--- a/src/http-mserv.c
+++ b/src/http-mserv.c
@@ -159,7 +159,7 @@ HMS_connect (const char *format, ...)
 		return NULL;
 	}
 
-	if (cv_masterserver_token.string[0])
+	if (cv_masterserver_token.string && cv_masterserver_token.string[0])
 	{
 		quack_token = curl_easy_escape(curl, cv_masterserver_token.string, 0);
 		token_length = ( sizeof "?token="-1 )+ strlen(quack_token);
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 0f40b86219..3764acf6a4 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -508,8 +508,6 @@ static int pivotlist_get(lua_State *L)
 	const char *field = luaL_checkstring(L, 2);
 	UINT8 frame;
 
-	I_Assert(framepivot != NULL);
-
 	frame = R_Char2Frame(field[0]);
 	if (frame == 255)
 		luaL_error(L, "invalid frame %s", field);
@@ -539,8 +537,6 @@ static int pivotlist_set(lua_State *L)
 	if (hook_cmd_running)
 		return luaL_error(L, "Do not alter spriteframepivot_t in CMD building code!");
 
-	I_Assert(pivotlist != NULL);
-
 	frame = R_Char2Frame(field[0]);
 	if (frame == 255)
 		luaL_error(L, "invalid frame %s", field);
diff --git a/src/m_cond.c b/src/m_cond.c
index b21778c697..13a483ea22 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -113,7 +113,7 @@ void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1
 	condition_t *cond;
 	UINT32 num, wnum;
 
-	I_Assert(set && set <= MAXCONDITIONSETS);
+	I_Assert(set < MAXCONDITIONSETS);
 
 	wnum = conditionSets[set - 1].numconditions;
 	num = ++conditionSets[set - 1].numconditions;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 4ca59285f7..8c0fa17ad9 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -11181,12 +11181,6 @@ void P_RemoveMobj(mobj_t *mobj)
 
 	P_SetTarget(&mobj->hnext, P_SetTarget(&mobj->hprev, NULL));
 
-	// DBG: set everything in mobj_t to 0xFF instead of leaving it. debug memory error.
-#ifdef SCRAMBLE_REMOVED
-	// Invalidate mobj_t data to cause crashes if accessed!
-	memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t));
-#endif
-
 	R_RemoveMobjInterpolator(mobj);
 
 	// free block
@@ -11205,6 +11199,17 @@ void P_RemoveMobj(mobj_t *mobj)
 	}
 
 	P_RemoveThinker((thinker_t *)mobj);
+
+#ifdef PARANOIA
+	// Saved to avoid being scrambled like below...
+	mobj->thinker.debug_mobjtype = mobj->type;
+#endif
+
+	// DBG: set everything in mobj_t to 0xFF instead of leaving it. debug memory error.
+#ifdef SCRAMBLE_REMOVED
+	// Invalidate mobj_t data to cause crashes if accessed!
+	memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t));
+#endif
 }
 
 // This does not need to be added to Lua.
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 8f11e63e58..faecd13770 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -2716,8 +2716,8 @@ static void P_NetArchiveThinkers(void)
 				continue;
 			}
 #ifdef PARANOIA
-			else if (th->function.acp1 != (actionf_p1)P_RemoveThinkerDelayed) // wait garbage collection
-				I_Error("unknown thinker type %p", th->function.acp1);
+			else
+				I_Assert(th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed); // wait garbage collection
 #endif
 		}
 
diff --git a/src/p_tick.c b/src/p_tick.c
index b1fd367ed9..ec5d8a2da0 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -30,6 +30,10 @@
 // Object place
 #include "m_cheat.h"
 
+#ifdef PARANOIA
+#include "deh_tables.h" // MOBJTYPE_LIST
+#endif
+
 tic_t leveltime;
 
 //
@@ -211,7 +215,48 @@ void P_AddThinker(const thinklistnum_t n, thinker_t *thinker)
 	thlist[n].prev = thinker;
 
 	thinker->references = 0;    // killough 11/98: init reference counter to 0
+
+#ifdef PARANOIA
+	thinker->debug_mobjtype = MT_NULL;
+#endif
+}
+
+#ifdef PARANOIA
+static const char *MobjTypeName(const mobj_t *mobj)
+{
+	actionf_p1 p1 = mobj->thinker.function.acp1;
+
+	if (p1 == (actionf_p1)P_MobjThinker)
+	{
+		return MOBJTYPE_LIST[mobj->type];
+	}
+	else if (p1 == (actionf_p1)P_RemoveThinkerDelayed)
+	{
+		if (mobj->thinker.debug_mobjtype != MT_NULL)
+		{
+			return MOBJTYPE_LIST[mobj->thinker.debug_mobjtype];
+		}
+	}
+
+	return "<Not a mobj>";
+}
+
+static const char *MobjThinkerName(const mobj_t *mobj)
+{
+	actionf_p1 p1 = mobj->thinker.function.acp1;
+
+	if (p1 == (actionf_p1)P_MobjThinker)
+	{
+		return "P_MobjThinker";
+	}
+	else if (p1 == (actionf_p1)P_RemoveThinkerDelayed)
+	{
+		return "P_RemoveThinkerDelayed";
+	}
+
+	return "<Unknown Thinker>";
 }
+#endif
 
 //
 // killough 11/98:
@@ -234,20 +279,34 @@ static thinker_t *currentthinker;
 void P_RemoveThinkerDelayed(thinker_t *thinker)
 {
 	thinker_t *next;
-#ifdef PARANOIA
-#define BEENAROUNDBIT (0x40000000) // has to be sufficiently high that it's unlikely to happen in regular gameplay. If you change this, pay attention to the bit pattern of INT32_MIN.
-	if (thinker->references & ~BEENAROUNDBIT)
+
+	if (thinker->references != 0)
 	{
-		if (thinker->references & BEENAROUNDBIT) // Usually gets cleared up in one frame; what's going on here, then?
-			CONS_Printf("Number of potentially faulty references: %d\n", (thinker->references & ~BEENAROUNDBIT));
-		thinker->references |= BEENAROUNDBIT;
+#ifdef PARANOIA
+		if (thinker->debug_time > leveltime)
+		{
+			thinker->debug_time = leveltime + 2; // do not print errors again
+		}
+		// Removed mobjs can be the target of another mobj. In
+		// that case, the other mobj will manage its reference
+		// to the removed mobj in P_MobjThinker. However, if
+		// the removed mobj is removed after the other object
+		// thinks, the reference management is delayed by one
+		// tic.
+		else if (thinker->debug_time < leveltime)
+		{
+			CONS_Printf(
+					"PARANOIA/P_RemoveThinkerDelayed: %p %s references=%d\n",
+					(void*)thinker,
+					MobjTypeName((mobj_t*)thinker),
+					thinker->references
+			);
+
+			thinker->debug_time = leveltime + 2; // do not print this error again
+		}
+#endif
 		return;
 	}
-#undef BEENAROUNDBIT
-#else
-	if (thinker->references)
-		return;
-#endif
 
 	/* Remove from main thinker list */
 	next = thinker->next;
@@ -291,12 +350,45 @@ void P_RemoveThinker(thinker_t *thinker)
  * references, and delay removal until the count is 0.
  */
 
-mobj_t *P_SetTarget(mobj_t **mop, mobj_t *targ)
+mobj_t *P_SetTarget2(mobj_t **mop, mobj_t *targ
+#ifdef PARANOIA
+		, const char *source_file, int source_line
+#endif
+)
 {
-	if (*mop)              // If there was a target already, decrease its refcount
+	if (*mop) // If there was a target already, decrease its refcount
+	{
 		(*mop)->thinker.references--;
-if ((*mop = targ) != NULL) // Set new target and if non-NULL, increase its counter
+
+#ifdef PARANOIA
+		if ((*mop)->thinker.references < 0)
+		{
+			CONS_Printf(
+					"PARANOIA/P_SetTarget: %p %s %s references=%d, references go negative! (%s:%d)\n",
+					(void*)*mop,
+					MobjTypeName(*mop),
+					MobjThinkerName(*mop),
+					(*mop)->thinker.references,
+					source_file,
+					source_line
+			);
+		}
+
+		(*mop)->thinker.debug_time = leveltime;
+#endif
+	}
+
+	if (targ != NULL) // Set new target and if non-NULL, increase its counter
+	{
 		targ->thinker.references++;
+
+#ifdef PARANOIA
+		targ->thinker.debug_time = leveltime;
+#endif
+	}
+
+	*mop = targ;
+
 	return targ;
 }
 
diff --git a/src/p_tick.h b/src/p_tick.h
index 594bbc7afb..bbc227e081 100644
--- a/src/p_tick.h
+++ b/src/p_tick.h
@@ -14,6 +14,8 @@
 #ifndef __P_TICK__
 #define __P_TICK__
 
+#include "doomdef.h"
+
 #ifdef __GNUG__
 #pragma interface
 #endif
@@ -28,6 +30,17 @@ void P_Ticker(boolean run);
 void P_PreTicker(INT32 frames);
 void P_DoTeamscrambling(void);
 void P_RemoveThinkerDelayed(thinker_t *thinker); //killed
-mobj_t *P_SetTarget(mobj_t **mo, mobj_t *target);   // killough 11/98
+
+mobj_t *P_SetTarget2(mobj_t **mo, mobj_t *target
+#ifdef PARANOIA
+		, const char *source_file, int source_line
+#endif
+);
+
+#ifdef PARANOIA
+#define P_SetTarget(...) P_SetTarget2(__VA_ARGS__, __FILE__, __LINE__)
+#else
+#define P_SetTarget P_SetTarget2
+#endif
 
 #endif
diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt
index aab83ca284..4c4cdafb64 100644
--- a/src/sdl/CMakeLists.txt
+++ b/src/sdl/CMakeLists.txt
@@ -1,12 +1,17 @@
 # Declare SDL2 interface sources
 
-target_sources(SRB2SDL2 PRIVATE mixer_sound.c)
-
-target_sourcefile(c)
-
-target_sources(SRB2SDL2 PRIVATE ogl_sdl.c)
-
-target_sources(SRB2SDL2 PRIVATE i_threads.c)
+target_sources(SRB2SDL2 PRIVATE
+	mixer_sound.c
+	ogl_sdl.c
+	i_threads.c
+	i_net.c
+	i_system.c
+	i_main.c
+	i_video.c
+	dosstr.c
+	endtxt.c
+	hwsym_sdl.c
+)
 
 if("${CMAKE_SYSTEM_NAME}" MATCHES Windows)
 	target_sources(SRB2SDL2 PRIVATE
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index c21226ac3f..075199a19a 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -23,12 +23,6 @@
 /// \file
 /// \brief SRB2 system stuff for SDL
 
-#ifdef CMAKECONFIG
-#include "config.h"
-#else
-#include "../config.h.in"
-#endif
-
 #include <signal.h>
 
 #ifdef _WIN32
@@ -2362,7 +2356,10 @@ INT32 I_StartupSystem(void)
 #endif
 	I_StartupConsole();
 #ifdef NEWSIGNALHANDLER
-	I_Fork();
+	// This is useful when debugging. It lets GDB attach to
+	// the correct process easily.
+	if (!M_CheckParm("-nofork"))
+		I_Fork();
 #endif
 	I_RegisterSignals();
 	I_OutputMsg("Compiled for SDL version: %d.%d.%d\n",
@@ -2648,9 +2645,10 @@ void I_ShutdownSystem(void)
 {
 	INT32 c;
 
-#ifndef NEWSIGNALHANDLER
-	I_ShutdownConsole();
+#ifdef NEWSIGNALHANDLER
+	if (M_CheckParm("-nofork"))
 #endif
+		I_ShutdownConsole();
 
 	for (c = MAX_QUIT_FUNCS-1; c >= 0; c--)
 		if (quit_funcs[c])
diff --git a/src/z_zone.h b/src/z_zone.h
index f00f577494..ce7af4a159 100644
--- a/src/z_zone.h
+++ b/src/z_zone.h
@@ -15,6 +15,7 @@
 #define __Z_ZONE__
 
 #include <stdio.h>
+#include "doomdef.h"
 #include "doomtype.h"
 
 #ifdef __GNUC__ // __attribute__ ((X))
diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt
index 7aff16601e..f33b3bf3f8 100644
--- a/thirdparty/CMakeLists.txt
+++ b/thirdparty/CMakeLists.txt
@@ -9,521 +9,13 @@ else()
 	set(NOT_SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES ON)
 endif()
 
-
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME SDL2
-		VERSION 2.24.2
-		URL "https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.24.2.zip"
-		EXCLUDE_FROM_ALL ON
-		OPTIONS
-			"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-			"SDL_SHARED ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-			"SDL_STATIC ${NOT_SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-			"SDL_TEST OFF"
-			"SDL2_DISABLE_SDL2MAIN ON"
-			"SDL2_DISABLE_INSTALL ON"
-	)
-endif()
-
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME SDL2_mixer
-		VERSION 2.6.2
-		URL "https://github.com/libsdl-org/SDL_mixer/archive/refs/tags/release-2.6.2.zip"
-		EXCLUDE_FROM_ALL ON
-		OPTIONS
-			"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-			"SDL2MIXER_INSTALL OFF"
-			"SDL2MIXER_DEPS_SHARED OFF"
-			"SDL2MIXER_SAMPLES OFF"
-			"SDL2MIXER_VENDORED ON"
-			"SDL2MIXER_FLAC ON"
-			"SDL2MIXER_FLAC_LIBFLAC OFF"
-			"SDL2MIXER_FLAC_DRFLAC ON"
-			"SDL2MIXER_MOD OFF"
-			"SDL2MIXER_MP3 ON"
-			"SDL2MIXER_MP3_DRMP3 ON"
-			"SDL2MIXER_MIDI ON"
-			"SDL2MIXER_OPUS OFF"
-			"SDL2MIXER_VORBIS STB"
-			"SDL2MIXER_WAVE ON"
-	)
-endif()
-
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME ZLIB
-		VERSION 1.2.13
-		URL "https://github.com/madler/zlib/archive/refs/tags/v1.2.13.zip"
-		EXCLUDE_FROM_ALL
-		DOWNLOAD_ONLY YES
-	)
-	if(ZLIB_ADDED)
-		set(ZLIB_SRCS
-			crc32.h
-			deflate.h
-			gzguts.h
-			inffast.h
-			inffixed.h
-			inflate.h
-			inftrees.h
-			trees.h
-			zutil.h
-
-			adler32.c
-			compress.c
-			crc32.c
-			deflate.c
-			gzclose.c
-			gzlib.c
-			gzread.c
-			gzwrite.c
-			inflate.c
-			infback.c
-			inftrees.c
-			inffast.c
-			trees.c
-			uncompr.c
-			zutil.c
-		)
-		list(TRANSFORM ZLIB_SRCS PREPEND "${ZLIB_SOURCE_DIR}/")
-
-		configure_file("${ZLIB_SOURCE_DIR}/zlib.pc.cmakein" "${ZLIB_BINARY_DIR}/zlib.pc" @ONLY)
-		configure_file("${ZLIB_SOURCE_DIR}/zconf.h.cmakein" "${ZLIB_BINARY_DIR}/include/zconf.h" @ONLY)
-		configure_file("${ZLIB_SOURCE_DIR}/zlib.h" "${ZLIB_BINARY_DIR}/include/zlib.h" @ONLY)
-
-		add_library(ZLIB ${SRB2_INTERNAL_LIBRARY_TYPE} ${ZLIB_SRCS})
-		set_target_properties(ZLIB PROPERTIES
-			VERSION 1.2.13
-			OUTPUT_NAME "z"
-		)
-		target_include_directories(ZLIB PRIVATE "${ZLIB_SOURCE_DIR}")
-		target_include_directories(ZLIB PUBLIC "${ZLIB_BINARY_DIR}/include")
-		if(MSVC)
-			target_compile_definitions(ZLIB PRIVATE -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE)
-		endif()
-		add_library(ZLIB::ZLIB ALIAS ZLIB)
-	endif()
-endif()
-
 if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME png
-		VERSION 1.6.38
-		URL "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.38.zip"
-		# png cmake build is broken on msys/mingw32
-		DOWNLOAD_ONLY YES
-	)
-
-	if(png_ADDED)
-		# Since png's cmake build is broken, we're going to create a target manually
-		set(
-			PNG_SOURCES
-
-			png.h
-			pngconf.h
-
-			pngpriv.h
-			pngdebug.h
-			pnginfo.h
-			pngstruct.h
-
-			png.c
-			pngerror.c
-			pngget.c
-			pngmem.c
-			pngpread.c
-			pngread.c
-			pngrio.c
-			pngrtran.c
-			pngrutil.c
-			pngset.c
-			pngtrans.c
-			pngwio.c
-			pngwrite.c
-			pngwtran.c
-			pngwutil.c
-		)
-		list(TRANSFORM PNG_SOURCES PREPEND "${png_SOURCE_DIR}/")
-
-		add_custom_command(
-			OUTPUT "${png_BINARY_DIR}/include/png.h" "${png_BINARY_DIR}/include/pngconf.h"
-			COMMAND ${CMAKE_COMMAND} -E copy "${png_SOURCE_DIR}/png.h" "${png_SOURCE_DIR}/pngconf.h" "${png_BINARY_DIR}/include"
-			DEPENDS "${png_SOURCE_DIR}/png.h" "${png_SOURCE_DIR}/pngconf.h"
-			VERBATIM
-		)
-		add_custom_command(
-			OUTPUT "${png_BINARY_DIR}/include/pnglibconf.h"
-			COMMAND ${CMAKE_COMMAND} -E copy "${png_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt" "${png_BINARY_DIR}/include/pnglibconf.h"
-			DEPENDS "${png_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt"
-			VERBATIM
-		)
-		list(
-			APPEND PNG_SOURCES
-			"${png_BINARY_DIR}/include/png.h"
-			"${png_BINARY_DIR}/include/pngconf.h"
-			"${png_BINARY_DIR}/include/pnglibconf.h"
-		)
-		add_library(png "${SRB2_INTERNAL_LIBRARY_TYPE}" ${PNG_SOURCES})
-
-		# Disable ARM NEON since having it automatic breaks libpng external build on clang for some reason
-		target_compile_definitions(png PRIVATE -DPNG_ARM_NEON_OPT=0)
-
-		# The png includes need to be available to consumers
-		target_include_directories(png PUBLIC "${png_BINARY_DIR}/include")
-
-		# ... and these also need to be present only for png build
-		target_include_directories(png PRIVATE "${ZLIB_SOURCE_DIR}")
-		target_include_directories(png PRIVATE "${ZLIB_BINARY_DIR}")
-		target_include_directories(png PRIVATE "${png_BINARY_DIR}")
-
-		target_link_libraries(png PRIVATE ZLIB::ZLIB)
-		add_library(PNG::PNG ALIAS png)
-	endif()
+	include("cpm-sdl2.cmake")
+	include("cpm-sdl2-mixer.cmake")
+	include("cpm-zlib.cmake")
+	include("cpm-png.cmake")
+	include("cpm-curl.cmake")
+	include("cpm-openmpt.cmake")
 endif()
 
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	set(
-		internal_curl_options
-
-		"BUILD_CURL_EXE OFF"
-		"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-		"CURL_DISABLE_TESTS ON"
-		"HTTP_ONLY ON"
-		"CURL_DISABLE_CRYPTO_AUTH ON"
-		"CURL_DISABLE_NTLM ON"
-		"ENABLE_MANUAL OFF"
-		"ENABLE_THREADED_RESOLVER OFF"
-		"CURL_USE_LIBPSL OFF"
-		"CURL_USE_LIBSSH2 OFF"
-		"USE_LIBIDN2 OFF"
-		"CURL_ENABLE_EXPORT_TARGET OFF"
-	)
-	if(${CMAKE_SYSTEM} MATCHES Windows)
-		list(APPEND internal_curl_options "CURL_USE_OPENSSL OFF")
-		list(APPEND internal_curl_options "CURL_USE_SCHANNEL ON")
-	endif()
-	if(${CMAKE_SYSTEM} MATCHES Darwin)
-		list(APPEND internal_curl_options "CURL_USE_OPENSSL OFF")
-		list(APPEND internal_curl_options "CURL_USE_SECTRANSP ON")
-	endif()
-	if(${CMAKE_SYSTEM} MATCHES Linux)
-		list(APPEND internal_curl_options "CURL_USE_OPENSSL ON")
-	endif()
-
-	CPMAddPackage(
-		NAME curl
-		VERSION 7.86.0
-		URL "https://github.com/curl/curl/archive/refs/tags/curl-7_86_0.zip"
-		EXCLUDE_FROM_ALL ON
-		OPTIONS ${internal_curl_options}
-	)
-endif()
-
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME openmpt
-		VERSION 0.4.30
-		URL "https://github.com/OpenMPT/openmpt/archive/refs/tags/libopenmpt-0.4.30.zip"
-		DOWNLOAD_ONLY ON
-	)
-
-	if(openmpt_ADDED)
-		set(
-			openmpt_SOURCES
-
-			# minimp3
-			# -DMPT_WITH_MINIMP3
-			include/minimp3/minimp3.c
-
-			common/mptStringParse.cpp
-			common/mptLibrary.cpp
-			common/Logging.cpp
-			common/Profiler.cpp
-			common/version.cpp
-			common/mptCPU.cpp
-			common/ComponentManager.cpp
-			common/mptOS.cpp
-			common/serialization_utils.cpp
-			common/mptStringFormat.cpp
-			common/FileReader.cpp
-			common/mptWine.cpp
-			common/mptPathString.cpp
-			common/mptAlloc.cpp
-			common/mptUUID.cpp
-			common/mptTime.cpp
-			common/mptString.cpp
-			common/mptFileIO.cpp
-			common/mptStringBuffer.cpp
-			common/mptRandom.cpp
-			common/mptIO.cpp
-			common/misc_util.cpp
-
-			common/mptCRC.h
-			common/mptLibrary.h
-			common/mptIO.h
-			common/version.h
-			common/stdafx.h
-			common/ComponentManager.h
-			common/Endianness.h
-			common/mptStringFormat.h
-			common/mptMutex.h
-			common/mptUUID.h
-			common/mptExceptionText.h
-			common/BuildSettings.h
-			common/mptAlloc.h
-			common/mptTime.h
-			common/FileReaderFwd.h
-			common/Logging.h
-			common/mptException.h
-			common/mptWine.h
-			common/mptStringBuffer.h
-			common/misc_util.h
-			common/mptBaseMacros.h
-			common/mptMemory.h
-			common/mptFileIO.h
-			common/serialization_utils.h
-			common/mptSpan.h
-			common/mptThread.h
-			common/FlagSet.h
-			common/mptString.h
-			common/mptStringParse.h
-			common/mptBaseUtils.h
-			common/mptRandom.h
-			common/CompilerDetect.h
-			common/FileReader.h
-			common/mptAssert.h
-			common/mptPathString.h
-			common/Profiler.h
-			common/mptOS.h
-			common/mptBaseTypes.h
-			common/mptCPU.h
-			common/mptBufferIO.h
-			common/versionNumber.h
-
-			soundlib/WAVTools.cpp
-			soundlib/ITTools.cpp
-			soundlib/AudioCriticalSection.cpp
-			soundlib/Load_stm.cpp
-			soundlib/MixerLoops.cpp
-			soundlib/Load_dbm.cpp
-			soundlib/ModChannel.cpp
-			soundlib/Load_gdm.cpp
-			soundlib/Snd_fx.cpp
-			soundlib/Load_mid.cpp
-			soundlib/mod_specifications.cpp
-			soundlib/Snd_flt.cpp
-			soundlib/Load_psm.cpp
-			soundlib/Load_far.cpp
-			soundlib/patternContainer.cpp
-			soundlib/Load_med.cpp
-			soundlib/Load_dmf.cpp
-			soundlib/Paula.cpp
-			soundlib/modcommand.cpp
-			soundlib/Message.cpp
-			soundlib/SoundFilePlayConfig.cpp
-			soundlib/Load_uax.cpp
-			soundlib/plugins/PlugInterface.cpp
-			soundlib/plugins/LFOPlugin.cpp
-			soundlib/plugins/PluginManager.cpp
-			soundlib/plugins/DigiBoosterEcho.cpp
-			soundlib/plugins/dmo/DMOPlugin.cpp
-			soundlib/plugins/dmo/Flanger.cpp
-			soundlib/plugins/dmo/Distortion.cpp
-			soundlib/plugins/dmo/ParamEq.cpp
-			soundlib/plugins/dmo/Gargle.cpp
-			soundlib/plugins/dmo/I3DL2Reverb.cpp
-			soundlib/plugins/dmo/Compressor.cpp
-			soundlib/plugins/dmo/WavesReverb.cpp
-			soundlib/plugins/dmo/Echo.cpp
-			soundlib/plugins/dmo/Chorus.cpp
-			soundlib/Load_ams.cpp
-			soundlib/tuningbase.cpp
-			soundlib/ContainerUMX.cpp
-			soundlib/Load_ptm.cpp
-			soundlib/ContainerXPK.cpp
-			soundlib/SampleFormatMP3.cpp
-			soundlib/tuning.cpp
-			soundlib/Sndfile.cpp
-			soundlib/ContainerMMCMP.cpp
-			soundlib/Load_amf.cpp
-			soundlib/Load_669.cpp
-			soundlib/modsmp_ctrl.cpp
-			soundlib/Load_mtm.cpp
-			soundlib/OggStream.cpp
-			soundlib/Load_plm.cpp
-			soundlib/Tables.cpp
-			soundlib/Load_c67.cpp
-			soundlib/Load_mod.cpp
-			soundlib/Load_sfx.cpp
-			soundlib/Sndmix.cpp
-			soundlib/load_j2b.cpp
-			soundlib/ModSequence.cpp
-			soundlib/SampleFormatFLAC.cpp
-			soundlib/ModInstrument.cpp
-			soundlib/Load_mo3.cpp
-			soundlib/ModSample.cpp
-			soundlib/Dlsbank.cpp
-			soundlib/Load_itp.cpp
-			soundlib/UpgradeModule.cpp
-			soundlib/MIDIMacros.cpp
-			soundlib/ContainerPP20.cpp
-			soundlib/RowVisitor.cpp
-			soundlib/Load_imf.cpp
-			soundlib/SampleFormatVorbis.cpp
-			soundlib/Load_dsm.cpp
-			soundlib/Load_mt2.cpp
-			soundlib/MixerSettings.cpp
-			soundlib/S3MTools.cpp
-			soundlib/Load_xm.cpp
-			soundlib/MIDIEvents.cpp
-			soundlib/pattern.cpp
-			soundlib/Load_digi.cpp
-			soundlib/Load_s3m.cpp
-			soundlib/tuningCollection.cpp
-			soundlib/SampleIO.cpp
-			soundlib/Dither.cpp
-			soundlib/Load_mdl.cpp
-			soundlib/OPL.cpp
-			soundlib/WindowedFIR.cpp
-			soundlib/SampleFormats.cpp
-			soundlib/Load_wav.cpp
-			soundlib/Load_it.cpp
-			soundlib/UMXTools.cpp
-			soundlib/Load_stp.cpp
-			soundlib/Load_okt.cpp
-			soundlib/Load_ult.cpp
-			soundlib/MixFuncTable.cpp
-			soundlib/SampleFormatOpus.cpp
-			soundlib/Fastmix.cpp
-			soundlib/Tagging.cpp
-			soundlib/ITCompression.cpp
-			soundlib/Load_dtm.cpp
-			soundlib/MPEGFrame.cpp
-			soundlib/XMTools.cpp
-			soundlib/SampleFormatMediaFoundation.cpp
-			soundlib/InstrumentExtensions.cpp
-
-			soundlib/MixerInterface.h
-			soundlib/SoundFilePlayConfig.h
-			soundlib/ModSample.h
-			soundlib/MIDIEvents.h
-			soundlib/ModSampleCopy.h
-			soundlib/patternContainer.h
-			soundlib/ChunkReader.h
-			soundlib/ITCompression.h
-			soundlib/Dither.h
-			soundlib/S3MTools.h
-			soundlib/MPEGFrame.h
-			soundlib/WAVTools.h
-			soundlib/mod_specifications.h
-			soundlib/ITTools.h
-			soundlib/RowVisitor.h
-			soundlib/plugins/PluginMixBuffer.h
-			soundlib/plugins/PluginStructs.h
-			soundlib/plugins/LFOPlugin.h
-			soundlib/plugins/PlugInterface.h
-			soundlib/plugins/DigiBoosterEcho.h
-			soundlib/plugins/OpCodes.h
-			soundlib/plugins/dmo/Echo.h
-			soundlib/plugins/dmo/I3DL2Reverb.h
-			soundlib/plugins/dmo/WavesReverb.h
-			soundlib/plugins/dmo/ParamEq.h
-			soundlib/plugins/dmo/Gargle.h
-			soundlib/plugins/dmo/DMOPlugin.h
-			soundlib/plugins/dmo/Chorus.h
-			soundlib/plugins/dmo/Compressor.h
-			soundlib/plugins/dmo/Distortion.h
-			soundlib/plugins/dmo/Flanger.h
-			soundlib/plugins/PluginManager.h
-			soundlib/SampleIO.h
-			soundlib/Container.h
-			soundlib/ModSequence.h
-			soundlib/UMXTools.h
-			soundlib/Message.h
-			soundlib/modcommand.h
-			soundlib/XMTools.h
-			soundlib/Snd_defs.h
-			soundlib/MixFuncTable.h
-			soundlib/pattern.h
-			soundlib/modsmp_ctrl.h
-			soundlib/Tagging.h
-			soundlib/tuningcollection.h
-			soundlib/Mixer.h
-			soundlib/FloatMixer.h
-			soundlib/AudioCriticalSection.h
-			soundlib/Tables.h
-			soundlib/tuningbase.h
-			soundlib/WindowedFIR.h
-			soundlib/Sndfile.h
-			soundlib/Paula.h
-			soundlib/ModInstrument.h
-			soundlib/Dlsbank.h
-			soundlib/IntMixer.h
-			soundlib/OPL.h
-			soundlib/Resampler.h
-			soundlib/ModChannel.h
-			soundlib/MixerSettings.h
-			soundlib/AudioReadTarget.h
-			soundlib/MixerLoops.h
-			soundlib/tuning.h
-			soundlib/MIDIMacros.h
-			soundlib/OggStream.h
-			soundlib/Loaders.h
-			soundlib/BitReader.h
-			soundlib/opal.h
-
-			sounddsp/AGC.cpp
-			sounddsp/EQ.cpp
-			sounddsp/DSP.cpp
-			sounddsp/Reverb.cpp
-			sounddsp/Reverb.h
-			sounddsp/EQ.h
-			sounddsp/DSP.h
-			sounddsp/AGC.h
-
-			libopenmpt/libopenmpt_c.cpp
-			libopenmpt/libopenmpt_cxx.cpp
-			libopenmpt/libopenmpt_impl.cpp
-			libopenmpt/libopenmpt_ext_impl.cpp
-		)
-		list(TRANSFORM openmpt_SOURCES PREPEND "${openmpt_SOURCE_DIR}/")
-
-		# -DLIBOPENMPT_BUILD
-		configure_file("openmpt_svn_version.h" "svn_version.h")
-		add_library(openmpt "${SRB2_INTERNAL_LIBRARY_TYPE}" ${openmpt_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/svn_version.h)
-		if("${CMAKE_C_COMPILER_ID}" STREQUAL GNU OR "${CMAKE_C_COMPILER_ID}" STREQUAL Clang OR "${CMAKE_C_COMPILER_ID}" STREQUAL AppleClang)
-			target_compile_options(openmpt PRIVATE "-g0")
-		endif()
-		if("${CMAKE_SYSTEM_NAME}" STREQUAL Windows AND "${CMAKE_C_COMPILER_ID}" STREQUAL MSVC)
-			target_link_libraries(openmpt PRIVATE Rpcrt4)
-		endif()
-		target_compile_features(openmpt PRIVATE cxx_std_11)
-		target_compile_definitions(openmpt PRIVATE -DLIBOPENMPT_BUILD)
-
-		target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/common")
-		target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/src")
-		target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/include")
-		target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}")
-		target_include_directories(openmpt PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
-
-		# I wish this wasn't necessary, but it is
-		target_include_directories(openmpt PUBLIC "${openmpt_SOURCE_DIR}")
-	endif()
-endif()
-
-if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}")
-	CPMAddPackage(
-		NAME libgme
-		VERSION 0.6.3
-		URL "https://bitbucket.org/mpyne/game-music-emu/get/e76bdc0cb916e79aa540290e6edd0c445879d3ba.zip"
-		EXCLUDE_FROM_ALL ON
-		OPTIONS
-			"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
-			"ENABLE_UBSAN OFF"
-			"GME_YM2612_EMU MAME"
-	)
-	target_compile_features(gme PRIVATE cxx_std_11)
-	target_link_libraries(gme PRIVATE ZLIB::ZLIB)
-endif()
+include("cpm-libgme.cmake")
diff --git a/thirdparty/cpm-curl.cmake b/thirdparty/cpm-curl.cmake
new file mode 100644
index 0000000000..3d8c6e61d4
--- /dev/null
+++ b/thirdparty/cpm-curl.cmake
@@ -0,0 +1,35 @@
+set(
+	internal_curl_options
+
+	"BUILD_CURL_EXE OFF"
+	"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+	"CURL_DISABLE_TESTS ON"
+	"HTTP_ONLY ON"
+	"CURL_DISABLE_CRYPTO_AUTH ON"
+	"CURL_DISABLE_NTLM ON"
+	"ENABLE_MANUAL OFF"
+	"ENABLE_THREADED_RESOLVER OFF"
+	"CURL_USE_LIBPSL OFF"
+	"CURL_USE_LIBSSH2 OFF"
+	"USE_LIBIDN2 OFF"
+	"CURL_ENABLE_EXPORT_TARGET OFF"
+)
+if(${CMAKE_SYSTEM} MATCHES Windows)
+	list(APPEND internal_curl_options "CURL_USE_OPENSSL OFF")
+	list(APPEND internal_curl_options "CURL_USE_SCHANNEL ON")
+endif()
+if(${CMAKE_SYSTEM} MATCHES Darwin)
+	list(APPEND internal_curl_options "CURL_USE_OPENSSL OFF")
+	list(APPEND internal_curl_options "CURL_USE_SECTRANSP ON")
+endif()
+if(${CMAKE_SYSTEM} MATCHES Linux)
+	list(APPEND internal_curl_options "CURL_USE_OPENSSL ON")
+endif()
+
+CPMAddPackage(
+	NAME curl
+	VERSION 7.86.0
+	URL "https://github.com/curl/curl/archive/refs/tags/curl-7_86_0.zip"
+	EXCLUDE_FROM_ALL ON
+	OPTIONS ${internal_curl_options}
+)
diff --git a/thirdparty/cpm-libgme.cmake b/thirdparty/cpm-libgme.cmake
new file mode 100644
index 0000000000..f15bc3b31c
--- /dev/null
+++ b/thirdparty/cpm-libgme.cmake
@@ -0,0 +1,16 @@
+CPMAddPackage(
+	NAME libgme
+	VERSION 0.6.3
+	URL "https://bitbucket.org/mpyne/game-music-emu/get/e76bdc0cb916e79aa540290e6edd0c445879d3ba.zip"
+	EXCLUDE_FROM_ALL ON
+	OPTIONS
+		"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+		"ENABLE_UBSAN OFF"
+		"GME_YM2612_EMU MAME"
+)
+
+if(libgme_ADDED)
+	target_compile_features(gme PRIVATE cxx_std_11)
+	# libgme's CMakeLists.txt already links this
+	#target_link_libraries(gme PRIVATE ZLIB::ZLIB)
+endif()
diff --git a/thirdparty/cpm-openmpt.cmake b/thirdparty/cpm-openmpt.cmake
new file mode 100644
index 0000000000..01f7ff75f6
--- /dev/null
+++ b/thirdparty/cpm-openmpt.cmake
@@ -0,0 +1,289 @@
+CPMAddPackage(
+	NAME openmpt
+	VERSION 0.4.30
+	URL "https://github.com/OpenMPT/openmpt/archive/refs/tags/libopenmpt-0.4.30.zip"
+	DOWNLOAD_ONLY ON
+)
+
+if(openmpt_ADDED)
+	set(
+		openmpt_SOURCES
+
+		# minimp3
+		# -DMPT_WITH_MINIMP3
+		include/minimp3/minimp3.c
+
+		common/mptStringParse.cpp
+		common/mptLibrary.cpp
+		common/Logging.cpp
+		common/Profiler.cpp
+		common/version.cpp
+		common/mptCPU.cpp
+		common/ComponentManager.cpp
+		common/mptOS.cpp
+		common/serialization_utils.cpp
+		common/mptStringFormat.cpp
+		common/FileReader.cpp
+		common/mptWine.cpp
+		common/mptPathString.cpp
+		common/mptAlloc.cpp
+		common/mptUUID.cpp
+		common/mptTime.cpp
+		common/mptString.cpp
+		common/mptFileIO.cpp
+		common/mptStringBuffer.cpp
+		common/mptRandom.cpp
+		common/mptIO.cpp
+		common/misc_util.cpp
+
+		common/mptCRC.h
+		common/mptLibrary.h
+		common/mptIO.h
+		common/version.h
+		common/stdafx.h
+		common/ComponentManager.h
+		common/Endianness.h
+		common/mptStringFormat.h
+		common/mptMutex.h
+		common/mptUUID.h
+		common/mptExceptionText.h
+		common/BuildSettings.h
+		common/mptAlloc.h
+		common/mptTime.h
+		common/FileReaderFwd.h
+		common/Logging.h
+		common/mptException.h
+		common/mptWine.h
+		common/mptStringBuffer.h
+		common/misc_util.h
+		common/mptBaseMacros.h
+		common/mptMemory.h
+		common/mptFileIO.h
+		common/serialization_utils.h
+		common/mptSpan.h
+		common/mptThread.h
+		common/FlagSet.h
+		common/mptString.h
+		common/mptStringParse.h
+		common/mptBaseUtils.h
+		common/mptRandom.h
+		common/CompilerDetect.h
+		common/FileReader.h
+		common/mptAssert.h
+		common/mptPathString.h
+		common/Profiler.h
+		common/mptOS.h
+		common/mptBaseTypes.h
+		common/mptCPU.h
+		common/mptBufferIO.h
+		common/versionNumber.h
+
+		soundlib/WAVTools.cpp
+		soundlib/ITTools.cpp
+		soundlib/AudioCriticalSection.cpp
+		soundlib/Load_stm.cpp
+		soundlib/MixerLoops.cpp
+		soundlib/Load_dbm.cpp
+		soundlib/ModChannel.cpp
+		soundlib/Load_gdm.cpp
+		soundlib/Snd_fx.cpp
+		soundlib/Load_mid.cpp
+		soundlib/mod_specifications.cpp
+		soundlib/Snd_flt.cpp
+		soundlib/Load_psm.cpp
+		soundlib/Load_far.cpp
+		soundlib/patternContainer.cpp
+		soundlib/Load_med.cpp
+		soundlib/Load_dmf.cpp
+		soundlib/Paula.cpp
+		soundlib/modcommand.cpp
+		soundlib/Message.cpp
+		soundlib/SoundFilePlayConfig.cpp
+		soundlib/Load_uax.cpp
+		soundlib/plugins/PlugInterface.cpp
+		soundlib/plugins/LFOPlugin.cpp
+		soundlib/plugins/PluginManager.cpp
+		soundlib/plugins/DigiBoosterEcho.cpp
+		soundlib/plugins/dmo/DMOPlugin.cpp
+		soundlib/plugins/dmo/Flanger.cpp
+		soundlib/plugins/dmo/Distortion.cpp
+		soundlib/plugins/dmo/ParamEq.cpp
+		soundlib/plugins/dmo/Gargle.cpp
+		soundlib/plugins/dmo/I3DL2Reverb.cpp
+		soundlib/plugins/dmo/Compressor.cpp
+		soundlib/plugins/dmo/WavesReverb.cpp
+		soundlib/plugins/dmo/Echo.cpp
+		soundlib/plugins/dmo/Chorus.cpp
+		soundlib/Load_ams.cpp
+		soundlib/tuningbase.cpp
+		soundlib/ContainerUMX.cpp
+		soundlib/Load_ptm.cpp
+		soundlib/ContainerXPK.cpp
+		soundlib/SampleFormatMP3.cpp
+		soundlib/tuning.cpp
+		soundlib/Sndfile.cpp
+		soundlib/ContainerMMCMP.cpp
+		soundlib/Load_amf.cpp
+		soundlib/Load_669.cpp
+		soundlib/modsmp_ctrl.cpp
+		soundlib/Load_mtm.cpp
+		soundlib/OggStream.cpp
+		soundlib/Load_plm.cpp
+		soundlib/Tables.cpp
+		soundlib/Load_c67.cpp
+		soundlib/Load_mod.cpp
+		soundlib/Load_sfx.cpp
+		soundlib/Sndmix.cpp
+		soundlib/load_j2b.cpp
+		soundlib/ModSequence.cpp
+		soundlib/SampleFormatFLAC.cpp
+		soundlib/ModInstrument.cpp
+		soundlib/Load_mo3.cpp
+		soundlib/ModSample.cpp
+		soundlib/Dlsbank.cpp
+		soundlib/Load_itp.cpp
+		soundlib/UpgradeModule.cpp
+		soundlib/MIDIMacros.cpp
+		soundlib/ContainerPP20.cpp
+		soundlib/RowVisitor.cpp
+		soundlib/Load_imf.cpp
+		soundlib/SampleFormatVorbis.cpp
+		soundlib/Load_dsm.cpp
+		soundlib/Load_mt2.cpp
+		soundlib/MixerSettings.cpp
+		soundlib/S3MTools.cpp
+		soundlib/Load_xm.cpp
+		soundlib/MIDIEvents.cpp
+		soundlib/pattern.cpp
+		soundlib/Load_digi.cpp
+		soundlib/Load_s3m.cpp
+		soundlib/tuningCollection.cpp
+		soundlib/SampleIO.cpp
+		soundlib/Dither.cpp
+		soundlib/Load_mdl.cpp
+		soundlib/OPL.cpp
+		soundlib/WindowedFIR.cpp
+		soundlib/SampleFormats.cpp
+		soundlib/Load_wav.cpp
+		soundlib/Load_it.cpp
+		soundlib/UMXTools.cpp
+		soundlib/Load_stp.cpp
+		soundlib/Load_okt.cpp
+		soundlib/Load_ult.cpp
+		soundlib/MixFuncTable.cpp
+		soundlib/SampleFormatOpus.cpp
+		soundlib/Fastmix.cpp
+		soundlib/Tagging.cpp
+		soundlib/ITCompression.cpp
+		soundlib/Load_dtm.cpp
+		soundlib/MPEGFrame.cpp
+		soundlib/XMTools.cpp
+		soundlib/SampleFormatMediaFoundation.cpp
+		soundlib/InstrumentExtensions.cpp
+
+		soundlib/MixerInterface.h
+		soundlib/SoundFilePlayConfig.h
+		soundlib/ModSample.h
+		soundlib/MIDIEvents.h
+		soundlib/ModSampleCopy.h
+		soundlib/patternContainer.h
+		soundlib/ChunkReader.h
+		soundlib/ITCompression.h
+		soundlib/Dither.h
+		soundlib/S3MTools.h
+		soundlib/MPEGFrame.h
+		soundlib/WAVTools.h
+		soundlib/mod_specifications.h
+		soundlib/ITTools.h
+		soundlib/RowVisitor.h
+		soundlib/plugins/PluginMixBuffer.h
+		soundlib/plugins/PluginStructs.h
+		soundlib/plugins/LFOPlugin.h
+		soundlib/plugins/PlugInterface.h
+		soundlib/plugins/DigiBoosterEcho.h
+		soundlib/plugins/OpCodes.h
+		soundlib/plugins/dmo/Echo.h
+		soundlib/plugins/dmo/I3DL2Reverb.h
+		soundlib/plugins/dmo/WavesReverb.h
+		soundlib/plugins/dmo/ParamEq.h
+		soundlib/plugins/dmo/Gargle.h
+		soundlib/plugins/dmo/DMOPlugin.h
+		soundlib/plugins/dmo/Chorus.h
+		soundlib/plugins/dmo/Compressor.h
+		soundlib/plugins/dmo/Distortion.h
+		soundlib/plugins/dmo/Flanger.h
+		soundlib/plugins/PluginManager.h
+		soundlib/SampleIO.h
+		soundlib/Container.h
+		soundlib/ModSequence.h
+		soundlib/UMXTools.h
+		soundlib/Message.h
+		soundlib/modcommand.h
+		soundlib/XMTools.h
+		soundlib/Snd_defs.h
+		soundlib/MixFuncTable.h
+		soundlib/pattern.h
+		soundlib/modsmp_ctrl.h
+		soundlib/Tagging.h
+		soundlib/tuningcollection.h
+		soundlib/Mixer.h
+		soundlib/FloatMixer.h
+		soundlib/AudioCriticalSection.h
+		soundlib/Tables.h
+		soundlib/tuningbase.h
+		soundlib/WindowedFIR.h
+		soundlib/Sndfile.h
+		soundlib/Paula.h
+		soundlib/ModInstrument.h
+		soundlib/Dlsbank.h
+		soundlib/IntMixer.h
+		soundlib/OPL.h
+		soundlib/Resampler.h
+		soundlib/ModChannel.h
+		soundlib/MixerSettings.h
+		soundlib/AudioReadTarget.h
+		soundlib/MixerLoops.h
+		soundlib/tuning.h
+		soundlib/MIDIMacros.h
+		soundlib/OggStream.h
+		soundlib/Loaders.h
+		soundlib/BitReader.h
+		soundlib/opal.h
+
+		sounddsp/AGC.cpp
+		sounddsp/EQ.cpp
+		sounddsp/DSP.cpp
+		sounddsp/Reverb.cpp
+		sounddsp/Reverb.h
+		sounddsp/EQ.h
+		sounddsp/DSP.h
+		sounddsp/AGC.h
+
+		libopenmpt/libopenmpt_c.cpp
+		libopenmpt/libopenmpt_cxx.cpp
+		libopenmpt/libopenmpt_impl.cpp
+		libopenmpt/libopenmpt_ext_impl.cpp
+	)
+	list(TRANSFORM openmpt_SOURCES PREPEND "${openmpt_SOURCE_DIR}/")
+
+	# -DLIBOPENMPT_BUILD
+	configure_file("openmpt_svn_version.h" "svn_version.h")
+	add_library(openmpt "${SRB2_INTERNAL_LIBRARY_TYPE}" ${openmpt_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/svn_version.h)
+	if("${CMAKE_C_COMPILER_ID}" STREQUAL GNU OR "${CMAKE_C_COMPILER_ID}" STREQUAL Clang OR "${CMAKE_C_COMPILER_ID}" STREQUAL AppleClang)
+		target_compile_options(openmpt PRIVATE "-g0")
+	endif()
+	if("${CMAKE_SYSTEM_NAME}" STREQUAL Windows AND "${CMAKE_C_COMPILER_ID}" STREQUAL MSVC)
+		target_link_libraries(openmpt PRIVATE Rpcrt4)
+	endif()
+	target_compile_features(openmpt PRIVATE cxx_std_11)
+	target_compile_definitions(openmpt PRIVATE -DLIBOPENMPT_BUILD)
+
+	target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/common")
+	target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/src")
+	target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/include")
+	target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}")
+	target_include_directories(openmpt PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
+
+	# I wish this wasn't necessary, but it is
+	target_include_directories(openmpt PUBLIC "${openmpt_SOURCE_DIR}")
+endif()
diff --git a/thirdparty/cpm-png.cmake b/thirdparty/cpm-png.cmake
new file mode 100644
index 0000000000..f16ac037b0
--- /dev/null
+++ b/thirdparty/cpm-png.cmake
@@ -0,0 +1,69 @@
+CPMAddPackage(
+	NAME png
+	VERSION 1.6.38
+	URL "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.38.zip"
+	# png cmake build is broken on msys/mingw32
+	DOWNLOAD_ONLY YES
+)
+
+if(png_ADDED)
+	# Since png's cmake build is broken, we're going to create a target manually
+	set(
+		PNG_SOURCES
+		png.h
+		pngconf.h
+		pngpriv.h
+		pngdebug.h
+		pnginfo.h
+		pngstruct.h
+		png.c
+		pngerror.c
+		pngget.c
+		pngmem.c
+		pngpread.c
+		pngread.c
+		pngrio.c
+		pngrtran.c
+		pngrutil.c
+		pngset.c
+		pngtrans.c
+		pngwio.c
+		pngwrite.c
+		pngwtran.c
+		pngwutil.c
+	)
+	list(TRANSFORM PNG_SOURCES PREPEND "${png_SOURCE_DIR}/")
+
+	add_custom_command(
+		OUTPUT "${png_BINARY_DIR}/include/png.h" "${png_BINARY_DIR}/include/pngconf.h"
+		COMMAND ${CMAKE_COMMAND} -E copy "${png_SOURCE_DIR}/png.h" "${png_SOURCE_DIR}/pngconf.h" "${png_BINARY_DIR}/include"
+		DEPENDS "${png_SOURCE_DIR}/png.h" "${png_SOURCE_DIR}/pngconf.h"
+		VERBATIM
+	)
+	add_custom_command(
+		OUTPUT "${png_BINARY_DIR}/include/pnglibconf.h"
+		COMMAND ${CMAKE_COMMAND} -E copy "${png_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt" "${png_BINARY_DIR}/include/pnglibconf.h"
+		DEPENDS "${png_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt"
+		VERBATIM
+	)
+	list(
+		APPEND PNG_SOURCES
+		"${png_BINARY_DIR}/include/png.h"
+		"${png_BINARY_DIR}/include/pngconf.h"
+		"${png_BINARY_DIR}/include/pnglibconf.h"
+	)
+	add_library(png "${SRB2_INTERNAL_LIBRARY_TYPE}" ${PNG_SOURCES})
+
+	# Disable ARM NEON since having it automatic breaks libpng external build on clang for some reason
+	target_compile_definitions(png PRIVATE -DPNG_ARM_NEON_OPT=0)
+
+	# The png includes need to be available to consumers
+	target_include_directories(png PUBLIC "${png_BINARY_DIR}/include")
+
+	# ... and these also need to be present only for png build
+	target_include_directories(png PRIVATE "${ZLIB_SOURCE_DIR}")
+	target_include_directories(png PRIVATE "${ZLIB_BINARY_DIR}")
+	target_include_directories(png PRIVATE "${png_BINARY_DIR}")
+	target_link_libraries(png PRIVATE ZLIB::ZLIB)
+	add_library(PNG::PNG ALIAS png)
+endif()
diff --git a/thirdparty/cpm-sdl2-mixer.cmake b/thirdparty/cpm-sdl2-mixer.cmake
new file mode 100644
index 0000000000..b7dfeae0d3
--- /dev/null
+++ b/thirdparty/cpm-sdl2-mixer.cmake
@@ -0,0 +1,22 @@
+CPMAddPackage(
+	NAME SDL2_mixer
+	VERSION 2.6.2
+	URL "https://github.com/libsdl-org/SDL_mixer/archive/refs/tags/release-2.6.2.zip"
+	EXCLUDE_FROM_ALL ON
+	OPTIONS
+		"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+		"SDL2MIXER_INSTALL OFF"
+		"SDL2MIXER_DEPS_SHARED OFF"
+		"SDL2MIXER_SAMPLES OFF"
+		"SDL2MIXER_VENDORED ON"
+		"SDL2MIXER_FLAC ON"
+		"SDL2MIXER_FLAC_LIBFLAC OFF"
+		"SDL2MIXER_FLAC_DRFLAC ON"
+		"SDL2MIXER_MOD OFF"
+		"SDL2MIXER_MP3 ON"
+		"SDL2MIXER_MP3_DRMP3 ON"
+		"SDL2MIXER_MIDI ON"
+		"SDL2MIXER_OPUS OFF"
+		"SDL2MIXER_VORBIS STB"
+		"SDL2MIXER_WAVE ON"
+)
diff --git a/thirdparty/cpm-sdl2.cmake b/thirdparty/cpm-sdl2.cmake
new file mode 100644
index 0000000000..58cf9afc2a
--- /dev/null
+++ b/thirdparty/cpm-sdl2.cmake
@@ -0,0 +1,13 @@
+CPMAddPackage(
+	NAME SDL2
+	VERSION 2.24.2
+	URL "https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.24.2.zip"
+	EXCLUDE_FROM_ALL ON
+	OPTIONS
+		"BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+		"SDL_SHARED ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+		"SDL_STATIC ${NOT_SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}"
+		"SDL_TEST OFF"
+		"SDL2_DISABLE_SDL2MAIN ON"
+		"SDL2_DISABLE_INSTALL ON"
+)
diff --git a/thirdparty/cpm-zlib.cmake b/thirdparty/cpm-zlib.cmake
new file mode 100644
index 0000000000..5368366fd1
--- /dev/null
+++ b/thirdparty/cpm-zlib.cmake
@@ -0,0 +1,53 @@
+CPMAddPackage(
+	NAME ZLIB
+	VERSION 1.2.13
+	URL "https://github.com/madler/zlib/archive/refs/tags/v1.2.13.zip"
+	EXCLUDE_FROM_ALL
+	DOWNLOAD_ONLY YES
+)
+
+if(ZLIB_ADDED)
+	set(ZLIB_SRCS
+		crc32.h
+		deflate.h
+		gzguts.h
+		inffast.h
+		inffixed.h
+		inflate.h
+		inftrees.h
+		trees.h
+		zutil.h
+		adler32.c
+		compress.c
+		crc32.c
+		deflate.c
+		gzclose.c
+		gzlib.c
+		gzread.c
+		gzwrite.c
+		inflate.c
+		infback.c
+		inftrees.c
+		inffast.c
+		trees.c
+		uncompr.c
+		zutil.c
+	)
+	list(TRANSFORM ZLIB_SRCS PREPEND "${ZLIB_SOURCE_DIR}/")
+
+	configure_file("${ZLIB_SOURCE_DIR}/zlib.pc.cmakein" "${ZLIB_BINARY_DIR}/zlib.pc" @ONLY)
+	configure_file("${ZLIB_SOURCE_DIR}/zconf.h.cmakein" "${ZLIB_BINARY_DIR}/include/zconf.h" @ONLY)
+	configure_file("${ZLIB_SOURCE_DIR}/zlib.h" "${ZLIB_BINARY_DIR}/include/zlib.h" @ONLY)
+
+	add_library(ZLIB ${SRB2_INTERNAL_LIBRARY_TYPE} ${ZLIB_SRCS})
+	set_target_properties(ZLIB PROPERTIES
+		VERSION 1.2.13
+		OUTPUT_NAME "z"
+	)
+	target_include_directories(ZLIB PRIVATE "${ZLIB_SOURCE_DIR}")
+	target_include_directories(ZLIB PUBLIC "${ZLIB_BINARY_DIR}/include")
+	if(MSVC)
+		target_compile_definitions(ZLIB PRIVATE -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE)
+	endif()
+	add_library(ZLIB::ZLIB ALIAS ZLIB)
+endif()
-- 
GitLab


From 70928120c26b38f8bba06bcacfd753b78fbab7dd Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Tue, 1 Aug 2023 12:45:58 +0100
Subject: [PATCH 305/518] allow use of P_IsFlagAtBase in HUD code, since it
 doesn't actually modify anything

---
 src/lua_baselib.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index bf3fd1decc..915669bb27 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -2456,7 +2456,7 @@ static int lib_pFadeLight(lua_State *L)
 static int lib_pIsFlagAtBase(lua_State *L)
 {
 	mobjtype_t flag = luaL_checkinteger(L, 1);
-	NOHUD
+	//HUDSAFE
 	INLEVEL
 	if (flag >= NUMMOBJTYPES)
 		return luaL_error(L, "mobj type %d out of range (0 - %d)", flag, NUMMOBJTYPES-1);
-- 
GitLab


From d91d9bf7fca33682eee1231f0d1d918ce3d1ff94 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Tue, 1 Aug 2023 12:57:03 +0100
Subject: [PATCH 306/518] add redflag, blueflag, rflagpoint and bflagpoint to
 Lua

---
 src/lua_script.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/lua_script.c b/src/lua_script.c
index a8263ea5fe..6a23c23bd3 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -225,6 +225,18 @@ int LUA_PushGlobals(lua_State *L, const char *word)
 	} else if (fastcmp(word,"pointlimit")) {
 		lua_pushinteger(L, cv_pointlimit.value);
 		return 1;
+	} else if (fastcmp(word, "redflag")) {
+		LUA_PushUserdata(L, redflag, META_MOBJ);
+		return 1;
+	} else if (fastcmp(word, "blueflag")) {
+		LUA_PushUserdata(L, blueflag, META_MOBJ);
+		return 1;
+	} else if (fastcmp(word, "rflagpoint")) {
+		LUA_PushUserdata(L, rflagpoint, META_MAPTHING);
+		return 1;
+	} else if (fastcmp(word, "bflagpoint")) {
+		LUA_PushUserdata(L, bflagpoint, META_MAPTHING);
+		return 1;
 	// begin map vars
 	} else if (fastcmp(word,"spstage_start")) {
 		lua_pushinteger(L, spstage_start);
-- 
GitLab


From e8256e2b43561254101fb66b2e99703b8b4a4a85 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Tue, 1 Aug 2023 13:17:52 +0100
Subject: [PATCH 307/518] allow archiving/unarchiving of skin_t userdata in
 netgames

---
 src/lua_script.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/lua_script.c b/src/lua_script.c
index 6a23c23bd3..6a59820063 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -989,6 +989,7 @@ enum
 	ARCH_MAPHEADER,
 	ARCH_SKINCOLOR,
 	ARCH_MOUSE,
+	ARCH_SKIN,
 
 	ARCH_TEND=0xFF,
 };
@@ -1017,6 +1018,7 @@ static const struct {
 	{META_MAPHEADER,   ARCH_MAPHEADER},
 	{META_SKINCOLOR,   ARCH_SKINCOLOR},
 	{META_MOUSE,    ARCH_MOUSE},
+	{META_SKIN,     ARCH_SKIN},
 	{NULL,          ARCH_NULL}
 };
 
@@ -1338,6 +1340,13 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
 			WRITEUINT8(save_p, m == &mouse ? 1 : 2);
 			break;
 		}
+		case ARCH_SKIN:
+		{
+			skin_t *skin = *((skin_t **)lua_touserdata(gL, myindex));
+			WRITEUINT8(save_p, ARCH_SKIN);
+			WRITEUINT8(save_p, skin - skins); // UINT8 because MAXSKINS is only 32
+			break;
+		}
 		default:
 			WRITEUINT8(save_p, ARCH_NULL);
 			return 2;
@@ -1584,6 +1593,9 @@ static UINT8 UnArchiveValue(int TABLESINDEX)
 	case ARCH_MOUSE:
 		LUA_PushUserdata(gL, READUINT16(save_p) == 1 ? &mouse : &mouse2, META_MOUSE);
 		break;
+	case ARCH_SKIN:
+		LUA_PushUserdata(gL, &skins[READUINT8(save_p)], META_SKIN);
+		break;
 	case ARCH_TEND:
 		return 1;
 	}
-- 
GitLab


From b2441114e8da63b442386f8577494c3d215a9f02 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Tue, 1 Aug 2023 18:24:07 +0200
Subject: [PATCH 308/518] Reapply recent netcode changes

---
 src/g_demo.c                    |   4 +-
 src/netcode/client_connection.c |  34 +-
 src/netcode/client_connection.h |   2 +-
 src/netcode/commands.c          |   2 +-
 src/netcode/commands.h          |   2 +-
 src/netcode/d_clisrv.c          | 270 +++++++++++---
 src/netcode/d_clisrv.h          |   8 +-
 src/netcode/d_net.c             |  33 +-
 src/netcode/d_net.h             |   2 +-
 src/netcode/d_netcmd.c          | 600 +++++++++++++++++---------------
 src/netcode/d_netcmd.h          |  14 +-
 src/netcode/d_netfil.c          |   4 +-
 src/netcode/d_netfil.h          |   2 +-
 src/netcode/gamestate.c         |   5 +-
 src/netcode/gamestate.h         |   2 +-
 src/netcode/http-mserv.c        |  30 +-
 src/netcode/i_addrinfo.c        |   2 +-
 src/netcode/i_addrinfo.h        |   2 +-
 src/netcode/i_net.h             |  13 +-
 src/netcode/i_tcp.c             |  73 ++--
 src/netcode/i_tcp.h             |   2 +-
 src/netcode/mserv.c             |  10 +-
 src/netcode/mserv.h             |   6 +-
 src/netcode/net_command.c       |  16 +-
 src/netcode/net_command.h       |   2 +-
 src/netcode/protocol.h          |   5 +-
 src/netcode/server_connection.c |  22 +-
 src/netcode/server_connection.h |   2 +-
 src/netcode/tic_command.c       |  61 ++--
 src/netcode/tic_command.h       |   2 +-
 src/snake.c                     |  70 +++-
 src/snake.h                     |   3 +
 32 files changed, 839 insertions(+), 466 deletions(-)

diff --git a/src/g_demo.c b/src/g_demo.c
index dea80e7930..0a51143515 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -39,7 +39,7 @@
 #include "v_video.h"
 #include "lua_hook.h"
 #include "md5.h" // demo checksums
-#include "d_netfil.h" // G_CheckDemoExtraFiles
+#include "netcode/d_netfil.h" // G_CheckDemoExtraFiles
 
 boolean timingdemo; // if true, exit with report on completion
 boolean nodrawers; // for comparative timing purposes
@@ -1885,7 +1885,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
 	switch(oldversion) // demoversion
 	{
 	case DEMOVERSION: // latest always supported
-	case 0x000f: // The previous demoversions also supported 
+	case 0x000f: // The previous demoversions also supported
 	case 0x000e:
 	case 0x000d: // all that changed between then and now was longer color name
 	case 0x000c:
diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c
index d363d7d5ad..8155d1b33f 100644
--- a/src/netcode/client_connection.c
+++ b/src/netcode/client_connection.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -17,11 +17,12 @@
 #include "../d_main.h"
 #include "../f_finale.h"
 #include "../g_game.h"
-#include "../i_gamepad.h"
+#include "../g_input.h"
 #include "i_net.h"
 #include "../i_system.h"
 #include "../i_time.h"
 #include "../i_video.h"
+#include "../keys.h"
 #include "../m_menu.h"
 #include "../m_misc.h"
 #include "../snake.h"
@@ -233,6 +234,7 @@ static boolean CL_AskFileList(INT32 firstfile)
 boolean CL_SendJoin(void)
 {
 	UINT8 localplayers = 1;
+	char const *player2name;
 	if (netgame)
 		CONS_Printf(M_GetText("Sending join request...\n"));
 	netbuffer->packettype = PT_CLIENTJOIN;
@@ -250,8 +252,14 @@ boolean CL_SendJoin(void)
 	if (splitscreen)
 		CleanupPlayerName(1, cv_playername2.zstring); // 1 is a HACK? oh no
 
+	// Avoid empty string on bots to avoid softlocking in singleplayer
+	if (botingame)
+		player2name = strcmp(cv_playername.zstring, "Tails") == 0 ? "Tail" : "Tails";
+	else
+		player2name = cv_playername2.zstring;
+
 	strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME);
-	strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME);
+	strncpy(netbuffer->u.clientcfg.names[1], player2name, MAXPLAYERNAME);
 
 	return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
 }
@@ -470,9 +478,9 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
 
 static void M_ConfirmConnect(event_t *ev)
 {
-	if (ev->type == ev_keydown || ev->type == ev_gamepad_down)
+	if (ev->type == ev_keydown)
 	{
-		if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A))
+		if (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER || ev->key == KEY_JOY1)
 		{
 			if (totalfilesrequestednum > 0)
 			{
@@ -487,7 +495,7 @@ static void M_ConfirmConnect(event_t *ev)
 
 			M_ClearMenus(true);
 		}
-		else if ((ev->type == ev_keydown && (ev->key == 'n' || ev->key == KEY_ESCAPE)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_B))
+		else if (ev->key == 'n' || ev->key == KEY_ESCAPE || ev->key == KEY_JOY1 + 3)
 		{
 			cl_mode = CL_ABORTED;
 			M_ClearMenus(true);
@@ -663,7 +671,7 @@ static const char * InvalidServerReason (serverinfo_pak *info)
 		case REFUSE_SLOTS_FULL:
 			return va(
 					"Maximum players reached: %d\n" EOT,
-					info->maxplayer);
+					info->maxplayer - D_NumBots());
 		default:
 			if (info->refusereason)
 			{
@@ -893,11 +901,12 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 			// my hand has been forced and I am dearly sorry for this awful hack :vomit:
 			for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
 			{
-				G_MapEventsToControls(&events[eventtail]);
+				if (!Snake_JoyGrabber(snake, &events[eventtail]))
+					G_MapEventsToControls(&events[eventtail]);
 			}
 		}
 
-		if (gamekeydown[KEY_ESCAPE] || gamepads[0].buttons[GAMEPAD_BUTTON_B] || cl_mode == CL_ABORTED)
+		if (gamekeydown[KEY_ESCAPE] || gamekeydown[KEY_JOY1+1] || cl_mode == CL_ABORTED)
 		{
 			CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
 			M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
@@ -922,7 +931,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 		{
 			if (!snake)
 			{
-				F_MenuPresTicker(true); // title sky
+				F_MenuPresTicker(); // title sky
 				F_TitleScreenTicker(true);
 				F_TitleScreenDrawer();
 			}
@@ -1020,6 +1029,9 @@ void CL_ConnectToServer(void)
 	}
 	while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes))));
 
+	if (netgame)
+		F_StartWaitingPlayers();
+
 	DEBFILE(va("Synchronisation Finished\n"));
 
 	displayplayer = consoleplayer;
@@ -1136,6 +1148,8 @@ void PT_ServerCFG(SINT8 node)
 		maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
 		G_SetGametype(netbuffer->u.servercfg.gametype);
 		modifiedgame = netbuffer->u.servercfg.modifiedgame;
+		if (netbuffer->u.servercfg.usedCheats)
+			G_SetUsedCheats(true);
 		memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
 	}
 
diff --git a/src/netcode/client_connection.h b/src/netcode/client_connection.h
index 74cff61fff..4d75160d4a 100644
--- a/src/netcode/client_connection.h
+++ b/src/netcode/client_connection.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/netcode/commands.c b/src/netcode/commands.c
index 4d9a48b6bc..4228027d2a 100644
--- a/src/netcode/commands.c
+++ b/src/netcode/commands.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/netcode/commands.h b/src/netcode/commands.h
index 5ff4d1cae6..d328114eec 100644
--- a/src/netcode/commands.h
+++ b/src/netcode/commands.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index 18eae580cd..f06192f2c8 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -25,8 +25,6 @@
 #include "../st_stuff.h"
 #include "../hu_stuff.h"
 #include "../keys.h"
-#include "../g_input.h"
-#include "../i_gamepad.h"
 #include "../m_menu.h"
 #include "../console.h"
 #include "d_netfil.h"
@@ -34,7 +32,6 @@
 #include "../p_saveg.h"
 #include "../z_zone.h"
 #include "../p_local.h"
-#include "../p_haptic.h"
 #include "../m_misc.h"
 #include "../am_map.h"
 #include "../m_random.h"
@@ -103,6 +100,8 @@ boolean acceptnewnode = true;
 
 UINT16 software_MAXPACKETLENGTH;
 
+static tic_t gametime = 0;
+
 static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}};
 consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL);
 
@@ -114,6 +113,8 @@ consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_
 static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}};
 consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL);
 
+consvar_t cv_dedicatedidletime = CVAR_INIT ("dedicatedidletime", "10", CV_SAVE, CV_Unsigned, NULL);
+
 void ResetNode(INT32 node)
 {
 	memset(&netnodes[node], 0, sizeof(*netnodes));
@@ -210,14 +211,13 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 
 		if (server && I_GetNodeAddress)
 		{
+			char addressbuffer[64];
 			const char *address = I_GetNodeAddress(node);
-			char *port = NULL;
 			if (address) // MI: fix msvcrt.dll!_mbscat crash?
 			{
-				strcpy(playeraddress[newplayernum], address);
-				port = strchr(playeraddress[newplayernum], ':');
-				if (port)
-					*port = '\0';
+				strcpy(addressbuffer, address);
+				strcpy(playeraddress[newplayernum],
+						I_NetSplitAddress(addressbuffer, NULL));
 			}
 		}
 	}
@@ -744,6 +744,9 @@ void SV_ResetServer(void)
 
 	CV_RevertNetVars();
 
+	// Ensure synched when creating a new server
+	M_CopyGameData(serverGamedata, clientGamedata);
+
 	DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
 }
 
@@ -997,6 +1000,45 @@ static void PT_Ping(SINT8 node, INT32 netconsole)
 	}
 }
 
+static void PT_BasicKeepAlive(SINT8 node, INT32 netconsole)
+{
+	if (client)
+		return;
+
+	// This should probably still timeout though, as the node should always have a player 1 number
+	if (netconsole == -1)
+		return;
+
+	// If a client sends this it should mean they are done receiving the savegame
+	netnodes[node].sendingsavegame = false;
+
+	// As long as clients send keep alives, the server can keep running, so reset the timeout
+	/// \todo Use a separate cvar for that kind of timeout?
+	netnodes[node].freezetimeout = I_GetTime() + connectiontimeout;
+	return;
+}
+
+// Confusing, but this DOESN'T send PT_NODEKEEPALIVE, it sends PT_BASICKEEPALIVE
+// Used during wipes to tell the server that a node is still connected
+static void CL_SendClientKeepAlive(void)
+{
+	netbuffer->packettype = PT_BASICKEEPALIVE;
+
+	HSendPacket(servernode, false, 0, 0);
+}
+
+static void SV_SendServerKeepAlive(void)
+{
+	for (INT32 n = 1; n < MAXNETNODES; n++)
+	{
+		if (netnodes[n].ingame)
+		{
+			netbuffer->packettype = PT_BASICKEEPALIVE;
+			HSendPacket(n, false, 0, 0);
+		}
+	}
+}
+
 /** Handles a packet received from a node that isn't in game
   *
   * \param node The packet sender
@@ -1052,6 +1094,7 @@ static void HandlePacketFromPlayer(SINT8 node)
 		netconsole = 0;
 	else
 		netconsole = netnodes[node].player;
+
 #ifdef PARANOIA
 	if (netconsole >= MAXPLAYERS)
 		I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
@@ -1068,6 +1111,7 @@ static void HandlePacketFromPlayer(SINT8 node)
 		case PT_NODEKEEPALIVEMIS:
 			PT_ClientCmd(node, netconsole);
 			break;
+		case PT_BASICKEEPALIVE     : PT_BasicKeepAlive     (node, netconsole); break;
 		case PT_TEXTCMD            : PT_TextCmd            (node, netconsole); break;
 		case PT_TEXTCMD2           : PT_TextCmd            (node, netconsole); break;
 		case PT_LOGIN              : PT_Login              (node, netconsole); break;
@@ -1209,9 +1253,76 @@ boolean TryRunTics(tic_t realtics)
 	}
 }
 
+static void UpdatePingTable(void)
+{
+	if (server)
+	{
+		if (netgame && !(gametime % 35)) // update once per second.
+			PingUpdate();
+		// update node latency values so we can take an average later.
+		for (INT32 i = 0; i < MAXPLAYERS; i++)
+			if (playeringame[i] && playernode[i] != UINT8_MAX)
+				realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
+		pingmeasurecount++;
+	}
+}
+
+// Handle timeouts to prevent definitive freezes from happenning
+static void HandleNodeTimeouts(void)
+{
+	if (server)
+	{
+		for (INT32 i = 1; i < MAXNETNODES; i++)
+			if (netnodes[i].ingame && netnodes[i].freezetimeout < I_GetTime())
+				Net_ConnectionTimeout(i);
+
+		// In case the cvar value was lowered
+		if (joindelay)
+			joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE);
+	}
+}
+
+// Keep the network alive while not advancing tics!
+void NetKeepAlive(void)
+{
+	tic_t nowtime;
+	INT32 realtics;
+
+	nowtime = I_GetTime();
+	realtics = nowtime - gametime;
+
+	// return if there's no time passed since the last call
+	if (realtics <= 0) // nothing new to update
+		return;
+
+	UpdatePingTable();
+
+	GetPackets();
+
+#ifdef MASTERSERVER
+	MasterClient_Ticker();
+#endif
+
+	if (client)
+	{
+		// send keep alive
+		CL_SendClientKeepAlive();
+		// No need to check for resynch because we aren't running any tics
+	}
+	else
+	{
+		SV_SendServerKeepAlive();
+	}
+
+	// No else because no tics are being run and we can't resynch during this
+
+	Net_AckTicker();
+	HandleNodeTimeouts();
+	FileSendTicker();
+}
+
 void NetUpdate(void)
 {
-	static tic_t gametime = 0;
 	static tic_t resptime = 0;
 	tic_t nowtime;
 	INT32 realtics;
@@ -1221,6 +1332,7 @@ void NetUpdate(void)
 
 	if (realtics <= 0) // nothing new to update
 		return;
+
 	if (realtics > 5)
 	{
 		if (server)
@@ -1229,18 +1341,73 @@ 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;
+ 	}
+
 	gametime = nowtime;
 
-	if (server)
-	{
-		if (netgame && !(gametime % 35)) // update once per second.
-			PingUpdate();
-		// update node latency values so we can take an average later.
-		for (INT32 i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i] && playernode[i] != UINT8_MAX)
-				realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
-		pingmeasurecount++;
-	}
+	UpdatePingTable();
 
 	if (client)
 		maketic = neededtic;
@@ -1270,17 +1437,18 @@ void NetUpdate(void)
 	}
 	else
 	{
-		if (!demoplayback)
+		if (!demoplayback && realtics > 0)
 		{
 			hu_redownloadinggamestate = false;
 
 			firstticstosend = gametic;
 			for (INT32 i = 0; i < MAXNETNODES; i++)
-				if (netnodes[i].ingame && netnodes[i].tic < firstticstosend)
+				if (netnodes[i].ingame)
 				{
-					firstticstosend = netnodes[i].tic;
+					if (netnodes[i].tic < firstticstosend)
+						firstticstosend = netnodes[i].tic;
 
-					if (maketic + 1 >= netnodes[i].tic + BACKUPTICS)
+					if (maketic + realtics >= netnodes[i].tic + BACKUPTICS - TICRATE)
 						Net_ConnectionTimeout(i);
 				}
 
@@ -1302,20 +1470,10 @@ void NetUpdate(void)
 	}
 
 	Net_AckTicker();
-
-	// Handle timeouts to prevent definitive freezes from happenning
-	if (server)
-	{
-		for (INT32 i = 1; i < MAXNETNODES; i++)
-			if (netnodes[i].ingame && netnodes[i].freezetimeout < I_GetTime())
-				Net_ConnectionTimeout(i);
-
-		// In case the cvar value was lowered
-		if (joindelay)
-			joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE);
-	}
+	HandleNodeTimeouts();
 
 	nowtime /= NEWTICRATERATIO;
+
 	if (nowtime > resptime)
 	{
 		resptime = nowtime;
@@ -1338,22 +1496,22 @@ void D_ClientServerInit(void)
 	DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n",
 		VERSION/100, VERSION%100, SUBVERSION));
 
-	COM_AddCommand("getplayernum", Command_GetPlayerNum);
-	COM_AddCommand("kick", Command_Kick);
-	COM_AddCommand("ban", Command_Ban);
-	COM_AddCommand("banip", Command_BanIP);
-	COM_AddCommand("clearbans", Command_ClearBans);
-	COM_AddCommand("showbanlist", Command_ShowBan);
-	COM_AddCommand("reloadbans", Command_ReloadBan);
-	COM_AddCommand("connect", Command_connect);
-	COM_AddCommand("nodes", Command_Nodes);
-	COM_AddCommand("resendgamestate", Command_ResendGamestate);
+	COM_AddCommand("getplayernum", Command_GetPlayerNum, COM_LUA);
+	COM_AddCommand("kick", Command_Kick, COM_LUA);
+	COM_AddCommand("ban", Command_Ban, COM_LUA);
+	COM_AddCommand("banip", Command_BanIP, COM_LUA);
+	COM_AddCommand("clearbans", Command_ClearBans, COM_LUA);
+	COM_AddCommand("showbanlist", Command_ShowBan, COM_LUA);
+	COM_AddCommand("reloadbans", Command_ReloadBan, COM_LUA);
+	COM_AddCommand("connect", Command_connect, COM_LUA);
+	COM_AddCommand("nodes", Command_Nodes, COM_LUA);
+	COM_AddCommand("resendgamestate", Command_ResendGamestate, COM_LUA);
 #ifdef PACKETDROP
-	COM_AddCommand("drop", Command_Drop);
-	COM_AddCommand("droprate", Command_Droprate);
+	COM_AddCommand("drop", Command_Drop, COM_LUA);
+	COM_AddCommand("droprate", Command_Droprate, COM_LUA);
 #endif
 #ifdef _DEBUG
-	COM_AddCommand("numnodes", Command_Numnodes);
+	COM_AddCommand("numnodes", Command_Numnodes, COM_LUA);
 #endif
 
 	RegisterNetXCmd(XD_KICK, Got_KickCmd);
@@ -1422,6 +1580,20 @@ INT32 D_NumPlayers(void)
 	return num;
 }
 
+/** Similar to the above, but counts only bots.
+  * Purpose is to remove bots from both the player count and the
+  * max player count on the server view
+*/
+INT32 D_NumBots(void)
+{
+	INT32 num = 0, ix;
+	for (ix = 0; ix < MAXPLAYERS; ix++)
+		if (playeringame[ix] && players[ix].bot)
+			num++;
+	return num;
+}
+
+
 //
 // Consistancy
 //
diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h
index 0abd638ce9..d87ead9ecb 100644
--- a/src/netcode/d_clisrv.h
+++ b/src/netcode/d_clisrv.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -73,7 +73,7 @@ extern UINT32 realpingtable[MAXPLAYERS];
 extern UINT32 playerpingtable[MAXPLAYERS];
 extern tic_t servermaxping;
 
-extern consvar_t cv_netticbuffer,  cv_resynchattempts, cv_blamecfail, cv_playbackspeed;
+extern consvar_t cv_netticbuffer, cv_resynchattempts, cv_blamecfail, cv_playbackspeed, cv_dedicatedidletime;
 
 // Used in d_net, the only dependence
 void D_ClientServerInit(void);
@@ -81,6 +81,9 @@ void D_ClientServerInit(void);
 // Create any new ticcmds and broadcast to other players.
 void NetUpdate(void);
 
+// Maintain connections to nodes without timing them all out.
+void NetKeepAlive(void);
+
 void GetPackets(void);
 void ResetNode(INT32 node);
 INT16 Consistancy(void);
@@ -118,6 +121,7 @@ extern char motd[254], server_context[8];
 extern UINT8 playernode[MAXPLAYERS];
 
 INT32 D_NumPlayers(void);
+INT32 D_NumBots(void);
 
 tic_t GetLag(INT32 node);
 
diff --git a/src/netcode/d_net.c b/src/netcode/d_net.c
index a4b0778e36..cfb1963b9f 100644
--- a/src/netcode/d_net.c
+++ b/src/netcode/d_net.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -760,6 +760,8 @@ static const char *packettypename[NUMPACKETTYPE] =
 	"ASKLUAFILE",
 	"HASLUAFILE",
 
+	"PT_BASICKEEPALIVE",
+
 	"FILEFRAGMENT",
 	"FILEACK",
 	"FILERECEIVED",
@@ -818,6 +820,9 @@ static void DebugPrintpacket(const char *header)
 				(UINT32)ExpandTics(netbuffer->u.clientpak.client_tic, doomcom->remotenode),
 				(UINT32)ExpandTics (netbuffer->u.clientpak.resendfrom, doomcom->remotenode));
 			break;
+		case PT_BASICKEEPALIVE:
+			fprintf(debugfile, "    wipetime\n");
+			break;
 		case PT_TEXTCMD:
 		case PT_TEXTCMD2:
 			fprintf(debugfile, "    length %d\n    ", netbuffer->u.textcmd[0]);
@@ -1140,26 +1145,32 @@ static void Internal_FreeNodenum(INT32 nodenum)
 	(void)nodenum;
 }
 
+char *I_NetSplitAddress(char *host, char **port)
+{
+	boolean v4 = (strchr(host, '.') != NULL);
+
+	host = strtok(host, v4 ? ":" : "[]");
+
+	if (port)
+		*port = strtok(NULL, ":");
+
+	return host;
+}
+
 SINT8 I_NetMakeNode(const char *hostname)
 {
 	SINT8 newnode = -1;
 	if (I_NetMakeNodewPort)
 	{
 		char *localhostname = strdup(hostname);
-		char  *t = localhostname;
-		const char *port;
+		char *port;
 		if (!localhostname)
 			return newnode;
-		// retrieve portnum from address!
-		strtok(localhostname, ":");
-		port = strtok(NULL, ":");
 
-		// remove the port in the hostname as we've it already
-		while ((*t != ':') && (*t != '\0'))
-			t++;
-		*t = '\0';
+		// retrieve portnum from address!
+		hostname = I_NetSplitAddress(localhostname, &port);
 
-		newnode = I_NetMakeNodewPort(localhostname, port);
+		newnode = I_NetMakeNodewPort(hostname, port);
 		free(localhostname);
 	}
 	return newnode;
diff --git a/src/netcode/d_net.h b/src/netcode/d_net.h
index 039f5b3b48..549f2b93c1 100644
--- a/src/netcode/d_net.h
+++ b/src/netcode/d_net.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c
index 0d17855105..8f5b433bc5 100644
--- a/src/netcode/d_netcmd.c
+++ b/src/netcode/d_netcmd.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -21,7 +21,6 @@
 #include "../g_game.h"
 #include "../hu_stuff.h"
 #include "../g_input.h"
-#include "../i_gamepad.h"
 #include "../m_menu.h"
 #include "../r_local.h"
 #include "../r_skins.h"
@@ -52,6 +51,7 @@
 #include "../m_anigif.h"
 #include "../md5.h"
 #include "../m_perfstats.h"
+#include "../u_list.h"
 
 #ifdef NETGAME_DEVMODE
 #define CV_RESTRICT CV_NETVAR
@@ -184,6 +184,14 @@ static CV_PossibleValue_t mouse2port_cons_t[] = {{1, "COM1"}, {2, "COM2"}, {3, "
 	{0, NULL}};
 #endif
 
+#ifdef LJOYSTICK
+static CV_PossibleValue_t joyport_cons_t[] = {{1, "/dev/js0"}, {2, "/dev/js1"}, {3, "/dev/js2"},
+	{4, "/dev/js3"}, {0, NULL}};
+#else
+// accept whatever value - it is in fact the joystick device number
+#define usejoystick_cons_t NULL
+#endif
+
 static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}};
 
 static CV_PossibleValue_t startingliveslimit_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}};
@@ -197,37 +205,37 @@ static CV_PossibleValue_t matchboxes_cons_t[] = {{0, "Normal"}, {1, "Mystery"},
 static CV_PossibleValue_t chances_cons_t[] = {{0, "MIN"}, {9, "MAX"}, {0, NULL}};
 static CV_PossibleValue_t pause_cons_t[] = {{0, "Server"}, {1, "All"}, {0, NULL}};
 
-consvar_t cv_showinputjoy = CVAR_INIT ("showinputjoy", "Off", 0, CV_OnOff, NULL);
+consvar_t cv_showinputjoy = CVAR_INIT ("showinputjoy", "Off", CV_ALLOWLUA, CV_OnOff, NULL);
 
 #ifdef NETGAME_DEVMODE
 static consvar_t cv_fishcake = CVAR_INIT ("fishcake", "Off", CV_CALL|CV_NOSHOWHELP|CV_RESTRICT, CV_OnOff, Fishcake_OnChange);
 #endif
 static consvar_t cv_dummyconsvar = CVAR_INIT ("dummyconsvar", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, DummyConsvar_OnChange);
 
-consvar_t cv_restrictskinchange = CVAR_INIT ("restrictskinchange", "Yes", CV_SAVE|CV_NETVAR|CV_CHEAT, CV_YesNo, NULL);
-consvar_t cv_allowteamchange = CVAR_INIT ("allowteamchange", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL);
+consvar_t cv_restrictskinchange = CVAR_INIT ("restrictskinchange", "Yes", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, CV_YesNo, NULL);
+consvar_t cv_allowteamchange = CVAR_INIT ("allowteamchange", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
 
-consvar_t cv_startinglives = CVAR_INIT ("startinglives", "3", CV_SAVE|CV_NETVAR|CV_CHEAT, startingliveslimit_cons_t, NULL);
+consvar_t cv_startinglives = CVAR_INIT ("startinglives", "3", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, startingliveslimit_cons_t, NULL);
 
 static CV_PossibleValue_t respawntime_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "Off"}, {0, NULL}};
-consvar_t cv_respawntime = CVAR_INIT ("respawndelay", "3", CV_SAVE|CV_NETVAR|CV_CHEAT, respawntime_cons_t, NULL);
+consvar_t cv_respawntime = CVAR_INIT ("respawndelay", "3", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, respawntime_cons_t, NULL);
 
-consvar_t cv_competitionboxes = CVAR_INIT ("competitionboxes", "Mystery", CV_SAVE|CV_NETVAR|CV_CHEAT, competitionboxes_cons_t, NULL);
+consvar_t cv_competitionboxes = CVAR_INIT ("competitionboxes", "Mystery", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, competitionboxes_cons_t, NULL);
 
 static CV_PossibleValue_t seenames_cons_t[] = {{0, "Off"}, {1, "Colorless"}, {2, "Team"}, {3, "Ally/Foe"}, {0, NULL}};
-consvar_t cv_seenames = CVAR_INIT ("seenames", "Ally/Foe", CV_SAVE, seenames_cons_t, 0);
-consvar_t cv_allowseenames = CVAR_INIT ("allowseenames", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL);
+consvar_t cv_seenames = CVAR_INIT ("seenames", "Ally/Foe", CV_SAVE|CV_ALLOWLUA, seenames_cons_t, 0);
+consvar_t cv_allowseenames = CVAR_INIT ("allowseenames", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
 
 // names
 consvar_t cv_playername = CVAR_INIT ("name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange);
 consvar_t cv_playername2 = CVAR_INIT ("name2", "Tails", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name2_OnChange);
 // player colors
 UINT16 lastgoodcolor = SKINCOLOR_BLUE, lastgoodcolor2 = SKINCOLOR_BLUE;
-consvar_t cv_playercolor = CVAR_INIT ("color", "Blue", CV_CALL|CV_NOINIT, Color_cons_t, Color_OnChange);
-consvar_t cv_playercolor2 = CVAR_INIT ("color2", "Orange", CV_CALL|CV_NOINIT, Color_cons_t, Color2_OnChange);
+consvar_t cv_playercolor = CVAR_INIT ("color", "Blue", CV_CALL|CV_NOINIT|CV_ALLOWLUA, Color_cons_t, Color_OnChange);
+consvar_t cv_playercolor2 = CVAR_INIT ("color2", "Orange", CV_CALL|CV_NOINIT|CV_ALLOWLUA, Color_cons_t, Color2_OnChange);
 // player's skin, saved for commodity, when using a favorite skins wad..
-consvar_t cv_skin = CVAR_INIT ("skin", DEFAULTSKIN, CV_CALL|CV_NOINIT, NULL, Skin_OnChange);
-consvar_t cv_skin2 = CVAR_INIT ("skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT, NULL, Skin2_OnChange);
+consvar_t cv_skin = CVAR_INIT ("skin", DEFAULTSKIN, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin_OnChange);
+consvar_t cv_skin2 = CVAR_INIT ("skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin2_OnChange);
 
 // saved versions of the above six
 consvar_t cv_defaultplayercolor = CVAR_INIT ("defaultcolor", "Blue", CV_SAVE, Color_cons_t, NULL);
@@ -242,61 +250,19 @@ INT32 cv_debug;
 consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse);
 consvar_t cv_usemouse2 = CVAR_INIT ("use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2);
 
-// We use cv_usegamepad.string as the USER-SET var
-// and cv_usegamepad.value as the INTERNAL var
-//
-// In practice, if cv_usegamepad.string == 0, this overrides
-// cv_usegamepad.value and always disables
-
-static void UseGamepad_OnChange(void)
-{
-	I_ChangeGamepad(0);
-}
-
-static void UseGamepad2_OnChange(void)
-{
-	I_ChangeGamepad(1);
-}
-
-consvar_t cv_usegamepad[2] = {
-	CVAR_INIT ("use_gamepad", "1", CV_SAVE|CV_CALL, NULL, UseGamepad_OnChange),
-	CVAR_INIT ("use_gamepad2", "2", CV_SAVE|CV_CALL, NULL, UseGamepad2_OnChange)
-};
-
-static void PadScale_OnChange(void)
-{
-	I_SetGamepadDigital(0, cv_gamepad_scale[0].value == 0);
-}
-
-static void PadScale2_OnChange(void)
-{
-	I_SetGamepadDigital(1, cv_gamepad_scale[1].value == 0);
-}
-
-consvar_t cv_gamepad_scale[2] = {
-	CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, PadScale_OnChange),
-	CVAR_INIT ("padscale2", "1", CV_SAVE|CV_CALL, NULL, PadScale2_OnChange)
-};
-
-static void PadRumble_OnChange(void)
-{
-	if (!cv_gamepad_rumble[0].value)
-		I_StopGamepadRumble(0);
-}
-
-static void PadRumble2_OnChange(void)
-{
-	if (!cv_gamepad_rumble[1].value)
-		I_StopGamepadRumble(1);
-}
-
-consvar_t cv_gamepad_rumble[2] = {
-	CVAR_INIT ("padrumble", "Off", CV_SAVE|CV_CALL, CV_OnOff, PadRumble_OnChange),
-	CVAR_INIT ("padrumble2", "Off", CV_SAVE|CV_CALL, CV_OnOff, PadRumble2_OnChange)
-};
-
-consvar_t cv_gamepad_autopause = CVAR_INIT ("pauseongamepaddisconnect", "On", CV_SAVE, CV_OnOff, NULL);
-
+consvar_t cv_usejoystick = CVAR_INIT ("use_gamepad", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick);
+consvar_t cv_usejoystick2 = CVAR_INIT ("use_gamepad2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick2);
+#if (defined (LJOYSTICK) || defined (HAVE_SDL))
+#ifdef LJOYSTICK
+consvar_t cv_joyport = CVAR_INIT ("padport", "/dev/js0", CV_SAVE, joyport_cons_t, NULL);
+consvar_t cv_joyport2 = CVAR_INIT ("padport2", "/dev/js0", CV_SAVE, joyport_cons_t, NULL); //Alam: for later
+#endif
+consvar_t cv_joyscale = CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale);
+consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2);
+#else
+consvar_t cv_joyscale = CVAR_INIT ("padscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save
+consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save
+#endif
 #if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON)
 consvar_t cv_mouse2port = CVAR_INIT ("mouse2port", "/dev/gpmdata", CV_SAVE, mouse2port_cons_t, NULL);
 consvar_t cv_mouse2opt = CVAR_INIT ("mouse2opt", "0", CV_SAVE, NULL, NULL);
@@ -304,43 +270,43 @@ consvar_t cv_mouse2opt = CVAR_INIT ("mouse2opt", "0", CV_SAVE, NULL, NULL);
 consvar_t cv_mouse2port = CVAR_INIT ("mouse2port", "COM2", CV_SAVE, mouse2port_cons_t, NULL);
 #endif
 
-consvar_t cv_matchboxes = CVAR_INIT ("matchboxes", "Normal", CV_SAVE|CV_NETVAR|CV_CHEAT, matchboxes_cons_t, NULL);
-consvar_t cv_specialrings = CVAR_INIT ("specialrings", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
-consvar_t cv_powerstones = CVAR_INIT ("powerstones", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
-
-consvar_t cv_recycler =      CVAR_INIT ("tv_recycler",      "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-consvar_t cv_teleporters =   CVAR_INIT ("tv_teleporter",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-consvar_t cv_superring =     CVAR_INIT ("tv_superring",     "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-consvar_t cv_supersneakers = CVAR_INIT ("tv_supersneaker",  "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-consvar_t cv_invincibility = CVAR_INIT ("tv_invincibility", "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-consvar_t cv_jumpshield =    CVAR_INIT ("tv_jumpshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-consvar_t cv_watershield =   CVAR_INIT ("tv_watershield",   "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-consvar_t cv_ringshield =    CVAR_INIT ("tv_ringshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-consvar_t cv_forceshield =   CVAR_INIT ("tv_forceshield",   "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-consvar_t cv_bombshield =    CVAR_INIT ("tv_bombshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-consvar_t cv_1up =           CVAR_INIT ("tv_1up",           "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-consvar_t cv_eggmanbox =     CVAR_INIT ("tv_eggman",        "5", CV_SAVE|CV_NETVAR|CV_CHEAT, chances_cons_t, NULL);
-
-consvar_t cv_ringslinger = CVAR_INIT ("ringslinger", "No", CV_NETVAR|CV_NOSHOWHELP|CV_CALL|CV_CHEAT, CV_YesNo, Ringslinger_OnChange);
-consvar_t cv_gravity = CVAR_INIT ("gravity", "0.5", CV_RESTRICT|CV_FLOAT|CV_CALL, NULL, Gravity_OnChange);
+consvar_t cv_matchboxes = CVAR_INIT ("matchboxes", "Normal", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, matchboxes_cons_t, NULL);
+consvar_t cv_specialrings = CVAR_INIT ("specialrings", "On", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
+consvar_t cv_powerstones = CVAR_INIT ("powerstones", "On", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
+
+consvar_t cv_recycler =      CVAR_INIT ("tv_recycler",      "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+consvar_t cv_teleporters =   CVAR_INIT ("tv_teleporter",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+consvar_t cv_superring =     CVAR_INIT ("tv_superring",     "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+consvar_t cv_supersneakers = CVAR_INIT ("tv_supersneaker",  "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+consvar_t cv_invincibility = CVAR_INIT ("tv_invincibility", "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+consvar_t cv_jumpshield =    CVAR_INIT ("tv_jumpshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+consvar_t cv_watershield =   CVAR_INIT ("tv_watershield",   "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+consvar_t cv_ringshield =    CVAR_INIT ("tv_ringshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+consvar_t cv_forceshield =   CVAR_INIT ("tv_forceshield",   "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+consvar_t cv_bombshield =    CVAR_INIT ("tv_bombshield",    "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+consvar_t cv_1up =           CVAR_INIT ("tv_1up",           "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+consvar_t cv_eggmanbox =     CVAR_INIT ("tv_eggman",        "5", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, chances_cons_t, NULL);
+
+consvar_t cv_ringslinger = CVAR_INIT ("ringslinger", "No", CV_NETVAR|CV_NOSHOWHELP|CV_CALL|CV_CHEAT|CV_ALLOWLUA, CV_YesNo, Ringslinger_OnChange);
+consvar_t cv_gravity = CVAR_INIT ("gravity", "0.5", CV_RESTRICT|CV_FLOAT|CV_CALL|CV_ALLOWLUA, NULL, Gravity_OnChange);
 
 consvar_t cv_soundtest = CVAR_INIT ("soundtest", "0", CV_CALL, NULL, SoundTest_OnChange);
 
 static CV_PossibleValue_t minitimelimit_cons_t[] = {{1, "MIN"}, {9999, "MAX"}, {0, NULL}};
-consvar_t cv_countdowntime = CVAR_INIT ("countdowntime", "60", CV_SAVE|CV_NETVAR|CV_CHEAT, minitimelimit_cons_t, NULL);
+consvar_t cv_countdowntime = CVAR_INIT ("countdowntime", "60", CV_SAVE|CV_NETVAR|CV_CHEAT|CV_ALLOWLUA, minitimelimit_cons_t, NULL);
 
-consvar_t cv_touchtag = CVAR_INIT ("touchtag", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
-consvar_t cv_hidetime = CVAR_INIT ("hidetime", "30", CV_SAVE|CV_NETVAR|CV_CALL, minitimelimit_cons_t, Hidetime_OnChange);
+consvar_t cv_touchtag = CVAR_INIT ("touchtag", "Off", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
+consvar_t cv_hidetime = CVAR_INIT ("hidetime", "30", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, minitimelimit_cons_t, Hidetime_OnChange);
 
-consvar_t cv_autobalance = CVAR_INIT ("autobalance", "Off", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, AutoBalance_OnChange);
-consvar_t cv_teamscramble = CVAR_INIT ("teamscramble", "Off", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, teamscramble_cons_t, TeamScramble_OnChange);
-consvar_t cv_scrambleonchange = CVAR_INIT ("scrambleonchange", "Off", CV_SAVE|CV_NETVAR, teamscramble_cons_t, NULL);
+consvar_t cv_autobalance = CVAR_INIT ("autobalance", "Off", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, CV_OnOff, AutoBalance_OnChange);
+consvar_t cv_teamscramble = CVAR_INIT ("teamscramble", "Off", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, teamscramble_cons_t, TeamScramble_OnChange);
+consvar_t cv_scrambleonchange = CVAR_INIT ("scrambleonchange", "Off", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, teamscramble_cons_t, NULL);
 
-consvar_t cv_friendlyfire = CVAR_INIT ("friendlyfire", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
-consvar_t cv_itemfinder = CVAR_INIT ("itemfinder", "Off", CV_CALL, CV_OnOff, ItemFinder_OnChange);
+consvar_t cv_friendlyfire = CVAR_INIT ("friendlyfire", "Off", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
+consvar_t cv_itemfinder = CVAR_INIT ("itemfinder", "Off", CV_CALL|CV_ALLOWLUA, CV_OnOff, ItemFinder_OnChange);
 
 // Scoring type options
-consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL);
+consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
 
 consvar_t cv_rollingdemos = CVAR_INIT ("rollingdemos", "On", CV_SAVE, CV_OnOff, NULL);
 
@@ -351,13 +317,13 @@ static CV_PossibleValue_t powerupdisplay_cons_t[] = {{0, "Never"}, {1, "First-pe
 consvar_t cv_powerupdisplay = CVAR_INIT ("powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL);
 
 static CV_PossibleValue_t pointlimit_cons_t[] = {{1, "MIN"}, {MAXSCORE, "MAX"}, {0, "None"}, {0, NULL}};
-consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange);
+consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, pointlimit_cons_t, PointLimit_OnChange);
 static CV_PossibleValue_t timelimit_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "None"}, {0, NULL}};
-consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, TimeLimit_OnChange);
+consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, timelimit_cons_t, TimeLimit_OnChange);
 static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, NULL}};
-consvar_t cv_numlaps = CVAR_INIT ("numlaps", "4", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_cons_t, NumLaps_OnChange);
+consvar_t cv_numlaps = CVAR_INIT ("numlaps", "4", CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, numlaps_cons_t, NumLaps_OnChange);
 static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, "Map default"}, {0, NULL}};
-consvar_t cv_basenumlaps = CVAR_INIT ("basenumlaps", "Map default", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, basenumlaps_cons_t, BaseNumLaps_OnChange);
+consvar_t cv_basenumlaps = CVAR_INIT ("basenumlaps", "Map default", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT|CV_ALLOWLUA, basenumlaps_cons_t, BaseNumLaps_OnChange);
 
 // Point and time limits for every gametype
 INT32 pointlimits[NUMGAMETYPES];
@@ -366,11 +332,11 @@ INT32 timelimits[NUMGAMETYPES];
 // log elemental hazards -- not a netvar, is local to current player
 consvar_t cv_hazardlog = CVAR_INIT ("hazardlog", "Yes", 0, CV_YesNo, NULL);
 
-consvar_t cv_forceskin = CVAR_INIT ("forceskin", "None", CV_NETVAR|CV_CALL|CV_CHEAT, NULL, ForceSkin_OnChange);
+consvar_t cv_forceskin = CVAR_INIT ("forceskin", "None", CV_NETVAR|CV_CALL|CV_CHEAT|CV_ALLOWLUA, NULL, ForceSkin_OnChange);
 consvar_t cv_downloading = CVAR_INIT ("downloading", "On", 0, CV_OnOff, NULL);
-consvar_t cv_allowexitlevel = CVAR_INIT ("allowexitlevel", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL);
+consvar_t cv_allowexitlevel = CVAR_INIT ("allowexitlevel", "No", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
 
-consvar_t cv_killingdead = CVAR_INIT ("killingdead", "Off", CV_NETVAR, CV_OnOff, NULL);
+consvar_t cv_killingdead = CVAR_INIT ("killingdead", "Off", CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
 
 consvar_t cv_netstat = CVAR_INIT ("netstat", "Off", 0, CV_OnOff, NULL); // show bandwidth statistics
 static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
@@ -388,26 +354,26 @@ consvar_t cv_showping = CVAR_INIT ("showping", "Warning", CV_SAVE, showping_cons
 
 // Intermission time Tails 04-19-2002
 static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}};
-consvar_t cv_inttime = CVAR_INIT ("inttime", "10", CV_SAVE|CV_NETVAR, inttime_cons_t, NULL);
+consvar_t cv_inttime = CVAR_INIT ("inttime", "10", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, inttime_cons_t, NULL);
 
 static CV_PossibleValue_t coopstarposts_cons_t[] = {{0, "Per-player"}, {1, "Shared"}, {2, "Teamwork"}, {0, NULL}};
-consvar_t cv_coopstarposts = CVAR_INIT ("coopstarposts", "Per-player", CV_SAVE|CV_NETVAR|CV_CALL, coopstarposts_cons_t, CoopStarposts_OnChange);
+consvar_t cv_coopstarposts = CVAR_INIT ("coopstarposts", "Per-player", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, coopstarposts_cons_t, CoopStarposts_OnChange);
 
 static CV_PossibleValue_t cooplives_cons_t[] = {{0, "Infinite"}, {1, "Per-player"}, {2, "Avoid Game Over"}, {3, "Single pool"}, {0, NULL}};
-consvar_t cv_cooplives = CVAR_INIT ("cooplives", "Avoid Game Over", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, cooplives_cons_t, CoopLives_OnChange);
+consvar_t cv_cooplives = CVAR_INIT ("cooplives", "Avoid Game Over", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT|CV_ALLOWLUA, cooplives_cons_t, CoopLives_OnChange);
 
 static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {0, NULL}};
-consvar_t cv_advancemap = CVAR_INIT ("advancemap", "Next", CV_SAVE|CV_NETVAR, advancemap_cons_t, NULL);
+consvar_t cv_advancemap = CVAR_INIT ("advancemap", "Next", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, advancemap_cons_t, NULL);
 
 static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "1/4"}, {2, "Half"}, {3, "3/4"}, {4, "All"}, {0, NULL}};
-consvar_t cv_playersforexit = CVAR_INIT ("playersforexit", "All", CV_SAVE|CV_NETVAR, playersforexit_cons_t, NULL);
+consvar_t cv_playersforexit = CVAR_INIT ("playersforexit", "All", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, playersforexit_cons_t, NULL);
 
-consvar_t cv_exitmove = CVAR_INIT ("exitmove", "On", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, ExitMove_OnChange);
+consvar_t cv_exitmove = CVAR_INIT ("exitmove", "On", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, CV_OnOff, ExitMove_OnChange);
 
-consvar_t cv_runscripts = CVAR_INIT ("runscripts", "Yes", 0, CV_YesNo, NULL);
+consvar_t cv_runscripts = CVAR_INIT ("runscripts", "Yes", CV_ALLOWLUA, CV_YesNo, NULL);
 
-consvar_t cv_pause = CVAR_INIT ("pausepermission", "Server", CV_SAVE|CV_NETVAR, pause_cons_t, NULL);
-consvar_t cv_mute = CVAR_INIT ("mute", "Off", CV_NETVAR|CV_CALL, CV_OnOff, Mute_OnChange);
+consvar_t cv_pause = CVAR_INIT ("pausepermission", "Server", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, pause_cons_t, NULL);
+consvar_t cv_mute = CVAR_INIT ("mute", "Off", CV_NETVAR|CV_CALL|CV_ALLOWLUA, CV_OnOff, Mute_OnChange);
 
 consvar_t cv_sleep = CVAR_INIT ("cpusleep", "1", CV_SAVE, sleeping_cons_t, NULL);
 
@@ -500,57 +466,57 @@ void D_RegisterServerCommands(void)
 	RegisterNetXCmd(XD_LUAFILE, Got_LuaFile);
 
 	// Remote Administration
-	COM_AddCommand("password", Command_Changepassword_f);
-	COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin
-	COM_AddCommand("promote", Command_Verify_f);
+	COM_AddCommand("password", Command_Changepassword_f, COM_LUA);
+	COM_AddCommand("login", Command_Login_f, COM_LUA); // useful in dedicated to kick off remote admin
+	COM_AddCommand("promote", Command_Verify_f, COM_LUA);
 	RegisterNetXCmd(XD_VERIFIED, Got_Verification);
-	COM_AddCommand("demote", Command_RemoveAdmin_f);
+	COM_AddCommand("demote", Command_RemoveAdmin_f, COM_LUA);
 	RegisterNetXCmd(XD_DEMOTED, Got_Removal);
 
-	COM_AddCommand("motd", Command_MotD_f);
+	COM_AddCommand("motd", Command_MotD_f, COM_LUA);
 	RegisterNetXCmd(XD_SETMOTD, Got_MotD_f); // For remote admin
 
 	RegisterNetXCmd(XD_TEAMCHANGE, Got_Teamchange);
-	COM_AddCommand("serverchangeteam", Command_ServerTeamChange_f);
+	COM_AddCommand("serverchangeteam", Command_ServerTeamChange_f, COM_LUA);
 
 	RegisterNetXCmd(XD_CLEARSCORES, Got_Clearscores);
-	COM_AddCommand("clearscores", Command_Clearscores_f);
-	COM_AddCommand("map", Command_Map_f);
+	COM_AddCommand("clearscores", Command_Clearscores_f, COM_LUA);
+	COM_AddCommand("map", Command_Map_f, COM_LUA);
 
-	COM_AddCommand("exitgame", Command_ExitGame_f);
-	COM_AddCommand("retry", Command_Retry_f);
-	COM_AddCommand("exitlevel", Command_ExitLevel_f);
-	COM_AddCommand("showmap", Command_Showmap_f);
-	COM_AddCommand("mapmd5", Command_Mapmd5_f);
+	COM_AddCommand("exitgame", Command_ExitGame_f, COM_LUA);
+	COM_AddCommand("retry", Command_Retry_f, COM_LUA);
+	COM_AddCommand("exitlevel", Command_ExitLevel_f, COM_LUA);
+	COM_AddCommand("showmap", Command_Showmap_f, COM_LUA);
+	COM_AddCommand("mapmd5", Command_Mapmd5_f, COM_LUA);
 
-	COM_AddCommand("addfolder", Command_Addfolder);
-	COM_AddCommand("addfile", Command_Addfile);
-	COM_AddCommand("listwad", Command_ListWADS_f);
+	COM_AddCommand("addfolder", Command_Addfolder, COM_LUA);
+	COM_AddCommand("addfile", Command_Addfile, COM_LUA);
+	COM_AddCommand("listwad", Command_ListWADS_f, COM_LUA);
 
-	COM_AddCommand("runsoc", Command_RunSOC);
-	COM_AddCommand("pause", Command_Pause);
-	COM_AddCommand("suicide", Command_Suicide);
+	COM_AddCommand("runsoc", Command_RunSOC, COM_LUA);
+	COM_AddCommand("pause", Command_Pause, COM_LUA);
+	COM_AddCommand("suicide", Command_Suicide, COM_LUA);
 
-	COM_AddCommand("gametype", Command_ShowGametype_f);
-	COM_AddCommand("version", Command_Version_f);
+	COM_AddCommand("gametype", Command_ShowGametype_f, COM_LUA);
+	COM_AddCommand("version", Command_Version_f, COM_LUA);
 #ifdef UPDATE_ALERT
-	COM_AddCommand("mod_details", Command_ModDetails_f);
+	COM_AddCommand("mod_details", Command_ModDetails_f, COM_LUA);
 #endif
-	COM_AddCommand("quit", Command_Quit_f);
-
-	COM_AddCommand("saveconfig", Command_SaveConfig_f);
-	COM_AddCommand("loadconfig", Command_LoadConfig_f);
-	COM_AddCommand("changeconfig", Command_ChangeConfig_f);
-	COM_AddCommand("isgamemodified", Command_Isgamemodified_f); // test
-	COM_AddCommand("showscores", Command_ShowScores_f);
-	COM_AddCommand("showtime", Command_ShowTime_f);
-	COM_AddCommand("cheats", Command_Cheats_f); // test
+	COM_AddCommand("quit", Command_Quit_f, COM_LUA);
+
+	COM_AddCommand("saveconfig", Command_SaveConfig_f, 0);
+	COM_AddCommand("loadconfig", Command_LoadConfig_f, 0);
+	COM_AddCommand("changeconfig", Command_ChangeConfig_f, 0);
+	COM_AddCommand("isgamemodified", Command_Isgamemodified_f, COM_LUA); // test
+	COM_AddCommand("showscores", Command_ShowScores_f, COM_LUA);
+	COM_AddCommand("showtime", Command_ShowTime_f, COM_LUA);
+	COM_AddCommand("cheats", Command_Cheats_f, COM_LUA); // test
 #ifdef _DEBUG
-	COM_AddCommand("togglemodified", Command_Togglemodified_f);
-	COM_AddCommand("archivetest", Command_Archivetest_f);
+	COM_AddCommand("togglemodified", Command_Togglemodified_f, COM_LUA);
+	COM_AddCommand("archivetest", Command_Archivetest_f, COM_LUA);
 #endif
 
-	COM_AddCommand("downloads", Command_Downloads_f);
+	COM_AddCommand("downloads", Command_Downloads_f, COM_LUA);
 
 	// for master server connection
 	AddMServCommands();
@@ -633,8 +599,9 @@ void D_RegisterServerCommands(void)
 	CV_RegisterVar(&cv_allownewplayer);
 	CV_RegisterVar(&cv_showjoinaddress);
 	CV_RegisterVar(&cv_blamecfail);
+	CV_RegisterVar(&cv_dedicatedidletime);
 
-	COM_AddCommand("ping", Command_Ping_f);
+	COM_AddCommand("ping", Command_Ping_f, COM_LUA);
 	CV_RegisterVar(&cv_nettimeout);
 	CV_RegisterVar(&cv_jointimeout);
 
@@ -646,6 +613,10 @@ void D_RegisterServerCommands(void)
 
 	CV_RegisterVar(&cv_allowseenames);
 
+	// Other filesrch.c consvars are defined in D_RegisterClientCommands
+	CV_RegisterVar(&cv_addons_option);
+	CV_RegisterVar(&cv_addons_folder);
+
 	CV_RegisterVar(&cv_dummyconsvar);
 }
 
@@ -678,25 +649,25 @@ void D_RegisterClientCommands(void)
 	if (dedicated)
 		return;
 
-	COM_AddCommand("numthinkers", Command_Numthinkers_f);
-	COM_AddCommand("countmobjs", Command_CountMobjs_f);
+	COM_AddCommand("numthinkers", Command_Numthinkers_f, COM_LUA);
+	COM_AddCommand("countmobjs", Command_CountMobjs_f, COM_LUA);
 
-	COM_AddCommand("changeteam", Command_Teamchange_f);
-	COM_AddCommand("changeteam2", Command_Teamchange2_f);
+	COM_AddCommand("changeteam", Command_Teamchange_f, COM_LUA);
+	COM_AddCommand("changeteam2", Command_Teamchange2_f, COM_LUA);
 
-	COM_AddCommand("playdemo", Command_Playdemo_f);
-	COM_AddCommand("timedemo", Command_Timedemo_f);
-	COM_AddCommand("stopdemo", Command_Stopdemo_f);
-	COM_AddCommand("playintro", Command_Playintro_f);
+	COM_AddCommand("playdemo", Command_Playdemo_f, 0);
+	COM_AddCommand("timedemo", Command_Timedemo_f, 0);
+	COM_AddCommand("stopdemo", Command_Stopdemo_f, COM_LUA);
+	COM_AddCommand("playintro", Command_Playintro_f, COM_LUA);
 
-	COM_AddCommand("resetcamera", Command_ResetCamera_f);
+	COM_AddCommand("resetcamera", Command_ResetCamera_f, COM_LUA);
 
-	COM_AddCommand("setcontrol", Command_Setcontrol_f);
-	COM_AddCommand("setcontrol2", Command_Setcontrol2_f);
+	COM_AddCommand("setcontrol", Command_Setcontrol_f, 0);
+	COM_AddCommand("setcontrol2", Command_Setcontrol2_f, 0);
 
-	COM_AddCommand("screenshot", M_ScreenShot);
-	COM_AddCommand("startmovie", Command_StartMovie_f);
-	COM_AddCommand("stopmovie", Command_StopMovie_f);
+	COM_AddCommand("screenshot", M_ScreenShot, COM_LUA);
+	COM_AddCommand("startmovie", Command_StartMovie_f, COM_LUA);
+	COM_AddCommand("stopmovie", Command_StopMovie_f, COM_LUA);
 
 	CV_RegisterVar(&cv_screenshot_option);
 	CV_RegisterVar(&cv_screenshot_folder);
@@ -758,7 +729,7 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_ghost_last);
 	CV_RegisterVar(&cv_ghost_guest);
 
-	COM_AddCommand("displayplayer", Command_Displayplayer_f);
+	COM_AddCommand("displayplayer", Command_Displayplayer_f, COM_LUA);
 
 	// FIXME: not to be here.. but needs be done for config loading
 	CV_RegisterVar(&cv_globalgamma);
@@ -805,30 +776,30 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_pauseifunfocused);
 
 	// g_input.c
-	CV_RegisterVar(&cv_sideaxis[0]);
-	CV_RegisterVar(&cv_sideaxis[1]);
-	CV_RegisterVar(&cv_turnaxis[0]);
-	CV_RegisterVar(&cv_turnaxis[1]);
-	CV_RegisterVar(&cv_moveaxis[0]);
-	CV_RegisterVar(&cv_moveaxis[1]);
-	CV_RegisterVar(&cv_lookaxis[0]);
-	CV_RegisterVar(&cv_lookaxis[1]);
-	CV_RegisterVar(&cv_jumpaxis[0]);
-	CV_RegisterVar(&cv_jumpaxis[1]);
-	CV_RegisterVar(&cv_spinaxis[0]);
-	CV_RegisterVar(&cv_spinaxis[1]);
-	CV_RegisterVar(&cv_fireaxis[0]);
-	CV_RegisterVar(&cv_fireaxis[1]);
-	CV_RegisterVar(&cv_firenaxis[0]);
-	CV_RegisterVar(&cv_firenaxis[1]);
-	CV_RegisterVar(&cv_deadzone[0]);
-	CV_RegisterVar(&cv_deadzone[1]);
-	CV_RegisterVar(&cv_digitaldeadzone[0]);
-	CV_RegisterVar(&cv_digitaldeadzone[1]);
+	CV_RegisterVar(&cv_sideaxis);
+	CV_RegisterVar(&cv_sideaxis2);
+	CV_RegisterVar(&cv_turnaxis);
+	CV_RegisterVar(&cv_turnaxis2);
+	CV_RegisterVar(&cv_moveaxis);
+	CV_RegisterVar(&cv_moveaxis2);
+	CV_RegisterVar(&cv_lookaxis);
+	CV_RegisterVar(&cv_lookaxis2);
+	CV_RegisterVar(&cv_jumpaxis);
+	CV_RegisterVar(&cv_jumpaxis2);
+	CV_RegisterVar(&cv_spinaxis);
+	CV_RegisterVar(&cv_spinaxis2);
+	CV_RegisterVar(&cv_fireaxis);
+	CV_RegisterVar(&cv_fireaxis2);
+	CV_RegisterVar(&cv_firenaxis);
+	CV_RegisterVar(&cv_firenaxis2);
+	CV_RegisterVar(&cv_deadzone);
+	CV_RegisterVar(&cv_deadzone2);
+	CV_RegisterVar(&cv_digitaldeadzone);
+	CV_RegisterVar(&cv_digitaldeadzone2);
 
 	// filesrch.c
-	CV_RegisterVar(&cv_addons_option);
-	CV_RegisterVar(&cv_addons_folder);
+	//CV_RegisterVar(&cv_addons_option); // These two are now defined
+	//CV_RegisterVar(&cv_addons_folder); // in D_RegisterServerCommands
 	CV_RegisterVar(&cv_addons_md5);
 	CV_RegisterVar(&cv_addons_showall);
 	CV_RegisterVar(&cv_addons_search_type);
@@ -853,14 +824,14 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_mousemove);
 	CV_RegisterVar(&cv_mousemove2);
 
-	for (i = 0; i < 2; i++)
-	{
-		CV_RegisterVar(&cv_usegamepad[i]);
-		CV_RegisterVar(&cv_gamepad_scale[i]);
-		CV_RegisterVar(&cv_gamepad_rumble[i]);
-	}
-
-	CV_RegisterVar(&cv_gamepad_autopause);
+	CV_RegisterVar(&cv_usejoystick);
+	CV_RegisterVar(&cv_usejoystick2);
+#ifdef LJOYSTICK
+	CV_RegisterVar(&cv_joyport);
+	CV_RegisterVar(&cv_joyport2);
+#endif
+	CV_RegisterVar(&cv_joyscale);
+	CV_RegisterVar(&cv_joyscale2);
 
 	// Analog Control
 	CV_RegisterVar(&cv_analog[0]);
@@ -902,10 +873,15 @@ void D_RegisterClientCommands(void)
 	// screen.c
 	CV_RegisterVar(&cv_fullscreen);
 	CV_RegisterVar(&cv_renderview);
+	CV_RegisterVar(&cv_renderhitboxinterpolation);
+	CV_RegisterVar(&cv_renderhitboxgldepth);
+	CV_RegisterVar(&cv_renderhitbox);
 	CV_RegisterVar(&cv_renderer);
 	CV_RegisterVar(&cv_scr_depth);
 	CV_RegisterVar(&cv_scr_width);
 	CV_RegisterVar(&cv_scr_height);
+	CV_RegisterVar(&cv_scr_width_w);
+	CV_RegisterVar(&cv_scr_height_w);
 
 	CV_RegisterVar(&cv_soundtest);
 
@@ -914,7 +890,7 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_ps_descriptor);
 
 	// ingame object placing
-	COM_AddCommand("objectplace", Command_ObjectPlace_f);
+	COM_AddCommand("objectplace", Command_ObjectPlace_f, COM_LUA);
 	//COM_AddCommand("writethings", Command_Writethings_f);
 	CV_RegisterVar(&cv_speed);
 	CV_RegisterVar(&cv_opflags);
@@ -926,32 +902,32 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_freedemocamera);
 
 	// add cheat commands
-	COM_AddCommand("noclip", Command_CheatNoClip_f);
-	COM_AddCommand("god", Command_CheatGod_f);
-	COM_AddCommand("notarget", Command_CheatNoTarget_f);
-	COM_AddCommand("getallemeralds", Command_Getallemeralds_f);
-	COM_AddCommand("resetemeralds", Command_Resetemeralds_f);
-	COM_AddCommand("setrings", Command_Setrings_f);
-	COM_AddCommand("setlives", Command_Setlives_f);
-	COM_AddCommand("setcontinues", Command_Setcontinues_f);
-	COM_AddCommand("devmode", Command_Devmode_f);
-	COM_AddCommand("savecheckpoint", Command_Savecheckpoint_f);
-	COM_AddCommand("scale", Command_Scale_f);
-	COM_AddCommand("gravflip", Command_Gravflip_f);
-	COM_AddCommand("hurtme", Command_Hurtme_f);
-	COM_AddCommand("jumptoaxis", Command_JumpToAxis_f);
-	COM_AddCommand("charability", Command_Charability_f);
-	COM_AddCommand("charspeed", Command_Charspeed_f);
-	COM_AddCommand("teleport", Command_Teleport_f);
-	COM_AddCommand("rteleport", Command_RTeleport_f);
-	COM_AddCommand("skynum", Command_Skynum_f);
-	COM_AddCommand("weather", Command_Weather_f);
-	COM_AddCommand("toggletwod", Command_Toggletwod_f);
+	COM_AddCommand("noclip", Command_CheatNoClip_f, COM_LUA);
+	COM_AddCommand("god", Command_CheatGod_f, COM_LUA);
+	COM_AddCommand("notarget", Command_CheatNoTarget_f, COM_LUA);
+	COM_AddCommand("getallemeralds", Command_Getallemeralds_f, COM_LUA);
+	COM_AddCommand("resetemeralds", Command_Resetemeralds_f, COM_LUA);
+	COM_AddCommand("setrings", Command_Setrings_f, COM_LUA);
+	COM_AddCommand("setlives", Command_Setlives_f, COM_LUA);
+	COM_AddCommand("setcontinues", Command_Setcontinues_f, COM_LUA);
+	COM_AddCommand("devmode", Command_Devmode_f, COM_LUA);
+	COM_AddCommand("savecheckpoint", Command_Savecheckpoint_f, COM_LUA);
+	COM_AddCommand("scale", Command_Scale_f, COM_LUA);
+	COM_AddCommand("gravflip", Command_Gravflip_f, COM_LUA);
+	COM_AddCommand("hurtme", Command_Hurtme_f, COM_LUA);
+	COM_AddCommand("jumptoaxis", Command_JumpToAxis_f, COM_LUA);
+	COM_AddCommand("charability", Command_Charability_f, COM_LUA);
+	COM_AddCommand("charspeed", Command_Charspeed_f, COM_LUA);
+	COM_AddCommand("teleport", Command_Teleport_f, COM_LUA);
+	COM_AddCommand("rteleport", Command_RTeleport_f, COM_LUA);
+	COM_AddCommand("skynum", Command_Skynum_f, COM_LUA);
+	COM_AddCommand("weather", Command_Weather_f, COM_LUA);
+	COM_AddCommand("toggletwod", Command_Toggletwod_f, COM_LUA);
 #ifdef _DEBUG
-	COM_AddCommand("causecfail", Command_CauseCfail_f);
+	COM_AddCommand("causecfail", Command_CauseCfail_f, COM_LUA);
 #endif
 #ifdef LUA_ALLOW_BYTECODE
-	COM_AddCommand("dumplua", Command_Dumplua_f);
+	COM_AddCommand("dumplua", Command_Dumplua_f, COM_LUA);
 #endif
 }
 
@@ -1666,9 +1642,14 @@ static void Command_Playdemo_f(void)
 {
 	char name[256];
 
-	if (COM_Argc() != 2)
+	if (COM_Argc() < 2)
 	{
-		CONS_Printf(M_GetText("playdemo <demoname>: playback a demo\n"));
+		CONS_Printf("playdemo <demoname> [-addfiles / -force]:\n");
+		CONS_Printf(M_GetText(
+					"Play back a demo file. The full path from your SRB2 directory must be given.\n\n"
+
+					"* With \"-addfiles\", any required files are added from a list contained within the demo file.\n"
+					"* With \"-force\", the demo is played even if the necessary files have not been added.\n"));
 		return;
 	}
 
@@ -1690,6 +1671,16 @@ static void Command_Playdemo_f(void)
 
 	CONS_Printf(M_GetText("Playing back demo '%s'.\n"), name);
 
+	demofileoverride = DFILE_OVERRIDE_NONE;
+	if (strcmp(COM_Argv(2), "-addfiles") == 0)
+	{
+		demofileoverride = DFILE_OVERRIDE_LOAD;
+	}
+	else if (strcmp(COM_Argv(2), "-force") == 0)
+	{
+		demofileoverride = DFILE_OVERRIDE_SKIP;
+	}
+
 	// Internal if no extension, external if one exists
 	// If external, convert the file name to a path in SRB2's home directory
 	if (FIL_CheckExtension(name))
@@ -1910,7 +1901,7 @@ static void Command_Map_f(void)
 	const char *gametypename;
 	boolean newresetplayers;
 
-	boolean mustmodifygame;
+	boolean wouldSetCheats;
 
 	INT32 newmapnum;
 
@@ -1931,11 +1922,11 @@ static void Command_Map_f(void)
 	option_gametype =   COM_CheckPartialParm("-g");
 	newresetplayers = ! COM_CheckParm("-noresetplayers");
 
-	mustmodifygame =
-		!( netgame     || multiplayer ) &&
-		(!modifiedgame || savemoddata );
+	wouldSetCheats =
+		!( netgame || multiplayer ) &&
+		!( usedCheats );
 
-	if (mustmodifygame && !option_force)
+	if (wouldSetCheats && !option_force)
 	{
 		/* May want to be more descriptive? */
 		CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
@@ -1989,9 +1980,9 @@ static void Command_Map_f(void)
 		return;
 	}
 
-	if (mustmodifygame && option_force)
+	if (wouldSetCheats && option_force)
 	{
-		G_SetGameModified(false);
+		G_SetUsedCheats(false);
 	}
 
 	// new gametype value
@@ -2064,7 +2055,7 @@ static void Command_Map_f(void)
 	// ... unless you're in a dedicated server.  Yes, technically this means you can view any level by
 	// running a dedicated server and joining it yourself, but that's better than making dedicated server's
 	// lives hell.
-	if (!dedicated && M_MapLocked(newmapnum))
+	if (!dedicated && M_MapLocked(newmapnum, serverGamedata))
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
 		Z_Free(realmapname);
@@ -2199,7 +2190,7 @@ static void Command_Pause(void)
 
 	if (cv_pause.value || server || (IsPlayerAdmin(consoleplayer)))
 	{
-		if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) || (marathonmode && gamestate == GS_INTERMISSION))
+		if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_WAITINGPLAYERS) || (marathonmode && gamestate == GS_INTERMISSION))
 		{
 			CONS_Printf(M_GetText("You can't pause here.\n"));
 			return;
@@ -2248,14 +2239,9 @@ static void Got_Pause(UINT8 **cp, INT32 playernum)
 		{
 			if (!menuactive || netgame)
 				S_PauseAudio();
-
-			P_PauseRumble(NULL);
 		}
 		else
-		{
 			S_ResumeAudio();
-			P_UnpauseRumble(NULL);
-		}
 	}
 
 	I_UpdateMouseGrab();
@@ -2367,7 +2353,7 @@ static void Got_Clearscores(UINT8 **cp, INT32 playernum)
 	}
 
 	for (i = 0; i < MAXPLAYERS; i++)
-		players[i].score = 0;
+		players[i].score = players[i].recordscore = 0;
 
 	CONS_Printf(M_GetText("Scores have been reset by the server.\n"));
 }
@@ -3310,6 +3296,69 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum)
 	G_SetGameModified(true);
 }
 
+// C++ would make this SO much simpler!
+typedef struct addedfile_s
+{
+	struct addedfile_s *next;
+	struct addedfile_s *prev;
+	char *value;
+} addedfile_t;
+
+static boolean AddedFileContains(addedfile_t *list, const char *value)
+{
+	addedfile_t *node;
+	for (node = list; node; node = node->next)
+	{
+		if (!strcmp(value, node->value))
+			return true;
+	}
+
+	return false;
+}
+
+static void AddedFilesAdd(addedfile_t **list, const char *value)
+{
+	addedfile_t *item = Z_Calloc(sizeof(addedfile_t), PU_STATIC, NULL);
+	item->value = Z_StrDup(value);
+	ListAdd(item, (listitem_t**)list);
+}
+
+static void AddedFilesRemove(void *pItem, addedfile_t **itemHead)
+{
+	addedfile_t *item = (addedfile_t *)pItem;
+
+	if (item == *itemHead) // Start of list
+	{
+		*itemHead = item->next;
+
+		if (*itemHead)
+			(*itemHead)->prev = NULL;
+	}
+	else if (item->next == NULL) // end of list
+	{
+		item->prev->next = NULL;
+	}
+	else // Somewhere in between
+	{
+		item->prev->next = item->next;
+		item->next->prev = item->prev;
+	}
+
+	Z_Free(item->value);
+	Z_Free(item);
+}
+
+static void AddedFilesClearList(addedfile_t **itemHead)
+{
+	addedfile_t *item;
+	addedfile_t *next;
+	for (item = *itemHead; item; item = next)
+	{
+		next = item->next;
+		AddedFilesRemove(item, itemHead);
+	}
+}
+
 /** Adds a pwad at runtime.
   * Searches for sounds, maps, music, new images.
   */
@@ -3318,8 +3367,7 @@ static void Command_Addfile(void)
 	size_t argc = COM_Argc(); // amount of arguments total
 	size_t curarg; // current argument index
 
-	const char *addedfiles[argc]; // list of filenames already processed
-	size_t numfilesadded = 0; // the amount of filenames processed
+	addedfile_t *addedfiles = NULL; // list of filenames already processed
 
 	if (argc < 2)
 	{
@@ -3334,25 +3382,14 @@ static void Command_Addfile(void)
 		char buf[256];
 		char *buf_p = buf;
 		INT32 i;
-		size_t ii;
 		int musiconly; // W_VerifyNMUSlumps isn't boolean
 		boolean fileadded = false;
 
 		fn = COM_Argv(curarg);
 
 		// For the amount of filenames previously processed...
-		for (ii = 0; ii < numfilesadded; ii++)
-		{
-			// If this is one of them, don't try to add it.
-			if (!strcmp(fn, addedfiles[ii]))
-			{
-				fileadded = true;
-				break;
-			}
-		}
-
-		// If we've added this one, skip to the next one.
-		if (fileadded)
+		fileadded = AddedFileContains(addedfiles, fn);
+		if (fileadded) // If this is one of them, don't try to add it.
 		{
 			CONS_Alert(CONS_WARNING, M_GetText("Already processed %s, skipping\n"), fn);
 			continue;
@@ -3361,13 +3398,16 @@ static void Command_Addfile(void)
 		// Disallow non-printing characters and semicolons.
 		for (i = 0; fn[i] != '\0'; i++)
 			if (!isprint(fn[i]) || fn[i] == ';')
+			{
+				AddedFilesClearList(&addedfiles);
 				return;
+			}
 
 		musiconly = W_VerifyNMUSlumps(fn, false);
 
 		if (musiconly == -1)
 		{
-			addedfiles[numfilesadded++] = fn;
+			AddedFilesAdd(&addedfiles, fn);
 			continue;
 		}
 
@@ -3386,7 +3426,7 @@ static void Command_Addfile(void)
 		if (!(netgame || multiplayer) || musiconly)
 		{
 			P_AddWadFile(fn);
-			addedfiles[numfilesadded++] = fn;
+			AddedFilesAdd(&addedfiles, fn);
 			continue;
 		}
 
@@ -3401,6 +3441,7 @@ static void Command_Addfile(void)
 		if (numwadfiles >= MAX_WADFILES)
 		{
 			CONS_Alert(CONS_ERROR, M_GetText("Too many files loaded to add %s\n"), fn);
+			AddedFilesClearList(&addedfiles);
 			return;
 		}
 
@@ -3440,13 +3481,15 @@ static void Command_Addfile(void)
 			WRITEMEM(buf_p, md5sum, 16);
 		}
 
-		addedfiles[numfilesadded++] = fn;
+		AddedFilesAdd(&addedfiles, fn);
 
 		if (IsPlayerAdmin(consoleplayer) && (!server)) // Request to add file
 			SendNetXCmd(XD_REQADDFILE, buf, buf_p - buf);
 		else
 			SendNetXCmd(XD_ADDFILE, buf, buf_p - buf);
 	}
+
+	AddedFilesClearList(&addedfiles);
 }
 
 static void Command_Addfolder(void)
@@ -3454,8 +3497,7 @@ static void Command_Addfolder(void)
 	size_t argc = COM_Argc(); // amount of arguments total
 	size_t curarg; // current argument index
 
-	const char *addedfolders[argc]; // list of filenames already processed
-	size_t numfoldersadded = 0; // the amount of filenames processed
+	addedfile_t *addedfolders = NULL; // list of filenames already processed
 
 	if (argc < 2)
 	{
@@ -3471,24 +3513,13 @@ static void Command_Addfolder(void)
 		char buf[256];
 		char *buf_p = buf;
 		INT32 i, stat;
-		size_t ii;
 		boolean folderadded = false;
 
 		fn = COM_Argv(curarg);
 
 		// For the amount of filenames previously processed...
-		for (ii = 0; ii < numfoldersadded; ii++)
-		{
-			// If this is one of them, don't try to add it.
-			if (!strcmp(fn, addedfolders[ii]))
-			{
-				folderadded = true;
-				break;
-			}
-		}
-
-		// If we've added this one, skip to the next one.
-		if (folderadded)
+		folderadded = AddedFileContains(addedfolders, fn);
+		if (folderadded) // If we've added this one, skip to the next one.
 		{
 			CONS_Alert(CONS_WARNING, M_GetText("Already processed %s, skipping\n"), fn);
 			continue;
@@ -3497,13 +3528,16 @@ static void Command_Addfolder(void)
 		// Disallow non-printing characters and semicolons.
 		for (i = 0; fn[i] != '\0'; i++)
 			if (!isprint(fn[i]) || fn[i] == ';')
+			{
+				AddedFilesClearList(&addedfolders);
 				return;
+			}
 
 		// Add file on your client directly if you aren't in a netgame.
 		if (!(netgame || multiplayer))
 		{
 			P_AddFolder(fn);
-			addedfolders[numfoldersadded++] = fn;
+			AddedFilesAdd(&addedfolders, fn);
 			continue;
 		}
 
@@ -3525,6 +3559,7 @@ static void Command_Addfolder(void)
 		if (numwadfiles >= MAX_WADFILES)
 		{
 			CONS_Alert(CONS_ERROR, M_GetText("Too many files loaded to add %s\n"), fn);
+			AddedFilesClearList(&addedfolders);
 			return;
 		}
 
@@ -3570,7 +3605,7 @@ static void Command_Addfolder(void)
 
 		Z_Free(fullpath);
 
-		addedfolders[numfoldersadded++] = fn;
+		AddedFilesAdd(&addedfolders, fn);
 
 		WRITESTRINGN(buf_p,p,240);
 
@@ -3929,18 +3964,12 @@ void ItemFinder_OnChange(void)
 	if (!cv_itemfinder.value)
 		return; // it's fine.
 
-	if (!M_SecretUnlocked(SECRET_ITEMFINDER))
+	if (!M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata))
 	{
 		CONS_Printf(M_GetText("You haven't earned this yet.\n"));
 		CV_StealthSetValue(&cv_itemfinder, 0);
 		return;
 	}
-	else if (netgame || multiplayer)
-	{
-		CONS_Printf(M_GetText("This only works in single player.\n"));
-		CV_StealthSetValue(&cv_itemfinder, 0);
-		return;
-	}
 }
 
 /** Deals with a pointlimit change by printing the change to the console.
@@ -4289,7 +4318,7 @@ void D_GameTypeChanged(INT32 lastgametype)
 
 static void Ringslinger_OnChange(void)
 {
-	if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && cv_ringslinger.value && !cv_debug)
+	if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !netgame && cv_ringslinger.value && !cv_debug)
 	{
 		CONS_Printf(M_GetText("You haven't earned this yet.\n"));
 		CV_StealthSetValue(&cv_ringslinger, 0);
@@ -4297,12 +4326,12 @@ static void Ringslinger_OnChange(void)
 	}
 
 	if (cv_ringslinger.value) // Only if it's been turned on
-		G_SetGameModified(multiplayer);
+		G_SetUsedCheats(false);
 }
 
 static void Gravity_OnChange(void)
 {
-	if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && !cv_debug
+	if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !netgame && !cv_debug
 		&& strcmp(cv_gravity.string, cv_gravity.defaultvalue))
 	{
 		CONS_Printf(M_GetText("You haven't earned this yet.\n"));
@@ -4318,7 +4347,7 @@ static void Gravity_OnChange(void)
 #endif
 
 	if (!CV_IsSetToDefault(&cv_gravity))
-		G_SetGameModified(multiplayer);
+		G_SetUsedCheats(false);
 	gravity = cv_gravity.value;
 }
 
@@ -4599,10 +4628,9 @@ void Command_ExitGame_f(void)
 	botskin = 0;
 	cv_debug = 0;
 	emeralds = 0;
+	automapactive = false;
 	memset(&luabanks, 0, sizeof(luabanks));
 
-	P_StopRumble(NULL);
-
 	if (dirmenu)
 		closefilemenu(true);
 
@@ -4636,7 +4664,7 @@ static void Fishcake_OnChange(void)
 	// so don't make modifiedgame always on!
 	if (cv_debug)
 	{
-		G_SetGameModified(multiplayer);
+		G_SetUsedCheats(false);
 	}
 
 	else if (cv_debug != cv_fishcake.value)
@@ -4653,11 +4681,11 @@ static void Fishcake_OnChange(void)
 static void Command_Isgamemodified_f(void)
 {
 	if (savemoddata)
-		CONS_Printf(M_GetText("modifiedgame is true, but you can save emblem and time data in this mod.\n"));
+		CONS_Printf(M_GetText("modifiedgame is true, but you can save time data in this mod.\n"));
 	else if (modifiedgame)
-		CONS_Printf(M_GetText("modifiedgame is true, extras will not be unlocked\n"));
+		CONS_Printf(M_GetText("modifiedgame is true, time data can't be saved\n"));
 	else
-		CONS_Printf(M_GetText("modifiedgame is false, you can unlock extras\n"));
+		CONS_Printf(M_GetText("modifiedgame is false, you can save time data\n"));
 }
 
 static void Command_Cheats_f(void)
diff --git a/src/netcode/d_netcmd.h b/src/netcode/d_netcmd.h
index 797a686a78..22ee0695d0 100644
--- a/src/netcode/d_netcmd.h
+++ b/src/netcode/d_netcmd.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -33,10 +33,14 @@ extern consvar_t cv_defaultskin2;
 
 extern consvar_t cv_seenames, cv_allowseenames;
 extern consvar_t cv_usemouse;
-extern consvar_t cv_usegamepad[2];
-extern consvar_t cv_gamepad_scale[2];
-extern consvar_t cv_gamepad_rumble[2];
-extern consvar_t cv_gamepad_autopause;
+extern consvar_t cv_usejoystick;
+extern consvar_t cv_usejoystick2;
+#ifdef LJOYSTICK
+extern consvar_t cv_joyport;
+extern consvar_t cv_joyport2;
+#endif
+extern consvar_t cv_joyscale;
+extern consvar_t cv_joyscale2;
 
 // splitscreen with second mouse
 extern consvar_t cv_mouse2port;
diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c
index 10b7359adc..c5ddef7b65 100644
--- a/src/netcode/d_netfil.c
+++ b/src/netcode/d_netfil.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -504,7 +504,7 @@ INT32 CL_CheckFiles(void)
 		CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename);
 
 		// Check in already loaded files
-		for (j = mainwads; wadfiles[j]; j++)
+		for (j = mainwads; j < numwadfiles; j++)
 		{
 			nameonly(strcpy(wadfilename, wadfiles[j]->filename));
 			if (!stricmp(wadfilename, fileneeded[i].filename) &&
diff --git a/src/netcode/d_netfil.h b/src/netcode/d_netfil.h
index 850e24d490..fdbec8c539 100644
--- a/src/netcode/d_netfil.h
+++ b/src/netcode/d_netfil.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/netcode/gamestate.c b/src/netcode/gamestate.c
index c1ceb95b5d..9c243ea734 100644
--- a/src/netcode/gamestate.c
+++ b/src/netcode/gamestate.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -29,7 +29,6 @@
 #include "../lua_script.h"
 #include "../lzf.h"
 #include "../m_misc.h"
-#include "../p_haptic.h"
 #include "../p_local.h"
 #include "../p_saveg.h"
 #include "../r_main.h"
@@ -200,8 +199,6 @@ void CL_LoadReceivedSavegame(boolean reloading)
 	titledemo = false;
 	automapactive = false;
 
-	P_StopRumble(NULL);
-
 	// load a base level
 	if (P_LoadNetGame(reloading))
 	{
diff --git a/src/netcode/gamestate.h b/src/netcode/gamestate.h
index 9d27797722..a2fae1f14a 100644
--- a/src/netcode/gamestate.h
+++ b/src/netcode/gamestate.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/netcode/http-mserv.c b/src/netcode/http-mserv.c
index 68e46b52a1..7dc157ee4e 100644
--- a/src/netcode/http-mserv.c
+++ b/src/netcode/http-mserv.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2020-2022 by James R.
+// Copyright (C) 2020-2023 by James R.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -66,6 +66,8 @@ static I_mutex hms_api_mutex;
 
 static char *hms_server_token;
 
+static char hms_useragent[512];
+
 struct HMS_buffer
 {
 	CURL *curl;
@@ -82,6 +84,22 @@ Contact_error (void)
 	);
 }
 
+static void
+get_user_agent(char *buf, size_t len)
+{
+	if (snprintf(buf, len, "%s/%s (%s; %s; %i; %i) SRB2BASE/%i", SRB2APPLICATION, VERSIONSTRING, compbranch, comprevision,  MODID, MODVERSION, CODEBASE) < 0)
+		I_Error("http-mserv: get_user_agent failed");
+}
+
+static void
+init_user_agent_once(void)
+{
+	if (hms_useragent[0] != '\0')
+		return;
+
+	get_user_agent(hms_useragent, 512);
+}
+
 static size_t
 HMS_on_read (char *s, size_t _1, size_t n, void *userdata)
 {
@@ -157,6 +175,8 @@ HMS_connect (const char *format, ...)
 	I_lock_mutex(&hms_api_mutex);
 #endif
 
+	init_user_agent_once();
+
 	seek = strlen(hms_api) + 1;/* + '/' */
 
 	va_start (ap, format);
@@ -197,12 +217,18 @@ HMS_connect (const char *format, ...)
 
 	curl_easy_setopt(curl, CURLOPT_URL, url);
 	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
-	curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+
+#ifndef NO_IPV6
+	if (M_CheckParm("-noipv6"))
+#endif
+		curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
 
 	curl_easy_setopt(curl, CURLOPT_TIMEOUT, cv_masterserver_timeout.value);
 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read);
 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer);
 
+	curl_easy_setopt(curl, CURLOPT_USERAGENT, hms_useragent);
+
 	curl_free(quack_token);
 	free(url);
 
diff --git a/src/netcode/i_addrinfo.c b/src/netcode/i_addrinfo.c
index 49aadf27d2..9efaff4dac 100644
--- a/src/netcode/i_addrinfo.c
+++ b/src/netcode/i_addrinfo.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2011-2022 by Sonic Team Junior.
+// Copyright (C) 2011-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/netcode/i_addrinfo.h b/src/netcode/i_addrinfo.h
index 592e693f4c..79cfb05b25 100644
--- a/src/netcode/i_addrinfo.h
+++ b/src/netcode/i_addrinfo.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2011-2022 by Sonic Team Junior.
+// Copyright (C) 2011-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/netcode/i_net.h b/src/netcode/i_net.h
index 66126d0503..09b842296c 100644
--- a/src/netcode/i_net.h
+++ b/src/netcode/i_net.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -109,6 +109,17 @@ extern boolean (*I_NetCanSend)(void);
 */
 extern void (*I_NetFreeNodenum)(INT32 nodenum);
 
+/**
+	\brief	split a string into address and port
+
+	\param	address	string to split
+
+	\param	port	double pointer to hold port component (optional)
+
+	\return	address component
+*/
+extern char *I_NetSplitAddress(char *address, char **port);
+
 /**	\brief	open a connection with specified address
 
 	\param	address	address to connect to
diff --git a/src/netcode/i_tcp.c b/src/netcode/i_tcp.c
index bd950c3559..6982345791 100644
--- a/src/netcode/i_tcp.c
+++ b/src/netcode/i_tcp.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -328,8 +328,14 @@ static inline void I_UPnP_rem(const char *port, const char * servicetype)
 
 static const char *SOCK_AddrToStr(mysockaddr_t *sk)
 {
-	static char s[64]; // 255.255.255.255:65535 or IPv6:65535
+	static char s[64]; // 255.255.255.255:65535 or
+	// [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
 #ifdef HAVE_NTOP
+#ifdef HAVE_IPV6
+	int v6 = (sk->any.sa_family == AF_INET6);
+#else
+	int v6 = 0;
+#endif
 	void *addr;
 
 	if(sk->any.sa_family == AF_INET)
@@ -343,14 +349,21 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk)
 
 	if(addr == NULL)
 		sprintf(s, "No address");
-	else if(inet_ntop(sk->any.sa_family, addr, s, sizeof (s)) == NULL)
+	else if(inet_ntop(sk->any.sa_family, addr, &s[v6], sizeof (s) - v6) == NULL)
 		sprintf(s, "Unknown family type, error #%u", errno);
 #ifdef HAVE_IPV6
-	else if(sk->any.sa_family == AF_INET6 && sk->ip6.sin6_port != 0)
-		strcat(s, va(":%d", ntohs(sk->ip6.sin6_port)));
+	else if(sk->any.sa_family == AF_INET6)
+	{
+		s[0] = '[';
+		strcat(s, "]");
+
+		if (sk->ip6.sin6_port != 0)
+			strcat(s, va(":%d", ntohs(sk->ip6.sin6_port)));
+	}
 #endif
 	else if(sk->any.sa_family == AF_INET  && sk->ip4.sin_port  != 0)
 		strcat(s, va(":%d", ntohs(sk->ip4.sin_port)));
+
 #else
 	if (sk->any.sa_family == AF_INET)
 	{
@@ -401,7 +414,7 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
 			&& (b->ip4.sin_port == 0 || (a->ip4.sin_port == b->ip4.sin_port));
 #ifdef HAVE_IPV6
 	else if (b->any.sa_family == AF_INET6)
-		return memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, sizeof(b->ip6.sin6_addr))
+		return !memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, sizeof(b->ip6.sin6_addr))
 			&& (b->ip6.sin6_port == 0 || (a->ip6.sin6_port == b->ip6.sin6_port));
 #endif
 	else
@@ -692,8 +705,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 	unsigned long trueval = true;
 #endif
 	mysockaddr_t straddr;
-	struct sockaddr_in sin;
-	socklen_t len = sizeof(sin);
+	socklen_t len = sizeof(straddr);
 
 	if (s == (SOCKET_TYPE)ERRSOCKET)
 		return (SOCKET_TYPE)ERRSOCKET;
@@ -711,14 +723,12 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 	}
 #endif
 
-	straddr.any = *addr;
+	memcpy(&straddr, addr, addrlen);
 	I_OutputMsg("Binding to %s\n", SOCK_AddrToStr(&straddr));
 
 	if (family == AF_INET)
 	{
-		mysockaddr_t tmpaddr;
-		tmpaddr.any = *addr ;
-		if (tmpaddr.ip4.sin_addr.s_addr == htonl(INADDR_ANY))
+		if (straddr.ip4.sin_addr.s_addr == htonl(INADDR_ANY))
 		{
 			opt = true;
 			opts = (socklen_t)sizeof(opt);
@@ -735,7 +745,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 #ifdef HAVE_IPV6
 	else if (family == AF_INET6)
 	{
-		if (memcmp(addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL
+		if (memcmp(&straddr.ip6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL
 		{
 			opt = true;
 			opts = (socklen_t)sizeof(opt);
@@ -745,7 +755,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 		// make it IPv6 ony
 		opt = true;
 		opts = (socklen_t)sizeof(opt);
-		if (setsockopt(s, SOL_SOCKET, IPV6_V6ONLY, (char *)&opt, opts))
+		if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, opts))
 		{
 			CONS_Alert(CONS_WARNING, M_GetText("Could not limit IPv6 bind\n")); // I do not care anymore
 		}
@@ -787,10 +797,17 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 			CONS_Printf(M_GetText("Network system buffer set to: %dKb\n"), opt>>10);
 	}
 
-	if (getsockname(s, (struct sockaddr *)&sin, &len) == -1)
+	if (getsockname(s, &straddr.any, &len) == -1)
 		CONS_Alert(CONS_WARNING, M_GetText("Failed to get port number\n"));
 	else
-		current_port = (UINT16)ntohs(sin.sin_port);
+	{
+		if (family == AF_INET)
+			current_port = (UINT16)ntohs(straddr.ip4.sin_port);
+#ifdef HAVE_IPV6
+		else if (family == AF_INET6)
+			current_port = (UINT16)ntohs(straddr.ip6.sin6_port);
+#endif
+	}
 
 	return s;
 }
@@ -801,7 +818,7 @@ static boolean UDP_Socket(void)
 	struct my_addrinfo *ai, *runp, hints;
 	int gaie;
 #ifdef HAVE_IPV6
-	const INT32 b_ipv6 = M_CheckParm("-ipv6");
+	const INT32 b_ipv6 = !M_CheckParm("-noipv6");
 #endif
 	const char *serv;
 
@@ -1105,6 +1122,7 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
 	SINT8 newnode = -1;
 	struct my_addrinfo *ai = NULL, *runp, hints;
 	int gaie;
+	size_t i;
 
 	 if (!port || !port[0])
 		port = DEFAULTPORT;
@@ -1132,13 +1150,24 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
 
 	while (runp != NULL)
 	{
-		// find ip of the server
-		if (sendto(mysockets[0], NULL, 0, 0, runp->ai_addr, runp->ai_addrlen) == 0)
+		// test ip address of server
+		for (i = 0; i < mysocketses; ++i)
 		{
-			memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
-			break;
+			/* sendto tests that there is a network to this
+				address */
+			if (runp->ai_addr->sa_family == myfamily[i] &&
+					sendto(mysockets[i], NULL, 0, 0,
+						runp->ai_addr, runp->ai_addrlen) == 0)
+			{
+				memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
+				break;
+			}
 		}
-		runp = runp->ai_next;
+
+		if (i < mysocketses)
+			runp = runp->ai_next;
+		else
+			break;
 	}
 	I_freeaddrinfo(ai);
 	return newnode;
diff --git a/src/netcode/i_tcp.h b/src/netcode/i_tcp.h
index b6e5b92351..ae9983bf17 100644
--- a/src/netcode/i_tcp.h
+++ b/src/netcode/i_tcp.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/netcode/mserv.c b/src/netcode/mserv.c
index f603d78e5b..1c7f3e08d4 100644
--- a/src/netcode/mserv.c
+++ b/src/netcode/mserv.c
@@ -1,8 +1,8 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
-// Copyright (C) 2020-2022 by James R.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 2020-2023 by James R.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -61,7 +61,7 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = {
 };
 
 consvar_t cv_masterserver = CVAR_INIT ("masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange);
-consvar_t cv_servername = CVAR_INIT ("servername", "SRB2 server", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, NULL, Update_parameters);
+consvar_t cv_servername = CVAR_INIT ("servername", "SRB2 server", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Update_parameters);
 
 consvar_t cv_masterserver_update_rate = CVAR_INIT ("masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters);
 
@@ -95,8 +95,8 @@ void AddMServCommands(void)
 	CV_RegisterVar(&cv_masterserver_token);
 	CV_RegisterVar(&cv_servername);
 #ifdef MASTERSERVER
-	COM_AddCommand("listserv", Command_Listserv_f);
-	COM_AddCommand("masterserver_update", Update_parameters); // allows people to updates manually in case you were delisted by accident
+	COM_AddCommand("listserv", Command_Listserv_f, 0);
+	COM_AddCommand("masterserver_update", Update_parameters, COM_LUA); // allows people to updates manually in case you were delisted by accident
 #endif
 }
 
diff --git a/src/netcode/mserv.h b/src/netcode/mserv.h
index 7fdf3ed1b9..0bc8c8e6b8 100644
--- a/src/netcode/mserv.h
+++ b/src/netcode/mserv.h
@@ -1,8 +1,8 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
-// Copyright (C) 2020-2022 by James R.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
+// Copyright (C) 2020-2023 by James R.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -33,7 +33,7 @@ typedef union
 typedef struct
 {
 	msg_header_t header;
-	char ip[16];
+	char ip[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
 	char port[8];
 	char name[32];
 	INT32 room;
diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c
index efc8bd0efc..2b3abfd023 100644
--- a/src/netcode/net_command.c
+++ b/src/netcode/net_command.c
@@ -321,22 +321,18 @@ void SV_WriteNetCommandsForTic(tic_t tic, UINT8 **buf)
 	}
 }
 
-void CL_CopyNetCommandsFromServerPacket(tic_t tic)
+void CL_CopyNetCommandsFromServerPacket(tic_t tic, UINT8 **buf)
 {
-	servertics_pak *packet = &netbuffer->u.serverpak;
-	UINT8 *cmds = (UINT8*)&packet->cmds[packet->numslots * packet->numtics];
-	UINT8 numcmds;
-
-	numcmds = *cmds++;
+	UINT8 numcmds = *(*buf)++;
 
 	for (UINT32 i = 0; i < numcmds; i++)
 	{
-		INT32 playernum = *cmds++; // playernum
-		size_t size = cmds[0]+1;
+		INT32 playernum = *(*buf)++; // playernum
+		size_t size = (*buf)[0]+1;
 
 		if (tic >= gametic) // Don't copy old net commands
-			M_Memcpy(D_GetTextcmd(tic, playernum), cmds, size);
-		cmds += size;
+			M_Memcpy(D_GetTextcmd(tic, playernum), *buf, size);
+		*buf += size;
 	}
 }
 
diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h
index cc26aeb0ef..a0c46f3a2f 100644
--- a/src/netcode/net_command.h
+++ b/src/netcode/net_command.h
@@ -58,7 +58,7 @@ size_t TotalTextCmdPerTic(tic_t tic);
 
 void PT_TextCmd(SINT8 node, INT32 netconsole);
 void SV_WriteNetCommandsForTic(tic_t tic, UINT8 **buf);
-void CL_CopyNetCommandsFromServerPacket(tic_t tic);
+void CL_CopyNetCommandsFromServerPacket(tic_t tic, UINT8 **buf);
 void CL_SendNetCommands(void);
 void SendKick(UINT8 playernum, UINT8 msg);
 void SendKicksForNode(SINT8 node, UINT8 msg);
diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h
index 9866e4c5a5..a992e3b69d 100644
--- a/src/netcode/protocol.h
+++ b/src/netcode/protocol.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -72,6 +72,8 @@ typedef enum
 	PT_ASKLUAFILE,     // Client telling the server they don't have the file
 	PT_HASLUAFILE,     // Client telling the server they have the file
 
+	PT_BASICKEEPALIVE, // Keep the network alive during wipes, as tics aren't advanced and NetUpdate isn't called
+
 	// Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
 
 	PT_CANFAIL,       // This is kind of a priority. Anything bigger than CANFAIL
@@ -143,6 +145,7 @@ typedef struct
 
 	UINT8 gametype;
 	UINT8 modifiedgame;
+	UINT8 usedCheats;
 
 	char server_context[8]; // Unique context id, generated at server startup.
 } ATTRPACK serverconfig_pak;
diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c
index f8ec3c7bd5..2164f411a2 100644
--- a/src/netcode/server_connection.c
+++ b/src/netcode/server_connection.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -39,10 +39,10 @@ char playeraddress[MAXPLAYERS][64];
 
 consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
 
-consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
+consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_OnOff, NULL);
 
 static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}};
-consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL);
+consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, maxplayers_cons_t, NULL);
 
 static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}};
 consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL);
@@ -52,9 +52,9 @@ consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "2", CV_SAVE|CV_NETVAR|
 
 static INT32 FindRejoinerNum(SINT8 node)
 {
-	char strippednodeaddress[64];
+	char addressbuffer[64];
 	const char *nodeaddress;
-	char *port;
+	const char *strippednodeaddress;
 
 	// Make sure there is no dead dress before proceeding to the stripping
 	if (!I_GetNodeAddress)
@@ -64,10 +64,8 @@ static INT32 FindRejoinerNum(SINT8 node)
 		return -1;
 
 	// Strip the address of its port
-	strcpy(strippednodeaddress, nodeaddress);
-	port = strchr(strippednodeaddress, ':');
-	if (port)
-		*port = '\0';
+	strcpy(addressbuffer, nodeaddress);
+	strippednodeaddress = I_NetSplitAddress(addressbuffer, NULL);
 
 	// Check if any player matches the stripped address
 	for (INT32 i = 0; i < MAXPLAYERS; i++)
@@ -110,8 +108,9 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
 	netbuffer->u.serverinfo.time = (tic_t)LONG(servertime);
 	netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime);
 
-	netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
-	netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
+	// Exclude bots from both counts
+	netbuffer->u.serverinfo.numberofplayer = (UINT8)(D_NumPlayers() - D_NumBots());
+	netbuffer->u.serverinfo.maxplayer = (UINT8)(cv_maxplayers.value - D_NumBots());
 
 	netbuffer->u.serverinfo.refusereason = GetRefuseReason(node);
 
@@ -237,6 +236,7 @@ static boolean SV_SendServerConfig(INT32 node)
 	netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
 	netbuffer->u.servercfg.gametype = (UINT8)gametype;
 	netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
+	netbuffer->u.servercfg.usedCheats = (UINT8)usedCheats;
 
 	memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
 
diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h
index 7481d0eb56..14ac5913c9 100644
--- a/src/netcode/server_connection.h
+++ b/src/netcode/server_connection.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c
index 620a10f7a2..7721bc3f1b 100644
--- a/src/netcode/tic_command.c
+++ b/src/netcode/tic_command.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -100,9 +100,9 @@ void D_ResetTiccmds(void)
 }
 
 // Check ticcmd for "speed hacks"
-static void CheckTiccmdHacks(INT32 playernum)
+static void CheckTiccmdHacks(INT32 playernum, tic_t tic)
 {
-	ticcmd_t *cmd = &netcmds[maketic%BACKUPTICS][playernum];
+	ticcmd_t *cmd = &netcmds[tic%BACKUPTICS][playernum];
 	if (cmd->forwardmove > MAXPLMOVE || cmd->forwardmove < -MAXPLMOVE
 		|| cmd->sidemove > MAXPLMOVE || cmd->sidemove < -MAXPLMOVE)
 	{
@@ -177,31 +177,43 @@ void PT_ClientCmd(SINT8 nodenum, INT32 netconsole)
 	// Update the nettics
 	node->tic = realend;
 
-	// Don't do anything for packets of type NODEKEEPALIVE?
-	if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
-		|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
+	// This should probably still timeout though, as the node should always have a player 1 number
+	if (netconsole == -1)
 		return;
 
 	// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
 	/// \todo Use a separate cvar for that kind of timeout?
 	node->freezetimeout = I_GetTime() + connectiontimeout;
 
+	// Don't do anything for packets of type NODEKEEPALIVE?
+	// Sryder 2018/07/01: Update the freezetimeout still!
+	if (netbuffer->packettype == PT_NODEKEEPALIVE
+		|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
+		return;
+
+	// If we've alredy received a ticcmd for this tic, just submit it for the next one.
+	tic_t faketic = maketic;
+	if ((!!(netcmds[maketic % BACKUPTICS][netconsole].angleturn & TICCMD_RECEIVED))
+		&& (maketic - firstticstosend < BACKUPTICS - 1))
+		faketic++;
+
 	// Copy ticcmd
-	G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
+	G_MoveTiccmd(&netcmds[faketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
 
 	// Splitscreen cmd
 	if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
 		&& node->player2 >= 0)
-		G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)node->player2],
+		G_MoveTiccmd(&netcmds[faketic%BACKUPTICS][(UINT8)node->player2],
 			&netbuffer->u.client2pak.cmd2, 1);
 
-	CheckTiccmdHacks(netconsole);
+	CheckTiccmdHacks(netconsole, faketic);
 	CheckConsistancy(nodenum, realstart);
 }
 
 void PT_ServerTics(SINT8 node, INT32 netconsole)
 {
 	tic_t realend, realstart;
+	servertics_pak *packet = &netbuffer->u.serverpak;
 
 	if (!netnodes[node].ingame)
 	{
@@ -223,15 +235,16 @@ void PT_ServerTics(SINT8 node, INT32 netconsole)
 		return;
 	}
 
-	realstart = netbuffer->u.serverpak.starttic;
-	realend = realstart + netbuffer->u.serverpak.numtics;
+	realstart = packet->starttic;
+	realend = realstart + packet->numtics;
 
 	realend = min(realend, gametic + CLIENTBACKUPTICS);
 	cl_packetmissed = realstart > neededtic;
 
 	if (realstart <= neededtic && realend > neededtic)
 	{
-		UINT8 *pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
+		UINT8 *pak = (UINT8 *)&packet->cmds;
+		UINT8 *txtpak = (UINT8 *)&packet->cmds[packet->numslots * packet->numtics];
 
 		for (tic_t i = realstart; i < realend; i++)
 		{
@@ -240,9 +253,9 @@ void PT_ServerTics(SINT8 node, INT32 netconsole)
 
 			// copy the tics
 			pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
-				netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
+				packet->numslots*sizeof (ticcmd_t));
 
-			CL_CopyNetCommandsFromServerPacket(i);
+			CL_CopyNetCommandsFromServerPacket(i, &txtpak);
 		}
 
 		neededtic = realend;
@@ -257,35 +270,39 @@ void PT_ServerTics(SINT8 node, INT32 netconsole)
 void CL_SendClientCmd(void)
 {
 	size_t packetsize = 0;
+	boolean mis = false;
 
 	netbuffer->packettype = PT_CLIENTCMD;
 
 	if (cl_packetmissed)
-		netbuffer->packettype++;
+	{
+		netbuffer->packettype = PT_CLIENTMIS;
+		mis = true;
+	}
+
 	netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX);
 	netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX);
 
 	if (gamestate == GS_WAITINGPLAYERS)
 	{
 		// Send PT_NODEKEEPALIVE packet
-		netbuffer->packettype += 4;
+		netbuffer->packettype = (mis ? PT_NODEKEEPALIVEMIS : PT_NODEKEEPALIVE);
 		packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16);
 		HSendPacket(servernode, false, 0, packetsize);
 	}
 	else if (gamestate != GS_NULL && (addedtogame || dedicated))
 	{
+		packetsize = sizeof (clientcmd_pak);
 		G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
 		netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
 
 		// Send a special packet with 2 cmd for splitscreen
 		if (splitscreen || botingame)
 		{
-			netbuffer->packettype += 2;
-			G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1);
+			netbuffer->packettype = (mis ? PT_CLIENT2MIS : PT_CLIENT2CMD);
 			packetsize = sizeof (client2cmd_pak);
+			G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1);
 		}
-		else
-			packetsize = sizeof (clientcmd_pak);
 
 		HSendPacket(servernode, false, 0, packetsize);
 	}
@@ -346,7 +363,7 @@ void SV_SendTics(void)
 	for (INT32 n = 1; n < MAXNETNODES; n++)
 		if (netnodes[n].ingame)
 		{
-			netnode_t *node = netnodes[n];
+			netnode_t *node = &netnodes[n];
 
 			// assert node->supposedtic>=node->tic
 			realfirsttic = node->supposedtic;
@@ -408,7 +425,7 @@ void Local_Maketic(INT32 realtics)
 	                   // and G_MapEventsToControls
 	if (!dedicated)
 		rendergametic = gametic;
-	// translate inputs (keyboard/mouse/gamepad) into game controls
+	// translate inputs (keyboard/mouse/joystick) into game controls
 	G_BuildTiccmd(&localcmds, realtics, 1);
 	if (splitscreen || botingame)
 		G_BuildTiccmd(&localcmds2, realtics, 2);
diff --git a/src/netcode/tic_command.h b/src/netcode/tic_command.h
index 289750fb30..725037216d 100644
--- a/src/netcode/tic_command.h
+++ b/src/netcode/tic_command.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2022 by Sonic Team Junior.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/snake.c b/src/snake.c
index 21e79401df..6482759ed4 100644
--- a/src/snake.c
+++ b/src/snake.c
@@ -11,6 +11,8 @@
 
 #include "snake.h"
 #include "g_input.h"
+#include "g_game.h"
+#include "i_joy.h"
 #include "m_random.h"
 #include "s_sound.h"
 #include "screen.h"
@@ -67,6 +69,9 @@ typedef struct snake_s
 	enum bonustype_s bonustype;
 	UINT8 bonusx;
 	UINT8 bonusy;
+
+	event_t *joyevents[MAXEVENTS];
+	UINT16 joyeventcount;
 } snake_t;
 
 static const char *bonuspatches[] = {
@@ -113,6 +118,8 @@ static void Initialise(snake_t *snake)
 	snake->appley = M_RandomKey(NUM_BLOCKS_Y);
 
 	snake->bonustype = BONUS_NONE;
+
+	snake->joyeventcount = 0;
 }
 
 static UINT8 GetOppositeDir(UINT8 dir)
@@ -160,18 +167,19 @@ void Snake_Update(void *opaque)
 	UINT8 oldx, oldy;
 	UINT16 i;
 	UINT16 joystate = 0;
+	static INT32 pjoyx = 0, pjoyy = 0;
 
 	snake_t *snake = opaque;
 
 	// Handle retry
-	if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER]))
+	if (snake->gameover && (PLAYER1INPUTDOWN(GC_JUMP) || gamekeydown[KEY_ENTER]))
 	{
 		Initialise(snake);
 		snake->pausepressed = true; // Avoid accidental pause on respawn
 	}
 
 	// Handle pause
-	if (G_PlayerInputDown(0, GC_PAUSE) || gamekeydown[KEY_ENTER])
+	if (PLAYER1INPUTDOWN(GC_PAUSE) || gamekeydown[KEY_ENTER])
 	{
 		if (!snake->pausepressed)
 			snake->paused = !snake->paused;
@@ -190,23 +198,58 @@ void Snake_Update(void *opaque)
 	oldx = snake->snakex[1];
 	oldy = snake->snakey[1];
 
+	// Process the input events in here dear lord
+	for (UINT16 j = 0; j < snake->joyeventcount; j++)
+	{
+		event_t *ev = snake->joyevents[j];
+		const INT32 jdeadzone = (JOYAXISRANGE * cv_digitaldeadzone.value) / FRACUNIT;
+		if (ev->y != INT32_MAX)
+		{
+			if (Joystick.bGamepadStyle || abs(ev->y) > jdeadzone)
+			{
+				if (ev->y < 0 && pjoyy >= 0)
+					joystate = 1;
+				else if (ev->y > 0 && pjoyy <= 0)
+					joystate = 2;
+				pjoyy = ev->y;
+			}
+			else
+				pjoyy = 0;
+		}
+
+		if (ev->x != INT32_MAX)
+		{
+			if (Joystick.bGamepadStyle || abs(ev->x) > jdeadzone)
+			{
+				if (ev->x < 0 && pjoyx >= 0)
+					joystate = 3;
+				else if (ev->x > 0 && pjoyx <= 0)
+					joystate = 4;
+				pjoyx = ev->x;
+			}
+			else
+				pjoyx = 0;
+		}
+	}
+	snake->joyeventcount = 0;
+
 	// Update direction
-	if (G_PlayerInputDown(0, GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3)
+	if (PLAYER1INPUTDOWN(GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3)
 	{
 		if (snake->snakelength < 2 || x <= oldx)
 			snake->snakedir[0] = 1;
 	}
-	else if (G_PlayerInputDown(0, GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4)
+	else if (PLAYER1INPUTDOWN(GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4)
 	{
 		if (snake->snakelength < 2 || x >= oldx)
 			snake->snakedir[0] = 2;
 	}
-	else if (G_PlayerInputDown(0, GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1)
+	else if (PLAYER1INPUTDOWN(GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1)
 	{
 		if (snake->snakelength < 2 || y <= oldy)
 			snake->snakedir[0] = 3;
 	}
-	else if (G_PlayerInputDown(0, GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2)
+	else if (PLAYER1INPUTDOWN(GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2)
 	{
 		if (snake->snakelength < 2 || y >= oldy)
 			snake->snakedir[0] = 4;
@@ -533,3 +576,18 @@ void Snake_Free(void **opaque)
 		*opaque = NULL;
 	}
 }
+
+// I'm screaming the hack is clean - ashi
+boolean Snake_JoyGrabber(void *opaque, event_t *ev)
+{
+	snake_t *snake = opaque;
+
+	if (ev->type == ev_joystick  && ev->key == 0)
+	{
+		snake->joyevents[snake->joyeventcount] = ev;
+		snake->joyeventcount++;
+		return true;
+	}
+	else
+		return false;
+}
diff --git a/src/snake.h b/src/snake.h
index a3106bb0f0..6bca338e96 100644
--- a/src/snake.h
+++ b/src/snake.h
@@ -12,9 +12,12 @@
 #ifndef __SNAKE__
 #define __SNAKE__
 
+#include "d_event.h"
+
 void Snake_Allocate(void **opaque);
 void Snake_Update(void *opaque);
 void Snake_Draw(void *opaque);
 void Snake_Free(void **opaque);
+boolean Snake_JoyGrabber(void *opaque, event_t *ev);
 
 #endif
-- 
GitLab


From c9efaf3a1a036493e8bc3e7f1c576973a7e61d46 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Tue, 1 Aug 2023 17:52:06 +0100
Subject: [PATCH 309/518] fix warping from SP pause level select breaking if
 game was started with no save slot (such as from extras menu)

---
 src/m_menu.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index e10bb75472..3451b90d63 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -7112,8 +7112,22 @@ static void M_LevelSelectWarp(INT32 choice)
 	fromlevelselect = true;
 
 	if (currentMenu == &SP_LevelSelectDef || currentMenu == &SP_PauseLevelSelectDef)
-		G_LoadGame((UINT32)cursaveslot, startmap); // reload from SP save data: this is needed to keep score/lives/continues from reverting to defaults
-	else
+	{
+		if (cursaveslot > 0) // do we have a save slot to load?
+			G_LoadGame((UINT32)cursaveslot, startmap); // reload from SP save data: this is needed to keep score/lives/continues from reverting to defaults
+		else // no save slot, start new game but keep the current skin
+		{
+			M_ClearMenus(true);
+
+			G_DeferedInitNew(false, G_BuildMapName(startmap), cv_skin.value, false, fromlevelselect); // Not sure about using cv_skin here, but it seems fine in testing.
+			COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this
+
+			if (levelselect.rows)
+				Z_Free(levelselect.rows);
+			levelselect.rows = NULL;
+		}
+	}
+	else // start new game
 	{
 		cursaveslot = 0;
 		M_SetupChoosePlayer(0);
-- 
GitLab


From fd4c50589d90c3ca97d6cd781bd79c6f21983efe Mon Sep 17 00:00:00 2001
From: SteelT <steeltitanium1@gmail.com>
Date: Tue, 1 Aug 2023 15:23:14 -0400
Subject: [PATCH 310/518] Don't set recordscore to 0 in G_PlayerFinishLevel

Fixes the recordscore being reset on intermission start
---
 src/g_game.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/g_game.c b/src/g_game.c
index df6bb9679c..04b1ffe4da 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2523,7 +2523,6 @@ static inline void G_PlayerFinishLevel(INT32 player)
 
 	memset(p->powers, 0, sizeof (p->powers));
 	p->ringweapons = 0;
-	p->recordscore = 0;
 
 	p->mo->flags2 &= ~MF2_SHADOW; // cancel invisibility
 	P_FlashPal(p, 0, 0); // Resets
-- 
GitLab


From 24cad832875e0fda15fbf3aab4c83e3503952178 Mon Sep 17 00:00:00 2001
From: SMS Alfredo <65426124+SMS-Alfredo@users.noreply.github.com>
Date: Tue, 1 Aug 2023 17:43:39 -0500
Subject: [PATCH 311/518] Make both sign state and sprite2 disable models

---
 src/hardware/hw_md2.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 4ce3b38f6b..d005f00377 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1146,7 +1146,7 @@ static void HWR_GetBlendedTexture(patch_t *patch, patch_t *blendpatch, INT32 ski
 static boolean HWR_AllowModel(mobj_t *mobj)
 {
 	// Signpost overlay. Not needed.
-	if (mobj->sprite2 == SPR2_SIGN)
+	if (mobj->sprite2 == SPR2_SIGN || mobj->state-states == S_PLAY_SIGN)
 		return false;
 
 	// Otherwise, render the model.
-- 
GitLab


From 8990b83071f7764afb3b7f0250928c712e4e767e Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Wed, 2 Aug 2023 17:05:38 +0200
Subject: [PATCH 312/518] Handle empty/"-" music lump name on music change

---
 src/p_spec.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index 6709515201..169f129b95 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2561,11 +2561,13 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				// Change the music and apply position/fade operations
 				else
 				{
-					if (!line->stringargs[0])
-						break;
-
-					strncpy(mapmusname, line->stringargs[0], 7);
-					mapmusname[6] = 0;
+					if (!line->stringargs[0] || !strcmp(line->stringargs[0], "-"))
+						strcpy(mapmusname, "");
+					else
+					{
+						strncpy(mapmusname, line->stringargs[0], 7);
+						mapmusname[6] = 0;
+					}
 
 					mapmusflags = tracknum & MUSIC_TRACKMASK;
 					if (!(line->args[0] & TMM_NORELOAD))
-- 
GitLab


From 33c7a930a7432b7725e67416c139cebd3bbf04a9 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Wed, 2 Aug 2023 11:31:05 -0400
Subject: [PATCH 313/518] Total score starts at record score

Means there's actually feedback for how much score you got in the current level.
---
 src/y_inter.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/y_inter.c b/src/y_inter.c
index 1b1f49e0b7..5e071171f3 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -2044,7 +2044,7 @@ static void Y_AwardCoopBonuses(void)
 	y_bonus_t localbonuses[4];
 
 	// set score/total first
-	data.coop.total = 0;
+	data.coop.total = players[consoleplayer].recordscore;
 	data.coop.score = players[consoleplayer].score;
 	data.coop.gotperfbonus = -1;
 	memset(data.coop.bonuses, 0, sizeof(data.coop.bonuses));
-- 
GitLab


From b120d00c9f0987aeb2c29b42d1eb76b894d301ea Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Thu, 3 Aug 2023 16:39:17 -0700
Subject: [PATCH 314/518] Fix glibc 2.38 compile

glibc 2.38 added strlcpy and strlcat.
---
 src/doomtype.h | 7 ++++++-
 src/string.c   | 2 +-
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/doomtype.h b/src/doomtype.h
index f6c236e20b..dd5fb0f8dc 100644
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -122,6 +122,11 @@ int endswith (const char *base, const char *tag);
 #define HAVE_DOSSTR_FUNCS
 #endif
 
+// glibc 2.38: added strlcpy and strlcat to _DEFAULT_SOURCE
+#if defined (__APPLE__) || __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38)
+#define HAVE_STRLCPY
+#endif
+
 #ifndef HAVE_DOSSTR_FUNCS
 int strupr(char *n); // from dosstr.c
 int strlwr(char *n); // from dosstr.c
@@ -129,7 +134,7 @@ int strlwr(char *n); // from dosstr.c
 
 #include <stddef.h> // for size_t
 
-#ifndef __APPLE__
+#ifndef HAVE_STRLCPY
 size_t strlcat(char *dst, const char *src, size_t siz);
 size_t strlcpy(char *dst, const char *src, size_t siz);
 #endif
diff --git a/src/string.c b/src/string.c
index dd3080a979..cbc8ea5826 100644
--- a/src/string.c
+++ b/src/string.c
@@ -15,7 +15,7 @@
 #include <string.h>
 #include "doomdef.h"
 
-#if !defined (__APPLE__)
+#ifndef HAVE_STRLCPY
 
 // Like the OpenBSD version, but it doesn't check for src not being a valid
 // C string.
-- 
GitLab


From 39175c6c7044d5a105f2568902539bb6070df72f Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Fri, 4 Aug 2023 14:36:56 -0400
Subject: [PATCH 315/518] Improved version checks for demo file lists

No longer relies on the demoversion global variable (which was incorrect for a couple instances, like ghosts), now we pass the version to the file list functions. Also don't set any demo global variables when checking a demo for valid files before playback. Also also, exit said file check function asap if its a title demo instead of doing it after we've already opened the file.
---
 src/g_demo.c | 67 +++++++++++++++++++++++++---------------------------
 1 file changed, 32 insertions(+), 35 deletions(-)

diff --git a/src/g_demo.c b/src/g_demo.c
index adb8e891d6..3c300d78c4 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -1617,7 +1617,7 @@ void G_BeginMetal(void)
 	oldmetal.angle = mo->angle>>24;
 }
 
-static void G_LoadDemoExtraFiles(UINT8 **pp)
+static void G_LoadDemoExtraFiles(UINT8 **pp, UINT16 this_demo_version)
 {
 	UINT16 totalfiles;
 	char filename[MAX_WADPATH];
@@ -1627,6 +1627,12 @@ static void G_LoadDemoExtraFiles(UINT8 **pp)
 	boolean alreadyloaded;
 	UINT16 i, j;
 
+	if (this_demo_version < 0x0010)
+	{
+		// demo has no file list
+		return;
+	}
+
 	totalfiles = READUINT16((*pp));
 	for (i = 0; i < totalfiles; ++i)
 	{
@@ -1686,12 +1692,12 @@ static void G_LoadDemoExtraFiles(UINT8 **pp)
 	}
 }
 
-static void G_SkipDemoExtraFiles(UINT8 **pp)
+static void G_SkipDemoExtraFiles(UINT8 **pp, UINT16 this_demo_version)
 {
 	UINT16 totalfiles;
 	UINT16 i;
 
-	if (demoversion < 0x0010)
+	if (this_demo_version < 0x0010)
 	{
 		// demo has no file list
 		return;
@@ -1707,7 +1713,7 @@ static void G_SkipDemoExtraFiles(UINT8 **pp)
 
 // G_CheckDemoExtraFiles: checks if our loaded WAD list matches the demo's.
 // Enabling quick prevents filesystem checks to see if needed files are available to load.
-static UINT8 G_CheckDemoExtraFiles(UINT8 **pp, boolean quick)
+static UINT8 G_CheckDemoExtraFiles(UINT8 **pp, boolean quick, UINT16 this_demo_version)
 {
 	UINT16 totalfiles, filesloaded, nmusfilecount;
 	char filename[MAX_WADPATH];
@@ -1717,7 +1723,7 @@ static UINT8 G_CheckDemoExtraFiles(UINT8 **pp, boolean quick)
 	UINT16 i, j;
 	UINT8 error = DFILE_ERROR_NONE;
 
-	if (demoversion < 0x0010)
+	if (this_demo_version < 0x0010)
 	{
 		// demo has no file list
 		return DFILE_ERROR_NONE;
@@ -1816,10 +1822,9 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
 	UINT8 *buffer,*p;
 	UINT8 flags;
 	UINT32 oldtime, newtime, oldscore, newscore;
-	UINT16 oldrings, newrings, oldversion;
+	UINT16 oldrings, newrings, oldversion, newversion;
 	size_t bufsize ATTRUNUSED;
 	UINT8 c;
-	UINT16 s ATTRUNUSED;
 	UINT8 aflags = 0;
 
 	// load the new file
@@ -1835,15 +1840,15 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
 	I_Assert(c == VERSION);
 	c = READUINT8(p); // SUBVERSION
 	I_Assert(c == SUBVERSION);
-	s = READUINT16(p);
-	I_Assert(s >= 0x000c);
+	newversion = READUINT16(p);
+	I_Assert(newversion == DEMOVERSION);
 	p += 16; // demo checksum
 	I_Assert(!memcmp(p, "PLAY", 4));
 	p += 4; // PLAY
 	p += 2; // gamemap
 	p += 16; // map md5
 	flags = READUINT8(p); // demoflags
-	G_SkipDemoExtraFiles(&p);
+	G_SkipDemoExtraFiles(&p, newversion);
 	aflags = flags & (DF_RECORDATTACK|DF_NIGHTSATTACK);
 	I_Assert(aflags);
 	if (flags & DF_RECORDATTACK)
@@ -1909,7 +1914,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
 		p += 2; // gamemap
 	p += 16; // mapmd5
 	flags = READUINT8(p);
-	G_SkipDemoExtraFiles(&p);
+	G_SkipDemoExtraFiles(&p, oldversion);
 	if (!(flags & aflags))
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("File '%s' not from same game mode. It will be overwritten.\n"), oldname);
@@ -2074,26 +2079,22 @@ void G_DoPlayDemo(char *defdemoname)
 
 	demoflags = READUINT8(demo_p);
 
-	if (demoversion < 0x0010)
-	{
-		; // Don't do anything with files.
-	}
-	else if (titledemo)
+	if (titledemo)
 	{
 		// Titledemos should always play and ought to always be compatible with whatever wadlist is running.
-		G_SkipDemoExtraFiles(&demo_p);
+		G_SkipDemoExtraFiles(&demo_p, demoversion);
 	}
 	else if (demofileoverride == DFILE_OVERRIDE_LOAD)
 	{
-		G_LoadDemoExtraFiles(&demo_p);
+		G_LoadDemoExtraFiles(&demo_p, demoversion);
 	}
 	else if (demofileoverride == DFILE_OVERRIDE_SKIP)
 	{
-		G_SkipDemoExtraFiles(&demo_p);
+		G_SkipDemoExtraFiles(&demo_p, demoversion);
 	}
 	else
 	{
-		UINT8 error = G_CheckDemoExtraFiles(&demo_p, false);
+		UINT8 error = G_CheckDemoExtraFiles(&demo_p, false, demoversion);
 
 		if (error)
 		{
@@ -2302,6 +2303,13 @@ UINT8 G_CheckDemoForError(char *defdemoname)
 {
 	lumpnum_t l;
 	char *n,*pdemoname;
+	UINT16 our_demo_version;
+
+	if (titledemo)
+	{
+		// Don't do anything with files for these.
+		return DFILE_ERROR_NONE;
+	}
 
 	n = defdemoname+strlen(defdemoname);
 	while (*n != '/' && *n != '\\' && n != defdemoname)
@@ -2340,9 +2348,8 @@ UINT8 G_CheckDemoForError(char *defdemoname)
 
 	demo_p++; // version
 	demo_p++; // subversion
-	demoversion = READUINT16(demo_p);
-	demo_forwardmove_rng = (demoversion < 0x0010);
-	switch(demoversion)
+	our_demo_version = READUINT16(demo_p);
+	switch(our_demo_version)
 	{
 	case 0x000d:
 	case 0x000e:
@@ -2368,17 +2375,7 @@ UINT8 G_CheckDemoForError(char *defdemoname)
 
 	demo_p++; // demoflags
 
-	// Don't do anything with files.
-	if (demoversion < 0x0010)
-	{
-		return DFILE_ERROR_NONE;
-	}
-	else if (titledemo)
-	{
-		return DFILE_ERROR_NONE;
-	}
-
-	return G_CheckDemoExtraFiles(&demo_p, true);
+	return G_CheckDemoExtraFiles(&demo_p, our_demo_version);
 }
 
 void G_AddGhost(char *defdemoname)
@@ -2487,7 +2484,7 @@ void G_AddGhost(char *defdemoname)
 		return;
 	}
 
-	G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts.
+	G_SkipDemoExtraFiles(&p, ghostversion); // Don't wanna modify the file list for ghosts.
 
 	switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT)
 	{
-- 
GitLab


From 6780f895745615bfdb1138f06120f6deb9397c56 Mon Sep 17 00:00:00 2001
From: SteelT <steeltitanium1@gmail.com>
Date: Fri, 4 Aug 2023 15:59:47 -0400
Subject: [PATCH 316/518] Add missing param to G_CheckDemoExtraFiles in
 G_CheckDemoForError

---
 src/g_demo.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/g_demo.c b/src/g_demo.c
index 3c300d78c4..4b9ff56e80 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -2375,7 +2375,7 @@ UINT8 G_CheckDemoForError(char *defdemoname)
 
 	demo_p++; // demoflags
 
-	return G_CheckDemoExtraFiles(&demo_p, our_demo_version);
+	return G_CheckDemoExtraFiles(&demo_p, true, our_demo_version);
 }
 
 void G_AddGhost(char *defdemoname)
-- 
GitLab


From f11538c478f923de087a027f9c5cf05926c90a55 Mon Sep 17 00:00:00 2001
From: Sal <tehrealsalt@gmail.com>
Date: Fri, 4 Aug 2023 20:05:35 +0000
Subject: [PATCH 317/518] Make UDMF scale compatible with ZDoom's spec

---
 src/doomdata.h    |  1 +
 src/lua_mobjlib.c | 18 +++++++++++++++++-
 src/p_mobj.c      |  3 +++
 src/p_setup.c     | 16 ++++++++++++++--
 4 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/src/doomdata.h b/src/doomdata.h
index 4c5bdefaf9..576fe03e29 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -211,6 +211,7 @@ typedef struct
 	UINT8 extrainfo;
 	taglist_t tags;
 	fixed_t scale;
+	fixed_t spritexscale, spriteyscale;
 	INT32 args[NUMMAPTHINGARGS];
 	char *stringargs[NUMMAPTHINGSTRINGARGS];
 	struct mobj_s *mobj;
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index fd8dcec925..fddf958beb 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -925,6 +925,8 @@ enum mapthing_e {
 	mapthing_type,
 	mapthing_options,
 	mapthing_scale,
+	mapthing_spritexscale,
+	mapthing_spriteyscale,
 	mapthing_z,
 	mapthing_extrainfo,
 	mapthing_tag,
@@ -944,6 +946,8 @@ const char *const mapthing_opt[] = {
 	"type",
 	"options",
 	"scale",
+	"spritexscale",
+	"spriteyscale",
 	"z",
 	"extrainfo",
 	"tag",
@@ -999,7 +1003,13 @@ static int mapthing_get(lua_State *L)
 			lua_pushinteger(L, mt->options);
 			break;
 		case mapthing_scale:
-			lua_pushinteger(L, mt->scale);
+			lua_pushfixed(L, mt->scale);
+			break;
+		case mapthing_spritexscale:
+			lua_pushfixed(L, mt->spritexscale);
+			break;
+		case mapthing_spriteyscale:
+			lua_pushfixed(L, mt->spriteyscale);
 			break;
 		case mapthing_z:
 			lua_pushinteger(L, mt->z);
@@ -1072,6 +1082,12 @@ static int mapthing_set(lua_State *L)
 		case mapthing_scale:
 			mt->scale = luaL_checkfixed(L, 3);
 			break;
+		case mapthing_spritexscale:
+			mt->spritexscale = luaL_checkfixed(L, 3);
+			break;
+		case mapthing_spriteyscale:
+			mt->spriteyscale = luaL_checkfixed(L, 3);
+			break;
 		case mapthing_z:
 			mt->z = (INT16)luaL_checkinteger(L, 3);
 			break;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 8c0fa17ad9..2a40175dc7 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -13332,6 +13332,9 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y,
 	P_SetScale(mobj, FixedMul(mobj->scale, mthing->scale));
 	mobj->destscale = FixedMul(mobj->destscale, mthing->scale);
 
+	mobj->spritexscale = mthing->spritexscale;
+	mobj->spriteyscale = mthing->spriteyscale;
+
 	if (!P_SetupSpawnedMapThing(mthing, mobj, &doangle))
 		return mobj;
 
diff --git a/src/p_setup.c b/src/p_setup.c
index aac02417d4..cbb817d83b 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1510,6 +1510,7 @@ static void P_LoadThings(UINT8 *data)
 		mt->extrainfo = (UINT8)(mt->type >> 12);
 		Tag_FSet(&mt->tags, 0);
 		mt->scale = FRACUNIT;
+		mt->spritexscale = mt->spriteyscale = FRACUNIT;
 		memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
 		memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
 		mt->pitch = mt->roll = 0;
@@ -2013,7 +2014,13 @@ static void ParseTextmapThingParameter(UINT32 i, const char *param, const char *
 		mapthings[i].roll = atol(val);
 	else if (fastcmp(param, "type"))
 		mapthings[i].type = atol(val);
-	else if (fastcmp(param, "scale") || fastcmp(param, "scalex") || fastcmp(param, "scaley"))
+	else if (fastcmp(param, "scale"))
+		mapthings[i].spritexscale = mapthings[i].spriteyscale = FLOAT_TO_FIXED(atof(val));
+	else if (fastcmp(param, "scalex"))
+		mapthings[i].spritexscale = FLOAT_TO_FIXED(atof(val));
+	else if (fastcmp(param, "scaley"))
+		mapthings[i].spriteyscale = FLOAT_TO_FIXED(atof(val));
+	else if (fastcmp(param, "mobjscale"))
 		mapthings[i].scale = FLOAT_TO_FIXED(atof(val));
 	// Flags
 	else if (fastcmp(param, "flip") && fastcmp("true", val))
@@ -2431,8 +2438,12 @@ static void P_WriteTextmap(void)
 			fprintf(f, "roll = %d;\n", wmapthings[i].roll);
 		if (wmapthings[i].type != 0)
 			fprintf(f, "type = %d;\n", wmapthings[i].type);
+		if (wmapthings[i].spritexscale != FRACUNIT)
+			fprintf(f, "scalex = %f;\n", FIXED_TO_FLOAT(wmapthings[i].spritexscale));
+		if (wmapthings[i].spriteyscale != FRACUNIT)
+			fprintf(f, "scaley = %f;\n", FIXED_TO_FLOAT(wmapthings[i].spriteyscale));
 		if (wmapthings[i].scale != FRACUNIT)
-			fprintf(f, "scale = %f;\n", FIXED_TO_FLOAT(wmapthings[i].scale));
+			fprintf(f, "mobjscale = %f;\n", FIXED_TO_FLOAT(wmapthings[i].scale));
 		if (wmapthings[i].options & MTF_OBJECTFLIP)
 			fprintf(f, "flip = true;\n");
 		for (j = 0; j < NUMMAPTHINGARGS; j++)
@@ -2978,6 +2989,7 @@ static void P_LoadTextmap(void)
 		mt->extrainfo = 0;
 		Tag_FSet(&mt->tags, 0);
 		mt->scale = FRACUNIT;
+		mt->spritexscale = mt->spriteyscale = FRACUNIT;
 		memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
 		memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
 		mt->mobj = NULL;
-- 
GitLab


From d06098c0aaa8712f018dd6cf05bb12be1c2bac09 Mon Sep 17 00:00:00 2001
From: MIDIMan <miditheman2.0@gmail.com>
Date: Sat, 5 Aug 2023 20:22:11 -0400
Subject: [PATCH 318/518] Changed P_ConvertBinaryLinedefTypes to use args[1]
 instead of args[3] for linedef type 442

---
 src/p_setup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index cbb817d83b..0c8ea656ec 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -5467,7 +5467,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 			break;
 		case 442: //Change object type state
 			lines[i].args[0] = tag;
-			lines[i].args[3] = (lines[i].sidenum[1] == 0xffff) ? 1 : 0;
+			lines[i].args[1] = (lines[i].sidenum[1] == 0xffff) ? 1 : 0;
 			break;
 		case 443: //Call Lua function
 			if (lines[i].stringargs[0] == NULL)
-- 
GitLab


From 3377fa986a19c91dca3488b774f60039a7638624 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Mon, 7 Aug 2023 13:30:32 -0300
Subject: [PATCH 319/518] Remember the player's color after a team gametype

---
 src/am_map.c   |  1 -
 src/d_netcmd.c | 41 +----------------------------------------
 src/g_game.c   | 19 -------------------
 src/p_enemy.c  |  4 ++--
 src/p_inter.c  |  6 +++---
 src/p_local.h  |  1 +
 src/p_mobj.c   | 11 +----------
 src/p_user.c   | 21 +++++++++++++++++----
 src/st_stuff.c |  9 ++++++---
 9 files changed, 31 insertions(+), 82 deletions(-)

diff --git a/src/am_map.c b/src/am_map.c
index 331d1a5e0d..df3a45cfff 100644
--- a/src/am_map.c
+++ b/src/am_map.c
@@ -1071,7 +1071,6 @@ static inline void AM_drawPlayers(void)
 		return;
 	}
 
-	// multiplayer (how??)
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
 		if (!playeringame[i] || players[i].spectator)
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 77a271825e..a42fc19324 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1233,15 +1233,6 @@ static void SendNameAndColor(void)
 
 	p = buf;
 
-	// normal player colors
-	if (G_GametypeHasTeams())
-	{
-		if (players[consoleplayer].ctfteam == 1 && cv_playercolor.value != skincolor_redteam)
-			CV_StealthSetValue(&cv_playercolor, skincolor_redteam);
-		else if (players[consoleplayer].ctfteam == 2 && cv_playercolor.value != skincolor_blueteam)
-			CV_StealthSetValue(&cv_playercolor, skincolor_blueteam);
-	}
-
 	// don't allow inaccessible colors
 	if (!skincolors[cv_playercolor.value].accessible)
 	{
@@ -1367,16 +1358,6 @@ static void SendNameAndColor2(void)
 	else // HACK
 		secondplaya = 1;
 
-	// normal player colors
-	if (G_GametypeHasTeams())
-	{
-		if (players[secondplaya].ctfteam == 1 && cv_playercolor2.value != skincolor_redteam)
-			CV_StealthSetValue(&cv_playercolor2, skincolor_redteam);
-		else if (players[secondplaya].ctfteam == 2 && cv_playercolor2.value != skincolor_blueteam)
-			CV_StealthSetValue(&cv_playercolor2, skincolor_blueteam);
-	}
-
-	// don't allow inaccessible colors
 	if (!skincolors[cv_playercolor2.value].accessible)
 	{
 		if (players[secondplaya].skincolor && skincolors[players[secondplaya].skincolor].accessible)
@@ -1498,7 +1479,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
 	// set color
 	p->skincolor = color % numskincolors;
 	if (p->mo)
-		p->mo->color = (UINT16)p->skincolor;
+		p->mo->color = P_GetPlayerColor(p);
 
 	// normal player colors
 	if (server && (p != &players[consoleplayer] && p != &players[secondarydisplayplayer]))
@@ -1507,15 +1488,6 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
 		UINT32 unlockShift = 0;
 		UINT32 i;
 
-		// team colors
-		if (G_GametypeHasTeams())
-		{
-			if (p->ctfteam == 1 && p->skincolor != skincolor_redteam)
-				kick = true;
-			else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam)
-				kick = true;
-		}
-
 		// don't allow inaccessible colors
 		if (skincolors[p->skincolor].accessible == false)
 			kick = true;
@@ -2904,17 +2876,6 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
 		displayplayer = consoleplayer;
 	}
 
-	if (G_GametypeHasTeams())
-	{
-		if (NetPacket.packet.newteam)
-		{
-			if (playernum == consoleplayer) //CTF and Team Match colors.
-				CV_SetValue(&cv_playercolor, NetPacket.packet.newteam + 5);
-			else if (playernum == secondarydisplayplayer)
-				CV_SetValue(&cv_playercolor2, NetPacket.packet.newteam + 5);
-		}
-	}
-
 	// In tag, check to see if you still have a game.
 	if (G_TagGametype())
 		P_CheckSurvivors();
diff --git a/src/g_game.c b/src/g_game.c
index 04b1ffe4da..c49b202adf 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2748,25 +2748,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 	//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;
 
diff --git a/src/p_enemy.c b/src/p_enemy.c
index eebb65f3cb..93c828fbec 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -5248,7 +5248,7 @@ void A_SignPlayer(mobj_t *actor)
 			return;
 
 		skin = &skins[actor->target->player->skin];
-		facecolor = actor->target->player->skincolor;
+		facecolor = P_GetPlayerColor(actor->target->player);
 
 		if (signcolor)
 			;
@@ -9059,7 +9059,7 @@ void A_Dye(mobj_t *actor)
 	if (!color)
 	{
 		target->colorized = false;
-		target->color = target->player ? target->player->skincolor : SKINCOLOR_NONE;
+		target->color = target->player ? P_GetPlayerColor(target->player) : SKINCOLOR_NONE;
 	}
 	else if (!(target->player))
 	{
diff --git a/src/p_inter.c b/src/p_inter.c
index 4d22ba3438..2d132b6844 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2630,7 +2630,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 			}
 		}
 
-		target->color = target->player->skincolor;
+		target->color = P_GetPlayerColor(target->player);
 		target->colorized = false;
 		G_GhostAddColor(GHC_NORMAL);
 
@@ -3323,7 +3323,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
 
 	// Get rid of shield
 	player->powers[pw_shield] = SH_NONE;
-	player->mo->color = player->skincolor;
+	player->mo->color = P_GetPlayerColor(player);
 
 	// Get rid of emeralds
 	player->powers[pw_emeralds] = 0;
@@ -3440,7 +3440,7 @@ void P_RemoveShield(player_t *player)
 	{ // Second layer shields
 		if (((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) && !(player->powers[pw_super] || (mariomode && player->powers[pw_invulnerability])))
 		{
-			player->mo->color = player->skincolor;
+			player->mo->color = P_GetPlayerColor(player);
 			G_GhostAddColor(GHC_NORMAL);
 		}
 		player->powers[pw_shield] = SH_NONE;
diff --git a/src/p_local.h b/src/p_local.h
index 563e257d8f..4b330184bd 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -146,6 +146,7 @@ void P_ForceLocalAngle(player_t *player, angle_t angle);
 boolean P_PlayerFullbright(player_t *player);
 boolean P_PlayerCanEnterSpinGaps(player_t *player);
 boolean P_PlayerShouldUseSpinHeight(player_t *player);
+UINT16 P_GetPlayerColor(player_t *player);
 
 boolean P_IsObjectInGoop(mobj_t *mo);
 boolean P_IsObjectOnGround(mobj_t *mo);
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 2a40175dc7..ff5e5006ed 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -11564,8 +11564,6 @@ void P_SpawnPlayer(INT32 playernum)
 				// Spawn as a spectator,
 				// yes even in splitscreen mode
 				p->spectator = true;
-				if (playernum&1) p->skincolor = skincolor_redteam;
-				else             p->skincolor = skincolor_blueteam;
 
 				// but immediately send a team change packet.
 				NetPacket.packet.playernum = playernum;
@@ -11585,13 +11583,6 @@ void P_SpawnPlayer(INT32 playernum)
 		// 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;
 	}
 
 	if ((netgame || multiplayer) && ((gametyperules & GTR_SPAWNINVUL) || leveltime) && !p->spectator && !(maptol & TOL_NIGHTS))
@@ -11603,7 +11594,7 @@ void P_SpawnPlayer(INT32 playernum)
 	mobj->angle = 0;
 
 	// set color translations for player sprites
-	mobj->color = p->skincolor;
+	mobj->color = P_GetPlayerColor(p);
 
 	// set 'spritedef' override in mobj for player skins.. (see ProjectSprite)
 	// (usefulness: when body mobj is detached from player (who respawns),
diff --git a/src/p_user.c b/src/p_user.c
index cf48182987..39626b5e21 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -686,7 +686,7 @@ static void P_DeNightserizePlayer(player_t *player)
 
 	player->mo->skin = &skins[player->skin];
 	player->followitem = skins[player->skin].followitem;
-	player->mo->color = player->skincolor;
+	player->mo->color = P_GetPlayerColor(player);
 	G_GhostAddColor(GHC_RETURNSKIN);
 
 	// Restore aiming angle
@@ -3029,7 +3029,7 @@ static void P_CheckInvincibilityTimer(player_t *player)
 				}
 				else
 				{
-					player->mo->color = player->skincolor;
+					player->mo->color = P_GetPlayerColor(player);
 					G_GhostAddColor(GHC_NORMAL);
 				}
 			}
@@ -4302,7 +4302,7 @@ static void P_DoSuperStuff(player_t *player)
 			}
 			else
 			{
-				player->mo->color = player->skincolor;
+				player->mo->color = P_GetPlayerColor(player);
 				G_GhostAddColor(GHC_NORMAL);
 			}
 
@@ -4352,7 +4352,7 @@ static void P_DoSuperStuff(player_t *player)
 			}
 			else
 			{
-				player->mo->color = player->skincolor;
+				player->mo->color = P_GetPlayerColor(player);
 				G_GhostAddColor(GHC_NORMAL);
 			}
 
@@ -13113,3 +13113,16 @@ boolean P_PlayerShouldUseSpinHeight(player_t *player)
 			&& player->dashmode >= DASHMODE_THRESHOLD && player->mo->state-states == S_PLAY_DASH)
 		|| JUMPCURLED(player));
 }
+
+UINT16 P_GetPlayerColor(player_t *player)
+{
+	if (G_GametypeHasTeams() && player->ctfteam)
+	{
+		if (player->ctfteam == 1)
+			return skincolor_redteam;
+		else if (player->ctfteam == 2)
+			return skincolor_blueteam;
+	}
+
+	return player->skincolor;
+}
diff --git a/src/st_stuff.c b/src/st_stuff.c
index c6e6befc62..1a6e944c8f 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -827,6 +827,8 @@ static void ST_drawLivesArea(void)
 	V_DrawSmallScaledPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
 		hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, livesback);
 
+	UINT16 facecolor = P_GetPlayerColor(stplyr);
+
 	// face
 	if (stplyr->spectator)
 	{
@@ -856,10 +858,10 @@ static void ST_drawLivesArea(void)
 			}
 		}
 	}
-	else if (stplyr->skincolor)
+	else if (facecolor)
 	{
 		// skincolor face
-		UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE);
+		UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, facecolor, GTC_CACHE);
 		V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
 			hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, faceprefix[stplyr->skin], colormap);
 	}
@@ -1030,7 +1032,8 @@ static void ST_drawLivesArea(void)
 
 static void ST_drawInput(void)
 {
-	const INT32 accent = V_SNAPTOLEFT|V_SNAPTOBOTTOM|(stplyr->skincolor ? skincolors[stplyr->skincolor].ramp[4] : 0);
+	UINT16 color = P_GetPlayerColor(stplyr);
+	const INT32 accent = V_SNAPTOLEFT|V_SNAPTOBOTTOM|(color ? skincolors[color].ramp[4] : 0);
 	INT32 col;
 	UINT8 offs;
 
-- 
GitLab


From b92dc42848a5023a5be32b2185ff9f5520b1ad14 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Sat, 5 Aug 2023 22:15:38 -0300
Subject: [PATCH 320/518] Add gametype parameter to G_SetCustomExitVars and
 G_ExitLevel

(cherry picked from commit 86367e4ec123f3912a91d41a626c11bf3ad00761)
---
 src/d_netcmd.c    |  9 ++++-----
 src/doomstat.h    |  1 +
 src/g_game.c      | 32 +++++++++++++++++++++++---------
 src/lua_baselib.c | 13 ++++++++-----
 4 files changed, 36 insertions(+), 19 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 77a271825e..967a69cc0d 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -2120,11 +2120,13 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
 
 	lastgametype = gametype;
 	gametype = READUINT8(*cp);
-	G_SetGametype(gametype); // I fear putting that macro as an argument
 
 	if (gametype < 0 || gametype >= gametypecount)
 		gametype = lastgametype;
-	else if (gametype != lastgametype)
+	else
+		G_SetGametype(gametype);
+
+	if (gametype != lastgametype)
 		D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
 
 	skipprecutscene = ((flags & (1<<2)) != 0);
@@ -4252,9 +4254,6 @@ void D_GameTypeChanged(INT32 lastgametype)
 	else if (!multiplayer && !netgame)
 	{
 		G_SetGametype(GT_COOP);
-		// These shouldn't matter anymore
-		//CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue);
-		//CV_SetValue(&cv_itemrespawn, 0);
 	}
 
 	// reset timelimit and pointlimit in race/coop, prevent stupid cheats
diff --git a/src/doomstat.h b/src/doomstat.h
index a812cc304f..fdd0d0b834 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -249,6 +249,7 @@ extern textprompt_t *textprompts[MAX_PROMPTS];
 // For the Custom Exit linedef.
 extern INT16 nextmapoverride;
 extern UINT8 skipstats;
+extern INT16 nextgametype;
 
 extern UINT32 ssspheres; //  Total # of spheres in a level
 
diff --git a/src/g_game.c b/src/g_game.c
index 04b1ffe4da..ecae11f04d 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -156,6 +156,7 @@ textprompt_t *textprompts[MAX_PROMPTS];
 
 INT16 nextmapoverride;
 UINT8 skipstats;
+INT16 nextgametype = -1;
 
 // Pointers to each CTF flag
 mobj_t *redflag;
@@ -3461,9 +3462,7 @@ UINT32 gametypedefaultrules[NUMGAMETYPES] =
 };
 
 //
-// G_SetGametype
-//
-// Set a new gametype, also setting gametype rules accordingly. Yay!
+// Sets a new gametype.
 //
 void G_SetGametype(INT16 gtype)
 {
@@ -4053,6 +4052,13 @@ static void G_DoCompleted(void)
 			nextmap = 1100-1; // No infinite loop for you
 	}
 
+	INT16 gametype_to_use;
+
+	if (nextgametype >= 0 && nextgametype < gametypecount)
+		gametype_to_use = nextgametype;
+	else
+		gametype_to_use = gametype;
+
 	// If nextmap is actually going to get used, make sure it points to
 	// a map of the proper gametype -- skip levels that don't support
 	// the current gametype. (Helps avoid playing boss levels in Race,
@@ -4061,8 +4067,8 @@ static void G_DoCompleted(void)
 	{
 		if (nextmap >= 0 && nextmap < NUMMAPS)
 		{
-			register INT16 cm = nextmap;
-			UINT32 tolflag = G_TOLFlag(gametype);
+			INT16 cm = nextmap;
+			UINT32 tolflag = G_TOLFlag(gametype_to_use);
 			UINT8 visitedmap[(NUMMAPS+7)/8];
 
 			memset(visitedmap, 0, sizeof (visitedmap));
@@ -4142,7 +4148,7 @@ static void G_DoCompleted(void)
 		if (cv_advancemap.value == 0) // Stay on same map.
 			nextmap = prevmap;
 		else if (cv_advancemap.value == 2) // Go to random map.
-			nextmap = RandMap(G_TOLFlag(gametype), prevmap);
+			nextmap = RandMap(G_TOLFlag(gametype_to_use), prevmap);
 	}
 
 	// We are committed to this map now.
@@ -4151,7 +4157,6 @@ static void G_DoCompleted(void)
 	if (nextmap < NUMMAPS && !mapheaderinfo[nextmap])
 		P_AllocMapHeader(nextmap);
 
-	// If the current gametype has no intermission screen set, then don't start it.
 	Y_DetermineIntermissionType();
 
 	if ((skipstats && !modeattacking) || (modeattacking && stagefailed) || (intertype == int_none))
@@ -4217,12 +4222,21 @@ static void G_DoWorldDone(void)
 {
 	if (server)
 	{
+		INT16 gametype_to_use;
+
+		if (nextgametype >= 0 && nextgametype < gametypecount)
+			gametype_to_use = nextgametype;
+		else
+			gametype_to_use = gametype;
+
 		if (gametyperules & GTR_CAMPAIGN)
 			// don't reset player between maps
-			D_MapChange(nextmap+1, gametype, ultimatemode, false, 0, false, false);
+			D_MapChange(nextmap+1, gametype_to_use, ultimatemode, false, 0, false, false);
 		else
 			// resetplayer in match/chaos/tag/CTF/race for more equality
-			D_MapChange(nextmap+1, gametype, ultimatemode, true, 0, false, false);
+			D_MapChange(nextmap+1, gametype_to_use, ultimatemode, true, 0, false, false);
+
+		nextgametype = -1;
 	}
 
 	gameaction = ga_nothing;
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 915669bb27..a03ede8ca2 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -3826,7 +3826,7 @@ static int lib_gDoReborn(lua_State *L)
 }
 
 // Another Lua function that doesn't actually exist!
-// Sets nextmapoverride & skipstats without instantly ending the level, for instances where other sources should be exiting the level, like normal signposts.
+// Sets nextmapoverride, skipstats and nextgametype without instantly ending the level, for instances where other sources should be exiting the level, like normal signposts.
 static int lib_gSetCustomExitVars(lua_State *L)
 {
 	int n = lua_gettop(L); // Num arguments
@@ -3835,18 +3835,21 @@ static int lib_gSetCustomExitVars(lua_State *L)
 
 	// LUA EXTENSION: Custom exit like support
 	// Supported:
-	//	G_SetCustomExitVars();			[reset to defaults]
-	//	G_SetCustomExitVars(int)		[nextmap override only]
-	//	G_SetCustomExitVars(nil, int)	[skipstats only]
-	//	G_SetCustomExitVars(int, int)	[both of the above]
+	//	G_SetCustomExitVars();               [reset to defaults]
+	//	G_SetCustomExitVars(int)             [nextmap override only]
+	//	G_SetCustomExitVars(nil, int)        [skipstats only]
+	//	G_SetCustomExitVars(int, int)        [both of the above]
+	//	G_SetCustomExitVars(int, int, int)   [nextmapoverride, skipstats and nextgametype]
 
 	nextmapoverride = 0;
 	skipstats = 0;
+	nextgametype = -1;
 
 	if (n >= 1)
 	{
 		nextmapoverride = (INT16)luaL_optinteger(L, 1, 0);
 		skipstats = (INT16)luaL_optinteger(L, 2, 0);
+		nextgametype = (INT16)luaL_optinteger(L, 3, 0);
 	}
 
 	return 0;
-- 
GitLab


From f17493f379c5f53e75f7c8983c4b8425462ccb73 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Mon, 7 Aug 2023 14:30:39 -0300
Subject: [PATCH 321/518] Use -1 as the default, not 0

---
 src/lua_baselib.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index a03ede8ca2..4af5066aeb 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -3849,7 +3849,7 @@ static int lib_gSetCustomExitVars(lua_State *L)
 	{
 		nextmapoverride = (INT16)luaL_optinteger(L, 1, 0);
 		skipstats = (INT16)luaL_optinteger(L, 2, 0);
-		nextgametype = (INT16)luaL_optinteger(L, 3, 0);
+		nextgametype = (INT16)luaL_optinteger(L, 3, -1);
 	}
 
 	return 0;
-- 
GitLab


From 20e4e8a5c42dfa54463face8f04de5ab1bd06372 Mon Sep 17 00:00:00 2001
From: SteelT <steeltitanium1@gmail.com>
Date: Mon, 7 Aug 2023 14:06:24 -0400
Subject: [PATCH 322/518] Fix crash handler showing garbage string for signal
 title
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This fixes the signal title being shown as something like "Process killed by signal: Process killed by signal: ó>"

Did some minor cleaning while I'm also here
---
 src/sdl/i_system.c | 42 ++++++++++++++++++++----------------------
 1 file changed, 20 insertions(+), 22 deletions(-)

diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 075199a19a..c60e86a088 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -325,8 +325,10 @@ static void write_backtrace(INT32 signal)
 static void I_ReportSignal(int num, int coredumped)
 {
 	//static char msg[] = "oh no! back to reality!\r\n";
-	const char *sigmsg, *sigttl;
+	const char *sigmsg, *signame;
 	char ttl[128];
+	char sigttl[512] = "Process killed by signal: ";
+	const char *reportmsg = "\n\nTo help us figure out the cause, you can visit our official Discord server\nwhere you will find more instructions on how to submit a crash report.\n\nSorry for the inconvenience!";
 
 	switch (num)
 	{
@@ -335,16 +337,16 @@ static void I_ReportSignal(int num, int coredumped)
 //		sigmsg = "SRB2 was interrupted prematurely by the user.";
 //		break;
 	case SIGILL:
-		sigmsg = "SRB2 has attempted to execute an illegal instruction and needs to close. %s";
-		sigttl = "SIGILL"; // illegal instruction - invalid function image
+		sigmsg = "SRB2 has attempted to execute an illegal instruction and needs to close.";
+		signame = "SIGILL"; // illegal instruction - invalid function image
 		break;
 	case SIGFPE:
-		sigmsg = "SRB2 has encountered a mathematical exception and needs to close. %s";
-		sigttl = "SIGFPE"; // mathematical exception
+		sigmsg = "SRB2 has encountered a mathematical exception and needs to close.";
+		signame = "SIGFPE"; // mathematical exception
 		break;
 	case SIGSEGV:
-		sigmsg = "SRB2 has attempted to access a memory location that it shouldn't and needs to close. %s";
-		sigttl = "SIGSEGV"; // segment violation
+		sigmsg = "SRB2 has attempted to access a memory location that it shouldn't and needs to close.";
+		signame = "SIGSEGV"; // segment violation
 		break;
 //	case SIGTERM:
 //		sigmsg = "SRB2 was terminated by a kill signal.";
@@ -355,34 +357,31 @@ static void I_ReportSignal(int num, int coredumped)
 //		sigttl = "SIGBREAK" // Ctrl-Break sequence
 //		break;
 	case SIGABRT:
-		sigmsg = "SRB2 was terminated by an abort signal. %s";
-		sigttl = "SIGABRT"; // abnormal termination triggered by abort call
+		sigmsg = "SRB2 was terminated by an abort signal.";
+		signame = "SIGABRT"; // abnormal termination triggered by abort call
 		break;
 	default:
-		sigmsg = "SRB2 was terminated by an unknown signal. %s";
+		sigmsg = "SRB2 was terminated by an unknown signal.";
 
 		sprintf(ttl, "number %d", num);
 		if (coredumped)
-			sigttl = 0;
+			signame = 0;
 		else
-			sigttl = ttl;
+			signame = ttl;
 	}
 
 	if (coredumped)
 	{
-		if (sigttl)
-			sprintf(ttl, "%s (core dumped)", sigttl);
+		if (signame)
+			sprintf(ttl, "%s (core dumped)", signame);
 		else
 			strcat(ttl, " (core dumped)");
 
-		sigttl = ttl;
+		signame = ttl;
 	}
 
-	sprintf(ttl, "Process killed by signal: %s", sigttl);
-
-	sigttl = ttl;
-
-	I_OutputMsg("\n%s\n\n", sigttl);
+	strcat(sigttl, signame);
+	I_OutputMsg("%s\n", sigttl);
 
 	if (M_CheckParm("-dedicated"))
 		return;
@@ -396,8 +395,7 @@ static void I_ReportSignal(int num, int coredumped)
 		SDL_MESSAGEBOX_ERROR, /* .flags */
 		NULL, /* .window */
 		sigttl, /* .title */
-		va(sigmsg,
-			"\n\nTo help us figure out the cause, you can visit our official Discord server\nwhere you will find more instructions on how to submit a crash report.\n\nSorry for the inconvenience!"), /* .message */
+		va("%s %s", sigmsg, reportmsg), /* .message */
 		SDL_arraysize(buttons), /* .numbuttons */
 		buttons, /* .buttons */
 		NULL /* .colorScheme */
-- 
GitLab


From 492fe94597a6985f320fc5ff73b6ff2b5f86e911 Mon Sep 17 00:00:00 2001
From: Sal <tehrealsalt@gmail.com>
Date: Mon, 7 Aug 2023 18:35:20 +0000
Subject: [PATCH 323/518] Warp cheat adjustments

---
 src/d_main.c     |  13 ++---
 src/d_netcmd.c   | 123 +++++++++++++++++++++++++++++++----------------
 src/d_netcmd.h   |   1 +
 src/f_finale.c   |   2 +-
 src/g_game.c     |   4 +-
 src/lua_hudlib.c |   3 +-
 src/m_cond.c     |  51 ++++++++++++++++++++
 src/m_cond.h     |   1 +
 src/p_inter.c    |  18 +++----
 src/p_setup.c    |   2 +-
 src/p_user.c     |   4 +-
 src/y_inter.c    |   3 +-
 12 files changed, 158 insertions(+), 67 deletions(-)

diff --git a/src/d_main.c b/src/d_main.c
index 22a6762554..24c70843a3 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -1737,14 +1737,15 @@ void D_SRB2Main(void)
 			// Prevent warping to nonexistent levels
 			if (W_CheckNumForName(G_BuildMapName(pstartmap)) == LUMPERROR)
 				I_Error("Could not warp to %s (map not found)\n", G_BuildMapName(pstartmap));
-			// Prevent warping to locked levels
-			// ... unless you're in a dedicated server.  Yes, technically this means you can view any level by
-			// running a dedicated server and joining it yourself, but that's better than making dedicated server's
-			// lives hell.
-			else if (!dedicated && M_MapLocked(pstartmap, serverGamedata))
-				I_Error("You need to unlock this level before you can warp to it!\n");
 			else
 			{
+				if (M_CampaignWarpIsCheat(gametype, pstartmap, serverGamedata))
+				{
+					// If you're warping via command line, you know what you're doing.
+					// No need to I_Error over this.
+					G_SetUsedCheats(false);
+				}
+
 				D_MapChange(pstartmap, gametype, ultimatemode, true, 0, false, false);
 			}
 		}
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 77a271825e..6df79c7bd7 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1902,8 +1902,8 @@ static void Command_Map_f(void)
 	size_t option_gametype;
 	const char *gametypename;
 	boolean newresetplayers;
-
-	boolean wouldSetCheats;
+	boolean prevent_cheat;
+	boolean set_cheated;
 
 	INT32 newmapnum;
 
@@ -1924,21 +1924,33 @@ static void Command_Map_f(void)
 	option_gametype =   COM_CheckPartialParm("-g");
 	newresetplayers = ! COM_CheckParm("-noresetplayers");
 
-	wouldSetCheats =
-		!( netgame || multiplayer ) &&
-		!( usedCheats );
+	prevent_cheat = !( usedCheats ) && !( option_force || cv_debug );
 
-	if (wouldSetCheats && !option_force)
+	if (!( netgame || multiplayer ))
 	{
-		/* May want to be more descriptive? */
-		CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
-		return;
+		if (prevent_cheat)
+		{
+			/* May want to be more descriptive? */
+			CONS_Printf(M_GetText("Cheats must be enabled to level change in single player.\n"));
+			return;
+		}
+		else
+		{
+			set_cheated = true;
+		}
 	}
 
-	if (!newresetplayers && !cv_debug)
+	if (!newresetplayers)
 	{
-		CONS_Printf(M_GetText("DEVMODE must be enabled.\n"));
-		return;
+		if (prevent_cheat)
+		{
+			CONS_Printf(M_GetText("Cheats must be enabled to use -noresetplayers.\n"));
+			return;
+		}
+		else
+		{
+			set_cheated = true;
+		}
 	}
 
 	if (option_gametype)
@@ -1946,7 +1958,7 @@ static void Command_Map_f(void)
 		if (!multiplayer)
 		{
 			CONS_Printf(M_GetText(
-						"You can't switch gametypes in single player!\n"));
+				"You can't switch gametypes in single player!\n"));
 			return;
 		}
 		else if (COM_Argc() < option_gametype + 2)/* no argument after? */
@@ -1959,7 +1971,9 @@ static void Command_Map_f(void)
 	}
 
 	if (!( first_option = COM_FirstOption() ))
+	{
 		first_option = COM_Argc();
+	}
 
 	if (first_option < 2)
 	{
@@ -1982,11 +1996,6 @@ static void Command_Map_f(void)
 		return;
 	}
 
-	if (wouldSetCheats && option_force)
-	{
-		G_SetUsedCheats(false);
-	}
-
 	// new gametype value
 	// use current one by default
 	if (option_gametype)
@@ -2028,15 +2037,13 @@ static void Command_Map_f(void)
 	}
 
 	// don't use a gametype the map doesn't support
-	if (cv_debug || option_force || cv_skipmapcheck.value)
-		fromlevelselect = false; // The player wants us to trek on anyway.  Do so.
 	// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
-	else
+	if (!(
+			mapheaderinfo[newmapnum-1] &&
+			mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)
+	))
 	{
-		if (!(
-					mapheaderinfo[newmapnum-1] &&
-					mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)
-		))
+		if (prevent_cheat && !cv_skipmapcheck.value)
 		{
 			CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum),
 				(multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player"));
@@ -2046,23 +2053,33 @@ static void Command_Map_f(void)
 		}
 		else
 		{
-			fromlevelselect =
-				( netgame || multiplayer ) &&
-				newgametype == gametype    &&
-				gametypedefaultrules[newgametype] & GTR_CAMPAIGN;
+			// The player wants us to trek on anyway.  Do so.
+			fromlevelselect = false;
+			set_cheated = ((gametypedefaultrules[newgametype] & GTR_CAMPAIGN) == GTR_CAMPAIGN);
 		}
 	}
+	else
+	{
+		fromlevelselect =
+			( netgame || multiplayer ) &&
+			newgametype == gametype    &&
+			(gametypedefaultrules[newgametype] & GTR_CAMPAIGN);
+	}
 
 	// Prevent warping to locked levels
-	// ... unless you're in a dedicated server.  Yes, technically this means you can view any level by
-	// running a dedicated server and joining it yourself, but that's better than making dedicated server's
-	// lives hell.
-	if (!dedicated && M_MapLocked(newmapnum, serverGamedata))
+	if (M_CampaignWarpIsCheat(newgametype, newmapnum, serverGamedata))
 	{
-		CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
-		Z_Free(realmapname);
-		Z_Free(mapname);
-		return;
+		if (prevent_cheat)
+		{
+			CONS_Alert(CONS_NOTICE, M_GetText("Cheats must be enabled to warp to a locked level!\n"));
+			Z_Free(realmapname);
+			Z_Free(mapname);
+			return;
+		}
+		else
+		{
+			set_cheated = true;
+		}
 	}
 
 	// Ultimate Mode only in SP via menu
@@ -2079,6 +2096,11 @@ static void Command_Map_f(void)
 	}
 	tutorialmode = false; // warping takes us out of tutorial mode
 
+	if (set_cheated && !usedCheats)
+	{
+		G_SetUsedCheats(false);
+	}
+
 	D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, fromlevelselect);
 
 	Z_Free(realmapname);
@@ -4555,25 +4577,37 @@ static void Command_Mapmd5_f(void)
 		CONS_Printf(M_GetText("You must be in a level to use this.\n"));
 }
 
+void D_SendExitLevel(boolean cheat)
+{
+	UINT8 buf[8];
+	UINT8 *buf_p = buf;
+
+	WRITEUINT8(buf_p, cheat);
+
+	SendNetXCmd(XD_EXITLEVEL, &buf, buf_p - buf);
+}
+
 static void Command_ExitLevel_f(void)
 {
-	if (!(netgame || (multiplayer && gametype != GT_COOP)) && !cv_debug)
-		CONS_Printf(M_GetText("This only works in a netgame.\n"));
-	else if (!(server || (IsPlayerAdmin(consoleplayer))))
+	if (!(server || (IsPlayerAdmin(consoleplayer))))
 		CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
 	else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS ) || demoplayback)
 		CONS_Printf(M_GetText("You must be in a level to use this.\n"));
 	else
-		SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+		D_SendExitLevel(true);
 }
 
 static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
 {
-	(void)cp;
+	boolean cheat = false;
+
+	cheat = (boolean)READUINT8(*cp);
 
 	// Ignore duplicate XD_EXITLEVEL commands.
 	if (gameaction == ga_completed)
+	{
 		return;
+	}
 
 	if (playernum != serverplayer && !IsPlayerAdmin(playernum))
 	{
@@ -4583,6 +4617,11 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
 		return;
 	}
 
+	if (G_CoopGametype() && cheat)
+	{
+		G_SetUsedCheats(false);
+	}
+
 	G_ExitLevel();
 }
 
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index 26bf4d5c6b..8bbc801d0e 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -201,6 +201,7 @@ void D_SendPlayerConfig(void);
 void Command_ExitGame_f(void);
 void Command_Retry_f(void);
 void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore
+void D_SendExitLevel(boolean cheat);
 void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pultmode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect);
 boolean IsPlayerAdmin(INT32 playernum);
 void SetAdminPlayer(INT32 playernum);
diff --git a/src/f_finale.c b/src/f_finale.c
index d03795dc43..162e87d294 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -1644,7 +1644,7 @@ void F_GameEvaluationTicker(void)
 		sparklloop = 0;
 	}
 
-	if (finalecount == 5*TICRATE)
+	if (G_CoopGametype() && !stagefailed && finalecount == 5*TICRATE)
 	{
 		serverGamedata->timesBeaten++;
 		clientGamedata->timesBeaten++;
diff --git a/src/g_game.c b/src/g_game.c
index 04b1ffe4da..24a771372b 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2137,7 +2137,7 @@ boolean G_Responder(event_t *ev)
 			if (! netgame)
 				F_StartGameEvaluation();
 			else if (server || IsPlayerAdmin(consoleplayer))
-				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+				D_SendExitLevel(false);
 			return true;
 		}
 	}
@@ -3861,7 +3861,7 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
 	for (ix = 0; ix < NUMMAPS; ix++)
 		if (mapheaderinfo[ix] && (mapheaderinfo[ix]->typeoflevel & tolflags) == tolflags
 		 && ix != pprevmap // Don't pick the same map.
-		 && (dedicated || !M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps.
+		 && (!M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps.
 		)
 			okmaps[numokmaps++] = ix;
 
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 2e3bb9c683..63a866606f 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -1487,7 +1487,6 @@ void LUA_SetHudHook(int hook, huddrawlist_h list)
 			break;
 
 		case HUD_HOOK(intermission):
-			lua_pushboolean(gL, intertype == int_spec &&
-					stagefailed);
+			lua_pushboolean(gL, stagefailed);
 	}
 }
diff --git a/src/m_cond.c b/src/m_cond.c
index 13a483ea22..3dfb1dceb2 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -467,6 +467,15 @@ UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data)
 
 UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data)
 {
+	if (dedicated)
+	{
+		// If you're in a dedicated server, every level is unlocked.
+		// Yes, technically this means you can view any level by
+		// running a dedicated server and joining it yourself, but
+		// that's better than making dedicated server's lives hell.
+		return false;
+	}
+
 	if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0)
 	{
 		return false;
@@ -480,6 +489,48 @@ UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data)
 	return false;
 }
 
+UINT8 M_CampaignWarpIsCheat(INT32 gt, INT32 mapnum, gamedata_t *data)
+{
+	if (M_MapLocked(mapnum, data) == true)
+	{
+		// Warping to locked maps is definitely always a cheat
+		return true;
+	}
+
+	if ((gametypedefaultrules[gt] & GTR_CAMPAIGN) == 0)
+	{
+		// Not a campaign, do whatever you want.
+		return false;
+	}
+
+	if (G_IsSpecialStage(mapnum))
+	{
+		// Warping to special stages is a cheat
+		return true;
+	}
+
+	if (mapheaderinfo[mapnum-1]->menuflags & LF2_HIDEINMENU)
+	{
+		// You're never allowed to warp to this level.
+		return true;
+	}
+
+	if (mapheaderinfo[mapnum-1]->menuflags & LF2_NOVISITNEEDED)
+	{
+		// You're always allowed to warp to this level.
+		return false;
+	}
+
+	if (mapnum == spstage_start)
+	{
+		// Warping to the first level is never a cheat
+		return false;
+	}
+
+	// It's only a cheat if you've never been there.
+	return (!(data->mapvisited[mapnum]));
+}
+
 INT32 M_CountEmblems(gamedata_t *data)
 {
 	INT32 found = 0, i;
diff --git a/src/m_cond.h b/src/m_cond.h
index 2be8d564be..2491a384c0 100644
--- a/src/m_cond.h
+++ b/src/m_cond.h
@@ -252,6 +252,7 @@ void M_SilentUpdateSkinAvailabilites(void);
 UINT8 M_AnySecretUnlocked(gamedata_t *data);
 UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data);
 UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data);
+UINT8 M_CampaignWarpIsCheat(INT32 gt, INT32 mapnum, gamedata_t *data);
 INT32 M_CountEmblems(gamedata_t *data);
 
 // Emblem shit
diff --git a/src/p_inter.c b/src/p_inter.c
index 4d22ba3438..74ca6972fb 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2235,7 +2235,7 @@ void P_CheckTimeLimit(void)
 		}
 
 		if (server)
-			SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+			D_SendExitLevel(false);
 	}
 
 	//Optional tie-breaker for Match/CTF
@@ -2298,11 +2298,11 @@ void P_CheckTimeLimit(void)
 			}
 		}
 		if (server)
-			SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+			D_SendExitLevel(false);
 	}
 
 	if (server)
-		SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+		D_SendExitLevel(false);
 }
 
 /** Checks if a player's score is over the pointlimit and the round should end.
@@ -2331,7 +2331,7 @@ void P_CheckPointLimit(void)
 		if ((UINT32)cv_pointlimit.value <= redscore || (UINT32)cv_pointlimit.value <= bluescore)
 		{
 			if (server)
-				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+				D_SendExitLevel(false);
 		}
 	}
 	else
@@ -2344,7 +2344,7 @@ void P_CheckPointLimit(void)
 			if ((UINT32)cv_pointlimit.value <= players[i].score)
 			{
 				if (server)
-					SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+					D_SendExitLevel(false);
 				return;
 			}
 		}
@@ -2388,7 +2388,7 @@ void P_CheckSurvivors(void)
 		{
 			CONS_Printf(M_GetText("The IT player has left the game.\n"));
 			if (server)
-				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+				D_SendExitLevel(false);
 
 			return;
 		}
@@ -2408,7 +2408,7 @@ void P_CheckSurvivors(void)
 			{
 				CONS_Printf(M_GetText("All players have been tagged!\n"));
 				if (server)
-					SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+					D_SendExitLevel(false);
 			}
 
 			return;
@@ -2420,7 +2420,7 @@ void P_CheckSurvivors(void)
 		{
 			CONS_Printf(M_GetText("There are no players able to become IT.\n"));
 			if (server)
-				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+				D_SendExitLevel(false);
 		}
 
 		return;
@@ -2432,7 +2432,7 @@ void P_CheckSurvivors(void)
 	{
 		CONS_Printf(M_GetText("All players have been tagged!\n"));
 		if (server)
-			SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+			D_SendExitLevel(false);
 	}
 }
 
diff --git a/src/p_setup.c b/src/p_setup.c
index cbb817d83b..a1440c0ced 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -8238,7 +8238,7 @@ static boolean P_LoadAddon(UINT16 numlumps)
 	{
 		CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap);
 		if (server)
-			SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+			D_SendExitLevel(false);
 	}
 
 	return true;
diff --git a/src/p_user.c b/src/p_user.c
index cf48182987..84d0c0abf0 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -11758,7 +11758,7 @@ void P_PlayerThink(player_t *player)
 			if (!total || ((4*exiting)/total) >= numneeded)
 			{
 				if (server)
-					SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+					D_SendExitLevel(false);
 			}
 			else
 				player->exiting = 3;
@@ -11766,7 +11766,7 @@ void P_PlayerThink(player_t *player)
 		else
 		{
 			if (server)
-				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
+				D_SendExitLevel(false);
 		}
 	}
 
diff --git a/src/y_inter.c b/src/y_inter.c
index 5e071171f3..796ced8b89 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -998,8 +998,7 @@ void Y_Ticker(void)
 	if (paused || P_AutoPause())
 		return;
 
-	LUA_HookBool(intertype == int_spec && stagefailed,
-			HOOK(IntermissionThinker));
+	LUA_HookBool(stagefailed, HOOK(IntermissionThinker));
 
 	intertic++;
 
-- 
GitLab


From 3d14d155e08090c4e13f95072ba8846a5c101ad9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 12 Aug 2023 12:31:59 +0200
Subject: [PATCH 324/518] Fix segfault when spectating on an Emerald Hunt map

---
 src/st_stuff.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/st_stuff.c b/src/st_stuff.c
index c6e6befc62..a6bd77cfae 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2512,6 +2512,8 @@ num:
 static INT32 ST_drawEmeraldHuntIcon(mobj_t *hunt, patch_t **patches, INT32 offset)
 {
 	INT32 interval, i;
+	if (stplyr->mo == NULL)
+		return 0;  // player just joined after spectating, can happen on custom gamemodes.
 	UINT32 dist = ((UINT32)P_AproxDistance(P_AproxDistance(stplyr->mo->x - hunt->x, stplyr->mo->y - hunt->y), stplyr->mo->z - hunt->z))>>FRACBITS;
 
 	if (dist < 128)
-- 
GitLab


From 5830ff500fdef29b1cc05938dede65073bf30e6b Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Mon, 14 Aug 2023 19:05:48 +0200
Subject: [PATCH 325/518] Fix mobj flag fumblings

---
 src/p_mobj.c | 2 +-
 src/p_user.c | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index 2a40175dc7..e89298fddf 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -9228,7 +9228,7 @@ static void P_DragonbomberThink(mobj_t *mobj)
 		else
 		{
 			fixed_t vspeed = FixedMul(mobj->info->speed >> 3, mobj->scale);
-			fixed_t z = mobj->target->z + (mobj->height >> 1) + (mobj->flags & MFE_VERTICALFLIP ? -128*mobj->scale : 128*mobj->scale + mobj->target->height);
+			fixed_t z = mobj->target->z + (mobj->height >> 1) + (mobj->eflags & MFE_VERTICALFLIP ? -128*mobj->scale : (128*mobj->scale + mobj->target->height));
 			angle_t diff = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y) - mobj->angle;
 			if (diff > ANGLE_180)
 				mobj->angle -= DRAGONTURNSPEED;
diff --git a/src/p_user.c b/src/p_user.c
index 84d0c0abf0..8a0af41d93 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2005,7 +2005,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 	ghost->standingslope = mobj->standingslope;
 
 	if (mobj->flags2 & MF2_OBJECTFLIP)
-		ghost->flags |= MF2_OBJECTFLIP;
+		ghost->flags2 |= MF2_OBJECTFLIP;
 
 	if (mobj->player && mobj->player->followmobj)
 	{
@@ -11507,7 +11507,7 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
 	}
 
 	fume->movecount = dashmode; // keeps track of previous dashmode value so we know whether Metal is entering or leaving it
-	fume->eflags = (fume->flags2 & ~MF2_OBJECTFLIP) | (mo->flags2 & MF2_OBJECTFLIP); // Make sure to flip in reverse gravity!
+	fume->flags2 = (fume->flags2 & ~MF2_OBJECTFLIP) | (mo->flags2 & MF2_OBJECTFLIP); // Make sure to flip in reverse gravity!
 	fume->eflags = (fume->eflags & ~MFE_VERTICALFLIP) | (mo->eflags & MFE_VERTICALFLIP); // Make sure to flip in reverse gravity!
 
 	// Finally, set its position
-- 
GitLab


From 9dc244755940795b0d3fdf1df9095aaa80d77a7e Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 15 Aug 2023 03:46:55 -0300
Subject: [PATCH 326/518] Ensure the correct color is set for the player object
 in splitscreen

---
 src/d_netcmd.c | 32 ++------------------------------
 1 file changed, 2 insertions(+), 30 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index a42fc19324..d022514c6a 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1271,7 +1271,7 @@ static void SendNameAndColor(void)
 		players[consoleplayer].skincolor = cv_playercolor.value;
 
 		if (players[consoleplayer].mo && !players[consoleplayer].powers[pw_dye])
-			players[consoleplayer].mo->color = players[consoleplayer].skincolor;
+			players[consoleplayer].mo->color = P_GetPlayerColor(&players[consoleplayer]);
 
 		if (metalrecording)
 		{ // Starring Metal Sonic as themselves, obviously.
@@ -1280,24 +1280,10 @@ static void SendNameAndColor(void)
 		}
 		else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUsable(consoleplayer, foundskin))
 		{
-			//boolean notsame;
-
 			cv_skin.value = foundskin;
 
-			//notsame = (cv_skin.value != players[consoleplayer].skin);
-
 			SetPlayerSkin(consoleplayer, cv_skin.string);
 			CV_StealthSet(&cv_skin, skins[cv_skin.value].name);
-
-			/*if (notsame)
-			{
-				CV_StealthSetValue(&cv_playercolor, skins[cv_skin.value].prefcolor);
-
-				players[consoleplayer].skincolor = cv_playercolor.value % numskincolors;
-
-				if (players[consoleplayer].mo)
-					players[consoleplayer].mo->color = (UINT16)players[consoleplayer].skincolor;
-			}*/
 		}
 		else
 		{
@@ -1399,7 +1385,7 @@ static void SendNameAndColor2(void)
 		// don't use secondarydisplayplayer: the second player must be 1
 		players[secondplaya].skincolor = cv_playercolor2.value;
 		if (players[secondplaya].mo && !players[secondplaya].powers[pw_dye])
-			players[secondplaya].mo->color = players[secondplaya].skincolor;
+			players[secondplaya].mo->color = P_GetPlayerColor(&players[secondplaya]);
 
 		if (cv_forceskin.value >= 0 && (netgame || multiplayer)) // Server wants everyone to use the same player
 		{
@@ -1410,24 +1396,10 @@ static void SendNameAndColor2(void)
 		}
 		else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUsable(secondplaya, foundskin))
 		{
-			//boolean notsame;
-
 			cv_skin2.value = foundskin;
 
-			//notsame = (cv_skin2.value != players[secondplaya].skin);
-
 			SetPlayerSkin(secondplaya, cv_skin2.string);
 			CV_StealthSet(&cv_skin2, skins[cv_skin2.value].name);
-
-			/*if (notsame)
-			{
-				CV_StealthSetValue(&cv_playercolor2, skins[players[secondplaya].skin].prefcolor);
-
-				players[secondplaya].skincolor = cv_playercolor2.value % numskincolors;
-
-				if (players[secondplaya].mo)
-					players[secondplaya].mo->color = players[secondplaya].skincolor;
-			}*/
 		}
 		else
 		{
-- 
GitLab


From 0bb65166c9bda1038c1175269ceddf4e9c8a6e41 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 15 Aug 2023 03:49:31 -0300
Subject: [PATCH 327/518] Always allow changing color in multiplayer player
 setup menu

---
 src/m_menu.c | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 3451b90d63..63cb1e1c6d 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -12523,11 +12523,7 @@ static void M_SetupMultiPlayer(INT32 choice)
 	else
 		MP_PlayerSetupMenu[1].status = (IT_KEYHANDLER|IT_STRING);
 
-	// ditto with colour
-	if (Playing() && G_GametypeHasTeams())
-		MP_PlayerSetupMenu[2].status = (IT_GRAYEDOUT);
-	else
-		MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER|IT_STRING);
+	MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER|IT_STRING);
 
 	multi_spr2 = P_GetSkinSprite2(&skins[setupm_fakeskin], SPR2_WALK, NULL);
 
@@ -12568,11 +12564,7 @@ static void M_SetupMultiPlayer2(INT32 choice)
 	else
 		MP_PlayerSetupMenu[1].status = (IT_KEYHANDLER | IT_STRING);
 
-	// ditto with colour
-	if (Playing() && G_GametypeHasTeams())
-		MP_PlayerSetupMenu[2].status = (IT_GRAYEDOUT);
-	else
-		MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER|IT_STRING);
+	MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER|IT_STRING);
 
 	multi_spr2 = P_GetSkinSprite2(&skins[setupm_fakeskin], SPR2_WALK, NULL);
 
-- 
GitLab


From 062af20628fabac5c150a92b7d853aa174a36ac6 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 15 Aug 2023 04:29:50 -0300
Subject: [PATCH 328/518] Don't change cv_skin or cv_playercolor if there is no
 need to

---
 src/d_main.c   |  8 ++++++++
 src/d_netcmd.c | 39 ++++++++++++++-------------------------
 src/g_demo.c   |  9 ++++++---
 src/g_game.c   | 14 +++++---------
 src/g_game.h   |  3 +--
 src/g_state.h  |  4 +++-
 src/p_setup.c  |  6 ------
 src/r_skins.c  |  4 ----
 8 files changed, 37 insertions(+), 50 deletions(-)

diff --git a/src/d_main.c b/src/d_main.c
index 22a6762554..a48e94e7af 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -1626,6 +1626,14 @@ void D_SRB2Main(void)
 		autostart = true;
 	}
 
+	// Set default singleplayer skin
+	if (!dedicated)
+	{
+		pickedchar = R_SkinAvailable(cv_defaultskin.string);
+		if (pickedchar == -1)
+			pickedchar = 0;
+	}
+
 	// user settings come before "+" parameters.
 	if (dedicated)
 		COM_ImmedExecute(va("exec \"%s"PATHSEP"adedserv.cfg\"\n", srb2home));
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index d022514c6a..369118dcce 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1274,25 +1274,22 @@ static void SendNameAndColor(void)
 			players[consoleplayer].mo->color = P_GetPlayerColor(&players[consoleplayer]);
 
 		if (metalrecording)
-		{ // Starring Metal Sonic as themselves, obviously.
+		{
+			// Starring Metal Sonic as themselves, obviously.
 			SetPlayerSkinByNum(consoleplayer, 5);
-			CV_StealthSet(&cv_skin, skins[5].name);
 		}
-		else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUsable(consoleplayer, foundskin))
+		else if (splitscreen)
 		{
-			cv_skin.value = foundskin;
-
-			SetPlayerSkin(consoleplayer, cv_skin.string);
-			CV_StealthSet(&cv_skin, skins[cv_skin.value].name);
+			if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUsable(consoleplayer, foundskin))
+				SetPlayerSkin(consoleplayer, cv_skin.string);
+			else
+			{
+				// will always be same as current
+				SetPlayerSkin(consoleplayer, cv_skin.string);
+			}
 		}
 		else
-		{
-			cv_skin.value = players[consoleplayer].skin;
-			CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
-			// will always be same as current
-			SetPlayerSkin(consoleplayer, cv_skin.string);
-		}
-
+			SetPlayerSkinByNum(consoleplayer, pickedchar);
 		return;
 	}
 
@@ -1365,7 +1362,6 @@ static void SendNameAndColor2(void)
 	if (!Playing())
 		return;
 
-	// If you're not in a netgame, merely update the skin, color, and name.
 	if (botingame)
 	{
 		players[secondplaya].skincolor = botcolor;
@@ -1377,6 +1373,7 @@ static void SendNameAndColor2(void)
 	}
 	else if (!netgame)
 	{
+		// If you're not in a netgame, merely update the skin, color, and name.
 		INT32 foundskin;
 
 		CleanupPlayerName(secondplaya, cv_playername2.zstring);
@@ -1387,7 +1384,7 @@ static void SendNameAndColor2(void)
 		if (players[secondplaya].mo && !players[secondplaya].powers[pw_dye])
 			players[secondplaya].mo->color = P_GetPlayerColor(&players[secondplaya]);
 
-		if (cv_forceskin.value >= 0 && (netgame || multiplayer)) // Server wants everyone to use the same player
+		if (cv_forceskin.value >= 0 && multiplayer) // Server wants everyone to use the same player
 		{
 			const INT32 forcedskin = cv_forceskin.value;
 
@@ -1395,16 +1392,9 @@ static void SendNameAndColor2(void)
 			CV_StealthSet(&cv_skin2, skins[forcedskin].name);
 		}
 		else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUsable(secondplaya, foundskin))
-		{
-			cv_skin2.value = foundskin;
-
 			SetPlayerSkin(secondplaya, cv_skin2.string);
-			CV_StealthSet(&cv_skin2, skins[cv_skin2.value].name);
-		}
-		else
+		else if (splitscreen)
 		{
-			cv_skin2.value = players[secondplaya].skin;
-			CV_StealthSet(&cv_skin2, skins[players[secondplaya].skin].name);
 			// will always be same as current
 			SetPlayerSkin(secondplaya, cv_skin2.string);
 		}
@@ -2094,7 +2084,6 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
 	{
 		SetPlayerSkinByNum(0, cv_chooseskin.value-1);
 		players[0].skincolor = skins[players[0].skin].prefcolor;
-		CV_StealthSetValue(&cv_playercolor, players[0].skincolor);
 	}
 
 	mapnumber = M_MapNumber(mapname[3], mapname[4]);
diff --git a/src/g_demo.c b/src/g_demo.c
index 4b9ff56e80..e3377ab138 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -1500,8 +1500,12 @@ void G_BeginRecording(void)
 	demo_p += 16;
 
 	// Color
-	for (i = 0; i < MAXCOLORNAME && cv_playercolor.string[i]; i++)
-		name[i] = cv_playercolor.string[i];
+	UINT16 skincolor = players[0].skincolor;
+	if (skincolor >= numskincolors)
+		skincolor = SKINCOLOR_NONE;
+	const char *skincolor_name = skincolors[skincolor].name;
+	for (i = 0; i < MAXCOLORNAME && skincolor_name[i]; i++)
+		name[i] = skincolor_name[i];
 	for (; i < MAXCOLORNAME; i++)
 		name[i] = '\0';
 	M_Memcpy(demo_p,name,MAXCOLORNAME);
@@ -2263,7 +2267,6 @@ void G_DoPlayDemo(char *defdemoname)
 			players[0].skincolor = i;
 			break;
 		}
-	CV_StealthSetValue(&cv_playercolor, players[0].skincolor);
 	if (players[0].mo)
 	{
 		players[0].mo->color = players[0].skincolor;
diff --git a/src/g_game.c b/src/g_game.c
index c49b202adf..70ff31f301 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -55,6 +55,8 @@ gameaction_t gameaction;
 gamestate_t gamestate = GS_NULL;
 UINT8 ultimatemode = false;
 
+INT32 pickedchar;
+
 boolean botingame;
 UINT8 botskin;
 UINT16 botcolor;
@@ -4770,12 +4772,9 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
 	Z_Free(savebuffer);
 	save_p = savebuffer = NULL;
 
-//	gameaction = ga_nothing;
-//	G_SetGamestate(GS_LEVEL);
 	displayplayer = consoleplayer;
 	multiplayer = splitscreen = false;
 
-//	G_DeferedInitNew(sk_medium, G_BuildMapName(1), 0, 0, 1);
 	if (setsizeneeded)
 		R_ExecuteSetViewSize();
 
@@ -4968,9 +4967,9 @@ cleanup:
 // Can be called by the startup code or the menu task,
 // consoleplayer, displayplayer, playeringame[] should be set.
 //
-void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, boolean SSSG, boolean FLS)
+void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 character, boolean SSSG, boolean FLS)
 {
-	UINT16 color = skins[pickedchar].prefcolor;
+	pickedchar = character;
 	paused = false;
 
 	if (demoplayback)
@@ -4991,10 +4990,7 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, b
 		SplitScreen_OnChange();
 	}
 
-	color = skins[pickedchar].prefcolor;
-	SetPlayerSkinByNum(consoleplayer, pickedchar);
-	CV_StealthSet(&cv_skin, skins[pickedchar].name);
-	CV_StealthSetValue(&cv_playercolor, color);
+	SetPlayerSkinByNum(consoleplayer, character);
 
 	if (mapname)
 		D_MapChange(M_MapNumber(mapname[3], mapname[4]), gametype, pultmode, true, 1, false, FLS);
diff --git a/src/g_game.h b/src/g_game.h
index a8c285f79f..cb38fc1d5b 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -174,8 +174,7 @@ void G_SpawnPlayer(INT32 playernum);
 
 // Can be called by the startup code or M_Responder.
 // A normal game starts at map 1, but a warp test can start elsewhere
-void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar,
-	boolean SSSG, boolean FLS);
+void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 character, boolean SSSG, boolean FLS);
 void G_DoLoadLevel(boolean resetplayer);
 void G_StartTitleCard(void);
 void G_PreLevelTitleCard(void);
diff --git a/src/g_state.h b/src/g_state.h
index 8f97930bb6..4b07f3d903 100644
--- a/src/g_state.h
+++ b/src/g_state.h
@@ -53,9 +53,11 @@ typedef enum
 
 extern gamestate_t gamestate;
 extern UINT8 titlemapinaction;
-extern UINT8 ultimatemode; // was sk_insane
+extern UINT8 ultimatemode;
 extern gameaction_t gameaction;
 
+extern INT32 pickedchar;
+
 extern boolean botingame;
 extern UINT8 botskin;
 extern UINT16 botcolor;
diff --git a/src/p_setup.c b/src/p_setup.c
index 0c8ea656ec..747cf6d7f9 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -7179,19 +7179,13 @@ static void P_ForceCharacter(const char *forcecharskin)
 		{
 			SetPlayerSkin(secondarydisplayplayer, forcecharskin);
 			if ((unsigned)cv_playercolor2.value != skins[players[secondarydisplayplayer].skin].prefcolor)
-			{
-				CV_StealthSetValue(&cv_playercolor2, skins[players[secondarydisplayplayer].skin].prefcolor);
 				players[secondarydisplayplayer].skincolor = skins[players[secondarydisplayplayer].skin].prefcolor;
-			}
 		}
 
 		SetPlayerSkin(consoleplayer, forcecharskin);
 		// normal player colors in single player
 		if ((unsigned)cv_playercolor.value != skins[players[consoleplayer].skin].prefcolor)
-		{
-			CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin].prefcolor);
 			players[consoleplayer].skincolor = skins[players[consoleplayer].skin].prefcolor;
-		}
 	}
 }
 
diff --git a/src/r_skins.c b/src/r_skins.c
index 9443ad49b0..8bd75e35bf 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -346,10 +346,6 @@ static void SetSkin(player_t *player, INT32 skinnum)
 
 	if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback))
 	{
-		if (player == &players[consoleplayer])
-			CV_StealthSetValue(&cv_playercolor, skin->prefcolor);
-		else if (player == &players[secondarydisplayplayer])
-			CV_StealthSetValue(&cv_playercolor2, skin->prefcolor);
 		player->skincolor = newcolor = skin->prefcolor;
 		if (player->bot && botingame)
 		{
-- 
GitLab


From e106b56b53e289af3d85868ddec6dc5764ccc67d Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 15 Aug 2023 04:45:50 -0300
Subject: [PATCH 329/518] Only set the picked character once

---
 src/d_netcmd.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 369118dcce..bce1a28ef4 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1288,8 +1288,13 @@ static void SendNameAndColor(void)
 				SetPlayerSkin(consoleplayer, cv_skin.string);
 			}
 		}
-		else
+		else if (pickedchar != -1)
+		{
 			SetPlayerSkinByNum(consoleplayer, pickedchar);
+			pickedchar = -1;
+		}
+		else
+			SetPlayerSkin(consoleplayer, cv_skin.string);
 		return;
 	}
 
-- 
GitLab


From a7459972435646a41f48ff95ab425ff125bc7308 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 15 Aug 2023 12:27:38 -0300
Subject: [PATCH 330/518] Better handling of cv_skin/cv_playercolor in
 singleplayer

---
 src/d_main.c   |  8 -----
 src/d_netcmd.c | 80 ++++++++++++++++++++++++++++----------------------
 2 files changed, 45 insertions(+), 43 deletions(-)

diff --git a/src/d_main.c b/src/d_main.c
index a48e94e7af..22a6762554 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -1626,14 +1626,6 @@ void D_SRB2Main(void)
 		autostart = true;
 	}
 
-	// Set default singleplayer skin
-	if (!dedicated)
-	{
-		pickedchar = R_SkinAvailable(cv_defaultskin.string);
-		if (pickedchar == -1)
-			pickedchar = 0;
-	}
-
 	// user settings come before "+" parameters.
 	if (dedicated)
 		COM_ImmedExecute(va("exec \"%s"PATHSEP"adedserv.cfg\"\n", srb2home));
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index bce1a28ef4..9ad0b0ab08 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1224,6 +1224,33 @@ static void ForceAllSkins(INT32 forcedskin)
 
 static INT32 snacpending = 0, snac2pending = 0, chmappending = 0;
 
+static void SetSkinLocal(INT32 skinnum)
+{
+	if (metalrecording)
+	{
+		// Starring Metal Sonic as themselves, obviously.
+		SetPlayerSkinByNum(consoleplayer, 5);
+	}
+	else if (splitscreen)
+	{
+		INT32 foundskin = R_SkinAvailable(cv_skin.string);
+		if (foundskin != -1 && R_SkinUsable(consoleplayer, foundskin))
+			SetPlayerSkinByNum(consoleplayer, foundskin);
+		else
+			SetPlayerSkinByNum(consoleplayer, GetPlayerDefaultSkin(consoleplayer));
+	}
+	else
+		SetPlayerSkinByNum(consoleplayer, skinnum);
+}
+
+static void SetColorLocal(void)
+{
+	players[consoleplayer].skincolor = cv_playercolor.value;
+
+	if (players[consoleplayer].mo && !players[consoleplayer].powers[pw_dye])
+		players[consoleplayer].mo->color = P_GetPlayerColor(&players[consoleplayer]);
+}
+
 // name, color, or skin has changed
 //
 static void SendNameAndColor(void)
@@ -1263,38 +1290,11 @@ static void SendNameAndColor(void)
 	// If you're not in a netgame, merely update the skin, color, and name.
 	if (!netgame)
 	{
-		INT32 foundskin;
-
 		CleanupPlayerName(consoleplayer, cv_playername.zstring);
 		strcpy(player_names[consoleplayer], cv_playername.zstring);
 
-		players[consoleplayer].skincolor = cv_playercolor.value;
-
-		if (players[consoleplayer].mo && !players[consoleplayer].powers[pw_dye])
-			players[consoleplayer].mo->color = P_GetPlayerColor(&players[consoleplayer]);
-
-		if (metalrecording)
-		{
-			// Starring Metal Sonic as themselves, obviously.
-			SetPlayerSkinByNum(consoleplayer, 5);
-		}
-		else if (splitscreen)
-		{
-			if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUsable(consoleplayer, foundskin))
-				SetPlayerSkin(consoleplayer, cv_skin.string);
-			else
-			{
-				// will always be same as current
-				SetPlayerSkin(consoleplayer, cv_skin.string);
-			}
-		}
-		else if (pickedchar != -1)
-		{
-			SetPlayerSkinByNum(consoleplayer, pickedchar);
-			pickedchar = -1;
-		}
-		else
-			SetPlayerSkin(consoleplayer, cv_skin.string);
+		SetColorLocal();
+		SetSkinLocal(pickedchar);
 		return;
 	}
 
@@ -4747,10 +4747,17 @@ static void Skin_OnChange(void)
 	if (!Playing())
 		return; // do whatever you want
 
-	if (!(cv_debug || devparm) && !(multiplayer || netgame) // In single player.
-		&& (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y
+	if (!(multiplayer || netgame)) // In single player.
 	{
-		CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
+		if (!(cv_debug || devparm)
+		&& (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y
+		{
+			CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
+			return;
+		}
+
+		// Just do this here if devmode is enabled
+		SetSkinLocal(R_SkinAvailable(cv_skin.string));
 		return;
 	}
 
@@ -4788,15 +4795,18 @@ static void Skin2_OnChange(void)
   */
 static void Color_OnChange(void)
 {
-	if (!Playing()) {
+	if (!Playing())
+	{
 		if (!cv_playercolor.value || !skincolors[cv_playercolor.value].accessible)
 			CV_StealthSetValue(&cv_playercolor, lastgoodcolor);
 	}
 	else
 	{
-		if (!(cv_debug || devparm) && !(multiplayer || netgame)) // In single player.
+		if (!(multiplayer || netgame)) // In single player.
 		{
-			CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
+			// Just do it here if devmode is enabled
+			if (cv_debug || devparm)
+				SetColorLocal();
 			return;
 		}
 
-- 
GitLab


From 8d56a4d32ec84a9d68f6a52519c9e07da5ee266b Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 15 Aug 2023 12:37:18 -0300
Subject: [PATCH 331/518] Move splitscreen code path out of SetSkinLocal and
 into SendNameAndColor

---
 src/d_main.c   |  3 +++
 src/d_netcmd.c | 20 ++++++++++----------
 2 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/src/d_main.c b/src/d_main.c
index 22a6762554..5c6db2e299 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -981,6 +981,7 @@ void D_StartTitle(void)
 	emeralds = 0;
 	memset(&luabanks, 0, sizeof(luabanks));
 	lastmaploaded = 0;
+	pickedchar = R_SkinAvailable(cv_defaultskin.string);
 
 	// In case someone exits out at the same time they start a time attack run,
 	// reset modeattacking
@@ -1626,6 +1627,8 @@ void D_SRB2Main(void)
 		autostart = true;
 	}
 
+	pickedchar = R_SkinAvailable(cv_defaultskin.string);
+
 	// user settings come before "+" parameters.
 	if (dedicated)
 		COM_ImmedExecute(va("exec \"%s"PATHSEP"adedserv.cfg\"\n", srb2home));
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 9ad0b0ab08..8fdb73af4b 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1230,17 +1230,13 @@ static void SetSkinLocal(INT32 skinnum)
 	{
 		// Starring Metal Sonic as themselves, obviously.
 		SetPlayerSkinByNum(consoleplayer, 5);
+		return;
 	}
-	else if (splitscreen)
-	{
-		INT32 foundskin = R_SkinAvailable(cv_skin.string);
-		if (foundskin != -1 && R_SkinUsable(consoleplayer, foundskin))
-			SetPlayerSkinByNum(consoleplayer, foundskin);
-		else
-			SetPlayerSkinByNum(consoleplayer, GetPlayerDefaultSkin(consoleplayer));
-	}
-	else
+
+	if (skinnum != -1 && R_SkinUsable(consoleplayer, skinnum))
 		SetPlayerSkinByNum(consoleplayer, skinnum);
+	else
+		SetPlayerSkinByNum(consoleplayer, GetPlayerDefaultSkin(consoleplayer));
 }
 
 static void SetColorLocal(void)
@@ -1294,7 +1290,11 @@ static void SendNameAndColor(void)
 		strcpy(player_names[consoleplayer], cv_playername.zstring);
 
 		SetColorLocal();
-		SetSkinLocal(pickedchar);
+
+		if (splitscreen)
+			SetSkinLocal(R_SkinAvailable(cv_skin.string));
+		else
+			SetSkinLocal(pickedchar);
 		return;
 	}
 
-- 
GitLab


From dd4deaa5e9403d4b39b96a27aab5daca338a3fd6 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 15 Aug 2023 12:43:31 -0300
Subject: [PATCH 332/518] Reduce code duplication

---
 src/d_netcmd.c | 61 +++++++++++++++++++-------------------------------
 1 file changed, 23 insertions(+), 38 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 8fdb73af4b..486fa1dbeb 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1224,27 +1224,27 @@ static void ForceAllSkins(INT32 forcedskin)
 
 static INT32 snacpending = 0, snac2pending = 0, chmappending = 0;
 
-static void SetSkinLocal(INT32 skinnum)
+static void SetSkinLocal(INT32 playernum, INT32 skinnum)
 {
-	if (metalrecording)
+	if (metalrecording && playernum == consoleplayer)
 	{
 		// Starring Metal Sonic as themselves, obviously.
-		SetPlayerSkinByNum(consoleplayer, 5);
+		SetPlayerSkinByNum(playernum, 5);
 		return;
 	}
 
-	if (skinnum != -1 && R_SkinUsable(consoleplayer, skinnum))
-		SetPlayerSkinByNum(consoleplayer, skinnum);
+	if (skinnum != -1 && R_SkinUsable(playernum, skinnum))
+		SetPlayerSkinByNum(playernum, skinnum);
 	else
-		SetPlayerSkinByNum(consoleplayer, GetPlayerDefaultSkin(consoleplayer));
+		SetPlayerSkinByNum(playernum, GetPlayerDefaultSkin(playernum));
 }
 
-static void SetColorLocal(void)
+static void SetColorLocal(INT32 playernum, UINT16 color)
 {
-	players[consoleplayer].skincolor = cv_playercolor.value;
+	players[playernum].skincolor = color;
 
-	if (players[consoleplayer].mo && !players[consoleplayer].powers[pw_dye])
-		players[consoleplayer].mo->color = P_GetPlayerColor(&players[consoleplayer]);
+	if (players[playernum].mo && !players[playernum].powers[pw_dye])
+		players[playernum].mo->color = P_GetPlayerColor(&players[playernum]);
 }
 
 // name, color, or skin has changed
@@ -1289,12 +1289,12 @@ static void SendNameAndColor(void)
 		CleanupPlayerName(consoleplayer, cv_playername.zstring);
 		strcpy(player_names[consoleplayer], cv_playername.zstring);
 
-		SetColorLocal();
+		SetColorLocal(consoleplayer, cv_playercolor.value);
 
 		if (splitscreen)
-			SetSkinLocal(R_SkinAvailable(cv_skin.string));
+			SetSkinLocal(consoleplayer, R_SkinAvailable(cv_skin.string));
 		else
-			SetSkinLocal(pickedchar);
+			SetSkinLocal(consoleplayer, pickedchar);
 		return;
 	}
 
@@ -1369,40 +1369,25 @@ static void SendNameAndColor2(void)
 
 	if (botingame)
 	{
-		players[secondplaya].skincolor = botcolor;
-		if (players[secondplaya].mo && !players[secondplaya].powers[pw_dye])
-			players[secondplaya].mo->color = players[secondplaya].skincolor;
-
+		SetColorLocal(secondplaya, botcolor);
 		SetPlayerSkinByNum(secondplaya, botskin-1);
 		return;
 	}
 	else if (!netgame)
 	{
 		// If you're not in a netgame, merely update the skin, color, and name.
-		INT32 foundskin;
-
 		CleanupPlayerName(secondplaya, cv_playername2.zstring);
 		strcpy(player_names[secondplaya], cv_playername2.zstring);
 
-		// don't use secondarydisplayplayer: the second player must be 1
-		players[secondplaya].skincolor = cv_playercolor2.value;
-		if (players[secondplaya].mo && !players[secondplaya].powers[pw_dye])
-			players[secondplaya].mo->color = P_GetPlayerColor(&players[secondplaya]);
-
-		if (cv_forceskin.value >= 0 && multiplayer) // Server wants everyone to use the same player
-		{
-			const INT32 forcedskin = cv_forceskin.value;
+		SetColorLocal(secondplaya, cv_playercolor2.value);
 
-			SetPlayerSkinByNum(secondplaya, forcedskin);
-			CV_StealthSet(&cv_skin2, skins[forcedskin].name);
-		}
-		else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUsable(secondplaya, foundskin))
-			SetPlayerSkin(secondplaya, cv_skin2.string);
-		else if (splitscreen)
+		if (cv_forceskin.value >= 0)
 		{
-			// will always be same as current
-			SetPlayerSkin(secondplaya, cv_skin2.string);
+			SetSkinLocal(secondplaya, cv_forceskin.value);
+			CV_StealthSet(&cv_skin2, skins[cv_forceskin.value].name);
 		}
+		else
+			SetSkinLocal(secondplaya, R_SkinAvailable(cv_skin2.string));
 		return;
 	}
 
@@ -4756,8 +4741,8 @@ static void Skin_OnChange(void)
 			return;
 		}
 
-		// Just do this here if devmode is enabled
-		SetSkinLocal(R_SkinAvailable(cv_skin.string));
+		// Just do it here if devmode is enabled
+		SetSkinLocal(consoleplayer, R_SkinAvailable(cv_skin.string));
 		return;
 	}
 
@@ -4806,7 +4791,7 @@ static void Color_OnChange(void)
 		{
 			// Just do it here if devmode is enabled
 			if (cv_debug || devparm)
-				SetColorLocal();
+				SetColorLocal(consoleplayer, cv_playercolor.value);
 			return;
 		}
 
-- 
GitLab


From 976b9b862dc43e264591e3c4896770a97ebb561b Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 15 Aug 2023 13:51:12 -0300
Subject: [PATCH 333/518] Restore player skins if forced skin restrictions are
 lifted

---
 src/d_netcmd.c | 59 +++++++++++++++++++++-----------------------------
 src/g_game.c   |  7 ++++++
 src/p_setup.c  | 36 ++++++++++++------------------
 src/r_skins.c  | 15 +++++++++++++
 src/r_skins.h  |  1 +
 5 files changed, 62 insertions(+), 56 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 486fa1dbeb..60013b5c5d 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -225,6 +225,7 @@ consvar_t cv_seenames = CVAR_INIT ("seenames", "Ally/Foe", CV_SAVE|CV_ALLOWLUA,
 consvar_t cv_allowseenames = CVAR_INIT ("allowseenames", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
 
 // names
+static char *lastskinnames[2];
 consvar_t cv_playername = CVAR_INIT ("name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange);
 consvar_t cv_playername2 = CVAR_INIT ("name2", "Tails", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name2_OnChange);
 // player colors
@@ -1203,22 +1204,10 @@ UINT8 CanChangeSkin(INT32 playernum)
 
 static void ForceAllSkins(INT32 forcedskin)
 {
-	INT32 i;
-	for (i = 0; i < MAXPLAYERS; ++i)
+	for (INT32 i = 0; i < MAXPLAYERS; ++i)
 	{
-		if (!playeringame[i])
-			continue;
-
-		SetPlayerSkinByNum(i, forcedskin);
-
-		// If it's me (or my brother), set appropriate skin value in cv_skin/cv_skin2
-		if (!dedicated) // But don't do this for dedicated servers, of course.
-		{
-			if (i == consoleplayer)
-				CV_StealthSet(&cv_skin, skins[forcedskin].name);
-			else if (i == secondarydisplayplayer)
-				CV_StealthSet(&cv_skin2, skins[forcedskin].name);
-		}
+		if (playeringame[i])
+			SetPlayerSkinByNum(i, forcedskin);
 	}
 }
 
@@ -1311,10 +1300,6 @@ static void SendNameAndColor(void)
 	else // Cleanup name if changing it
 		CleanupPlayerName(consoleplayer, cv_playername.zstring);
 
-	// Don't change skin if the server doesn't want you to.
-	if (!CanChangeSkin(consoleplayer))
-		CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
-
 	// check if player has the skin loaded (cv_skin may have
 	// the name of a skin that was available in the previous game)
 	cv_skin.value = R_SkinAvailable(cv_skin.string);
@@ -1382,10 +1367,7 @@ static void SendNameAndColor2(void)
 		SetColorLocal(secondplaya, cv_playercolor2.value);
 
 		if (cv_forceskin.value >= 0)
-		{
 			SetSkinLocal(secondplaya, cv_forceskin.value);
-			CV_StealthSet(&cv_skin2, skins[cv_forceskin.value].name);
-		}
 		else
 			SetSkinLocal(secondplaya, R_SkinAvailable(cv_skin2.string));
 		return;
@@ -1480,16 +1462,9 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
 	}
 
 	// set skin
-	if (cv_forceskin.value >= 0 && (netgame || multiplayer)) // Server wants everyone to use the same player
-	{
-		const INT32 forcedskin = cv_forceskin.value;
+	INT32 forcedskin = R_GetForcedSkin(playernum);
+	if (forcedskin != -1 && (netgame || multiplayer)) // Server wants everyone to use the same player (or the level is forcing one.)
 		SetPlayerSkinByNum(playernum, forcedskin);
-
-		if (playernum == consoleplayer)
-			CV_StealthSet(&cv_skin, skins[forcedskin].name);
-		else if (playernum == secondarydisplayplayer)
-			CV_StealthSet(&cv_skin2, skins[forcedskin].name);
-	}
 	else
 		SetPlayerSkinByNum(playernum, skin);
 }
@@ -4691,7 +4666,10 @@ static void ForceSkin_OnChange(void)
 		return;
 
 	if (cv_forceskin.value < 0)
+	{
 		CONS_Printf("The server has lifted the forced skin restrictions.\n");
+		D_SendPlayerConfig();
+	}
 	else
 	{
 		CONS_Printf("The server is restricting all players to skin \"%s\".\n",skins[cv_forceskin.value].name);
@@ -4709,7 +4687,6 @@ static void Name_OnChange(void)
 	}
 	else
 		SendNameAndColor();
-
 }
 
 static void Name2_OnChange(void)
@@ -4732,6 +4709,9 @@ static void Skin_OnChange(void)
 	if (!Playing())
 		return; // do whatever you want
 
+	if (lastskinnames[0] == NULL)
+		lastskinnames[0] = Z_StrDup(cv_skin.string);
+
 	if (!(multiplayer || netgame)) // In single player.
 	{
 		if (!(cv_debug || devparm)
@@ -4747,11 +4727,15 @@ static void Skin_OnChange(void)
 	}
 
 	if (CanChangeSkin(consoleplayer) && !P_PlayerMoving(consoleplayer))
+	{
 		SendNameAndColor();
+		Z_Free(lastskinnames[0]);
+		lastskinnames[0] = Z_StrDup(cv_skin.string);
+	}
 	else
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
-		CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
+		CV_StealthSet(&cv_skin, lastskinnames[0]);
 	}
 }
 
@@ -4765,12 +4749,19 @@ static void Skin2_OnChange(void)
 	if (!Playing() || !splitscreen)
 		return; // do whatever you want
 
+	if (lastskinnames[1] == NULL)
+		lastskinnames[1] = Z_StrDup(cv_skin2.string);
+
 	if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer))
+	{
 		SendNameAndColor2();
+		Z_Free(lastskinnames[1]);
+		lastskinnames[1] = Z_StrDup(cv_skin.string);
+	}
 	else
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
-		CV_StealthSet(&cv_skin2, skins[players[secondarydisplayplayer].skin].name);
+		CV_StealthSet(&cv_skin2, lastskinnames[1]);
 	}
 }
 
diff --git a/src/g_game.c b/src/g_game.c
index 70ff31f301..eac5b29e58 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -5070,6 +5070,10 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
 			CV_StealthSetValue(&cv_itemfinder, 0);
 	}
 
+	// Restore each player's skin if it was previously forced to be a specific one
+	// (Looks a bit silly, but it works.)
+	boolean reset_skin = netgame && mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0';
+
 	// internal game map
 	// well this check is useless because it is done before (d_netcmd.c::command_map_f)
 	// but in case of for demos....
@@ -5097,6 +5101,9 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
 	automapactive = false;
 	imcontinuing = false;
 
+	if (reset_skin)
+		D_SendPlayerConfig();
+
 	// fetch saved data if available
 	if (savedata.lives > 0)
 	{
diff --git a/src/p_setup.c b/src/p_setup.c
index 747cf6d7f9..d14d91fa59 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -7161,31 +7161,23 @@ static void P_RunLevelScript(const char *scriptname)
 
 static void P_ForceCharacter(const char *forcecharskin)
 {
-	if (netgame)
-	{
-		char skincmd[33];
-		if (splitscreen)
-		{
-			sprintf(skincmd, "skin2 %s\n", forcecharskin);
-			CV_Set(&cv_skin2, forcecharskin);
-		}
+	// Don't do anything if the server is forcing a skin already.
+	if (netgame && cv_forceskin.value >= 0)
+		return;
 
-		sprintf(skincmd, "skin %s\n", forcecharskin);
-		COM_BufAddText(skincmd);
-	}
-	else
+	for (unsigned i = 0; i < MAXPLAYERS; i++)
 	{
-		if (splitscreen)
-		{
-			SetPlayerSkin(secondarydisplayplayer, forcecharskin);
-			if ((unsigned)cv_playercolor2.value != skins[players[secondarydisplayplayer].skin].prefcolor)
-				players[secondarydisplayplayer].skincolor = skins[players[secondarydisplayplayer].skin].prefcolor;
-		}
+		if (!playeringame[i])
+			continue;
+
+		INT32 skinnum = R_SkinAvailable(forcecharskin);
+		if (skinnum == -1 || !R_SkinUsable(i, skinnum))
+			continue;
+
+		SetPlayerSkinByNum(i, skinnum);
 
-		SetPlayerSkin(consoleplayer, forcecharskin);
-		// normal player colors in single player
-		if ((unsigned)cv_playercolor.value != skins[players[consoleplayer].skin].prefcolor)
-			players[consoleplayer].skincolor = skins[players[consoleplayer].skin].prefcolor;
+		if (!netgame)
+			players[i].skincolor = skins[skinnum].prefcolor;
 	}
 }
 
diff --git a/src/r_skins.c b/src/r_skins.c
index 8bd75e35bf..4b72d9a275 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -305,6 +305,21 @@ INT32 R_SkinAvailable(const char *name)
 	return -1;
 }
 
+INT32 R_GetForcedSkin(INT32 playernum)
+{
+	if (netgame && cv_forceskin.value >= 0 && R_SkinUsable(playernum, cv_forceskin.value))
+		return cv_forceskin.value;
+
+	if (mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0')
+	{
+		INT32 skinnum = R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter);
+		if (skinnum != -1 && R_SkinUsable(playernum, skinnum))
+			return skinnum;
+	}
+
+	return -1;
+}
+
 // Auxillary function that actually sets the skin
 static void SetSkin(player_t *player, INT32 skinnum)
 {
diff --git a/src/r_skins.h b/src/r_skins.h
index 082b690dda..70164ff7ad 100644
--- a/src/r_skins.h
+++ b/src/r_skins.h
@@ -95,6 +95,7 @@ void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002
 boolean R_SkinUsable(INT32 playernum, INT32 skinnum);
 UINT32 R_GetSkinAvailabilities(void);
 INT32 R_SkinAvailable(const char *name);
+INT32 R_GetForcedSkin(INT32 playernum);
 void R_AddSkins(UINT16 wadnum, boolean mainfile);
 void R_PatchSkins(UINT16 wadnum, boolean mainfile);
 
-- 
GitLab


From db59ec59989eba4a053557e2e7ae4444e983cb1a Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Thu, 17 Aug 2023 17:39:24 +0200
Subject: [PATCH 334/518] Fix interpolation when curling up while flipped

Also fix scaling interpolation while flipped, and fix the NiGHTS Drone flip interpolation on its first tic
---
 src/hardware/hw_main.c | 5 ++++-
 src/hardware/hw_md2.c  | 7 ++++++-
 src/p_mobj.c           | 2 +-
 src/p_user.c           | 5 ++++-
 src/r_things.c         | 6 +++++-
 5 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 36ff86abd7..f253861de3 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -5378,7 +5378,10 @@ static void HWR_ProjectSprite(mobj_t *thing)
 
 		if (vflip)
 		{
-			gz = FIXED_TO_FLOAT(interp.z + thing->height) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale);
+			if (thing->scale != thing->old_scale) // Interpolate heights in reverse gravity when scaling mobjs
+				gz = FIXED_TO_FLOAT(interp.z + FixedMul(thing->height, FixedDiv(interp.scale, thing->scale))) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale);
+			else
+				gz = FIXED_TO_FLOAT(interp.z + thing->height) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale);
 			gzt = gz + (FIXED_TO_FLOAT(spr_height) * this_yscale);
 		}
 		else
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index d005f00377..87881be8d6 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1585,7 +1585,12 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		p.y = FIXED_TO_FLOAT(interp.y)+md2->offset;
 
 		if (flip)
-			p.z = FIXED_TO_FLOAT(interp.z + spr->mobj->height);
+		{
+			if (spr->mobj->scale != spr->mobj->old_scale) // Interpolate heights in reverse gravity when scaling mobjs
+				p.z = FIXED_TO_FLOAT(interp.z + FixedMul(spr->mobj->height, FixedDiv(interp.scale, spr->mobj->scale)));
+			else
+				p.z = FIXED_TO_FLOAT(interp.z + spr->mobj->height);
+		}
 		else
 			p.z = FIXED_TO_FLOAT(interp.z);
 
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 2a40175dc7..9550535f10 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -12593,7 +12593,7 @@ static boolean P_SetupNiGHTSDrone(mapthing_t *mthing, mobj_t *mobj)
 	dronemangoaldiff = max(mobjinfo[MT_NIGHTSDRONE_MAN].height - mobjinfo[MT_NIGHTSDRONE_GOAL].height, 0);
 
 	if (flip && mobj->height != oldheight)
-		P_MoveOrigin(mobj, mobj->x, mobj->y, mobj->z - (mobj->height - oldheight));
+		P_SetOrigin(mobj, mobj->x, mobj->y, mobj->z - (mobj->height - oldheight));
 
 	if (!flip)
 	{
diff --git a/src/p_user.c b/src/p_user.c
index 84d0c0abf0..48141815c3 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8717,7 +8717,10 @@ void P_MovePlayer(player_t *player)
 			player->mo->height = P_GetPlayerHeight(player);
 
 		if (player->mo->eflags & MFE_VERTICALFLIP && player->mo->height != oldheight) // adjust z height for reverse gravity, similar to how it's done for scaling
-			player->mo->z -= player->mo->height - oldheight;
+		{
+			player->mo->z     -= player->mo->height - oldheight;
+			player->mo->old_z -= player->mo->height - oldheight; // Snap the Z adjustment, while keeping the Z interpolation
+		}
 
 		// Crush test...
 		if ((player->mo->ceilingz - player->mo->floorz < player->mo->height)
diff --git a/src/r_things.c b/src/r_things.c
index 90b80dda8f..e69fe1b822 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -2114,7 +2114,11 @@ static void R_ProjectSprite(mobj_t *thing)
 			// When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned.
 			// sprite height - sprite topoffset is the proper inverse of the vertical offset, of course.
 			// remember gz and gzt should be seperated by sprite height, not thing height - thing height can be shorter than the sprite itself sometimes!
-			gz = interp.z + oldthing->height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
+
+			if (oldthing->scale != oldthing->old_scale) // Interpolate heights in reverse gravity when scaling mobjs
+				gz = interp.z + FixedMul(oldthing->height, FixedDiv(interp.scale, oldthing->scale)) - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
+			else
+				gz = interp.z + oldthing->height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
 			gzt = gz + FixedMul(spr_height, FixedMul(spriteyscale, this_scale));
 		}
 		else
-- 
GitLab


From 9b296a15476c48a7cdaaadc9fa016051057b4d15 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 17 Aug 2023 19:27:33 +0200
Subject: [PATCH 335/518] Disable Makefile deprecation warning for now

---
 src/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Makefile b/src/Makefile
index e1d9cb384f..41cef2a179 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -392,4 +392,4 @@ else
 	@:
 endif
 
-$(warning The handwritten GNU Makefile for SRB2 is deprecated, and may be removed in the future. Please consider switching to CMake.)
+#$(warning The handwritten GNU Makefile for SRB2 is deprecated, and may be removed in the future. Please consider switching to CMake.)
-- 
GitLab


From 66cc6a5f44bfab88ed73f33699bd49ba2f80c4b7 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 17 Aug 2023 19:31:55 +0200
Subject: [PATCH 336/518] Correct flags for partially intangible FOFs

---
 src/p_spec.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index 169f129b95..b4f6cdb3f0 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -6565,10 +6565,10 @@ void P_SpawnSpecials(boolean fromnetsave)
 				//Cutting options
 				if (ffloorflags & FOF_RENDERALL)
 				{
-					//If inside is visible, cut inner walls
-					if ((lines[i].args[1] < 255) || (lines[i].args[3] & TMFA_SPLAT) || (lines[i].args[4] & TMFT_VISIBLEFROMINSIDE))
+					//If inside is visible from the outside, cut inner walls
+					if (lines[i].args[1] < 255 || (lines[i].args[3] & TMFA_SPLAT))
 						ffloorflags |= FOF_CUTEXTRA|FOF_EXTRA;
-					else
+					else if (!(lines[i].args[3] & TMFT_VISIBLEFROMINSIDE))
 						ffloorflags |= FOF_CUTLEVEL;
 				}
 
@@ -6624,20 +6624,19 @@ void P_SpawnSpecials(boolean fromnetsave)
 				if (lines[i].args[4] & TMFC_SPLAT)
 					ffloorflags |= FOF_SPLAT;
 
-				//If inside is visible, cut inner walls
-				if (lines[i].args[1] < 0xff || (lines[i].args[3] & TMFT_VISIBLEFROMINSIDE) || (lines[i].args[4] & TMFC_SPLAT))
+				//If inside is visible from the outside, cut inner walls
+				if (lines[i].args[1] < 255 || (lines[i].args[4] & TMFC_SPLAT))
 					ffloorflags |= FOF_CUTEXTRA|FOF_EXTRA;
-				else
-					ffloorflags |= FOF_CUTLEVEL;
-
-				//If player can enter it, render insides
-				if (lines[i].args[3] & TMFT_VISIBLEFROMINSIDE)
+				//If player can view it from the inside, render insides
+				else if (lines[i].args[3] & TMFT_VISIBLEFROMINSIDE)
 				{
 					if (ffloorflags & FOF_RENDERPLANES)
 						ffloorflags |= FOF_BOTHPLANES;
 					if (ffloorflags & FOF_RENDERSIDES)
 						ffloorflags |= FOF_ALLSIDES;
 				}
+				else
+					ffloorflags |= FOF_CUTLEVEL;
 
 				P_AddFakeFloorsByLine(i, lines[i].args[1], lines[i].args[2], ffloorflags, secthinkers);
 				if (lines[i].args[4] & TMFC_AIRBOB)
@@ -6688,10 +6687,10 @@ void P_SpawnSpecials(boolean fromnetsave)
 				//Cutting options
 				if (ffloorflags & FOF_RENDERALL)
 				{
-					//If inside is visible, cut inner walls
-					if ((lines[i].args[1] < 255) || (lines[i].args[3] & TMFA_SPLAT) || (lines[i].args[4] & TMFT_VISIBLEFROMINSIDE))
+					//If inside is visible from the outside, cut inner walls
+					if (lines[i].args[1] < 255 || (lines[i].args[3] & TMFA_SPLAT))
 						ffloorflags |= FOF_CUTEXTRA|FOF_EXTRA;
-					else
+					else if (!(lines[i].args[3] & TMFT_VISIBLEFROMINSIDE))
 						ffloorflags |= FOF_CUTLEVEL;
 				}
 
-- 
GitLab


From 07ef7c46392c9fffd8f8a3ea6fd2b4c555970901 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 17 Aug 2023 20:15:03 +0200
Subject: [PATCH 337/518] Prevent FOF alpha from wrapping around in OpenGL

---
 src/hardware/hw_main.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 36ff86abd7..d83ee710d0 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -1699,7 +1699,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 					if ((rover->fofflags & FOF_TRANSLUCENT && !(rover->fofflags & FOF_SPLAT)) || rover->blend)
 					{
 						blendmode = rover->blend ? HWR_GetBlendModeFlag(rover->blend) : PF_Translucent;
-						Surf.PolyColor.s.alpha = (UINT8)rover->alpha-1 > 255 ? 255 : rover->alpha-1;
+						Surf.PolyColor.s.alpha = max(0, min(rover->alpha, 255));
 					}
 
 					if (gl_frontsector->numlights)
@@ -1822,7 +1822,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 					if ((rover->fofflags & FOF_TRANSLUCENT && !(rover->fofflags & FOF_SPLAT)) || rover->blend)
 					{
 						blendmode = rover->blend ? HWR_GetBlendModeFlag(rover->blend) : PF_Translucent;
-						Surf.PolyColor.s.alpha = (UINT8)rover->alpha-1 > 255 ? 255 : rover->alpha-1;
+						Surf.PolyColor.s.alpha = max(0, min(rover->alpha, 255));
 					}
 
 					if (gl_backsector->numlights)
@@ -3095,7 +3095,7 @@ static void HWR_Subsector(size_t num)
 										   false,
 					                       *rover->bottomheight,
 					                       *gl_frontsector->lightlist[light].lightlevel,
-					                       rover->alpha-1 > 255 ? 255 : rover->alpha-1, rover->master->frontsector,
+					                       max(0, min(rover->alpha, 255)), rover->master->frontsector,
 					                       HWR_RippleBlend(gl_frontsector, rover, false) | (rover->blend ? HWR_GetBlendModeFlag(rover->blend) : PF_Translucent),
 					                       false, *gl_frontsector->lightlist[light].extra_colormap);
 				}
@@ -3141,7 +3141,7 @@ static void HWR_Subsector(size_t num)
 											true,
 					                        *rover->topheight,
 					                        *gl_frontsector->lightlist[light].lightlevel,
-					                        rover->alpha-1 > 255 ? 255 : rover->alpha-1, rover->master->frontsector,
+					                        max(0, min(rover->alpha, 255)), rover->master->frontsector,
 					                        HWR_RippleBlend(gl_frontsector, rover, false) | (rover->blend ? HWR_GetBlendModeFlag(rover->blend) : PF_Translucent),
 					                        false, *gl_frontsector->lightlist[light].extra_colormap);
 				}
-- 
GitLab


From 623fa126ad73d5be389816a74c61dafcebe278dd Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Fri, 18 Aug 2023 15:08:12 +0200
Subject: [PATCH 338/518] Fix backside ceiling vertex slopes

---
 src/p_slopes.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/p_slopes.c b/src/p_slopes.c
index 6aca116a58..1c0ee81a7e 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -566,6 +566,7 @@ static void line_SpawnViaMapthingVertexes(const int linenum, const boolean spawn
 	case TMSP_BACKCEILING:
 		slopetoset = &line->backsector->c_slope;
 		side = &sides[line->sidenum[1]];
+		break;
 	default:
 		return;
 	}
-- 
GitLab


From 93f11508fc5298f04fb9b3f53aa8a2e3385cd94b Mon Sep 17 00:00:00 2001
From: bitten2up <seant9140@gmail.com>
Date: Sat, 19 Aug 2023 19:52:10 -0500
Subject: [PATCH 339/518] remove stray else

---
 src/sdl/i_video.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index d2fbb90063..4cac59ced2 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -1593,7 +1593,6 @@ boolean VID_CheckRenderer(void)
 			else if (vid.glstate == VID_GL_LIBRARY_ERROR)
 				rendererchanged = false;
 		}
-		else
 #endif
 
 		if (!contextcreated)
-- 
GitLab


From 7ef7de1bc58a87c2e7131a60bc26e04f76ff3114 Mon Sep 17 00:00:00 2001
From: Sal <tehrealsalt@gmail.com>
Date: Sun, 20 Aug 2023 03:16:14 +0000
Subject: [PATCH 340/518] Level select cheat fixes

---
 src/d_netcmd.c | 1 +
 src/m_cond.c   | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 9d4156e1df..33281e9924 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1925,6 +1925,7 @@ static void Command_Map_f(void)
 	newresetplayers = ! COM_CheckParm("-noresetplayers");
 
 	prevent_cheat = !( usedCheats ) && !( option_force || cv_debug );
+	set_cheated = false;
 
 	if (!( netgame || multiplayer ))
 	{
diff --git a/src/m_cond.c b/src/m_cond.c
index 3dfb1dceb2..6c87ebf6e5 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -528,7 +528,7 @@ UINT8 M_CampaignWarpIsCheat(INT32 gt, INT32 mapnum, gamedata_t *data)
 	}
 
 	// It's only a cheat if you've never been there.
-	return (!(data->mapvisited[mapnum]));
+	return (!(data->mapvisited[mapnum-1]));
 }
 
 INT32 M_CountEmblems(gamedata_t *data)
-- 
GitLab


From d589953fc4287ee998f6751553e018bf367e9b2d Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sat, 19 Aug 2023 23:39:54 -0400
Subject: [PATCH 341/518] Fix P_WriteSkincolor

---
 src/p_setup.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/p_setup.c b/src/p_setup.c
index 13187bb41c..77f5370dd0 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -50,6 +50,7 @@
 #include "m_random.h"
 
 #include "dehacked.h" // for map headers
+#include "deh_tables.h" // FREE_SKINCOLORS
 #include "r_main.h"
 #include "m_cond.h" // for emblems
 
@@ -1266,12 +1267,19 @@ static void P_WriteDuplicateText(const char *text, char **target)
 
 static void P_WriteSkincolor(INT32 constant, char **target)
 {
+	const char *color_name;
+
 	if (constant <= SKINCOLOR_NONE
 	|| constant >= (INT32)numskincolors)
 		return;
 
+	if (constant >= SKINCOLOR_FIRSTFREESLOT)
+		color_name = FREE_SKINCOLORS[constant - SKINCOLOR_FIRSTFREESLOT];
+	else
+		color_name = COLOR_ENUMS[constant];
+
 	P_WriteDuplicateText(
-		va("SKINCOLOR_%s", skincolors[constant].name),
+		va("SKINCOLOR_%s", color_name),
 		target
 	);
 }
-- 
GitLab


From 9d9b6d83c25a74ab7635413eb9185f89aa0ac5cc Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sat, 19 Aug 2023 23:58:43 -0400
Subject: [PATCH 342/518] Don't NiGHTS pull emblems you can't collect

---
 src/p_inter.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_inter.c b/src/p_inter.c
index 74ca6972fb..271b6ebc45 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -1144,7 +1144,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 					if (!(mo2->type == MT_RING || mo2->type == MT_COIN
 						|| mo2->type == MT_BLUESPHERE || mo2->type == MT_BOMBSPHERE
 						|| mo2->type == MT_NIGHTSCHIP || mo2->type == MT_NIGHTSSTAR
-						|| ((mo2->type == MT_EMBLEM) && (mo2->reactiontime & GE_NIGHTSPULL))))
+						|| ((mo2->type == MT_EMBLEM) && (mo2->reactiontime & GE_NIGHTSPULL) && P_CanPickupEmblem(player, mo2->health - 1) && !P_EmblemWasCollected(mo2->health - 1))))
 						continue;
 
 					// Yay! The thing's in reach! Pull it in!
-- 
GitLab


From 909e07be65d74eb50fc7a41b27852ae387475738 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Sun, 6 Aug 2023 19:34:50 -0700
Subject: [PATCH 343/518] Rename HAVE_STRLCPY to SRB2_HAVE_STRLCPY, fix
 non-glibc compile

- Fix compile with msvcrt
- Fix compile with SDL 2.28.2
---
 src/doomtype.h | 12 ++++++++----
 src/string.c   |  2 +-
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/doomtype.h b/src/doomtype.h
index dd5fb0f8dc..4321c707d7 100644
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -122,9 +122,13 @@ int endswith (const char *base, const char *tag);
 #define HAVE_DOSSTR_FUNCS
 #endif
 
-// glibc 2.38: added strlcpy and strlcat to _DEFAULT_SOURCE
-#if defined (__APPLE__) || __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38)
-#define HAVE_STRLCPY
+#if defined (__APPLE__)
+	#define SRB2_HAVE_STRLCPY
+#elif defined (__GLIBC_PREREQ)
+	// glibc 2.38: added strlcpy and strlcat to _DEFAULT_SOURCE
+	#if __GLIBC_PREREQ(2, 38)
+		#define SRB2_HAVE_STRLCPY
+	#endif
 #endif
 
 #ifndef HAVE_DOSSTR_FUNCS
@@ -134,7 +138,7 @@ int strlwr(char *n); // from dosstr.c
 
 #include <stddef.h> // for size_t
 
-#ifndef HAVE_STRLCPY
+#ifndef SRB2_HAVE_STRLCPY
 size_t strlcat(char *dst, const char *src, size_t siz);
 size_t strlcpy(char *dst, const char *src, size_t siz);
 #endif
diff --git a/src/string.c b/src/string.c
index cbc8ea5826..2f16fa4c68 100644
--- a/src/string.c
+++ b/src/string.c
@@ -15,7 +15,7 @@
 #include <string.h>
 #include "doomdef.h"
 
-#ifndef HAVE_STRLCPY
+#ifndef SRB2_HAVE_STRLCPY
 
 // Like the OpenBSD version, but it doesn't check for src not being a valid
 // C string.
-- 
GitLab


From 2f1db66e78975f265bd7e3f9064398928090130f Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 20 Aug 2023 07:23:07 -0400
Subject: [PATCH 344/518] Fix drawing of final color

---
 src/m_menu.c | 35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 3451b90d63..ff9d04c531 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -12183,21 +12183,36 @@ colordraw:
 		{
 			for (i = 0; i < 16; i++)
 			{
-				if (skincolors[mc->color].accessible && !stoprow)
+				if (skincolors[mc->color].accessible)
 				{
 					M_DrawColorRamp(x + i*w, y + j*16, w, 1, skincolors[mc->color]);
-					if (mc->color == setupm_fakecolor->color) // store current color position
-						{
-							cx = x + i*w;
-							cy = y + j*16;
-						}
+
+					if (mc == setupm_fakecolor) // store current color position
+					{
+						cx = x + i*w;
+						cy = y + j*16;
+					}
 				}
-				mc = mc->next;
-				while (!skincolors[mc->color].accessible && !stoprow) // Find accessible color after this one
+
+				if (stoprow)
 				{
-					mc = mc->next;
-					if (mc == menucolortail) stoprow = true;
+					break;
 				}
+
+				// Find accessible color after this one
+				do
+				{
+					mc = mc->next;
+					if (mc == menucolortail)
+					{
+						stoprow = true;
+					}
+				} while (!skincolors[mc->color].accessible && !stoprow);
+			}
+
+			if (stoprow)
+			{
+				break;
 			}
 		}
 
-- 
GitLab


From 4d1e6df2d7c7e0c62d76e32ace11cd6b05af602e Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 20 Aug 2023 11:28:39 -0400
Subject: [PATCH 345/518] Fix color grid input bugs

There's still some slight awkwardness trying to wrap downwards into uneven row grids, but this is significantly better and this already took up way more time than I wanted it to.
---
 src/m_menu.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 189 insertions(+), 14 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index ff9d04c531..6531524519 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -12054,6 +12054,136 @@ static INT32        setupm_fakeskin;
 static menucolor_t *setupm_fakecolor;
 static boolean      colorgrid;
 
+#define COLOR_GRID_ROW_SIZE (16)
+
+static UINT16 M_GetColorGridIndex(UINT16 color)
+{
+	menucolor_t *look;
+	UINT16 i = 0;
+
+	if (!skincolors[color].accessible)
+	{
+		return 0;
+	}
+
+	for (look = menucolorhead; ; i++, look = look->next)
+	{
+		while (!skincolors[look->color].accessible) // skip inaccessible colors
+		{
+			if (look == menucolortail)
+			{
+				return 0;
+			}
+
+			look = look->next;
+		}
+
+		if (look->color == color)
+		{
+			return i;
+		}
+
+		if (look == menucolortail)
+		{
+			return 0;
+		}
+	}
+}
+
+static INT32 M_GridIndexToX(UINT16 index)
+{
+	return (index % COLOR_GRID_ROW_SIZE);
+}
+
+static INT32 M_GridIndexToY(UINT16 index)
+{
+	return (index / COLOR_GRID_ROW_SIZE);
+}
+
+static UINT16 M_ColorGridLen(void)
+{
+	menucolor_t *look;
+	UINT16 i = 0;
+
+	for (look = menucolorhead; ; i++)
+	{
+		do
+		{
+			if (look == menucolortail)
+			{
+				return i;
+			}
+
+			look = look->next;
+		}
+		while (!skincolors[look->color].accessible); // skip inaccessible colors
+	}
+}
+
+static UINT16 M_GridPosToGridIndex(INT32 x, INT32 y)
+{
+	const UINT16 grid_len = M_ColorGridLen();
+	const UINT16 grid_height = ((grid_len - 1) / COLOR_GRID_ROW_SIZE) + 1;
+	const UINT16 last_row_len = COLOR_GRID_ROW_SIZE - ((grid_height * COLOR_GRID_ROW_SIZE) - grid_len);
+
+	UINT16 row_len = COLOR_GRID_ROW_SIZE;
+	UINT16 new_index = 0;
+
+	while (y < 0)
+	{
+		y += grid_height;
+	}
+	y = (y % grid_height);
+
+	if (y >= grid_height-1 && last_row_len > 0)
+	{
+		row_len = last_row_len;
+	}
+
+	while (x < 0)
+	{
+		x += row_len;
+	}
+	x = (x % row_len);
+
+	new_index = (y * COLOR_GRID_ROW_SIZE) + x;
+	if (new_index >= grid_len)
+	{
+		new_index = grid_len - 1;
+	}
+
+	return new_index;
+}
+
+static menucolor_t *M_GridIndexToMenuColor(UINT16 index)
+{
+	menucolor_t *look = menucolorhead;
+	UINT16 i = 0;
+
+	for (look = menucolorhead; ; i++, look = look->next)
+	{
+		while (!skincolors[look->color].accessible) // skip inaccessible colors
+		{
+			if (look == menucolortail)
+			{
+				return menucolorhead;
+			}
+
+			look = look->next;
+		}
+
+		if (i == index)
+		{
+			return look;
+		}
+
+		if (look == menucolortail)
+		{
+			return menucolorhead;
+		}
+	}
+}
+
 static void M_DrawSetupMultiPlayerMenu(void)
 {
 	INT32 x, y, cursory = 0, flags = 0;
@@ -12333,15 +12463,16 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 		case KEY_DOWNARROW:
 		case KEY_UPARROW:
 			{
-				UINT8 i;
 				if (itemOn == 2 && colorgrid)
 				{
-					for (i = 0; i < 16; i++)
-					{
-						setupm_fakecolor = (choice == KEY_UPARROW) ? setupm_fakecolor->prev : setupm_fakecolor->next;
-						while (!skincolors[setupm_fakecolor->color].accessible) // skip inaccessible colors
-							setupm_fakecolor = (choice == KEY_UPARROW) ? setupm_fakecolor->prev : setupm_fakecolor->next;
-					}
+					UINT16 index = M_GetColorGridIndex(setupm_fakecolor->color);
+					INT32 x = M_GridIndexToX(index);
+					INT32 y = M_GridIndexToY(index);
+
+					y += (choice == KEY_UPARROW) ? -1 : 1;
+
+					index = M_GridPosToGridIndex(x, y);
+					setupm_fakecolor = M_GridIndexToMenuColor(index);
 				}
 				else if (choice == KEY_UPARROW)
 					M_PrevOpt();
@@ -12368,8 +12499,23 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 			}
 			else if (itemOn == 2) // player color
 			{
+				if (colorgrid)
+				{
+					UINT16 index = M_GetColorGridIndex(setupm_fakecolor->color);
+					INT32 x = M_GridIndexToX(index);
+					INT32 y = M_GridIndexToY(index);
+
+					x--;
+
+					index = M_GridPosToGridIndex(x, y);
+					setupm_fakecolor = M_GridIndexToMenuColor(index);
+				}
+				else
+				{
+					setupm_fakecolor = setupm_fakecolor->prev;
+				}
+
 				S_StartSound(NULL,sfx_menu1); // Tails
-				setupm_fakecolor = setupm_fakecolor->prev;
 			}
 			break;
 
@@ -12408,8 +12554,23 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 			}
 			else if (itemOn == 2) // player color
 			{
+				if (colorgrid)
+				{
+					UINT16 index = M_GetColorGridIndex(setupm_fakecolor->color);
+					INT32 x = M_GridIndexToX(index);
+					INT32 y = M_GridIndexToY(index);
+
+					x++;
+
+					index = M_GridPosToGridIndex(x, y);
+					setupm_fakecolor = M_GridIndexToMenuColor(index);
+				}
+				else
+				{
+					setupm_fakecolor = setupm_fakecolor->next;
+				}
+
 				S_StartSound(NULL,sfx_menu1); // Tails
-				setupm_fakecolor = setupm_fakecolor->next;
 			}
 			break;
 
@@ -12419,13 +12580,28 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 				UINT8 i;
 				if (itemOn == 2) // player color
 				{
-					S_StartSound(NULL,sfx_menu1);
-					for (i = 0; i < (colorgrid ? 64 : 13); i++) // or (282-charw)/(2*indexwidth)
+					if (colorgrid)
+					{
+						UINT16 index = M_GetColorGridIndex(setupm_fakecolor->color);
+						INT32 x = M_GridIndexToX(index);
+						INT32 y = M_GridIndexToY(index);
+
+						y += (choice == KEY_UPARROW) ? -4 : 4;
+
+						index = M_GridPosToGridIndex(x, y);
+						setupm_fakecolor = M_GridIndexToMenuColor(index);
+					}
+					else
 					{
-						setupm_fakecolor = (choice == KEY_PGUP) ? setupm_fakecolor->prev : setupm_fakecolor->next;
-						while (!skincolors[setupm_fakecolor->color].accessible) // skip inaccessible colors
+						for (i = 0; i < 13; i++) // or (282-charw)/(2*indexwidth)
+						{
 							setupm_fakecolor = (choice == KEY_PGUP) ? setupm_fakecolor->prev : setupm_fakecolor->next;
+							while (!skincolors[setupm_fakecolor->color].accessible) // skip inaccessible colors
+								setupm_fakecolor = (choice == KEY_PGUP) ? setupm_fakecolor->prev : setupm_fakecolor->next;
+						}
 					}
+
+					S_StartSound(NULL, sfx_menu1); // Tails
 				}
 			}
 			break;
@@ -12455,7 +12631,6 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 				}
 			}
 			break;
-			break;
 
 		case KEY_DEL:
 			if (itemOn == 0 && (l = strlen(setupm_name))!=0)
-- 
GitLab


From 259732cccb4b7e37338bfa347bf1d67c37fc46d0 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 20 Aug 2023 17:42:27 +0200
Subject: [PATCH 346/518] Make FOF fades use 0-255 alpha, not 1-256

---
 src/p_spec.c | 38 +++++++++++++++++---------------------
 1 file changed, 17 insertions(+), 21 deletions(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index b4f6cdb3f0..45619014dd 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -3286,19 +3286,18 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 						foundrover = true;
 
 						// If fading an invisible FOF whose render flags we did not yet set,
-						// initialize its alpha to 1
-						// for relative alpha calc
+						// initialize its alpha to 0 for relative alpha calculation
 						if (!(line->args[3] & TMST_DONTDOTRANSLUCENT) &&      // do translucent
 							(rover->spawnflags & FOF_NOSHADE) && // do not include light blocks, which don't set FOF_NOSHADE
 							!(rover->spawnflags & FOF_RENDERSIDES) &&
 							!(rover->spawnflags & FOF_RENDERPLANES) &&
 							!(rover->fofflags & FOF_RENDERALL))
-							rover->alpha = 1;
+							rover->alpha = 0;
 
 						P_RemoveFakeFloorFader(rover);
 						P_FadeFakeFloor(rover,
 							rover->alpha,
-							max(1, min(256, (line->args[3] & TMST_RELATIVE) ? rover->alpha + destvalue : destvalue)),
+							max(0, min(255, (line->args[3] & TMST_RELATIVE) ? rover->alpha + destvalue : destvalue)),
 							0,                                         // set alpha immediately
 							false, NULL,                               // tic-based logic
 							false,                                     // do not handle FOF_EXISTS
@@ -3372,19 +3371,18 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 						else
 						{
 							// If fading an invisible FOF whose render flags we did not yet set,
-							// initialize its alpha to 1
-							// for relative alpha calc
+							// initialize its alpha to 1 for relative alpha calculation
 							if (!(line->args[4] & TMFT_DONTDOTRANSLUCENT) &&      // do translucent
 								(rover->spawnflags & FOF_NOSHADE) && // do not include light blocks, which don't set FOF_NOSHADE
 								!(rover->spawnflags & FOF_RENDERSIDES) &&
 								!(rover->spawnflags & FOF_RENDERPLANES) &&
 								!(rover->fofflags & FOF_RENDERALL))
-								rover->alpha = 1;
+								rover->alpha = 0;
 
 							P_RemoveFakeFloorFader(rover);
 							P_FadeFakeFloor(rover,
 								rover->alpha,
-								max(1, min(256, (line->args[4] & TMFT_RELATIVE) ? rover->alpha + destvalue : destvalue)),
+								max(0, min(255, (line->args[4] & TMFT_RELATIVE) ? rover->alpha + destvalue : destvalue)),
 								0,                                         // set alpha immediately
 								false, NULL,                               // tic-based logic
 								!(line->args[4] & TMFT_DONTDOEXISTS),      // do not handle FOF_EXISTS
@@ -7755,15 +7753,14 @@ static boolean P_FadeFakeFloor(ffloor_t *rover, INT16 sourcevalue, INT16 destval
 	if (rover->master->special == 258) // Laser block
 		return false;
 
-	// If fading an invisible FOF whose render flags we did not yet set,
-	// initialize its alpha to 1
+	// If fading an invisible FOF whose render flags we did not yet set, initialize its alpha to 1
 	if (dotranslucent &&
 		(rover->spawnflags & FOF_NOSHADE) && // do not include light blocks, which don't set FOF_NOSHADE
 		!(rover->fofflags & FOF_FOG) && // do not include fog
 		!(rover->spawnflags & FOF_RENDERSIDES) &&
 		!(rover->spawnflags & FOF_RENDERPLANES) &&
 		!(rover->fofflags & FOF_RENDERALL))
-		rover->alpha = 1;
+		rover->alpha = 0;
 
 	if (fadingdata)
 		alpha = fadingdata->alpha;
@@ -7849,7 +7846,7 @@ static boolean P_FadeFakeFloor(ffloor_t *rover, INT16 sourcevalue, INT16 destval
 	{
 		if (doexists && !(rover->spawnflags & FOF_BUSTUP))
 		{
-			if (alpha <= 1)
+			if (alpha <= 0)
 				rover->fofflags &= ~FOF_EXISTS;
 			else
 				rover->fofflags |= FOF_EXISTS;
@@ -7861,7 +7858,7 @@ static boolean P_FadeFakeFloor(ffloor_t *rover, INT16 sourcevalue, INT16 destval
 
 		if (dotranslucent && !(rover->fofflags & FOF_FOG))
 		{
-			if (alpha >= 256)
+			if (alpha >= 255)
 			{
 				if (!(rover->fofflags & FOF_CUTSOLIDS) &&
 					(rover->spawnflags & FOF_CUTSOLIDS))
@@ -7961,11 +7958,11 @@ static boolean P_FadeFakeFloor(ffloor_t *rover, INT16 sourcevalue, INT16 destval
 		else // clamp fadingdata->alpha to software's alpha levels
 		{
 			if (alpha < 12)
-				rover->alpha = destvalue < 12 ? destvalue : 1; // Don't even draw it
+				rover->alpha = destvalue < 12 ? destvalue : 0; // Don't even draw it
 			else if (alpha < 38)
 				rover->alpha = destvalue >= 12 && destvalue < 38 ? destvalue : 25;
 			else if (alpha < 64)
-				rover->alpha = destvalue >=38 && destvalue < 64 ? destvalue : 51;
+				rover->alpha = destvalue >= 38 && destvalue < 64 ? destvalue : 51;
 			else if (alpha < 89)
 				rover->alpha = destvalue >= 64 && destvalue < 89 ? destvalue : 76;
 			else if (alpha < 115)
@@ -7981,7 +7978,7 @@ static boolean P_FadeFakeFloor(ffloor_t *rover, INT16 sourcevalue, INT16 destval
 			else if (alpha < 243)
 				rover->alpha = destvalue >= 217 && destvalue < 243 ? destvalue : 230;
 			else // Opaque
-				rover->alpha = destvalue >= 243 ? destvalue : 256;
+				rover->alpha = destvalue >= 243 ? destvalue : 255;
 		}
 	}
 
@@ -8011,17 +8008,16 @@ static void P_AddFakeFloorFader(ffloor_t *rover, size_t sectornum, size_t ffloor
 {
 	fade_t *d;
 
-	// If fading an invisible FOF whose render flags we did not yet set,
-	// initialize its alpha to 1
+	// If fading an invisible FOF whose render flags we did not yet set, initialize its alpha to 1
 	if (dotranslucent &&
 		(rover->spawnflags & FOF_NOSHADE) && // do not include light blocks, which don't set FOF_NOSHADE
 		!(rover->spawnflags & FOF_RENDERSIDES) &&
 		!(rover->spawnflags & FOF_RENDERPLANES) &&
 		!(rover->fofflags & FOF_RENDERALL))
-		rover->alpha = 1;
+		rover->alpha = 0;
 
 	// already equal, nothing to do
-	if (rover->alpha == max(1, min(256, relative ? rover->alpha + destvalue : destvalue)))
+	if (rover->alpha == max(0, min(255, relative ? rover->alpha + destvalue : destvalue)))
 		return;
 
 	d = Z_Malloc(sizeof *d, PU_LEVSPEC, NULL);
@@ -8032,7 +8028,7 @@ static void P_AddFakeFloorFader(ffloor_t *rover, size_t sectornum, size_t ffloor
 	d->ffloornum = (UINT32)ffloornum;
 
 	d->alpha = d->sourcevalue = rover->alpha;
-	d->destvalue = max(1, min(256, relative ? rover->alpha + destvalue : destvalue)); // rover->alpha is 1-256
+	d->destvalue = max(0, min(255, relative ? rover->alpha + destvalue : destvalue)); // rover->alpha is 0-255
 
 	if (ticbased)
 	{
-- 
GitLab


From f62c2f4c7541e887705fe58248c141b3d2e82b93 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 20 Aug 2023 17:55:06 +0200
Subject: [PATCH 347/518] Fix arg # typos for "visible from inside" checks

---
 src/p_spec.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index 45619014dd..28ecc60f4d 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -6566,7 +6566,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 					//If inside is visible from the outside, cut inner walls
 					if (lines[i].args[1] < 255 || (lines[i].args[3] & TMFA_SPLAT))
 						ffloorflags |= FOF_CUTEXTRA|FOF_EXTRA;
-					else if (!(lines[i].args[3] & TMFT_VISIBLEFROMINSIDE))
+					else if (!(lines[i].args[4] & TMFT_VISIBLEFROMINSIDE))
 						ffloorflags |= FOF_CUTLEVEL;
 				}
 
@@ -6688,7 +6688,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 					//If inside is visible from the outside, cut inner walls
 					if (lines[i].args[1] < 255 || (lines[i].args[3] & TMFA_SPLAT))
 						ffloorflags |= FOF_CUTEXTRA|FOF_EXTRA;
-					else if (!(lines[i].args[3] & TMFT_VISIBLEFROMINSIDE))
+					else if (!(lines[i].args[4] & TMFT_VISIBLEFROMINSIDE))
 						ffloorflags |= FOF_CUTLEVEL;
 				}
 
-- 
GitLab


From 9c8dd5475111d2b66f1a9a1d18836e9f14703ecb Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 20 Aug 2023 11:56:55 -0400
Subject: [PATCH 348/518] Fix color grid not extending to the very bottom

---
 src/m_menu.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 6531524519..6a694b8e32 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -12302,16 +12302,23 @@ colordraw:
 		boolean stoprow = false;
 		menucolor_t *mc; // Last accessed color
 
+		const UINT16 grid_len = M_ColorGridLen();
+		const UINT16 grid_end_y = M_GridIndexToY(grid_len - 1);
+
+		INT32 grid_select = M_GetColorGridIndex(setupm_fakecolor->color);
+		INT32 grid_select_y = M_GridIndexToY(grid_select);
+
 		x = 132;
 		y = 66;
-		pos = min(max(0, 16*((M_GetColorIndex(setupm_fakecolor->color)-1)/16) - 64), 16*(M_GetColorIndex(menucolortail->color)/16-1) - 128);
-		mc = M_GetColorFromIndex(pos);
+
+		pos = M_GridPosToGridIndex(0, max(0, min(grid_select_y - 3, grid_end_y - 7)));
+		mc = M_GridIndexToMenuColor(pos);
 
 		// Draw grid
 		V_DrawFill(x-2, y-2, 132, 132, 159);
 		for (j = 0; j < 8; j++)
 		{
-			for (i = 0; i < 16; i++)
+			for (i = 0; i < COLOR_GRID_ROW_SIZE; i++)
 			{
 				if (skincolors[mc->color].accessible)
 				{
-- 
GitLab


From 40c37ce4c2997dbb3487dcda00b9dfc11d30c895 Mon Sep 17 00:00:00 2001
From: Sally Coolatta <tehrealsalt@gmail.com>
Date: Sun, 20 Aug 2023 12:05:58 -0400
Subject: [PATCH 349/518] Revert left/right movement

---
 src/m_menu.c | 34 ++--------------------------------
 1 file changed, 2 insertions(+), 32 deletions(-)

diff --git a/src/m_menu.c b/src/m_menu.c
index 6a694b8e32..c494421415 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -12506,22 +12506,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 			}
 			else if (itemOn == 2) // player color
 			{
-				if (colorgrid)
-				{
-					UINT16 index = M_GetColorGridIndex(setupm_fakecolor->color);
-					INT32 x = M_GridIndexToX(index);
-					INT32 y = M_GridIndexToY(index);
-
-					x--;
-
-					index = M_GridPosToGridIndex(x, y);
-					setupm_fakecolor = M_GridIndexToMenuColor(index);
-				}
-				else
-				{
-					setupm_fakecolor = setupm_fakecolor->prev;
-				}
-
+				setupm_fakecolor = setupm_fakecolor->prev;
 				S_StartSound(NULL,sfx_menu1); // Tails
 			}
 			break;
@@ -12561,22 +12546,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
 			}
 			else if (itemOn == 2) // player color
 			{
-				if (colorgrid)
-				{
-					UINT16 index = M_GetColorGridIndex(setupm_fakecolor->color);
-					INT32 x = M_GridIndexToX(index);
-					INT32 y = M_GridIndexToY(index);
-
-					x++;
-
-					index = M_GridPosToGridIndex(x, y);
-					setupm_fakecolor = M_GridIndexToMenuColor(index);
-				}
-				else
-				{
-					setupm_fakecolor = setupm_fakecolor->next;
-				}
-
+				setupm_fakecolor = setupm_fakecolor->next;
 				S_StartSound(NULL,sfx_menu1); // Tails
 			}
 			break;
-- 
GitLab


From 50fc1abf9a0d6721012a42aa6bf53127854aca1c Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 20 Aug 2023 18:30:30 +0200
Subject: [PATCH 350/518] Revert invcolor tweaks for Red/Rosy/Lavender

---
 src/info.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/info.c b/src/info.c
index abcf4b4999..54f3822e2c 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21604,15 +21604,15 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Moss",      {0x58, 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f, 0x5f}, SKINCOLOR_BEIGE,     13, V_GREENMAP,  true}, // SKINCOLOR_MOSS
 	{"Azure",     {0x90, 0x90, 0x91, 0x91, 0xaa, 0xaa, 0xab, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf}, SKINCOLOR_PINK,      5,  V_AZUREMAP,  true}, // SKINCOLOR_AZURE
 	{"Eggplant",  {   4,   8,    11,   11,   16,  195,  195,  195,  196,  186,  187,  187,  254,  254,   30,   31}, SKINCOLOR_ROSEBUSH,  5,  V_PURPLEMAP, true}, // SKINCOLOR_EGGPLANT
-	{"Lavender",  {0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7}, SKINCOLOR_HEADLIGHT, 8,  V_PURPLEMAP, true}, // SKINCOLOR_LAVENDER
+	{"Lavender",  {0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7}, SKINCOLOR_GOLD,      4,  V_PURPLEMAP, true}, // SKINCOLOR_LAVENDER
 
 	// Viv's vivid colours (toast 21/07/17)
 	// Tweaks & additions (Lach, sphere, Alice, MotorRoach 26/10/22)
 	{"Ruby",       {0xb0, 0xb0, 0xc9, 0xca, 0xcc, 0x26, 0x27, 0x28, 0x29, 0x2a, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfd}, SKINCOLOR_EMERALD,    10, V_REDMAP,     true}, // SKINCOLOR_RUBY
 	{"Cherry",     { 202,  203,  204,  205,  206,   40,   41,   42,   43,   44,  186,  187,   28,   29,   30,   31}, SKINCOLOR_MIDNIGHT,   10, V_REDMAP,     true}, // SKINCOLOR_CHERRY
 	{"Salmon",     {0xd0, 0xd0, 0xd1, 0xd2, 0x20, 0x21, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e}, SKINCOLOR_FOREST,     6,  V_REDMAP,     true}, // SKINCOLOR_SALMON
-	{"Pepper",     { 210,   32,   33,   34,   35,   35,   36,   37,   38,   39,   41,   43,   45,   45,   46,   47}, SKINCOLOR_GREEN,      10, V_REDMAP,     true}, // SKINCOLOR_PEPPER
-	{"Red",        {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x47, 0x2e, 0x2f}, SKINCOLOR_MASTER,     8,  V_REDMAP,     true}, // SKINCOLOR_RED
+	{"Pepper",     { 210,   32,   33,   34,   35,   35,   36,   37,   38,   39,   41,   43,   45,   45,   46,   47}, SKINCOLOR_MASTER,     8,  V_REDMAP,     true}, // SKINCOLOR_PEPPER
+	{"Red",        {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x47, 0x2e, 0x2f}, SKINCOLOR_GREEN,      10, V_REDMAP,     true}, // SKINCOLOR_RED
 	{"Crimson",    {0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x1f}, SKINCOLOR_ICY,        10, V_REDMAP,     true}, // SKINCOLOR_CRIMSON
 	{"Flame",      {0x31, 0x32, 0x33, 0x36, 0x22, 0x22, 0x25, 0x25, 0x25, 0xcd, 0xcf, 0xcf, 0xc5, 0xc5, 0xc7, 0xc7}, SKINCOLOR_PURPLE,     8,  V_REDMAP,     true}, // SKINCOLOR_FLAME
 	{"Garnet",     {   0,   83,   50,   53,   34,   35,   37,   38,   39,   40,   42,   44,   45,   46,   47,   47}, SKINCOLOR_AQUAMARINE, 6,  V_REDMAP,     true}, // SKINCOLOR_GARNET
@@ -21626,7 +21626,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Orange",     {  49,   50,   51,   52,   53,   54,   55,   57,   58,   59,   60,   42,   44,   45,   46,   46}, SKINCOLOR_BLUE,       4,  V_ORANGEMAP,  true}, // SKINCOLOR_ORANGE
 	{"Pumpkin",    {  51,   52,   53,   54,   56,   58,   59,   59,   61,   61,   63,   45,   46,   47,   47,   31}, SKINCOLOR_ARCTIC,     12, V_ORANGEMAP,  true}, // SKINCOLOR_PUMPKIN
 	{"Rust",       {0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3f, 0x2c, 0x2d, 0x47, 0x2e, 0x2f, 0x2f}, SKINCOLOR_YOGURT,     8,  V_ORANGEMAP,  true}, // SKINCOLOR_RUST
-	{"Gold",       {0x51, 0x51, 0x54, 0x54, 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x3f, 0x2d, 0x2e, 0x2f, 0x2f}, SKINCOLOR_MAUVE,      8,  V_YELLOWMAP,  true}, // SKINCOLOR_GOLD
+	{"Gold",       {0x51, 0x51, 0x54, 0x54, 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x3f, 0x2d, 0x2e, 0x2f, 0x2f}, SKINCOLOR_LAVENDER,   10, V_YELLOWMAP,  true}, // SKINCOLOR_GOLD
 	{"Topaz",      {   0,   81,   83,   73,   74,   74,   65,   52,   53,   54,   56,   58,   60,   42,   43,   45}, SKINCOLOR_METEORITE,  10, V_YELLOWMAP,  true}, // SKINCOLOR_TOPAZ
 	{"Sandy",      {0x53, 0x40, 0x41, 0x42, 0x43, 0xe6, 0xe9, 0xe9, 0xea, 0xec, 0xec, 0xc6, 0xc6, 0xc7, 0xc7, 0xfe}, SKINCOLOR_SKY,        8,  V_YELLOWMAP,  true}, // SKINCOLOR_SANDY
 	{"Goldenrod",  {   0,   80,   81,   81,   83,   73,   73,   64,   65,   66,   67,   68,   69,   62,   44,   45}, SKINCOLOR_MAJESTY,    8,  V_YELLOWMAP,  true}, // SKINCOLOR_GOLDENROD
@@ -21638,18 +21638,18 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Peridot",    {0x58, 0x58, 0xbc, 0xbc, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0x5e, 0x5e, 0x5f, 0x5f, 0x77, 0x77}, SKINCOLOR_COBALT,     2,  V_PERIDOTMAP, true}, // SKINCOLOR_PERIDOT
 	{"Apple",      {0x49, 0x49, 0xbc, 0xbd, 0xbe, 0xbe, 0xbe, 0x67, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6d}, SKINCOLOR_RASPBERRY,  13, V_PERIDOTMAP, true}, // SKINCOLOR_APPLE
 	{"Chartreuse", {  80,   82,   72,   73,  188,  188,  113,  114,  114,  125,  126,  137,  138,  139,  253,  254}, SKINCOLOR_NOBLE,      9,  V_PERIDOTMAP, true}, // SKINCOLOR_CHARTREUSE
-	{"Green",      {0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_PEPPER,     8,  V_GREENMAP,   true}, // SKINCOLOR_GREEN
+	{"Green",      {0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_RED,        6,  V_GREENMAP,   true}, // SKINCOLOR_GREEN
 	{"Forest",     {0x65, 0x66, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f}, SKINCOLOR_SALMON,     9,  V_GREENMAP,   true}, // SKINCOLOR_FOREST
 	{"Shamrock",   {0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77}, SKINCOLOR_CRYSTAL,    10, V_GREENMAP,   true}, // SKINCOLOR_SHAMROCK
-	{"Jade",       { 128,  120,  121,  122,  122,  113,  114,  114,  115,  116,  117,  118,  119,  110,  111,   30}, SKINCOLOR_ROSY,       7,  V_GREENMAP,   true}, // SKINCOLOR_JADE
-	{"Headlight",  {   0,   80,   81,   82,   73,   84,   64,   65,   91,   91,  124,  125,  126,  137,  138,  139}, SKINCOLOR_LAVENDER,   10, V_YELLOWMAP,  true}, // SKINCOLOR_HEADLIGHT
+	{"Jade",       { 128,  120,  121,  122,  122,  113,  114,  114,  115,  116,  117,  118,  119,  110,  111,   30}, SKINCOLOR_TAFFY,      10, V_GREENMAP,   true}, // SKINCOLOR_JADE
+	{"Headlight",  {   0,   80,   81,   82,   73,   84,   64,   65,   91,   91,  124,  125,  126,  137,  138,  139}, SKINCOLOR_MAUVE,      8,  V_YELLOWMAP,  true}, // SKINCOLOR_HEADLIGHT
 	{"Mint",       {0x00, 0x00, 0x58, 0x58, 0x59, 0x62, 0x62, 0x62, 0x64, 0x67, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a}, SKINCOLOR_VIOLET,     5,  V_GREENMAP,   true}, // SKINCOLOR_MINT
-	{"Master",     {   0,   80,   88,   96,  112,  113,   99,  100,  124,  125,  126,  117,  107,  118,  119,  111}, SKINCOLOR_RED,        6,  V_GREENMAP,   true}, // SKINCOLOR_MASTER
+	{"Master",     {   0,   80,   88,   96,  112,  113,   99,  100,  124,  125,  126,  117,  107,  118,  119,  111}, SKINCOLOR_PEPPER,     8,  V_GREENMAP,   true}, // SKINCOLOR_MASTER
 	{"Emerald",    {  80,   96,  112,  113,  114,  114,  125,  125,  126,  126,  137,  137,  138,  138,  139,  139}, SKINCOLOR_RUBY,       9,  V_GREENMAP,   true}, // SKINCOLOR_EMERALD
 	{"Bottle",     {   0,    1,    3,    4,    5,  140,  141,  141,  124,  125,  126,  127,  118,  119,  111,  111}, SKINCOLOR_LATTE,      14, V_AQUAMAP,    true}, // SKINCOLOR_BOTTLE
 	{"Seafoam",    {0x01, 0x58, 0x59, 0x5a, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a, 0x8b, 0xfd, 0xfd}, SKINCOLOR_PLUM,       6,  V_AQUAMAP,    true}, // SKINCOLOR_SEAFOAM
 	{"Island",     {  96,   97,  113,  113,  114,  124,  142,  136,  136,  150,  151,  153,  168,  168,  169,  169}, SKINCOLOR_GALAXY,     7,  V_AQUAMAP,    true}, // SKINCOLOR_ISLAND
-	{"Aqua",       {0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x76, 0x77}, SKINCOLOR_TAFFY,      10, V_AQUAMAP,    true}, // SKINCOLOR_AQUA
+	{"Aqua",       {0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x76, 0x77}, SKINCOLOR_ROSY,       7,  V_AQUAMAP,    true}, // SKINCOLOR_AQUA
 	{"Teal",       {0x78, 0x78, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0x8a}, SKINCOLOR_PEACHY,     7,  V_SKYMAP,     true}, // SKINCOLOR_TEAL
 	{"Wave",       {0x00, 0x78, 0x78, 0x79, 0x8d, 0x87, 0x88, 0x89, 0x89, 0xae, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_QUAIL,      5,  V_SKYMAP,     true}, // SKINCOLOR_WAVE
 	{"Cyan",       {0x80, 0x81, 0xff, 0xff, 0x83, 0x83, 0x8d, 0x8d, 0x8d, 0x8e, 0x7e, 0x7f, 0x76, 0x76, 0x77, 0x6e}, SKINCOLOR_APRICOT,    6,  V_SKYMAP,     true}, // SKINCOLOR_CYAN
@@ -21682,12 +21682,12 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Violet",     {0xd0, 0xd1, 0xd2, 0xca, 0xcc, 0xb8, 0xb9, 0xb9, 0xba, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfe, 0xfe}, SKINCOLOR_MINT,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_VIOLET
 	{"Royal",      { 208,  209,  192,  192,  192,  193,  193,  194,  194,  172,  173,  174,  175,  175,  139,  139}, SKINCOLOR_FANCY,      9,  V_PURPLEMAP,  true}, // SKINCOLOR_ROYAL
 	{"Lilac",      {0x00, 0xd0, 0xd1, 0xd2, 0xd3, 0xc1, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xfe, 0x1f}, SKINCOLOR_VAPOR,      4,  V_ROSYMAP,    true}, // SKINCOLOR_LILAC
-	{"Mauve",      { 176,  177,  178,  192,  193,  194,  195,  195,  196,  185,  185,  186,  186,  187,  187,  253}, SKINCOLOR_GOLD,       4,  V_PURPLEMAP,  true}, // SKINCOLOR_MAUVE
+	{"Mauve",      { 176,  177,  178,  192,  193,  194,  195,  195,  196,  185,  185,  186,  186,  187,  187,  253}, SKINCOLOR_HEADLIGHT,  8,  V_PURPLEMAP,  true}, // SKINCOLOR_MAUVE
 	{"Eventide",   {  51,   52,   53,   33,   34,  204,  183,  183,  184,  184,  166,  167,  168,  169,  253,  254}, SKINCOLOR_DAYBREAK,   13, V_MAGENTAMAP, true}, // SKINCOLOR_EVENTIDE
 	{"Plum",       {0xc8, 0xd3, 0xd5, 0xd6, 0xd7, 0xce, 0xcf, 0xb9, 0xb9, 0xba, 0xba, 0xa9, 0xa9, 0xa9, 0xfd, 0xfe}, SKINCOLOR_MINT,       7,  V_ROSYMAP,    true}, // SKINCOLOR_PLUM
 	{"Raspberry",  {0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xce, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfe, 0xfe}, SKINCOLOR_APPLE,      13, V_ROSYMAP,    true}, // SKINCOLOR_RASPBERRY
-	{"Taffy",      {   1,  176,  176,  177,  178,  179,  202,  203,  204,  204,  205,  206,  207,   44,   45,   46}, SKINCOLOR_AQUA,       1,  V_ROSYMAP,    true}, // SKINCOLOR_TAFFY
-	{"Rosy",       {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_JADE,       8,  V_ROSYMAP,    true}, // SKINCOLOR_ROSY
+	{"Taffy",      {   1,  176,  176,  177,  178,  179,  202,  203,  204,  204,  205,  206,  207,   44,   45,   46}, SKINCOLOR_JADE,       8,  V_ROSYMAP,    true}, // SKINCOLOR_TAFFY
+	{"Rosy",       {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_AQUA,       1,  V_ROSYMAP,    true}, // SKINCOLOR_ROSY
 	{"Fancy",      {   0,  208,   49,  210,  210,  202,  202,  203,  204,  204,  205,  206,  207,  207,  186,  186}, SKINCOLOR_ROYAL,      9,  V_ROSYMAP,    true}, // SKINCOLOR_FANCY
 	{"Sangria",    { 210,   32,   33,   34,   34,  215,  215,  207,  207,  185,  186,  186,  186,  169,  169,  253}, SKINCOLOR_TURQUOISE,  12, V_ROSYMAP,    true}, // SKINCOLOR_SANGRIA
 	{"Volcanic",   {  35,   38,   41,   42,   44,   46,   46,  169,  169,  159,  253,  254,   30,   30,   31,   31}, SKINCOLOR_BRONZE,     9,  V_REDMAP,     true}, // SKINCOLOR_VOLCANIC
-- 
GitLab


From b52a36f04ca311087d32b064c75f9eb8d2f63133 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 20 Aug 2023 18:38:14 +0200
Subject: [PATCH 351/518] Small tweaks to Foundation and Midnight

---
 src/info.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/info.c b/src/info.c
index 54f3822e2c..023303035f 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21619,7 +21619,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Ketchup",    {0x48, 0x49, 0x40, 0x33, 0x34, 0x36, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2b, 0x2c, 0x47, 0x2e, 0x2f}, SKINCOLOR_BOULDER,    8,  V_REDMAP,     true}, // SKINCOLOR_KETCHUP
 	{"Peachy",     {0xd0, 0x30, 0x31, 0x31, 0x32, 0x32, 0xdc, 0xdc, 0xdc, 0xd3, 0xd4, 0xd4, 0xcc, 0xcd, 0xce, 0xcf}, SKINCOLOR_TEAL,       7,  V_ROSYMAP,    true}, // SKINCOLOR_PEACHY
 	{"Quail",      {0xd8, 0xd9, 0xdb, 0xdc, 0xde, 0xdf, 0xd5, 0xd5, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0x1d, 0x1f}, SKINCOLOR_WAVE,       5,  V_BROWNMAP,   true}, // SKINCOLOR_QUAIL
-	{"Foundation", {  80,   81,   82,   84,  219,  221,  221,  212,  213,  214,  215,  195,  196,  186,  187,   30}, SKINCOLOR_DREAM,      6,  V_ORANGEMAP,  true}, // SKINCOLOR_FOUNDATION
+	{"Foundation", {  80,   81,   82,   84,  219,  221,  221,  212,  213,  214,  215,  197,  186,  187,  187,   30}, SKINCOLOR_DREAM,      6,  V_ORANGEMAP,  true}, // SKINCOLOR_FOUNDATION
 	{"Sunset",     {0x51, 0x52, 0x40, 0x40, 0x34, 0x36, 0xd5, 0xd5, 0xd6, 0xd7, 0xcf, 0xcf, 0xc6, 0xc6, 0xc7, 0xfe}, SKINCOLOR_SAPPHIRE,   5,  V_ORANGEMAP,  true}, // SKINCOLOR_SUNSET
 	{"Copper",     {0x58, 0x54, 0x40, 0x34, 0x35, 0x38, 0x3a, 0x3c, 0x3d, 0x2a, 0x2b, 0x2c, 0x2c, 0xba, 0xba, 0xbb}, SKINCOLOR_BLUEBELL,   5,  V_ORANGEMAP,  true}, // SKINCOLOR_COPPER
 	{"Apricot",    {0x00, 0xd8, 0xd9, 0xda, 0xdb, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e}, SKINCOLOR_CYAN,       4,  V_ORANGEMAP,  true}, // SKINCOLOR_APRICOT
@@ -21666,7 +21666,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Cornflower", {0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x9a, 0x9c, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e}, SKINCOLOR_YELLOW,     4,  V_BLUEMAP,    true}, // SKINCOLOR_CORNFLOWER
 	{"Blue",       {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_ORANGE,     5,  V_BLUEMAP,    true}, // SKINCOLOR_BLUE
 	{"Cobalt",     { 145,  147,  149,  150,  151,  153,  154,  155,  156,  157,  158,  159,  253,  253,  254,  254}, SKINCOLOR_PERIDOT,    5,  V_BLUEMAP,    true}, // SKINCOLOR_COBALT
-	{"Midnight",   { 171,  171,  172,  173,  173,  174,  155,  156,  157,  159,  253,  253,  254,  254,   31,   31}, SKINCOLOR_CHERRY,     10, V_GRAYMAP,    true}, // SKINCOLOR_MIDNIGHT
+	{"Midnight",   { 171,  171,  172,  173,  173,  174,  156,  157,  158,  159,  253,  253,  254,  254,   31,   31}, SKINCOLOR_CHERRY,     10, V_GRAYMAP,    true}, // SKINCOLOR_MIDNIGHT
 	{"Galaxy",     { 160,  161,  162,  163,  164,  165,  166,  166,  154,  155,  156,  157,  159,  253,  254,   31}, SKINCOLOR_ISLAND,     7,  V_PURPLEMAP,  true}, // SKINCOLOR_GALAXY
 	{"Vapor",      {0x80, 0x81, 0x83, 0x86, 0x94, 0x94, 0xa3, 0xa3, 0xa4, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_LILAC,      4,  V_SKYMAP,     true}, // SKINCOLOR_VAPOR
 	{"Dusk",       {0x92, 0x93, 0x94, 0x94, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_OLIVE,      0,  V_BLUEMAP,    true}, // SKINCOLOR_DUSK
-- 
GitLab


From 1a0d9eab796bef404e7eb011fa7cd518da39391b Mon Sep 17 00:00:00 2001
From: katsy <205-katsy@users.noreply.git.do.srb2.org>
Date: Sun, 20 Aug 2023 17:29:03 +0000
Subject: [PATCH 352/518] Add supername parameter to S_SKIN: displays a custom
 super name on the GOT THEM ALL screen

---
 src/r_skins.c | 46 ++++++++++++++++++++++++++++++++++++++++------
 src/r_skins.h |  3 ++-
 src/y_inter.c |  8 ++++----
 3 files changed, 46 insertions(+), 11 deletions(-)

diff --git a/src/r_skins.c b/src/r_skins.c
index 86c0bbc544..f8b5216b29 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -113,6 +113,7 @@ static void Sk_SetDefaultValue(skin_t *skin)
 
 	strcpy(skin->realname, "Someone");
 	strcpy(skin->hudname, "???");
+	strcpy(skin->supername, "Someone super");
 
 	skin->starttranscolor = 96;
 	skin->prefcolor = SKINCOLOR_GREEN;
@@ -680,7 +681,7 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile)
 	char *value;
 	size_t size;
 	skin_t *skin;
-	boolean hudname, realname;
+	boolean hudname, realname, supername;
 
 	//
 	// search for all skin markers in pwad
@@ -710,7 +711,7 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile)
 		skin = &skins[numskins];
 		Sk_SetDefaultValue(skin);
 		skin->wadnum = wadnum;
-		hudname = realname = false;
+		hudname = realname = supername = false;
 		// parse
 		stoken = strtok (buf2, "\r\n= ");
 		while (stoken)
@@ -753,7 +754,7 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile)
 					Z_Free(value2);
 				}
 
-				// copy to hudname and fullname as a default.
+				// copy to hudname, realname, and supername as a default.
 				if (!realname)
 				{
 					STRBUFCPY(skin->realname, skin->name);
@@ -769,6 +770,19 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile)
 					strupr(skin->hudname);
 					SYMBOLCONVERT(skin->hudname)
 				}
+				else if (!supername)
+				{
+					char super[7], someone[SKINNAMESIZE+1];
+					strcpy(super, "Super ");
+					strcpy(someone, skin->realname);
+					STRBUFCPY(skin->supername, strcat(super, someone));
+				}
+			}
+			else if (!stricmp(stoken, "supername"))
+			{ // Super name (eg. "Super Knuckles")
+				supername = true;
+				STRBUFCPY(skin->supername, value);
+				SYMBOLCONVERT(skin->supername)
 			}
 			else if (!stricmp(stoken, "realname"))
 			{ // Display name (eg. "Knuckles")
@@ -777,6 +791,13 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile)
 				SYMBOLCONVERT(skin->realname)
 				if (!hudname)
 					HUDNAMEWRITE(skin->realname);
+				if (!supername) //copy over default to capitalise the name
+				{
+					char super[7], someone[SKINNAMESIZE+1];
+					strcpy(super, "Super ");
+					strcpy(someone, skin->realname);
+					STRBUFCPY(skin->supername, strcat(super, someone));
+				}
 			}
 			else if (!stricmp(stoken, "hudname"))
 			{ // Life icon name (eg. "K.T.E")
@@ -829,7 +850,7 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile)
 	char *value;
 	size_t size;
 	skin_t *skin;
-	boolean noskincomplain, realname, hudname;
+	boolean noskincomplain, realname, hudname, supername;
 
 	//
 	// search for all skin patch markers in pwad
@@ -853,7 +874,7 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile)
 		buf2[size] = '\0';
 
 		skin = NULL;
-		noskincomplain = realname = hudname = false;
+		noskincomplain = realname = hudname = supername = false;
 
 		/*
 		Parse. Has more phases than the parser in R_AddSkins because it needs to have the patching name first (no default skin name is acceptible for patching, unlike skin creation)
@@ -892,13 +913,26 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile)
 			else // Get the properties!
 			{
 				// Some of these can't go in R_ProcessPatchableFields because they have side effects for future lines.
-				if (!stricmp(stoken, "realname"))
+				if (!stricmp(stoken, "supername"))
+				{ // Super name (eg. "Super Knuckles")
+					supername = true;
+					STRBUFCPY(skin->supername, value);
+					SYMBOLCONVERT(skin->supername)
+				}
+				else if (!stricmp(stoken, "realname"))
 				{ // Display name (eg. "Knuckles")
 					realname = true;
 					STRBUFCPY(skin->realname, value);
 					SYMBOLCONVERT(skin->realname)
 					if (!hudname)
 						HUDNAMEWRITE(skin->realname);
+					if (!supername) //copy over default to capitalise the name
+					{
+						char super[7], someone[SKINNAMESIZE+1];
+						strcpy(super, "Super ");
+						strcpy(someone, skin->realname);
+						STRBUFCPY(skin->supername, strcat(super, someone));
+					}
 				}
 				else if (!stricmp(stoken, "hudname"))
 				{ // Life icon name (eg. "K.T.E")
diff --git a/src/r_skins.h b/src/r_skins.h
index a38997f4dd..5f71850bf8 100644
--- a/src/r_skins.h
+++ b/src/r_skins.h
@@ -35,8 +35,9 @@ typedef struct
 	UINT16 wadnum;
 	skinflags_t flags;
 
-	char realname[SKINNAMESIZE+1]; // Display name for level completion.
+	char realname[SKINNAMESIZE+1]; // Display name for level completion
 	char hudname[SKINNAMESIZE+1]; // HUD name to display (officially exactly 5 characters long)
+	char supername[SKINNAMESIZE+7]; // Super name to display when collecting all emeralds
 
 	UINT8 ability; // ability definition
 	UINT8 ability2; // secondary ability definition
diff --git a/src/y_inter.c b/src/y_inter.c
index 288a821e6f..4815b55202 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -1451,10 +1451,10 @@ void Y_StartIntermission(void)
 				if (players[consoleplayer].charflags & SF_SUPER)
 				{
 					strcpy(data.spec.passed3, "can now become");
-					snprintf(data.spec.passed4,
-						sizeof data.spec.passed4, "Super %s",
-						skins[players[consoleplayer].skin].realname);
-					data.spec.passed4[sizeof data.spec.passed4 - 1] = '\0';
+					if (strlen(skins[players[consoleplayer].skin].supername) > 20) //too long, use generic
+						strcpy(data.spec.passed4, "their super form");
+					else
+						strcpy(data.spec.passed4, skins[players[consoleplayer].skin].supername);
 				}
 			}
 			else
-- 
GitLab


From ce721f9f782b052ad474d2e4e1a3c8363df8869e Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 20 Aug 2023 23:40:13 +0200
Subject: [PATCH 353/518] Allow input during title animation after 1st play

---
 src/g_game.c | 2 +-
 src/m_menu.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/g_game.c b/src/g_game.c
index 6b4890a1c6..fce9919f19 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2080,7 +2080,7 @@ boolean G_Responder(event_t *ev)
 	if (gameaction == ga_nothing && !singledemo &&
 		((demoplayback && !modeattacking && !titledemo) || gamestate == GS_TITLESCREEN))
 	{
-		if (ev->type == ev_keydown && ev->key != 301 && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE))
+		if (ev->type == ev_keydown && ev->key != 301 && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE && cv_tutorialprompt.value))
 		{
 			M_StartControlPanel();
 			return true;
diff --git a/src/m_menu.c b/src/m_menu.c
index c494421415..81ff0f6795 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3196,7 +3196,7 @@ boolean M_Responder(event_t *ev)
 	|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION || gamestate == GS_GAMEEND)
 		return false;
 
-	if (gamestate == GS_TITLESCREEN && finalecount < TICRATE)
+	if (gamestate == GS_TITLESCREEN && finalecount < TICRATE && cv_tutorialprompt.value)
 		return false;
 
 	if (CON_Ready() && gamestate != GS_WAITINGPLAYERS)
-- 
GitLab


From 40d9614c32c0332c1056b479abf04f8d62f4f69c Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Mon, 21 Aug 2023 00:43:01 +0200
Subject: [PATCH 354/518] Clear thing flags for NiGHTS bumpers/hoops again

---
 src/p_setup.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/p_setup.c b/src/p_setup.c
index e7d86cf160..e289b83469 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -6783,6 +6783,7 @@ static void P_ConvertBinaryThingTypes(void)
 			break;
 		case 1704: //NiGHTS bumper
 			mapthings[i].pitch = 30 * (((mapthings[i].options & 15) + 9) % 12);
+			mapthings[i].options &= ~0xF;
 			break;
 		case 1705: //Hoop
 		case 1713: //Hoop (Customizable)
@@ -6791,6 +6792,7 @@ static void P_ConvertBinaryThingTypes(void)
 			mapthings[i].angle = (mapthings[i].extrainfo == 1) ? oldangle - 90  : ((oldangle >> 8)*360)/256;
 			mapthings[i].pitch = (mapthings[i].extrainfo == 1) ? oldangle / 360 : ((oldangle & 255)*360)/256;
 			mapthings[i].args[0] = (mapthings[i].type == 1705) ? 96 : (mapthings[i].options & 0xF)*16 + 32;
+			mapthings[i].options &= ~0xF;
 			mapthings[i].type = 1713;
 			break;
 		}
-- 
GitLab


From 5548b67462a242a54942cb4faf62a0cd687556f5 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 22 Aug 2023 16:32:21 +0200
Subject: [PATCH 355/518] Set spritexscale/spriteyscale in objectplace

---
 src/m_cheat.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/m_cheat.c b/src/m_cheat.c
index e370335f83..7ad86353ac 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -1102,6 +1102,8 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c
 
 	mt->options = (mt->z << ZSHIFT) | (UINT16)cv_opflags.value;
 	mt->scale = player->mo->scale;
+	mt->spritexscale = player->mo->spritexscale;
+	mt->spriteyscale = player->mo->spriteyscale;
 	memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
 	memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
 	mt->pitch = mt->roll = 0;
-- 
GitLab


From 55b4458001b2ffe76774ff07a11b56ebf1f70de9 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 22 Aug 2023 13:34:09 -0300
Subject: [PATCH 356/518] Fix a possible crash in R_RenderMaskedSegRange
 R_AllocTextureColumnTables wasn't checking if the tables were never
 allocated, making the renderer later attempt to render midtextures for
 drawsegs that don't contain actual midtextures.

---
 src/r_segs.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/r_segs.c b/src/r_segs.c
index 9ee3bcfec6..9af83f0c7f 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -1487,6 +1487,9 @@ static void R_AllocClippingTables(size_t range)
 	openings = Z_Realloc(openings, numopenings * sizeof (*openings), PU_STATIC, NULL);
 	lastopening = openings + pos;
 
+	if (oldopenings == NULL)
+		return;
+
 	// borrowed fix from *cough* zdoom *cough*
 	// [RH] We also need to adjust the openings pointers that
 	//    were already stored in drawsegs.
@@ -1519,6 +1522,9 @@ static void R_AllocTextureColumnTables(size_t range)
 	texturecolumntable = Z_Realloc(texturecolumntable, texturecolumntablesize * sizeof (*texturecolumntable), PU_STATIC, NULL);
 	curtexturecolumntable = texturecolumntable + pos;
 
+	if (oldtable == NULL)
+		return;
+
 	for (drawseg_t *ds = drawsegs; ds < ds_p; ds++)
 	{
 		// Check if it's in range of the tables
-- 
GitLab


From d8d352a11a12068a9d54130814edf937491eab02 Mon Sep 17 00:00:00 2001
From: katsy <katmint@live.com>
Date: Tue, 22 Aug 2023 15:11:47 -0500
Subject: [PATCH 357/518] fix some issues with supername code

---
 src/r_skins.c | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/src/r_skins.c b/src/r_skins.c
index 3e918de6e4..72598f3818 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -772,12 +772,12 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile)
 					strupr(skin->hudname);
 					SYMBOLCONVERT(skin->hudname)
 				}
-				else if (!supername)
+				if (!supername)
 				{
-					char super[7], someone[SKINNAMESIZE+1];
-					strcpy(super, "Super ");
-					strcpy(someone, skin->realname);
-					STRBUFCPY(skin->supername, strcat(super, someone));
+					char superstring[SKINNAMESIZE+7];
+					strcpy(superstring, "Super ");
+					strlcat(superstring, skin->name, sizeof(superstring));
+					STRBUFCPY(skin->supername, superstring);
 				}
 			}
 			else if (!stricmp(stoken, "supername"))
@@ -795,10 +795,10 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile)
 					HUDNAMEWRITE(skin->realname);
 				if (!supername) //copy over default to capitalise the name
 				{
-					char super[7], someone[SKINNAMESIZE+1];
-					strcpy(super, "Super ");
-					strcpy(someone, skin->realname);
-					STRBUFCPY(skin->supername, strcat(super, someone));
+					char superstring[SKINNAMESIZE+7];
+					strcpy(superstring, "Super ");
+					strlcat(superstring, skin->realname, sizeof(superstring));
+					STRBUFCPY(skin->supername, superstring);
 				}
 			}
 			else if (!stricmp(stoken, "hudname"))
@@ -930,10 +930,10 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile)
 						HUDNAMEWRITE(skin->realname);
 					if (!supername) //copy over default to capitalise the name
 					{
-						char super[7], someone[SKINNAMESIZE+1];
-						strcpy(super, "Super ");
-						strcpy(someone, skin->realname);
-						STRBUFCPY(skin->supername, strcat(super, someone));
+						char superstring[SKINNAMESIZE+7];
+						strcpy(superstring, "Super ");
+						strlcat(superstring, skin->realname, sizeof(superstring));
+						STRBUFCPY(skin->supername, superstring);
 					}
 				}
 				else if (!stricmp(stoken, "hudname"))
-- 
GitLab


From 6c416c61b1876b75a01e3dfddb6f1be9b3991de3 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 24 Aug 2023 13:33:38 +0200
Subject: [PATCH 358/518] Final 2.2.12 skincolor changes: - Replaced Mercury &
 Pumpkin with Ocean & Tangerine - Renamed Crystal to Siberite & Meteorite to
 Moonstone - Tiny tweaks to color ordering and NiGHTS link colors

---
 src/deh_tables.c | 14 +++++++-------
 src/doomdef.h    | 14 +++++++-------
 src/info.c       | 22 +++++++++++-----------
 src/st_stuff.c   |  2 +-
 4 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/src/deh_tables.c b/src/deh_tables.c
index 3580e15c6e..0801cf935a 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4614,8 +4614,7 @@ const char *COLOR_ENUMS[] = {
 	// Desaturated
 	"AETHER",     	// SKINCOLOR_AETHER,
 	"SLATE",     	// SKINCOLOR_SLATE,
-	"METEORITE",   	// SKINCOLOR_METEORITE,
-	"MERCURY",     	// SKINCOLOR_MERCURY,
+	"MOONSTONE",   	// SKINCOLOR_MOONSTONE,
 	"BLUEBELL",   	// SKINCOLOR_BLUEBELL,
 	"PINK",     	// SKINCOLOR_PINK,
 	"ROSEWOOD",   	// SKINCOLOR_ROSEWOOD,
@@ -4652,10 +4651,10 @@ const char *COLOR_ENUMS[] = {
 	"COPPER",     	// SKINCOLOR_COPPER,
 	"APRICOT",     	// SKINCOLOR_APRICOT,
 	"ORANGE",     	// SKINCOLOR_ORANGE,
-	"PUMPKIN",     	// SKINCOLOR_PUMPKIN,
 	"RUST",     	// SKINCOLOR_RUST,
-	"GOLD",     	// SKINCOLOR_GOLD,
+	"TANGERINE",   	// SKINCOLOR_TANGERINE,
 	"TOPAZ",     	// SKINCOLOR_TOPAZ,
+	"GOLD",     	// SKINCOLOR_GOLD,
 	"SANDY",     	// SKINCOLOR_SANDY,
 	"GOLDENROD",   	// SKINCOLOR_GOLDENROD,
 	"YELLOW",     	// SKINCOLOR_YELLOW,
@@ -4665,20 +4664,21 @@ const char *COLOR_ENUMS[] = {
 	"LIME",     	// SKINCOLOR_LIME,
 	"PERIDOT",     	// SKINCOLOR_PERIDOT,
 	"APPLE",     	// SKINCOLOR_APPLE,
+	"HEADLIGHT",	// SKINCOLOR_HEADLIGHT,
 	"CHARTREUSE",   // SKINCOLOR_CHARTREUSE,
 	"GREEN",     	// SKINCOLOR_GREEN,
 	"FOREST",     	// SKINCOLOR_FOREST,
 	"SHAMROCK",    	// SKINCOLOR_SHAMROCK,
 	"JADE",     	// SKINCOLOR_JADE,
-	"HEADLIGHT",	// SKINCOLOR_HEADLIGHT,
 	"MINT",     	// SKINCOLOR_MINT,
 	"MASTER",     	// SKINCOLOR_MASTER,
 	"EMERALD",     	// SKINCOLOR_EMERALD,
-	"BOTTLE",     	// SKINCOLOR_BOTTLE,
 	"SEAFOAM",     	// SKINCOLOR_SEAFOAM,
 	"ISLAND",     	// SKINCOLOR_ISLAND,
+	"BOTTLE",     	// SKINCOLOR_BOTTLE,
 	"AQUA",     	// SKINCOLOR_AQUA,
 	"TEAL",     	// SKINCOLOR_TEAL,
+	"OCEAN",     	// SKINCOLOR_OCEAN,
 	"WAVE",     	// SKINCOLOR_WAVE,
 	"CYAN",     	// SKINCOLOR_CYAN,
 	"TURQUOISE",    // SKINCOLOR_TURQUOISE,
@@ -4704,7 +4704,7 @@ const char *COLOR_ENUMS[] = {
 	"NOBLE",     	// SKINCOLOR_NOBLE,
 	"FUCHSIA",     	// SKINCOLOR_FUCHSIA,
 	"BUBBLEGUM",   	// SKINCOLOR_BUBBLEGUM,
-	"CRYSTAL",    	// SKINCOLOR_CRYSTAL,
+	"SIBERITE",   	// SKINCOLOR_SIBERITE,
 	"MAGENTA",     	// SKINCOLOR_MAGENTA,
 	"NEON",     	// SKINCOLOR_NEON,
 	"VIOLET",     	// SKINCOLOR_VIOLET,
diff --git a/src/doomdef.h b/src/doomdef.h
index f82b0a4cd4..45d6645faa 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -269,8 +269,7 @@ typedef enum
 	// Desaturated
 	SKINCOLOR_AETHER,
 	SKINCOLOR_SLATE,
-	SKINCOLOR_METEORITE,
-	SKINCOLOR_MERCURY,
+	SKINCOLOR_MOONSTONE,
 	SKINCOLOR_BLUEBELL,
 	SKINCOLOR_PINK,
 	SKINCOLOR_ROSEWOOD,
@@ -307,10 +306,10 @@ typedef enum
 	SKINCOLOR_COPPER,
 	SKINCOLOR_APRICOT,
 	SKINCOLOR_ORANGE,
-	SKINCOLOR_PUMPKIN,
 	SKINCOLOR_RUST,
-	SKINCOLOR_GOLD,
+	SKINCOLOR_TANGERINE,
 	SKINCOLOR_TOPAZ,
+	SKINCOLOR_GOLD,
 	SKINCOLOR_SANDY,
 	SKINCOLOR_GOLDENROD,
 	SKINCOLOR_YELLOW,
@@ -320,20 +319,21 @@ typedef enum
 	SKINCOLOR_LIME,
 	SKINCOLOR_PERIDOT,
 	SKINCOLOR_APPLE,
+	SKINCOLOR_HEADLIGHT,
 	SKINCOLOR_CHARTREUSE,
 	SKINCOLOR_GREEN,
 	SKINCOLOR_FOREST,
 	SKINCOLOR_SHAMROCK,
 	SKINCOLOR_JADE,
-	SKINCOLOR_HEADLIGHT,
 	SKINCOLOR_MINT,
 	SKINCOLOR_MASTER,
 	SKINCOLOR_EMERALD,
-	SKINCOLOR_BOTTLE,
 	SKINCOLOR_SEAFOAM,
 	SKINCOLOR_ISLAND,
+	SKINCOLOR_BOTTLE,
 	SKINCOLOR_AQUA,
 	SKINCOLOR_TEAL,
+	SKINCOLOR_OCEAN,
 	SKINCOLOR_WAVE,
 	SKINCOLOR_CYAN,
 	SKINCOLOR_TURQUOISE,
@@ -359,7 +359,7 @@ typedef enum
 	SKINCOLOR_NOBLE,
 	SKINCOLOR_FUCHSIA,
 	SKINCOLOR_BUBBLEGUM,
-	SKINCOLOR_CRYSTAL,
+	SKINCOLOR_SIBERITE,
 	SKINCOLOR_MAGENTA,
 	SKINCOLOR_NEON,
 	SKINCOLOR_VIOLET,
diff --git a/src/info.c b/src/info.c
index 11e9c9fe86..399b5c936c 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21586,8 +21586,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	// Desaturated
 	{"Aether",    {0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x91, 0x91, 0x91, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf}, SKINCOLOR_GREY,      15, 0,           true}, // SKINCOLOR_AETHER
 	{"Slate",     {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0xaa, 0xaa, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_SILVER,    12, 0,           true}, // SKINCOLOR_SLATE
-	{"Meteorite", {   0,    4,    8,    9,   11,   12,   14,   15,  171,  172,  173,  174,  175,   27,   29,   31}, SKINCOLOR_TOPAZ,     15, V_GRAYMAP,   true}, // SKINCOLOR_METEORITE
-	{"Mercury",   {   0,    3,    4,    7,   11,   12,   14,   15,  171,  172,  173,  155,  157,  159,  253,  254}, SKINCOLOR_ECRU,      15, V_AZUREMAP,  true}, // SKINCOLOR_MERCURY
+	{"Moonstone", {   0,    4,    8,    9,   11,   12,   14,   15,  171,  172,  173,  174,  175,   27,   29,   31}, SKINCOLOR_TOPAZ,     15, V_GRAYMAP,   true}, // SKINCOLOR_MOONSTONE
 	{"Bluebell",  {0x90, 0x91, 0x92, 0x93, 0x94, 0x94, 0x95, 0xac, 0xac, 0xad, 0xad, 0xa8, 0xa8, 0xa9, 0xfd, 0xfe}, SKINCOLOR_COPPER,    4,  V_BLUEMAP,   true}, // SKINCOLOR_BLUEBELL
 	{"Pink",      {0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0x2b, 0x2c, 0x2e}, SKINCOLOR_AZURE,     9,  V_REDMAP,    true}, // SKINCOLOR_PINK
 	{"Rosewood",  { 209,  210,  211,  212,  213,  214,  228,  230,  232,  234,  235,  237,   26,   27,   28,   29}, SKINCOLOR_SEPIA,     5,  V_BROWNMAP,  true}, // SKINCOLOR_ROSEWOOD
@@ -21597,7 +21596,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Boulder",   {0xde, 0xe0, 0xe1, 0xe4, 0xe7, 0xe9, 0xeb, 0xec, 0xed, 0xed, 0xed, 0x19, 0x19, 0x1b, 0x1d, 0x1e}, SKINCOLOR_KETCHUP,   0,  V_BROWNMAP,  true}, // SKINCOLOR_BOULDER
 	{"Bronze",    {  82,   84,   50,   51,  223,  228,  230,  232,  234,  236,  237,  238,  239,  239,   30,   31}, SKINCOLOR_VOLCANIC,  9,  V_BROWNMAP,  true}, // SKINCOLOR_BRONZE
 	{"Sepia",     {  88,   84,   85,   86,  224,  226,  228,  230,  232,  235,  236,  237,  238,  239,   28,   28}, SKINCOLOR_ROSEWOOD,  5,  V_BROWNMAP,  true}, // SKINCOLOR_SEPIA
-	{"Ecru",      {  80,   83,   84,   85,   86,  242,  243,  245,  230,  232,  234,  236,  238,  239,   47,   47}, SKINCOLOR_MERCURY,   15, V_BROWNMAP,  true}, // SKINCOLOR_ECRU
+	{"Ecru",      {  80,   83,   84,   85,   86,  242,  243,  245,  230,  232,  234,  236,  238,  239,   47,   47}, SKINCOLOR_ARCTIC,    12, V_BROWNMAP,  true}, // SKINCOLOR_ECRU
 	{"Tan",       {0x51, 0x51, 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0xf5, 0xf5, 0xf9, 0xf9, 0xed, 0xed}, SKINCOLOR_BROWN,     12, V_BROWNMAP,  true}, // SKINCOLOR_TAN
 	{"Beige",     {0x54, 0x55, 0x56, 0x56, 0xf2, 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xed, 0xed}, SKINCOLOR_MOSS,      5,  V_BROWNMAP,  true}, // SKINCOLOR_BEIGE
 	{"Rosebush",  { 208,  216,  209,   85,   90,   91,   91,   92,  191,   93,   94,  107,  109,  110,  111,  111}, SKINCOLOR_EGGPLANT,  5,  V_GREENMAP,  true}, // SKINCOLOR_ROSEBUSH
@@ -21624,10 +21623,10 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Copper",     {0x58, 0x54, 0x40, 0x34, 0x35, 0x38, 0x3a, 0x3c, 0x3d, 0x2a, 0x2b, 0x2c, 0x2c, 0xba, 0xba, 0xbb}, SKINCOLOR_BLUEBELL,   5,  V_ORANGEMAP,  true}, // SKINCOLOR_COPPER
 	{"Apricot",    {0x00, 0xd8, 0xd9, 0xda, 0xdb, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e}, SKINCOLOR_CYAN,       4,  V_ORANGEMAP,  true}, // SKINCOLOR_APRICOT
 	{"Orange",     {  49,   50,   51,   52,   53,   54,   55,   57,   58,   59,   60,   42,   44,   45,   46,   46}, SKINCOLOR_BLUE,       4,  V_ORANGEMAP,  true}, // SKINCOLOR_ORANGE
-	{"Pumpkin",    {  51,   52,   53,   54,   56,   58,   59,   59,   61,   61,   63,   45,   46,   47,   47,   31}, SKINCOLOR_ARCTIC,     12, V_ORANGEMAP,  true}, // SKINCOLOR_PUMPKIN
 	{"Rust",       {0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3f, 0x2c, 0x2d, 0x47, 0x2e, 0x2f, 0x2f}, SKINCOLOR_YOGURT,     8,  V_ORANGEMAP,  true}, // SKINCOLOR_RUST
+	{"Tangerine",  {  81,   83,   64,   64,   51,   52,   53,   54,   56,   58,   60,   61,   63,   45,   46,   47}, SKINCOLOR_OCEAN,      12, V_ORANGEMAP,  true}, // SKINCOLOR_TANGERINE
+	{"Topaz",      {   0,   81,   83,   73,   74,   74,   65,   52,   53,   54,   56,   58,   60,   42,   43,   45}, SKINCOLOR_MOONSTONE,  10, V_YELLOWMAP,  true}, // SKINCOLOR_TOPAZ
 	{"Gold",       {0x51, 0x51, 0x54, 0x54, 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x3f, 0x2d, 0x2e, 0x2f, 0x2f}, SKINCOLOR_LAVENDER,   10, V_YELLOWMAP,  true}, // SKINCOLOR_GOLD
-	{"Topaz",      {   0,   81,   83,   73,   74,   74,   65,   52,   53,   54,   56,   58,   60,   42,   43,   45}, SKINCOLOR_METEORITE,  10, V_YELLOWMAP,  true}, // SKINCOLOR_TOPAZ
 	{"Sandy",      {0x53, 0x40, 0x41, 0x42, 0x43, 0xe6, 0xe9, 0xe9, 0xea, 0xec, 0xec, 0xc6, 0xc6, 0xc7, 0xc7, 0xfe}, SKINCOLOR_SKY,        8,  V_YELLOWMAP,  true}, // SKINCOLOR_SANDY
 	{"Goldenrod",  {   0,   80,   81,   81,   83,   73,   73,   64,   65,   66,   67,   68,   69,   62,   44,   45}, SKINCOLOR_MAJESTY,    8,  V_YELLOWMAP,  true}, // SKINCOLOR_GOLDENROD
 	{"Yellow",     {0x52, 0x53, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4e, 0x4f, 0xed}, SKINCOLOR_CORNFLOWER, 8,  V_YELLOWMAP,  true}, // SKINCOLOR_YELLOW
@@ -21637,20 +21636,21 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Lime",       {0x50, 0x51, 0x52, 0x53, 0x48, 0xbc, 0xbd, 0xbe, 0xbe, 0xbf, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_MAGENTA,    9,  V_PERIDOTMAP, true}, // SKINCOLOR_LIME
 	{"Peridot",    {0x58, 0x58, 0xbc, 0xbc, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0x5e, 0x5e, 0x5f, 0x5f, 0x77, 0x77}, SKINCOLOR_COBALT,     2,  V_PERIDOTMAP, true}, // SKINCOLOR_PERIDOT
 	{"Apple",      {0x49, 0x49, 0xbc, 0xbd, 0xbe, 0xbe, 0xbe, 0x67, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6d}, SKINCOLOR_RASPBERRY,  13, V_PERIDOTMAP, true}, // SKINCOLOR_APPLE
+	{"Headlight",  {   0,   80,   81,   82,   73,   84,   64,   65,   91,   91,  124,  125,  126,  137,  138,  139}, SKINCOLOR_MAUVE,      8,  V_YELLOWMAP,  true}, // SKINCOLOR_HEADLIGHT
 	{"Chartreuse", {  80,   82,   72,   73,  188,  188,  113,  114,  114,  125,  126,  137,  138,  139,  253,  254}, SKINCOLOR_NOBLE,      9,  V_PERIDOTMAP, true}, // SKINCOLOR_CHARTREUSE
 	{"Green",      {0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_RED,        6,  V_GREENMAP,   true}, // SKINCOLOR_GREEN
 	{"Forest",     {0x65, 0x66, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f}, SKINCOLOR_SALMON,     9,  V_GREENMAP,   true}, // SKINCOLOR_FOREST
-	{"Shamrock",   {0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77}, SKINCOLOR_CRYSTAL,    10, V_GREENMAP,   true}, // SKINCOLOR_SHAMROCK
+	{"Shamrock",   {0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77}, SKINCOLOR_SIBERITE,   10, V_GREENMAP,   true}, // SKINCOLOR_SHAMROCK
 	{"Jade",       { 128,  120,  121,  122,  122,  113,  114,  114,  115,  116,  117,  118,  119,  110,  111,   30}, SKINCOLOR_TAFFY,      10, V_GREENMAP,   true}, // SKINCOLOR_JADE
-	{"Headlight",  {   0,   80,   81,   82,   73,   84,   64,   65,   91,   91,  124,  125,  126,  137,  138,  139}, SKINCOLOR_MAUVE,      8,  V_YELLOWMAP,  true}, // SKINCOLOR_HEADLIGHT
 	{"Mint",       {0x00, 0x00, 0x58, 0x58, 0x59, 0x62, 0x62, 0x62, 0x64, 0x67, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a}, SKINCOLOR_VIOLET,     5,  V_GREENMAP,   true}, // SKINCOLOR_MINT
 	{"Master",     {   0,   80,   88,   96,  112,  113,   99,  100,  124,  125,  126,  117,  107,  118,  119,  111}, SKINCOLOR_PEPPER,     8,  V_GREENMAP,   true}, // SKINCOLOR_MASTER
 	{"Emerald",    {  80,   96,  112,  113,  114,  114,  125,  125,  126,  126,  137,  137,  138,  138,  139,  139}, SKINCOLOR_RUBY,       9,  V_GREENMAP,   true}, // SKINCOLOR_EMERALD
-	{"Bottle",     {   0,    1,    3,    4,    5,  140,  141,  141,  124,  125,  126,  127,  118,  119,  111,  111}, SKINCOLOR_LATTE,      14, V_AQUAMAP,    true}, // SKINCOLOR_BOTTLE
 	{"Seafoam",    {0x01, 0x58, 0x59, 0x5a, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a, 0x8b, 0xfd, 0xfd}, SKINCOLOR_PLUM,       6,  V_AQUAMAP,    true}, // SKINCOLOR_SEAFOAM
 	{"Island",     {  96,   97,  113,  113,  114,  124,  142,  136,  136,  150,  151,  153,  168,  168,  169,  169}, SKINCOLOR_GALAXY,     7,  V_AQUAMAP,    true}, // SKINCOLOR_ISLAND
+	{"Bottle",     {   0,    1,    3,    4,    5,  140,  141,  141,  124,  125,  126,  127,  118,  119,  111,  111}, SKINCOLOR_LATTE,      14, V_AQUAMAP,    true}, // SKINCOLOR_BOTTLE
 	{"Aqua",       {0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x76, 0x77}, SKINCOLOR_ROSY,       7,  V_AQUAMAP,    true}, // SKINCOLOR_AQUA
 	{"Teal",       {0x78, 0x78, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0x8a}, SKINCOLOR_PEACHY,     7,  V_SKYMAP,     true}, // SKINCOLOR_TEAL
+	{"Ocean",      { 120,  121,  122,  122,  123,  141,  142,  142,  136,  137,  138,  138,  139,  139,  253,  253}, SKINCOLOR_TANGERINE,  4,  V_AQUAMAP,    true}, // SKINCOLOR_OCEAN
 	{"Wave",       {0x00, 0x78, 0x78, 0x79, 0x8d, 0x87, 0x88, 0x89, 0x89, 0xae, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_QUAIL,      5,  V_SKYMAP,     true}, // SKINCOLOR_WAVE
 	{"Cyan",       {0x80, 0x81, 0xff, 0xff, 0x83, 0x83, 0x8d, 0x8d, 0x8d, 0x8e, 0x7e, 0x7f, 0x76, 0x76, 0x77, 0x6e}, SKINCOLOR_APRICOT,    6,  V_SKYMAP,     true}, // SKINCOLOR_CYAN
 	{"Turquoise",  {  0,   120,  121,  122,  123,  141,  141,  135,  136,  136,  150,  153,  155,  157,  159,  253}, SKINCOLOR_SANGRIA,    12, V_SKYMAP,     true}, // SKINCOLOR_TURQUOISE
@@ -21661,8 +21661,8 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Dream",      {  80,  208,  200,  200,  146,  146,  133,  134,  135,  136,  137,  138,  139,  139,  254,  254}, SKINCOLOR_FOUNDATION, 9,  V_SKYMAP,     true}, // SKINCOLOR_DREAM
 	{"Icy",        {0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0x83, 0x83, 0x86, 0x87, 0x95, 0x95, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_CRIMSON,    0,  V_SKYMAP,     true}, // SKINCOLOR_ICY
 	{"Daybreak",   {  80,   81,   82,   72,   64,    9,   11,  171,  149,  150,  151,  153,  156,  157,  159,  253}, SKINCOLOR_EVENTIDE,   12, V_BLUEMAP,    true}, // SKINCOLOR_DAYBREAK
-	{"Sapphire",   {0x80, 0x82, 0x86, 0x87, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_SUNSET,     5,  V_SKYMAP,     true}, // SKINCOLOR_SAPPHIRE
-	{"Arctic",     {   0,    1,    3,    4,  146,  146,  147,  148,  148,  149,  150,  153,  156,  159,  253,  254}, SKINCOLOR_PUMPKIN,    6,  V_BLUEMAP,    true}, // SKINCOLOR_ARCTIC
+	{"Sapphire",   {0x80, 0x82, 0x86, 0x87, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_SUNSET,     5,  V_BLUEMAP,    true}, // SKINCOLOR_SAPPHIRE
+	{"Arctic",     {   0,    1,    3,    4,  145,  146,  147,  148,  148,  149,  150,  153,  156,  159,  253,  254}, SKINCOLOR_ECRU,       15, V_BLUEMAP,    true}, // SKINCOLOR_ARCTIC
 	{"Cornflower", {0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x9a, 0x9c, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e}, SKINCOLOR_YELLOW,     4,  V_BLUEMAP,    true}, // SKINCOLOR_CORNFLOWER
 	{"Blue",       {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_ORANGE,     5,  V_BLUEMAP,    true}, // SKINCOLOR_BLUE
 	{"Cobalt",     { 145,  147,  149,  150,  151,  153,  154,  155,  156,  157,  158,  159,  253,  253,  254,  254}, SKINCOLOR_PERIDOT,    5,  V_BLUEMAP,    true}, // SKINCOLOR_COBALT
@@ -21676,7 +21676,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Noble",      { 144,  146,  147,  148,  149,  164,  164,  165,  166,  185,  186,  186,  187,  187,   28,   29}, SKINCOLOR_CHARTREUSE, 12, V_PURPLEMAP,  true}, // SKINCOLOR_NOBLE
 	{"Fuchsia",    { 200,  201,  203,  204,  204,  183,  184,  184,  165,  166,  167,  168,  169,  159,  253,  254}, SKINCOLOR_LEMON,      10, V_PURPLEMAP,  true}, // SKINCOLOR_FUCHSIA
 	{"Bubblegum",  {   0,  208,  208,  176,  177,  178,  179,  180,  181,  182,  164,  166,  167,  168,  169,  253}, SKINCOLOR_PASTEL,     8,  V_MAGENTAMAP, true}, // SKINCOLOR_BUBBLEGUM
-	{"Crystal",    { 252,  177,  179,  180,  181,  181,  182,  182,  183,  164,  166,  167,  167,  168,  169,  159}, SKINCOLOR_EMERALD,    8,  V_MAGENTAMAP, true}, // SKINCOLOR_CRYSTAL
+	{"Siberite",   { 252,  177,  179,  180,  181,  181,  182,  182,  183,  164,  166,  167,  167,  168,  169,  159}, SKINCOLOR_EMERALD,    8,  V_MAGENTAMAP, true}, // SKINCOLOR_SIBERITE
 	{"Magenta",    {0xb3, 0xb3, 0xb4, 0xb5, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb}, SKINCOLOR_LIME,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_MAGENTA
 	{"Neon",       {0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb, 0xc7, 0xc7, 0x1d, 0x1d, 0x1e}, SKINCOLOR_CERULEAN,   2,  V_MAGENTAMAP, true}, // SKINCOLOR_NEON
 	{"Violet",     {0xd0, 0xd1, 0xd2, 0xca, 0xcc, 0xb8, 0xb9, 0xb9, 0xba, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfe, 0xfe}, SKINCOLOR_MINT,       6,  V_MAGENTAMAP, true}, // SKINCOLOR_VIOLET
diff --git a/src/st_stuff.c b/src/st_stuff.c
index a6bd77cfae..b9f0c6bb93 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -174,7 +174,7 @@ static huddrawlist_h luahuddrawlist_titlecard;
 skincolornum_t linkColor[3][NUMLINKCOLORS] = {
 {SKINCOLOR_SHAMROCK, SKINCOLOR_AQUA, SKINCOLOR_SKY, SKINCOLOR_BLUE, SKINCOLOR_PURPLE, SKINCOLOR_MAGENTA,
  SKINCOLOR_ROSY, SKINCOLOR_RED, SKINCOLOR_ORANGE, SKINCOLOR_GOLD, SKINCOLOR_YELLOW, SKINCOLOR_PERIDOT},
-{SKINCOLOR_EMERALD, SKINCOLOR_AQUAMARINE, SKINCOLOR_WAVE, SKINCOLOR_SAPPHIRE, SKINCOLOR_GALAXY, SKINCOLOR_CRYSTAL,
+{SKINCOLOR_EMERALD, SKINCOLOR_OCEAN, SKINCOLOR_AQUAMARINE, SKINCOLOR_SAPPHIRE, SKINCOLOR_GALAXY, SKINCOLOR_SIBERITE,
  SKINCOLOR_TAFFY, SKINCOLOR_RUBY, SKINCOLOR_GARNET, SKINCOLOR_TOPAZ, SKINCOLOR_LEMON, SKINCOLOR_LIME},
 {SKINCOLOR_ISLAND, SKINCOLOR_TURQUOISE, SKINCOLOR_DREAM, SKINCOLOR_DAYBREAK, SKINCOLOR_VAPOR, SKINCOLOR_FUCHSIA,
  SKINCOLOR_VIOLET, SKINCOLOR_EVENTIDE, SKINCOLOR_KETCHUP, SKINCOLOR_FOUNDATION, SKINCOLOR_HEADLIGHT, SKINCOLOR_CHARTREUSE}};
-- 
GitLab


From f42c2403f9a1bd3db9dea5923f95884b49efe442 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Fri, 25 Aug 2023 10:42:12 +0200
Subject: [PATCH 359/518] Interpolate radius/height when scaling mobjs

---
 src/hardware/hw_main.c | 28 +++++++++---------------
 src/hardware/hw_md2.c  | 11 +++-------
 src/r_fps.c            | 20 ++++++++++++++++-
 src/r_fps.h            |  2 ++
 src/r_things.c         | 49 +++++++++++++++++++++---------------------
 5 files changed, 58 insertions(+), 52 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index bc66955fca..4208a44860 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -3595,7 +3595,7 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale)
 			return;
 	}
 
-	floordiff = abs((flip < 0 ? thing->height : 0) + interp.z - groundz);
+	floordiff = abs((flip < 0 ? interp.height : 0) + interp.z - groundz);
 
 	alpha = floordiff / (4*FRACUNIT) + 75;
 	if (alpha >= 255) return;
@@ -3606,9 +3606,7 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale)
 	HWR_GetPatch(gpatch);
 
 	scalemul = FixedMul(FRACUNIT - floordiff/640, scale);
-	scalemul = FixedMul(scalemul, (thing->radius*2) / gpatch->height);
-	if ((thing->scale != thing->old_scale) && (thing->scale >= FRACUNIT/1024)) // Interpolate shadows when scaling mobjs
-		scalemul = FixedMul(scalemul, FixedDiv(interp.scale, thing->scale));
+	scalemul = FixedMul(scalemul, (interp.radius*2) / gpatch->height);
 
 	fscale = FIXED_TO_FLOAT(scalemul);
 	fx = FIXED_TO_FLOAT(interp.x);
@@ -3720,7 +3718,7 @@ static void HWR_RotateSpritePolyToAim(gl_vissprite_t *spr, FOutVector *wallVerts
 
 		if (P_MobjFlip(spr->mobj) == -1)
 		{
-			basey = FIXED_TO_FLOAT(interp.z + spr->mobj->height);
+			basey = FIXED_TO_FLOAT(interp.z + interp.height);
 		}
 		else
 		{
@@ -5326,7 +5324,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 			}
 
 			groundz = R_GetShadowZ(thing, NULL);
-			floordiff = abs(((thing->eflags & MFE_VERTICALFLIP) ? caster->height : 0) + casterinterp.z - groundz);
+			floordiff = abs(((thing->eflags & MFE_VERTICALFLIP) ? casterinterp.height : 0) + casterinterp.z - groundz);
 
 			shadowheight = FIXED_TO_FLOAT(floordiff);
 			shadowscale = FIXED_TO_FLOAT(FixedMul(FRACUNIT - floordiff/640, casterinterp.scale));
@@ -5378,10 +5376,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 
 		if (vflip)
 		{
-			if (thing->scale != thing->old_scale) // Interpolate heights in reverse gravity when scaling mobjs
-				gz = FIXED_TO_FLOAT(interp.z + FixedMul(thing->height, FixedDiv(interp.scale, thing->scale))) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale);
-			else
-				gz = FIXED_TO_FLOAT(interp.z + thing->height) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale);
+			gz = FIXED_TO_FLOAT(interp.z + interp.height) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale);
 			gzt = gz + (FIXED_TO_FLOAT(spr_height) * this_yscale);
 		}
 		else
@@ -5687,7 +5682,6 @@ static void HWR_ProjectBoundingBox(mobj_t *thing)
 	gl_vissprite_t *vis;
 	float tr_x, tr_y;
 	float tz;
-	float rad;
 
 	if (!thing)
 		return;
@@ -5722,15 +5716,13 @@ static void HWR_ProjectBoundingBox(mobj_t *thing)
 	tr_x += gl_viewx;
 	tr_y += gl_viewy;
 
-	rad = FIXED_TO_FLOAT(thing->radius);
-
 	vis = HWR_NewVisSprite();
-	vis->x1 = tr_x - rad;
-	vis->x2 = tr_x + rad;
-	vis->z1 = tr_y - rad;
-	vis->z2 = tr_y + rad;
+	vis->x1 = tr_x - FIXED_TO_FLOAT(interp.radius);
+	vis->x2 = tr_x + FIXED_TO_FLOAT(interp.radius);
+	vis->z1 = tr_y - FIXED_TO_FLOAT(interp.radius);
+	vis->z2 = tr_y + FIXED_TO_FLOAT(interp.radius);
 	vis->gz = FIXED_TO_FLOAT(interp.z);
-	vis->gzt = vis->gz + FIXED_TO_FLOAT(thing->height);
+	vis->gzt = vis->gz + FIXED_TO_FLOAT(interp.height);
 	vis->mobj = thing;
 
 	vis->precip = false;
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 87881be8d6..6123eb9a93 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1585,12 +1585,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		p.y = FIXED_TO_FLOAT(interp.y)+md2->offset;
 
 		if (flip)
-		{
-			if (spr->mobj->scale != spr->mobj->old_scale) // Interpolate heights in reverse gravity when scaling mobjs
-				p.z = FIXED_TO_FLOAT(interp.z + FixedMul(spr->mobj->height, FixedDiv(interp.scale, spr->mobj->scale)));
-			else
-				p.z = FIXED_TO_FLOAT(interp.z + spr->mobj->height);
-		}
+			p.z = FIXED_TO_FLOAT(interp.z + interp.height);
 		else
 			p.z = FIXED_TO_FLOAT(interp.z);
 
@@ -1626,8 +1621,8 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 				p.roll = true;
 
 				// rotation pivot
-				p.centerx = FIXED_TO_FLOAT(spr->mobj->radius / 2);
-				p.centery = FIXED_TO_FLOAT(spr->mobj->height / 2);
+				p.centerx = FIXED_TO_FLOAT(interp.radius / 2);
+				p.centery = FIXED_TO_FLOAT(interp.height / 2);
 
 				// rotation axes relative to camera
 				p.rollx = FIXED_TO_FLOAT(FINECOSINE(FixedAngle(camAngleDiff) >> ANGLETOFINESHIFT));
diff --git a/src/r_fps.c b/src/r_fps.c
index c6eb594821..de450aaa7f 100644
--- a/src/r_fps.c
+++ b/src/r_fps.c
@@ -292,6 +292,8 @@ void R_InterpolateMobjState(mobj_t *mobj, fixed_t frac, interpmobjstate_t *out)
 		out->y = mobj->y;
 		out->z = mobj->z;
 		out->scale = mobj->scale;
+		out->radius = mobj->radius;
+		out->height = mobj->height;
 		out->subsector = mobj->subsector;
 		out->angle = mobj->player ? mobj->player->drawangle : mobj->angle;
 		out->pitch = mobj->pitch;
@@ -307,10 +309,22 @@ void R_InterpolateMobjState(mobj_t *mobj, fixed_t frac, interpmobjstate_t *out)
 	out->x = R_LerpFixed(mobj->old_x, mobj->x, frac);
 	out->y = R_LerpFixed(mobj->old_y, mobj->y, frac);
 	out->z = R_LerpFixed(mobj->old_z, mobj->z, frac);
-	out->scale = mobj->resetinterp ? mobj->scale : R_LerpFixed(mobj->old_scale, mobj->scale, frac);
 	out->spritexscale = mobj->resetinterp ? mobj->spritexscale : R_LerpFixed(mobj->old_spritexscale, mobj->spritexscale, frac);
 	out->spriteyscale = mobj->resetinterp ? mobj->spriteyscale : R_LerpFixed(mobj->old_spriteyscale, mobj->spriteyscale, frac);
 
+	if (mobj->scale == mobj->old_scale) // Tiny optimisation - scale is usually unchanging, so let's skip a lerp, two FixedMuls, and two FixedDivs
+	{
+		out->scale = mobj->scale;
+		out->radius = mobj->radius;
+		out->height = mobj->height;
+	}
+	else
+	{
+		out->scale = R_LerpFixed(mobj->old_scale, mobj->scale, frac);
+		out->radius = FixedMul(mobj->radius, FixedDiv(out->scale, mobj->scale));
+		out->height = FixedMul(mobj->height, FixedDiv(out->scale, mobj->scale));
+	}
+
 	// Sprite offsets are not interpolated until we have a way to interpolate them explicitly in Lua.
 	// It seems existing mods visually break more often than not if it is interpolated.
 	out->spritexoffset = mobj->spritexoffset;
@@ -340,6 +354,8 @@ void R_InterpolatePrecipMobjState(precipmobj_t *mobj, fixed_t frac, interpmobjst
 		out->y = mobj->y;
 		out->z = mobj->z;
 		out->scale = FRACUNIT;
+		out->radius = mobj->radius;
+		out->height = mobj->height;
 		out->subsector = mobj->subsector;
 		out->angle = mobj->angle;
 		out->pitch = mobj->angle;
@@ -356,6 +372,8 @@ void R_InterpolatePrecipMobjState(precipmobj_t *mobj, fixed_t frac, interpmobjst
 	out->y = R_LerpFixed(mobj->old_y, mobj->y, frac);
 	out->z = R_LerpFixed(mobj->old_z, mobj->z, frac);
 	out->scale = FRACUNIT;
+	out->radius = mobj->radius;
+	out->height = mobj->height;
 	out->spritexscale = R_LerpFixed(mobj->old_spritexscale, mobj->spritexscale, frac);
 	out->spriteyscale = R_LerpFixed(mobj->old_spriteyscale, mobj->spriteyscale, frac);
 	out->spritexoffset = R_LerpFixed(mobj->old_spritexoffset, mobj->spritexoffset, frac);
diff --git a/src/r_fps.h b/src/r_fps.h
index 9a8bfa38ae..f43d29f300 100644
--- a/src/r_fps.h
+++ b/src/r_fps.h
@@ -63,6 +63,8 @@ typedef struct {
 	angle_t roll;
 	angle_t spriteroll;
 	fixed_t scale;
+	fixed_t radius;
+	fixed_t height;
 	fixed_t spritexscale;
 	fixed_t spriteyscale;
 	fixed_t spritexoffset;
diff --git a/src/r_things.c b/src/r_things.c
index e69fe1b822..cd92c3b4b8 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1184,7 +1184,7 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope)
 		R_InterpolateMobjState(thing, FRACUNIT, &interp);
 	}
 
-	halfHeight = interp.z + (thing->height >> 1);
+	halfHeight = interp.z + (interp.height >> 1);
 	floorz = P_GetFloorZ(thing, interp.subsector->sector, interp.x, interp.y, NULL);
 	ceilingz = P_GetCeilingZ(thing, interp.subsector->sector, interp.x, interp.y, NULL);
 
@@ -1248,8 +1248,8 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope)
 		}
 	}
 
-	if (isflipped ? (ceilingz < groundz - (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2)))
-		: (floorz > groundz + (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2))))
+	if (isflipped ? (ceilingz < groundz - (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), interp.radius*3/2)))
+		: (floorz > groundz + (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), interp.radius*3/2))))
 	{
 		groundz = isflipped ? ceilingz : floorz;
 		groundslope = NULL;
@@ -1292,9 +1292,9 @@ static void R_SkewShadowSprite(
 	//CONS_Printf("Shadow is sloped by %d %d\n", xslope, zslope);
 
 	if (viewz < groundz)
-		*shadowyscale += FixedMul(FixedMul(thing->radius*2 / spriteheight, scalemul), zslope);
+		*shadowyscale += FixedMul(FixedMul(interp.radius*2 / spriteheight, scalemul), zslope);
 	else
-		*shadowyscale -= FixedMul(FixedMul(thing->radius*2 / spriteheight, scalemul), zslope);
+		*shadowyscale -= FixedMul(FixedMul(interp.radius*2 / spriteheight, scalemul), zslope);
 
 	*shadowyscale = abs((*shadowyscale));
 	*shadowskew = xslope;
@@ -1345,20 +1345,18 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 			return;
 	}
 
-	floordiff = abs((isflipped ? thing->height : 0) + interp.z - groundz);
+	floordiff = abs((isflipped ? interp.height : 0) + interp.z - groundz);
 
 	trans = floordiff / (100*FRACUNIT) + 3;
 	if (trans >= 9) return;
 
 	scalemul = FixedMul(FRACUNIT - floordiff/640, scale);
-	if ((thing->scale != thing->old_scale) && (thing->scale >= FRACUNIT/1024)) // Interpolate shadows when scaling mobjs
-		scalemul = FixedMul(scalemul, FixedDiv(interp.scale, thing->scale));
 
 	patch = W_CachePatchName("DSHADOW", PU_SPRITE);
 	xscale = FixedDiv(projection, tz);
 	yscale = FixedDiv(projectiony, tz);
-	shadowxscale = FixedMul(thing->radius*2, scalemul);
-	shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(groundz - viewz), tz));
+	shadowxscale = FixedMul(interp.radius*2, scalemul);
+	shadowyscale = FixedMul(FixedMul(interp.radius*2, scalemul), FixedDiv(abs(groundz - viewz), tz));
 	shadowyscale = min(shadowyscale, shadowxscale) / patch->height;
 	shadowxscale /= patch->width;
 	shadowskew = 0;
@@ -1484,8 +1482,8 @@ static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis)
 	// 0--2
 
 	// start in the (0) corner
-	gx = interp.x - thing->radius - viewx;
-	gy = interp.y - thing->radius - viewy;
+	gx = interp.x - interp.radius - viewx;
+	gy = interp.y - interp.radius - viewy;
 
 	tz = FixedMul(gx, viewcos) + FixedMul(gy, viewsin);
 
@@ -1507,14 +1505,14 @@ static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis)
 	box = R_NewVisSprite();
 	box->mobj = thing;
 	box->mobjflags = thing->flags;
-	box->thingheight = thing->height;
+	box->thingheight = interp.height;
 	box->cut = SC_BBOX;
 
 	box->gx = tx;
 	box->gy = tz;
 
-	box->scale = 2 * FixedMul(thing->radius, viewsin);
-	box->xscale = 2 * FixedMul(thing->radius, viewcos);
+	box->scale  = 2 * FixedMul(interp.radius, viewsin);
+	box->xscale = 2 * FixedMul(interp.radius, viewcos);
 
 	box->pz = interp.z;
 	box->pzt = box->pz + box->thingheight;
@@ -1563,6 +1561,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	fixed_t tr_x, tr_y;
 	fixed_t tx, tz;
 	fixed_t xscale, yscale; //added : 02-02-98 : aaargll..if I were a math-guy!!!
+	fixed_t radius, height; // For drop shadows
 	fixed_t sortscale, sortsplat = 0;
 	fixed_t linkscale = 0;
 	fixed_t sort_x = 0, sort_y = 0, sort_z;
@@ -1638,6 +1637,8 @@ static void R_ProjectSprite(mobj_t *thing)
 	}
 
 	this_scale = interp.scale;
+	radius = interp.radius; // For drop shadows
+	height = interp.height; // Ditto
 
 	// transform the origin point
 	tr_x = interp.x - viewx;
@@ -1977,6 +1978,8 @@ static void R_ProjectSprite(mobj_t *thing)
 		{
 			R_InterpolateMobjState(thing, FRACUNIT, &tracer_interp);
 		}
+		radius = tracer_interp.radius; // For drop shadows
+		height = tracer_interp.height; // Ditto
 
 		tr_x = (tracer_interp.x + sort_x) - viewx;
 		tr_y = (tracer_interp.y + sort_y) - viewy;
@@ -2068,7 +2071,7 @@ static void R_ProjectSprite(mobj_t *thing)
 				if (abs(groundz-viewz)/tz > 4)
 					return; // Prevent stretchy shadows and possible crashes
 
-				floordiff = abs((isflipped ? caster->height : 0) + casterinterp.z - groundz);
+				floordiff = abs((isflipped ? casterinterp.height : 0) + casterinterp.z - groundz);
 				trans += ((floordiff / (100*FRACUNIT)) + 3);
 				shadowscale = FixedMul(FRACUNIT - floordiff/640, casterinterp.scale);
 			}
@@ -2083,8 +2086,8 @@ static void R_ProjectSprite(mobj_t *thing)
 
 		if (shadowdraw)
 		{
-			spritexscale = FixedMul(thing->radius * 2, FixedMul(shadowscale, spritexscale));
-			spriteyscale = FixedMul(thing->radius * 2, FixedMul(shadowscale, spriteyscale));
+			spritexscale = FixedMul(radius * 2, FixedMul(shadowscale, spritexscale));
+			spriteyscale = FixedMul(radius * 2, FixedMul(shadowscale, spriteyscale));
 			spriteyscale = FixedMul(spriteyscale, FixedDiv(abs(groundz - viewz), tz));
 			spriteyscale = min(spriteyscale, spritexscale) / patch->height;
 			spritexscale /= patch->width;
@@ -2099,7 +2102,7 @@ static void R_ProjectSprite(mobj_t *thing)
 		{
 			R_SkewShadowSprite(thing, thing->standingslope, groundz, patch->height, shadowscale, &spriteyscale, &sheartan);
 
-			gzt = (isflipped ? (interp.z + thing->height) : interp.z) + patch->height * spriteyscale / 2;
+			gzt = (isflipped ? (interp.z + height) : interp.z) + patch->height * spriteyscale / 2;
 			gz = gzt - patch->height * spriteyscale;
 
 			cut |= SC_SHEAR;
@@ -2114,11 +2117,7 @@ static void R_ProjectSprite(mobj_t *thing)
 			// When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned.
 			// sprite height - sprite topoffset is the proper inverse of the vertical offset, of course.
 			// remember gz and gzt should be seperated by sprite height, not thing height - thing height can be shorter than the sprite itself sometimes!
-
-			if (oldthing->scale != oldthing->old_scale) // Interpolate heights in reverse gravity when scaling mobjs
-				gz = interp.z + FixedMul(oldthing->height, FixedDiv(interp.scale, oldthing->scale)) - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
-			else
-				gz = interp.z + oldthing->height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
+			gz = interp.z + interp.height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
 			gzt = gz + FixedMul(spr_height, FixedMul(spriteyscale, this_scale));
 		}
 		else
@@ -2197,7 +2196,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	vis->gy = interp.y;
 	vis->gz = gz;
 	vis->gzt = gzt;
-	vis->thingheight = thing->height;
+	vis->thingheight = height;
 	vis->pz = interp.z;
 	vis->pzt = vis->pz + vis->thingheight;
 	vis->texturemid = FixedDiv(gzt - viewz, spriteyscale);
-- 
GitLab


From 91a17fdbdd78c121d00731ff009fd07901d5ecc7 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Fri, 25 Aug 2023 10:42:25 +0200
Subject: [PATCH 360/518] Use decimal instead of octal vertex IDs

---
 src/hardware/hw_main.c | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 4208a44860..f2022bcea3 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -4053,32 +4053,32 @@ static void HWR_DrawBoundingBox(gl_vissprite_t *vis)
 	// repeat this 4 times (overhead)
 	//
 	//
-	// 17    20  21    11
-	//    16 15  14 10
-	// 27 22  *--*  07 12
+	// 15    16  17    09
+	//    14 13  12 08
+	// 23 18  *--*  07 10
 	//        |  |
-	// 26 23  *--*  06 13
-	//    24 00  01 02
-	// 25    05  04    03
+	// 22 19  *--*  06 11
+	//    20 00  01 02
+	// 21    05  04    03
 	//
 
-	v[000].x = v[005].x = v[015].x = v[016].x = v[017].x = v[020].x =
-		v[022].x = v[023].x = v[024].x = v[025].x = v[026].x = v[027].x = vis->x1; // west
+	v[ 0].x = v[ 5].x = v[13].x = v[14].x = v[15].x = v[16].x =
+		v[18].x = v[19].x = v[20].x = v[21].x = v[22].x = v[23].x = vis->x1; // west
 
-	v[001].x = v[002].x = v[003].x = v[004].x = v[006].x = v[007].x =
-		v[010].x = v[011].x = v[012].x = v[013].x = v[014].x = v[021].x = vis->x2; // east
+	v[ 1].x = v[ 2].x = v[ 3].x = v[ 4].x = v[ 6].x = v[ 7].x =
+		v[ 8].x = v[ 9].x = v[10].x = v[11].x = v[12].x = v[17].x = vis->x2; // east
 
-	v[000].z = v[001].z = v[002].z = v[003].z = v[004].z = v[005].z =
-		v[006].z = v[013].z = v[023].z = v[024].z = v[025].z = v[026].z = vis->z1; // south
+	v[ 0].z = v[ 1].z = v[ 2].z = v[ 3].z = v[ 4].z = v[ 5].z =
+		v[ 6].z = v[11].z = v[19].z = v[20].z = v[21].z = v[22].z = vis->z1; // south
 
-	v[007].z = v[010].z = v[011].z = v[012].z = v[014].z = v[015].z =
-		v[016].z = v[017].z = v[020].z = v[021].z = v[022].z = v[027].z = vis->z2; // north
+	v[ 7].z = v[ 8].z = v[ 9].z = v[10].z = v[12].z = v[13].z =
+		v[14].z = v[15].z = v[16].z = v[17].z = v[18].z = v[23].z = vis->z2; // north
 
-	v[000].y = v[001].y = v[002].y = v[006].y = v[007].y = v[010].y =
-		v[014].y = v[015].y = v[016].y = v[022].y = v[023].y = v[024].y = vis->gz; // bottom
+	v[ 0].y = v[ 1].y = v[ 2].y = v[ 6].y = v[ 7].y = v[ 8].y =
+		v[12].y = v[13].y = v[14].y = v[18].y = v[19].y = v[20].y = vis->gz; // bottom
 
-	v[003].y = v[004].y = v[005].y = v[011].y = v[012].y = v[013].y =
-		v[017].y = v[020].y = v[021].y = v[025].y = v[026].y = v[027].y = vis->gzt; // top
+	v[ 3].y = v[ 4].y = v[ 5].y = v[ 9].y = v[10].y = v[11].y =
+		v[15].y = v[16].y = v[17].y = v[21].y = v[22].y = v[23].y = vis->gzt; // top
 
 	Surf.PolyColor = V_GetColor(R_GetBoundingBoxColor(vis->mobj));
 	
-- 
GitLab


From 7a1f952d9eadada9f419d38b19d9d118d780f670 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Tue, 29 Aug 2023 17:11:52 +0200
Subject: [PATCH 361/518] Make a comment clearer

---
 src/p_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_user.c b/src/p_user.c
index cb6f9d9b93..035ae35b72 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2012,7 +2012,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 		mobj_t *ghost2 = P_SpawnGhostMobj(mobj->player->followmobj);
 		P_SetTarget(&ghost2->tracer, ghost);
 		P_SetTarget(&ghost->tracer, ghost2);
-		P_SetTarget(&ghost2->dontdrawforviewmobj, mobj); // Hide the follow-ghost for the non-follow object
+		P_SetTarget(&ghost2->dontdrawforviewmobj, mobj); // Hide the follow-ghost for the non-follow target
 		ghost2->flags2 |= (mobj->player->followmobj->flags2 & MF2_LINKDRAW);
 	}
 
-- 
GitLab


From 2febb6ced95fdbc3c55d1d114c68caa2c639d11e Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Tue, 29 Aug 2023 17:12:04 +0200
Subject: [PATCH 362/518] Don't copy dontdrawforviewmobj to MT_OVERLAYs

---
 src/p_mobj.c | 1 -
 src/p_user.c | 3 +++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index de2c3a72c7..f72e18ee14 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -6882,7 +6882,6 @@ void P_RunOverlays(void)
 		mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP) | (mo->target->eflags & MFE_VERTICALFLIP);
 		mo->scale = mo->destscale = mo->target->scale;
 		mo->angle = (mo->target->player ? mo->target->player->drawangle : mo->target->angle) + mo->movedir;
-		P_SetTarget(&mo->dontdrawforviewmobj, mo->target->dontdrawforviewmobj); // Hide the overlay from the view that its target is hidden from - But don't copy drawonlyforplayer!
 
 		if (!(mo->state->frame & FF_ANIMATE))
 			zoffs = FixedMul(((signed)mo->state->var2)*FRACUNIT, mo->scale);
diff --git a/src/p_user.c b/src/p_user.c
index 035ae35b72..f6f1a7588f 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -1855,6 +1855,7 @@ void P_SpawnShieldOrb(player_t *player)
 	{
 		ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY);
 		P_SetTarget(&ov->target, shieldobj);
+		P_SetTarget(&ov->dontdrawforviewmobj, player->mo); // Hide the shield in first-person
 		P_SetMobjState(ov, shieldobj->info->seestate);
 		P_SetTarget(&shieldobj->tracer, ov);
 	}
@@ -1862,12 +1863,14 @@ void P_SpawnShieldOrb(player_t *player)
 	{
 		ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY);
 		P_SetTarget(&ov->target, shieldobj);
+		P_SetTarget(&ov->dontdrawforviewmobj, player->mo); // Hide the shield in first-person
 		P_SetMobjState(ov, shieldobj->info->meleestate);
 	}
 	if (shieldobj->info->missilestate)
 	{
 		ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY);
 		P_SetTarget(&ov->target, shieldobj);
+		P_SetTarget(&ov->dontdrawforviewmobj, player->mo); // Hide the shield in first-person
 		P_SetMobjState(ov, shieldobj->info->missilestate);
 	}
 	if (player->powers[pw_shield] & SH_FORCE)
-- 
GitLab


From b98bec9a03bbe83f6dfeda13c2e2c1b0bdaab6b4 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Wed, 30 Aug 2023 00:07:49 +0200
Subject: [PATCH 363/518] Final final 2.2.12 color tweaks (real) - Tweaked
 Aether, Midnight and Volcanic - Credit Chrispy & Saneko for new skincolors

---
 src/info.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/info.c b/src/info.c
index 399b5c936c..36389d849a 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21584,7 +21584,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Black",  {0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1f, 0x1f}, SKINCOLOR_WHITE,  7,  V_GRAYMAP, true}, // SKINCOLOR_BLACK
 
 	// Desaturated
-	{"Aether",    {0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x91, 0x91, 0x91, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf}, SKINCOLOR_GREY,      15, 0,           true}, // SKINCOLOR_AETHER
+	{"Aether",    {0x00, 0x00, 0x01, 0x01, 0x90, 0x90, 0x91, 0x91, 0x92, 0xaa, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xae}, SKINCOLOR_GREY,      15, 0,           true}, // SKINCOLOR_AETHER
 	{"Slate",     {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0xaa, 0xaa, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_SILVER,    12, 0,           true}, // SKINCOLOR_SLATE
 	{"Moonstone", {   0,    4,    8,    9,   11,   12,   14,   15,  171,  172,  173,  174,  175,   27,   29,   31}, SKINCOLOR_TOPAZ,     15, V_GRAYMAP,   true}, // SKINCOLOR_MOONSTONE
 	{"Bluebell",  {0x90, 0x91, 0x92, 0x93, 0x94, 0x94, 0x95, 0xac, 0xac, 0xad, 0xad, 0xa8, 0xa8, 0xa9, 0xfd, 0xfe}, SKINCOLOR_COPPER,    4,  V_BLUEMAP,   true}, // SKINCOLOR_BLUEBELL
@@ -21606,7 +21606,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Lavender",  {0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7}, SKINCOLOR_GOLD,      4,  V_PURPLEMAP, true}, // SKINCOLOR_LAVENDER
 
 	// Viv's vivid colours (toast 21/07/17)
-	// Tweaks & additions (Lach, sphere, Alice, MotorRoach 26/10/22)
+	// Tweaks & additions (Lach, Chrispy, sphere, Alice, MotorRoach & Saneko 26/10/22)
 	{"Ruby",       {0xb0, 0xb0, 0xc9, 0xca, 0xcc, 0x26, 0x27, 0x28, 0x29, 0x2a, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfd}, SKINCOLOR_EMERALD,    10, V_REDMAP,     true}, // SKINCOLOR_RUBY
 	{"Cherry",     { 202,  203,  204,  205,  206,   40,   41,   42,   43,   44,  186,  187,   28,   29,   30,   31}, SKINCOLOR_MIDNIGHT,   10, V_REDMAP,     true}, // SKINCOLOR_CHERRY
 	{"Salmon",     {0xd0, 0xd0, 0xd1, 0xd2, 0x20, 0x21, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e}, SKINCOLOR_FOREST,     6,  V_REDMAP,     true}, // SKINCOLOR_SALMON
@@ -21666,7 +21666,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Cornflower", {0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x9a, 0x9c, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e}, SKINCOLOR_YELLOW,     4,  V_BLUEMAP,    true}, // SKINCOLOR_CORNFLOWER
 	{"Blue",       {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_ORANGE,     5,  V_BLUEMAP,    true}, // SKINCOLOR_BLUE
 	{"Cobalt",     { 145,  147,  149,  150,  151,  153,  154,  155,  156,  157,  158,  159,  253,  253,  254,  254}, SKINCOLOR_PERIDOT,    5,  V_BLUEMAP,    true}, // SKINCOLOR_COBALT
-	{"Midnight",   { 171,  171,  172,  173,  173,  174,  156,  157,  158,  159,  253,  253,  254,  254,   31,   31}, SKINCOLOR_CHERRY,     10, V_GRAYMAP,    true}, // SKINCOLOR_MIDNIGHT
+	{"Midnight",   { 171,  171,  172,  173,  173,  174,  175,  157,  158,  159,  253,  253,  254,  254,   31,   31}, SKINCOLOR_CHERRY,     10, V_GRAYMAP,    true}, // SKINCOLOR_MIDNIGHT
 	{"Galaxy",     { 160,  161,  162,  163,  164,  165,  166,  166,  154,  155,  156,  157,  159,  253,  254,   31}, SKINCOLOR_ISLAND,     7,  V_PURPLEMAP,  true}, // SKINCOLOR_GALAXY
 	{"Vapor",      {0x80, 0x81, 0x83, 0x86, 0x94, 0x94, 0xa3, 0xa3, 0xa4, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_LILAC,      4,  V_SKYMAP,     true}, // SKINCOLOR_VAPOR
 	{"Dusk",       {0x92, 0x93, 0x94, 0x94, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_OLIVE,      0,  V_BLUEMAP,    true}, // SKINCOLOR_DUSK
@@ -21690,7 +21690,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = {
 	{"Rosy",       {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_AQUA,       1,  V_ROSYMAP,    true}, // SKINCOLOR_ROSY
 	{"Fancy",      {   0,  208,   49,  210,  210,  202,  202,  203,  204,  204,  205,  206,  207,  207,  186,  186}, SKINCOLOR_ROYAL,      9,  V_ROSYMAP,    true}, // SKINCOLOR_FANCY
 	{"Sangria",    { 210,   32,   33,   34,   34,  215,  215,  207,  207,  185,  186,  186,  186,  169,  169,  253}, SKINCOLOR_TURQUOISE,  12, V_ROSYMAP,    true}, // SKINCOLOR_SANGRIA
-	{"Volcanic",   {  35,   38,   41,   42,   44,   46,   46,  169,  169,  159,  253,  254,   30,   30,   31,   31}, SKINCOLOR_BRONZE,     9,  V_REDMAP,     true}, // SKINCOLOR_VOLCANIC
+	{"Volcanic",   {  54,   36,   42,   44,   45,   46,   46,   47,   28,  253,  253,  254,  254,   30,   31,   31}, SKINCOLOR_BRONZE,     9,  V_REDMAP,     true}, // SKINCOLOR_VOLCANIC
 
 	// super
 	{"Super Silver 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03}, SKINCOLOR_BLACK, 15, 0,         false}, // SKINCOLOR_SUPERSILVER1
-- 
GitLab


From 25c220d363f91449d3bbaad3910a247fe0063749 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Thu, 31 Aug 2023 16:25:14 +0200
Subject: [PATCH 364/518] Some credits updates

---
 src/f_finale.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/f_finale.c b/src/f_finale.c
index 162e87d294..11ea909e94 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -1062,12 +1062,14 @@ static const char *credits[] = {
 	"\"Golden\"",
 	"Vivian \"toaster\" Grannell",
 	"Julio \"Chaos Zero 64\" Guir",
+	"\"Hanicef\"",
 	"\"Hannu_Hanhi\"", // For many OpenGL performance improvements!
 	"Kepa \"Nev3r\" Iceta",
 	"Thomas \"Shadow Hog\" Igoe",
 	"Iestyn \"Monster Iestyn\" Jealous",
 	"\"Kaito Sinclaire\"",
 	"\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog
+	"\"katsy\"",
 	"Ronald \"Furyhunter\" Kinard", // The SDL2 port
 	"\"Lat'\"", // SRB2-CHAT, the chat window from Kart
 	"\"LZA\"",
@@ -1090,6 +1092,7 @@ static const char *credits[] = {
 	"Ben \"Cue\" Woodford",
 	"Lachlan \"Lach\" Wright",
 	"Marco \"mazmazz\" Zafra",
+	"\"Zwip-Zwap Zapony\"",
 	"",
 	"\1Art",
 	"Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D:
@@ -1197,6 +1200,7 @@ static const char *credits[] = {
 	"FreeDoom Project", // Used some of the mancubus and rocket launcher sprites for Brak
 	"Kart Krew",
 	"Alex \"MistaED\" Fuller",
+	"Howard Drossin", // Virtual Sonic - Sonic & Knuckles Theme
 	"Pascal \"CodeImp\" vd Heiden", // Doom Builder developer
 	"Randi Heit (<!>)", // For their MSPaint <!> sprite that we nicked
 	"Simon \"sirjuddington\" Judd", // SLADE developer
-- 
GitLab


From 6414e9db79771494f87662f902999a028aa385cd Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Thu, 31 Aug 2023 15:02:59 -0300
Subject: [PATCH 365/518] Fix possible crashes in the Lua API when accessing
 invalid fields

---
 src/lua_consolelib.c |  2 +-
 src/lua_hudlib.c     |  3 +--
 src/lua_maplib.c     | 12 ++++++++----
 3 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index 9e5d1e2ee0..3783b8f7b6 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -615,7 +615,7 @@ static int cvar_get(lua_State *L)
 		break;
 	default:
 		if (devparm)
-			return luaL_error(L, LUA_QL("consvar_t") " has no field named " LUA_QS, field);
+			return luaL_error(L, LUA_QL("consvar_t") " has no field named " LUA_QS ".", lua_tostring(L, 2));
 		else
 			return 0;
 	}
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 63a866606f..6eec912733 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -282,7 +282,6 @@ static int patch_get(lua_State *L)
 	patch_t *patch = *((patch_t **)luaL_checkudata(L, 1, META_PATCH));
 	enum patch field = Lua_optoption(L, 2, -1, patch_fields_ref);
 
-	// patches are invalidated when switching renderers
 	if (!patch) {
 		if (field == patch_valid) {
 			lua_pushboolean(L, 0);
@@ -436,7 +435,7 @@ static int camera_set(lua_State *L)
 		cam->momz = luaL_checkfixed(L, 3);
 		break;
 	default:
-		return luaL_error(L, LUA_QL("camera_t") " has no field named " LUA_QS, camera_opt[field]);
+		return luaL_error(L, LUA_QL("camera_t") " has no field named " LUA_QS ".", lua_tostring(L, 2));
 	}
 	return 0;
 }
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 3d95cdb751..e343979939 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -800,8 +800,9 @@ static int sector_set(lua_State *L)
 	case sector_fslope: // f_slope
 	case sector_cslope: // c_slope
 	case sector_friction: // friction
-	default:
 		return luaL_error(L, "sector_t field " LUA_QS " cannot be set.", sector_opt[field]);
+	default:
+		return luaL_error(L, "sector_t has no field named " LUA_QS ".", lua_tostring(L, 2));
 	case sector_floorheight: { // floorheight
 		boolean flag;
 		mobj_t *ptmthing = tmthing;
@@ -1279,8 +1280,9 @@ static int side_set(lua_State *L)
 	case side_sector:
 	case side_special:
 	case side_text:
-	default:
 		return luaL_error(L, "side_t field " LUA_QS " cannot be set.", side_opt[field]);
+	default:
+		return luaL_error(L, "side_t has no field named " LUA_QS ".", lua_tostring(L, 2));
 	case side_textureoffset:
 		side->textureoffset = luaL_checkfixed(L, 3);
 		break;
@@ -2291,8 +2293,9 @@ static int ffloor_set(lua_State *L)
 	case ffloor_target: // target
 	case ffloor_next: // next
 	case ffloor_prev: // prev
-	default:
 		return luaL_error(L, "ffloor_t field " LUA_QS " cannot be set.", ffloor_opt[field]);
+	default:
+		return luaL_error(L, "ffloor_t has no field named " LUA_QS ".", lua_tostring(L, 2));
 	case ffloor_topheight: { // topheight
 		boolean flag;
 		fixed_t lastpos = *ffloor->topheight;
@@ -2426,8 +2429,9 @@ static int slope_set(lua_State *L)
 	case slope_d: // d
 	case slope_flags: // flags
 	case slope_normal: // normal
-	default:
 		return luaL_error(L, "pslope_t field " LUA_QS " cannot be set.", slope_opt[field]);
+	default:
+		return luaL_error(L, "pslope_t has no field named " LUA_QS ".", lua_tostring(L, 2));
 	case slope_o: { // o
 		luaL_checktype(L, 3, LUA_TTABLE);
 
-- 
GitLab


From 3faa29ded9e0d21d885713aef328becc10fa16b6 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Fri, 1 Sep 2023 22:36:15 +0200
Subject: [PATCH 366/518] Fix the Record Attack background jumping

---
 src/f_finale.c | 2 +-
 src/m_menu.c   | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/f_finale.c b/src/f_finale.c
index 11ea909e94..91c06b3165 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -2260,7 +2260,7 @@ void F_InitMenuPresValues(void)
 	curfadevalue = 16;
 	curbgcolor = -1;
 	curbgxspeed = (gamestate == GS_TIMEATTACK) ? 0 : titlescrollxspeed;
-	curbgyspeed = (gamestate == GS_TIMEATTACK) ? 22 : titlescrollyspeed;
+	curbgyspeed = (gamestate == GS_TIMEATTACK) ? 18 : titlescrollyspeed;
 	curbghide = (gamestate == GS_TIMEATTACK) ? false : true;
 
 	curhidepics = hidetitlepics;
diff --git a/src/m_menu.c b/src/m_menu.c
index c494421415..9fcd1e85a7 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -2649,7 +2649,7 @@ static boolean MIT_SetCurBackground(UINT32 menutype, INT32 level, INT32 *retval,
 		{
 			strncpy(curbgname, defaultname, 9);
 			curbgxspeed = (gamestate == GS_TIMEATTACK) ? 0 : titlescrollxspeed;
-			curbgyspeed = (gamestate == GS_TIMEATTACK) ? 0 : titlescrollyspeed;
+			curbgyspeed = (gamestate == GS_TIMEATTACK) ? 18 : titlescrollyspeed;
 		}
 	}
 	return false;
@@ -2843,8 +2843,8 @@ static void M_HandleMenuPresState(menu_t *newMenu)
 	curfadevalue = 16;
 	curhidepics = hidetitlepics;
 	curbgcolor = -1;
-	curbgxspeed = titlescrollxspeed;
-	curbgyspeed = titlescrollyspeed;
+	curbgxspeed = (gamestate == GS_TIMEATTACK) ? 0 : titlescrollxspeed;
+	curbgyspeed = (gamestate == GS_TIMEATTACK) ? 18 : titlescrollyspeed;
 	curbghide = (gamestate != GS_TIMEATTACK); // show in time attack, hide in other menus
 
 	curttmode = ttmode;
-- 
GitLab


From d4a2bb5675bfcc8dfbe9c886b0831c40d07f86dd Mon Sep 17 00:00:00 2001
From: Eidolon <furyhunter600@gmail.com>
Date: Sun, 3 Sep 2023 10:08:03 -0500
Subject: [PATCH 367/518] Fix sprite scale column iteration crash

---
 src/r_things.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/r_things.c b/src/r_things.c
index e69fe1b822..4126358256 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -2235,7 +2235,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	vis->xscale = FixedMul(spritexscale, xscale); //SoM: 4/17/2000
 	vis->scale = FixedMul(spriteyscale, yscale); //<<detailshift;
-	vis->thingscale = interp.scale;
+	vis->thingscale = this_scale;
 
 	vis->spritexscale = spritexscale;
 	vis->spriteyscale = spriteyscale;
-- 
GitLab


From b8313ceda283ac0c8dd9ab6141e83a31f0bb9639 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Sun, 3 Sep 2023 17:02:35 -0300
Subject: [PATCH 368/518] Optimize sprite rendering by ignoring completely
 occluded sprites

---
 src/r_main.c   | 28 +++-----------------
 src/r_main.h   |  2 +-
 src/r_things.c | 71 ++++++++++++++++++++++++++++++++++++++++----------
 src/r_things.h | 29 +++++++++++----------
 4 files changed, 77 insertions(+), 53 deletions(-)

diff --git a/src/r_main.c b/src/r_main.c
index 952171405c..54f7d7639e 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -41,16 +41,6 @@
 #include "hardware/hw_main.h"
 #endif
 
-//profile stuff ---------------------------------------------------------
-//#define TIMING
-#ifdef TIMING
-#include "p5prof.h"
-INT64 mycount;
-INT64 mytotal = 0;
-//unsigned long  nombre = 100000;
-#endif
-//profile stuff ---------------------------------------------------------
-
 // Fineangles in the SCREENWIDTH wide window.
 #define FIELDOFVIEW 2048
 
@@ -157,7 +147,8 @@ consvar_t cv_flipcam2 = CVAR_INIT ("flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT,
 
 consvar_t cv_shadow = CVAR_INIT ("shadow", "On", CV_SAVE, CV_OnOff, NULL);
 consvar_t cv_skybox = CVAR_INIT ("skybox", "On", CV_SAVE, CV_OnOff, NULL);
-consvar_t cv_ffloorclip = CVAR_INIT ("ffloorclip", "On", CV_SAVE, CV_OnOff, NULL);
+consvar_t cv_ffloorclip = CVAR_INIT ("r_ffloorclip", "On", CV_SAVE, CV_OnOff, NULL);
+consvar_t cv_spriteclip = CVAR_INIT ("r_spriteclip", "On", CV_SAVE, CV_OnOff, NULL);
 consvar_t cv_allowmlook = CVAR_INIT ("allowmlook", "Yes", CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
 consvar_t cv_showhud = CVAR_INIT ("showhud", "Yes", CV_CALL|CV_ALLOWLUA,  CV_YesNo, R_SetViewSize);
 consvar_t cv_translucenthud = CVAR_INIT ("translucenthud", "10", CV_SAVE, translucenthud_cons_t, NULL);
@@ -1490,29 +1481,17 @@ void R_RenderPlayerView(player_t *player)
 
 	Mask_Pre(&masks[nummasks - 1]);
 	curdrawsegs = ds_p;
-//profile stuff ---------------------------------------------------------
-#ifdef TIMING
-	mytotal = 0;
-	ProfZeroTimer();
-#endif
 	ps_numbspcalls.value.i = ps_numpolyobjects.value.i = ps_numdrawnodes.value.i = 0;
 	PS_START_TIMING(ps_bsptime);
 	R_RenderBSPNode((INT32)numnodes - 1);
 	PS_STOP_TIMING(ps_bsptime);
-	ps_numsprites.value.i = visspritecount;
-#ifdef TIMING
-	RDMSR(0x10, &mycount);
-	mytotal += mycount; // 64bit add
-
-	CONS_Debug(DBG_RENDER, "RenderBSPNode: 0x%d %d\n", *((INT32 *)&mytotal + 1), (INT32)mytotal);
-#endif
-//profile stuff ---------------------------------------------------------
 	Mask_Post(&masks[nummasks - 1]);
 
 	PS_START_TIMING(ps_sw_spritecliptime);
 	R_ClipSprites(drawsegs, NULL);
 	PS_STOP_TIMING(ps_sw_spritecliptime);
 
+	ps_numsprites.value.i = numvisiblesprites;
 
 	// Add skybox portals caused by sky visplanes.
 	if (cv_skybox.value && skyboxmo[0])
@@ -1603,6 +1582,7 @@ void R_RegisterEngineStuff(void)
 	CV_RegisterVar(&cv_shadow);
 	CV_RegisterVar(&cv_skybox);
 	CV_RegisterVar(&cv_ffloorclip);
+	CV_RegisterVar(&cv_spriteclip);
 
 	CV_RegisterVar(&cv_cam_dist);
 	CV_RegisterVar(&cv_cam_still);
diff --git a/src/r_main.h b/src/r_main.h
index f08070d0f3..a6fb42ba24 100644
--- a/src/r_main.h
+++ b/src/r_main.h
@@ -114,7 +114,7 @@ extern consvar_t cv_chasecam, cv_chasecam2;
 extern consvar_t cv_flipcam, cv_flipcam2;
 
 extern consvar_t cv_shadow;
-extern consvar_t cv_ffloorclip;
+extern consvar_t cv_ffloorclip, cv_spriteclip;
 extern consvar_t cv_translucency;
 extern consvar_t cv_drawdist, cv_drawdist_nights, cv_drawdist_precip;
 extern consvar_t cv_fov;
diff --git a/src/r_things.c b/src/r_things.c
index e69fe1b822..ba262a8b0e 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -524,7 +524,8 @@ void R_AddSpriteDefs(UINT16 wadnum)
 //
 // GAME FUNCTIONS
 //
-UINT32 visspritecount;
+UINT32 visspritecount, numvisiblesprites;
+
 static UINT32 clippedvissprites;
 static vissprite_t *visspritechunks[MAXVISSPRITES >> VISSPRITECHUNKBITS] = {NULL};
 
@@ -598,7 +599,7 @@ void R_InitSprites(void)
 //
 void R_ClearSprites(void)
 {
-	visspritecount = clippedvissprites = 0;
+	visspritecount = numvisiblesprites = clippedvissprites = 0;
 }
 
 //
@@ -2637,6 +2638,14 @@ static void R_SortVisSprites(vissprite_t* vsprsortedhead, UINT32 start, UINT32 e
 	// bundle linkdraw
 	for (ds = unsorted.prev; ds != &unsorted; ds = ds->prev)
 	{
+		// Remove this sprite if it was determined to not be visible
+		if (ds->cut & SC_NOTVISIBLE)
+		{
+			ds->next->prev = ds->prev;
+			ds->prev->next = ds->next;
+			continue;
+		}
+
 		if (!(ds->cut & SC_LINKDRAW))
 			continue;
 
@@ -2663,21 +2672,27 @@ static void R_SortVisSprites(vissprite_t* vsprsortedhead, UINT32 start, UINT32 e
 				continue;
 
 			// don't connect if the tracer's top is cut off, but lower than the link's top
-			if ((dsfirst->cut & SC_TOP)
-			&& dsfirst->szt > ds->szt)
+			if ((dsfirst->cut & SC_TOP) && dsfirst->szt > ds->szt)
 				continue;
 
 			// don't connect if the tracer's bottom is cut off, but higher than the link's bottom
-			if ((dsfirst->cut & SC_BOTTOM)
-			&& dsfirst->sz < ds->sz)
+			if ((dsfirst->cut & SC_BOTTOM) && dsfirst->sz < ds->sz)
 				continue;
 
+			// If the object isn't visible, then the bounding box isn't either
+			if (ds->cut & SC_BBOX && dsfirst->cut & SC_NOTVISIBLE)
+				ds->cut |= SC_NOTVISIBLE;
+
 			break;
 		}
 
 		// remove from chain
 		ds->next->prev = ds->prev;
 		ds->prev->next = ds->next;
+
+		if (ds->cut & SC_NOTVISIBLE)
+			continue;
+
 		linkedvissprites++;
 
 		if (dsfirst != &unsorted)
@@ -2729,12 +2744,15 @@ static void R_SortVisSprites(vissprite_t* vsprsortedhead, UINT32 start, UINT32 e
 				best = ds;
 			}
 		}
-		best->next->prev = best->prev;
-		best->prev->next = best->next;
-		best->next = vsprsortedhead;
-		best->prev = vsprsortedhead->prev;
-		vsprsortedhead->prev->next = best;
-		vsprsortedhead->prev = best;
+		if (best)
+		{
+			best->next->prev = best->prev;
+			best->prev->next = best->next;
+			best->next = vsprsortedhead;
+			best->prev = vsprsortedhead->prev;
+			vsprsortedhead->prev->next = best;
+			vsprsortedhead->prev = best;
+		}
 	}
 }
 
@@ -3308,8 +3326,7 @@ static void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, portal_t* port
 			spr->clipbot[x] = (INT16)viewheight;
 
 		if (spr->cliptop[x] == -2)
-			//Fab : 26-04-98: was -1, now clips against console bottom
-			spr->cliptop[x] = (INT16)con_clipviewtop;
+			spr->cliptop[x] = -1;
 	}
 
 	if (portal)
@@ -3334,6 +3351,23 @@ static void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, portal_t* port
 			spr->cliptop[x] = -1;
 		}
 	}
+
+	// Check if it'll be visible
+	// Not done for floorsprites.
+	if (cv_spriteclip.value && (spr->cut & SC_SPLAT) == 0)
+	{
+		for (x = x1; x <= x2; x++)
+		{
+			if (spr->cliptop[x] < spr->clipbot[x]
+			&& spr->sz > spr->cliptop[x]
+			&& spr->szt < spr->clipbot[x])
+			{
+				return;
+			}
+		}
+
+		spr->cut |= SC_NOTVISIBLE;
+	}
 }
 
 void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
@@ -3402,6 +3436,12 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
 	{
 		vissprite_t *spr = R_GetVisSprite(clippedvissprites);
 
+		if (spr->szt > vid.height || spr->sz < 0)
+		{
+			spr->cut |= SC_NOTVISIBLE;
+			continue;
+		}
+
 		if (spr->cut & SC_BBOX)
 			continue;
 
@@ -3425,6 +3465,9 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
 		}
 
 		R_ClipVisSprite(spr, x1, x2, portal);
+
+		if ((spr->cut & SC_NOTVISIBLE) == 0)
+			numvisiblesprites++;
 	}
 }
 
diff --git a/src/r_things.h b/src/r_things.h
index e110053635..318234886b 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -123,21 +123,22 @@ typedef enum
 	SC_NONE       = 0,
 	SC_TOP        = 1,
 	SC_BOTTOM     = 1<<1,
+	SC_NOTVISIBLE = 1<<2,
 	// other flags
-	SC_PRECIP     = 1<<2,
-	SC_LINKDRAW   = 1<<3,
-	SC_FULLBRIGHT = 1<<4,
-	SC_SEMIBRIGHT = 1<<5,
-	SC_FULLDARK   = 1<<6,
-	SC_VFLIP      = 1<<7,
-	SC_ISSCALED   = 1<<8,
-	SC_ISROTATED  = 1<<9,
-	SC_SHADOW     = 1<<10,
-	SC_SHEAR      = 1<<11,
-	SC_SPLAT      = 1<<12,
-	SC_BBOX       = 1<<13,
+	SC_PRECIP     = 1<<3,
+	SC_LINKDRAW   = 1<<4,
+	SC_FULLBRIGHT = 1<<5,
+	SC_SEMIBRIGHT = 1<<6,
+	SC_FULLDARK   = 1<<7,
+	SC_VFLIP      = 1<<8,
+	SC_ISSCALED   = 1<<9,
+	SC_ISROTATED  = 1<<10,
+	SC_SHADOW     = 1<<11,
+	SC_SHEAR      = 1<<12,
+	SC_SPLAT      = 1<<13,
+	SC_BBOX       = 1<<14,
 	// masks
-	SC_CUTMASK    = SC_TOP|SC_BOTTOM,
+	SC_CUTMASK    = SC_TOP|SC_BOTTOM|SC_NOTVISIBLE,
 	SC_FLAGMASK   = ~SC_CUTMASK
 } spritecut_e;
 
@@ -219,7 +220,7 @@ typedef struct vissprite_s
 	INT32 dispoffset; // copy of mobj->dispoffset, affects ordering but not drawing
 } vissprite_t;
 
-extern UINT32 visspritecount;
+extern UINT32 visspritecount, numvisiblesprites;
 
 void R_ClipSprites(drawseg_t* dsstart, portal_t* portal);
 
-- 
GitLab


From 58bd31fdca435770499cd7e9ad6b0a3a99cbd6a3 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Sun, 3 Sep 2023 17:14:48 -0300
Subject: [PATCH 369/518] Make hitboxes count towards the total sprite count
 This has the effect of misrepresenting how many sprites are actually visible,
 but it's more "accurate".

---
 src/r_things.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/r_things.c b/src/r_things.c
index ba262a8b0e..b1790933d6 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -3443,7 +3443,10 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
 		}
 
 		if (spr->cut & SC_BBOX)
+		{
+			numvisiblesprites++;
 			continue;
+		}
 
 		INT32 x1 = (spr->cut & SC_SPLAT) ? 0 : spr->x1;
 		INT32 x2 = (spr->cut & SC_SPLAT) ? viewwidth : spr->x2;
-- 
GitLab


From c35456d5cb1569ae6c1eb9bbe2a0d13c33bc91c5 Mon Sep 17 00:00:00 2001
From: Eidolon <furyhunter600@gmail.com>
Date: Sun, 3 Sep 2023 16:31:08 -0500
Subject: [PATCH 370/518] Actually fix papersprites near camera crash

---
 src/r_things.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/r_things.c b/src/r_things.c
index 4126358256..6e334990bb 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -844,6 +844,15 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // ditto
 	}
 
+	// TODO This check should not be necessary. But Papersprites near to the camera will sometimes create invalid values
+	// for the vissprite's startfrac. This happens because they are not depth culled like other sprites.
+	// Someone who is more familiar with papersprites pls check and try to fix <3
+	if (vis->startfrac < 0 || vis->startfrac > (patch->width << FRACBITS))
+	{
+		// never draw vissprites with startfrac out of patch range
+		return;
+	}
+
 	colfunc = colfuncs[BASEDRAWFUNC]; // hack: this isn't resetting properly somewhere.
 	dc_colormap = vis->colormap;
 	dc_translation = R_GetSpriteTranslation(vis);
@@ -2235,7 +2244,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	vis->xscale = FixedMul(spritexscale, xscale); //SoM: 4/17/2000
 	vis->scale = FixedMul(spriteyscale, yscale); //<<detailshift;
-	vis->thingscale = this_scale;
+	vis->thingscale = interp.scale;
 
 	vis->spritexscale = spritexscale;
 	vis->spriteyscale = spriteyscale;
-- 
GitLab


From d777b62e1bd24eb89e2dcf246f83c38beb460a08 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Sun, 3 Sep 2023 18:59:39 -0300
Subject: [PATCH 371/518] Improve checks for papersprites

---
 src/r_things.c | 68 +++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 50 insertions(+), 18 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index b1790933d6..f8456fa118 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -888,7 +888,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	frac = vis->startfrac;
 	windowtop = windowbottom = sprbotscreen = INT32_MAX;
 
-	if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && ((skin_t *)vis->mobj->skin)->flags & SF_HIRES)
+	if (vis->cut & SC_SHADOW && vis->mobj->skin && ((skin_t *)vis->mobj->skin)->flags & SF_HIRES)
 		this_scale = FixedMul(this_scale, ((skin_t *)vis->mobj->skin)->highresscale);
 	if (this_scale <= 0)
 		this_scale = 1;
@@ -898,10 +898,10 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		{
 			vis->scale = FixedMul(vis->scale, this_scale);
 			vis->scalestep = FixedMul(vis->scalestep, this_scale);
-			vis->xiscale = FixedDiv(vis->xiscale,this_scale);
+			vis->xiscale = FixedDiv(vis->xiscale, this_scale);
 			vis->cut |= SC_ISSCALED;
 		}
-		dc_texturemid = FixedDiv(dc_texturemid,this_scale);
+		dc_texturemid = FixedDiv(dc_texturemid, this_scale);
 	}
 
 	spryscale = vis->scale;
@@ -1759,9 +1759,6 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	I_Assert(lump < max_spritelumps);
 
-	if (thing->skin && ((skin_t *)thing->skin)->flags & SF_HIRES)
-		this_scale = FixedMul(this_scale, ((skin_t *)thing->skin)->highresscale);
-
 	spr_width = spritecachedinfo[lump].width;
 	spr_height = spritecachedinfo[lump].height;
 	spr_offset = spritecachedinfo[lump].offset;
@@ -1811,6 +1808,14 @@ static void R_ProjectSprite(mobj_t *thing)
 	// calculate edges of the shape
 	spritexscale = interp.spritexscale;
 	spriteyscale = interp.spriteyscale;
+
+	if (thing->skin && ((skin_t *)thing->skin)->flags & SF_HIRES)
+	{
+		fixed_t highresscale = ((skin_t *)thing->skin)->highresscale;
+		spritexscale = FixedMul(spritexscale, highresscale);
+		spriteyscale = FixedMul(spriteyscale, highresscale);
+	}
+
 	if (spritexscale < 1 || spriteyscale < 1)
 		return;
 
@@ -3183,6 +3188,40 @@ static void R_HeightSecClip(vissprite_t *spr, INT32 x1, INT32 x2)
 	}
 }
 
+static boolean R_CheckSpriteVisible(vissprite_t *spr, INT32 x1, INT32 x2)
+{
+	INT16 sz = spr->sz;
+	INT16 szt = spr->szt;
+
+	fixed_t texturemid, yscale, scalestep = spr->scalestep;
+
+	if (scalestep)
+	{
+		yscale = spr->scale;
+		scalestep = FixedMul(scalestep, spr->spriteyscale);
+
+		if (spr->thingscale != FRACUNIT)
+			texturemid = FixedDiv(spr->texturemid, max(spr->thingscale, 1));
+		else
+			texturemid = spr->texturemid;
+	}
+
+	for (INT32 x = x1; x <= x2; x++)
+	{
+		if (scalestep)
+		{
+			szt = (INT16)((centeryfrac - FixedMul(texturemid, yscale))>>FRACBITS);
+			sz = (INT16)((centeryfrac - FixedMul(texturemid, yscale))>>FRACBITS);
+			yscale += scalestep;
+		}
+
+		if (spr->cliptop[x] < spr->clipbot[x] && sz > spr->cliptop[x] && szt < spr->clipbot[x])
+			return true;
+	}
+
+	return false;
+}
+
 // R_ClipVisSprite
 // Clips vissprites without drawing, so that portals can work. -Red
 static void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, portal_t* portal)
@@ -3356,17 +3395,8 @@ static void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, portal_t* port
 	// Not done for floorsprites.
 	if (cv_spriteclip.value && (spr->cut & SC_SPLAT) == 0)
 	{
-		for (x = x1; x <= x2; x++)
-		{
-			if (spr->cliptop[x] < spr->clipbot[x]
-			&& spr->sz > spr->cliptop[x]
-			&& spr->szt < spr->clipbot[x])
-			{
-				return;
-			}
-		}
-
-		spr->cut |= SC_NOTVISIBLE;
+		if (!R_CheckSpriteVisible(spr, x1, x2))
+			spr->cut |= SC_NOTVISIBLE;
 	}
 }
 
@@ -3436,7 +3466,9 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
 	{
 		vissprite_t *spr = R_GetVisSprite(clippedvissprites);
 
-		if (spr->szt > vid.height || spr->sz < 0)
+		if (cv_spriteclip.value
+		&& (spr->szt > vid.height || spr->sz < 0)
+		&& !((spr->cut & SC_SPLAT) || spr->scalestep))
 		{
 			spr->cut |= SC_NOTVISIBLE;
 			continue;
-- 
GitLab


From 57a880a286e53ccbdd8ee0f9df59522afd733cbc Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Sun, 3 Sep 2023 19:35:58 -0300
Subject: [PATCH 372/518] Fix a bug

---
 src/r_things.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index f8456fa118..385cbaaa5c 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -3194,9 +3194,11 @@ static boolean R_CheckSpriteVisible(vissprite_t *spr, INT32 x1, INT32 x2)
 	INT16 szt = spr->szt;
 
 	fixed_t texturemid, yscale, scalestep = spr->scalestep;
+	INT32 height;
 
 	if (scalestep)
 	{
+		height = spr->patch->height;
 		yscale = spr->scale;
 		scalestep = FixedMul(scalestep, spr->spriteyscale);
 
@@ -3210,8 +3212,10 @@ static boolean R_CheckSpriteVisible(vissprite_t *spr, INT32 x1, INT32 x2)
 	{
 		if (scalestep)
 		{
-			szt = (INT16)((centeryfrac - FixedMul(texturemid, yscale))>>FRACBITS);
-			sz = (INT16)((centeryfrac - FixedMul(texturemid, yscale))>>FRACBITS);
+			fixed_t top = centeryfrac - FixedMul(texturemid, yscale);
+			fixed_t bottom = top + (height * yscale);
+			szt = (INT16)(top >> FRACBITS);
+			sz = (INT16)(bottom >> FRACBITS);
 			yscale += scalestep;
 		}
 
-- 
GitLab


From 01670bd96feebfeceffdb58c2338b631fe442cce Mon Sep 17 00:00:00 2001
From: SteelT <steeltitanium1@gmail.com>
Date: Mon, 4 Sep 2023 13:45:45 -0400
Subject: [PATCH 373/518] gitignore: add CMakeUserPresets.json

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index 2b4d9d2fbf..268e363290 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,4 @@ Win32_LIB_ASM_Release
 /bin
 /build
 /build/*
+/CMakeUserPresets.json
\ No newline at end of file
-- 
GitLab


From 52d356d09e647aa19f1c36db5a0963305e9a058e Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 5 Sep 2023 14:43:30 +0200
Subject: [PATCH 374/518] Don't attract bomb spheres with Nightopian Helper

---
 src/p_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_user.c b/src/p_user.c
index cb6f9d9b93..085e9e9010 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -11867,7 +11867,7 @@ void P_PlayerThink(player_t *player)
 			mo2 = (mobj_t *)th;
 
 			if (!(mo2->type == MT_RING || mo2->type == MT_COIN
-				|| mo2->type == MT_BLUESPHERE || mo2->type == MT_BOMBSPHERE
+				|| mo2->type == MT_BLUESPHERE // || mo2->type == MT_BOMBSPHERE
 				|| mo2->type == MT_NIGHTSCHIP || mo2->type == MT_NIGHTSSTAR))
 				continue;
 
-- 
GitLab


From a29d1ca219df070ee1b3fbed814c22dfb9427a34 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 5 Sep 2023 18:03:51 +0200
Subject: [PATCH 375/518] Make camera noclip during NiGHTS gameplay

---
 src/p_map.c  | 7 +++----
 src/p_mobj.c | 4 ++--
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/p_map.c b/src/p_map.c
index 132a3cf854..80135db747 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -2524,7 +2524,6 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam)
 boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam)
 {
 	subsector_t *s = R_PointInSubsector(x, y);
-	boolean retval = true;
 	boolean itsatwodlevel = false;
 
 	floatok = false;
@@ -2539,8 +2538,8 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam)
 		fixed_t tryx = thiscam->x;
 		fixed_t tryy = thiscam->y;
 
-		if ((thiscam == &camera && (players[displayplayer].pflags & PF_NOCLIP))
-		|| (thiscam == &camera2 && (players[secondarydisplayplayer].pflags & PF_NOCLIP)))
+		if ((thiscam == &camera && (players[displayplayer].pflags & PF_NOCLIP || players[displayplayer].powers[pw_carry] == CR_NIGHTSMODE))
+		|| (thiscam == &camera2 && (players[secondarydisplayplayer].pflags & PF_NOCLIP || players[secondarydisplayplayer].powers[pw_carry] == CR_NIGHTSMODE)))
 		{ // Noclipping player camera noclips too!!
 			floatok = true;
 			thiscam->floorz = thiscam->z;
@@ -2608,7 +2607,7 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam)
 	thiscam->y = y;
 	thiscam->subsector = s;
 
-	return retval;
+	return true;
 }
 
 //
diff --git a/src/p_mobj.c b/src/p_mobj.c
index de2c3a72c7..5f25608e85 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -3688,7 +3688,7 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
 			dummy.y = thiscam->y;
 			dummy.z = thiscam->z;
 			dummy.height = thiscam->height;
-			if (!resetcalled && !(player->pflags & PF_NOCLIP) && !P_CheckSight(&dummy, player->mo)) // TODO: "P_CheckCameraSight" instead.
+			if (!resetcalled && !(player->pflags & PF_NOCLIP || player->powers[pw_carry] == CR_NIGHTSMODE) && !P_CheckSight(&dummy, player->mo)) // TODO: "P_CheckCameraSight" instead.
 				P_ResetCamera(player, thiscam);
 			else
 			{
@@ -3719,7 +3719,7 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
 		// adjust height
 		thiscam->z += thiscam->momz + player->mo->pmomz;
 
-		if (!itsatwodlevel && !(player->pflags & PF_NOCLIP))
+		if (!itsatwodlevel && !(player->pflags & PF_NOCLIP || player->powers[pw_carry] == CR_NIGHTSMODE))
 		{
 			// clip movement
 			if (thiscam->z <= thiscam->floorz) // hit the floor
-- 
GitLab


From 969e21017d83bbd9a4f81f6fb68d90d80334790e Mon Sep 17 00:00:00 2001
From: katsy <205-katsy@users.noreply.git.do.srb2.org>
Date: Tue, 5 Sep 2023 17:23:34 +0000
Subject: [PATCH 376/518] Make renderhitbox a netvar to prevent clientside
 wallhacking

---
 src/r_bbox.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/r_bbox.c b/src/r_bbox.c
index 4302d1b9ab..6d468e47d9 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -34,7 +34,7 @@ static CV_PossibleValue_t renderhitbox_cons_t[] = {
 	{RENDERHITBOX_RINGS, "Rings"},
 	{0}};
 
-consvar_t cv_renderhitbox = CVAR_INIT ("renderhitbox", "Off", CV_CHEAT, renderhitbox_cons_t, NULL);
+consvar_t cv_renderhitbox = CVAR_INIT ("renderhitbox", "Off", CV_CHEAT|CV_NETVAR, renderhitbox_cons_t, NULL);
 consvar_t cv_renderhitboxinterpolation = CVAR_INIT ("renderhitbox_interpolation", "On", CV_SAVE, CV_OnOff, NULL);
 consvar_t cv_renderhitboxgldepth = CVAR_INIT ("renderhitbox_gldepth", "Off", CV_SAVE, CV_OnOff, NULL);
 
-- 
GitLab


From b7b1e0b1e5259bc0d05a6c87ec339460039651a1 Mon Sep 17 00:00:00 2001
From: sphere <spherallic@gmail.com>
Date: Tue, 5 Sep 2023 17:24:03 +0000
Subject: [PATCH 377/518] Add toggle for instant retry in Record Attack

---
 src/d_netcmd.c | 2 ++
 src/g_game.c   | 6 ++++--
 src/g_game.h   | 2 ++
 src/hu_stuff.c | 2 +-
 4 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 33281e9924..3426c1f835 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -776,6 +776,8 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_showfocuslost);
 	CV_RegisterVar(&cv_pauseifunfocused);
 
+	CV_RegisterVar(&cv_instantretry);
+
 	// g_input.c
 	CV_RegisterVar(&cv_sideaxis);
 	CV_RegisterVar(&cv_sideaxis2);
diff --git a/src/g_game.c b/src/g_game.c
index 9780bcd4d2..a5f969243a 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -316,6 +316,8 @@ consvar_t cv_consolechat = CVAR_INIT ("chatmode", "Window", CV_SAVE, consolechat
 // Pause game upon window losing focus
 consvar_t cv_pauseifunfocused = CVAR_INIT ("pauseifunfocused", "Yes", CV_SAVE, CV_YesNo, NULL);
 
+consvar_t cv_instantretry = CVAR_INIT ("instantretry", "No", CV_SAVE, CV_YesNo, NULL);
+
 consvar_t cv_crosshair = CVAR_INIT ("crosshair", "Cross", CV_SAVE, crosshair_cons_t, NULL);
 consvar_t cv_crosshair2 = CVAR_INIT ("crosshair2", "Cross", CV_SAVE, crosshair_cons_t, NULL);
 consvar_t cv_invertmouse = CVAR_INIT ("invertmouse", "Off", CV_SAVE, CV_OnOff, NULL);
@@ -2173,9 +2175,9 @@ boolean G_Responder(event_t *ev)
 					if (menuactive || pausedelay < 0 || leveltime < 2)
 						return true;
 
-					if (pausedelay < 1+(NEWTICRATE/2))
+					if (!cv_instantretry.value && pausedelay < 1+(NEWTICRATE/2))
 						pausedelay = 1+(NEWTICRATE/2);
-					else if (++pausedelay > 1+(NEWTICRATE/2)+(NEWTICRATE/3))
+					else if (cv_instantretry.value || ++pausedelay > 1+(NEWTICRATE/2)+(NEWTICRATE/3))
 					{
 						G_SetModeAttackRetryFlag();
 						return true;
diff --git a/src/g_game.h b/src/g_game.h
index a8c285f79f..9873430b93 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -49,6 +49,8 @@ extern boolean promptactive;
 
 extern consvar_t cv_pauseifunfocused;
 
+extern consvar_t cv_instantretry;
+
 // used in game menu
 extern consvar_t cv_tutorialprompt;
 extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection, cv_compactscoreboard;
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 091e2b2fba..e223d32081 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -2025,7 +2025,7 @@ void HU_Drawer(void)
 		V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_YELLOWMAP | V_ALLOWLOWERCASE, resynch_text);
 	}
 
-	if (modeattacking && pausedelay > 0 && !pausebreakkey)
+	if (modeattacking && pausedelay > 0 && !(pausebreakkey || cv_instantretry.value))
 	{
 		INT32 strength = ((pausedelay - 1 - NEWTICRATE/2)*10)/(NEWTICRATE/3);
 		INT32 y = hudinfo[HUD_LIVES].y - 13;
-- 
GitLab


From 85fc55bfc1b0f14f22a966e7a83bce00acf2ba0b Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Tue, 5 Sep 2023 19:38:45 +0200
Subject: [PATCH 378/518] Prevent title input during negative finalecount

---
 src/g_game.c | 2 +-
 src/m_menu.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/g_game.c b/src/g_game.c
index fce9919f19..4140a91eba 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2080,7 +2080,7 @@ boolean G_Responder(event_t *ev)
 	if (gameaction == ga_nothing && !singledemo &&
 		((demoplayback && !modeattacking && !titledemo) || gamestate == GS_TITLESCREEN))
 	{
-		if (ev->type == ev_keydown && ev->key != 301 && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE && cv_tutorialprompt.value))
+		if (ev->type == ev_keydown && ev->key != 301 && !(gamestate == GS_TITLESCREEN && finalecount < (cv_tutorialprompt.value ? TICRATE : 0)))
 		{
 			M_StartControlPanel();
 			return true;
diff --git a/src/m_menu.c b/src/m_menu.c
index 81ff0f6795..f7b65910da 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3196,7 +3196,7 @@ boolean M_Responder(event_t *ev)
 	|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION || gamestate == GS_GAMEEND)
 		return false;
 
-	if (gamestate == GS_TITLESCREEN && finalecount < TICRATE && cv_tutorialprompt.value)
+	if (gamestate == GS_TITLESCREEN && finalecount < (cv_tutorialprompt.value ? TICRATE : 0))
 		return false;
 
 	if (CON_Ready() && gamestate != GS_WAITINGPLAYERS)
-- 
GitLab


From 49ee744ba17fa613c181fbc652dbc591959d1a47 Mon Sep 17 00:00:00 2001
From: Krabs <75001008+krabsisa@users.noreply.github.com>
Date: Wed, 6 Sep 2023 12:50:32 -0400
Subject: [PATCH 379/518] Update version number

---
 appveyor.yml         | 2 +-
 src/version.h        | 4 ++--
 src/win32/Srb2win.rc | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/appveyor.yml b/appveyor.yml
index 9770cb37df..9c39e3facc 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: 2.2.11.{branch}-{build}
+version: 2.2.12.{branch}-{build}
 os: MinGW
 
 environment:
diff --git a/src/version.h b/src/version.h
index 083c531343..c3ec0ad491 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,4 +1,4 @@
-#define SRB2VERSION "2.2.11"/* this must be the first line, for cmake !! */
+#define SRB2VERSION "2.2.12"/* this must be the first line, for cmake !! */
 
 // The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/members/?key=ms_admin ).
 // DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server.
@@ -9,7 +9,7 @@
 // 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.2.0 is not version "1".
-#define MODVERSION 52
+#define MODVERSION 53
 
 // Define this as a prerelease version suffix (pre#, RC#)
 //#define BETAVERSION "pre1"
diff --git a/src/win32/Srb2win.rc b/src/win32/Srb2win.rc
index 869c0e7d36..54687a8317 100644
--- a/src/win32/Srb2win.rc
+++ b/src/win32/Srb2win.rc
@@ -77,8 +77,8 @@ END
 #include "../doomdef.h" // Needed for version string
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,2,11,0
- PRODUCTVERSION 2,2,11,0
+ FILEVERSION 2,2,12,0
+ PRODUCTVERSION 2,2,12,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
-- 
GitLab


From 858f102ca4e82fa8af6a9caea72028c447857448 Mon Sep 17 00:00:00 2001
From: Krabs <75001008+krabsisa@users.noreply.github.com>
Date: Wed, 6 Sep 2023 12:50:51 -0400
Subject: [PATCH 380/518] Update hash for patch.pk3

---
 src/config.h.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/config.h.in b/src/config.h.in
index 0ca0f26e90..daa3857a32 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -43,7 +43,7 @@
 #define ASSET_HASH_ZONES_PK3  "1c8adf8d079ecb87d00081f158acf3c7"
 #define ASSET_HASH_PLAYER_DTA "2e7aaae8a6b1b77d90ffe7606ceadb6c"
 #ifdef USE_PATCH_DTA
-#define ASSET_HASH_PATCH_PK3  "2e69558bce3b9610624549a75e29e19b"
+#define ASSET_HASH_PATCH_PK3  "3c7b73f34af7e9a7bceb2d5260f76172"
 #endif
 
 #endif
-- 
GitLab


From 11a2fe86133a63ab5597a66529ee723714179048 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Wed, 6 Sep 2023 21:02:04 +0200
Subject: [PATCH 381/518] Expose skin.supername to Lua

---
 src/lua_skinlib.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c
index 13e0dd9871..041c5d5985 100644
--- a/src/lua_skinlib.c
+++ b/src/lua_skinlib.c
@@ -25,6 +25,7 @@ enum skin {
 	skin_flags,
 	skin_realname,
 	skin_hudname,
+	skin_supername,
 	skin_ability,
 	skin_ability2,
 	skin_thokitem,
@@ -63,6 +64,7 @@ static const char *const skin_opt[] = {
 	"flags",
 	"realname",
 	"hudname",
+	"supername",
 	"ability",
 	"ability2",
 	"thokitem",
@@ -126,6 +128,9 @@ static int skin_get(lua_State *L)
 	case skin_hudname:
 		lua_pushstring(L, skin->hudname);
 		break;
+	case skin_supername:
+		lua_pushstring(L, skin->supername);
+		break;
 	case skin_ability:
 		lua_pushinteger(L, skin->ability);
 		break;
-- 
GitLab


From 72662efc8d0aee01b31718f102dbdaf1b037870a Mon Sep 17 00:00:00 2001
From: SteelT <steeltitanium1@gmail.com>
Date: Fri, 8 Sep 2023 13:25:20 -0400
Subject: [PATCH 382/518] Replace CV_NETVAR with CV_NOTINNET for
 cv_renderhitbox

The CV_NETVAR flag being used for cvars not registered in a dedicated server context will completely mess with the internal netid to cvar linkage.
And I'm not really keen on registering a cvar purely for rendering on dedicated servers, so let's just prevent renderhitbox from being changed in netgames entirely.
---
 src/r_bbox.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/r_bbox.c b/src/r_bbox.c
index 6d468e47d9..6b4c4c4fb0 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -34,7 +34,7 @@ static CV_PossibleValue_t renderhitbox_cons_t[] = {
 	{RENDERHITBOX_RINGS, "Rings"},
 	{0}};
 
-consvar_t cv_renderhitbox = CVAR_INIT ("renderhitbox", "Off", CV_CHEAT|CV_NETVAR, renderhitbox_cons_t, NULL);
+consvar_t cv_renderhitbox = CVAR_INIT ("renderhitbox", "Off", CV_CHEAT|CV_NOTINNET, renderhitbox_cons_t, NULL);
 consvar_t cv_renderhitboxinterpolation = CVAR_INIT ("renderhitbox_interpolation", "On", CV_SAVE, CV_OnOff, NULL);
 consvar_t cv_renderhitboxgldepth = CVAR_INIT ("renderhitbox_gldepth", "Off", CV_SAVE, CV_OnOff, NULL);
 
-- 
GitLab


From 14eb589611a69f154f58a77a3f0f87a9c12f9d05 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Fri, 8 Sep 2023 17:26:42 -0300
Subject: [PATCH 383/518] Fix floorsprite rendering with 1x1 images

---
 src/r_splats.c | 54 ++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 46 insertions(+), 8 deletions(-)

diff --git a/src/r_splats.c b/src/r_splats.c
index 737b6d110a..6f2887aae6 100644
--- a/src/r_splats.c
+++ b/src/r_splats.c
@@ -314,7 +314,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
 	fixed_t planeheight = 0;
 	fixed_t step;
 
-	int spanfunctype = SPANDRAWFUNC_SPRITE;
+	int spanfunctype;
 
 	prepare_rastertab();
 
@@ -379,9 +379,12 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
 	ds_source = (UINT8 *)pSplat->pic;
 	ds_flatwidth = pSplat->width;
 	ds_flatheight = pSplat->height;
-	ds_powersoftwo = false;
 
-	if (R_CheckPowersOfTwo())
+	ds_powersoftwo = ds_solidcolor = false;
+
+	if (R_CheckSolidColorFlat())
+		ds_solidcolor = true;
+	else if (R_CheckPowersOfTwo())
 	{
 		R_SetFlatVars(ds_flatwidth * ds_flatheight);
 		ds_powersoftwo = true;
@@ -392,7 +395,6 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
 		R_SetTiltedSpan(0);
 		R_SetScaledSlopePlane(pSplat->slope, vis->viewpoint.x, vis->viewpoint.y, vis->viewpoint.z, pSplat->xscale, pSplat->yscale, -pSplat->verts[0].x, pSplat->verts[0].y, vis->viewpoint.angle, pSplat->angle);
 		R_CalculateSlopeVectors();
-		spanfunctype = SPANDRAWFUNC_TILTEDSPRITE;
 	}
 	else
 	{
@@ -429,19 +431,55 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
 			ds_colormap = &vis->extra_colormap->colormap[ds_colormap - colormaps];
 	}
 
-	if (vis->transmap)
+	ds_transmap = vis->transmap;
+
+	// Determine which R_DrawWhatever to use
+
+	// Solid color
+	if (ds_solidcolor)
 	{
-		ds_transmap = vis->transmap;
+		UINT16 px = *(UINT16 *)ds_source;
+
+		// Uh, it's not visible.
+		if (!(px & 0xFF00))
+			return;
 
+		// Pixel color is contained in the lower 8 bits (upper 8 are the opacity), so advance the pointer
+		ds_source++;
+
+		if (pSplat->slope)
+		{
+			if (ds_transmap)
+				spanfunctype = SPANDRAWFUNC_TILTEDTRANSSOLID;
+			else
+				spanfunctype = SPANDRAWFUNC_TILTEDSOLID;
+		}
+		else
+		{
+			if (ds_transmap)
+				spanfunctype = SPANDRAWFUNC_TRANSSOLID;
+			else
+				spanfunctype = SPANDRAWFUNC_SOLID;
+		}
+	}
+	// Transparent
+	else if (ds_transmap)
+	{
 		if (pSplat->slope)
 			spanfunctype = SPANDRAWFUNC_TILTEDTRANSSPRITE;
 		else
 			spanfunctype = SPANDRAWFUNC_TRANSSPRITE;
 	}
+	// Opaque
 	else
-		ds_transmap = NULL;
+	{
+		if (pSplat->slope)
+			spanfunctype = SPANDRAWFUNC_TILTEDSPRITE;
+		else
+			spanfunctype = SPANDRAWFUNC_SPRITE;
+	}
 
-	if (ds_powersoftwo)
+	if (ds_powersoftwo || ds_solidcolor)
 		spanfunc = spanfuncs[spanfunctype];
 	else
 		spanfunc = spanfuncs_npo2[spanfunctype];
-- 
GitLab


From 27021d29218310fb9d508ba6f30e813d10197b58 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Fri, 8 Sep 2023 17:43:55 -0300
Subject: [PATCH 384/518] Optimize

---
 src/r_splats.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/r_splats.c b/src/r_splats.c
index 6f2887aae6..0b482d7988 100644
--- a/src/r_splats.c
+++ b/src/r_splats.c
@@ -316,8 +316,6 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
 
 	int spanfunctype;
 
-	prepare_rastertab();
-
 #define RASTERPARAMS(vnum1, vnum2, tv1, tv2, tc, dir) \
     x1 = verts[vnum1].x; \
     ry1 = verts[vnum1].y; \
@@ -367,15 +365,6 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
     if (ry1 > maxy) \
         maxy = ry1;
 
-	// do segment a -> top of texture
-	RASTERPARAMS(3,2,0,pSplat->width-1,0,0);
-	// do segment b -> right side of texture
-	RASTERPARAMS(2,1,0,pSplat->width-1,pSplat->height-1,0);
-	// do segment c -> bottom of texture
-	RASTERPARAMS(1,0,pSplat->width-1,0,pSplat->height-1,0);
-	// do segment d -> left side of texture
-	RASTERPARAMS(0,3,pSplat->width-1,0,0,1);
-
 	ds_source = (UINT8 *)pSplat->pic;
 	ds_flatwidth = pSplat->width;
 	ds_flatheight = pSplat->height;
@@ -396,7 +385,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
 		R_SetScaledSlopePlane(pSplat->slope, vis->viewpoint.x, vis->viewpoint.y, vis->viewpoint.z, pSplat->xscale, pSplat->yscale, -pSplat->verts[0].x, pSplat->verts[0].y, vis->viewpoint.angle, pSplat->angle);
 		R_CalculateSlopeVectors();
 	}
-	else
+	else if (!ds_solidcolor)
 	{
 		planeheight = abs(pSplat->z - vis->viewpoint.z);
 
@@ -484,6 +473,17 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
 	else
 		spanfunc = spanfuncs_npo2[spanfunctype];
 
+	prepare_rastertab();
+
+	// do segment a -> top of texture
+	RASTERPARAMS(3,2,0,pSplat->width-1,0,0);
+	// do segment b -> right side of texture
+	RASTERPARAMS(2,1,0,pSplat->width-1,pSplat->height-1,0);
+	// do segment c -> bottom of texture
+	RASTERPARAMS(1,0,pSplat->width-1,0,pSplat->height-1,0);
+	// do segment d -> left side of texture
+	RASTERPARAMS(0,3,pSplat->width-1,0,0,1);
+
 	if (maxy >= vid.height)
 		maxy = vid.height-1;
 
@@ -538,7 +538,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
 		if (x2 < x1)
 			continue;
 
-		if (!pSplat->slope)
+		if (!ds_solidcolor && !pSplat->slope)
 		{
 			fixed_t xstep, ystep;
 			fixed_t distance, span;
@@ -587,7 +587,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
 		rastertab[y].maxx = INT32_MIN;
 	}
 
-	if (pSplat->angle && !pSplat->slope)
+	if (!ds_solidcolor && pSplat->angle && !pSplat->slope)
 		memset(cachedheight, 0, sizeof(cachedheight));
 }
 
-- 
GitLab


From 82568f81fc1b98b5673caf59f3259f6dd7488162 Mon Sep 17 00:00:00 2001
From: katsy <katmint@live.com>
Date: Fri, 8 Sep 2023 17:46:13 -0500
Subject: [PATCH 385/518] Disable renderhitbox in multiplaye altogether

---
 src/r_bbox.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/r_bbox.c b/src/r_bbox.c
index 6b4c4c4fb0..cf417ec376 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -268,6 +268,9 @@ boolean R_ThingBoundingBoxVisible(mobj_t *thing)
 {
 	INT32 cvmode = cv_renderhitbox.value;
 
+	if (multiplayer) // No hitboxes in multiplayer to avoid cheating
+		return false;
+
 	// Do not render bbox for these
 	switch (thing->type)
 	{
-- 
GitLab


From a4a3b5b0944720a536a94c9d471b64c822cdac61 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sat, 9 Sep 2023 01:16:28 +0200
Subject: [PATCH 386/518] 2.2.13

---
 appveyor.yml         | 2 +-
 src/config.h.in      | 2 ++
 src/version.h        | 4 ++--
 src/win32/Srb2win.rc | 4 ++--
 4 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/appveyor.yml b/appveyor.yml
index 9c39e3facc..63d801b734 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: 2.2.12.{branch}-{build}
+version: 2.2.13.{branch}-{build}
 os: MinGW
 
 environment:
diff --git a/src/config.h.in b/src/config.h.in
index daa3857a32..6d49a69893 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -38,6 +38,8 @@
  * Last updated 2021 / 05 / 06 - v2.2.9 - patch.pk3 & zones.pk3
  * Last updated 2022 / 03 / 06 - v2.2.10 - main assets
  * Last updated 2023 / 05 / 02 - v2.2.11 - patch.pk3 & zones.pk3
+ * Last updated 2023 / 09 / 06 - v2.2.12 - patch.pk3
+ * Last updated 2023 / 09 / 09 - v2.2.13 - none
  */
 #define ASSET_HASH_SRB2_PK3   "ad911f29a28a18968ee5b2d11c2acb39"
 #define ASSET_HASH_ZONES_PK3  "1c8adf8d079ecb87d00081f158acf3c7"
diff --git a/src/version.h b/src/version.h
index c3ec0ad491..3242cad672 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,4 +1,4 @@
-#define SRB2VERSION "2.2.12"/* this must be the first line, for cmake !! */
+#define SRB2VERSION "2.2.13"/* this must be the first line, for cmake !! */
 
 // The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/members/?key=ms_admin ).
 // DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server.
@@ -9,7 +9,7 @@
 // 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.2.0 is not version "1".
-#define MODVERSION 53
+#define MODVERSION 54
 
 // Define this as a prerelease version suffix (pre#, RC#)
 //#define BETAVERSION "pre1"
diff --git a/src/win32/Srb2win.rc b/src/win32/Srb2win.rc
index 54687a8317..b699007463 100644
--- a/src/win32/Srb2win.rc
+++ b/src/win32/Srb2win.rc
@@ -77,8 +77,8 @@ END
 #include "../doomdef.h" // Needed for version string
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,2,12,0
- PRODUCTVERSION 2,2,12,0
+ FILEVERSION 2,2,13,0
+ PRODUCTVERSION 2,2,13,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
-- 
GitLab


From 9b5cfe856c38846c965c535a07c8810f4fb0f3bf Mon Sep 17 00:00:00 2001
From: MIDIMan <miditheman2.0@gmail.com>
Date: Fri, 8 Sep 2023 22:03:25 -0400
Subject: [PATCH 387/518] Add a mapheader check to M_CampaignWarpIsCheat

---
 src/m_cond.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/m_cond.c b/src/m_cond.c
index 6c87ebf6e5..706a1b5106 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -509,7 +509,7 @@ UINT8 M_CampaignWarpIsCheat(INT32 gt, INT32 mapnum, gamedata_t *data)
 		return true;
 	}
 
-	if (mapheaderinfo[mapnum-1]->menuflags & LF2_HIDEINMENU)
+	if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->menuflags & LF2_HIDEINMENU)
 	{
 		// You're never allowed to warp to this level.
 		return true;
-- 
GitLab


From d5703be3e7482a312cbfa8f31339e7d1a098ba20 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 8 Sep 2023 22:48:10 -0400
Subject: [PATCH 388/518] CircleCI: try using new cimg base image

---
 .circleci/config.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 3faca372cd..7ce2ba5be9 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -3,7 +3,7 @@ jobs:
   build:
     working_directory: /root/SRB2
     docker:
-      - image: debian:stretch
+      - image: cimg/base:current
         environment:
           CC: ccache gcc -m32
           PKG_CONFIG_LIBDIR: /usr/lib/i386-linux-gnu/pkgconfig
-- 
GitLab


From dcdb09769fab9cd17823668e71ffdd0aa92322ee Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 8 Sep 2023 23:20:11 -0400
Subject: [PATCH 389/518] CircleCI: try to build binary as circleci user

---
 .circleci/config.yml | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 7ce2ba5be9..4c78f974c4 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,7 +1,7 @@
 version: 2
 jobs:
   build:
-    working_directory: /root/SRB2
+    working_directory: /home/circleci/SRB2
     docker:
       - image: cimg/base:current
         environment:
@@ -25,39 +25,39 @@ jobs:
     steps:
       - run:
           name: Add i386 arch
-          command: dpkg --add-architecture i386
+          command: sudo dpkg --add-architecture i386
       - run:
           name: Add STJr PPA
           command: |
-            apt-get -qq update
-            apt-get -qq -y install dirmngr
-            apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0B1702D71499D9C25F986507F240F4449D3B0EC6
-            echo "deb http://ppa.launchpad.net/stjr/srb2/ubuntu trusty main" >> /etc/apt/sources.list
+            sudo apt-get -qq update
+            sudo apt-get -qq -y install dirmngr
+            sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0B1702D71499D9C25F986507F240F4449D3B0EC6
+            echo "deb http://ppa.launchpad.net/stjr/srb2/ubuntu trusty main" | sudo tee -a /etc/apt/sources.list
       - run:
           name: Make APT cache folder
-          command: mkdir -p /root/.cache/apt/archives/partial
+          command: mkdir -p /home/circleci/.cache/apt/archives/partial
       - run:
           name: Make APT cache usage by _apt
-          command: chown -Rv _apt:root /root/.cache/apt/archives/partial
+          command: sudo chown -Rv _apt:root /home/circleci/.cache/apt/archives/partial
       - run:
           name: Update APT listing
-          command: apt-get -qq update
+          command: sudo apt-get -qq update
       - run:
           name: Support S3 upload
-          command: apt-get -qq -y install ca-certificates
+          command: sudo apt-get -qq -y install ca-certificates
       - restore_cache:
           keys:
             - v1-SRB2-APT
       - run:
           name: Install SDK
-          command: apt-get -o Dir::Cache="/root/.cache/apt" -qq -y --no-install-recommends install git build-essential libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 libcurl4-openssl-dev:i386 libopenmpt-dev:i386 gettext ccache wget gcc-multilib upx openssh-client
+          command: sudo apt-get -o Dir::Cache="/home/circleci/.cache/apt" -qq -y --no-install-recommends install git build-essential libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 libcurl4-openssl-dev:i386 libopenmpt-dev:i386 gettext ccache wget gcc-multilib upx openssh-client
       - run:
           name: make md5sum
-          command: find /root/.cache/apt/archives -type f -print0 | sort -z | xargs -r0 md5sum > /root/.cache/apt_archives.md5
+          command: find /home/circleci/.cache/apt/archives -type f -print0 | sort -z | sudo xargs -r0 md5sum > /home/circleci/.cache/apt_archives.md5
       - save_cache:
-          key: v1-SRB2-APT-{{ checksum "/root/.cache/apt_archives.md5" }}
+          key: v1-SRB2-APT-{{ checksum "/home/circleci/.cache/apt_archives.md5" }}
           paths:
-            - /root/.cache/apt
+            - /home/circleci/.cache/apt
       - checkout
       - run:
           name: Compile without network support
@@ -78,9 +78,9 @@ jobs:
           name: Compile
           command: make -C src LINUX=1 ERRORMODE=1 -k -j4
       - store_artifacts:
-          path: /root/SRB2/bin/
+          path: /home/circleci/SRB2/bin/
           destination: bin
       - save_cache:
           key: v1-SRB2-{{ .Branch }}-{{ checksum "make/linux/SDL.deps" }}
           paths:
-            - /root/.ccache
+            - /home/circleci/.ccache
-- 
GitLab


From 9851c8775611799d603aac3357aa6d829d64c503 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 9 Sep 2023 09:36:26 -0400
Subject: [PATCH 390/518] CircieCI: remove curl amd64 SDK so we can install the
 i386 version

---
 .circleci/config.yml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 4c78f974c4..e202443673 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -49,7 +49,10 @@ jobs:
           keys:
             - v1-SRB2-APT
       - run:
-          name: Install SDK
+         name: Uninstall amd64 SDK
+         command: sudo apt-get -o Dir::Cache="/home/circleci/.cache/apt" -qq -y --no-install-recommends remove libcurl4-openssl-dev:amd64
+      - run:
+          name: Install i386 SDK
           command: sudo apt-get -o Dir::Cache="/home/circleci/.cache/apt" -qq -y --no-install-recommends install git build-essential libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 libcurl4-openssl-dev:i386 libopenmpt-dev:i386 gettext ccache wget gcc-multilib upx openssh-client
       - run:
           name: make md5sum
-- 
GitLab


From 5037483a1ea307a019a085b5c784d8eaa0fc5d61 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 9 Sep 2023 15:48:14 -0400
Subject: [PATCH 391/518] CircleCI: look for files as root

---
 .circleci/config.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index e202443673..bad8162866 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -56,7 +56,7 @@ jobs:
           command: sudo apt-get -o Dir::Cache="/home/circleci/.cache/apt" -qq -y --no-install-recommends install git build-essential libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 libcurl4-openssl-dev:i386 libopenmpt-dev:i386 gettext ccache wget gcc-multilib upx openssh-client
       - run:
           name: make md5sum
-          command: find /home/circleci/.cache/apt/archives -type f -print0 | sort -z | sudo xargs -r0 md5sum > /home/circleci/.cache/apt_archives.md5
+          command: sudo find /home/circleci/.cache/apt/archives -type f -print0 | sort -z | sudo xargs -r0 md5sum > /home/circleci/.cache/apt_archives.md5
       - save_cache:
           key: v1-SRB2-APT-{{ checksum "/home/circleci/.cache/apt_archives.md5" }}
           paths:
-- 
GitLab


From 4d217014152bd39d76702035c32e9529d64cd1b0 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 9 Sep 2023 16:03:46 -0400
Subject: [PATCH 392/518] CircleCI: compile for GCC 8,1+

---
 .circleci/config.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index bad8162866..b3b97363ee 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -11,7 +11,7 @@ jobs:
           LIBGME_LDFLAGS: -lgme
           CCACHE_COMPRESS: true
           WFLAGS: -Wno-unsuffixed-float-constants
-          GCC49: true
+          GCC81: true
       #- image: ubuntu:trusty
       #  environment:
       #    CC: ccache gcc -m32
-- 
GitLab


From 05223fbc6de63621ce4503dd707ccbbfff39124f Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 9 Sep 2023 16:10:42 -0400
Subject: [PATCH 393/518] backtrace: store result of write() in a junk var

---
 src/sdl/i_system.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 902194f4f3..7b64d6fff8 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -259,10 +259,10 @@ UINT8 keyboard_started = false;
 
 #ifdef UNIXBACKTRACE
 #define STDERR_WRITE(string) if (fd != -1) I_OutputMsg("%s", string)
-#define CRASHLOG_WRITE(string) if (fd != -1) write(fd, string, strlen(string))
+#define CRASHLOG_WRITE(string) if (fd != -1) junk = write(fd, string, strlen(string))
 #define CRASHLOG_STDERR_WRITE(string) \
 	if (fd != -1)\
-		write(fd, string, strlen(string));\
+		junk = write(fd, string, strlen(string));\
 	I_OutputMsg("%s", string)
 
 static void write_backtrace(INT32 signal)
@@ -271,6 +271,7 @@ static void write_backtrace(INT32 signal)
 	size_t size;
 	time_t rawtime;
 	struct tm timeinfo;
+	ssize_t junk;
 
 	enum { BT_SIZE = 1024, STR_SIZE = 32 };
 	void *array[BT_SIZE];
-- 
GitLab


From 64eb1b0bffc836abb148eef16ee2254340d1d437 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 9 Sep 2023 16:21:04 -0400
Subject: [PATCH 394/518] backtrace: do not care for junk var

---
 src/sdl/i_system.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 7b64d6fff8..23b82da739 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -315,7 +315,7 @@ static void write_backtrace(INT32 signal)
 	backtrace_symbols_fd(array, size, STDERR_FILENO);
 
 	CRASHLOG_WRITE("\n"); // Write another newline to the log so it looks nice :)
-
+	(void)junk;
 	close(fd);
 }
 #undef STDERR_WRITE
-- 
GitLab


From 06d4d71b4146209609ee40d9fa16377c7fdfd3de Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Tue, 12 Sep 2023 19:52:24 +0200
Subject: [PATCH 395/518] Fix missing includes for Linux platforms

---
 src/netcode/client_connection.c | 3 +++
 src/netcode/gamestate.c         | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c
index 8155d1b33f..a81ac7e97f 100644
--- a/src/netcode/client_connection.c
+++ b/src/netcode/client_connection.c
@@ -32,6 +32,9 @@
 #include "../z_zone.h"
 #include "../doomtype.h"
 #include "../doomstat.h"
+#if defined (__GNUC__) || defined (__unix__)
+#include <unistd.h>
+#endif
 
 cl_mode_t cl_mode = CL_SEARCHING;
 static UINT16 cl_lastcheckedfilecount = 0;	// used for full file list
diff --git a/src/netcode/gamestate.c b/src/netcode/gamestate.c
index 9c243ea734..005e81ba9e 100644
--- a/src/netcode/gamestate.c
+++ b/src/netcode/gamestate.c
@@ -34,6 +34,9 @@
 #include "../r_main.h"
 #include "../tables.h"
 #include "../z_zone.h"
+#if defined (__GNUC__) || defined (__unix__)
+#include <unistd.h>
+#endif
 
 #define SAVEGAMESIZE (768*1024)
 
-- 
GitLab


From ebca345a44834cc845df21b20a8ebbd5ba487362 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 12 Sep 2023 19:32:15 -0300
Subject: [PATCH 396/518] Check if in a game

---
 src/d_netcmd.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 5c93601edc..d0f6e1a751 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -4709,12 +4709,14 @@ static void ForceSkin_OnChange(void)
 	if (cv_forceskin.value < 0)
 	{
 		CONS_Printf("The server has lifted the forced skin restrictions.\n");
-		D_SendPlayerConfig();
+		if (Playing())
+			D_SendPlayerConfig();
 	}
 	else
 	{
 		CONS_Printf("The server is restricting all players to skin \"%s\".\n",skins[cv_forceskin.value].name);
-		ForceAllSkins(cv_forceskin.value);
+		if (Playing())
+			ForceAllSkins(cv_forceskin.value);
 	}
 }
 
-- 
GitLab


From 8558d123ee0511e47656307c0759370986b654ea Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Tue, 12 Sep 2023 19:32:47 -0400
Subject: [PATCH 397/518] SDL: add version check for SDL_OPenURL()

---
 src/sdl/i_system.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 23b82da739..be46cd8044 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -406,8 +406,10 @@ static void I_ReportSignal(int num, int coredumped)
 
 	SDL_ShowMessageBox(&messageboxdata, &buttonid);
 
+#if SDL_VERSION_ATLEAST(2,0,14)
 	if (buttonid == 1)
 		SDL_OpenURL("https://www.srb2.org/discord");
+#endif
 }
 
 #ifndef NEWSIGNALHANDLER
-- 
GitLab


From 65547ae036def724c6ba8c5390dba6abefe2cba4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Thu, 14 Sep 2023 18:20:00 +0200
Subject: [PATCH 398/518] Fix memory leak when other players are riding a
 minecart

---
 src/p_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_user.c b/src/p_user.c
index a443b73238..2e95a81972 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -11159,7 +11159,7 @@ static void P_MinecartThink(player_t *player)
 					detright->drawonlyforplayer = player;
 				}
 				else
-					P_RemoveMobj(detleft);
+					P_RemoveMobj(detright);
 			}
 		}
 		else
-- 
GitLab


From 8f1618018e4fbd6a0ec5f3ce5c6246c3e5e0f3b4 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Fri, 15 Sep 2023 19:12:55 +0200
Subject: [PATCH 399/518] Fix free

---
 src/snake.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/snake.c b/src/snake.c
index 6482759ed4..2349d5fdbf 100644
--- a/src/snake.c
+++ b/src/snake.c
@@ -572,7 +572,7 @@ void Snake_Free(void **opaque)
 {
 	if (*opaque)
 	{
-		free(opaque);
+		free(*opaque);
 		*opaque = NULL;
 	}
 }
-- 
GitLab


From 68e25737c05818eb14f522b326973268806a1e84 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Mon, 18 Sep 2023 18:51:11 -0300
Subject: [PATCH 400/518] Fix minor spelling mistake

---
 src/netcode/client_connection.c | 2 +-
 src/netcode/gamestate.c         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c
index a81ac7e97f..907021e7da 100644
--- a/src/netcode/client_connection.c
+++ b/src/netcode/client_connection.c
@@ -572,7 +572,7 @@ static boolean CL_FinishedFileList(void)
 			CL_Reset();
 			D_StartTitle();
 			M_StartMessage(M_GetText(
-				"An error occured when trying to\n"
+				"An error occurred when trying to\n"
 				"download missing addons.\n"
 				"(This is almost always a problem\n"
 				"with the server, not your game.)\n\n"
diff --git a/src/netcode/gamestate.c b/src/netcode/gamestate.c
index 005e81ba9e..f36347c6d8 100644
--- a/src/netcode/gamestate.c
+++ b/src/netcode/gamestate.c
@@ -284,7 +284,7 @@ void Command_ResendGamestate(void)
 	netbuffer->packettype = PT_WILLRESENDGAMESTATE;
 	if (!HSendPacket(playernode[playernum], true, 0, 0))
 	{
-		CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n"));
+		CONS_Alert(CONS_ERROR, M_GetText("A problem occurred, please try again.\n"));
 		return;
 	}
 }
-- 
GitLab


From cfedee9dc986faee3a70e374988a84feb921fb9b Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Tue, 19 Sep 2023 16:08:06 +0200
Subject: [PATCH 401/518] Fix CMake compiling

---
 src/Android.mk                       |   7 +-
 src/CMakeLists.txt                   |   9 +-
 src/netcode/CMakeLists.txt           |  15 +++
 src/sdl/Srb2SDL-vc10.vcxproj         |  44 ++++++---
 src/sdl/Srb2SDL-vc10.vcxproj.filters | 138 +++++++++++++++++++--------
 5 files changed, 147 insertions(+), 66 deletions(-)
 create mode 100644 src/netcode/CMakeLists.txt

diff --git a/src/Android.mk b/src/Android.mk
index 035d488877..235c4b81e5 100644
--- a/src/Android.mk
+++ b/src/Android.mk
@@ -8,11 +8,7 @@ LOCAL_SRC_FILES :=      am_map.c \
                         command.c \
                         comptime.c \
                         console.c \
-                        d_clisrv.c \
                         d_main.c \
-                        d_net.c \
-                        d_netcmd.c \
-                        d_netfil.c \
                         dehacked.c \
                         f_finale.c \
                         f_wipe.c \
@@ -20,7 +16,6 @@ LOCAL_SRC_FILES :=      am_map.c \
                         g_game.c \
                         g_input.c \
                         hu_stuff.c \
-                        i_tcp.c \
                         info.c \
                         lzf.c \
                         m_argv.c \
@@ -32,7 +27,6 @@ LOCAL_SRC_FILES :=      am_map.c \
                         m_queue.c \
                         m_random.c \
                         md5.c \
-                        mserv.c \
                         p_ceilng.c \
                         p_enemy.c \
                         p_fab.c \
@@ -61,6 +55,7 @@ LOCAL_SRC_FILES :=      am_map.c \
                         r_things.c \
                         s_sound.c \
                         screen.c \
+                        snake.c \
                         sounds.c \
                         st_stuff.c \
                         string.c \
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b926b3b7a3..22c1def275 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -6,10 +6,6 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
 	config.h.in
 	string.c
 	d_main.c
-	d_clisrv.c
-	d_net.c
-	d_netfil.c
-	d_netcmd.c
 	dehacked.c
 	deh_soc.c
 	deh_lua.c
@@ -83,12 +79,10 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
 	sounds.c
 	w_wad.c
 	filesrch.c
-	mserv.c
-	http-mserv.c
-	i_tcp.c
 	lzf.c
 	b_bot.c
 	u_list.c
+	snake.c
 	lua_script.c
 	lua_baselib.c
 	lua_mathlib.c
@@ -149,6 +143,7 @@ set(SRB2_CONFIG_DEV_BUILD OFF CACHE BOOL
 	"Compile a development build of SRB2.")
 
 add_subdirectory(blua)
+add_subdirectory(netcode)
 
 # OS macros
 if (UNIX)
diff --git a/src/netcode/CMakeLists.txt b/src/netcode/CMakeLists.txt
new file mode 100644
index 0000000000..69b976d17e
--- /dev/null
+++ b/src/netcode/CMakeLists.txt
@@ -0,0 +1,15 @@
+target_sources(SRB2SDL2 PRIVATE
+	d_clisrv.c
+	server_connection.c
+	client_connection.c
+	tic_command.c
+	net_command.c
+	gamestate.c
+	commands.c
+	d_net.c
+	d_netcmd.c
+	d_netfil.c
+	http-mserv.c
+	i_tcp.c
+	mserv.c
+)
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index 0b95cd0b2e..9b51cfb809 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -262,7 +262,6 @@
     <ClInclude Include="..\u_list.h" />
     <ClInclude Include="..\hu_stuff.h" />
     <ClInclude Include="..\info.h" />
-    <ClInclude Include="..\i_addrinfo.h" />
     <ClInclude Include="..\i_joy.h" />
     <ClInclude Include="..\i_net.h" />
     <ClInclude Include="..\i_sound.h" />
@@ -297,6 +296,21 @@
     <ClInclude Include="..\m_queue.h" />
     <ClInclude Include="..\m_random.h" />
     <ClInclude Include="..\m_swap.h" />
+    <ClInclude Include="..\netcode\client_connection.h" />
+    <ClInclude Include="..\netcode\commands.h" />
+    <ClInclude Include="..\netcode\d_clisrv.h" />
+    <ClInclude Include="..\netcode\d_net.h" />
+    <ClInclude Include="..\netcode\d_netcmd.h" />
+    <ClInclude Include="..\netcode\d_netfil.h" />
+    <ClInclude Include="..\netcode\gamestate.h" />
+    <ClInclude Include="..\netcode\i_addrinfo.h" />
+    <ClInclude Include="..\netcode\i_net.h" />
+    <ClInclude Include="..\netcode\i_tcp.h" />
+    <ClInclude Include="..\netcode\mserv.h" />
+    <ClInclude Include="..\netcode\net_command.h" />
+    <ClInclude Include="..\netcode\protocol.h" />
+    <ClInclude Include="..\netcode\server_connection.h" />
+    <ClInclude Include="..\netcode\tic_command.h" />
     <ClInclude Include="..\p5prof.h" />
     <ClInclude Include="..\p_haptic.h" />
     <ClInclude Include="..\p_local.h" />
@@ -329,6 +343,7 @@
     <ClInclude Include="..\r_textures.h" />
     <ClInclude Include="..\r_things.h" />
     <ClInclude Include="..\screen.h" />
+    <ClInclude Include="..\snake.h" />
     <ClInclude Include="..\sounds.h" />
     <ClInclude Include="..\st_stuff.h" />
     <ClInclude Include="..\s_sound.h" />
@@ -400,11 +415,7 @@
     <ClCompile Include="..\deh_soc.c" />
     <ClCompile Include="..\deh_lua.c" />
     <ClCompile Include="..\deh_tables.c" />
-    <ClCompile Include="..\d_clisrv.c" />
     <ClCompile Include="..\d_main.c" />
-    <ClCompile Include="..\d_net.c" />
-    <ClCompile Include="..\d_netcmd.c" />
-    <ClCompile Include="..\d_netfil.c" />
     <ClCompile Include="..\filesrch.c" />
     <ClCompile Include="..\f_finale.c" />
     <ClCompile Include="..\f_wipe.c" />
@@ -427,10 +438,6 @@
     <ClCompile Include="..\u_list.c" />
     <ClCompile Include="..\hu_stuff.c" />
     <ClCompile Include="..\info.c" />
-    <ClCompile Include="..\i_addrinfo.c">
-      <ExcludedFromBuild>true</ExcludedFromBuild>
-    </ClCompile>
-    <ClCompile Include="..\i_tcp.c" />
     <ClCompile Include="..\i_time.c" />
     <ClCompile Include="..\lua_baselib.c" />
     <ClCompile Include="..\lua_blockmaplib.c" />
@@ -451,8 +458,6 @@
     <ClCompile Include="..\lua_thinkerlib.c" />
     <ClCompile Include="..\lzf.c" />
     <ClCompile Include="..\md5.c" />
-    <ClCompile Include="..\mserv.c" />
-    <ClCompile Include="..\http-mserv.c" />
     <ClCompile Include="..\m_aatree.c" />
     <ClCompile Include="..\m_anigif.c" />
     <ClCompile Include="..\m_argv.c" />
@@ -466,6 +471,22 @@
     <ClCompile Include="..\m_perfstats.c" />
     <ClCompile Include="..\m_queue.c" />
     <ClCompile Include="..\m_random.c" />
+    <ClCompile Include="..\netcode\client_connection.c" />
+    <ClCompile Include="..\netcode\commands.c" />
+    <ClCompile Include="..\netcode\d_clisrv.c" />
+    <ClCompile Include="..\netcode\d_net.c" />
+    <ClCompile Include="..\netcode\d_netcmd.c" />
+    <ClCompile Include="..\netcode\d_netfil.c" />
+    <ClCompile Include="..\netcode\gamestate.c" />
+    <ClCompile Include="..\netcode\http-mserv.c" />
+    <ClCompile Include="..\netcode\i_addrinfo.c">
+      <ExcludedFromBuild>true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="..\netcode\i_tcp.c" />
+    <ClCompile Include="..\netcode\mserv.c" />
+    <ClCompile Include="..\netcode\net_command.c" />
+    <ClCompile Include="..\netcode\server_connection.c" />
+    <ClCompile Include="..\netcode\tic_command.c" />
     <ClCompile Include="..\p_ceilng.c" />
     <ClCompile Include="..\p_enemy.c" />
     <ClCompile Include="..\p_floor.c" />
@@ -510,6 +531,7 @@
     <ClCompile Include="..\r_textures.c" />
     <ClCompile Include="..\r_things.c" />
     <ClCompile Include="..\screen.c" />
+    <ClCompile Include="..\snake.c" />
     <ClCompile Include="..\sounds.c" />
     <ClCompile Include="..\string.c" />
     <ClCompile Include="..\st_stuff.c" />
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index eb37ce9da6..96501b2160 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -156,24 +156,12 @@
     <ClInclude Include="..\doomtype.h">
       <Filter>D_Doom</Filter>
     </ClInclude>
-    <ClInclude Include="..\d_clisrv.h">
-      <Filter>D_Doom</Filter>
-    </ClInclude>
     <ClInclude Include="..\d_event.h">
       <Filter>D_Doom</Filter>
     </ClInclude>
     <ClInclude Include="..\d_main.h">
       <Filter>D_Doom</Filter>
     </ClInclude>
-    <ClInclude Include="..\d_net.h">
-      <Filter>D_Doom</Filter>
-    </ClInclude>
-    <ClInclude Include="..\d_netcmd.h">
-      <Filter>D_Doom</Filter>
-    </ClInclude>
-    <ClInclude Include="..\d_netfil.h">
-      <Filter>D_Doom</Filter>
-    </ClInclude>
     <ClInclude Include="..\d_player.h">
       <Filter>D_Doom</Filter>
     </ClInclude>
@@ -279,9 +267,6 @@
     <ClInclude Include="..\filesrch.h">
       <Filter>I_Interface</Filter>
     </ClInclude>
-    <ClInclude Include="..\i_addrinfo.h">
-      <Filter>I_Interface</Filter>
-    </ClInclude>
     <ClInclude Include="..\i_joy.h">
       <Filter>I_Interface</Filter>
     </ClInclude>
@@ -306,12 +291,6 @@
     <ClInclude Include="..\keys.h">
       <Filter>I_Interface</Filter>
     </ClInclude>
-    <ClInclude Include="..\mserv.h">
-      <Filter>I_Interface</Filter>
-    </ClInclude>
-    <ClInclude Include="..\http-mserv.h">
-      <Filter>I_Interface</Filter>
-    </ClInclude>
     <ClInclude Include="..\lua_hook.h">
       <Filter>LUA</Filter>
     </ClInclude>
@@ -372,6 +351,54 @@
     <ClInclude Include="..\m_swap.h">
       <Filter>M_Misc</Filter>
     </ClInclude>
+    <ClInclude Include="..\netcode\client_connection.h">
+      <Filter>D_Doom</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\commands.h">
+      <Filter>D_Doom</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\d_clisrv.h">
+      <Filter>D_Doom</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\d_net.h">
+      <Filter>D_Doom</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\d_netcmd.h">
+      <Filter>D_Doom</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\d_netfil.h">
+      <Filter>D_Doom</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\gamestate.h">
+      <Filter>D_Doom</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\http-mserv.h">
+      <Filter>I_Interface</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\i_addrinfo.h">
+      <Filter>I_Interface</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\i_net.h">
+      <Filter>I_Interface</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\i_tcp.h">
+      <Filter>I_Interface</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\mserv.h">
+      <Filter>I_Interface</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\net_command.h">
+      <Filter>D_Doom</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\protocol.h">
+      <Filter>D_Doom</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\server_connection.h">
+      <Filter>D_Doom</Filter>
+    </ClInclude>
+    <ClInclude Include="..\netcode\tic_command.h">
+      <Filter>D_Doom</Filter>
+    </ClInclude>
     <ClInclude Include="..\comptime.h">
       <Filter>O_Other</Filter>
     </ClInclude>
@@ -465,6 +492,9 @@
     <ClInclude Include="..\v_video.h">
       <Filter>R_Rend</Filter>
     </ClInclude>
+    <ClInclude Include="..\snake.h">
+      <Filter>M_Misc</Filter>
+    </ClInclude>
     <ClInclude Include="..\sounds.h">
       <Filter>S_Sounds</Filter>
     </ClInclude>
@@ -639,21 +669,9 @@
     <ClCompile Include="..\deh_tables.c">
       <Filter>D_Doom</Filter>
     </ClCompile>
-    <ClCompile Include="..\d_clisrv.c">
-      <Filter>D_Doom</Filter>
-    </ClCompile>
     <ClCompile Include="..\d_main.c">
       <Filter>D_Doom</Filter>
     </ClCompile>
-    <ClCompile Include="..\d_net.c">
-      <Filter>D_Doom</Filter>
-    </ClCompile>
-    <ClCompile Include="..\d_netcmd.c">
-      <Filter>D_Doom</Filter>
-    </ClCompile>
-    <ClCompile Include="..\d_netfil.c">
-      <Filter>D_Doom</Filter>
-    </ClCompile>
     <ClCompile Include="..\z_zone.c">
       <Filter>D_Doom</Filter>
     </ClCompile>
@@ -732,18 +750,9 @@
     <ClCompile Include="..\filesrch.c">
       <Filter>I_Interface</Filter>
     </ClCompile>
-    <ClCompile Include="..\i_addrinfo.c">
-      <Filter>I_Interface</Filter>
-    </ClCompile>
     <ClCompile Include="..\i_tcp.c">
       <Filter>I_Interface</Filter>
     </ClCompile>
-    <ClCompile Include="..\mserv.c">
-      <Filter>I_Interface</Filter>
-    </ClCompile>
-    <ClCompile Include="..\http-mserv.c">
-      <Filter>I_Interface</Filter>
-    </ClCompile>
     <ClCompile Include="..\lua_baselib.c">
       <Filter>LUA</Filter>
     </ClCompile>
@@ -831,6 +840,48 @@
     <ClCompile Include="..\m_random.c">
       <Filter>M_Misc</Filter>
     </ClCompile>
+    <ClCompile Include="..\netcode\client_connection.c">
+      <Filter>D_Doom</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\commands.c">
+      <Filter>D_Doom</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\d_clisrv.c">
+      <Filter>D_Doom</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\d_net.c">
+      <Filter>D_Doom</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\d_netcmd.c">
+      <Filter>D_Doom</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\d_netfil.c">
+      <Filter>D_Doom</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\gamestate.c">
+      <Filter>D_Doom</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\http-mserv.c">
+      <Filter>I_Interface</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\i_addrinfo.c">
+      <Filter>I_Interface</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\i_tcp.c">
+      <Filter>I_Interface</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\mserv.c">
+      <Filter>I_Interface</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\net_command.c">
+      <Filter>D_Doom</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\server_connection.c">
+      <Filter>D_Doom</Filter>
+    </ClCompile>
+    <ClCompile Include="..\netcode\tic_command.c">
+      <Filter>D_Doom</Filter>
+    </ClCompile>
     <ClCompile Include="..\string.c">
       <Filter>M_Misc</Filter>
     </ClCompile>
@@ -954,6 +1005,9 @@
     <ClCompile Include="..\v_video.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
+    <ClCompile Include="..\snake.c">
+      <Filter>M_Misc</Filter>
+    </ClCompile>
     <ClCompile Include="..\sounds.c">
       <Filter>S_Sounds</Filter>
     </ClCompile>
-- 
GitLab


From fb9e6c9c6d34cdb4b3750969bdae81095db327f4 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Fri, 22 Sep 2023 18:16:54 +0200
Subject: [PATCH 402/518] Use if-range instead of switch for demo versions

---
 src/g_demo.c | 89 +++++++++++++---------------------------------------
 1 file changed, 22 insertions(+), 67 deletions(-)

diff --git a/src/g_demo.c b/src/g_demo.c
index 4e959bcf3e..cb168dfd97 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -1891,16 +1891,9 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
 	p++; // VERSION
 	p++; // SUBVERSION
 	oldversion = READUINT16(p);
-	switch(oldversion) // demoversion
+	if (oldversion < 0x000c || oldversion > DEMOVERSION)
 	{
-	case DEMOVERSION: // latest always supported
-	case 0x000f: // The previous demoversions also supported
-	case 0x000e:
-	case 0x000d: // all that changed between then and now was longer color name
-	case 0x000c:
-		break;
-	// too old, cannot support.
-	default:
+		// too old (or new), cannot support
 		CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname);
 		Z_Free(buffer);
 		return UINT8_MAX;
@@ -1973,14 +1966,11 @@ void G_DoPlayDemo(char *defdemoname)
 	UINT8 i;
 	lumpnum_t l;
 	char skin[17],color[MAXCOLORNAME+1],*n,*pdemoname;
-	UINT8 version,subversion,charability,charability2,thrustfactor,accelstart,acceleration,cnamelen;
+	UINT8 version,subversion,charability,charability2,thrustfactor,accelstart,acceleration;
 	pflags_t pflags;
 	UINT32 randseed, followitem;
 	fixed_t camerascale,shieldscale,actionspd,mindash,maxdash,normalspeed,runspeed,jumpfactor,height,spinheight;
 	char msg[1024];
-#ifdef OLD22DEMOCOMPAT
-	boolean use_old_demo_vars = false;
-#endif
 
 	skin[16] = '\0';
 	color[MAXCOLORNAME] = '\0';
@@ -2039,23 +2029,13 @@ void G_DoPlayDemo(char *defdemoname)
 	subversion = READUINT8(demo_p);
 	demoversion = READUINT16(demo_p);
 	demo_forwardmove_rng = (demoversion < 0x0010);
-	switch(demoversion)
-	{
-	case 0x000f:
-	case 0x000d:
-	case 0x000e:
-	case DEMOVERSION: // latest always supported
-		cnamelen = MAXCOLORNAME;
-		break;
 #ifdef OLD22DEMOCOMPAT
-	// all that changed between then and now was longer color name
-	case 0x000c:
-		cnamelen = 16;
-		use_old_demo_vars = true;
-		break;
+	if (demoversion < 0x000c || demoversion > DEMOVERSION)
+#else
+	if (demoversion < 0x000d || demoversion > DEMOVERSION)
 #endif
-	// too old, cannot support.
-	default:
+	{
+		// too old (or new), cannot support
 		snprintf(msg, 1024, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemoname);
 		CONS_Alert(CONS_ERROR, "%s", msg);
 		M_StartMessage(msg, NULL, MM_NOTHING);
@@ -2182,8 +2162,8 @@ void G_DoPlayDemo(char *defdemoname)
 	demo_p += 16;
 
 	// Color
-	M_Memcpy(color,demo_p,cnamelen);
-	demo_p += cnamelen;
+	M_Memcpy(color, demo_p, (demoversion < 0x000d) ? 16 : MAXCOLORNAME);
+	demo_p += (demoversion < 0x000d) ? 16 : MAXCOLORNAME;
 
 	charability = READUINT8(demo_p);
 	charability2 = READUINT8(demo_p);
@@ -2219,7 +2199,7 @@ void G_DoPlayDemo(char *defdemoname)
 
 	// net var data
 #ifdef OLD22DEMOCOMPAT
-	if (use_old_demo_vars)
+	if (demoversion < 0x000d)
 		CV_LoadOldDemoVars(&demo_p);
 	else
 #endif
@@ -2352,19 +2332,13 @@ UINT8 G_CheckDemoForError(char *defdemoname)
 	demo_p++; // version
 	demo_p++; // subversion
 	our_demo_version = READUINT16(demo_p);
-	switch(our_demo_version)
-	{
-	case 0x000d:
-	case 0x000e:
-	case 0x000f:
-	case DEMOVERSION: // latest always supported
-		break;
 #ifdef OLD22DEMOCOMPAT
-	case 0x000c:
-		break;
+	if (our_demo_version < 0x000c || our_demo_version > DEMOVERSION)
+#else
+	if (our_demo_version < 0x000d || our_demo_version > DEMOVERSION)
 #endif
-	// too old, cannot support.
-	default:
+	{
+		// too old (or new), cannot support
 		return DFILE_ERROR_NOTDEMO;
 	}
 	demo_p += 16; // demo checksum
@@ -2386,7 +2360,6 @@ void G_AddGhost(char *defdemoname)
 	INT32 i;
 	lumpnum_t l;
 	char name[17],skin[17],color[MAXCOLORNAME+1],*n,*pdemoname,md5[16];
-	UINT8 cnamelen;
 	demoghost *gh;
 	UINT8 flags, subversion;
 	UINT8 *buffer,*p;
@@ -2438,20 +2411,9 @@ void G_AddGhost(char *defdemoname)
 	p++; // VERSION
 	subversion = READUINT8(p); // SUBVERSION
 	ghostversion = READUINT16(p);
-	switch(ghostversion)
+	if (ghostversion < 0x000c || ghostversion > DEMOVERSION)
 	{
-	case 0x000f:
-	case 0x000d:
-	case 0x000e:
-	case DEMOVERSION: // latest always supported
-		cnamelen = MAXCOLORNAME;
-		break;
-	// all that changed between then and now was longer color name
-	case 0x000c:
-		cnamelen = 16;
-		break;
-	// too old, cannot support.
-	default:
+		// too old (or new), cannot support
 		CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo version incompatible.\n"), pdemoname);
 		Z_Free(pdemoname);
 		Z_Free(buffer);
@@ -2514,8 +2476,8 @@ void G_AddGhost(char *defdemoname)
 	p += 16;
 
 	// Color
-	M_Memcpy(color, p,cnamelen);
-	p += cnamelen;
+	M_Memcpy(color, p, (ghostversion < 0x000d) ? 16 : MAXCOLORNAME);
+	p += (ghostversion < 0x000d) ? 16 : MAXCOLORNAME;
 
 	// Ghosts do not have a player structure to put this in.
 	p++; // charability
@@ -2698,16 +2660,9 @@ void G_DoPlayMetal(void)
 	metal_p++; // VERSION
 	metal_p++; // SUBVERSION
 	metalversion = READUINT16(metal_p);
-	switch(metalversion)
+	if (metalversion < 0x000c || metalversion > DEMOVERSION)
 	{
-	case DEMOVERSION: // latest always supported
-	case 0x000f:
-	case 0x000e: // There are checks wheter the momentum is from older demo versions or not
-	case 0x000d: // all that changed between then and now was longer color name
-	case 0x000c:
-		break;
-	// too old, cannot support.
-	default:
+		// too old (or new), cannot support
 		CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, format version incompatible.\n"));
 		Z_Free(metalbuffer);
 		return;
-- 
GitLab


From 12d595399ec20b30dfcb99ff3358cd309fafc980 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sun, 1 Oct 2023 17:34:13 +0200
Subject: [PATCH 403/518] Avoid branch prediction slowdowns in R_PointOnSide

---
 src/r_main.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/r_main.c b/src/r_main.c
index 54f7d7639e..0655bd06f0 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -249,7 +249,7 @@ static void FlipCam2_OnChange(void)
 //
 // killough 5/2/98: reformatted
 //
-INT32 R_PointOnSide(fixed_t x, fixed_t y, node_t *node)
+INT32 R_PointOnSide(fixed_t x, fixed_t y, node_t *restrict node)
 {
 	if (!node->dx)
 		return x <= node->x ? node->dy > 0 : node->dy < 0;
@@ -261,9 +261,10 @@ INT32 R_PointOnSide(fixed_t x, fixed_t y, node_t *node)
 	fixed_t dy = (y >> 1) - (node->y >> 1);
 
 	// Try to quickly decide by looking at sign bits.
-	if ((node->dy ^ node->dx ^ dx ^ dy) < 0)
-		return (node->dy ^ dx) < 0;  // (left is negative)
-	return FixedMul(dy, node->dx>>FRACBITS) >= FixedMul(node->dy>>FRACBITS, dx);
+	// also use a mask to avoid branch prediction
+	INT32 mask = (node->dy ^ node->dx ^ dx ^ dy) >> 31;
+	return (mask & ((node->dy ^ dx) < 0)) |  // (left is negative)
+		(~mask & (FixedMul(dy, node->dx>>FRACBITS) >= FixedMul(node->dy>>FRACBITS, dx)));
 }
 
 // killough 5/2/98: reformatted
-- 
GitLab


From 5aa89a712ab29c514e0aa6e7df493abdb5a2fbd7 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Tue, 10 Oct 2023 23:40:41 +0000
Subject: [PATCH 404/518] Update .gitlab-ci.yml file

---
 .gitlab-ci.yml | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 .gitlab-ci.yml

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000..d260df55cf
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,35 @@
+# This file is a template, and might need editing before it works on your project.
+# This is a sample GitLab CI/CD configuration file that should run without any modifications.
+# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
+# it uses echo commands to simulate the pipeline execution.
+#
+# A pipeline is composed of independent jobs that run scripts, grouped into stages.
+# Stages run in sequential order, but jobs within stages run in parallel.
+#
+# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages
+#
+# You can copy and paste this template into a new `.gitlab-ci.yml` file.
+# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
+#
+# To contribute improvements to CI/CD templates, please follow the Development guide at:
+# https://docs.gitlab.com/ee/development/cicd/templates.html
+# This specific template is located at:
+# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml
+
+stages:          # List of stages for jobs, and their order of execution
+  - build
+
+
+build-job:       # This job runs in the build stage, which runs first.
+  stage: build
+  image: cimg
+  #artifacts:
+  #  paths:
+  #    - "*.bin"
+
+  script:
+    - "ls"
+
+
+
+
-- 
GitLab


From eb44efbc2f412474ffcec9136bd43337edd980f9 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Tue, 10 Oct 2023 23:43:50 +0000
Subject: [PATCH 405/518] Update .gitlab-ci.yml file

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d260df55cf..cc2f898682 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -29,7 +29,7 @@ build-job:       # This job runs in the build stage, which runs first.
 
   script:
     - "ls"
-
+    - "pwd"
 
 
 
-- 
GitLab


From 124aca3267da8b4f4f504f6e8feb55bc265c5429 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Tue, 10 Oct 2023 23:46:11 +0000
Subject: [PATCH 406/518] Update .gitlab-ci.yml file

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cc2f898682..4432958817 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -22,7 +22,7 @@ stages:          # List of stages for jobs, and their order of execution
 
 build-job:       # This job runs in the build stage, which runs first.
   stage: build
-  image: cimg
+  image: debian
   #artifacts:
   #  paths:
   #    - "*.bin"
-- 
GitLab


From 9f8bc2125363eef882e62ef9f42f011bc45f31d6 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Tue, 10 Oct 2023 23:54:46 +0000
Subject: [PATCH 407/518] Update .gitlab-ci.yml file try running the makefile
 with nothing

---
 .gitlab-ci.yml | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4432958817..76f4783ca0 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -28,8 +28,9 @@ build-job:       # This job runs in the build stage, which runs first.
   #    - "*.bin"
 
   script:
-    - "ls"
-    - "pwd"
+    - "apt update"
+    - "apt-get install --no-install-recommends --yes make"
+    - "make --directory=src"
 
 
 
-- 
GitLab


From 959c1753d3c7e7abc5593fa3907ee620296cc372 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 00:05:38 +0000
Subject: [PATCH 408/518] Update .gitlab-ci.yml file try running the makefile
 with some dispends libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev
 libgme-dev upx git libopenmpt-dev gettext

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 76f4783ca0..aac53e06b6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -29,7 +29,7 @@ build-job:       # This job runs in the build stage, which runs first.
 
   script:
     - "apt update"
-    - "apt-get install --no-install-recommends --yes make"
+    - "apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev upx git libopenmpt-dev gettext"
     - "make --directory=src"
 
 
-- 
GitLab


From 7083dffc01be2e91bda59c96fa40d75fbfbb0037 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 00:08:16 +0000
Subject: [PATCH 409/518] Update .gitlab-ci.yml file remove upx install

---
 .gitlab-ci.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index aac53e06b6..e768c17359 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -29,8 +29,8 @@ build-job:       # This job runs in the build stage, which runs first.
 
   script:
     - "apt update"
-    - "apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev upx git libopenmpt-dev gettext"
-    - "make --directory=src"
+    - "apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext"
+    - "make --directory=src "
 
 
 
-- 
GitLab


From 4adce8e3cddad16d48e7244edd0616a166ed17f4 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 00:23:05 +0000
Subject: [PATCH 410/518] Update .gitlab-ci.yml file keep artifacts try using
 ccache

---
 .gitlab-ci.yml | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e768c17359..de3ff8a735 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -23,14 +23,15 @@ stages:          # List of stages for jobs, and their order of execution
 build-job:       # This job runs in the build stage, which runs first.
   stage: build
   image: debian
-  #artifacts:
-  #  paths:
-  #    - "*.bin"
+  artifacts:
+    paths:
+      - "bin/*"
 
   script:
+    - ""
     - "apt update"
-    - "apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext"
-    - "make --directory=src "
+    - "apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache"
+    - "CC=\"ccache gcc\" make --directory=src "
 
 
 
-- 
GitLab


From b3273a899aa5773c86a9110a7f57403b8544f6e8 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 00:40:49 +0000
Subject: [PATCH 411/518] Update .gitlab-ci.yml file cache the ccache

---
 .gitlab-ci.yml | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index de3ff8a735..913823b673 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -23,15 +23,22 @@ stages:          # List of stages for jobs, and their order of execution
 build-job:       # This job runs in the build stage, which runs first.
   stage: build
   image: debian
+  cache:
+    - key:
+        files:
+          - .ccache/stats
+      paths:
+        - .ccache
   artifacts:
     paths:
       - "bin/*"
 
   script:
-    - ""
     - "apt update"
     - "apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache"
-    - "CC=\"ccache gcc\" make --directory=src "
+    - "ccache --max-size 10M"
+    - "CC=\"ccache gcc\" make --directory=src"
+    - "ccache --show-stats"
 
 
 
-- 
GitLab


From 67dcc2dd0af4f48e4232b6c9d358fbfae5138f9c Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 00:49:43 +0000
Subject: [PATCH 412/518] Update .gitlab-ci.yml file cache the ccache part two

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 913823b673..a8eb0854f1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -28,7 +28,7 @@ build-job:       # This job runs in the build stage, which runs first.
         files:
           - .ccache/stats
       paths:
-        - .ccache
+        - .ccache/
   artifacts:
     paths:
       - "bin/*"
-- 
GitLab


From 219691895df27ad5314304c4fe5eaca1977cc407 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 01:10:02 +0000
Subject: [PATCH 413/518] Update .gitlab-ci.yml file use variables to test if
 we are using DigitalOcean

---
 .gitlab-ci.yml | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a8eb0854f1..b593c478c8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -16,6 +16,8 @@
 # This specific template is located at:
 # https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml
 
+
+
 stages:          # List of stages for jobs, and their order of execution
   - build
 
@@ -23,6 +25,9 @@ stages:          # List of stages for jobs, and their order of execution
 build-job:       # This job runs in the build stage, which runs first.
   stage: build
   image: debian
+  variables:
+    CCMaxSize: "10M"
+    CCC: "ccache gcc"
   cache:
     - key:
         files:
@@ -36,9 +41,10 @@ build-job:       # This job runs in the build stage, which runs first.
   script:
     - "apt update"
     - "apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache"
-    - "ccache --max-size 10M"
-    - "CC=\"ccache gcc\" make --directory=src"
+    - "ccache --max-size $CCMaxSize"
+    - "CC=\"$CCC\" make --directory=src"
     - "ccache --show-stats"
+    - "echo Are we running on DO? $DigitalOceanDebianMirror"
 
 
 
-- 
GitLab


From 0f2d3e9134ed658ac7f94de87b83f2b18b1931bb Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 01:26:22 +0000
Subject: [PATCH 414/518] Update .gitlab-ci.yml file fix ccache path

---
 .gitlab-ci.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b593c478c8..d2c27a2216 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -31,9 +31,9 @@ build-job:       # This job runs in the build stage, which runs first.
   cache:
     - key:
         files:
-          - .ccache/stats
+          - .cache/ccache/stats
       paths:
-        - .ccache/
+        - .cache/ccache/
   artifacts:
     paths:
       - "bin/*"
-- 
GitLab


From 2fae2b54db1f203a7b5d538372924491fdb98798 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 01:39:08 +0000
Subject: [PATCH 415/518] Update .gitlab-ci.yml file fix ccache path part two

---
 .gitlab-ci.yml | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d2c27a2216..7ee079494e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -27,7 +27,8 @@ build-job:       # This job runs in the build stage, which runs first.
   image: debian
   variables:
     CCMaxSize: "10M"
-    CCC: "ccache gcc"
+    CC: "ccache gcc"
+    CCACHE_DIR: "$CI_BUILDS_DIR/.cache/ccache"
   cache:
     - key:
         files:
@@ -39,10 +40,13 @@ build-job:       # This job runs in the build stage, which runs first.
       - "bin/*"
 
   script:
+    - "export $CC"
+    - "export $CCACHE_DIR"
+    - "export $CI_BUILDS_DIR"
     - "apt update"
     - "apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache"
     - "ccache --max-size $CCMaxSize"
-    - "CC=\"$CCC\" make --directory=src"
+    - "make --directory=src"
     - "ccache --show-stats"
     - "echo Are we running on DO? $DigitalOceanDebianMirror"
 
-- 
GitLab


From 16741b2cb0577d8c5ad917a5909187ed359a4c63 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 02:08:04 +0000
Subject: [PATCH 416/518] Update .gitlab-ci.yml file fix ccache path part three

---
 .gitlab-ci.yml | 39 ++++++++++++++++++++++++---------------
 1 file changed, 24 insertions(+), 15 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7ee079494e..7388952683 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -27,28 +27,37 @@ build-job:       # This job runs in the build stage, which runs first.
   image: debian
   variables:
     CCMaxSize: "10M"
-    CC: "ccache gcc"
-    CCACHE_DIR: "$CI_BUILDS_DIR/.cache/ccache"
   cache:
-    - key:
-        files:
-          - .cache/ccache/stats
+    - key: ccache-$CI_JOB_NAME_SLUG
+      fallback_keys:
+        - cache-$CI_DEFAULT_BRANCH
+        - cache-default
       paths:
-        - .cache/ccache/
+        - ccache
   artifacts:
     paths:
       - "bin/*"
+  before_script:
+    - export PATH="/usr/lib/ccache:$PATH"
+    - export CCACHE_BASEDIR="$PWD"
+    - export CCACHE_DIR="$PWD/ccache"
+    - export CCACHE_COMPILERCHECK=content
+    - ccache --max-size $CCMaxSize
+    - ccache --zero-stats || true
+    - ccache --show-stats || true
+  after_script:
+    - export CCACHE_DIR="$PWD/ccache"
+    - ccache --show-stats
+
 
   script:
-    - "export $CC"
-    - "export $CCACHE_DIR"
-    - "export $CI_BUILDS_DIR"
-    - "apt update"
-    - "apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache"
-    - "ccache --max-size $CCMaxSize"
-    - "make --directory=src"
-    - "ccache --show-stats"
-    - "echo Are we running on DO? $DigitalOceanDebianMirror"
+    - pwd
+    - echo "$CI_JOB_NAME_SLUG"
+    - echo "$CCACHE_DIR"
+    - echo Are we running on DO? "$DigitalOceanDebianMirror"
+    - apt update
+    - apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache
+    - make --directory=src
 
 
 
-- 
GitLab


From 84a2463d2f45287e04a69056fc1485692c824d18 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 02:09:54 +0000
Subject: [PATCH 417/518] Update .gitlab-ci.yml file fix ccache path part four

---
 .gitlab-ci.yml | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7388952683..2f089efc9a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -42,6 +42,8 @@ build-job:       # This job runs in the build stage, which runs first.
     - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
     - export CCACHE_COMPILERCHECK=content
+    - apt update
+    - apt-get install --no-install-recommends ccache
     - ccache --max-size $CCMaxSize
     - ccache --zero-stats || true
     - ccache --show-stats || true
@@ -55,8 +57,7 @@ build-job:       # This job runs in the build stage, which runs first.
     - echo "$CI_JOB_NAME_SLUG"
     - echo "$CCACHE_DIR"
     - echo Are we running on DO? "$DigitalOceanDebianMirror"
-    - apt update
-    - apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache
+    - apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext
     - make --directory=src
 
 
-- 
GitLab


From 0ff83947ac4349f89d5f46835aeedff7364a4b9b Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 02:11:04 +0000
Subject: [PATCH 418/518] Update .gitlab-ci.yml file fix ccache path part 5

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2f089efc9a..0376084162 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -43,7 +43,7 @@ build-job:       # This job runs in the build stage, which runs first.
     - export CCACHE_DIR="$PWD/ccache"
     - export CCACHE_COMPILERCHECK=content
     - apt update
-    - apt-get install --no-install-recommends ccache
+    - apt-get install --no-install-recommends --yes ccache
     - ccache --max-size $CCMaxSize
     - ccache --zero-stats || true
     - ccache --show-stats || true
-- 
GitLab


From 4b015c91e12dcc7d8a7056e0d16c2431f2272884 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 03:03:37 +0000
Subject: [PATCH 419/518] Update .gitlab-ci.yml file rename ccache keys up
 ccache size to 30M

---
 .gitlab-ci.yml | 17 +++++------------
 1 file changed, 5 insertions(+), 12 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0376084162..68d5c5eb1c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -22,16 +22,16 @@ stages:          # List of stages for jobs, and their order of execution
   - build
 
 
-build-job:       # This job runs in the build stage, which runs first.
+build-native:       # This job runs in the build stage, which runs first.
   stage: build
   image: debian
   variables:
-    CCMaxSize: "10M"
+    CCACHE_MAXSIZE: "30M"
   cache:
-    - key: ccache-$CI_JOB_NAME_SLUG
+    - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG
       fallback_keys:
-        - cache-$CI_DEFAULT_BRANCH
-        - cache-default
+        - cache-$CI_PROJECT_PATH_SLUG-$CI_DEFAULT_BRANCH
+        - cache-$CI_PROJECT_PATH_SLUG-default
       paths:
         - ccache
   artifacts:
@@ -44,19 +44,12 @@ build-job:       # This job runs in the build stage, which runs first.
     - export CCACHE_COMPILERCHECK=content
     - apt update
     - apt-get install --no-install-recommends --yes ccache
-    - ccache --max-size $CCMaxSize
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
     - export CCACHE_DIR="$PWD/ccache"
     - ccache --show-stats
-
-
   script:
-    - pwd
-    - echo "$CI_JOB_NAME_SLUG"
-    - echo "$CCACHE_DIR"
-    - echo Are we running on DO? "$DigitalOceanDebianMirror"
     - apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext
     - make --directory=src
 
-- 
GitLab


From 92f16c8142cd035049cbde95008400072cdade28 Mon Sep 17 00:00:00 2001
From: Logan Arias <logan.gba@gmail.com>
Date: Wed, 11 Oct 2023 04:11:56 +0000
Subject: [PATCH 420/518] Update .gitlab-ci.yml file use git clone options up
 CCACHE size to 50M

---
 .gitlab-ci.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 68d5c5eb1c..3a0eff4adb 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -26,7 +26,9 @@ build-native:       # This job runs in the build stage, which runs first.
   stage: build
   image: debian
   variables:
-    CCACHE_MAXSIZE: "30M"
+    CCACHE_MAXSIZE: "50M"
+    GIT_STRATEGY: clone
+    GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
   cache:
     - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG
       fallback_keys:
-- 
GitLab


From 3ee5ed2dacfadfcfad4ddc38bfae17c24379a6ab Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 11 Oct 2023 04:25:05 +0000
Subject: [PATCH 421/518] Update .gitlab-ci.yml file cache apt packages

---
 .gitlab-ci.yml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3a0eff4adb..48cf42697e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -36,6 +36,9 @@ build-native:       # This job runs in the build stage, which runs first.
         - cache-$CI_PROJECT_PATH_SLUG-default
       paths:
         - ccache
+    - key: apt
+      paths:
+        - /var/cache/apt
   artifacts:
     paths:
       - "bin/*"
@@ -44,7 +47,7 @@ build-native:       # This job runs in the build stage, which runs first.
     - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
     - export CCACHE_COMPILERCHECK=content
-    - apt update
+    - apt-get update
     - apt-get install --no-install-recommends --yes ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
-- 
GitLab


From 3ef89e990216233abbd95db7201d25d70da261a0 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 11 Oct 2023 04:27:50 +0000
Subject: [PATCH 422/518] Update .gitlab-ci.yml file cache apt packages, part 2

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 48cf42697e..37c6d76e89 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -38,7 +38,7 @@ build-native:       # This job runs in the build stage, which runs first.
         - ccache
     - key: apt
       paths:
-        - /var/cache/apt
+        - /var/cache/apt/
   artifacts:
     paths:
       - "bin/*"
-- 
GitLab


From 9597a19ecf0106f5198df19b7bb9e8847f6ec858 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 11 Oct 2023 04:37:01 +0000
Subject: [PATCH 423/518] Update .gitlab-ci.yml file cache apt packages, part 3

---
 .gitlab-ci.yml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 37c6d76e89..22724f679a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -38,7 +38,7 @@ build-native:       # This job runs in the build stage, which runs first.
         - ccache
     - key: apt
       paths:
-        - /var/cache/apt/
+        - apt
   artifacts:
     paths:
       - "bin/*"
@@ -47,6 +47,7 @@ build-native:       # This job runs in the build stage, which runs first.
     - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
     - export CCACHE_COMPILERCHECK=content
+    - echo Dir::Cache="$PWD/apt" > /etc/apt/apt.conf.d/99cache
     - apt-get update
     - apt-get install --no-install-recommends --yes ccache
     - ccache --zero-stats || true
-- 
GitLab


From 7231631a3ff391be9b3fb889ee20cfc6d033d84c Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 11 Oct 2023 04:39:48 +0000
Subject: [PATCH 424/518] Update .gitlab-ci.yml file cache apt packages, part 4

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 22724f679a..2e388ac187 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -47,7 +47,7 @@ build-native:       # This job runs in the build stage, which runs first.
     - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
     - export CCACHE_COMPILERCHECK=content
-    - echo Dir::Cache="$PWD/apt" > /etc/apt/apt.conf.d/99cache
+    - echo Dir::Cache="$PWD/apt" | tee /etc/apt/apt.conf.d/99cache
     - apt-get update
     - apt-get install --no-install-recommends --yes ccache
     - ccache --zero-stats || true
-- 
GitLab


From eb6ed2e7e7e657abf48b63e0faa17058d111b509 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 11 Oct 2023 04:43:22 +0000
Subject: [PATCH 425/518] Update .gitlab-ci.yml file cache apt packages, part 5

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2e388ac187..18ae4459cd 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -47,7 +47,7 @@ build-native:       # This job runs in the build stage, which runs first.
     - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
     - export CCACHE_COMPILERCHECK=content
-    - echo Dir::Cache="$PWD/apt" | tee /etc/apt/apt.conf.d/99cache
+    - echo -n Dir::Cache="$PWD/apt" | tee /etc/apt/apt.conf.d/99cache
     - apt-get update
     - apt-get install --no-install-recommends --yes ccache
     - ccache --zero-stats || true
-- 
GitLab


From 3db57df3532db301b4dd4374fcf119ffbb68f584 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 11 Oct 2023 04:57:29 +0000
Subject: [PATCH 426/518] Update .gitlab-ci.yml file cache apt packages, part 6

---
 .gitlab-ci.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 18ae4459cd..7bc250338b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -47,7 +47,9 @@ build-native:       # This job runs in the build stage, which runs first.
     - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
     - export CCACHE_COMPILERCHECK=content
-    - echo -n Dir::Cache="$PWD/apt" | tee /etc/apt/apt.conf.d/99cache
+    - export APT_CACHE_DIR=`pwd`/apt-cache
+    - mkdir --parents --verbose $APT_CACHE_DIR/partial/
+    - echo dir::cache::archives="$APT_CACHE_DIR" | tee --append /etc/apt/apt.conf.d/99cache
     - apt-get update
     - apt-get install --no-install-recommends --yes ccache
     - ccache --zero-stats || true
-- 
GitLab


From 97096445dfb445f1eb35c3e4d573100a65043930 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 11 Oct 2023 05:00:08 +0000
Subject: [PATCH 427/518] Update .gitlab-ci.yml file cache apt packages, part 7

---
 .gitlab-ci.yml | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7bc250338b..30db01e1eb 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -49,16 +49,15 @@ build-native:       # This job runs in the build stage, which runs first.
     - export CCACHE_COMPILERCHECK=content
     - export APT_CACHE_DIR=`pwd`/apt-cache
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-    - echo dir::cache::archives="$APT_CACHE_DIR" | tee --append /etc/apt/apt.conf.d/99cache
     - apt-get update
-    - apt-get install --no-install-recommends --yes ccache
+    - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
     - export CCACHE_DIR="$PWD/ccache"
     - ccache --show-stats
   script:
-    - apt-get install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext
+    - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext
     - make --directory=src
 
 
-- 
GitLab


From 37909c95b18a328381a587c440a49695821732f9 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 11 Oct 2023 05:03:19 +0000
Subject: [PATCH 428/518] Update .gitlab-ci.yml file cache apt packages, part 8

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 30db01e1eb..02b2edc928 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -38,7 +38,7 @@ build-native:       # This job runs in the build stage, which runs first.
         - ccache
     - key: apt
       paths:
-        - apt
+        - apt-cache
   artifacts:
     paths:
       - "bin/*"
-- 
GitLab


From e7417ea11653ed3f52d9c813150ab63a35e4f16a Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 11 Oct 2023 05:07:25 +0000
Subject: [PATCH 429/518] Update .gitlab-ci.yml file switch docker image to
 debian, slim version

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 02b2edc928..2e21653d76 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -24,7 +24,7 @@ stages:          # List of stages for jobs, and their order of execution
 
 build-native:       # This job runs in the build stage, which runs first.
   stage: build
-  image: debian
+  image: debian-slim
   variables:
     CCACHE_MAXSIZE: "50M"
     GIT_STRATEGY: clone
-- 
GitLab


From 4b43f92b134f2d8841a8b1af65460d44e6b28bd5 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 11 Oct 2023 05:10:23 +0000
Subject: [PATCH 430/518] Update .gitlab-ci.yml file switch docker image to
 debian, slim version, part 2

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2e21653d76..597a6cb8a7 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -24,7 +24,7 @@ stages:          # List of stages for jobs, and their order of execution
 
 build-native:       # This job runs in the build stage, which runs first.
   stage: build
-  image: debian-slim
+  image: debian:stable-slim
   variables:
     CCACHE_MAXSIZE: "50M"
     GIT_STRATEGY: clone
-- 
GitLab


From 8b4548384249d211a8d8f82bb18281f8d75dace5 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 01:06:00 +0000
Subject: [PATCH 431/518] Update .gitlab-ci.yml file autoclean apt cache and
 change cache key to image name

---
 .gitlab-ci.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 597a6cb8a7..0cf36d679b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -36,7 +36,7 @@ build-native:       # This job runs in the build stage, which runs first.
         - cache-$CI_PROJECT_PATH_SLUG-default
       paths:
         - ccache
-    - key: apt
+    - key: apt-$CI_JOB_IMAGE
       paths:
         - apt-cache
   artifacts:
@@ -50,14 +50,14 @@ build-native:       # This job runs in the build stage, which runs first.
     - export APT_CACHE_DIR=`pwd`/apt-cache
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
     - apt-get update
-    - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes ccache
+    - pt-get -o dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
     - export CCACHE_DIR="$PWD/ccache"
     - ccache --show-stats
+    - apt-get autoclean
   script:
-    - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext
     - make --directory=src
 
 
-- 
GitLab


From 53eafa046cef88f21d347fd71769da72fbce4c7f Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 01:07:42 +0000
Subject: [PATCH 432/518] Update .gitlab-ci.yml file fixup typo

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0cf36d679b..c7301e4185 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -50,7 +50,7 @@ build-native:       # This job runs in the build stage, which runs first.
     - export APT_CACHE_DIR=`pwd`/apt-cache
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
     - apt-get update
-    - pt-get -o dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache
+    - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
-- 
GitLab


From d7ea647f9bc74ff8787c9d3fa817d8bc01aa81ab Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 01:21:23 +0000
Subject: [PATCH 433/518] Update .gitlab-ci.yml file apt cache do not need to
 be protected also, name artifact for binaries from build-native job

---
 .gitlab-ci.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c7301e4185..e182098b53 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -39,9 +39,11 @@ build-native:       # This job runs in the build stage, which runs first.
     - key: apt-$CI_JOB_IMAGE
       paths:
         - apt-cache
+      unprotect: true
   artifacts:
     paths:
       - "bin/*"
+    name: "Debian native"
   before_script:
     - export PATH="/usr/lib/ccache:$PATH"
     - export CCACHE_BASEDIR="$PWD"
@@ -51,12 +53,12 @@ build-native:       # This job runs in the build stage, which runs first.
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
     - apt-get update
     - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache
+    - apt-get autoclean
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
     - export CCACHE_DIR="$PWD/ccache"
     - ccache --show-stats
-    - apt-get autoclean
   script:
     - make --directory=src
 
-- 
GitLab


From dc2a5b949692c35505d60910afbf22fd1ccfb331 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 01:36:29 +0000
Subject: [PATCH 434/518] Update .gitlab-ci.yml file do not play games with
 $PATH

---
 .gitlab-ci.yml | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e182098b53..14f171d042 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -45,21 +45,24 @@ build-native:       # This job runs in the build stage, which runs first.
       - "bin/*"
     name: "Debian native"
   before_script:
-    - export PATH="/usr/lib/ccache:$PATH"
-    - export CCACHE_BASEDIR="$PWD"
-    - export CCACHE_DIR="$PWD/ccache"
-    - export CCACHE_COMPILERCHECK=content
     - export APT_CACHE_DIR=`pwd`/apt-cache
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
     - apt-get update
     - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache
     - apt-get autoclean
+    - export CCACHE_BASEDIR="$PWD"
+    - export CCACHE_DIR="$PWD/ccache"
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
+    - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
     - ccache --show-stats
   script:
+    - export CC="ccache gcc"
+    - export CCACHE_BASEDIR="$PWD"
+    - export CCACHE_DIR="$PWD/ccache"
+    - export CCACHE_COMPILERCHECK=content
     - make --directory=src
 
 
-- 
GitLab


From dbd40ef4220ad83100a42dacc6d7228fc32439b3 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 01:52:52 +0000
Subject: [PATCH 435/518] Update .gitlab-ci.yml file auto cache our apt cache

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 14f171d042..7abbfc9808 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -49,7 +49,7 @@ build-native:       # This job runs in the build stage, which runs first.
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
     - apt-get update
     - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache
-    - apt-get autoclean
+    - apt-get -o dir::cache::archives="$APT_CACHE_DIR" autoclean
     - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
     - ccache --zero-stats || true
-- 
GitLab


From 01fc7810e28028304a899f55007e7808d33f594c Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 02:03:04 +0000
Subject: [PATCH 436/518] Update .gitlab-ci.yml file use DO mirror?

---
 .gitlab-ci.yml | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7abbfc9808..94e1676a26 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -45,11 +45,13 @@ build-native:       # This job runs in the build stage, which runs first.
       - "bin/*"
     name: "Debian native"
   before_script:
+    - sed --expression="s/deb.debian.org/mirrors.digitalocean.com/g" /etc/apt/sources.list > /etc/apt/sources.list.d/debian.list
+    - rm --force /etc/apt/sources.list
+    - apt-get update
     - export APT_CACHE_DIR=`pwd`/apt-cache
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-    - apt-get update
-    - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache
-    - apt-get -o dir::cache::archives="$APT_CACHE_DIR" autoclean
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
     - ccache --zero-stats || true
-- 
GitLab


From 27776173ca16431067aeadeba06b8b0ef8bd8701 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 02:08:24 +0000
Subject: [PATCH 437/518] Update .gitlab-ci.yml file let see what in the
 /etc/apt folder

---
 .gitlab-ci.yml | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 94e1676a26..61c03e315c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -45,8 +45,9 @@ build-native:       # This job runs in the build stage, which runs first.
       - "bin/*"
     name: "Debian native"
   before_script:
-    - sed --expression="s/deb.debian.org/mirrors.digitalocean.com/g" /etc/apt/sources.list > /etc/apt/sources.list.d/debian.list
-    - rm --force /etc/apt/sources.list
+    - ls --human-readable --all -l /etc/apt/sources.list*
+    - rem sed --expression="s/deb.debian.org/mirrors.digitalocean.com/g" /etc/apt/sources.list > /etc/apt/sources.list.d/debian.list
+    - rem rm --force /etc/apt/sources.list
     - apt-get update
     - export APT_CACHE_DIR=`pwd`/apt-cache
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-- 
GitLab


From 58a10836f0520c05b22422f9e49d7ec00e1c244d Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 02:15:47 +0000
Subject: [PATCH 438/518] Update .gitlab-ci.yml file try to overwrite mirrors
 file

---
 .gitlab-ci.yml | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 61c03e315c..edc47f3b98 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -45,9 +45,7 @@ build-native:       # This job runs in the build stage, which runs first.
       - "bin/*"
     name: "Debian native"
   before_script:
-    - ls --human-readable --all -l /etc/apt/sources.list*
-    - rem sed --expression="s/deb.debian.org/mirrors.digitalocean.com/g" /etc/apt/sources.list > /etc/apt/sources.list.d/debian.list
-    - rem rm --force /etc/apt/sources.list
+    - if [ $DigitalOceanDebianMirror == "1" ]; then echo http://mirrors.digitalocean.com/debian | tee /etc/apt/mirrors/debian.list; fi
     - apt-get update
     - export APT_CACHE_DIR=`pwd`/apt-cache
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-- 
GitLab


From f015b6ea68b5a1df40ea63b03bb36936246f7253 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 02:18:29 +0000
Subject: [PATCH 439/518] Update .gitlab-ci.yml file let look inside
 debian.sources

---
 .gitlab-ci.yml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index edc47f3b98..172fba5bc9 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -45,7 +45,8 @@ build-native:       # This job runs in the build stage, which runs first.
       - "bin/*"
     name: "Debian native"
   before_script:
-    - if [ $DigitalOceanDebianMirror == "1" ]; then echo http://mirrors.digitalocean.com/debian | tee /etc/apt/mirrors/debian.list; fi
+    - cat /etc/apt/sources.list.d/debian.sources
+    - if [ $DigitalOceanDebianMirror == "1" ]; then echo http://mirrors.digitalocean.com/debian | tee /etc/apt/mirrors/debian.list; fi || true
     - apt-get update
     - export APT_CACHE_DIR=`pwd`/apt-cache
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-- 
GitLab


From 735b8314e82a50bfb8f22a28d622dd0a62670790 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 02:23:36 +0000
Subject: [PATCH 440/518] Update .gitlab-ci.yml file forget about change the
 Debian mirror

---
 .gitlab-ci.yml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 172fba5bc9..529d5d9149 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -45,8 +45,6 @@ build-native:       # This job runs in the build stage, which runs first.
       - "bin/*"
     name: "Debian native"
   before_script:
-    - cat /etc/apt/sources.list.d/debian.sources
-    - if [ $DigitalOceanDebianMirror == "1" ]; then echo http://mirrors.digitalocean.com/debian | tee /etc/apt/mirrors/debian.list; fi || true
     - apt-get update
     - export APT_CACHE_DIR=`pwd`/apt-cache
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-- 
GitLab


From 95c81696a5594442ae9795fedccace91dd7b72d7 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 03:00:34 +0000
Subject: [PATCH 441/518] Update .gitlab-ci.yml file build Mingw64 binary

---
 .gitlab-ci.yml | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 529d5d9149..75415adc9f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -48,7 +48,7 @@ build-native:       # This job runs in the build stage, which runs first.
     - apt-get update
     - export APT_CACHE_DIR=`pwd`/apt-cache
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache mingw-w64
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
@@ -59,11 +59,10 @@ build-native:       # This job runs in the build stage, which runs first.
     - export CCACHE_DIR="$PWD/ccache"
     - ccache --show-stats
   script:
-    - export CC="ccache gcc"
     - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
     - export CCACHE_COMPILERCHECK=content
-    - make --directory=src
-
+    - make --directory=src CCACHE=1
+    - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64
 
 
-- 
GitLab


From ae25f9ac4495e18bfc0c6c119e7dea9356477d3d Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 03:06:17 +0000
Subject: [PATCH 442/518] Update .gitlab-ci.yml file yea the Mingw-w64 is
 called "x86_64-w64-mingw32", not "x86_64-w64"

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 75415adc9f..1ceaa1ef1f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -63,6 +63,6 @@ build-native:       # This job runs in the build stage, which runs first.
     - export CCACHE_DIR="$PWD/ccache"
     - export CCACHE_COMPILERCHECK=content
     - make --directory=src CCACHE=1
-    - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64
+    - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
 
 
-- 
GitLab


From ff4c4c806a9a9a41915b4eff736dd75eb01eb3dd Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 04:12:15 +0000
Subject: [PATCH 443/518] Update .gitlab-ci.yml file split build job for each
 target: x86-64, Win32, Win64

---
 .gitlab-ci.yml | 45 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 34 insertions(+), 11 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1ceaa1ef1f..88872359f4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -21,8 +21,7 @@
 stages:          # List of stages for jobs, and their order of execution
   - build
 
-
-build-native:       # This job runs in the build stage, which runs first.
+.job_template: &job_build # This job runs in the build stage, which runs first.
   stage: build
   image: debian:stable-slim
   variables:
@@ -40,10 +39,6 @@ build-native:       # This job runs in the build stage, which runs first.
       paths:
         - apt-cache
       unprotect: true
-  artifacts:
-    paths:
-      - "bin/*"
-    name: "Debian native"
   before_script:
     - apt-get update
     - export APT_CACHE_DIR=`pwd`/apt-cache
@@ -58,11 +53,39 @@ build-native:       # This job runs in the build stage, which runs first.
     - export CCACHE_BASEDIR="$PWD"
     - export CCACHE_DIR="$PWD/ccache"
     - ccache --show-stats
+
+.default_Scripts: &ccache
+  - export CCACHE_BASEDIR="$PWD"
+  - export CCACHE_DIR="$PWD/ccache"
+  - export CCACHE_COMPILERCHECK=content
+
+build-x86_64-linux-gnu:
+  <<: *job_build
+  artifacts:
+    paths:
+      - "bin/lsdl2srb2*"
+    name: "x86-64"
   script:
-    - export CCACHE_BASEDIR="$PWD"
-    - export CCACHE_DIR="$PWD/ccache"
-    - export CCACHE_COMPILERCHECK=content
-    - make --directory=src CCACHE=1
-    - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
+    - *ccache
+    - export CC="x86_64-linux-gnu-gcc
+    - make --directory=src CCACHE=1 LINUX64=1
 
+build-i686-w64-mingw32:
+  <<: *job_build
+  artifacts:
+    paths:
+      - "bin/srb2win.exe*"
+    name: "Win32"
+  script:
+    - *ccache
+    - make --directory=src CCACHE=1 MINGW=1 PREFIX=i686-w64-mingw32
 
+build-x86_64-w64-mingw32:
+  <<: *job_build
+  artifacts:
+    paths:
+      - "bin/srb2win64.exe*"
+    name: "Win64"
+  script:
+    - *ccache
+    - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
-- 
GitLab


From bd1adb266771c7272e9b01edc9c8a5298a6c44de Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 04:22:15 +0000
Subject: [PATCH 444/518] Update .gitlab-ci.yml file inline the script to setup
 ccache should be done in one line?

---
 .gitlab-ci.yml | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 88872359f4..5c1f4a3ee6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -21,6 +21,9 @@
 stages:          # List of stages for jobs, and their order of execution
   - build
 
+.default_Scripts: &ccache
+  export CCACHE_BASEDIR="$PWD";export CCACHE_DIR="$PWD/ccache";export CCACHE_COMPILERCHECK=content
+
 .job_template: &job_build # This job runs in the build stage, which runs first.
   stage: build
   image: debian:stable-slim
@@ -45,20 +48,13 @@ stages:          # List of stages for jobs, and their order of execution
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache mingw-w64
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
-    - export CCACHE_BASEDIR="$PWD"
-    - export CCACHE_DIR="$PWD/ccache"
+    - *ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
-    - export CCACHE_BASEDIR="$PWD"
-    - export CCACHE_DIR="$PWD/ccache"
+    - *ccache
     - ccache --show-stats
 
-.default_Scripts: &ccache
-  - export CCACHE_BASEDIR="$PWD"
-  - export CCACHE_DIR="$PWD/ccache"
-  - export CCACHE_COMPILERCHECK=content
-
 build-x86_64-linux-gnu:
   <<: *job_build
   artifacts:
-- 
GitLab


From ce760342c22e4a204408d6269268bcf4d7ee0059 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 04:26:02 +0000
Subject: [PATCH 445/518] Update .gitlab-ci.yml file fix setting $CC in
 build-x86_64-linux-gnu job

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5c1f4a3ee6..5e0f919236 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -63,7 +63,7 @@ build-x86_64-linux-gnu:
     name: "x86-64"
   script:
     - *ccache
-    - export CC="x86_64-linux-gnu-gcc
+    - export CC="x86_64-linux-gnu-gcc"
     - make --directory=src CCACHE=1 LINUX64=1
 
 build-i686-w64-mingw32:
-- 
GitLab


From 82b29bdfcf349795fafb28384b3d8b251d14a0f7 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 05:04:02 +0000
Subject: [PATCH 446/518] Update .gitlab-ci.yml file Name the artifacts by
 project name,branch, then SHA

---
 .gitlab-ci.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5e0f919236..f2c9573dca 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -60,7 +60,7 @@ build-x86_64-linux-gnu:
   artifacts:
     paths:
       - "bin/lsdl2srb2*"
-    name: "x86-64"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - *ccache
     - export CC="x86_64-linux-gnu-gcc"
@@ -71,7 +71,7 @@ build-i686-w64-mingw32:
   artifacts:
     paths:
       - "bin/srb2win.exe*"
-    name: "Win32"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - *ccache
     - make --directory=src CCACHE=1 MINGW=1 PREFIX=i686-w64-mingw32
@@ -81,7 +81,7 @@ build-x86_64-w64-mingw32:
   artifacts:
     paths:
       - "bin/srb2win64.exe*"
-    name: "Win64"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
     - *ccache
     - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
-- 
GitLab


From ea02f5c8104ea3b157323835131240113156b28b Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 05:22:42 +0000
Subject: [PATCH 447/518] Update .gitlab-ci.yml file since each build job run
 in their own docker, only install packages as needed for each job

---
 .gitlab-ci.yml | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f2c9573dca..63fcb805b4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -46,7 +46,7 @@ stages:          # List of stages for jobs, and their order of execution
     - apt-get update
     - export APT_CACHE_DIR=`pwd`/apt-cache
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes build-essential libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev git libopenmpt-dev gettext ccache mingw-w64
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes make git ccache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - *ccache
     - ccache --zero-stats || true
@@ -63,6 +63,8 @@ build-x86_64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - *ccache
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev gcc
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - export CC="x86_64-linux-gnu-gcc"
     - make --directory=src CCACHE=1 LINUX64=1
 
@@ -74,6 +76,8 @@ build-i686-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - *ccache
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - make --directory=src CCACHE=1 MINGW=1 PREFIX=i686-w64-mingw32
 
 build-x86_64-w64-mingw32:
@@ -84,4 +88,6 @@ build-x86_64-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
     - *ccache
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64-x86-64
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
-- 
GitLab


From caf2b486c4716f68e7e03d2bd065091ab92deb7c Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 05:31:31 +0000
Subject: [PATCH 448/518] Update .gitlab-ci.yml file do not install posix
 version of mingw-w64

---
 .gitlab-ci.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 63fcb805b4..4498c1d85b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -76,7 +76,7 @@ build-i686-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - *ccache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64-win32
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - make --directory=src CCACHE=1 MINGW=1 PREFIX=i686-w64-mingw32
 
@@ -88,6 +88,6 @@ build-x86_64-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
     - *ccache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64-x86-64
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64-x86-64-win32
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
-- 
GitLab


From 567565968d0487e57ab04ca865cc38f52da54024 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 05:38:11 +0000
Subject: [PATCH 449/518] Update .gitlab-ci.yml file use
 gcc-mingw-w64-(i686|x86-64)-win32 package

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4498c1d85b..805ad781cd 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -76,7 +76,7 @@ build-i686-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - *ccache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64-win32
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64-i686-win32
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - make --directory=src CCACHE=1 MINGW=1 PREFIX=i686-w64-mingw32
 
-- 
GitLab


From 50c32451fb9b0353667c74c01109e615da14dee9 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 12:55:10 +0000
Subject: [PATCH 450/518] Update .gitlab-ci.yml file Try to build i686 linux
 binaries

---
 .gitlab-ci.yml | 50 ++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 36 insertions(+), 14 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 805ad781cd..97ec843725 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -16,13 +16,18 @@
 # This specific template is located at:
 # https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml
 
-
-
 stages:          # List of stages for jobs, and their order of execution
   - build
 
-.default_Scripts: &ccache
-  export CCACHE_BASEDIR="$PWD";export CCACHE_DIR="$PWD/ccache";export CCACHE_COMPILERCHECK=content
+.ccache_Scripts: &ccache
+  - export CCACHE_BASEDIR="$PWD"
+  - export CCACHE_DIR="$PWD/ccache"
+  - export CCACHE_COMPILERCHECK=content
+
+.aptcache_Scripts: &aptcache
+  - export APT_CACHE_DIR=`pwd`/apt-cache
+  - mkdir --parents --verbose $APT_CACHE_DIR/partial/
+  
 
 .job_template: &job_build # This job runs in the build stage, which runs first.
   stage: build
@@ -43,18 +48,34 @@ stages:          # List of stages for jobs, and their order of execution
         - apt-cache
       unprotect: true
   before_script:
+    - dpkg --add-architecture i386
+    - dpkg --add-architecture amd64
     - apt-get update
-    - export APT_CACHE_DIR=`pwd`/apt-cache
-    - mkdir --parents --verbose $APT_CACHE_DIR/partial/
+    - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes make git ccache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - *ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
+    - *aptcache
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - *ccache
     - ccache --show-stats
 
+build-i686-linux-gnu:
+  <<: *job_build
+  artifacts:
+    paths:
+      - "bin/lsdl2srb2*"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
+  script:
+    - *aptcache
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-i686-linux-gnu
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386 
+    - export CC="i686-linux-gnu-gcc"
+    - *ccache
+    - make --directory=src CCACHE=1 LINUX=1
+
 build-x86_64-linux-gnu:
   <<: *job_build
   artifacts:
@@ -62,10 +83,11 @@ build-x86_64-linux-gnu:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
-    - *ccache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
+    - *aptcache
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-x86-64-linux-gnu
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
     - export CC="x86_64-linux-gnu-gcc"
+    - *ccache
     - make --directory=src CCACHE=1 LINUX64=1
 
 build-i686-w64-mingw32:
@@ -75,9 +97,9 @@ build-i686-w64-mingw32:
       - "bin/srb2win.exe*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
-    - *ccache
+    - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64-i686-win32
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
+    - *ccache
     - make --directory=src CCACHE=1 MINGW=1 PREFIX=i686-w64-mingw32
 
 build-x86_64-w64-mingw32:
@@ -87,7 +109,7 @@ build-x86_64-w64-mingw32:
       - "bin/srb2win64.exe*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
-    - *ccache
+    - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64-x86-64-win32
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
+    - *ccache
     - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
-- 
GitLab


From 83628d6049b00c5e01b48de191d54eb65288bd2c Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 13:11:16 +0000
Subject: [PATCH 451/518] Update .gitlab-ci.yml file Set PKG_CONFIG_PATH for
 targeted archs

---
 .gitlab-ci.yml | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 97ec843725..76d62bab5a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -52,7 +52,7 @@ stages:          # List of stages for jobs, and their order of execution
     - dpkg --add-architecture amd64
     - apt-get update
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes make git ccache
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes make git ccache gcc
     - *ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
@@ -70,9 +70,10 @@ build-i686-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-i686-linux-gnu
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-i686-linux-gnu || true
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386 
     - export CC="i686-linux-gnu-gcc"
+    - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
     - *ccache
     - make --directory=src CCACHE=1 LINUX=1
 
@@ -84,9 +85,10 @@ build-x86_64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-x86-64-linux-gnu
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-x86-64-linux-gnu || true
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
     - export CC="x86_64-linux-gnu-gcc"
+    - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
     - *ccache
     - make --directory=src CCACHE=1 LINUX64=1
 
-- 
GitLab


From 7ba7c5e6a0f101538dfa2513a41d6d07239fe45e Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 16:17:10 +0000
Subject: [PATCH 452/518] Update .gitlab-ci.yml file Add build for ARM64

---
 .gitlab-ci.yml | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 76d62bab5a..83320aac1e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -77,6 +77,21 @@ build-i686-linux-gnu:
     - *ccache
     - make --directory=src CCACHE=1 LINUX=1
 
+build-aarch64-linux-gnu:
+  <<: *job_build
+  artifacts:
+    paths:
+      - "bin/lsdl2srb2*"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
+  script:
+    - *aptcache
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-aarch64-linux-gnu || true
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
+    - export CC="aarch64-linux-gnu-gcc"
+    - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
+    - *ccache
+    - make --directory=src CCACHE=1 LINUX64=1 NONX86=1
+
 build-x86_64-linux-gnu:
   <<: *job_build
   artifacts:
-- 
GitLab


From f5cf1ce5a5ab7f6fe7256cb20ba985b4cc35e3d4 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 16:53:32 +0000
Subject: [PATCH 453/518] Update .gitlab-ci.yml file Forget to add arm64 arch
 to the Debian system

---
 .gitlab-ci.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 83320aac1e..00072725b2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -50,6 +50,7 @@ stages:          # List of stages for jobs, and their order of execution
   before_script:
     - dpkg --add-architecture i386
     - dpkg --add-architecture amd64
+    - dpkg --add-architecture arm64
     - apt-get update
     - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes make git ccache gcc
-- 
GitLab


From 88ec9233b5973fe102f201bbfb4b9d517188e0ec Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 17:36:37 +0000
Subject: [PATCH 454/518] Update .gitlab-ci.yml file Overwrite OBJCOPY and
 OBJDUMP settings for Linux targets

---
 .gitlab-ci.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 00072725b2..5828037acb 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -76,7 +76,7 @@ build-i686-linux-gnu:
     - export CC="i686-linux-gnu-gcc"
     - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX=1
+    - make --directory=src CCACHE=1 LINUX=1 OBJCOPY=i686-linux-gnu-objcopy OBJDUMP=i686-linux-gnu-objdump
 
 build-aarch64-linux-gnu:
   <<: *job_build
@@ -91,7 +91,7 @@ build-aarch64-linux-gnu:
     - export CC="aarch64-linux-gnu-gcc"
     - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX64=1 NONX86=1
+    - make --directory=src CCACHE=1 LINUX64=1 NONX86=1 OBJCOPY=aarch64-linux-gnu-objcopy OBJDUMP=aarch64-linux-gnu-objdump
 
 build-x86_64-linux-gnu:
   <<: *job_build
@@ -106,7 +106,7 @@ build-x86_64-linux-gnu:
     - export CC="x86_64-linux-gnu-gcc"
     - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX64=1
+    - make --directory=src CCACHE=1 LINUX64=1 OBJCOPY=x86_64-linux-gnu-objcopy OBJDUMP=x86_64-linux-gnu-objdump
 
 build-i686-w64-mingw32:
   <<: *job_build
-- 
GitLab


From fa76733feeffb1184413e2dcd310449596e3fe23 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 18:01:44 +0000
Subject: [PATCH 455/518] Update .gitlab-ci.yml file let see if the Makefile
 system will just read from env

---
 .gitlab-ci.yml | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5828037acb..681c6e61f2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -73,10 +73,12 @@ build-i686-linux-gnu:
     - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-i686-linux-gnu || true
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386 
-    - export CC="i686-linux-gnu-gcc"
+    - export CC=i686-linux-gnu-gcc
+    - export OBJCOPY=i686-linux-gnu-objcopy
+    - export OBJDUMP=i686-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX=1 OBJCOPY=i686-linux-gnu-objcopy OBJDUMP=i686-linux-gnu-objdump
+    - make --directory=src CCACHE=1 LINUX=1
 
 build-aarch64-linux-gnu:
   <<: *job_build
@@ -88,10 +90,12 @@ build-aarch64-linux-gnu:
     - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-aarch64-linux-gnu || true
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
-    - export CC="aarch64-linux-gnu-gcc"
+    - export CC=aarch64-linux-gnu-gcc
+    - export OBJCOPY=aarch64-linux-gnu-objcopy
+    - export OBJDUMP=aarch64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX64=1 NONX86=1 OBJCOPY=aarch64-linux-gnu-objcopy OBJDUMP=aarch64-linux-gnu-objdump
+    - make --directory=src CCACHE=1 LINUX64=1 NONX86=1
 
 build-x86_64-linux-gnu:
   <<: *job_build
@@ -103,10 +107,12 @@ build-x86_64-linux-gnu:
     - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-x86-64-linux-gnu || true
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
-    - export CC="x86_64-linux-gnu-gcc"
+    - export CC=x86_64-linux-gnu-gcc
+    - export OBJCOPY=x86_64-linux-gnu-objcopy
+    - export OBJDUMP=x86_64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX64=1 OBJCOPY=x86_64-linux-gnu-objcopy OBJDUMP=x86_64-linux-gnu-objdump
+    - make --directory=src CCACHE=1 LINUX64=1
 
 build-i686-w64-mingw32:
   <<: *job_build
-- 
GitLab


From fe65f2b27150afb550919e7429574967a2c36e66 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 18:12:23 +0000
Subject: [PATCH 456/518] Update cpdebug.mk Allow Env to overwrite Makefile's
 default OBJCOPY and OBJDUMP, just like CC

---
 cpdebug.mk | 64 +++++++++++++++++++++++++++---------------------------
 1 file changed, 32 insertions(+), 32 deletions(-)

diff --git a/cpdebug.mk b/cpdebug.mk
index 6baedf2275..e7b0763039 100644
--- a/cpdebug.mk
+++ b/cpdebug.mk
@@ -1,32 +1,32 @@
-#Add-on Makefile for wxDev-C++ project file
-ifdef ComSpec
-COMSPEC=$(ComSpec)
-endif
-ifdef COMSPEC
-OBJCOPY=objcopy.exe
-OBJDUMP=objdump.exe
-GZIP?=gzip.exe
-else
-OBJCOPY=objcopy
-OBJDUMP=objdump
-GZIP?=gzip
-endif
-DBGNAME=$(BIN).debug
-OBJDUMP_OPTS?=--wide --source --line-numbers
-GZIP_OPTS?=-9 -f -n
-GZIP_OPT2=$(GZIP_OPTS) --rsyncable
-UPX?=upx
-UPX_OPTS?=--best --preserve-build-id
-UPX_OPTS+=-q
-
-all-after:
-	$(OBJDUMP) $(OBJDUMP_OPTS) "$(BIN)" > "$(DBGNAME).txt"
-	$(OBJCOPY) $(BIN) $(DBGNAME)
-	$(OBJCOPY) --strip-debug $(BIN)
-	-$(OBJCOPY) --add-gnu-debuglink=$(DBGNAME) $(BIN)
-	-$(GZIP) $(GZIP_OPTS) "$(DBGNAME).txt"
-ifndef COMSPEC
-	-$(GZIP) $(GZIP_OPT2) "$(DBGNAME).txt"
-endif
-	-$(UPX) $(UPX_OPTS) $(BIN)
-
+#Add-on Makefile for wxDev-C++ project file
+ifdef ComSpec
+COMSPEC=$(ComSpec)
+endif
+ifdef COMSPEC
+OBJCOPY?=objcopy.exe
+OBJDUMP?=objdump.exe
+GZIP?=gzip.exe
+else
+OBJCOPY?=objcopy
+OBJDUMP?=objdump
+GZIP?=gzip
+endif
+DBGNAME=$(BIN).debug
+OBJDUMP_OPTS?=--wide --source --line-numbers
+GZIP_OPTS?=-9 -f -n
+GZIP_OPT2=$(GZIP_OPTS) --rsyncable
+UPX?=upx
+UPX_OPTS?=--best --preserve-build-id
+UPX_OPTS+=-q
+
+all-after:
+	$(OBJDUMP) $(OBJDUMP_OPTS) "$(BIN)" > "$(DBGNAME).txt"
+	$(OBJCOPY) $(BIN) $(DBGNAME)
+	$(OBJCOPY) --strip-debug $(BIN)
+	-$(OBJCOPY) --add-gnu-debuglink=$(DBGNAME) $(BIN)
+	-$(GZIP) $(GZIP_OPTS) "$(DBGNAME).txt"
+ifndef COMSPEC
+	-$(GZIP) $(GZIP_OPT2) "$(DBGNAME).txt"
+endif
+	-$(UPX) $(UPX_OPTS) $(BIN)
+
-- 
GitLab


From 5449ca2dbfb3ddaa3ee6336b7b461757bf93fdba Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 18:25:02 +0000
Subject: [PATCH 457/518] Update .gitlab-ci.yml file force the Makefile system
 to use our cross-compile binutil package

---
 .gitlab-ci.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 681c6e61f2..e2ec249960 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -78,7 +78,7 @@ build-i686-linux-gnu:
     - export OBJDUMP=i686-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX=1
+    - make --directory=src CCACHE=1 OBJCOPY=$OBJCOPY OBJDUMP=$OBJDUMP LINUX=1
 
 build-aarch64-linux-gnu:
   <<: *job_build
@@ -95,7 +95,7 @@ build-aarch64-linux-gnu:
     - export OBJDUMP=aarch64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX64=1 NONX86=1
+    - make --directory=src CCACHE=1 OBJCOPY=$OBJCOPY OBJDUMP=$OBJDUMP LINUX64=1 NONX86=1
 
 build-x86_64-linux-gnu:
   <<: *job_build
@@ -112,7 +112,7 @@ build-x86_64-linux-gnu:
     - export OBJDUMP=x86_64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX64=1
+    - make --directory=src CCACHE=1 OBJCOPY=$OBJCOPY OBJDUMP=$OBJDUMP LINUX64=1
 
 build-i686-w64-mingw32:
   <<: *job_build
-- 
GitLab


From 4e2324216d6d3c276322fe4c4ef722221265c10f Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 19:40:35 -0400
Subject: [PATCH 458/518] Makefile Also allow overwrite of OBJCOPY, OBJDUMP and
 WINDRES

---
 .gitlab-ci.yml |  6 ++---
 cpdebug.mk     | 64 +++++++++++++++++++++++++-------------------------
 src/Makefile   |  6 ++---
 3 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e2ec249960..681c6e61f2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -78,7 +78,7 @@ build-i686-linux-gnu:
     - export OBJDUMP=i686-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 OBJCOPY=$OBJCOPY OBJDUMP=$OBJDUMP LINUX=1
+    - make --directory=src CCACHE=1 LINUX=1
 
 build-aarch64-linux-gnu:
   <<: *job_build
@@ -95,7 +95,7 @@ build-aarch64-linux-gnu:
     - export OBJDUMP=aarch64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 OBJCOPY=$OBJCOPY OBJDUMP=$OBJDUMP LINUX64=1 NONX86=1
+    - make --directory=src CCACHE=1 LINUX64=1 NONX86=1
 
 build-x86_64-linux-gnu:
   <<: *job_build
@@ -112,7 +112,7 @@ build-x86_64-linux-gnu:
     - export OBJDUMP=x86_64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 OBJCOPY=$OBJCOPY OBJDUMP=$OBJDUMP LINUX64=1
+    - make --directory=src CCACHE=1 LINUX64=1
 
 build-i686-w64-mingw32:
   <<: *job_build
diff --git a/cpdebug.mk b/cpdebug.mk
index e7b0763039..75f08c66f4 100644
--- a/cpdebug.mk
+++ b/cpdebug.mk
@@ -1,32 +1,32 @@
-#Add-on Makefile for wxDev-C++ project file
-ifdef ComSpec
-COMSPEC=$(ComSpec)
-endif
-ifdef COMSPEC
-OBJCOPY?=objcopy.exe
-OBJDUMP?=objdump.exe
-GZIP?=gzip.exe
-else
-OBJCOPY?=objcopy
-OBJDUMP?=objdump
-GZIP?=gzip
-endif
-DBGNAME=$(BIN).debug
-OBJDUMP_OPTS?=--wide --source --line-numbers
-GZIP_OPTS?=-9 -f -n
-GZIP_OPT2=$(GZIP_OPTS) --rsyncable
-UPX?=upx
-UPX_OPTS?=--best --preserve-build-id
-UPX_OPTS+=-q
-
-all-after:
-	$(OBJDUMP) $(OBJDUMP_OPTS) "$(BIN)" > "$(DBGNAME).txt"
-	$(OBJCOPY) $(BIN) $(DBGNAME)
-	$(OBJCOPY) --strip-debug $(BIN)
-	-$(OBJCOPY) --add-gnu-debuglink=$(DBGNAME) $(BIN)
-	-$(GZIP) $(GZIP_OPTS) "$(DBGNAME).txt"
-ifndef COMSPEC
-	-$(GZIP) $(GZIP_OPT2) "$(DBGNAME).txt"
-endif
-	-$(UPX) $(UPX_OPTS) $(BIN)
-
+#Add-on Makefile for wxDev-C++ project file
+ifdef ComSpec
+COMSPEC=$(ComSpec)
+endif
+ifdef COMSPEC
+OBJCOPY?=objcopy.exe
+OBJDUMP?=objdump.exe
+GZIP?=gzip.exe
+else
+OBJCOPY?=objcopy
+OBJDUMP?=objdump
+GZIP?=gzip
+endif
+DBGNAME=$(BIN).debug
+OBJDUMP_OPTS?=--wide --source --line-numbers
+GZIP_OPTS?=-9 -f -n
+GZIP_OPT2=$(GZIP_OPTS) --rsyncable
+UPX?=upx
+UPX_OPTS?=--best --preserve-build-id
+UPX_OPTS+=-q
+
+all-after:
+	$(OBJDUMP) $(OBJDUMP_OPTS) "$(BIN)" > "$(DBGNAME).txt"
+	$(OBJCOPY) $(BIN) $(DBGNAME)
+	$(OBJCOPY) --strip-debug $(BIN)
+	-$(OBJCOPY) --add-gnu-debuglink=$(DBGNAME) $(BIN)
+	-$(GZIP) $(GZIP_OPTS) "$(DBGNAME).txt"
+ifndef COMSPEC
+	-$(GZIP) $(GZIP_OPT2) "$(DBGNAME).txt"
+endif
+	-$(UPX) $(UPX_OPTS) $(BIN)
+
diff --git a/src/Makefile b/src/Makefile
index 41cef2a179..539c2fa743 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -141,9 +141,9 @@ endif
 
 OBJDUMP_OPTS?=--wide --source --line-numbers
 
-OBJCOPY:=$(call Prefix,objcopy)
-OBJDUMP:=$(call Prefix,objdump)
-WINDRES:=$(call Prefix,windres)
+OBJCOPY?=$(call Prefix,objcopy)
+OBJDUMP?=$(call Prefix,objdump)
+WINDRES?=$(call Prefix,windres)
 
 GZIP?=gzip
 GZIP_OPTS?=-9 -f -n
-- 
GitLab


From 903792f1090f5c0f72bae544c1d63a93dc50c80d Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 20:07:22 -0400
Subject: [PATCH 459/518] Update .gitlib-ci.yml Remove whitespace

---
 .gitlab-ci.yml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 681c6e61f2..bad819d34e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -27,7 +27,6 @@ stages:          # List of stages for jobs, and their order of execution
 .aptcache_Scripts: &aptcache
   - export APT_CACHE_DIR=`pwd`/apt-cache
   - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-  
 
 .job_template: &job_build # This job runs in the build stage, which runs first.
   stage: build
-- 
GitLab


From 008efa9b0edc00dca994d55c3b11c4454e4bd4dc Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 02:08:53 +0000
Subject: [PATCH 460/518] Update .gitlab-ci.yml file We do not need to install
 GCC for w64-mingw32 jobs

---
 .gitlab-ci.yml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bad819d34e..aa63365a5f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -52,7 +52,7 @@ stages:          # List of stages for jobs, and their order of execution
     - dpkg --add-architecture arm64
     - apt-get update
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes make git ccache gcc
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes make git ccache
     - *ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
@@ -70,7 +70,7 @@ build-i686-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-i686-linux-gnu || true
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-i686-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386 
     - export CC=i686-linux-gnu-gcc
     - export OBJCOPY=i686-linux-gnu-objcopy
@@ -87,7 +87,7 @@ build-aarch64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-aarch64-linux-gnu || true
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-aarch64-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
     - export CC=aarch64-linux-gnu-gcc
     - export OBJCOPY=aarch64-linux-gnu-objcopy
@@ -104,7 +104,7 @@ build-x86_64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-x86-64-linux-gnu || true
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-x86-64-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
     - export CC=x86_64-linux-gnu-gcc
     - export OBJCOPY=x86_64-linux-gnu-objcopy
-- 
GitLab


From 4f116673f3e03ab0e95b4e8ca15577a6902467ab Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 22:26:43 -0400
Subject: [PATCH 461/518] Undo src/Makefile Let see, revert OBJCOPY,OBJDUMP and
 WINDRES statements

---
 src/Makefile | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/Makefile b/src/Makefile
index 539c2fa743..41cef2a179 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -141,9 +141,9 @@ endif
 
 OBJDUMP_OPTS?=--wide --source --line-numbers
 
-OBJCOPY?=$(call Prefix,objcopy)
-OBJDUMP?=$(call Prefix,objdump)
-WINDRES?=$(call Prefix,windres)
+OBJCOPY:=$(call Prefix,objcopy)
+OBJDUMP:=$(call Prefix,objdump)
+WINDRES:=$(call Prefix,windres)
 
 GZIP?=gzip
 GZIP_OPTS?=-9 -f -n
-- 
GitLab


From dfe181058033d4f5a94ae2433ffd1cef33d69149 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 22:30:26 -0400
Subject: [PATCH 462/518] Revert "Undo src/Makefile"

This reverts commit 4f116673f3e03ab0e95b4e8ca15577a6902467ab.
---
 src/Makefile | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/Makefile b/src/Makefile
index 41cef2a179..539c2fa743 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -141,9 +141,9 @@ endif
 
 OBJDUMP_OPTS?=--wide --source --line-numbers
 
-OBJCOPY:=$(call Prefix,objcopy)
-OBJDUMP:=$(call Prefix,objdump)
-WINDRES:=$(call Prefix,windres)
+OBJCOPY?=$(call Prefix,objcopy)
+OBJDUMP?=$(call Prefix,objdump)
+WINDRES?=$(call Prefix,windres)
 
 GZIP?=gzip
 GZIP_OPTS?=-9 -f -n
-- 
GitLab


From 5acceaa2389b35307ecf973dcc0019426da198d2 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 02:46:00 +0000
Subject: [PATCH 463/518] Update .gitlab-ci.yml file Let see if we can pass an
 option for what architecture we are installing for

---
 .gitlab-ci.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index aa63365a5f..d5a92e5e6d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -71,7 +71,7 @@ build-i686-linux-gnu:
   script:
     - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-i686-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386 
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" --option APT::Architecture="i386" install --no-install-recommends --yes libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
     - export CC=i686-linux-gnu-gcc
     - export OBJCOPY=i686-linux-gnu-objcopy
     - export OBJDUMP=i686-linux-gnu-objdump
@@ -88,7 +88,7 @@ build-aarch64-linux-gnu:
   script:
     - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-aarch64-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" --option APT::Architecture="arm64" install --no-install-recommends --yes libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
     - export CC=aarch64-linux-gnu-gcc
     - export OBJCOPY=aarch64-linux-gnu-objcopy
     - export OBJDUMP=aarch64-linux-gnu-objdump
@@ -105,7 +105,7 @@ build-x86_64-linux-gnu:
   script:
     - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-x86-64-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" --option APT::Architecture="amd64" install --no-install-recommends --yes libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
     - export CC=x86_64-linux-gnu-gcc
     - export OBJCOPY=x86_64-linux-gnu-objcopy
     - export OBJDUMP=x86_64-linux-gnu-objdump
-- 
GitLab


From 8b33dd604b21b5c8e00082a50163df2e51ecb1b1 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 22:48:34 -0400
Subject: [PATCH 464/518] Revert "Update .gitlab-ci.yml file"

This reverts commit 5acceaa2389b35307ecf973dcc0019426da198d2.
---
 .gitlab-ci.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d5a92e5e6d..aa63365a5f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -71,7 +71,7 @@ build-i686-linux-gnu:
   script:
     - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-i686-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" --option APT::Architecture="i386" install --no-install-recommends --yes libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386 
     - export CC=i686-linux-gnu-gcc
     - export OBJCOPY=i686-linux-gnu-objcopy
     - export OBJDUMP=i686-linux-gnu-objdump
@@ -88,7 +88,7 @@ build-aarch64-linux-gnu:
   script:
     - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-aarch64-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" --option APT::Architecture="arm64" install --no-install-recommends --yes libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
     - export CC=aarch64-linux-gnu-gcc
     - export OBJCOPY=aarch64-linux-gnu-objcopy
     - export OBJDUMP=aarch64-linux-gnu-objdump
@@ -105,7 +105,7 @@ build-x86_64-linux-gnu:
   script:
     - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-x86-64-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" --option APT::Architecture="amd64" install --no-install-recommends --yes libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
     - export CC=x86_64-linux-gnu-gcc
     - export OBJCOPY=x86_64-linux-gnu-objcopy
     - export OBJDUMP=x86_64-linux-gnu-objdump
-- 
GitLab


From 0368a1ce8b95169d0663bb4ebdfc196273411fae Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 12 Oct 2023 22:49:44 -0400
Subject: [PATCH 465/518] cleanup whitespace in .gitlab-ci.yml

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index aa63365a5f..f1ec1edabc 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -71,7 +71,7 @@ build-i686-linux-gnu:
   script:
     - *aptcache
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-i686-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386 
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
     - export CC=i686-linux-gnu-gcc
     - export OBJCOPY=i686-linux-gnu-objcopy
     - export OBJDUMP=i686-linux-gnu-objdump
-- 
GitLab


From aa9d84b6d23d31f28474e86d05dad9b2f98a7c5d Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 16:13:31 +0000
Subject: [PATCH 466/518] Update .gitlab-ci.yml file

Let try building on Debian Testing
---
 .gitlab-ci.yml | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f1ec1edabc..cacb68744b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -28,9 +28,11 @@ stages:          # List of stages for jobs, and their order of execution
   - export APT_CACHE_DIR=`pwd`/apt-cache
   - mkdir --parents --verbose $APT_CACHE_DIR/partial/
 
-.job_template: &job_build # This job runs in the build stage, which runs first.
-  stage: build
+default:
   image: debian:stable-slim
+
+.job_template: &job_build # This job runs in the build stage, which runs first.
+  stage: build  
   variables:
     CCACHE_MAXSIZE: "50M"
     GIT_STRATEGY: clone
@@ -136,3 +138,18 @@ build-x86_64-w64-mingw32:
     - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64-x86-64-win32
     - *ccache
     - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
+
+build-testing:
+  <<: *job_build
+  image: debian:testing-slim
+  allow_failure: true
+  artifacts:
+    paths:
+      - "bin/lsdl2srb2*"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
+  script:
+    - *aptcache
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
+    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+    - *ccache
+    - make --directory=src CCACHE=1 NONX86=1
\ No newline at end of file
-- 
GitLab


From f6d37d504b2eb73589dc99153b385454e3cd54a9 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 16:37:06 +0000
Subject: [PATCH 467/518] Update .gitlab-ci.yml file

quiet out the apt-get commands
---
 .gitlab-ci.yml | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cacb68744b..927ad705e6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -52,15 +52,15 @@ default:
     - dpkg --add-architecture i386
     - dpkg --add-architecture amd64
     - dpkg --add-architecture arm64
-    - apt-get update
+    - apt-get --quiet update
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes make git ccache
+    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install make git ccache
     - *ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" autoclean
+    - apt-get --quiet --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - *ccache
     - ccache --show-stats
 
@@ -72,8 +72,8 @@ build-i686-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-i686-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
+    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-i686-linux-gnu || apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
+    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
     - export CC=i686-linux-gnu-gcc
     - export OBJCOPY=i686-linux-gnu-objcopy
     - export OBJDUMP=i686-linux-gnu-objdump
@@ -89,8 +89,8 @@ build-aarch64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-aarch64-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
+    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-aarch64-linux-gnu || apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install  gcc
+    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
     - export CC=aarch64-linux-gnu-gcc
     - export OBJCOPY=aarch64-linux-gnu-objcopy
     - export OBJDUMP=aarch64-linux-gnu-objdump
@@ -106,8 +106,8 @@ build-x86_64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-x86-64-linux-gnu || apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
+    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-x86-64-linux-gnu || apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
+    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
     - export CC=x86_64-linux-gnu-gcc
     - export OBJCOPY=x86_64-linux-gnu-objcopy
     - export OBJDUMP=x86_64-linux-gnu-objdump
@@ -123,7 +123,7 @@ build-i686-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64-i686-win32
+    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-i686-win32
     - *ccache
     - make --directory=src CCACHE=1 MINGW=1 PREFIX=i686-w64-mingw32
 
@@ -135,7 +135,7 @@ build-x86_64-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc-mingw-w64-x86-64-win32
+    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-x86-64-win32
     - *ccache
     - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
 
@@ -149,7 +149,7 @@ build-testing:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
   script:
     - *aptcache
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes gcc
-    - apt-get --option dir::cache::archives="$APT_CACHE_DIR" install --no-install-recommends --yes libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
+    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
     - *ccache
     - make --directory=src CCACHE=1 NONX86=1
\ No newline at end of file
-- 
GitLab


From b8fb0280ab322a54ea7fa57873b0b383ed8660f1 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 16:45:02 +0000
Subject: [PATCH 468/518] Update .gitlab-ci.yml file

more quiet out the apt-get commands
---
 .gitlab-ci.yml | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 927ad705e6..1efb639fb5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -52,15 +52,15 @@ default:
     - dpkg --add-architecture i386
     - dpkg --add-architecture amd64
     - dpkg --add-architecture arm64
-    - apt-get --quiet update
+    - apt-get --quiet --quiet update
     - *aptcache
-    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install make git ccache
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install make git ccache
     - *ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
     - *aptcache
-    - apt-get --quiet --option dir::cache::archives="$APT_CACHE_DIR" autoclean
+    - apt-get --quiet --quiet --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - *ccache
     - ccache --show-stats
 
@@ -72,8 +72,8 @@ build-i686-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-i686-linux-gnu || apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
-    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-i686-linux-gnu || apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
     - export CC=i686-linux-gnu-gcc
     - export OBJCOPY=i686-linux-gnu-objcopy
     - export OBJDUMP=i686-linux-gnu-objdump
@@ -89,8 +89,8 @@ build-aarch64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-aarch64-linux-gnu || apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install  gcc
-    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-aarch64-linux-gnu || apt-get --no-install-recommend --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install  gcc
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
     - export CC=aarch64-linux-gnu-gcc
     - export OBJCOPY=aarch64-linux-gnu-objcopy
     - export OBJDUMP=aarch64-linux-gnu-objdump
@@ -106,8 +106,8 @@ build-x86_64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-x86-64-linux-gnu || apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
-    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-x86-64-linux-gnu || apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
     - export CC=x86_64-linux-gnu-gcc
     - export OBJCOPY=x86_64-linux-gnu-objcopy
     - export OBJDUMP=x86_64-linux-gnu-objdump
@@ -123,7 +123,7 @@ build-i686-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-i686-win32
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-i686-win32
     - *ccache
     - make --directory=src CCACHE=1 MINGW=1 PREFIX=i686-w64-mingw32
 
@@ -135,7 +135,7 @@ build-x86_64-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-x86-64-win32
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-x86-64-win32
     - *ccache
     - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
 
@@ -149,7 +149,7 @@ build-testing:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
-    - apt-get --no-install-recommends --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
     - *ccache
     - make --directory=src CCACHE=1 NONX86=1
\ No newline at end of file
-- 
GitLab


From a6038dd6f2a226cd439c3bfb750b4308caaeae92 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 19:49:43 +0000
Subject: [PATCH 469/518] Update detect.mk

Add support for GCC 13.2
---
 src/Makefile.d/detect.mk | 215 ++++++++++++++++++++-------------------
 1 file changed, 109 insertions(+), 106 deletions(-)

diff --git a/src/Makefile.d/detect.mk b/src/Makefile.d/detect.mk
index aca4987213..4719d65934 100644
--- a/src/Makefile.d/detect.mk
+++ b/src/Makefile.d/detect.mk
@@ -1,106 +1,109 @@
-#
-# Detect the host system and compiler version.
-#
-
-# Previously featured:\
-	PANDORA\
-	HAIKU\
-	DUMMY\
-	DJGPPDOS\
-	SOLARIS\
-	MACOSX\
-
-all_systems:=\
-	LINUX64\
-	MINGW64\
-	MINGW\
-	UNIX\
-	LINUX\
-	FREEBSD\
-
-# check for user specified system
-ifeq (,$(filter $(all_systems),$(.VARIABLES)))
-ifeq ($(OS),Windows_NT) # all windows are Windows_NT...
-
-_m=Detected a Windows system,\
-	compiling for 32-bit MinGW SDL...)
-$(call Print,$(_m))
-
-# go for a 32-bit sdl mingw exe by default
-MINGW:=1
-
-else # if you on the *nix
-
-system:=$(shell uname -s)
-
-ifeq ($(system),Linux)
-new_system:=LINUX
-else
-
-$(error \
-	Could not automatically detect your system,\
-	try specifying a system manually)
-
-endif
-
-ifeq ($(shell getconf LONG_BIT),64)
-system+=64-bit
-new_system:=$(new_system)64
-endif
-
-$(call Print,Detected $(system) ($(new_system))...)
-$(new_system):=1
-
-endif
-endif
-
-# This must have high to low order.
-gcc_versions:=\
-	102 101\
-	93 92 91\
-	84 83 82 81\
-	75 74 73 72 71\
-	64 63 62 61\
-	55 54 53 52 51\
-	49 48 47 46 45 44 43 42 41 40
-
-latest_gcc_version:=10.2
-
-# Automatically set version flag, but not if one was
-# manually set. And don't bother if this is a clean only
-# run.
-ifeq (,$(call Wildvar,GCC% destructive))
-
-# can't use $(CC) --version here since that uses argv[0] to display the name
-# also gcc outputs the information to stderr, so I had to do 2>&1
-# this program really doesn't like identifying itself
-version:=$(shell $(CC) -v 2>&1)
-
-# check if this is in fact GCC
-ifneq (,$(findstring gcc version,$(version)))
-
-# in stark contrast to the name, gcc will give me a nicely formatted version number for free
-version:=$(shell $(CC) -dumpfullversion)
-
-# Turn version into words of major, minor
-v:=$(subst ., ,$(version))
-# concat. major minor
-v:=$(word 1,$(v))$(word 2,$(v))
-
-# If this version is not in the list,
-# default to the latest supported
-ifeq (,$(filter $(v),$(gcc_versions)))
-define line =
-Your compiler version, GCC $(version), \
-is not supported by the Makefile.
-The Makefile will assume GCC $(latest_gcc_version).
-endef
-$(call Print,$(line))
-GCC$(subst .,,$(latest_gcc_version)):=1
-else
-$(call Print,Detected GCC $(version) (GCC$(v)))
-GCC$(v):=1
-endif
-
-endif
-endif
+#
+# Detect the host system and compiler version.
+#
+
+# Previously featured:\
+	PANDORA\
+	HAIKU\
+	DUMMY\
+	DJGPPDOS\
+	SOLARIS\
+	MACOSX\
+
+all_systems:=\
+	LINUX64\
+	MINGW64\
+	MINGW\
+	UNIX\
+	LINUX\
+	FREEBSD\
+
+# check for user specified system
+ifeq (,$(filter $(all_systems),$(.VARIABLES)))
+ifeq ($(OS),Windows_NT) # all windows are Windows_NT...
+
+_m=Detected a Windows system,\
+	compiling for 32-bit MinGW SDL...)
+$(call Print,$(_m))
+
+# go for a 32-bit sdl mingw exe by default
+MINGW:=1
+
+else # if you on the *nix
+
+system:=$(shell uname -s)
+
+ifeq ($(system),Linux)
+new_system:=LINUX
+else
+
+$(error \
+	Could not automatically detect your system,\
+	try specifying a system manually)
+
+endif
+
+ifeq ($(shell getconf LONG_BIT),64)
+system+=64-bit
+new_system:=$(new_system)64
+endif
+
+$(call Print,Detected $(system) ($(new_system))...)
+$(new_system):=1
+
+endif
+endif
+
+# This must have high to low order.
+gcc_versions:=\
+	132 131\
+    123 122 121\
+    114 113 112 111\
+	105 104 103 102 101\
+	95 94 93 92 91\
+	85 84 83 82 81\
+	75 74 73 72 71\
+	64 63 62 61\
+	55 54 53 52 51\
+	49 48 47 46 45 44 43 42 41 40
+
+latest_gcc_version:=13.2
+
+# Automatically set version flag, but not if one was
+# manually set. And don't bother if this is a clean only
+# run.
+ifeq (,$(call Wildvar,GCC% destructive))
+
+# can't use $(CC) --version here since that uses argv[0] to display the name
+# also gcc outputs the information to stderr, so I had to do 2>&1
+# this program really doesn't like identifying itself
+version:=$(shell $(CC) -v 2>&1)
+
+# check if this is in fact GCC
+ifneq (,$(findstring gcc version,$(version)))
+
+# in stark contrast to the name, gcc will give me a nicely formatted version number for free
+version:=$(shell $(CC) -dumpfullversion)
+
+# Turn version into words of major, minor
+v:=$(subst ., ,$(version))
+# concat. major minor
+v:=$(word 1,$(v))$(word 2,$(v))
+
+# If this version is not in the list,
+# default to the latest supported
+ifeq (,$(filter $(v),$(gcc_versions)))
+define line =
+Your compiler version, GCC $(version), \
+is not supported by the Makefile.
+The Makefile will assume GCC $(latest_gcc_version).
+endef
+$(call Print,$(line))
+GCC$(subst .,,$(latest_gcc_version)):=1
+else
+$(call Print,Detected GCC $(version) (GCC$(v)))
+GCC$(v):=1
+endif
+
+endif
+endif
-- 
GitLab


From fb299dd63e6f4cb0067538ee07807e13db118c8d Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 20:09:00 +0000
Subject: [PATCH 470/518] Update .gitlab-ci.yml file

Make the buildbot yell at us for warnings
---
 .gitlab-ci.yml | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1efb639fb5..ec1e7dc0a4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -54,6 +54,7 @@ default:
     - dpkg --add-architecture arm64
     - apt-get --quiet --quiet update
     - *aptcache
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install apt-utils
     - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install make git ccache
     - *ccache
     - ccache --zero-stats || true
@@ -79,7 +80,7 @@ build-i686-linux-gnu:
     - export OBJDUMP=i686-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX=1
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
 
 build-aarch64-linux-gnu:
   <<: *job_build
@@ -96,7 +97,7 @@ build-aarch64-linux-gnu:
     - export OBJDUMP=aarch64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX64=1 NONX86=1
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
 
 build-x86_64-linux-gnu:
   <<: *job_build
@@ -113,7 +114,7 @@ build-x86_64-linux-gnu:
     - export OBJDUMP=x86_64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src CCACHE=1 LINUX64=1
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
 
 build-i686-w64-mingw32:
   <<: *job_build
@@ -125,7 +126,7 @@ build-i686-w64-mingw32:
     - *aptcache
     - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-i686-win32
     - *ccache
-    - make --directory=src CCACHE=1 MINGW=1 PREFIX=i686-w64-mingw32
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
 
 build-x86_64-w64-mingw32:
   <<: *job_build
@@ -137,7 +138,7 @@ build-x86_64-w64-mingw32:
     - *aptcache
     - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-x86-64-win32
     - *ccache
-    - make --directory=src CCACHE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
 
 build-testing:
   <<: *job_build
@@ -152,4 +153,4 @@ build-testing:
     - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
     - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
     - *ccache
-    - make --directory=src CCACHE=1 NONX86=1
\ No newline at end of file
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
\ No newline at end of file
-- 
GitLab


From af020810bf0a1a500222a01e674c2a6a7fbe656f Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 17:02:33 -0400
Subject: [PATCH 471/518] fix compiling for GCC 11+

---
 src/Makefile.d/detect.mk | 4 ++--
 src/p_map.c              | 2 ++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/Makefile.d/detect.mk b/src/Makefile.d/detect.mk
index 4719d65934..ab6268757f 100644
--- a/src/Makefile.d/detect.mk
+++ b/src/Makefile.d/detect.mk
@@ -57,8 +57,8 @@ endif
 # This must have high to low order.
 gcc_versions:=\
 	132 131\
-    123 122 121\
-    114 113 112 111\
+	123 122 121\
+	114 113 112 111\
 	105 104 103 102 101\
 	95 94 93 92 91\
 	85 84 83 82 81\
diff --git a/src/p_map.c b/src/p_map.c
index 80135db747..a9d2cf45d5 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -3732,6 +3732,8 @@ void P_SlideMove(mobj_t *mo)
 	vertex_t v1, v2; // fake vertexes
 	line_t junk; // fake linedef
 
+	memset(&junk, 1, sizeof(junk));
+
 	if (tmhitthing && mo->z + mo->height > tmhitthing->z && mo->z < tmhitthing->z + tmhitthing->height)
 	{
 		// Don't mess with your momentum if it's a pushable object. Pushables do their own crazy things already.
-- 
GitLab


From 796adec979834ca55ba2d2fa547fe8b4105800ce Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 21:40:41 +0000
Subject: [PATCH 472/518] Update .gitlab-ci.yml file

Retry building so we point out why it failed to compile
---
 .gitlab-ci.yml | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ec1e7dc0a4..4d59ef1a61 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -37,6 +37,7 @@ default:
     CCACHE_MAXSIZE: "50M"
     GIT_STRATEGY: clone
     GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
+    ENV DEBIAN_FRONTEND: noninteractive
   cache:
     - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG
       fallback_keys:
@@ -80,7 +81,7 @@ build-i686-linux-gnu:
     - export OBJDUMP=i686-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
 
 build-aarch64-linux-gnu:
   <<: *job_build
@@ -97,7 +98,7 @@ build-aarch64-linux-gnu:
     - export OBJDUMP=aarch64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
 
 build-x86_64-linux-gnu:
   <<: *job_build
@@ -114,7 +115,7 @@ build-x86_64-linux-gnu:
     - export OBJDUMP=x86_64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
 
 build-i686-w64-mingw32:
   <<: *job_build
@@ -126,7 +127,7 @@ build-i686-w64-mingw32:
     - *aptcache
     - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-i686-win32
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
 
 build-x86_64-w64-mingw32:
   <<: *job_build
@@ -138,7 +139,7 @@ build-x86_64-w64-mingw32:
     - *aptcache
     - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-x86-64-win32
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
 
 build-testing:
   <<: *job_build
@@ -153,4 +154,4 @@ build-testing:
     - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
     - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
\ No newline at end of file
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
\ No newline at end of file
-- 
GitLab


From 96beee1d4d61c8b6cd1413db8a6e8c4b5f80acbf Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 21:43:15 +0000
Subject: [PATCH 473/518] Update .gitlab-ci.yml file

set DEBIAN_FRONTEND envvar in aptcache
---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4d59ef1a61..347aeea943 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -27,6 +27,7 @@ stages:          # List of stages for jobs, and their order of execution
 .aptcache_Scripts: &aptcache
   - export APT_CACHE_DIR=`pwd`/apt-cache
   - mkdir --parents --verbose $APT_CACHE_DIR/partial/
+  - export DEBIAN_FRONTEND=noninteractive
 
 default:
   image: debian:stable-slim
@@ -37,7 +38,6 @@ default:
     CCACHE_MAXSIZE: "50M"
     GIT_STRATEGY: clone
     GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
-    ENV DEBIAN_FRONTEND: noninteractive
   cache:
     - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG
       fallback_keys:
-- 
GitLab


From 1bf78686e27fb61449ea652a9bdc70f9c0cb8754 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 19:33:31 -0400
Subject: [PATCH 474/518] let not pass a point of a temp stack var around

---
 src/p_map.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_map.c b/src/p_map.c
index a9d2cf45d5..2911e4d402 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -3730,7 +3730,7 @@ void P_SlideMove(mobj_t *mo)
 
 	boolean papercol = false;
 	vertex_t v1, v2; // fake vertexes
-	line_t junk; // fake linedef
+	static line_t junk; // fake linedef
 
 	memset(&junk, 1, sizeof(junk));
 
-- 
GitLab


From fc586b5c62bf34212b51dc245427f33c204e4607 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 23:46:44 +0000
Subject: [PATCH 475/518] Update .gitlab-ci.yml file

Build in the following order: testing, win32, amd64, i386, arm64 then win64
---
 .gitlab-ci.yml | 74 +++++++++++++++++++++++++-------------------------
 1 file changed, 37 insertions(+), 37 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 347aeea943..9f3229d5c6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -66,39 +66,32 @@ default:
     - *ccache
     - ccache --show-stats
 
-build-i686-linux-gnu:
+build-testing:
   <<: *job_build
+  image: debian:testing-slim
+  allow_failure: true
   artifacts:
     paths:
       - "bin/lsdl2srb2*"
-    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-i686-linux-gnu || apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
-    - export CC=i686-linux-gnu-gcc
-    - export OBJCOPY=i686-linux-gnu-objcopy
-    - export OBJDUMP=i686-linux-gnu-objdump
-    - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
 
-build-aarch64-linux-gnu:
+build-i686-w64-mingw32:
   <<: *job_build
   artifacts:
     paths:
-      - "bin/lsdl2srb2*"
-    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
+      - "bin/srb2win.exe*"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-aarch64-linux-gnu || apt-get --no-install-recommend --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install  gcc
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
-    - export CC=aarch64-linux-gnu-gcc
-    - export OBJCOPY=aarch64-linux-gnu-objcopy
-    - export OBJDUMP=aarch64-linux-gnu-objdump
-    - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-i686-win32
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
 
 build-x86_64-linux-gnu:
   <<: *job_build
@@ -117,41 +110,48 @@ build-x86_64-linux-gnu:
     - *ccache
     - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
 
-build-i686-w64-mingw32:
+build-i686-linux-gnu:
   <<: *job_build
   artifacts:
     paths:
-      - "bin/srb2win.exe*"
-    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
+      - "bin/lsdl2srb2*"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-i686-win32
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-i686-linux-gnu || apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
+    - export CC=i686-linux-gnu-gcc
+    - export OBJCOPY=i686-linux-gnu-objcopy
+    - export OBJDUMP=i686-linux-gnu-objdump
+    - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
 
-build-x86_64-w64-mingw32:
+build-aarch64-linux-gnu:
   <<: *job_build
   artifacts:
     paths:
-      - "bin/srb2win64.exe*"
-    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
+      - "bin/lsdl2srb2*"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-x86-64-win32
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-aarch64-linux-gnu || apt-get --no-install-recommend --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install  gcc
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
+    - export CC=aarch64-linux-gnu-gcc
+    - export OBJCOPY=aarch64-linux-gnu-objcopy
+    - export OBJDUMP=aarch64-linux-gnu-objdump
+    - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
 
-build-testing:
+build-x86_64-w64-mingw32:
   <<: *job_build
-  image: debian:testing-slim
-  allow_failure: true
   artifacts:
     paths:
-      - "bin/lsdl2srb2*"
-    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
+      - "bin/srb2win64.exe*"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-x86-64-win32
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
\ No newline at end of file
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
\ No newline at end of file
-- 
GitLab


From 8f00667abec65a57f3b2a5581b9bbf42536d2846 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 20:10:34 -0400
Subject: [PATCH 476/518] Update src/Makefile.d/*.mk

both Makefile and *.mk should be in the same EOL
---
 .gitattributes             |   4 +-
 .gitlab-ci.yml             |   4 +-
 src/Makefile.d/detect.mk   | 218 ++++++++++++++++++-------------------
 src/Makefile.d/features.mk | 136 +++++++++++------------
 4 files changed, 182 insertions(+), 180 deletions(-)

diff --git a/.gitattributes b/.gitattributes
index 7751149ac0..c2e507352e 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,15 +1,17 @@
 #Source code
+/Makefile text=auto
 /src/*.c text=auto
 /src/*.h text=auto
 /src/*.s text=auto
 /src/*.m text=auto
 /src/*.xpm text=auto
 /src/Makefile text=auto
+/tools/Makefile text=auto
 /src/Make*.cfg text=auto
 /src/CMakeLists.txt text=auto
+*.mk -whitespace text=auto
 # Windows EOL
 *.cs -crlf -whitespace
-*.mk -crlf -whitespace
 *.bat -crlf -whitespace
 *.dev -crlf -whitespace
 *.dsp -crlf -whitespace
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9f3229d5c6..7ea13db6ad 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -33,7 +33,7 @@ default:
   image: debian:stable-slim
 
 .job_template: &job_build # This job runs in the build stage, which runs first.
-  stage: build  
+  stage: build
   variables:
     CCACHE_MAXSIZE: "50M"
     GIT_STRATEGY: clone
@@ -154,4 +154,4 @@ build-x86_64-w64-mingw32:
     - *aptcache
     - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-x86-64-win32
     - *ccache
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
\ No newline at end of file
+    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
diff --git a/src/Makefile.d/detect.mk b/src/Makefile.d/detect.mk
index ab6268757f..0cd618c691 100644
--- a/src/Makefile.d/detect.mk
+++ b/src/Makefile.d/detect.mk
@@ -1,109 +1,109 @@
-#
-# Detect the host system and compiler version.
-#
-
-# Previously featured:\
-	PANDORA\
-	HAIKU\
-	DUMMY\
-	DJGPPDOS\
-	SOLARIS\
-	MACOSX\
-
-all_systems:=\
-	LINUX64\
-	MINGW64\
-	MINGW\
-	UNIX\
-	LINUX\
-	FREEBSD\
-
-# check for user specified system
-ifeq (,$(filter $(all_systems),$(.VARIABLES)))
-ifeq ($(OS),Windows_NT) # all windows are Windows_NT...
-
-_m=Detected a Windows system,\
-	compiling for 32-bit MinGW SDL...)
-$(call Print,$(_m))
-
-# go for a 32-bit sdl mingw exe by default
-MINGW:=1
-
-else # if you on the *nix
-
-system:=$(shell uname -s)
-
-ifeq ($(system),Linux)
-new_system:=LINUX
-else
-
-$(error \
-	Could not automatically detect your system,\
-	try specifying a system manually)
-
-endif
-
-ifeq ($(shell getconf LONG_BIT),64)
-system+=64-bit
-new_system:=$(new_system)64
-endif
-
-$(call Print,Detected $(system) ($(new_system))...)
-$(new_system):=1
-
-endif
-endif
-
-# This must have high to low order.
-gcc_versions:=\
-	132 131\
-	123 122 121\
-	114 113 112 111\
-	105 104 103 102 101\
-	95 94 93 92 91\
-	85 84 83 82 81\
-	75 74 73 72 71\
-	64 63 62 61\
-	55 54 53 52 51\
-	49 48 47 46 45 44 43 42 41 40
-
-latest_gcc_version:=13.2
-
-# Automatically set version flag, but not if one was
-# manually set. And don't bother if this is a clean only
-# run.
-ifeq (,$(call Wildvar,GCC% destructive))
-
-# can't use $(CC) --version here since that uses argv[0] to display the name
-# also gcc outputs the information to stderr, so I had to do 2>&1
-# this program really doesn't like identifying itself
-version:=$(shell $(CC) -v 2>&1)
-
-# check if this is in fact GCC
-ifneq (,$(findstring gcc version,$(version)))
-
-# in stark contrast to the name, gcc will give me a nicely formatted version number for free
-version:=$(shell $(CC) -dumpfullversion)
-
-# Turn version into words of major, minor
-v:=$(subst ., ,$(version))
-# concat. major minor
-v:=$(word 1,$(v))$(word 2,$(v))
-
-# If this version is not in the list,
-# default to the latest supported
-ifeq (,$(filter $(v),$(gcc_versions)))
-define line =
-Your compiler version, GCC $(version), \
-is not supported by the Makefile.
-The Makefile will assume GCC $(latest_gcc_version).
-endef
-$(call Print,$(line))
-GCC$(subst .,,$(latest_gcc_version)):=1
-else
-$(call Print,Detected GCC $(version) (GCC$(v)))
-GCC$(v):=1
-endif
-
-endif
-endif
+#
+# Detect the host system and compiler version.
+#
+
+# Previously featured:\
+	PANDORA\
+	HAIKU\
+	DUMMY\
+	DJGPPDOS\
+	SOLARIS\
+	MACOSX\
+
+all_systems:=\
+	LINUX64\
+	MINGW64\
+	MINGW\
+	UNIX\
+	LINUX\
+	FREEBSD\
+
+# check for user specified system
+ifeq (,$(filter $(all_systems),$(.VARIABLES)))
+ifeq ($(OS),Windows_NT) # all windows are Windows_NT...
+
+_m=Detected a Windows system,\
+	compiling for 32-bit MinGW SDL...)
+$(call Print,$(_m))
+
+# go for a 32-bit sdl mingw exe by default
+MINGW:=1
+
+else # if you on the *nix
+
+system:=$(shell uname -s)
+
+ifeq ($(system),Linux)
+new_system:=LINUX
+else
+
+$(error \
+	Could not automatically detect your system,\
+	try specifying a system manually)
+
+endif
+
+ifeq ($(shell getconf LONG_BIT),64)
+system+=64-bit
+new_system:=$(new_system)64
+endif
+
+$(call Print,Detected $(system) ($(new_system))...)
+$(new_system):=1
+
+endif
+endif
+
+# This must have high to low order.
+gcc_versions:=\
+	132 131\
+	123 122 121\
+	114 113 112 111\
+	105 104 103 102 101\
+	95 94 93 92 91\
+	85 84 83 82 81\
+	75 74 73 72 71\
+	64 63 62 61\
+	55 54 53 52 51\
+	49 48 47 46 45 44 43 42 41 40
+
+latest_gcc_version:=13.2
+
+# Automatically set version flag, but not if one was
+# manually set. And don't bother if this is a clean only
+# run.
+ifeq (,$(call Wildvar,GCC% destructive))
+
+# can't use $(CC) --version here since that uses argv[0] to display the name
+# also gcc outputs the information to stderr, so I had to do 2>&1
+# this program really doesn't like identifying itself
+version:=$(shell $(CC) -v 2>&1)
+
+# check if this is in fact GCC
+ifneq (,$(findstring gcc version,$(version)))
+
+# in stark contrast to the name, gcc will give me a nicely formatted version number for free
+version:=$(shell $(CC) -dumpfullversion)
+
+# Turn version into words of major, minor
+v:=$(subst ., ,$(version))
+# concat. major minor
+v:=$(word 1,$(v))$(word 2,$(v))
+
+# If this version is not in the list,
+# default to the latest supported
+ifeq (,$(filter $(v),$(gcc_versions)))
+define line =
+Your compiler version, GCC $(version), \
+is not supported by the Makefile.
+The Makefile will assume GCC $(latest_gcc_version).
+endef
+$(call Print,$(line))
+GCC$(subst .,,$(latest_gcc_version)):=1
+else
+$(call Print,Detected GCC $(version) (GCC$(v)))
+GCC$(v):=1
+endif
+
+endif
+endif
diff --git a/src/Makefile.d/features.mk b/src/Makefile.d/features.mk
index 1787f94cb8..653100cb54 100644
--- a/src/Makefile.d/features.mk
+++ b/src/Makefile.d/features.mk
@@ -1,68 +1,68 @@
-#
-# Makefile for feature flags.
-#
-
-passthru_opts+=\
-	NONET NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\
-	MOBJCONSISTANCY PACKETDROP ZDEBUG\
-	HAVE_MINIUPNPC\
-
-# build with debugging information
-ifdef DEBUGMODE
-PACKETDROP=1
-opts+=-DPARANOIA -DRANGECHECK
-endif
-
-ifndef NOHW
-opts+=-DHWRENDER
-sources+=$(call List,hardware/Sourcefile)
-endif
-
-ifndef NOMD5
-sources+=md5.c
-endif
-
-ifndef NOZLIB
-ifndef NOPNG
-ifdef PNG_PKGCONFIG
-$(eval $(call Use_pkg_config,PNG_PKGCONFIG))
-else
-PNG_CONFIG?=$(call Prefix,libpng-config)
-$(eval $(call Configure,PNG,$(PNG_CONFIG) \
-	$(if $(PNG_STATIC),--static),,--ldflags))
-endif
-ifdef LINUX
-opts+=-D_LARGEFILE64_SOURCE
-endif
-opts+=-DHAVE_PNG
-sources+=apng.c
-endif
-endif
-
-ifndef NONET
-ifndef NOCURL
-CURLCONFIG?=curl-config
-$(eval $(call Configure,CURL,$(CURLCONFIG)))
-opts+=-DHAVE_CURL
-endif
-endif
-
-ifdef HAVE_MINIUPNPC
-libs+=-lminiupnpc
-endif
-
-# (Valgrind is a memory debugger.)
-ifdef VALGRIND
-VALGRIND_PKGCONFIG?=valgrind
-$(eval $(call Use_pkg_config,VALGRIND))
-ZDEBUG=1
-opts+=-DHAVE_VALGRIND
-endif
-
-default_packages:=\
-	GME/libgme/LIBGME\
-	OPENMPT/libopenmpt/LIBOPENMPT\
-	ZLIB/zlib\
-
-$(foreach p,$(default_packages),\
-	$(eval $(call Check_pkg_config,$(p))))
+#
+# Makefile for feature flags.
+#
+
+passthru_opts+=\
+	NONET NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\
+	MOBJCONSISTANCY PACKETDROP ZDEBUG\
+	HAVE_MINIUPNPC\
+
+# build with debugging information
+ifdef DEBUGMODE
+PACKETDROP=1
+opts+=-DPARANOIA -DRANGECHECK
+endif
+
+ifndef NOHW
+opts+=-DHWRENDER
+sources+=$(call List,hardware/Sourcefile)
+endif
+
+ifndef NOMD5
+sources+=md5.c
+endif
+
+ifndef NOZLIB
+ifndef NOPNG
+ifdef PNG_PKGCONFIG
+$(eval $(call Use_pkg_config,PNG_PKGCONFIG))
+else
+PNG_CONFIG?=$(call Prefix,libpng-config)
+$(eval $(call Configure,PNG,$(PNG_CONFIG) \
+	$(if $(PNG_STATIC),--static),,--ldflags))
+endif
+ifdef LINUX
+opts+=-D_LARGEFILE64_SOURCE
+endif
+opts+=-DHAVE_PNG
+sources+=apng.c
+endif
+endif
+
+ifndef NONET
+ifndef NOCURL
+CURLCONFIG?=curl-config
+$(eval $(call Configure,CURL,$(CURLCONFIG)))
+opts+=-DHAVE_CURL
+endif
+endif
+
+ifdef HAVE_MINIUPNPC
+libs+=-lminiupnpc
+endif
+
+# (Valgrind is a memory debugger.)
+ifdef VALGRIND
+VALGRIND_PKGCONFIG?=valgrind
+$(eval $(call Use_pkg_config,VALGRIND))
+ZDEBUG=1
+opts+=-DHAVE_VALGRIND
+endif
+
+default_packages:=\
+	GME/libgme/LIBGME\
+	OPENMPT/libopenmpt/LIBOPENMPT\
+	ZLIB/zlib\
+
+$(foreach p,$(default_packages),\
+	$(eval $(call Check_pkg_config,$(p))))
-- 
GitLab


From d3d3ee54519660a74de6161cb0c90140b9ec1642 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 01:37:05 +0000
Subject: [PATCH 477/518] Update .gitlab-ci.yml file

Try to keep a stats log of ccache
---
 .gitlab-ci.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7ea13db6ad..b0f82607c2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -23,6 +23,7 @@ stages:          # List of stages for jobs, and their order of execution
   - export CCACHE_BASEDIR="$PWD"
   - export CCACHE_DIR="$PWD/ccache"
   - export CCACHE_COMPILERCHECK=content
+  - export CCACHE_STATSLOG=CCACHE_DIR="$PWD/ccache_statslog"
 
 .aptcache_Scripts: &aptcache
   - export APT_CACHE_DIR=`pwd`/apt-cache
@@ -35,7 +36,7 @@ default:
 .job_template: &job_build # This job runs in the build stage, which runs first.
   stage: build
   variables:
-    CCACHE_MAXSIZE: "50M"
+    CCACHE_MAXSIZE: 50M
     GIT_STRATEGY: clone
     GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
   cache:
@@ -65,6 +66,7 @@ default:
     - apt-get --quiet --quiet --option dir::cache::archives="$APT_CACHE_DIR" autoclean
     - *ccache
     - ccache --show-stats
+    - ccache --show-log-stats || true
 
 build-testing:
   <<: *job_build
-- 
GitLab


From 2fe5755f531f13c9689aac1ba727aa842bdfba1f Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 01:44:23 +0000
Subject: [PATCH 478/518] Update .gitlab-ci.yml file

Fix CCACHE_STATSLOG?
---
 .gitlab-ci.yml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b0f82607c2..ccf1907811 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -23,7 +23,8 @@ stages:          # List of stages for jobs, and their order of execution
   - export CCACHE_BASEDIR="$PWD"
   - export CCACHE_DIR="$PWD/ccache"
   - export CCACHE_COMPILERCHECK=content
-  - export CCACHE_STATSLOG=CCACHE_DIR="$PWD/ccache_statslog"
+  - export CCACHE_STATS=true
+  - export CCACHE_STATSLOG="$PWD/ccache_statslog"
 
 .aptcache_Scripts: &aptcache
   - export APT_CACHE_DIR=`pwd`/apt-cache
-- 
GitLab


From 98fd34e76c843f248de13a898b1912cdce20a95b Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 02:11:52 +0000
Subject: [PATCH 479/518] Update .gitlab-ci.yml file

save ccache_statslog between runs?
---
 .gitlab-ci.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ccf1907811..82c8516011 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -47,6 +47,7 @@ default:
         - cache-$CI_PROJECT_PATH_SLUG-default
       paths:
         - ccache
+        - ccache_statslog
     - key: apt-$CI_JOB_IMAGE
       paths:
         - apt-cache
-- 
GitLab


From 8533955da808295bfa0825ec8d932e0225f2f944 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 13 Oct 2023 22:50:19 -0400
Subject: [PATCH 480/518] Update src/p_map.c

it seems line_t have pointers, clear all of it
---
 src/p_map.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_map.c b/src/p_map.c
index 2911e4d402..251837876c 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -3732,7 +3732,7 @@ void P_SlideMove(mobj_t *mo)
 	vertex_t v1, v2; // fake vertexes
 	static line_t junk; // fake linedef
 
-	memset(&junk, 1, sizeof(junk));
+	memset(&junk, 0x00, sizeof(junk));
 
 	if (tmhitthing && mo->z + mo->height > tmhitthing->z && mo->z < tmhitthing->z + tmhitthing->height)
 	{
-- 
GitLab


From b469064e40290850c8e660e30f8f33697cfc6d85 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 03:11:56 +0000
Subject: [PATCH 481/518] Update .gitlab-ci.yml file

remove double space
---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 82c8516011..4eb4b48f49 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -139,7 +139,7 @@ build-aarch64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-aarch64-linux-gnu || apt-get --no-install-recommend --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install  gcc
+    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-aarch64-linux-gnu || apt-get --no-install-recommend --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
     - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
     - export CC=aarch64-linux-gnu-gcc
     - export OBJCOPY=aarch64-linux-gnu-objcopy
-- 
GitLab


From d659ce563ccbd239abbf38a1598538f3b184c4cc Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 11:40:25 +0000
Subject: [PATCH 482/518] Update .gitlab-ci.yml file

Set common apt settings to /etc/apt/apt.conf.d/99build
---
 .gitlab-ci.yml | 55 +++++++++++++++++++-------------------------------
 1 file changed, 21 insertions(+), 34 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4eb4b48f49..754da90a1c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,21 +1,3 @@
-# This file is a template, and might need editing before it works on your project.
-# This is a sample GitLab CI/CD configuration file that should run without any modifications.
-# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
-# it uses echo commands to simulate the pipeline execution.
-#
-# A pipeline is composed of independent jobs that run scripts, grouped into stages.
-# Stages run in sequential order, but jobs within stages run in parallel.
-#
-# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages
-#
-# You can copy and paste this template into a new `.gitlab-ci.yml` file.
-# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
-#
-# To contribute improvements to CI/CD templates, please follow the Development guide at:
-# https://docs.gitlab.com/ee/development/cicd/templates.html
-# This specific template is located at:
-# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml
-
 stages:          # List of stages for jobs, and their order of execution
   - build
 
@@ -23,12 +5,10 @@ stages:          # List of stages for jobs, and their order of execution
   - export CCACHE_BASEDIR="$PWD"
   - export CCACHE_DIR="$PWD/ccache"
   - export CCACHE_COMPILERCHECK=content
-  - export CCACHE_STATS=true
   - export CCACHE_STATSLOG="$PWD/ccache_statslog"
 
 .aptcache_Scripts: &aptcache
   - export APT_CACHE_DIR=`pwd`/apt-cache
-  - mkdir --parents --verbose $APT_CACHE_DIR/partial/
   - export DEBIAN_FRONTEND=noninteractive
 
 default:
@@ -56,16 +36,23 @@ default:
     - dpkg --add-architecture i386
     - dpkg --add-architecture amd64
     - dpkg --add-architecture arm64
-    - apt-get --quiet --quiet update
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install apt-utils
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install make git ccache
+    - touch /etc/apt/apt.conf.d/99build
+    - echo Adding options to apt.conf':'
+    - echo APT::Install-Recommends "false"\;       | tee --append /etc/apt/apt.conf.d/99build
+    - echo quiet "2"\;                             | tee --append /etc/apt/apt.conf.d/99build
+    - echo APT::Get::Assume-Yes "true"\;           | tee --append /etc/apt/apt.conf.d/99build
+    - echo Dir::Cache::Archives "$APT_CACHE_DIR"\; | tee --append /etc/apt/apt.conf.d/99build
+    - apt-get update
+    - mkdir --parents --verbose $APT_CACHE_DIR/partial/
+    - apt-get install apt-utils
+    - apt-get install make git ccache
     - *ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
     - *aptcache
-    - apt-get --quiet --quiet --option dir::cache::archives="$APT_CACHE_DIR" autoclean
+    - apt-get autoclean
     - *ccache
     - ccache --show-stats
     - ccache --show-log-stats || true
@@ -80,8 +67,8 @@ build-testing:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+    - apt-get install gcc
+    - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
     - *ccache
     - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
 
@@ -93,7 +80,7 @@ build-i686-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-i686-win32
+    - apt-get install gcc-mingw-w64-i686-win32
     - *ccache
     - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
 
@@ -105,8 +92,8 @@ build-x86_64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-x86-64-linux-gnu || apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
+    - apt-get install gcc-x86-64-linux-gnu || apt-get gcc
+    - apt-get libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
     - export CC=x86_64-linux-gnu-gcc
     - export OBJCOPY=x86_64-linux-gnu-objcopy
     - export OBJDUMP=x86_64-linux-gnu-objdump
@@ -122,8 +109,8 @@ build-i686-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-i686-linux-gnu || apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
+    - apt-get gcc-i686-linux-gnu || apt-get install gcc
+    - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
     - export CC=i686-linux-gnu-gcc
     - export OBJCOPY=i686-linux-gnu-objcopy
     - export OBJDUMP=i686-linux-gnu-objdump
@@ -139,8 +126,8 @@ build-aarch64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-aarch64-linux-gnu || apt-get --no-install-recommend --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
+    - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
+    - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
     - export CC=aarch64-linux-gnu-gcc
     - export OBJCOPY=aarch64-linux-gnu-objcopy
     - export OBJDUMP=aarch64-linux-gnu-objdump
@@ -156,6 +143,6 @@ build-x86_64-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
     - *aptcache
-    - apt-get --no-install-recommends --quiet --quiet --yes --option dir::cache::archives="$APT_CACHE_DIR" install gcc-mingw-w64-x86-64-win32
+    - apt-get install gcc-mingw-w64-x86-64-win32
     - *ccache
     - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
-- 
GitLab


From 52e4ed33af2bbdc8328c41d2885b7bd382fdb88b Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 11:54:16 +0000
Subject: [PATCH 483/518] Update .gitlab-ci.yml file

Fix install command for GCC in build jobs
---
 .gitlab-ci.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 754da90a1c..e10ad1bd5e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -92,8 +92,8 @@ build-x86_64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - *aptcache
-    - apt-get install gcc-x86-64-linux-gnu || apt-get gcc
-    - apt-get libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
+    - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
+    - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
     - export CC=x86_64-linux-gnu-gcc
     - export OBJCOPY=x86_64-linux-gnu-objcopy
     - export OBJDUMP=x86_64-linux-gnu-objdump
@@ -109,7 +109,7 @@ build-i686-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
     - *aptcache
-    - apt-get gcc-i686-linux-gnu || apt-get install gcc
+    - apt-get install gcc-i686-linux-gnu || apt-get install gcc
     - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
     - export CC=i686-linux-gnu-gcc
     - export OBJCOPY=i686-linux-gnu-objcopy
-- 
GitLab


From 806c8f259cb8cbec347ef5f1c4e59d138131e559 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 08:23:03 -0400
Subject: [PATCH 484/518] Update src/Makefile.d/detect.mk

Support Mingw64 toolchain versions
---
 src/Makefile.d/detect.mk | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/src/Makefile.d/detect.mk b/src/Makefile.d/detect.mk
index 0cd618c691..9e27369462 100644
--- a/src/Makefile.d/detect.mk
+++ b/src/Makefile.d/detect.mk
@@ -56,15 +56,15 @@ endif
 
 # This must have high to low order.
 gcc_versions:=\
-	132 131\
-	123 122 121\
-	114 113 112 111\
-	105 104 103 102 101\
-	95 94 93 92 91\
-	85 84 83 82 81\
-	75 74 73 72 71\
-	64 63 62 61\
-	55 54 53 52 51\
+	132 131 130\
+	123 122 121 120\
+	114 113 112 111 110\
+	105 104 103 102 101 100\
+	95 94 93 92 91 90\
+	85 84 83 82 81 80\
+	75 74 73 72 71 70\
+	64 63 62 61 60\
+	55 54 53 52 51 50\
 	49 48 47 46 45 44 43 42 41 40
 
 latest_gcc_version:=13.2
@@ -77,13 +77,18 @@ ifeq (,$(call Wildvar,GCC% destructive))
 # can't use $(CC) --version here since that uses argv[0] to display the name
 # also gcc outputs the information to stderr, so I had to do 2>&1
 # this program really doesn't like identifying itself
-version:=$(shell $(CC) -v 2>&1)
+shellversion:=$(shell $(CC) -v 2>&1)
+# Try to remove "-win32"
+version:=$(subst -win32,.0,$(shellversion))
 
 # check if this is in fact GCC
 ifneq (,$(findstring gcc version,$(version)))
 
 # in stark contrast to the name, gcc will give me a nicely formatted version number for free
-version:=$(shell $(CC) -dumpfullversion)
+shellversion:=$(shell $(CC) -dumpfullversion)
+
+# Try to remove "-win32"
+version:=$(subst -win32,.0,$(shellversion))
 
 # Turn version into words of major, minor
 v:=$(subst ., ,$(version))
-- 
GitLab


From ff3993257a46bc1f913a28015015b9d62dc94904 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 13:01:32 +0000
Subject: [PATCH 485/518] Update .gitlab-ci.yml file

place sections around "apt-get install" and "make" commands
---
 .gitlab-ci.yml | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e10ad1bd5e..c35824c8cf 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -45,8 +45,12 @@ default:
     - echo Dir::Cache::Archives "$APT_CACHE_DIR"\; | tee --append /etc/apt/apt.conf.d/99build
     - apt-get update
     - mkdir --parents --verbose $APT_CACHE_DIR/partial/
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_pre[collapsed=true]\r\e[0Kinstalling pre packages"
     - apt-get install apt-utils
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_pre\r\e[0K"
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0Kinstalling common packages "
     - apt-get install make git ccache
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_common\r\e[0K"
     - *ccache
     - ccache --zero-stats || true
     - ccache --show-stats || true
@@ -67,10 +71,16 @@ build-testing:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
   script:
     - *aptcache
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
     - apt-get install gcc
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
     - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
     - *ccache
+    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
     - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
+    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-w64-mingw32:
   <<: *job_build
@@ -80,9 +90,13 @@ build-i686-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - *aptcache
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages
     - apt-get install gcc-mingw-w64-i686-win32
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
     - *ccache
+    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
     - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
+    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-linux-gnu:
   <<: *job_build
@@ -92,14 +106,20 @@ build-x86_64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - *aptcache
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages
     - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
     - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
     - export CC=x86_64-linux-gnu-gcc
     - export OBJCOPY=x86_64-linux-gnu-objcopy
     - export OBJDUMP=x86_64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
     - *ccache
+    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
     - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
+    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-linux-gnu:
   <<: *job_build
@@ -109,14 +129,20 @@ build-i686-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
     - *aptcache
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages
     - apt-get install gcc-i686-linux-gnu || apt-get install gcc
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
     - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
     - export CC=i686-linux-gnu-gcc
     - export OBJCOPY=i686-linux-gnu-objcopy
     - export OBJDUMP=i686-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
     - *ccache
+    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
     - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
+    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-aarch64-linux-gnu:
   <<: *job_build
@@ -126,14 +152,20 @@ build-aarch64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
     - *aptcache
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages
     - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
     - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
     - export CC=aarch64-linux-gnu-gcc
     - export OBJCOPY=aarch64-linux-gnu-objcopy
     - export OBJDUMP=aarch64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
     - *ccache
+    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
     - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
+    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-w64-mingw32:
   <<: *job_build
@@ -143,6 +175,10 @@ build-x86_64-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
     - *aptcache
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages
     - apt-get install gcc-mingw-w64-x86-64-win32
+    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
     - *ccache
+    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
     - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
+    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
-- 
GitLab


From 1a0fad75f9014bd1b169ad0429c68f0eeca97d77 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 13:09:59 +0000
Subject: [PATCH 486/518] Update .gitlab-ci.yml file

fixup EOL on section commands
---
 .gitlab-ci.yml | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c35824c8cf..e65a6f41eb 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -90,7 +90,7 @@ build-i686-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - *aptcache
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
     - apt-get install gcc-mingw-w64-i686-win32
     - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
     - *ccache
@@ -106,7 +106,7 @@ build-x86_64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - *aptcache
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
     - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
     - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
     - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
@@ -129,7 +129,7 @@ build-i686-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
     - *aptcache
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
     - apt-get install gcc-i686-linux-gnu || apt-get install gcc
     - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
     - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
@@ -152,7 +152,7 @@ build-aarch64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
     - *aptcache
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
     - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
     - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
     - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
@@ -175,7 +175,7 @@ build-x86_64-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
     - *aptcache
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
     - apt-get install gcc-mingw-w64-x86-64-win32
     - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
     - *ccache
-- 
GitLab


From c17c5327a8ba7247aef0f93446ac766d91d2ceff Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 13:11:49 +0000
Subject: [PATCH 487/518] Update .gitlab-ci.yml file

remove extra space
---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e65a6f41eb..203f44fdb6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -48,7 +48,7 @@ default:
     - echo -e "\e[0Ksection_start:`date +%s`:apt_pre[collapsed=true]\r\e[0Kinstalling pre packages"
     - apt-get install apt-utils
     - echo -e "\e[0Ksection_end:`date +%s`:apt_pre\r\e[0K"
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0Kinstalling common packages "
+    - echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0Kinstalling common packages"
     - apt-get install make git ccache
     - echo -e "\e[0Ksection_end:`date +%s`:apt_common\r\e[0K"
     - *ccache
-- 
GitLab


From 1886b9f9458e503eb05436b549501186822daa2e Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 14:18:23 +0000
Subject: [PATCH 488/518] Update .gitlab-ci.yml file

Add more sections to the build log
---
 .gitlab-ci.yml | 178 ++++++++++++++++++++++++++-----------------------
 1 file changed, 95 insertions(+), 83 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 203f44fdb6..333ab25bae 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,15 +1,9 @@
 stages:          # List of stages for jobs, and their order of execution
   - build
 
-.ccache_Scripts: &ccache
-  - export CCACHE_BASEDIR="$PWD"
-  - export CCACHE_DIR="$PWD/ccache"
-  - export CCACHE_COMPILERCHECK=content
-  - export CCACHE_STATSLOG="$PWD/ccache_statslog"
-
 .aptcache_Scripts: &aptcache
-  - export APT_CACHE_DIR=`pwd`/apt-cache
-  - export DEBIAN_FRONTEND=noninteractive
+  export APT_CACHE_DIR=`pwd`/apt-cache;
+  export DEBIAN_FRONTEND=noninteractive;
 
 default:
   image: debian:stable-slim
@@ -17,7 +11,6 @@ default:
 .job_template: &job_build # This job runs in the build stage, which runs first.
   stage: build
   variables:
-    CCACHE_MAXSIZE: 50M
     GIT_STRATEGY: clone
     GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
   cache:
@@ -33,31 +26,56 @@ default:
         - apt-cache
       unprotect: true
   before_script:
-    - dpkg --add-architecture i386
-    - dpkg --add-architecture amd64
-    - dpkg --add-architecture arm64
-    - *aptcache
-    - touch /etc/apt/apt.conf.d/99build
-    - echo Adding options to apt.conf':'
-    - echo APT::Install-Recommends "false"\;       | tee --append /etc/apt/apt.conf.d/99build
-    - echo quiet "2"\;                             | tee --append /etc/apt/apt.conf.d/99build
-    - echo APT::Get::Assume-Yes "true"\;           | tee --append /etc/apt/apt.conf.d/99build
-    - echo Dir::Cache::Archives "$APT_CACHE_DIR"\; | tee --append /etc/apt/apt.conf.d/99build
-    - apt-get update
-    - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_pre[collapsed=true]\r\e[0Kinstalling pre packages"
-    - apt-get install apt-utils
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_pre\r\e[0K"
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0Kinstalling common packages"
-    - apt-get install make git ccache
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_common\r\e[0K"
-    - *ccache
+    - - echo -e "\e[0Ksection_start:`date +%s`:dpkg_aa[collapsed=true]\r\e[0KAdding architectures to dpkg"
+      - dpkg --add-architecture i386
+      - dpkg --add-architecture amd64
+      - dpkg --add-architecture arm64
+      - echo -e "\e[0Ksection_end:`date +%s`:dpkg_aa\r\e[0K"
+
+    - - echo -e "\e[0Ksection_start:`date +%s`:ac_pre[collapsed=true]\r\e[0KSetting up APT cache"
+      - *aptcache
+      - echo -e "\e[0Ksection_end:`date +%s`:ac_pre\r\e[0K"
+
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_conf[collapsed=true]\r\e[0KSetting up APT conf"
+      - touch /etc/apt/apt.conf.d/99build
+      - echo Adding options to apt.conf':'
+      - echo APT::Install-Recommends "false"\;       | tee --append /etc/apt/apt.conf.d/99build
+      - echo quiet "1"\;                             | tee --append /etc/apt/apt.conf.d/99build
+      - echo APT::Get::Assume-Yes "true"\;           | tee --append /etc/apt/apt.conf.d/99build
+      - echo Dir::Cache::Archives "$APT_CACHE_DIR"\; | tee --append /etc/apt/apt.conf.d/99build
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_conf\r\e[0K"
+
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_update[collapsed=true]\r\e[0KUpdating APT listing"
+      - apt-get update
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_update\r\e[0K"
+
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_cache[collapsed=true]\r\e[0KMaking APT cache directory"
+      - mkdir --parents --verbose $APT_CACHE_DIR/partial/
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_cache\r\e[0K"
+
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_pre[collapsed=true]\r\e[0KInstalling pre packages"
+      - apt-get install apt-utils
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_pre\r\e[0K"
+
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0KInstalling common packages"
+      - apt-get install make git ccache
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_common\r\e[0K"
+
+    - - echo -e "\e[0Ksection_start:`date +%s`:ccache_config[collapsed=true]\r\e[0KSetting up ccache config"
+      - echo Adding ccache configution option
+      - touch ~/.ccache/ccache.conf
+      - echo base_dir = "$PWD"                  | tee ~/.ccache/ccache.conf
+      - echo cache_dir = "$PWD/ccache"          | tee ~/.ccache/ccache.conf
+      - echo compiler_check = content           | tee ~/.ccache/ccache.conf
+      - echo stats_log = "$PWD/ccache_statslog" | tee ~/.ccache/ccache.conf
+      - echo max_size = 50M                     | tee ~/.ccache/ccache.conf
+      - echo -e "\e[0Ksection_end:`date +%s`:ccache_config\r\e[0K"
+
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
     - *aptcache
     - apt-get autoclean
-    - *ccache
     - ccache --show-stats
     - ccache --show-log-stats || true
 
@@ -71,16 +89,15 @@ build-testing:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
   script:
     - *aptcache
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
-    - apt-get install gcc
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
-    - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
-    - *ccache
-    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
-    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - apt-get install gcc
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
+      - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
+      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-w64-mingw32:
   <<: *job_build
@@ -90,13 +107,12 @@ build-i686-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - *aptcache
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
-    - apt-get install gcc-mingw-w64-i686-win32
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
-    - *ccache
-    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
-    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - apt-get install gcc-mingw-w64-i686-win32
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
+      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-linux-gnu:
   <<: *job_build
@@ -106,20 +122,19 @@ build-x86_64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - *aptcache
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
-    - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
-    - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
+      - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
     - export CC=x86_64-linux-gnu-gcc
     - export OBJCOPY=x86_64-linux-gnu-objcopy
     - export OBJDUMP=x86_64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
-    - *ccache
-    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
-    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
+      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-linux-gnu:
   <<: *job_build
@@ -129,20 +144,19 @@ build-i686-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
     - *aptcache
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
-    - apt-get install gcc-i686-linux-gnu || apt-get install gcc
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
-    - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - apt-get install gcc-i686-linux-gnu || apt-get install gcc
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
+      - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
     - export CC=i686-linux-gnu-gcc
     - export OBJCOPY=i686-linux-gnu-objcopy
     - export OBJDUMP=i686-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
-    - *ccache
-    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
-    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
+      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-aarch64-linux-gnu:
   <<: *job_build
@@ -152,20 +166,19 @@ build-aarch64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
     - *aptcache
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
-    - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
-    - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
+      - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
     - export CC=aarch64-linux-gnu-gcc
     - export OBJCOPY=aarch64-linux-gnu-objcopy
     - export OBJDUMP=aarch64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
-    - *ccache
-    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
-    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
+      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-w64-mingw32:
   <<: *job_build
@@ -175,10 +188,9 @@ build-x86_64-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
     - *aptcache
-    - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
-    - apt-get install gcc-mingw-w64-x86-64-win32
-    - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
-    - *ccache
-    - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
-    - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
-    - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - apt-get install gcc-mingw-w64-x86-64-win32
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
+      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
-- 
GitLab


From 2e4d5b6fa9d31b761546ef56c575cff435521eb3 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 14:29:57 +0000
Subject: [PATCH 489/518] Update .gitlab-ci.yml file

mkdir ~/.ccache before make the config file
---
 .gitlab-ci.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 333ab25bae..5447fe7f8c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -63,6 +63,7 @@ default:
 
     - - echo -e "\e[0Ksection_start:`date +%s`:ccache_config[collapsed=true]\r\e[0KSetting up ccache config"
       - echo Adding ccache configution option
+      - mkdir --parents --verbose ~/.ccache
       - touch ~/.ccache/ccache.conf
       - echo base_dir = "$PWD"                  | tee ~/.ccache/ccache.conf
       - echo cache_dir = "$PWD/ccache"          | tee ~/.ccache/ccache.conf
-- 
GitLab


From e6044ec9f10888d9b47fb486c46aa060567b3746 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 15:05:50 +0000
Subject: [PATCH 490/518] Update .gitlab-ci.yml file

append, not overwrite ccache.conf
---
 .gitlab-ci.yml | 43 +++++++++++++++++++++++++++++--------------
 1 file changed, 29 insertions(+), 14 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5447fe7f8c..d8a6f42472 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -63,20 +63,22 @@ default:
 
     - - echo -e "\e[0Ksection_start:`date +%s`:ccache_config[collapsed=true]\r\e[0KSetting up ccache config"
       - echo Adding ccache configution option
-      - mkdir --parents --verbose ~/.ccache
       - touch ~/.ccache/ccache.conf
-      - echo base_dir = "$PWD"                  | tee ~/.ccache/ccache.conf
-      - echo cache_dir = "$PWD/ccache"          | tee ~/.ccache/ccache.conf
-      - echo compiler_check = content           | tee ~/.ccache/ccache.conf
-      - echo stats_log = "$PWD/ccache_statslog" | tee ~/.ccache/ccache.conf
-      - echo max_size = 50M                     | tee ~/.ccache/ccache.conf
+      - echo base_dir = $PWD                  | tee --append ~/.ccache/ccache.conf
+      - echo cache_dir = $PWD/ccache          | tee --append ~/.ccache/ccache.conf
+      - echo compiler_check = content         | tee --append ~/.ccache/ccache.conf
+      - echo stats_log = $PWD/ccache_statslog | tee --append ~/.ccache/ccache.conf
+      - echo max_size = 50M                   | tee --append ~/.ccache/ccache.conf
       - echo -e "\e[0Ksection_end:`date +%s`:ccache_config\r\e[0K"
 
     - ccache --zero-stats || true
     - ccache --show-stats || true
   after_script:
-    - *aptcache
-    - apt-get autoclean
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_clean[collapsed=true]\r\e[0KCleaning of unneeded APT packages"
+      - *aptcache
+      - apt-get autoclean
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_clean\r\e[0K"
+
     - ccache --show-stats
     - ccache --show-log-stats || true
 
@@ -89,13 +91,15 @@ build-testing:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
   script:
-    - *aptcache
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - *aptcache
       - apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
       - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
@@ -107,10 +111,11 @@ build-i686-w64-mingw32:
       - "bin/srb2win.exe*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
-    - *aptcache
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - *aptcache
       - apt-get install gcc-mingw-w64-i686-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
@@ -122,17 +127,20 @@ build-x86_64-linux-gnu:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
-    - *aptcache
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - *aptcache
       - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
       - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+
     - export CC=x86_64-linux-gnu-gcc
     - export OBJCOPY=x86_64-linux-gnu-objcopy
     - export OBJDUMP=x86_64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
+
     - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
@@ -144,17 +152,20 @@ build-i686-linux-gnu:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
-    - *aptcache
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - *aptcache
       - apt-get install gcc-i686-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
       - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+
     - export CC=i686-linux-gnu-gcc
     - export OBJCOPY=i686-linux-gnu-objcopy
     - export OBJDUMP=i686-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
+
     - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
@@ -166,17 +177,20 @@ build-aarch64-linux-gnu:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
-    - *aptcache
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - *aptcache
       - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
       - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+
     - export CC=aarch64-linux-gnu-gcc
     - export OBJCOPY=aarch64-linux-gnu-objcopy
     - export OBJDUMP=aarch64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
+
     - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
@@ -188,10 +202,11 @@ build-x86_64-w64-mingw32:
       - "bin/srb2win64.exe*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
-    - *aptcache
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+      - *aptcache
       - apt-get install gcc-mingw-w64-x86-64-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
-- 
GitLab


From dee306c58b71a25aa25b2dc171d024637a37afad Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 15:25:04 +0000
Subject: [PATCH 491/518] Update .gitlab-ci.yml file

I need to make the .ccache folder
---
 .gitlab-ci.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d8a6f42472..a196ce2817 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -63,6 +63,7 @@ default:
 
     - - echo -e "\e[0Ksection_start:`date +%s`:ccache_config[collapsed=true]\r\e[0KSetting up ccache config"
       - echo Adding ccache configution option
+      - mkdir --parents --verbose ~/.ccache/
       - touch ~/.ccache/ccache.conf
       - echo base_dir = $PWD                  | tee --append ~/.ccache/ccache.conf
       - echo cache_dir = $PWD/ccache          | tee --append ~/.ccache/ccache.conf
-- 
GitLab


From c74711fefa1f4233ee444dfa51bc9e04801c89f9 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 19:51:11 +0000
Subject: [PATCH 492/518] Update .gitlab-ci.yml file

section off ccache output
---
 .gitlab-ci.yml | 32 ++++++++++++++++++--------------
 1 file changed, 18 insertions(+), 14 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a196ce2817..ea0d6668ff 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -72,16 +72,20 @@ default:
       - echo max_size = 50M                   | tee --append ~/.ccache/ccache.conf
       - echo -e "\e[0Ksection_end:`date +%s`:ccache_config\r\e[0K"
 
-    - ccache --zero-stats || true
-    - ccache --show-stats || true
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_reset[collapsed=true]\r\e[0KResetting ccache statistics"
+      - ccache --zero-stats || true
+      - ccache --show-stats || true
+      - echo -e "\e[0Ksection_end:`date +%s`:ccache_reset\r\e[0K"
   after_script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_clean[collapsed=true]\r\e[0KCleaning of unneeded APT packages"
       - *aptcache
       - apt-get autoclean
       - echo -e "\e[0Ksection_end:`date +%s`:apt_clean\r\e[0K"
 
-    - ccache --show-stats
-    - ccache --show-log-stats || true
+    - - echo -e "\e[0Ksection_start:`date +%s`:ccache_stats[collapsed=false]\r\e[0Kccache statistics:"
+      - ccache --show-stats --verbose
+      - ccache --show-log-stats --verbose || true
+      - echo -e "\e[0Ksection_end:`date +%s`:ccache_stats\r\e[0K"
 
 build-testing:
   <<: *job_build
@@ -92,12 +96,12 @@ build-testing:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - *aptcache
       - apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
@@ -112,7 +116,7 @@ build-i686-w64-mingw32:
       - "bin/srb2win.exe*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - *aptcache
       - apt-get install gcc-mingw-w64-i686-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
@@ -128,12 +132,12 @@ build-x86_64-linux-gnu:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - *aptcache
       - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
@@ -153,12 +157,12 @@ build-i686-linux-gnu:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - *aptcache
       - apt-get install gcc-i686-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
@@ -178,12 +182,12 @@ build-aarch64-linux-gnu:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - *aptcache
       - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0Kinstalling development packages"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
@@ -203,7 +207,7 @@ build-x86_64-w64-mingw32:
       - "bin/srb2win64.exe*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0Kinstalling toolchain packages"
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - *aptcache
       - apt-get install gcc-mingw-w64-x86-64-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
-- 
GitLab


From 50d8cb4c00de891b8d13a06e36b6104a241a7f81 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sat, 14 Oct 2023 21:08:13 +0000
Subject: [PATCH 493/518] Update .gitlab-ci.yml file

hide ccache statistics
---
 .gitlab-ci.yml | 45 +++++++++++++++------------------------------
 1 file changed, 15 insertions(+), 30 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ea0d6668ff..206d1d8887 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,10 +1,6 @@
 stages:          # List of stages for jobs, and their order of execution
   - build
 
-.aptcache_Scripts: &aptcache
-  export APT_CACHE_DIR=`pwd`/apt-cache;
-  export DEBIAN_FRONTEND=noninteractive;
-
 default:
   image: debian:stable-slim
 
@@ -13,6 +9,7 @@ default:
   variables:
     GIT_STRATEGY: clone
     GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
+    DEBIAN_FRONTEND: noninteractive
   cache:
     - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG
       fallback_keys:
@@ -32,27 +29,21 @@ default:
       - dpkg --add-architecture arm64
       - echo -e "\e[0Ksection_end:`date +%s`:dpkg_aa\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:ac_pre[collapsed=true]\r\e[0KSetting up APT cache"
-      - *aptcache
-      - echo -e "\e[0Ksection_end:`date +%s`:ac_pre\r\e[0K"
-
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_conf[collapsed=true]\r\e[0KSetting up APT conf"
+      - export APT_CACHE_DIR=`pwd`/apt-cache
       - touch /etc/apt/apt.conf.d/99build
       - echo Adding options to apt.conf':'
       - echo APT::Install-Recommends "false"\;       | tee --append /etc/apt/apt.conf.d/99build
       - echo quiet "1"\;                             | tee --append /etc/apt/apt.conf.d/99build
       - echo APT::Get::Assume-Yes "true"\;           | tee --append /etc/apt/apt.conf.d/99build
       - echo Dir::Cache::Archives "$APT_CACHE_DIR"\; | tee --append /etc/apt/apt.conf.d/99build
+      - mkdir --parents --verbose $APT_CACHE_DIR/partial/
       - echo -e "\e[0Ksection_end:`date +%s`:apt_conf\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_update[collapsed=true]\r\e[0KUpdating APT listing"
       - apt-get update
       - echo -e "\e[0Ksection_end:`date +%s`:apt_update\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_cache[collapsed=true]\r\e[0KMaking APT cache directory"
-      - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_cache\r\e[0K"
-
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_pre[collapsed=true]\r\e[0KInstalling pre packages"
       - apt-get install apt-utils
       - echo -e "\e[0Ksection_end:`date +%s`:apt_pre\r\e[0K"
@@ -72,19 +63,19 @@ default:
       - echo max_size = 50M                   | tee --append ~/.ccache/ccache.conf
       - echo -e "\e[0Ksection_end:`date +%s`:ccache_config\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_reset[collapsed=true]\r\e[0KResetting ccache statistics"
-      - ccache --zero-stats || true
-      - ccache --show-stats || true
+    - - echo -e "\e[0Ksection_start:`date +%s`:ccache_reset[collapsed=true]\r\e[0KResetting ccache statistics"
+      - ccache --zero-stats
+      - ccache --show-stats
       - echo -e "\e[0Ksection_end:`date +%s`:ccache_reset\r\e[0K"
+
   after_script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_clean[collapsed=true]\r\e[0KCleaning of unneeded APT packages"
-      - *aptcache
       - apt-get autoclean
       - echo -e "\e[0Ksection_end:`date +%s`:apt_clean\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:ccache_stats[collapsed=false]\r\e[0Kccache statistics:"
+    - - echo -e "\e[0Ksection_start:`date +%s`:ccache_stats[collapsed=true]\r\e[0Kccache statistics:"
       - ccache --show-stats --verbose
-      - ccache --show-log-stats --verbose || true
+      - ccache --show-log-stats --verbose
       - echo -e "\e[0Ksection_end:`date +%s`:ccache_stats\r\e[0K"
 
 build-testing:
@@ -97,7 +88,6 @@ build-testing:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
-      - *aptcache
       - apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
@@ -105,7 +95,7 @@ build-testing:
       - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
@@ -117,11 +107,10 @@ build-i686-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
-      - *aptcache
       - apt-get install gcc-mingw-w64-i686-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
@@ -133,7 +122,6 @@ build-x86_64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
-      - *aptcache
       - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
@@ -146,7 +134,7 @@ build-x86_64-linux-gnu:
     - export OBJDUMP=x86_64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
@@ -158,7 +146,6 @@ build-i686-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
-      - *aptcache
       - apt-get install gcc-i686-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
@@ -171,7 +158,7 @@ build-i686-linux-gnu:
     - export OBJDUMP=i686-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
@@ -183,7 +170,6 @@ build-aarch64-linux-gnu:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
-      - *aptcache
       - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
@@ -196,7 +182,7 @@ build-aarch64-linux-gnu:
     - export OBJDUMP=aarch64-linux-gnu-objdump
     - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
@@ -208,10 +194,9 @@ build-x86_64-w64-mingw32:
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
-      - *aptcache
       - apt-get install gcc-mingw-w64-x86-64-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make\r\e[0KCompiling SRB2"
+    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
-- 
GitLab


From 49041773124b8e1c7fa1be5fe6ca9e91df551ffc Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 04:34:39 +0000
Subject: [PATCH 494/518] Update .gitlab-ci.yml file

Move the exports to variables section
---
 .gitlab-ci.yml | 50 +++++++++++++++++++++++++++++---------------------
 1 file changed, 29 insertions(+), 21 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 206d1d8887..eb0c60a545 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -10,6 +10,8 @@ default:
     GIT_STRATEGY: clone
     GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
     DEBIAN_FRONTEND: noninteractive
+    CCACHE: 1
+    ERRORMODE: 1
   cache:
     - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG
       fallback_keys:
@@ -86,6 +88,8 @@ build-testing:
     paths:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
+  variables:
+    CC: gcc
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc
@@ -96,7 +100,7 @@ build-testing:
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
+      - make --directory=src --keep-going NONX86=1 || make --directory=src --keep-going NONX86=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-w64-mingw32:
@@ -105,13 +109,15 @@ build-i686-w64-mingw32:
     paths:
       - "bin/srb2win.exe*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
+  variables:
+    PREFIX: i686-w64-mingw32
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-mingw-w64-i686-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 PREFIX=i686-w64-mingw32
+      - make --directory=src --keep-going MINGW=1 || make --directory=src --keep-going MINGW=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-linux-gnu:
@@ -120,6 +126,11 @@ build-x86_64-linux-gnu:
     paths:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
+  variables:
+    CC: x86_64-linux-gnu-gcc
+    OBJCOPY: x86_64-linux-gnu-objcopy
+    OBJDUMP: x86_64-linux-gnu-objdump
+    PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
@@ -129,13 +140,8 @@ build-x86_64-linux-gnu:
       - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
-    - export CC=x86_64-linux-gnu-gcc
-    - export OBJCOPY=x86_64-linux-gnu-objcopy
-    - export OBJDUMP=x86_64-linux-gnu-objdump
-    - export PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
-
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
+      - make --directory=src --keep-going LINUX64=1 || make --directory=src --keep-going LINUX64=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-linux-gnu:
@@ -144,6 +150,11 @@ build-i686-linux-gnu:
     paths:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
+  variables:
+    CC: i686-linux-gnu-gcc
+    OBJCOPY: i686-linux-gnu-objcopy
+    OBJDUMP: i686-linux-gnu-objdump
+    PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-i686-linux-gnu || apt-get install gcc
@@ -153,13 +164,8 @@ build-i686-linux-gnu:
       - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
-    - export CC=i686-linux-gnu-gcc
-    - export OBJCOPY=i686-linux-gnu-objcopy
-    - export OBJDUMP=i686-linux-gnu-objdump
-    - export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
-
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
+      - make --directory=src --keep-going LINUX=1 || make --directory=src --keep-going LINUX=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-aarch64-linux-gnu:
@@ -168,6 +174,11 @@ build-aarch64-linux-gnu:
     paths:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
+  variables:
+    CC: aarch64-linux-gnu-gcc
+    OBJCOPY: aarch64-linux-gnu-objcopy
+    OBJDUMP: aarch64-linux-gnu-objdump
+    PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
@@ -177,13 +188,8 @@ build-aarch64-linux-gnu:
       - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
-    - export CC=aarch64-linux-gnu-gcc
-    - export OBJCOPY=aarch64-linux-gnu-objcopy
-    - export OBJDUMP=aarch64-linux-gnu-objdump
-    - export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
-
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
+      - make --directory=src --keep-going LINUX64=1 NONX86=1 || make --directory=src --keep-going LINUX64=1 NONX86=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-w64-mingw32:
@@ -192,11 +198,13 @@ build-x86_64-w64-mingw32:
     paths:
       - "bin/srb2win64.exe*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
+  variables:
+    PREFIX: x86_64-w64-mingw32
   script:
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-mingw-w64-x86-64-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 PREFIX=x86_64-w64-mingw32
+      - make --directory=src --keep-going MINGW64=1 || make --directory=src --keep-going MINGW64=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
-- 
GitLab


From de7f0cba1b82e6c9ad575fa7b350f5a6ca210f2a Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 04:55:29 +0000
Subject: [PATCH 495/518] Update .gitlab-ci.yml file

Can not set CCACHE=1 in shell env
---
 .gitlab-ci.yml | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index eb0c60a545..306c6942ab 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -10,7 +10,6 @@ default:
     GIT_STRATEGY: clone
     GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
     DEBIAN_FRONTEND: noninteractive
-    CCACHE: 1
     ERRORMODE: 1
   cache:
     - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG
@@ -100,7 +99,7 @@ build-testing:
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going NONX86=1 || make --directory=src --keep-going NONX86=1
+      - make --directory=src --keep-going CCACHE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 NONX86=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-w64-mingw32:
@@ -117,7 +116,7 @@ build-i686-w64-mingw32:
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going MINGW=1 || make --directory=src --keep-going MINGW=1
+      - make --directory=src --keep-going CCACHE=1 MINGW=1 || make --directory=src --keep-going CCACHE=1 MINGW=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-linux-gnu:
@@ -141,7 +140,7 @@ build-x86_64-linux-gnu:
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going LINUX64=1 || make --directory=src --keep-going LINUX64=1
+      - make --directory=src --keep-going CCACHE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 LINUX64=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-linux-gnu:
@@ -165,7 +164,7 @@ build-i686-linux-gnu:
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going LINUX=1 || make --directory=src --keep-going LINUX=1
+      - make --directory=src --keep-going CCACHE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 LINUX=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-aarch64-linux-gnu:
@@ -189,7 +188,7 @@ build-aarch64-linux-gnu:
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going LINUX64=1 NONX86=1 || make --directory=src --keep-going LINUX64=1 NONX86=1
+      - make --directory=src --keep-going CCACHE=1 LINUX64=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 LINUX64=1 NONX86=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-w64-mingw32:
@@ -206,5 +205,5 @@ build-x86_64-w64-mingw32:
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going MINGW64=1 || make --directory=src --keep-going MINGW64=1
+      - make --directory=src --keep-going CCACHE=1 MINGW64=1 || make --directory=src --keep-going CCACHE=1 MINGW64=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
-- 
GitLab


From 8ace36efbf6477617bfa0a572df3a0d789e52d09 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 14:44:17 +0000
Subject: [PATCH 496/518] Update .gitlab-ci.yml file

Try to list packages that can be upgraded
---
 .gitlab-ci.yml | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 306c6942ab..f7fdc18260 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,7 +9,10 @@ default:
   variables:
     GIT_STRATEGY: clone
     GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
-    DEBIAN_FRONTEND: noninteractive
+    DEBIAN_FRONTEND: "noninteractive"
+    DEBIAN_PRIORITY: "low"
+    DEBCONF_NOWARNINGS: "yes"
+    DEBCONF_NONINTERACTIVE_SEEN: "true"
     ERRORMODE: 1
   cache:
     - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG
@@ -49,6 +52,10 @@ default:
       - apt-get install apt-utils
       - echo -e "\e[0Ksection_end:`date +%s`:apt_pre\r\e[0K"
 
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_upgrade[collapsed=true]\r\e[0KUpdating existing packages (dry-run)"
+      - apt-get upgrade --simulate
+      - echo -e "\e[0Ksection_end:`date +%s`:apt_upgraden\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0KInstalling common packages"
       - apt-get install make git ccache
       - echo -e "\e[0Ksection_end:`date +%s`:apt_common\r\e[0K"
-- 
GitLab


From 595ce3e22534b097d22077fc0a4f1e23d6267177 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 16:08:15 +0000
Subject: [PATCH 497/518] Update .gitlab-ci.yml file

Update Debian image
---
 .gitlab-ci.yml | 54 ++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 41 insertions(+), 13 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f7fdc18260..2582d0cd30 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,16 +4,16 @@ stages:          # List of stages for jobs, and their order of execution
 default:
   image: debian:stable-slim
 
+.debconf: &debconf
+  export DEBIAN_FRONTEND="noninteractive";
+  export DEBIAN_PRIORITY="low";
+  export DEBCONF_NONINTERACTIVE_SEEN="true";
+ 
 .job_template: &job_build # This job runs in the build stage, which runs first.
   stage: build
   variables:
     GIT_STRATEGY: clone
     GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
-    DEBIAN_FRONTEND: "noninteractive"
-    DEBIAN_PRIORITY: "low"
-    DEBCONF_NOWARNINGS: "yes"
-    DEBCONF_NONINTERACTIVE_SEEN: "true"
-    ERRORMODE: 1
   cache:
     - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG
       fallback_keys:
@@ -27,6 +27,10 @@ default:
         - apt-cache
       unprotect: true
   before_script:
+    - - echo -e "\e[0Ksection_start:`date +%s`:debconf_pre[collapsed=true]\r\e[0KSetup debconf's environment"
+      - *debconf
+      - echo -e "\e[0Ksection_end:`date +%s`:debconf_pre\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:dpkg_aa[collapsed=true]\r\e[0KAdding architectures to dpkg"
       - dpkg --add-architecture i386
       - dpkg --add-architecture amd64
@@ -52,8 +56,8 @@ default:
       - apt-get install apt-utils
       - echo -e "\e[0Ksection_end:`date +%s`:apt_pre\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_upgrade[collapsed=true]\r\e[0KUpdating existing packages (dry-run)"
-      - apt-get upgrade --simulate
+    - - echo -e "\e[0Ksection_start:`date +%s`:apt_upgrade[collapsed=true]\r\e[0KUpdating existing packages"
+      - apt-get upgrade
       - echo -e "\e[0Ksection_end:`date +%s`:apt_upgraden\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0KInstalling common packages"
@@ -97,6 +101,10 @@ build-testing:
   variables:
     CC: gcc
   script:
+    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
+      - *debconf
+      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
@@ -106,7 +114,7 @@ build-testing:
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 NONX86=1
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-w64-mingw32:
@@ -118,12 +126,16 @@ build-i686-w64-mingw32:
   variables:
     PREFIX: i686-w64-mingw32
   script:
+    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
+      - *debconf
+      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-mingw-w64-i686-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 MINGW=1 || make --directory=src --keep-going CCACHE=1 MINGW=1
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-linux-gnu:
@@ -138,6 +150,10 @@ build-x86_64-linux-gnu:
     OBJDUMP: x86_64-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig
   script:
+    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
+      - *debconf
+      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
@@ -147,7 +163,7 @@ build-x86_64-linux-gnu:
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 LINUX64=1
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-linux-gnu:
@@ -162,6 +178,10 @@ build-i686-linux-gnu:
     OBJDUMP: i686-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig
   script:
+    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
+      - *debconf
+      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-i686-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
@@ -171,7 +191,7 @@ build-i686-linux-gnu:
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 LINUX=1
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-aarch64-linux-gnu:
@@ -186,6 +206,10 @@ build-aarch64-linux-gnu:
     OBJDUMP: aarch64-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
   script:
+    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
+      - *debconf
+      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
@@ -195,7 +219,7 @@ build-aarch64-linux-gnu:
       - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 LINUX64=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 LINUX64=1 NONX86=1
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-w64-mingw32:
@@ -207,10 +231,14 @@ build-x86_64-w64-mingw32:
   variables:
     PREFIX: x86_64-w64-mingw32
   script:
+    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
+      - *debconf
+      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
+
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-mingw-w64-x86-64-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 MINGW64=1 || make --directory=src --keep-going CCACHE=1 MINGW64=1
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1
       - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
-- 
GitLab


From b8861b19127dabd54b0aed9032bf32975c85a50d Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 17:11:44 +0000
Subject: [PATCH 498/518] Update .gitlab-ci.yml file

Let see if I need to need to run export again in the main build script
---
 .gitlab-ci.yml | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2582d0cd30..abd769f035 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -101,10 +101,6 @@ build-testing:
   variables:
     CC: gcc
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
-      - *debconf
-      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
-
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
-- 
GitLab


From 3adc15d5214b86fb87c959b85ce551db8c37d9ab Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 17:30:07 +0000
Subject: [PATCH 499/518] Update .gitlab-ci.yml file

Only need to setup debconf in before_script
---
 .gitlab-ci.yml | 48 +++++++++++++++---------------------------------
 1 file changed, 15 insertions(+), 33 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index abd769f035..27d95d6fe5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,19 +1,17 @@
+default:
+  image: debian:stable-slim
+
 stages:          # List of stages for jobs, and their order of execution
   - build
 
-default:
-  image: debian:stable-slim
+variables:
+  GIT_STRATEGY: clone
+  GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
 
-.debconf: &debconf
-  export DEBIAN_FRONTEND="noninteractive";
-  export DEBIAN_PRIORITY="low";
-  export DEBCONF_NONINTERACTIVE_SEEN="true";
- 
 .job_template: &job_build # This job runs in the build stage, which runs first.
+
   stage: build
-  variables:
-    GIT_STRATEGY: clone
-    GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
+
   cache:
     - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG
       fallback_keys:
@@ -22,14 +20,18 @@ default:
       paths:
         - ccache
         - ccache_statslog
+
     - key: apt-$CI_JOB_IMAGE
       paths:
         - apt-cache
       unprotect: true
+
   before_script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:debconf_pre[collapsed=true]\r\e[0KSetup debconf's environment"
-      - *debconf
-      - echo -e "\e[0Ksection_end:`date +%s`:debconf_pre\r\e[0K"
+    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
+      - export DEBIAN_FRONTEND="noninteractive"
+      - export DEBIAN_PRIORITY="low"
+      - export DEBCONF_NONINTERACTIVE_SEEN="true"
+      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
 
     - - echo -e "\e[0Ksection_start:`date +%s`:dpkg_aa[collapsed=true]\r\e[0KAdding architectures to dpkg"
       - dpkg --add-architecture i386
@@ -122,10 +124,6 @@ build-i686-w64-mingw32:
   variables:
     PREFIX: i686-w64-mingw32
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
-      - *debconf
-      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
-
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-mingw-w64-i686-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
@@ -146,10 +144,6 @@ build-x86_64-linux-gnu:
     OBJDUMP: x86_64-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
-      - *debconf
-      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
-
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
@@ -174,10 +168,6 @@ build-i686-linux-gnu:
     OBJDUMP: i686-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
-      - *debconf
-      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
-
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-i686-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
@@ -202,10 +192,6 @@ build-aarch64-linux-gnu:
     OBJDUMP: aarch64-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
-      - *debconf
-      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
-
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
@@ -227,10 +213,6 @@ build-x86_64-w64-mingw32:
   variables:
     PREFIX: x86_64-w64-mingw32
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
-      - *debconf
-      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
-
     - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-mingw-w64-x86-64-win32
       - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
-- 
GitLab


From aa21dcad33857980e6203fe67da2954c26beff5a Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 17:50:45 +0000
Subject: [PATCH 500/518] Update .gitlab-ci.yml file

Try to hide echos
---
 .gitlab-ci.yml | 201 ++++++++++++++++++++++++++++++++-----------------
 1 file changed, 133 insertions(+), 68 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 27d95d6fe5..c1d983e53f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -27,70 +27,103 @@ variables:
       unprotect: true
 
   before_script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
       - export DEBIAN_FRONTEND="noninteractive"
       - export DEBIAN_PRIORITY="low"
       - export DEBCONF_NONINTERACTIVE_SEEN="true"
-      - echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:dpkg_aa[collapsed=true]\r\e[0KAdding architectures to dpkg"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:dpkg_aa[collapsed=true]\r\e[0KAdding architectures to dpkg"
       - dpkg --add-architecture i386
       - dpkg --add-architecture amd64
       - dpkg --add-architecture arm64
-      - echo -e "\e[0Ksection_end:`date +%s`:dpkg_aa\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:dpkg_aa\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_conf[collapsed=true]\r\e[0KSetting up APT conf"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_conf[collapsed=true]\r\e[0KSetting up APT conf"
       - export APT_CACHE_DIR=`pwd`/apt-cache
-      - touch /etc/apt/apt.conf.d/99build
-      - echo Adding options to apt.conf':'
-      - echo APT::Install-Recommends "false"\;       | tee --append /etc/apt/apt.conf.d/99build
-      - echo quiet "1"\;                             | tee --append /etc/apt/apt.conf.d/99build
-      - echo APT::Get::Assume-Yes "true"\;           | tee --append /etc/apt/apt.conf.d/99build
-      - echo Dir::Cache::Archives "$APT_CACHE_DIR"\; | tee --append /etc/apt/apt.conf.d/99build
       - mkdir --parents --verbose $APT_CACHE_DIR/partial/
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_conf\r\e[0K"
-
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_update[collapsed=true]\r\e[0KUpdating APT listing"
+      - touch /etc/apt/apt.conf.d/99build
+      - |
+       echo Adding options to apt.conf':'
+      - |
+       echo APT::Install-Recommends "false"\;       | tee --append /etc/apt/apt.conf.d/99build
+      - |
+       echo quiet "1"\;                             | tee --append /etc/apt/apt.conf.d/99build
+      - |
+       echo APT::Get::Assume-Yes "true"\;           | tee --append /etc/apt/apt.conf.d/99build
+      - |
+       echo Dir::Cache::Archives "$APT_CACHE_DIR"\; | tee --append /etc/apt/apt.conf.d/99build
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_conf\r\e[0K"
+
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_update[collapsed=true]\r\e[0KUpdating APT listing"
       - apt-get update
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_update\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_update\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_pre[collapsed=true]\r\e[0KInstalling pre packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_pre[collapsed=true]\r\e[0KInstalling pre packages"
       - apt-get install apt-utils
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_pre\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_pre\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_upgrade[collapsed=true]\r\e[0KUpdating existing packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_upgrade[collapsed=true]\r\e[0KUpdating existing packages"
       - apt-get upgrade
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_upgraden\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_upgraden\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0KInstalling common packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0KInstalling common packages"
       - apt-get install make git ccache
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_common\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_common\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:ccache_config[collapsed=true]\r\e[0KSetting up ccache config"
-      - echo Adding ccache configution option
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:ccache_config[collapsed=true]\r\e[0KSetting up ccache config"
       - mkdir --parents --verbose ~/.ccache/
       - touch ~/.ccache/ccache.conf
-      - echo base_dir = $PWD                  | tee --append ~/.ccache/ccache.conf
-      - echo cache_dir = $PWD/ccache          | tee --append ~/.ccache/ccache.conf
-      - echo compiler_check = content         | tee --append ~/.ccache/ccache.conf
-      - echo stats_log = $PWD/ccache_statslog | tee --append ~/.ccache/ccache.conf
-      - echo max_size = 50M                   | tee --append ~/.ccache/ccache.conf
-      - echo -e "\e[0Ksection_end:`date +%s`:ccache_config\r\e[0K"
-
-    - - echo -e "\e[0Ksection_start:`date +%s`:ccache_reset[collapsed=true]\r\e[0KResetting ccache statistics"
+      - |
+       echo Adding ccache configution option
+      - |
+       echo base_dir = $PWD                  | tee --append ~/.ccache/ccache.conf
+      - |
+       echo cache_dir = $PWD/ccache          | tee --append ~/.ccache/ccache.conf
+      - |
+       echo compiler_check = content         | tee --append ~/.ccache/ccache.conf
+      - |
+       echo stats_log = $PWD/ccache_statslog | tee --append ~/.ccache/ccache.conf
+      - |
+       echo max_size = 50M                   | tee --append ~/.ccache/ccache.conf
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:ccache_config\r\e[0K"
+
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:ccache_reset[collapsed=true]\r\e[0KResetting ccache statistics"
       - ccache --zero-stats
       - ccache --show-stats
-      - echo -e "\e[0Ksection_end:`date +%s`:ccache_reset\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:ccache_reset\r\e[0K"
 
   after_script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_clean[collapsed=true]\r\e[0KCleaning of unneeded APT packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_clean[collapsed=true]\r\e[0KCleaning of unneeded APT packages"
       - apt-get autoclean
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_clean\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_clean\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:ccache_stats[collapsed=true]\r\e[0Kccache statistics:"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:ccache_stats[collapsed=true]\r\e[0Kccache statistics:"
       - ccache --show-stats --verbose
       - ccache --show-log-stats --verbose
-      - echo -e "\e[0Ksection_end:`date +%s`:ccache_stats\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:ccache_stats\r\e[0K"
 
 build-testing:
   <<: *job_build
@@ -103,17 +136,23 @@ build-testing:
   variables:
     CC: gcc
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
-      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-w64-mingw32:
   <<: *job_build
@@ -124,13 +163,17 @@ build-i686-w64-mingw32:
   variables:
     PREFIX: i686-w64-mingw32
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-mingw-w64-i686-win32
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1
-      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-linux-gnu:
   <<: *job_build
@@ -144,17 +187,23 @@ build-x86_64-linux-gnu:
     OBJDUMP: x86_64-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
-      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-i686-linux-gnu:
   <<: *job_build
@@ -168,17 +217,23 @@ build-i686-linux-gnu:
     OBJDUMP: i686-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-i686-linux-gnu || apt-get install gcc
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
-      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-aarch64-linux-gnu:
   <<: *job_build
@@ -192,17 +247,23 @@ build-aarch64-linux-gnu:
     OBJDUMP: aarch64-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
-      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 build-x86_64-w64-mingw32:
   <<: *job_build
@@ -213,10 +274,14 @@ build-x86_64-w64-mingw32:
   variables:
     PREFIX: x86_64-w64-mingw32
   script:
-    - - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-mingw-w64-x86-64-win32
-      - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
-    - - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1
-      - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
-- 
GitLab


From 35f57882e47c1206076567ac9e078d46b91a355a Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 14:16:13 -0400
Subject: [PATCH 501/518] signalhandlers are function of NORETURN

---
 src/sdl/i_system.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index be46cd8044..98e036130e 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -2265,7 +2265,7 @@ void I_Sleep(UINT32 ms)
 }
 
 #ifdef NEWSIGNALHANDLER
-static void newsignalhandler_Warn(const char *pr)
+ATTRNORETURN static FUNCNORETURN void newsignalhandler_Warn(const char *pr)
 {
 	char text[128];
 
-- 
GitLab


From aaebcc6ce1b442589a352e0568a40407cd86c4f3 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 14:24:20 -0400
Subject: [PATCH 502/518] Update src/hardware/hw_batching.c

fix misleading indentation
---
 src/hardware/hw_batching.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/hardware/hw_batching.c b/src/hardware/hw_batching.c
index d1b84a5eec..dc0b5ee5b0 100644
--- a/src/hardware/hw_batching.c
+++ b/src/hardware/hw_batching.c
@@ -42,10 +42,10 @@ int unsortedVertexArrayAllocSize = 65536;
 // Call HWR_RenderBatches to render all the collected geometry.
 void HWR_StartBatching(void)
 {
-    if (currently_batching)
-        I_Error("Repeat call to HWR_StartBatching without HWR_RenderBatches");
+	if (currently_batching)
+		I_Error("Repeat call to HWR_StartBatching without HWR_RenderBatches");
 
-    // init arrays if that has not been done yet
+	// init arrays if that has not been done yet
 	if (!finalVertexArray)
 	{
 		finalVertexArray = malloc(finalVertexArrayAllocSize * sizeof(FOutVector));
@@ -55,7 +55,7 @@ void HWR_StartBatching(void)
 		unsortedVertexArray = malloc(unsortedVertexArrayAllocSize * sizeof(FOutVector));
 	}
 
-    currently_batching = true;
+	currently_batching = true;
 }
 
 // This replaces the direct calls to pfnSetTexture in cases where batching is available.
-- 
GitLab


From bc852fa099ffcaa345b1230cc094e8989a098721 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 14:34:53 -0400
Subject: [PATCH 503/518] remove unused variables that was only set

---
 src/hardware/hw_main.c  | 3 ---
 src/hardware/hw_model.c | 3 ---
 2 files changed, 6 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index f2022bcea3..8260271bdd 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -4141,14 +4141,11 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 		float xscale, yscale;
 		float xoffset, yoffset;
 		float leftoffset, topoffset;
-		float scale = spr->scale;
 		float zoffset = (P_MobjFlip(spr->mobj) * 0.05f);
 		pslope_t *splatslope = NULL;
 		INT32 i;
 
 		renderflags_t renderflags = spr->renderflags;
-		if (renderflags & RF_SHADOWEFFECTS)
-			scale *= spr->shadowscale;
 
 		if (spr->rotateflags & SRF_3D || renderflags & RF_NOSPLATBILLBOARD)
 			angle = spr->mobj->angle;
diff --git a/src/hardware/hw_model.c b/src/hardware/hw_model.c
index b69bce0e2d..9319939c00 100644
--- a/src/hardware/hw_model.c
+++ b/src/hardware/hw_model.c
@@ -663,7 +663,6 @@ void GeneratePolygonNormals(model_t *model, int ztag)
 		{
 			int k;
 			mdlframe_t *frame = &mesh->frames[j];
-			const float *vertices = frame->vertices;
 			vector_t *polyNormals;
 
 			frame->polyNormals = (vector_t*)Z_Malloc(sizeof(vector_t) * mesh->numTriangles, ztag, 0);
@@ -672,8 +671,6 @@ void GeneratePolygonNormals(model_t *model, int ztag)
 
 			for (k = 0; k < mesh->numTriangles; k++)
 			{
-//				Vector::Normal(vertices, polyNormals);
-				vertices += 3 * 3;
 				polyNormals++;
 			}
 		}
-- 
GitLab


From 6c19fcc607534c2fd9bc79453cc08cd31b7c4782 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 18:56:06 +0000
Subject: [PATCH 504/518] Update .gitlab-ci.yml file

Try compiling with clang
---
 .gitlab-ci.yml | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c1d983e53f..ed75730fd5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -128,13 +128,17 @@ variables:
 build-testing:
   <<: *job_build
   image: debian:testing-slim
+
   allow_failure: true
+
   artifacts:
     paths:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
+
   variables:
     CC: gcc
+
   script:
     - - |
        echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
@@ -156,12 +160,15 @@ build-testing:
 
 build-i686-w64-mingw32:
   <<: *job_build
+
   artifacts:
     paths:
       - "bin/srb2win.exe*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
+
   variables:
     PREFIX: i686-w64-mingw32
+
   script:
     - - |
        echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
@@ -177,15 +184,18 @@ build-i686-w64-mingw32:
 
 build-x86_64-linux-gnu:
   <<: *job_build
+
   artifacts:
     paths:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
+
   variables:
     CC: x86_64-linux-gnu-gcc
     OBJCOPY: x86_64-linux-gnu-objcopy
     OBJDUMP: x86_64-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig
+
   script:
     - - |
        echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
@@ -207,15 +217,18 @@ build-x86_64-linux-gnu:
 
 build-i686-linux-gnu:
   <<: *job_build
+
   artifacts:
     paths:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
+
   variables:
     CC: i686-linux-gnu-gcc
     OBJCOPY: i686-linux-gnu-objcopy
     OBJDUMP: i686-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig
+
   script:
     - - |
        echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
@@ -237,15 +250,18 @@ build-i686-linux-gnu:
 
 build-aarch64-linux-gnu:
   <<: *job_build
+
   artifacts:
     paths:
       - "bin/lsdl2srb2*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
+
   variables:
     CC: aarch64-linux-gnu-gcc
     OBJCOPY: aarch64-linux-gnu-objcopy
     OBJDUMP: aarch64-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
+
   script:
     - - |
        echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
@@ -267,12 +283,15 @@ build-aarch64-linux-gnu:
 
 build-x86_64-w64-mingw32:
   <<: *job_build
+
   artifacts:
     paths:
       - "bin/srb2win64.exe*"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
+
   variables:
     PREFIX: x86_64-w64-mingw32
+
   script:
     - - |
        echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
@@ -285,3 +304,37 @@ build-x86_64-w64-mingw32:
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1
       - |
        echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+
+build-clang:
+  <<: *job_build
+
+  allow_failure: true
+
+  artifacts:
+    paths:
+      - "bin/lsdl2srb2*"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
+
+  variables:
+    CC: clang
+    CFLAGS: -Wno-cast-align
+
+  script:
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+      - apt-get install clang
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+      - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+
-- 
GitLab


From b6aaf582d12c745c542a71c3c75df741fded1bf2 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 15:29:20 -0400
Subject: [PATCH 505/518] Update src/hardware/mw_md2.c

fscanf need 26 chars in the name buffer
---
 src/hardware/hw_md2.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 6123eb9a93..914683db70 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -486,7 +486,7 @@ void HWR_InitModels(void)
 	size_t i;
 	INT32 s;
 	FILE *f;
-	char name[24], filename[32];
+	char name[26], filename[32];
 	float scale, offset;
 	size_t prefixlen;
 
-- 
GitLab


From 1372b60db9912288121a980709e38c186189b9f0 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 16:00:25 -0400
Subject: [PATCH 506/518] Update src/hardware/mw_md2.c

there are 2 more name buffers that need to be bigger
---
 src/hardware/hw_md2.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 914683db70..0f83421359 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -585,7 +585,7 @@ modelfound:
 void HWR_AddPlayerModel(int skin) // For skins that were added after startup
 {
 	FILE *f;
-	char name[24], filename[32];
+	char name[26], filename[32];
 	float scale, offset;
 	size_t prefixlen;
 
@@ -644,7 +644,7 @@ void HWR_AddSpriteModel(size_t spritenum) // For sprites that were added after s
 	// name[24] is used to check for names in the models.dat file that match with sprites or player skins
 	// sprite names are always 4 characters long, and names is for player skins can be up to 19 characters long
 	// PLAYERMODELPREFIX is 6 characters long
-	char name[24], filename[32];
+	char name[26], filename[32];
 	float scale, offset;
 
 	if (nomd2s)
-- 
GitLab


From 976a2850af54c0abcff0fab991e5a3b66bea8636 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 16:02:27 -0400
Subject: [PATCH 507/518] Update .gitlab-ci.yml file

change name of artifact for clang build
---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ed75730fd5..7db8198d30 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -313,7 +313,7 @@ build-clang:
   artifacts:
     paths:
       - "bin/lsdl2srb2*"
-    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-clang"
 
   variables:
     CC: clang
-- 
GitLab


From b27de309a87051160903035a565f65c9cfd13a60 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 20:32:11 +0000
Subject: [PATCH 508/518] Update .gitlab-ci.yml file

Testing compiling with Debian's testing clang package
---
 .gitlab-ci.yml | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7db8198d30..890b4b94ab 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -134,7 +134,7 @@ build-testing:
   artifacts:
     paths:
       - "bin/lsdl2srb2*"
-    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing-gcc"
 
   variables:
     CC: gcc
@@ -338,3 +338,39 @@ build-clang:
       - |
        echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
+
+build-clang-testing:
+  <<: *job_build
+
+  image: debian:testing-slim
+
+  allow_failure: true
+
+  artifacts:
+    paths:
+      - "bin/lsdl2srb2*"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing-clang"
+
+  variables:
+    CC: clang
+    CFLAGS: -Wno-cast-align
+
+  script:
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+      - apt-get install clang
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+      - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+
+    - - |
+       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
+      - |
+       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+
-- 
GitLab


From 4bec14342760a5f6b547d0e80a4581a5e8e992a1 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 23:42:27 +0000
Subject: [PATCH 509/518] Update .gitlab-ci.yml file

Do not care for non-prototypes for clang-testing job
---
 .gitlab-ci.yml | 26 +++-----------------------
 1 file changed, 3 insertions(+), 23 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 890b4b94ab..527dc18046 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -342,9 +342,9 @@ build-clang:
 build-clang-testing:
   <<: *job_build
 
-  image: debian:testing-slim
+  extends: build-clang
 
-  allow_failure: true
+  image: debian:testing-slim
 
   artifacts:
     paths:
@@ -353,24 +353,4 @@ build-clang-testing:
 
   variables:
     CC: clang
-    CFLAGS: -Wno-cast-align
-
-  script:
-    - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
-      - apt-get install clang
-      - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
-
-    - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
-      - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
-      - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
-
-    - - |
-       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
-      - |
-       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
-
+    CFLAGS: -Wno-cast-align -Wno-deprecated-non-prototype
-- 
GitLab


From 6a37b3c0c66ee30ecdcc67b8bad793d97c12c70d Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 20:30:06 -0400
Subject: [PATCH 510/518] Update src/hardware/mw_model.c

Restore old code in GeneratePolygonNormals(), add TODO
---
 src/hardware/hw_model.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/hardware/hw_model.c b/src/hardware/hw_model.c
index 9319939c00..4b6bce6f7b 100644
--- a/src/hardware/hw_model.c
+++ b/src/hardware/hw_model.c
@@ -663,6 +663,7 @@ void GeneratePolygonNormals(model_t *model, int ztag)
 		{
 			int k;
 			mdlframe_t *frame = &mesh->frames[j];
+			const float *vertices = frame->vertices;
 			vector_t *polyNormals;
 
 			frame->polyNormals = (vector_t*)Z_Malloc(sizeof(vector_t) * mesh->numTriangles, ztag, 0);
@@ -671,6 +672,11 @@ void GeneratePolygonNormals(model_t *model, int ztag)
 
 			for (k = 0; k < mesh->numTriangles; k++)
 			{
+				/// TODO: normalize vectors
+				(void)vertices;
+				(void)polyNormals;
+//				Vector::Normal(vertices, polyNormals);
+				vertices += 3 * 3;
 				polyNormals++;
 			}
 		}
-- 
GitLab


From de4a8a193bc56eb6b1e3cf936cdec89407cdb0c9 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 20:31:57 -0400
Subject: [PATCH 511/518] Update src/d_clisrv.c

Remove unused i var in Ban_Load_File()
---
 src/d_clisrv.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 83482b527c..1ec9cf1e94 100755
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2743,7 +2743,6 @@ static void Command_ClearBans(void)
 static void Ban_Load_File(boolean warning)
 {
 	FILE *f;
-	size_t i;
 	const char *address, *mask;
 	char buffer[MAX_WADPATH];
 
@@ -2761,7 +2760,7 @@ static void Ban_Load_File(boolean warning)
 
 	Ban_Clear();
 
-	for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++)
+	for (; fgets(buffer, (int)sizeof(buffer), f);)
 	{
 		address = strtok(buffer, " \t\r\n");
 		mask = strtok(NULL, " \t\r\n");
-- 
GitLab


From 2865873e7040222632a6d3aa1573c4ea44d704d5 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 20:35:04 -0400
Subject: [PATCH 512/518] Update src/lua_baselib.c

No need of counting bots.
---
 src/lua_baselib.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 4af5066aeb..1d183cdec6 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -3544,7 +3544,7 @@ static int lib_gAddGametype(lua_State *L)
 // Partly lifted from Got_AddPlayer
 static int lib_gAddPlayer(lua_State *L)
 {
-	INT16 i, newplayernum, botcount = 1;
+	INT16 i, newplayernum;
 	player_t *newplayer;
 	SINT8 skinnum = 0, bot;
 
@@ -3552,10 +3552,8 @@ static int lib_gAddPlayer(lua_State *L)
 	{
 		if (!playeringame[i])
 			break;
-
-		if (players[i].bot)
-			botcount++; // How many of us are there already?
 	}
+
 	if (i >= MAXPLAYERS)
 	{
 		lua_pushnil(L);
-- 
GitLab


From 33f6deb7f02e4f7f88a6e4903af24fe42473e89a Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 15 Oct 2023 23:57:00 -0400
Subject: [PATCH 513/518] Update sc/netcode/commands.c

'i' variable is set but unused in Ban_Load_File()
---
 src/netcode/commands.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/netcode/commands.c b/src/netcode/commands.c
index 4228027d2a..4b67198bab 100644
--- a/src/netcode/commands.c
+++ b/src/netcode/commands.c
@@ -96,7 +96,7 @@ void Ban_Load_File(boolean warning)
 
 	Ban_Clear();
 
-	for (size_t i=0; fgets(buffer, (int)sizeof(buffer), f); i++)
+	for (; fgets(buffer, (int)sizeof(buffer), f);)
 	{
 		address = strtok(buffer, " \t\r\n");
 		mask = strtok(NULL, " \t\r\n");
-- 
GitLab


From 330b10cbb593bf859726334b7435b7a8d114cfdf Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Tue, 17 Oct 2023 01:41:43 +0000
Subject: [PATCH 514/518] Update .gitlab-ci.yml file

---
 .gitlab-ci.yml | 263 ++++++++++++++++++++++++++++++++-----------------
 1 file changed, 170 insertions(+), 93 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 527dc18046..a849aea283 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -28,104 +28,134 @@ variables:
 
   before_script:
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
+         # debconf
+         echo -e "\e[0Ksection_start:`date +%s`:debconf[collapsed=true]\r\e[0KSetup debconf's environment"
       - export DEBIAN_FRONTEND="noninteractive"
       - export DEBIAN_PRIORITY="low"
       - export DEBCONF_NONINTERACTIVE_SEEN="true"
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
-
+          # debconf 
+          echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:dpkg_aa[collapsed=true]\r\e[0KAdding architectures to dpkg"
+          # dpkg_aa
+          echo -e "\e[0Ksection_start:`date +%s`:dpkg_aa[collapsed=true]\r\e[0KAdding architectures to dpkg"
       - dpkg --add-architecture i386
       - dpkg --add-architecture amd64
       - dpkg --add-architecture arm64
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:dpkg_aa\r\e[0K"
-
+          # dpkg_aa
+          echo -e "\e[0Ksection_end:`date +%s`:dpkg_aa\r\e[0K"
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_conf[collapsed=true]\r\e[0KSetting up APT conf"
+          # apt_conf
+          echo -e "\e[0Ksection_start:`date +%s`:apt_conf[collapsed=true]\r\e[0KSetting up APT conf"
       - export APT_CACHE_DIR=`pwd`/apt-cache
       - mkdir --parents --verbose $APT_CACHE_DIR/partial/
       - touch /etc/apt/apt.conf.d/99build
       - |
-       echo Adding options to apt.conf':'
+          # apt.conf
+          echo Adding options to apt.conf':'
       - |
-       echo APT::Install-Recommends "false"\;       | tee --append /etc/apt/apt.conf.d/99build
+          # APT::Install-Recommends
+          echo APT::Install-Recommends "false"\;       | tee --append /etc/apt/apt.conf.d/99build
       - |
-       echo quiet "1"\;                             | tee --append /etc/apt/apt.conf.d/99build
+          # quit
+          echo quiet "1"\;                             | tee --append /etc/apt/apt.conf.d/99build
       - |
-       echo APT::Get::Assume-Yes "true"\;           | tee --append /etc/apt/apt.conf.d/99build
+          # APT::Get::Assume-Yes
+          echo APT::Get::Assume-Yes "true"\;           | tee --append /etc/apt/apt.conf.d/99build
       - |
-       echo Dir::Cache::Archives "$APT_CACHE_DIR"\; | tee --append /etc/apt/apt.conf.d/99build
+          # Dir::Cache::Archives
+          echo Dir::Cache::Archives "$APT_CACHE_DIR"\; | tee --append /etc/apt/apt.conf.d/99build
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_conf\r\e[0K"
-
+          # apt_conf 
+          echo -e "\e[0Ksection_end:`date +%s`:apt_conf\r\e[0K"
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_update[collapsed=true]\r\e[0KUpdating APT listing"
+          # apt_update
+          echo -e "\e[0Ksection_start:`date +%s`:apt_update[collapsed=true]\r\e[0KUpdating APT listing"
       - apt-get update
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_update\r\e[0K"
+          # apt_update
+          echo -e "\e[0Ksection_end:`date +%s`:apt_update\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_pre[collapsed=true]\r\e[0KInstalling pre packages"
+          # apt_pre
+          echo -e "\e[0Ksection_start:`date +%s`:apt_pre[collapsed=true]\r\e[0KInstalling pre packages"
       - apt-get install apt-utils
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_pre\r\e[0K"
+          # apt_pre
+          echo -e "\e[0Ksection_end:`date +%s`:apt_pre\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_upgrade[collapsed=true]\r\e[0KUpdating existing packages"
+          # apt_upgrade
+          echo -e "\e[0Ksection_start:`date +%s`:apt_upgrade[collapsed=true]\r\e[0KUpdating existing packages"
       - apt-get upgrade
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_upgraden\r\e[0K"
+          # apt_update
+          echo -e "\e[0Ksection_end:`date +%s`:apt_upgrade\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0KInstalling common packages"
+          # apt_common
+          echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0KInstalling common packages"
       - apt-get install make git ccache
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_common\r\e[0K"
+          # apt_common
+          echo -e "\e[0Ksection_end:`date +%s`:apt_common\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:ccache_config[collapsed=true]\r\e[0KSetting up ccache config"
+          # ccache_config
+          echo -e "\e[0Ksection_start:`date +%s`:ccache_config[collapsed=true]\r\e[0KSetting up ccache config"
       - mkdir --parents --verbose ~/.ccache/
       - touch ~/.ccache/ccache.conf
       - |
-       echo Adding ccache configution option
+          # cache.conf
+          echo Adding ccache configution option
       - |
-       echo base_dir = $PWD                  | tee --append ~/.ccache/ccache.conf
+          # base_dir 
+          echo base_dir = $PWD                  | tee --append ~/.ccache/ccache.conf
       - |
-       echo cache_dir = $PWD/ccache          | tee --append ~/.ccache/ccache.conf
+          # cache_dir
+          echo cache_dir = $PWD/ccache          | tee --append ~/.ccache/ccache.conf
       - |
-       echo compiler_check = content         | tee --append ~/.ccache/ccache.conf
+          # compiler_check
+          echo compiler_check = content         | tee --append ~/.ccache/ccache.conf
       - |
-       echo stats_log = $PWD/ccache_statslog | tee --append ~/.ccache/ccache.conf
+          # stats_log
+          echo stats_log = $PWD/ccache_statslog | tee --append ~/.ccache/ccache.conf
       - |
-       echo max_size = 50M                   | tee --append ~/.ccache/ccache.conf
+          # max_size
+          echo max_size = 50M                   | tee --append ~/.ccache/ccache.conf
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:ccache_config\r\e[0K"
+          # ccache_config
+          echo -e "\e[0Ksection_end:`date +%s`:ccache_config\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:ccache_reset[collapsed=true]\r\e[0KResetting ccache statistics"
+          # cache_reset
+          echo -e "\e[0Ksection_start:`date +%s`:ccache_reset[collapsed=true]\r\e[0KResetting ccache statistics"
       - ccache --zero-stats
       - ccache --show-stats
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:ccache_reset\r\e[0K"
+          # ccache_reset
+          echo -e "\e[0Ksection_end:`date +%s`:ccache_reset\r\e[0K"
 
   after_script:
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_clean[collapsed=true]\r\e[0KCleaning of unneeded APT packages"
+           # apt_clean
+           echo -e "\e[0Ksection_start:`date +%s`:apt_clean[collapsed=true]\r\e[0KCleaning of unneeded APT packages"
       - apt-get autoclean
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_clean\r\e[0K"
+          # apt_clean
+          echo -e "\e[0Ksection_end:`date +%s`:apt_clean\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:ccache_stats[collapsed=true]\r\e[0Kccache statistics:"
+          # ccache_stats
+          echo -e "\e[0Ksection_start:`date +%s`:ccache_stats[collapsed=true]\r\e[0Kccache statistics:"
       - ccache --show-stats --verbose
       - ccache --show-log-stats --verbose
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:ccache_stats\r\e[0K"
+          # ccahe_stats
+          echo -e "\e[0Ksection_end:`date +%s`:ccache_stats\r\e[0K"
 
-build-testing:
+Debian testing GCC:
   <<: *job_build
   image: debian:testing-slim
 
@@ -133,7 +163,8 @@ build-testing:
 
   artifacts:
     paths:
-      - "bin/lsdl2srb2*"
+      - "bin/"
+      - "src/comptime.h"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing-gcc"
 
   variables:
@@ -141,29 +172,36 @@ build-testing:
 
   script:
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+          # apt_toolchain
+          echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+          # apt_toolchain
+          echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+          # apt_development
+          echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+          # apt_development
+          echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+          # make
+          echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+          # make
+          echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
-build-i686-w64-mingw32:
+Windows x86:
   <<: *job_build
 
   artifacts:
     paths:
-      - "bin/srb2win.exe*"
+      - "bin/"
+      - "src/comptime.h"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
 
   variables:
@@ -171,23 +209,28 @@ build-i686-w64-mingw32:
 
   script:
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+          # apt_toolchain
+          echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-mingw-w64-i686-win32
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+          # apt_toolchain
+          echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+          # make
+          echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+          # make
+          echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
-build-x86_64-linux-gnu:
+Debian stable:amd64:
   <<: *job_build
 
   artifacts:
     paths:
-      - "bin/lsdl2srb2*"
+      - "bin/"
+      - "src/comptime.h"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
 
   variables:
@@ -198,29 +241,36 @@ build-x86_64-linux-gnu:
 
   script:
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+          # apt_toolchain
+          echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+          # apt_toolchain
+          echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+          # apt_development
+          echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+          # apt_development
+          echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+          # make
+          echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+          # make
+          echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
-build-i686-linux-gnu:
+Debian stable:i386:
   <<: *job_build
 
   artifacts:
     paths:
-      - "bin/lsdl2srb2*"
+      - "bin/"
+      - "src/comptime.h"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
 
   variables:
@@ -231,29 +281,36 @@ build-i686-linux-gnu:
 
   script:
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+          # apt_toolchain
+          echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-i686-linux-gnu || apt-get install gcc
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+          # apt_toolchain
+          echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+          # apt_development
+          echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+          # apt_development
+          echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+          # make
+          echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX=1
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+          # make
+          echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
-build-aarch64-linux-gnu:
+Debian stable:arm64:
   <<: *job_build
 
   artifacts:
     paths:
-      - "bin/lsdl2srb2*"
+      - "bin/"
+      - "src/comptime.h"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
 
   variables:
@@ -264,29 +321,37 @@ build-aarch64-linux-gnu:
 
   script:
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+          # apt_toolchain
+          echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+          # apt_toolchain
+          echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+          # apt_development
+          echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+          # apt_development
+          echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+          # make
+          echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+          # make
+          echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
-build-x86_64-w64-mingw32:
+Windows x64:
   <<: *job_build
 
   artifacts:
     paths:
-      - "bin/srb2win64.exe*"
+      - "bin/"
+      - "src/comptime.h"
+    expose_as: "Win64"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win64"
 
   variables:
@@ -294,25 +359,31 @@ build-x86_64-w64-mingw32:
 
   script:
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+          # apt_toolchain
+          echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install gcc-mingw-w64-x86-64-win32
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+          # apt_toolchain
+          echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+          # make
+          echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+          # make
+          echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
-build-clang:
+Debian stable Clang:
   <<: *job_build
 
   allow_failure: true
 
   artifacts:
     paths:
-      - "bin/lsdl2srb2*"
+      - "bin/"
+      - "src/comptime.h"
+    expose_as: "Debian with clang"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-clang"
 
   variables:
@@ -321,34 +392,40 @@ build-clang:
 
   script:
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+          # apt_toolchain
+          echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
       - apt-get install clang
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+          # apt_toolchain
+          echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+          # apt_development
+          echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
       - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+          # apt_development
+          echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
 
     - - |
-       echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+          # make
+          echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
       - |
-       echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+          # make 
+          echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 
-build-clang-testing:
-  <<: *job_build
-
-  extends: build-clang
+Debian testing Clang:
+  extends: Debian stable Clang
 
   image: debian:testing-slim
 
   artifacts:
     paths:
-      - "bin/lsdl2srb2*"
+      - "bin/"
+      - "src/comptime.h"
+    expose_as: "Debian testing with clang"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing-clang"
 
   variables:
-- 
GitLab


From 265c1ac0c8dd57a689962d72b7c103710f7bb1a7 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Tue, 17 Oct 2023 13:11:17 +0000
Subject: [PATCH 515/518] Update .gitlib-ci.yml

---
 .gitlab-ci.yml | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a849aea283..f13dac2f8e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -34,7 +34,7 @@ variables:
       - export DEBIAN_PRIORITY="low"
       - export DEBCONF_NONINTERACTIVE_SEEN="true"
       - |
-          # debconf 
+          # debconf
           echo -e "\e[0Ksection_end:`date +%s`:debconf\r\e[0K"
     - - |
           # dpkg_aa
@@ -67,7 +67,7 @@ variables:
           # Dir::Cache::Archives
           echo Dir::Cache::Archives "$APT_CACHE_DIR"\; | tee --append /etc/apt/apt.conf.d/99build
       - |
-          # apt_conf 
+          # apt_conf
           echo -e "\e[0Ksection_end:`date +%s`:apt_conf\r\e[0K"
     - - |
           # apt_update
@@ -110,7 +110,7 @@ variables:
           # cache.conf
           echo Adding ccache configution option
       - |
-          # base_dir 
+          # base_dir
           echo base_dir = $PWD                  | tee --append ~/.ccache/ccache.conf
       - |
           # cache_dir
@@ -165,6 +165,7 @@ Debian testing GCC:
     paths:
       - "bin/"
       - "src/comptime.h"
+    expose_as: "Debian GCC testing"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing-gcc"
 
   variables:
@@ -231,6 +232,7 @@ Debian stable:amd64:
     paths:
       - "bin/"
       - "src/comptime.h"
+    expose_as: "Debian amd64"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-x86-64"
 
   variables:
@@ -271,6 +273,7 @@ Debian stable:i386:
     paths:
       - "bin/"
       - "src/comptime.h"
+    expose_as: "Debian i386"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-i686"
 
   variables:
@@ -311,6 +314,7 @@ Debian stable:arm64:
     paths:
       - "bin/"
       - "src/comptime.h"
+    expose_as: "Debian arm64"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-aarch64"
 
   variables:
@@ -383,7 +387,7 @@ Debian stable Clang:
     paths:
       - "bin/"
       - "src/comptime.h"
-    expose_as: "Debian with clang"
+    expose_as: "Debian Clang"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-clang"
 
   variables:
@@ -412,10 +416,9 @@ Debian stable Clang:
           echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
       - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1
       - |
-          # make 
+          # make
           echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
-
 Debian testing Clang:
   extends: Debian stable Clang
 
@@ -425,7 +428,7 @@ Debian testing Clang:
     paths:
       - "bin/"
       - "src/comptime.h"
-    expose_as: "Debian testing with clang"
+    expose_as: "Debina Clang testing"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing-clang"
 
   variables:
-- 
GitLab


From eb3826782303f8a9edd22727dfacd353e8e706b0 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 20 Oct 2023 13:03:52 +0000
Subject: [PATCH 516/518] GitLab CI: More work

---
 .gitlab-ci.yml | 60 ++++++++++++++++++++++++--------------------------
 1 file changed, 29 insertions(+), 31 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f13dac2f8e..b292bfc06c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,16 +1,9 @@
-default:
-  image: debian:stable-slim
-
-stages:          # List of stages for jobs, and their order of execution
-  - build
-
 variables:
   GIT_STRATEGY: clone
   GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
 
-.job_template: &job_build # This job runs in the build stage, which runs first.
-
-  stage: build
+default:
+  image: debian:stable-slim
 
   cache:
     - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG
@@ -96,7 +89,7 @@ variables:
     - - |
           # apt_common
           echo -e "\e[0Ksection_start:`date +%s`:apt_common[collapsed=true]\r\e[0KInstalling common packages"
-      - apt-get install make git ccache
+      - apt-get install make git ccache nasm
       - |
           # apt_common
           echo -e "\e[0Ksection_end:`date +%s`:apt_common\r\e[0K"
@@ -137,6 +130,12 @@ variables:
           # ccache_reset
           echo -e "\e[0Ksection_end:`date +%s`:ccache_reset\r\e[0K"
 
+  artifacts:
+    paths:
+      - "bin/"
+      - "src/comptime.h"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-$CI_JOB_NAME_SLUG"
+
   after_script:
     - - |
            # apt_clean
@@ -155,21 +154,21 @@ variables:
           # ccahe_stats
           echo -e "\e[0Ksection_end:`date +%s`:ccache_stats\r\e[0K"
 
+stages:
+  - build
+
 Debian testing GCC:
-  <<: *job_build
+  stage: build
   image: debian:testing-slim
 
   allow_failure: true
 
   artifacts:
-    paths:
-      - "bin/"
-      - "src/comptime.h"
-    expose_as: "Debian GCC testing"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing-gcc"
 
   variables:
     CC: gcc
+    LDFLAGS: -Wl,-fuse-ld=gold
 
   script:
     - - |
@@ -197,12 +196,13 @@ Debian testing GCC:
           echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 Windows x86:
-  <<: *job_build
+  stage: build
 
   artifacts:
     paths:
       - "bin/"
       - "src/comptime.h"
+    expose_as: "Win32"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Win32"
 
   variables:
@@ -220,13 +220,13 @@ Windows x86:
     - - |
           # make
           echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 SDL=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW=1 SDL=1
       - |
           # make
           echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 Debian stable:amd64:
-  <<: *job_build
+  stage: build
 
   artifacts:
     paths:
@@ -237,6 +237,7 @@ Debian stable:amd64:
 
   variables:
     CC: x86_64-linux-gnu-gcc
+    LDFLAGS: -Wl,-fuse-ld=gold
     OBJCOPY: x86_64-linux-gnu-objcopy
     OBJDUMP: x86_64-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig
@@ -267,7 +268,7 @@ Debian stable:amd64:
           echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 Debian stable:i386:
-  <<: *job_build
+  stage: build
 
   artifacts:
     paths:
@@ -308,7 +309,7 @@ Debian stable:i386:
           echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 Debian stable:arm64:
-  <<: *job_build
+  stage: build
 
   artifacts:
     paths:
@@ -319,6 +320,7 @@ Debian stable:arm64:
 
   variables:
     CC: aarch64-linux-gnu-gcc
+    LDFLAGS: -Wl,-fuse-ld=gold
     OBJCOPY: aarch64-linux-gnu-objcopy
     OBJDUMP: aarch64-linux-gnu-objdump
     PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
@@ -349,7 +351,7 @@ Debian stable:arm64:
           echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 Windows x64:
-  <<: *job_build
+  stage: build
 
   artifacts:
     paths:
@@ -373,26 +375,24 @@ Windows x64:
     - - |
           # make
           echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
-      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 SDL=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 MINGW64=1 SDL=1
       - |
           # make
           echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
 Debian stable Clang:
-  <<: *job_build
+  stage: build
 
   allow_failure: true
 
   artifacts:
-    paths:
-      - "bin/"
-      - "src/comptime.h"
-    expose_as: "Debian Clang"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-clang"
 
   variables:
     CC: clang
+    WFLAGS: -Wno-cast-align
     CFLAGS: -Wno-cast-align
+    LDFLAGS: -Wl,-fuse-ld=gold
 
   script:
     - - |
@@ -425,12 +425,10 @@ Debian testing Clang:
   image: debian:testing-slim
 
   artifacts:
-    paths:
-      - "bin/"
-      - "src/comptime.h"
-    expose_as: "Debina Clang testing"
     name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing-clang"
 
   variables:
     CC: clang
+    WFLAGS: -Wno-cast-align -Wno-deprecated-non-prototype
     CFLAGS: -Wno-cast-align -Wno-deprecated-non-prototype
+    LDFLAGS: -Wl,-fuse-ld=gold
-- 
GitLab


From b544c4202ad6dd5a1ecb3a86f707bace4919b9ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Thu, 19 Oct 2023 18:05:16 +0200
Subject: [PATCH 517/518] Fix FreeBSD build errors (again)

---
 src/sdl/i_system.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 98e036130e..2a26f3f501 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -3038,11 +3038,11 @@ size_t I_GetFreeMem(size_t *total)
 #ifdef FREEBSD
 	u_int v_free_count, v_page_size, v_page_count;
 	size_t size = sizeof(v_free_count);
-	sysctlbyname("vm.stat.vm.v_free_count", &v_free_count, &size, NULL, 0);
-	size_t size = sizeof(v_page_size);
-	sysctlbyname("vm.stat.vm.v_page_size", &v_page_size, &size, NULL, 0);
-	size_t size = sizeof(v_page_count);
-	sysctlbyname("vm.stat.vm.v_page_count", &v_page_count, &size, NULL, 0);
+	sysctlbyname("vm.stats.vm.v_free_count", &v_free_count, &size, NULL, 0);
+	size = sizeof(v_page_size);
+	sysctlbyname("vm.stats.vm.v_page_size", &v_page_size, &size, NULL, 0);
+	size = sizeof(v_page_count);
+	sysctlbyname("vm.stats.vm.v_page_count", &v_page_count, &size, NULL, 0);
 
 	if (total)
 		*total = v_page_count * v_page_size;
-- 
GitLab


From 610fd76edb2a79bab3a9dbb243b05f6e1f4a9561 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Sun, 22 Oct 2023 11:09:58 -0400
Subject: [PATCH 518/518] dummy: fixup warnings

---
 src/dummy/i_system.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c
index 125d2e8aec..70e1ef4ec2 100644
--- a/src/dummy/i_system.c
+++ b/src/dummy/i_system.c
@@ -14,13 +14,18 @@ size_t I_GetFreeMem(size_t *total)
 	return 0;
 }
 
-void I_Sleep(UINT32 ms){}
+void I_Sleep(UINT32 ms)
+{
+	(void)ms;
+}
 
-precise_t I_GetPreciseTime(void) {
+precise_t I_GetPreciseTime(void)
+{
 	return 0;
 }
 
-UINT64 I_GetPrecisePrecision(void) {
+UINT64 I_GetPrecisePrecision(void)
+{
 	return 1000000;
 }
 
@@ -182,10 +187,12 @@ const char *I_ClipboardPaste(void)
 
 size_t I_GetRandomBytes(char *destination, size_t amount)
 {
+	(void)destination;
+	(void)amount;
 	return 0;
 }
 
-void I_RegisterSysCommands(void) {}
+void I_RegisterSysCommands(void){}
 
 void I_GetCursorPosition(INT32 *x, INT32 *y)
 {
-- 
GitLab