diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index 5ed05d4d693ed233fc931250ac93c7dd41bb7163..a0d40cdf0003dcc9f8ed81b0812825a625f6048a 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -760,6 +760,7 @@ linedeftypes
 			flags128text = "[7] Intangible";
 			flags256text = "[8] Stopped by pushables";
 			flags512text = "[9] Render flats";
+			flags8192text = "[13] Cut cyan flat pixels";
 		}
 
 		30
@@ -914,6 +915,7 @@ linedeftypes
 			flags32text = "[5] Only block player";
 			flags64text = "[6] Render insides";
 			flags128text = "[7] Only block non-players";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "195F";
 			flags643dfloorflagsadd = "7C80";
@@ -973,6 +975,7 @@ linedeftypes
 			flags32text = "[5] Only block player";
 			flags64text = "[6] Don't cast shadow";
 			flags128text = "[7] Render insides/block non-plr";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "200191F";
 			flags1283dfloorflagsadd = "7C80";
@@ -986,6 +989,7 @@ linedeftypes
 			flags32text = "[5] Only block player";
 			flags64text = "[6] Don't cast shadow";
 			flags128text = "[7] Render insides/block non-plr";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "2001917";
 			flags1283dfloorflagsadd = "7C80";
@@ -1013,6 +1017,7 @@ linedeftypes
 			flags32text = "[5] Only block player";
 			flags64text = "[6] Don't cast shadow";
 			flags128text = "[7] Render insides/block non-plr";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "400191F";
 			flags1283dfloorflagsadd = "7C80";
@@ -1026,6 +1031,7 @@ linedeftypes
 			flags32text = "[5] Only block player";
 			flags64text = "[6] Don't cast shadow";
 			flags128text = "[7] Render insides/block non-plr";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "4001917";
 			flags1283dfloorflagsadd = "7C80";
@@ -1071,6 +1077,7 @@ linedeftypes
 			flags64text = "[6] Use two light levels";
 			flags512text = "[9] Use target light level";
 			flags1024text = "[10] Ripple effect";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "9F39";
 			flags643dfloorflagsadd = "20000";
@@ -1099,6 +1106,7 @@ linedeftypes
 			flags64text = "[6] Use two light levels";
 			flags512text = "[9] Use target light level";
 			flags1024text = "[10] Ripple effect";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "1F31";
 			flags643dfloorflagsadd = "20000";
@@ -1114,6 +1122,7 @@ linedeftypes
 			flags64text = "[6] Use two light levels";
 			flags512text = "[9] Use target light level";
 			flags1024text = "[10] Ripple effect";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "209F39";
 			flags643dfloorflagsadd = "20000";
@@ -1128,6 +1137,7 @@ linedeftypes
 			flags64text = "[6] Use two light levels";
 			flags512text = "[9] Use target light level";
 			flags1024text = "[10] Ripple effect";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "201F31";
 			flags643dfloorflagsadd = "20000";
@@ -1150,6 +1160,7 @@ linedeftypes
 			prefix = "(221)";
 			flags8text = "[3] Slope skew sides";
 			flags64text = "[6] Cast shadow";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "1B59";
 			flags643dfloorflagsremove = "40";
@@ -1273,6 +1284,7 @@ linedeftypes
 			flags32text = "[5] Only block player";
 			flags64text = "[6] Spindash to move";
 			flags128text = "[7] Only block non-players";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "195F";
 		}
@@ -1312,6 +1324,7 @@ linedeftypes
 			flags32text = "[5] Only block player";
 			flags64text = "[6] Spindash, no shadow";
 			flags128text = "[7] Only block non-players";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "2009D1F";
 			flags643dfloorflagsadd = "40";
@@ -1378,6 +1391,7 @@ linedeftypes
 			flags32text = "[5] Only block player";
 			flags64text = "[6] Don't cast shadow";
 			flags128text = "[7] Only block non-players";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "210959F";
 			flags643dfloorflagsadd = "40";
@@ -1391,6 +1405,7 @@ linedeftypes
 			flags32text = "[5] Only block player";
 			flags64text = "[6] Don't cast shadow";
 			flags128text = "[7] Only block non-players";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "218959F";
 			flags643dfloorflagsadd = "40";
@@ -1529,6 +1544,7 @@ linedeftypes
 			flags8text = "[3] Slope skew sides";
 			flags512text = "[9] Shattered by pushables";
 			flags1024text = "[10] Trigger linedef executor";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "880101D";
 		}
@@ -1570,6 +1586,7 @@ linedeftypes
 			flags128text = "[7] Only block non-players";
 			flags512text = "[9] Shattered by pushables";
 			flags1024text = "[10] Trigger linedef executor";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "1080101F";
 		}
@@ -1591,6 +1608,7 @@ linedeftypes
 			prefix = "(258)";
 			flags8text = "[3] Slope skew sides";
 			flags32text = "[5] Don't damage bosses";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorflags = "959";
 		}
@@ -1604,6 +1622,7 @@ linedeftypes
 			flags128text = "[7] Only block non-players";
 			flags512text = "[9] Shattered by pushables";
 			flags1024text = "[10] Trigger linedef executor";
+			flags8192text = "[13] Cut cyan flat pixels";
 			3dfloor = true;
 			3dfloorcustom = true;
 		}
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index bcb0afa6e30a03cdaa8e0a547245af2ac8999e8b..df51198143f86cc9749cd1f53ae82beabf60e198 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -3862,20 +3862,21 @@ static void HWR_DrawDropShadow(mobj_t *thing, gr_vissprite_t *spr, fixed_t scale
 	UINT8 lightlevel = 255;
 	extracolormap_t *colormap = NULL;
 	UINT8 i;
+	SINT8 flip = P_MobjFlip(thing);
 
 	INT32 light;
 	fixed_t scalemul;
 	UINT16 alpha;
 	fixed_t floordiff;
-	fixed_t floorz;
+	fixed_t groundz;
 	fixed_t slopez;
-	pslope_t *floorslope;
+	pslope_t *groundslope;
 
-	floorz = R_GetShadowZ(thing, &floorslope);
+	groundz = R_GetShadowZ(thing, &groundslope);
 
-	//if (abs(floorz - gr_viewz) / tz > 4) return; // Prevent stretchy shadows and possible crashes
+	//if (abs(groundz - gr_viewz) / tz > 4) return; // Prevent stretchy shadows and possible crashes
 
-	floordiff = abs(thing->z - floorz);
+	floordiff = abs((flip < 0 ? thing->height : 0) + thing->z - groundz);
 
 	alpha = floordiff / (4*FRACUNIT) + 75;
 	if (alpha >= 255) return;
@@ -3907,18 +3908,18 @@ static void HWR_DrawDropShadow(mobj_t *thing, gr_vissprite_t *spr, fixed_t scale
 	shadowVerts[0].z = shadowVerts[1].z = fy - offset;
 	shadowVerts[3].z = shadowVerts[2].z = fy + offset;
 
-	if (floorslope)
+	if (groundslope)
 	{
 		for (i = 0; i < 4; i++)
 		{
-			slopez = P_GetSlopeZAt(floorslope, FLOAT_TO_FIXED(shadowVerts[i].x), FLOAT_TO_FIXED(shadowVerts[i].z));
-			shadowVerts[i].y = FIXED_TO_FLOAT(slopez) + 0.05f;
+			slopez = P_GetSlopeZAt(groundslope, FLOAT_TO_FIXED(shadowVerts[i].x), FLOAT_TO_FIXED(shadowVerts[i].z));
+			shadowVerts[i].y = FIXED_TO_FLOAT(slopez) + flip * 0.05f;
 		}
 	}
 	else
 	{
 		for (i = 0; i < 4; i++)
-			shadowVerts[i].y = FIXED_TO_FLOAT(floorz) + 0.05f;
+			shadowVerts[i].y = FIXED_TO_FLOAT(groundz) + flip * 0.05f;
 	}
 
 	if (spr->flip)
@@ -3946,7 +3947,7 @@ static void HWR_DrawDropShadow(mobj_t *thing, gr_vissprite_t *spr, fixed_t scale
 
 	if (thing->subsector->sector->numlights)
 	{
-		light = R_GetPlaneLight(thing->subsector->sector, floorz, false); // Always use the light at the top instead of whatever I was doing before
+		light = R_GetPlaneLight(thing->subsector->sector, groundz, false); // Always use the light at the top instead of whatever I was doing before
 
 		if (*thing->subsector->sector->lightlist[light].lightlevel > 255)
 			lightlevel = 255;
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index d4fe72682dfe62702e9fb16a2950f672e9cd5290..b3390eb95279a6bc27a586d035f8432e1fcdaf03 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -108,6 +108,12 @@ static hook_p linedefexecutorhooks;
 // For other hooks, a unique linked list
 hook_p roothook;
 
+static void PushHook(lua_State *L, hook_p hookp)
+{
+	lua_pushfstring(L, FMT_HOOKID, hookp->id);
+	lua_gettable(L, LUA_REGISTRYINDEX);
+}
+
 // Takes hook, function, and additional arguments (mobj type to act on, etc.)
 static int lib_addHook(lua_State *L)
 {
@@ -253,6 +259,7 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which)
 	I_Assert(mo->type < NUMMOBJTYPES);
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	// Look for all generic mobj hooks
 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
@@ -260,12 +267,11 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which)
 		if (hookp->type != which)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 			LUA_PushUserdata(gL, mo, META_MOBJ);
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -2);
-		if (lua_pcall(gL, 1, 1, 0)) {
+		if (lua_pcall(gL, 1, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -282,12 +288,11 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which)
 		if (hookp->type != which)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 			LUA_PushUserdata(gL, mo, META_MOBJ);
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -2);
-		if (lua_pcall(gL, 1, 1, 0)) {
+		if (lua_pcall(gL, 1, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -311,18 +316,18 @@ boolean LUAh_PlayerHook(player_t *plr, enum hook which)
 		return false;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	for (hookp = playerhooks; hookp; hookp = hookp->next)
 	{
 		if (hookp->type != which)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 			LUA_PushUserdata(gL, plr, META_PLAYER);
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -2);
-		if (lua_pcall(gL, 1, 1, 0)) {
+		if (lua_pcall(gL, 1, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -346,6 +351,7 @@ void LUAh_MapChange(INT16 mapnumber)
 		return;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 	lua_pushinteger(gL, mapnumber);
 
 	for (hookp = roothook; hookp; hookp = hookp->next)
@@ -353,10 +359,12 @@ void LUAh_MapChange(INT16 mapnumber)
 		if (hookp->type != hook_MapChange)
 			continue;
 
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -2);
-		LUA_Call(gL, 1);
+		if (lua_pcall(gL, 1, 0, 1)) {
+			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+		}
 	}
 
 	lua_settop(gL, 0);
@@ -370,6 +378,7 @@ void LUAh_MapLoad(void)
 		return;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 	lua_pushinteger(gL, gamemap);
 
 	for (hookp = roothook; hookp; hookp = hookp->next)
@@ -377,10 +386,12 @@ void LUAh_MapLoad(void)
 		if (hookp->type != hook_MapLoad)
 			continue;
 
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -2);
-		LUA_Call(gL, 1);
+		if (lua_pcall(gL, 1, 0, 1)) {
+			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+		}
 	}
 
 	lua_settop(gL, 0);
@@ -394,6 +405,7 @@ void LUAh_PlayerJoin(int playernum)
 		return;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 	lua_pushinteger(gL, playernum);
 
 	for (hookp = roothook; hookp; hookp = hookp->next)
@@ -401,10 +413,12 @@ void LUAh_PlayerJoin(int playernum)
 		if (hookp->type != hook_PlayerJoin)
 			continue;
 
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -2);
-		LUA_Call(gL, 1);
+		if (lua_pcall(gL, 1, 0, 1)) {
+			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+		}
 	}
 
 	lua_settop(gL, 0);
@@ -417,20 +431,23 @@ void LUAh_PreThinkFrame(void)
 	if (!gL || !(hooksAvailable[hook_PreThinkFrame/8] & (1<<(hook_PreThinkFrame%8))))
 		return;
 
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
+
 	for (hookp = roothook; hookp; hookp = hookp->next)
 	{
 		if (hookp->type != hook_PreThinkFrame)
 			continue;
 
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
-		if (lua_pcall(gL, 0, 0, 0)) {
+		PushHook(gL, hookp);
+		if (lua_pcall(gL, 0, 0, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
 			hookp->error = true;
 		}
 	}
+
+	lua_pop(gL, 1); // Pop error handler
 }
 
 // Hook for frame (after mobj and player thinkers)
@@ -440,22 +457,24 @@ void LUAh_ThinkFrame(void)
 	if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8))))
 		return;
 
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
+
 	for (hookp = roothook; hookp; hookp = hookp->next)
 	{
 		if (hookp->type != hook_ThinkFrame)
 			continue;
 
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
-		if (lua_pcall(gL, 0, 0, 0)) {
+		PushHook(gL, hookp);
+		if (lua_pcall(gL, 0, 0, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
 			hookp->error = true;
 		}
 	}
-}
 
+	lua_pop(gL, 1); // Pop error handler
+}
 
 // Hook for frame (at end of tick, ie after overlays, precipitation, specials)
 void LUAh_PostThinkFrame(void)
