From 8919b35b936b365730001e5d69ca050a75dc830f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Fri, 2 Jun 2023 22:19:30 +0200
Subject: [PATCH 01/36] Fix buffer overflow when linedef type 415 executes

---
 src/p_spec.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/p_spec.c b/src/p_spec.c
index aa04a723e..d91129610 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2613,7 +2613,15 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				if (lumpnum == LUMPERROR || W_LumpLength(lumpnum) == 0)
 					CONS_Debug(DBG_SETUP, "Line type 415 Executor: script lump %s not found/not valid.\n", line->stringargs[0]);
 				else
-					COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE));
+				{
+					void *lump = W_CacheLumpNum(lumpnum, PU_CACHE);
+					size_t len = W_LumpLength(lumpnum);
+					char *text = Z_Malloc(len + 1, PU_CACHE, NULL);
+					memcpy(text, lump, len);
+					text[len] = '\0';
+					COM_BufInsertText(text);
+					Z_Free(text);
+				}
 			}
 			break;
 

From aaced79b41db8aea3e56b38ef3857aa836b3b6f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Tue, 25 Jul 2023 16:37:20 +0200
Subject: [PATCH 02/36] Fix segfaults on some systems due to polyvertex being
 NULL

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

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 36ff86abd..c650ecb51 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -582,10 +582,26 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool
 				P_ClosestPointOnLine(viewx, viewy, line->linedef, &v);
 				dist = FIXED_TO_FLOAT(R_PointToDist(v.x, v.y));
 
-				x1 = ((polyvertex_t *)line->pv1)->x;
-				y1 = ((polyvertex_t *)line->pv1)->y;
-				xd = ((polyvertex_t *)line->pv2)->x - x1;
-				yd = ((polyvertex_t *)line->pv2)->y - y1;
+				if (line->pv1)
+				{
+					x1 = ((polyvertex_t *)line->pv1)->x;
+					y1 = ((polyvertex_t *)line->pv1)->y;
+				}
+				else
+				{
+					x1 = FIXED_TO_FLOAT(line->v1->x);
+					y1 = FIXED_TO_FLOAT(line->v1->x);
+				}
+				if (line->pv2)
+				{
+					xd = ((polyvertex_t *)line->pv2)->x - x1;
+					yd = ((polyvertex_t *)line->pv2)->y - y1;
+				}
+				else
+				{
+					xd = FIXED_TO_FLOAT(line->v2->x) - x1;
+					yd = FIXED_TO_FLOAT(line->v2->y) - y1;
+				}
 
 				// Based on the seg length and the distance from the line, split horizon into multiple poly sets to reduce distortion
 				dist = sqrtf((xd*xd) + (yd*yd)) / dist / 16.0f;
@@ -1068,10 +1084,26 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 	gl_sidedef = gl_curline->sidedef;
 	gl_linedef = gl_curline->linedef;
 
-	vs.x = ((polyvertex_t *)gl_curline->pv1)->x;
-	vs.y = ((polyvertex_t *)gl_curline->pv1)->y;
-	ve.x = ((polyvertex_t *)gl_curline->pv2)->x;
-	ve.y = ((polyvertex_t *)gl_curline->pv2)->y;
+	if (gl_curline->pv1)
+	{
+		vs.x = ((polyvertex_t *)gl_curline->pv1)->x;
+		vs.y = ((polyvertex_t *)gl_curline->pv1)->y;
+	}
+	else
+	{
+		vs.x = FIXED_TO_FLOAT(gl_curline->v1->x);
+		vs.y = FIXED_TO_FLOAT(gl_curline->v1->y);
+	}
+	if (gl_curline->pv2)
+	{
+		ve.x = ((polyvertex_t *)gl_curline->pv2)->x;
+		ve.y = ((polyvertex_t *)gl_curline->pv2)->y;
+	}
+	else
+	{
+		ve.x = FIXED_TO_FLOAT(gl_curline->v2->x);
+		ve.y = FIXED_TO_FLOAT(gl_curline->v2->y);
+	}
 
 	v1x = FLOAT_TO_FIXED(vs.x);
 	v1y = FLOAT_TO_FIXED(vs.y);
@@ -1866,10 +1898,26 @@ static boolean CheckClip(seg_t * seg, sector_t * afrontsector, sector_t * abacks
 	if (afrontsector->f_slope || afrontsector->c_slope || abacksector->f_slope || abacksector->c_slope)
 	{
 		fixed_t v1x, v1y, v2x, v2y; // the seg's vertexes as fixed_t
-		v1x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->x);
-		v1y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->y);
-		v2x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->x);
-		v2y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->y);
+		if (gl_curline->pv1)
+		{
+			v1x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->x);
+			v1y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->y);
+		}
+		else
+		{
+			v1x = gl_curline->v1->x;
+			v1y = gl_curline->v1->y;
+		}
+		if (gl_curline->pv2)
+		{
+			v2x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->x);
+			v2y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->y);
+		}
+		else
+		{
+			v2x = gl_curline->v2->x;
+			v2y = gl_curline->v2->y;
+		}
 #define SLOPEPARAMS(slope, end1, end2, normalheight) \
 		end1 = P_GetZAt(slope, v1x, v1y, normalheight); \
 		end2 = P_GetZAt(slope, v2x, v2y, normalheight);
@@ -2242,10 +2290,26 @@ static void HWR_AddLine(seg_t * line)
 
 	gl_curline = line;
 
-	v1x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->x);
-	v1y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->y);
-	v2x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->x);
-	v2y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->y);
+	if (gl_curline->pv1)
+	{
+		v1x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->x);
+		v1y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv1)->y);
+	}
+	else
+	{
+		v1x = gl_curline->v1->x;
+		v1y = gl_curline->v1->y;
+	}
+	if (gl_curline->pv2)
+	{
+		v2x = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->x);
+		v2y = FLOAT_TO_FIXED(((polyvertex_t *)gl_curline->pv2)->y);
+	}
+	else
+	{
+		v2x = gl_curline->v2->x;
+		v2y = gl_curline->v2->y;
+	}
 
 	// OPTIMIZE: quickly reject orthogonal back sides.
 	angle1 = R_PointToAngle64(v1x, v1y);

From 4ea2887d7fa41cd753b46b05de6920b9d84d3fbc Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Fri, 4 Aug 2023 00:31:51 -0300
Subject: [PATCH 03/36] Lua colorlib: extracolormap support

---
 src/CMakeLists.txt |   1 +
 src/lua_baselib.c  |  22 +++
 src/lua_colorlib.c | 335 +++++++++++++++++++++++++++++++++++++++++++++
 src/lua_libs.h     |   2 +
 src/lua_maplib.c   |   5 +
 src/lua_script.c   |   1 +
 src/m_misc.c       |  14 ++
 src/m_misc.h       |   3 +
 src/p_local.h      |   4 +
 src/p_map.c        |  32 +++++
 src/r_data.c       |  55 ++++----
 src/r_data.h       |   1 +
 src/r_things.c     |  42 +-----
 13 files changed, 457 insertions(+), 60 deletions(-)
 create mode 100644 src/lua_colorlib.c

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b926b3b7a..92cc11637 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -105,6 +105,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
 	lua_blockmaplib.c
 	lua_hudlib.c
 	lua_hudlib_drawlist.c
+	lua_colorlib.c
 	lua_inputlib.c
 )
 
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 915669bb2..ecebae4ac 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -213,6 +213,7 @@ static const struct {
 	{META_HUDINFO,      "hudinfo_t"},
 	{META_PATCH,        "patch_t"},
 	{META_COLORMAP,     "colormap"},
+	{META_EXTRACOLORMAP,"extracolormap_t"},
 	{META_CAMERA,       "camera_t"},
 
 	{META_ACTION,       "action"},
@@ -1928,6 +1929,26 @@ static int lib_pCeilingzAtPos(lua_State *L)
 	return 1;
 }
 
+static int lib_pGetSectorColormapAt(lua_State *L)
+{
+	sector_t *sector = NULL;
+	if (!lua_isnone(L, 1) && lua_isuserdata(L, 1))
+		sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
+	fixed_t x = luaL_checkfixed(L, 2);
+	fixed_t y = luaL_checkfixed(L, 3);
+	fixed_t z = luaL_checkfixed(L, 4);
+	INLEVEL
+	if (!sector)
+		return LUA_ErrInvalid(L, "sector_t");
+	extracolormap_t *exc;
+	if (sector)
+		exc = P_GetColormapFromSectorAt(sector, x, y, z);
+	else
+		exc = P_GetSectorColormapAt(x, y, z);
+	LUA_PushUserdata(L, exc, META_EXTRACOLORMAP);
+	return 1;
+}
+
 static int lib_pDoSpring(lua_State *L)
 {
 	mobj_t *spring = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@@ -4151,6 +4172,7 @@ static luaL_Reg lib[] = {
 	{"P_RadiusAttack",lib_pRadiusAttack},
 	{"P_FloorzAtPos",lib_pFloorzAtPos},
 	{"P_CeilingzAtPos",lib_pCeilingzAtPos},
+	{"P_GetSectorColormapAt",lib_pGetSectorColormapAt},
 	{"P_DoSpring",lib_pDoSpring},
 	{"P_TryCameraMove", lib_pTryCameraMove},
 	{"P_TeleportCameraMove", lib_pTeleportCameraMove},
diff --git a/src/lua_colorlib.c b/src/lua_colorlib.c
new file mode 100644
index 000000000..ee7074e84
--- /dev/null
+++ b/src/lua_colorlib.c
@@ -0,0 +1,335 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2021-2022 by "Lactozilla".
+// Copyright (C) 2014-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  lua_colorlib.c
+/// \brief color and colormap libraries for Lua scripting
+
+#include "doomdef.h"
+#include "fastcmp.h"
+#include "r_data.h"
+
+#include "lua_script.h"
+#include "lua_libs.h"
+
+static UINT8 GetRGBAColorsFromTable(lua_State *L, UINT32 index, UINT8 *rgba, boolean useAlpha)
+{
+	UINT8 num = 0;
+
+	lua_pushnil(L);
+
+	while (lua_next(L, index)) {
+		lua_Integer i = 0;
+		const char *field = NULL;
+		if (lua_isnumber(L, -2))
+			i = lua_tointeger(L, -2);
+		else
+			field = luaL_checkstring(L, -2);
+
+#define CHECKFIELD(p, c) (i == p || (field && fastcmp(field, c)))
+#define RGBASET(p, c) { \
+			INT32 color = luaL_checkinteger(L, -1); \
+			if (color < 0 || color > 255) \
+				luaL_error(L, c " channel %d out of range (0 - 255)", color); \
+			rgba[p] = (UINT8)color; \
+			num++; \
+		}
+
+		if (CHECKFIELD(1, "r")) {
+			RGBASET(0, "red color");
+		} else if (CHECKFIELD(2, "g")) {
+			RGBASET(1, "green color");
+		} else if (CHECKFIELD(3, "b")) {
+			RGBASET(2, "blue color");
+		} else if (useAlpha && CHECKFIELD(4, "a")) {
+			RGBASET(3, "alpha");
+		}
+
+#undef CHECKFIELD
+#undef RGBASET
+
+		lua_pop(L, 1);
+	}
+
+	return num;
+}
+
+#define IS_HEX_CHAR(x) ((x >= '0' && x <= '9') || (x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F'))
+#define ARE_HEX_CHARS(str, i) IS_HEX_CHAR(str[i]) && IS_HEX_CHAR(str[i + 1])
+
+static UINT32 hex2int(char x)
+{
+	if (x >= '0' && x <= '9')
+		return x - '0';
+	else if (x >= 'a' && x <= 'f')
+		return x - 'a' + 10;
+	else if (x >= 'A' && x <= 'F')
+		return x - 'A' + 10;
+
+	return 0;
+}
+
+static UINT8 ParseHTMLColor(const char *str, UINT8 *rgba, size_t numc)
+{
+	const char *hex = str;
+
+	if (hex[0] == '#')
+		hex++;
+	else if (!IS_HEX_CHAR(hex[0]))
+		return 0;
+
+	size_t len = strlen(hex);
+
+	if (len == 3)
+	{
+		// Shorthand like #09C
+		for (unsigned i = 0; i < 3; i++)
+		{
+			if (!IS_HEX_CHAR(hex[i]))
+				return 0;
+
+			UINT32 hx = hex2int(hex[i]);
+			*rgba++ = (hx * 16) + hx;
+		}
+
+		return 3;
+	}
+	else if (len == 6 || len == 8)
+	{
+		if (numc != 4)
+			len = 6;
+
+		// A triplet like #0099CC
+		for (unsigned i = 0; i < len; i += 2)
+		{
+			if (!ARE_HEX_CHARS(hex, i))
+				return false;
+
+			*rgba++ = (hex2int(hex[i]) * 16) + hex2int(hex[i + 1]);
+		}
+
+		return len;
+	}
+
+	return 0;
+}
+
+/////////////////////////
+// extracolormap userdata
+/////////////////////////
+
+enum extracolormap_e {
+	extracolormap_r = 0,
+	extracolormap_g,
+	extracolormap_b,
+	extracolormap_a,
+	extracolormap_rgba,
+	extracolormap_fade_r,
+	extracolormap_fade_g,
+	extracolormap_fade_b,
+	extracolormap_fade_a,
+	extracolormap_fade_rgba,
+	extracolormap_fade_start,
+	extracolormap_fade_end,
+	extracolormap_colormap
+};
+
+static const char *const extracolormap_opt[] = {
+	"r",
+	"g",
+	"b",
+	"a",
+	"rgba",
+	"fade_r",
+	"fade_g",
+	"fade_b",
+	"fade_a",
+	"fade_rgba",
+	"fade_start",
+	"fade_end",
+	"colormap",
+	NULL};
+
+#define ALPHA_SCALE_FACTOR 102 // (255 / 25) * 10
+#define SCALE_ALPHA_UP(alpha) (((alpha) * ALPHA_SCALE_FACTOR) / 10)
+#define SCALE_ALPHA_DOWN(alpha) (((alpha) * 10) / ALPHA_SCALE_FACTOR)
+
+static int extracolormap_get(lua_State *L)
+{
+	extracolormap_t *exc = *((extracolormap_t **)luaL_checkudata(L, 1, META_EXTRACOLORMAP));
+	enum extracolormap_e field = luaL_checkoption(L, 2, NULL, extracolormap_opt);
+
+	switch (field)
+	{
+	case extracolormap_r:
+		lua_pushinteger(L, R_GetRgbaR(exc->rgba));
+		break;
+	case extracolormap_g:
+		lua_pushinteger(L, R_GetRgbaG(exc->rgba));
+		break;
+	case extracolormap_b:
+		lua_pushinteger(L, R_GetRgbaB(exc->rgba));
+		break;
+	case extracolormap_a:
+		lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->rgba)));
+		break;
+	case extracolormap_rgba:
+		lua_pushinteger(L, R_GetRgbaR(exc->rgba));
+		lua_pushinteger(L, R_GetRgbaG(exc->rgba));
+		lua_pushinteger(L, R_GetRgbaB(exc->rgba));
+		lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->rgba)));
+		return 4;
+	case extracolormap_fade_r:
+		lua_pushinteger(L, R_GetRgbaR(exc->fadergba));
+		break;
+	case extracolormap_fade_g:
+		lua_pushinteger(L, R_GetRgbaG(exc->fadergba));
+		break;
+	case extracolormap_fade_b:
+		lua_pushinteger(L, R_GetRgbaB(exc->fadergba));
+		break;
+	case extracolormap_fade_a:
+		lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->fadergba)));
+		break;
+	case extracolormap_fade_rgba:
+		lua_pushinteger(L, R_GetRgbaR(exc->fadergba));
+		lua_pushinteger(L, R_GetRgbaG(exc->fadergba));
+		lua_pushinteger(L, R_GetRgbaB(exc->fadergba));
+		lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->fadergba)));
+		return 4;
+	case extracolormap_fade_start:
+		lua_pushinteger(L, exc->fadestart);
+		break;
+	case extracolormap_fade_end:
+		lua_pushinteger(L, exc->fadeend);
+		break;
+	case extracolormap_colormap:
+		LUA_PushUserdata(L, exc->colormap, META_COLORMAP);
+		break;
+	}
+	return 1;
+}
+
+static void GetExtraColormapRGBA(lua_State *L, UINT8 *rgba)
+{
+	rgba[3] = SCALE_ALPHA_UP(rgba[3]);
+
+	if (lua_type(L, 3) == LUA_TSTRING)
+	{
+		const char *str = lua_tostring(L, 3);
+		UINT8 parsed = ParseHTMLColor(str, rgba, 4);
+		if (!parsed)
+			luaL_error(L, "Malformed HTML color '%s'", str);
+	}
+	else
+		GetRGBAColorsFromTable(L, 3, rgba, true);
+
+	rgba[3] = SCALE_ALPHA_DOWN(rgba[3]);
+}
+
+static int extracolormap_set(lua_State *L)
+{
+	extracolormap_t *exc = *((extracolormap_t **)luaL_checkudata(L, 1, META_EXTRACOLORMAP));
+	enum extracolormap_e field = luaL_checkoption(L, 2, NULL, extracolormap_opt);
+
+	UINT8 r = R_GetRgbaR(exc->rgba);
+	UINT8 g = R_GetRgbaG(exc->rgba);
+	UINT8 b = R_GetRgbaB(exc->rgba);
+	UINT8 a = R_GetRgbaA(exc->rgba);
+
+	UINT8 fr = R_GetRgbaR(exc->fadergba);
+	UINT8 fg = R_GetRgbaG(exc->fadergba);
+	UINT8 fb = R_GetRgbaB(exc->fadergba);
+	UINT8 fa = R_GetRgbaA(exc->fadergba);
+
+	UINT8 rgba[4];
+
+	INT32 old_rgba = exc->rgba, old_fade_rgba = exc->fadergba; // It's not unsigned?
+	UINT8 old_fade_start = exc->fadestart, old_fade_end = exc->fadeend;
+
+#define val luaL_checkinteger(L, 3)
+
+	switch(field)
+	{
+	case extracolormap_r:
+		exc->rgba = R_PutRgbaRGBA(val, g, b, a);
+		break;
+	case extracolormap_g:
+		exc->rgba = R_PutRgbaRGBA(r, val, b, a);
+		break;
+	case extracolormap_b:
+		exc->rgba = R_PutRgbaRGBA(r, g, val, a);
+		break;
+	case extracolormap_a:
+		exc->rgba = R_PutRgbaRGBA(r, g, b, SCALE_ALPHA_DOWN(val));
+		break;
+	case extracolormap_rgba:
+		rgba[0] = r;
+		rgba[1] = g;
+		rgba[2] = b;
+		rgba[3] = a;
+		GetExtraColormapRGBA(L, rgba);
+		exc->rgba = R_PutRgbaRGBA(rgba[0], rgba[1], rgba[2], rgba[3]);
+		break;
+	case extracolormap_fade_r:
+		exc->fadergba = R_PutRgbaRGBA(val, fg, fb, fa);
+		break;
+	case extracolormap_fade_g:
+		exc->fadergba = R_PutRgbaRGBA(fr, val, fb, fa);
+		break;
+	case extracolormap_fade_b:
+		exc->fadergba = R_PutRgbaRGBA(fr, fg, val, fa);
+		break;
+	case extracolormap_fade_a:
+		exc->fadergba = R_PutRgbaRGBA(fr, fg, fb, SCALE_ALPHA_DOWN(val));
+		break;
+	case extracolormap_fade_rgba:
+		rgba[0] = fr;
+		rgba[1] = fg;
+		rgba[2] = fb;
+		rgba[3] = fa;
+		GetExtraColormapRGBA(L, rgba);
+		exc->fadergba = R_PutRgbaRGBA(rgba[0], rgba[1], rgba[2], rgba[3]);
+		break;
+	case extracolormap_fade_start:
+		if (val > 31)
+			return luaL_error(L, "fade start %d out of range (0 - 31)", val);
+		exc->fadestart = val;
+		break;
+	case extracolormap_fade_end:
+		if (val > 31)
+			return luaL_error(L, "fade end %d out of range (0 - 31)", val);
+		exc->fadeend = val;
+		break;
+	case extracolormap_colormap:
+		return luaL_error(L, LUA_QL("extracolormap_t") " field " LUA_QS " should not be set directly.", extracolormap_opt[field]);
+	}
+
+#undef val
+
+	if (exc->rgba != old_rgba
+		|| exc->fadergba != old_fade_rgba
+		|| exc->fadestart != old_fade_start
+		|| exc->fadeend != old_fade_end)
+	R_GenerateLightTable(exc, true);
+
+	return 0;
+}
+
+int LUA_ColorLib(lua_State *L)
+{
+	luaL_newmetatable(L, META_EXTRACOLORMAP);
+		lua_pushcfunction(L, extracolormap_get);
+		lua_setfield(L, -2, "__index");
+
+		lua_pushcfunction(L, extracolormap_set);
+		lua_setfield(L, -2, "__newindex");
+	lua_pop(L, 1);
+
+	return 0;
+}
diff --git a/src/lua_libs.h b/src/lua_libs.h
index 7f8d21f38..b520bb983 100644
--- a/src/lua_libs.h
+++ b/src/lua_libs.h
@@ -84,6 +84,7 @@ extern boolean mousegrabbedbylua;
 #define META_HUDINFO "HUDINFO_T*"
 #define META_PATCH "PATCH_T*"
 #define META_COLORMAP "COLORMAP"
+#define META_EXTRACOLORMAP "EXTRACOLORMAP_T"
 #define META_CAMERA "CAMERA_T*"
 
 #define META_ACTION "ACTIONF_T*"
@@ -111,4 +112,5 @@ int LUA_TagLib(lua_State *L);
 int LUA_PolyObjLib(lua_State *L);
 int LUA_BlockmapLib(lua_State *L);
 int LUA_HudLib(lua_State *L);
+int LUA_ColorLib(lua_State *L);
 int LUA_InputLib(lua_State *L);
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 3d95cdb75..440d1fce4 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -57,6 +57,7 @@ enum sector_e {
 	sector_ffloors,
 	sector_fslope,
 	sector_cslope,
+	sector_extracolormap,
 	sector_flags,
 	sector_specialflags,
 	sector_damagetype,
@@ -95,6 +96,7 @@ static const char *const sector_opt[] = {
 	"ffloors",
 	"f_slope",
 	"c_slope",
+	"extracolormap",
 	"flags",
 	"specialflags",
 	"damagetype",
@@ -751,6 +753,9 @@ static int sector_get(lua_State *L)
 	case sector_cslope: // c_slope
 		LUA_PushUserdata(L, sector->c_slope, META_SLOPE);
 		return 1;
+	case sector_extracolormap: // extra_colormap
+		LUA_PushUserdata(L, sector->extra_colormap, META_EXTRACOLORMAP);
+		return 1;
 	case sector_flags: // flags
 		lua_pushinteger(L, sector->flags);
 		return 1;
diff --git a/src/lua_script.c b/src/lua_script.c
index 6a5982006..8b081c587 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -58,6 +58,7 @@ static lua_CFunction liblist[] = {
 	LUA_PolyObjLib, // polyobj_t
 	LUA_BlockmapLib, // blockmap stuff
 	LUA_HudLib, // HUD stuff
+	LUA_ColorLib, // general color functions
 	LUA_InputLib, // inputs
 	NULL
 };
diff --git a/src/m_misc.c b/src/m_misc.c
index f547f5c41..68bc52055 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -2805,3 +2805,17 @@ boolean M_IsStringEmpty(const char *s)
 
 	return true;
 }
+
+// Rounds off floating numbers and checks for 0 - 255 bounds
+int M_RoundUp(double number)
+{
+	if (number > 255.0l)
+		return 255;
+	if (number < 0.0l)
+		return 0;
+
+	if ((int)number <= (int)(number - 0.5f))
+		return (int)number + 1;
+
+	return (int)number;
+}
diff --git a/src/m_misc.h b/src/m_misc.h
index 8cad7ba9a..753991e70 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -112,6 +112,9 @@ boolean M_IsStringEmpty(const char *s);
 // counting bits, for weapon ammo code, usually
 FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size);
 
