diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c
index 8d47f3dc1e31a092f8a9a25e9e9fe667ca178092..6b4b6229f68ffc869e1f6eccdd6999018d150ae0 100644
--- a/src/lua_blockmaplib.c
+++ b/src/lua_blockmaplib.c
@@ -34,46 +34,28 @@ typedef UINT8 (*blockmap_func)(lua_State *, INT32, INT32, mobj_t *);
 static boolean blockfuncerror = false; // errors should only print once per search blockmap call
 
 // Helper function for "objects" search
-static UINT8 lib_searchBlockmap_Objects(lua_State *L, INT32 x, INT32 y, mobj_t *thing)
+static UINT8 lib_searchBlockmap_Objects(lua_State *L, mobj_t *thing, mobj_t *mobj)
 {
-	mobj_t *mobj, *bnext = NULL;
-
-	if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
-		return 0;
-
-	// Check interaction with the objects in the blockmap.
-	for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext)
-	{
-		P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed!
-		if (mobj == thing)
-			continue; // our thing just found itself, so move on
-		lua_pushvalue(L, 1); // push function
-		LUA_PushUserdata(L, thing, META_MOBJ);
-		LUA_PushUserdata(L, mobj, META_MOBJ);
-		if (lua_pcall(gL, 2, 1, 0)) {
-			if (!blockfuncerror || cv_debug & DBG_LUA)
-				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
-			lua_pop(gL, 1);
-			blockfuncerror = true;
-			P_SetTarget(&bnext, NULL);
-			return 0; // *shrugs*
-		}
-		if (!lua_isnil(gL, -1))
-		{ // if nil, continue
-			P_SetTarget(&bnext, NULL);
-			if (lua_toboolean(gL, -1))
-				return 2; // stop whole search
-			else
-				return 1; // stop block search
-		}
+	lua_pushvalue(L, 1); // push function
+	LUA_PushUserdata(L, thing, META_MOBJ);
+	LUA_PushUserdata(L, mobj, META_MOBJ);
+	if (lua_pcall(gL, 2, 1, 0)) {
+		if (!blockfuncerror || cv_debug & DBG_LUA)
+			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 		lua_pop(gL, 1);
-		if (P_MobjWasRemoved(thing) // func just popped our thing, cannot continue.
-		|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
-		{
-			P_SetTarget(&bnext, NULL);
-			return (P_MobjWasRemoved(thing)) ? 2 : 1;
-		}
+		blockfuncerror = true;
+		return 0; // *shrugs*
 	}
+	if (!lua_isnil(gL, -1))
+	{ // if nil, continue
+		if (lua_toboolean(gL, -1))
+			return 2; // stop whole search
+		else
+			return 1; // stop block search
+	}
+	lua_pop(gL, 1);
+	if (P_MobjWasRemoved(thing)) // func just popped our thing, cannot continue.
+		return P_MobjWasRemoved(thing) ? 2 : 1;
 	return 0;
 }
 
@@ -233,6 +215,7 @@ static int lib_searchBlockmap(lua_State *L)
 	boolean retval = true;
 	UINT8 funcret = 0;
 	blockmap_func searchFunc;
+	boolean searchObjects = false;
 
 	lua_remove(L, 1); // remove searchtype, stack is now function, mobj, [x1, x2, y1, y2]
 	luaL_checktype(L, 1, LUA_TFUNCTION);
@@ -241,7 +224,8 @@ static int lib_searchBlockmap(lua_State *L)
 	{
 		case 0: // "objects"
 		default:
-			searchFunc = lib_searchBlockmap_Objects;
+			searchObjects = true;
+			searchFunc = NULL;
 			break;
 		case 1: // "lines"
 			searchFunc = lib_searchBlockmap_Lines;
@@ -286,24 +270,66 @@ static int lib_searchBlockmap(lua_State *L)
 	BMBOUNDFIX(xl, xh, yl, yh);
 
 	blockfuncerror = false; // reset
-	validcount++;
-	for (bx = xl; bx <= xh; bx++)
-		for (by = yl; by <= yh; by++)
-		{
-			funcret = searchFunc(L, bx, by, mobj);
-			// return value of searchFunc determines searchFunc's return value and/or when to stop
-			if (funcret == 2){ // stop whole search
-				lua_pushboolean(L, false); // return false
-				return 1;
+
+	if (!searchObjects) {
+		validcount++;
+
+		for (bx = xl; bx <= xh; bx++)
+			for (by = yl; by <= yh; by++)
+			{
+				funcret = searchFunc(L, bx, by, mobj);
+
+				// return value of searchFunc determines searchFunc's return value and/or when to stop
+				if (funcret == 2) { // stop whole search
+					lua_pushboolean(L, false); // return false
+					return 1;
+				}
+				else if (funcret == 1) // search was interrupted for this block
+					retval = false; // this changes the return value, but doesn't stop the whole search
+
+				// else don't do anything, continue as normal
+				if (P_MobjWasRemoved(mobj)) { // ...unless the original object was removed
+					lua_pushboolean(L, false); // in which case we have to stop now regardless
+					return 1;
+				}
 			}
-			else if (funcret == 1) // search was interrupted for this block
-				retval = false; // this changes the return value, but doesn't stop the whole search
-			// else don't do anything, continue as normal
-			if (P_MobjWasRemoved(mobj)){ // ...unless the original object was removed
-				lua_pushboolean(L, false); // in which case we have to stop now regardless
-				return 1;
+	}
+	else {
+		bthingit_t *it = P_NewBlockThingsIterator(xl, yl, xh, yh);
+		if (!it) {
+			lua_pushboolean(L, false);
+			return 1;
+		}
+
+		mobj_t *itmobj = NULL;
+
+		do
+		{
+			itmobj = P_BlockThingsIteratorNext(it, false);
+			if (itmobj)
+			{
+				if (mobj == itmobj)
+					continue; // our thing just found itself, so move on
+
+				funcret = lib_searchBlockmap_Objects(L, mobj, itmobj);
+				if (funcret == 2) {
+					lua_pushboolean(L, false);
+					return 1;
+				}
+				else if (funcret == 1)
+					retval = false;
+
+				if (P_MobjWasRemoved(mobj)) {
+					retval = false;
+					break;
+				}
 			}
 		}
+		while (itmobj != NULL);
+
+		P_FreeBlockThingsIterator(it);
+	}
+
 	lua_pushboolean(L, retval);
 	return 1;
 }
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 85e4590c571e23cfa701f6f9fb15b5948350e1f0..5a3f8ad114b9f1269e3a49b76cb4aa9dd76417ad 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -355,8 +355,12 @@ static int mobj_get(lua_State *L)
 		lua_pushinteger(L, mo->blendmode);
 		break;
 	case mobj_bnext:
-		LUA_PushUserdata(L, mo->bnext, META_MOBJ);
-		break;
+		if (mo->blocknode && mo->blocknode->bnext) {
+			LUA_PushUserdata(L, mo->blocknode->bnext->mobj, META_MOBJ);
+			break;
+		}
+		else
+			return 0;
 	case mobj_bprev:
 		// bprev -- same deal as sprev above, but for the blockmap.
 		return UNIMPLEMENTED;
@@ -669,7 +673,6 @@ static int mobj_set(lua_State *L)
 				sector_list = NULL;
 			}
 			mo->snext = NULL, mo->sprev = NULL;
-			mo->bnext = NULL, mo->bprev = NULL;
 			P_SetThingPosition(mo);
 		}
 		else
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 4d268117f7f72c4d2de3582e9ae5f15a8c0fa24e..5314de8abfa7807fee22d2f2e08dde8d50ac33ec 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -5821,15 +5821,12 @@ void A_MinusDigging(mobj_t *actor)
 		fixed_t yl = (unsigned)(actor->y - radius - bmaporgy) >> MAPBLOCKSHIFT;
 		fixed_t xh = (unsigned)(actor->x + radius - bmaporgx) >> MAPBLOCKSHIFT;
 		fixed_t xl = (unsigned)(actor->x - radius - bmaporgx) >> MAPBLOCKSHIFT;
-		fixed_t bx, by;
 
 		BMBOUNDFIX(xl, xh, yl, yh);
 
 		minus = actor;
 
-		for (bx = xl; bx <= xh; bx++)
-			for (by = yl; by <= yh; by++)
-				P_BlockThingsIterator(bx, by, PIT_MinusCarry);
+		P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_MinusCarry);
 	}
 	else
 	{
@@ -13889,7 +13886,7 @@ void A_DustDevilThink(mobj_t *actor)
 {
 	fixed_t scale = actor->scale;
 	mobj_t *layer = actor->tracer;
-	INT32 bx, by, xl, xh, yl, yh;
+	INT32 xl, xh, yl, yh;
 	fixed_t radius = actor->radius;
 
 	if (LUA_CallAction(A_DUSTDEVILTHINK, actor))
@@ -13953,9 +13950,7 @@ void A_DustDevilThink(mobj_t *actor)
 
 	dustdevil = actor;
 
-	for (bx = xl; bx <= xh; bx++)
-		for (by = yl; by <= yh; by++)
-			P_BlockThingsIterator(bx, by, PIT_DustDevilLaunch);
+	P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_DustDevilLaunch);
 
 	//Whirlwind sound effect.
 	if (leveltime % 70 == 0)
@@ -14035,7 +14030,6 @@ static boolean PIT_TNTExplode(mobj_t *nearby)
 void A_TNTExplode(mobj_t *actor)
 {
 	INT32 locvar1 = var1;
-	INT32 x, y;
 	INT32 xl, xh, yl, yh;
 	static mappoint_t epicenter = {0,0,0};
 
@@ -14072,9 +14066,7 @@ void A_TNTExplode(mobj_t *actor)
 
 	barrel = actor;
 
-	for (x = xl; x <= xh; x++)
-		for (y = yl; y <= yh; y++)
-			P_BlockThingsIterator(x, y, PIT_TNTExplode);
+	P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_TNTExplode);
 
 	// cause a quake -- P_StartQuake does not exist yet
 	epicenter.x = actor->x;
diff --git a/src/p_local.h b/src/p_local.h
index 8bdc89841fd28e60623ea00cdf575e48e3d87065..3b61b92295363a8d0ec789ce7920cb94cf417c2d 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -462,7 +462,7 @@ extern INT32 bmapwidth;
 extern INT32 bmapheight; // in mapblocks
 extern fixed_t bmaporgx;
 extern fixed_t bmaporgy; // origin of block map
-extern mobj_t **blocklinks; // for thing chains
+extern blocknode_t **blocklinks; // for thing chains
 
 //
 // P_INTER
diff --git a/src/p_map.c b/src/p_map.c
index e28f4ae450f581e6006f0b65b2dbf88e7f97d46a..7887c117de56907d7a51eccb3715ec0a32e14d61 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -752,20 +752,25 @@ static void P_PlayerBarrelCollide(mobj_t *toucher, mobj_t *barrel)
 		P_DamageMobj(barrel, toucher, toucher, 1, 0);
 }
 
