diff --git a/.travis.yml b/.travis.yml
index c652584f8879153f879a24c0f3f5a741cc8eae4f..5815e711f1611f76ea7b395a093730ff46ebaffa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,6 +10,8 @@ compiler:
   - clang
 
 cache:
+  apt:  true
+  ccache: true
   directories:
   - $HOME/srb2_cache
 
diff --git a/cmake/Modules/FindGME.cmake b/cmake/Modules/FindGME.cmake
index 3b0c68de735e67458ce4652c600b5768c8124046..ea80af45442c098f21cef962082ce0602546cd29 100644
--- a/cmake/Modules/FindGME.cmake
+++ b/cmake/Modules/FindGME.cmake
@@ -6,16 +6,16 @@ find_path(GME_INCLUDE_DIR
 	NAMES gme.h
 	PATHS
 		${GME_PKGCONF_INCLUDE_DIRS}
-		/usr/include/gme
-		/usr/local/include/gme
+		"/usr/include/gme"
+		"/usr/local/include/gme"
 )
 
 find_library(GME_LIBRARY
 	NAMES gme
 	PATHS
 		${GME_PKGCONF_LIBRARY_DIRS}
-		/usr/lib
-		/usr/local/lib
+		"/usr/lib"
+		"/usr/local/lib"
 )
 
 set(GME_PROCESS_INCLUDES GME_INCLUDE_DIR)
diff --git a/comptime.bat b/comptime.bat
index 9e127f001a984ebf3e6febffb8307adb21d08aaf..0c7ea06d60955ec1cd5015a9b660625dae0b6d2c 100644
--- a/comptime.bat
+++ b/comptime.bat
@@ -1,4 +1,4 @@
-@ECHO OFF
+@echo off
 set BRA=Unknown
 set REV=illegal
 
@@ -13,20 +13,20 @@ goto filwri
 :gitrev
 set GIT=%2
 if "%GIT%"=="" set GIT=git
-FOR /F "usebackq" %%s IN (`%GIT% rev-parse --abbrev-ref HEAD`) DO @SET BRA=%%s
-FOR /F "usebackq" %%s IN (`%GIT% rev-parse HEAD`) DO @SET REV=%%s
+for /f "usebackq" %%s in (`%GIT% rev-parse --abbrev-ref HEAD`) do @set BRA=%%s
+for /f "usebackq" %%s in (`%GIT% rev-parse HEAD`) do @set REV=%%s
 set REV=%REV:~0,8%
 goto filwri
 
 :svnrev
 set BRA=Subversion
-FOR /F "usebackq" %%s IN (`svnversion .`) DO @SET REV=%%s
+for /f "usebackq" %%s in (`svnversion .`) do @set REV=%%s
 set REV=r%REV%
 goto filwri
 
 :filwri
-ECHO // Do not edit!  This file was autogenerated > %1\comptime.h
-ECHO // by the %0 batch file >> %1\comptime.h
-ECHO // >> %1\comptime.h
-ECHO const char* compbranch = "%BRA%"; >> %1\comptime.h
-ECHO const char* comprevision = "%REV%"; >> %1\comptime.h
+echo // Do not edit!  This file was autogenerated > %1\comptime.h
+echo // by the %0 batch file >> %1\comptime.h
+echo // >> %1\comptime.h
+echo const char* compbranch = "%BRA%"; >> %1\comptime.h
+echo const char* comprevision = "%REV%"; >> %1\comptime.h
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 54a08ea0bb31b03d23c3758b37874dc99f3fdcd0..13d3312dd02aae2d0efcaec4da9287ac4072fc15 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -316,6 +316,7 @@ if(${SRB2_CONFIG_HAVE_GME})
 	find_package(GME)
 	if(${GME_FOUND})
 		set(SRB2_HAVE_GME ON)
+		add_definitions(-DHAVE_LIBGME)
 	else()
 		message(WARNING "You have specified that GME is available but it was not found.")
 	endif()
diff --git a/src/Makefile b/src/Makefile
index bee60804720da1dcbee09065c29f616e145848f0..449b4065d2967f742a8ad4f917f6b4c96a2f0e19 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -262,9 +262,7 @@ else
 	OBJS+=$(OBJDIR)/hw3sound.o
 endif
 
-ifndef NOVERSION
 OPTS += -DCOMPVERSION
-endif
 
 ifndef NONX86
 ifndef GCC29
@@ -551,15 +549,11 @@ cleandep:
 	$(REMOVE) comptime.h
 
 pre-build:
-ifdef NOVERSION
-	-@touch comptime.c
-else
 ifdef WINDOWSHELL
 	-..\comptime.bat .
 else
 	-@../comptime.sh .
 endif
-endif
 
 clean:
 	$(REMOVE) *~ *.flc
diff --git a/src/config.h.in b/src/config.h.in
index 5cd75fa5a50962f34a638b3dda519cfe2483fa47..eef4fec1314a44baa2ddaf1f59cb8f460dda9c9f 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -15,7 +15,9 @@
 #define ASSET_HASH_PLAYER_DTA "${SRB2_ASSET_player.dta_HASH}"
 #define ASSET_HASH_RINGS_DTA  "${SRB2_ASSET_rings.dta_HASH}"
 #define ASSET_HASH_ZONES_DTA  "${SRB2_ASSET_zones.dta_HASH}"
+#ifdef USE_PATCH_DTA
 #define ASSET_HASH_PATCH_DTA  "${SRB2_ASSET_patch.dta_HASH}"
+#endif
 
 #define SRB2_COMP_REVISION    "${SRB2_COMP_REVISION}"
 #define SRB2_COMP_BRANCH      "${SRB2_COMP_BRANCH}"