@@ -464,20 +483,23 @@ void LUAh_PostThinkFrame(void)
 	if (!gL || !(hooksAvailable[hook_PostThinkFrame/8] & (1<<(hook_PostThinkFrame%8))))
 		return;
 
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
+
 	for (hookp = roothook; hookp; hookp = hookp->next)
 	{
 		if (hookp->type != hook_PostThinkFrame)
 			continue;
 
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
-		if (lua_pcall(gL, 0, 0, 0)) {
+		PushHook(gL, hookp);
+		if (lua_pcall(gL, 0, 0, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
 			hookp->error = true;
 		}
 	}
+
+	lua_pop(gL, 1); // Pop error handler
 }
 
 // Hook for mobj collisions
@@ -491,6 +513,7 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
 	I_Assert(thing1->type < NUMMOBJTYPES);
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	// Look for all generic mobj collision hooks
 	for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
@@ -498,16 +521,15 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
 		if (hookp->type != which)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, thing1, META_MOBJ);
 			LUA_PushUserdata(gL, thing2, META_MOBJ);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -529,16 +551,15 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
 		if (hookp->type != which)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, thing1, META_MOBJ);
 			LUA_PushUserdata(gL, thing2, META_MOBJ);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -569,6 +590,7 @@ UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which)
 	I_Assert(thing->type < NUMMOBJTYPES);
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	// Look for all generic mobj collision hooks
 	for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
@@ -576,16 +598,15 @@ UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which)
 		if (hookp->type != which)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, thing, META_MOBJ);
 			LUA_PushUserdata(gL, line, META_LINE);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -607,16 +628,15 @@ UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which)
 		if (hookp->type != which)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, thing, META_MOBJ);
 			LUA_PushUserdata(gL, line, META_LINE);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -648,16 +668,16 @@ boolean LUAh_MobjThinker(mobj_t *mo)
 	I_Assert(mo->type < NUMMOBJTYPES);
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	// Look for all generic mobj thinker hooks
 	for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next)
 	{
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 			LUA_PushUserdata(gL, mo, META_MOBJ);
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -2);
-		if (lua_pcall(gL, 1, 1, 0)) {
+		if (lua_pcall(gL, 1, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -671,12 +691,11 @@ boolean LUAh_MobjThinker(mobj_t *mo)
 
 	for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next)
 	{
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 			LUA_PushUserdata(gL, mo, META_MOBJ);
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -2);
-		if (lua_pcall(gL, 1, 1, 0)) {
+		if (lua_pcall(gL, 1, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -703,6 +722,7 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
 	I_Assert(special->type < NUMMOBJTYPES);
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	// Look for all generic touch special hooks
 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
@@ -710,16 +730,15 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
 		if (hookp->type != hook_TouchSpecial)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, special, META_MOBJ);
 			LUA_PushUserdata(gL, toucher, META_MOBJ);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -736,16 +755,15 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
 		if (hookp->type != hook_TouchSpecial)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, special, META_MOBJ);
 			LUA_PushUserdata(gL, toucher, META_MOBJ);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -772,6 +790,7 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 	I_Assert(target->type < NUMMOBJTYPES);
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	// Look for all generic should damage hooks
 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
@@ -779,7 +798,7 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 		if (hookp->type != hook_ShouldDamage)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, target, META_MOBJ);
 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
@@ -787,14 +806,13 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 			lua_pushinteger(gL, damage);
 			lua_pushinteger(gL, damagetype);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
-		if (lua_pcall(gL, 5, 1, 0)) {
+		if (lua_pcall(gL, 5, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -815,7 +833,7 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 	{
 		if (hookp->type != hook_ShouldDamage)
 			continue;
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, target, META_MOBJ);
 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
@@ -823,14 +841,13 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 			lua_pushinteger(gL, damage);
 			lua_pushinteger(gL, damagetype);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
-		if (lua_pcall(gL, 5, 1, 0)) {
+		if (lua_pcall(gL, 5, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -862,6 +879,7 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 	I_Assert(target->type < NUMMOBJTYPES);
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	// Look for all generic mobj damage hooks
 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
@@ -869,7 +887,7 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 		if (hookp->type != hook_MobjDamage)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, target, META_MOBJ);
 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
@@ -877,14 +895,13 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 			lua_pushinteger(gL, damage);
 			lua_pushinteger(gL, damagetype);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
-		if (lua_pcall(gL, 5, 1, 0)) {
+		if (lua_pcall(gL, 5, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -901,7 +918,7 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 		if (hookp->type != hook_MobjDamage)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, target, META_MOBJ);
 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
@@ -909,14 +926,13 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 			lua_pushinteger(gL, damage);
 			lua_pushinteger(gL, damagetype);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
-		if (lua_pcall(gL, 5, 1, 0)) {
+		if (lua_pcall(gL, 5, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -943,6 +959,7 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8
 	I_Assert(target->type < NUMMOBJTYPES);
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	// Look for all generic mobj death hooks
 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
@@ -950,20 +967,19 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8
 		if (hookp->type != hook_MobjDeath)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, target, META_MOBJ);
 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
 			LUA_PushUserdata(gL, source, META_MOBJ);
 			lua_pushinteger(gL, damagetype);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
-		if (lua_pcall(gL, 4, 1, 0)) {
+		if (lua_pcall(gL, 4, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -980,20 +996,19 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8
 		if (hookp->type != hook_MobjDeath)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, target, META_MOBJ);
 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
 			LUA_PushUserdata(gL, source, META_MOBJ);
 			lua_pushinteger(gL, damagetype);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
-		if (lua_pcall(gL, 4, 1, 0)) {
+		if (lua_pcall(gL, 4, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1018,22 +1033,22 @@ boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd)
 		return false;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	for (hookp = roothook; hookp; hookp = hookp->next)
 	{
 		if (hookp->type != hook_BotTiccmd)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, bot, META_PLAYER);
 			LUA_PushUserdata(gL, cmd, META_TICCMD);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1058,6 +1073,7 @@ boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 		return false;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	for (hookp = roothook; hookp; hookp = hookp->next)
 	{
@@ -1065,16 +1081,15 @@ boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 		|| (hookp->s.str && strcmp(hookp->s.str, ((skin_t*)tails->skin)->name)))
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, sonic, META_MOBJ);
 			LUA_PushUserdata(gL, tails, META_MOBJ);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 8, 0)) {
+		if (lua_pcall(gL, 2, 8, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1121,22 +1136,22 @@ boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails)
 		return false;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	for (hookp = roothook; hookp; hookp = hookp->next)
 	{
 		if (hookp->type != hook_BotRespawn)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, sonic, META_MOBJ);
 			LUA_PushUserdata(gL, tails, META_MOBJ);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1166,24 +1181,27 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector)
 		return 0;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next)
 	{
 		if (strcmp(hookp->s.str, line->text))
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, line, META_LINE);
 			LUA_PushUserdata(gL, mo, META_MOBJ);
 			LUA_PushUserdata(gL, sector, META_SECTOR);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -4);
 		lua_pushvalue(gL, -4);
 		lua_pushvalue(gL, -4);
-		LUA_Call(gL, 3);
+		if (lua_pcall(gL, 3, 0, 1)) {
+			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+		}
 		hooked = true;
 	}
 
@@ -1200,13 +1218,14 @@ boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
 		return false;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	for (hookp = roothook; hookp; hookp = hookp->next)
 	{
 		if (hookp->type != hook_PlayerMsg)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, &players[source], META_PLAYER); // Source player
 			if (flags & 2 /*HU_CSAY*/) { // csay TODO: make HU_CSAY accessible outside hu_stuff.c
@@ -1224,13 +1243,12 @@ boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
 			}
 			lua_pushstring(gL, msg); // msg
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
-		if (lua_pcall(gL, 4, 1, 0)) {
+		if (lua_pcall(gL, 4, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1256,6 +1274,7 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8
 		return false;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	for (hookp = roothook; hookp; hookp = hookp->next)
 	{
@@ -1263,20 +1282,19 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8
 		|| (hookp->s.mt && !(inflictor && hookp->s.mt == inflictor->type)))
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, player, META_PLAYER);
 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
 			LUA_PushUserdata(gL, source, META_MOBJ);
 			lua_pushinteger(gL, damagetype);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
 		lua_pushvalue(gL, -5);
-		if (lua_pcall(gL, 4, 1, 0)) {
+		if (lua_pcall(gL, 4, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1295,7 +1313,7 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8
 void LUAh_NetArchiveHook(lua_CFunction archFunc)
 {
 	hook_p hookp;
-
+	int errorhandlerindex;
 	if (!gL || !(hooksAvailable[hook_NetVars/8] & (1<<(hook_NetVars%8))))
 		return;
 
@@ -1303,8 +1321,11 @@ void LUAh_NetArchiveHook(lua_CFunction archFunc)
 	I_Assert(lua_gettop(gL) > 0);
 	I_Assert(lua_istable(gL, -1));
 
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
+	errorhandlerindex = lua_gettop(gL);
+
 	// tables becomes an upvalue of archFunc
-	lua_pushvalue(gL, -1);
+	lua_pushvalue(gL, -2);
 	lua_pushcclosure(gL, archFunc, 1);
 	// stack: tables, archFunc
 
@@ -1313,13 +1334,15 @@ void LUAh_NetArchiveHook(lua_CFunction archFunc)
 		if (hookp->type != hook_NetVars)
 			continue;
 
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -2); // archFunc
-		LUA_Call(gL, 1);
+		if (lua_pcall(gL, 1, 0, errorhandlerindex)) {
+			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+		}
 	}
 
-	lua_pop(gL, 1); // pop archFunc
+	lua_pop(gL, 2); // Pop archFunc and error handler
 	// stack: tables
 }
 
@@ -1331,6 +1354,7 @@ boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing)
 		return false;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	// Look for all generic mobj map thing spawn hooks
 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
@@ -1338,16 +1362,15 @@ boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing)
 		if (hookp->type != hook_MapThingSpawn)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, mo, META_MOBJ);
 			LUA_PushUserdata(gL, mthing, META_MAPTHING);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1364,16 +1387,15 @@ boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing)
 		if (hookp->type != hook_MapThingSpawn)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, mo, META_MOBJ);
 			LUA_PushUserdata(gL, mthing, META_MAPTHING);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1398,6 +1420,7 @@ boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj)
 		return 0;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	// Look for all generic mobj follow item hooks
 	for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
@@ -1405,16 +1428,15 @@ boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj)
 		if (hookp->type != hook_FollowMobj)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, player, META_PLAYER);
 			LUA_PushUserdata(gL, mobj, META_MOBJ);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1431,16 +1453,15 @@ boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj)
 		if (hookp->type != hook_FollowMobj)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, player, META_PLAYER);
 			LUA_PushUserdata(gL, mobj, META_MOBJ);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1465,22 +1486,22 @@ UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj)
 		return 0;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	for (hookp = playerhooks; hookp; hookp = hookp->next)
 	{
 		if (hookp->type != hook_PlayerCanDamage)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, player, META_PLAYER);
 			LUA_PushUserdata(gL, mobj, META_MOBJ);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1508,22 +1529,25 @@ void LUAh_PlayerQuit(player_t *plr, kickreason_t reason)
 		return;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	for (hookp = roothook; hookp; hookp = hookp->next)
 	{
 		if (hookp->type != hook_PlayerQuit)
 			continue;
 
-	    if (lua_gettop(gL) == 0)
+	    if (lua_gettop(gL) == 1)
 	    {
 	        LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit
 	        lua_pushinteger(gL, reason); // Reason for quitting
 	    }
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		LUA_Call(gL, 2);
+		if (lua_pcall(gL, 2, 0, 1)) {
+			CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+		}
 	}
 
 	lua_settop(gL, 0);
@@ -1536,20 +1560,23 @@ void LUAh_IntermissionThinker(void)
 	if (!gL || !(hooksAvailable[hook_IntermissionThinker/8] & (1<<(hook_IntermissionThinker%8))))
 		return;
 
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
+
 	for (hookp = roothook; hookp; hookp = hookp->next)
 	{
 		if (hookp->type != hook_IntermissionThinker)
 			continue;
 
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
-		if (lua_pcall(gL, 0, 0, 0)) {
+		PushHook(gL, hookp);
+		if (lua_pcall(gL, 0, 0, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
 			hookp->error = true;
 		}
 	}
+
+	lua_pop(gL, 1); // Pop error handler
 }
 
 // Hook for team switching
@@ -1562,13 +1589,14 @@ boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, b
 		return true;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
 
 	for (hookp = playerhooks; hookp; hookp = hookp->next)
 	{
 		if (hookp->type != hook_TeamSwitch)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, player, META_PLAYER);
 			lua_pushinteger(gL, newteam);
@@ -1576,14 +1604,13 @@ boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, b
 			lua_pushboolean(gL, tryingautobalance);
 			lua_pushboolean(gL, tryingscramble);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
 		lua_pushvalue(gL, -6);
-		if (lua_pcall(gL, 5, 1, 0)) {
+		if (lua_pcall(gL, 5, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1608,6 +1635,8 @@ UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean
 		return 0;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
+
 	hud_running = true; // local hook
 
 	for (hookp = playerhooks; hookp; hookp = hookp->next)
@@ -1615,18 +1644,17 @@ UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean
 		if (hookp->type != hook_ViewpointSwitch)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, player, META_PLAYER);
 			LUA_PushUserdata(gL, newdisplayplayer, META_PLAYER);
 			lua_pushboolean(gL, forced);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -4);
 		lua_pushvalue(gL, -4);
 		lua_pushvalue(gL, -4);
-		if (lua_pcall(gL, 3, 1, 0)) {
+		if (lua_pcall(gL, 3, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1644,6 +1672,7 @@ UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean
 	}
 
 	lua_settop(gL, 0);
+
 	hud_running = false;
 
 	return canSwitchView;
@@ -1659,6 +1688,8 @@ boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend)
 		return true;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
+
 	hud_running = true; // local hook
 
 	for (hookp = playerhooks; hookp; hookp = hookp->next)
@@ -1666,16 +1697,15 @@ boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend)
 		if (hookp->type != hook_SeenPlayer)
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, player, META_PLAYER);
 			LUA_PushUserdata(gL, seenfriend, META_PLAYER);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1688,6 +1718,7 @@ boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend)
 	}
 
 	lua_settop(gL, 0);
+
 	hud_running = false;
 
 	return hasSeenPlayer;
@@ -1702,6 +1733,8 @@ boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname)
 		return true;
 
 	lua_settop(gL, 0);
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
+
 	hud_running = true; // local hook
 
 	for (hookp = roothook; hookp; hookp = hookp->next)
