diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index 585fa7857d18b7b843425121811c9ea3281779d5..ea783908a933e63319548291e57d475ff74cbfab 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -3900,6 +3900,8 @@ thingtypes
 		{
 			title = "Emerald Hunt Location";
 			sprite = "SHRDA0";
+			flags8height = 24;
+			flags8text = "[8] Float";
 		}
 		321
 		{
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index b7902ef79a8e580282633a76820b775cfb44622b..a82403097835cf38cb9c8c92b801c26e8724eec8 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1631,7 +1631,6 @@ int LUA_InfoLib(lua_State *L)
 			lua_pushcfunction(L, lib_spriteinfolen);
 			lua_setfield(L, -2, "__len");
 		lua_setmetatable(L, -2);
-	lua_pushvalue(L, -1);
 	lua_setglobal(L, "spriteinfo");
 
 	luaL_newmetatable(L, META_LUABANKS);
diff --git a/src/lua_script.c b/src/lua_script.c
index fff5b88062eb87ff246ec428611327d69d1a0a38..c7ff4d4c37815cf9f420524fc19f2662706e22f2 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -444,9 +444,9 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump)
 	else // If it's not a .lua file, copy the lump name in too.
 	{
 		lumpinfo_t *lump_p = &wadfiles[wad]->lumpinfo[lump];
-		len += 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
+		len += 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name
 		name = malloc(len+1);
-		sprintf(name, "%s|%s", wadfiles[wad]->filename, lump_p->name2);
+		sprintf(name, "%s|%s", wadfiles[wad]->filename, lump_p->fullname);
 		name[len] = '\0';
 	}
 
diff --git a/src/p_ceilng.c b/src/p_ceilng.c
index e80859189bfe70d760bd7c33eb384a28fb28e0ae..f355ee0110b3d8c323f388f58dbe65b23903774c 100644
--- a/src/p_ceilng.c
+++ b/src/p_ceilng.c
@@ -47,8 +47,7 @@ void T_MoveCeiling(ceiling_t *ceiling)
 		case 0: // IN STASIS
 			break;
 		case 1: // UP
-			res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->topheight, false,
-				1, ceiling->direction);
+			res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->topheight, false, true, ceiling->direction);
 
 			if (ceiling->type == bounceCeiling)
 			{
@@ -159,8 +158,7 @@ void T_MoveCeiling(ceiling_t *ceiling)
 			break;
 
 		case -1: // DOWN
-			res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight,
-				ceiling->crush, 1, ceiling->direction);
+			res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight, ceiling->crush, true, ceiling->direction);
 
 			if (ceiling->type == bounceCeiling)
 			{
@@ -314,11 +312,10 @@ void T_CrushCeiling(ceiling_t *ceiling)
 			if (ceiling->type == crushBothOnce)
 			{
 				// Move the floor
-				T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight-(ceiling->topheight-ceiling->bottomheight), false, 0, -ceiling->direction);
+				T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight-(ceiling->topheight-ceiling->bottomheight), false, false, -ceiling->direction);
 			}
 
-			res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->topheight,
-				false, 1, ceiling->direction);
+			res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->topheight, false, true, ceiling->direction);
 
 			if (res == pastdest)
 			{
@@ -357,11 +354,10 @@ void T_CrushCeiling(ceiling_t *ceiling)
 			if (ceiling->type == crushBothOnce)
 			{
 				// Move the floor
-				T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight, ceiling->crush, 0, -ceiling->direction);
+				T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight, ceiling->crush, false, -ceiling->direction);
 			}
 
-			res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight,
-				ceiling->crush, 1, ceiling->direction);
+			res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight, ceiling->crush, true, ceiling->direction);
 
 			if (res == pastdest)
 			{
diff --git a/src/p_floor.c b/src/p_floor.c
index 91c2644352a2c8b16f0f5a06148bd26acf3bc392..fe58050bc06ee0b4333d61899022d206b98e7f0f 100644
--- a/src/p_floor.c
+++ b/src/p_floor.c
@@ -30,146 +30,127 @@
 // Move a plane (floor or ceiling) and check for crushing
 //
 result_e T_MovePlane(sector_t *sector, fixed_t speed, fixed_t dest, boolean crush,
-	INT32 floorOrCeiling, INT32 direction)
+	boolean ceiling, INT32 direction)
 {
-	boolean flag;
 	fixed_t lastpos;
 	fixed_t destheight; // used to keep floors/ceilings from moving through each other
 	sector->moved = true;
 
-	switch (floorOrCeiling)
+	if (ceiling)
 	{
-		case 0:
-			// moving a floor
-			switch (direction)
-			{
-				case -1:
-					// Moving a floor down
-					if (sector->floorheight - speed < dest)
+		lastpos = sector->ceilingheight;
+		// moving a ceiling
+		switch (direction)
+		{
+			case -1:
+				// moving a ceiling down
+				// keep ceiling from moving through floors
+				destheight = (dest > sector->floorheight) ? dest : sector->floorheight;
+				if (sector->ceilingheight - speed < destheight)
+				{
+					sector->ceilingheight = destheight;
+					if (P_CheckSector(sector, crush))
 					{
-						lastpos = sector->floorheight;
-						sector->floorheight = dest;
-						flag = P_CheckSector(sector, crush);
-						if (flag && sector->numattached)
-						{
-							sector->floorheight = lastpos;
-							P_CheckSector(sector, crush);
-						}
-						return pastdest;
+						sector->ceilingheight = lastpos;
+						P_CheckSector(sector, crush);
 					}
-					else
+					return pastdest;
+				}
+				else
+				{
+					// crushing is possible
+					sector->ceilingheight -= speed;
+					if (P_CheckSector(sector, crush))
 					{
-						lastpos = sector->floorheight;
-						sector->floorheight -= speed;
-						flag = P_CheckSector(sector, crush);
-						if (flag && sector->numattached)
-						{
-							sector->floorheight = lastpos;
-							P_CheckSector(sector, crush);
-							return crushed;
-						}
+						sector->ceilingheight = lastpos;
+						P_CheckSector(sector, crush);
+						return crushed;
 					}
-					break;
+				}
+				break;
 
-				case 1:
-					// Moving a floor up
-					// keep floor from moving through ceilings
-					destheight = (dest < sector->ceilingheight) ? dest : sector->ceilingheight;
-					if (sector->floorheight + speed > destheight)
+			case 1:
+				// moving a ceiling up
+				if (sector->ceilingheight + speed > dest)
+				{
+					sector->ceilingheight = dest;
+					if (P_CheckSector(sector, crush) && sector->numattached)
 					{
-						lastpos = sector->floorheight;
-						sector->floorheight = destheight;
-						flag = P_CheckSector(sector, crush);
-						if (flag)
-						{
-							sector->floorheight = lastpos;
-							P_CheckSector(sector, crush);
-						}
-						return pastdest;
+						sector->ceilingheight = lastpos;
+						P_CheckSector(sector, crush);
 					}
-					else
+					return pastdest;
+				}
+				else
+				{
+					sector->ceilingheight += speed;
+					if (P_CheckSector(sector, crush) && sector->numattached)
 					{
-						// crushing is possible
-						lastpos = sector->floorheight;
-						sector->floorheight += speed;
-						flag = P_CheckSector(sector, crush);
-						if (flag)
-						{
-							sector->floorheight = lastpos;
-							P_CheckSector(sector, crush);
-							return crushed;
-						}
+						sector->ceilingheight = lastpos;
+						P_CheckSector(sector, crush);
+						return crushed;
 					}
-					break;
-			}
-			break;
-
-		case 1:
-			// moving a ceiling
-			switch (direction)
-			{
-				case -1:
-					// moving a ceiling down
-					// keep ceiling from moving through floors
-					destheight = (dest > sector->floorheight) ? dest : sector->floorheight;
-					if (sector->ceilingheight - speed < destheight)
+				}
+				break;
+		}
+	}
+	else
+	{
+		lastpos = sector->floorheight;
+		// moving a floor
+		switch (direction)
+		{
+			case -1:
+				// Moving a floor down
+				if (sector->floorheight - speed < dest)
+				{
+					sector->floorheight = dest;
+					if (P_CheckSector(sector, crush) && sector->numattached)
 					{
-						lastpos = sector->ceilingheight;
-						sector->ceilingheight = destheight;
-						flag = P_CheckSector(sector, crush);
-
-						if (flag)
-						{
-							sector->ceilingheight = lastpos;
-							P_CheckSector(sector, crush);
-						}
-						return pastdest;
+						sector->floorheight = lastpos;
+						P_CheckSector(sector, crush);
 					}
-					else
+					return pastdest;
+				}
+				else
+				{
+					sector->floorheight -= speed;
+					if (P_CheckSector(sector, crush) && sector->numattached)
 					{
-						// crushing is possible
-						lastpos = sector->ceilingheight;
-						sector->ceilingheight -= speed;
-						flag = P_CheckSector(sector, crush);
-
-						if (flag)
-						{
-							sector->ceilingheight = lastpos;
-							P_CheckSector(sector, crush);
-							return crushed;
-						}
+						sector->floorheight = lastpos;
+						P_CheckSector(sector, crush);
+						return crushed;
 					}
-					break;
+				}
+				break;
 
-				case 1:
-					// moving a ceiling up
-					if (sector->ceilingheight + speed > dest)
+			case 1:
+				// Moving a floor up
+				// keep floor from moving through ceilings
+				destheight = (dest < sector->ceilingheight) ? dest : sector->ceilingheight;
+				if (sector->floorheight + speed > destheight)
+				{
+					sector->floorheight = destheight;
+					if (P_CheckSector(sector, crush))
 					{
-						lastpos = sector->ceilingheight;
-						sector->ceilingheight = dest;
-						flag = P_CheckSector(sector, crush);
-						if (flag && sector->numattached)
-						{
-							sector->ceilingheight = lastpos;
-							P_CheckSector(sector, crush);
-						}
-						return pastdest;
+						sector->floorheight = lastpos;
+						P_CheckSector(sector, crush);
 					}
-					else
+					return pastdest;
+				}
+				else
+				{
+					// crushing is possible
+					sector->floorheight += speed;
+					if (P_CheckSector(sector, crush))
 					{
-						lastpos = sector->ceilingheight;
-						sector->ceilingheight += speed;
-						flag = P_CheckSector(sector, crush);
-						if (flag && sector->numattached)
-						{
-							sector->ceilingheight = lastpos;
-							P_CheckSector(sector, crush);
-							return crushed;
-						}
+						sector->floorheight = lastpos;
+						P_CheckSector(sector, crush);
+						return crushed;
 					}
-					break;
-			}
-			break;
+				}
+				break;
+		}
 	}
 
 	return ok;
@@ -192,7 +173,7 @@ void T_MoveFloor(floormove_t *movefloor)
 	res = T_MovePlane(movefloor->sector,
 	                  movefloor->speed,
 	                  movefloor->floordestheight,
-	                  movefloor->crush, 0, movefloor->direction);
+	                  movefloor->crush, false, movefloor->direction);
 
 	if (movefloor->type == bounceFloor)
 	{
@@ -385,7 +366,7 @@ void T_MoveElevator(elevator_t *elevator)
 			elevator->speed,
 			elevator->ceilingdestheight,
 			elevator->distance,
-			1,                          // move floor
+			true,                          // move ceiling
 			elevator->direction
 		);
 
@@ -395,7 +376,7 @@ void T_MoveElevator(elevator_t *elevator)
 			elevator->speed,
 			elevator->floordestheight,
 			elevator->distance,
-			0,                        // move ceiling
+			false,                        // move floor
 			elevator->direction
 		);
 
@@ -447,7 +428,7 @@ void T_MoveElevator(elevator_t *elevator)
 			elevator->speed,
 			elevator->floordestheight,
 			elevator->distance,
-			0,                          // move ceiling
+			false,                          // move floor
 			elevator->direction
 		);
 
@@ -459,7 +440,7 @@ void T_MoveElevator(elevator_t *elevator)
 				elevator->speed,
 				elevator->ceilingdestheight,
 				elevator->distance,
-				1,                        // move floor
+				true,                        // move ceiling
 				elevator->direction
 			);
 		}
@@ -580,43 +561,18 @@ void T_MoveElevator(elevator_t *elevator)
 //
 // Useful for things like intermittent falling lava.
 //
-void T_ContinuousFalling(levelspecthink_t *faller)
+void T_ContinuousFalling(continuousfall_t *faller)
 {
-#define speed vars[0]
-#define direction vars[1]
-#define floorwasheight vars[2]
-#define ceilingwasheight vars[3]
-#define floordestheight vars[4]
-#define ceilingdestheight vars[5]
-
-	if (faller->direction == -1)
-	{
-		faller->sector->ceilingheight -= faller->speed;
-		faller->sector->floorheight -= faller->speed;
-	}
-	else
-	{
-		faller->sector->ceilingheight += faller->speed;
-		faller->sector->floorheight += faller->speed;
-	}
+	faller->sector->ceilingheight += faller->speed*faller->direction;
+	faller->sector->floorheight += faller->speed*faller->direction;
 
 	P_CheckSector(faller->sector, false);
 
-	if (faller->direction == -1) // Down
+	if ((faller->direction == -1 && faller->sector->ceilingheight <= faller->destheight)
+		|| (faller->direction == 1 && faller->sector->floorheight >= faller->destheight))
 	{
-		if (faller->sector->ceilingheight <= faller->ceilingdestheight)            // if destination height acheived
-		{
-			faller->sector->ceilingheight = faller->ceilingwasheight;
-			faller->sector->floorheight = faller->floorwasheight;
-		}
-	}
-	else // Up
-	{
-		if (faller->sector->floorheight >= faller->floordestheight)            // if destination height acheived
-		{
-			faller->sector->ceilingheight = faller->ceilingwasheight;
-			faller->sector->floorheight = faller->floorwasheight;
-		}
+		faller->sector->ceilingheight = faller->ceilingstartheight;
+		faller->sector->floorheight = faller->floorstartheight;
 	}
 
 	P_CheckSector(faller->sector, false); // you might think this is irrelevant. you would be wrong
@@ -624,12 +580,6 @@ void T_ContinuousFalling(levelspecthink_t *faller)
 	faller->sector->floorspeed = faller->speed*faller->direction;
 	faller->sector->ceilspeed = 42;
 	faller->sector->moved = true;
-#undef speed
-#undef direction
-#undef floorwasheight
-#undef ceilingwasheight
-#undef floordestheight
-#undef ceilingdestheight
 }
 
 //
@@ -675,25 +625,22 @@ static fixed_t P_SectorCheckWater(sector_t *analyzesector,
 //////////////////////////////////////////////////
 // Bounces a floating cheese
 
-void T_BounceCheese(levelspecthink_t *bouncer)
+void T_BounceCheese(bouncecheese_t *bouncer)
 {
-#define speed vars[0]
-#define distance vars[1]
-#define low vars[2]
-#define ceilingwasheight vars[3]
-#define floorwasheight vars[4]
+	fixed_t sectorheight;
 	fixed_t halfheight;
 	fixed_t waterheight;
 	fixed_t floorheight;
 	sector_t *actionsector;
+	boolean remove;
 	INT32 i;
 	mtag_t tag = Tag_FGet(&bouncer->sourceline->tags);
 	TAG_ITER_C
 
-	if (bouncer->sector->crumblestate == 4 || bouncer->sector->crumblestate == 1
-		|| bouncer->sector->crumblestate == 2) // Oops! Crumbler says to remove yourself!
+	if (bouncer->sector->crumblestate == CRUMBLE_RESTORE || bouncer->sector->crumblestate == CRUMBLE_WAIT
+		|| bouncer->sector->crumblestate == CRUMBLE_ACTIVATED) // Oops! Crumbler says to remove yourself!
 	{
-		bouncer->sector->crumblestate = 1;
+		bouncer->sector->crumblestate = CRUMBLE_WAIT;
 		bouncer->sector->ceilingdata = NULL;
 		bouncer->sector->ceilspeed = 0;
 		bouncer->sector->floordata = NULL;
@@ -708,35 +655,39 @@ void T_BounceCheese(levelspecthink_t *bouncer)
 		actionsector = &sectors[i];
 		actionsector->moved = true;
 
-		halfheight = abs(bouncer->sector->ceilingheight - bouncer->sector->floorheight) >> 1;
+		sectorheight = abs(bouncer->sector->ceilingheight - bouncer->sector->floorheight);
+		halfheight = sectorheight/2;
 
 		waterheight = P_SectorCheckWater(actionsector, bouncer->sector); // sorts itself out if there's no suitable water in the sector
 
-		floorheight = P_FloorzAtPos(actionsector->soundorg.x, actionsector->soundorg.y, bouncer->sector->floorheight, halfheight << 1);
+		floorheight = P_FloorzAtPos(actionsector->soundorg.x, actionsector->soundorg.y, bouncer->sector->floorheight, sectorheight);
+
+		remove = false;
 
 		// Water level is up to the ceiling.
 		if (waterheight > bouncer->sector->ceilingheight - halfheight && bouncer->sector->ceilingheight >= actionsector->ceilingheight) // Tails 01-08-2004
 		{
 			bouncer->sector->ceilingheight = actionsector->ceilingheight;
-			bouncer->sector->floorheight = bouncer->sector->ceilingheight - (halfheight*2);
-			T_MovePlane(bouncer->sector, 0, bouncer->sector->ceilingheight, 0, 1, -1); // update things on ceiling
-			T_MovePlane(bouncer->sector, 0, bouncer->sector->floorheight, 0, 0, -1); // update things on floor
-			P_RecalcPrecipInSector(actionsector);
-			bouncer->sector->ceilingdata = NULL;
-			bouncer->sector->floordata = NULL;
-			bouncer->sector->floorspeed = 0;
-			bouncer->sector->ceilspeed = 0;
-			bouncer->sector->moved = true;
-			P_RemoveThinker(&bouncer->thinker); // remove bouncer from actives
-			return;
+			bouncer->sector->floorheight = actionsector->ceilingheight - sectorheight;
+			remove = true;
 		}
 		// Water level is too shallow.
 		else if (waterheight < bouncer->sector->floorheight + halfheight && bouncer->sector->floorheight <= floorheight)
 		{
-			bouncer->sector->ceilingheight = floorheight + (halfheight << 1);
+			bouncer->sector->ceilingheight = floorheight + sectorheight;
 			bouncer->sector->floorheight = floorheight;
-			T_MovePlane(bouncer->sector, 0, bouncer->sector->ceilingheight, 0, 1, -1); // update things on ceiling
-			T_MovePlane(bouncer->sector, 0, bouncer->sector->floorheight, 0, 0, -1); // update things on floor
+			remove = true;
+		}
+		else
+		{
+			bouncer->ceilingwasheight = waterheight + halfheight;
+			bouncer->floorwasheight = waterheight - halfheight;
+		}
+
+		if (remove)
+		{
+			T_MovePlane(bouncer->sector, 0, bouncer->sector->ceilingheight, false, true, -1); // update things on ceiling
+			T_MovePlane(bouncer->sector, 0, bouncer->sector->floorheight, false, false, -1); // update things on floor
 			P_RecalcPrecipInSector(actionsector);
 			bouncer->sector->ceilingdata = NULL;
 			bouncer->sector->floordata = NULL;
@@ -746,43 +697,24 @@ void T_BounceCheese(levelspecthink_t *bouncer)
 			P_RemoveThinker(&bouncer->thinker); // remove bouncer from actives
 			return;
 		}
-		else
-		{
-			bouncer->ceilingwasheight = waterheight + halfheight;
-			bouncer->floorwasheight = waterheight - halfheight;
-		}
 
 		T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->ceilingheight -
-			70*FRACUNIT, 0, 1, -1); // move ceiling
+			70*FRACUNIT, false, true, -1); // move ceiling
 		T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->floorheight - 70*FRACUNIT,
-			0, 0, -1); // move floor
+			false, false, -1); // move floor
 
 		bouncer->sector->floorspeed = -bouncer->speed/2;
 		bouncer->sector->ceilspeed = 42;
 