@@ -26,10 +28,16 @@
 
 #else
 
+/* Manually defined asset hashes for non-CMake builds
+ * Last updated 2000 / 00 / 00
+ */
 #define ASSET_HASH_SRB2_SRB   "c1b9577687f8a795104aef4600720ea7"
 #define ASSET_HASH_ZONES_DTA  "303838c6c534d9540288360fa49cca60"
 #define ASSET_HASH_PLAYER_DTA "cfca0f1c73023cbbd8f844f45480f799"
 #define ASSET_HASH_RINGS_DTA  "85901ad4bf94637e5753d2ac2c03ea26"
+#ifdef USE_PATCH_DTA
+#define ASSET_HASH_PATCH_DTA  "0c66790502e648bfce90fdc5bb15722e"
+#endif
 
 #endif
 #endif
diff --git a/src/d_main.c b/src/d_main.c
index 3918d8118c0c1cfd242c70b7f04148b26612f8c8..b04c55cbf668510284410c1bd36b75e0488e3f10 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -513,7 +513,6 @@ static void D_Display(void)
 // =========================================================================
 
 tic_t rendergametic;
-boolean supdate;
 
 void D_SRB2Loop(void)
 {
@@ -604,7 +603,6 @@ void D_SRB2Loop(void)
 
 			// Update display, next frame, with current state.
 			D_Display();
-			supdate = false;
 
 			if (moviemode)
 				M_SaveFrame();
@@ -841,8 +839,10 @@ static void IdentifyVersion(void)
 	// Add the weapons
 	D_AddFile(va(pandf,srb2waddir,"rings.dta"));
 
+#ifdef USE_PATCH_DTA
 	// Add our crappy patches to fix our bugs
-	// D_AddFile(va(pandf,srb2waddir,"patch.dta"));
+	D_AddFile(va(pandf,srb2waddir,"patch.dta"));
+#endif
 
 #if !defined (HAVE_SDL) || defined (HAVE_MIXER)
 	{
@@ -1133,12 +1133,18 @@ void D_SRB2Main(void)
 	W_VerifyFileMD5(1, ASSET_HASH_ZONES_DTA); // zones.dta
 	W_VerifyFileMD5(2, ASSET_HASH_PLAYER_DTA); // player.dta
 	W_VerifyFileMD5(3, ASSET_HASH_RINGS_DTA); // rings.dta
-	//W_VerifyFileMD5(4, "0c66790502e648bfce90fdc5bb15722e"); // patch.dta
+#ifdef USE_PATCH_DTA
+	W_VerifyFileMD5(4, ASSET_HASH_PATCH_DTA); // patch.dta
+#endif
+
 	// don't check music.dta because people like to modify it, and it doesn't matter if they do
 	// ...except it does if they slip maps in there, and that's what W_VerifyNMUSlumps is for.
-#endif
+#endif //ifndef DEVELOP
 
-	mainwads = 4; // there are 5 wads not to unload
+	mainwads = 4; // there are 4 wads not to unload
+#ifdef USE_PATCH_DTA
+	++mainwads; // patch.dta adds one more
+#endif
 
 	cht_Init();
 
diff --git a/src/d_main.h b/src/d_main.h
index 800b61f531fb9c79f6b19f4d2eb0d23c1430b045..c5ce19ef4cb199ca489840e1899f0e40d0c3f775 100644
--- a/src/d_main.h
+++ b/src/d_main.h
@@ -17,7 +17,6 @@
 #include "d_event.h"
 #include "w_wad.h"   // for MAX_WADFILES
 
-extern boolean supdate;
 extern boolean advancedemo;
 
 // make sure not to write back the config until it's been correctly loaded
diff --git a/src/dehacked.c b/src/dehacked.c
index c21e8fb99316648163d34e7a82e7a3cf6e600996..4dc23777d3b342b110a74557fbd7702d40ea748e 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -6229,9 +6229,7 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	// Collectible Items
 	"MT_RING",
 	"MT_FLINGRING", // Lost ring
-#ifdef BLUE_SPHERES
 	"MT_BLUEBALL",  // Blue sphere replacement for special stages
-#endif
 	"MT_REDTEAMRING",  //Rings collectable by red team.
 	"MT_BLUETEAMRING", //Rings collectable by blue team.
 	"MT_EMMY", // emerald token for special stage
@@ -6684,6 +6682,7 @@ static const char *const MOBJFLAG2_LIST[] = {
 	"EXPLOSION",	// Thrown ring has explosive properties
 	"SCATTER",		// Thrown ring has scatter properties
 	"BEYONDTHEGRAVE",// Source of this missile has died and has since respawned.
+	"PUSHED",		// Mobj was already pushed this tic
 	"SLIDEPUSH",	// MF_PUSHABLE that pushes continuously.
 	"CLASSICPUSH",	// Drops straight down when object has negative Z.
 	"STANDONME",	// While not pushable, stand on me anyway.
@@ -6712,7 +6711,7 @@ static const char *const MOBJEFLAG_LIST[] = {
 	"JUSTSTEPPEDDOWN", // used for ramp sectors
 	"VERTICALFLIP", // Vertically flip sprite/allow upside-down physics
 	"GOOWATER", // Goo water
-	"PUSHED", // Mobj was already pushed this tic
+	"\x01", // free: 1<<7 (name un-matchable)
 	"SPRUNG", // Mobj was already sprung this tic
 	"APPLYPMOMZ", // Platform movement
 	NULL
@@ -7267,36 +7266,36 @@ struct {
 	{"FF_GOOWATER",FF_GOOWATER},               ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop.
 
 	// Angles
-	{"ANG1",ANG1>>16},
-	{"ANG2",ANG2>>16},
-	{"ANG10",ANG10>>16},
-	{"ANG15",ANG15>>16},
-	{"ANG20",ANG20>>16},
-	{"ANG30",ANG30>>16},
-	{"ANG60",ANG60>>16},
-	{"ANG64h",ANG64h>>16},
-	{"ANG105",ANG105>>16},
-	{"ANG210",ANG210>>16},
-	{"ANG255",ANG255>>16},
-	{"ANG340",ANG340>>16},
-	{"ANG350",ANG350>>16},
-	{"ANGLE_11hh",ANGLE_11hh>>16},
-	{"ANGLE_22h",ANGLE_22h>>16},
-	{"ANGLE_45",ANGLE_45>>16},
-	{"ANGLE_67h",ANGLE_67h>>16},
-	{"ANGLE_90",ANGLE_90>>16},
-	{"ANGLE_112h",ANGLE_112h>>16},
-	{"ANGLE_135",ANGLE_135>>16},
-	{"ANGLE_157h",ANGLE_157h>>16},
-	{"ANGLE_180",ANGLE_180>>16},
-	{"ANGLE_202h",ANGLE_202h>>16},
-	{"ANGLE_225",ANGLE_225>>16},
-	{"ANGLE_247h",ANGLE_247h>>16},
-	{"ANGLE_270",ANGLE_270>>16},
-	{"ANGLE_292h",ANGLE_292h>>16},
-	{"ANGLE_315",ANGLE_315>>16},
-	{"ANGLE_337h",ANGLE_337h>>16},
-	{"ANGLE_MAX",ANGLE_MAX>>16},
+	{"ANG1",ANG1},
+	{"ANG2",ANG2},
+	{"ANG10",ANG10},
+	{"ANG15",ANG15},
+	{"ANG20",ANG20},
+	{"ANG30",ANG30},
+	{"ANG60",ANG60},
+	{"ANG64h",ANG64h},
+	{"ANG105",ANG105},
+	{"ANG210",ANG210},
+	{"ANG255",ANG255},
+	{"ANG340",ANG340},
+	{"ANG350",ANG350},
+	{"ANGLE_11hh",ANGLE_11hh},
+	{"ANGLE_22h",ANGLE_22h},
+	{"ANGLE_45",ANGLE_45},
+	{"ANGLE_67h",ANGLE_67h},
+	{"ANGLE_90",ANGLE_90},
+	{"ANGLE_112h",ANGLE_112h},
+	{"ANGLE_135",ANGLE_135},
+	{"ANGLE_157h",ANGLE_157h},
+	{"ANGLE_180",ANGLE_180},
+	{"ANGLE_202h",ANGLE_202h},
+	{"ANGLE_225",ANGLE_225},
+	{"ANGLE_247h",ANGLE_247h},
+	{"ANGLE_270",ANGLE_270},
+	{"ANGLE_292h",ANGLE_292h},
+	{"ANGLE_315",ANGLE_315},
+	{"ANGLE_337h",ANGLE_337h},
+	{"ANGLE_MAX",ANGLE_MAX},
 
 	// P_Chase directions (dirtype_t)
 	{"DI_NODIR",DI_NODIR},
diff --git a/src/doomdef.h b/src/doomdef.h
index 0fc4a1fea7c43ee695719ed586637da082a83c41..c3a0bfa6e3ca37ec3f534c15ab67aa42c5217e4c 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -148,13 +148,17 @@ extern FILE *logstream;
 // we use comprevision and compbranch instead.
 #else
 #define VERSION    201 // Game version
-#define SUBVERSION 14  // more precise version number
-#define VERSIONSTRING "v2.1.14"
-#define VERSIONSTRINGW L"v2.1.14"
+#define SUBVERSION 15  // more precise version number
+#define VERSIONSTRING "v2.1.15"
+#define VERSIONSTRINGW L"v2.1.15"
 // Hey! If you change this, add 1 to the MODVERSION below!
 // Otherwise we can't force updates!
 #endif
 
+// Does this version require an added patch file?
+// Comment or uncomment this as necessary.
+#define USE_PATCH_DTA
+
 // Modification options
 // If you want to take advantage of the Master Server's ability to force clients to update
 // to the latest version, fill these out.  Otherwise, just comment out UPDATE_ALERT and leave
@@ -208,7 +212,7 @@ extern FILE *logstream;
 // it's only for detection of the version the player is using so the MS can alert them of an update.
 // Only set it higher, not lower, obviously.
 // Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1".
-#define MODVERSION 19
+#define MODVERSION 20
 
 // =========================================================================
 
@@ -427,9 +431,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
 ///	\note	obsoleted by cv_maxportals
 //#define PORTAL_LIMIT 8
 
-///	Fun experimental slope stuff!
-//#define SLOPENESS
-
 /// Kalaron/Eternity Engine slope code (SRB2CB ported)
 #define ESLOPE
 
@@ -449,10 +450,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
 ///	Polyobject fake flat code
 #define POLYOBJECTS_PLANES
 
-///	Blue spheres for future use.
-///	\todo	Remove this define.
-#define BLUE_SPHERES // Blue spheres for future use.
-
 ///	Improved way of dealing with ping values and a ping limit.
 #define NEWPING
 
diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index 83dff02f877e17db254f5e0e35bb47095bc7c18e..94eef1d3e46e95f825f7ac89088cbb6674636225 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -36,9 +36,7 @@ typedef struct
 {
 	float x;
 	float y;
-//#ifdef SLOPENESS
 	float z;
-//#endif
 } polyvertex_t;
 
 #ifdef _MSC_VER
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 409900b986f989b7a001bfd262ec1bf33a394eba..58e4e87f2bf431ec3168c78ce45d33143e95ef82 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -539,6 +539,8 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, fixed_t fi
 	static FOutVector *planeVerts = NULL;
 	static UINT16 numAllocedPlaneVerts = 0;
 
+	(void)sector;
+
 	// no convex poly were generated for this subsector
 	if (!xsub->planepoly)
 		return;
@@ -678,25 +680,6 @@ static void HWR_RenderPlane(sector_t *sector, extrasubsector_t *xsub, fixed_t fi
 		v3d->x = pv->x;
 		v3d->y = height;
 		v3d->z = pv->y;
-#ifdef SLOPENESS
-		if (sector && sector->special == 65535)
-		{
-			size_t q;
-			for (q = 0; q < sector->linecount; q++)
-			{
-				if (v3d->x == sector->lines[q]->v1->x>>FRACBITS)
-				{
-					if (v3d->z == sector->lines[q]->v1->y>>FRACBITS)
-					{
-						v3d->y += sector->lines[q]->v1->z>>FRACBITS;
-						break;
-					}
-				}
-			}
-		}
-#else
-		(void)sector;
-#endif
 	}
 
 	// only useful for flat coloured triangles
diff --git a/src/info.c b/src/info.c
index fb19fa2b43113e5f5e5747e654c38b205ef1a5d9..0eda417708b0980515f528e05cd253b697621e7c 100644
--- a/src/info.c
+++ b/src/info.c
@@ -4569,7 +4569,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-#ifdef BLUE_SPHERES
 	{           // MT_BLUEBALL
 		-1,             // doomednum
 		S_BLUEBALL,    // spawnstate
@@ -4596,7 +4595,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
 		S_NULL          // raisestate
 	},
-#endif
 
 	{           // MT_REDTEAMRING
 		308,            // doomednum
diff --git a/src/info.h b/src/info.h
index 9596f038421bd82f2b7c8b6235c63a8ce1994326..6e14448f470db76359c825d5ec993603249e1a4d 100644
--- a/src/info.h
+++ b/src/info.h
@@ -3049,9 +3049,7 @@ typedef enum mobj_type
 	// Collectible Items
 	MT_RING,
 	MT_FLINGRING, // Lost ring
-#ifdef BLUE_SPHERES
 	MT_BLUEBALL,  // Blue sphere replacement for special stages
-#endif
 	MT_REDTEAMRING,  //Rings collectable by red team.
 	MT_BLUETEAMRING, //Rings collectable by blue team.
 	MT_EMMY, // emerald token for special stage
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index c415eecb8e10f2c2853cb5b123d6fb3126ff58de..2cc79db47e7e3b2b7fae01905601902176decdb3 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -85,6 +85,14 @@ static int lib_print(lua_State *L)
 	return 0;
 }
 
+static int lib_evalMath(lua_State *L)
+{
+	const char *word = luaL_checkstring(L, 1);
+	LUA_Deprecated(L, "EvalMath(string)", "_G[string]");
+	lua_pushinteger(L, LUA_EvalMath(word));
+	return 1;
+}
+
 // M_RANDOM
 //////////////
 
@@ -1899,6 +1907,7 @@ static int lib_gTicsToMilliseconds(lua_State *L)
 
 static luaL_Reg lib[] = {
 	{"print", lib_print},
+	{"EvalMath", lib_evalMath},
 
 	// m_random
 	{"P_Random",lib_pRandom},
diff --git a/src/lua_script.h b/src/lua_script.h
index ec67703c33d3b8494ad810f695be6900d11bfb17..45fab2f53570f4118fdb14026807532b59dc9080 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -30,9 +30,9 @@
 #define lua_pushfixed(L, f) lua_pushinteger(L, f)
 
 // angle_t casting
-// we reduce the angle to a fixed point between 0.0 and 1.0
-#define luaL_checkangle(L, i) (((angle_t)(luaL_checkfixed(L, i)&0xFFFF))<<16)
-#define lua_pushangle(L, a) lua_pushfixed(L, a>>16)
+// TODO deal with signedness
+#define luaL_checkangle(L, i) ((angle_t)luaL_checkinteger(L, i))
+#define lua_pushangle(L, a) lua_pushinteger(L, a)
 
 #ifdef _DEBUG
 void LUA_ClearExtVars(void);
@@ -70,4 +70,15 @@ void COM_Lua_f(void);
 
 #define LUA_ErrInvalid(L, type) luaL_error(L, "accessed " type " doesn't exist anymore, please check 'valid' before using " type ".");
 
+// Deprecation warnings
+// Shows once upon use. Then doesn't show again.
+#define LUA_Deprecated(L,this_func,use_instead)\
+{\
+	static UINT8 seen = 0;\
+	if (!seen) {\
+		seen = 1;\
+		CONS_Alert(CONS_WARNING,"\"%s\" is deprecated and will be removed.\nUse \"%s\" instead.\n", this_func, use_instead);\
+	}\
+}
+
 #endif
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 367d5714a4b50b66511a53c7fb4015a1ad294acc..ffb69082213b86374f923ab2875452407ea0ac3f 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -7223,7 +7223,7 @@ void A_ChangeAngleAbsolute(mobj_t *actor)
 	//const angle_t amin = FixedAngle(locvar1*FRACUNIT);
 	//const angle_t amax = FixedAngle(locvar2*FRACUNIT);
 #ifdef HAVE_BLUA
-	if (LUA_CallAction("A_ChangeAngelAbsolute", actor))
+	if (LUA_CallAction("A_ChangeAngleAbsolute", actor))
 		return;
 #endif
 
diff --git a/src/p_floor.c b/src/p_floor.c
index b8d3f7b5e0098605bb44dd37e4e25dfca6d4d81b..cacaadf8101406b47f10a75969ea7783cbca4edf 100644
--- a/src/p_floor.c
+++ b/src/p_floor.c
@@ -1973,71 +1973,51 @@ void T_NoEnemiesSector(levelspecthink_t *nobaddies)
 {
 	size_t i;
 	fixed_t upperbound, lowerbound;
-	sector_t *sec = NULL;
-	sector_t *targetsec = NULL;
-	INT32 secnum = -1;
+	INT32 s;
+	sector_t *checksector;
 	msecnode_t *node;
 	mobj_t *thing;
-	boolean FOFsector = false;
+	boolean exists = false;
 
-	while ((secnum = P_FindSectorFromLineTag(nobaddies->sourceline, secnum)) >= 0)
+	for (i = 0; i < nobaddies->sector->linecount; i++)
 	{
-		sec = &sectors[secnum];
-
-		FOFsector = false;
-
-		// Check the lines of this sector, to see if it is a FOF control sector.
-		for (i = 0; i < sec->linecount; i++)
+		if (nobaddies->sector->lines[i]->special == 223)
 		{
-			INT32 targetsecnum = -1;
-
-			if (sec->lines[i]->special < 100 || sec->lines[i]->special >= 300)
-				continue;
 
-			FOFsector = true;
+			upperbound = nobaddies->sector->ceilingheight;
+			lowerbound = nobaddies->sector->floorheight;
 
-			while ((targetsecnum = P_FindSectorFromLineTag(sec->lines[i], targetsecnum)) >= 0)
+			for (s = -1; (s = P_FindSectorFromLineTag(nobaddies->sector->lines[i], s)) >= 0 ;)
 			{
-				targetsec = &sectors[targetsecnum];
+				checksector = &sectors[s];
 
-				upperbound = targetsec->ceilingheight;
-				lowerbound = targetsec->floorheight;
-				node = targetsec->touching_thinglist; // things touching this sector
+				node = checksector->touching_thinglist; // things touching this sector
 				while (node)
 				{
 					thing = node->m_thing;
 
 					if ((thing->flags & (MF_ENEMY|MF_BOSS)) && thing->health > 0
-					&& thing->z < upperbound && thing->z+thing->height > lowerbound)
-						return;
+						&& thing->z < upperbound && thing->z+thing->height > lowerbound)
+					{
+						exists = true;
+						goto foundenemy;
+					}
 
 					node = node->m_snext;
 				}
 			}
 		}
-
-		if (!FOFsector)
-		{
-			upperbound = sec->ceilingheight;
-			lowerbound = sec->floorheight;
-			node = sec->touching_thinglist; // things touching this sector
-			while (node)
-			{
-				thing = node->m_thing;
-
-				if ((thing->flags & (MF_ENEMY|MF_BOSS)) && thing->health > 0
-				&& thing->z < upperbound && thing->z+thing->height > lowerbound)
-					return;
-
-				node = node->m_snext;
-			}
-		}
 	}
+foundenemy:
+	if (exists)
+		return;
+
+	s = P_AproxDistance(nobaddies->sourceline->dx, nobaddies->sourceline->dy)>>FRACBITS;
 
-	CONS_Debug(DBG_GAMELOGIC, "Running no-more-enemies exec with tag of %d\n", nobaddies->sourceline->tag);
+	CONS_Debug(DBG_GAMELOGIC, "Running no-more-enemies exec with tag of %d\n", s);
 
-	// No enemies found, run the linedef exec and terminate this thinker
-	P_RunTriggerLinedef(nobaddies->sourceline, NULL, NULL);
+	// Otherwise, run the linedef exec and terminate this thinker
+	P_LinedefExecute((INT16)s, NULL, NULL);
 	P_RemoveThinker(&nobaddies->thinker);
 }
 
diff --git a/src/p_inter.c b/src/p_inter.c
index 6ab6aaf40dd5c823a07e698e460e0a2b22fc6293..b8101f12b1561f97c88e64bebd30ce99e215c189 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -405,7 +405,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			if ((maptol & TOL_NIGHTS) && special->type != MT_FLINGCOIN)
 				P_DoNightsScore(player);
 			break;
-#ifdef BLUE_SPHERES
 		case MT_BLUEBALL:
 			if (!(P_CanPickupItem(player, false)))
 				return;
@@ -422,7 +421,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			if (maptol & TOL_NIGHTS)
 				P_DoNightsScore(player);
 			break;
-#endif
 		case MT_AUTOPICKUP:
 		case MT_BOUNCEPICKUP:
 		case MT_SCATTERPICKUP:
@@ -766,10 +764,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 					}
 
 					if (!(mo2->type == MT_NIGHTSWING || mo2->type == MT_RING || mo2->type == MT_COIN
-#ifdef BLUE_SPHERES
-					      || mo2->type == MT_BLUEBALL
-#endif
-					     ))
+					   || mo2->type == MT_BLUEBALL))
 						continue;
 
 					// Yay! The thing's in reach! Pull it in!
diff --git a/src/p_mobj.c b/src/p_mobj.c
index d2454f267616901413bdc445d270abe899357ae6..4e60ad6121c83f92aebe9508a6d40b09965fa60c 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -2188,9 +2188,7 @@ static boolean P_ZMovement(mobj_t *mo)
 
 		case MT_RING: // Ignore still rings
 		case MT_COIN:
-#ifdef BLUE_SPHERES
 		case MT_BLUEBALL:
-#endif
 		case MT_REDTEAMRING:
 		case MT_BLUETEAMRING:
 		case MT_FLINGRING:
@@ -6083,7 +6081,8 @@ void P_MobjThinker(mobj_t *mobj)
 	if (mobj->tracer && P_MobjWasRemoved(mobj->tracer))
 		P_SetTarget(&mobj->tracer, NULL);
 
-	mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG);
+	mobj->flags2 &= ~MF2_PUSHED;
+	mobj->eflags &= ~MFE_SPRUNG;
 
 	tmfloorthing = tmhitthing = NULL;
 
@@ -6478,14 +6477,12 @@ void P_MobjThinker(mobj_t *mobj)
 	else if (mobj->health <= 0) // Dead things think differently than the living.
 		switch (mobj->type)
 		{
-#ifdef BLUE_SPHERES
 		case MT_BLUEBALL:
 			if ((mobj->tics>>2)+1 > 0 && (mobj->tics>>2)+1 <= tr_trans60) // tr_trans50 through tr_trans90, shifting once every second frame
 				mobj->frame = (NUMTRANSMAPS-((mobj->tics>>2)+1))<<FF_TRANSSHIFT;
 			else // tr_trans60 otherwise
 				mobj->frame = tr_trans60<<FF_TRANSSHIFT;
 			break;
-#endif
 		case MT_EGGCAPSULE:
 			if (mobj->z <= mobj->floorz)
 			{
@@ -6943,9 +6940,7 @@ void P_MobjThinker(mobj_t *mobj)
 			break;
 		case MT_RING:
 		case MT_COIN:
-#ifdef BLUE_SPHERES
 		case MT_BLUEBALL:
-#endif
 		case MT_REDTEAMRING:
 		case MT_BLUETEAMRING:
 			// No need to check water. Who cares?
@@ -7711,9 +7706,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 			break;
 		case MT_RING:
 		case MT_COIN:
-#ifdef BLUE_SPHERES
 		case MT_BLUEBALL:
-#endif
 			nummaprings++;
 		default:
 			break;
@@ -7839,9 +7832,7 @@ void P_RemoveMobj(mobj_t *mobj)
 	if (mobj->spawnpoint &&
 		(mobj->type == MT_RING
 		|| mobj->type == MT_COIN
-#ifdef BLUE_SPHERES
 		|| mobj->type == MT_BLUEBALL
-#endif
 		|| mobj->type == MT_REDTEAMRING
 		|| mobj->type == MT_BLUETEAMRING
 		|| P_WeaponOrPanel(mobj->type))
@@ -9627,11 +9618,9 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 				ringthing = (gametype == GT_CTF) ? MT_BLUETEAMRING : MT_RING;
 				break;
 			default:
-#ifdef BLUE_SPHERES
 				// Spawn rings as blue spheres in special stages, ala S3+K.
 				if (G_IsSpecialStage(gamemap) && useNightsSS)
 					ringthing = MT_BLUEBALL;
-#endif
 				break;
 		}
 
@@ -9696,11 +9685,9 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 		if (ultimatemode && !(G_IsSpecialStage(gamemap) || maptol & TOL_NIGHTS))
 			return;
 
-#ifdef BLUE_SPHERES
 		// Spawn rings as blue spheres in special stages, ala S3+K.
 		if (G_IsSpecialStage(gamemap) && useNightsSS)
 			ringthing = MT_BLUEBALL;
-#endif
 
 		for (r = 1; r <= 5; r++)
 		{
@@ -9751,11 +9738,9 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 		if (ultimatemode && !(G_IsSpecialStage(gamemap) || maptol & TOL_NIGHTS))
 			return;
 
-#ifdef BLUE_SPHERES
 		// Spawn rings as blue spheres in special stages, ala S3+K.
 		if (G_IsSpecialStage(gamemap) && useNightsSS)
 			ringthing = MT_BLUEBALL;
-#endif
 
 		angle >>= ANGLETOFINESHIFT;
 
@@ -9848,11 +9833,9 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 				if (ultimatemode && !(G_IsSpecialStage(gamemap) || (maptol & TOL_NIGHTS)))
 					continue;
 
-#ifdef BLUE_SPHERES
 				// Spawn rings as blue spheres in special stages, ala S3+K.
 				if (G_IsSpecialStage(gamemap) && useNightsSS)
 					itemToSpawn = MT_BLUEBALL;
-#endif
 			}
 
 			fa = i*FINEANGLES/numitems;
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 6198f5bec1c623876265144d508d400d48bbac2c..e24b09652d61ddd91113422ec65a9101c47fb7d1 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -175,23 +175,24 @@ typedef enum
 	MF2_EXPLOSION      = 1<<7,  // Thrown ring has explosive properties
 	MF2_SCATTER        = 1<<8,  // Thrown ring has scatter properties
 	MF2_BEYONDTHEGRAVE = 1<<9,  // Source of this missile has died and has since respawned.
-	MF2_SLIDEPUSH      = 1<<10, // MF_PUSHABLE that pushes continuously.
-	MF2_CLASSICPUSH    = 1<<11, // Drops straight down when object has negative Z.
-	MF2_STANDONME      = 1<<12, // While not pushable, stand on me anyway.
-	MF2_INFLOAT        = 1<<13, // Floating to a height for a move, don't auto float to target's height.
-	MF2_DEBRIS         = 1<<14, // Splash ring from explosion ring
-	MF2_NIGHTSPULL     = 1<<15, // Attracted from a paraloop
-	MF2_JUSTATTACKED   = 1<<16, // can be pushed by other moving mobjs
-	MF2_FIRING         = 1<<17, // turret fire
-	MF2_SUPERFIRE      = 1<<18, // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it.
-	MF2_SHADOW         = 1<<19, // Fuzzy draw, makes targeting harder.
-	MF2_STRONGBOX      = 1<<20, // Flag used for "strong" random monitors.
-	MF2_OBJECTFLIP     = 1<<21, // Flag for objects that always have flipped gravity.
-	MF2_SKULLFLY       = 1<<22, // Special handling: skull in flight.
-	MF2_FRET           = 1<<23, // Flashing from a previous hit
-	MF2_BOSSNOTRAP     = 1<<24, // No Egg Trap after boss
-	MF2_BOSSFLEE       = 1<<25, // Boss is fleeing!
-	MF2_BOSSDEAD       = 1<<26, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
+	MF2_PUSHED         = 1<<10, // Mobj was already pushed this tic
+	MF2_SLIDEPUSH      = 1<<11, // MF_PUSHABLE that pushes continuously.
+	MF2_CLASSICPUSH    = 1<<12, // Drops straight down when object has negative Z.
+	MF2_STANDONME      = 1<<13, // While not pushable, stand on me anyway.
+	MF2_INFLOAT        = 1<<14, // Floating to a height for a move, don't auto float to target's height.
+	MF2_DEBRIS         = 1<<15, // Splash ring from explosion ring
+	MF2_NIGHTSPULL     = 1<<16, // Attracted from a paraloop
+	MF2_JUSTATTACKED   = 1<<17, // can be pushed by other moving mobjs
+	MF2_FIRING         = 1<<18, // turret fire
+	MF2_SUPERFIRE      = 1<<19, // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it.
+	MF2_SHADOW         = 1<<20, // Fuzzy draw, makes targeting harder.
+	MF2_STRONGBOX      = 1<<21, // Flag used for "strong" random monitors.
+	MF2_OBJECTFLIP     = 1<<22, // Flag for objects that always have flipped gravity.
+	MF2_SKULLFLY       = 1<<23, // Special handling: skull in flight.
+	MF2_FRET           = 1<<24, // Flashing from a previous hit
+	MF2_BOSSNOTRAP     = 1<<25, // No Egg Trap after boss
+	MF2_BOSSFLEE       = 1<<26, // Boss is fleeing!
+	MF2_BOSSDEAD       = 1<<27, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
 	// free: to and including 1<<31
 } mobjflag2_t;
 
@@ -231,8 +232,7 @@ typedef enum
 	MFE_VERTICALFLIP      = 1<<5,
 	// Goo water
 	MFE_GOOWATER          = 1<<6,
-	// Mobj was already pushed this tic
-	MFE_PUSHED            = 1<<7,
+	// free: to and including 1<<7
 	// Mobj was already sprung this tic
 	MFE_SPRUNG            = 1<<8,
 	// Platform movement
diff --git a/src/p_spec.c b/src/p_spec.c
index f9fa5f1f1104002ac8c53b3fb1a0bc0131dbcabd..277fe19eb3616cc4da0f5f5a567fb75372272425 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1891,7 +1891,6 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 	 || specialtype == 304  // Ring count - Once
 	 || specialtype == 307  // Character ability - Once
 	 || specialtype == 308  // Race only - Once
-	 || specialtype == 313  // No More Enemies - Once
 	 || specialtype == 315  // No of pushables - Once
 	 || specialtype == 318  // Unlockable trigger - Once
 	 || specialtype == 320  // Unlockable - Once
@@ -6029,31 +6028,6 @@ void P_SpawnSpecials(INT32 fromnetsave)
 				P_AddRaiseThinker(lines[i].frontsector, &lines[i]);
 				break;
 
-#ifdef SLOPENESS
-			case 999:
-				sec = sides[*lines[i].sidenum].sector-sectors;
-				for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
-				{
-					size_t counting;
-
-					sectors[s].floorangle = ANGLE_45;
-					for (counting = 0; counting < sectors[s].linecount/2; counting++)
-					{
-						sectors[s].lines[counting]->v1->z = sectors[sec].floorheight;
-						CONS_Debug(DBG_GAMELOGIC, "Set it to %d\n", sectors[s].lines[counting]->v1->z>>FRACBITS);
-					}
-
-					for (counting = sectors[s].linecount/2; counting < sectors[s].linecount; counting++)
-					{
-						sectors[s].lines[counting]->v1->z = sectors[sec].ceilingheight;
-						CONS_Debug(DBG_GAMELOGIC, "Set it to %d\n", sectors[s].lines[counting]->v1->z>>FRACBITS);
-					}
-					sectors[s].special = 65535;
-					CONS_Debug(DBG_GAMELOGIC, "Found & Set slope!\n");
-				}
-				break;
-#endif
-
 			case 200: // Double light effect
 				P_AddFakeFloorsByLine(i, FF_EXISTS|FF_CUTSPRITES|FF_DOUBLESHADOW, secthinkers);
 				break;
@@ -6468,7 +6442,7 @@ static void P_DoScrollMove(mobj_t *thing, fixed_t dx, fixed_t dy, INT32 exclusiv
 	thing->momy += dy;
 
 	if (exclusive)
-		thing->eflags |= MFE_PUSHED;
+		thing->flags2 |= MF2_PUSHED;
 }
 
 /** Processes an active scroller.
@@ -6572,7 +6546,7 @@ void T_Scroll(scroll_t *s)
 					{
 						thing = node->m_thing;
 
-						if (thing->eflags & MFE_PUSHED) // Already pushed this tic by an exclusive pusher.
+						if (thing->flags2 & MF2_PUSHED) // Already pushed this tic by an exclusive pusher.
 							continue;
 
 						height = P_GetSpecialBottomZ(thing, sec, psec);
@@ -6594,7 +6568,7 @@ void T_Scroll(scroll_t *s)
 				{
 					thing = node->m_thing;
 
-					if (thing->eflags & MFE_PUSHED)
+					if (thing->flags2 & MF2_PUSHED)
 						continue;
 
 					height = P_GetSpecialBottomZ(thing, sec, sec);
@@ -6635,7 +6609,7 @@ void T_Scroll(scroll_t *s)
 					{
 						thing = node->m_thing;
 
-						if (thing->eflags & MFE_PUSHED)
+						if (thing->flags2 & MF2_PUSHED)
 							continue;
 
 						height = P_GetSpecialTopZ(thing, sec, psec);
@@ -6657,7 +6631,7 @@ void T_Scroll(scroll_t *s)
 				{
 					thing = node->m_thing;
 
-					if (thing->eflags & MFE_PUSHED)
+					if (thing->flags2 & MF2_PUSHED)
 						continue;
 
 					height = P_GetSpecialTopZ(thing, sec, sec);
@@ -7140,7 +7114,7 @@ static pusher_t *tmpusher; // pusher structure for blockmap searches
   */
 static inline boolean PIT_PushThing(mobj_t *thing)
 {
-	if (thing->eflags & MFE_PUSHED)
+	if (thing->flags2 & MF2_PUSHED)
 		return false;
 
 	if (thing->player && thing->player->pflags & PF_ROPEHANG)
@@ -7270,7 +7244,7 @@ static inline boolean PIT_PushThing(mobj_t *thing)
 	}
 
 	if (tmpusher->exclusive)
-		thing->eflags |= MFE_PUSHED;
+		thing->flags2 |= MF2_PUSHED;
 
 	return true;
 }
@@ -7373,7 +7347,7 @@ void T_Pusher(pusher_t *p)
 			|| thing->type == MT_BIGTUMBLEWEED))
 			continue;
 