-//
-// PIT_CheckThing
-//
-static boolean PIT_CheckThing(mobj_t *thing)
+enum
+{
+	CHECKTHING_NOCOLLIDE,
+	CHECKTHING_COLLIDE,
+	CHECKTHING_DONE,
+	CHECKTHING_IGNORE
+};
+
+static unsigned PIT_DoCheckThing(mobj_t *thing)
 {
 	fixed_t blockdist;
 
 	// don't clip against self
 	if (thing == tmthing)
-		return true;
+		return CHECKTHING_IGNORE;
 
 	// Ignore... things.
 	if (!tmthing || !thing || P_MobjWasRemoved(thing))
-		return true;
+		return CHECKTHING_IGNORE;
 
 	I_Assert(!P_MobjWasRemoved(tmthing));
 	I_Assert(!P_MobjWasRemoved(thing));
@@ -773,7 +778,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	// Ignore spectators
 	if ((tmthing->player && tmthing->player->spectator)
 	|| (thing->player && thing->player->spectator))
-		return true;
+		return CHECKTHING_IGNORE;
 
 	// Do name checks all the way up here
 	// So that NOTHING ELSE can see MT_NAMECHECK because it is client-side.
@@ -781,24 +786,24 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	{
 		// Ignore things that aren't players, ignore spectators, ignore yourself.
 		if (!thing->player || !(tmthing->target && tmthing->target->player) || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player))
-			return true;
+			return CHECKTHING_IGNORE;
 
 		// Now check that you actually hit them.
 		blockdist = thing->radius + tmthing->radius;
 		if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
-			return true; // didn't hit it
+			return CHECKTHING_NOCOLLIDE; // didn't hit it
 		// see if it went over / under
 		if (tmthing->z > thing->z + thing->height)
-			return true; // overhead
+			return CHECKTHING_NOCOLLIDE; // overhead
 		if (tmthing->z + tmthing->height < thing->z)
-			return true; // underneath
+			return CHECKTHING_NOCOLLIDE; // underneath
 
-		// REX HAS SEEN YOU
+		// Call any SeenPlayer Lua hooks
 		if (!LUA_HookSeenPlayer(tmthing->target->player, thing->player))
-			return false;
+			return CHECKTHING_DONE;
 
 		seenplayer = thing->player;
-		return false;
+		return CHECKTHING_DONE;
 	}
 
 	// Metal Sonic destroys tiny baby objects.
@@ -808,15 +813,15 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	|| thing->type == MT_WALLSPIKE)))
 	{
 		if ((thing->flags & (MF_ENEMY|MF_BOSS)) && (thing->health <= 0 || !(thing->flags & MF_SHOOTABLE)))
-			return true;
+			return CHECKTHING_IGNORE;
 		blockdist = thing->radius + tmthing->radius;
 		if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
-			return true; // didn't hit it
+			return CHECKTHING_NOCOLLIDE; // didn't hit it
 		// see if it went over / under
 		if (tmthing->z > thing->z + thing->height)
-			return true; // overhead
+			return CHECKTHING_NOCOLLIDE; // overhead
 		if (tmthing->z + tmthing->height < thing->z)
-			return true; // underneath
+			return CHECKTHING_NOCOLLIDE; // underneath
 		if (thing->type == MT_SPIKE
 		|| thing->type == MT_WALLSPIKE)
 		{
@@ -832,28 +837,28 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			thing->health = 0;
 			P_KillMobj(thing, tmthing, tmthing, 0);
 		}
-		return true;
+		return CHECKTHING_COLLIDE;
 	}
 
 	// STR_SPIKE users destroy spikes
 	if ((tmthing->player) && ((tmthing->player->powers[pw_strong] & STR_SPIKE) && (thing->type == MT_SPIKE || thing->type == MT_WALLSPIKE)))
 	{
 		mobj_t *iter;
-        	blockdist = thing->radius + tmthing->radius;
-        	if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
-            		return true; // didn't hit it
-        	// see if it went over / under
-        	if (tmthing->z > thing->z + thing->height)
-            		return true; // overhead
-        	if (tmthing->z + tmthing->height < thing->z)
-            		return true; // underneath
+		blockdist = thing->radius + tmthing->radius;
+		if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
+			return CHECKTHING_NOCOLLIDE; // didn't hit it
+		// see if it went over / under
+		if (tmthing->z > thing->z + thing->height)
+			return CHECKTHING_NOCOLLIDE; // overhead
+		if (tmthing->z + tmthing->height < thing->z)
+			return CHECKTHING_NOCOLLIDE; // underneath
 
 		if (thing->flags & MF_SOLID)
 			S_StartSound(tmthing, thing->info->deathsound);
 		for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
 			if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
 				P_KillMobj(iter, tmthing, tmthing, 0);
-		return true;
+		return CHECKTHING_COLLIDE;
 	}
 
 	// vectorise metal - done in a special case as at this point neither has the right flags for touching
@@ -865,30 +870,30 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		blockdist = thing->radius + tmthing->radius;
 
 		if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
-			return true; // didn't hit it
+			return CHECKTHING_NOCOLLIDE; // didn't hit it
 
 		if (tmthing->z > thing->z + thing->height)
-			return true; // overhead
+			return CHECKTHING_NOCOLLIDE; // overhead
 		if (tmthing->z + tmthing->height < thing->z)
-			return true; // underneath
+			return CHECKTHING_NOCOLLIDE; // underneath
 
 		thing->flags2 |= MF2_CLASSICPUSH;
 
-		return true;
+		return CHECKTHING_COLLIDE;
 	}
 
 	if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING)))