-		if (bouncer->sector->ceilingheight < bouncer->ceilingwasheight && bouncer->low == 0) // Down
+		if ((bouncer->sector->ceilingheight < bouncer->ceilingwasheight && !bouncer->low) // Down
+			|| (bouncer->sector->ceilingheight > bouncer->ceilingwasheight && bouncer->low)) // Up
 		{
 			if (abs(bouncer->speed) < 6*FRACUNIT)
 				bouncer->speed -= bouncer->speed/3;
 			else
 				bouncer->speed -= bouncer->speed/2;
 
-			bouncer->low = 1;
-			if (abs(bouncer->speed) > 6*FRACUNIT)
-			{
-				mobj_t *mp = (void *)&actionsector->soundorg;
-				actionsector->soundorg.z = bouncer->sector->floorheight;
-				S_StartSound(mp, sfx_splash);
-			}
-		}
-		else if (bouncer->sector->ceilingheight > bouncer->ceilingwasheight && bouncer->low) // Up
-		{
-			if (abs(bouncer->speed) < 6*FRACUNIT)
-				bouncer->speed -= bouncer->speed/3;
-			else
-				bouncer->speed -= bouncer->speed/2;
-
-			bouncer->low = 0;
+			bouncer->low = !bouncer->low;
 			if (abs(bouncer->speed) > 6*FRACUNIT)
 			{
 				mobj_t *mp = (void *)&actionsector->soundorg;
@@ -805,8 +737,8 @@ void T_BounceCheese(levelspecthink_t *bouncer)
 		{
 			bouncer->sector->floorheight = bouncer->floorwasheight;
 			bouncer->sector->ceilingheight = bouncer->ceilingwasheight;
-			T_MovePlane(bouncer->sector, 0, bouncer->sector->ceilingheight, 0, 1, -1); // update things on ceiling
-			T_MovePlane(bouncer->sector, 0, bouncer->sector->floorheight, 0, 0, -1); // update things on floor
+			T_MovePlane(bouncer->sector, 0, bouncer->sector->ceilingheight, false, true, -1); // update things on ceiling
+			T_MovePlane(bouncer->sector, 0, bouncer->sector->floorheight, false, false, -1); // update things on floor
 			bouncer->sector->ceilingdata = NULL;
 			bouncer->sector->floordata = NULL;
 			bouncer->sector->floorspeed = 0;
@@ -821,26 +753,12 @@ void T_BounceCheese(levelspecthink_t *bouncer)
 		if (actionsector)
 			P_RecalcPrecipInSector(actionsector);
 	}
-#undef speed
-#undef distance
-#undef low
-#undef ceilingwasheight
-#undef floorwasheight
 }
 
 //////////////////////////////////////////////////
 // T_StartCrumble ////////////////////////////////
 //////////////////////////////////////////////////
 // Crumbling platform Tails 03-11-2002
-//
-// DEFINITION OF THE 'CRUMBLESTATE'S:
-//
-// 0 - No crumble thinker
-// 1 - Don't float on water because this is supposed to wait for a crumble
-// 2 - Crumble thinker activated, but hasn't fallen yet
-// 3 - Crumble thinker is falling
-// 4 - Crumble thinker is about to restore to original position
-//
 void T_StartCrumble(elevator_t *elevator)
 {
 	ffloor_t *rover;
@@ -938,13 +856,13 @@ void T_StartCrumble(elevator_t *elevator)
 		// so set this to let other thinkers know what is
 		// about to happen.
 		if (elevator->distance < 0 && elevator->distance > -3)
-			elevator->sector->crumblestate = 4; // makes T_BounceCheese remove itself
+			elevator->sector->crumblestate = CRUMBLE_RESTORE; // makes T_BounceCheese remove itself
 	}
 
 	if ((elevator->floordestheight == 0 && elevator->direction == -1)
 		|| (elevator->floordestheight == 1 && elevator->direction == 1)) // Down
 	{
-		elevator->sector->crumblestate = 3; // Allow floating now.
+		elevator->sector->crumblestate = CRUMBLE_FALL; // Allow floating now.
 
 		// Only fall like this if it isn't meant to float on water
 		if (elevator->high != 42)
@@ -966,8 +884,8 @@ void T_StartCrumble(elevator_t *elevator)
 				  elevator->sector,
 				  elevator->speed,
 				  dest,
-				  0,
-				  1, // move floor
+				  false,
+				  true, // move ceiling
 				  elevator->direction
 				);
 
@@ -981,8 +899,8 @@ void T_StartCrumble(elevator_t *elevator)
 					elevator->sector,
 					elevator->speed,
 					dest,
-					0,
-					0,                        // move ceiling
+					false,
+					false, // move floor
 					elevator->direction
 				);
 
@@ -993,7 +911,7 @@ void T_StartCrumble(elevator_t *elevator)
 	}
 	else // Up (restore to original position)
 	{
-		elevator->sector->crumblestate = 1;
+		elevator->sector->crumblestate = CRUMBLE_WAIT;
 		elevator->sector->ceilingheight = elevator->ceilingwasheight;
 		elevator->sector->floorheight = elevator->floorwasheight;
 		elevator->sector->floordata = NULL;
@@ -1017,25 +935,18 @@ void T_StartCrumble(elevator_t *elevator)
 //////////////////////////////////////////////////
 // Mario hits a block!
 //
-void T_MarioBlock(levelspecthink_t *block)
+void T_MarioBlock(mariothink_t *block)
 {
 	INT32 i;
 	TAG_ITER_C
 
-#define speed vars[1]
-#define direction vars[2]
-#define floorwasheight vars[3]
-#define ceilingwasheight vars[4]
-#define distance vars[5]
-#define low vars[6]
-
 	T_MovePlane
 	(
 	  block->sector,
 	  block->speed,
 	  block->sector->ceilingheight + 70*FRACUNIT * block->direction,
-	  0,
-	  1, // move floor
+	  false,
+	  true, // move ceiling
 	  block->direction
 	);
 
@@ -1044,17 +955,17 @@ void T_MarioBlock(levelspecthink_t *block)
 	  block->sector,
 	  block->speed,
 	  block->sector->floorheight + 70*FRACUNIT * block->direction,
-	  0,
-	  0, // move ceiling
+	  false,
+	  false, // move floor
 	  block->direction
 	);
 
-	if (block->sector->ceilingheight >= block->ceilingwasheight + 32*FRACUNIT) // Go back down now..
-		block->direction = -block->direction;
-	else if (block->sector->ceilingheight <= block->ceilingwasheight)
+	if (block->sector->ceilingheight >= block->ceilingstartheight + 32*FRACUNIT) // Go back down now..
+		block->direction *= -1;
+	else if (block->sector->ceilingheight <= block->ceilingstartheight)
 	{
-		block->sector->ceilingheight = block->ceilingwasheight;
-		block->sector->floorheight = block->floorwasheight;
+		block->sector->ceilingheight = block->ceilingstartheight;
+		block->sector->floorheight = block->floorstartheight;
 		P_RemoveThinker(&block->thinker);
 		block->sector->floordata = NULL;
 		block->sector->ceilingdata = NULL;
@@ -1062,121 +973,40 @@ void T_MarioBlock(levelspecthink_t *block)
 		block->sector->ceilspeed = 0;
 		block->direction = 0;
 	}
-	TAG_ITER_SECTORS((INT16)block->vars[0], i)
+	TAG_ITER_SECTORS((INT16)block->tag, i)
 		P_RecalcPrecipInSector(&sectors[i]);
-
-#undef speed
-#undef direction
-#undef floorwasheight
-#undef ceilingwasheight
-#undef distance
-#undef low
 }
 
-void T_SpikeSector(levelspecthink_t *spikes)
-{
-	mobj_t *thing;
-	msecnode_t *node;
-	boolean dothepain;
-	sector_t *affectsec;
-
-	node = spikes->sector->touching_thinglist; // things touching this sector
-
-	for (; node; node = node->m_thinglist_next)
-	{
-		thing = node->m_thing;
-		if (!thing->player)
-			continue;
-
-		dothepain = false;
-		affectsec = &sectors[spikes->vars[0]];
-
-		if (affectsec == spikes->sector) // Applied to an actual sector
-		{
-			fixed_t affectfloor = P_GetSpecialBottomZ(thing, affectsec, affectsec);
-			fixed_t affectceil = P_GetSpecialTopZ(thing, affectsec, affectsec);
-
-			if (affectsec->flags & SF_FLIPSPECIAL_FLOOR)
-			{
-				if (!(thing->eflags & MFE_VERTICALFLIP) && thing->momz > 0)
-					continue;
-
-				if (thing->z == affectfloor)
-					dothepain = true;
-			}
-
-			if (affectsec->flags & SF_FLIPSPECIAL_CEILING)
-			{
-				if ((thing->eflags & MFE_VERTICALFLIP) && thing->momz < 0)
-					continue;
-
-				if (thing->z + thing->height == affectceil)
-					dothepain = true;
-			}
-		}
-		else
-		{
-			fixed_t affectfloor = P_GetSpecialBottomZ(thing, affectsec, spikes->sector);
-			fixed_t affectceil = P_GetSpecialTopZ(thing, affectsec, spikes->sector);
-			if (affectsec->flags & SF_FLIPSPECIAL_FLOOR)
-			{
-				if (!(thing->eflags & MFE_VERTICALFLIP) && thing->momz > 0)
-					continue;
-
-				if (thing->z == affectceil)
-					dothepain = true;
-			}
-
-			if (affectsec->flags & SF_FLIPSPECIAL_CEILING)
-			{
-				if ((thing->eflags & MFE_VERTICALFLIP) && thing->momz < 0)
-					continue;
-
-				if (thing->z + thing->height == affectfloor)
-					dothepain = true;
-			}
-		}
-
-		if (dothepain)
-		{
-			P_DamageMobj(thing, NULL, NULL, 1, DMG_SPIKE);
-			break;
-		}
-	}
-}
-
-void T_FloatSector(levelspecthink_t *floater)
+void T_FloatSector(floatthink_t *floater)
 {
 	fixed_t cheeseheight;
+	fixed_t waterheight;
 	sector_t *actionsector;
 	INT32 secnum;
 
-	cheeseheight = (floater->sector->ceilingheight + floater->sector->floorheight)>>1;
-
 	// Just find the first sector with the tag.
 	// Doesn't work with multiple sectors that have different floor/ceiling heights.
-	if ((secnum = Tag_Iterate_Sectors((INT16)floater->vars[0], 0)) >= 0)
-		actionsector = &sectors[secnum];
-	else
-		actionsector = NULL;
+	if ((secnum = Tag_Iterate_Sectors((INT16)floater->tag, 0)) < 0)
+		return;
+	actionsector = &sectors[secnum];
 
-	if (actionsector)
-	{
-		//boolean floatanyway = false; // Ignore the crumblestate setting.
-		fixed_t waterheight = P_SectorCheckWater(actionsector, floater->sector); // find the highest suitable water block around
-
-		if (waterheight == cheeseheight) // same height, no floating needed
-			;
-		else if (floater->sector->floorheight == actionsector->floorheight && waterheight < cheeseheight) // too low
-			;
-		else if (floater->sector->ceilingheight == actionsector->ceilingheight && waterheight > cheeseheight) // too high
-			;
-		// we have something to float in! Or we're for some reason above the ground, let's fall anyway
-		else if (floater->sector->crumblestate == 0 || floater->sector->crumblestate >= 3/* || floatanyway*/)
-			EV_BounceSector(floater->sector, FRACUNIT, floater->sourceline);
-
-		P_RecalcPrecipInSector(actionsector);
-	}
+	cheeseheight = (floater->sector->ceilingheight + floater->sector->floorheight)>>1;
+
+	//boolean floatanyway = false; // Ignore the crumblestate setting.
+	waterheight = P_SectorCheckWater(actionsector, floater->sector); // find the highest suitable water block around
+
+	if (waterheight == cheeseheight) // same height, no floating needed
+		return;
+
+	if (floater->sector->floorheight == actionsector->floorheight && waterheight < cheeseheight) // too low
+		return;
+
+	if (floater->sector->ceilingheight == actionsector->ceilingheight && waterheight > cheeseheight) // too high
+		return;
+
+	// we have something to float in! Or we're for some reason above the ground, let's fall anyway
+	if (floater->sector->crumblestate == CRUMBLE_NONE || floater->sector->crumblestate >= CRUMBLE_FALL/* || floatanyway*/)
+		EV_BounceSector(floater->sector, FRACUNIT, floater->sourceline);
 }
 
 static mobj_t *SearchMarioNode(msecnode_t *node)
@@ -1244,202 +1074,202 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
 	return thing;
 }
 
-void T_MarioBlockChecker(levelspecthink_t *block)
+void T_MarioBlockChecker(mariocheck_t *block)
 {
 	line_t *masterline = block->sourceline;
-	if (block->vars[2] == 1) // Don't update the textures when the block's being bumped upwards.
-		return;
 	if (SearchMarioNode(block->sector->touching_thinglist))
 	{
 		sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].bottomtexture; // Update textures
 		if (masterline->backsector)
-		{
 			block->sector->ceilingpic = block->sector->floorpic = masterline->backsector->ceilingpic; // Update flats to be backside's ceiling
-		}
 	}
 	else
 	{
 		sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].toptexture;
 		if (masterline->backsector)
-		{
 			block->sector->ceilingpic = block->sector->floorpic = masterline->backsector->floorpic; // Update flats to be backside's floor
-		}
 	}
 }
 
+static boolean P_IsPlayerValid(size_t playernum)
+{
+	if (!playeringame[playernum])
+		return false;
+
+	if (!players[playernum].mo)
+		return false;
+
+	if (players[playernum].mo->health <= 0)
+		return false;
+
+	if (players[playernum].spectator)
+		return false;
+
+	return true;
+}
+
 // This is the Thwomp's 'brain'. It looks around for players nearby, and if
 // it finds any, **SMASH**!!! Muahahhaa....
-void T_ThwompSector(levelspecthink_t *thwomp)
+void T_ThwompSector(thwomp_t *thwomp)
 {
-#define speed vars[1]
-#define direction vars[2]
-#define distance vars[3]
-#define floorwasheight vars[4]
-#define ceilingwasheight vars[5]
 	fixed_t thwompx, thwompy;
 	sector_t *actionsector;
 	ffloor_t *rover = NULL;
 	INT32 secnum;
+	fixed_t speed;
 
 	// If you just crashed down, wait a second before coming back up.
-	if (--thwomp->distance > 0)
-	{
-		sides[thwomp->sourceline->sidenum[0]].midtexture = sides[thwomp->sourceline->sidenum[0]].bottomtexture;
+	if (--thwomp->delay > 0)
 		return;
-	}
 
 	// Just find the first sector with the tag.
 	// Doesn't work with multiple sectors that have different floor/ceiling heights.
-	if ((secnum = Tag_Iterate_Sectors((INT16)thwomp->vars[0], 0)) >= 0)
+	if ((secnum = Tag_Iterate_Sectors((INT16)thwomp->tag, 0)) < 0)
+		return;
+
+	actionsector = &sectors[secnum];
+
+	// Look for thwomp FOF
+	for (rover = actionsector->ffloors; rover; rover = rover->next)
 	{
-		actionsector = &sectors[secnum];
+		if (rover->master == thwomp->sourceline)
+			break;
+	}
+
+	if (!rover)
+		return; // Didn't find any FOFs, so bail out
 
-		// Look for thwomp FFloor
-		for (rover = actionsector->ffloors; rover; rover = rover->next)
+	thwompx = actionsector->soundorg.x;
+	thwompy = actionsector->soundorg.y;
+
+	if (thwomp->direction == 0) // Not going anywhere, so look for players.
+	{
+		if (rover->flags & FF_EXISTS)
 		{
-			if (rover->master == thwomp->sourceline)
+			UINT8 i;
+			// scan the players to find victims!
+			for (i = 0; i < MAXPLAYERS; i++)
+			{
+				if (!P_IsPlayerValid(i))
+					continue;
+
+				if (players[i].mo->z > thwomp->sector->ceilingheight)
+					continue;
+
+				if (P_AproxDistance(thwompx - players[i].mo->x, thwompy - players[i].mo->y) > 96*FRACUNIT)
+					continue;
+
+				thwomp->direction = -1;
 				break;
+			}
 		}
+
+		thwomp->sector->ceilspeed = 0;
+		thwomp->sector->floorspeed = 0;
 	}
 	else
-		return; // Bad bad bad!
-
-	thwompx = actionsector->soundorg.x;
-	thwompy = actionsector->soundorg.y;
-
-	if (thwomp->direction > 0) // Moving back up..
 	{
 		result_e res = 0;
 
-		// Set the texture from the lower one (normal)
-		sides[thwomp->sourceline->sidenum[0]].midtexture = sides[thwomp->sourceline->sidenum[0]].bottomtexture;
-		/// \note this should only have to be done once, but is already done repeatedly, above
-
-		if (thwomp->sourceline->flags & ML_EFFECT5)
-			thwomp->speed = thwomp->sourceline->dx/8;
-		else
-			thwomp->speed = 2*FRACUNIT;
+		if (thwomp->direction > 0) //Moving back up..
+		{
+			// Set the texture from the lower one (normal)
+			sides[thwomp->sourceline->sidenum[0]].midtexture = sides[thwomp->sourceline->sidenum[0]].bottomtexture;
 
-		res = T_MovePlane
-		(
-			thwomp->sector,         // sector
-			thwomp->speed,          // speed
-			thwomp->floorwasheight, // dest
-			0,                      // crush
-			0,                      // floor or ceiling (0 for floor)
-			thwomp->direction       // direction
-		);
+			speed = thwomp->retractspeed;
 
-		if (res == ok || res == pastdest)
-			T_MovePlane
+			res = T_MovePlane
 			(
 				thwomp->sector,           // sector
-				thwomp->speed,            // speed
-				thwomp->ceilingwasheight, // dest
-				0,                        // crush
-				1,                        // floor or ceiling (1 for ceiling)
+				speed,                    // speed
+				thwomp->floorstartheight, // dest
+				false,                    // crush
+				false,                    // ceiling?
 				thwomp->direction         // direction
 			);
 
-		if (res == pastdest)
-			thwomp->direction = 0; // stop moving
-
-		thwomp->sector->ceilspeed = 42;
-		thwomp->sector->floorspeed = thwomp->speed*thwomp->direction;
-	}
-	else if (thwomp->direction < 0) // Crashing down!
-	{
-		result_e res = 0;
+			if (res == ok || res == pastdest)
+				T_MovePlane
+				(
+					thwomp->sector,             // sector
+					speed,                      // speed
+					thwomp->ceilingstartheight, // dest
+					false,                      // crush
+					true,                       // ceiling?
+					thwomp->direction           // direction
+				);
 
-		// Set the texture from the upper one (angry)
-		sides[thwomp->sourceline->sidenum[0]].midtexture = sides[thwomp->sourceline->sidenum[0]].toptexture;
+			if (res == pastdest)
+				thwomp->direction = 0; // stop moving
+			}
+		else // Crashing down!
+		{
+			// Set the texture from the upper one (angry)
+			sides[thwomp->sourceline->sidenum[0]].midtexture = sides[thwomp->sourceline->sidenum[0]].toptexture;
 
-		if (thwomp->sourceline->flags & ML_EFFECT5)
-			thwomp->speed = thwomp->sourceline->dy/8;
-		else
-			thwomp->speed = 10*FRACUNIT;
+			speed = thwomp->crushspeed;
 
-		res = T_MovePlane
-		(
-			thwomp->sector,   // sector
-			thwomp->speed,    // speed
-			P_FloorzAtPos(thwompx, thwompy, thwomp->sector->floorheight,
-				thwomp->sector->ceilingheight - thwomp->sector->floorheight), // dest
-			0,                  // crush
-			0,                  // floor or ceiling (0 for floor)
-			thwomp->direction // direction
-		);
-
-		if (res == ok || res == pastdest)
-			T_MovePlane
+			res = T_MovePlane
 			(
 				thwomp->sector,   // sector
-				thwomp->speed,    // speed
+				speed,            // speed
 				P_FloorzAtPos(thwompx, thwompy, thwomp->sector->floorheight,
-					thwomp->sector->ceilingheight
-					- (thwomp->sector->floorheight + thwomp->speed))
-					+ (thwomp->sector->ceilingheight
-					- (thwomp->sector->floorheight + thwomp->speed/2)), // dest
-				0,                  // crush
-				1,                  // floor or ceiling (1 for ceiling)
+					thwomp->sector->ceilingheight - thwomp->sector->floorheight), // dest
+				false,              // crush
+				false,              // ceiling?
 				thwomp->direction // direction
 			);
 
-		if (res == pastdest)
-		{
-			mobj_t *mp = (void *)&actionsector->soundorg;
+			if (res == ok || res == pastdest)
+				T_MovePlane
+				(
+					thwomp->sector,   // sector
+					speed,            // speed
+					P_FloorzAtPos(thwompx, thwompy, thwomp->sector->floorheight,
+						thwomp->sector->ceilingheight
+						- (thwomp->sector->floorheight + speed))
+						+ (thwomp->sector->ceilingheight
+						- (thwomp->sector->floorheight + speed/2)), // dest
+					false,             // crush
+					true,              // ceiling?
+					thwomp->direction // direction
+				);
 
-			if (!rover || (rover->flags & FF_EXISTS))
+			if (res == pastdest)
 			{
-				if (thwomp->sourceline->flags & ML_EFFECT4)
-					S_StartSound(mp, sides[thwomp->sourceline->sidenum[0]].textureoffset>>FRACBITS);
-				else
-					S_StartSound(mp, sfx_thwomp);
-			}
+				if (rover->flags & FF_EXISTS)
+					S_StartSound((void *)&actionsector->soundorg, thwomp->sound);
 
-			thwomp->direction = 1; // start heading back up
-			thwomp->distance = TICRATE; // but only after a small delay
+				thwomp->direction = 1; // start heading back up
+				thwomp->delay = TICRATE; // but only after a small delay
+			}
 		}
 
 		thwomp->sector->ceilspeed = 42;
-		thwomp->sector->floorspeed = thwomp->speed*thwomp->direction;
+		thwomp->sector->floorspeed = speed*thwomp->direction;
 	}