-		if (thing->eflags & MFE_PUSHED)
+		if (thing->flags2 & MF2_PUSHED)
 			continue;
 
 		if (thing->player && thing->player->pflags & PF_ROPEHANG)
@@ -7540,7 +7514,7 @@ void T_Pusher(pusher_t *p)
 			}
 
 			if (p->exclusive)
-				thing->eflags |= MFE_PUSHED;
+				thing->flags2 |= MF2_PUSHED;
 		}
 	}
 }
diff --git a/src/p_user.c b/src/p_user.c
index 68916d5a6a60875dffd829294cb7c19eed4893a4..edb5b4c070800c8f550a0de0810b6ae122335767 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8857,10 +8857,7 @@ void P_PlayerThink(player_t *player)
 			mo2 = (mobj_t *)th;
 
 			if (!(mo2->type == MT_NIGHTSWING || mo2->type == MT_RING || mo2->type == MT_COIN
-#ifdef BLUE_SPHERES
-			      || mo2->type == MT_BLUEBALL
-#endif
-			     ))
+			   || mo2->type == MT_BLUEBALL))
 				continue;
 
 			if (P_AproxDistance(P_AproxDistance(mo2->x - x, mo2->y - y), mo2->z - z) > FixedMul(128*FRACUNIT, player->mo->scale))