-		return true;
+		return CHECKTHING_IGNORE;
 
 	// Don't collide with your buddies while NiGHTS-flying.
 	if (tmthing->player && thing->player && (maptol & TOL_NIGHTS)
 		&& ((tmthing->player->powers[pw_carry] == CR_NIGHTSMODE) || (thing->player->powers[pw_carry] == CR_NIGHTSMODE)))
-		return true;
+		return CHECKTHING_IGNORE;
 
 	blockdist = thing->radius + tmthing->radius;
 
 	if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
-		return true; // didn't hit it
+		return CHECKTHING_NOCOLLIDE; // didn't hit it
 
 	if (thing->flags & MF_PAPERCOLLISION) // CAUTION! Very easy to get stuck inside MF_SOLID objects. Giving the player MF_PAPERCOLLISION is a bad idea unless you know what you're doing.
 	{
@@ -915,23 +920,23 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			fixed_t tmcosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
 			fixed_t tmsinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
 			if (abs(thing->x - tmx) >= (abs(tmcosradius) + abs(cosradius)) || abs(thing->y - tmy) >= (abs(tmsinradius) + abs(sinradius)))
-				return true; // didn't hit it
+				return CHECKTHING_NOCOLLIDE; // didn't hit it
 			check1 = P_PointOnLineSide(tmx - tmcosradius, tmy - tmsinradius, &junk);
 			check2 = P_PointOnLineSide(tmx + tmcosradius, tmy + tmsinradius, &junk);
 			check3 = P_PointOnLineSide(tmx + tmthing->momx - tmcosradius, tmy + tmthing->momy - tmsinradius, &junk);
 			check4 = P_PointOnLineSide(tmx + tmthing->momx + tmcosradius, tmy + tmthing->momy + tmsinradius, &junk);
 			if ((check1 == check2) && (check2 == check3) && (check3 == check4))
-				return true; // the line doesn't cross between collider's start or end
+				return CHECKTHING_NOCOLLIDE; // the line doesn't cross between collider's start or end
 		}
 		else
 		{
 			if (abs(thing->x - tmx) >= (tmthing->radius + abs(cosradius)) || abs(thing->y - tmy) >= (tmthing->radius + abs(sinradius)))
-				return true; // didn't hit it
+				return CHECKTHING_NOCOLLIDE; // didn't hit it
 			if ((P_PointOnLineSide(tmx - tmthing->radius, tmy - tmthing->radius, &junk)
 			== P_PointOnLineSide(tmx + tmthing->radius, tmy + tmthing->radius, &junk))
 			&& (P_PointOnLineSide(tmx + tmthing->radius, tmy - tmthing->radius, &junk)
 			== P_PointOnLineSide(tmx - tmthing->radius, tmy + tmthing->radius, &junk)))
-				return true; // the line doesn't cross between either pair of opposite corners
+				return CHECKTHING_NOCOLLIDE; // the line doesn't cross between either pair of opposite corners
 		}
 	}
 	else if (tmthing->flags & MF_PAPERCOLLISION)
@@ -944,7 +949,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		tmsinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
 
 		if (abs(thing->x - tmx) >= (thing->radius + abs(tmcosradius)) || abs(thing->y - tmy) >= (thing->radius + abs(tmsinradius)))
-			return true; // didn't hit it
+			return CHECKTHING_NOCOLLIDE; // didn't hit it
 
 		v1.x = tmx - tmcosradius;
 		v1.y = tmy - tmsinradius;
@@ -961,32 +966,32 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		== P_PointOnLineSide(thing->x + thing->radius, thing->y + thing->radius, &junk))
 		&& (P_PointOnLineSide(thing->x + thing->radius, thing->y - thing->radius, &junk)
 		== P_PointOnLineSide(thing->x - thing->radius, thing->y + thing->radius, &junk)))
-			return true; // the line doesn't cross between either pair of opposite corners
+			return CHECKTHING_NOCOLLIDE; // the line doesn't cross between either pair of opposite corners
 	}
 
 	{
 		UINT8 shouldCollide = LUA_Hook2Mobj(thing, tmthing, MOBJ_HOOK(MobjCollide)); // checks hook for thing's type
 		if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing))
-			return true; // one of them was removed???
+			return CHECKTHING_NOCOLLIDE; // one of them was removed???
 		if (shouldCollide == 1)
-			return false; // force collide
+			return CHECKTHING_DONE; // force collide
 		else if (shouldCollide == 2)
-			return true; // force no collide
+			return CHECKTHING_NOCOLLIDE; // force no collide
 
 		shouldCollide = LUA_Hook2Mobj(tmthing, thing, MOBJ_HOOK(MobjMoveCollide)); // checks hook for tmthing's type
 		if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing))
-			return true; // one of them was removed???
+			return CHECKTHING_NOCOLLIDE; // one of them was removed???
 		if (shouldCollide == 1)
-			return false; // force collide
+			return CHECKTHING_DONE; // force collide
 		else if (shouldCollide == 2)
-			return true; // force no collide
+			return CHECKTHING_NOCOLLIDE; // force no collide
 	}
 
 	if (tmthing->type == MT_LAVAFALL_LAVA && (thing->type == MT_RING || thing->type == MT_REDTEAMRING || thing->type == MT_BLUETEAMRING || thing->type == MT_FLINGRING))
 	{
 		//height check
 		if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !(thing->health))
-			return true;
+			return CHECKTHING_NOCOLLIDE;
 
 		P_KillMobj(thing, tmthing, tmthing, DMG_FIRE);
 	}
@@ -995,7 +1000,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	{
 		//height check
 		if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !(thing->health))
-			return true;
+			return CHECKTHING_NOCOLLIDE;
 
 		if (thing->type == MT_TNTBARREL)
 			P_KillMobj(thing, tmthing, tmthing->target, 0);
@@ -1018,7 +1023,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			fixed_t s = FINESINE((ang >> ANGLETOFINESHIFT) & FINEMASK);
 			S_StartSound(tmthing, thing->info->activesound);
 			thing->extravalue2 += 2*FixedMul(s, dm)/3;
-			return true;
+			return CHECKTHING_COLLIDE;
 		}
 	}
 
@@ -1026,14 +1031,14 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	{
 		if (((thing->flags2 & MF2_AMBUSH) && (tmthing->z <= thing->z + thing->height) && (tmthing->z + tmthing->height >= thing->z))
 			|| (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer)))