@@ -1710,16 +1743,15 @@ boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname)
 			|| (hookp->s.str && strcmp(hookp->s.str, musname)))
 			continue;
 
-		if (lua_gettop(gL) == 0)
+		if (lua_gettop(gL) == 1)
 		{
 			LUA_PushUserdata(gL, player, META_PLAYER);
 			lua_pushstring(gL, musname);
 		}
-		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
-		lua_gettable(gL, LUA_REGISTRYINDEX);
+		PushHook(gL, hookp);
 		lua_pushvalue(gL, -3);
 		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		if (lua_pcall(gL, 2, 1, 1)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1732,6 +1764,7 @@ boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname)
 	}
 
 	lua_settop(gL, 0);
+
 	hud_running = false;
 
 	return keepplaying;
diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c
index 76c80c541314a2914ccff349f41928bf4dcd7b9a..7cbe7a6cc9bbc579d47ff6f6a03ab3f2ad6b5ef4 100644
--- a/src/lua_mathlib.c
+++ b/src/lua_mathlib.c
@@ -113,7 +113,8 @@ static int lib_fixeddiv(lua_State *L)
 
 static int lib_fixedrem(lua_State *L)
 {
-	lua_pushfixed(L, FixedRem(luaL_checkfixed(L, 1), luaL_checkfixed(L, 2)));
+	LUA_Deprecated(L, "FixedRem(a, b)", "a % b");
+	lua_pushfixed(L, luaL_checkfixed(L, 1) % luaL_checkfixed(L, 2));
 	return 1;
 }
 
diff --git a/src/lua_script.c b/src/lua_script.c
index 4f94b007eca31f8bda5bf383d960c613a43cd6fa..06ea18b0ee9e574604a38cc524ed4da9c185ffd5 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -78,6 +78,58 @@ FUNCNORETURN static int LUA_Panic(lua_State *L)
 #endif
 }
 
+#define LEVELS1 12 // size of the first part of the stack
+#define LEVELS2 10 // size of the second part of the stack
+
+// Error handler used with pcall() when loading scripts or calling hooks
+// Takes a string with the original error message,
+// appends the traceback to it, and return the result
+int LUA_GetErrorMessage(lua_State *L)
+{
+	int level = 1;
+	int firstpart = 1; // still before eventual `...'
+	lua_Debug ar;
+
+	lua_pushliteral(L, "\nstack traceback:");
+	while (lua_getstack(L, level++, &ar))
+	{
+		if (level > LEVELS1 && firstpart)
+		{
+			// no more than `LEVELS2' more levels?
+			if (!lua_getstack(L, level + LEVELS2, &ar))
+				level--; // keep going
+			else
+			{
+				lua_pushliteral(L, "\n    ..."); // too many levels
+				while (lua_getstack(L, level + LEVELS2, &ar)) // find last levels
+					level++;
+			}
+			firstpart = 0;
+			continue;
+		}
+		lua_pushliteral(L, "\n    ");
+		lua_getinfo(L, "Snl", &ar);
+		lua_pushfstring(L, "%s:", ar.short_src);
+		if (ar.currentline > 0)
+			lua_pushfstring(L, "%d:", ar.currentline);
+		if (*ar.namewhat != '\0') // is there a name?
+			lua_pushfstring(L, " in function " LUA_QS, ar.name);
+		else
+		{
+			if (*ar.what == 'm') // main?
+				lua_pushfstring(L, " in main chunk");
+			else if (*ar.what == 'C' || *ar.what == 't')
+				lua_pushliteral(L, " ?"); // C function or tail call
+			else
+				lua_pushfstring(L, " in function <%s:%d>",
+					ar.short_src, ar.linedefined);
+		}
+		lua_concat(L, lua_gettop(L));
+	}
+	lua_concat(L, lua_gettop(L));
+	return 1;
+}
+
 // Moved here from lib_getenum.
 int LUA_PushGlobals(lua_State *L, const char *word)
 {
@@ -410,11 +462,13 @@ static inline void LUA_LoadFile(MYFILE *f, char *name)
 
 	lua_lumploading = true; // turn on loading flag
 
-	if (luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)) || lua_pcall(gL, 0, 0, 0)) {
+	lua_pushcfunction(gL, LUA_GetErrorMessage);
+	if (luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)) || lua_pcall(gL, 0, 0, lua_gettop(gL) - 1)) {
 		CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1));
 		lua_pop(gL,1);
 	}
 	lua_gc(gL, LUA_GCCOLLECT, 0);
+	lua_pop(gL, 1); // Pop error handler
 
 	lua_lumploading = false; // turn off again
 }
diff --git a/src/lua_script.h b/src/lua_script.h
index 8d5bed7c72524fd393e064fa5e943fd250ab395d..9568503e13e33a94538254fc498e5580e24d6cb3 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -39,6 +39,7 @@ void LUA_ClearExtVars(void);
 
 extern boolean lua_lumploading; // is LUA_LoadLump being called?
 
+int LUA_GetErrorMessage(lua_State *L);
 void LUA_LoadLump(UINT16 wad, UINT16 lump);
 #ifdef LUA_ALLOW_BYTECODE
 void LUA_DumpFile(const char *filename);
diff --git a/src/m_fixed.h b/src/m_fixed.h
index cc54c1aea257d5d0ef8180fe3ef15d25b392afb6..289ca442a03e7740a7e1844303a8d84c97f9aa22 100644
--- a/src/m_fixed.h
+++ b/src/m_fixed.h
@@ -204,18 +204,6 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedDiv(fixed_t a, fixed_t b)
 	return FixedDiv2(a, b);
 }
 