+// Rounds off floating numbers and checks for 0 - 255 bounds
+int M_RoundUp(double number);
+
 #include "w_wad.h"
 extern char configfile[MAX_WADPATH];
 
diff --git a/src/p_local.h b/src/p_local.h
index 563e257d8..7110d3494 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -438,6 +438,10 @@ boolean PIT_PushableMoved(mobj_t *thing);
 
 boolean P_DoSpring(mobj_t *spring, mobj_t *object);
 
+INT32 P_GetSectorLightAt(sector_t *sector, fixed_t x, fixed_t y, fixed_t z);
+extracolormap_t *P_GetColormapFromSectorAt(sector_t *sector, fixed_t x, fixed_t y, fixed_t z);
+extracolormap_t *P_GetSectorColormapAt(fixed_t x, fixed_t y, fixed_t z);
+
 //
 // P_SETUP
 //
diff --git a/src/p_map.c b/src/p_map.c
index 132a3cf85..9ffe238e9 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -5067,3 +5067,35 @@ fixed_t P_CeilingzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height)
 
 	return ceilingz;
 }
+
+INT32 P_GetSectorLightAt(sector_t *sector, fixed_t x, fixed_t y, fixed_t z)
+{
+	if (!sector->numlights)
+		return -1;
+
+	INT32 light = sector->numlights - 1;
+
+	// R_GetPlaneLight won't work on sloped lights!
+	for (INT32 lightnum = 1; lightnum < sector->numlights; lightnum++) {
+		fixed_t h = P_GetLightZAt(&sector->lightlist[lightnum], x, y);
+		if (h <= z) {
+			light = lightnum - 1;
+			break;
+		}
+	}
+
+	return light;
+}
+
+extracolormap_t *P_GetColormapFromSectorAt(sector_t *sector, fixed_t x, fixed_t y, fixed_t z)
+{
+	if (sector->numlights)
+		return *sector->lightlist[P_GetSectorLightAt(sector, x, y, z)].extra_colormap;
+	else
+		return sector->extra_colormap;
+}
+
+extracolormap_t *P_GetSectorColormapAt(fixed_t x, fixed_t y, fixed_t z)
+{
+	return P_GetColormapFromSectorAt(R_PointInSubsector(x, y)->sector, x, y, z);
+}
diff --git a/src/r_data.c b/src/r_data.c
index 4b7492f90..1795e7727 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -692,9 +692,26 @@ extracolormap_t *R_ColormapForName(char *name)
 //
 static double deltas[256][3], map[256][3];
 
-static int RoundUp(double number);
+static colorlookup_t lighttable_lut;
+
+static UINT8 LightTableNearest(UINT8 r, UINT8 g, UINT8 b)
+{
+	return NearestColor(r, g, b);
+}
+
+static UINT8 LightTableNearest_LUT(UINT8 r, UINT8 g, UINT8 b)
+{
+	return GetColorLUT(&lighttable_lut, r, g, b);
+}
 
 lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
+{
+	extra_colormap->colormap = Z_MallocAlign((256 * 34) + 10, PU_LEVEL, NULL, 8);
+	R_GenerateLightTable(extra_colormap, false);
+	return extra_colormap->colormap;
+}
+
+void R_GenerateLightTable(extracolormap_t *extra_colormap, boolean uselookup)
 {
 	double cmaskr, cmaskg, cmaskb, cdestr, cdestg, cdestb;
 	double maskamt = 0, othermask = 0;
@@ -711,7 +728,6 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
 	UINT8 fadestart = extra_colormap->fadestart,
 		fadedist = extra_colormap->fadeend - extra_colormap->fadestart;
 
-	lighttable_t *lighttable = NULL;
 	size_t i;
 
 	/////////////////////
@@ -753,6 +769,16 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
 		int p;
 		char *colormap_p;
 
+		UINT8 (*NearestColorFunc)(UINT8, UINT8, UINT8);
+
+		if (uselookup)
+		{
+			InitColorLUT(&lighttable_lut, pMasterPalette, false);
+			NearestColorFunc = LightTableNearest_LUT;
+		}
+		else
+			NearestColorFunc = LightTableNearest;
+
 		// Initialise the map and delta arrays
 		// map[i] stores an RGB color (as double) for index i,
 		//  which is then converted to SRB2's palette later
@@ -783,8 +809,7 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
 
 		// Now allocate memory for the actual colormap array itself!
 		// aligned on 8 bit for asm code
-		colormap_p = Z_MallocAlign((256 * 34) + 10, PU_LEVEL, NULL, 8);
-		lighttable = (UINT8 *)colormap_p;
+		colormap_p = (char *)extra_colormap->colormap;
 
 		// Calculate the palette index for each palette index, for each light level
 		// (as well as the two unused colormap lines we inherited from Doom)
@@ -792,9 +817,9 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
 		{
 			for (i = 0; i < 256; i++)
 			{
-				*colormap_p = NearestColor((UINT8)RoundUp(map[i][0]),
-					(UINT8)RoundUp(map[i][1]),
-					(UINT8)RoundUp(map[i][2]));
+				*colormap_p = NearestColorFunc((UINT8)M_RoundUp(map[i][0]),
+					(UINT8)M_RoundUp(map[i][1]),
+					(UINT8)M_RoundUp(map[i][2]));
 				colormap_p++;
 
 				if ((UINT32)p < fadestart)
@@ -818,8 +843,6 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
 			}
 		}
 	}
-
-	return lighttable;
 }
 
 extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3)
@@ -1133,20 +1156,6 @@ UINT8 NearestPaletteColor(UINT8 r, UINT8 g, UINT8 b, RGBA_t *palette)
 	return (UINT8)bestcolor;
 }
 
-// Rounds off floating numbers and checks for 0 - 255 bounds
-static int RoundUp(double number)
-{
-	if (number > 255.0l)
-		return 255;
-	if (number < 0.0l)
-		return 0;
-
-	if ((int)number <= (int)(number - 0.5f))
-		return (int)number + 1;
-
-	return (int)number;
-}
-
 #ifdef EXTRACOLORMAPLUMPS
 const char *R_NameForColormap(extracolormap_t *extra_colormap)
 {
diff --git a/src/r_data.h b/src/r_data.h
index ef5c967e5..364f85b6d 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -92,6 +92,7 @@ typedef enum
 	TMCF_OVERRIDE     = 1<<13,
 } textmapcolormapflags_t;
 
+void R_GenerateLightTable(extracolormap_t *extra_colormap, boolean uselookup);
 lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap);
 extracolormap_t * R_CreateColormapFromLinedef(char *p1, char *p2, char *p3);
 extracolormap_t* R_CreateColormap(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags);
diff --git a/src/r_things.c b/src/r_things.c
index 90b80dda8..a0b49072e 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1306,8 +1306,8 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 	patch_t *patch;
 	fixed_t xscale, yscale, shadowxscale, shadowyscale, shadowskew, x1, x2;
 	INT32 heightsec, phs;
-	INT32 light = 0;
-	fixed_t scalemul; UINT8 trans;
+	fixed_t scalemul;
+	UINT8 trans;
 	fixed_t floordiff;
 	fixed_t groundz;
 	pslope_t *groundslope;
@@ -1425,27 +1425,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 	if (thing->renderflags & RF_NOCOLORMAPS)
 		shadow->extra_colormap = NULL;
 	else
-	{
-		if (thing->subsector->sector->numlights)
-		{
-			INT32 lightnum;
-			light = thing->subsector->sector->numlights - 1;
-
-			// R_GetPlaneLight won't work on sloped lights!
-			for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) {
-				fixed_t h = P_GetLightZAt(&thing->subsector->sector->lightlist[lightnum], interp.x, interp.y);
-				if (h <= shadow->gzt) {
-					light = lightnum - 1;
-					break;
-				}
-			}
-		}
-
-		if (thing->subsector->sector->numlights)
-			shadow->extra_colormap = *thing->subsector->sector->lightlist[light].extra_colormap;
-		else
-			shadow->extra_colormap = thing->subsector->sector->extra_colormap;
-	}
+		shadow->extra_colormap = P_GetColormapFromSectorAt(thing->subsector->sector, interp.x, interp.y, shadow->gzt);
 
 	shadow->transmap = R_GetTranslucencyTable(trans + 1);
 	shadow->colormap = scalelight[0][0]; // full dark!
@@ -2132,21 +2112,9 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	if (thing->subsector->sector->numlights)
 	{
-		INT32 lightnum;
-		fixed_t top = (splat) ? gz : gzt;
-		light = thing->subsector->sector->numlights - 1;
-
-		// R_GetPlaneLight won't work on sloped lights!
-		for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) {
-			fixed_t h = P_GetLightZAt(&thing->subsector->sector->lightlist[lightnum], interp.x, interp.y);
-			if (h <= top) {
-				light = lightnum - 1;
-				break;
-			}
-		}
-		//light = R_GetPlaneLight(thing->subsector->sector, gzt, false);
-		lightnum = (*thing->subsector->sector->lightlist[light].lightlevel >> LIGHTSEGSHIFT);
+		light = P_GetSectorLightAt(thing->subsector->sector, interp.x, interp.y, splat ? gz : gzt);
 
+		INT32 lightnum = (*thing->subsector->sector->lightlist[light].lightlevel >> LIGHTSEGSHIFT);
 		if (lightnum < 0)
 			spritelights = scalelight[0];
 		else if (lightnum >= LIGHTLEVELS)

From b670f97ed1512655c7bd0463fc54b9f06106e40d Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Fri, 4 Aug 2023 00:36:11 -0300
Subject: [PATCH 04/36] Add lighttable_t support

---
 src/lua_baselib.c  |  1 +
 src/lua_colorlib.c | 33 ++++++++++++++++++++++++++++++++-
 src/lua_libs.h     |  3 ++-
 src/r_defs.h       |  3 +++
 src/r_draw.c       |  2 --
 5 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index ecebae4ac..cdda48e8f 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -214,6 +214,7 @@ static const struct {
 	{META_PATCH,        "patch_t"},
 	{META_COLORMAP,     "colormap"},
 	{META_EXTRACOLORMAP,"extracolormap_t"},
+	{META_LIGHTTABLE,   "lighttable_t"},
 	{META_CAMERA,       "camera_t"},
 
 	{META_ACTION,       "action"},
diff --git a/src/lua_colorlib.c b/src/lua_colorlib.c
index ee7074e84..a963a6a6b 100644
--- a/src/lua_colorlib.c
+++ b/src/lua_colorlib.c
@@ -209,7 +209,7 @@ static int extracolormap_get(lua_State *L)
 		lua_pushinteger(L, exc->fadeend);
 		break;
 	case extracolormap_colormap:
-		LUA_PushUserdata(L, exc->colormap, META_COLORMAP);
+		LUA_PushUserdata(L, exc->colormap, META_LIGHTTABLE);
 		break;
 	}
 	return 1;
@@ -321,6 +321,29 @@ static int extracolormap_set(lua_State *L)
 	return 0;
 }
 
+static int lighttable_get(lua_State *L)
+{
+	void **userdata;
+
+	lighttable_t *table = *((lighttable_t **)luaL_checkudata(L, 1, META_LIGHTTABLE));
+	UINT32 row = luaL_checkinteger(L, 2);
+	if (row < 1 || row > 34)
+		return luaL_error(L, "lighttable row %d out of range (1 - %d)", row, 34);
+
+	userdata = lua_newuserdata(L, sizeof(void *));
+	*userdata = &table[256 * (row - 1)];
+	luaL_getmetatable(L, META_COLORMAP);
+	lua_setmetatable(L, -2);
+
+	return 1;
+}
+
+static int lighttable_len(lua_State *L)
+{
+	lua_pushinteger(L, NUM_PALETTE_ENTRIES);
+	return 1;
+}
+
 int LUA_ColorLib(lua_State *L)
 {
 	luaL_newmetatable(L, META_EXTRACOLORMAP);
@@ -331,5 +354,13 @@ int LUA_ColorLib(lua_State *L)
 		lua_setfield(L, -2, "__newindex");
 	lua_pop(L, 1);
 
+	luaL_newmetatable(L, META_LIGHTTABLE);
+		lua_pushcfunction(L, lighttable_get);
+		lua_setfield(L, -2, "__index");
+
+		lua_pushcfunction(L, lighttable_len);
+		lua_setfield(L, -2, "__len");
+	lua_pop(L, 1);
+
 	return 0;
 }
diff --git a/src/lua_libs.h b/src/lua_libs.h
index b520bb983..65d5acb1b 100644
--- a/src/lua_libs.h
+++ b/src/lua_libs.h
@@ -84,7 +84,8 @@ extern boolean mousegrabbedbylua;
 #define META_HUDINFO "HUDINFO_T*"
 #define META_PATCH "PATCH_T*"
 #define META_COLORMAP "COLORMAP"
-#define META_EXTRACOLORMAP "EXTRACOLORMAP_T"
+#define META_EXTRACOLORMAP "EXTRACOLORMAP_T*"
+#define META_LIGHTTABLE "LIGHTTABLE_T*"
 #define META_CAMERA "CAMERA_T*"
 
 #define META_ACTION "ACTIONF_T*"
diff --git a/src/r_defs.h b/src/r_defs.h
index a9b9a4a08..1edbba473 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -53,6 +53,9 @@ typedef struct
 // Could even use more than 32 levels.
 typedef UINT8 lighttable_t;
 
+#define NUM_PALETTE_ENTRIES 256
+#define DEFAULT_STARTTRANSCOLOR 96
+
 #define CMF_FADEFULLBRIGHTSPRITES  1
 #define CMF_FOG 4
 
diff --git a/src/r_draw.c b/src/r_draw.c
index df9e1a460..671767b21 100644
--- a/src/r_draw.c
+++ b/src/r_draw.c
@@ -132,8 +132,6 @@ UINT32 nflatxshift, nflatyshift, nflatshiftup, nflatmask;
 #define RAINBOW_TT_CACHE_INDEX (MAXSKINS + 4)
 #define BLINK_TT_CACHE_INDEX (MAXSKINS + 5)
 #define DASHMODE_TT_CACHE_INDEX (MAXSKINS + 6)
-#define DEFAULT_STARTTRANSCOLOR 96
-#define NUM_PALETTE_ENTRIES 256
 
 static UINT8 **translationtablecache[MAXSKINS + 7] = {NULL};
 UINT8 skincolor_modified[MAXSKINCOLORS];

From ec8757d716e196d5d0f226a1fbfdef6827d287b5 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Fri, 4 Aug 2023 05:00:46 -0300
Subject: [PATCH 05/36] Extend extracolormap alpha range

---
 src/hardware/hw_defs.h           |  3 --
 src/hardware/hw_main.c           |  8 ++---
 src/hardware/hw_md2.c            |  3 --
 src/hardware/r_opengl/r_opengl.c |  2 +-
 src/lua_colorlib.c               | 20 ++++--------
 src/r_data.c                     | 54 ++++++++++++++++----------------
 6 files changed, 38 insertions(+), 52 deletions(-)

diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index 74c4ed7d2..b02f2824a 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -276,9 +276,6 @@ struct FSurfaceInfo
 };
 typedef struct FSurfaceInfo FSurfaceInfo;
 
-#define GL_DEFAULTMIX 0x00000000
-#define GL_DEFAULTFOG 0xFF000000
-
 //Hurdler: added for backward compatibility
 enum hwdsetspecialstate
 {
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 36ff86abd..568161425 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -186,8 +186,8 @@ void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *col
 	RGBA_t poly_color, tint_color, fade_color;
 
 	poly_color.rgba = 0xFFFFFFFF;
-	tint_color.rgba = (colormap != NULL) ? (UINT32)colormap->rgba : GL_DEFAULTMIX;
-	fade_color.rgba = (colormap != NULL) ? (UINT32)colormap->fadergba : GL_DEFAULTFOG;
+	tint_color.rgba = (colormap != NULL) ? (UINT32)colormap->rgba : 0x00000000;
+	fade_color.rgba = (colormap != NULL) ? (UINT32)colormap->fadergba : 0xFF000000;
 
 	// Crappy backup coloring if you can't do shaders
 	if (!HWR_UseShader())
@@ -201,7 +201,7 @@ void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *col
 		blue = (float)poly_color.s.blue;
 
 		// 48 is just an arbritrary value that looked relatively okay.
-		tint_alpha = (float)(sqrt(tint_color.s.alpha) * 48) / 255.0f;
+		tint_alpha = (float)(sqrt((float)tint_color.s.alpha / 10.2) * 48) / 255.0f;
 
 		// 8 is roughly the brightness of the "close" color in Software, and 16 the brightness of the "far" color.
 		// 8 is too bright for dark levels, and 16 is too dark for bright levels.
@@ -242,7 +242,7 @@ UINT8 HWR_FogBlockAlpha(INT32 light, extracolormap_t *colormap) // Let's see if
 	RGBA_t realcolor, surfcolor;
 	INT32 alpha;
 
-	realcolor.rgba = (colormap != NULL) ? colormap->rgba : GL_DEFAULTMIX;
+	realcolor.rgba = (colormap != NULL) ? colormap->rgba : 0x00000000;
 
 	if (cv_glshaders.value && gl_shadersavailable)
 	{
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index d005f0037..6d1f12664 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1140,9 +1140,6 @@ static void HWR_GetBlendedTexture(patch_t *patch, patch_t *blendpatch, INT32 ski
 	Z_ChangeTag(newMipmap->data, PU_HWRMODELTEXTURE_UNLOCKED);
 }
 
-#define NORMALFOG 0x00000000
-#define FADEFOG 0x19000000
-
 static boolean HWR_AllowModel(mobj_t *mobj)
 {
 	// Signpost overlay. Not needed.
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 71cb5ca70..6c0c90fc5 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -697,7 +697,7 @@ static GLRGBAFloat shader_defaultcolor = {1.0f, 1.0f, 1.0f, 1.0f};
 #define GLSL_SOFTWARE_TINT_EQUATION \
 	"if (tint_color.a > 0.0) {\n" \
 		"float color_bright = sqrt((base_color.r * base_color.r) + (base_color.g * base_color.g) + (base_color.b * base_color.b));\n" \
-		"float strength = sqrt(9.0 * tint_color.a);\n" \
+		"float strength = sqrt(tint_color.a);\n" \
 		"final_color.r = clamp((color_bright * (tint_color.r * strength)) + (base_color.r * (1.0 - strength)), 0.0, 1.0);\n" \
 		"final_color.g = clamp((color_bright * (tint_color.g * strength)) + (base_color.g * (1.0 - strength)), 0.0, 1.0);\n" \
 		"final_color.b = clamp((color_bright * (tint_color.b * strength)) + (base_color.b * (1.0 - strength)), 0.0, 1.0);\n" \
diff --git a/src/lua_colorlib.c b/src/lua_colorlib.c
index a963a6a6b..1aed69836 100644
--- a/src/lua_colorlib.c
+++ b/src/lua_colorlib.c
@@ -155,10 +155,6 @@ static const char *const extracolormap_opt[] = {
 	"colormap",
 	NULL};
 
-#define ALPHA_SCALE_FACTOR 102 // (255 / 25) * 10
-#define SCALE_ALPHA_UP(alpha) (((alpha) * ALPHA_SCALE_FACTOR) / 10)
-#define SCALE_ALPHA_DOWN(alpha) (((alpha) * 10) / ALPHA_SCALE_FACTOR)
-
 static int extracolormap_get(lua_State *L)
 {
 	extracolormap_t *exc = *((extracolormap_t **)luaL_checkudata(L, 1, META_EXTRACOLORMAP));
@@ -176,13 +172,13 @@ static int extracolormap_get(lua_State *L)
 		lua_pushinteger(L, R_GetRgbaB(exc->rgba));
 		break;
 	case extracolormap_a:
-		lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->rgba)));
+		lua_pushinteger(L, R_GetRgbaA(exc->rgba));
 		break;
 	case extracolormap_rgba:
 		lua_pushinteger(L, R_GetRgbaR(exc->rgba));
 		lua_pushinteger(L, R_GetRgbaG(exc->rgba));
 		lua_pushinteger(L, R_GetRgbaB(exc->rgba));
-		lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->rgba)));
+		lua_pushinteger(L, R_GetRgbaA(exc->rgba));
 		return 4;
 	case extracolormap_fade_r:
 		lua_pushinteger(L, R_GetRgbaR(exc->fadergba));
@@ -194,13 +190,13 @@ static int extracolormap_get(lua_State *L)
 		lua_pushinteger(L, R_GetRgbaB(exc->fadergba));
 		break;
 	case extracolormap_fade_a:
-		lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->fadergba)));
+		lua_pushinteger(L, R_GetRgbaA(exc->fadergba));
 		break;
 	case extracolormap_fade_rgba:
 		lua_pushinteger(L, R_GetRgbaR(exc->fadergba));
 		lua_pushinteger(L, R_GetRgbaG(exc->fadergba));
 		lua_pushinteger(L, R_GetRgbaB(exc->fadergba));
-		lua_pushinteger(L, SCALE_ALPHA_UP(R_GetRgbaA(exc->fadergba)));
+		lua_pushinteger(L, R_GetRgbaA(exc->fadergba));
 		return 4;
 	case extracolormap_fade_start:
 		lua_pushinteger(L, exc->fadestart);
@@ -217,8 +213,6 @@ static int extracolormap_get(lua_State *L)
 
 static void GetExtraColormapRGBA(lua_State *L, UINT8 *rgba)
 {
-	rgba[3] = SCALE_ALPHA_UP(rgba[3]);
-
 	if (lua_type(L, 3) == LUA_TSTRING)
 	{
 		const char *str = lua_tostring(L, 3);
@@ -228,8 +222,6 @@ static void GetExtraColormapRGBA(lua_State *L, UINT8 *rgba)
 	}
 	else
 		GetRGBAColorsFromTable(L, 3, rgba, true);
-
-	rgba[3] = SCALE_ALPHA_DOWN(rgba[3]);
 }
 
 static int extracolormap_set(lua_State *L)
@@ -266,7 +258,7 @@ static int extracolormap_set(lua_State *L)
 		exc->rgba = R_PutRgbaRGBA(r, g, val, a);
 		break;
 	case extracolormap_a:
-		exc->rgba = R_PutRgbaRGBA(r, g, b, SCALE_ALPHA_DOWN(val));
+		exc->rgba = R_PutRgbaRGBA(r, g, b, val);
 		break;
 	case extracolormap_rgba:
 		rgba[0] = r;
@@ -286,7 +278,7 @@ static int extracolormap_set(lua_State *L)
 		exc->fadergba = R_PutRgbaRGBA(fr, fg, val, fa);
 		break;
 	case extracolormap_fade_a:
-		exc->fadergba = R_PutRgbaRGBA(fr, fg, fb, SCALE_ALPHA_DOWN(val));
+		exc->fadergba = R_PutRgbaRGBA(fr, fg, fb, val);
 		break;
 	case extracolormap_fade_rgba:
 		rgba[0] = fr;
diff --git a/src/r_data.c b/src/r_data.c
index 1795e7727..e2b74da40 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -438,7 +438,7 @@ extracolormap_t *R_CreateDefaultColormap(boolean lighttable)
 	exc->fadeend = 31;
 	exc->flags = 0;
 	exc->rgba = 0;
-	exc->fadergba = 0x19000000;
+	exc->fadergba = 0xFF000000;
 	exc->colormap = lighttable ? R_CreateLightTable(exc) : NULL;
 #ifdef EXTRACOLORMAPLUMPS
 	exc->lump = LUMPERROR;