-			return true;
+			return CHECKTHING_COLLIDE;
 	}
 
 	if (thing->type == MT_ROLLOUTROCK && tmthing->player && tmthing->health)
 	{
 		if (tmthing->player->powers[pw_carry] == CR_ROLLOUT)
 		{
-			return true;
+			return CHECKTHING_NOCOLLIDE;
 		}
 		if ((thing->flags & MF_PUSHABLE) // not carrying a player
 			&& (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something
@@ -1052,26 +1057,26 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			P_SetTarget(&tmthing->tracer, thing);
 			if (!P_IsObjectOnGround(thing))
 				thing->momz += tmthing->momz;
-			return true;
+			return CHECKTHING_COLLIDE;
 		}
 	}
 	else if (tmthing->type == MT_ROLLOUTROCK)
 	{
 		if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !thing->health)
-			return true;
+			return CHECKTHING_NOCOLLIDE;
 
 		if (thing == tmthing->tracer) // don't collide with rider
-			return true;
+			return CHECKTHING_IGNORE;
 
 		if (thing->flags & MF_SPRING) // bounce on springs
 		{
 			P_DoSpring(thing, tmthing);
-			return true;
+			return CHECKTHING_COLLIDE;
 		}
 		else if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) == (MF_MONITOR|MF_SHOOTABLE) && !(tmthing->flags & MF_PUSHABLE)) // pop monitors while carrying a player
 		{
 			P_KillMobj(thing, tmthing, tmthing->tracer, 0);
-			return true;
+			return CHECKTHING_COLLIDE;
 		}
 
 		if (thing->type == tmthing->type // bounce against other rollout rocks
@@ -1106,11 +1111,11 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	if (tmthing->type == MT_FANG && thing->type == MT_FSGNB)
 	{
 		if (thing->z > tmthing->z + tmthing->height)
-			return true; // overhead
+			return CHECKTHING_NOCOLLIDE; // overhead
 		if (thing->z + thing->height < tmthing->z)
-			return true; // underneath
+			return CHECKTHING_NOCOLLIDE; // underneath
 		if (!thing->tracer || !thing->tracer->tracer)
-			return true;
+			return CHECKTHING_IGNORE;
 		P_SlapStick(tmthing, thing);
 		// no return value was used in the original prototype script at this point,
 		// so I'm assuming we fall back on the solid code to determine how it all ends?
@@ -1123,13 +1128,13 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		if (tmthing->type == MT_BIGMINE)
 		{
 			if (!tmthing->momx && !tmthing->momy)
-				return true;
+				return CHECKTHING_IGNORE;
 			if ((statenum_t)(thing->state-states) >= thing->info->meleestate)
-				return true;
+				return CHECKTHING_IGNORE;
 			if (thing->z > tmthing->z + tmthing->height)
-				return true; // overhead
+				return CHECKTHING_NOCOLLIDE; // overhead
 			if (thing->z + thing->height < tmthing->z)
-				return true; // underneath
+				return CHECKTHING_NOCOLLIDE; // underneath
 
 			thing->momx = tmthing->momx/3;
 			thing->momy = tmthing->momy/3;
@@ -1141,18 +1146,18 @@ static boolean PIT_CheckThing(mobj_t *thing)
 				S_StartSound(thing, thing->info->activesound);
 			P_SetMobjState(thing, thing->info->meleestate);
 			P_SetTarget(&thing->tracer, tmthing->tracer);
-			return true;
+			return CHECKTHING_COLLIDE;
 		}
 		else if (tmthing->type == MT_CRUSHCLAW)
 		{
 			if (tmthing->extravalue1 <= 0)
-				return true;
+				return CHECKTHING_IGNORE;
 			if ((statenum_t)(thing->state-states) >= thing->info->meleestate)
-				return true;
+				return CHECKTHING_IGNORE;
 			if (thing->z > tmthing->z + tmthing->height)
-				return true; // overhead
+				return CHECKTHING_NOCOLLIDE; // overhead
 			if (thing->z + thing->height < tmthing->z)
-				return true; // underneath
+				return CHECKTHING_NOCOLLIDE; // underneath
 
 			thing->momx = P_ReturnThrustX(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3);
 			thing->momy = P_ReturnThrustY(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3);
@@ -1161,7 +1166,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			P_SetMobjState(thing, thing->info->meleestate);
 			if (tmthing->tracer)
 				P_SetTarget(&thing->tracer, tmthing->tracer->target);
-			return false;
+			return CHECKTHING_DONE;
 		}
 	}
 
@@ -1169,9 +1174,9 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	if (tmthing->type == MT_SPIKE && (thing->flags & MF_SOLID) && (tmthing->flags & MF_SOLID))
 	{
 		if (thing->z > tmthing->z + tmthing->height)
-			return true; // overhead
+			return CHECKTHING_NOCOLLIDE; // overhead
 		if (thing->z + thing->height < tmthing->z)
-			return true; // underneath
+			return CHECKTHING_NOCOLLIDE; // underneath
 
 		if (tmthing->eflags & MFE_VERTICALFLIP)
 			P_SetOrigin(thing, thing->x, thing->y, tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale));
@@ -1179,16 +1184,16 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			P_SetOrigin(thing, thing->x, thing->y, tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale));
 		if (thing->flags & MF_SHOOTABLE)
 			P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
-		return true;
+		return CHECKTHING_COLLIDE;
 	}
 
 	if (thing->flags & MF_PAIN && tmthing->player)
 	{ // Player touches painful thing sitting on the floor
 		// see if it went over / under
 		if (thing->z > tmthing->z + tmthing->height)
-			return true; // overhead
+			return CHECKTHING_NOCOLLIDE; // overhead
 		if (thing->z + thing->height < tmthing->z)
-			return true; // underneath
+			return CHECKTHING_NOCOLLIDE; // underneath
 		if (tmthing->flags & MF_SHOOTABLE && thing->health > 0)
 		{
 			UINT32 damagetype = (thing->info->mass & 0xFF);
@@ -1196,16 +1201,17 @@ static boolean PIT_CheckThing(mobj_t *thing)
 				damagetype = DMG_FIRE;
 			if (P_DamageMobj(tmthing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8)))
 				S_StartSound(thing, damagetype);
+			return CHECKTHING_COLLIDE;
 		}
-		return true;
+		return CHECKTHING_NOCOLLIDE;
 	}
 	else if (tmthing->flags & MF_PAIN && thing->player)
 	{ // Painful thing splats player in the face
 		// see if it went over / under
 		if (tmthing->z > thing->z + thing->height)
-			return true; // overhead
+			return CHECKTHING_NOCOLLIDE; // overhead
 		if (tmthing->z + tmthing->height < thing->z)
-			return true; // underneath
+			return CHECKTHING_NOCOLLIDE; // underneath
 		if (thing->flags & MF_SHOOTABLE && tmthing->health > 0)
 		{
 			UINT32 damagetype = (tmthing->info->mass & 0xFF);
@@ -1213,32 +1219,33 @@ static boolean PIT_CheckThing(mobj_t *thing)
 				damagetype = DMG_FIRE;
 			if (P_DamageMobj(thing, tmthing, tmthing, 1, damagetype) && (damagetype = (tmthing->info->mass>>8)))
 				S_StartSound(tmthing, damagetype);
+			return CHECKTHING_COLLIDE;
 		}
-		return true;
+		return CHECKTHING_NOCOLLIDE;
 	}
 
 	if (thing->type == MT_HOOPCOLLIDE && thing->flags & MF_SPECIAL && tmthing->player)
 	{
 		P_TouchSpecialThing(thing, tmthing, true);
-		return true;
+		return CHECKTHING_COLLIDE;
 	}
 
 	// check for skulls slamming into things
 	if (tmthing->flags2 & MF2_SKULLFLY)
 	{
 		if (tmthing->type == MT_EGGMOBILE) // Don't make Eggman stop!
-			return true; // Let him RUN YOU RIGHT OVER. >:3
+			return CHECKTHING_COLLIDE; // Let him RUN YOU RIGHT OVER. >:3
 		else
 		{
 			// see if it went over / under
 			if (tmthing->z > thing->z + thing->height)
-				return true; // overhead
+				return CHECKTHING_NOCOLLIDE; // overhead
 			if (tmthing->z + tmthing->height < thing->z)
-				return true; // underneath
+				return CHECKTHING_NOCOLLIDE; // underneath
 
 			tmthing->flags2 &= ~MF2_SKULLFLY;
 			tmthing->momx = tmthing->momy = tmthing->momz = 0;
-			return false; // stop moving
+			return CHECKTHING_DONE; // stop moving
 		}
 	}
 
@@ -1254,10 +1261,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		if ((tmznext <= thzh && tmz > thzh) || (tmznext > thzh - sprarea && tmznext < thzh))
 		{
 			P_DoSpring(thing, tmthing);
-			return true;
+			return CHECKTHING_COLLIDE;
 		}
 		else if (tmz > thzh - sprarea && tmz < thzh) // Don't damage people springing up / down
-			return true;
+			return CHECKTHING_NOCOLLIDE;
 	}
 
 	// missiles can hit other things