-	else // Not going anywhere, so look for players.
+
+	P_RecalcPrecipInSector(actionsector);
+}
+
+static boolean T_SectorHasEnemies(sector_t *sec)
+{
+	msecnode_t *node = sec->touching_thinglist; // things touching this sector
+	mobj_t *mo;
+	while (node)
 	{
-		if (!rover || (rover->flags & FF_EXISTS))
-		{
-			UINT8 i;
-			// scan the players to find victims!
-			for (i = 0; i < MAXPLAYERS; i++)
-			{
-				if (!playeringame[i])
-					continue;
-				if (players[i].spectator)
-					continue;
-				if (!players[i].mo)
-					continue;
-				if (!players[i].mo->health)
-					continue;
-				if (players[i].mo->z > thwomp->sector->ceilingheight)
-					continue;
-				if (P_AproxDistance(thwompx - players[i].mo->x, thwompy - players[i].mo->y) > 96 * FRACUNIT)
-					continue;
+		mo = node->m_thing;
 
-				thwomp->direction = -1;
-				break;
-			}
-		}
+		if ((mo->flags & (MF_ENEMY|MF_BOSS))
+			&& mo->health > 0
+			&& mo->z < sec->ceilingheight
+			&& mo->z + mo->height > sec->floorheight)
+			return true;
 
-		thwomp->sector->ceilspeed = 0;
-		thwomp->sector->floorspeed = 0;
+		node = node->m_thinglist_next;
 	}
 
-	P_RecalcPrecipInSector(actionsector);
-#undef speed
-#undef direction
-#undef distance
-#undef floorwasheight
-#undef ceilingwasheight
+	return false;
 }
 
 //
@@ -1448,15 +1278,11 @@ void T_ThwompSector(levelspecthink_t *thwomp)
 // Runs a linedef exec when no more MF_ENEMY/MF_BOSS objects with health are in the area
 // \sa P_AddNoEnemiesThinker
 //
-void T_NoEnemiesSector(levelspecthink_t *nobaddies)
+void T_NoEnemiesSector(noenemies_t *nobaddies)
 {
 	size_t i;
-	fixed_t upperbound, lowerbound;
 	sector_t *sec = NULL;
-	sector_t *targetsec = NULL;
 	INT32 secnum = -1;
-	msecnode_t *node;
-	mobj_t *thing;
 	boolean FOFsector = false;
 	mtag_t tag = Tag_FGet(&nobaddies->sourceline->tags);
 	TAG_ITER_C
@@ -1481,40 +1307,13 @@ void T_NoEnemiesSector(levelspecthink_t *nobaddies)
 
 			TAG_ITER_SECTORS(tag2, targetsecnum)
 			{
-				targetsec = &sectors[targetsecnum];
-
-				upperbound = targetsec->ceilingheight;
-				lowerbound = targetsec->floorheight;
-				node = targetsec->touching_thinglist; // things touching this sector
-				while (node)
-				{
-					thing = node->m_thing;
-
-					if ((thing->flags & (MF_ENEMY|MF_BOSS)) && thing->health > 0
-					&& thing->z < upperbound && thing->z+thing->height > lowerbound)
-						return;
-
-					node = node->m_thinglist_next;
-				}
-			}
-		}
-
-		if (!FOFsector)
-		{
-			upperbound = sec->ceilingheight;
-			lowerbound = sec->floorheight;
-			node = sec->touching_thinglist; // things touching this sector
-			while (node)
-			{
-				thing = node->m_thing;
-
-				if ((thing->flags & (MF_ENEMY|MF_BOSS)) && thing->health > 0
-				&& thing->z < upperbound && thing->z+thing->height > lowerbound)
+				if (T_SectorHasEnemies(&sectors[targetsecnum]))
 					return;
-
-				node = node->m_thinglist_next;
 			}
 		}
+
+		if (!FOFsector && T_SectorHasEnemies(sec))
+			return;
 	}
 
 	CONS_Debug(DBG_GAMELOGIC, "Running no-more-enemies exec with tag of %d\n", tag);
@@ -1551,30 +1350,23 @@ static boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec)
 	return false;
 }
 
-//
-// P_HavePlayersEnteredArea
-//
-// Helper function for T_EachTimeThinker
-//
-static INT32 P_HavePlayersEnteredArea(boolean *curPlayers, boolean *oldPlayers, boolean inAndOut)
+static boolean P_IsMobjTouchingSector(mobj_t *mo, sector_t *sec)
 {
-	INT32 i;
+	msecnode_t *node;
 
-	// Easy check... nothing has changed
-	if (!memcmp(curPlayers, oldPlayers, sizeof(boolean)*MAXPLAYERS))
-		return -1;
+	if (mo->subsector->sector == sec)
+		return true;
 
-	// Otherwise, we have to check if any new players have entered
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		if (inAndOut && !curPlayers[i] && oldPlayers[i])
-			return i;
+	if (!(sec->flags & SF_TRIGGERSPECIAL_TOUCH))
+		return false;
 
-		if (curPlayers[i] && !oldPlayers[i])
-			return i;
+	for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
+	{
+		if (node->m_sector == sec)
+			return true;
 	}
 
-	return -1;
+	return false;
 }
 
 //
@@ -1585,46 +1377,29 @@ static INT32 P_HavePlayersEnteredArea(boolean *curPlayers, boolean *oldPlayers,
 //
 // \sa P_AddEachTimeThinker
 //
-void T_EachTimeThinker(levelspecthink_t *eachtime)
+void T_EachTimeThinker(eachtime_t *eachtime)
 {
 	size_t i, j;
 	sector_t *sec = NULL;
 	sector_t *targetsec = NULL;
-	//sector_t *usesec = NULL;
 	INT32 secnum = -1;
-	INT32 affectPlayer = 0;
 	boolean oldPlayersInArea[MAXPLAYERS];
-	boolean playersInArea[MAXPLAYERS];
 	boolean oldPlayersOnArea[MAXPLAYERS];
-	boolean playersOnArea[MAXPLAYERS];
 	boolean *oldPlayersArea;
 	boolean *playersArea;
 	boolean FOFsector = false;
-	boolean inAndOut = false;
 	boolean floortouch = false;
 	fixed_t bottomheight, topheight;
-	msecnode_t *node;
 	ffloor_t *rover;
 	mtag_t tag = Tag_FGet(&eachtime->sourceline->tags);
 	TAG_ITER_C
 
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
-		if (i & 1)
-		{
-			oldPlayersInArea[i] = eachtime->vars[i/2] & 65535;
-			oldPlayersOnArea[i] = eachtime->var2s[i/2] & 65535;
-			eachtime->vars[i/2] = 0;
-			eachtime->var2s[i/2] = 0;
-		}
-		else
-		{
-			oldPlayersInArea[i] = eachtime->vars[i/2] >> 16;
-			oldPlayersOnArea[i] = eachtime->var2s[i/2] >> 16;
-		}
-
-		playersInArea[i] = false;
-		playersOnArea[i] = false;
+		oldPlayersInArea[i] = eachtime->playersInArea[i];
+		oldPlayersOnArea[i] = eachtime->playersOnArea[i];
+		eachtime->playersInArea[i] = false;
+		eachtime->playersOnArea[i] = false;
 	}
 
 	TAG_ITER_SECTORS(tag, secnum)
@@ -1671,35 +1446,10 @@ void T_EachTimeThinker(levelspecthink_t *eachtime)
 
 				for (j = 0; j < MAXPLAYERS; j++)
 				{
-					if (!playeringame[j])
+					if (!P_IsPlayerValid(j))
 						continue;
 
-					if (!players[j].mo)
-						continue;
-
-					if (players[j].mo->health <= 0)
-						continue;
-
-					if ((netgame || multiplayer) && players[j].spectator)
-						continue;
-
-					if (players[j].mo->subsector->sector == targetsec)
-						;
-					else if (sec->flags & SF_TRIGGERSPECIAL_TOUCH)
-					{
-						boolean insector = false;
-						for (node = players[j].mo->touching_sectorlist; node; node = node->m_sectorlist_next)
-						{
-							if (node->m_sector == targetsec)
-							{
-								insector = true;
-								break;
-							}
-						}
-						if (!insector)
-							continue;
-					}
-					else
+					if (!P_IsMobjTouchingSector(players[j].mo, targetsec))
 						continue;
 
 					topheight = P_GetSpecialTopZ(players[j].mo, sec, targetsec);
@@ -1711,24 +1461,10 @@ void T_EachTimeThinker(levelspecthink_t *eachtime)
 					if (players[j].mo->z + players[j].mo->height < bottomheight)
 						continue;
 
-					if (floortouch == true && P_IsObjectOnGroundIn(players[j].mo, targetsec))
-					{
-						if (j & 1)
-							eachtime->var2s[j/2] |= 1;
-						else
-							eachtime->var2s[j/2] |= 1 << 16;
-
-						playersOnArea[j] = true;
-					}
+					if (floortouch && P_IsObjectOnGroundIn(players[j].mo, targetsec))
+						eachtime->playersOnArea[j] = true;
 					else
-					{
-						if (j & 1)
-							eachtime->vars[j/2] |= 1;
-						else
-							eachtime->vars[j/2] |= 1 << 16;
-
-						playersInArea[j] = true;
-					}
+						eachtime->playersInArea[j] = true;
 				}
 			}
 		}
@@ -1737,102 +1473,61 @@ void T_EachTimeThinker(levelspecthink_t *eachtime)
 		{
 			for (i = 0; i < MAXPLAYERS; i++)
 			{
-				if (!playeringame[i])
-					continue;
-
-				if (!players[i].mo)
-					continue;
-
-				if (players[i].mo->health <= 0)
+				if (!P_IsPlayerValid(i))
 					continue;
 
-				if ((netgame || multiplayer) && players[i].spectator)
-					continue;
-
-				if (players[i].mo->subsector->sector == sec)
-					;
-				else if (sec->flags & SF_TRIGGERSPECIAL_TOUCH)
-				{
-					boolean insector = false;
-					for (node = players[i].mo->touching_sectorlist; node; node = node->m_sectorlist_next)
-					{
-						if (node->m_sector == sec)
-						{
-							insector = true;
-							break;
-						}
-					}
-					if (!insector)
-						continue;
-				}
-				else
+				if (!P_IsMobjTouchingSector(players[i].mo, sec))
 					continue;
 
 				if (!(players[i].mo->subsector->sector == sec
 					|| P_PlayerTouchingSectorSpecial(&players[i], 2, (GETSECSPECIAL(sec->special, 2))) == sec))
 					continue;
 
-				if (floortouch == true && P_IsObjectOnRealGround(players[i].mo, sec))
-				{
-					if (i & 1)
-						eachtime->var2s[i/2] |= 1;
-					else
-						eachtime->var2s[i/2] |= 1 << 16;
-
-					playersOnArea[i] = true;
-				}
+				if (floortouch && P_IsObjectOnRealGround(players[i].mo, sec))
+					eachtime->playersOnArea[i] = true;
 				else
-				{
-					if (i & 1)
-						eachtime->vars[i/2] |= 1;
-					else
-						eachtime->vars[i/2] |= 1 << 16;
-
-					playersInArea[i] = true;
-				}
+					eachtime->playersInArea[i] = true;
 			}
 		}
 	}
 
-	if ((eachtime->sourceline->flags & ML_BOUNCY) == ML_BOUNCY)
-		inAndOut = true;
-
 	// Check if a new player entered.
 	// If not, check if a player hit the floor.
 	// If either condition is true, execute.
-	if (floortouch == true)
+	if (floortouch)
 	{
-		playersArea = playersOnArea;
+		playersArea = eachtime->playersOnArea;
 		oldPlayersArea = oldPlayersOnArea;
 	}
 	else
 	{
-		playersArea = playersInArea;
+		playersArea = eachtime->playersInArea;
 		oldPlayersArea = oldPlayersInArea;
 	}
 
-	while ((affectPlayer = P_HavePlayersEnteredArea(playersArea, oldPlayersArea, inAndOut)) != -1)
+	// Easy check... nothing has changed
+	if (!memcmp(playersArea, oldPlayersArea, sizeof(boolean)*MAXPLAYERS))
+		return;
+
+	// If sector has an "all players" trigger type, all players need to be in area
+	if (GETSECSPECIAL(sec->special, 2) == 2 || GETSECSPECIAL(sec->special, 2) == 3)
 	{
-		if (GETSECSPECIAL(sec->special, 2) == 2 || GETSECSPECIAL(sec->special, 2) == 3)
+		for (i = 0; i < MAXPLAYERS; i++)
 		{
-			for (i = 0; i < MAXPLAYERS; i++)
-			{
-				if (!playeringame[i])
-					continue;
-
-				if (!players[i].mo)
-					continue;
-
-				if (players[i].mo->health <= 0)
-					continue;
+			if (P_IsPlayerValid(i) && playersArea[i])
+				continue;
+		}
+	}
 
-				if ((netgame || multiplayer) && players[i].spectator)
-					continue;
+	// Trigger for every player who has entered (and exited, if triggerOnExit)
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (playersArea[i] == oldPlayersArea[i])
+			continue;
 
-				if (!playersArea[i])
-					return;
-			}
-		}
+		// If player has just left, check if still valid
+		if (!playersArea[i] && (!eachtime->triggerOnExit || !P_IsPlayerValid(i)))
+			continue;
 
 		CONS_Debug(DBG_GAMELOGIC, "Trying to activate each time executor with tag %d\n", tag);
 
@@ -1840,12 +1535,10 @@ void T_EachTimeThinker(levelspecthink_t *eachtime)
 		// No more stupid hacks involving changing eachtime->sourceline's tag or special or whatever!
 		// This should now run ONLY the stuff for eachtime->sourceline itself, instead of all trigger linedefs sharing the same tag.
 		// Makes much more sense doing it this way, honestly.
-		P_RunTriggerLinedef(eachtime->sourceline, players[affectPlayer].mo, sec);
+		P_RunTriggerLinedef(eachtime->sourceline, players[i].mo, sec);
 
 		if (!eachtime->sourceline->special) // this happens only for "Trigger on X calls" linedefs
 			P_RemoveThinker(&eachtime->thinker);
-
-		oldPlayersArea[affectPlayer]=playersArea[affectPlayer];
 	}
 }
 
@@ -1855,19 +1548,23 @@ void T_EachTimeThinker(levelspecthink_t *eachtime)
 // Rises up to its topmost position when a
 // player steps on it. Lowers otherwise.
 //
-void T_RaiseSector(levelspecthink_t *raise)
+void T_RaiseSector(raise_t *raise)
 {
 	msecnode_t *node;
 	mobj_t *thing;
 	sector_t *sector;
 	INT32 i;
 	boolean playeronme = false, active = false;
+	boolean moveUp;
 	fixed_t ceilingdestination, floordestination;
+	fixed_t speed, origspeed;
+	fixed_t distToNearestEndpoint;
+	INT32 direction;
 	result_e res = 0;
 	mtag_t tag = Tag_FGet(&raise->sourceline->tags);
 	TAG_ITER_C
 
-	if (raise->sector->crumblestate >= 3 || raise->sector->ceilingdata)
+	if (raise->sector->crumblestate >= CRUMBLE_FALL || raise->sector->ceilingdata)
 		return;
 
 	TAG_ITER_SECTORS(tag, i)
@@ -1887,7 +1584,7 @@ void T_RaiseSector(levelspecthink_t *raise)
 				continue;
 
 			// Option to require spindashing.
-			if (raise->vars[1] && !(thing->player->pflags & PF_STARTDASH))
+			if (raise->flags & RF_SPINDASH && !(thing->player->pflags & PF_STARTDASH))
 				continue;
 
 			if (!(thing->z == P_GetSpecialTopZ(thing, raise->sector, sector)))
@@ -1898,43 +1595,43 @@ void T_RaiseSector(levelspecthink_t *raise)
 		}
 	}
 