@@ -553,7 +553,7 @@ boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba,
 				&& !flags)
 			)
 		&& (!checkrgba ? true : rgba == 0)
-		&& (!checkfadergba ? true : fadergba == 0x19000000)
+		&& (!checkfadergba ? true : (unsigned)fadergba == 0xFF000000)
 #ifdef EXTRACOLORMAPLUMPS
 		&& lump == LUMPERROR
 		&& extra_colormap->lumpname[0] == 0
@@ -654,7 +654,7 @@ extracolormap_t *R_ColormapForName(char *name)
 	if (lump == LUMPERROR)
 		I_Error("R_ColormapForName: Cannot find colormap lump %.8s\n", name);
 
-	exc = R_GetColormapFromListByValues(0, 0x19000000, 0, 31, 0, lump);
+	exc = R_GetColormapFromListByValues(0, 0xFF000000, 0, 31, 0, lump);
 	if (exc)
 		return exc;
 
@@ -674,7 +674,7 @@ extracolormap_t *R_ColormapForName(char *name)
 	exc->fadeend = 31;
 	exc->flags = 0;
 	exc->rgba = 0;
-	exc->fadergba = 0x19000000;
+	exc->fadergba = 0xFF000000;
 
 	R_AddColormapToList(exc);
 
@@ -737,7 +737,7 @@ void R_GenerateLightTable(extracolormap_t *extra_colormap, boolean uselookup)
 	cmaskg = cg;
 	cmaskb = cb;
 
-	maskamt = (double)(ca/24.0l);
+	maskamt = (double)(ca/255.0l);
 	othermask = 1 - maskamt;
 	maskamt /= 0xff;
 
@@ -753,7 +753,7 @@ void R_GenerateLightTable(extracolormap_t *extra_colormap, boolean uselookup)
 	cdestb = cfb;
 
 	// fade alpha unused in software
-	// maskamt = (double)(cfa/24.0l);
+	// maskamt = (double)(cfa/255.0l);
 	// othermask = 1 - maskamt;
 	// maskamt /= 0xff;
 
@@ -851,7 +851,7 @@ extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3)
 	UINT8 cr = 0, cg = 0, cb = 0, ca = 0, cfr = 0, cfg = 0, cfb = 0, cfa = 25;
 	UINT32 fadestart = 0, fadeend = 31;
 	UINT8 flags = 0;
-	INT32 rgba = 0, fadergba = 0x19000000;
+	INT32 rgba = 0, fadergba = 0xFF000000;
 
 #define HEX2INT(x) (UINT32)(x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0)
 #define ALPHA2INT(x) (x >= 'a' && x <= 'z' ? x - 'a' : x >= 'A' && x <= 'Z' ? x - 'A' : x >= '0' && x <= '9' ? 25 : 0)
@@ -859,13 +859,13 @@ extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3)
 	// Get base colormap value
 	// First alpha-only, then full value
 	if (p1[0] >= 'a' && p1[0] <= 'z' && !p1[1])
-		ca = (p1[0] - 'a');
+		ca = ((p1[0] - 'a') * 102) / 10;
 	else if (p1[0] == '#' && p1[1] >= 'a' && p1[1] <= 'z' && !p1[2])
-		ca = (p1[1] - 'a');
+		ca = ((p1[1] - 'a') * 102) / 10;
 	else if (p1[0] >= 'A' && p1[0] <= 'Z' && !p1[1])
-		ca = (p1[0] - 'A');
+		ca = ((p1[0] - 'A') * 102) / 10;
 	else if (p1[0] == '#' && p1[1] >= 'A' && p1[1] <= 'Z' && !p1[2])
-		ca = (p1[1] - 'A');
+		ca = ((p1[1] - 'A') * 102) / 10;
 	else if (p1[0] == '#')
 	{
 		// For each subsequent value, the value before it must exist
@@ -881,20 +881,20 @@ extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3)
 					cb = ((HEX2INT(p1[5]) * 16) + HEX2INT(p1[6]));
 
 					if (p1[7] >= 'a' && p1[7] <= 'z')
-						ca = (p1[7] - 'a');
+						ca = ((p1[7] - 'a') * 102) / 10;
 					else if (p1[7] >= 'A' && p1[7] <= 'Z')
-						ca = (p1[7] - 'A');
+						ca = ((p1[7] - 'A') * 102) / 10;
 					else
-						ca = 25;
+						ca = 255;
 				}
 				else
-					ca = 25;
+					ca = 255;
 			}
 			else
-				ca = 25;
+				ca = 255;
 		}
 		else
-			ca = 25;
+			ca = 255;
 	}
 
 #define NUMFROMCHAR(c) (c >= '0' && c <= '9' ? c - '0' : 0)
@@ -924,13 +924,13 @@ extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3)
 	// Get fade (dark) colormap value
 	// First alpha-only, then full value
 	if (p3[0] >= 'a' && p3[0] <= 'z' && !p3[1])
-		cfa = (p3[0] - 'a');
+		cfa = ((p3[0] - 'a') * 102) / 10;
 	else if (p3[0] == '#' && p3[1] >= 'a' && p3[1] <= 'z' && !p3[2])
-		cfa = (p3[1] - 'a');
+		cfa = ((p3[1] - 'a') * 102) / 10;
 	else if (p3[0] >= 'A' && p3[0] <= 'Z' && !p3[1])
-		cfa = (p3[0] - 'A');
+		cfa = ((p3[0] - 'A') * 102) / 10;
 	else if (p3[0] == '#' && p3[1] >= 'A' && p3[1] <= 'Z' && !p3[2])
-		cfa = (p3[1] - 'A');
+		cfa = ((p3[1] - 'A') * 102) / 10;
 	else if (p3[0] == '#')
 	{
 		// For each subsequent value, the value before it must exist
@@ -946,20 +946,20 @@ extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3)
 					cfb = ((HEX2INT(p3[5]) * 16) + HEX2INT(p3[6]));
 
 					if (p3[7] >= 'a' && p3[7] <= 'z')
-						cfa = (p3[7] - 'a');
+						cfa = ((p3[7] - 'a') * 102) / 10;
 					else if (p3[7] >= 'A' && p3[7] <= 'Z')
-						cfa = (p3[7] - 'A');
+						cfa = ((p3[7] - 'A') * 102) / 10;
 					else
-						cfa = 25;
+						cfa = 255;
 				}
 				else
-					cfa = 25;
+					cfa = 255;
 			}
 			else
-				cfa = 25;
+				cfa = 255;
 		}
 		else
-			cfa = 25;
+			cfa = 255;
 	}
 #undef ALPHA2INT
 #undef HEX2INT

From a48030d02dc6d26cfc8a6a145cdbfa4a625631ce Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Fri, 4 Aug 2023 05:08:22 -0300
Subject: [PATCH 06/36] Support number as accepted value for "rgba" or
 "fade_rgba" fields

---
 src/lua_colorlib.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/src/lua_colorlib.c b/src/lua_colorlib.c
index 1aed69836..312b32571 100644
--- a/src/lua_colorlib.c
+++ b/src/lua_colorlib.c
@@ -220,8 +220,26 @@ static void GetExtraColormapRGBA(lua_State *L, UINT8 *rgba)
 		if (!parsed)
 			luaL_error(L, "Malformed HTML color '%s'", str);
 	}
-	else
+	else if (lua_type(L, 3) == LUA_TTABLE)
 		GetRGBAColorsFromTable(L, 3, rgba, true);
+	else
+	{
+		UINT32 colors = lua_tointeger(L, 3);
+		if (colors > 0xFFFFFF)
+		{
+			rgba[0] = (colors >> 24) & 0xFF;
+			rgba[1] = (colors >> 16) & 0xFF;
+			rgba[2] = (colors >> 8) & 0xFF;
+			rgba[3] = colors & 0xFF;
+		}
+		else
+		{
+			rgba[0] = (colors >> 16) & 0xFF;
+			rgba[1] = (colors >> 8) & 0xFF;
+			rgba[2] = colors & 0xFF;
+			rgba[3] = 0xFF;
+		}
+	}
 }
 
 static int extracolormap_set(lua_State *L)

From 0e2b6a94234ac0fe01263b4ede5f77bff6119faa Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Fri, 4 Aug 2023 05:13:56 -0300
Subject: [PATCH 07/36] Rename fields

---
 src/lua_colorlib.c | 83 ++++++++++++++++++++++++----------------------
 1 file changed, 43 insertions(+), 40 deletions(-)

diff --git a/src/lua_colorlib.c b/src/lua_colorlib.c
index 312b32571..f4a641744 100644
--- a/src/lua_colorlib.c
+++ b/src/lua_colorlib.c
@@ -124,32 +124,32 @@ static UINT8 ParseHTMLColor(const char *str, UINT8 *rgba, size_t numc)
 /////////////////////////
 
 enum extracolormap_e {
-	extracolormap_r = 0,
-	extracolormap_g,
-	extracolormap_b,
-	extracolormap_a,
-	extracolormap_rgba,
-	extracolormap_fade_r,
-	extracolormap_fade_g,
-	extracolormap_fade_b,
-	extracolormap_fade_a,
-	extracolormap_fade_rgba,
+	extracolormap_red = 0,
+	extracolormap_green,
+	extracolormap_blue,
+	extracolormap_alpha,
+	extracolormap_color,
+	extracolormap_fade_red,
+	extracolormap_fade_green,
+	extracolormap_fade_blue,
+	extracolormap_fade_alpha,
+	extracolormap_fade_color,
 	extracolormap_fade_start,
 	extracolormap_fade_end,
 	extracolormap_colormap
 };
 
 static const char *const extracolormap_opt[] = {
-	"r",
-	"g",
-	"b",
-	"a",
-	"rgba",
-	"fade_r",
-	"fade_g",
-	"fade_b",
-	"fade_a",
-	"fade_rgba",
+	"red",
+	"green",
+	"blue",
+	"alpha",
+	"color",
+	"fade_red",
+	"fade_green",
+	"fade_blue",
+	"fade_alpha",
+	"fade_color",
 	"fade_start",
 	"fade_end",
 	"colormap",
@@ -162,37 +162,37 @@ static int extracolormap_get(lua_State *L)
 
 	switch (field)
 	{
-	case extracolormap_r:
+	case extracolormap_red:
 		lua_pushinteger(L, R_GetRgbaR(exc->rgba));
 		break;
-	case extracolormap_g:
+	case extracolormap_green:
 		lua_pushinteger(L, R_GetRgbaG(exc->rgba));
 		break;
-	case extracolormap_b:
+	case extracolormap_blue:
 		lua_pushinteger(L, R_GetRgbaB(exc->rgba));
 		break;
-	case extracolormap_a:
+	case extracolormap_alpha:
 		lua_pushinteger(L, R_GetRgbaA(exc->rgba));
 		break;
-	case extracolormap_rgba:
+	case extracolormap_color:
 		lua_pushinteger(L, R_GetRgbaR(exc->rgba));
 		lua_pushinteger(L, R_GetRgbaG(exc->rgba));
 		lua_pushinteger(L, R_GetRgbaB(exc->rgba));
 		lua_pushinteger(L, R_GetRgbaA(exc->rgba));
 		return 4;
-	case extracolormap_fade_r:
+	case extracolormap_fade_red:
 		lua_pushinteger(L, R_GetRgbaR(exc->fadergba));
 		break;
-	case extracolormap_fade_g:
+	case extracolormap_fade_green:
 		lua_pushinteger(L, R_GetRgbaG(exc->fadergba));
 		break;
-	case extracolormap_fade_b:
+	case extracolormap_fade_blue:
 		lua_pushinteger(L, R_GetRgbaB(exc->fadergba));
 		break;
-	case extracolormap_fade_a:
+	case extracolormap_fade_alpha:
 		lua_pushinteger(L, R_GetRgbaA(exc->fadergba));
 		break;
-	case extracolormap_fade_rgba:
+	case extracolormap_fade_color:
 		lua_pushinteger(L, R_GetRgbaR(exc->fadergba));
 		lua_pushinteger(L, R_GetRgbaG(exc->fadergba));
 		lua_pushinteger(L, R_GetRgbaB(exc->fadergba));
@@ -205,6 +205,9 @@ static int extracolormap_get(lua_State *L)
 		lua_pushinteger(L, exc->fadeend);
 		break;
 	case extracolormap_colormap:
+		// I'm not sure if making the colormap available makes sense.
+		// It's a read-only field, only used by one of the renderers, and
+		// the only way to manipulate it is by modifying the other fields.
 		LUA_PushUserdata(L, exc->colormap, META_LIGHTTABLE);
 		break;
 	}
@@ -266,19 +269,19 @@ static int extracolormap_set(lua_State *L)
 
 	switch(field)
 	{
-	case extracolormap_r:
+	case extracolormap_red:
 		exc->rgba = R_PutRgbaRGBA(val, g, b, a);
 		break;
-	case extracolormap_g:
+	case extracolormap_green:
 		exc->rgba = R_PutRgbaRGBA(r, val, b, a);
 		break;
-	case extracolormap_b:
+	case extracolormap_blue:
 		exc->rgba = R_PutRgbaRGBA(r, g, val, a);
 		break;
-	case extracolormap_a:
+	case extracolormap_alpha:
 		exc->rgba = R_PutRgbaRGBA(r, g, b, val);
 		break;
-	case extracolormap_rgba:
+	case extracolormap_color:
 		rgba[0] = r;
 		rgba[1] = g;
 		rgba[2] = b;
@@ -286,19 +289,19 @@ static int extracolormap_set(lua_State *L)
 		GetExtraColormapRGBA(L, rgba);
 		exc->rgba = R_PutRgbaRGBA(rgba[0], rgba[1], rgba[2], rgba[3]);
 		break;
-	case extracolormap_fade_r:
+	case extracolormap_fade_red:
 		exc->fadergba = R_PutRgbaRGBA(val, fg, fb, fa);
 		break;
-	case extracolormap_fade_g:
+	case extracolormap_fade_green:
 		exc->fadergba = R_PutRgbaRGBA(fr, val, fb, fa);
 		break;
-	case extracolormap_fade_b:
+	case extracolormap_fade_blue:
 		exc->fadergba = R_PutRgbaRGBA(fr, fg, val, fa);
 		break;
-	case extracolormap_fade_a:
+	case extracolormap_fade_alpha:
 		exc->fadergba = R_PutRgbaRGBA(fr, fg, fb, val);
 		break;
-	case extracolormap_fade_rgba:
+	case extracolormap_fade_color:
 		rgba[0] = fr;
 		rgba[1] = fg;
 		rgba[2] = fb;

From a54d3a274a49297b22a8a3da95bd78a1914aa86c Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Fri, 4 Aug 2023 14:29:26 -0300
Subject: [PATCH 08/36] Remove assignment by table

---
 src/lua_colorlib.c | 59 +++++-----------------------------------------
 1 file changed, 6 insertions(+), 53 deletions(-)

diff --git a/src/lua_colorlib.c b/src/lua_colorlib.c
index f4a641744..1e6e82333 100644
--- a/src/lua_colorlib.c
+++ b/src/lua_colorlib.c
@@ -17,48 +17,6 @@
 #include "lua_script.h"
 #include "lua_libs.h"
 
-static UINT8 GetRGBAColorsFromTable(lua_State *L, UINT32 index, UINT8 *rgba, boolean useAlpha)
-{
-	UINT8 num = 0;
-
-	lua_pushnil(L);
-
-	while (lua_next(L, index)) {
-		lua_Integer i = 0;
-		const char *field = NULL;
-		if (lua_isnumber(L, -2))
-			i = lua_tointeger(L, -2);
-		else
-			field = luaL_checkstring(L, -2);
-
-#define CHECKFIELD(p, c) (i == p || (field && fastcmp(field, c)))
-#define RGBASET(p, c) { \
-			INT32 color = luaL_checkinteger(L, -1); \
-			if (color < 0 || color > 255) \
-				luaL_error(L, c " channel %d out of range (0 - 255)", color); \
-			rgba[p] = (UINT8)color; \
-			num++; \
-		}
-
-		if (CHECKFIELD(1, "r")) {
-			RGBASET(0, "red color");
-		} else if (CHECKFIELD(2, "g")) {
-			RGBASET(1, "green color");
-		} else if (CHECKFIELD(3, "b")) {
-			RGBASET(2, "blue color");
-		} else if (useAlpha && CHECKFIELD(4, "a")) {
-			RGBASET(3, "alpha");
-		}
-
-#undef CHECKFIELD
-#undef RGBASET
-
-		lua_pop(L, 1);
-	}
-
-	return num;
-}
-
 #define IS_HEX_CHAR(x) ((x >= '0' && x <= '9') || (x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F'))
 #define ARE_HEX_CHARS(str, i) IS_HEX_CHAR(str[i]) && IS_HEX_CHAR(str[i + 1])
 
@@ -205,29 +163,24 @@ static int extracolormap_get(lua_State *L)
 		lua_pushinteger(L, exc->fadeend);
 		break;
 	case extracolormap_colormap:
-		// I'm not sure if making the colormap available makes sense.
-		// It's a read-only field, only used by one of the renderers, and
-		// the only way to manipulate it is by modifying the other fields.
 		LUA_PushUserdata(L, exc->colormap, META_LIGHTTABLE);
 		break;
 	}
 	return 1;
 }
 