@@ -1265,31 +1272,31 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	{
 		// see if it went over / under
 		if (tmthing->z > thing->z + thing->height)
-			return true; // overhead
+			return CHECKTHING_NOCOLLIDE; // overhead
 		if (tmthing->z + tmthing->height < thing->z)
-			return true; // underneath
+			return CHECKTHING_NOCOLLIDE; // underneath
 
 		if (tmthing->type != MT_SHELL && tmthing->target && tmthing->target->type == thing->type)
 		{
 			// Don't hit same species as originator.
 			if (thing == tmthing->target)
-				return true;
+				return CHECKTHING_IGNORE;
 
 			if (thing->type != MT_PLAYER)
 			{
 				// Explode, but do no damage.
 				// Let players missile other players.
-				return false;
+				return CHECKTHING_DONE;
 			}
 		}
 
 		// Special case for bounce rings so they don't get caught behind solid objects.
 		if ((tmthing->type == MT_THROWNBOUNCE && tmthing->fuse > 8*TICRATE) && thing->flags & MF_SOLID)
-			return true;
+			return CHECKTHING_IGNORE;
 
 		// Missiles ignore Brak's helper.
 		if (thing->type == MT_BLACKEGGMAN_HELPER)
-			return true;
+			return CHECKTHING_IGNORE;
 
 		// Hurting Brak
 		if (tmthing->type == MT_BLACKEGGMAN_MISSILE
@@ -1298,19 +1305,22 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			// Not if Brak's already in pain
 			if (!(thing->state >= &states[S_BLACKEGG_PAIN1] && thing->state <= &states[S_BLACKEGG_PAIN35]))
 				P_SetMobjState(thing, thing->info->painstate);
-			return false;
+			return CHECKTHING_DONE;
 		}
 
 		if (!(thing->flags & MF_SHOOTABLE) && !(thing->type == MT_EGGSHIELD))
 		{
 			// didn't do any damage
-			return !(thing->flags & MF_SOLID);
+			if (thing->flags & MF_SOLID)
+				return CHECKTHING_COLLIDE;
+			else
+				return CHECKTHING_NOCOLLIDE;
 		}
 
 		if (tmthing->flags & MF_MISSILE && thing->player && tmthing->target && tmthing->target->player
 		&& thing->player->ctfteam == tmthing->target->player->ctfteam
 		&& thing->player->powers[pw_carry] == CR_PLAYER && thing->tracer == tmthing->target)
-			return true; // Don't give rings to your carry player by accident.
+			return CHECKTHING_IGNORE; // Don't give rings to your carry player by accident.
 
 		if (thing->type == MT_EGGSHIELD)
 		{
@@ -1318,7 +1328,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 
 			if (angle < ANGLE_180) // hit shield from behind, shield is destroyed!
 				P_KillMobj(thing, tmthing, tmthing, 0);
-			return false;
+			return CHECKTHING_DONE;
 		}
 
 		// damage / explode
@@ -1344,7 +1354,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			if (!demoplayback || P_ControlStyle(thing->player) == CS_LMAOGALOG)
 				P_SetPlayerAngle(thing->player, thing->angle);
 
-			return true;
+			return CHECKTHING_COLLIDE;
 		}
 		else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player && ((thing->player->powers[pw_carry] == CR_GENERIC) || (thing->player->pflags & PF_JUMPED)))
 		{
@@ -1371,11 +1381,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			P_KillMobj(tmthing, NULL, NULL, 0);
 
 		// don't traverse any more
-
 		if (tmthing->type == MT_SHELL)
-			return true;
+			return CHECKTHING_COLLIDE;
 		else
-			return false;
+			return CHECKTHING_DONE;
 	}
 
 	if (thing->flags & MF_PUSHABLE && (tmthing->player || tmthing->flags & MF_PUSHABLE)
@@ -1478,13 +1487,13 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	if (thing->flags & MF_SPECIAL && tmthing->player)
 	{
 		P_TouchSpecialThing(thing, tmthing, true); // can remove thing
-		return true;
+		return CHECKTHING_COLLIDE;
 	}
 	// check again for special pickup
 	if (tmthing->flags & MF_SPECIAL && thing->player)
 	{
 		P_TouchSpecialThing(tmthing, thing, true); // can remove thing
-		return true;
+		return CHECKTHING_COLLIDE;
 	}
 
 	// Sprite Spikes!
@@ -1544,7 +1553,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			if (playerangle > ANGLE_180)
 				playerangle = InvAngle(playerangle);
 			if (playerangle < ANGLE_90)
-				return true; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them.
+				return CHECKTHING_IGNORE; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them.
 		}
 
 		bottomz = thing->z;
@@ -1578,15 +1587,15 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		if (thing->type == MT_FAN || thing->type == MT_STEAM)
 		{
 			P_DoFanAndGasJet(thing, tmthing);
-			return true;
+			return CHECKTHING_COLLIDE;
 		}
 		else if (thing->flags & MF_SPRING)
 		{
 			if ( thing->z <= tmthing->z + tmthing->height
 			&& tmthing->z <= thing->z + thing->height)
 				if (P_DoSpring(thing, tmthing))
-					return false;
-			return true;
+					return CHECKTHING_DONE;
+			return CHECKTHING_COLLIDE;
 		}
 	}
 
@@ -1595,7 +1604,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	{
 		if ((thing->z + thing->height >= tmthing->z)
 		&& (tmthing->z + tmthing->height >= thing->z))
-			return false;
+			return CHECKTHING_DONE;
 	}
 
 	// Damage other players when invincible
@@ -1630,7 +1639,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		if (tmthing->player && thing->player)
 		{
 			P_DoTailsCarry(thing->player, tmthing->player);
-			return true;
+			return CHECKTHING_COLLIDE;
 		}
 	}
 	else if (thing->player) {
@@ -1677,7 +1686,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	if (tmthing->player) // Is the moving/interacting object the player?
 	{
 		if (!tmthing->health)
-			return true;
+			return CHECKTHING_IGNORE;
 
 		if (thing->type == MT_FAN || thing->type == MT_STEAM)
 			P_DoFanAndGasJet(thing, tmthing);
@@ -1686,14 +1695,16 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			if ( thing->z <= tmthing->z + tmthing->height
 			&& tmthing->z <= thing->z + thing->height)
 				if (P_DoSpring(thing, tmthing))
-					return false;
-			return true;
+					return CHECKTHING_DONE;
+			return CHECKTHING_COLLIDE;
 		}
 		// Monitor?
 		else if (thing->flags & MF_MONITOR
 		&& !((thing->type == MT_RING_REDBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_RING_BLUEBOX && tmthing->player->ctfteam != 2))
 		&& (!(thing->flags & MF_SOLID) || P_PlayerCanDamage(tmthing->player, thing)))
 		{
+			unsigned collide = CHECKTHING_NOCOLLIDE;
+
 			if (thing->z - thing->scale <= tmthing->z + tmthing->height
 			&& thing->z + thing->height + thing->scale >= tmthing->z)
 			{
@@ -1725,21 +1736,25 @@ static boolean PIT_CheckThing(mobj_t *thing)
 							*momz /= 2;
 						*momz -= (*momz/(underwater ? 8 : 4)); // Cap the height!
 					}
+					collide = CHECKTHING_COLLIDE;
 				}
 				if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough.
 				{
 					if (player->pflags & PF_BOUNCING)
 						P_DoAbilityBounce(player, false);
-					return false;
+					collide = CHECKTHING_DONE;
 				}
 				else
 					*z -= *momz; // to ensure proper collision.
 			}
 
-			return true;
+			return collide;
 		}
 	}
 
+	// not solid not blocked
+	unsigned collide = CHECKTHING_NOCOLLIDE;
+
 	if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player))
 		; // springs, gas jets and springs should never be able to step up onto a player
 	// z checking at last
@@ -1762,25 +1777,27 @@ static boolean PIT_CheckThing(mobj_t *thing)
 					tmfloorrover = NULL;
 					tmfloorslope = NULL;
 				}
-				return true;
+				return CHECKTHING_COLLIDE;
 			}
 
 			topz = thing->z - thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways
 
 			// block only when jumping not high enough,
 			// (dont climb max. 24units while already in air)
-			// since return false doesn't handle momentum properly,
+			// since return CHECKTHING_DONE doesn't handle momentum properly,
 			// we lie to P_TryMove() so it's always too high
 			if (tmthing->player && tmthing->z + tmthing->height > topz
 				&& tmthing->z + tmthing->height < tmthing->ceilingz)
 			{
 				if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack...
-					return false;
+					return CHECKTHING_DONE;
 
 				tmfloorz = tmceilingz = topz; // block while in air
 				tmceilingrover = NULL;
 				tmceilingslope = NULL;
 				tmfloorthing = thing; // needed for side collision
+
+				collide = CHECKTHING_COLLIDE;
 			}
 			else if (topz < tmceilingz && tmthing->z <= thing->z+thing->height)
 			{
@@ -1788,6 +1805,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
 				tmceilingrover = NULL;
 				tmceilingslope = NULL;
 				tmfloorthing = thing; // thing we may stand on
+
+				collide = CHECKTHING_COLLIDE;
 			}
 		}
 		else
@@ -1803,25 +1822,27 @@ static boolean PIT_CheckThing(mobj_t *thing)
 					tmceilingrover = NULL;
 					tmceilingslope = NULL;
 				}
