diff --git a/extras/conf/udb/Includes/SRB222_misc.cfg b/extras/conf/udb/Includes/SRB222_misc.cfg
index 709514df872483a1d6ab1cfba77df58bc5f370e0..d6210e9f1ebad7cfc4b44ff5ef30f820f1f5dec2 100644
--- a/extras/conf/udb/Includes/SRB222_misc.cfg
+++ b/extras/conf/udb/Includes/SRB222_misc.cfg
@@ -82,6 +82,25 @@ sectorflags
 	gravityflip = "Flip Objects in Reverse Gravity";
 	heatwave = "Heat Wave";
 	noclipcamera = "Intangible to the Camera";
+	outerspace = "Space Countdown";
+	doublestepup = "Ramp Sector (double step-up/down)";
+	nostepdown = "Non-Ramp Sector (No step-down)";
+	windcurrent = "Wind/Current";
+	conveyor = "Conveyor Belt";
+	speedpad = "Speed Pad";
+	starpostactivator = "Star Post Activator";
+	exit = "Exit";
+	specialstagepit = "Special Stage Pit";
+	returnflag = "Return Flag";
+	redteambase = "Red Team Base";
+	blueteambase = "Blue Team Base";
+	fan = "Fan Sector";
+	supertransform = "Super Sonic Transform";
+	forcespin = "Force Spin";
+	zoomtubestart = "Zoom Tube Start";
+	zoomtubeend = "Zoom Tube End";
+	finishline = "Circuit Finish Line";
+	ropehang = "Rope Hang";
 }
 
 thingflags
diff --git a/src/deh_lua.c b/src/deh_lua.c
index 10f5eb46dccda5ae829e567fe0e395583b553b68..56d2ec1e92deda284ff322ad07cb11de5563ee23 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -353,6 +353,16 @@ static inline int lib_getenum(lua_State *L)
 		if (mathlib) return luaL_error(L, "sector flag '%s' could not be found.\n", word);
 		return 0;
 	}
