diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index d448aa55e847015fa953d5cf6b9401f0bc62eb83..e0548e50d420dbf59457c89c75a8cd31a233a841 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -1532,7 +1532,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 				wallVerts[3].t += (gl_frontsector->ceilingheight - worldtop) * yscale * grTex->scaleY;
 				wallVerts[2].t += (gl_frontsector->ceilingheight - worldtopslope) * yscale * grTex->scaleY;
 				wallVerts[0].t += (gl_frontsector->floorheight - worldbottom) * yscale * grTex->scaleY;
-				wallVerts[1].t += (gl_frontsector->floorheight - worldbottomslope) * yscale * yscale;
+				wallVerts[1].t += (gl_frontsector->floorheight - worldbottomslope) * yscale * grTex->scaleY;
 			} else if (gl_linedef->flags & ML_DONTPEGBOTTOM) {
 				wallVerts[3].t = wallVerts[0].t + ((worldbottom - worldtop) * yscale) * grTex->scaleY;
 				wallVerts[2].t = wallVerts[1].t + ((worldbottomslope - worldtopslope) * yscale) * grTex->scaleY;
@@ -1625,12 +1625,17 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 
 				side_t *side = &sides[rover->master->sidenum[0]];
 
+				INT16 lineflags;
+
 				if (rover->master->flags & ML_TFERLINE)
 				{
 					size_t linenum = gl_curline->linedef-gl_backsector->lines[0];
 					newline = rover->master->frontsector->lines[0] + linenum;
 					side = &sides[newline->sidenum[0]];
+					lineflags = newline->flags;
 				}
+				else
+					lineflags = gl_curline->linedef->flags;
 
 				texnum = R_GetTextureNum(side->midtexture);
 
@@ -1669,13 +1674,13 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 					// ...Oh well, anyway, Lower Unpegged now changes pegging of FOFs like in software
 					// -- Monster Iestyn 26/06/18
 					fixed_t texturevpeg = side->rowoffset + side->offsety_mid;
-					boolean attachtobottom = !!(rover->master->flags & ML_DONTPEGBOTTOM);
+					boolean attachtobottom = !!(lineflags & ML_DONTPEGBOTTOM);
 
 					grTex = HWR_GetTexture(texnum);
 					xscale = FixedToFloat(side->scalex_mid);
 					yscale = FixedToFloat(side->scaley_mid);
 
-					if (!(rover->master->flags & ML_SKEWTD)) // no skewing
+					if (!(lineflags & ML_SKEWTD)) // no skewing
 					{
 						if (attachtobottom)
 							texturevpeg -= (*rover->topheight - *rover->bottomheight) * yscale;
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 68b3332e5fc2267b571352e9d176e20dd1d8fb47..46814270078b27d3c6f40387e7297b84901d37ff 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1960,6 +1960,45 @@ static int lib_pMoveOrigin(lua_State *L)
 	return 2;
 }
 
+static int lib_pLineIsBlocking(lua_State *L)
+{
+	mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
+	line_t *line = *((line_t **)luaL_checkudata(L, 2, META_LINE));
+	NOHUD
+	INLEVEL
+	if (!mo)
+		return LUA_ErrInvalid(L, "mobj_t");
+	if (!line)
+		return LUA_ErrInvalid(L, "line_t");
+	
+	// P_LineOpening in P_LineIsBlocking sets these variables.
+	// We want to keep their old values after so that whatever
+	// map collision code uses them doesn't get messed up.
+	fixed_t oldopentop = opentop;
+	fixed_t oldopenbottom = openbottom;
+	fixed_t oldopenrange = openrange;
+	fixed_t oldlowfloor = lowfloor;
+	fixed_t oldhighceiling = highceiling;
+	pslope_t *oldopentopslope = opentopslope;
+	pslope_t *oldopenbottomslope = openbottomslope;
+	ffloor_t *oldopenfloorrover = openfloorrover;
+	ffloor_t *oldopenceilingrover = openceilingrover;
+	
+	lua_pushboolean(L, P_LineIsBlocking(mo, line));
+	
+	opentop = oldopentop;
+	openbottom = oldopenbottom;
+	openrange = oldopenrange;
+	lowfloor = oldlowfloor;
+	highceiling = oldhighceiling;
+	opentopslope = oldopentopslope;
+	openbottomslope = oldopenbottomslope;
+	openfloorrover = oldopenfloorrover;
+	openceilingrover = oldopenceilingrover;
+	
+	return 1;
+}
+
 static int lib_pSlideMove(lua_State *L)
 {
 	mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@@ -4454,6 +4493,7 @@ static luaL_Reg lib[] = {
 	{"P_TeleportMove",lib_pTeleportMove},
 	{"P_SetOrigin",lib_pSetOrigin},
 	{"P_MoveOrigin",lib_pMoveOrigin},
+	{"P_LineIsBlocking",lib_pLineIsBlocking},
 	{"P_SlideMove",lib_pSlideMove},
 	{"P_BounceMove",lib_pBounceMove},
 	{"P_CheckSight", lib_pCheckSight},
diff --git a/src/lua_colorlib.c b/src/lua_colorlib.c
index 1e6e82333f18f28421a651728afd42ffc6d387f5..1ef21a41c704b1f3827fc6785fbd7b67c73db252 100644
--- a/src/lua_colorlib.c
+++ b/src/lua_colorlib.c
@@ -13,10 +13,33 @@
 #include "doomdef.h"
 #include "fastcmp.h"
 #include "r_data.h"
+#include "v_video.h"
 
 #include "lua_script.h"
 #include "lua_libs.h"
 
+#define COLORLIB_USE_LOOKUP
+
+#ifdef COLORLIB_USE_LOOKUP
+	static colorlookup_t colormix_lut;
+	#define GetNearestColor(r, g, b) GetColorLUT(&colormix_lut, r, g, b)
+#else
+	#define GetNearestColor(r, g, b) NearestPaletteColor(r, g, b, pMasterPalette)
+#endif
+
+////////////////
+// Color library
+////////////////
+
+static int lib_colorPaletteToRgb(lua_State *L)
+{
+	RGBA_t color = V_GetMasterColor((UINT8)luaL_checkinteger(L, 1));
+	lua_pushinteger(L, color.s.red);
+	lua_pushinteger(L, color.s.green);
+	lua_pushinteger(L, color.s.blue);
+	return 3;
+}
+
 #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])
 
@@ -32,6 +55,13 @@ static UINT32 hex2int(char x)
 	return 0;
 }
 
+static UINT8 GetHTMLColorLength(const char *str)
+{
+	if (str[0] == '#')
+		str++;
+	return strlen(str) >= 8 ? 4 : 3;
+}
+
 static UINT8 ParseHTMLColor(const char *str, UINT8 *rgba, size_t numc)
 {
 	const char *hex = str;
@@ -77,6 +107,302 @@ static UINT8 ParseHTMLColor(const char *str, UINT8 *rgba, size_t numc)
 	return 0;
 }
 
+static UINT8 GetPackedRGBA(UINT32 colors, UINT8 *rgba)
+{
+	if (colors > 0xFFFFFF)
+	{
+		rgba[0] = (colors >> 24) & 0xFF;
+		rgba[1] = (colors >> 16) & 0xFF;
+		rgba[2] = (colors >> 8) & 0xFF;
+		rgba[3] = colors & 0xFF;
+		return 4;
+	}
+	else
+	{
+		rgba[0] = (colors >> 16) & 0xFF;
+		rgba[1] = (colors >> 8) & 0xFF;
+		rgba[2] = colors & 0xFF;
+		rgba[3] = 0xFF;
+		return 3;
+	}
+}
+
+static UINT8 GetArgsRGBA(lua_State *L, UINT8 index, INT32 *r, INT32 *g, INT32 *b, INT32 *a)
+{
+	UINT8 rgba[4] = { 0, 0, 0, 255 };
+	UINT8 num = 0;
+
+	if (lua_gettop(L) == 1 && lua_type(L, index) == LUA_TNUMBER)
+	{
+		num = GetPackedRGBA(luaL_checkinteger(L, 1), rgba);
+
+		*r = rgba[0];
+		*g = rgba[1];
+		*b = rgba[2];
+		if (a)
+			*a = rgba[3];
+	}
+	else if (lua_type(L, index) == LUA_TSTRING)
+	{
+		const char *str = lua_tostring(L, index);
+		UINT8 parsed = ParseHTMLColor(str, rgba, GetHTMLColorLength(str));
+		if (!parsed)
+			luaL_error(L, "Malformed HTML color '%s'", str);
+
+		num = parsed == 8 ? 4 : 3;
+
+		*r = rgba[0];
+		*g = rgba[1];
+		*b = rgba[2];
+		if (a)
+			*a = rgba[3];
+	}
+	else
+	{
+		INT32 temp;
+
+#define CHECKINT(i) luaL_checkinteger(L, i)
+#define GETCOLOR(c, i, desc) { \
+			temp = CHECKINT(i); \
+			if (temp < 0 || temp > 255) \
+				luaL_error(L, desc " channel %d out of range (0 - 255)", temp); \
+			c = temp; \
+			num++; \
+		}
+
+		GETCOLOR(*r, index + 0, "red color");
+		GETCOLOR(*g, index + 1, "green color");
+		GETCOLOR(*b, index + 2, "blue color");
+#undef CHECKINT
+#define CHECKINT(i) luaL_optinteger(L, i, 255)
+		if (a)
+			GETCOLOR(*a, index + 3, "alpha");
+#undef CHECKINT
+#undef GETCOLOR
+
+		num = 3 + (lua_type(L, index + 3) == LUA_TNUMBER);
+	}
+
+	return num;
+}
+
+static int lib_colorRgbToPalette(lua_State *L)
+{
+	INT32 r, g, b;
+	GetArgsRGBA(L, 1, &r, &g, &b, NULL);
+
+#ifdef COLORLIB_USE_LOOKUP
+	InitColorLUT(&colormix_lut, pMasterPalette, false);
+#endif
+
+	lua_pushinteger(L, GetNearestColor(r, g, b));
+	return 1;
+}
+
+#define SCALE_UINT8_TO_FIXED(val) FixedDiv(val * FRACUNIT, 255 * FRACUNIT)
+#define SCALE_FIXED_TO_UINT8(val) FixedRound(FixedMul(val, 255 * FRACUNIT)) / FRACUNIT
+
+static fixed_t hue2rgb(fixed_t p, fixed_t q, fixed_t t)
+{
+	if (t < 0)
+		t += FRACUNIT;
+	if (t > FRACUNIT)
+		t -= FRACUNIT;
+
+	fixed_t out;
+
+	if (t < FRACUNIT / 6)
+		out = p + FixedMul(FixedMul(q - p, 6 * FRACUNIT), t);
+	else if (t < FRACUNIT / 2)
+		out = q;
+	else if (t < 2 * FRACUNIT / 3)
+		out = p + FixedMul(FixedMul(q - p, 2 * FRACUNIT / 3 - t), 6 * FRACUNIT);
+	else
+		out = p;
+
+	return out;
+}
+
+static int lib_colorHslToRgb(lua_State *L)
+{
+	fixed_t h, s, l;
+
+#define GETHSL(c, i, desc) \
+	c = luaL_checkinteger(L, i); \
+	if (c < 0 || c > 255) \
+		luaL_error(L, desc " %d out of range (0 - 255)", c)
+
+	GETHSL(h, 1, "hue");
+	GETHSL(s, 2, "saturation");
+	GETHSL(l, 3, "value");
+#undef GETHSL
+
+	if (!s)
+	{
+		lua_pushinteger(L, l);
+		lua_pushinteger(L, l);
+		lua_pushinteger(L, l);
+	}
+	else
+	{
+		h = SCALE_UINT8_TO_FIXED(h);
+		s = SCALE_UINT8_TO_FIXED(s);
+		l = SCALE_UINT8_TO_FIXED(l);
+
+		fixed_t q, p;
+
+		if (l < FRACUNIT/2)
+			q = FixedMul(l, FRACUNIT + s);
+		else
+			q = l + s - FixedMul(l, s);
+
+		p = l * 2 - q;
+
+		lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h + FRACUNIT/3)));
+		lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h)));
+		lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h - FRACUNIT/3)));
+	}
+
+	return 3;
+}
+
+static int lib_colorRgbToHsl(lua_State *L)
+{
+	INT32 ir, ig, ib;
+	GetArgsRGBA(L, 1, &ir, &ig, &ib, NULL);
+
+	fixed_t r = SCALE_UINT8_TO_FIXED(ir);
+	fixed_t g = SCALE_UINT8_TO_FIXED(ig);
+	fixed_t b = SCALE_UINT8_TO_FIXED(ib);
+
+	fixed_t cmin = min(min(r, g), b);
+	fixed_t cmax = max(max(r, g), b);
+
+	fixed_t h, s, l = (cmax + cmin) / 2;
+	fixed_t delta = cmax - cmin;
+
+	if (!delta)
+		h = s = 0;
+	else
+	{
+		if (l > FRACUNIT / 2)
+			s = FixedDiv(delta, (FRACUNIT * 2) - cmax - cmin);
+		else
+			s = FixedDiv(delta, cmax + cmin);
+
+		if (r > g && r > b)
+		{
+			h = FixedDiv(g - b, delta);
+
+			if (g < b)
+				h += FRACUNIT * 6;
+		}
+		else
+		{
+			h = FixedDiv(r - g, delta);
+
+			if (g > b)
+				h += FRACUNIT * 2;
+			else
+				h += FRACUNIT * 4;
+		}
+
+		h = FixedDiv(h, FRACUNIT * 6);
+	}
+
+	lua_pushinteger(L, SCALE_FIXED_TO_UINT8(h));
+	lua_pushinteger(L, SCALE_FIXED_TO_UINT8(s));
+	lua_pushinteger(L, SCALE_FIXED_TO_UINT8(l));
+
+	return 3;
+}
+
+static int lib_colorHexToRgb(lua_State *L)
+{
+	UINT8 rgba[4] = { 0, 0, 0, 255 };
+
+	const char *str = luaL_checkstring(L, 1);
+	UINT8 parsed = ParseHTMLColor(str, rgba, 4), num = 3;
+	if (!parsed)
+		luaL_error(L, "Malformed HTML color '%s'", str);
+	else if (parsed == 8)
+		num++;
+
+	lua_pushinteger(L, rgba[0]);
+	lua_pushinteger(L, rgba[1]);
+	lua_pushinteger(L, rgba[2]);
+	if (num == 4)
+		lua_pushinteger(L, rgba[3]);
+
+	return num;
+}
+
+static int lib_colorRgbToHex(lua_State *L)
+{
+	INT32 r, g, b, a;
+	UINT8 num = GetArgsRGBA(L, 1, &r, &g, &b, &a);
+
+	char buffer[10];
+	if (num >= 4)
+		snprintf(buffer, sizeof buffer, "#%02X%02X%02X%02X", r, g, b, a);
+	else
+		snprintf(buffer, sizeof buffer, "#%02X%02X%02X", r, g, b);
+
+	lua_pushstring(L, buffer);
+	return 1;
+}
+
+static int lib_colorPackRgb(lua_State *L)
+{
+	INT32 r, g, b;
+
+	GetArgsRGBA(L, 1, &r, &g, &b, NULL);
+
+	UINT32 packed = ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
+
+	lua_pushinteger(L, packed);
+	return 1;
+}
+
+static int lib_colorPackRgba(lua_State *L)
+{
+	INT32 r, g, b, a;
+
+	GetArgsRGBA(L, 1, &r, &g, &b, &a);
+
+	UINT32 packed = ((r & 0xFF) << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF);
+
+	lua_pushinteger(L, packed);
+	return 1;
+}
+
+static int lib_colorUnpackRgb(lua_State *L)
+{
+	UINT8 rgba[4];
+
+	UINT8 num = GetPackedRGBA(lua_tointeger(L, 1), rgba);
+
+	for (UINT8 i = 0; i < num; i++)
+	{
+		lua_pushinteger(L, rgba[i]);
+	}
+
+	return num;
+}
+
+static luaL_Reg color_lib[] = {
+	{"paletteToRgb", lib_colorPaletteToRgb},
+	{"rgbToPalette", lib_colorRgbToPalette},
+	{"hslToRgb", lib_colorHslToRgb},
+	{"rgbToHsl", lib_colorRgbToHsl},
+	{"hexToRgb", lib_colorHexToRgb},
+	{"rgbToHex", lib_colorRgbToHex},
+	{"packRgb", lib_colorPackRgb},
+	{"packRgba", lib_colorPackRgba},
+	{"unpackRgb", lib_colorUnpackRgb},
+	{NULL, NULL}
+};
+
 /////////////////////////
 // extracolormap userdata
 /////////////////////////