-				return true;
+				return CHECKTHING_COLLIDE;
 			}
 
 			topz = thing->z + thing->height + thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways
 
 			// block only when jumping not high enough,
 			// (dont climb max. 24units while already in air)
-			// since return false doesn't handle momentum properly,
+			// since return CHECKTHING_DONE doesn't handle momentum properly,
 			// we lie to P_TryMove() so it's always too high
 			if (tmthing->player && tmthing->z < topz
 				&& tmthing->z > tmthing->floorz)
 			{
 				if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack...
-					return false;
+					return CHECKTHING_DONE;
 
 				tmfloorz = tmceilingz = topz; // block while in air
 				tmfloorrover = NULL;
 				tmfloorslope = NULL;
 				tmfloorthing = thing; // needed for side collision
+
+				collide = CHECKTHING_COLLIDE;
 			}
 			else if (topz > tmfloorz && tmthing->z+tmthing->height >= thing->z)
 			{
@@ -1829,12 +1850,18 @@ static boolean PIT_CheckThing(mobj_t *thing)
 				tmfloorrover = NULL;
 				tmfloorslope = NULL;
 				tmfloorthing = thing; // thing we may stand on
+
+				collide = CHECKTHING_COLLIDE;
 			}
 		}
 	}
 
-	// not solid not blocked
-	return true;
+	return collide;
+}
+
+static boolean PIT_CheckThing(mobj_t *thing)
+{
+	return PIT_DoCheckThing(thing) != CHECKTHING_DONE;
 }
 
 // PIT_CheckCameraLine
@@ -2010,38 +2037,6 @@ static boolean PIT_CheckLine(line_t *ld)
 // =========================================================================
 //                         MOVEMENT CLIPPING
 // =========================================================================
-
-//
-// P_CheckPosition
-// This is purely informative, nothing is modified
-// (except things picked up).
-//
-// in:
-//  a mobj_t (can be valid or invalid)
-//  a position to be checked
-//   (doesn't need to be related to the mobj_t->x,y)
-//
-// during:
-//  special things are touched if MF_PICKUP
-//  early out on solid lines?
-//
-// out:
-//  newsubsec
-//  tmfloorz
-//  tmceilingz
-//  tmdropoffz
-//  tmdrpoffceilz
-//   the lowest point contacted
-//   (monsters won't move to a dropoff)
-//  speciallines[]
-//  numspeciallines
-//
-
-// tmfloorz
-//     the nearest floor or thing's top under tmthing
-// tmceilingz
-//     the nearest ceiling or thing's bottom over tmthing
-//
 boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
 {
 	INT32 xl, xh, yl, yh, bx, by;
@@ -2282,9 +2277,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
 	if (tmflags & MF_NOCLIP)
 		return true;
 
-	// Check things first, possibly picking things up.
-
-	// MF_NOCLIPTHING: used by camera to not be blocked by things
+	// Check things first.
 	if (!(thing->flags & MF_NOCLIPTHING))
 	{
 		for (bx = xl; bx <= xh; bx++)
@@ -2893,7 +2886,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 	// standing on top and move it, too.
 	if (thing->flags & MF_PUSHABLE)
 	{
-		INT32 bx, by, xl, xh, yl, yh;
+		INT32 xl, xh, yl, yh;
 
 		yh = (unsigned)(thing->y + MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
 		yl = (unsigned)(thing->y - MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
@@ -2906,9 +2899,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 		standx = x;
 		standy = y;
 
-		for (by = yl; by <= yh; by++)
-			for (bx = xl; bx <= xh; bx++)
-				P_BlockThingsIterator(bx, by, PIT_PushableMoved);
+		P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushableMoved);
 	}
 
 	// Link the thing into its new position
@@ -4217,7 +4208,6 @@ static boolean PIT_RadiusAttack(mobj_t *thing)
 //
 void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype, boolean sightcheck)
 {
-	INT32 x, y;
 	INT32 xl, xh, yl, yh;
 	fixed_t dist;
 
@@ -4235,9 +4225,7 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
 	bombdamagetype = damagetype;
 	bombsightcheck = sightcheck;
 
-	for (y = yl; y <= yh; y++)
-		for (x = xl; x <= xh; x++)
-			P_BlockThingsIterator(x, y, PIT_RadiusAttack);
+	P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_RadiusAttack);
 }
 
 //
@@ -4392,16 +4380,18 @@ static boolean P_CheckSectorPolyObjects(sector_t *sector, boolean realcrush, boo
 			for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
 			{
 				mobj_t *mo;
+				blocknode_t *block;
 
 				if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
 					continue;
 
-				mo = blocklinks[y * bmapwidth + x];
+				block = blocklinks[y * bmapwidth + x];
 
-				for (; mo; mo = mo->bnext)
+				for (; block; block = block->mnext)
 				{
-					// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
+					mo = block->mobj;
 
+					// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
 					if (!P_MobjInsidePolyobj(po, mo))
 						continue;
 
diff --git a/src/p_maputl.c b/src/p_maputl.c
index 9c30d3ead094bcbbcf7e3c98e90fc159285a36b0..758a71ca343b86c4c07181f1e0ef31b52dc7dfdf 100644
--- a/src/p_maputl.c
+++ b/src/p_maputl.c
@@ -680,6 +680,35 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
 	openrange = opentop - openbottom;
 }
 
+static blocknode_t *freeblocks;
+
+static blocknode_t *P_CreateBlockNode(mobj_t *thing, int x, int y)
+{
+	blocknode_t *block;
+
+	if (freeblocks != NULL)
+	{
+		block = freeblocks;
+		freeblocks = block->bnext;
+	}
+	else
+		block = Z_Malloc(sizeof(blocknode_t), PU_LEVEL, NULL);
+
+	block->blockindex = x + y*bmapwidth;
+	block->mobj = thing;
+	block->mnext = NULL;
+	block->mprev = NULL;
+	block->bprev = NULL;
+	block->bnext = NULL;
+
+	return block;
+}
+
+static void P_ReleaseBlockNode(blocknode_t *node)
+{
+	node->bnext = freeblocks;
+	freeblocks = node;
+}
 
 //
 // THING POSITION SETTING
@@ -730,20 +759,20 @@ void P_UnsetThingPosition(mobj_t *thing)
 
 	if (!(thing->flags & MF_NOBLOCKMAP))
 	{
-		/* inert things don't need to be in blockmap
-		*
-		* killough 8/11/98: simpler scheme using pointers-to-pointers for prev
-		* pointers, allows head node pointers to be treated like everything else
-		*
-		* Also more robust, since it doesn't depend on current position for
-		* unlinking. Old method required computing head node based on position
-		* at time of unlinking, assuming it was the same position as during
-		* linking.
-		*/
+		// [RH] Unlink from all blocks this actor uses
+		blocknode_t *block = thing->blocknode;
 
-		mobj_t *bnext, **bprev = thing->bprev;
-		if (bprev && (*bprev = bnext = thing->bnext) != NULL)  // unlink from block map
-			bnext->bprev = bprev;
+		while (block != NULL)
+		{
+			if (block->mnext != NULL)
+				block->mnext->mprev = block->mprev;
+			*(block->mprev) = block->mnext;
+			blocknode_t *next = block->bnext;
+			P_ReleaseBlockNode(block);
+			block = next;
+		}
+
+		thing->blocknode = NULL;
 	}
 }
 
@@ -814,24 +843,45 @@ void P_SetThingPosition(mobj_t *thing)
 	if (!(thing->flags & MF_NOBLOCKMAP))
 	{
 		// inert things don't need to be in blockmap
-		const INT32 blockx = (unsigned)(thing->x - bmaporgx)>>MAPBLOCKSHIFT;
-		const INT32 blocky = (unsigned)(thing->y - bmaporgy)>>MAPBLOCKSHIFT;
-		if (blockx >= 0 && blockx < bmapwidth
-			&& blocky >= 0 && blocky < bmapheight)
+		INT32 x1 = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
+		INT32 y1 = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
+		INT32 x2 = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
+		INT32 y2 = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
+
+		thing->blocknode = NULL;
+
+		blocknode_t **alink = &thing->blocknode;
+
+		if (!(x1 >= bmapwidth || x2 < 0 || y1 >= bmapheight || y2 < 0))
 		{
-			// killough 8/11/98: simpler scheme using
-			// pointer-to-pointer prev pointers --
-			// allows head nodes to be treated like everything else
-
-			mobj_t **link = &blocklinks[blocky*bmapwidth + blockx];
-			mobj_t *bnext = *link;
-			if ((thing->bnext = bnext) != NULL)
-				bnext->bprev = &thing->bnext;
-			thing->bprev = link;
-			*link = thing;
+			// [RH] Link into every block this actor touches, not just the center one
+			x1 = max(0, x1);
+			y1 = max(0, y1);
+			x2 = min(bmapwidth - 1, x2);
+			y2 = min(bmapheight - 1, y2);
+			for (int y = y1; y <= y2; ++y)
+			{
+				for (int x = x1; x <= x2; ++x)
+				{
+					blocknode_t **link = &blocklinks[y*bmapwidth + x];
+					blocknode_t *node = P_CreateBlockNode(thing, x, y);
+
+					// Link in to block
+					if ((node->mnext = *link) != NULL)
+					{
+						(*link)->mprev = &node->mnext;
+					}
+					node->mprev = link;
+					*link = node;
+
+					// Link in to actor
+					node->bprev = alink;
+					node->bnext = NULL;
+					(*alink) = node;
+					alink = &node->bnext;
+				}
+			}
 		}
-		else // thing is off the map
-			thing->bnext = NULL, thing->bprev = NULL;
 	}
 
 	// Allows you to 'step' on a new linedef exec when the previous
@@ -971,34 +1021,220 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *))
 	return true; // Everything was checked.
 }
 