-	if (raise->vars[9]) // Dynamically Sinking Platform^tm
+	if (raise->flags & RF_DYNAMIC) // Dynamically Sinking Platform^tm
 	{
 #define shaketime 10
-		if (raise->vars[11] > shaketime) // State: moving
+		if (raise->shaketimer > shaketime) // State: moving
 		{
 			if (playeronme) // If player is standing on the platform, accelerate
 			{
-				raise->vars[10] += (FRACUNIT >> 5);
+				raise->extraspeed += (FRACUNIT >> 5);
 			}
 			else // otherwise, decelerate until inflection
 			{
-				raise->vars[10] -= FRACUNIT >> 3;
-				if (raise->vars[10] <= 0) // inflection!
+				raise->extraspeed -= FRACUNIT >> 3;
+				if (raise->extraspeed <= 0) // inflection!
 				{
-					raise->vars[10] = 0;
-					raise->vars[11] = 0; // allow the shake to occur again (fucks over players attempting to jump-cheese)
+					raise->extraspeed = 0;
+					raise->shaketimer = 0; // allow the shake to occur again (fucks over players attempting to jump-cheese)
 				}
 			}
-			active = raise->vars[10] > 0;
+			active = raise->extraspeed > 0;
 		}
 		else // State: shaking
 		{
-			if (playeronme || raise->vars[11])
+			if (playeronme || raise->shaketimer)
 			{
 				active = true;
-				if (++raise->vars[11] > shaketime)
+				if (++raise->shaketimer > shaketime)
 				{
 					if (playeronme)
-						raise->vars[10] = FRACUNIT >> 5;
+						raise->extraspeed = FRACUNIT >> 5;
 					else
-						raise->vars[10] = FRACUNIT << 1;
+						raise->extraspeed = FRACUNIT << 1;
 				}
 				else
 				{
-					raise->vars[10] = ((shaketime/2) - raise->vars[11]) << FRACBITS;
-					if (raise->vars[10] < -raise->vars[2]/2)
-						raise->vars[10] = -raise->vars[2]/2;
+					raise->extraspeed = ((shaketime/2) - raise->shaketimer) << FRACBITS;
+					if (raise->extraspeed < -raise->basespeed/2)
+						raise->extraspeed = -raise->basespeed/2;
 				}
 			}
 		}
@@ -1943,125 +1640,59 @@ void T_RaiseSector(levelspecthink_t *raise)
 	else // Air bobbing platform (not a Dynamically Sinking Platform^tm)
 		active = playeronme;
 
-	if (active)
-	{
-		raise->vars[3] = raise->vars[2];
+	moveUp = active ^ (raise->flags & RF_REVERSE);
+	ceilingdestination = moveUp ? raise->ceilingtop : raise->ceilingbottom;
+	floordestination = ceilingdestination - (raise->sector->ceilingheight - raise->sector->floorheight);
 
-		if (raise->vars[0] == 1)
-		{
-			if (raise->sector->ceilingheight <= raise->vars[7])
-			{
-				raise->sector->floorheight = raise->vars[7] - (raise->sector->ceilingheight - raise->sector->floorheight);
-				raise->sector->ceilingheight = raise->vars[7];
-				raise->sector->ceilspeed = 0;
-				raise->sector->floorspeed = 0;
-				return;
-			}
-
-			raise->vars[8] = -1;
-			ceilingdestination = raise->vars[7];
-			floordestination = raise->vars[6];
-		}
-		else // elevateUp
-		{
-			if (raise->sector->ceilingheight >= raise->vars[5])
-			{
-				raise->sector->floorheight = raise->vars[5] - (raise->sector->ceilingheight - raise->sector->floorheight);
-				raise->sector->ceilingheight = raise->vars[5];
-				raise->sector->ceilspeed = 0;
-				raise->sector->floorspeed = 0;
-				return;
-			}
-
-			raise->vars[8] = 1;
-			ceilingdestination = raise->vars[5];
-			floordestination = raise->vars[4];
-		}
-	}
-	else
+	if ((moveUp && raise->sector->ceilingheight >= ceilingdestination)
+		|| (!moveUp && raise->sector->ceilingheight <= ceilingdestination))
 	{
-		raise->vars[3] = raise->vars[2]/2;
-
-		if (raise->vars[0] == 1)
-		{
-			if (raise->sector->ceilingheight >= raise->vars[5])
-			{
-				raise->sector->floorheight = raise->vars[5] - (raise->sector->ceilingheight - raise->sector->floorheight);
-				raise->sector->ceilingheight = raise->vars[5];
-				raise->sector->ceilspeed = 0;
-				raise->sector->floorspeed = 0;
-				return;
-			}
-			raise->vars[8] = 1;
-			ceilingdestination = raise->vars[5];
-			floordestination = raise->vars[4];
-		}
-		else // elevateUp
-		{
-			if (raise->sector->ceilingheight <= raise->vars[7])
-			{
-				raise->sector->floorheight = raise->vars[7] - (raise->sector->ceilingheight - raise->sector->floorheight);
-				raise->sector->ceilingheight = raise->vars[7];
-				raise->sector->ceilspeed = 0;
-				raise->sector->floorspeed = 0;
-				return;
-			}
-			raise->vars[8] = -1;
-			ceilingdestination = raise->vars[7];
-			floordestination = raise->vars[6];
-		}
+		raise->sector->floorheight = floordestination;
+		raise->sector->ceilingheight = ceilingdestination;
+		raise->sector->ceilspeed = 0;
+		raise->sector->floorspeed = 0;
+		return;
 	}
+	direction = moveUp ? 1 : -1;
 
-	if ((raise->sector->ceilingheight - raise->vars[7])
-		< (raise->vars[5] - raise->sector->ceilingheight))
-	{
-		fixed_t origspeed = raise->vars[3];
+	origspeed = raise->basespeed;
+	if (!active)
+		origspeed /= 2;
 
-		// Slow down as you get closer to the bottom
-		raise->vars[3] = FixedMul(raise->vars[3],FixedDiv(raise->sector->ceilingheight - raise->vars[7], (raise->vars[5] - raise->vars[7])>>5));
+	// Speed up as you get closer to the middle, then slow down again
+	distToNearestEndpoint = min(raise->sector->ceilingheight - raise->ceilingbottom, raise->ceilingtop - raise->sector->ceilingheight);
+	speed = FixedMul(origspeed, FixedDiv(distToNearestEndpoint, (raise->ceilingtop - raise->ceilingbottom) >> 5));
 
-		if (raise->vars[3] <= origspeed/16)
-			raise->vars[3] = origspeed/16;
-		else if (raise->vars[3] > origspeed)
-			raise->vars[3] = origspeed;
-	}
-	else
-	{
-		fixed_t origspeed = raise->vars[3];
-		// Slow down as you get closer to the top
-		raise->vars[3] = FixedMul(raise->vars[3],FixedDiv(raise->vars[5] - raise->sector->ceilingheight, (raise->vars[5] - raise->vars[7])>>5));
-
-		if (raise->vars[3] <= origspeed/16)
-			raise->vars[3] = origspeed/16;
-		else if (raise->vars[3] > origspeed)
-			raise->vars[3] = origspeed;
-	}
+	if (speed <= origspeed/16)
+		speed = origspeed/16;
+	else if (speed > origspeed)
+		speed = origspeed;
 
-	raise->vars[3] += raise->vars[10];
+	speed += raise->extraspeed;
 
 	res = T_MovePlane
 	(
-		raise->sector,         // sector
-		raise->vars[3],          // speed
+		raise->sector,      // sector
+		speed,              // speed
 		ceilingdestination, // dest
-		0,                        // crush
-		1,                        // floor or ceiling (1 for ceiling)
-		raise->vars[8]       // direction
+		false,              // crush
+		true,               // ceiling?
+		direction           // direction
 	);
 
 	if (res == ok || res == pastdest)
 		T_MovePlane
 		(
-			raise->sector,           // sector
-			raise->vars[3],            // speed
+			raise->sector,    // sector
+			speed,            // speed
 			floordestination, // dest
-			0,                          // crush
-			0,                          // floor or ceiling (0 for floor)
-			raise->vars[8]         // direction
+			false,            // crush
+			false,            // ceiling?
+			direction         // direction
 		);
 
 	raise->sector->ceilspeed = 42;
-	raise->sector->floorspeed = raise->vars[3]*raise->vars[8];
+	raise->sector->floorspeed = speed*direction;
 
 	TAG_ITER_SECTORS(tag, i)
 		P_RecalcPrecipInSector(&sectors[i]);
@@ -2158,9 +1789,9 @@ void T_PlaneDisplace(planedisplace_t *pd)
 	}
 
 	if (pd->type == pd_floor || pd->type == pd_both)
-		T_MovePlane(target, INT32_MAX/2, target->floorheight+diff, 0, 0, direction); // move floor
+		T_MovePlane(target, INT32_MAX/2, target->floorheight+diff, false, false, direction); // move floor
 	if (pd->type == pd_ceiling || pd->type == pd_both)
-		T_MovePlane(target, INT32_MAX/2, target->ceilingheight+diff, 0, 1, direction); // move ceiling
+		T_MovePlane(target, INT32_MAX/2, target->ceilingheight+diff, false, true, direction); // move ceiling
 
 	pd->last_height = control->floorheight;
 }
@@ -2174,9 +1805,9 @@ void T_PlaneDisplace(planedisplace_t *pd)
 // (egg capsule button), P_PlayerInSpecialSector (buttons),
 // and P_SpawnSpecials (continuous floor movers and instant lower).
 //
-INT32 EV_DoFloor(line_t *line, floor_e floortype)
+void EV_DoFloor(line_t *line, floor_e floortype)
 {
-	INT32 rtn = 0, firstone = 1;
+	INT32 firstone = 1;
 	INT32 secnum = -1;
 	sector_t *sec;
 	floormove_t *dofloor;
@@ -2191,7 +1822,6 @@ INT32 EV_DoFloor(line_t *line, floor_e floortype)
 			continue; // then don't add another one
 
 		// new floor thinker
-		rtn = 1;
 		dofloor = Z_Calloc(sizeof (*dofloor), PU_LEVSPEC, NULL);
 		P_AddThinker(THINK_MAIN, &dofloor->thinker);
 
@@ -2381,8 +2011,6 @@ INT32 EV_DoFloor(line_t *line, floor_e floortype)
 
 		firstone = 0;
 	}
-
-	return rtn;
 }
 
 // SoM: Boom elevator support.
@@ -2395,10 +2023,9 @@ INT32 EV_DoFloor(line_t *line, floor_e floortype)
 //
 // jff 2/22/98 new type to move floor and ceiling in parallel
 //
-INT32 EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed)
+void EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed)
 {
 	INT32 secnum = -1;
-	INT32 rtn = 0;
 	sector_t *sec;
 	elevator_t *elevator;
 	mtag_t tag = Tag_FGet(&line->tags);
@@ -2414,7 +2041,6 @@ INT32 EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed)
 			continue;
 
 		// create and initialize new elevator thinker
-		rtn = 1;
 		elevator = Z_Calloc(sizeof (*elevator), PU_LEVSPEC, NULL);
 		P_AddThinker(THINK_MAIN, &elevator->thinker);
 		sec->floordata = elevator;
@@ -2516,7 +2142,6 @@ INT32 EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed)
 				break;
 		}
 	}
-	return rtn;
 }
 
 void EV_CrumbleChain(sector_t *sec, ffloor_t *rover)
@@ -2650,16 +2275,13 @@ void EV_CrumbleChain(sector_t *sec, ffloor_t *rover)
 }
 
 // Used for bobbing platforms on the water
-INT32 EV_BounceSector(sector_t *sec, fixed_t momz, line_t *sourceline)
+void EV_BounceSector(sector_t *sec, fixed_t momz, line_t *sourceline)
 {
-#define speed vars[0]
-#define distance vars[1]
-#define low vars[2]
-	levelspecthink_t *bouncer;
+	bouncecheese_t *bouncer;
 
 	// create and initialize new thinker
 	if (sec->ceilingdata) // One at a time, ma'am.
-		return 0;
+		return;
 
 	bouncer = Z_Calloc(sizeof (*bouncer), PU_LEVSPEC, NULL);
 	P_AddThinker(THINK_MAIN, &bouncer->thinker);
@@ -2667,31 +2289,20 @@ INT32 EV_BounceSector(sector_t *sec, fixed_t momz, line_t *sourceline)
 	bouncer->thinker.function.acp1 = (actionf_p1)T_BounceCheese;
 
 	// set up the fields according to the type of elevator action
+	bouncer->sourceline = sourceline;
 	bouncer->sector = sec;
 	bouncer->speed = momz/2;
-	bouncer->sourceline = sourceline;
 	bouncer->distance = FRACUNIT;
-	bouncer->low = 1;
-
-	return 1;
-#undef speed
-#undef distance
-#undef low
+	bouncer->low = true;
 }
 
 // For T_ContinuousFalling special
-INT32 EV_DoContinuousFall(sector_t *sec, sector_t *backsector, fixed_t spd, boolean backwards)
+void EV_DoContinuousFall(sector_t *sec, sector_t *backsector, fixed_t spd, boolean backwards)
 {
-#define speed vars[0]
-#define direction vars[1]
-#define floorwasheight vars[2]
-#define ceilingwasheight vars[3]
-#define floordestheight vars[4]
-#define ceilingdestheight vars[5]
-	levelspecthink_t *faller;
+	continuousfall_t *faller;
 
 	// workaround for when there is no back sector
-	if (backsector == NULL)
+	if (!backsector)
 		backsector = sec;
 
 	// create and initialize new thinker
@@ -2703,29 +2314,11 @@ INT32 EV_DoContinuousFall(sector_t *sec, sector_t *backsector, fixed_t spd, bool
 	faller->sector = sec;
 	faller->speed = spd;
 
-	faller->floorwasheight = sec->floorheight;
-	faller->ceilingwasheight = sec->ceilingheight;
-
-	if (backwards)
-	{
-		faller->ceilingdestheight = backsector->ceilingheight;
-		faller->floordestheight = faller->ceilingdestheight;
-		faller->direction = 1; // Up!
-	}
-	else
-	{
-		faller->floordestheight = backsector->floorheight;
-		faller->ceilingdestheight = faller->floordestheight;
-		faller->direction = -1;
-	}
+	faller->floorstartheight = sec->floorheight;
+	faller->ceilingstartheight = sec->ceilingheight;
 
-	return 1;
-#undef speed
-#undef direction
-#undef floorwasheight
-#undef ceilingwasheight
-#undef floordestheight
-#undef ceilingdestheight
+	faller->destheight = backwards ? backsector->ceilingheight : backsector->floorheight;
+	faller->direction = backwards ? 1 : -1;
 }
 
 // Some other 3dfloor special things Tails 03-11-2002 (Search p_mobj.c for description)
@@ -2742,7 +2335,7 @@ INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating,
 	if (sec->floordata)
 		return 0;
 
-	if (sec->crumblestate > 1)
+	if (sec->crumblestate >= CRUMBLE_ACTIVATED)
 		return 0;
 
 	// create and initialize new elevator thinker
@@ -2787,7 +2380,7 @@ INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating,
 	else
 		elevator->high = 0;
 
-	elevator->sector->crumblestate = 2;
+	elevator->sector->crumblestate = CRUMBLE_ACTIVATED;
 
 	TAG_ITER_SECTORS(tag, i)
 	{
@@ -2799,11 +2392,11 @@ INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating,
 	return 1;
 }
 
-INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher)
+void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher)
 {
 	sector_t *roversec = rover->master->frontsector;
 	fixed_t topheight = *rover->topheight;
-	levelspecthink_t *block;
+	mariothink_t *block;
 	mobj_t *thing;
 	fixed_t oldx = 0, oldy = 0, oldz = 0;
 
@@ -2811,7 +2404,7 @@ INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher)
 	I_Assert(puncher->player != NULL);
 
 	if (roversec->floordata || roversec->ceilingdata)
-		return 0;
+		return;
 
 	if (!(rover->flags & FF_SOLID))
 		rover->flags |= (FF_SOLID|FF_RENDERALL|FF_CUTLEVEL);
@@ -2819,8 +2412,9 @@ INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher)
 	// Find an item to pop out!
 	thing = SearchMarioNode(roversec->touching_thinglist);
 
-	// Found something!
-	if (thing)
+	if (!thing)
+		S_StartSound(puncher, sfx_mario1); // "Thunk!" sound - puncher is "close enough".
+	else // Found something!
 	{
 		const boolean itsamonitor = (thing->flags & MF_MONITOR) == MF_MONITOR;
 		// create and initialize new elevator thinker
@@ -2833,13 +2427,11 @@ INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher)
 
 		// Set up the fields
 		block->sector = roversec;
-		block->vars[0] = Tag_FGet(&sector->tags); // actionsector
-		block->vars[1] = 4*FRACUNIT; // speed
-		block->vars[2] = 1; // Up // direction
-		block->vars[3] = block->sector->floorheight; // floorwasheight
-		block->vars[4] = block->sector->ceilingheight; // ceilingwasheight
-		block->vars[5] = FRACUNIT; // distance
-		block->vars[6] = 1; // low
+		block->speed = 4*FRACUNIT;
+		block->direction = 1;
+		block->floorstartheight = block->sector->floorheight;
+		block->ceilingstartheight = block->sector->ceilingheight;
+		block->tag = (INT16)Tag_FGet(&sector->tags);
 
 		if (itsamonitor)
 		{
@@ -2880,8 +2472,4 @@ INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher)
 			P_SetThingPosition(thing);
 		}
 	}
-	else
-		S_StartSound(puncher, sfx_mario1); // "Thunk!" sound - puncher is "close enough".
-
-	return 1;
 }
diff --git a/src/p_mobj.c b/src/p_mobj.c
index cbf59f2bf588cd3cc0198f5c013585aec602ee09..0baee131115a1853fcffbd88966b347a9fcb52ec 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -1691,7 +1691,7 @@ static void P_PushableCheckBustables(mobj_t *mo)
 				// Needs ML_EFFECT4 flag for pushables to break it
 				if (!(rover->master->flags & ML_EFFECT4)) continue;
 
-				if (!rover->master->frontsector->crumblestate)
+				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);
@@ -11635,7 +11635,7 @@ static fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x,
 			+ FixedMul(scale, offset);
 }
 