@@ -180,21 +506,7 @@ static void GetExtraColormapRGBA(lua_State *L, UINT8 *rgba, int arg)
 	}
 	else
 	{
-		UINT32 colors = lua_tointeger(L, arg);
-		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;
-		}
+		GetPackedRGBA(lua_tointeger(L, arg), rgba);
 	}
 }
 
@@ -328,5 +640,7 @@ int LUA_ColorLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
+	luaL_register(L, "color", color_lib);
+
 	return 0;
 }
diff --git a/src/lua_script.c b/src/lua_script.c
index 48291410297642234e7f60109367faa6ed062eff..ecd85f11b4427b881ed5a5c43974b759e2ea9597 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -38,6 +38,8 @@
 #include "doomstat.h"
 #include "g_state.h"
 
+#include "hu_stuff.h"
+
 lua_State *gL = NULL;
 
 // List of internal libraries to load from SRB2
@@ -432,6 +434,9 @@ int LUA_PushGlobals(lua_State *L, const char *word)
 			return 0;
 		LUA_PushUserdata(L, &camera2, META_CAMERA);
 		return 1;
+	} else if (fastcmp(word, "chatactive")) {
+		lua_pushboolean(L, chat_on);
+		return 1;
 	}
 	return 0;
 }
diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c
index 4ede33f10361980f682daf7d4cd097633db72de3..e183d8dda4a08b4c64dcc2429937cf919d743f67 100644
--- a/src/lua_skinlib.c
+++ b/src/lua_skinlib.c
@@ -54,7 +54,8 @@ enum skin {
 	skin_contspeed,
 	skin_contangle,
 	skin_soundsid,
-	skin_sprites
+	skin_sprites,
+	skin_natkcolor
 };
 
 static const char *const skin_opt[] = {
@@ -94,6 +95,7 @@ static const char *const skin_opt[] = {
 	"contangle",
 	"soundsid",
 	"sprites",
+	"natkcolor",
 	NULL};
 
 #define UNIMPLEMENTED luaL_error(L, LUA_QL("skin_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", skin_opt[field])
@@ -218,6 +220,9 @@ static int skin_get(lua_State *L)
 	case skin_sprites:
 		LUA_PushUserdata(L, skin->sprites, META_SKINSPRITES);
 		break;
+	case skin_natkcolor:
+		lua_pushinteger(L, skin->natkcolor);
+		break;
 	}
 	return 1;
 }