-static void GetExtraColormapRGBA(lua_State *L, UINT8 *rgba)
+static void GetExtraColormapRGBA(lua_State *L, UINT8 *rgba, int arg)
 {
-	if (lua_type(L, 3) == LUA_TSTRING)
+	if (lua_type(L, arg) == LUA_TSTRING)
 	{
-		const char *str = lua_tostring(L, 3);
+		const char *str = lua_tostring(L, arg);
 		UINT8 parsed = ParseHTMLColor(str, rgba, 4);
 		if (!parsed)
 			luaL_error(L, "Malformed HTML color '%s'", str);
 	}
-	else if (lua_type(L, 3) == LUA_TTABLE)
-		GetRGBAColorsFromTable(L, 3, rgba, true);
 	else
 	{
-		UINT32 colors = lua_tointeger(L, 3);
+		UINT32 colors = lua_tointeger(L, arg);
 		if (colors > 0xFFFFFF)
 		{
 			rgba[0] = (colors >> 24) & 0xFF;
@@ -286,7 +239,7 @@ static int extracolormap_set(lua_State *L)
 		rgba[1] = g;
 		rgba[2] = b;
 		rgba[3] = a;
-		GetExtraColormapRGBA(L, rgba);
+		GetExtraColormapRGBA(L, rgba, 3);
 		exc->rgba = R_PutRgbaRGBA(rgba[0], rgba[1], rgba[2], rgba[3]);
 		break;
 	case extracolormap_fade_red:
@@ -306,7 +259,7 @@ static int extracolormap_set(lua_State *L)
 		rgba[1] = fg;
 		rgba[2] = fb;
 		rgba[3] = fa;
-		GetExtraColormapRGBA(L, rgba);
+		GetExtraColormapRGBA(L, rgba, 3);
 		exc->fadergba = R_PutRgbaRGBA(rgba[0], rgba[1], rgba[2], rgba[3]);
 		break;
 	case extracolormap_fade_start:

From 4f6da8dc0f6b96c214a9f7b190de4a181ee59a01 Mon Sep 17 00:00:00 2001
From: Zwip-Zwap Zapony <ZwipZwapZapony@gmail.com>
Date: Sun, 10 Sep 2023 16:10:32 +0200
Subject: [PATCH 09/36] Fix a "maybe-uninitialized" compiler warning

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

diff --git a/src/r_things.c b/src/r_things.c
index be9c5cdff..8fabb8027 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -3201,8 +3201,8 @@ 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;
-	INT32 height;
+	fixed_t texturemid = 0, yscale = 0, scalestep = spr->scalestep; // "= 0" pleases the compiler
+	INT32 height = 0;
 
 	if (scalestep)
 	{

From 05926ceec8a87671696cc5cc617bb9d5bcec5d29 Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Wed, 20 Sep 2023 15:42:19 +0200
Subject: [PATCH 10/36] Group NiGHTS star/chip hitboxes with rings

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

diff --git a/src/r_bbox.c b/src/r_bbox.c
index cf417ec37..5b2a817f1 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -267,18 +267,17 @@ static boolean is_tangible (mobj_t *thing)
 boolean R_ThingBoundingBoxVisible(mobj_t *thing)
 {
 	INT32 cvmode = cv_renderhitbox.value;
+	boolean ring = false;
 
 	if (multiplayer) // No hitboxes in multiplayer to avoid cheating
 		return false;
 
-	// 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!
+			// 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;
@@ -290,6 +289,16 @@ boolean R_ThingBoundingBoxVisible(mobj_t *thing)
 			// are rendered using portals in Software,
 			// r_viewmobj does not point here.
 			return false;
+
+		case MT_RING:
+		case MT_BLUESPHERE:
+		case MT_NIGHTSSTAR:
+		case MT_NIGHTSCHIP:
+			// Rings and similar objects are often placed
+			// in large amounts, so they are handled
+			// separately from other tangible objects.
+			ring = true;
+			break;
 	}
 
 	switch (cvmode)
@@ -304,16 +313,10 @@ boolean R_ThingBoundingBoxVisible(mobj_t *thing)
 			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);
+			return !ring && is_tangible(thing);
 
 		case RENDERHITBOX_RINGS:
-			return (thing->type == MT_RING || thing->type == MT_BLUESPHERE);
+			return ring;
 
 		default:
 			return false;

From 8fd9a82a19d49cae116ccbd0eb338c231eaa4cd3 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Thu, 21 Sep 2023 02:06:06 -0300
Subject: [PATCH 11/36] Raise sidedef limits Also, fixes reading of GL3 nodes
 to use 32-bit indices for line IDs.

---
 src/lua_maplib.c |   2 +-
 src/p_map.c      |   2 +-
 src/p_maputl.c   |   4 +-
 src/p_saveg.c    |  29 ++++++------
 src/p_setup.c    | 121 ++++++++++++++++++++++++++---------------------
 src/p_spec.c     |  14 +++---
 src/r_defs.h     |   4 +-
 7 files changed, 97 insertions(+), 79 deletions(-)

diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index e34397993..7302c727a 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -1072,7 +1072,7 @@ static int line_get(lua_State *L)
 		LUA_PushUserdata(L, &sides[line->sidenum[0]], META_SIDE);
 		return 1;
 	case line_backside: // backside
-		if (line->sidenum[1] == 0xffff)
+		if (line->sidenum[1] == NO_SIDEDEF)
 			return 0;
 		LUA_PushUserdata(L, &sides[line->sidenum[1]], META_SIDE);
 		return 1;
diff --git a/src/p_map.c b/src/p_map.c
index 80135db74..e328dc9e6 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -3900,7 +3900,7 @@ retry:
 	P_PathTraverse(leadx, traily, leadx + mo->momx, traily + mo->momy,
 		PT_ADDLINES, PTR_SlideTraverse);
 
-	if (bestslideline && mo->player && bestslideline->sidenum[1] != 0xffff)
+	if (bestslideline && mo->player && bestslideline->sidenum[1] != NO_SIDEDEF)
 	{
 		sector_t *sec = P_PointOnLineSide(mo->x, mo->y, bestslideline) ? bestslideline->frontsector : bestslideline->backsector;
 		P_CheckLavaWall(mo, sec);
diff --git a/src/p_maputl.c b/src/p_maputl.c
index e36d5fd72..9c30d3ead 100644
--- a/src/p_maputl.c
+++ b/src/p_maputl.c
@@ -290,7 +290,7 @@ void P_CameraLineOpening(line_t *linedef)
 	sector_t *back;
 	fixed_t frontfloor, frontceiling, backfloor, backceiling;
 
-	if (linedef->sidenum[1] == 0xffff)
+	if (linedef->sidenum[1] == NO_SIDEDEF)
 	{
 		// single sided line
 		openrange = 0;
@@ -426,7 +426,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
 {
 	sector_t *front, *back;
 
-	if (linedef->sidenum[1] == 0xffff)
+	if (linedef->sidenum[1] == NO_SIDEDEF)
 	{
 		// single sided line
 		openrange = 0;
diff --git a/src/p_saveg.c b/src/p_saveg.c
index faecd1377..3bcfea7cd 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1079,7 +1079,7 @@ static void ArchiveSectors(void)
 
 		if (diff)
 		{
-			WRITEUINT16(save_p, i);
+			WRITEUINT32(save_p, i);
 			WRITEUINT8(save_p, diff);
 			if (diff & SD_DIFF2)
 				WRITEUINT8(save_p, diff2);
@@ -1150,18 +1150,19 @@ static void ArchiveSectors(void)
 		}
 	}
 
-	WRITEUINT16(save_p, 0xffff);
+	WRITEUINT32(save_p, 0xffffffff);
 }
 
 static void UnArchiveSectors(void)
 {
-	UINT16 i, j;
+	UINT32 i;
+	UINT16 j;
 	UINT8 diff, diff2, diff3, diff4;
 	for (;;)
 	{
-		i = READUINT16(save_p);
+		i = READUINT32(save_p);
 
-		if (i == 0xffff)
+		if (i == 0xffffffff)
 			break;
 
 		if (i > numsectors)
@@ -1299,7 +1300,7 @@ static void ArchiveLines(void)
 		if (li->executordelay != spawnli->executordelay)
 			diff2 |= LD_EXECUTORDELAY;
 
-		if (li->sidenum[0] != 0xffff)
+		if (li->sidenum[0] != NO_SIDEDEF)
 		{
 			si = &sides[li->sidenum[0]];
 			spawnsi = &spawnsides[li->sidenum[0]];
@@ -1313,7 +1314,7 @@ static void ArchiveLines(void)
 			if (si->midtexture != spawnsi->midtexture)
 				diff |= LD_S1MIDTEX;
 		}
-		if (li->sidenum[1] != 0xffff)
+		if (li->sidenum[1] != NO_SIDEDEF)
 		{
 			si = &sides[li->sidenum[1]];
 			spawnsi = &spawnsides[li->sidenum[1]];
@@ -1332,7 +1333,7 @@ static void ArchiveLines(void)
 
 		if (diff)
 		{
-			WRITEINT16(save_p, i);
+			WRITEUINT32(save_p, i);
 			WRITEUINT8(save_p, diff);
 			if (diff & LD_DIFF2)
 				WRITEUINT8(save_p, diff2);
@@ -1391,21 +1392,21 @@ static void ArchiveLines(void)
 				WRITEINT32(save_p, li->executordelay);
 		}
 	}
-	WRITEUINT16(save_p, 0xffff);
+	WRITEUINT32(save_p, 0xffffffff);
 }
 
 static void UnArchiveLines(void)
 {
-	UINT16 i;
+	UINT32 i;
 	line_t *li;
 	side_t *si;
 	UINT8 diff, diff2; // no diff3
 
 	for (;;)
 	{
-		i = READUINT16(save_p);
+		i = READUINT32(save_p);
 
-		if (i == 0xffff)
+		if (i == 0xffffffff)
 			break;
 		if (i > numlines)
 			I_Error("Invalid line number %u from server", i);
@@ -1892,7 +1893,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 	if (diff & MD_RTIME)
 		WRITEINT32(save_p, mobj->reactiontime);
 	if (diff & MD_STATE)
-		WRITEUINT16(save_p, mobj->state-states);
+		WRITEUINT32(save_p, mobj->state-states);
 	if (diff & MD_TICS)
 		WRITEINT32(save_p, mobj->tics);
 	if (diff & MD_SPRITE) {
@@ -2916,7 +2917,7 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		mobj->reactiontime = mobj->info->reactiontime;
 
 	if (diff & MD_STATE)
-		mobj->state = &states[READUINT16(save_p)];
+		mobj->state = &states[READUINT32(save_p)];
 	else
 		mobj->state = &states[mobj->info->spawnstate];
 	if (diff & MD_TICS)
diff --git a/src/p_setup.c b/src/p_setup.c
index 0390761b6..733a47792 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1111,40 +1111,40 @@ static void P_InitializeLinedef(line_t *ld)
 	// cph 2006/09/30 - fix sidedef errors right away.
 	// cph 2002/07/20 - these errors are fatal if not fixed, so apply them
 	for (j = 0; j < 2; j++)
-		if (ld->sidenum[j] != 0xffff && ld->sidenum[j] >= (UINT16)numsides)
+		if (ld->sidenum[j] != NO_SIDEDEF && ld->sidenum[j] >= (UINT32)numsides)
 		{
-			ld->sidenum[j] = 0xffff;
+			ld->sidenum[j] = NO_SIDEDEF;
 			CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s has out-of-range sidedef number\n", sizeu1((size_t)(ld - lines)));
 		}
 
 	// killough 11/98: fix common wad errors (missing sidedefs):
-	if (ld->sidenum[0] == 0xffff)
+	if (ld->sidenum[0] == NO_SIDEDEF)
 	{
 		ld->sidenum[0] = 0;  // Substitute dummy sidedef for missing right side
 		// cph - print a warning about the bug
 		CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s missing first sidedef\n", sizeu1((size_t)(ld - lines)));
 	}
 
-	if ((ld->sidenum[1] == 0xffff) && (ld->flags & ML_TWOSIDED))
+	if ((ld->sidenum[1] == NO_SIDEDEF) && (ld->flags & ML_TWOSIDED))
 	{
 		ld->flags &= ~ML_TWOSIDED;  // Clear 2s flag for missing left side
 		// cph - print a warning about the bug
 		CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s has two-sided flag set, but no second sidedef\n", sizeu1((size_t)(ld - lines)));
 	}
 
-	if (ld->sidenum[0] != 0xffff)
+	if (ld->sidenum[0] != NO_SIDEDEF)
 	{
 		sides[ld->sidenum[0]].special = ld->special;
 		sides[ld->sidenum[0]].line = ld;
 	}
-	if (ld->sidenum[1] != 0xffff)
+	if (ld->sidenum[1] != NO_SIDEDEF)
 	{
 		sides[ld->sidenum[1]].special = ld->special;
 		sides[ld->sidenum[1]].line = ld;
 	}
 }
 
-static void P_SetLinedefV1(size_t i, UINT16 vertex_num)
+static void P_SetLinedefV1(size_t i, UINT32 vertex_num)
 {
 	if (vertex_num >= numvertexes)
 	{
@@ -1154,7 +1154,7 @@ static void P_SetLinedefV1(size_t i, UINT16 vertex_num)
 	lines[i].v1 = &vertexes[vertex_num];
 }
 
-static void P_SetLinedefV2(size_t i, UINT16 vertex_num)
+static void P_SetLinedefV2(size_t i, UINT32 vertex_num)
 {
 	if (vertex_num >= numvertexes)
 	{
@@ -1179,17 +1179,22 @@ static void P_LoadLinedefs(UINT8 *data)
 		memset(ld->stringargs, 0x00, NUMLINESTRINGARGS*sizeof(*ld->stringargs));
 		ld->alpha = FRACUNIT;
 		ld->executordelay = 0;
-		P_SetLinedefV1(i, SHORT(mld->v1));
-		P_SetLinedefV2(i, SHORT(mld->v2));
+		P_SetLinedefV1(i, (UINT16)SHORT(mld->v1));
+		P_SetLinedefV2(i, (UINT16)SHORT(mld->v2));
 
-		ld->sidenum[0] = SHORT(mld->sidenum[0]);
-		ld->sidenum[1] = SHORT(mld->sidenum[1]);
+		ld->sidenum[0] = (UINT16)SHORT(mld->sidenum[0]);
+		ld->sidenum[1] = (UINT16)SHORT(mld->sidenum[1]);
+
+		if (ld->sidenum[0] == 0xffff)
+			ld->sidenum[0] = NO_SIDEDEF;
+		if (ld->sidenum[1] == 0xffff)
+			ld->sidenum[1] = NO_SIDEDEF;
 
 		P_InitializeLinedef(ld);
 	}
 }
 
-static void P_SetSidedefSector(size_t i, UINT16 sector_num)
+static void P_SetSidedefSector(size_t i, UINT32 sector_num)
 {
 	// cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead
 	if (sector_num >= numsectors)
@@ -1350,7 +1355,7 @@ static void P_LoadSidedefs(UINT8 *data)
 		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));
+		P_SetSidedefSector(i, (UINT16)SHORT(msd->sector));
 
 		// Special info stored in texture fields!
 		switch (sd->special)
@@ -2489,7 +2494,7 @@ static void P_WriteTextmap(void)
 		fprintf(f, "v1 = %s;\n", sizeu1(wlines[i].v1 - vertexes));
 		fprintf(f, "v2 = %s;\n", sizeu1(wlines[i].v2 - vertexes));
 		fprintf(f, "sidefront = %d;\n", wlines[i].sidenum[0]);
-		if (wlines[i].sidenum[1] != 0xffff)
+		if (wlines[i].sidenum[1] != NO_SIDEDEF)
 			fprintf(f, "sideback = %d;\n", wlines[i].sidenum[1]);
 		firsttag = Tag_FGet(&wlines[i].tags);
 		if (firsttag != 0)
@@ -2954,8 +2959,8 @@ static void P_LoadTextmap(void)
 		memset(ld->stringargs, 0x00, NUMLINESTRINGARGS*sizeof(*ld->stringargs));
 		ld->alpha = FRACUNIT;
 		ld->executordelay = 0;
-		ld->sidenum[0] = 0xffff;
-		ld->sidenum[1] = 0xffff;
+		ld->sidenum[0] = NO_SIDEDEF;
+		ld->sidenum[1] = NO_SIDEDEF;
 
 		TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter);
 
@@ -2963,7 +2968,7 @@ static void P_LoadTextmap(void)
 			I_Error("P_LoadTextmap: linedef %s has no v1 value set!\n", sizeu1(i));
 		if (!ld->v2)
 			I_Error("P_LoadTextmap: linedef %s has no v2 value set!\n", sizeu1(i));
-		if (ld->sidenum[0] == 0xffff)
+		if (ld->sidenum[0] == NO_SIDEDEF)
 			I_Error("P_LoadTextmap: linedef %s has no sidefront value set!\n", sizeu1(i));
 
 		P_InitializeLinedef(ld);
@@ -3017,7 +3022,7 @@ static void P_ProcessLinedefsAfterSidedefs(void)
 	for (; i--; ld++)
 	{
 		ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here
-		ld->backsector = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0;
+		ld->backsector = ld->sidenum[1] != NO_SIDEDEF ? sides[ld->sidenum[1]].sector : 0;
 
 		if (udmf)
 			continue;
@@ -3256,9 +3261,9 @@ static void P_InitializeSeg(seg_t *seg)
 {
 	if (seg->linedef)
 	{
-		UINT16 side = seg->linedef->sidenum[seg->side];
+		UINT32 side = seg->linedef->sidenum[seg->side];
 
-		if (side == 0xffff)
+		if (side == NO_SIDEDEF)
 			I_Error("P_InitializeSeg: Seg %s refers to side %d of linedef %s, which doesn't exist!\n", sizeu1((size_t)(seg - segs)), seg->side, sizeu1((size_t)(seg->linedef - lines)));
 
 		seg->sidedef = &sides[side];
@@ -3443,7 +3448,7 @@ static boolean P_LoadExtraVertices(UINT8 **data)
 static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype)
 {
 	size_t i, k;
-	INT16 m;
+	size_t m;
 	seg_t *seg;
 
 	// Subsectors
@@ -3466,23 +3471,33 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 		{
 		case NT_XGLN:
 		case NT_XGL3:
-			for (m = 0; m < subsectors[i].numlines; m++, k++)
+			for (m = 0; m < (size_t)subsectors[i].numlines; m++, k++)
 			{
 				UINT32 vertexnum = READUINT32((*data));
-				UINT16 linenum;
-
 				if (vertexnum >= numvertexes)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid vertex %d!\n", sizeu1(k), m, vertexnum);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid vertex %d!\n", sizeu1(k), sizeu1(m), vertexnum);
 
 				segs[k - 1 + ((m == 0) ? subsectors[i].numlines : 0)].v2 = segs[k].v1 = &vertexes[vertexnum];
 
 				READUINT32((*data)); // partner, can be ignored by software renderer
 
-				linenum = (nodetype == NT_XGL3) ? READUINT32((*data)) : READUINT16((*data));
-				if (linenum != 0xFFFF && linenum >= numlines)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid linedef %d!\n", sizeu1(k), sizeu2(i), linenum);
-				segs[k].glseg = (linenum == 0xFFFF);
-				segs[k].linedef = (linenum == 0xFFFF) ? NULL : &lines[linenum];
+				if (nodetype == NT_XGL3)
+				{
+					UINT32 linenum = READUINT32((*data));
+					if (linenum != 0xFFFFFFFF && linenum >= numlines)
+						I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid linedef %d!\n", sizeu1(k), sizeu2(i), linenum);
+					segs[k].glseg = linenum == 0xFFFFFFFF;
+					segs[k].linedef = linenum == 0xFFFFFFFF ? NULL : &lines[linenum];
+				}
+				else
+				{
+					UINT16 linenum = READUINT16((*data));
+					if (linenum != 0xFFFF && linenum >= numlines)
+						I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid linedef %d!\n", sizeu1(k), sizeu2(i), linenum);
+					segs[k].glseg = linenum == 0xFFFF;
+					segs[k].linedef = linenum == 0xFFFF ? NULL : &lines[linenum];
+				}
+
 				segs[k].side = READUINT8((*data));
 			}
 			while (segs[subsectors[i].firstline].glseg)
@@ -3494,18 +3509,18 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 			break;
 
 		case NT_XNOD:
-			for (m = 0; m < subsectors[i].numlines; m++, k++)
+			for (m = 0; m < (size_t)subsectors[i].numlines; m++, k++)
 			{
 				UINT32 v1num = READUINT32((*data));
 				UINT32 v2num = READUINT32((*data));
 				UINT16 linenum = READUINT16((*data));
 
 				if (v1num >= numvertexes)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v1 %d!\n", sizeu1(k), m, v1num);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid v1 %d!\n", sizeu1(k), sizeu1(m), v1num);
 				if (v2num >= numvertexes)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v2 %d!\n", sizeu1(k), m, v2num);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid v2 %d!\n", sizeu1(k), sizeu1(m), v2num);
 				if (linenum >= numlines)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d!\n", sizeu1(k), m, linenum);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid linedef %d!\n", sizeu1(k), sizeu1(m), linenum);
 
 				segs[k].v1 = &vertexes[v1num];
 				segs[k].v2 = &vertexes[v2num];
@@ -3990,7 +4005,7 @@ static void P_LinkMapData(void)
 		if (!seg->sidedef)
 			CorruptMapError(va("P_LinkMapData: seg->sidedef is NULL "
 				"(subsector %s, firstline is %d)", sizeu1(i), ss->firstline));
-		if (seg->sidedef - sides < 0 || seg->sidedef - sides > (UINT16)numsides)
+		if (seg->sidedef - sides < 0 || seg->sidedef - sides > (UINT32)numsides)
 			CorruptMapError(va("P_LinkMapData: seg->sidedef refers to sidedef %s of %s "
 				"(subsector %s, firstline is %d)", sizeu1(sidei), sizeu2(numsides),
 				sizeu3(i), ss->firstline));
@@ -4915,7 +4930,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 
 			break;
 		case 259: //Custom FOF
-			if (lines[i].sidenum[1] == 0xffff)
+			if (lines[i].sidenum[1] == NO_SIDEDEF)
 				I_Error("Custom FOF (tag %d) found without a linedef back side!", tag);
 
 			lines[i].args[0] = tag;
@@ -5269,8 +5284,8 @@ static void P_ConvertBinaryLinedefTypes(void)
 			lines[i].args[1] = sides[lines[i].sidenum[0]].midtexture;
 			lines[i].args[2] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
 			lines[i].args[3] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
-			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[4] = (lines[i].sidenum[1] != NO_SIDEDEF) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : 0;
+			lines[i].args[5] = (lines[i].sidenum[1] != NO_SIDEDEF) ? sides[lines[i].sidenum[1]].rowoffset >> FRACBITS : -1;
 			lines[i].args[6] = sides[lines[i].sidenum[0]].bottomtexture;
 			break;
 		case 414: //Play sound effect
@@ -5374,7 +5389,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 				lines[i].args[1] = max(sides[lines[i].sidenum[0]].textureoffset >> FRACBITS, 0);
 				// failsafe: if user specifies Back Y Offset and NOT Front Y Offset, use the Back Offset
 				// to be consistent with other light and fade specials
-				lines[i].args[2] = ((lines[i].sidenum[1] != 0xFFFF && !(sides[lines[i].sidenum[0]].rowoffset >> FRACBITS)) ?
+				lines[i].args[2] = ((lines[i].sidenum[1] != NO_SIDEDEF && !(sides[lines[i].sidenum[0]].rowoffset >> FRACBITS)) ?
 					max(min(sides[lines[i].sidenum[1]].rowoffset >> FRACBITS, 255), 0)
 					: max(min(sides[lines[i].sidenum[0]].rowoffset >> FRACBITS, 255), 0));
 			}
@@ -5479,7 +5494,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 			break;
 		case 442: //Change object type state
 			lines[i].args[0] = tag;
-			lines[i].args[1] = (lines[i].sidenum[1] == 0xffff) ? 1 : 0;
+			lines[i].args[1] = (lines[i].sidenum[1] == NO_SIDEDEF) ? 1 : 0;
 			break;
 		case 443: //Call Lua function
 			if (lines[i].stringargs[0] == NULL)
@@ -5547,7 +5562,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 		case 452: //Set FOF translucency
 			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].sidenum[1] != 0xffff ? (sides[lines[i].sidenum[1]].textureoffset >> FRACBITS) : (P_AproxDistance(lines[i].dx, lines[i].dy) >> FRACBITS);
+			lines[i].args[2] = lines[i].sidenum[1] != NO_SIDEDEF ? (sides[lines[i].sidenum[1]].textureoffset >> FRACBITS) : (P_AproxDistance(lines[i].dx, lines[i].dy) >> FRACBITS);
 			if (lines[i].flags & ML_MIDPEG)
 				lines[i].args[3] |= TMST_RELATIVE;
 			if (lines[i].flags & ML_NOCLIMB)
@@ -5556,8 +5571,8 @@ static void P_ConvertBinaryLinedefTypes(void)
 		case 453: //Fade FOF
 			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].sidenum[1] != 0xffff ? (sides[lines[i].sidenum[1]].textureoffset >> FRACBITS) : (lines[i].dx >> FRACBITS);
-			lines[i].args[3] = lines[i].sidenum[1] != 0xffff ? (sides[lines[i].sidenum[1]].rowoffset >> FRACBITS) : (abs(lines[i].dy) >> FRACBITS);
+			lines[i].args[2] = lines[i].sidenum[1] != NO_SIDEDEF ? (sides[lines[i].sidenum[1]].textureoffset >> FRACBITS) : (lines[i].dx >> FRACBITS);
+			lines[i].args[3] = lines[i].sidenum[1] != NO_SIDEDEF ? (sides[lines[i].sidenum[1]].rowoffset >> FRACBITS) : (abs(lines[i].dy) >> FRACBITS);
 			if (lines[i].flags & ML_MIDPEG)
 				lines[i].args[4] |= TMFT_RELATIVE;
 			if (lines[i].flags & ML_WRAPMIDTEX)
@@ -5584,7 +5599,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 			break;
 		case 455: //Fade colormap
 		{
-			INT32 speed = (INT32)((((lines[i].flags & ML_DONTPEGBOTTOM) || !sides[lines[i].sidenum[0]].rowoffset) && lines[i].sidenum[1] != 0xFFFF) ?
+			INT32 speed = (INT32)((((lines[i].flags & ML_DONTPEGBOTTOM) || !sides[lines[i].sidenum[0]].rowoffset) && lines[i].sidenum[1] != NO_SIDEDEF) ?
 				abs(sides[lines[i].sidenum[1]].rowoffset >> FRACBITS)
 				: abs(sides[lines[i].sidenum[0]].rowoffset >> FRACBITS));
 
@@ -5614,7 +5629,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 			lines[i].args[0] = tag;
 			lines[i].args[1] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
 			lines[i].args[2] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
-			lines[i].args[3] = (lines[i].sidenum[1] != 0xffff) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : 0;
+			lines[i].args[3] = (lines[i].sidenum[1] != NO_SIDEDEF) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : 0;
 			lines[i].args[4] = !!(lines[i].flags & ML_NOSKEW);
 			break;
 		case 459: //Control text prompt
@@ -5634,7 +5649,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 				lines[i].args[2] |= TMP_ALLPLAYERS;
 			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;
+			lines[i].args[3] = (lines[i].sidenum[1] != NO_SIDEDEF) ? sides[lines[i].sidenum[1]].textureoffset >> FRACBITS : tag;
 			break;
 		case 460: //Award rings
 			lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
@@ -5647,7 +5662,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 			lines[i].args[3] = (lines[i].flags & ML_SKEWTD) ? AngleFixed(R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y)) >> FRACBITS : 0;
 			if (lines[i].flags & ML_NOCLIMB)
 			{
-				if (lines[i].sidenum[1] != 0xffff) // Make sure the linedef has a back side
+				if (lines[i].sidenum[1] != NO_SIDEDEF) // Make sure the linedef has a back side
 				{
 					lines[i].args[4] = 1;
 					lines[i].args[5] = sides[lines[i].sidenum[1]].textureoffset >> FRACBITS;
@@ -5681,7 +5696,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 			lines[i].args[0] = tag;
 			lines[i].args[1] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
 			lines[i].args[2] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS;
-			if (lines[i].sidenum[1] != 0xffff)
+			if (lines[i].sidenum[1] != NO_SIDEDEF)
 				lines[i].args[3] = sides[lines[i].sidenum[1]].textureoffset >> FRACBITS;
 			break;
 		case 482: //Polyobject - move
@@ -5753,7 +5768,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 			if (!(lines[i].flags & ML_DONTPEGBOTTOM))
 				lines[i].args[1] /= 100;
 			// allow Back Y Offset to be consistent with other fade specials
-			lines[i].args[2] = (lines[i].sidenum[1] != 0xffff && !sides[lines[i].sidenum[0]].rowoffset) ?
+			lines[i].args[2] = (lines[i].sidenum[1] != NO_SIDEDEF && !sides[lines[i].sidenum[0]].rowoffset) ?
 				abs(sides[lines[i].sidenum[1]].rowoffset >> FRACBITS)
 				: abs(sides[lines[i].sidenum[0]].rowoffset >> FRACBITS);
 			if (lines[i].flags & ML_MIDPEG)
@@ -5780,7 +5795,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 			lines[i].args[0] = tag;
 			if (lines[i].flags & ML_MIDPEG)
 			{
-				if (lines[i].sidenum[1] == 0xffff)
+				if (lines[i].sidenum[1] == NO_SIDEDEF)
 				{
 					CONS_Debug(DBG_GAMELOGIC, "Line special %d (line #%s) missing back side!\n", lines[i].special, sizeu1(i));
 					lines[i].special = 0;
@@ -5810,7 +5825,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 			lines[i].args[0] = lines[i].special >= 507;
 			if (lines[i].special % 2 == 0)
 			{
-				if (lines[i].sidenum[1] == 0xffff)
+				if (lines[i].sidenum[1] == NO_SIDEDEF)
 				{
 					CONS_Debug(DBG_GAMELOGIC, "Line special %d (line #%s) missing back side!\n", lines[i].special, sizeu1(i));
 					lines[i].special = 0;
@@ -5966,7 +5981,7 @@ static void P_ConvertBinaryLinedefTypes(void)
 			{
 				UINT8 side = lines[i].special >= 714;
 
-				if (side == 1 && lines[i].sidenum[1] == 0xffff)
+				if (side == 1 && lines[i].sidenum[1] == NO_SIDEDEF)
 					CONS_Debug(DBG_GAMELOGIC, "P_ConvertBinaryMap: Line special %d (line #%s) missing 2nd side!\n", lines[i].special, sizeu1(i));
 				else
 				{
diff --git a/src/p_spec.c b/src/p_spec.c
index 28ecc60f4..73bb0da88 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -557,7 +557,7 @@ static inline sector_t *getSector(INT32 currentSector, INT32 line, INT32 side)
   */
 static inline boolean twoSided(INT32 sector, INT32 line)
 {
-	return (sectors[sector].lines[line])->sidenum[1] != 0xffff;
+	return (sectors[sector].lines[line])->sidenum[1] != NO_SIDEDEF;
 }
 #endif
 
@@ -2897,7 +2897,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 			{
 				size_t linenum;
 				side_t *setfront = &sides[line->sidenum[0]];
-				side_t *setback = (line->args[3] && line->sidenum[1] != 0xffff) ? &sides[line->sidenum[1]] : setfront;
+				side_t *setback = (line->args[3] && line->sidenum[1] != NO_SIDEDEF) ? &sides[line->sidenum[1]] : setfront;
 				side_t *this;
 				boolean always = !(line->args[2]); // If args[2] is set: Only change mid texture if mid texture already exists on tagged lines, etc.
 
@@ -2919,7 +2919,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					}
 
 					// Back side
-					if (line->args[1] != TMSD_FRONT && lines[linenum].sidenum[1] != 0xffff)
+					if (line->args[1] != TMSD_FRONT && lines[linenum].sidenum[1] != NO_SIDEDEF)
 					{
 						this = &sides[lines[linenum].sidenum[1]];
 						if (always || this->toptexture) this->toptexture = setback->toptexture;
@@ -3153,7 +3153,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 
 				if (line->args[2] & TMCF_RELATIVE)
 				{
-					extracolormap_t *target = (!udmf && (line->flags & ML_TFERLINE) && line->sidenum[1] != 0xFFFF) ?
+					extracolormap_t *target = (!udmf && (line->flags & ML_TFERLINE) && line->sidenum[1] != NO_SIDEDEF) ?
 						sides[line->sidenum[1]].colormap_data : sectors[secnum].extra_colormap; // use back colormap instead of target sector
 
 						extracolormap_t *exc = R_AddColormaps(
@@ -3482,7 +3482,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				}
 
 				if (!udmf && (line->flags & ML_TFERLINE)) // use back colormap instead of target sector
-					sectors[secnum].extra_colormap = (line->sidenum[1] != 0xFFFF) ?
+					sectors[secnum].extra_colormap = (line->sidenum[1] != NO_SIDEDEF) ?
 					sides[line->sidenum[1]].colormap_data : NULL;
 
 				exc = sectors[secnum].extra_colormap;
@@ -7598,7 +7598,7 @@ static void P_SpawnScrollers(void)
 					{
 						if (l->args[1] != TMSD_BACK)
 							Add_Scroller(sc_side, l->args[2] << (FRACBITS - SCROLL_SHIFT), l->args[3] << (FRACBITS - SCROLL_SHIFT), control, lines[s].sidenum[0], accel, 0);
-						if (l->args[1] != TMSD_FRONT && lines[s].sidenum[1] != 0xffff)
+						if (l->args[1] != TMSD_FRONT && lines[s].sidenum[1] != NO_SIDEDEF)
 							Add_Scroller(sc_side, l->args[2] << (FRACBITS - SCROLL_SHIFT), l->args[3] << (FRACBITS - SCROLL_SHIFT), control, lines[s].sidenum[1], accel, 0);
 					}
 				break;
@@ -7609,7 +7609,7 @@ static void P_SpawnScrollers(void)
 					Add_Scroller(sc_side, -l->args[1] << FRACBITS, l->args[2] << FRACBITS, -1, l->sidenum[0], accel, 0);
 				if (l->args[0] != TMSD_FRONT)
 				{
-					if (l->sidenum[1] != 0xffff)
+					if (l->sidenum[1] != NO_SIDEDEF)
 						Add_Scroller(sc_side, -l->args[1] << FRACBITS, l->args[2] << FRACBITS, -1, l->sidenum[1], accel, 0);
 					else
 						CONS_Debug(DBG_GAMELOGIC, "Line special 500 (line #%s) missing back side!\n", sizeu1(i));
diff --git a/src/r_defs.h b/src/r_defs.h
index dfd2d6d70..6463e6f5d 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -512,6 +512,8 @@ typedef enum
 #define NUMLINEARGS 10
 #define NUMLINESTRINGARGS 2
 
+#define NO_SIDEDEF 0xFFFFFFFF
+
 typedef struct line_s
 {
 	// Vertices, from v1 to v2.
@@ -529,7 +531,7 @@ typedef struct line_s
 	char *stringargs[NUMLINESTRINGARGS];
 
 	// Visual appearance: sidedefs.
-	UINT16 sidenum[2]; // sidenum[1] will be 0xffff if one-sided
+	UINT32 sidenum[2]; // sidenum[1] will be NO_SIDEDEF if one-sided
 	fixed_t alpha; // translucency
 	UINT8 blendmode; // blendmode
 	INT32 executordelay;

From 5e501bae0767124adee00f8f0db0334b32422342 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Thu, 21 Sep 2023 13:19:38 +0100
Subject: [PATCH 12/36] v.getSpritePatch/v.getSprite2Patch fix: Fix offset
 inconsistency between rotated sprites and non-rotated sprites

This was caused by rotated sprites having FEETADJUST offsets applied, when non-rotated sprites did not. FEETADJUST is not actually needed in the HUD, so I've removed it from the rotated sprites (thankfully this is also the easiest way to fix it too)
---
 src/lua_hudlib.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 6eec91273..899e5a2dc 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -517,7 +517,7 @@ static int libd_getSpritePatch(lua_State *L)
 		INT32 rot = R_GetRollAngle(rollangle);
 
 		if (rot) {
-			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), true, &spriteinfo[i], rot);
+			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), false, &spriteinfo[i], rot);
 			LUA_PushUserdata(L, rotsprite, META_PATCH);
 			lua_pushboolean(L, false);
 			lua_pushboolean(L, true);
@@ -629,7 +629,7 @@ static int libd_getSprite2Patch(lua_State *L)
 		INT32 rot = R_GetRollAngle(rollangle);
 
 		if (rot) {
-			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), true, &skins[i].sprinfo[j], rot);
+			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), false, &skins[i].sprinfo[j], rot);
 			LUA_PushUserdata(L, rotsprite, META_PATCH);
 			lua_pushboolean(L, false);
 			lua_pushboolean(L, true);

From ee0bab2941b8e59054e34cb98f5e87d2580add51 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Thu, 21 Sep 2023 15:50:30 +0100
Subject: [PATCH 13/36] Remove adjustfeet parameter from
 Patch_GetRotatedSprite, remove [2] from spriteframe_t's rotated array.

Following from my previous commit, these things are no longer needed: they existed ONLY for adding FEETADJUST offsets to rotated sprites in Lua HUD code.
---
 src/hardware/hw_main.c |  2 +-
 src/lua_hudlib.c       |  4 ++--
 src/r_defs.h           |  2 +-
 src/r_patch.h          |  2 +-
 src/r_patchrotation.c  | 11 +++--------
 src/r_things.c         |  2 +-
 6 files changed, 9 insertions(+), 14 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 59af61753..4a000f83a 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -5259,7 +5259,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 			rollangle = R_GetRollAngle(spriterotangle);
 		}
 
-		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);
+		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, sprinfo, rollangle);
 
 		if (rotsprite != NULL)
 		{
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 899e5a2dc..cf03776e0 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -517,7 +517,7 @@ static int libd_getSpritePatch(lua_State *L)
 		INT32 rot = R_GetRollAngle(rollangle);
 
 		if (rot) {
-			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), false, &spriteinfo[i], rot);
+			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), &spriteinfo[i], rot);
 			LUA_PushUserdata(L, rotsprite, META_PATCH);
 			lua_pushboolean(L, false);
 			lua_pushboolean(L, true);
@@ -629,7 +629,7 @@ static int libd_getSprite2Patch(lua_State *L)
 		INT32 rot = R_GetRollAngle(rollangle);
 
 		if (rot) {
-			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), false, &skins[i].sprinfo[j], rot);
+			patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<<angle), &skins[i].sprinfo[j], rot);
 			LUA_PushUserdata(L, rotsprite, META_PATCH);
 			lua_pushboolean(L, false);
 			lua_pushboolean(L, true);
diff --git a/src/r_defs.h b/src/r_defs.h
index dfd2d6d70..b862ad7a9 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -922,7 +922,7 @@ typedef struct
 	UINT16 flip;
 
 #ifdef ROTSPRITE
-	rotsprite_t *rotated[2][16]; // Rotated patches
+	rotsprite_t *rotated[16]; // Rotated patches
 #endif
 } spriteframe_t;
 
diff --git a/src/r_patch.h b/src/r_patch.h
index a0ab3e75a..cc41639b3 100644
--- a/src/r_patch.h
+++ b/src/r_patch.h
@@ -37,7 +37,7 @@ patch_t *Patch_GetRotated(patch_t *patch, INT32 angle, boolean flip);
 patch_t *Patch_GetRotatedSprite(
 	spriteframe_t *sprite,
 	size_t frame, size_t spriteangle,
-	boolean flip, boolean adjustfeet,
+	boolean flip,
 	void *info, INT32 rotationangle);
 angle_t R_ModelRotationAngle(interpmobjstate_t *interp);
 angle_t R_SpriteRotationAngle(interpmobjstate_t *interp);
diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c
index b0cbeaa42..0cf4a9841 100644
--- a/src/r_patchrotation.c
+++ b/src/r_patchrotation.c
@@ -66,23 +66,22 @@ patch_t *Patch_GetRotated(patch_t *patch, INT32 angle, boolean flip)
 patch_t *Patch_GetRotatedSprite(
 	spriteframe_t *sprite,
 	size_t frame, size_t spriteangle,
-	boolean flip, boolean adjustfeet,
+	boolean flip,
 	void *info, INT32 rotationangle)
 {
 	rotsprite_t *rotsprite;
 	spriteinfo_t *sprinfo = (spriteinfo_t *)info;
 	INT32 idx = rotationangle;
-	UINT8 type = (adjustfeet ? 1 : 0);
 
 	if (rotationangle < 1 || rotationangle >= ROTANGLES)
 		return NULL;
 
-	rotsprite = sprite->rotated[type][spriteangle];
+	rotsprite = sprite->rotated[spriteangle];
 
 	if (rotsprite == NULL)
 	{
 		rotsprite = RotatedPatch_Create(ROTANGLES);
-		sprite->rotated[type][spriteangle] = rotsprite;
+		sprite->rotated[spriteangle] = rotsprite;
 	}
 
 	if (flip)
@@ -111,10 +110,6 @@ patch_t *Patch_GetRotatedSprite(
 		}
 
 		RotatedPatch_DoRotation(rotsprite, patch, rotationangle, xpivot, ypivot, flip);
-
-		//BP: we cannot use special tric in hardware mode because feet in ground caused by z-buffer
-		if (adjustfeet)
-			((patch_t *)rotsprite->patches[idx])->topoffset += FEETADJUST>>FRACBITS;
 	}
 
 	return rotsprite->patches[idx];
diff --git a/src/r_things.c b/src/r_things.c
index 89c4f35eb..85d76fecd 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1794,7 +1794,7 @@ static void R_ProjectSprite(mobj_t *thing)
 			rollangle = R_GetRollAngle(spriterotangle);
 		}
 
-		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);
+		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, sprinfo, rollangle);
 
 		if (rotsprite != NULL)
 		{

From 340eee8691697e2dd55f7eab84da0da0e83feb46 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Thu, 21 Sep 2023 16:13:55 +0100
Subject: [PATCH 14/36] Added some notes related to FEETADJUST for reference,
 made another tweak

---
 src/r_patchrotation.c | 6 ++----
 src/r_things.c        | 5 +++++
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c
index 0cf4a9841..b91069849 100644
--- a/src/r_patchrotation.c
+++ b/src/r_patchrotation.c
@@ -10,7 +10,7 @@
 /// \brief Patch rotation.
 
 #include "r_patchrotation.h"
-#include "r_things.h" // FEETADJUST
+#include "r_things.h" // FEETADJUST (todo: is this needed anymore? -- Monster Iestyn 21 Sep 2023 )
 #include "z_zone.h"
 #include "w_wad.h"
 #include "r_main.h" // R_PointToAngle
@@ -69,15 +69,13 @@ patch_t *Patch_GetRotatedSprite(
 	boolean flip,
 	void *info, INT32 rotationangle)
 {
-	rotsprite_t *rotsprite;
+	rotsprite_t *rotsprite = sprite->rotated[spriteangle];
 	spriteinfo_t *sprinfo = (spriteinfo_t *)info;
 	INT32 idx = rotationangle;
 
 	if (rotationangle < 1 || rotationangle >= ROTANGLES)
 		return NULL;
 
-	rotsprite = sprite->rotated[spriteangle];
-
 	if (rotsprite == NULL)
 	{
 		rotsprite = RotatedPatch_Create(ROTANGLES);
diff --git a/src/r_things.c b/src/r_things.c
index 85d76fecd..0d4b9a1e5 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -337,6 +337,11 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16
 			spritecachedinfo[numspritelumps].height = height<<FRACBITS;
 
 			// BP: we cannot use special tric in hardware mode because feet in ground caused by z-buffer
+			// Monster Iestyn (21 Sep 2023): the above comment no longer makes sense in context!!! So I give an explanation here!
+			// FEETADJUST was originally an OpenGL-exclusive hack from Doom Legacy to avoid the player's feet being clipped as
+			// a result of rendering partially under the ground, but sometime before SRB2 2.1's release this was changed to apply
+			// to the software renderer as well.
+			// TODO: kill FEETADJUST altogether somehow and somehow fix OpenGL not to clip sprites that are partially underground (if possible)?
 			spritecachedinfo[numspritelumps].topoffset += FEETADJUST;
 
 			//----------------------------------------------------

From 733dabe9a00088b74cbeb1155611114f12c04d2e Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Mon, 2 Oct 2023 23:31:21 +0200
Subject: [PATCH 15/36] Coins?

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

diff --git a/src/r_bbox.c b/src/r_bbox.c
index 5b2a817f1..8ccad2bb5 100644
--- a/src/r_bbox.c
+++ b/src/r_bbox.c
@@ -294,6 +294,7 @@ boolean R_ThingBoundingBoxVisible(mobj_t *thing)
 		case MT_BLUESPHERE:
 		case MT_NIGHTSSTAR:
 		case MT_NIGHTSCHIP:
+		case MT_COIN:
 			// Rings and similar objects are often placed
 			// in large amounts, so they are handled
 			// separately from other tangible objects.

From 9d650b0ddca510f61d2d9f948bffb948175f5ba0 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 25 Oct 2023 07:26:34 -0400
Subject: [PATCH 16/36] Makefile: add lua_colorlib.c

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

diff --git a/src/Sourcefile b/src/Sourcefile
index 6ed1f3b4c..7beb98c9e 100644
--- a/src/Sourcefile
+++ b/src/Sourcefile
@@ -94,3 +94,4 @@ lua_blockmaplib.c
 lua_hudlib.c
 lua_hudlib_drawlist.c
 lua_inputlib.c
+lua_colorlib.c

From 229d0364d4a8b37beec8e9f4851da6be426533b5 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 25 Oct 2023 07:40:55 -0400
Subject: [PATCH 17/36] Fixed lookup sides for 32-bit systems

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

diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 7302c727a..2a02b1a24 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -1243,6 +1243,9 @@ static int side_get(lua_State *L)
 		return 1;
 	case side_text:
 		{
+			boolean isfrontside;
+			size_t sidei = side-sides;
+
 			if (udmf)
 			{
 				LUA_Deprecated(L, "(sidedef_t).text", "(sidedef_t).line.stringargs");
@@ -1250,7 +1253,7 @@ static int side_get(lua_State *L)
 				return 1;
 			}
 
-			boolean isfrontside = side->line->sidenum[0] == side-sides;
+			isfrontside = side->line->sidenum[0] == sidei;
 
 			lua_pushstring(L, side->line->stringargs[isfrontside ? 0 : 1]);
 			return 1;
diff --git a/src/p_setup.c b/src/p_setup.c
index 733a47792..effd2c368 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -4005,7 +4005,7 @@ static void P_LinkMapData(void)
 		if (!seg->sidedef)
 			CorruptMapError(va("P_LinkMapData: seg->sidedef is NULL "
 				"(subsector %s, firstline is %d)", sizeu1(i), ss->firstline));
-		if (seg->sidedef - sides < 0 || seg->sidedef - sides > (UINT32)numsides)
+		if (seg->sidedef - sides < 0 || sidei > numsides)
 			CorruptMapError(va("P_LinkMapData: seg->sidedef refers to sidedef %s of %s "
 				"(subsector %s, firstline is %d)", sizeu1(sidei), sizeu2(numsides),
 				sizeu3(i), ss->firstline));

From ec1b629609cf6fccf1cf6c85705d76b11fb0c998 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 25 Oct 2023 08:00:04 -0400
Subject: [PATCH 18/36] spriteframe_t only gave 1D array, not 2D for rotated

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

diff --git a/src/r_things.c b/src/r_things.c
index 0d4b9a1e5..66a637b13 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -138,8 +138,7 @@ static void R_InstallSpriteLump(UINT16 wad,            // graphics patch
 #ifdef ROTSPRITE
 	for (r = 0; r < 16; r++)
 	{
-		sprtemp[frame].rotated[0][r] = NULL;
-		sprtemp[frame].rotated[1][r] = NULL;
+		sprtemp[frame].rotated[r] = NULL;
 	}
 #endif
 

From fd41bf188dc895a1039a1f8f12ebadadb728c3e7 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Wed, 25 Oct 2023 12:16:10 -0400
Subject: [PATCH 19/36] sidenum[0] is an uint32_t, not uint16_t

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

diff --git a/src/p_spec.c b/src/p_spec.c
index 73bb0da88..2dcd45f4d 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1796,7 +1796,7 @@ void 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);
+					CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger (sidedef %u): bad trigger ID %d\n", triggerline->sidenum[0], trigid);
 					return;
 				}
 				else if (!(unlocktriggers & (1 << trigid)))
@@ -1809,7 +1809,7 @@ void 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);
+					CONS_Debug(DBG_GAMELOGIC, "Unlockable check (sidedef %u): bad unlockable ID %d\n", triggerline->sidenum[0], unlockid);
 					return;
 				}
 				else if (!(serverGamedata->unlocked[unlockid-1]))
@@ -2940,7 +2940,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				INT32 trigid = line->args[0];
 
 				if (trigid < 0 || trigid > 31) // limited by 32 bit variable
-					CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger (sidedef %hu): bad trigger ID %d\n", line->sidenum[0], trigid);
+					CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger (sidedef %u): bad trigger ID %d\n", line->sidenum[0], trigid);
 				else
 				{
 					unlocktriggers |= 1 << trigid;

From e264caffe3599f37c119aeac91c3e54f16bce9d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Fri, 2 Jun 2023 23:44:50 +0200
Subject: [PATCH 20/36] Fix dangling pointer in mapthing after removing mobj

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

diff --git a/src/p_mobj.c b/src/p_mobj.c
index a81845918..5c352cd04 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -11204,6 +11204,10 @@ void P_RemoveMobj(mobj_t *mobj)
 
 	P_SetTarget(&mobj->hnext, P_SetTarget(&mobj->hprev, NULL));
 
+	// clear the reference from the mapthing
+	if (mobj->spawnpoint)
+		mobj->spawnpoint->mobj = NULL;
+
 	R_RemoveMobjInterpolator(mobj);
 
 	// free block

From d6b92de6910fccb845820fb206e3af3d7979270b Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 31 Oct 2023 16:52:12 -0300
Subject: [PATCH 21/36] Revert these

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

diff --git a/src/p_saveg.c b/src/p_saveg.c
index 3bcfea7cd..b496dce50 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1893,7 +1893,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 	if (diff & MD_RTIME)
 		WRITEINT32(save_p, mobj->reactiontime);
 	if (diff & MD_STATE)
-		WRITEUINT32(save_p, mobj->state-states);
+		WRITEUINT16(save_p, mobj->state-states);
 	if (diff & MD_TICS)
 		WRITEINT32(save_p, mobj->tics);
 	if (diff & MD_SPRITE) {
@@ -2917,7 +2917,7 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		mobj->reactiontime = mobj->info->reactiontime;
 
 	if (diff & MD_STATE)
-		mobj->state = &states[READUINT32(save_p)];
+		mobj->state = &states[READUINT16(save_p)];
 	else
 		mobj->state = &states[mobj->info->spawnstate];
 	if (diff & MD_TICS)

From 5d188221429e439d535956b2cc867f129691427f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 4 Nov 2023 14:21:17 +0100
Subject: [PATCH 22/36] Improve file search performance on Linux and FreeBSD

---
 src/filesrch.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/filesrch.c b/src/filesrch.c
index 313f286e1..111dfd6e7 100644
--- a/src/filesrch.c
+++ b/src/filesrch.c
@@ -433,9 +433,19 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
 		// okay, now we actually want searchpath to incorporate d_name
 		strcpy(&searchpath[searchpathindex[depthleft]],dent->d_name);
 
+#if defined(__linux__) || defined(__FreeBSD__)
+		if (dent->d_type == DT_UNKNOWN)
+			if (lstat(searchpath,&fsstat) == 0 && S_ISDIR(fsstat.st_mode))
+				dent->d_type = DT_DIR;
+
+		// Linux and FreeBSD has a special field for file type on dirent, so use that to speed up lookups.
+		// FIXME: should we also follow symlinks?
+		if (dent->d_type == DT_DIR && depthleft)
+#else
 		if (stat(searchpath,&fsstat) < 0) // do we want to follow symlinks? if not: change it to lstat
 			; // was the file (re)moved? can't stat it
 		else if (S_ISDIR(fsstat.st_mode) && depthleft)
+#endif
 		{
 			searchpathindex[--depthleft] = strlen(searchpath) + 1;
 			dirhandle[depthleft] = opendir(searchpath);

From 7c986c6d292d8470accd242a81ed14656cc63d18 Mon Sep 17 00:00:00 2001
From: SMS Alfredo <65426124+SMS-Alfredo@users.noreply.github.com>
Date: Thu, 9 Nov 2023 17:01:27 -0600
Subject: [PATCH 23/36] Make sure bot has CA_FLY or CA2_SPINDASH for their
 behaviors

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

diff --git a/src/b_bot.c b/src/b_bot.c
index 57f762304..033288a86 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -239,7 +239,8 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 	// SPINNING
 	if (!(player->pflags & (PF_SPINNING|PF_STARTDASH)) && mem->thinkstate == AI_SPINFOLLOW)
 		mem->thinkstate = AI_FOLLOW;
-	else if (mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_SPINFOLLOW)
+	else if ((mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_SPINFOLLOW)
+		&& bot->charability2 == CA2_SPINDASH)
 	{
 		if (!_2d)
 		{
@@ -329,7 +330,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 	if (mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_CATCHUP || (mem->thinkstate == AI_SPINFOLLOW && player->pflags & PF_JUMPED))
 	{
 		// Flying catch-up
-		if (bot->pflags & PF_THOKKED)
+		if (bot->charability == CA_FLY && bot->pflags & PF_THOKKED)
 		{
 			cmd->forwardmove = min(MAXPLMOVE, (dist/scale)>>3);
 			if (zdist < -64*scale)

From 2062553d22a5c4a544f11080e17a09addf93cd1b Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 9 Nov 2023 20:21:04 -0500
Subject: [PATCH 24/36] Update i_system.c

Add a check for overflowing the TERIOS buffer
---
 src/sdl/i_system.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 2a26f3f50..9dd80e6ef 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -607,6 +607,7 @@ void I_GetConsoleEvents(void)
 		return;
 
 	ev.type = ev_console;
+	ev.key = 0;
 	if (read(STDIN_FILENO, &key, 1) == -1 || !key)
 		return;
 
@@ -633,7 +634,7 @@ void I_GetConsoleEvents(void)
 		}
 		else return;
 	}
-	else
+	else if (tty_con.cursor < sizeof (tty_con.buffer))
 	{
 		// push regular character
 		ev.key = tty_con.buffer[tty_con.cursor] = key;

From f644877bd3926ff841587a53e0821ee2d0fbda97 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Fri, 10 Nov 2023 15:42:37 +0000
Subject: [PATCH 25/36] Update .gitlab-ci.yml file

Fixup ARM64 build
---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ef2304410..4e284ce65 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -356,7 +356,7 @@ Debian stable:arm64:
     - - |
           # 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=1C || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 ARM64=1
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 ERRORMODE=1 NONX86=1 ARM64=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 ARM64=1
       - |
           # make
           echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"

From 8cc7a88c5f09de7391d50a0f10401ac7ff7861d6 Mon Sep 17 00:00:00 2001
From: LJ Sonic <lamr@free.fr>
Date: Thu, 16 Nov 2023 22:00:23 +0100
Subject: [PATCH 26/36] Make module music volume more accurate

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

diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 0a39c7f28..bd4a23c0d 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -759,8 +759,8 @@ static void mix_gme(void *udata, Uint8 *stream, int len)
 		music_volume = 18;
 
 	// apply volume to stream
-	for (i = 0, p = (short *)stream; i < len/2; i++, p++)
-		*p = ((INT32)*p) * (music_volume*internal_volume/100)*2 / 40;
+	for (i = 0, p = (short *)stream; i < len / 2; i++, p++)
+		*p = ((INT32)*p) * music_volume * internal_volume / 100 / 20;
 }
 #endif
 
@@ -783,8 +783,8 @@ static void mix_openmpt(void *udata, Uint8 *stream, int len)
 		music_volume = 18;
 
 	// apply volume to stream
-	for (i = 0, p = (short *)stream; i < len/2; i++, p++)
-		*p = ((INT32)*p) * (music_volume*internal_volume/100)*2 / 40;
+	for (i = 0, p = (short *)stream; i < len / 2; i++, p++)
+		*p = ((INT32)*p) * music_volume * internal_volume / 100 / 20;
 }
 #endif
 

From 13aed6ad25fd723c8ea2bf08c0bdc5486806fc77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 18 Nov 2023 14:44:59 +0100
Subject: [PATCH 27/36] Add apropos console command

---
 src/command.c | 34 +++++++++++++++++++++++++++++++++-
 1 file changed, 33 insertions(+), 1 deletion(-)

diff --git a/src/command.c b/src/command.c
index e0deff8e1..d20c9c4ff 100644
--- a/src/command.c
+++ b/src/command.c
@@ -51,9 +51,11 @@ static void COM_CEchoDuration_f(void);
 static void COM_Exec_f(void);
 static void COM_Wait_f(void);
 static void COM_Help_f(void);
+static void COM_Apropos_f(void);
 static void COM_Toggle_f(void);
 static void COM_Add_f(void);
 
+
 static void CV_EnforceExecVersion(void);
 static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr);
 static boolean CV_Command(void);
@@ -344,6 +346,7 @@ void COM_Init(void)
 	COM_AddCommand("exec", COM_Exec_f, 0);
 	COM_AddCommand("wait", COM_Wait_f, 0);
 	COM_AddCommand("help", COM_Help_f, COM_LUA);
+	COM_AddCommand("apropos", COM_Apropos_f, COM_LUA);
 	COM_AddCommand("toggle", COM_Toggle_f, COM_LUA);
 	COM_AddCommand("add", COM_Add_f, COM_LUA);
 	RegisterNetXCmd(XD_NETVAR, Got_NetVar);
@@ -879,7 +882,7 @@ static void COM_Help_f(void)
 			boolean floatmode = false;
 			const char *cvalue = NULL;
 			CONS_Printf("\x82""Variable %s:\n", cvar->name);
-			CONS_Printf(M_GetText("  flags :"));
+			CONS_Printf(M_GetText("  flags: "));
 			if (cvar->flags & CV_SAVE)
 				CONS_Printf("AUTOSAVE ");
 			if (cvar->flags & CV_FLOAT)
@@ -1030,6 +1033,35 @@ static void COM_Help_f(void)
 	}
 }
 
+static void COM_Apropos_f(void)
+{
+	xcommand_t *cmd;
+	consvar_t *cvar;
+
+	if (COM_Argc() != 2)
+	{
+		CONS_Printf(M_GetText("apropos <text>: Search for cvars and cmds containing text\n"));
+		return;
+	}
+
+	CONS_Printf("\x82""Matching variables:\n");
+	for (cvar = consvar_vars; cvar; cvar = cvar->next)
+	{
+		if (cvar->flags & CV_NOSHOWHELP)
+			continue;
+		if (strstr(cvar->name, COM_Argv(1)) != NULL)
+			CONS_Printf("%s ", cvar->name);
+	}
+
+	CONS_Printf("\x82""\nMatching commands:\n");
+	for (cmd = com_commands; cmd; cmd = cmd->next)
+	{
+		if (strstr(cmd->name, COM_Argv(1)) != NULL)
+			CONS_Printf("%s ", cmd->name);
+	}
+	CONS_Printf("\n");
+}
+
 /** Toggles a console variable. Useful for on/off values.
   *
   * This works on on/off, yes/no values only

From fa050cdebf89b8ea0aeb888a2b9289e5ae0ffd98 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sat, 18 Nov 2023 20:44:26 +0100
Subject: [PATCH 28/36] Redesign search mechanism

---
 src/command.c | 90 ++++++++++++++++++++++++++++++---------------------
 src/console.c | 27 ++--------------
 2 files changed, 57 insertions(+), 60 deletions(-)

diff --git a/src/command.c b/src/command.c
index d20c9c4ff..d41a55153 100644
--- a/src/command.c
+++ b/src/command.c
@@ -51,7 +51,7 @@ static void COM_CEchoDuration_f(void);
 static void COM_Exec_f(void);
 static void COM_Wait_f(void);
 static void COM_Help_f(void);
-static void COM_Apropos_f(void);
+static void COM_Find_f(void);
 static void COM_Toggle_f(void);
 static void COM_Add_f(void);
 
@@ -346,7 +346,7 @@ void COM_Init(void)
 	COM_AddCommand("exec", COM_Exec_f, 0);
 	COM_AddCommand("wait", COM_Wait_f, 0);
 	COM_AddCommand("help", COM_Help_f, COM_LUA);
-	COM_AddCommand("apropos", COM_Apropos_f, COM_LUA);
+	COM_AddCommand("find", COM_Find_f, COM_LUA);
 	COM_AddCommand("toggle", COM_Toggle_f, COM_LUA);
 	COM_AddCommand("add", COM_Add_f, COM_LUA);
 	RegisterNetXCmd(XD_NETVAR, Got_NetVar);
@@ -979,31 +979,8 @@ static void COM_Help_f(void)
 				return;
 			}
 
-			CONS_Printf("No exact match, searching...\n");
-
-			// variables
-			CONS_Printf("\x82""Variables:\n");
-			for (cvar = consvar_vars; cvar; cvar = cvar->next)
-			{
-				if ((cvar->flags & CV_NOSHOWHELP) || (!strstr(cvar->name, help)))
-					continue;
-				CONS_Printf("%s ", cvar->name);
-				i++;
-			}
-
-			// commands
-			CONS_Printf("\x82""\nCommands:\n");
-			for (cmd = com_commands; cmd; cmd = cmd->next)
-			{
-				if (!strstr(cmd->name, help))
-					continue;
-				CONS_Printf("%s ",cmd->name);
-				i++;
-			}
-
-			CONS_Printf("\x82""\nCheck wiki.srb2.org for more or type help <command or variable>\n");
-
-			CONS_Debug(DBG_GAMELOGIC, "\x87Total : %d\n", i);
+			CONS_Printf("No variable or command named %s", help);
+			CONS_Printf("\x82""\nCheck wiki.srb2.org for more or try typing help without arguments\n");
 		}
 		return;
 	}
@@ -1033,33 +1010,74 @@ static void COM_Help_f(void)
 	}
 }
 
-static void COM_Apropos_f(void)
+static void COM_Find_f(void)
 {
+	static char prefix[80];
 	xcommand_t *cmd;
 	consvar_t *cvar;
+	cmdalias_t *alias;
+	const char *match;
+	const char *help;
+	size_t helplen;
+	boolean matchesany;
 
 	if (COM_Argc() != 2)
 	{
-		CONS_Printf(M_GetText("apropos <text>: Search for cvars and cmds containing text\n"));
+		CONS_Printf(M_GetText("find <text>: Search for variables, commands and aliases containing <text>\n"));
 		return;
 	}
 
-	CONS_Printf("\x82""Matching variables:\n");
+	help = COM_Argv(1);
+	helplen = strlen(help);
+	CONS_Printf("\x82""Variables:\n");
+	matchesany = false;
 	for (cvar = consvar_vars; cvar; cvar = cvar->next)
 	{
 		if (cvar->flags & CV_NOSHOWHELP)
 			continue;
-		if (strstr(cvar->name, COM_Argv(1)) != NULL)
-			CONS_Printf("%s ", cvar->name);
+		match = strstr(cvar->name, help);
+		if (match != NULL)
+		{
+			memcpy(prefix, cvar->name, match - cvar->name);
+			prefix[match - cvar->name] = '\0';
+			CONS_Printf("  %s\x83%s\x80%s\n", prefix, help, &match[helplen]);
+			matchesany = true;
+		}
 	}
+	if (!matchesany)
+		CONS_Printf("  (none)\n");
 
-	CONS_Printf("\x82""\nMatching commands:\n");
+	CONS_Printf("\x82""Commands:\n");
+	matchesany = false;
 	for (cmd = com_commands; cmd; cmd = cmd->next)
 	{
-		if (strstr(cmd->name, COM_Argv(1)) != NULL)
-			CONS_Printf("%s ", cmd->name);
+		match = strstr(cmd->name, help);
+		if (match != NULL)
+		{
+			memcpy(prefix, cmd->name, match - cmd->name);
+			prefix[match - cmd->name] = '\0';
+			CONS_Printf("  %s\x83%s\x80%s\n", prefix, help, &match[helplen]);
+			matchesany = true;
+		}
 	}
-	CONS_Printf("\n");
+	if (!matchesany)
+		CONS_Printf("  (none)\n");
+
+	CONS_Printf("\x82""Aliases:\n");
+	matchesany = false;
+	for (alias = com_alias; alias; alias = alias->next)
+	{
+		match = strstr(alias->name, help);
+		if (match != NULL)
+		{
+			memcpy(prefix, alias->name, match - alias->name);
+			prefix[match - alias->name] = '\0';
+			CONS_Printf("  %s\x83%s\x80%s\n", prefix, help, &match[helplen]);
+			matchesany = true;
+		}
+	}
+	if (!matchesany)
+		CONS_Printf("  (none)\n");
 }
 
 /** Toggles a console variable. Useful for on/off values.
diff --git a/src/console.c b/src/console.c
index dbd7c938a..01b90ebc3 100644
--- a/src/console.c
+++ b/src/console.c
@@ -921,7 +921,8 @@ boolean CON_Responder(event_t *ev)
 	static UINT8 consdown = false; // console is treated differently due to rare usage
 
 	// sequential completions a la 4dos
-	static char completion[80];
+	static char completioncmd[80 + sizeof("find ")] = "find ";
+	static char *completion = &completioncmd[sizeof("find ")-1];
 
 	static INT32 skips;
 
@@ -1057,36 +1058,14 @@ boolean CON_Responder(event_t *ev)
 		// show all cvars/commands that match what we have inputted
 		if (key == KEY_TAB)
 		{
-			size_t i, len;
-
 			if (!completion[0])
 			{
 				if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
 					return true;
 				strcpy(completion, inputlines[inputline]);
 			}
-			len = strlen(completion);
-
-			//first check commands
-			CONS_Printf("\nCommands:\n");
-			for (i = 0, cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, ++i))
-				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, cmd+len);
-			if (i == 0) CONS_Printf("  (none)\n");
-
-			//now we move on to CVARs
-			CONS_Printf("Variables:\n");
-			for (i = 0, cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, ++i))
-				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, cmd+len);
-			if (i == 0) CONS_Printf("  (none)\n");
-
-			//and finally aliases
-			CONS_Printf("Aliases:\n");
-			for (i = 0, cmd = COM_CompleteAlias(completion, i); cmd; cmd = COM_CompleteAlias(completion, ++i))
-				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, cmd+len);
-			if (i == 0) CONS_Printf("  (none)\n");
-
+			COM_BufInsertText(completioncmd);
 			completion[0] = 0;
-
 			return true;
 		}
 		// ---

From e6dfeace843570ad7a13565eb729d143ce95a1e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Sun, 19 Nov 2023 13:17:18 +0100
Subject: [PATCH 29/36] Fix anisotropic and FOV settings not saving

---
 src/hardware/hw_main.c | 17 ++---------------
 src/hardware/hw_main.h |  1 -
 src/r_main.c           |  2 +-
 3 files changed, 3 insertions(+), 17 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 2d7c99861..a473a9292 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -169,7 +169,6 @@ ps_metric_t ps_hw_batchdrawtime = {0};
 
 boolean gl_init = false;
 boolean gl_maploaded = false;
-boolean gl_sessioncommandsadded = false;
 boolean gl_shadersavailable = true;
 
 // ==========================================================================
@@ -6595,7 +6594,7 @@ consvar_t cv_glfakecontrast = CVAR_INIT ("gr_fakecontrast", "Smooth", CV_SAVE, g
 consvar_t cv_glslopecontrast = CVAR_INIT ("gr_slopecontrast", "Off", CV_SAVE, CV_OnOff, NULL);
 
 consvar_t cv_glfiltermode = CVAR_INIT ("gr_filtermode", "Nearest", CV_SAVE|CV_CALL, glfiltermode_cons_t, CV_glfiltermode_OnChange);
-consvar_t cv_glanisotropicmode = CVAR_INIT ("gr_anisotropicmode", "1", CV_CALL, glanisotropicmode_cons_t, CV_glanisotropic_OnChange);
+consvar_t cv_glanisotropicmode = CVAR_INIT ("gr_anisotropicmode", "1", CV_SAVE|CV_CALL, glanisotropicmode_cons_t, CV_glanisotropic_OnChange);
 
 consvar_t cv_glsolvetjoin = CVAR_INIT ("gr_solvetjoin", "On", 0, CV_OnOff, NULL);
 
@@ -6637,6 +6636,7 @@ void HWR_AddCommands(void)
 	CV_RegisterVar(&cv_glallowshaders);
 
 	CV_RegisterVar(&cv_glfiltermode);
+	CV_RegisterVar(&cv_glanisotropicmode);
 	CV_RegisterVar(&cv_glsolvetjoin);
 
 	CV_RegisterVar(&cv_glbatching);
@@ -6646,14 +6646,6 @@ void HWR_AddCommands(void)
 #endif
 }
 
-void HWR_AddSessionCommands(void)
-{
-	if (gl_sessioncommandsadded)
-		return;
-	CV_RegisterVar(&cv_glanisotropicmode);
-	gl_sessioncommandsadded = true;
-}
-
 // --------------------------------------------------------------------------
 // Setup the hardware renderer
 // --------------------------------------------------------------------------
@@ -6664,7 +6656,6 @@ void HWR_Startup(void)
 		CONS_Printf("HWR_Startup()...\n");
 
 		HWR_InitPolyPool();
-		HWR_AddSessionCommands();
 		HWR_InitMapTextures();
 		HWR_InitModels();
 #ifdef ALAM_LIGHTING
@@ -6687,10 +6678,6 @@ void HWR_Startup(void)
 // --------------------------------------------------------------------------
 void HWR_Switch(void)
 {
-	// Add session commands
-	if (!gl_sessioncommandsadded)
-		HWR_AddSessionCommands();
-
 	// Set special states from CVARs
 	HWD.pfnSetSpecialState(HWD_SET_TEXTUREFILTERMODE, cv_glfiltermode.value);
 	HWD.pfnSetSpecialState(HWD_SET_TEXTUREANISOTROPICMODE, cv_glanisotropicmode.value);
diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h
index 9450ca2c5..cce4e8f0a 100644
--- a/src/hardware/hw_main.h
+++ b/src/hardware/hw_main.h
@@ -54,7 +54,6 @@ UINT8 *HWR_GetScreenshot(void);
 boolean HWR_Screenshot(const char *pathname);
 
 void HWR_AddCommands(void);
-void HWR_AddSessionCommands(void);
 void transform(float *cx, float *cy, float *cz);
 INT32 HWR_GetTextureUsed(void);
 void HWR_DoPostProcessor(player_t *player);
diff --git a/src/r_main.c b/src/r_main.c
index 6c7bedbf1..0cfccab8c 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -158,7 +158,7 @@ consvar_t cv_drawdist = CVAR_INIT ("drawdist", "Infinite", CV_SAVE, drawdist_con
 consvar_t cv_drawdist_nights = CVAR_INIT ("drawdist_nights", "2048", CV_SAVE, drawdist_cons_t, NULL);
 consvar_t cv_drawdist_precip = CVAR_INIT ("drawdist_precip", "1024", CV_SAVE, drawdist_precip_cons_t, NULL);
 //consvar_t cv_precipdensity = CVAR_INIT ("precipdensity", "Moderate", CV_SAVE, precipdensity_cons_t, NULL);
-consvar_t cv_fov = CVAR_INIT ("fov", "90", CV_FLOAT|CV_CALL, fov_cons_t, Fov_OnChange);
+consvar_t cv_fov = CVAR_INIT ("fov", "90", CV_SAVE|CV_FLOAT|CV_CALL, fov_cons_t, Fov_OnChange);
 
 // Okay, whoever said homremoval causes a performance hit should be shot.
 consvar_t cv_homremoval = CVAR_INIT ("homremoval", "No", CV_SAVE, homremoval_cons_t, NULL);

From 977cd0db24e4af2770b3b168dfefee741e8ea5f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Wed, 22 Nov 2023 19:41:35 +0100
Subject: [PATCH 30/36] Fix LAN discovery for SRB2 servers

---
 src/netcode/i_tcp.c | 95 ++++++++++++++++++++-------------------------
 1 file changed, 42 insertions(+), 53 deletions(-)

diff --git a/src/netcode/i_tcp.c b/src/netcode/i_tcp.c
index 698234579..9c0e8eb2b 100644
--- a/src/netcode/i_tcp.c
+++ b/src/netcode/i_tcp.c
@@ -154,6 +154,8 @@ typedef union
 	#define ERRSOCKET (-1)
 #endif
 
+#define IPV6_MULTICAST_ADDRESS "ff15::57e1:1a12"
+
 // define socklen_t in DOS/Windows if it is not already defined
 #ifdef USE_WINSOCK1
 	typedef int socklen_t;
@@ -621,6 +623,7 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr
 	socklen_t d6 = (socklen_t)sizeof(struct sockaddr_in6);
 #endif
 	socklen_t d, da = (socklen_t)sizeof(mysockaddr_t);
+	ssize_t status;
 
 	switch (sockaddr->any.sa_family)
 	{
@@ -631,7 +634,12 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr
 		default:       d = da; break;
 	}
 
-	return sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d);
+	status = sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d);
+	if (status == -1)
+	{
+		CONS_Alert(CONS_WARNING, "Unable to send packet to %s: %s\n", SOCK_AddrToStr(sockaddr), strerror(errno));
+	}
+	return status;
 }
 
 static void SOCK_Send(void)
@@ -770,6 +778,24 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 		return (SOCKET_TYPE)ERRSOCKET;
 	}
 
+#ifdef HAVE_IPV6
+	if (family == AF_INET6)
+	{
+		// we need to set all of this *after* binding to an address!
+		if (memcmp(&straddr.ip6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL
+		{
+			struct ipv6_mreq maddr;
+
+			inet_pton(AF_INET6, IPV6_MULTICAST_ADDRESS, &maddr.ipv6mr_multiaddr);
+			maddr.ipv6mr_interface = 0;
+			if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &maddr, sizeof(maddr)) != 0)
+			{
+				CONS_Alert(CONS_WARNING, M_GetText("Could not register multicast address\n"));
+			}
+		}
+	}
+#endif
+
 #ifdef FIONBIO
 	// make it non blocking
 	opt = true;
@@ -950,65 +976,28 @@ static boolean UDP_Socket(void)
 	// ip + udp
 	packetheaderlength = 20 + 8; // for stats
 
-	hints.ai_family = AF_INET;
-	gaie = I_getaddrinfo("127.0.0.1", "0", &hints, &ai);
-	if (gaie == 0)
-	{
-		runp = ai;
-		while (runp != NULL && s < MAXNETNODES+1)
-		{
-			memcpy(&clientaddress[s], runp->ai_addr, runp->ai_addrlen);
-			s++;
-			runp = runp->ai_next;
-		}
-		I_freeaddrinfo(ai);
-	}
-	else
-	{
-		clientaddress[s].any.sa_family = AF_INET;
-		clientaddress[s].ip4.sin_port = htons(0);
-		clientaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); //GetLocalAddress(); // my own ip
-		s++;
-	}
+	clientaddress[s].any.sa_family = AF_INET;
+	clientaddress[s].ip4.sin_port = htons(0);
+	clientaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); //GetLocalAddress(); // my own ip
+	s++;
 
 	s = 0;
 
 	// setup broadcast adress to BROADCASTADDR entry
-	gaie = I_getaddrinfo("255.255.255.255", "0", &hints, &ai);
-	if (gaie == 0)
-	{
-		runp = ai;
-		while (runp != NULL && s < MAXNETNODES+1)
-		{
-			memcpy(&broadcastaddress[s], runp->ai_addr, runp->ai_addrlen);
-			s++;
-			runp = runp->ai_next;
-		}
-		I_freeaddrinfo(ai);
-	}
-	else
-	{
-		broadcastaddress[s].any.sa_family = AF_INET;
-		broadcastaddress[s].ip4.sin_port = htons(0);
-		broadcastaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_BROADCAST);
-		s++;
-	}
+	broadcastaddress[s].any.sa_family = AF_INET;
+	broadcastaddress[s].ip4.sin_port = htons(atoi(DEFAULTPORT));
+	broadcastaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+	s++;
+
 #ifdef HAVE_IPV6
 	if (b_ipv6)
 	{
-		hints.ai_family = AF_INET6;
-		gaie = I_getaddrinfo("ff02::1", "0", &hints, &ai);
-		if (gaie == 0)
-		{
-			runp = ai;
-			while (runp != NULL && s < MAXNETNODES+1)
-			{
-				memcpy(&broadcastaddress[s], runp->ai_addr, runp->ai_addrlen);
-				s++;
-				runp = runp->ai_next;
-			}
-			I_freeaddrinfo(ai);
-		}
+		broadcastaddress[s].any.sa_family = AF_INET6;
+		broadcastaddress[s].ip6.sin6_port = htons(atoi(DEFAULTPORT));
+		broadcastaddress[s].ip6.sin6_flowinfo = 0;
+		inet_pton(AF_INET6, IPV6_MULTICAST_ADDRESS, &broadcastaddress[s].ip6.sin6_addr);
+		broadcastaddress[s].ip6.sin6_scope_id = 0;
+		s++;
 	}
 #endif
 

From 3c921044a35734e1c1326f7008c4049f7fce5b78 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= <gustaf@hanicef.me>
Date: Wed, 22 Nov 2023 20:26:55 +0100
Subject: [PATCH 31/36] Fix Windows build

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

diff --git a/src/netcode/i_tcp.c b/src/netcode/i_tcp.c
index 9c0e8eb2b..0b650de49 100644
--- a/src/netcode/i_tcp.c
+++ b/src/netcode/i_tcp.c
@@ -788,7 +788,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
 
 			inet_pton(AF_INET6, IPV6_MULTICAST_ADDRESS, &maddr.ipv6mr_multiaddr);
 			maddr.ipv6mr_interface = 0;
-			if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &maddr, sizeof(maddr)) != 0)
+			if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *)&maddr, sizeof(maddr)) != 0)
 			{
 				CONS_Alert(CONS_WARNING, M_GetText("Could not register multicast address\n"));
 			}

From 563ce141ce0406a915cee9260033a209d53181ab Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Tue, 21 Nov 2023 21:15:09 -0300
Subject: [PATCH 32/36] Fix P_GetSectorColormapAt

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

diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 97bc9319c..3074187a3 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -2025,14 +2025,18 @@ static int lib_pCeilingzAtPos(lua_State *L)
 
 static int lib_pGetSectorColormapAt(lua_State *L)
 {
+	boolean has_sector = false;
 	sector_t *sector = NULL;
-	if (!lua_isnone(L, 1) && lua_isuserdata(L, 1))
+	if (!lua_isnoneornil(L, 1))
+	{
+		has_sector = true;
 		sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
+	}
 	fixed_t x = luaL_checkfixed(L, 2);
 	fixed_t y = luaL_checkfixed(L, 3);
 	fixed_t z = luaL_checkfixed(L, 4);
 	INLEVEL
-	if (!sector)
+	if (has_sector && !sector)
 		return LUA_ErrInvalid(L, "sector_t");
 	extracolormap_t *exc;
 	if (sector)

From 46bd7344c5b17b72e1cbe647f3e0649be8c54abe Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Thu, 23 Nov 2023 13:39:24 -0300
Subject: [PATCH 33/36] Rename 'sector.extracolormap' to just 'sector.colormap'

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

diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 2cc4ec2a2..1d85b786f 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -57,7 +57,7 @@ enum sector_e {
 	sector_ffloors,
 	sector_fslope,
 	sector_cslope,
-	sector_extracolormap,
+	sector_colormap,
 	sector_flags,
 	sector_specialflags,
 	sector_damagetype,
@@ -96,7 +96,7 @@ static const char *const sector_opt[] = {
 	"ffloors",
 	"f_slope",
 	"c_slope",
-	"extracolormap",
+	"colormap",
 	"flags",
 	"specialflags",
 	"damagetype",
@@ -753,7 +753,7 @@ static int sector_get(lua_State *L)
 	case sector_cslope: // c_slope
 		LUA_PushUserdata(L, sector->c_slope, META_SLOPE);
 		return 1;
-	case sector_extracolormap: // extra_colormap
+	case sector_colormap: // extra_colormap
 		LUA_PushUserdata(L, sector->extra_colormap, META_EXTRACOLORMAP);
 		return 1;
 	case sector_flags: // flags

From 1009c31ad1eb9fccc30c757e3c236e3e82a08d45 Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Thu, 23 Nov 2023 14:36:02 -0300
Subject: [PATCH 34/36] Change some instances of sizeu1 to sizeu2

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

diff --git a/src/p_setup.c b/src/p_setup.c
index effd2c368..211ce54e2 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -3264,7 +3264,7 @@ static void P_InitializeSeg(seg_t *seg)
 		UINT32 side = seg->linedef->sidenum[seg->side];
 
 		if (side == NO_SIDEDEF)
-			I_Error("P_InitializeSeg: Seg %s refers to side %d of linedef %s, which doesn't exist!\n", sizeu1((size_t)(seg - segs)), seg->side, sizeu1((size_t)(seg->linedef - lines)));
+			I_Error("P_InitializeSeg: Seg %s refers to side %d of linedef %s, which doesn't exist!\n", sizeu1((size_t)(seg - segs)), seg->side, sizeu2((size_t)(seg->linedef - lines)));
 
 		seg->sidedef = &sides[side];
 
@@ -3475,7 +3475,7 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 			{
 				UINT32 vertexnum = READUINT32((*data));
 				if (vertexnum >= numvertexes)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid vertex %d!\n", sizeu1(k), sizeu1(m), vertexnum);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid vertex %d!\n", sizeu1(k), sizeu2(m), vertexnum);
 
 				segs[k - 1 + ((m == 0) ? subsectors[i].numlines : 0)].v2 = segs[k].v1 = &vertexes[vertexnum];
 
@@ -3516,11 +3516,11 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
 				UINT16 linenum = READUINT16((*data));
 
 				if (v1num >= numvertexes)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid v1 %d!\n", sizeu1(k), sizeu1(m), v1num);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid v1 %d!\n", sizeu1(k), sizeu2(m), v1num);
 				if (v2num >= numvertexes)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid v2 %d!\n", sizeu1(k), sizeu1(m), v2num);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid v2 %d!\n", sizeu1(k), sizeu2(m), v2num);
 				if (linenum >= numlines)
-					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid linedef %d!\n", sizeu1(k), sizeu1(m), linenum);
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %s has invalid linedef %d!\n", sizeu1(k), sizeu2(m), linenum);
 
 				segs[k].v1 = &vertexes[v1num];
 				segs[k].v2 = &vertexes[v2num];
@@ -4012,7 +4012,7 @@ static void P_LinkMapData(void)
 		if (!seg->sidedef->sector)
 			CorruptMapError(va("P_LinkMapData: seg->sidedef->sector is NULL "
 				"(subsector %s, firstline is %d, sidedef is %s)", sizeu1(i), ss->firstline,
-				sizeu1(sidei)));
+				sizeu2(sidei)));
 		ss->sector = seg->sidedef->sector;
 	}
 

From e50993861eec17cf26cb96d4c63fa22a1f8982c0 Mon Sep 17 00:00:00 2001
From: SMS Alfredo <msriofan3629@gmail.com>
Date: Sun, 26 Nov 2023 20:38:49 +0000
Subject: [PATCH 35/36] Dedicated Shield / Super Transformation Button

---
 src/d_player.h    |   4 +
 src/d_ticcmd.h    |  27 +++--
 src/deh_lua.c     |   2 +-
 src/deh_tables.c  |   7 +-
 src/g_game.c      |  10 +-
 src/g_input.c     |  22 ++--
 src/g_input.h     |   4 +-
 src/lua_baselib.c |   3 +-
 src/m_menu.c      |  57 ++++-----
 src/p_local.h     |   2 +-
 src/p_user.c      | 296 +++++++++++++++++++++++-----------------------
 src/y_inter.c     |   6 +-
 12 files changed, 227 insertions(+), 213 deletions(-)

diff --git a/src/d_player.h b/src/d_player.h
index ca700d4e1..1409e28b3 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -158,6 +158,10 @@ typedef enum
 	PF_FORCESTRAFE = 1<<28, // Turning inputs are translated into strafing inputs
 	PF_CANCARRY    = 1<<29, // Can carry another player?
 	PF_FINISHED    = 1<<30, // The player finished the level. NOT the same as exiting
+	
+	// True if shield button down last tic
+	// This may be the final flag, but 2.3 could free up the others
+	PF_SHIELDDOWN    = 1<<31,
 
 	// up to 1<<31 is free
 } pflags_t;
diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h
index 2481ed738..43eb0f00b 100644
--- a/src/d_ticcmd.h
+++ b/src/d_ticcmd.h
@@ -26,20 +26,23 @@
 // Button/action code definitions.
 typedef enum
 {
-	// First 4 bits are weapon change info, DO NOT USE!
-	BT_WEAPONMASK = 0x0F, //our first four bits.
+	// First 3 bits are weapon change info, DO NOT USE!
+	BT_WEAPONMASK = 0x07,  //our first three bits.
+	
+	BT_SHIELD     = 1<<3,  // shield or super action
 
-	BT_WEAPONNEXT = 1<<4,
-	BT_WEAPONPREV = 1<<5,
-
-	BT_ATTACK     = 1<<6, // shoot rings
-	BT_SPIN       = 1<<7,
-	BT_CAMLEFT    = 1<<8, // turn camera left
-	BT_CAMRIGHT   = 1<<9, // turn camera right
-	BT_TOSSFLAG   = 1<<10,
-	BT_JUMP       = 1<<11,
-	BT_FIRENORMAL = 1<<12, // Fire a normal ring no matter what
+	BT_WEAPONNEXT = 1<<4,  // select next weapon
+	BT_WEAPONPREV = 1<<5,  // select previous weapon
 
+	BT_ATTACK     = 1<<6,  // shoot rings
+	BT_SPIN       = 1<<7,  // spin action
+	BT_CAMLEFT    = 1<<8,  // turn camera left
+	BT_CAMRIGHT   = 1<<9,  // turn camera right
+	BT_TOSSFLAG   = 1<<10, // toss flag or emeralds
+	BT_JUMP       = 1<<11, // jump action
+	BT_FIRENORMAL = 1<<12, // fire a normal ring no matter what
+	
+	// custom lua buttons
 	BT_CUSTOM1    = 1<<13,
 	BT_CUSTOM2    = 1<<14,
 	BT_CUSTOM3    = 1<<15,
diff --git a/src/deh_lua.c b/src/deh_lua.c
index e596e166f..b8daa0430 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -589,7 +589,7 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word)
 	{
 		CacheAndPushConstant(L, word, (lua_Integer)BT_SPIN);
 		return 1;
-	}
+	} 
 
 	for (i = 0; INT_CONST[i].n; i++)
 		if (fastcmp(word,INT_CONST[i].n)) {
diff --git a/src/deh_tables.c b/src/deh_tables.c
index bf148d82b..b53cd00c8 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -5576,7 +5576,8 @@ struct int_const_s const INT_CONST[] = {
 	{"ROTAXIS_Z",ROTAXIS_Z},
 
 	// Buttons (ticcmd_t)
-	{"BT_WEAPONMASK",BT_WEAPONMASK}, //our first four bits.
+	{"BT_WEAPONMASK",BT_WEAPONMASK}, //our first three bits.
+	{"BT_SHIELD",BT_SHIELD},
 	{"BT_WEAPONNEXT",BT_WEAPONNEXT},
 	{"BT_WEAPONPREV",BT_WEAPONPREV},
 	{"BT_ATTACK",BT_ATTACK}, // shoot rings
@@ -5756,9 +5757,7 @@ struct int_const_s const INT_CONST[] = {
 	{"GC_WEPSLOT5",GC_WEPSLOT5},
 	{"GC_WEPSLOT6",GC_WEPSLOT6},
 	{"GC_WEPSLOT7",GC_WEPSLOT7},
-	{"GC_WEPSLOT8",GC_WEPSLOT8},
-	{"GC_WEPSLOT9",GC_WEPSLOT9},
-	{"GC_WEPSLOT10",GC_WEPSLOT10},
+	{"GC_SHIELD",GC_SHIELD},
 	{"GC_FIRE",GC_FIRE},
 	{"GC_FIRENORMAL",GC_FIRENORMAL},
 	{"GC_TOSSFLAG",GC_TOSSFLAG},
diff --git a/src/g_game.c b/src/g_game.c
index 438428b90..1388148e7 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1335,7 +1335,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 #if NUM_WEAPONS > 10
 "Add extra inputs to g_input.h/gamecontrols_e"
 #endif
-	//use the four avaliable bits to determine the weapon.
+	//use the three avaliable bits to determine the weapon.
 	cmd->buttons &= ~BT_WEAPONMASK;
 	for (i = 0; i < NUM_WEAPONS; ++i)
 		if (PLAYERINPUTDOWN(ssplayer, GC_WEPSLOT1 + i))
@@ -1353,9 +1353,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 	axis = PlayerJoyAxis(ssplayer, JA_FIRENORMAL);
 	if (PLAYERINPUTDOWN(ssplayer, GC_FIRENORMAL) || (usejoystick && axis > 0))
 		cmd->buttons |= BT_FIRENORMAL;
-
+	
+	// Toss flag button
 	if (PLAYERINPUTDOWN(ssplayer, GC_TOSSFLAG))
 		cmd->buttons |= BT_TOSSFLAG;
+	
+	// Shield button
+	if (PLAYERINPUTDOWN(ssplayer, GC_SHIELD))
+		cmd->buttons |= BT_SHIELD;
 
 	// Lua scriptable buttons
 	if (PLAYERINPUTDOWN(ssplayer, GC_CUSTOM1))
@@ -2748,6 +2753,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 	p->pflags |= PF_SPINDOWN;
 	p->pflags |= PF_ATTACKDOWN;
 	p->pflags |= PF_JUMPDOWN;
+	p->pflags |= PF_SHIELDDOWN;
 
 	p->playerstate = PST_LIVE;
 	p->panim = PA_IDLE; // standing animation
diff --git a/src/g_input.c b/src/g_input.c
index 8b5056331..3f1be37ba 100644
--- a/src/g_input.c
+++ b/src/g_input.c
@@ -576,9 +576,7 @@ static const char *gamecontrolname[NUM_GAMECONTROLS] =
 	"weapon5",
 	"weapon6",
 	"weapon7",
-	"weapon8",
-	"weapon9",
-	"weapon10",
+	"shield",
 	"fire",
 	"firenormal",
 	"tossflag",
@@ -693,6 +691,7 @@ void G_DefineDefaultControls(void)
 	gamecontroldefault[gcs_fps][GC_CENTERVIEW ][0] = KEY_LCTRL;
 	gamecontroldefault[gcs_fps][GC_JUMP       ][0] = KEY_SPACE;
 	gamecontroldefault[gcs_fps][GC_SPIN       ][0] = KEY_LSHIFT;
+	gamecontroldefault[gcs_fps][GC_SHIELD     ][0] = KEY_LALT;
 	gamecontroldefault[gcs_fps][GC_FIRE       ][0] = KEY_RCTRL;
 	gamecontroldefault[gcs_fps][GC_FIRE       ][1] = KEY_MOUSE1+0;
 	gamecontroldefault[gcs_fps][GC_FIRENORMAL ][0] = KEY_RALT;
@@ -713,6 +712,7 @@ void G_DefineDefaultControls(void)
 	gamecontroldefault[gcs_platform][GC_CENTERVIEW ][0] = KEY_END;
 	gamecontroldefault[gcs_platform][GC_JUMP       ][0] = KEY_SPACE;
 	gamecontroldefault[gcs_platform][GC_SPIN       ][0] = KEY_LSHIFT;
+	gamecontroldefault[gcs_platform][GC_SHIELD     ][0] = KEY_LALT;
 	gamecontroldefault[gcs_platform][GC_FIRE       ][0] = 's';
 	gamecontroldefault[gcs_platform][GC_FIRE       ][1] = KEY_MOUSE1+0;
 	gamecontroldefault[gcs_platform][GC_FIRENORMAL ][0] = 'w';
@@ -728,9 +728,6 @@ void G_DefineDefaultControls(void)
 		gamecontroldefault[i][GC_WEPSLOT5     ][0] = '5';
 		gamecontroldefault[i][GC_WEPSLOT6     ][0] = '6';
 		gamecontroldefault[i][GC_WEPSLOT7     ][0] = '7';
-		gamecontroldefault[i][GC_WEPSLOT8     ][0] = '8';
-		gamecontroldefault[i][GC_WEPSLOT9     ][0] = '9';
-		gamecontroldefault[i][GC_WEPSLOT10    ][0] = '0';
 		gamecontroldefault[i][GC_TOSSFLAG     ][0] = '\'';
 		gamecontroldefault[i][GC_CAMTOGGLE    ][0] = 'v';
 		gamecontroldefault[i][GC_CAMRESET     ][0] = 'r';
@@ -749,15 +746,15 @@ void G_DefineDefaultControls(void)
 		gamecontroldefault[i][GC_CUSTOM1      ][1] = KEY_JOY1+1; // B
 		gamecontroldefault[i][GC_CUSTOM2      ][1] = KEY_JOY1+3; // Y
 		gamecontroldefault[i][GC_CUSTOM3      ][1] = KEY_JOY1+8; // Left Stick
-		gamecontroldefault[i][GC_CAMTOGGLE    ][1] = KEY_JOY1+4; // LB
+		gamecontroldefault[i][GC_SHIELD       ][1] = KEY_JOY1+4; // LB
 		gamecontroldefault[i][GC_CENTERVIEW   ][1] = KEY_JOY1+5; // RB
-		gamecontroldefault[i][GC_SCREENSHOT   ][1] = KEY_JOY1+6; // Back
+		gamecontroldefault[i][GC_SCORES       ][1] = KEY_JOY1+6; // Back
 		gamecontroldefault[i][GC_SYSTEMMENU   ][0] = KEY_JOY1+7; // Start
 		gamecontroldefault[i][GC_WEAPONPREV   ][1] = KEY_HAT1+2; // D-Pad Left
 		gamecontroldefault[i][GC_WEAPONNEXT   ][1] = KEY_HAT1+3; // D-Pad Right
 		gamecontroldefault[i][GC_VIEWPOINTNEXT][1] = KEY_JOY1+9; // Right Stick
 		gamecontroldefault[i][GC_TOSSFLAG     ][1] = KEY_HAT1+0; // D-Pad Up
-		gamecontroldefault[i][GC_SCORES       ][1] = KEY_HAT1+1; // D-Pad Down
+		gamecontroldefault[i][GC_CAMTOGGLE    ][1] = KEY_HAT1+1; // D-Pad Down
 
 		// Second player controls only have joypad defaults
 		gamecontrolbisdefault[i][GC_JUMP         ][1] = KEY_2JOY1+0; // A
@@ -765,15 +762,15 @@ void G_DefineDefaultControls(void)
 		gamecontrolbisdefault[i][GC_CUSTOM1      ][1] = KEY_2JOY1+1; // B
 		gamecontrolbisdefault[i][GC_CUSTOM2      ][1] = KEY_2JOY1+3; // Y
 		gamecontrolbisdefault[i][GC_CUSTOM3      ][1] = KEY_2JOY1+8; // Left Stick
-		gamecontrolbisdefault[i][GC_CAMTOGGLE    ][1] = KEY_2JOY1+4; // LB
+		gamecontrolbisdefault[i][GC_SHIELD       ][1] = KEY_2JOY1+4; // LB
 		gamecontrolbisdefault[i][GC_CENTERVIEW   ][1] = KEY_2JOY1+5; // RB
-		gamecontrolbisdefault[i][GC_SCREENSHOT   ][1] = KEY_2JOY1+6; // Back
+		//gamecontrolbisdefault[i][GC_SCORES       ][1] = KEY_2JOY1+6; // Back
 		//gamecontrolbisdefault[i][GC_SYSTEMMENU   ][0] = KEY_2JOY1+7; // Start
 		gamecontrolbisdefault[i][GC_WEAPONPREV   ][1] = KEY_2HAT1+2; // D-Pad Left
 		gamecontrolbisdefault[i][GC_WEAPONNEXT   ][1] = KEY_2HAT1+3; // D-Pad Right
 		gamecontrolbisdefault[i][GC_VIEWPOINTNEXT][1] = KEY_2JOY1+9; // Right Stick
 		gamecontrolbisdefault[i][GC_TOSSFLAG     ][1] = KEY_2HAT1+0; // D-Pad Up
-		//gamecontrolbisdefault[i][GC_SCORES       ][1] = KEY_2HAT1+1; // D-Pad Down
+		gamecontrolbisdefault[i][GC_CAMTOGGLE    ][1] = KEY_2HAT1+1; // D-Pad Down
 	}
 }
 
@@ -1004,6 +1001,7 @@ static void setcontrol(INT32 (*gc)[2])
 
 	// TODO: 2.3: Delete the "use" alias
 	namectrl = (stricmp(COM_Argv(1), "use")) ? COM_Argv(1) : "spin";
+		
 
 	for (numctrl = 0; numctrl < NUM_GAMECONTROLS && stricmp(namectrl, gamecontrolname[numctrl]);
 		numctrl++)
diff --git a/src/g_input.h b/src/g_input.h
index e9c909e6e..48c103076 100644
--- a/src/g_input.h
+++ b/src/g_input.h
@@ -74,9 +74,7 @@ typedef enum
 	GC_WEPSLOT5,
 	GC_WEPSLOT6,
 	GC_WEPSLOT7,
-	GC_WEPSLOT8,
-	GC_WEPSLOT9,
-	GC_WEPSLOT10,
+	GC_SHIELD,
 	GC_FIRE,
 	GC_FIRENORMAL,
 	GC_TOSSFLAG,
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 16f238abe..bdc5c4501 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1686,11 +1686,12 @@ static int lib_pHomingAttack(lua_State *L)
 static int lib_pSuperReady(lua_State *L)
 {
 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	boolean transform = (boolean)lua_opttrueboolean(L, 2);
 	//HUDSAFE
 	INLEVEL
 	if (!player)
 		return LUA_ErrInvalid(L, "player_t");
-	lua_pushboolean(L, P_SuperReady(player));
+	lua_pushboolean(L, P_SuperReady(player, transform));
 	return 1;
 }
 
diff --git a/src/m_menu.c b/src/m_menu.c
index 629f53d24..45e74c0fc 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -1068,6 +1068,7 @@ static menuitem_t OP_ChangeControlsMenu[] =
 	{IT_CALL | IT_STRING2, NULL, "Move Right",       M_ChangeControl, GC_STRAFERIGHT },
 	{IT_CALL | IT_STRING2, NULL, "Jump",             M_ChangeControl, GC_JUMP      },
 	{IT_CALL | IT_STRING2, NULL, "Spin",             M_ChangeControl, GC_SPIN     },
+	{IT_CALL | IT_STRING2, NULL, "Shield",           M_ChangeControl, GC_SHIELD    },
 	{IT_HEADER, NULL, "Camera", NULL, 0},
 	{IT_SPACE, NULL, NULL, NULL, 0}, // padding
 	{IT_CALL | IT_STRING2, NULL, "Look Up",        M_ChangeControl, GC_LOOKUP      },
@@ -13167,23 +13168,23 @@ static void M_Setup1PControlsMenu(INT32 choice)
 	currentMenu->lastOn = itemOn;
 
 	// Unhide the nine non-P2 controls and their headers
-	//OP_ChangeControlsMenu[18+0].status = IT_HEADER;
-	//OP_ChangeControlsMenu[18+1].status = IT_SPACE;
+	//OP_ChangeControlsMenu[19+0].status = IT_HEADER;
+	//OP_ChangeControlsMenu[19+1].status = IT_SPACE;
 	// ...
-	OP_ChangeControlsMenu[18+2].status = IT_CALL|IT_STRING2;
-	OP_ChangeControlsMenu[18+3].status = IT_CALL|IT_STRING2;
-	OP_ChangeControlsMenu[18+4].status = IT_CALL|IT_STRING2;
-	OP_ChangeControlsMenu[18+5].status = IT_CALL|IT_STRING2;
-	OP_ChangeControlsMenu[18+6].status = IT_CALL|IT_STRING2;
-	//OP_ChangeControlsMenu[18+7].status = IT_CALL|IT_STRING2;
-	//OP_ChangeControlsMenu[18+8].status = IT_CALL|IT_STRING2;
-	OP_ChangeControlsMenu[18+9].status = IT_CALL|IT_STRING2;
+	OP_ChangeControlsMenu[19+2].status = IT_CALL|IT_STRING2;
+	OP_ChangeControlsMenu[19+3].status = IT_CALL|IT_STRING2;
+	OP_ChangeControlsMenu[19+4].status = IT_CALL|IT_STRING2;
+	OP_ChangeControlsMenu[19+5].status = IT_CALL|IT_STRING2;
+	OP_ChangeControlsMenu[19+6].status = IT_CALL|IT_STRING2;
+	//OP_ChangeControlsMenu[19+7].status = IT_CALL|IT_STRING2;
+	//OP_ChangeControlsMenu[19+8].status = IT_CALL|IT_STRING2;
+	OP_ChangeControlsMenu[19+9].status = IT_CALL|IT_STRING2;
 	// ...
-	OP_ChangeControlsMenu[28+0].status = IT_HEADER;
-	OP_ChangeControlsMenu[28+1].status = IT_SPACE;
+	OP_ChangeControlsMenu[29+0].status = IT_HEADER;
+	OP_ChangeControlsMenu[29+1].status = IT_SPACE;
 	// ...
-	OP_ChangeControlsMenu[28+2].status = IT_CALL|IT_STRING2;
-	OP_ChangeControlsMenu[28+3].status = IT_CALL|IT_STRING2;
+	OP_ChangeControlsMenu[29+2].status = IT_CALL|IT_STRING2;
+	OP_ChangeControlsMenu[29+3].status = IT_CALL|IT_STRING2;
 
 	OP_ChangeControlsDef.prevMenu = &OP_P1ControlsDef;
 	OP_ChangeControlsDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); // remove second level
@@ -13199,23 +13200,23 @@ static void M_Setup2PControlsMenu(INT32 choice)
 	currentMenu->lastOn = itemOn;
 
 	// Hide the nine non-P2 controls and their headers
-	//OP_ChangeControlsMenu[18+0].status = IT_GRAYEDOUT2;
-	//OP_ChangeControlsMenu[18+1].status = IT_GRAYEDOUT2;
+	//OP_ChangeControlsMenu[19+0].status = IT_GRAYEDOUT2;
+	//OP_ChangeControlsMenu[19+1].status = IT_GRAYEDOUT2;
 	// ...
-	OP_ChangeControlsMenu[18+2].status = IT_GRAYEDOUT2;
-	OP_ChangeControlsMenu[18+3].status = IT_GRAYEDOUT2;
-	OP_ChangeControlsMenu[18+4].status = IT_GRAYEDOUT2;
-	OP_ChangeControlsMenu[18+5].status = IT_GRAYEDOUT2;
-	OP_ChangeControlsMenu[18+6].status = IT_GRAYEDOUT2;
-	//OP_ChangeControlsMenu[18+7].status = IT_GRAYEDOUT2;
-	//OP_ChangeControlsMenu[18+8].status = IT_GRAYEDOUT2;
-	OP_ChangeControlsMenu[18+9].status = IT_GRAYEDOUT2;
+	OP_ChangeControlsMenu[19+2].status = IT_GRAYEDOUT2;
+	OP_ChangeControlsMenu[19+3].status = IT_GRAYEDOUT2;
+	OP_ChangeControlsMenu[19+4].status = IT_GRAYEDOUT2;
+	OP_ChangeControlsMenu[19+5].status = IT_GRAYEDOUT2;
+	OP_ChangeControlsMenu[19+6].status = IT_GRAYEDOUT2;
+	//OP_ChangeControlsMenu[19+7].status = IT_GRAYEDOUT2;
+	//OP_ChangeControlsMenu[19+8].status = IT_GRAYEDOUT2;
+	OP_ChangeControlsMenu[19+9].status = IT_GRAYEDOUT2;
 	// ...
-	OP_ChangeControlsMenu[28+0].status = IT_GRAYEDOUT2;
-	OP_ChangeControlsMenu[28+1].status = IT_GRAYEDOUT2;
+	OP_ChangeControlsMenu[29+0].status = IT_GRAYEDOUT2;
+	OP_ChangeControlsMenu[29+1].status = IT_GRAYEDOUT2;
 	// ...
-	OP_ChangeControlsMenu[28+2].status = IT_GRAYEDOUT2;
-	OP_ChangeControlsMenu[28+3].status = IT_GRAYEDOUT2;
+	OP_ChangeControlsMenu[29+2].status = IT_GRAYEDOUT2;
+	OP_ChangeControlsMenu[29+3].status = IT_GRAYEDOUT2;
 
 	OP_ChangeControlsDef.prevMenu = &OP_P2ControlsDef;
 	OP_ChangeControlsDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); // remove second level
diff --git a/src/p_local.h b/src/p_local.h
index c26c09860..1f1548b86 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -202,7 +202,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet);
 void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius);
 void P_Earthquake(mobj_t *inflictor, mobj_t *source, fixed_t radius);
 boolean P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user
-boolean P_SuperReady(player_t *player);
+boolean P_SuperReady(player_t *player, boolean transform);
 void P_DoJump(player_t *player, boolean soundandstate);
 void P_DoSpinDashDust(player_t *player);
 #define P_AnalogMove(player) (P_ControlStyle(player) == CS_LMAOGALOG)
diff --git a/src/p_user.c b/src/p_user.c
index c0e87bf73..a856d2592 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -666,7 +666,7 @@ static void P_DeNightserizePlayer(player_t *player)
 	player->powers[pw_carry] = CR_NIGHTSFALL;
 
 	player->powers[pw_underwater] = 0;
-	player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING|PF_DRILLING|PF_TRANSFERTOCLOSEST);
+	player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_SHIELDDOWN|PF_STARTDASH|PF_GLIDING|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING|PF_DRILLING|PF_TRANSFERTOCLOSEST);
 	player->secondjump = 0;
 	player->homing = 0;
 	player->climbing = 0;
@@ -794,7 +794,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 		}
 	}
 
-	player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY|PF_SPINNING|PF_DRILLING);
+	player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_SHIELDDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY|PF_SPINNING|PF_DRILLING);
 	player->homing = 0;
 	player->mo->fuse = 0;
 	player->speed = 0;
@@ -1342,8 +1342,6 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
 	if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC) && P_IsLocalPlayer(player))
 		P_PlayJingle(player, JT_SUPER);
 
-	S_StartSound(NULL, sfx_supert); //let all players hear it -mattw_cfi
-
 	player->mo->momx = player->mo->momy = player->mo->momz = player->cmomx = player->cmomy = player->rmomx = player->rmomy = 0;
 
 	// Transformation animation
@@ -1360,8 +1358,11 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
 		player->powers[pw_sneakers] = 0;
 	}
 
-	if (!G_CoopGametype())
+	if (G_CoopGametype())
+		S_StartSound(player->mo, sfx_supert); //only hear it near yourself in co-op
+	else
 	{
+		S_StartSound(NULL, sfx_supert); //let all players hear it -mattw_cfi
 		HU_SetCEchoFlags(0);
 		HU_SetCEchoDuration(5);
 		HU_DoCEcho(va("%s\\is now super.\\\\\\\\", player_names[player-players]));
@@ -1370,6 +1371,56 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
 	P_PlayerFlagBurst(player, false);
 }
 
+//
+// P_DoSuperDetransformation
+//
+// Detransform into regular Sonic!
+static void P_DoSuperDetransformation(player_t *player)
+{
+	player->powers[pw_emeralds] = 0; // lost the power stones
+	P_SpawnGhostMobj(player->mo);
+
+	player->powers[pw_super] = 0;
+
+	// Restore color
+	if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER)
+	{
+		player->mo->color = SKINCOLOR_WHITE;
+		G_GhostAddColor(GHC_FIREFLOWER);
+	}
+	else
+	{
+		player->mo->color = P_GetPlayerColor(player);
+		G_GhostAddColor(GHC_NORMAL);
+	}
+
+	if (!G_CoopGametype())
+		player->powers[pw_flashing] = flashingtics-1;
+
+	if (player->mo->sprite2 & FF_SPR2SUPER)
+		P_SetPlayerMobjState(player->mo, player->mo->state-states);
+
+	// Inform the netgame that the champion has fallen in the heat of battle.
+	if (!G_CoopGametype())
+	{
+		S_StartSound(NULL, sfx_s3k66); //let all players hear it.
+		HU_SetCEchoFlags(0);
+		HU_SetCEchoDuration(5);
+		HU_DoCEcho(va("%s\\is no longer super.\\\\\\\\", player_names[player-players]));
+	}
+
+	// Resume normal music if you're the console player
+	if (P_IsLocalPlayer(player))
+	{
+		music_stack_noposition = true; // HACK: Do not reposition next music
+		music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music
+	}
+	P_RestoreMusic(player);
+
+	// If you had a shield, restore its visual significance.
+	P_SpawnShieldOrb(player);
+}
+
 // Adds to the player's score
 void P_AddPlayerScore(player_t *player, UINT32 amount)
 {
@@ -4086,6 +4137,16 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
 
 	I_Assert(player != NULL);
 	I_Assert(!P_MobjWasRemoved(player->mo));
+	
+	// Toss a flag
+	if (cmd->buttons & BT_TOSSFLAG && G_GametypeHasTeams()
+		&& !(player->powers[pw_super]) && !(player->tossdelay))
+	{
+		if (!(player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
+			P_PlayerEmeraldBurst(player, true); // Toss emeralds
+		else
+			P_PlayerFlagBurst(player, true);
+	}
 
 	if (!(cmd->buttons & (BT_ATTACK|BT_FIRENORMAL)))
 	{
@@ -4095,9 +4156,10 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
 		return;
 	}
 
-	if (player->pflags & PF_ATTACKDOWN || player->climbing || (G_TagGametype() && !(player->pflags & PF_TAGIT)))
+	if (player->pflags & PF_ATTACKDOWN || player->climbing)
 		return;
 
+	// Fire a fireball if we have the Fire Flower powerup!
 	if (((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) && !(player->weapondelay))
 	{
 		player->pflags |= PF_ATTACKDOWN;
@@ -4109,7 +4171,8 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
 		return;
 	}
 
-	if (!G_RingSlingerGametype() || player->weapondelay)
+	// No ringslinging outside of ringslinger!
+	if (!G_RingSlingerGametype() || player->weapondelay || (G_TagGametype() && !(player->pflags & PF_TAGIT)))
 		return;
 
 	player->pflags |= PF_ATTACKDOWN;
@@ -4287,34 +4350,7 @@ static void P_DoSuperStuff(player_t *player)
 		// If you're super and not Sonic, de-superize!
 		if (!(ALL7EMERALDS(emeralds) && player->charflags & SF_SUPER))
 		{
-			player->powers[pw_super] = 0;
-			P_SetPlayerMobjState(player->mo, S_PLAY_STND);
-			if (P_IsLocalPlayer(player))
-			{
-				music_stack_noposition = true; // HACK: Do not reposition next music
-				music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music
-			}
-			P_RestoreMusic(player);
-			P_SpawnShieldOrb(player);
-
-			// Restore color
-			if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER)
-			{
-				player->mo->color = SKINCOLOR_WHITE;
-				G_GhostAddColor(GHC_FIREFLOWER);
-			}
-			else
-			{
-				player->mo->color = P_GetPlayerColor(player);
-				G_GhostAddColor(GHC_NORMAL);
-			}
-
-			if (!G_CoopGametype())
-			{
-				HU_SetCEchoFlags(0);
-				HU_SetCEchoDuration(5);
-				HU_DoCEcho(va("%s\\is no longer super.\\\\\\\\", player_names[player-players]));
-			}
+			P_DoSuperDetransformation(player);
 			return;
 		}
 
@@ -4341,69 +4377,37 @@ static void P_DoSuperStuff(player_t *player)
 
 		// Ran out of rings while super!
 		if (player->rings <= 0 || player->exiting)
-		{
-			player->powers[pw_emeralds] = 0; // lost the power stones
-			P_SpawnGhostMobj(player->mo);
-
-			player->powers[pw_super] = 0;
-
-			// Restore color
-			if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER)
-			{
-				player->mo->color = SKINCOLOR_WHITE;
-				G_GhostAddColor(GHC_FIREFLOWER);
-			}
-			else
-			{
-				player->mo->color = P_GetPlayerColor(player);
-				G_GhostAddColor(GHC_NORMAL);
-			}
-
-			if (!G_CoopGametype())
-				player->powers[pw_flashing] = flashingtics-1;
-
-			if (player->mo->sprite2 & FF_SPR2SUPER)
-				P_SetPlayerMobjState(player->mo, player->mo->state-states);
-
-			// Inform the netgame that the champion has fallen in the heat of battle.
-			if (!G_CoopGametype())
-			{
-				S_StartSound(NULL, sfx_s3k66); //let all players hear it.
-				HU_SetCEchoFlags(0);
-				HU_SetCEchoDuration(5);
-				HU_DoCEcho(va("%s\\is no longer super.\\\\\\\\", player_names[player-players]));
-			}
-
-			// Resume normal music if you're the console player
-			if (P_IsLocalPlayer(player))
-			{
-				music_stack_noposition = true; // HACK: Do not reposition next music
-				music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music
-			}
-			P_RestoreMusic(player);
-
-			// If you had a shield, restore its visual significance.
-			P_SpawnShieldOrb(player);
-		}
+			P_DoSuperDetransformation(player);
 	}
 }
 
 //
 // P_SuperReady
 //
-// Returns true if player is ready to turn super, duh
+// Returns true if player is ready to transform or detransform
 //
-boolean P_SuperReady(player_t *player)
+boolean P_SuperReady(player_t *player, boolean transform)
 {
-	if (!player->powers[pw_super]
-	&& !player->powers[pw_invulnerability]
+	if (!transform &&
+	(player->powers[pw_super] < TICRATE*3/2
+	|| !G_CoopGametype())) // No turning back in competitive!
+		return false;
+	else if (transform
+	&& (player->powers[pw_super]
+	|| !ALL7EMERALDS(emeralds)
+	|| !(player->rings >= 50)))
+		return false;
+	
+	if (player->mo
 	&& !player->powers[pw_tailsfly]
+	&& !player->powers[pw_carry]
 	&& (player->charflags & SF_SUPER)
-	&& (player->pflags & PF_JUMPED)
-	&& !(player->powers[pw_shield] & SH_NOSTACK)
-	&& !(maptol & TOL_NIGHTS)
-	&& ALL7EMERALDS(emeralds)
-	&& (player->rings >= 50))
+	&& !P_PlayerInPain(player)
+	&& !player->climbing
+	&& !(player->pflags & (PF_FULLSTASIS|PF_THOKKED|PF_STARTDASH|PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY))
+	&& ((player->pflags & PF_JUMPED) || (P_IsObjectOnGround(player->mo) && (player->panim == PA_IDLE || player->panim == PA_EDGE
+	|| player->panim == PA_WALK || player->panim == PA_RUN || (player->charflags & SF_DASHMODE && player->panim == PA_DASH))))
+	&& !(maptol & TOL_NIGHTS))
 		return true;
 
 	return false;
@@ -5094,7 +5098,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
 {
 	mobj_t *lockonshield = NULL;
 
-	if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_SPINDOWN)
+	if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_SHIELDDOWN)
 		&& ((!(player->pflags & PF_THOKKED) || (((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP || (player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) && player->secondjump == UINT8_MAX) ))) // thokked is optional if you're bubblewrapped / 3dblasted
 	{
 		if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT && !(player->charflags & SF_NOSHIELDABILITY))
@@ -5121,7 +5125,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
 				}
 			}
 		}
-		if ((!(player->charflags & SF_NOSHIELDABILITY)) && (cmd->buttons & BT_SPIN && !LUA_HookPlayer(player, HOOK(ShieldSpecial)))) // Spin button effects
+		if ((!(player->charflags & SF_NOSHIELDABILITY)) && (cmd->buttons & BT_SHIELD && !LUA_HookPlayer(player, HOOK(ShieldSpecial)))) // Shield button effects
 		{
 			// Force stop
 			if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE)
@@ -5242,52 +5246,45 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 			;
 		else if (P_PlayerShieldThink(player, cmd, lockonthok, visual))
 			;
-		else if ((cmd->buttons & BT_SPIN))
+		else if ((cmd->buttons & BT_SPIN) && !LUA_HookPlayer(player, HOOK(JumpSpinSpecial)))
 		{
-			if (!(player->pflags & PF_SPINDOWN) && P_SuperReady(player))
+			switch (player->charability)
 			{
-				// If you can turn super and aren't already,
-				// and you don't have a shield, do it!
-				P_DoSuperTransformation(player, false);
-			}
-			else if (!LUA_HookPlayer(player, HOOK(JumpSpinSpecial)))
-				switch (player->charability)
-				{
-					case CA_THOK:
-						if (player->powers[pw_super]) // Super Sonic float
+				case CA_THOK:
+					if (player->powers[pw_super]) // Super Sonic float
+					{
+						if ((player->speed > 5*player->mo->scale) // FixedMul(5<<FRACBITS, player->mo->scale), but scale is FRACUNIT-based
+						&& (P_MobjFlip(player->mo)*player->mo->momz <= 0))
 						{
-							if ((player->speed > 5*player->mo->scale) // FixedMul(5<<FRACBITS, player->mo->scale), but scale is FRACUNIT-based
-							&& (P_MobjFlip(player->mo)*player->mo->momz <= 0))
+							if (player->panim != PA_RUN && player->panim != PA_WALK)
 							{
-								if (player->panim != PA_RUN && player->panim != PA_WALK)
-								{
-									if (player->speed >= FixedMul(player->runspeed, player->mo->scale))
-										P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT_RUN);
-									else
-										P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT);
-								}
-
-								player->mo->momz = 0;
-								player->pflags &= ~(PF_STARTJUMP|PF_SPINNING);
-								player->secondjump = 1;
+								if (player->speed >= FixedMul(player->runspeed, player->mo->scale))
+									P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT_RUN);
+								else
+									P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT);
 							}
+
+							player->mo->momz = 0;
+							player->pflags &= ~(PF_STARTJUMP|PF_SPINNING);
+							player->secondjump = 1;
 						}
-						break;
-					case CA_TELEKINESIS:
-						if (!(player->pflags & (PF_THOKKED|PF_SPINDOWN)) || (player->charflags & SF_MULTIABILITY))
-						{
-							P_Telekinesis(player,
-								-FixedMul(player->actionspd, player->mo->scale), // -ve thrust (pulling towards player)
-								FixedMul(384*FRACUNIT, player->mo->scale));
-						}
-						break;
-					case CA_TWINSPIN:
-						if ((player->charability2 == CA2_MELEE) && (!(player->pflags & (PF_THOKKED|PF_SPINDOWN)) || player->charflags & SF_MULTIABILITY))
-							P_DoTwinSpin(player);
-						break;
-					default:
-						break;
-				}
+					}
+					break;
+				case CA_TELEKINESIS:
+					if (!(player->pflags & (PF_THOKKED|PF_SPINDOWN)) || (player->charflags & SF_MULTIABILITY))
+					{
+						P_Telekinesis(player,
+							-FixedMul(player->actionspd, player->mo->scale), // -ve thrust (pulling towards player)
+							FixedMul(384*FRACUNIT, player->mo->scale));
+					}
+					break;
+				case CA_TWINSPIN:
+					if ((player->charability2 == CA2_MELEE) && (!(player->pflags & (PF_THOKKED|PF_SPINDOWN)) || player->charflags & SF_MULTIABILITY))
+						P_DoTwinSpin(player);
+					break;
+				default:
+					break;
+			}
 		}
 	}
 
@@ -5350,12 +5347,6 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 		}
 		else if (player->pflags & PF_SLIDING || ((gametyperules & GTR_TEAMFLAGS) && player->gotflag) || player->pflags & PF_SHIELDABILITY)
 			;
-		/*else if (P_SuperReady(player))
-		{
-			// If you can turn super and aren't already,
-			// and you don't have a shield, do it!
-			P_DoSuperTransformation(player, false);
-		}*/
 		else if (player->pflags & PF_JUMPED)
 		{
 			if (!LUA_HookPlayer(player, HOOK(AbilitySpecial)))
@@ -8686,18 +8677,31 @@ void P_MovePlayer(player_t *player)
 	&& player->panim == PA_IDLE && !(player->powers[pw_carry]))
 		P_DoTeeter(player);
 
-	// Toss a flag
-	if (G_GametypeHasTeams() && (cmd->buttons & BT_TOSSFLAG) && !(player->powers[pw_super]) && !(player->tossdelay))
+	// Check for fire and shield buttons
+	if (!player->exiting && !(player->pflags & PF_STASIS))
 	{
-		if (!(player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
-			P_PlayerEmeraldBurst(player, true); // Toss emeralds
-		else
-			P_PlayerFlagBurst(player, true);
-	}
-
-	// check for fire
-	if (!player->exiting)
+		// Check for fire buttons
 		P_DoFiring(player, cmd);
+		
+		// Release the shield button
+		if (!(cmd->buttons & BT_SHIELD))
+			player->pflags &= ~PF_SHIELDDOWN;
+		
+		// Shield button behavior
+		// Check P_PlayerShieldThink for actual shields!
+		else if (!(player->pflags & PF_SHIELDDOWN))
+		{
+			// Transform into super if we can!
+			if (P_SuperReady(player, true))
+				P_DoSuperTransformation(player, false);
+			
+			// Detransform from super if we can!
+			else if (P_SuperReady(player, false))
+				P_DoSuperDetransformation(player);
+			
+			player->pflags |= PF_SHIELDDOWN;
+		}
+	}
 
 	{
 		boolean atspinheight = false;
diff --git a/src/y_inter.c b/src/y_inter.c
index 369ec3904..cbe057582 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -579,9 +579,9 @@ void Y_IntermissionDrawer(void)
 		{
 			if (LUA_HudEnabled(hud_intermissiontitletext))
 			{
-				const char *ringtext = "\x82" "50 rings, no shield";
-				const char *tut1text = "\x82" "press " "\x80" "spin";
-				const char *tut2text = "\x82" "mid-" "\x80" "jump";
+				const char *ringtext = "\x82" "get 50 rings then";
+				const char *tut1text = "\x82" "press " "\x80" "shield";
+				const char *tut2text = "\x82" "to " "\x80" "transform";
 				ttheight = 8;
 				V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1);
 				ttheight += V_LevelNameHeight(data.spec.passed3) + 2;

From 30e48fd555c7d91b0e43fa99034a18a16e484d3f Mon Sep 17 00:00:00 2001
From: bitten2up <575-bitten2up@users.noreply.git.do.srb2.org>
Date: Wed, 29 Nov 2023 11:19:53 +0000
Subject: [PATCH 36/36] I_SetSongTrack(int) should be I_SetSongTrack(INT32)

---
 src/dummy/i_sound.c   | 2 +-
 src/sdl/mixer_sound.c | 2 +-
 src/sdl/sdl_sound.c   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/dummy/i_sound.c b/src/dummy/i_sound.c
index ba0fc6423..436187805 100644
--- a/src/dummy/i_sound.c
+++ b/src/dummy/i_sound.c
@@ -164,7 +164,7 @@ void I_SetMusicVolume(UINT8 volume)
 	(void)volume;
 }
 
-boolean I_SetSongTrack(int track)
+boolean I_SetSongTrack(INT32 track)
 {
 	(void)track;
 	return false;
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 0a39c7f28..a2a4d3ef0 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -1441,7 +1441,7 @@ void I_SetMusicVolume(UINT8 volume)
 	Mix_VolumeMusic(get_real_volume(music_volume));
 }
 
-boolean I_SetSongTrack(int track)
+boolean I_SetSongTrack(INT32 track)
 {
 #ifdef HAVE_GME
 	// If the specified track is within the number of tracks playing, then change it
diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c
index 2ca35b954..2705261d6 100644
--- a/src/sdl/sdl_sound.c
+++ b/src/sdl/sdl_sound.c
@@ -1471,7 +1471,7 @@ void I_SetMusicVolume(UINT8 volume)
 	(void)volume;
 }
 
-boolean I_SetSongTrack(int track)
+boolean I_SetSongTrack(INT32 track)
 {
 	(void)track;
 	return false;