-static fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mthing, const fixed_t x, const fixed_t y)
+fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mthing, const fixed_t x, const fixed_t y)
 {
 	fixed_t offset = mthing->z << FRACBITS;
 	boolean flip = (!!(mobjinfo[mobjtype].flags & MF_SPAWNCEILING) ^ !!(mthing->options & MTF_OBJECTFLIP));
@@ -11675,6 +11675,7 @@ static fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthin
 
 	// Ring-like items, may float additional units with MTF_AMBUSH.
 	case MT_SPIKEBALL:
+	case MT_EMERHUNT:
 	case MT_EMERALDSPAWN:
 	case MT_TOKEN:
 	case MT_EMBLEM:
diff --git a/src/p_mobj.h b/src/p_mobj.h
index c7a36b05c70f576cbdd7a994a40cffdcd04d1e66..b45b8b1ab4e4337be0ca27d7104d8db789f3fdf3 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -451,6 +451,8 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing);
 void P_MovePlayerToStarpost(INT32 playernum);
 void P_AfterPlayerSpawn(INT32 playernum);
 
+fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mthing, const fixed_t x, const fixed_t y);
+
 mobj_t *P_SpawnMapThing(mapthing_t *mthing);
 void P_SpawnHoop(mapthing_t *mthing);
 void P_SetBonusTime(mobj_t *mobj);
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 47f555722998d395a979bfb07a4bf63461470f74..78a978ad769536daa6a7e0c1f67d3bba947ce6fc 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -755,6 +755,7 @@ static void P_NetUnArchiveColormaps(void)
 // diff3 flags
 #define SD_TAGLIST   0x01
 #define SD_COLORMAP  0x02
+#define SD_CRUMBLESTATE 0x04
 
 #define LD_FLAG     0x01
 #define LD_SPECIAL  0x02
@@ -859,6 +860,8 @@ static void P_NetArchiveWorld(void)
 
 		if (ss->extra_colormap != spawnss->extra_colormap)
 			diff3 |= SD_COLORMAP;
+		if (ss->crumblestate)
+			diff3 |= SD_CRUMBLESTATE;
 
 		// Check if any of the sector's FOFs differ from how they spawned
 		if (ss->ffloors)
@@ -925,6 +928,8 @@ static void P_NetArchiveWorld(void)
 			if (diff3 & SD_COLORMAP)
 				WRITEUINT32(put, CheckAddNetColormapToList(ss->extra_colormap));
 					// returns existing index if already added, or appends to net_colormaps and returns new index
+			if (diff3 & SD_CRUMBLESTATE)
+				WRITEINT32(put, ss->crumblestate);
 
 			// Special case: save the stats of all modified ffloors along with their ffloor "number"s
 			// we don't bother with ffloors that haven't changed, that would just add to savegame even more than is really needed
@@ -1176,6 +1181,8 @@ static void P_NetUnArchiveWorld(void)
 
 		if (diff3 & SD_COLORMAP)
 			sectors[i].extra_colormap = GetNetColormapFromList(READUINT32(get));
+		if (diff3 & SD_CRUMBLESTATE)
+			sectors[i].crumblestate = READINT32(get);
 
 		if (diff & SD_FFLOORS)
 		{
@@ -1371,7 +1378,6 @@ typedef enum
 	tc_startcrumble,
 	tc_marioblock,
 	tc_marioblockchecker,
-	tc_spikesector,
 	tc_floatsector,
 	tc_crushceiling,
 	tc_scroll,
@@ -1743,22 +1749,151 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 }
 
 //
-// SaveSpecialLevelThinker
+// SaveNoEnemiesThinker
 //
-// Saves a levelspecthink_t thinker
+// Saves a noenemies_t thinker
 //
-static void SaveSpecialLevelThinker(const thinker_t *th, const UINT8 type)
+static void SaveNoEnemiesThinker(const thinker_t *th, const UINT8 type)
 {
-	const levelspecthink_t *ht  = (const void *)th;
+	const noenemies_t *ht  = (const void *)th;
+	WRITEUINT8(save_p, type);
+	WRITEUINT32(save_p, SaveLine(ht->sourceline));
+}
+
+//
+// SaveBounceCheeseThinker
+//
+// Saves a bouncecheese_t thinker
+//
+static void SaveBounceCheeseThinker(const thinker_t *th, const UINT8 type)
+{
+	const bouncecheese_t *ht  = (const void *)th;
+	WRITEUINT8(save_p, type);
+	WRITEUINT32(save_p, SaveLine(ht->sourceline));
+	WRITEUINT32(save_p, SaveSector(ht->sector));
+	WRITEFIXED(save_p, ht->speed);
+	WRITEFIXED(save_p, ht->distance);
+	WRITEFIXED(save_p, ht->floorwasheight);
+	WRITEFIXED(save_p, ht->ceilingwasheight);
+	WRITECHAR(save_p, ht->low);
+}
+
+//
+// SaveContinuousFallThinker
+//
+// Saves a continuousfall_t thinker
+//
+static void SaveContinuousFallThinker(const thinker_t *th, const UINT8 type)
+{
+	const continuousfall_t *ht  = (const void *)th;
+	WRITEUINT8(save_p, type);
+	WRITEUINT32(save_p, SaveSector(ht->sector));
+	WRITEFIXED(save_p, ht->speed);
+	WRITEINT32(save_p, ht->direction);
+	WRITEFIXED(save_p, ht->floorstartheight);
+	WRITEFIXED(save_p, ht->ceilingstartheight);
+	WRITEFIXED(save_p, ht->destheight);
+}
+
+//
+// SaveMarioBlockThinker
+//
+// Saves a mariothink_t thinker
+//
+static void SaveMarioBlockThinker(const thinker_t *th, const UINT8 type)
+{
+	const mariothink_t *ht  = (const void *)th;
+	WRITEUINT8(save_p, type);
+	WRITEUINT32(save_p, SaveSector(ht->sector));
+	WRITEFIXED(save_p, ht->speed);
+	WRITEINT32(save_p, ht->direction);
+	WRITEFIXED(save_p, ht->floorstartheight);
+	WRITEFIXED(save_p, ht->ceilingstartheight);
+	WRITEINT16(save_p, ht->tag);
+}
+
+//
+// SaveMarioCheckThinker
+//
+// Saves a mariocheck_t thinker
+//
+static void SaveMarioCheckThinker(const thinker_t *th, const UINT8 type)
+{
+	const mariocheck_t *ht  = (const void *)th;
+	WRITEUINT8(save_p, type);
+	WRITEUINT32(save_p, SaveLine(ht->sourceline));
+	WRITEUINT32(save_p, SaveSector(ht->sector));
+}
+
+//
+// SaveThwompThinker
+//
+// Saves a thwomp_t thinker
+//
+static void SaveThwompThinker(const thinker_t *th, const UINT8 type)
+{
+	const thwomp_t *ht  = (const void *)th;
+	WRITEUINT8(save_p, type);
+	WRITEUINT32(save_p, SaveLine(ht->sourceline));
+	WRITEUINT32(save_p, SaveSector(ht->sector));
+	WRITEFIXED(save_p, ht->crushspeed);
+	WRITEFIXED(save_p, ht->retractspeed);
+	WRITEINT32(save_p, ht->direction);
+	WRITEFIXED(save_p, ht->floorstartheight);
+	WRITEFIXED(save_p, ht->ceilingstartheight);
+	WRITEINT32(save_p, ht->delay);
+	WRITEINT16(save_p, ht->tag);
+	WRITEUINT16(save_p, ht->sound);
+}
+
+//
+// SaveFloatThinker
+//
+// Saves a floatthink_t thinker
+//
+static void SaveFloatThinker(const thinker_t *th, const UINT8 type)
+{
+	const floatthink_t *ht  = (const void *)th;
+	WRITEUINT8(save_p, type);
+	WRITEUINT32(save_p, SaveLine(ht->sourceline));
+	WRITEUINT32(save_p, SaveSector(ht->sector));
+	WRITEINT16(save_p, ht->tag);
+}
+
+// SaveEachTimeThinker
+//
+// Loads a eachtime_t from a save game
+//
+static void SaveEachTimeThinker(const thinker_t *th, const UINT8 type)
+{
+	const eachtime_t *ht  = (const void *)th;
 	size_t i;
 	WRITEUINT8(save_p, type);
-	for (i = 0; i < 16; i++)
+	WRITEUINT32(save_p, SaveLine(ht->sourceline));
+	for (i = 0; i < MAXPLAYERS; i++)
 	{
-		WRITEFIXED(save_p, ht->vars[i]); //var[16]
-		WRITEFIXED(save_p, ht->var2s[i]); //var[16]
+		WRITECHAR(save_p, ht->playersInArea[i]);
+		WRITECHAR(save_p, ht->playersOnArea[i]);
 	}
+	WRITECHAR(save_p, ht->triggerOnExit);
+}
+
+// SaveRaiseThinker
+//
+// Saves a raise_t thinker
+//
+static void SaveRaiseThinker(const thinker_t *th, const UINT8 type)
+{
+	const raise_t *ht  = (const void *)th;
+	WRITEUINT8(save_p, type);
 	WRITEUINT32(save_p, SaveLine(ht->sourceline));
 	WRITEUINT32(save_p, SaveSector(ht->sector));
+	WRITEFIXED(save_p, ht->ceilingbottom);
+	WRITEFIXED(save_p, ht->ceilingtop);
+	WRITEFIXED(save_p, ht->basespeed);
+	WRITEFIXED(save_p, ht->extraspeed);
+	WRITEUINT8(save_p, ht->shaketimer);
+	WRITEUINT8(save_p, ht->flags);
 }
 
 //
@@ -2319,27 +2454,27 @@ static void P_NetArchiveThinkers(void)
 			}
 			else if (th->function.acp1 == (actionf_p1)T_ContinuousFalling)
 			{
-				SaveSpecialLevelThinker(th, tc_continuousfalling);
+				SaveContinuousFallThinker(th, tc_continuousfalling);
 				continue;
 			}
 			else if (th->function.acp1 == (actionf_p1)T_ThwompSector)
 			{
-				SaveSpecialLevelThinker(th, tc_thwomp);
+				SaveThwompThinker(th, tc_thwomp);
 				continue;
 			}
 			else if (th->function.acp1 == (actionf_p1)T_NoEnemiesSector)
 			{
-				SaveSpecialLevelThinker(th, tc_noenemies);
+				SaveNoEnemiesThinker(th, tc_noenemies);
 				continue;
 			}
 			else if (th->function.acp1 == (actionf_p1)T_EachTimeThinker)
 			{
-				SaveSpecialLevelThinker(th, tc_eachtime);
+				SaveEachTimeThinker(th, tc_eachtime);
 				continue;
 			}
 			else if (th->function.acp1 == (actionf_p1)T_RaiseSector)
 			{
-				SaveSpecialLevelThinker(th, tc_raisesector);
+				SaveRaiseThinker(th, tc_raisesector);
 				continue;
 			}
 			else if (th->function.acp1 == (actionf_p1)T_CameraScanner)
@@ -2364,7 +2499,7 @@ static void P_NetArchiveThinkers(void)
 			}
 			else if (th->function.acp1 == (actionf_p1)T_BounceCheese)
 			{
-				SaveSpecialLevelThinker(th, tc_bouncecheese);
+				SaveBounceCheeseThinker(th, tc_bouncecheese);
 				continue;
 			}
 			else if (th->function.acp1 == (actionf_p1)T_StartCrumble)
@@ -2374,22 +2509,17 @@ static void P_NetArchiveThinkers(void)
 			}
 			else if (th->function.acp1 == (actionf_p1)T_MarioBlock)
 			{
-				SaveSpecialLevelThinker(th, tc_marioblock);
+				SaveMarioBlockThinker(th, tc_marioblock);
 				continue;
 			}
 			else if (th->function.acp1 == (actionf_p1)T_MarioBlockChecker)
 			{
-				SaveSpecialLevelThinker(th, tc_marioblockchecker);
-				continue;
-			}
-			else if (th->function.acp1 == (actionf_p1)T_SpikeSector)
-			{
-				SaveSpecialLevelThinker(th, tc_spikesector);
+				SaveMarioCheckThinker(th, tc_marioblockchecker);
 				continue;
 			}
 			else if (th->function.acp1 == (actionf_p1)T_FloatSector)
 			{
-				SaveSpecialLevelThinker(th, tc_floatsector);
+				SaveFloatThinker(th, tc_floatsector);
 				continue;
 			}
 			else if (th->function.acp1 == (actionf_p1)T_LaserFlash)
@@ -2841,38 +2971,153 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 	return &mobj->thinker;
 }
 
+// LoadNoEnemiesThinker
+//
+// Loads a noenemies_t from a save game
+//
+static thinker_t* LoadNoEnemiesThinker(actionf_p1 thinker)
+{
+	noenemies_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+	ht->thinker.function.acp1 = thinker;
+	ht->sourceline = LoadLine(READUINT32(save_p));
+	return &ht->thinker;
+}
+
+// LoadBounceCheeseThinker
 //
-// LoadSpecialLevelThinker
+// Loads a bouncecheese_t from a save game
 //
-// Loads a levelspecthink_t from a save game
+static thinker_t* LoadBounceCheeseThinker(actionf_p1 thinker)
+{
+	bouncecheese_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+	ht->thinker.function.acp1 = thinker;
+	ht->sourceline = LoadLine(READUINT32(save_p));
+	ht->sector = LoadSector(READUINT32(save_p));
+	ht->speed = READFIXED(save_p);
+	ht->distance = READFIXED(save_p);
+	ht->floorwasheight = READFIXED(save_p);
+	ht->ceilingwasheight = READFIXED(save_p);
+	ht->low = READCHAR(save_p);
+	return &ht->thinker;
+}
+
+// LoadContinuousFallThinker
 //
-// floorOrCeiling:
-//		0 - Don't set
-//		1 - Floor Only
-//		2 - Ceiling Only
-//		3 - Both
+// Loads a continuousfall_t from a save game
 //
-static thinker_t* LoadSpecialLevelThinker(actionf_p1 thinker, UINT8 floorOrCeiling)
+static thinker_t* LoadContinuousFallThinker(actionf_p1 thinker)
 {
-	levelspecthink_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
-	size_t i;
+	continuousfall_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+	ht->thinker.function.acp1 = thinker;
+	ht->sector = LoadSector(READUINT32(save_p));
+	ht->speed = READFIXED(save_p);
+	ht->direction = READINT32(save_p);
+	ht->floorstartheight = READFIXED(save_p);
+	ht->ceilingstartheight = READFIXED(save_p);
+	ht->destheight = READFIXED(save_p);
+	return &ht->thinker;
+}
+
+// LoadMarioBlockThinker
+//
+// Loads a mariothink_t from a save game
+//
+static thinker_t* LoadMarioBlockThinker(actionf_p1 thinker)
+{
+	mariothink_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+	ht->thinker.function.acp1 = thinker;
+	ht->sector = LoadSector(READUINT32(save_p));
+	ht->speed = READFIXED(save_p);
+	ht->direction = READINT32(save_p);
+	ht->floorstartheight = READFIXED(save_p);
+	ht->ceilingstartheight = READFIXED(save_p);
+	ht->tag = READINT16(save_p);
+	return &ht->thinker;
+}
+
+// LoadMarioCheckThinker
+//
+// Loads a mariocheck_t from a save game
+//
+static thinker_t* LoadMarioCheckThinker(actionf_p1 thinker)
+{
+	mariocheck_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
 	ht->thinker.function.acp1 = thinker;
-	for (i = 0; i < 16; i++)
-	{
-		ht->vars[i] = READFIXED(save_p); //var[16]
-		ht->var2s[i] = READFIXED(save_p); //var[16]
-	}
 	ht->sourceline = LoadLine(READUINT32(save_p));
 	ht->sector = LoadSector(READUINT32(save_p));
+	return &ht->thinker;
+}
 
-	if (ht->sector)
+// LoadThwompThinker
+//
+// Loads a thwomp_t from a save game
+//
+static thinker_t* LoadThwompThinker(actionf_p1 thinker)
+{
+	thwomp_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+	ht->thinker.function.acp1 = thinker;
+	ht->sourceline = LoadLine(READUINT32(save_p));
+	ht->sector = LoadSector(READUINT32(save_p));
+	ht->crushspeed = READFIXED(save_p);
+	ht->retractspeed = READFIXED(save_p);
+	ht->direction = READINT32(save_p);
+	ht->floorstartheight = READFIXED(save_p);
+	ht->ceilingstartheight = READFIXED(save_p);
+	ht->delay = READINT32(save_p);
+	ht->tag = READINT16(save_p);
+	ht->sound = READUINT16(save_p);
+	return &ht->thinker;
+}
+
+// LoadFloatThinker
+//
+// Loads a floatthink_t from a save game
+//
+static thinker_t* LoadFloatThinker(actionf_p1 thinker)
+{
+	floatthink_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+	ht->thinker.function.acp1 = thinker;
+	ht->sourceline = LoadLine(READUINT32(save_p));
+	ht->sector = LoadSector(READUINT32(save_p));
+	ht->tag = READINT16(save_p);
+	return &ht->thinker;
+}
+
+// LoadEachTimeThinker
+//
+// Loads a eachtime_t from a save game
+//
+static thinker_t* LoadEachTimeThinker(actionf_p1 thinker)
+{
+	size_t i;
+	eachtime_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+	ht->thinker.function.acp1 = thinker;
+	ht->sourceline = LoadLine(READUINT32(save_p));
+	for (i = 0; i < MAXPLAYERS; i++)
 	{
-		if (floorOrCeiling & 2)
-			ht->sector->ceilingdata = ht;
-		if (floorOrCeiling & 1)
-			ht->sector->floordata = ht;
+		ht->playersInArea[i] = READCHAR(save_p);
+		ht->playersOnArea[i] = READCHAR(save_p);
 	}
+	ht->triggerOnExit = READCHAR(save_p);
+	return &ht->thinker;
+}
 
+// LoadRaiseThinker
+//
+// Loads a raise_t from a save game
+//
+static thinker_t* LoadRaiseThinker(actionf_p1 thinker)
+{
+	raise_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+	ht->thinker.function.acp1 = thinker;
+	ht->sourceline = LoadLine(READUINT32(save_p));
+	ht->sector = LoadSector(READUINT32(save_p));
+	ht->ceilingbottom = READFIXED(save_p);
+	ht->ceilingtop = READFIXED(save_p);
+	ht->basespeed = READFIXED(save_p);
+	ht->extraspeed = READFIXED(save_p);
+	ht->shaketimer = READUINT8(save_p);
+	ht->flags = READUINT8(save_p);
 	return &ht->thinker;
 }
 
@@ -3538,23 +3783,23 @@ static void P_NetUnArchiveThinkers(void)
 					break;
 
 				case tc_continuousfalling:
-					th = LoadSpecialLevelThinker((actionf_p1)T_ContinuousFalling, 3);
+					th = LoadContinuousFallThinker((actionf_p1)T_ContinuousFalling);
 					break;
 
 				case tc_thwomp:
-					th = LoadSpecialLevelThinker((actionf_p1)T_ThwompSector, 3);
+					th = LoadThwompThinker((actionf_p1)T_ThwompSector);
 					break;
 
 				case tc_noenemies:
-					th = LoadSpecialLevelThinker((actionf_p1)T_NoEnemiesSector, 0);
+					th = LoadNoEnemiesThinker((actionf_p1)T_NoEnemiesSector);
 					break;
 
 				case tc_eachtime:
-					th = LoadSpecialLevelThinker((actionf_p1)T_EachTimeThinker, 0);
+					th = LoadEachTimeThinker((actionf_p1)T_EachTimeThinker);
 					break;
 
 				case tc_raisesector:
-					th = LoadSpecialLevelThinker((actionf_p1)T_RaiseSector, 0);
+					th = LoadRaiseThinker((actionf_p1)T_RaiseSector);
 					break;
 
 				/// \todo rewrite all the code that uses an elevator_t but isn't an elevator
@@ -3564,7 +3809,7 @@ static void P_NetUnArchiveThinkers(void)
 					break;
 
 				case tc_bouncecheese:
-					th = LoadSpecialLevelThinker((actionf_p1)T_BounceCheese, 2);
+					th = LoadBounceCheeseThinker((actionf_p1)T_BounceCheese);
 					break;
 
 				case tc_startcrumble:
@@ -3572,19 +3817,15 @@ static void P_NetUnArchiveThinkers(void)
 					break;
 
 				case tc_marioblock:
-					th = LoadSpecialLevelThinker((actionf_p1)T_MarioBlock, 3);
+					th = LoadMarioBlockThinker((actionf_p1)T_MarioBlock);
 					break;
 
 				case tc_marioblockchecker:
-					th = LoadSpecialLevelThinker((actionf_p1)T_MarioBlockChecker, 0);
-					break;
-
-				case tc_spikesector:
-					th = LoadSpecialLevelThinker((actionf_p1)T_SpikeSector, 0);
+					th = LoadMarioCheckThinker((actionf_p1)T_MarioBlockChecker);
 					break;
 
 				case tc_floatsector:
-					th = LoadSpecialLevelThinker((actionf_p1)T_FloatSector, 0);
+					th = LoadFloatThinker((actionf_p1)T_FloatSector);
 					break;
 
 				case tc_laserflash:
diff --git a/src/p_setup.c b/src/p_setup.c
index 351ce3861bf278c0e700bfb7ae19cf10ef38ed1b..f8b6fe6ff02f876d3265ddaae42fb463b5a03992 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -696,47 +696,27 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum)
 
 static void P_SpawnEmeraldHunt(void)
 {
-	INT32 emer1, emer2, emer3;
-	INT32 timeout = 0; // keeps from getting stuck
+	INT32 emer[3], num[MAXHUNTEMERALDS], i, randomkey;
+	fixed_t x, y, z;
 
-	emer1 = emer2 = emer3 = 0;
+	for (i = 0; i < numhuntemeralds; i++)
+		num[i] = i;
 
-	//increment spawn numbers because zero is valid.
-	emer1 = (P_RandomKey(numhuntemeralds)) + 1;
-	while (timeout++ < 100)
+	for (i = 0; i < 3; i++)
 	{
-		emer2 = (P_RandomKey(numhuntemeralds)) + 1;
+		// generate random index, shuffle afterwards
+		randomkey = P_RandomKey(numhuntemeralds--);
+		emer[i] = num[randomkey];
+		num[randomkey] = num[numhuntemeralds];
+		num[numhuntemeralds] = emer[i];
 
-		if (emer2 != emer1)
-			break;
+		// spawn emerald
+		x = huntemeralds[emer[i]]->x<<FRACBITS;
+		y = huntemeralds[emer[i]]->y<<FRACBITS;
+		z = P_GetMapThingSpawnHeight(MT_EMERHUNT, huntemeralds[emer[i]], x, y);
+		P_SetMobjStateNF(P_SpawnMobj(x, y, z, MT_EMERHUNT),
+			mobjinfo[MT_EMERHUNT].spawnstate+i);
 	}
-
-	timeout = 0;
-	while (timeout++ < 100)
-	{
-		emer3 = (P_RandomKey(numhuntemeralds)) + 1;
-
-		if (emer3 != emer2 && emer3 != emer1)
-			break;
-	}
-
-	//decrement spawn values to the actual number because zero is valid.
-	if (emer1--)
-		P_SpawnMobj(huntemeralds[emer1]->x<<FRACBITS,
-			huntemeralds[emer1]->y<<FRACBITS,
-			huntemeralds[emer1]->z<<FRACBITS, MT_EMERHUNT);
-
-	if (emer2--)
-		P_SetMobjStateNF(P_SpawnMobj(huntemeralds[emer2]->x<<FRACBITS,
-			huntemeralds[emer2]->y<<FRACBITS,
-			huntemeralds[emer2]->z<<FRACBITS, MT_EMERHUNT),
-		mobjinfo[MT_EMERHUNT].spawnstate+1);
-
-	if (emer3--)
-		P_SetMobjStateNF(P_SpawnMobj(huntemeralds[emer3]->x<<FRACBITS,
-			huntemeralds[emer3]->y<<FRACBITS,
-			huntemeralds[emer3]->z<<FRACBITS, MT_EMERHUNT),
-		mobjinfo[MT_EMERHUNT].spawnstate+2);
 }
 
 static void P_SpawnMapThings(boolean spawnemblems)
@@ -867,7 +847,7 @@ static void P_InitializeSector(sector_t *ss)
 	ss->camsec = -1;
 
 	ss->floorlightsec = ss->ceilinglightsec = -1;
-	ss->crumblestate = 0;
+	ss->crumblestate = CRUMBLE_NONE;
 
 	ss->touching_thinglist = NULL;
 
