diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 20caecf7bd6a65a20631318799016da319d3f13d..fbd29cddda5e3caae29aaee01bbed579375730a5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -172,6 +172,11 @@ if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
 	endif()
 endif()
 
+if("${CMAKE_SYSTEM_NAME}" MATCHES "Haiku")
+	target_compile_definitions(SRB2SDL2 PRIVATE -DNOEXECINFO)
+	target_link_libraries(SRB2SDL2 PRIVATE network)
+endif()
+
 if("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
 	target_compile_definitions(SRB2SDL2 PRIVATE -DMACOSX)
 endif()
diff --git a/src/Makefile.d/detect.mk b/src/Makefile.d/detect.mk
index 9e27369462b00c407f02cb95dbda22ef284a91d1..853ad3d6df728d7c69ea64b204cf099547f59e45 100644
--- a/src/Makefile.d/detect.mk
+++ b/src/Makefile.d/detect.mk
@@ -4,7 +4,6 @@
 
 # Previously featured:\
 	PANDORA\
-	HAIKU\
 	DUMMY\
 	DJGPPDOS\
 	SOLARIS\
@@ -17,6 +16,7 @@ all_systems:=\
 	UNIX\
 	LINUX\
 	FREEBSD\
+	HAIKU\
 
 # check for user specified system
 ifeq (,$(filter $(all_systems),$(.VARIABLES)))
@@ -35,6 +35,8 @@ system:=$(shell uname -s)
 
 ifeq ($(system),Linux)
 new_system:=LINUX
+else ifeq ($(system),Haiku)
+new_system:=HAIKU
 else
 
 $(error \
diff --git a/src/Makefile.d/haiku.mk b/src/Makefile.d/haiku.mk
new file mode 100644
index 0000000000000000000000000000000000000000..6742ec75b58f992a916a99752c72ca827f4dab5e
--- /dev/null
+++ b/src/Makefile.d/haiku.mk
@@ -0,0 +1,71 @@
+#
+# Makefile options for Haiku
+#
+
+opts+=-DUNIXCOMMON -DLUA_USE_POSIX
+
+ifndef DEDICATED
+ifndef DUMMY
+SDL?=1
+DEDICATED?=0
+endif
+endif
+
+NOEXECINFO=1
+
+ifeq (${SDL},1)
+EXENAME?=srb2haiku
+else ifeq (${DEDICATED},1)
+EXENAME?=srb2haikud
+endif
+
+ifndef NONET
+libs+=-lnetwork
+endif
+
+define _set =
+$(1)_CFLAGS?=$($(1)_opts)
+$(1)_LDFLAGS?=$($(1)_libs)
+endef
+
+lib:=../libs/gme
+LIBGME_opts:=-I$(lib)/include
+LIBGME_libs:=-l:libgme.so.0
+$(eval $(call _set,LIBGME))
+
+lib:=../libs/libopenmpt
+LIBOPENMPT_opts:=-I$(lib)/inc
+LIBOPENMPT_libs:=-l:libopenmpt.so.0
+$(eval $(call _set,LIBOPENMPT))
+
+ifdef SDL
+lib:=../libs/SDL2_mixer
+mixer_opts:=-I$(lib)/include
+mixer_libs:=-l:libSDL2_mixer-2.0.so.0
+
+lib:=../libs/SDL2
+SDL_opts:=-I$(lib)/include $(mixer_opts)
+SDL_libs:=$(mixer_libs) -l:libSDL2-2.0.so.0
+$(eval $(call _set,SDL))
+endif
+
+lib:=../libs/zlib
+ZLIB_opts:=-I$(lib)
+ZLIB_libs:=-l:libz.so.1
+$(eval $(call _set,ZLIB))
+
+ifndef PNG_CONFIG
+PNG_opts:=
+PNG_libs:=-l:libpng16.so.16
+$(eval $(call _set,PNG))
+endif
+
+lib:=../libs/curl
+CURL_opts:=-I$(lib)/include
+CURL_libs:=-l:libcurl.so.4
+$(eval $(call _set,CURL))
+
+lib:=../libs/miniupnpc
+MINIUPNPC_opts:=-I$(lib)/include
+MINIUPNPC_libs:=-l:libminiupnpc.so.17
+$(eval $(call _set,MINIUPNPC))
diff --git a/src/Makefile.d/platform.mk b/src/Makefile.d/platform.mk
index d9a2954f60c54b30c121c0467022c1707f720fc0..d5423a397384922689260546b71948447846f3e1 100644
--- a/src/Makefile.d/platform.mk
+++ b/src/Makefile.d/platform.mk
@@ -35,6 +35,10 @@ endif
 else ifdef FREEBSD
 UNIX=1
 platform=freebsd
+else ifdef HAIKU
+# Give Haiku its own configuration, since it
+# isn't actually UNIX.
+include Makefile.d/haiku.mk
 else ifdef SOLARIS # FIXME: UNTESTED
 UNIX=1
 platform=solaris
diff --git a/src/Makefile.d/sdl.mk b/src/Makefile.d/sdl.mk
index a1bfa33038bbacebada85790a7565fceb9440985..d5e83989c2d1955146a8c228c15034d9f2b9e091 100644
--- a/src/Makefile.d/sdl.mk
+++ b/src/Makefile.d/sdl.mk
@@ -33,11 +33,13 @@ else
 opts+=-DHAVE_MIXER
 sources+=sdl/mixer_sound.c
 
-  ifdef HAVE_MIXERX
-  opts+=-DHAVE_MIXERX
-  libs+=-lSDL2_mixer_ext
-  else
-  libs+=-lSDL2_mixer
+  ifndef HAIKU # Haiku has a special import path
+    ifdef HAVE_MIXERX
+    opts+=-DHAVE_MIXERX
+    libs+=-lSDL2_mixer_ext
+    else
+    libs+=-lSDL2_mixer
+    endif
   endif
 endif
 
diff --git a/src/d_think.h b/src/d_think.h
index 58912458783a2f3b2187e50ebaf00a667a0e0a78..76c1bb5b8af11a5557bab32705897445e699ca5b 100644
--- a/src/d_think.h
+++ b/src/d_think.h
@@ -51,6 +51,8 @@ typedef struct thinker_s
 	// killough 11/98: count of how many other objects reference
 	// this one using pointers. Used for garbage collection.
 	INT32 references;
+
+	boolean removing;
 	boolean cachable;
 
 #ifdef PARANOIA
diff --git a/src/f_finale.c b/src/f_finale.c
index 37e4ba0703468d062be0762c52eb86b180fb6a7b..a992a0dfdfd1f49030840dac445a1d6f6d7864e2 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -3435,7 +3435,7 @@ void F_TitleScreenTicker(boolean run)
 		{
 			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 			{
-				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+				if (th->removing)
 					continue;
 
 				mo2 = (mobj_t *)th;
diff --git a/src/g_demo.c b/src/g_demo.c
index b0e16addad0ad976faa46cf69b8743c47c3645dc..8315e716bfad9f80387d438d1128078aa3e2ca0f 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -614,7 +614,7 @@ void G_ConsGhostTic(void)
 				mobj = NULL;
 				for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 				{
-					if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+					if (th->removing)
 						continue;
 					mobj = (mobj_t *)th;
 					if (mobj->type == (mobjtype_t)type && mobj->x == x && mobj->y == y && mobj->z == z)
@@ -2696,7 +2696,7 @@ void G_DoPlayMetal(void)
 	// find metal sonic
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo = (mobj_t *)th;
diff --git a/src/g_game.c b/src/g_game.c
index d409bb2cf721b0132f086113932c5402da289615..8940635851adbbf56b95e296c1fa908da642b828 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1384,7 +1384,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 	if (PLAYERINPUTDOWN(ssplayer, GC_SPIN) || (usejoystick && axis > 0))
 		cmd->buttons |= BT_SPIN;
 
-	if (gamestate != GS_LEVEL) // not in a level, don't build anything else
+	if (gamestate == GS_INTRO) // prevent crash in intro
 	{
 		cmd->angleturn = ticcmd_oldangleturn[forplayer];
 		cmd->aiming = G_ClipAimingPitch(myaiming);
@@ -1722,7 +1722,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 	// At this point, cmd doesn't contain the final angle yet,
 	// So we need to temporarily transform it so Lua scripters
 	// don't need to handle it differently than in other hooks.
-	if (addedtogame)
+	if (addedtogame && gamestate == GS_LEVEL)
 	{
 		INT16 extra = ticcmd_oldangleturn[forplayer] - player->oldrelangleturn;
 		INT16 origangle = cmd->angleturn;
@@ -3072,7 +3072,7 @@ void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo)
 	// scan all thinkers
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c
index c29eadecc625214615c2546abd2216c9b6630254..f570c229b97b138d6d10e714c4c2cdd8175b1596 100644
--- a/src/lua_blockmaplib.c
+++ b/src/lua_blockmaplib.c
@@ -254,11 +254,10 @@ static int lib_searchBlockmap(lua_State *L)
 	}
 	else // mobj and function only - search around mobj's radius by default
 	{
-		fixed_t radius = mobj->radius + MAXRADIUS;
-		x1 = mobj->x - radius;
-		x2 = mobj->x + radius;
-		y1 = mobj->y - radius;
-		y2 = mobj->y + radius;
+		x1 = mobj->x - mobj->radius;
+		x2 = mobj->x + mobj->radius;
+		y1 = mobj->y - mobj->radius;
+		y2 = mobj->y + mobj->radius;
 	}
 	lua_settop(L, 2); // pop everything except function, mobj
 
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index ebe995ea8cf17a9ed52341a7c2feb18d3a04a9a2..6bdebf774df50d0527e3f5713043e008f5c3e33e 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -198,12 +198,12 @@ static int mobj_get(lua_State *L)
 	enum mobj_e field = Lua_optoption(L, 2, -1, mobj_fields_ref);
 	lua_settop(L, 2);
 
-	if (!mo || !ISINLEVEL) {
+	if (P_MobjWasRemoved(mo) || !ISINLEVEL) {
 		if (field == mobj_valid) {
 			lua_pushboolean(L, 0);
 			return 1;
 		}
-		if (!mo) {
+		if (P_MobjWasRemoved(mo)) {
 			return LUA_ErrInvalid(L, "mobj_t");
 		} else
 			return luaL_error(L, "Do not access an mobj_t field outside a level!");
diff --git a/src/lua_script.c b/src/lua_script.c
index 623d8867351590cec65d0c656e37a5b03f0a58cc..686555a16d6b09b98d839cefec0ac0fc876181ae 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -1779,7 +1779,7 @@ void LUA_Archive(save_t *save_p)
 
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		// archive function will determine when to skip mobjs,
@@ -1817,7 +1817,7 @@ void LUA_UnArchive(save_t *save_p)
 		mobjnum = P_ReadUINT32(save_p); // read a mobjnum
 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			if (th->removing)
 				continue;
 			if (((mobj_t *)th)->mobjnum != mobjnum) // find matching mobj
 				continue;
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 2b32253fa5bc04e84b7e3f03c1cb3401cb393ca4..4be071bb20254f062bfbcb87dbf625abac6ea4ec 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -562,7 +562,7 @@ void Command_Teleport_f(void)
 
 			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 			{
-				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+				if (th->removing)
 					continue;
 
 				mo2 = (mobj_t *)th;
@@ -1072,7 +1072,7 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c
 
 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			if (th->removing)
 				continue;
 
 			mo = (mobj_t *)th;
diff --git a/src/m_menu.c b/src/m_menu.c
index 63a7024740bd94df0a83cee96a06e4fb3e8f6079..88a69b5a5f64774ef85d0583ee2f3b9fe55ac97d 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -8511,6 +8511,7 @@ static void M_StartTutorial(INT32 choice)
 	gamecomplete = 0;
 	cursaveslot = 0;
 	maplistoption = 0;
+	CV_StealthSet(&cv_skin, DEFAULTSKIN); // tutorial accounts for sonic only
 	G_DeferedInitNew(false, G_BuildMapName(tutorialmap), 0, false, false);
 }
 
diff --git a/src/m_perfstats.c b/src/m_perfstats.c
index b9948bdc0c3284a3473e7e6e8b95b2e3bd8fb568..33a774acfbb4ca9781ff62a177b93f9bffca28f8 100644
--- a/src/m_perfstats.c
+++ b/src/m_perfstats.c
@@ -453,7 +453,7 @@ static int PS_DrawPerfRows(int x, int y, int color, perfstatrow_t *rows)
 	return draw_y;
 }
 
-static void PS_UpdateMetricHistory(ps_metric_t *metric, boolean time_metric, boolean frame_metric, boolean set_user)
+static void PS_UpdateMetricHistory(ps_metric_t *metric, boolean time_metric, boolean frame_metric)
 {
 	int index = frame_metric ? ps_frame_index : ps_tick_index;
 
@@ -461,7 +461,7 @@ static void PS_UpdateMetricHistory(ps_metric_t *metric, boolean time_metric, boo
 	{
 		// allocate history table
 		int value_size = time_metric ? sizeof(precise_t) : sizeof(INT32);
-		void** memory_user = set_user ? &metric->history : NULL;
+		void** memory_user = &metric->history;
 
 		metric->history = Z_Calloc(value_size * cv_ps_samplesize.value, PU_PERFSTATS,
 				memory_user);
@@ -491,7 +491,7 @@ static void PS_UpdateRowHistories(perfstatrow_t *rows, boolean frame_metric)
 	for (row = rows; row->lores_label; row++)
 	{
 		if (PS_IsRowValid(row))
-			PS_UpdateMetricHistory(row->metric, !!(row->flags & PS_TIME), frame_metric, true);
+			PS_UpdateMetricHistory(row->metric, !!(row->flags & PS_TIME), frame_metric);
 	}
 }
 
@@ -584,7 +584,7 @@ static void PS_CountThinkers(void)
 		for (thinker = thlist[i].next; thinker != &thlist[i]; thinker = thinker->next)
 		{
 			ps_thinkercount.value.i++;
-			if (thinker->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			if (thinker->removing)
 				ps_removecount.value.i++;
 			else if (i == THINK_POLYOBJ)
 				ps_polythcount.value.i++;
@@ -649,17 +649,17 @@ void PS_UpdateTickStats(void)
 			if (cv_perfstats.value == 3)
 			{
 				for (i = 0; i < thinkframe_hooks_length; i++)
-					PS_UpdateMetricHistory(&thinkframe_hooks[i].time_taken, true, false, false);
+					PS_UpdateMetricHistory(&thinkframe_hooks[i].time_taken, true, false);
 			}
 			else if (cv_perfstats.value == 4)
 			{
 				for (i = 0; i < prethinkframe_hooks_length; i++)
-					PS_UpdateMetricHistory(&prethinkframe_hooks[i].time_taken, true, false, false);
+					PS_UpdateMetricHistory(&prethinkframe_hooks[i].time_taken, true, false);
 			}
 			else if (cv_perfstats.value == 5)
 			{
 				for (i = 0; i < postthinkframe_hooks_length; i++)
-					PS_UpdateMetricHistory(&postthinkframe_hooks[i].time_taken, true, false, false);
+					PS_UpdateMetricHistory(&postthinkframe_hooks[i].time_taken, true, false);
 			}
 		}
 		if (cv_perfstats.value)
diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c
index 917e32b598e03d86f8540b997329069f279817fb..c740d53a6a3845539bfe0637771e78827ae8b055 100644
--- a/src/netcode/client_connection.c
+++ b/src/netcode/client_connection.c
@@ -546,6 +546,7 @@ static void AbortConnection(void)
 {
 	Snake_Free(&snake);
 
+	CURLAbortFile();
 	D_QuitNetGame();
 	CL_Reset();
 	D_StartTitle();
@@ -1062,10 +1063,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
 				}
 			}
 
-			// Rusty TODO: multithread
-			if (filedownload.http_running)
-				CURLGetFile();
-
 			if (waitmore)
 				break; // exit the case
 
diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c
index d34dbc4e085e4d4f4a0c658ed376b37532ab214e..5919235716cd7b186030eb30b59149dfe6343cf8 100644
--- a/src/netcode/d_clisrv.c
+++ b/src/netcode/d_clisrv.c
@@ -1814,7 +1814,7 @@ INT16 Consistancy(void)
 	{
 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			if (th->removing)
 				continue;
 
 			mo = (mobj_t *)th;
diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c
index 630999cd0787372cd2a3f125f26f9ff2b5c7143d..8376f26b9b4e853197f7d4fe393e7fb52cf322fd 100644
--- a/src/netcode/d_netcmd.c
+++ b/src/netcode/d_netcmd.c
@@ -391,7 +391,7 @@ static CV_PossibleValue_t perfstats_cons_t[] = {
 consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", CV_CALL, perfstats_cons_t, PS_PerfStats_OnChange);
 static CV_PossibleValue_t ps_samplesize_cons_t[] = {
 	{1, "MIN"}, {1000, "MAX"}, {0, NULL}};
-consvar_t cv_ps_samplesize = CVAR_INIT ("ps_samplesize", "1", CV_CALL, ps_samplesize_cons_t, PS_SampleSize_OnChange);
+consvar_t cv_ps_samplesize = CVAR_INIT ("ps_samplesize", "175", CV_CALL, ps_samplesize_cons_t, PS_SampleSize_OnChange);
 static CV_PossibleValue_t ps_descriptor_cons_t[] = {
 	{1, "Average"}, {2, "SD"}, {3, "Minimum"}, {4, "Maximum"}, {0, NULL}};
 consvar_t cv_ps_descriptor = CVAR_INIT ("ps_descriptor", "Average", 0, ps_descriptor_cons_t, NULL);
diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c
index adec1a0e47ecd4a2996a7edcf0016eb9c4529936..313905f438e0203e4d757a1c41b6db596fe48941 100644
--- a/src/netcode/d_netfil.c
+++ b/src/netcode/d_netfil.c
@@ -95,6 +95,7 @@ static filetran_t transfer[MAXNETNODES];
 INT32 fileneedednum; // Number of files needed to join the server
 fileneeded_t *fileneeded; // List of needed files
 static tic_t lasttimeackpacketsent = 0;
+static I_mutex downloadmutex;
 char downloaddir[512] = "DOWNLOAD";
 
 // For resuming failed downloads
@@ -606,7 +607,7 @@ void AddLuaFileTransfer(const char *filename, const char *mode)
 		prevnext = &((*prevnext)->next);
 
 	// Allocate file transfer information and append it to the transfer list
-	filetransfer = malloc(sizeof(luafiletransfer_t));
+	filetransfer = calloc(1, sizeof(luafiletransfer_t));
 	if (!filetransfer)
 		I_Error("AddLuaFileTransfer: Out of memory\n");
 	*prevnext = filetransfer;
@@ -1609,11 +1610,13 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
 		I_Error("Attempted to download files in -nodownload mode");
 #endif
 
-	curl_global_init(CURL_GLOBAL_ALL);
+	if (!multi_handle)
+	{
+		curl_global_init(CURL_GLOBAL_ALL);
+		multi_handle = curl_multi_init();
+	}
 
 	http_handle = curl_easy_init();
-	multi_handle = curl_multi_init();
-
 	if (http_handle && multi_handle)
 	{
 		I_mkdir(downloaddir, 0755);
@@ -1672,6 +1675,8 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
 		filedownload.current = dfilenum;
 		filedownload.http_running = true;
 
+		I_spawn_thread("http-download", (I_thread_fn)CURLGetFile, NULL);
+
 		return true;
 	}
 
@@ -1680,103 +1685,119 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
 	return false;
 }
 
+void CURLAbortFile(void)
+{
+	filedownload.http_running = false;
+
+	// lock and unlock to wait for the download thread to exit
+	I_lock_mutex(&downloadmutex);
+	I_unlock_mutex(downloadmutex);
+}
+
 void CURLGetFile(void)
 {
+	I_lock_mutex(&downloadmutex);
 	CURLMcode mc; /* return code used by curl_multi_wait() */
 	CURLcode easyres; /* Return from easy interface */
-	int numfds;
 	CURLMsg *m; /* for picking up messages with the transfer status */
 	CURL *e;
 	int msgs_left; /* how many messages are left */
 	const char *easy_handle_error;
+	boolean running = true;
 
-	if (curl_runninghandles)
+	while (running && filedownload.http_running)
 	{
-		curl_multi_perform(multi_handle, &curl_runninghandles);
+		if (curl_runninghandles)
+		{
+			curl_multi_perform(multi_handle, &curl_runninghandles);
 
-		/* wait for activity, timeout or "nothing" */
-		mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds);
+			/* wait for activity, timeout or "nothing" */
+			mc = curl_multi_wait(multi_handle, NULL, 0, 1000, NULL);
 
-		if (mc != CURLM_OK)
-		{
-			CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc);
-			return;
+			if (mc != CURLM_OK)
+			{
+				CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc);
+				continue;
+			}
+			curl_curfile->currentsize = curl_dlnow;
+			curl_curfile->totalsize = curl_dltotal;
 		}
-		curl_curfile->currentsize = curl_dlnow;
-		curl_curfile->totalsize = curl_dltotal;
-	}
 
-	/* See how the transfers went */
-	while ((m = curl_multi_info_read(multi_handle, &msgs_left)))
-	{
-		if (m && (m->msg == CURLMSG_DONE))
+		/* See how the transfers went */
+		while ((m = curl_multi_info_read(multi_handle, &msgs_left)))
 		{
-			e = m->easy_handle;
-			easyres = m->data.result;
-
-			char *filename = Z_StrDup(curl_realname);
-			nameonly(filename);
-
-			if (easyres != CURLE_OK)
+			if (m && (m->msg == CURLMSG_DONE))
 			{
-				long response_code = 0;
+				running = false;
+				e = m->easy_handle;
+				easyres = m->data.result;
 
-				if (easyres == CURLE_HTTP_RETURNED_ERROR)
-					curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &response_code);
+				char *filename = Z_StrDup(curl_realname);
+				nameonly(filename);
 
-				if (response_code == 404)
-					curl_curfile->failed = FDOWNLOAD_FAIL_NOTFOUND;
-				else
-					curl_curfile->failed = FDOWNLOAD_FAIL_OTHER;
-
-				easy_handle_error = (response_code) ? va("HTTP response code %ld", response_code) : curl_easy_strerror(easyres);
-				curl_curfile->status = FS_FALLBACK;
-				curl_curfile->currentsize = curl_origfilesize;
-				curl_curfile->totalsize = curl_origtotalfilesize;
-				filedownload.http_failed = true;
-				fclose(curl_curfile->file);
-				remove(curl_curfile->filename);
-				CONS_Alert(CONS_ERROR, M_GetText("Failed to download addon \"%s\" (%s)\n"), filename, easy_handle_error);
-			}
-			else
-			{
-				fclose(curl_curfile->file);
+				if (easyres != CURLE_OK)
+				{
+					long response_code = 0;
 
-				CONS_Printf(M_GetText("Finished download of \"%s\"\n"), filename);
+					if (easyres == CURLE_HTTP_RETURNED_ERROR)
+						curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &response_code);
 
-				if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD)
-				{
-					CONS_Alert(CONS_WARNING, M_GetText("File \"%s\" does not match the version used by the server\n"), filename);
+					if (response_code == 404)
+						curl_curfile->failed = FDOWNLOAD_FAIL_NOTFOUND;
+					else
+						curl_curfile->failed = FDOWNLOAD_FAIL_OTHER;
+
+					easy_handle_error = (response_code) ? va("HTTP response code %ld", response_code) : curl_easy_strerror(easyres);
 					curl_curfile->status = FS_FALLBACK;
-					curl_curfile->failed = FDOWNLOAD_FAIL_MD5SUMBAD;
+					curl_curfile->currentsize = curl_origfilesize;
+					curl_curfile->totalsize = curl_origtotalfilesize;
 					filedownload.http_failed = true;
+					fclose(curl_curfile->file);
+					remove(curl_curfile->filename);
+					CONS_Alert(CONS_ERROR, M_GetText("Failed to download addon \"%s\" (%s)\n"), filename, easy_handle_error);
 				}
 				else
 				{
-					filedownload.completednum++;
-					filedownload.completedsize += curl_curfile->totalsize;
-					curl_curfile->status = FS_FOUND;
+					fclose(curl_curfile->file);
+
+					CONS_Printf(M_GetText("Finished download of \"%s\"\n"), filename);
+
+					if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD)
+					{
+						CONS_Alert(CONS_WARNING, M_GetText("File \"%s\" does not match the version used by the server\n"), filename);
+						curl_curfile->status = FS_FALLBACK;
+						curl_curfile->failed = FDOWNLOAD_FAIL_MD5SUMBAD;
+						filedownload.http_failed = true;
+					}
+					else
+					{
+						filedownload.completednum++;
+						filedownload.completedsize += curl_curfile->totalsize;
+						curl_curfile->status = FS_FOUND;
+					}
 				}
-			}
 
-			Z_Free(filename);
+				Z_Free(filename);
 
-			curl_curfile->file = NULL;
-			filedownload.http_running = false;
-			filedownload.remaining--;
-			curl_multi_remove_handle(multi_handle, e);
-			curl_easy_cleanup(e);
+				curl_curfile->file = NULL;
+				filedownload.remaining--;
+				curl_multi_remove_handle(multi_handle, e);
+				curl_easy_cleanup(e);
 
-			if (!filedownload.remaining)
-				break;
+				if (!filedownload.remaining)
+					break;
+			}
 		}
 	}
 
-	if (!filedownload.remaining)
+	if (!filedownload.remaining || !filedownload.http_running)
 	{
 		curl_multi_cleanup(multi_handle);
 		curl_global_cleanup();
+		multi_handle = NULL;
 	}
+	filedownload.http_running = false;
+	I_unlock_mutex(downloadmutex);
 }
 
 HTTP_login *
diff --git a/src/netcode/d_netfil.h b/src/netcode/d_netfil.h
index 4039b5e2d5cc4b0d81fbdc04e6d7c7bef46f724a..9f29d18bb79ff39dda69fffd68e2756f8d4f0db9 100644
--- a/src/netcode/d_netfil.h
+++ b/src/netcode/d_netfil.h
@@ -140,6 +140,7 @@ boolean CL_SendFileRequest(void);
 void PT_RequestFile(SINT8 node);
 
 boolean CURLPrepareFile(const char* url, int dfilenum);
+void CURLAbortFile(void);
 void CURLGetFile(void);
 HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next);
 
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 59ca954093f80b2242b0cec40be6ea60776d1e92..60cffebfc4b09dda07f6c84e65ff66a0869cb0aa 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -3744,7 +3744,7 @@ static void P_DoBossVictory(mobj_t *mo)
 	// scan the remaining thinkers to see if all bosses are dead
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -6449,7 +6449,7 @@ void A_RingExplode(mobj_t *actor)
 
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -8756,7 +8756,7 @@ void A_FindTarget(mobj_t *actor)
 	// scan the thinkers
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -8820,7 +8820,7 @@ void A_FindTracer(mobj_t *actor)
 	// scan the thinkers
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -9498,7 +9498,7 @@ void A_RemoteAction(mobj_t *actor)
 		// scan the thinkers
 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			if (th->removing)
 				continue;
 
 			mo2 = (mobj_t *)th;
@@ -9761,7 +9761,7 @@ void A_SetObjectTypeState(mobj_t *actor)
 
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -10391,7 +10391,7 @@ void A_CheckThingCount(mobj_t *actor)
 
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
diff --git a/src/p_inter.c b/src/p_inter.c
index dc606a47eabea03cd199a9cb6b84a7c48d28e872..0e63fea1b9500cb8b9d22642bfb9f0d9c7fd2b20 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -101,7 +101,7 @@ void P_ClearStarPost(INT32 postnum)
 	// scan the thinkers
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -130,7 +130,7 @@ void P_ResetStarposts(void)
 
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		post = (mobj_t *)th;
@@ -1003,7 +1003,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 						// scan the thinkers to find the corresponding anchorpoint
 						for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 						{
-							if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+							if (th->removing)
 								continue;
 
 							mo2 = (mobj_t *)th;
@@ -1097,7 +1097,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				// scan the remaining thinkers
 				for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 				{
-					if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+					if (th->removing)
 						continue;
 
 					mo2 = (mobj_t *)th;
@@ -1147,7 +1147,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				// in from the paraloop. Isn't this just so efficient?
 				for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 				{
-					if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+					if (th->removing)
 						continue;
 
 					mo2 = (mobj_t *)th;
@@ -1522,7 +1522,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				// scan the remaining thinkers to find koopa
 				for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 				{
-					if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+					if (th->removing)
 						continue;
 
 					mo2 = (mobj_t *)th;
@@ -2020,7 +2020,7 @@ void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost)
 
 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			if (th->removing)
 				continue;
 
 			mo2 = (mobj_t *)th;
@@ -2870,7 +2870,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 				// scan the thinkers to make sure all the old pinch dummies are gone on death
 				for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 				{
-					if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+					if (th->removing)
 						continue;
 
 					mo = (mobj_t *)th;
diff --git a/src/p_local.h b/src/p_local.h
index 39856bffbcc5d7d04272afdb143974af18b95d80..3253ef0b68f0b208ef3b8d8574d62c51fd6d47fc 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -39,11 +39,6 @@
 // Convenience macro to fix issue with collision along bottom/left edges of blockmap -Red
 #define BMBOUNDFIX(xl, xh, yl, yh) {if (xl > xh) xl = 0; if (yl > yh) yl = 0;}
 
-// MAXRADIUS is for precalculated sector block boxes
-// the spider demon is larger,
-// but we do not have any moving sectors nearby
-#define MAXRADIUS (32*FRACUNIT)
-
 // max Z move up or down without jumping
 // above this, a height difference is considered as a 'dropoff'
 #define MAXSTEPMOVE (24*FRACUNIT)
diff --git a/src/p_map.c b/src/p_map.c
index 1116ae06ad0a9dc792ffc444ca456803b22e8bf8..fce17f8c4bb2357eb9d789a46261119e67f9e946 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -36,6 +36,9 @@
 
 #include "m_perfstats.h" // ps_checkposition_calls
 
+// Formerly called MAXRADIUS
+#define MAXTRYMOVE (32*FRACUNIT)
+
 fixed_t tmbbox[4];
 mobj_t *tmthing;
 static INT32 tmflags;
@@ -2165,15 +2168,10 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
 		}
 	}
 
-	// The bounding box is extended by MAXRADIUS
-	// because mobj_ts are grouped into mapblocks
-	// based on their origin point, and can overlap
-	// into adjacent blocks by up to MAXRADIUS units.
-
-	xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
-	xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
-	yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
-	yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
+	xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
+	xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
+	yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
+	yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
 
 	BMBOUNDFIX(xl, xh, yl, yh);
 
@@ -2393,11 +2391,6 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam)
 		}
 	}
 
-	// The bounding box is extended by MAXRADIUS
-	// because mobj_ts are grouped into mapblocks
-	// based on their origin point, and can overlap
-	// into adjacent blocks by up to MAXRADIUS units.
-
 	xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
 	xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
 	yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
@@ -2528,16 +2521,16 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam)
 		}
 
 		do {
-			if (x-tryx > MAXRADIUS)
-				tryx += MAXRADIUS;
-			else if (x-tryx < -MAXRADIUS)
-				tryx -= MAXRADIUS;
+			if (x-tryx > MAXTRYMOVE)
+				tryx += MAXTRYMOVE;
+			else if (x-tryx < -MAXTRYMOVE)
+				tryx -= MAXTRYMOVE;
 			else
 				tryx = x;
-			if (y-tryy > MAXRADIUS)
-				tryy += MAXRADIUS;
-			else if (y-tryy < -MAXRADIUS)
-				tryy -= MAXRADIUS;
+			if (y-tryy > MAXTRYMOVE)
+				tryy += MAXTRYMOVE;
+			else if (y-tryy < -MAXTRYMOVE)
+				tryy -= MAXTRYMOVE;
 			else
 				tryy = y;
 
@@ -2683,7 +2676,7 @@ increment_move
 	floatok = false;
 
 	// This makes sure that there are no freezes from computing extremely small movements.
-	// Originally was MAXRADIUS/2, but that can cause some bad inconsistencies for small players.
+	// Originally was MAXTRYMOVE/2, but that can cause some bad inconsistencies for small players.
 	radius = max(radius, thing->scale);
 
 	// And we also have to prevent Big Large (tm) movements, as those can skip too far
@@ -2872,10 +2865,10 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 	{
 		INT32 xl, xh, yl, yh;
 
-		yh = (unsigned)(thing->y + MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
-		yl = (unsigned)(thing->y - MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
-		xh = (unsigned)(thing->x + MAXRADIUS - bmaporgx)>>MAPBLOCKSHIFT;
-		xl = (unsigned)(thing->x - MAXRADIUS - bmaporgx)>>MAPBLOCKSHIFT;
+		yh = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
+		yl = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
+		xh = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
+		xl = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
 
 		BMBOUNDFIX(xl, xh, yl, yh);
 
@@ -2947,16 +2940,16 @@ boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y)
 	tryx = thing->x;
 	tryy = thing->y;
 	do {
-		if (x-tryx > MAXRADIUS)
-			tryx += MAXRADIUS;
-		else if (x-tryx < -MAXRADIUS)
-			tryx -= MAXRADIUS;
+		if (x-tryx > MAXTRYMOVE)
+			tryx += MAXTRYMOVE;
+		else if (x-tryx < -MAXTRYMOVE)
+			tryx -= MAXTRYMOVE;
 		else
 			tryx = x;
-		if (y-tryy > MAXRADIUS)
-			tryy += MAXRADIUS;
-		else if (y-tryy < -MAXRADIUS)
-			tryy -= MAXRADIUS;
+		if (y-tryy > MAXTRYMOVE)
+			tryy += MAXTRYMOVE;
+		else if (y-tryy < -MAXTRYMOVE)
+			tryy -= MAXTRYMOVE;
 		else
 			tryy = y;
 
@@ -4215,7 +4208,8 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
 	INT32 xl, xh, yl, yh;
 	fixed_t dist;
 
-	dist = FixedMul(damagedist, spot->scale) + MAXRADIUS;
+	dist = FixedMul(damagedist, spot->scale);
+
 	yh = (unsigned)(spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
 	yl = (unsigned)(spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
 	xh = (unsigned)(spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
@@ -4385,15 +4379,15 @@ static boolean P_CheckSectorPolyObjects(sector_t *sector, boolean realcrush, boo
 			{
 				mobj_t *mo;
 				blocknode_t *block;
+				blocknode_t *next = NULL;
 
 				if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
 					continue;
 
-				block = blocklinks[y * bmapwidth + x];
-
-				for (; block; block = block->mnext)
+				for (block = blocklinks[y * bmapwidth + x]; block != NULL; block = next)
 				{
 					mo = block->mobj;
+					next = block->mnext;
 
 					// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
 					if (!P_MobjInsidePolyobj(po, mo))
diff --git a/src/p_maputl.c b/src/p_maputl.c
index f10a396a3f2d2b74946a22f90c1f13a2c3f6bfc5..5398fd7a4c584022e8f33bb49312a176207e74b8 100644
--- a/src/p_maputl.c
+++ b/src/p_maputl.c
@@ -1052,35 +1052,23 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *))
 //
 boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *))
 {
-	mobj_t *bnext = NULL;
 	blocknode_t *block, *next = NULL;
 
 	if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
 		return true;
 
 	// Check interaction with the objects in the blockmap.
-	for (block = blocklinks[y*bmapwidth + x]; block; block = next)
+	for (block = blocklinks[y*bmapwidth + x]; block != NULL; block = next)
 	{
-		next = block->mnext;
-		if (next)
-			P_SetTarget(&bnext, next->mobj); // We want to note our reference to bnext here in case it is MF_NOTHINK and gets removed!
+		next = block->mnext; // We want to note our reference to mnext here!
 
 		if (!func(block->mobj))
-		{
-			P_SetTarget(&bnext, NULL);
 			return false;
-		}
 
-		if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, cannot continue.
-		|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
-		{
-			P_SetTarget(&bnext, NULL);
+		if (P_MobjWasRemoved(tmthing)) // func just popped our tmthing, cannot continue.
 			return true;
-		}
 	}
 
-	P_SetTarget(&bnext, NULL);
-
 	return true;
 }
 
diff --git a/src/p_mobj.c b/src/p_mobj.c
index fb9e7d78ef7174bde7bb71901a7da9f034b6eb35..dcebd334fbd2f1d974c25bb4994f48e38812b373 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -765,7 +765,7 @@ void P_EmeraldManager(void)
 
 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
 	{
-		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (think->removing)
 			continue;
 
 		mo = (mobj_t *)think;
@@ -3455,7 +3455,7 @@ void P_DestroyRobots(void)
 
 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
 	{
-		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (think->removing)
 			continue;
 
 		mo = (mobj_t *)think;
@@ -4254,7 +4254,7 @@ static void P_Boss3Thinker(mobj_t *mobj)
 			// this can happen if the boss was hurt earlier than expected
 			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 			{
-				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+				if (th->removing)
 					continue;
 
 				mo2 = (mobj_t *)th;
@@ -5343,7 +5343,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
 		// Build a hoop linked list of 'em!
 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			if (th->removing)
 				continue;
 
 			mo2 = (mobj_t *)th;
@@ -6045,7 +6045,7 @@ mobj_t *P_GetClosestAxis(mobj_t *source)
 	// scan the thinkers to find the closest axis point
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -9337,10 +9337,10 @@ static void P_PointPushThink(mobj_t *mobj)
 	radius = mobj->spawnpoint->args[0] << FRACBITS;
 
 	pushmobj = mobj;
-	xl = (unsigned)(mobj->x - radius - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
-	xh = (unsigned)(mobj->x + radius - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
-	yl = (unsigned)(mobj->y - radius - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
-	yh = (unsigned)(mobj->y + radius - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
+	xl = (unsigned)(mobj->x - radius - bmaporgx)>>MAPBLOCKSHIFT;
+	xh = (unsigned)(mobj->x + radius - bmaporgx)>>MAPBLOCKSHIFT;
+	yl = (unsigned)(mobj->y - radius - bmaporgy)>>MAPBLOCKSHIFT;
+	yh = (unsigned)(mobj->y + radius - bmaporgy)>>MAPBLOCKSHIFT;
 
 	P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushThing);
 }
@@ -11183,17 +11183,16 @@ tic_t itemrespawntime[ITEMQUESIZE];
 size_t iquehead, iquetail;
 
 #ifdef PARANOIA
-#define SCRAMBLE_REMOVED // Force debug build to crash when Removed mobj is accessed
+#define SCRAMBLE_REMOVED // Force debug build to crash when a removed mobj is accessed
 #endif
 void P_RemoveMobj(mobj_t *mobj)
 {
 	I_Assert(mobj != NULL);
-	if (P_MobjWasRemoved(mobj))
-		return; // something already removing this mobj.
+	if (P_MobjWasRemoved(mobj) || mobj->thinker.removing)
+		return; // Something already removed or is removing this mobj.
 
-	mobj->thinker.function.acp1 = (actionf_p1)P_RemoveThinkerDelayed; // shh. no recursing.
+	mobj->thinker.removing = true; // Set earlier to avoid recursion.
 	LUA_HookMobj(mobj, MOBJ_HOOK(MobjRemoved));
-	mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; // needed for P_UnsetThingPosition, etc. to work.
 
 	// Rings only, please!
 	if (mobj->spawnpoint &&
@@ -12863,7 +12862,7 @@ static boolean P_MapAlreadyHasStarPost(mobj_t *mobj)
 
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
diff --git a/src/p_polyobj.c b/src/p_polyobj.c
index 168fca61f331ce96772f6124270b0be8ab66f2df..3d1a38d36ff9d9b8f7324df0b18029e35519870d 100644
--- a/src/p_polyobj.c
+++ b/src/p_polyobj.c
@@ -878,15 +878,15 @@ static void Polyobj_carryThings(polyobj_t *po, fixed_t dx, fixed_t dy)
 		{
 			mobj_t *mo;
 			blocknode_t *block;
+			blocknode_t *next = NULL;
 
 			if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
 				continue;
 
-			block = blocklinks[y * bmapwidth + x];
-
-			for (; block; block = block->mnext)
+			for (block = blocklinks[y * bmapwidth + x]; block != NULL; block = next)
 			{
 				mo = block->mobj;
+				next = block->mnext;
 
 				if (mo->lastlook == pomovecount)
 					continue;
@@ -927,11 +927,11 @@ static INT32 Polyobj_clipThings(polyobj_t *po, line_t *line)
 	if (!(po->flags & POF_SOLID))
 		return hitflags;
 
-	// adjust linedef bounding box to blockmap, extend by MAXRADIUS
-	linebox[BOXLEFT]   = (unsigned)(line->bbox[BOXLEFT]   - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT;
-	linebox[BOXRIGHT]  = (unsigned)(line->bbox[BOXRIGHT]  - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT;
-	linebox[BOXBOTTOM] = (unsigned)(line->bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT;
-	linebox[BOXTOP]    = (unsigned)(line->bbox[BOXTOP]    - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT;
+	// adjust linedef bounding box to blockmap
+	linebox[BOXLEFT]   = (unsigned)(line->bbox[BOXLEFT]   - bmaporgx) >> MAPBLOCKSHIFT;
+	linebox[BOXRIGHT]  = (unsigned)(line->bbox[BOXRIGHT]  - bmaporgx) >> MAPBLOCKSHIFT;
+	linebox[BOXBOTTOM] = (unsigned)(line->bbox[BOXBOTTOM] - bmaporgy) >> MAPBLOCKSHIFT;
+	linebox[BOXTOP]    = (unsigned)(line->bbox[BOXTOP]    - bmaporgy) >> MAPBLOCKSHIFT;
 
 	// check all mobj blockmap cells the line contacts
 	for (y = linebox[BOXBOTTOM]; y <= linebox[BOXTOP]; ++y)
@@ -942,9 +942,11 @@ static INT32 Polyobj_clipThings(polyobj_t *po, line_t *line)
 			{
 				mobj_t *mo = NULL;
 				blocknode_t *block = blocklinks[y * bmapwidth + x];
+				blocknode_t *next = NULL;
 
-				for (; block; block = block->mnext)
+				for (; block != NULL; block = next)
 				{
+					next = block->mnext;
 					mo = block->mobj;
 
 					// Don't scroll objects that aren't affected by gravity
@@ -1115,15 +1117,15 @@ static void Polyobj_rotateThings(polyobj_t *po, vector2_t origin, angle_t delta,
 		{
 			mobj_t *mo;
 			blocknode_t *block;
+			blocknode_t *next = NULL;
 
 			if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
 				continue;
 
-			block = blocklinks[y * bmapwidth + x];
-
-			for (; block; block = block->mnext)
+			for (block = blocklinks[y * bmapwidth + x]; block != NULL; block = next)
 			{
 				mo = block->mobj;
+				next = block->mnext;
 
 				if (mo->lastlook == pomovecount)
 					continue;
@@ -1316,7 +1318,7 @@ void Polyobj_InitLevel(void)
 	// the mobj_t pointers on a queue for use below.
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo = (mobj_t *)th;
diff --git a/src/p_saveg.c b/src/p_saveg.c
index e939cf8e352257e78eec4f136f372616273e482c..650622f59f8130a896257748eb56a4116e963f7e 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -2972,8 +2972,7 @@ static void P_NetArchiveThinkers(save_t *save_p)
 		// save off the current thinkers
 		for (th = thlist[i].next; th != &thlist[i]; th = th->next)
 		{
-			if (!(th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed
-			 || th->function.acp1 == (actionf_p1)P_NullPrecipThinker))
+			if (!(th->removing || th->function.acp1 == (actionf_p1)P_NullPrecipThinker))
 				numsaved++;
 
 			if (th->function.acp1 == (actionf_p1)P_MobjThinker)
@@ -3186,7 +3185,7 @@ static void P_NetArchiveThinkers(save_t *save_p)
 			}
 #ifdef PARANOIA
 			else
-				I_Assert(th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed); // wait garbage collection
+				I_Assert(th->removing); // wait garbage collection
 #endif
 		}
 
@@ -3207,7 +3206,7 @@ mobj_t *P_FindNewPosition(UINT32 oldposition)
 
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mobj = (mobj_t *)th;
@@ -4528,7 +4527,7 @@ static inline void P_FinishMobjs(void)
 	for (currentthinker = thlist[THINK_MOBJ].next; currentthinker != &thlist[THINK_MOBJ];
 		currentthinker = currentthinker->next)
 	{
-		if (currentthinker->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (currentthinker->removing)
 			continue;
 
 		mobj = (mobj_t *)currentthinker;
@@ -4546,7 +4545,7 @@ static void P_RelinkPointers(void)
 	for (currentthinker = thlist[THINK_MOBJ].next; currentthinker != &thlist[THINK_MOBJ];
 		currentthinker = currentthinker->next)
 	{
-		if (currentthinker->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (currentthinker->removing)
 			continue;
 
 		mobj = (mobj_t *)currentthinker;
@@ -5376,7 +5375,7 @@ void P_SaveNetGame(save_t *save_p, boolean resending)
 	// Assign the mobjnumber for pointer tracking
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mobj = (mobj_t *)th;
diff --git a/src/p_setup.c b/src/p_setup.c
index 8f0d13190f3bc8e12ac529f31cfe5e92ebfd3fa1..a62e287ac19b8bf4608c8361a40b87e26d5b8a1a 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -692,7 +692,7 @@ void P_ReloadRings(void)
 	// scan the thinkers to find rings/spheres/hoops to unset
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo = (mobj_t *)th;
@@ -750,7 +750,7 @@ void P_SwitchSpheresBonusMode(boolean bonustime)
 	// scan the thinkers to find spheres to switch
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo = (mobj_t *)th;
@@ -7333,7 +7333,7 @@ void P_RespawnThings(void)
 
 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
 	{
-		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (think->removing)
 			continue;
 		P_RemoveMobj((mobj_t *)think);
 	}
@@ -7853,6 +7853,10 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 	maptol = mapheaderinfo[gamemap-1]->typeoflevel;
 	gametyperules = gametypedefaultrules[gametype];
 
+	// clear the target on map change, since the object will be invalidated
+	P_SetTarget(&ticcmd_ztargetfocus[0], NULL);
+	P_SetTarget(&ticcmd_ztargetfocus[1], NULL);
+
 	CON_Drawer(); // let the user know what we are going to do
 	I_FinishUpdate(); // page flip or blit buffer
 
@@ -8002,6 +8006,9 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 	// Free GPU textures before freeing patches.
 	if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
 		HWR_ClearAllTextures();
+
+	// Delete light table textures
+	HWR_ClearLightTables();
 #endif
 
 	Patch_FreeTag(PU_PATCH_LOWPRIORITY);
diff --git a/src/p_spec.c b/src/p_spec.c
index 9b124f9afcb6c29506c90996c346f82630e04a40..d375d3e2f23e7c65ad990b7b66a76965894be06a 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -3642,7 +3642,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					if (mo2->type != MT_EGGTRAP)
 						continue;
 
-					if (mo2->thinker.function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+					if (mo2->thinker.removing)
 						continue;
 
 					P_KillMobj(mo2, NULL, mo, 0);
@@ -3854,7 +3854,7 @@ void P_SetupSignExit(player_t *player)
 	// spin all signposts in the level then.
 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
 	{
-		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (think->removing)
 			continue;
 
 		thing = (mobj_t *)think;
@@ -3892,7 +3892,7 @@ boolean P_IsFlagAtBase(mobjtype_t flag)
 
 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
 	{
-		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (think->removing)
 			continue;
 
 		mo = (mobj_t *)think;
@@ -4395,7 +4395,7 @@ static void P_ProcessEggCapsule(player_t *player, sector_t *sector)
 	// The chimps are my friends.. heeheeheheehehee..... - LouisJM
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 		mo2 = (mobj_t *)th;
 		if (mo2->type != MT_EGGTRAP)
diff --git a/src/p_tick.c b/src/p_tick.c
index 68de091382d6a2ab76619e9bd4ccda36a6d693f5..db8688484ac3dbcf56fafc293af6bc995190e0f9 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -162,7 +162,7 @@ void Command_CountMobjs_f(void)
 
 			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 			{
-				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+				if (th->removing)
 					continue;
 
 				if (((mobj_t *)th)->type == i)
@@ -182,7 +182,7 @@ void Command_CountMobjs_f(void)
 
 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			if (th->removing)
 				continue;
 
 			if (((mobj_t *)th)->type == i)
@@ -348,6 +348,7 @@ void P_RemoveThinkerDelayed(thinker_t *thinker)
 void P_RemoveThinker(thinker_t *thinker)
 {
 	LUA_InvalidateUserdata(thinker);
+	thinker->removing = true;
 	thinker->function.acp1 = (actionf_p1)P_RemoveThinkerDelayed;
 }
 
diff --git a/src/p_user.c b/src/p_user.c
index a2bae228ba54d115ec94c9753a25fe30d54edc4a..3301987a2ac6ed576b762ccfc28a2adecd88e255 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -435,7 +435,7 @@ UINT8 P_FindLowestMare(void)
 	// to find the egg capsule with the lowest mare
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -488,7 +488,7 @@ boolean P_TransferToNextMare(player_t *player)
 	// to find the closest axis point
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -539,7 +539,7 @@ static mobj_t *P_FindAxis(INT32 mare, INT32 axisnum)
 	// to find the closest axis point
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -574,7 +574,7 @@ static mobj_t *P_FindAxisTransfer(INT32 mare, INT32 axisnum, mobjtype_t type)
 	// to find the closest axis point
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -615,7 +615,7 @@ void P_TransferToAxis(player_t *player, INT32 axisnum)
 	// to find the closest axis point
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -716,7 +716,7 @@ static void P_DeNightserizePlayer(player_t *player)
 	// Check to see if the player should be killed.
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -1899,7 +1899,7 @@ void P_SpawnShieldOrb(player_t *player)
 	// blaze through the thinkers to see if an orb already exists!
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		shieldobj = (mobj_t *)th;
@@ -5112,7 +5112,7 @@ void P_Telekinesis(player_t *player, fixed_t thrust, fixed_t range)
 
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -6479,7 +6479,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
 		// Find next waypoint
 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			if (th->removing)
 				continue;
 
 			mo2 = (mobj_t *)th;
@@ -6515,7 +6515,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
 		{
 			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 			{
-				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+				if (th->removing)
 					continue;
 
 				mo2 = (mobj_t *)th;
@@ -6544,7 +6544,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
 		{
 			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 			{
-				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+				if (th->removing)
 					continue;
 
 				mo2 = (mobj_t *)th;
@@ -7276,7 +7276,7 @@ static void P_NiGHTSMovement(player_t *player)
 		// to find the closest axis point
 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			if (th->removing)
 				continue;
 
 			mo2 = (mobj_t *)th;
@@ -8176,7 +8176,7 @@ void P_MovePlayer(player_t *player)
 
 			for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 			{
-				if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+				if (th->removing)
 					continue;
 
 				mo2 = (mobj_t *)th;
@@ -9161,7 +9161,7 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius)
 
 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
 	{
-		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (think->removing)
 			continue;
 
 		mo = (mobj_t *)think;
@@ -9259,7 +9259,7 @@ mobj_t *P_LookForFocusTarget(player_t *player, mobj_t *exclude, SINT8 direction,
 
 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
 	{
-		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (think->removing)
 			continue;
 
 		mo = (mobj_t *)think;
@@ -9378,7 +9378,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet)
 
 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
 	{
-		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (think->removing)
 			continue;
 
 		mo = (mobj_t *)think;
@@ -9524,7 +9524,7 @@ void P_FindEmerald(void)
 	// to find all emeralds
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
@@ -10925,7 +10925,7 @@ static mobj_t *P_GetAxis(INT32 num)
 
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mobj = (mobj_t *)th;
@@ -12007,7 +12007,7 @@ void P_PlayerThink(player_t *player)
 
 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 		{
-			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+			if (th->removing)
 				continue;
 
 			mo2 = (mobj_t *)th;
diff --git a/src/r_data.c b/src/r_data.c
index 244690ebe5e13626a856ff976e3db480cfc7254a..32faa07a8fb40370008218f2c4fa9ec3a4d3737b 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -426,9 +426,6 @@ void R_ClearColormaps(void)
 {
 	// Purged by PU_LEVEL, just overwrite the pointer
 	extra_colormaps = R_CreateDefaultColormap(true);
-#ifdef HWRENDER
-	HWR_ClearLightTables();
-#endif
 }
 
 //
diff --git a/src/r_plane.c b/src/r_plane.c
index 7642a4dd69a4310c78dc235f7fdc43d4d4a104bd..a0de048ea8c6fe7eac8265d1cdfd3559d9bba1d6 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -83,7 +83,7 @@ static fixed_t planeheight;
 fixed_t yslopetab[MAXVIDHEIGHT*16];
 fixed_t *yslope;
 
-static fixed_t xoffs, yoffs;
+static INT64 xoffs, yoffs;
 static dvector3_t slope_origin, slope_u, slope_v;
 static dvector3_t slope_lightu, slope_lightv;
 
@@ -376,19 +376,25 @@ visplane_t *R_FindPlane(sector_t *sector, fixed_t height, INT32 picnum, INT32 li
 	visplane_t *check;
 	unsigned hash;
 
+	float offset_xd = FixedToFloat(xoff) / FixedToFloat(xscale ? xscale : 1);
+	float offset_yd = FixedToFloat(yoff) / FixedToFloat(yscale ? yscale : 1);
+
+	INT64 offset_x = offset_xd * FRACUNIT;
+	INT64 offset_y = offset_yd * FRACUNIT;
+
 	if (!slope) // Don't mess with this right now if a slope is involved
 	{
-		xoff += FixedMul(viewx, xscale);
-		yoff -= FixedMul(viewy, yscale);
+		offset_x += viewx;
+		offset_y -= viewy;
 
 		if (plangle != 0)
 		{
 			// Add the view offset, rotated by the plane angle.
 			float ang = ANG2RAD(plangle);
-			float x = FixedToFloat(xoff);
-			float y = FixedToFloat(yoff);
-			xoff = FloatToFixed(x * cos(ang) + y * sin(ang));
-			yoff = FloatToFixed(-x * sin(ang) + y * cos(ang));
+			float x = offset_x / (float)FRACUNIT;
+			float y = offset_y / (float)FRACUNIT;
+			offset_x = (x * cos(ang) + y * sin(ang)) * FRACUNIT;
+			offset_y = (-x * sin(ang) + y * cos(ang)) * FRACUNIT;
 		}
 	}
 
@@ -399,16 +405,19 @@ visplane_t *R_FindPlane(sector_t *sector, fixed_t height, INT32 picnum, INT32 li
 			float ang = ANG2RAD(polyobj->angle);
 			float x = FixedToFloat(polyobj->centerPt.x);
 			float y = FixedToFloat(polyobj->centerPt.y);
-			xoff -= FloatToFixed(x * cos(ang) + y * sin(ang));
-			yoff -= FloatToFixed(x * sin(ang) - y * cos(ang));
+			offset_x -= (x * cos(ang) + y * sin(ang)) * FRACUNIT;
+			offset_y -= (x * sin(ang) - y * cos(ang)) * FRACUNIT;
 		}
 		else
 		{
-			xoff -= polyobj->centerPt.x;
-			yoff += polyobj->centerPt.y;
+			offset_x -= polyobj->centerPt.x;
+			offset_y += polyobj->centerPt.y;
 		}
 	}
 
+	offset_x = ((INT64)offset_x * xscale) / FRACUNIT;
+	offset_y = ((INT64)offset_y * yscale) / FRACUNIT;
+
 	// This appears to fix the Nimbus Ruins sky bug.
 	if (picnum == skyflatnum && pfloor)
 	{
@@ -423,7 +432,7 @@ visplane_t *R_FindPlane(sector_t *sector, fixed_t height, INT32 picnum, INT32 li
 		{
 			if (height == check->height && picnum == check->picnum
 				&& lightlevel == check->lightlevel
-				&& xoff == check->xoffs && yoff == check->yoffs
+				&& offset_x == check->xoffs && offset_y == check->yoffs
 				&& xscale == check->xscale && yscale == check->yscale
 				&& planecolormap == check->extra_colormap
 				&& check->viewx == viewx && check->viewy == viewy && check->viewz == viewz
@@ -449,8 +458,8 @@ visplane_t *R_FindPlane(sector_t *sector, fixed_t height, INT32 picnum, INT32 li
 	check->lightlevel = lightlevel;
 	check->minx = vid.width;
 	check->maxx = -1;
-	check->xoffs = xoff;
-	check->yoffs = yoff;
+	check->xoffs = offset_x;
+	check->yoffs = offset_y;
 	check->xscale = xscale;
 	check->yscale = yscale;
 	check->extra_colormap = planecolormap;
@@ -658,13 +667,13 @@ static void R_DrawSkyPlane(visplane_t *pl)
 }
 
 // Returns the height of the sloped plane at (x, y) as a double
-static double R_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y)
+static double R_GetSlopeZAt(const pslope_t *slope, INT64 x, INT64 y)
 {
 	// If you want to reimplement this using just the equation constants, use this instead:
 	// (d + a*x + b*y) * -(1.0 / c)
 
-	double px = FixedToDouble(x) - slope->dorigin.x;
-	double py = FixedToDouble(y) - slope->dorigin.y;
+	double px = (x / (double)FRACUNIT) - slope->dorigin.x;
+	double py = (y / (double)FRACUNIT) - slope->dorigin.y;
 
 	double dist = (px * slope->dnormdir.x) + (py * slope->dnormdir.y);
 
@@ -672,10 +681,10 @@ static double R_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y)
 }
 
 // Sets the texture origin vector of the sloped plane.
-static void R_SetSlopePlaneOrigin(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xoff, fixed_t yoff, fixed_t angle)
+static void R_SetSlopePlaneOrigin(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, INT64 xoff, INT64 yoff, fixed_t angle)
 {
-	INT64 vx = (INT64)xpos + (INT64)xoff;
-	INT64 vy = (INT64)ypos - (INT64)yoff;
+	INT64 vx = (INT64)xpos + xoff;
+	INT64 vy = (INT64)ypos - yoff;
 
 	float vxf = vx / (float)FRACUNIT;
 	float vyf = vy / (float)FRACUNIT;
@@ -702,7 +711,7 @@ void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos,
 		slope->moved = false;
 	}
 
-	R_SetSlopePlaneOrigin(slope, xpos, ypos, zpos, xoff, yoff, angle);
+	R_SetSlopePlaneOrigin(slope, xpos, ypos, zpos, (INT64)xoff, (INT64)yoff, angle);
 	height = R_GetSlopeZAt(slope, xpos, ypos);
 	zeroheight = height - FixedToDouble(zpos);
 
@@ -735,7 +744,7 @@ void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos,
 }
 
 // This function calculates all of the vectors necessary for drawing a sloped and scaled plane.
-void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xs, fixed_t ys, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle)
+void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xs, fixed_t ys, INT64 xoff, INT64 yoff, angle_t angle, angle_t plangle)
 {
 	double height, z_at_xy;
 	float ang;
@@ -836,9 +845,11 @@ static void CalcSlopePlaneVectors(visplane_t *pl, fixed_t xoff, fixed_t yoff)
 {
 	if (!ds_fog && (pl->xscale != FRACUNIT || pl->yscale != FRACUNIT))
 	{
+		float offset_x = FixedToFloat(xoff) / FixedToFloat(pl->xscale ? pl->xscale : 1);
+		float offset_y = FixedToFloat(yoff) / FixedToFloat(pl->yscale ? pl->yscale : 1);
 		R_SetScaledSlopePlane(pl->slope, pl->viewx, pl->viewy, pl->viewz,
 			FixedDiv(FRACUNIT, pl->xscale), FixedDiv(FRACUNIT, pl->yscale),
-			FixedDiv(xoff, pl->xscale), FixedDiv(yoff, pl->yscale), pl->viewangle, pl->plangle);
+			(INT64)(offset_x * FRACUNIT), (INT64)(offset_y * FRACUNIT), pl->viewangle, pl->plangle);
 	}
 	else
 		R_SetSlopePlane(pl->slope, pl->viewx, pl->viewy, pl->viewz, xoff, yoff, pl->viewangle, pl->plangle);
diff --git a/src/r_plane.h b/src/r_plane.h
index 69620f25e07cadefee8242f23ad3a26a57ffbc9d..cd947750119b8446e0683bd4d62ae7b88fa9c1a7 100644
--- a/src/r_plane.h
+++ b/src/r_plane.h
@@ -48,7 +48,7 @@ typedef struct visplane_s
 	UINT16 padbottomstart, bottom[MAXVIDWIDTH], padbottomend;
 	INT32 high, low; // R_PlaneBounds should set these.
 
-	fixed_t xoffs, yoffs; // Scrolling flats.
+	INT64 xoffs, yoffs; // Scrolling flats.
 	fixed_t xscale, yscale;
 
 	sector_t *sector;
@@ -85,7 +85,7 @@ void R_DrawSinglePlane(visplane_t *pl);
 
 // Calculates the slope vectors needed for tilted span drawing.
 void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle);
-void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xs, fixed_t ys, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle);
+void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xs, fixed_t ys, INT64 xoff, INT64 yoff, angle_t angle, angle_t plangle);
 
 typedef struct planemgr_s
 {
diff --git a/src/r_splats.c b/src/r_splats.c
index 813c4b2430b53941c449450c9b5926e2e922ec80..ce35a35b4c3ff24d1045a170a67d421310f837af 100644
--- a/src/r_splats.c
+++ b/src/r_splats.c
@@ -381,7 +381,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
 
 	if (pSplat->slope)
 	{
-		R_SetScaledSlopePlane(pSplat->slope, vis->viewpoint.x, vis->viewpoint.y, vis->viewpoint.z, pSplat->xscale, pSplat->yscale, -pSplat->verts[0].x, pSplat->verts[0].y, vis->viewpoint.angle, pSplat->angle);
+		R_SetScaledSlopePlane(pSplat->slope, vis->viewpoint.x, vis->viewpoint.y, vis->viewpoint.z, (INT64)pSplat->xscale, (INT64)pSplat->yscale, -pSplat->verts[0].x, pSplat->verts[0].y, vis->viewpoint.angle, pSplat->angle);
 	}
 	else if (!ds_solidcolor)
 	{
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 9fe50a6a2347583f0fe705ff5c6f6a0466ea51a9..07088b9578c2df2923700e37ee17d99005b1faef 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -2307,7 +2307,7 @@ void I_Sleep(UINT32 ms)
 
 void I_SleepDuration(precise_t duration)
 {
-#if defined(__linux__) || defined(__FreeBSD__)
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__HAIKU__)
 	UINT64 precision = I_GetPrecisePrecision();
 	struct timespec ts = {
 		.tv_sec = duration / precision,
diff --git a/src/st_stuff.c b/src/st_stuff.c
index a44771e4b6041dc1390c3967a7ee6497d8a6f1cb..5bb3aa98c88bdb4303d8d23bd6570f24bbb1f5e9 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2655,7 +2655,7 @@ static boolean ST_doItemFinderIconsAndSound(void)
 	// Scan thinkers to find emblem mobj with these ids
 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
+		if (th->removing)
 			continue;
 
 		mo2 = (mobj_t *)th;
diff --git a/src/w_wad.c b/src/w_wad.c
index 57dd09de61889449141b33f93938d0de86aed754..50ba622a9b32f538b18d50dcdf29fbfd457f7355 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1358,17 +1358,6 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump)
 	return i;
 }
 
-// Returns 0 if the folder is not empty, 1 if it is empty, -1 if it doesn't exist
-INT32 W_IsFolderEmpty(const char *name, UINT16 wad)
-{
-	UINT16 start = W_CheckNumForFolderStartPK3(name, wad, 0);
-	if (start == INT16_MAX)
-		return -1;
-
-	// Unlike W_CheckNumForFolderStartPK3, W_CheckNumForFolderEndPK3 doesn't return INT16_MAX.
-	return W_CheckNumForFolderEndPK3(name, wad, start) <= start;
-}
-
 char *W_GetLumpFolderPathPK3(UINT16 wad, UINT16 lump)
 {
 	const char *fullname = wadfiles[wad]->lumpinfo[lump].fullname;
@@ -1733,10 +1722,14 @@ static UINT16 W_CheckNumForPatchNamePwad(const char *name, UINT16 wad, boolean l
 	// TODO: cache namespace lump IDs
 	if (W_FileHasFolders(wadfiles[wad]))
 	{
-		if (!W_IsFolderEmpty("Flats/", wad))
+		start = W_CheckNumForFolderStartPK3("Flats/", wad, 0);
+		end = W_CheckNumForFolderEndPK3("Flats/", wad, start);
+
+		// if the start and end is the same, the folder is empty
+		if (end <= start)
 		{
-			start = W_CheckNumForFolderStartPK3("Flats/", wad, 0);
-			end = W_CheckNumForFolderEndPK3("Flats/", wad, start);
+			start = INT16_MAX;
+			end = INT16_MAX;
 		}
 	}
 	else
diff --git a/src/w_wad.h b/src/w_wad.h
index 5872ae50a98c017961cf6964ab203d71151d8f4d..84aafa3a40e2e48f662c723d5a5854138806b5b0 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -180,7 +180,6 @@ UINT16 W_CheckNumForMarkerStartPwad(const char *name, UINT16 wad, UINT16 startlu
 UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump);
 UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump);
 UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump);
-INT32 W_IsFolderEmpty(const char *name, UINT16 wad);
 char *W_GetLumpFolderPathPK3(UINT16 wad, UINT16 lump);
 char *W_GetLumpFolderNamePK3(UINT16 wad, UINT16 lump);