diff --git a/src/m_cond.c b/src/m_cond.c
index 7fea86c65700126f81134134a3ff3253065b23c8..bfb23e6723482045ed6fb22fe95aa1a464d184fc 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -475,6 +475,9 @@ UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data)
 		// that's better than making dedicated server's lives hell.
 		return false;
 	}
+	
+	if (cv_debug || devparm)
+		return false; // Unlock every level when in devmode.
 
 	if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0)
 	{
diff --git a/src/m_menu.c b/src/m_menu.c
index 48e316cc3581ae179d7e4adc00331555bb26c080..2630dcddf6ccc1fd17bdbfb5223d5ecc5355678a 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3651,9 +3651,12 @@ void M_StartControlPanel(void)
 	}
 	else if (!(netgame || multiplayer)) // Single Player
 	{
+		// Devmode unlocks Pandora's Box in the pause menu
+		boolean pandora = ((M_SecretUnlocked(SECRET_PANDORA, serverGamedata) || cv_debug || devparm) && !marathonmode);
+		
 		if (gamestate != GS_LEVEL || ultimatemode) // intermission, so gray out stuff.
 		{
-			SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA, serverGamedata)) ? (IT_GRAYEDOUT) : (IT_DISABLED);
+			SPauseMenu[spause_pandora].status = (pandora) ? (IT_GRAYEDOUT) : (IT_DISABLED);
 			SPauseMenu[spause_retry].status = IT_GRAYEDOUT;
 		}
 		else