@@ -4169,12 +4149,12 @@ static lumpinfo_t* FindFolder(const char *folName, UINT16 *start, UINT16 *end, l
 {
 	UINT16 numlumps = *pnumlumps;
 	size_t i = *pi;
-	if (!stricmp(lumpinfo->name2, folName))
+	if (!stricmp(lumpinfo->fullname, folName))
 	{
 		lumpinfo++;
 		*start = ++i;
 		for (; i < numlumps; i++, lumpinfo++)
-			if (strnicmp(lumpinfo->name2, folName, strlen(folName)))
+			if (strnicmp(lumpinfo->fullname, folName, strlen(folName)))
 				break;
 		lumpinfo--;
 		*end = i-- - *start;
diff --git a/src/p_spec.c b/src/p_spec.c
index acd96408ef80e3cc8b39b368cd67693a03ba1ba5..8b57ceffe8fc0e8040be0f21a7904338bb7ce070 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -114,12 +114,11 @@ static void P_ResetColormapFader(sector_t *sector);
 static void Add_ColormapFader(sector_t *sector, extracolormap_t *source_exc, extracolormap_t *dest_exc,
 	boolean ticbased, INT32 duration);
 static void P_AddBlockThinker(sector_t *sec, line_t *sourceline);
-static void P_AddFloatThinker(sector_t *sec, INT32 tag, line_t *sourceline);
+static void P_AddFloatThinker(sector_t *sec, UINT16 tag, line_t *sourceline);
 //static void P_AddBridgeThinker(line_t *sourceline, sector_t *sec);
 static void P_AddFakeFloorsByLine(size_t line, ffloortype_e ffloorflags, thinkerlist_t *secthinkers);
 static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec);
 static void Add_Friction(INT32 friction, INT32 movefactor, INT32 affectee, INT32 referrer);
-static void P_AddSpikeThinker(sector_t *sec, INT32 referrer);
 static void P_AddPlaneDisplaceThinker(INT32 type, fixed_t speed, INT32 control, INT32 affectee, UINT8 reverse);
 
 
@@ -4246,7 +4245,8 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
 				P_DamageMobj(player->mo, NULL, NULL, 1, DMG_ELECTRIC);
 			break;
 		case 5: // Spikes
-			// Don't do anything. In Soviet Russia, spikes find you.
+			if (roversector || P_MobjReadyToTrigger(player->mo, sector))
+				P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPIKE);
 			break;
 		case 6: // Death Pit (Camera Mod)
 		case 7: // Death Pit (No Camera Mod)
@@ -5554,7 +5554,6 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f
 	thinker_t *th;
 	friction_t *f;
 	pusher_t *p;
-	levelspecthink_t *lst;
 	size_t sec2num;
 	size_t i;
 
@@ -5655,16 +5654,8 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f
 		else if (th == &thlist[THINK_MAIN])
 			break;
 
-		// Should this FOF have spikeness?
-		if (th->function.acp1 == (actionf_p1)T_SpikeSector)
-		{
-			lst = (levelspecthink_t *)th;
-
-			if (lst->sector == sec2)
-				P_AddSpikeThinker(sec, (INT32)sec2num);
-		}
 		// Should this FOF have friction?
-		else if(th->function.acp1 == (actionf_p1)T_Friction)
+		if(th->function.acp1 == (actionf_p1)T_Friction)
 		{
 			f = (friction_t *)th;
 
@@ -5711,7 +5702,7 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f
 	}
 
 	if ((flags & FF_CRUMBLE))
-		sec2->crumblestate = 1;
+		sec2->crumblestate = CRUMBLE_WAIT;
 
 	if ((flags & FF_FLOATBOB))
 	{
@@ -5728,28 +5719,6 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f
 // SPECIAL SPAWNING
 //
 
-/** Adds a spike thinker.
-  * Sector type Section1:5 will result in this effect.
-  *
-  * \param sec Sector in which to add the thinker.
-  * \param referrer If != sec, then we're dealing with a FOF
-  * \sa P_SpawnSpecials, T_SpikeSector
-  * \author SSNTails <http://www.ssntails.org>
-  */
-static void P_AddSpikeThinker(sector_t *sec, INT32 referrer)
-{
-	levelspecthink_t *spikes;
-
-	// create and initialize new thinker
-	spikes = Z_Calloc(sizeof (*spikes), PU_LEVSPEC, NULL);
-	P_AddThinker(THINK_MAIN, &spikes->thinker);
-
-	spikes->thinker.function.acp1 = (actionf_p1)T_SpikeSector;
-
-	spikes->sector = sec;
-	spikes->vars[0] = referrer;
-}
-
 /** Adds a float thinker.
   * Float thinkers cause solid 3Dfloors to float on water.
   *
@@ -5758,9 +5727,9 @@ static void P_AddSpikeThinker(sector_t *sec, INT32 referrer)
   * \sa P_SpawnSpecials, T_FloatSector
   * \author SSNTails <http://www.ssntails.org>
   */
-static void P_AddFloatThinker(sector_t *sec, INT32 tag, line_t *sourceline)
+static void P_AddFloatThinker(sector_t *sec, UINT16 tag, line_t *sourceline)
 {
-	levelspecthink_t *floater;
+	floatthink_t *floater;
 
 	// create and initialize new thinker
 	floater = Z_Calloc(sizeof (*floater), PU_LEVSPEC, NULL);
@@ -5769,7 +5738,7 @@ static void P_AddFloatThinker(sector_t *sec, INT32 tag, line_t *sourceline)
 	floater->thinker.function.acp1 = (actionf_p1)T_FloatSector;
 
 	floater->sector = sec;
-	floater->vars[0] = tag;
+	floater->tag = (INT16)tag;
 	floater->sourceline = sourceline;
 }
 
@@ -5814,7 +5783,7 @@ static void P_AddPlaneDisplaceThinker(INT32 type, fixed_t speed, INT32 control,
   */
 static void P_AddBlockThinker(sector_t *sec, line_t *sourceline)
 {
-	levelspecthink_t *block;
+	mariocheck_t *block;
 
 	// create and initialize new elevator thinker
 	block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL);
@@ -5840,85 +5809,52 @@ static void P_AddBlockThinker(sector_t *sec, line_t *sourceline)
   * \sa P_SpawnSpecials, T_RaiseSector
   * \author SSNTails <http://www.ssntails.org>
   */
-static void P_AddRaiseThinker(sector_t *sec, line_t *sourceline)
+static void P_AddRaiseThinker(sector_t *sec, line_t *sourceline, boolean lower, boolean spindash)
 {
-	levelspecthink_t *raise;
+	raise_t *raise;
 
 	raise = Z_Calloc(sizeof (*raise), PU_LEVSPEC, NULL);
 	P_AddThinker(THINK_MAIN, &raise->thinker);
 
 	raise->thinker.function.acp1 = (actionf_p1)T_RaiseSector;
 
-	if (sourceline->flags & ML_BLOCKMONSTERS)
-		raise->vars[0] = 1;
-	else
-		raise->vars[0] = 0;
-
-	// set up the fields
+	raise->sourceline = sourceline;
 	raise->sector = sec;
 
-	// Require a spindash to activate
-	if (sourceline->flags & ML_NOCLIMB)
-		raise->vars[1] = 1;
-	else
-		raise->vars[1] = 0;
-
-	raise->vars[2] = P_AproxDistance(sourceline->dx, sourceline->dy);
-	raise->vars[2] = FixedDiv(raise->vars[2], 4*FRACUNIT);
-	raise->vars[3] = raise->vars[2];
+	raise->ceilingtop = P_FindHighestCeilingSurrounding(sec);
+	raise->ceilingbottom = P_FindLowestCeilingSurrounding(sec);
 
-	raise->vars[5] = P_FindHighestCeilingSurrounding(sec);
-	raise->vars[4] = raise->vars[5]
-		- (sec->ceilingheight - sec->floorheight);
+	raise->basespeed =  FixedDiv(P_AproxDistance(sourceline->dx, sourceline->dy), 4*FRACUNIT);
 
-	raise->vars[7] = P_FindLowestCeilingSurrounding(sec);
-	raise->vars[6] = raise->vars[7]
-		- (sec->ceilingheight - sec->floorheight);
-
-	raise->sourceline = sourceline;
+	if (lower)
+		raise->flags |= RF_REVERSE;
+	if (spindash)
+		raise->flags |= RF_SPINDASH;
 }
 
-static void P_AddAirbob(sector_t *sec, line_t *sourceline, boolean noadjust, boolean dynamic)
+static void P_AddAirbob(sector_t *sec, line_t *sourceline, fixed_t dist, boolean raise, boolean spindash, boolean dynamic)
 {
-	levelspecthink_t *airbob;
+	raise_t *airbob;
 
 	airbob = Z_Calloc(sizeof (*airbob), PU_LEVSPEC, NULL);
 	P_AddThinker(THINK_MAIN, &airbob->thinker);
 
 	airbob->thinker.function.acp1 = (actionf_p1)T_RaiseSector;
 
-	// set up the fields
+	airbob->sourceline = sourceline;
 	airbob->sector = sec;
 
-	// Require a spindash to activate
-	if (sourceline->flags & ML_NOCLIMB)
-		airbob->vars[1] = 1;
-	else
-		airbob->vars[1] = 0;
-
-	airbob->vars[2] = FRACUNIT;
-
-	if (noadjust)
-		airbob->vars[7] = airbob->sector->ceilingheight-16*FRACUNIT;
-	else
-		airbob->vars[7] = airbob->sector->ceilingheight - P_AproxDistance(sourceline->dx, sourceline->dy);
-	airbob->vars[6] = airbob->vars[7]
-		- (sec->ceilingheight - sec->floorheight);
-
-	airbob->vars[3] = airbob->vars[2];
-
-	if (sourceline->flags & ML_BLOCKMONSTERS)
-		airbob->vars[0] = 1;
-	else
-		airbob->vars[0] = 0;
+	airbob->ceilingtop = sec->ceilingheight;
+	airbob->ceilingbottom = sec->ceilingheight - dist;
 
-	airbob->vars[5] = sec->ceilingheight;
-	airbob->vars[4] = airbob->vars[5]
-			- (sec->ceilingheight - sec->floorheight);
+	airbob->basespeed = FRACUNIT;
 
-	airbob->vars[9] = dynamic ? 1 : 0;
-
-	airbob->sourceline = sourceline;
+	if (!raise)
+		airbob->flags |= RF_REVERSE;
+	if (spindash)
+		airbob->flags |= RF_SPINDASH;
+	if (dynamic)
+		airbob->flags |= RF_DYNAMIC;
 }
 
 /** Adds a thwomp thinker.
@@ -5932,12 +5868,7 @@ static void P_AddAirbob(sector_t *sec, line_t *sourceline, boolean noadjust, boo
   */
 static inline void P_AddThwompThinker(sector_t *sec, sector_t *actionsector, line_t *sourceline)
 {
-#define speed vars[1]
-#define direction vars[2]
-#define distance vars[3]
-#define floorwasheight vars[4]
-#define ceilingwasheight vars[5]
-	levelspecthink_t *thwomp;
+	thwomp_t *thwomp;
 
 	// You *probably* already have a thwomp in this sector. If you've combined it with something
 	// else that uses the floordata/ceilingdata, you must be weird.
@@ -5951,34 +5882,33 @@ static inline void P_AddThwompThinker(sector_t *sec, sector_t *actionsector, lin
 	thwomp->thinker.function.acp1 = (actionf_p1)T_ThwompSector;
 
 	// set up the fields according to the type of elevator action
+	thwomp->sourceline = sourceline;
 	thwomp->sector = sec;
-	thwomp->vars[0] = Tag_FGet(&sourceline->tags);
-	thwomp->floorwasheight = thwomp->sector->floorheight;
-	thwomp->ceilingwasheight = thwomp->sector->ceilingheight;
+	thwomp->crushspeed = (sourceline->flags & ML_EFFECT5) ? sourceline->dy >> 3 : 10*FRACUNIT;
+	thwomp->retractspeed = (sourceline->flags & ML_EFFECT5) ? sourceline->dx >> 3 : 2*FRACUNIT;
 	thwomp->direction = 0;
-	thwomp->distance = 1;
-	thwomp->sourceline = sourceline;
-	thwomp->sector->floordata = thwomp;
-	thwomp->sector->ceilingdata = thwomp;
-	return;
-#undef speed
-#undef direction
-#undef distance
-#undef floorwasheight
-#undef ceilingwasheight
+	thwomp->floorstartheight = sec->floorheight;
+	thwomp->ceilingstartheight = sec->ceilingheight;
+	thwomp->delay = 1;
+	thwomp->tag = Tag_FGet(&sourceline->tags);
+	thwomp->sound = (sourceline->flags & ML_EFFECT4) ? sides[sourceline->sidenum[0]].textureoffset >> FRACBITS : sfx_thwomp;
+
+	sec->floordata = thwomp;
+	sec->ceilingdata = thwomp;
+	// Start with 'resting' texture
+	sides[sourceline->sidenum[0]].midtexture = sides[sourceline->sidenum[0]].bottomtexture;
 }
 
 /** Adds a thinker which checks if any MF_ENEMY objects with health are in the defined area.
   * If not, a linedef executor is run once.
   *
-  * \param sec          Control sector.
   * \param sourceline   Control linedef.
   * \sa P_SpawnSpecials, T_NoEnemiesSector
   * \author SSNTails <http://www.ssntails.org>
   */
-static inline void P_AddNoEnemiesThinker(sector_t *sec, line_t *sourceline)
+static inline void P_AddNoEnemiesThinker(line_t *sourceline)
 {
-	levelspecthink_t *nobaddies;
+	noenemies_t *nobaddies;
 
 	// create and initialize new thinker
 	nobaddies = Z_Calloc(sizeof (*nobaddies), PU_LEVSPEC, NULL);
@@ -5986,21 +5916,19 @@ static inline void P_AddNoEnemiesThinker(sector_t *sec, line_t *sourceline)
 
 	nobaddies->thinker.function.acp1 = (actionf_p1)T_NoEnemiesSector;
 
-	nobaddies->sector = sec;
 	nobaddies->sourceline = sourceline;
 }
 
 /** Adds a thinker for Each-Time linedef executors. A linedef executor is run
   * only when a player enters the area and doesn't run again until they re-enter.
   *
-  * \param sec          Control sector that contains the lines of executors we will want to run.
   * \param sourceline   Control linedef.
   * \sa P_SpawnSpecials, T_EachTimeThinker
   * \author SSNTails <http://www.ssntails.org>
   */
-static void P_AddEachTimeThinker(sector_t *sec, line_t *sourceline)
+static void P_AddEachTimeThinker(line_t *sourceline)
 {
-	levelspecthink_t *eachtime;
+	eachtime_t *eachtime;
 
 	// create and initialize new thinker
 	eachtime = Z_Calloc(sizeof (*eachtime), PU_LEVSPEC, NULL);
@@ -6008,8 +5936,8 @@ static void P_AddEachTimeThinker(sector_t *sec, line_t *sourceline)
 
 	eachtime->thinker.function.acp1 = (actionf_p1)T_EachTimeThinker;
 
-	eachtime->sector = sec;
 	eachtime->sourceline = sourceline;
+	eachtime->triggerOnExit = !!(sourceline->flags & ML_BOUNCY);
 }
 
 /** Adds a camera scanner.
@@ -6236,9 +6164,11 @@ void P_SpawnSpecials(boolean fromnetsave)
 		switch(GETSECSPECIAL(sector->special, 1))
 		{
 			case 5: // Spikes
-				P_AddSpikeThinker(sector, (INT32)(sector-sectors));
+				//Terrible hack to replace an even worse hack:
+				//Spike damage automatically sets SF_TRIGGERSPECIAL_TOUCH.
+				//Yes, this also affects other specials on the same sector. Sorry.
+				sector->flags |= SF_TRIGGERSPECIAL_TOUCH;
 				break;
-
 			case 15: // Bouncy sector
 				CheckForBouncySector = true;
 				break;
@@ -6284,9 +6214,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 	// Firstly, find out how many there are in each sector
 	for (th = thlist[THINK_MAIN].next; th != &thlist[THINK_MAIN]; th = th->next)
 	{
-		if (th->function.acp1 == (actionf_p1)T_SpikeSector)
-			secthinkers[((levelspecthink_t *)th)->sector - sectors].count++;
-		else if (th->function.acp1 == (actionf_p1)T_Friction)
+		if (th->function.acp1 == (actionf_p1)T_Friction)
 			secthinkers[((friction_t *)th)->affectee].count++;
 		else if (th->function.acp1 == (actionf_p1)T_Pusher)
 			secthinkers[((pusher_t *)th)->affectee].count++;
@@ -6306,9 +6234,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 	{
 		size_t secnum = (size_t)-1;
 
-		if (th->function.acp1 == (actionf_p1)T_SpikeSector)
-			secnum = ((levelspecthink_t *)th)->sector - sectors;
-		else if (th->function.acp1 == (actionf_p1)T_Friction)
+		if (th->function.acp1 == (actionf_p1)T_Friction)
 			secnum = ((friction_t *)th)->affectee;
 		else if (th->function.acp1 == (actionf_p1)T_Pusher)
 			secnum = ((pusher_t *)th)->affectee;
@@ -6726,18 +6652,19 @@ void P_SpawnSpecials(boolean fromnetsave)
 
 			case 150: // Air bobbing platform
 			case 151: // Adjustable air bobbing platform
+			{
+				fixed_t dist = (lines[i].special == 150) ? 16*FRACUNIT : P_AproxDistance(lines[i].dx, lines[i].dy);
 				P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL, secthinkers);
-				lines[i].flags |= ML_BLOCKMONSTERS;
-				P_AddAirbob(lines[i].frontsector, lines + i, (lines[i].special != 151), false);
+				P_AddAirbob(lines[i].frontsector, lines + i, dist, false, !!(lines[i].flags & ML_NOCLIMB), false);
 				break;
+			}
 			case 152: // Adjustable air bobbing platform in reverse
 				P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL, secthinkers);
-				P_AddAirbob(lines[i].frontsector, lines + i, true, false);
+				P_AddAirbob(lines[i].frontsector, lines + i, P_AproxDistance(lines[i].dx, lines[i].dy), true, !!(lines[i].flags & ML_NOCLIMB), false);
 				break;
 			case 153: // Dynamic Sinking Platform
 				P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL, secthinkers);
-				lines[i].flags |= ML_BLOCKMONSTERS;
-				P_AddAirbob(lines[i].frontsector, lines + i, false, true);
+				P_AddAirbob(lines[i].frontsector, lines + i, P_AproxDistance(lines[i].dx, lines[i].dy), false, !!(lines[i].flags & ML_NOCLIMB), true);
 				break;
 
 			case 160: // Float/bob platform
@@ -6787,15 +6714,13 @@ void P_SpawnSpecials(boolean fromnetsave)
 
 			case 176: // Air bobbing platform that will crumble and bob on the water when it falls and hits
 				P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_FLOATBOB|FF_CRUMBLE, secthinkers);
-				lines[i].flags |= ML_BLOCKMONSTERS;
-				P_AddAirbob(lines[i].frontsector, lines + i, true, false);
+				P_AddAirbob(lines[i].frontsector, lines + i, 16*FRACUNIT, false, !!(lines[i].flags & ML_NOCLIMB), false);
 				break;
 
 			case 177: // Air bobbing platform that will crumble and bob on
 				// the water when it falls and hits, then never return
 				P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL|FF_FLOATBOB|FF_CRUMBLE|FF_NORETURN, secthinkers);
-				lines[i].flags |= ML_BLOCKMONSTERS;
-				P_AddAirbob(lines[i].frontsector, lines + i, true, false);
+				P_AddAirbob(lines[i].frontsector, lines + i, 16*FRACUNIT, false, !!(lines[i].flags & ML_NOCLIMB), false);
 				break;
 
 			case 178: // Crumbling platform that will float when it hits water
@@ -6808,28 +6733,27 @@ void P_SpawnSpecials(boolean fromnetsave)
 
 			case 180: // Air bobbing platform that will crumble
 				P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL|FF_CRUMBLE, secthinkers);
-				lines[i].flags |= ML_BLOCKMONSTERS;
-				P_AddAirbob(lines[i].frontsector, lines + i, true, false);
+				P_AddAirbob(lines[i].frontsector, lines + i, 16*FRACUNIT, false, !!(lines[i].flags & ML_NOCLIMB), false);
 				break;
 
 			case 190: // Rising Platform FOF (solid, opaque, shadows)
 				P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL, secthinkers);
-				P_AddRaiseThinker(lines[i].frontsector, &lines[i]);
+				P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
 				break;
 
 			case 191: // Rising Platform FOF (solid, opaque, no shadows)
 				P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_NOSHADE|FF_CUTLEVEL, secthinkers);
-				P_AddRaiseThinker(lines[i].frontsector, &lines[i]);
+				P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
 				break;
 
 			case 192: // Rising Platform TL block: FOF (solid, translucent)
 				P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_NOSHADE|FF_TRANSLUCENT|FF_EXTRA|FF_CUTEXTRA, secthinkers);
-				P_AddRaiseThinker(lines[i].frontsector, &lines[i]);
+				P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
 				break;
 
 			case 193: // Rising Platform FOF (solid, invisible)
 				P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_NOSHADE, secthinkers);
-				P_AddRaiseThinker(lines[i].frontsector, &lines[i]);
+				P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
 				break;
 
 			case 194: // Rising Platform 'Platform' - You can jump up through it
@@ -6839,7 +6763,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 					ffloorflags |= FF_NOSHADE;
 
 				P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
-				P_AddRaiseThinker(lines[i].frontsector, &lines[i]);
+				P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
 				break;
 
 			case 195: // Rising Platform Translucent "platform"
@@ -6849,7 +6773,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 					ffloorflags |= FF_NOSHADE;
 
 				P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
-				P_AddRaiseThinker(lines[i].frontsector, &lines[i]);
+				P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
 				break;
 
 			case 200: // Double light effect
@@ -6998,14 +6922,12 @@ void P_SpawnSpecials(boolean fromnetsave)
 			case 312:
 			case 332:
 			case 335:
-				sec = sides[*lines[i].sidenum].sector - sectors;
-				P_AddEachTimeThinker(&sectors[sec], &lines[i]);
+				P_AddEachTimeThinker(&lines[i]);
 				break;
 
 			// No More Enemies Linedef Exec
 			case 313:
-				sec = sides[*lines[i].sidenum].sector - sectors;
-				P_AddNoEnemiesThinker(&sectors[sec], &lines[i]);
+				P_AddNoEnemiesThinker(&lines[i]);
 				break;
 
 			// Pushable linedef executors (count # of pushables)
@@ -7029,10 +6951,7 @@ void P_SpawnSpecials(boolean fromnetsave)
 				else
 					lines[i].callcount = sides[lines[i].sidenum[0]].textureoffset>>FRACBITS;
 				if (lines[i].special == 322) // Each time
-				{
-					sec = sides[*lines[i].sidenum].sector - sectors;
-					P_AddEachTimeThinker(&sectors[sec], &lines[i]);
-				}
+					P_AddEachTimeThinker(&lines[i]);
 				break;
 
 			// NiGHTS trigger executors
diff --git a/src/p_spec.h b/src/p_spec.h
index 7c290c6f3d0b5b9600d43587ba4eacef967a2c8f..681f528930fac41a345a17676ee000542daf812b 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -309,11 +309,101 @@ typedef struct
 typedef struct
 {
 	thinker_t thinker;
-	fixed_t vars[16];   // Misc. variables
-	fixed_t var2s[16];   // Second misc variables buffer.
 	line_t *sourceline; // Source line of the thinker
-	sector_t *sector;   // Sector the thinker is from
-} levelspecthink_t;
+} noenemies_t;
+
+typedef struct
+{
+	thinker_t thinker;
+	sector_t *sector;
+	fixed_t speed;
+	INT32 direction;
+	fixed_t floorstartheight;
+	fixed_t ceilingstartheight;
+	fixed_t destheight;
+} continuousfall_t;
+
+typedef struct
+{
+	thinker_t thinker;
+	line_t *sourceline;
+	sector_t *sector;
+	fixed_t speed;
+	fixed_t distance;
+	fixed_t floorwasheight;
+	fixed_t ceilingwasheight;
+	boolean low;
+} bouncecheese_t;
+
+typedef struct
+{
+	thinker_t thinker;
+	sector_t *sector;
+	fixed_t speed;
+	INT32 direction;
+	fixed_t floorstartheight;
+	fixed_t ceilingstartheight;
+	INT16 tag;
+} mariothink_t;
+
+typedef struct
+{
+	thinker_t thinker;
+	line_t *sourceline;
+	sector_t *sector;
+} mariocheck_t;
+
+typedef struct
+{
+	thinker_t thinker;
+	line_t *sourceline;
+	sector_t *sector;
+	fixed_t crushspeed;
+	fixed_t retractspeed;
+	INT32 direction;
+	fixed_t floorstartheight;
+	fixed_t ceilingstartheight;
+	INT32 delay;
+	INT16 tag;
+	UINT16 sound;
+} thwomp_t;
+
+typedef struct
+{
+	thinker_t thinker;
+	line_t *sourceline;
+	sector_t *sector;
+	INT16 tag;
+} floatthink_t;
+
+typedef struct
+{
+	thinker_t thinker;
+	line_t *sourceline; // Source line of the thinker
+	boolean playersInArea[MAXPLAYERS];
+	boolean playersOnArea[MAXPLAYERS];
+	boolean triggerOnExit;
+} eachtime_t;
+
+typedef enum
+{
+	RF_REVERSE  = 1,    //Lower when stood on
+	RF_SPINDASH = 1<<1, //Require spindash to move
+	RF_DYNAMIC  = 1<<2, //Dynamically sinking platform
+} raiseflag_t;
+
+typedef struct
+{
+	thinker_t thinker;
+	line_t *sourceline;
+	sector_t *sector;
+	fixed_t ceilingbottom;
+	fixed_t ceilingtop;
+	fixed_t basespeed;
+	fixed_t extraspeed; //For dynamically sinking platform
+	UINT8 shaketimer; //For dynamically sinking platform
+	UINT8 flags;
+} raise_t;
 
 #define ELEVATORSPEED (FRACUNIT*4)
 #define FLOORSPEED (FRACUNIT)
@@ -326,35 +416,34 @@ typedef enum
 } result_e;
 
 result_e T_MovePlane(sector_t *sector, fixed_t speed, fixed_t dest, boolean crush,
-	INT32 floorOrCeiling, INT32 direction);
-INT32 EV_DoFloor(line_t *line, floor_e floortype);
-INT32 EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed);
+	boolean ceiling, INT32 direction);
+void EV_DoFloor(line_t *line, floor_e floortype);
+void EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed);
 void EV_CrumbleChain(sector_t *sec, ffloor_t *rover);
-INT32 EV_BounceSector(sector_t *sector, fixed_t momz, line_t *sourceline);
+void EV_BounceSector(sector_t *sector, fixed_t momz, line_t *sourceline);
 
 // Some other special 3dfloor types
 INT32 EV_StartCrumble(sector_t *sector, ffloor_t *rover,
 	boolean floating, player_t *player, fixed_t origalpha, boolean crumblereturn);
 
-INT32 EV_DoContinuousFall(sector_t *sec, sector_t *pbacksector, fixed_t spd, boolean backwards);
+void EV_DoContinuousFall(sector_t *sec, sector_t *backsector, fixed_t spd, boolean backwards);
 
-INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher);
+void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher);
 
 void T_MoveFloor(floormove_t *movefloor);
 
 void T_MoveElevator(elevator_t *elevator);
-void T_ContinuousFalling(levelspecthink_t *faller);
-void T_BounceCheese(levelspecthink_t *bouncer);
+void T_ContinuousFalling(continuousfall_t *faller);
+void T_BounceCheese(bouncecheese_t *bouncer);
 void T_StartCrumble(elevator_t *elevator);
-void T_MarioBlock(levelspecthink_t *block);
-void T_SpikeSector(levelspecthink_t *spikes);
-void T_FloatSector(levelspecthink_t *floater);
-void T_MarioBlockChecker(levelspecthink_t *block);
-void T_ThwompSector(levelspecthink_t *thwomp);
-void T_NoEnemiesSector(levelspecthink_t *nobaddies);
-void T_EachTimeThinker(levelspecthink_t *eachtime);
+void T_MarioBlock(mariothink_t *block);
+void T_FloatSector(floatthink_t *floater);
+void T_MarioBlockChecker(mariocheck_t *block);
+void T_ThwompSector(thwomp_t *thwomp);
+void T_NoEnemiesSector(noenemies_t *nobaddies);
+void T_EachTimeThinker(eachtime_t *eachtime);
 void T_CameraScanner(elevator_t *elevator);
-void T_RaiseSector(levelspecthink_t *sraise);
+void T_RaiseSector(raise_t *raise);
 
 typedef struct
 {
diff --git a/src/p_user.c b/src/p_user.c
index 25a069739090aed18d8d8ef32ad763dc8c961726..2142cb64c36e0a63e2f8a64d505c35cdd0d6a76e 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2566,7 +2566,7 @@ static void P_CheckBustableBlocks(player_t *player)
 			{
 				if (!(rover->flags & FF_EXISTS)) continue;
 
-				if ((rover->flags & FF_BUSTUP)/* && !rover->master->frontsector->crumblestate*/)
+				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)
diff --git a/src/r_defs.h b/src/r_defs.h
index 81802de1b7457b9dc804011d7b285c6238c55471..8a2faf5b47566fd236453933aa76eeb3f5ac5c67 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -279,6 +279,16 @@ typedef enum
 	SF_INVERTPRECIP            =  1<<4,
 } sectorflags_t;
 
+
+typedef enum
+{
+	CRUMBLE_NONE, // No crumble thinker
+	CRUMBLE_WAIT, // Don't float on water because this is supposed to wait for a crumble
+	CRUMBLE_ACTIVATED, // Crumble thinker activated, but hasn't fallen yet
+	CRUMBLE_FALL, // Crumble thinker is falling
+	CRUMBLE_RESTORE, // Crumble thinker is about to restore to original position
+} crumblestate_t;
+
 //
 // The SECTORS record, at runtime.
 // Stores things/mobjs.
diff --git a/src/r_segs.c b/src/r_segs.c
index 4fc74ce9dc87321e4684038db161c5db3078f9dd..0e8bf05ff03b8e372e64b468d67ad9778a64f2dd 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -237,14 +237,13 @@ static void R_DrawWallSplats(void)
 //  way we don't have to store extra post_t info with each column for
 //  multi-patch textures. They are not normally needed as multi-patch
 //  textures don't have holes in it. At least not for now.
-static INT32 column2s_length; // column->length : for multi-patch on 2sided wall = texture->height
 
 static void R_Render2sidedMultiPatchColumn(column_t *column)
 {
 	INT32 topscreen, bottomscreen;
 
 	topscreen = sprtopscreen; // + spryscale*column->topdelta;  topdelta is 0 for the wall
-	bottomscreen = topscreen + spryscale * column2s_length;
+	bottomscreen = topscreen + spryscale * lengthcol;
 
 	dc_yl = (sprtopscreen+FRACUNIT-1)>>FRACBITS;
 	dc_yh = (bottomscreen-1)>>FRACBITS;
@@ -276,13 +275,6 @@ static void R_Render2sidedMultiPatchColumn(column_t *column)
 	}
 }
 