+	else if (fastncmp("SSF_", word, 3)) {
+		p = word + 4;
+		for (i = 0; i < 19; i++)
+			if (SSF_LIST[i] && fastcmp(p, SSF_LIST[i])) {
+				lua_pushinteger(L, ((lua_Integer)1 << i));
+				return 1;
+			}
+		if (mathlib) return luaL_error(L, "sector special flag '%s' could not be found.\n", word);
+		return 0;
+	}
 	else if (fastncmp("S_",word,2)) {
 		p = word+2;
 		for (i = 0; i < NUMSTATEFREESLOTS; i++) {
diff --git a/src/deh_tables.c b/src/deh_tables.c
index 276bfce18a0f8b6f7660c84b0d7dd7acc6e6c0bf..26fa74c902c352cd03cd1a6595c9cd14355c5c2a 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4482,6 +4482,28 @@ const char *const MSF_LIST[7] = {
 	"NOCLIPCAMERA",
 };
 
+// Sector special flags
+const char* const SSF_LIST[19] = {
+	"OUTERSPACE",
+	"DOUBLESTEPUP",
+	"WINDCURRENT",
+	"CONVEYOR",
+	"SPEEDPAD",
+	"STARPOSTACTIVATOR",
+	"EXIT",
+	"SPECIALSTAGEPIT",
+	"RETURNFLAG",
+	"REDTEAMBASE",
+	"BLUETEAMBASE",
+	"FAN",
+	"SUPERTRANSFORM",
+	"FORCESPIN",
+	"ZOOMTUBESTART",
+	"ZOOMTUBEEND",
+	"FINISHLINE",
+	"ROPEHANG",
+};
+
 const char *COLOR_ENUMS[] = {
 	"NONE",			// SKINCOLOR_NONE,
 
diff --git a/src/deh_tables.h b/src/deh_tables.h
index 8cabb2063d2ee8bdce3e14ca8b781d5783d9b892..35f58c88224e001a7832f9e921d27c9cec316292 100644
--- a/src/deh_tables.h
+++ b/src/deh_tables.h
@@ -66,6 +66,7 @@ extern const char *const PLAYERFLAG_LIST[];
 extern const char *const GAMETYPERULE_LIST[];
 extern const char *const ML_LIST[16]; // Linedef flags
 extern const char* const MSF_LIST[7]; // Sector flags
+extern const char* const SSF_LIST[19]; // Sector special flags
 extern const char *COLOR_ENUMS[];
 extern const char *const POWERS_LIST[];
 extern const char *const HUDITEMS_LIST[];
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index d64f8abd2fa71beb473a0b673145df39a538ae81..27af76ed8a0600dc0fa483094a5a9f4bb862bbc5 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -2215,6 +2215,18 @@ static int lib_pMobjTouchingSectorSpecial(lua_State *L)
 	return 1;
 }
 
+static int lib_pMobjTouchingSectorSpecialFlag(lua_State *L)
+{
+	mobj_t *mo = *((mobj_t**)luaL_checkudata(L, 1, META_MOBJ));
+	sectorspecialflags_t flag = (INT32)luaL_checkinteger(L, 2);
+	//HUDSAFE
+	INLEVEL
+	if (!mo)
+		return LUA_ErrInvalid(L, "mobj_t");
+	LUA_PushUserdata(L, P_MobjTouchingSectorSpecialFlag(mo, flag), META_SECTOR);
+	return 1;
+}
+
 static int lib_pPlayerTouchingSectorSpecial(lua_State *L)
 {
 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@@ -2228,6 +2240,18 @@ static int lib_pPlayerTouchingSectorSpecial(lua_State *L)
 	return 1;
 }
 
+static int lib_pPlayerTouchingSectorSpecialFlag(lua_State *L)
+{
+	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	sectorspecialflags_t flag = (INT32)luaL_checkinteger(L, 2);
+	//HUDSAFE
+	INLEVEL
+	if (!player)
+		return LUA_ErrInvalid(L, "player_t");
+	LUA_PushUserdata(L, P_PlayerTouchingSectorSpecialFlag(player, flag), META_SECTOR);
+	return 1;
+}
+
 static int lib_pFindLowestFloorSurrounding(lua_State *L)
 {
 	sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
@@ -4059,7 +4083,9 @@ static luaL_Reg lib[] = {
 	{"P_DoSuperTransformation",lib_pDoSuperTransformation},
 	{"P_ExplodeMissile",lib_pExplodeMissile},
 	{"P_MobjTouchingSectorSpecial",lib_pMobjTouchingSectorSpecial},
+	{"P_MobjTouchingSectorSpecialFlag",lib_pMobjTouchingSectorSpecialFlag},
 	{"P_PlayerTouchingSectorSpecial",lib_pPlayerTouchingSectorSpecial},
+	{"P_PlayerTouchingSectorSpecialFlag",lib_pPlayerTouchingSectorSpecialFlag},
 	{"P_FindLowestFloorSurrounding",lib_pFindLowestFloorSurrounding},
 	{"P_FindHighestFloorSurrounding",lib_pFindHighestFloorSurrounding},
 	{"P_FindNextHighestFloor",lib_pFindNextHighestFloor},
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 74f45535f8b0730eee8208329894ce15a6c264ca..8ab8c7e83e594f3350f11ac3f861480e8a718e55 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -50,6 +50,7 @@ enum sector_e {
 	sector_fslope,
 	sector_cslope,
 	sector_flags,
+	sector_specialflags,
 	sector_friction,
 	sector_gravity,
 };
@@ -76,6 +77,7 @@ static const char *const sector_opt[] = {
 	"f_slope",
 	"c_slope",
 	"flags",
+	"specialflags",
 	"friction",
 	"gravity",
 	NULL};
@@ -658,6 +660,9 @@ static int sector_get(lua_State *L)
 	case sector_flags: // flags
 		lua_pushinteger(L, sector->flags);
 		return 1;
+	case sector_specialflags: // specialflags
+		lua_pushinteger(L, sector->specialflags);
+		return 1;
 	case sector_friction: // friction
 		lua_pushinteger(L, sector->friction);
 		return 1;
@@ -755,6 +760,9 @@ static int sector_set(lua_State *L)
 		sector->flags = luaL_checkinteger(L, 3);
 		CheckForReverseGravity |= (sector->flags & MSF_GRAVITYFLIP);
 		break;
+	case sector_specialflags:
+		sector->specialflags = luaL_checkinteger(L, 3);
+		break;
 	case sector_gravity:
 		sector->gravity = luaL_checkfixed(L, 3);
 		break;
diff --git a/src/p_inter.c b/src/p_inter.c
index 028ac0ef37f2348afc09b81e34b84c49193c7c16..caf247f96102636c084c6382f98c4d7db14a37b7 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -763,6 +763,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 //				return;
 			{
 				UINT8 flagteam = (special->type == MT_REDFLAG) ? 1 : 2;
+				sectorspecialflags_t specialflag = (special->type == MT_REDFLAG) ? SSF_REDTEAMBASE : SSF_BLUETEAMBASE;
 				const char *flagtext;
 				char flagcolor;
 				char plname[MAXPLAYERNAME+4];
@@ -792,7 +793,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 						special->fuse = 1;
 						special->flags2 |= MF2_JUSTATTACKED;
 
-						if (!P_PlayerTouchingSectorSpecial(player, 4, 2 + flagteam))
+						if (!P_PlayerTouchingSectorSpecialFlag(player, specialflag))
 						{
 							CONS_Printf(M_GetText("%s returned the %c%s%c to base.\n"), plname, flagcolor, flagtext, 0x80);
 
diff --git a/src/p_map.c b/src/p_map.c
index 8b236c7c342fa50dec1a6c24e8a5d81550b37dd2..beae1a8ea614965f671206f04ed482479449a969 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -2699,14 +2699,14 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 
 			if (thing->player)
 			{
-				// If using type Section1:13, double the maxstep.
-				if (P_PlayerTouchingSectorSpecial(thing->player, 1, 13)
-				|| GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 13)
+				// If using SSF_DOUBLESTEPUP, double the maxstep.
+				if (P_PlayerTouchingSectorSpecialFlag(thing->player, SSF_DOUBLESTEPUP)
+				|| (R_PointInSubsector(x, y)->sector->specialflags & SSF_DOUBLESTEPUP))
 					maxstep <<= 1;
 
-				// If using type Section1:14, no maxstep.
-				if (P_PlayerTouchingSectorSpecial(thing->player, 1, 14)
-				|| GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 14)
+				// If using SSF_NOSTEPDOWN, no maxstep.
+				if (P_PlayerTouchingSectorSpecialFlag(thing->player, SSF_NOSTEPDOWN)
+				|| (R_PointInSubsector(x, y)->sector->specialflags & SSF_NOSTEPDOWN))
 					maxstep = 0;
 
 				// Don't 'step up' while springing,
@@ -2717,12 +2717,12 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 			}
 			else if (thing->flags & MF_PUSHABLE)
 			{
-				// If using type Section1:13, double the maxstep.
-				if (GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 13)
+				// If using SSF_DOUBLESTEPUP, double the maxstep.
+				if (R_PointInSubsector(x, y)->sector->specialflags & SSF_DOUBLESTEPUP)
 					maxstep <<= 1;
 
-				// If using type Section1:14, no maxstep.
-				if (GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 14)
+				// If using SSF_NOSTEPDOWN, no maxstep.
+				if (R_PointInSubsector(x, y)->sector->specialflags & SSF_NOSTEPDOWN)
 					maxstep = 0;
 			}
 
diff --git a/src/p_mobj.c b/src/p_mobj.c
index bdfee2616a1d1024a50297bfe010a661ec3d7a57..4b31307efdacc6503d7db8c9031a5604c9051575 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -9738,7 +9738,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
 		break;
 	case MT_BLUEFLAG:
 	case MT_REDFLAG:
-		if (P_MobjTouchingSectorSpecial(mobj, 4, 2))
+		if (P_MobjTouchingSectorSpecialFlag(mobj, SSF_RETURNFLAG))
 			mobj->fuse = 1; // Return to base.
 		break;
 	case MT_SPINDUST: // Spindash dust
diff --git a/src/p_saveg.c b/src/p_saveg.c
index d7a9750b774623719493c2a5dcd8ba4ee8505bbc..1923fa57650685dad985cd8f07b5d227d40f0dcd 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -853,7 +853,8 @@ static void P_NetUnArchiveWaypoints(void)
 #define SD_FLOORLIGHT 0x08
 #define SD_CEILLIGHT 0x10
 #define SD_FLAG      0x20
-#define SD_GRAVITY   0x40
+#define SD_SPECIALFLAG 0x40
+#define SD_GRAVITY   0x80
 
 #define LD_FLAG     0x01
 #define LD_SPECIAL  0x02
@@ -1041,6 +1042,8 @@ static void ArchiveSectors(void)
 			diff3 |= SD_CEILLIGHT;
 		if (ss->flags != spawnss->flags)
 			diff3 |= SD_FLAG;
+		if (ss->specialflags != spawnss->specialflags)
+			diff3 |= SD_SPECIALFLAG;
 		if (ss->gravity != spawnss->gravity)
 			diff3 |= SD_GRAVITY;
 
@@ -1109,6 +1112,8 @@ static void ArchiveSectors(void)
 			}
 			if (diff3 & SD_FLAG)
 				WRITEUINT32(save_p, ss->flags);
+			if (diff3 & SD_SPECIALFLAG)
+				WRITEUINT32(save_p, ss->specialflags);
 			if (diff3 & SD_GRAVITY)
 				WRITEFIXED(save_p, ss->gravity);
 			if (diff & SD_FFLOORS)
@@ -1217,6 +1222,8 @@ static void UnArchiveSectors(void)
 			sectors[i].flags = READUINT32(save_p);
 			CheckForReverseGravity |= (sectors[i].flags & MSF_GRAVITYFLIP);
 		}
+		if (diff3 & SD_SPECIALFLAG)
+			sectors[i].specialflags = READUINT32(save_p);
 		if (diff3 & SD_GRAVITY)
 			sectors[i].gravity = READFIXED(save_p);
 
diff --git a/src/p_setup.c b/src/p_setup.c
index 0f398c5bcc5274691d577771801e5f2ed46bee21..17fd5bb6337afb3482e80ddaa579da453e5a1c2d 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1679,6 +1679,44 @@ static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val)
 		sectors[i].flags |= MSF_HEATWAVE;
 	else if (fastcmp(param, "noclipcamera") && fastcmp("true", val))
 		sectors[i].flags |= MSF_NOCLIPCAMERA;
+	else if (fastcmp(param, "outerspace") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_OUTERSPACE;
+	else if (fastcmp(param, "doublestepup") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_DOUBLESTEPUP;
+	else if (fastcmp(param, "nostepdown") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_NOSTEPDOWN;
+	else if (fastcmp(param, "windcurrent") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_WINDCURRENT;
+	else if (fastcmp(param, "conveyor") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_CONVEYOR;
+	else if (fastcmp(param, "speedpad") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_SPEEDPAD;
+	else if (fastcmp(param, "starpostactivator") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_STARPOSTACTIVATOR;
+	else if (fastcmp(param, "exit") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_EXIT;
+	else if (fastcmp(param, "specialstagepit") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_SPECIALSTAGEPIT;
+	else if (fastcmp(param, "returnflag") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_RETURNFLAG;
+	else if (fastcmp(param, "redteambase") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_REDTEAMBASE;
+	else if (fastcmp(param, "blueteambase") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_BLUETEAMBASE;
+	else if (fastcmp(param, "fan") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_FAN;
+	else if (fastcmp(param, "supertransform") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_SUPERTRANSFORM;
+	else if (fastcmp(param, "forcespin") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_FORCESPIN;
+	else if (fastcmp(param, "zoomtubestart") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_ZOOMTUBESTART;
+	else if (fastcmp(param, "zoomtubeend") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_ZOOMTUBEEND;
+	else if (fastcmp(param, "finishline") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_FINISHLINE;
+	else if (fastcmp(param, "ropehang") && fastcmp("true", val))
+		sectors[i].specialflags |= SSF_ROPEHANG;
 	else if (fastcmp(param, "friction"))
 		sectors[i].friction = atol(val);
 	else if (fastcmp(param, "gravity"))
@@ -5021,8 +5059,72 @@ static void P_ConvertBinaryMap(void)
 
 	for (i = 0; i < numsectors; i++)
 	{
+
+		switch(GETSECSPECIAL(sectors[i].special, 1))
+		{
+			case 12: //Space countdown
+				sectors[i].specialflags |= SSF_OUTERSPACE;
+				break;
+			case 13: //Ramp sector
+				sectors[i].specialflags |= SSF_DOUBLESTEPUP;
+				break;
+			case 14: //Non-ramp sector
+				sectors[i].specialflags |= SSF_NOSTEPDOWN;
+				break;
+			default:
+				break;
+		}
+
+		switch(GETSECSPECIAL(sectors[i].special, 3))
+		{
+			case 2: //Wind/Current
+				sectors[i].specialflags |= SSF_WINDCURRENT;
+				break;
+			case 4: //Conveyor belt
+				sectors[i].specialflags |= SSF_CONVEYOR;
+				break;
+			case 5: //Speed pad
+				sectors[i].specialflags |= SSF_SPEEDPAD;
+				break;
+			default:
+				break;
+		}
+
 		switch(GETSECSPECIAL(sectors[i].special, 4))
 		{
+			case 1: //Star post activator
+				sectors[i].specialflags |= SSF_STARPOSTACTIVATOR;
+				break;
+			case 2: //Exit/Special Stage pit/Return flag
+				sectors[i].specialflags |= SSF_EXIT|SSF_SPECIALSTAGEPIT|SSF_RETURNFLAG;
+				break;
+			case 3: //Red team base
+				sectors[i].specialflags |= SSF_REDTEAMBASE;
+				break;
+			case 4: //Blue team base
+				sectors[i].specialflags |= SSF_BLUETEAMBASE;
+				break;
+			case 5: //Fan sector
+				sectors[i].specialflags |= SSF_FAN;
+				break;
+			case 6: //Super Sonic transform
+				sectors[i].specialflags |= SSF_SUPERTRANSFORM;
+				break;
+			case 7: //Force spin
+				sectors[i].specialflags |= SSF_FORCESPIN;
+				break;
+			case 8: //Zoom tube start
+				sectors[i].specialflags |= SSF_ZOOMTUBESTART;
+				break;
+			case 9: //Zoom tube end
+				sectors[i].specialflags |= SSF_ZOOMTUBEEND;
+				break;
+			case 10: //Circuit finish line
+				sectors[i].specialflags |= SSF_FINISHLINE;
+				break;
+			case 11: //Rope hang
+				sectors[i].specialflags |= SSF_ROPEHANG;
+				break;
 			case 12: //Intangible to the camera
 				sectors[i].flags |= MSF_NOCLIPCAMERA;
 				break;
diff --git a/src/p_spec.c b/src/p_spec.c
index 388440d75b925d538ae1fde6594d7e9a87cfc7c1..2e48219722906be5dc84ccf08654cb69c84f99b4 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -3933,7 +3933,7 @@ boolean P_IsFlagAtBase(mobjtype_t flag)
 {
 	thinker_t *think;
 	mobj_t *mo;
-	INT32 specialnum = (flag == MT_REDFLAG) ? 3 : 4;
+	sectorspecialflags_t specialflag = (flag == MT_REDFLAG) ? SSF_REDTEAMBASE : SSF_BLUETEAMBASE;
 
 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
 	{
@@ -3945,7 +3945,7 @@ boolean P_IsFlagAtBase(mobjtype_t flag)
 		if (mo->type != flag)
 			continue;
 
-		if (GETSECSPECIAL(mo->subsector->sector->special, 4) == specialnum)
+		if (mo->subsector->sector->specialflags & specialflag)
 			return true;
 		else if (mo->subsector->sector->ffloors) // Check the 3D floors
 		{
@@ -3956,7 +3956,7 @@ boolean P_IsFlagAtBase(mobjtype_t flag)
 				if (!(rover->flags & FF_EXISTS))
 					continue;
 
-				if (GETSECSPECIAL(rover->master->frontsector->special, 4) != specialnum)
+				if (!(rover->master->frontsector->specialflags & specialflag))
 					continue;
 
 				if (!(mo->z <= P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector)
@@ -4041,6 +4041,30 @@ static sector_t *P_MobjTouching3DFloorSpecial(mobj_t *mo, sector_t *sector, INT3
 	return NULL;
 }
 
+static sector_t *P_MobjTouching3DFloorSpecialFlag(mobj_t *mo, sector_t *sector, sectorspecialflags_t flag)
+{
+	ffloor_t *rover;
+
+	for (rover = sector->ffloors; rover; rover = rover->next)
+	{
+		if (!(rover->master->frontsector->specialflags & flag))
+			continue;
+
+		if (!(rover->flags & FF_EXISTS))
+			continue;
+
+		if (!P_IsMobjTouching3DFloor(mo, rover, sector))
+			continue;
+
+		// This FOF has the special we're looking for, but are we allowed to touch it?
+		if (sector == mo->subsector->sector
+			|| (rover->master->frontsector->flags & MSF_TRIGGERSPECIAL_TOUCH))
+			return rover->master->frontsector;
+	}
+
+	return NULL;
+}
+
 static sector_t *P_MobjTouchingPolyobjSpecial(mobj_t *mo, INT32 section, INT32 number)
 {
 	polyobj_t *po;
@@ -4073,6 +4097,38 @@ static sector_t *P_MobjTouchingPolyobjSpecial(mobj_t *mo, INT32 section, INT32 n
 	return NULL;
 }
 
+static sector_t *P_MobjTouchingPolyobjSpecialFlag(mobj_t *mo, sectorspecialflags_t flag)
+{
+	polyobj_t *po;
+	sector_t *polysec;
+	boolean touching = false;
+	boolean inside = false;
+
+	for (po = mo->subsector->polyList; po; po = (polyobj_t *)(po->link.next))
+	{
+		if (po->flags & POF_NOSPECIALS)
+			continue;
+
+		polysec = po->lines[0]->backsector;
+
+		if (!(polysec->specialflags & flag))
+			continue;
+
+		touching = (polysec->flags & MSF_TRIGGERSPECIAL_TOUCH) && P_MobjTouchingPolyobj(po, mo);
+		inside = P_MobjInsidePolyobj(po, mo);
+
+		if (!(inside || touching))
+			continue;
+
+		if (!P_IsMobjTouchingPolyobj(mo, po, polysec))
+			continue;
+
+		return polysec;
+	}
+
+	return NULL;
+}
+
 sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number)
 {
 	msecnode_t *node;
@@ -4101,7 +4157,42 @@ sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number)
 		if (!(node->m_sector->flags & MSF_TRIGGERSPECIAL_TOUCH))
 			continue;
 
-		if (GETSECSPECIAL(mo->subsector->sector->special, section) == number)
+		if (GETSECSPECIAL(node->m_sector->special, section) == number)
+			return node->m_sector;
+	}
+
+	return NULL;
+}
+
+sector_t *P_MobjTouchingSectorSpecialFlag(mobj_t *mo, sectorspecialflags_t flag)
+{
+	msecnode_t *node;
+	sector_t *result;
+
+	result = P_MobjTouching3DFloorSpecialFlag(mo, mo->subsector->sector, flag);
+	if (result)
+		return result;
+
+	result = P_MobjTouchingPolyobjSpecialFlag(mo, flag);
+	if (result)
+		return result;
+
+	if (mo->subsector->sector->specialflags & flag)
+		return mo->subsector->sector;
+
+	for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
+	{
+		if (node->m_sector == mo->subsector->sector) // Don't duplicate
+			continue;
+
+		result = P_MobjTouching3DFloorSpecialFlag(mo, node->m_sector, flag);
+		if (result)
+			return result;
+
+		if (!(node->m_sector->flags & MSF_TRIGGERSPECIAL_TOUCH))
+			continue;
+
+		if (node->m_sector->specialflags & flag)
 			return node->m_sector;
 	}
 
@@ -4127,6 +4218,14 @@ sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 n
 	return P_MobjTouchingSectorSpecial(player->mo, section, number);
 }
 
+sector_t *P_PlayerTouchingSectorSpecialFlag(player_t *player, sectorspecialflags_t flag)
+{
+	if (!player->mo)
+		return NULL;
+
+	return P_MobjTouchingSectorSpecialFlag(player->mo, flag);
+}
+
 static sector_t *P_Check3DFloorTriggers(player_t *player, sector_t *sector, line_t *sourceline)
 {
 	ffloor_t *rover;
@@ -4371,7 +4470,7 @@ static void P_ProcessSpeedPad(player_t *player, sector_t *sector, sector_t *rove
 
 	if (lineindex == -1)
 	{
-		CONS_Debug(DBG_GAMELOGIC, "ERROR: Sector special %d missing line special #4.\n", sector->special);
+		CONS_Debug(DBG_GAMELOGIC, "ERROR: Speed pad missing line special #4.\n");
 		return;
 	}
 
@@ -4425,22 +4524,35 @@ static void P_ProcessSpeedPad(player_t *player, sector_t *sector, sector_t *rove
 	S_StartSound(player->mo, sfxnum);
 }
 
-static void P_ProcessExitSector(player_t *player, boolean isTouching, mtag_t sectag)
+static void P_ProcessSpecialStagePit(player_t* player)
 {
-	INT32 lineindex;
-
 	if (!(gametyperules & GTR_ALLOWEXIT))
 		return;
 
 	if (player->bot)
 		return;
 
-	if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))
-	{
-		if (player->nightstime > 6 && isTouching)
-			player->nightstime = 6; // Just let P_Ticker take care of the rest.
+	if (!G_IsSpecialStage(gamemap))
+		return;
+
+	if (maptol & TOL_NIGHTS)
+		return;
+
+	if (player->nightstime <= 6)
+		return;
+
+	player->nightstime = 6; // Just let P_Ticker take care of the rest.
+}
+
+static void P_ProcessExitSector(player_t *player, mtag_t sectag)
+{
+	INT32 lineindex;
+
+	if (!(gametyperules & GTR_ALLOWEXIT))
+		return;
+
+	if (player->bot)
 		return;
-	}
 
 	// Exit (for FOF exits; others are handled in P_PlayerThink in p_user.c)
 	P_DoPlayerFinish(player);
@@ -4515,7 +4627,7 @@ static void P_ProcessTeamBase(player_t *player, boolean redteam)
 	P_AddPlayerScore(player, 250);
 }
 
-static void P_ProcessZoomTube(player_t *player, sector_t *sector, mtag_t sectag, boolean end)
+static void P_ProcessZoomTube(player_t *player, mtag_t sectag, boolean end)
 {
 	INT32 sequence;
 	fixed_t speed;
@@ -4534,7 +4646,7 @@ static void P_ProcessZoomTube(player_t *player, sector_t *sector, mtag_t sectag,
 
 	if (lineindex == -1)
 	{
-		CONS_Debug(DBG_GAMELOGIC, "ERROR: Sector special %d missing line special #3.\n", sector->special);
+		CONS_Debug(DBG_GAMELOGIC, "ERROR: Zoom tube missing line special #3.\n");
 		return;
 	}
 
@@ -4630,7 +4742,7 @@ static void P_ProcessFinishLine(player_t *player)
 	}
 }
 
-static void P_ProcessRopeHang(player_t *player, sector_t *sector, mtag_t sectag)
+static void P_ProcessRopeHang(player_t *player, mtag_t sectag)
 {
 	INT32 sequence;
 	fixed_t speed;
@@ -4668,7 +4780,7 @@ static void P_ProcessRopeHang(player_t *player, sector_t *sector, mtag_t sectag)
 
 	if (lineindex == -1)
 	{
-		CONS_Debug(DBG_GAMELOGIC, "ERROR: Sector special %d missing line special #11.\n", sector->special);
+		CONS_Debug(DBG_GAMELOGIC, "ERROR: Rope hang missing line special #11.\n");
 		return;
 	}
 
@@ -4801,18 +4913,13 @@ static void P_ProcessRopeHang(player_t *player, sector_t *sector, mtag_t sectag)
   */
 void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *roversector)
 {
-	INT32 section1, section2, section3, section4;
+	INT32 section1, section2;
 	mtag_t sectag = Tag_FGet(&sector->tags);
 	boolean isTouching;
 
-	if (!sector->special)
+	if (!sector->special && sector->specialflags == 0)
 		return;
 
-	section1 = GETSECSPECIAL(sector->special, 1);
-	section2 = GETSECSPECIAL(sector->special, 2);
-	section3 = GETSECSPECIAL(sector->special, 3);
-	section4 = GETSECSPECIAL(sector->special, 4);
-
 	// Ignore spectators
 	if (player->spectator)
 		return;
@@ -4823,12 +4930,74 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
 	if (player->playerstate != PST_LIVE)
 		return;
 
-	// Conveyor stuff
-	if (section3 == 2 || section3 == 4)
-		player->onconveyor = section3;
-
 	isTouching = roversector || P_IsMobjTouchingSectorPlane(player->mo, sector);
 
+	if (sector->specialflags & SSF_OUTERSPACE)
+	{
+		if (!(player->powers[pw_shield] & SH_PROTECTWATER) && !player->powers[pw_spacetime])
+			player->powers[pw_spacetime] = spacetimetics + 1;
+	}
+	if (sector->specialflags & SSF_WINDCURRENT)
+		player->onconveyor = 2;
+	if (sector->specialflags & SSF_CONVEYOR)
+		player->onconveyor = 4;
+	if ((sector->specialflags & SSF_SPEEDPAD) && isTouching)
+		P_ProcessSpeedPad(player, sector, roversector, sectag);
+	if (sector->specialflags & SSF_STARPOSTACTIVATOR)
+	{
+		mobj_t *post = P_GetObjectTypeInSectorNum(MT_STARPOST, sector - sectors);
+		if (post)
+			P_TouchStarPost(post, player, false);
+	}
+	if (sector->specialflags & SSF_EXIT)
+		P_ProcessExitSector(player, sectag);
+	if ((sector->specialflags & SSF_SPECIALSTAGEPIT) && isTouching)
+		P_ProcessSpecialStagePit(player);
+	if ((sector->specialflags & SSF_REDTEAMBASE) && isTouching)
+		P_ProcessTeamBase(player, true);
+	if ((sector->specialflags & SSF_BLUETEAMBASE) && isTouching)
+		P_ProcessTeamBase(player, false);
+	if (sector->specialflags & SSF_FAN)
+	{
+		player->mo->momz += mobjinfo[MT_FAN].mass/4;
+
+		if (player->mo->momz > mobjinfo[MT_FAN].mass)
+			player->mo->momz = mobjinfo[MT_FAN].mass;
+
+		P_ResetPlayer(player);
+		if (player->panim != PA_FALL)
+			P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
+	}
+	if (sector->specialflags & SSF_SUPERTRANSFORM)
+	{
+		if (player->mo->health > 0 && !player->bot && (player->charflags & SF_SUPER) && !player->powers[pw_super] && ALL7EMERALDS(emeralds))
+			P_DoSuperTransformation(player, true);
+	}
+	if ((sector->specialflags & SSF_FORCESPIN) && isTouching)
+	{
+		if (!(player->pflags & PF_SPINNING))
+		{
+			player->pflags |= PF_SPINNING;
+			P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
+			S_StartAttackSound(player->mo, sfx_spin);
+
+			if (abs(player->rmomx) < FixedMul(5*FRACUNIT, player->mo->scale)
+			&& abs(player->rmomy) < FixedMul(5*FRACUNIT, player->mo->scale))
+				P_InstaThrust(player->mo, player->mo->angle, FixedMul(10*FRACUNIT, player->mo->scale));
+		}
+	}
+	if (sector->specialflags & SSF_ZOOMTUBESTART)
+		P_ProcessZoomTube(player, sectag, false);
+	if (sector->specialflags & SSF_ZOOMTUBEEND)
+		P_ProcessZoomTube(player, sectag, true);
+	if (sector->specialflags & SSF_FINISHLINE)
+		P_ProcessFinishLine(player);
+	if ((sector->specialflags & SSF_ROPEHANG) && isTouching)
+		P_ProcessRopeHang(player, sectag);
+
+	section1 = GETSECSPECIAL(sector->special, 1);
+	section2 = GETSECSPECIAL(sector->special, 2);
+
 	switch (section1)
 	{
 		case 1: // Damage (Generic)
@@ -4891,20 +5060,10 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
 
 			P_SpecialStageDamage(player, NULL, NULL);
 			break;
-		case 12: // Space Countdown
-			if (!(player->powers[pw_shield] & SH_PROTECTWATER) && !player->powers[pw_spacetime])
-				player->powers[pw_spacetime] = spacetimetics + 1;
-			break;
-		case 13: // Ramp Sector (Increase step-up/down)
-		case 14: // Non-Ramp Sector (Don't step-down)
-		case 15: // Bouncy FOF (deprecated)
-			break;
 	}
 
 	switch (section2)
 	{
-		case 1: // Trigger Linedef Exec (Pushable Objects)
-			break;
 		case 2: // Linedef executor requires all players present+doesn't require touching floor
 		case 3: // Linedef executor requires all players present
 			if (!P_DoAllPlayersTrigger(sectag))
@@ -4919,111 +5078,10 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
 			if (!player->bot)
 				P_LinedefExecute(sectag, player->mo, sector);
 			break;
-		case 8: // Tells pushable things to check FOFs
-			break;
 		case 9: // Egg trap capsule
 			if (!udmf && roversector)
 				P_ProcessEggCapsule(player, sector);
 			break;
-		case 10: // Special Stage Time/Rings
-		case 11: // Custom Gravity
-		case 12: // Lua sector special
-			break;
-	}
-
-	switch (section3)
-	{
-		case 1: // Unused
-		case 2: // Wind/Current
-		case 3: // Unused
-		case 4: // Conveyor Belt
-			break;
-		case 5: // Speed pad
-			if (isTouching)
-				P_ProcessSpeedPad(player, sector, roversector, sectag);
-			break;
-		case 6: // Unused
-		case 7: // Unused
-		case 8: // Unused
-		case 9: // Unused
-		case 10: // Unused
-		case 11: // Unused
-		case 12: // Unused
-		case 13: // Unused
-		case 14: // Unused
-		case 15: // Unused
-			break;
-	}
-
-	switch (section4)
-	{
-		case 1: // Starpost Activator
-		{
-			mobj_t *post = P_GetObjectTypeInSectorNum(MT_STARPOST, sector - sectors);
-
-			if (!post)
-				break;
-
-			P_TouchStarPost(post, player, false);
-			break;
-		}
-		case 2: // Special stage GOAL sector / Exit Sector / CTF Flag Return
-			P_ProcessExitSector(player, isTouching, sectag);
-			break;
-		case 3: // Red Team's Base
-			if (isTouching)
-				P_ProcessTeamBase(player, true);
-			break;
-		case 4: // Blue Team's Base
-			if (isTouching)
-				P_ProcessTeamBase(player, false);
-			break;
-		case 5: // Fan sector
-			player->mo->momz += mobjinfo[MT_FAN].mass/4;
-
-			if (player->mo->momz > mobjinfo[MT_FAN].mass)
-				player->mo->momz = mobjinfo[MT_FAN].mass;
-
-			P_ResetPlayer(player);
-			if (player->panim != PA_FALL)
-				P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
-			break;
-		case 6: // Super Sonic transformer
-			if (player->mo->health > 0 && !player->bot && (player->charflags & SF_SUPER) && !player->powers[pw_super] && ALL7EMERALDS(emeralds))
-				P_DoSuperTransformation(player, true);
-			break;
-		case 7: // Make player spin
-			if (!isTouching)
-				break;
-			if (!(player->pflags & PF_SPINNING))
-			{
-				player->pflags |= PF_SPINNING;
-				P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
-				S_StartAttackSound(player->mo, sfx_spin);
-
-				if (abs(player->rmomx) < FixedMul(5*FRACUNIT, player->mo->scale)
-				&& abs(player->rmomy) < FixedMul(5*FRACUNIT, player->mo->scale))
-					P_InstaThrust(player->mo, player->mo->angle, FixedMul(10*FRACUNIT, player->mo->scale));
-			}
-			break;
-		case 8: // Zoom Tube Start
-			P_ProcessZoomTube(player, sector, sectag, false);
-			break;
-		case 9: // Zoom Tube End
-			P_ProcessZoomTube(player, sector, sectag, true);
-			break;
-		case 10: // Finish Line
-			P_ProcessFinishLine(player);
-			break;
-		case 11: // Rope hang
-			if (isTouching)
-				P_ProcessRopeHang(player, sector, sectag);
-			break;
-		case 12: // Unused
-		case 13: // Unused
-		case 14: // Unused
-		case 15: // Unused
-			break;
 	}
 }
 
@@ -5042,7 +5100,7 @@ static void P_PlayerOnSpecial3DFloor(player_t *player, sector_t *sector)
 
 	for (rover = sector->ffloors; rover; rover = rover->next)
 	{
-		if (!rover->master->frontsector->special)
+		if (!rover->master->frontsector->special && rover->master->frontsector->specialflags == 0)
 			continue;
 
 		if (!(rover->flags & FF_EXISTS))
@@ -5076,7 +5134,7 @@ static void P_PlayerOnSpecialPolyobj(player_t *player)
 
 		polysec = po->lines[0]->backsector;
 
-		if (!polysec->special)
+		if (!polysec->special && polysec->specialflags == 0)
 			continue;
 
 		touching = (polysec->flags & MSF_TRIGGERSPECIAL_TOUCH) && P_MobjTouchingPolyobj(po, player->mo);
@@ -6025,6 +6083,12 @@ void P_SpawnSpecials(boolean fromnetsave)
 	{
 		CheckForReverseGravity |= (sector->flags & MSF_GRAVITYFLIP);
 
+		if (sector->specialflags & SSF_FINISHLINE)
+		{
+			if ((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE)
+				circuitmap = true;
+		}
+
 		if (!sector->special)
 			continue;
 
@@ -6063,15 +6127,6 @@ void P_SpawnSpecials(boolean fromnetsave)
 				gravity = sector->floorheight/1000;
 				break;
 		}
-
-		// Process Section 4
-		switch(GETSECSPECIAL(sector->special, 4))
-		{
-			case 10: // Circuit finish line
-				if ((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE)
-					circuitmap = true;
-				break;
-		}
 	}
 
 	P_SpawnScrollers(); // Add generalized scrollers
@@ -8332,17 +8387,17 @@ void T_Pusher(pusher_t *p)
 
 	sec = sectors + p->affectee;
 
-	// Be sure the special sector type is still turned on. If so, proceed.
-	// Else, bail out; the sector type has been changed on us.
+	// Be sure the sector special flag is still turned on. If so, proceed.
+	// Else, bail out; the flag has been changed on us.
 
 	if (p->roverpusher)
 	{
 		referrer = &sectors[p->referrer];
 
-		if (GETSECSPECIAL(referrer->special, 3) != 2)
+		if (!(referrer->specialflags & SSF_WINDCURRENT))
 			return;
 	}
-	else if (GETSECSPECIAL(sec->special, 3) != 2)
+	else if (!(sec->specialflags & SSF_WINDCURRENT))
 		return;
 
 	// For constant pushers (wind/current) there are 3 situations:
diff --git a/src/p_spec.h b/src/p_spec.h
index c1f25b3d6ee3f98ab42e64383209956f721f37df..19d3bd103147b39d2546617bc01feacb72de1e89 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -480,7 +480,9 @@ void P_SpawnSpecials(boolean fromnetsave);
 // every tic
 void P_UpdateSpecials(void);
 sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number);
+sector_t *P_MobjTouchingSectorSpecialFlag(mobj_t *mo, sectorspecialflags_t flag);
 sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 number);
+sector_t *P_PlayerTouchingSectorSpecialFlag(player_t *player, sectorspecialflags_t flag);
 void P_PlayerInSpecialSector(player_t *player);
 void P_CheckPushableTrigger(mobj_t *mobj, sector_t *sec);
 void P_CheckMobjTrigger(mobj_t *mobj);
diff --git a/src/p_user.c b/src/p_user.c
index 45c64500f3622c5f27e4834c4604e7992bf45a84..1006e21ed36b3202da284479a3186b9ce57ae0af 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2166,13 +2166,12 @@ void P_DoPlayerExit(player_t *player)
 	P_RestoreMusic(player);
 }
 
-#define SPACESPECIAL 12
 boolean P_InSpaceSector(mobj_t *mo) // Returns true if you are in space
 {
 	sector_t *sector = mo->subsector->sector;
 	fixed_t topheight, bottomheight;
 
-	if (GETSECSPECIAL(sector->special, 1) == SPACESPECIAL)
+	if (sector->specialflags & SSF_OUTERSPACE)
 		return true;
 
 	if (sector->ffloors)
@@ -2184,7 +2183,7 @@ boolean P_InSpaceSector(mobj_t *mo) // Returns true if you are in space
 			if (!(rover->flags & FF_EXISTS))
 				continue;
 
-			if (GETSECSPECIAL(rover->master->frontsector->special, 1) != SPACESPECIAL)
+			if (!(rover->master->frontsector->specialflags & SSF_OUTERSPACE))
 				continue;
 			topheight    = P_GetFFloorTopZAt   (rover, mo->x, mo->y);
 			bottomheight = P_GetFFloorBottomZAt(rover, mo->x, mo->y);
@@ -4678,7 +4677,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
 	if (onground && player->pflags & PF_SPINNING && !(player->pflags & PF_STARTDASH)
 		&& player->speed < 5*player->mo->scale && canstand)
 	{
-		if (GETSECSPECIAL(player->mo->subsector->sector->special, 4) == 7 || (player->mo->ceilingz - player->mo->floorz < P_GetPlayerHeight(player)))
+		if ((player->mo->subsector->sector->specialflags & SSF_FORCESPIN) || (player->mo->ceilingz - player->mo->floorz < P_GetPlayerHeight(player)))
 			P_InstaThrust(player->mo, player->mo->angle, 10*player->mo->scale);
 		else
 		{
diff --git a/src/r_defs.h b/src/r_defs.h
index 833958ab11f1cb1699cb43a7c0a2930a559330b2..2b1cbfa58c0d6d6dedf0748b4933dd1100f701c4 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -289,6 +289,28 @@ typedef enum
 	MSF_NOCLIPCAMERA            =  1<<7,
 } sectorflags_t;
 
+typedef enum
+{
+	SSF_OUTERSPACE = 1,
+	SSF_DOUBLESTEPUP = 1<<1,
+	SSF_NOSTEPDOWN = 1<<2,
+	SSF_WINDCURRENT = 1<<3,
+	SSF_CONVEYOR = 1<<4,
+	SSF_SPEEDPAD = 1<<5,
+	SSF_STARPOSTACTIVATOR = 1<<6,
+	SSF_EXIT = 1<<7,
+	SSF_SPECIALSTAGEPIT = 1<<8,
+	SSF_RETURNFLAG = 1<<9,
+	SSF_REDTEAMBASE = 1<<10,
+	SSF_BLUETEAMBASE = 1<<11,
+	SSF_FAN = 1<<12,
+	SSF_SUPERTRANSFORM = 1<<13,
+	SSF_FORCESPIN = 1<<14,
+	SSF_ZOOMTUBESTART = 1<<15,
+	SSF_ZOOMTUBEEND = 1<<16,
+	SSF_FINISHLINE = 1<<17,
+	SSF_ROPEHANG = 1<<18,
+} sectorspecialflags_t;
 
 typedef enum
 {
@@ -371,6 +393,7 @@ typedef struct sector_s
 	fixed_t gravity; // per-sector gravity factor
 	fixed_t *gravityptr; // For binary format: Read gravity from floor height of master sector
 	sectorflags_t flags;
+	sectorspecialflags_t specialflags;
 
 	INT32 friction;