-/**	\brief	The FixedRem function
-
-	\param	x	fixed_t number
-	\param	y	fixed_t number
-
-	\return	 remainder of dividing x by y
-*/
-FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedRem(fixed_t x, fixed_t y)
-{
-	return x % y;
-}
-
 /**	\brief	The FixedSqrt function
 
 	\param	x	fixed_t number
diff --git a/src/p_map.c b/src/p_map.c
index 6e4992473be3c81f03040175bd4c82c6f1373b63..b63ce84ba2a53f3ce23fbae730aad832b813c9f1 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -3291,157 +3291,150 @@ static boolean P_IsClimbingValid(player_t *player, angle_t angle)
 	return false;
 }
 
-//
-// PTR_SlideTraverse
-//
-static boolean PTR_SlideTraverse(intercept_t *in)
+static boolean PTR_LineIsBlocking(line_t *li)
 {
-	line_t *li;
-
-	I_Assert(in->isaline);
-
-	li = in->d.line;
-
 	// one-sided linedefs are always solid to sliding movement.
-	// one-sided linedef
 	if (!li->backsector)
-	{
-		if (P_PointOnLineSide(slidemo->x, slidemo->y, li))
-			return true; // don't hit the back side
-		goto isblocking;
-	}
+		return !P_PointOnLineSide(slidemo->x, slidemo->y, li);
 
 	if (!(slidemo->flags & MF_MISSILE))
 	{
 		if (li->flags & ML_IMPASSIBLE)
-			goto isblocking;
+			return true;
 
 		if ((slidemo->flags & (MF_ENEMY|MF_BOSS)) && li->flags & ML_BLOCKMONSTERS)
-			goto isblocking;
+			return true;
 	}
 
 	// set openrange, opentop, openbottom
 	P_LineOpening(li, slidemo);
 
 	if (openrange < slidemo->height)
-		goto isblocking; // doesn't fit
+		return true; // doesn't fit
 
 	if (opentop - slidemo->z < slidemo->height)
-		goto isblocking; // mobj is too high
+		return true; // mobj is too high
 
 	if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, slidemo->scale))
-		goto isblocking; // too big a step up
+		return true; // too big a step up
 
-	// this line doesn't block movement
-	return true;
+	return false;
+}
 
-	// the line does block movement,
-	// see if it is closer than best so far
-isblocking:
-	if (li->polyobj && slidemo->player)
-	{
-		if ((li->polyobj->lines[0]->backsector->flags & SF_TRIGGERSPECIAL_TOUCH) && !(li->polyobj->flags & POF_NOSPECIALS))
-			P_ProcessSpecialSector(slidemo->player, slidemo->subsector->sector, li->polyobj->lines[0]->backsector);
-	}
+static void PTR_GlideClimbTraverse(line_t *li)
+{
+	line_t *checkline = li;
+	ffloor_t *rover;
+	fixed_t topheight, bottomheight;
+	boolean fofline = false;
+	sector_t *checksector = (li->backsector && !P_PointOnLineSide(slidemo->x, slidemo->y, li)) ? li->backsector : li->frontsector;
 
-	if (slidemo->player && (slidemo->player->pflags & PF_GLIDING || slidemo->player->climbing)
-		&& slidemo->player->charability == CA_GLIDEANDCLIMB)
+	if (checksector->ffloors)
 	{
-		line_t *checkline = li;
-		sector_t *checksector;
-		ffloor_t *rover;
-		fixed_t topheight, bottomheight;
-		boolean fofline = false;
-		INT32 side = P_PointOnLineSide(slidemo->x, slidemo->y, li);
-
-		if (!side && li->backsector)
-			checksector = li->backsector;
-		else
-			checksector = li->frontsector;
-
-		if (checksector->ffloors)
+		for (rover = checksector->ffloors; rover; rover = rover->next)
 		{
-			for (rover = checksector->ffloors; rover; rover = rover->next)
-			{
-				if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP))
-					continue;
-
-				topheight    = P_GetFFloorTopZAt   (rover, slidemo->x, slidemo->y);
-				bottomheight = P_GetFFloorBottomZAt(rover, slidemo->x, slidemo->y);
+			if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP))
+				continue;
 
-				if (topheight < slidemo->z)
-					continue;
+			topheight    = P_GetFFloorTopZAt   (rover, slidemo->x, slidemo->y);
+			bottomheight = P_GetFFloorBottomZAt(rover, slidemo->x, slidemo->y);
 
-				if (bottomheight > slidemo->z + slidemo->height)
-					continue;
+			if (topheight < slidemo->z)
+				continue;
 
-				// Got this far, so I guess it's climbable. // TODO: Climbing check, also, better method to do this?
-				if (rover->master->flags & ML_TFERLINE)
-				{
-					size_t linenum = li-checksector->lines[0];
-					checkline = rover->master->frontsector->lines[0] + linenum;
-					fofline = true;
-				}
+			if (bottomheight > slidemo->z + slidemo->height)
+				continue;
 
-				break;
+			// Got this far, so I guess it's climbable. // TODO: Climbing check, also, better method to do this?
+			if (rover->master->flags & ML_TFERLINE)
+			{
+				size_t linenum = li-checksector->lines[0];
+				checkline = rover->master->frontsector->lines[0] + linenum;
+				fofline = true;
 			}
+
+			break;
 		}
+	}
 
-		// see about climbing on the wall
-		if (!(checkline->flags & ML_NOCLIMB) && checkline->special != HORIZONSPECIAL)
-		{
-			boolean canclimb;
-			angle_t climbangle, climbline;
-			INT32 whichside = P_PointOnLineSide(slidemo->x, slidemo->y, li);
+	// see about climbing on the wall
+	if (!(checkline->flags & ML_NOCLIMB) && checkline->special != HORIZONSPECIAL)
+	{
+		boolean canclimb;
+		angle_t climbangle, climbline;
+		INT32 whichside = P_PointOnLineSide(slidemo->x, slidemo->y, li);
 
-			climbangle = climbline = R_PointToAngle2(li->v1->x, li->v1->y, li->v2->x, li->v2->y);
+		climbangle = climbline = R_PointToAngle2(li->v1->x, li->v1->y, li->v2->x, li->v2->y);
 
-			if (whichside) // on second side?
-				climbline += ANGLE_180;
+		if (whichside) // on second side?
+			climbline += ANGLE_180;
 
-			climbangle += (ANGLE_90 * (whichside ? -1 : 1));
+		climbangle += (ANGLE_90 * (whichside ? -1 : 1));
 
-			canclimb = (li->backsector ? P_IsClimbingValid(slidemo->player, climbangle) : true);
+		canclimb = (li->backsector ? P_IsClimbingValid(slidemo->player, climbangle) : true);
 
-			if (((!slidemo->player->climbing && abs((signed)(slidemo->angle - ANGLE_90 - climbline)) < ANGLE_45)
+		if (((!slidemo->player->climbing && abs((signed)(slidemo->angle - ANGLE_90 - climbline)) < ANGLE_45)
 			|| (slidemo->player->climbing == 1 && abs((signed)(slidemo->angle - climbline)) < ANGLE_135))
 			&& canclimb)
+		{
+			slidemo->angle = climbangle;
+			/*if (!demoplayback || P_ControlStyle(slidemo->player) == CS_LMAOGALOG)
 			{
-				slidemo->angle = climbangle;
-				/*if (!demoplayback || P_ControlStyle(slidemo->player) == CS_LMAOGALOG)
-				{
-					if (slidemo->player == &players[consoleplayer])
-						localangle = slidemo->angle;
-					else if (slidemo->player == &players[secondarydisplayplayer])
-						localangle2 = slidemo->angle;
-				}*/
+				if (slidemo->player == &players[consoleplayer])
+					localangle = slidemo->angle;
+				else if (slidemo->player == &players[secondarydisplayplayer])
+					localangle2 = slidemo->angle;
+			}*/
 
-				if (!slidemo->player->climbing)
-				{
-					S_StartSound(slidemo->player->mo, sfx_s3k4a);
-					slidemo->player->climbing = 5;
-				}
-
-				slidemo->player->pflags &= ~(PF_GLIDING|PF_SPINNING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED);
-				slidemo->player->glidetime = 0;
-				slidemo->player->secondjump = 0;
+			if (!slidemo->player->climbing)
+			{
+				S_StartSound(slidemo->player->mo, sfx_s3k4a);
+				slidemo->player->climbing = 5;
+			}
 
-				if (slidemo->player->climbing > 1)
-					slidemo->momz = slidemo->momx = slidemo->momy = 0;
+			slidemo->player->pflags &= ~(PF_GLIDING|PF_SPINNING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED);
+			slidemo->player->glidetime = 0;
+			slidemo->player->secondjump = 0;
 
-				if (fofline)
-					whichside = 0;
+			if (slidemo->player->climbing > 1)
+				slidemo->momz = slidemo->momx = slidemo->momy = 0;
 
-				if (!whichside)
-				{
-					slidemo->player->lastsidehit = checkline->sidenum[whichside];
-					slidemo->player->lastlinehit = (INT16)(checkline - lines);
-				}
+			if (fofline)
+				whichside = 0;
 
-				P_Thrust(slidemo, slidemo->angle, FixedMul(5*FRACUNIT, slidemo->scale));
+			if (!whichside)
+			{
+				slidemo->player->lastsidehit = checkline->sidenum[whichside];
+				slidemo->player->lastlinehit = (INT16)(checkline - lines);
 			}
+
+			P_Thrust(slidemo, slidemo->angle, FixedMul(5*FRACUNIT, slidemo->scale));
 		}
 	}