-
 //
 // P_BlockThingsIterator
 //
 boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *))
 {
-	mobj_t *mobj, *bnext = NULL;
+	mobj_t *mobj;
+	blocknode_t *block;
 
 	if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
 		return true;
 
 	// Check interaction with the objects in the blockmap.
-	for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext)
+	for (block = blocklinks[y*bmapwidth + x]; block; block = block->mnext)
 	{
-		P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed!
+		mobj = block->mobj;
+
 		if (!func(mobj))
-		{
-			P_SetTarget(&bnext, NULL);
 			return false;
+		if (P_MobjWasRemoved(tmthing)) // func just broke blockmap chain, cannot continue.
+			return true;
+	}
+
+	return true;
+}
+
+boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *))
+{
+	boolean status = true;
+
+	for (INT32 bx = x1; bx <= x2; bx++)
+		for (INT32 by = y1; by <= y2; by++)
+			if (!P_BlockThingsIterator(bx, by, func))
+				status = false;
+
+	return status;
+}
+
+static bthingit_hash_entry_t *GetHashEntryForIterator(bthingit_t *it, int i)
+{
+	if (i < NUM_BTHINGIT_FIXEDHASH)
+		return &it->fixedhash[i];
+	else
+		return &it->dynhash[i - NUM_BTHINGIT_FIXEDHASH];
+}
+
+static blocknode_t *GetBlockmapBlock(int x, int y)
+{
+	if (x >= 0 && y >= 0 && x < bmapwidth && y < bmapheight)
+	{
+		return blocklinks[y*bmapwidth + x];
+	}
+	else
+	{
+		// invalid block
+		return NULL;
+	}
+}
+
+static bthingit_t *freeiters;
+
+bthingit_t *P_NewBlockThingsIterator(int x1, int y1, int x2, int y2)
+{
+	bthingit_t *it;
+	blocknode_t *block;
+
+	x1 = max(0, x1);
+	y1 = max(0, y1);
+	x2 = min(bmapwidth - 1, x2);
+	y2 = min(bmapheight - 1, y2);
+
+	if (x1 > x2 || y1 > y2)
+		return NULL;
+
+	block = GetBlockmapBlock(x1, y1);
+	if (!block)
+		return NULL;
+
+	if (freeiters != NULL)
+	{
+		it = freeiters;
+		freeiters = it->freechain;
+	}
+	else
+		it = Z_Calloc(sizeof(bthingit_t), PU_LEVEL, NULL);
+
+	it->x1 = x1;
+	it->y1 = y1;
+	it->x2 = x2;
+	it->y2 = y2;
+	it->curx = x1;
+	it->cury = y1;
+	it->block = block;
+	it->freechain = NULL;
+	it->numfixedhash = 0;
+	it->dynhashcount = 0;
+
+	for (size_t i = 0; i < NUM_BTHINGIT_BUCKETS; i++)
+		it->buckets[i] = -1;
+
+	return it;
+}
+
+mobj_t *P_BlockThingsIteratorNext(bthingit_t *it, boolean centeronly)
+{
+	for (;;)
+	{
+		while (it->block != NULL)
+		{
+			mobj_t *mobj = it->block->mobj;
+			blocknode_t *node = it->block;
+
+			it->block = it->block->mnext;
+
+			// Don't recheck things that were already checked
+			if (node->bnext == NULL && node->bprev == &mobj->blocknode)
+			{
+				// This actor doesn't span blocks, so we know it can only ever be checked once.
+				return mobj;
+			}
+			else
+			{
+				// Block boundaries for compatibility mode
+				if (centeronly)
+				{
+					fixed_t blockleft = (it->curx * MAPBLOCKUNITS) + bmaporgx;
+					fixed_t blockright = blockleft + MAPBLOCKUNITS;
+					fixed_t blockbottom = (it->cury * MAPBLOCKUNITS) + bmaporgy;
+					fixed_t blocktop = blockbottom + MAPBLOCKUNITS;
+
+					// only return actors with the center in this block
+					if (mobj->x >= blockleft && mobj->x < blockright &&
+						mobj->y >= blockbottom && mobj->y < blocktop)
+					{
+						return mobj;
+					}
+				}
+				else
+				{
+					bthingit_hash_entry_t *entry;
+					int i;
+					size_t hash = ((size_t)mobj >> 3) % NUM_BTHINGIT_BUCKETS;
+
+					for (i = it->buckets[hash]; i >= 0; )
+					{
+						entry = GetHashEntryForIterator(it, i);
+						if (entry->mobj == mobj)
+						{
+							// I've already been checked. Skip to the next mobj.
+							break;
+						}
+						i = entry->next;
+					}
+
+					if (i < 0)
+					{
+						// Add mobj to the hash table and return it.
+						if (it->numfixedhash < NUM_BTHINGIT_FIXEDHASH)
+						{
+							entry = &it->fixedhash[it->numfixedhash];
+							entry->next = it->buckets[hash];
+							it->buckets[hash] = it->numfixedhash++;
+						}
+						else
+						{
+							if (!it->dynhash)
+							{
+								it->dynhashcapacity = 50;
+								Z_Calloc(it->dynhashcapacity * sizeof(it->dynhashcapacity), PU_LEVEL, &it->dynhash);
+							}
+							if (it->dynhashcount == it->dynhashcapacity)
+							{
+								it->dynhashcapacity *= 2;
+								it->dynhash = Z_Realloc(it->dynhash, it->dynhashcapacity * sizeof(it->dynhashcapacity), PU_LEVEL, &it->dynhash);
+							}
+							i = (int)it->dynhashcount;
+							it->dynhashcount++;
+							entry = &it->dynhash[i];
+							entry->next = it->buckets[hash];
+							it->buckets[hash] = i + NUM_BTHINGIT_FIXEDHASH;
+						}
+
+						entry->mobj = mobj;
+						return mobj;
+					}
+				}
+			}
 		}
-		if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, cannot continue.
-		|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
+
+		if (++it->curx > it->x2)
 		{
-			P_SetTarget(&bnext, NULL);
-			return true;
+			it->curx = it->x1;
+			if (++it->cury > it->y2)
+				return NULL;
 		}
+
+		it->block = GetBlockmapBlock(it->curx, it->cury);
 	}
-	return true;
+
+	return NULL;
+}
+
+void P_FreeBlockThingsIterator(bthingit_t *it)
+{
+	if (it)
+	{
+		it->freechain = freeiters;
+		freeiters = it;
+	}
+}
+
+void P_ClearBlockNodes(void)
+{
+	freeblocks = NULL;
+	freeiters = NULL;
 }
 
 //