diff --git a/src/r_defs.h b/src/r_defs.h
index f18410fe88139cfd52827ae90bf3d18880fbc20b..2915b9259e46730069f0eb6fd614265eb2f41675 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -369,14 +369,6 @@ typedef struct sector_s
 	double lineoutLength;
 #endif // ----- end special tricks -----
 
-	// ZDoom C++ to Legacy C conversion (for slopes)
-	// store floor and ceiling planes instead of heights
-	//secplane_t floorplane, ceilingplane;
-#ifdef SLOPENESS
-	//fixed_t floortexz, ceilingtexz; // [RH] used for wall texture mapping
-	angle_t floorangle;
-#endif
-
 	// This points to the master's floorheight, so it can be changed in realtime!
 	fixed_t *gravity; // per-sector gravity
 	boolean verticalflip; // If gravity < 0, then allow flipped physics
diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt
index b3d734521bed110b4d3c107fd2e52db252ee4523..7190efaac6ec08b8e25bc4b1372fc1f4cc7afb4b 100644
--- a/src/sdl/CMakeLists.txt
+++ b/src/sdl/CMakeLists.txt
@@ -122,6 +122,7 @@ if(${SDL2_FOUND})
 		add_framework(SDL2 SRB2SDL2)
 		add_framework(SDL2_mixer SRB2SDL2)
 		target_link_libraries(SRB2SDL2 PRIVATE
+			${GME_LIBRARIES}
 			${PNG_LIBRARIES}
 			${ZLIB_LIBRARIES}
 			${OPENGL_LIBRARIES}
@@ -131,6 +132,7 @@ if(${SDL2_FOUND})
 		target_link_libraries(SRB2SDL2 PRIVATE
 			${SDL2_LIBRARIES}
 			${SDL2_MIXER_LIBRARIES}
+			${GME_LIBRARIES}
 			${PNG_LIBRARIES}
 			${ZLIB_LIBRARIES}
 			${OPENGL_LIBRARIES}
@@ -198,6 +200,7 @@ if(${SDL2_FOUND})
 	target_include_directories(SRB2SDL2 PRIVATE
 		${SDL2_INCLUDE_DIRS}
 		${SDL2_MIXER_INCLUDE_DIRS}
+		${GME_INCLUDE_DIRS}
 		${PNG_INCLUDE_DIRS}
 		${ZLIB_INCLUDE_DIRS}
 		${OPENGL_INCLUDE_DIRS}