+}
+
+static boolean PTR_SlideTraverse(intercept_t *in)
+{
+	line_t *li;
+
+	I_Assert(in->isaline);
+
+	li = in->d.line;
+
+	if (!PTR_LineIsBlocking(li))
+		return true;
+
+	// the line blocks movement,
+	// see if it is closer than best so far
+	if (li->polyobj && slidemo->player)
+	{
+		if ((li->polyobj->lines[0]->backsector->flags & SF_TRIGGERSPECIAL_TOUCH) && !(li->polyobj->flags & POF_NOSPECIALS))
+			P_ProcessSpecialSector(slidemo->player, slidemo->subsector->sector, li->polyobj->lines[0]->backsector);
+	}
+
+	if (slidemo->player && slidemo->player->charability == CA_GLIDEANDCLIMB
+		&& (slidemo->player->pflags & PF_GLIDING || slidemo->player->climbing))
+		PTR_GlideClimbTraverse(li);
 
 	if (in->frac < bestslidefrac && (!slidemo->player || !slidemo->player->climbing))
 	{
diff --git a/src/p_mobj.c b/src/p_mobj.c
index a22545f05a5a43caee981f05ae49b077dde01f1d..5c1c08b55311b052ab99b431a10a7e3eafbb169e 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -1669,70 +1669,74 @@ static void P_PushableCheckBustables(mobj_t *mo)
 
 	for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
 	{
+		ffloor_t *rover;
+		fixed_t topheight, bottomheight;
+
 		if (!node->m_sector)
 			break;
 
-		if (node->m_sector->ffloors)
+		if (!node->m_sector->ffloors)
+			continue;
+
+		for (rover = node->m_sector->ffloors; rover; rover = rover->next)
 		{
-			ffloor_t *rover;
-			fixed_t topheight, bottomheight;
+			if (!(rover->flags & FF_EXISTS))
+				continue;
 
-			for (rover = node->m_sector->ffloors; rover; rover = rover->next)
-			{
-				if (!(rover->flags & FF_EXISTS)) continue;
+			if (!(rover->flags & FF_BUSTUP))
+				continue;
 
-				if (!(rover->flags & FF_BUSTUP)) continue;
+			// Needs ML_EFFECT4 flag for pushables to break it
+			if (!(rover->master->flags & ML_EFFECT4))
+				continue;
 
-				// Needs ML_EFFECT4 flag for pushables to break it
-				if (!(rover->master->flags & ML_EFFECT4)) continue;
+			if (rover->master->frontsector->crumblestate != CRUMBLE_NONE)
+				continue;
 
-				if (rover->master->frontsector->crumblestate == CRUMBLE_NONE)
-				{
-					topheight = P_GetFOFTopZ(mo, node->m_sector, rover, mo->x, mo->y, NULL);
-					bottomheight = P_GetFOFBottomZ(mo, node->m_sector, rover, mo->x, mo->y, NULL);
-					// Height checks
-					if (rover->flags & FF_SHATTERBOTTOM)
-					{
-						if (mo->z+mo->momz + mo->height < bottomheight)
-							continue;
+			topheight = P_GetFOFTopZ(mo, node->m_sector, rover, mo->x, mo->y, NULL);
+			bottomheight = P_GetFOFBottomZ(mo, node->m_sector, rover, mo->x, mo->y, NULL);
 
-						if (mo->z+mo->height > bottomheight)
-							continue;
-					}
-					else if (rover->flags & FF_SPINBUST)
-					{
-						if (mo->z+mo->momz > topheight)
-							continue;
+			// Height checks
+			if (rover->flags & FF_SHATTERBOTTOM)
+			{
+				if (mo->z + mo->momz + mo->height < bottomheight)
+					continue;
 
-						if (mo->z+mo->height < bottomheight)
-							continue;
-					}
-					else if (rover->flags & FF_SHATTER)
-					{
-						if (mo->z+mo->momz > topheight)
-							continue;
+				if (mo->z + mo->height > bottomheight)
+					continue;
+			}
+			else if (rover->flags & FF_SPINBUST)
+			{
+				if (mo->z + mo->momz > topheight)
+					continue;
 
-						if (mo->z+mo->momz + mo->height < bottomheight)
-							continue;
-					}
-					else
-					{
-						if (mo->z >= topheight)
-							continue;
+				if (mo->z + mo->height < bottomheight)
+					continue;
+			}
+			else if (rover->flags & FF_SHATTER)
+			{
+				if (mo->z + mo->momz > topheight)
+					continue;
 
-						if (mo->z+mo->height < bottomheight)
-							continue;
-					}
+				if (mo->z + mo->momz + mo->height < bottomheight)
+					continue;
+			}
+			else
+			{
+				if (mo->z >= topheight)
+					continue;
+
+				if (mo->z + mo->height < bottomheight)
+					continue;
+			}
 
-					EV_CrumbleChain(NULL, rover); // node->m_sector
+			EV_CrumbleChain(NULL, rover); // node->m_sector
 
-					// Run a linedef executor??
-					if (rover->master->flags & ML_EFFECT5)
-						P_LinedefExecute((INT16)(P_AproxDistance(rover->master->dx, rover->master->dy)>>FRACBITS), mo, node->m_sector);
+			// Run a linedef executor??
+			if (rover->master->flags & ML_EFFECT5)
+				P_LinedefExecute((INT16)(P_AproxDistance(rover->master->dx, rover->master->dy)>>FRACBITS), mo, node->m_sector);
 
-					goto bustupdone;
-				}
-			}
+			goto bustupdone;
 		}
 	}
 bustupdone:
@@ -2793,6 +2797,94 @@ static boolean P_ZMovement(mobj_t *mo)
 	return true;
 }
 
+// Check for "Mario" blocks to hit and bounce them
+static void P_CheckMarioBlocks(mobj_t *mo)
+{
+	msecnode_t *node;
+
+	if (netgame && mo->player->spectator)
+		return;
+
+	for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
+	{
+		ffloor_t *rover;
+
+		if (!node->m_sector->ffloors)
+			continue;
+
+		for (rover = node->m_sector->ffloors; rover; rover = rover->next)
+		{
+			if (!(rover->flags & FF_EXISTS))
+				continue;
+
+			if (!(rover->flags & FF_MARIO))
+				continue;
+
+			if (mo->eflags & MFE_VERTICALFLIP)
+				continue; // if you were flipped, your head isn't actually hitting your ceilingz is it?
+
+			if (*rover->bottomheight != mo->ceilingz)
+				continue;
+
+			if (rover->flags & FF_SHATTERBOTTOM) // Brick block!
+				EV_CrumbleChain(node->m_sector, rover);
+			else // Question block!
+				EV_MarioBlock(rover, node->m_sector, mo);
+		}
+	}
+}
+
+// Check if we're on a polyobject that triggers a linedef executor.
+static boolean P_PlayerPolyObjectZMovement(mobj_t *mo)
+{
+	msecnode_t *node;
+	boolean stopmovecut = false;
+
+	for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
+	{
+		sector_t *sec = node->m_sector;
+		subsector_t *newsubsec;
+		size_t i;
+
+		for (i = 0; i < numsubsectors; i++)
+		{
+			polyobj_t *po;
+			sector_t *polysec;
+			newsubsec = &subsectors[i];
+
+			if (newsubsec->sector != sec)
+				continue;
+
+			for (po = newsubsec->polyList; po; po = (polyobj_t *)(po->link.next))
+			{
+				if (!(po->flags & POF_SOLID))
+					continue;
+
+				if (!P_MobjInsidePolyobj(po, mo))
+					continue;
+
+				polysec = po->lines[0]->backsector;
+
+				// Moving polyobjects should act like conveyors if the player lands on one. (I.E. none of the momentum cut thing below) -Red
+				if ((mo->z == polysec->ceilingheight || mo->z + mo->height == polysec->floorheight) && po->thinker)
+					stopmovecut = true;
+
+				if (!(po->flags & POF_LDEXEC))
+					continue;
+
+				if (mo->z != polysec->ceilingheight)
+					continue;
+
+				// We're landing on a PO, so check for a linedef executor.
+				// Trigger tags are 32000 + the PO's ID number.
+				P_LinedefExecute((INT16)(32000 + po->id), mo, NULL);
+			}
+		}
+	}
+
+	return stopmovecut;
+}
+
 static void P_PlayerZMovement(mobj_t *mo)
 {
 	boolean onground;
@@ -2889,68 +2981,10 @@ static void P_PlayerZMovement(mobj_t *mo)
 
 			mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack
 
-			{
-				// Check if we're on a polyobject
-				// that triggers a linedef executor.
-				msecnode_t *node;
-				boolean stopmovecut = false;
-
-				for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
-				{
-					sector_t *sec = node->m_sector;
-					subsector_t *newsubsec;
-					size_t i;
-
-					for (i = 0; i < numsubsectors; i++)
-					{
-						newsubsec = &subsectors[i];
-
-						if (newsubsec->sector != sec)
-							continue;
-
-						if (newsubsec->polyList)
-						{
-							polyobj_t *po = newsubsec->polyList;
-							sector_t *polysec;
-
-							while(po)
-							{
-								if (!P_MobjInsidePolyobj(po, mo) || !(po->flags & POF_SOLID))
-								{
-									po = (polyobj_t *)(po->link.next);
-									continue;
-								}
-
-								// We're inside it! Yess...
-								polysec = po->lines[0]->backsector;
-
-								// Moving polyobjects should act like conveyors if the player lands on one. (I.E. none of the momentum cut thing below) -Red
-								if ((mo->z == polysec->ceilingheight || mo->z+mo->height == polysec->floorheight) && po->thinker)
-									stopmovecut = true;
-
-								if (!(po->flags & POF_LDEXEC))
-								{
-									po = (polyobj_t *)(po->link.next);
-									continue;
-								}
-
-								if (mo->z == polysec->ceilingheight)
-								{
-									// We're landing on a PO, so check for
-									// a linedef executor.
-									// Trigger tags are 32000 + the PO's ID number.
-									P_LinedefExecute((INT16)(32000 + po->id), mo, NULL);
-								}
-
-								po = (polyobj_t *)(po->link.next);
-							}
-						}
-					}
-				}
-
 			clipmomz = P_PlayerHitFloor(mo->player, true);
 
-			if (!stopmovecut)
+			if (!P_PlayerPolyObjectZMovement(mo))
+			{
 				// Cut momentum in half when you hit the ground and
 				// aren't pressing any controls.
 				if (!(mo->player->cmd.forwardmove || mo->player->cmd.sidemove) && !mo->player->cmomx && !mo->player->cmomy && !(mo->player->pflags & PF_SPINNING))
@@ -3014,39 +3048,10 @@ nightsdone:
 			}
 		}
 
-		// Check for "Mario" blocks to hit and bounce them
 		if (P_MobjFlip(mo)*mo->momz > 0)
 		{
-			msecnode_t *node;
-
-			if (CheckForMarioBlocks && !(netgame && mo->player->spectator)) // Only let the player punch
-			{
-				// Search the touching sectors, from side-to-side...
-				for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
-				{
-					ffloor_t *rover;
-					if (!node->m_sector->ffloors)
-						continue;
-
-					for (rover = node->m_sector->ffloors; rover; rover = rover->next)
-					{
-						if (!(rover->flags & FF_EXISTS))
-							continue;
-
-						// Come on, it's time to go...
-						if (rover->flags & FF_MARIO
-						&& !(mo->eflags & MFE_VERTICALFLIP) // if you were flipped, your head isn't actually hitting your ceilingz is it?
-						&& *rover->bottomheight == mo->ceilingz) // The player's head hit the bottom!
-						{
-							// DO THE MARIO!
-							if (rover->flags & FF_SHATTERBOTTOM) // Brick block!
-								EV_CrumbleChain(node->m_sector, rover);
-							else // Question block!
-								EV_MarioBlock(rover, node->m_sector, mo);
-						}
-					}
-				} // Ugly ugly billions of braces! Argh!
-			}
+			if (CheckForMarioBlocks)
+				P_CheckMarioBlocks(mo);
 
 			// hit the ceiling
 			if (mariomode)
@@ -3750,13 +3755,114 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
 	return false;
 }
 
-//
-// P_PlayerMobjThinker
-//
-static void P_PlayerMobjThinker(mobj_t *mobj)
+static void P_CheckCrumblingPlatforms(mobj_t *mobj)
 {
 	msecnode_t *node;
 
+	if (netgame && mobj->player->spectator)
+		return;
+
+	for (node = mobj->touching_sectorlist; node; node = node->m_sectorlist_next)
+	{
+		ffloor_t *rover;
+
+		for (rover = node->m_sector->ffloors; rover; rover = rover->next)
+		{
+			if (!(rover->flags & FF_EXISTS))
+				continue;
+
+			if (!(rover->flags & FF_CRUMBLE))
+				continue;
+
+			if (mobj->eflags & MFE_VERTICALFLIP)
+			{
+				if (P_GetSpecialBottomZ(mobj, sectors + rover->secnum, node->m_sector) != mobj->z + mobj->height)
+					continue;
+			}
+			else
+			{
+				if (P_GetSpecialTopZ(mobj, sectors + rover->secnum, node->m_sector) != mobj->z)
+					continue;
+			}
+
+			EV_StartCrumble(rover->master->frontsector, rover, (rover->flags & FF_FLOATBOB), mobj->player, rover->alpha, !(rover->flags & FF_NORETURN));
+		}
+	}
+}
+
+static boolean P_MobjTouchesSectorWithWater(mobj_t *mobj)
+{
+	msecnode_t *node;
+
+	for (node = mobj->touching_sectorlist; node; node = node->m_sectorlist_next)
+	{
+		ffloor_t *rover;
+
+		if (!node->m_sector->ffloors)
+			continue;
+
+		for (rover = node->m_sector->ffloors; rover; rover = rover->next)
+		{
+			if (!(rover->flags & FF_EXISTS))
+				continue;
+
+			if (!(rover->flags & FF_SWIMMABLE))
+				continue;
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
+// Check for floating water platforms and bounce them
+static void P_CheckFloatbobPlatforms(mobj_t *mobj)
+{
+	msecnode_t *node;
+
+	// Can't land on anything if you're not moving downwards
+	if (P_MobjFlip(mobj)*mobj->momz >= 0)
+		return;
+
+	if (!P_MobjTouchesSectorWithWater(mobj))
+		return;
+
+	for (node = mobj->touching_sectorlist; node; node = node->m_sectorlist_next)
+	{
+		ffloor_t *rover;
+
+		if (!node->m_sector->ffloors)
+			continue;
+
+		for (rover = node->m_sector->ffloors; rover; rover = rover->next)
+		{
+			if (!(rover->flags & FF_EXISTS))
+				continue;
+
+			if (!(rover->flags & FF_FLOATBOB))
+				continue;
+
+
+			if (mobj->eflags & MFE_VERTICALFLIP)
+			{
+				if (abs(*rover->bottomheight - (mobj->z + mobj->height)) > abs(mobj->momz))
+					continue;
+			}
+			else
+			{
+				if (abs(*rover->topheight - mobj->z) > abs(mobj->momz))
+					continue;
+			}
+
+			// Initiate a 'bouncy' elevator function which slowly diminishes.
+			EV_BounceSector(rover->master->frontsector, -mobj->momz, rover->master);
+		}
+	}
+}
+
+static void P_PlayerMobjThinker(mobj_t *mobj)
+{
 	I_Assert(mobj != NULL);
 	I_Assert(mobj->player != NULL);
 	I_Assert(!P_MobjWasRemoved(mobj));
@@ -3809,77 +3915,10 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
 	else
 		P_TryMove(mobj, mobj->x, mobj->y, true);
 
-	if (!(netgame && mobj->player->spectator))
-	{
-		// Crumbling platforms
-		for (node = mobj->touching_sectorlist; node; node = node->m_sectorlist_next)
-		{
-			fixed_t topheight, bottomheight;
-			ffloor_t *rover;
+	P_CheckCrumblingPlatforms(mobj);
 
-			for (rover = node->m_sector->ffloors; rover; rover = rover->next)
-			{
-				if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_CRUMBLE))
-					continue;
-
-				topheight = P_GetSpecialTopZ(mobj, sectors + rover->secnum, node->m_sector);
-				bottomheight = P_GetSpecialBottomZ(mobj, sectors + rover->secnum, node->m_sector);
-
-				if ((topheight == mobj->z && !(mobj->eflags & MFE_VERTICALFLIP))
-				|| (bottomheight == mobj->z + mobj->height && mobj->eflags & MFE_VERTICALFLIP)) // You nut.
-					EV_StartCrumble(rover->master->frontsector, rover, (rover->flags & FF_FLOATBOB), mobj->player, rover->alpha, !(rover->flags & FF_NORETURN));
-			}
-		}
-	}
-
-	// Check for floating water platforms and bounce them
-	if (CheckForFloatBob && P_MobjFlip(mobj)*mobj->momz < 0)
-	{
-		boolean thereiswater = false;
-
-		for (node = mobj->touching_sectorlist; node; node = node->m_sectorlist_next)
-		{
-			if (node->m_sector->ffloors)
-			{
-				ffloor_t *rover;
-				// Get water boundaries first
-				for (rover = node->m_sector->ffloors; rover; rover = rover->next)
-				{
-					if (!(rover->flags & FF_EXISTS))
-						continue;
-
-					if (rover->flags & FF_SWIMMABLE) // Is there water?
-					{
-						thereiswater = true;
-						break;
-					}
-				}
-			}
-		}
-		if (thereiswater)
-		{
-			for (node = mobj->touching_sectorlist; node; node = node->m_sectorlist_next)
-			{
-				if (node->m_sector->ffloors)
-				{
-					ffloor_t *rover;
-					for (rover = node->m_sector->ffloors; rover; rover = rover->next)
-					{
-						if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_FLOATBOB))
-							continue;
-
-						if ((!(mobj->eflags & MFE_VERTICALFLIP) && abs(*rover->topheight-mobj->z) <= abs(mobj->momz)) // The player is landing on the cheese!
-						|| (mobj->eflags & MFE_VERTICALFLIP && abs(*rover->bottomheight-(mobj->z+mobj->height)) <= abs(mobj->momz)))
-						{
-							// Initiate a 'bouncy' elevator function
-							// which slowly diminishes.
-							EV_BounceSector(rover->master->frontsector, -mobj->momz, rover->master);
-						}
-					}
-				}
-			}
-		} // Ugly ugly billions of braces! Argh!
-	}
+	if (CheckForFloatBob)
+		P_CheckFloatbobPlatforms(mobj);
 
 	// always do the gravity bit now, that's simpler
 	// BUT CheckPosition only if wasn't done before.
diff --git a/src/p_polyobj.c b/src/p_polyobj.c
index 3b6195285bd2cea5cb36e7f380b0ef422ccb21d4..d94f363c3ec75f88e9c3711526a3c74065c7bca7 100644
--- a/src/p_polyobj.c
+++ b/src/p_polyobj.c
@@ -240,6 +240,9 @@ static void Polyobj_GetInfo(polyobj_t *po)
 	/*if (lines[i].flags & ML_EFFECT5)
 		po->flags &= ~POF_CLIPPLANES;*/
 
+	if (lines[i].flags & ML_EFFECT6)
+		po->flags |= POF_SPLAT;
+
 	if (lines[i].flags & ML_NOCLIMB) // Has a linedef executor
 		po->flags |= POF_LDEXEC;
 }
diff --git a/src/p_polyobj.h b/src/p_polyobj.h
index 8037c545f28877240893360018bf25e9a9027c8d..c6ae716f44bab61e4e45ead48f07a22e4bbb4214 100644
--- a/src/p_polyobj.h
+++ b/src/p_polyobj.h
@@ -38,7 +38,7 @@ typedef enum
 	POF_SOLID             = 0x3,       ///< Clips things.
 	POF_TESTHEIGHT        = 0x4,       ///< Test line collision with heights
 	POF_RENDERSIDES       = 0x8,       ///< Renders the sides.
-	POF_RENDERTOP         = 0x10,      ///< Renders the top..
+	POF_RENDERTOP         = 0x10,      ///< Renders the top.
 	POF_RENDERBOTTOM      = 0x20,      ///< Renders the bottom.
 	POF_RENDERPLANES      = 0x30,      ///< Renders top and bottom.
 	POF_RENDERALL         = 0x38,      ///< Renders everything.
@@ -49,6 +49,7 @@ typedef enum
 	POF_LDEXEC            = 0x400,     ///< This PO triggers a linedef executor.
 	POF_ONESIDE           = 0x800,     ///< Only use the first side of the linedef.
 	POF_NOSPECIALS        = 0x1000,    ///< Don't apply sector specials.
+	POF_SPLAT             = 0x2000,    ///< Use splat flat renderer (treat cyan pixels as invisible).
 } polyobjflags_e;
 
 //