@@ -3662,7 +3665,7 @@ void M_StartControlPanel(void)
 			if (players[consoleplayer].playerstate != PST_LIVE)
 				++numlives;
 
-			SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
+			SPauseMenu[spause_pandora].status = (pandora) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
 
 			// The list of things that can disable retrying is (was?) a little too complex
 			// for me to want to use the short if statement syntax
@@ -3673,7 +3676,11 @@ void M_StartControlPanel(void)
 		}
 
 		// We can always use level select though. :33
-		SPauseMenu[spause_levelselect].status = (maplistoption != 0) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
+		// Guarantee it if we have either it unlocked or devmode is enabled
+		if ((maplistoption != 0 || M_SecretUnlocked(SECRET_LEVELSELECT, serverGamedata) || cv_debug || devparm) && !marathonmode)
+			SPauseMenu[spause_levelselect].status = (IT_STRING | IT_CALL);
+		else
+			SPauseMenu[spause_levelselect].status = (IT_DISABLED);
 
 		// And emblem hints.
 		SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
@@ -5141,7 +5148,8 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt)
 			return false;
 
 		case LLM_LEVELSELECT:
-			if (!(mapheaderinfo[mapnum]->levelselect & maplistoption))
+			if (!(mapheaderinfo[mapnum]->levelselect & maplistoption)
+			&& !(cv_debug || devparm)) //Allow ALL levels in devmode!
 				return false;
 
 			return true;