-// quick wrapper for R_DrawFlippedMaskedColumn so it can be set as a colfunc_2s value
-// uses column2s_length for texture->height as above
-static void R_DrawFlippedMaskedSegColumn(column_t *column)
-{
-	R_DrawFlippedMaskedColumn(column, column2s_length);
-}
-
 transnum_t R_GetLinedefTransTable(fixed_t alpha)
 {
 	return (20*(FRACUNIT - alpha - 1) + FRACUNIT) >> (FRACBITS+1);
@@ -355,8 +347,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 	{
 		if (textures[texnum]->flip & 2) // vertically flipped?
 		{
-			colfunc_2s = R_DrawFlippedMaskedSegColumn;
-			column2s_length = textures[texnum]->height;
+			colfunc_2s = R_DrawFlippedMaskedColumn;
+			lengthcol = textures[texnum]->height;
 		}
 		else
 			colfunc_2s = R_DrawMaskedColumn; // render the usual 2sided single-patch packed texture
@@ -364,7 +356,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 	else
 	{
 		colfunc_2s = R_Render2sidedMultiPatchColumn; // render multipatch with no holes (no post_t info)
-		column2s_length = textures[texnum]->height;
+		lengthcol = textures[texnum]->height;
 	}
 
 	// Setup lighting based on the presence/lack-of 3D floors.
@@ -694,7 +686,7 @@ static void R_DrawRepeatMaskedColumn(column_t *col)
 static void R_DrawRepeatFlippedMaskedColumn(column_t *col)
 {
 	do {
-		R_DrawFlippedMaskedColumn(col, column2s_length);
+		R_DrawFlippedMaskedColumn(col);
 		sprtopscreen += dc_texheight*spryscale;
 	} while (sprtopscreen < sprbotscreen);
 }
@@ -989,7 +981,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 		if (textures[texnum]->flip & 2) // vertically flipped?
 		{
 			colfunc_2s = R_DrawRepeatFlippedMaskedColumn;
-			column2s_length = textures[texnum]->height;
+			lengthcol = textures[texnum]->height;
 		}
 		else
 			colfunc_2s = R_DrawRepeatMaskedColumn; // render the usual 2sided single-patch packed texture
@@ -997,7 +989,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	else
 	{
 		colfunc_2s = R_Render2sidedMultiPatchColumn;        //render multipatch with no holes (no post_t info)
-		column2s_length = textures[texnum]->height;
+		lengthcol = textures[texnum]->height;
 	}
 
 	// Set heights according to plane, or slope, whichever
diff --git a/src/r_things.c b/src/r_things.c
index aec4ed9502b11ba885c1d3c01a80cde562e5f0dd..b4ffd4408883aa7dd24e88841eeadda969ed53ae 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -639,10 +639,10 @@ void R_DrawMaskedColumn(column_t *column)
 			dc_yl = mceilingclip[dc_x]+1;
 		if (dc_yl < 0)
 			dc_yl = 0;
-		if (dc_yh >= vid.height)
+		if (dc_yh >= vid.height) // dc_yl must be < vid.height, so reduces number of checks in tight loop
 			dc_yh = vid.height - 1;
 
-		if (dc_yl <= dc_yh && dc_yl < vid.height && dc_yh > 0)
+		if (dc_yl <= dc_yh && dc_yh > 0)
 		{
 			dc_source = (UINT8 *)column + 3;
 			dc_texturemid = basetexturemid - (topdelta<<FRACBITS);
@@ -653,15 +653,10 @@ void R_DrawMaskedColumn(column_t *column)
 			// quick fix... something more proper should be done!!!
 			if (ylookup[dc_yl])
 				colfunc();
-			else if (colfunc == colfuncs[COLDRAWFUNC_BASE])
-			{
-				static INT32 first = 1;
-				if (first)
-				{
-					CONS_Debug(DBG_RENDER, "WARNING: avoiding a crash in %s %d\n", __FILE__, __LINE__);
-					first = 0;
-				}
-			}
+#ifdef PARANOIA
+			else
+				I_Error("R_DrawMaskedColumn: Invalid ylookup for dc_yl %d", dc_yl);
+#endif
 		}
 		column = (column_t *)((UINT8 *)column + column->length + 4);
 	}
@@ -669,7 +664,9 @@ void R_DrawMaskedColumn(column_t *column)
 	dc_texturemid = basetexturemid;
 }
 
-void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight)
+INT32 lengthcol; // column->length : for flipped column function pointers and multi-patch on 2sided wall = texture->height
+
+void R_DrawFlippedMaskedColumn(column_t *column)
 {
 	INT32 topscreen;
 	INT32 bottomscreen;
@@ -685,7 +682,7 @@ void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight)
 		if (topdelta <= prevdelta)
 			topdelta += prevdelta;
 		prevdelta = topdelta;
-		topdelta = texheight-column->length-topdelta;
+		topdelta = lengthcol-column->length-topdelta;
 		topscreen = sprtopscreen + spryscale*topdelta;
 		bottomscreen = sprbotscreen == INT32_MAX ? topscreen + spryscale*column->length
 		                                      : sprbotscreen + spryscale*column->length;
@@ -707,10 +704,10 @@ void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight)
 			dc_yl = mceilingclip[dc_x]+1;
 		if (dc_yl < 0)
 			dc_yl = 0;
-		if (dc_yh >= vid.height)
+		if (dc_yh >= vid.height) // dc_yl must be < vid.height, so reduces number of checks in tight loop
 			dc_yh = vid.height - 1;
 
-		if (dc_yl <= dc_yh && dc_yl < vid.height && dc_yh > 0)
+		if (dc_yl <= dc_yh && dc_yh > 0)
 		{
 			dc_source = ZZ_Alloc(column->length);
 			for (s = (UINT8 *)column+2+column->length, d = dc_source; d < dc_source+column->length; --s)
@@ -720,15 +717,10 @@ void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight)
 			// Still drawn by R_DrawColumn.
 			if (ylookup[dc_yl])
 				colfunc();
-			else if (colfunc == colfuncs[COLDRAWFUNC_BASE])
-			{
-				static INT32 first = 1;
-				if (first)
-				{
-					CONS_Debug(DBG_RENDER, "WARNING: avoiding a crash in %s %d\n", __FILE__, __LINE__);
-					first = 0;
-				}
-			}
+#ifdef PARANOIA
+			else
+				I_Error("R_DrawMaskedColumn: Invalid ylookup for dc_yl %d", dc_yl);
+#endif
 			Z_Free(dc_source);
 		}
 		column = (column_t *)((UINT8 *)column + column->length + 4);
@@ -744,7 +736,9 @@ void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight)
 static void R_DrawVisSprite(vissprite_t *vis)
 {
 	column_t *column;
+	void (*localcolfunc)(column_t *);
 	INT32 texturecolumn;
+	INT32 pwidth;
 	fixed_t frac;
 	patch_t *patch = vis->patch;
 	fixed_t this_scale = vis->mobj->scale;
@@ -893,50 +887,52 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	if (vis->x2 >= vid.width)
 		vis->x2 = vid.width-1;
 
+	localcolfunc = (vis->cut & SC_VFLIP) ? R_DrawFlippedMaskedColumn : R_DrawMaskedColumn;
+	lengthcol = patch->height;
+
 	// Split drawing loops for paper and non-paper to reduce conditional checks per sprite
 	if (vis->scalestep)
 	{
-		// Papersprite drawing loop
+		pwidth = SHORT(patch->width);
 
+		// Papersprite drawing loop
 		for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, spryscale += vis->scalestep)
 		{
 			angle_t angle = ((vis->centerangle + xtoviewangle[dc_x]) >> ANGLETOFINESHIFT) & 0xFFF;
 			texturecolumn = (vis->paperoffset - FixedMul(FINETANGENT(angle), vis->paperdistance)) / this_scale;
 
-			if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width))
+			if (texturecolumn < 0 || texturecolumn >= pwidth)
 				continue;
 
 			if (vis->xiscale < 0) // Flipped sprite
-				texturecolumn = SHORT(patch->width) - 1 - texturecolumn;
+				texturecolumn = pwidth - 1 - texturecolumn;
 
 			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
 			dc_iscale = (0xffffffffu / (unsigned)spryscale);
 
 			column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn]));
 
-			if (vis->cut & SC_VFLIP)
-				R_DrawFlippedMaskedColumn(column, patch->height);
-			else
-				R_DrawMaskedColumn(column);
+			localcolfunc (column);
 		}
 	}
 	else
 	{
+#ifdef RANGECHECK
+		pwidth = SHORT(patch->width);
+#endif
+
 		// Non-paper drawing loop
 		for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan)
 		{
 #ifdef RANGECHECK
 			texturecolumn = frac>>FRACBITS;
-			if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width))
+			if (texturecolumn < 0 || texturecolumn >= pwidth)
 				I_Error("R_DrawSpriteRange: bad texturecolumn at %d from end", vis->x2 - dc_x);
 			column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn]));
 #else
 			column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[frac>>FRACBITS]));
 #endif
-			if (vis->cut & SC_VFLIP)
-				R_DrawFlippedMaskedColumn(column, patch->height);
-			else
-				R_DrawMaskedColumn(column);
+			localcolfunc (column);
 		}
 	}
 
diff --git a/src/r_things.h b/src/r_things.h
index 05d6fb27b7728e7f3a1103af3956468120a8ae9c..7a0fe3a60ee557bc358e95e75c49dc5f53d6ef3f 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -44,9 +44,10 @@ extern fixed_t sprtopscreen;
 extern fixed_t sprbotscreen;
 extern fixed_t windowtop;
 extern fixed_t windowbottom;
+extern INT32 lengthcol;
 
 void R_DrawMaskedColumn(column_t *column);
-void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight);
+void R_DrawFlippedMaskedColumn(column_t *column);
 
 // ----------------
 // SPRITE RENDERING
diff --git a/src/w_wad.c b/src/w_wad.c
index 1008aca8fce83767c0096a7e5296ec5b996e9b0d..f273753c8dec38310fad33a28a57eb5cc9290afb 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -92,7 +92,7 @@ typedef struct
 
 typedef struct lumpnum_cache_s
 {
-	char lumpname[8];
+	char lumpname[32];
 	lumpnum_t lumpnum;
 } lumpnum_cache_t;
 
@@ -114,13 +114,18 @@ void W_Shutdown(void)
 {
 	while (numwadfiles--)
 	{
-		fclose(wadfiles[numwadfiles]->handle);
-		Z_Free(wadfiles[numwadfiles]->filename);
-		while (wadfiles[numwadfiles]->numlumps--)
-			Z_Free(wadfiles[numwadfiles]->lumpinfo[wadfiles[numwadfiles]->numlumps].name2);
+		wadfile_t *wad = wadfiles[numwadfiles];
 
-		Z_Free(wadfiles[numwadfiles]->lumpinfo);
-		Z_Free(wadfiles[numwadfiles]);
+		fclose(wad->handle);
+		Z_Free(wad->filename);
+		while (wad->numlumps--)
+		{
+			Z_Free(wad->lumpinfo[wad->numlumps].longname);
+			Z_Free(wad->lumpinfo[wad->numlumps].fullname);
+		}
+
+		Z_Free(wad->lumpinfo);
+		Z_Free(wad);
 	}
 }
 
@@ -206,9 +211,9 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum, boolean mainfile)
 		for(; posStart < posEnd; posStart++)
 		{
 			lumpinfo_t *lump_p = &wadfiles[wadnum]->lumpinfo[posStart];
-			size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
+			size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name
 			char *name = malloc(length + 1);
-			sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->name2);
+			sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->fullname);
 			name[length] = '\0';
 			CONS_Printf(M_GetText("Loading SOC from %s\n"), name);
 			DEH_LoadDehackedLumpPwad(wadnum, posStart, mainfile);
@@ -235,9 +240,9 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile)
 		for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++)
 			if (memcmp(lump_p->name,"SOC_",4)==0) // Check for generic SOC lump
 			{	// shameless copy+paste of code from LUA_LoadLump
-				size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
+				size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name
 				char *name = malloc(length + 1);
-				sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->name2);
+				sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->fullname);
 				name[length] = '\0';
 
 				CONS_Printf(M_GetText("Loading SOC from %s\n"), name);
@@ -339,10 +344,17 @@ static lumpinfo_t* ResGetLumpsStandalone (FILE* handle, UINT16* numlumps, const
 	lumpinfo->size = ftell(handle);
 	fseek(handle, 0, SEEK_SET);
 	strcpy(lumpinfo->name, lumpname);
+
+	// Allocate the lump's long name.
+	lumpinfo->longname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
+	strcpy(lumpinfo->longname, lumpname);
+	lumpinfo->longname[8] = '\0';
+
 	// Allocate the lump's full name.
-	lumpinfo->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
-	strcpy(lumpinfo->name2, lumpname);
-	lumpinfo->name2[8] = '\0';
+	lumpinfo->fullname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
+	strcpy(lumpinfo->fullname, lumpname);
+	lumpinfo->fullname[8] = '\0';
+
 	*numlumps = 1;
 	return lumpinfo;
 }
@@ -429,10 +441,16 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen
 			lump_p->compression = CM_NOCOMPRESSION;
 		memset(lump_p->name, 0x00, 9);
 		strncpy(lump_p->name, fileinfo->name, 8);
+
+		// Allocate the lump's long name.
+		lump_p->longname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
+		strncpy(lump_p->longname, fileinfo->name, 8);
+		lump_p->longname[8] = '\0';
+
 		// Allocate the lump's full name.
-		lump_p->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
-		strncpy(lump_p->name2, fileinfo->name, 8);
-		lump_p->name2[8] = '\0';
+		lump_p->fullname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
+		strncpy(lump_p->fullname, fileinfo->name, 8);
+		lump_p->fullname[8] = '\0';
 	}
 	free(fileinfov);
 	*nlmp = numlumps;
@@ -598,8 +616,11 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp)
 		memset(lump_p->name, '\0', 9); // Making sure they're initialized to 0. Is it necessary?
 		strncpy(lump_p->name, trimname, min(8, dotpos - trimname));
 
-		lump_p->name2 = Z_Calloc(zentry.namelen + 1, PU_STATIC, NULL);
-		strncpy(lump_p->name2, fullname, zentry.namelen);
+		lump_p->longname = Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL);
+		strlcpy(lump_p->longname, trimname, dotpos - trimname + 1);
+
+		lump_p->fullname = Z_Calloc(zentry.namelen + 1, PU_STATIC, NULL);
+		strncpy(lump_p->fullname, fullname, zentry.namelen);
 
 		free(fullname);
 
@@ -637,7 +658,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp)
 		// skip and ignore comments/extra fields
 		if ((fseek(handle, lump_p->position, SEEK_SET) != 0) || (fread(&zlentry, 1, sizeof(zlentry_t), handle) < sizeof(zlentry_t)))
 		{
-			CONS_Alert(CONS_ERROR, "Local headers for lump %s are corrupt\n", lump_p->name2);
+			CONS_Alert(CONS_ERROR, "Local headers for lump %s are corrupt\n", lump_p->fullname);
 			Z_Free(lumpinfo);
 			return NULL;
 		}
@@ -901,16 +922,14 @@ const char *W_CheckNameForNum(lumpnum_t lumpnum)
 UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump)
 {
 	UINT16 i;
-	static char uname[9];
-
-	memset(uname, 0x00, sizeof uname);
-	strncpy(uname, name, 8);
-	uname[8] = 0;
-	strupr(uname);
+	static char uname[256 + 1];
 
 	if (!TestValidLump(wad,0))
 		return INT16_MAX;
 
+	strlcpy(uname, name, sizeof uname);
+	strupr(uname);
+
 	//
 	// scan forward
 	// start at 'startlump', useful parameter when there are multiple
@@ -920,7 +939,7 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump)
 	{
 		lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
 		for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
-			if (memcmp(lump_p->name,uname,8) == 0)
+			if (!strcmp(lump_p->longname, uname))
 				return i;
 	}
 
@@ -947,10 +966,10 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum
 	name_length = strlen(name);
 	for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
 	{
-		if (strnicmp(name, lump_p->name2, name_length) == 0)
+		if (strnicmp(name, lump_p->fullname, name_length) == 0)
 		{
 			/* SLADE is special and puts a single directory entry. Skip that. */
-			if (strlen(lump_p->name2) == name_length)
+			if (strlen(lump_p->fullname) == name_length)
 				i++;
 			break;
 		}
@@ -967,7 +986,7 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump)
 	lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
 	for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
 	{
-		if (strnicmp(name, lump_p->name2, strlen(name)))
+		if (strnicmp(name, lump_p->fullname, strlen(name)))
 			break;
 	}
 	return i;
@@ -981,7 +1000,7 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump)
 	lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
 	for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
 	{
-		if (!strnicmp(name, lump_p->name2, strlen(name)))
+		if (!strnicmp(name, lump_p->fullname, strlen(name)))
 		{
 			return i;
 		}
@@ -1006,7 +1025,7 @@ lumpnum_t W_CheckNumForName(const char *name)
 	// most recent entries first
 	for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--)
 	{
-		if (strncmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name, 8) == 0)
+		if (strcmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name) == 0)
 		{
 			lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1);
 			return lumpnumcache[lumpnumcacheindex].lumpnum;
@@ -1026,7 +1045,7 @@ lumpnum_t W_CheckNumForName(const char *name)
 	{
 		// Update the cache.
 		lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1);
-		strncpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 8);
+		strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 32);
 		lumpnumcache[lumpnumcacheindex].lumpnum = (i<<16)+check;
 
 		return lumpnumcache[lumpnumcacheindex].lumpnum;
@@ -1151,7 +1170,7 @@ boolean W_IsLumpWad(lumpnum_t lumpnum)
 {
 	if (wadfiles[WADFILENUM(lumpnum)]->type == RET_PK3)
 	{
-		const char *lumpfullName = (wadfiles[WADFILENUM(lumpnum)]->lumpinfo + LUMPNUM(lumpnum))->name2;
+		const char *lumpfullName = (wadfiles[WADFILENUM(lumpnum)]->lumpinfo + LUMPNUM(lumpnum))->fullname;
 
 		if (strlen(lumpfullName) < 4)
 			return false; // can't possibly be a WAD can it?
@@ -1169,7 +1188,7 @@ boolean W_IsLumpFolder(UINT16 wad, UINT16 lump)
 {
 	if (wadfiles[wad]->type == RET_PK3)
 	{
-		const char *name = wadfiles[wad]->lumpinfo[lump].name2;
+		const char *name = wadfiles[wad]->lumpinfo[lump].fullname;
 
 		return (name[strlen(name)-1] == '/'); // folders end in '/'
 	}
@@ -1247,7 +1266,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
 		{
 			size_t bytesread = fread(dest, 1, size, handle);
 			if (R_IsLumpPNG((UINT8 *)dest, bytesread))
-				W_ThrowPNGError(l->name2, wadfiles[wad]->filename);
+				W_ThrowPNGError(l->fullname, wadfiles[wad]->filename);
 			return bytesread;
 		}
 #else
@@ -1289,7 +1308,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
 			Z_Free(decData);
 #ifdef NO_PNG_LUMPS
 			if (R_IsLumpPNG((UINT8 *)dest, size))
-				W_ThrowPNGError(l->name2, wadfiles[wad]->filename);
+				W_ThrowPNGError(l->fullname, wadfiles[wad]->filename);
 #endif
 			return size;
 #else
@@ -1352,7 +1371,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
 
 #ifdef NO_PNG_LUMPS
 			if (R_IsLumpPNG((UINT8 *)dest, size))
-				W_ThrowPNGError(l->name2, wadfiles[wad]->filename);
+				W_ThrowPNGError(l->fullname, wadfiles[wad]->filename);
 #endif
 			return size;
 		}
diff --git a/src/w_wad.h b/src/w_wad.h
index d4455ba1446e151abe7d33402315dc5c205d93c6..3af6148f407dda936d52eeca52bb82094fc1fec2 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -66,9 +66,10 @@ typedef struct
 {
 	unsigned long position; // filelump_t filepos
 	unsigned long disksize; // filelump_t size
-	char name[9]; // filelump_t name[]
-	char *name2; // Used by PK3s. Dynamically allocated name.
-	size_t size; // real (uncompressed) size
+	char name[9];           // filelump_t name[] e.g. "LongEntr"
+	char *longname;         //                   e.g. "LongEntryName"
+	char *fullname;         //                   e.g. "Folder/Subfolder/LongEntryName.extension"
+	size_t size;            // real (uncompressed) size
 	compmethod compression; // lump compression method
 } lumpinfo_t;