diff --git a/src/p_saveg.c b/src/p_saveg.c
index d6abf423258362f4aa9a277300de58c4f0108c72..eafc285a30e7ff5478b141417ad9b354457aaff9 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -717,6 +717,34 @@ static void P_NetUnArchiveColormaps(void)
 	net_colormaps = NULL;
 }
 
+static void P_NetArchiveWaypoints(void)
+{
+	INT32 i, j;
+
+	for (i = 0; i < NUMWAYPOINTSEQUENCES; i++)
+	{
+		WRITEUINT16(save_p, numwaypoints[i]);
+		for (j = 0; j < numwaypoints[i]; j++)
+			WRITEUINT32(save_p, waypoints[i][j] ? waypoints[i][j]->mobjnum : 0);
+	}
+}
+
+static void P_NetUnArchiveWaypoints(void)
+{
+	INT32 i, j;
+	UINT32 mobjnum;
+
+	for (i = 0; i < NUMWAYPOINTSEQUENCES; i++)
+	{
+		numwaypoints[i] = READUINT16(save_p);
+		for (j = 0; j < numwaypoints[i]; j++)
+		{
+			mobjnum = READUINT32(save_p);
+			waypoints[i][j] = (mobjnum == 0) ? NULL : P_FindNewPosition(mobjnum);
+		}
+	}
+}
+
 ///
 /// World Archiving
 ///
@@ -1995,6 +2023,7 @@ static inline void SavePolyrotatetThinker(const thinker_t *th, const UINT8 type)
 	WRITEINT32(save_p, ht->polyObjNum);
 	WRITEINT32(save_p, ht->speed);
 	WRITEINT32(save_p, ht->distance);
+	WRITEUINT8(save_p, ht->turnobjs);
 }
 
 static void SavePolymoveThinker(const thinker_t *th, const UINT8 type)
@@ -3127,6 +3156,7 @@ static inline thinker_t* LoadPolyrotatetThinker(actionf_p1 thinker)
 	ht->polyObjNum = READINT32(save_p);
 	ht->speed = READINT32(save_p);
 	ht->distance = READINT32(save_p);
+	ht->turnobjs = READUINT8(save_p);
 	return &ht->thinker;
 }
 
@@ -4042,6 +4072,7 @@ void P_SaveNetGame(void)
 		P_NetArchiveThinkers();
 		P_NetArchiveSpecials();
 		P_NetArchiveColormaps();
+		P_NetArchiveWaypoints();
 	}
 	LUA_Archive();
 
@@ -4080,6 +4111,7 @@ boolean P_LoadNetGame(void)
 		P_NetUnArchiveThinkers();
 		P_NetUnArchiveSpecials();
 		P_NetUnArchiveColormaps();
+		P_NetUnArchiveWaypoints();
 		P_RelinkPointers();
 		P_FinishMobjs();
 	}
diff --git a/src/p_user.c b/src/p_user.c
index 4b2849b1c1d39dddfbfd46be13e893565c786564..1cfcd0bde20c1d4185ed574f28ac2b0408a5a0c6 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2521,6 +2521,72 @@ boolean P_InQuicksand(mobj_t *mo) // Returns true if you are in quicksand
 	return false; // No sand here, Captain!
 }
 