diff --git a/src/p_maputl.h b/src/p_maputl.h
index 08de0cb0b5f43bff5609c3cea58b06b14cfd36bb..e894c08a2461caf82397c569549e5bd3213ae3b7 100644
--- a/src/p_maputl.h
+++ b/src/p_maputl.h
@@ -63,6 +63,39 @@ void P_LineOpening(line_t *plinedef, mobj_t *mobj);
 boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean(*func)(line_t *));
 boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean(*func)(mobj_t *));
 
+void P_ClearBlockNodes(void);
+
+typedef struct
+{
+	mobj_t *mobj;
+	int next;
+} bthingit_hash_entry_t;
+
+#define NUM_BTHINGIT_BUCKETS 32
+#define NUM_BTHINGIT_FIXEDHASH 10
+
+typedef struct bthingit_s
+{
+	int x1, y1, x2, y2;
+	int curx, cury;
+	blocknode_t *block;
+
+	int buckets[NUM_BTHINGIT_BUCKETS];
+	bthingit_hash_entry_t fixedhash[NUM_BTHINGIT_FIXEDHASH];
+	int numfixedhash;
+
+	bthingit_hash_entry_t *dynhash;
+	size_t dynhashcount;
+	size_t dynhashcapacity;
+
+	struct bthingit_s *freechain;
+} bthingit_t;
+
+bthingit_t *P_NewBlockThingsIterator(int x1, int y1, int x2, int y2);
+mobj_t *P_BlockThingsIteratorNext(bthingit_t *it, boolean centeronly);
+void P_FreeBlockThingsIterator(bthingit_t *it);
+boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *));
+
 #define PT_ADDLINES     1
 #define PT_ADDTHINGS    2
 #define PT_EARLYOUT     4
diff --git a/src/p_mobj.c b/src/p_mobj.c
index f16fef2f00312ef6dd7d8a0cc60bffb447377ccc..28dd9304f6ed390fe23861c66200e6d51dfcb656 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -9455,7 +9455,7 @@ static inline boolean PIT_PushThing(mobj_t *thing)
 
 static void P_PointPushThink(mobj_t *mobj)
 {
-	INT32 xl, xh, yl, yh, bx, by;
+	INT32 xl, xh, yl, yh;
 	fixed_t radius;
 
 	if (!mobj->spawnpoint)
@@ -9470,9 +9470,8 @@ static void P_PointPushThink(mobj_t *mobj)
 	xh = (unsigned)(mobj->x + radius - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
 	yl = (unsigned)(mobj->y - radius - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
 	yh = (unsigned)(mobj->y + radius - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
-	for (bx = xl; bx <= xh; bx++)
-		for (by = yl; by <= yh; by++)
-			P_BlockThingsIterator(bx, by, PIT_PushThing);
+
+	P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushThing);
 }
 
 static boolean P_MobjRegularThink(mobj_t *mobj)
diff --git a/src/p_mobj.h b/src/p_mobj.h
index f2e4cbf3d63ff1675825078c1e6fc165aa515af8..f833415ed1d0af3ee377ed6d4ced943d2782f499 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -273,6 +273,19 @@ typedef enum {
 	PCF_THUNK = 32,
 } precipflag_t;
 
+// [RH] Like msecnode_t, but for the blockmap
+typedef struct blocknode_s
+{
+	struct mobj_s *mobj;
+
+	int blockindex;     // index into blocklinks for the block this node is in
+
+	struct blocknode_s **mprev; // previous actor in this block
+	struct blocknode_s *mnext;  // next actor in this block
+	struct blocknode_s **bprev; // previous block this actor is in
+	struct blocknode_s *bnext;  // next block this actor is in
+} blocknode_t;
+
 // Map Object definition.
 typedef struct mobj_s
 {
@@ -344,8 +357,7 @@ typedef struct mobj_s
 
 	// Interaction info, by BLOCKMAP.
 	// Links in blocks (if needed).
-	struct mobj_s *bnext;
-	struct mobj_s **bprev; // killough 8/11/98: change to ptr-to-ptr
+	blocknode_t *blocknode;
 
 	// Additional pointers for NiGHTS hoops
 	struct mobj_s *hnext;
diff --git a/src/p_polyobj.c b/src/p_polyobj.c
index 331bc5c7f0397876d10c92ad6c316d2ffeb56d41..e779956e8d92092bf41a97db8efa5a7e7fd1d973 100644
--- a/src/p_polyobj.c
+++ b/src/p_polyobj.c
@@ -877,14 +877,17 @@ static void Polyobj_carryThings(polyobj_t *po, fixed_t dx, fixed_t dy)
 		for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
 		{
 			mobj_t *mo;
+			blocknode_t *block;
 
 			if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
 				continue;
 
-			mo = blocklinks[y * bmapwidth + x];
+			block = blocklinks[y * bmapwidth + x];
 
-			for (; mo; mo = mo->bnext)
+			for (; block; block = block->mnext)
 			{
+				mo = block->mobj;
+
 				if (mo->lastlook == pomovecount)
 					continue;
 
@@ -937,10 +940,12 @@ static INT32 Polyobj_clipThings(polyobj_t *po, line_t *line)
 		{
 			if (!(x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight))
 			{
-				mobj_t *mo = blocklinks[y * bmapwidth + x];
+				mobj_t *mo = NULL;
+				blocknode_t *block = blocklinks[y * bmapwidth + x];
 
-				for (; mo; mo = mo->bnext)
+				for (; block; block = block->mnext)
 				{
+					mo = block->mobj;
 
 					// Don't scroll objects that aren't affected by gravity
 					if (mo->flags & MF_NOGRAVITY)
@@ -1109,14 +1114,17 @@ static void Polyobj_rotateThings(polyobj_t *po, vector2_t origin, angle_t delta,
 		for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
 		{
 			mobj_t *mo;
+			blocknode_t *block;
 
 			if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
 				continue;
 
-			mo = blocklinks[y * bmapwidth + x];
+			block = blocklinks[y * bmapwidth + x];
 
-			for (; mo; mo = mo->bnext)
+			for (; block; block = block->mnext)
 			{
+				mo = block->mobj;
+
 				if (mo->lastlook == pomovecount)
 					continue;
 
diff --git a/src/p_setup.c b/src/p_setup.c
index fb22f6524c99aaa31a2012ffa991586194a742ed..614a820806ca714ba3d86c39b630b9b1ba6ab1bf 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -138,7 +138,7 @@ INT32 *blockmaplump; // Big blockmap
 // origin of block map
 fixed_t bmaporgx, bmaporgy;
 // for thing chains
-mobj_t **blocklinks;
+blocknode_t **blocklinks;
 
 // REJECT
 // For fast sight rejection.
@@ -7869,6 +7869,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
 		Z_Free(ss->attachedsolid);
 	}
 
+	P_ClearBlockNodes();
+
 	// Clear pointers that would be left dangling by the purge
 	R_FlushTranslationColormapCache();
 
diff --git a/src/p_user.c b/src/p_user.c
index 7e19e8d6367dbb874c7b21b105dd5f18e6dba9fb..200b7673659eb4f4975cb76723c998da7f3c449c 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -3807,6 +3807,8 @@ static boolean PIT_CheckSolidsTeeter(mobj_t *thing)
 	if (abs(thing->x - teeterer->x) >= blockdist || abs(thing->y - teeterer->y) >= blockdist)
 		return true; // didn't hit it
 
+	highesttop = INT32_MIN;
+
 	if (teeterer->eflags & MFE_VERTICALFLIP)
 	{
 		if (thingtop < teeterer->z)
@@ -4110,13 +4112,8 @@ static void P_DoTeeter(player_t *player)
 			teeteryl = teeteryh = player->mo->y;
 			couldteeter = false;
 			solidteeter = teeter;
-			for (by = yl; by <= yh; by++)
-				for (bx = xl; bx <= xh; bx++)
-				{
-					highesttop = INT32_MIN;
-					if (!P_BlockThingsIterator(bx, by, PIT_CheckSolidsTeeter))
-						goto teeterdone; // we've found something that stops us teetering at all, how about we stop already
-				}
+			if (!P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_CheckSolidsTeeter))
+				goto teeterdone; // we've found something that stops us teetering at all
 teeterdone:
 			teeter = solidteeter;
 			P_SetTarget(&tmthing, oldtmthing); // restore old tmthing, goodness knows what the game does with this before mobj thinkers