@@ -5974,16 +5982,6 @@ static void M_DrawNightsAttackBackground(void)
 	if (ntsatkdrawtimer < 0) ntsatkdrawtimer = 0;
 }
 
-// NiGHTS Attack floating Super Sonic.
-static patch_t *ntssupersonic[2];
-static void M_DrawNightsAttackSuperSonic(void)
-{
-	const UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_YELLOW, GTC_CACHE);
-	INT32 timer = FixedInt(ntsatkdrawtimer/4) % 2;
-	angle_t fa = (FixedAngle((FixedInt(ntsatkdrawtimer * 4) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK;
-	V_DrawFixedPatch(235<<FRACBITS, (120<<FRACBITS) - (8*FINESINE(fa)), FRACUNIT, 0, ntssupersonic[timer], colormap);
-}
-
 static void M_DrawLevelPlatterMenu(void)
 {
 	UINT8 iter = lsrow, sizeselect = (lswide(lsrow) ? 1 : 0);
@@ -7702,9 +7700,12 @@ static void M_PauseLevelSelect(INT32 choice)
 	SP_PauseLevelSelectDef.prevMenu = currentMenu;
 	levellistmode = LLM_LEVELSELECT;
 
-	// maplistoption is NOT specified, so that this
+	// maplistoption is only specified if not set already
+	// and we have the level select unlocked so that it
 	// transfers the level select list from the menu
 	// used to enter the game to the pause menu.
+	if (maplistoption == 0 && M_SecretUnlocked(SECRET_LEVELSELECT, serverGamedata))
+		maplistoption = 1;
 
 	if (!M_PrepareLevelPlatter(-1, true))
 	{
@@ -10369,8 +10370,38 @@ void M_DrawNightsAttackMenu(void)
 			V_DrawString(104-72, 180, V_TRANSLUCENT, M_GetText("Press ESC to exit"));
 		}
 
-		// Super Sonic
-		M_DrawNightsAttackSuperSonic();
+		// Draw selected character's NiGHTS sprite
+		patch_t *natksprite; //The patch for the sprite itself
+		INT32 spritetimer; //Timer for animating NiGHTS sprite
+		INT32 skinnumber; //Number for skin
+		UINT16 color; //natkcolor
+
+		if (skins[cv_chooseskin.value-1]->sprites[SPR2_NFLY].numframes == 0) //If we don't have NiGHTS sprites
+			skinnumber = 0; //Default to Sonic
+		else
+			skinnumber = (cv_chooseskin.value-1);
+			
+		spritedef_t *sprdef = &skins[skinnumber]->sprites[SPR2_NFLY]; //Make our patch the selected character's NFLY sprite
+		spritetimer = FixedInt(ntsatkdrawtimer/2) % skins[skinnumber]->sprites[SPR2_NFLY].numframes; //Make the sprite timer cycle though all the frames at 2 tics per frame
+		spriteframe_t *sprframe = &sprdef->spriteframes[spritetimer]; //Our animation frame is equal to the number on the timer
+
+		natksprite = W_CachePatchNum(sprframe->lumppat[6], PU_PATCH); //Draw the right facing angle
+
+		if (skins[skinnumber]->natkcolor) //If you set natkcolor use it
+			color = skins[skinnumber]->natkcolor;
+		else if ((skins[skinnumber]->flags & SF_SUPER) && !(skins[skinnumber]->flags & SF_NONIGHTSSUPER)) //If you go super in NiGHTS, use supercolor
+			color = skins[skinnumber]->supercolor+4;
+		else //If you don't go super in NiGHTS or at all, use prefcolor
+			color = skins[skinnumber]->prefcolor;
+				
+		angle_t fa = (FixedAngle(((FixedInt(ntsatkdrawtimer * 4)) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK;
+
+		V_DrawFixedPatch(270<<FRACBITS, (186<<FRACBITS) - 8*FINESINE(fa),
+						 FixedDiv(skins[skinnumber]->highresscale, skins[skinnumber]->shieldscale), 
+						 (sprframe->flip & 1<<6) ? V_FLIP : 0,
+						 natksprite,
+						 R_GetTranslationColormap(TC_BLINK, color, GTC_CACHE));
+
 		//if (P_HasGrades(cv_nextmap.value, 0))
 		//	V_DrawScaledPatch(235 - (((ngradeletters[bestoverall])->width)*3)/2, 135, 0, ngradeletters[bestoverall]);
 
@@ -10462,9 +10493,6 @@ static void M_NightsAttack(INT32 choice)
 	// This is really just to make sure Sonic is the played character, just in case
 	M_PatchSkinNameTable();
 
-	ntssupersonic[0] = W_CachePatchName("NTSSONC1", PU_PATCH);
-	ntssupersonic[1] = W_CachePatchName("NTSSONC2", PU_PATCH);
-
 	G_SetGamestate(GS_TIMEATTACK); // do this before M_SetupNextMenu so that menu meta state knows that we're switching
 	titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please
 	M_SetupNextMenu(&SP_NightsAttackDef);
diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c
index 4c11a681f6cad6d8e470449ebea2d1985ff1da91..0c3d519be11c93c12894f52a02d3c3276a38ef20 100644
--- a/src/netcode/d_netcmd.c
+++ b/src/netcode/d_netcmd.c
@@ -1825,7 +1825,7 @@ static void Command_Map_f(void)
 		return;
 	}
 
-	option_force    =   COM_CheckPartialParm("-f");
+	option_force    =   COM_CheckPartialParm("-f") || (cv_debug || devparm);
 	option_gametype =   COM_CheckPartialParm("-g");
 	newresetplayers = ! COM_CheckParm("-noresetplayers");
 
diff --git a/src/netcode/i_tcp.c b/src/netcode/i_tcp.c
index 3fa2306479bfcaffa576a2e1238e51812967d112..810453f45a9bca49ba22bde81d5e0dd57ddb07c2 100644
--- a/src/netcode/i_tcp.c
+++ b/src/netcode/i_tcp.c
@@ -424,7 +424,7 @@ static boolean SOCK_cmpipv6(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
 {
 	UINT8 bitmask;
 	I_Assert(mask <= 128);
-	if (memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, mask / 8) != 0)
+	if (memcmp(&a->ip6.sin6_addr.s6_addr, &b->ip6.sin6_addr.s6_addr, mask / 8) != 0)
 		return false;
 	if (mask % 8 == 0)
 		return true;
@@ -437,6 +437,9 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
 {
 	UINT32 bitmask = INADDR_NONE;
 
+	if (a->any.sa_family != b->any.sa_family)
+		return false;
+
 	if (mask && mask < 32)
 		bitmask = htonl((UINT32)(-1) << (32 - mask));
 
diff --git a/src/p_local.h b/src/p_local.h
index 611e747d25b83df487956676f814ba9a85cadcb3..c412dca5be4293e612c2c3396d8e7d73eabbe8a1 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -424,6 +424,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff);
 boolean P_Move(mobj_t *actor, fixed_t speed);
 boolean P_SetOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z);
 boolean P_MoveOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z);
+boolean P_LineIsBlocking(mobj_t *mo, line_t *li);
 void P_SlideMove(mobj_t *mo);
 void P_BounceMove(mobj_t *mo);
 boolean P_CheckSight(mobj_t *t1, mobj_t *t2);
diff --git a/src/p_map.c b/src/p_map.c
index 1888d92af36b149ccb96065934c6cfe9c8ec65a8..21582bbd74652f554d2431f039b87165029ce39b 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -3405,36 +3405,41 @@ static boolean P_IsClimbingValid(player_t *player, angle_t angle)
 	return false;
 }
 
-static boolean PTR_LineIsBlocking(line_t *li)
+//
+// P_LineIsBlocking
+//
+// Determines if line would block mo's movement
+//
+boolean P_LineIsBlocking(mobj_t *mo, line_t *li)
 {
 	// one-sided linedefs are always solid to sliding movement.
 	if (!li->backsector)
-		return !P_PointOnLineSide(slidemo->x, slidemo->y, li);
+		return !P_PointOnLineSide(mo->x, mo->y, li);
 
-	if (!(slidemo->flags & MF_MISSILE))
+	if (!(mo->flags & MF_MISSILE))
 	{
 		if (li->flags & ML_IMPASSIBLE)
 			return true;
 
-		if ((slidemo->flags & (MF_ENEMY|MF_BOSS)) && li->flags & ML_BLOCKMONSTERS)
+		if ((mo->flags & (MF_ENEMY|MF_BOSS)) && li->flags & ML_BLOCKMONSTERS)
 			return true;
 	}
 
 	// set openrange, opentop, openbottom
-	P_LineOpening(li, slidemo);
+	P_LineOpening(li, mo);
 
-	if (openrange < slidemo->height)
+	if (openrange < mo->height)
 		return true; // doesn't fit
 
-	if (opentop - slidemo->z < slidemo->height)
+	if (opentop - mo->z < mo->height)
 		return true; // mobj is too high
 
-	if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, slidemo->scale))
+	if (openbottom - mo->z > FixedMul(MAXSTEPMOVE, mo->scale))
 		return true; // too big a step up
 
-	if (slidemo->player
-		&& openrange < P_GetPlayerHeight(slidemo->player)
-		&& !P_PlayerCanEnterSpinGaps(slidemo->player))
+	if (mo->player
+		&& openrange < P_GetPlayerHeight(mo->player)
+		&& !P_PlayerCanEnterSpinGaps(mo->player))
 			return true; // nonspin character should not take this path
 
 	return false;
@@ -3538,7 +3543,7 @@ static boolean PTR_SlideTraverse(intercept_t *in)
 
 	li = in->d.line;
 
-	if (!PTR_LineIsBlocking(li))
+	if (!P_LineIsBlocking(slidemo, li))
 		return true;
 
 	// the line blocks movement,
diff --git a/src/r_segs.c b/src/r_segs.c
index 62fea352fca5d4b3b9b0a39d07b4d562481f5aa2..bd4869bdcc28ed4b4a891bcd8b8288e5e0b37263 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -525,6 +525,9 @@ static boolean R_IsFFloorTranslucent(visffloor_t *pfloor)
 //
 // R_RenderThickSideRange
 // Renders all the thick sides in the given range.
+
+static fixed_t ffloortexturecolumn[MAXVIDWIDTH];
+
 void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 {
 	size_t          pindex;
@@ -550,7 +553,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	fixed_t       left_top, left_bottom; // needed here for slope skewing
 	pslope_t      *skewslope = NULL;
 	boolean do_texture_skew;
-	UINT32 lineflags;
+	INT16 lineflags;
 	fixed_t wall_scalex, wall_scaley;
 
 	void (*colfunc_2s) (column_t *);
@@ -575,7 +578,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 		lineflags = newline->flags;
 	}
 	else
-		lineflags = pfloor->master->flags;
+		lineflags = curline->linedef->flags;
 
 	texnum = R_GetTextureNum(sidedef->midtexture);
 
@@ -732,10 +735,18 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	wall_scalex = FixedDiv(FRACUNIT, sidedef->scalex_mid);
 	wall_scaley = sidedef->scaley_mid;
 
-	thicksidecol = ds->thicksidecol;
+	thicksidecol = ffloortexturecolumn;
 
-	for (INT32 x = x1; x <= x2; x++)
-		thicksidecol[x] = FixedDiv(thicksidecol[x], wall_scalex) + ds->offsetx;
+	if (wall_scalex == FRACUNIT)
+	{
+		for (INT32 x = x1; x <= x2; x++)
+			thicksidecol[x] = ds->thicksidecol[x] + ds->offsetx;
+	}
+	else
+	{
+		for (INT32 x = x1; x <= x2; x++)
+			thicksidecol[x] = FixedDiv(ds->thicksidecol[x], wall_scalex) + ds->offsetx;
+	}
 
 	mfloorclip = ds->sprbottomclip;
 	mceilingclip = ds->sprtopclip;
@@ -746,10 +757,12 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	left_bottom = P_GetFFloorBottomZAt(pfloor, ds->leftpos.x, ds->leftpos.y) - viewz;
 
 	do_texture_skew = lineflags & ML_SKEWTD;
-	skewslope = *pfloor->t_slope; // skew using top slope by default
 
 	if (do_texture_skew)
+	{
+		skewslope = *pfloor->t_slope; // skew using top slope by default
 		dc_texturemid = FixedMul(left_top, wall_scaley);
+	}
 	else
 		dc_texturemid = FixedMul(*pfloor->topheight - viewz, wall_scaley);
 
@@ -757,14 +770,16 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 
 	if (lineflags & ML_DONTPEGBOTTOM)
 	{
-		skewslope = *pfloor->b_slope; // skew using bottom slope
 		if (do_texture_skew)
+		{
+			skewslope = *pfloor->b_slope; // skew using bottom slope
 			dc_texturemid = FixedMul(left_bottom, wall_scaley);
+		}
 		else
 			offsetvalue -= FixedMul(*pfloor->topheight - *pfloor->bottomheight, wall_scaley);
 	}
 
-	if (do_texture_skew && skewslope)
+	if (skewslope)
 	{
 		angle_t lineangle = R_PointToAngle2(curline->v1->x, curline->v1->y, curline->v2->x, curline->v2->y);
 		ffloortextureslide = FixedMul(skewslope->zdelta, FINECOSINE((lineangle-skewslope->xydirection)>>ANGLETOFINESHIFT));
diff --git a/src/r_skins.c b/src/r_skins.c
index 1605ca6c8921364974c9616ed98327c21eca1d59..6b4aeefe1726e46ee3f169540d5e87ad15dc271e 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -144,6 +144,8 @@ static void Sk_SetDefaultValue(skin_t *skin)
 	skin->contspeed = 17;
 	skin->contangle = 0;
 
+	skin->natkcolor = SKINCOLOR_NONE;
+
 	for (i = 0; i < sfx_skinsoundslot0; i++)
 		if (S_sfx[i].skinsound != -1)
 			skin->soundsid[S_sfx[i].skinsound] = i;
@@ -588,7 +590,6 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
 		UINT16 color = R_GetSuperColorByName(value);
 		skin->supercolor = (color ? color : SKINCOLOR_SUPERGOLD1);
 	}
-
 #define GETFLOAT(field) else if (!stricmp(stoken, #field)) skin->field = FLOAT_TO_FIXED(atof(value));
 	GETFLOAT(jumpfactor)
 	GETFLOAT(highresscale)
@@ -629,6 +630,9 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
 	GETFLAG(NOSHIELDABILITY)
 #undef GETFLAG
 
+	else if (!stricmp(stoken, "natkcolor"))
+		skin->natkcolor = R_GetColorByName(value); // SKINCOLOR_NONE is allowed here
+
 	else // let's check if it's a sound, otherwise error out
 	{
 		boolean found = false;
diff --git a/src/r_skins.h b/src/r_skins.h
index a97b24f9657840d99f5e4e4e572bf5da0f6ac0c2..cc3b63cbed2c97de3fc312ad338d6f201edcd65b 100644
--- a/src/r_skins.h
+++ b/src/r_skins.h
@@ -71,6 +71,7 @@ typedef struct
 	UINT16 prefcolor;
 	UINT16 supercolor;
 	UINT16 prefoppositecolor; // if 0 use tables instead
+	UINT16 natkcolor; //Color for Nights Attack Menu
 
 	fixed_t highresscale; // scale of highres, default is 0.5
 	UINT8 contspeed; // continue screen animation speed