+static boolean P_PlayerCanBust(player_t *player, ffloor_t *rover)
+{
+	if (!(rover->flags & FF_EXISTS))
+		return false;
+
+	if (!(rover->flags & FF_BUSTUP))
+		return false;
+
+	/*if (rover->master->frontsector->crumblestate != CRUMBLE_NONE)
+		return false;*/
+
+	// If it's an FF_SHATTER, you can break it just by touching it.
+	if (rover->flags & FF_SHATTER)
+		return true;
+
+	// If it's an FF_SPINBUST, you can break it if you are in your spinning frames
+	// (either from jumping or spindashing).
+	if (rover->flags & FF_SPINBUST)
+	{
+		if ((player->pflags & PF_SPINNING) && !(player->pflags & PF_STARTDASH))
+			return true;
+
+		if ((player->pflags & PF_JUMPED) && !(player->pflags & PF_NOJUMPDAMAGE))
+			return true;
+	}
+
+	// Strong abilities can break even FF_STRONGBUST.
+	if (player->charability == CA_GLIDEANDCLIMB)
+		return true;
+
+	if (player->pflags & PF_BOUNCING)
+		return true;
+
+	if (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)
+		return true;
+
+	if (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
+		return true;
+
+	// Everyone else is out of luck.
+	if (rover->flags & FF_STRONGBUST)
+		return false;
+
+	// Spinning (and not jumping)
+	if ((player->pflags & PF_SPINNING) && !(player->pflags & PF_JUMPED))
+		return true;
+
+	// Super
+	if (player->powers[pw_super])
+		return true;
+
+	// Dashmode
+	if ((player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE) && player->dashmode >= DASHMODE_THRESHOLD)
+		return true;
+
+	// NiGHTS drill
+	if (player->pflags & PF_DRILLING)
+		return true;
+
+	// Recording for Metal Sonic
+	if (metalrecording)
+		return true;
+
+	return false;
+}
+
 static void P_CheckBustableBlocks(player_t *player)
 {
 	msecnode_t *node;
@@ -2543,121 +2609,83 @@ static void P_CheckBustableBlocks(player_t *player)
 
 	for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next)
 	{
+		ffloor_t *rover;
+		fixed_t topheight, bottomheight;
+
 		if (!node->m_sector)
 			break;
 
-		if (node->m_sector->ffloors)
-		{
-			ffloor_t *rover;
-			fixed_t topheight, bottomheight;
-
-			for (rover = node->m_sector->ffloors; rover; rover = rover->next)
-			{
-				if (!(rover->flags & FF_EXISTS)) continue;
-
-				if ((rover->flags & FF_BUSTUP)/* && rover->master->frontsector->crumblestate == CRUMBLE_NONE*/)
-				{
-					// If it's an FF_SHATTER, you can break it just by touching it.
-					if (rover->flags & FF_SHATTER)
-						goto bust;
-
-					// If it's an FF_SPINBUST, you can break it if you are in your spinning frames
-					// (either from jumping or spindashing).
-					if (rover->flags & FF_SPINBUST
-						&& (((player->pflags & PF_SPINNING) && !(player->pflags & PF_STARTDASH))
-							|| (player->pflags & PF_JUMPED && !(player->pflags & PF_NOJUMPDAMAGE))))
-						goto bust;
-
-					// You can always break it if you have CA_GLIDEANDCLIMB
-					// or if you are bouncing on it
-					// or you are using CA_TWINSPIN/CA2_MELEE.
-					if (player->charability == CA_GLIDEANDCLIMB
-						|| (player->pflags & PF_BOUNCING)
-						|| ((player->charability == CA_TWINSPIN) && (player->panim == PA_ABILITY))
-						|| (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
-						goto bust;
-
-					if (rover->flags & FF_STRONGBUST)
-						continue;
+		if (!node->m_sector->ffloors)
+			continue;
 
-					// If it's not an FF_STRONGBUST, you can break if you are spinning (and not jumping)
-					// or you are super
-					// or you are in dashmode with SF_DASHMODE
-					// or you are drilling in NiGHTS
-					// or you are recording for Metal Sonic
-					if (!((player->pflags & PF_SPINNING) && !(player->pflags & PF_JUMPED))
-						&& !(player->powers[pw_super])
-						&& !(((player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE)) && (player->dashmode >= DASHMODE_THRESHOLD))
-						&& !(player->pflags & PF_DRILLING)
-						&& !metalrecording)
-						continue;
+		for (rover = node->m_sector->ffloors; rover; rover = rover->next)
+		{
+			if (!P_PlayerCanBust(player, rover))
+				continue;
 
-				bust:
-					topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
-					bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
+			topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
+			bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
 
-					if (((player->charability == CA_TWINSPIN) && (player->panim == PA_ABILITY))
-					|| ((P_MobjFlip(player->mo)*player->mo->momz < 0) && (player->pflags & PF_BOUNCING || ((player->charability2 == CA2_MELEE) && (player->panim == PA_ABILITY2)))))
-					{
-						topheight -= player->mo->momz;
-						bottomheight -= player->mo->momz;
-					}
+			if (((player->charability == CA_TWINSPIN) && (player->panim == PA_ABILITY))
+			|| ((P_MobjFlip(player->mo)*player->mo->momz < 0) && (player->pflags & PF_BOUNCING || ((player->charability2 == CA2_MELEE) && (player->panim == PA_ABILITY2)))))
+			{
+				topheight -= player->mo->momz;
+				bottomheight -= player->mo->momz;
+			}
 
-					// Height checks
-					if (rover->flags & FF_SHATTERBOTTOM)
-					{
-						if (player->mo->z+player->mo->momz + player->mo->height < bottomheight)
-							continue;
+			// Height checks
+			if (rover->flags & FF_SHATTERBOTTOM)
+			{
+				if (player->mo->z + player->mo->momz + player->mo->height < bottomheight)
+					continue;
 
-						if (player->mo->z+player->mo->height > bottomheight)
-							continue;
-					}
-					else if (rover->flags & FF_SPINBUST)
-					{
-						if (player->mo->z+player->mo->momz > topheight)
-							continue;
+				if (player->mo->z + player->mo->height > bottomheight)
+					continue;
+			}
+			else if (rover->flags & FF_SPINBUST)
+			{
+				if (player->mo->z + player->mo->momz > topheight)
+					continue;
 
-						if (player->mo->z + player->mo->height < bottomheight)
-							continue;
-					}
-					else if (rover->flags & FF_SHATTER)
-					{
-						if (player->mo->z + player->mo->momz > topheight)
-							continue;
+				if (player->mo->z + player->mo->height < bottomheight)
+					continue;
+			}
+			else if (rover->flags & FF_SHATTER)
+			{
+				if (player->mo->z + player->mo->momz > topheight)
+					continue;
 
-						if (player->mo->z+player->mo->momz + player->mo->height < bottomheight)
-							continue;
-					}
-					else
-					{
-						if (player->mo->z >= topheight)
-							continue;
+				if (player->mo->z + player->mo->momz + player->mo->height < bottomheight)
+					continue;
+			}
+			else
+			{
+				if (player->mo->z >= topheight)
+					continue;
 
-						if (player->mo->z + player->mo->height < bottomheight)
-							continue;
-					}
+				if (player->mo->z + player->mo->height < bottomheight)
+					continue;
+			}
 
-					// Impede the player's fall a bit
-					if (((rover->flags & FF_SPINBUST) || (rover->flags & FF_SHATTER)) && player->mo->z >= topheight)
-						player->mo->momz >>= 1;
-					else if (rover->flags & FF_SHATTER)
-					{
-						player->mo->momx >>= 1;
-						player->mo->momy >>= 1;
-					}
+			// Impede the player's fall a bit
+			if (((rover->flags & FF_SPINBUST) || (rover->flags & FF_SHATTER)) && player->mo->z >= topheight)
+				player->mo->momz >>= 1;
+			else if (rover->flags & FF_SHATTER)
+			{
+				player->mo->momx >>= 1;
+				player->mo->momy >>= 1;
+			}
 
-					//if (metalrecording)
-					//	G_RecordBustup(rover);
+			//if (metalrecording)
+			//	G_RecordBustup(rover);
 
-					EV_CrumbleChain(NULL, rover); // node->m_sector
+			EV_CrumbleChain(NULL, rover); // node->m_sector
 
-					// Run a linedef executor??
-					if (rover->master->flags & ML_EFFECT5)
-						P_LinedefExecute((INT16)(P_AproxDistance(rover->master->dx, rover->master->dy)>>FRACBITS), player->mo, node->m_sector);
+			// Run a linedef executor??
+			if (rover->master->flags & ML_EFFECT5)
+				P_LinedefExecute((INT16)(P_AproxDistance(rover->master->dx, rover->master->dy)>>FRACBITS), player->mo, node->m_sector);
 
-					goto bustupdone;
-				}
-			}
+			goto bustupdone;
 		}
 	}
 bustupdone:
@@ -2690,122 +2718,109 @@ static void P_CheckBouncySectors(player_t *player)
 
 	for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next)
 	{
+		ffloor_t *rover;
+
 		if (!node->m_sector)
 			break;
 
-		if (node->m_sector->ffloors)
+		if (!node->m_sector->ffloors)
+			continue;
+
+		for (rover = node->m_sector->ffloors; rover; rover = rover->next)
 		{
-			ffloor_t *rover;
-			boolean top = true;
+			fixed_t bouncestrength;
 			fixed_t topheight, bottomheight;
 
-			for (rover = node->m_sector->ffloors; rover; rover = rover->next)
-			{
-				if (!(rover->flags & FF_EXISTS))
-					continue; // FOFs should not be bouncy if they don't even "exist"
-
-				if (GETSECSPECIAL(rover->master->frontsector->special, 1) != 15)
-					continue; // this sector type is required for FOFs to be bouncy
-
-				topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
-				bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
-
-				if (player->mo->z > topheight)
-					continue;
-
-				if (player->mo->z + player->mo->height < bottomheight)
-					continue;
-
-				if (oldz < P_GetFOFTopZ(player->mo, node->m_sector, rover, oldx, oldy, NULL)
-						&& oldz + player->mo->height > P_GetFOFBottomZ(player->mo, node->m_sector, rover, oldx, oldy, NULL))
-					top = false;
-
-				{
-					fixed_t linedist;
-
-					linedist = P_AproxDistance(rover->master->v1->x-rover->master->v2->x, rover->master->v1->y-rover->master->v2->y);
+			if (!(rover->flags & FF_EXISTS))
+				continue; // FOFs should not be bouncy if they don't even "exist"
 
-					linedist = FixedDiv(linedist,100*FRACUNIT);
+			if (GETSECSPECIAL(rover->master->frontsector->special, 1) != 15)
+				continue; // this sector type is required for FOFs to be bouncy
 
-					if (top)
-					{
-						fixed_t newmom;
-
-						pslope_t *slope;
-						if (abs(oldz - topheight) < abs(oldz + player->mo->height - bottomheight)) { // Hit top
-							slope = *rover->t_slope;
-						} else { // Hit bottom
-							slope = *rover->b_slope;
-						}
+			topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
+			bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
 
-						momentum.x = player->mo->momx;
-						momentum.y = player->mo->momy;
-						momentum.z = player->mo->momz*2;
+			if (player->mo->z > topheight)
+				continue;
 
-						if (slope)
-							P_ReverseQuantizeMomentumToSlope(&momentum, slope);
+			if (player->mo->z + player->mo->height < bottomheight)
+				continue;
 
-						newmom = momentum.z = -FixedMul(momentum.z,linedist)/2;
+			bouncestrength = P_AproxDistance(rover->master->dx, rover->master->dy)/100;
 
-						if (abs(newmom) < (linedist*2))
-						{
-							goto bouncydone;
-						}
+			if (oldz < P_GetFOFTopZ(player->mo, node->m_sector, rover, oldx, oldy, NULL)
+					&& oldz + player->mo->height > P_GetFOFBottomZ(player->mo, node->m_sector, rover, oldx, oldy, NULL))
+			{
+				player->mo->momx = -FixedMul(player->mo->momx,bouncestrength);
+				player->mo->momy = -FixedMul(player->mo->momy,bouncestrength);
 
-						if (!(rover->master->flags & ML_BOUNCY))
-						{
-							if (newmom > 0)
-							{
-								if (newmom < 8*FRACUNIT)
-									newmom = 8*FRACUNIT;
-							}
-							else if (newmom > -8*FRACUNIT && newmom != 0)
-								newmom = -8*FRACUNIT;
-						}
+				if (player->pflags & PF_SPINNING)
+				{
+					player->pflags &= ~PF_SPINNING;
+					player->pflags |= P_GetJumpFlags(player);
+					player->pflags |= PF_THOKKED;
+				}
+			}
+			else
+			{
+				fixed_t newmom;
+				pslope_t *slope = (abs(oldz - topheight) < abs(oldz + player->mo->height - bottomheight)) ? *rover->t_slope : *rover->b_slope;
 
-						if (newmom > P_GetPlayerHeight(player)/2)
-							newmom = P_GetPlayerHeight(player)/2;
-						else if (newmom < -P_GetPlayerHeight(player)/2)
-							newmom = -P_GetPlayerHeight(player)/2;
+				momentum.x = player->mo->momx;
+				momentum.y = player->mo->momy;
+				momentum.z = player->mo->momz*2;
 
-						momentum.z = newmom*2;
+				if (slope)
+					P_ReverseQuantizeMomentumToSlope(&momentum, slope);
 
-						if (slope)
-							P_QuantizeMomentumToSlope(&momentum, slope);
+				newmom = momentum.z = -FixedMul(momentum.z,bouncestrength)/2;
 
-						player->mo->momx = momentum.x;
-						player->mo->momy = momentum.y;
-						player->mo->momz = momentum.z/2;
+				if (abs(newmom) < (bouncestrength*2))
+					goto bouncydone;
 
-						if (player->pflags & PF_SPINNING)
-						{
-							player->pflags &= ~PF_SPINNING;
-							player->pflags |= P_GetJumpFlags(player);
-							player->pflags |= PF_THOKKED;
-						}
-					}
-					else
+				if (!(rover->master->flags & ML_BOUNCY))
+				{
+					if (newmom > 0)
 					{
-						player->mo->momx = -FixedMul(player->mo->momx,linedist);
-						player->mo->momy = -FixedMul(player->mo->momy,linedist);
-
-						if (player->pflags & PF_SPINNING)
-						{
-							player->pflags &= ~PF_SPINNING;
-							player->pflags |= P_GetJumpFlags(player);
-							player->pflags |= PF_THOKKED;
-						}
+						if (newmom < 8*FRACUNIT)
+							newmom = 8*FRACUNIT;
 					}
-
-					if ((player->pflags & PF_SPINNING) && player->speed < FixedMul(1<<FRACBITS, player->mo->scale) && player->mo->momz)
+					else if (newmom < 0)
 					{
-						player->pflags &= ~PF_SPINNING;
-						player->pflags |= P_GetJumpFlags(player);
+						if (newmom > -8*FRACUNIT)
+							newmom = -8*FRACUNIT;
 					}
+				}
 
-					goto bouncydone;
+				if (newmom > P_GetPlayerHeight(player)/2)
+					newmom = P_GetPlayerHeight(player)/2;
+				else if (newmom < -P_GetPlayerHeight(player)/2)
+					newmom = -P_GetPlayerHeight(player)/2;
+
+				momentum.z = newmom*2;
+
+				if (slope)
+					P_QuantizeMomentumToSlope(&momentum, slope);
+
+				player->mo->momx = momentum.x;
+				player->mo->momy = momentum.y;
+				player->mo->momz = momentum.z/2;
+
+				if (player->pflags & PF_SPINNING)
+				{
+					player->pflags &= ~PF_SPINNING;
+					player->pflags |= P_GetJumpFlags(player);
+					player->pflags |= PF_THOKKED;
 				}
 			}
+
+			if ((player->pflags & PF_SPINNING) && player->speed < FixedMul(1<<FRACBITS, player->mo->scale) && player->mo->momz)
+			{
+				player->pflags &= ~PF_SPINNING;
+				player->pflags |= P_GetJumpFlags(player);
+			}
+
+			goto bouncydone;
 		}
 	}
 bouncydone:
diff --git a/src/r_plane.c b/src/r_plane.c
index 6857b6dca9b131cd025f69599ffc6123ac7a0cdb..92795d0fbba4b7797035c8d86a89bd5ae7daa321 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -943,23 +943,23 @@ void R_DrawSinglePlane(visplane_t *pl)
 #endif
 	spanfunc = spanfuncs[BASEDRAWFUNC];
 
-	if (pl->polyobj && pl->polyobj->translucency != 0)
+	if (pl->polyobj)
 	{
-		spanfunctype = SPANDRAWFUNC_TRANS;
-
 		// Hacked up support for alpha value in software mode Tails 09-24-2002 (sidenote: ported to polys 10-15-2014, there was no time travel involved -Red)
 		if (pl->polyobj->translucency >= 10)
 			return; // Don't even draw it
 		else if (pl->polyobj->translucency > 0)
+		{
+			spanfunctype = (pl->polyobj->flags & POF_SPLAT) ? SPANDRAWFUNC_TRANSSPLAT : SPANDRAWFUNC_TRANS;
 			ds_transmap = transtables + ((pl->polyobj->translucency-1)<<FF_TRANSSHIFT);
-		else // Opaque, but allow transparent flat pixels
+		}
+		else if (pl->polyobj->flags & POF_SPLAT) // Opaque, but allow transparent flat pixels
 			spanfunctype = SPANDRAWFUNC_SPLAT;
 
-		if ((spanfunctype == SPANDRAWFUNC_SPLAT) || (pl->extra_colormap && (pl->extra_colormap->flags & CMF_FOG)))
+		if (pl->polyobj->translucency == 0 || (pl->extra_colormap && (pl->extra_colormap->flags & CMF_FOG)))
 			light = (pl->lightlevel >> LIGHTSEGSHIFT);
 		else
 			light = LIGHTLEVELS-1;
-
 	}
 	else
 	{
@@ -984,7 +984,7 @@ void R_DrawSinglePlane(visplane_t *pl)
 
 			if (pl->ffloor->flags & FF_TRANSLUCENT)
 			{
-				spanfunctype = SPANDRAWFUNC_TRANS;
+				spanfunctype = (pl->ffloor->master->flags & ML_EFFECT6) ? SPANDRAWFUNC_TRANSSPLAT : SPANDRAWFUNC_TRANS;
 
 				// Hacked up support for alpha value in software mode Tails 09-24-2002
 				if (pl->ffloor->alpha < 12)
diff --git a/src/r_things.c b/src/r_things.c
index 14782d0c2be5260eb35d850119d4cad60980f1c1..228c86a643695c12ef6d5f379fae83b782e6497e 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1096,27 +1096,29 @@ static void R_SplitSprite(vissprite_t *sprite)
 //
 fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope)
 {
-	fixed_t z, floorz = INT32_MIN;
-	pslope_t *slope, *floorslope = NULL;
+	boolean isflipped = thing->eflags & MFE_VERTICALFLIP;
+	fixed_t z, groundz = isflipped ? INT32_MAX : INT32_MIN;
+	pslope_t *slope, *groundslope = NULL;
 	msecnode_t *node;
 	sector_t *sector;
 	ffloor_t *rover;
+#define CHECKZ (isflipped ? z > thing->z+thing->height/2 && z < groundz : z < thing->z+thing->height/2 && z > groundz)
 
 	for (node = thing->touching_sectorlist; node; node = node->m_sectorlist_next)
 	{
 		sector = node->m_sector;
 
-		slope = sector->heightsec != -1 ? NULL : sector->f_slope;
+		slope = sector->heightsec != -1 ? NULL : (isflipped ? sector->c_slope : sector->f_slope);
 
 		if (sector->heightsec != -1)
-			z = sectors[sector->heightsec].floorheight;
+			z = isflipped ? sectors[sector->heightsec].ceilingheight : sectors[sector->heightsec].floorheight;
 		else
-			z = P_GetSectorFloorZAt(sector, thing->x, thing->y);
+			z = isflipped ? P_GetSectorCeilingZAt(sector, thing->x, thing->y) : P_GetSectorFloorZAt(sector, thing->x, thing->y);
 
-		if (z < thing->z+thing->height/2 && z > floorz)
+		if CHECKZ
 		{
-			floorz = z;
-			floorslope = slope;
+			groundz = z;
+			groundslope = slope;
 		}
 
 		if (sector->ffloors)
@@ -1125,23 +1127,25 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope)
 				if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES) || (rover->alpha < 90 && !(rover->flags & FF_SWIMMABLE)))
 					continue;
 
-				z = P_GetFFloorTopZAt(rover, thing->x, thing->y);
-				if (z < thing->z+thing->height/2 && z > floorz)
+				z = isflipped ? P_GetFFloorBottomZAt(rover, thing->x, thing->y) : P_GetFFloorTopZAt(rover, thing->x, thing->y);
+				if CHECKZ
 				{
-					floorz = z;
-					floorslope = *rover->t_slope;
+					groundz = z;
+					groundslope = isflipped ? *rover->b_slope : *rover->t_slope;
 				}
 			}
 	}
 
-	if (thing->floorz > floorz + (!floorslope ? 0 : FixedMul(abs(floorslope->zdelta), thing->radius*3/2)))
+	if (isflipped ? (thing->ceilingz < groundz - (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2)))
+		: (thing->floorz > groundz + (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2))))
 	{
-		floorz = thing->floorz;
-		floorslope = NULL;
+		groundz = isflipped ? thing->ceilingz : thing->floorz;
+		groundslope = NULL;
 	}
 
 #if 0 // Unfortunately, this drops CEZ2 down to sub-17 FPS on my i7.
-	// Check polyobjects and see if floorz needs to be altered, for rings only because they don't update floorz
+	// NOTE: this section was not updated to reflect reverse gravity support
+	// Check polyobjects and see if groundz needs to be altered, for rings only because they don't update floorz
 	if (thing->type == MT_RING)
 	{
 		INT32 xl, xh, yl, yh, bx, by;
@@ -1186,10 +1190,10 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope)
 						// We're inside it! Yess...
 						z = po->lines[0]->backsector->ceilingheight;
 
-						if (z < thing->z+thing->height/2 && z > floorz)
+						if (z < thing->z+thing->height/2 && z > groundz)
 						{
-							floorz = z;
-							floorslope = NULL;
+							groundz = z;
+							groundslope = NULL;
 						}
 					}
 					plink = (polymaplink_t *)(plink->link.next);
@@ -1199,9 +1203,10 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope)
 #endif
 
 	if (shadowslope != NULL)
-		*shadowslope = floorslope;
+		*shadowslope = groundslope;
 
-	return floorz;
+	return groundz;
+#undef CHECKZ
 }
 
 static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, fixed_t tx, fixed_t tz)
@@ -1212,14 +1217,15 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 	INT32 light = 0;
 	fixed_t scalemul; UINT8 trans;
 	fixed_t floordiff;
-	fixed_t floorz;
-	pslope_t *floorslope;
+	fixed_t groundz;
+	pslope_t *groundslope;
+	boolean isflipped = thing->eflags & MFE_VERTICALFLIP;
 
-	floorz = R_GetShadowZ(thing, &floorslope);
+	groundz = R_GetShadowZ(thing, &groundslope);
 
-	if (abs(floorz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes
+	if (abs(groundz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes
 
-	floordiff = abs(thing->z - floorz);
+	floordiff = abs((isflipped ? thing->height : 0) + thing->z - groundz);
 
 	trans = floordiff / (100*FRACUNIT) + 3;
 	if (trans >= 9) return;
@@ -1230,23 +1236,23 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 	xscale = FixedDiv(projection, tz);
 	yscale = FixedDiv(projectiony, tz);
 	shadowxscale = FixedMul(thing->radius*2, scalemul);
-	shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(floorz - viewz), tz));
+	shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(groundz - viewz), tz));
 	shadowyscale = min(shadowyscale, shadowxscale) / SHORT(patch->height);
 	shadowxscale /= SHORT(patch->width);
 	shadowskew = 0;
 
-	if (floorslope)
+	if (groundslope)
 	{
 		// haha let's try some dumb stuff
 		fixed_t xslope, zslope;
-		angle_t sloperelang = (R_PointToAngle(thing->x, thing->y) - floorslope->xydirection) >> ANGLETOFINESHIFT;
+		angle_t sloperelang = (R_PointToAngle(thing->x, thing->y) - groundslope->xydirection) >> ANGLETOFINESHIFT;
 
-		xslope = FixedMul(FINESINE(sloperelang), floorslope->zdelta);
-		zslope = FixedMul(FINECOSINE(sloperelang), floorslope->zdelta);
+		xslope = FixedMul(FINESINE(sloperelang), groundslope->zdelta);
+		zslope = FixedMul(FINECOSINE(sloperelang), groundslope->zdelta);
 
 		//CONS_Printf("Shadow is sloped by %d %d\n", xslope, zslope);
 
-		if (viewz < floorz)
+		if (viewz < groundz)
 			shadowyscale += FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope);
 		else
 			shadowyscale -= FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope);
@@ -1271,7 +1277,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 	shadow->heightsec = vis->heightsec;
 
 	shadow->thingheight = FRACUNIT;
-	shadow->pz = floorz;
+	shadow->pz = groundz + (isflipped ? -shadow->thingheight : 0);
 	shadow->pzt = shadow->pz + shadow->thingheight;
 
 	shadow->mobjflags = 0;
@@ -1279,7 +1285,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
 	shadow->dispoffset = vis->dispoffset - 5;
 	shadow->gx = thing->x;
 	shadow->gy = thing->y;
-	shadow->gzt = shadow->pz + SHORT(patch->height) * shadowyscale / 2;
+	shadow->gzt = (isflipped ? shadow->pzt : shadow->pz) + SHORT(patch->height) * shadowyscale / 2;
 	shadow->gz = shadow->gzt - SHORT(patch->height) * shadowyscale;
 	shadow->texturemid = FixedMul(thing->scale, FixedDiv(shadow->gzt - viewz, shadowyscale));
 	if (thing->skin && ((skin_t *)thing->skin)->flags & SF_HIRES)
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index 6335b3028a841997ddb38b92f70164060390fbf5..6a55ac2d6837bd3e76fe5db3165732077143e8a5 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -338,6 +338,7 @@
     <ClCompile Include="..\blua\lfunc.c" />
     <ClCompile Include="..\blua\lgc.c" />
     <ClCompile Include="..\blua\linit.c" />
+    <ClCompile Include="..\blua\liolib.c" />
     <ClCompile Include="..\blua\llex.c" />
     <ClCompile Include="..\blua\lmem.c" />
     <ClCompile Include="..\blua\lobject.c" />
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index a226e8397286000cc794a0172c03d983270e6d16..89ba1b588ab5b149394327b6b5077853772ac5cf 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -534,6 +534,9 @@
     <ClCompile Include="..\blua\linit.c">
       <Filter>BLUA</Filter>
     </ClCompile>
+    <ClCompile Include="..\blua\liolib.c">
+      <Filter>BLUA</Filter>
+    </ClCompile>
     <ClCompile Include="..\blua\llex.c">
       <Filter>BLUA</Filter>
     </ClCompile>
diff --git a/src/tables.c b/src/tables.c
index 00424db22af16fc65b2cf0d3c1821c769fe3c80d..70a1ecd0addf7fae640d44847f7ffec8c70d8278 100644
--- a/src/tables.c
+++ b/src/tables.c
@@ -72,7 +72,7 @@ static FUNCMATH angle_t AngleAdj(const fixed_t fa, const fixed_t wf,
 	const angle_t adj = 0x77;
 	const boolean fan = fa < 0;
 	const fixed_t sl = FixedDiv(fa, wf*2);
-	const fixed_t lb = FixedRem(fa, wf*2);
+	const fixed_t lb = fa % (wf*2);
 	const fixed_t lo = (wf*2)-lb;
 
 	if (ra == 0)