diff --git a/.gitignore b/.gitignore
index 1dd1c19d51a706b7b792ba2e32895ed9d88f8228..4ba05f7ca698b968eec6391857fa5ec68e1b422e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,4 +24,5 @@ Win32_LIB_ASM_Release
 /build
 /build/*
 /CMakeUserPresets.json
-/out
\ No newline at end of file
+/out
+/objs/VC10
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2169ac894ca377cdfe2a40d290619dba3ff52c4b..cb794f3573276dbc2cbec4935dc8dd6bd08ce727 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -153,8 +153,8 @@ default:
     - - |
           # ccache_stats
           echo -e "\e[0Ksection_start:`date +%s`:ccache_stats[collapsed=true]\r\e[0Kccache statistics:"
-      - ccache --show-stats --verbose
-      - ccache --show-log-stats --verbose
+      - ccache --show-stats
+      - ccache --show-log-stats || true
       - |
           # ccahe_stats
           echo -e "\e[0Ksection_end:`date +%s`:ccache_stats\r\e[0K"
@@ -283,6 +283,47 @@ Debian stable:amd64:
           # make
           echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
+Debian oldstable:amd64:
+  extends: Debian stable:amd64
+
+  when: manual
+
+  image: git.do.srb2.org:5050/stjr/srb2ci/srb2ci:oldstable
+
+  allow_failure: true
+
+  artifacts:
+    paths:
+      - "bin/"
+      - "src/comptime.h"
+    expose_as: "Debian old amd64"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-old-x86-64"
+
+  script:
+    - - |
+          # apt_toolchain
+          echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+      - apt-get install gcc-x86-64-linux-gnu || apt-get install gcc
+      - |
+          # apt_toolchain
+          echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+
+    - - |
+          # apt_development
+          echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+      - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libopenmpt-dev:amd64 libminiupnpc-dev:amd64
+      - |
+          # apt_development
+          echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+
+    - - |
+          # make
+          echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NOGME=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NOGME=1
+      - |
+          # make
+          echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+
 Debian stable:i386:
   stage: build
 
@@ -370,6 +411,86 @@ Debian stable:arm64:
           # make
           echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
 
+Debian oldstable:arm64:
+  extends: Debian stable:arm64
+
+  when: manual
+
+  image: git.do.srb2.org:5050/stjr/srb2ci/srb2ci:oldstable
+
+  allow_failure: true
+
+  artifacts:
+    paths:
+      - "bin/"
+      - "src/comptime.h"
+    expose_as: "Debian old arm64"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-old-aarch64"
+
+  script:
+    - - |
+          # apt_toolchain
+          echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+      - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
+      - |
+          # apt_toolchain
+          echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+
+    - - |
+          # apt_development
+          echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+      - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libopenmpt-dev:arm64 libminiupnpc-dev:arm64
+      - |
+          # apt_development
+          echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+
+    - - |
+          # make
+          echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 ERRORMODE=1 NONX86=1 ARM64=1 NOGME=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 ARM64=1 NOGME=1
+      - |
+          # make
+          echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+
+batocera:arm64:
+  extends: Debian stable:arm64
+
+  when: manual
+
+  allow_failure: true
+
+  artifacts:
+    paths:
+      - "bin/"
+      - "src/comptime.h"
+    expose_as: "Debian old arm64"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-batocera-aarch64"
+
+  script:
+    - - |
+          # apt_toolchain
+          echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+      - apt-get install gcc-aarch64-linux-gnu || apt-get install gcc
+      - |
+          # apt_toolchain
+          echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K"
+
+    - - |
+          # apt_development
+          echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages"
+      - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libopenmpt-dev:arm64 libminiupnpc-dev:arm64
+      - |
+          # apt_development
+          echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K"
+
+    - - |
+          # make
+          echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 ERRORMODE=1 NONX86=1 ARM64=1 NOGME=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 LINUX64=1 NONX86=1 ARM64=1 NOGME=1
+      - |
+          # make
+          echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
+
 Windows x64:
   stage: build
 
@@ -596,3 +717,38 @@ Alpine 3 GCC:
       - |
           # ccahe_stats
           echo -e "\e[0Ksection_end:`date +%s`:ccache_stats\r\e[0K"
+
+Alpine 3 GCC Dedicated:
+  extends: Alpine 3 GCC
+
+  artifacts:
+    paths:
+      - "bin/"
+      - "src/comptime.h"
+    expose_as: "Apline-3-Dedicated"
+    name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Apline-3-Dedicated"
+
+  script:
+    - - |
+          # apk_toolchain
+          echo -e "\e[0Ksection_start:`date +%s`:apk_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages"
+      - apk add gcc
+      - |
+          # apk_toolchain
+          echo -e "\e[0Ksection_end:`date +%s`:apk_toolchain\r\e[0K"
+
+    - - |
+          # apk_development
+          echo -e "\e[0Ksection_start:`date +%s`:apk_development[collapsed=true]\r\e[0KInstalling development packages"
+      - apk add musl-dev libpng-dev curl-dev
+      - |
+          # apk_development
+          echo -e "\e[0Ksection_end:`date +%s`:apk_development\r\e[0K"
+
+    - - |
+          # make
+          echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2"
+      - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 NOEXECINFO=1 DEDICATED=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 NOEXECINFO=1 DEDICATED=1
+      - |
+          # make
+          echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K"
diff --git a/extras/conf/udb/Includes/SRB222_common.cfg b/extras/conf/udb/Includes/SRB222_common.cfg
index 8f37aabaa41de4072ede5b6910666fb654d7cfd0..aee7a70e4e0a4b806d8c0a055d77f06fce476ea6 100644
--- a/extras/conf/udb/Includes/SRB222_common.cfg
+++ b/extras/conf/udb/Includes/SRB222_common.cfg
@@ -40,7 +40,9 @@ common
 	defaultflatscale = 1.0f;
 	scaledtextureoffsets = true;
 
+	// Colormap/fade related options
 	maxcolormapalpha = 25;
+	// TODO: change to 255;
 
 	// Thing number for start position in 3D Mode
 	start3dmode = 3328;
@@ -100,11 +102,23 @@ mapformat_udmf
 
 	// When this is set to true, sectors with the same tag will light up when a line is highlighted
 	linetagindicatesectors = false;
+
+	// Enables support for individual offsets of upper/middle/lower sidedef textures
 	localsidedeftextureoffsets = true;
-	distinctfloorandceilingbrightness = true;
-	
+
+	// Enables support for plane equation slopes
 	planeequationsupport = true;
 
+	// Enables support for vertex heights
+	vertexheightsupport = true;
+
+	// Enables setting distinct brightness for floor, ceiling, and walls
+	distinctfloorandceilingbrightness = true;
+	distinctwallbrightness = false;
+
+	// Enables setting distinct brightness for upper, middle, and lower sidedef parts
+	distinctsidedefpartbrightness = false;
+
 	// Special linedefs
 	include("SRB222_misc.cfg", "speciallinedefs_udmf");
 
diff --git a/extras/conf/udb/Includes/SRB222_linedefs.cfg b/extras/conf/udb/Includes/SRB222_linedefs.cfg
index 152cafe211fe27ae8b2169e7fe795c253e8fb182..e297f473fd46d2848f3835765988bb913147e84f 100644
--- a/extras/conf/udb/Includes/SRB222_linedefs.cfg
+++ b/extras/conf/udb/Includes/SRB222_linedefs.cfg
@@ -9,7 +9,7 @@ udmf
 			title = "None";
 			prefix = "(0)";
 		}
-
+		
 		6
 		{
 			title = "Sector Set Portal";
@@ -97,6 +97,7 @@ udmf
 		41
 		{
 			title = "Horizon Effect";
+			id = "srb2_horizonline";
 			prefix = "(41)";
 		}
 
@@ -147,10 +148,12 @@ udmf
 			arg0
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg1
 			{
-				title = "Sequence";
+				title = "Waypoint sequence";
+				tooltip = "The sequence number of (zoom tube) waypoints to use.";
 			}
 			arg2
 			{
@@ -167,6 +170,7 @@ udmf
 			arg0
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg1
 			{
@@ -182,6 +186,7 @@ udmf
 			{
 				title = "Sound";
 				type = 2;
+				tooltip = "Takes a DS constant.\nExample: DSTHOK";
 			}
 		}
 
@@ -203,10 +208,12 @@ udmf
 			arg0
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg1
 			{
-				title = "Sequence";
+				title = "Waypoint sequence";
+				tooltip = "The sequence number of (zoom tube) waypoints to use.";
 			}
 			arg2
 			{
@@ -226,7 +233,7 @@ udmf
 			}
 			arg1
 			{
-				title = "Debris lifetime";
+				title = "Debris lifetime (tics)";
 			}
 			arg2
 			{
@@ -237,6 +244,7 @@ udmf
 			stringarg0
 			{
 				title = "Debris object type";
+				tooltip = "Uses a MT_ constant.\nExample: MT_ROCKCRUMBLE16\nDefaults to MT_ROCKCRUMBLE1.";
 				type = 2;
 			}
 		}
@@ -298,21 +306,22 @@ udmf
 
 		20
 		{
-			title = "First Line";
+			title = "PolyObject First Line";
 			prefix = "(20)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
 			{
-				title = "Parent ID";
+				title = "Parent PolyObject tag";
 				type = 14;
 			}
 			arg2
 			{
-				title = "Translucency";
+				title = "Translucency level";
+				tooltip = "Ranges from 0 (fully opaque) to 10 (fully transparent).";
 			}
 			arg3
 			{
@@ -338,45 +347,48 @@ udmf
 
 		30
 		{
-			title = "Waving Flag";
+			title = "Waving PolyObject Flag";
 			prefix = "(30)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg2
 			{
 				title = "Distance";
+				tooltip = "How far to move in either direction, in fracunits.";
 			}
 		}
 
 		31
 		{
-			title = "Displacement by Front Sector";
+			title = "Move PolyObject by Front Sector Displacement";
 			prefix = "(31)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
 			{
 				title = "Base speed";
+				tooltip = "How much the front sector displacement translates.\nA value of 256 amounts to a 1:1 translation.";
 			}
 		}
 
 		32
 		{
-			title = "Angular Displacement by Front Sector";
+			title = "Rotate PolyObject by Front Sector Displacement";
 			prefix = "(32)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
@@ -413,6 +425,7 @@ udmf
 			arg0
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg1
 			{
@@ -444,10 +457,12 @@ udmf
 			arg2
 			{
 				title = "Forward speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg3
 			{
 				title = "Return speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg4
 			{
@@ -477,10 +492,12 @@ udmf
 			arg2
 			{
 				title = "Forward speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg3
 			{
 				title = "Return speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg4
 			{
@@ -504,10 +521,11 @@ udmf
 			arg1
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg2
 			{
-				title = "Starting delay";
+				title = "Starting delay (tics)";
 			}
 			arg3
 			{
@@ -543,16 +561,18 @@ udmf
 			arg2
 			{
 				title = "Crush speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg3
 			{
 				title = "Retract speed";
+				tooltip = "In fracunits per tic.";
 			}
 		}
 
 		66
 		{
-			title = "Move Planes by Displacement";
+			title = "Move Planes by Front Sector Displacement";
 			prefix = "(66)";
 			arg0
 			{
@@ -589,6 +609,7 @@ udmf
 			arg1
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg2
 			{
@@ -640,14 +661,17 @@ udmf
 			arg1
 			{
 				title = "Falling speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg2
 			{
 				title = "Rising speed";
+				tooltip = "In fracunits per tic.";
 			}
 			stringarg0
 			{
 				title = "Crushing sound";
+				tooltip = "Takes a DS constant.\nExample: DSTHOK";
 				type = 2;
 			}
 		}
@@ -720,6 +744,7 @@ udmf
 			arg1
 			{
 				title = "Sinking speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg2
 			{
@@ -761,6 +786,7 @@ udmf
 			{
 				title = "Alpha";
 				default = 255;
+				tooltip = "Ranges from 0 (fully transparent) to 255 (fully opaque).";
 			}
 			arg2
 			{
@@ -804,6 +830,7 @@ udmf
 			{
 				title = "Alpha";
 				default = 128;
+				tooltip = "Ranges from 0 (fully transparent) to 255 (fully opaque).";
 			}
 			arg2
 			{
@@ -880,6 +907,7 @@ udmf
 			{
 				title = "Alpha";
 				default = 255;
+				tooltip = "Ranges from 0 (fully transparent) to 255 (fully opaque).";
 			}
 			arg2
 			{
@@ -922,6 +950,7 @@ udmf
 			{
 				title = "Alpha";
 				default = 255;
+				tooltip = "Ranges from 0 (fully transparent) to 255 (fully opaque).";
 			}
 			arg2
 			{
@@ -952,6 +981,7 @@ udmf
 			arg5
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg6
 			{
@@ -1009,6 +1039,7 @@ udmf
 			{
 				title = "Alpha";
 				default = 255;
+				tooltip = "Ranges from 0 (fully transparent) to 255 (fully opaque).";
 			}
 			arg2
 			{
@@ -1079,14 +1110,17 @@ udmf
 			arg1
 			{
 				title = "Falling speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg2
 			{
 				title = "Rising speed";
+				tooltip = "In fracunits per tic.";
 			}
 			stringarg0
 			{
 				title = "Crushing sound";
+				tooltip = "Takes a DS constant.\nExample: DSTHOK";
 				type = 2;
 			}
 		}
@@ -1105,6 +1139,7 @@ udmf
 			{
 				title = "Alpha";
 				default = 255;
+				tooltip = "Ranges from 0 (fully transparent) to 255 (fully opaque).";
 			}
 			arg2
 			{
@@ -1162,6 +1197,7 @@ udmf
 			arg2
 			{
 				title = "Sinking speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg3
 			{
@@ -1183,6 +1219,7 @@ udmf
 			{
 				title = "Alpha";
 				default = 128;
+				tooltip = "Ranges from 0 (fully transparent) to 255 (fully opaque).";
 			}
 			arg2
 			{
@@ -1216,6 +1253,7 @@ udmf
 			{
 				title = "Alpha";
 				default = 255;
+				tooltip = "Ranges from 0 (fully transparent) to 255 (fully opaque).";
 			}
 			arg2
 			{
@@ -2155,6 +2193,7 @@ udmf
 			arg1
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 		}
 
@@ -2260,6 +2299,7 @@ udmf
 			arg2
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg3
 			{
@@ -2276,7 +2316,7 @@ udmf
 
 		405
 		{
-			title = "Move Planes by Distance";
+			title = "Move Planes by Set Distance";
 			prefix = "(405)";
 			arg0
 			{
@@ -2296,6 +2336,7 @@ udmf
 			arg3
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg4
 			{
@@ -2328,14 +2369,15 @@ udmf
 			arg1
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg2
 			{
-				title = "Starting delay";
+				title = "Starting delay (tics)";
 			}
 			arg3
 			{
-				title = "Delay before flip";
+				title = "Delay before flip (tics)";
 			}
 			arg4
 			{
@@ -2367,10 +2409,12 @@ udmf
 			arg2
 			{
 				title = "Crush speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg3
 			{
 				title = "Retract speed";
+				tooltip = "In fracunits per tic.";
 			}
 		}
 	}
@@ -2385,7 +2429,7 @@ udmf
 			prefix = "(412)";
 			arg0
 			{
-				title = "Destination tag";
+				title = "Destination thing tag";
 				type = 14;
 			}
 			arg1
@@ -2771,6 +2815,7 @@ udmf
 			stringarg0
 			{
 				title = "Music name";
+				tooltip = "Takes a music lump name, without the O_ or D_ prefix. Use - to disable music.\nExample: GFZ1";
 				type = 2;
 			}
 		}
@@ -2810,6 +2855,7 @@ udmf
 			stringarg0
 			{
 				title = "Sound name";
+				tooltip = "Takes a DS constant.\nExample: DSTHOK";
 				type = 2;
 			}
 		}
@@ -3065,11 +3111,11 @@ udmf
 			prefix = "(448)";
 			arg0
 			{
-				title = "Viewpoint ID";
+				title = "Viewpoint thing tag";
 			}
 			arg1
 			{
-				title = "Centerpoint ID";
+				title = "Centerpoint thing tag";
 			}
 			arg2
 			{
@@ -3154,6 +3200,7 @@ udmf
 			arg2
 			{
 				title = "Alpha";
+				tooltip = "Ranges from 0 (fully transparent) to 255 (fully opaque).";
 			}
 			arg3
 			{
@@ -3184,10 +3231,11 @@ udmf
 			arg2
 			{
 				title = "Alpha";
+				tooltip = "Ranges from 0 (fully transparent) to 255 (fully opaque).";
 			}
 			arg3
 			{
-				title = "Fading speed";
+				title = "Fading speed (tics)";
 			}
 			arg4
 			{
@@ -3373,16 +3421,17 @@ udmf
 
 		480
 		{
-			title = "Door Slide";
+			title = "PolyObject Door Slide";
 			prefix = "(480)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg2
 			{
@@ -3390,22 +3439,23 @@ udmf
 			}
 			arg3
 			{
-				title = "Return delay";
+				title = "Return delay (tics)";
 			}
 		}
 
 		481
 		{
-			title = "Door Swing";
+			title = "PolyObject Door Swing";
 			prefix = "(481)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg2
 			{
@@ -3414,22 +3464,23 @@ udmf
 			}
 			arg3
 			{
-				title = "Return delay";
+				title = "Return delay (tics)";
 			}
 		}
 
 		482
 		{
-			title = "Move";
+			title = "Move PolyObject";
 			prefix = "(482)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg2
 			{
@@ -3445,20 +3496,22 @@ udmf
 
 		484
 		{
-			title = "Rotate";
+			title = "Rotate PolyObject";
 			prefix = "(484)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
 			{
 				title = "Speed";
+				tooltip = "In degrees per tic.";
 			}
 			arg2
 			{
 				title = "Rotation";
+				tooltip = "How many degrees the PolyObject will rotate.";
 				type = 8;
 			}
 			arg3
@@ -3477,20 +3530,22 @@ udmf
 
 		488
 		{
-			title = "Move by Waypoints";
+			title = "Move PolyObject by Waypoints";
 			prefix = "(488)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
 			{
 				title = "Speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg2
 			{
 				title = "Waypoint sequence";
+				tooltip = "The sequence number of (zoom tube) waypoints to use.";
 			}
 			arg3
 			{
@@ -3517,11 +3572,11 @@ udmf
 
 		489
 		{
-			title = "Set Visibility, Tangibility";
+			title = "Set PolyObject Visibility/Tangibility";
 			prefix = "(489)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
@@ -3550,16 +3605,17 @@ udmf
 
 		491
 		{
-			title = "Set Translucency";
+			title = "Set PolyObject Translucency";
 			prefix = "(491)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
 			{
 				title = "Translucency level";
+				tooltip = "Ranges from 0 (fully opaque) to 10 (fully transparent).";
 			}
 			arg2
 			{
@@ -3571,16 +3627,17 @@ udmf
 
 		492
 		{
-			title = "Fade Translucency";
+			title = "Fade PolyObject Translucency";
 			prefix = "(492)";
 			arg0
 			{
-				title = "PolyObject ID";
+				title = "PolyObject tag";
 				type = 14;
 			}
 			arg1
 			{
 				title = "Translucency level";
+				tooltip = "Ranges from 0 (fully opaque) to 10 (fully transparent).";
 			}
 			arg2
 			{
@@ -3608,7 +3665,7 @@ udmf
 
 		500
 		{
-			title = "Scroll Walls";
+			title = "Scroll Wall";
 			prefix = "(500)";
 			arg0
 			{
@@ -3628,7 +3685,7 @@ udmf
 
 		502
 		{
-			title = "Scroll Walls Remotely";
+			title = "Scroll Tagged Walls";
 			prefix = "(502)";
 			arg0
 			{
@@ -3665,6 +3722,7 @@ udmf
 			{
 				title = "Sector tag";
 				type = 13;
+				tooltip = "A tag of 0 will scroll the planes of this line's front sector.";
 			}
 			arg1
 			{
@@ -3702,6 +3760,7 @@ udmf
 			{
 				title = "Sector tag";
 				type = 13;
+				tooltip = "A tag of 0 will apply the effect to this line's front sector.";
 			}
 			arg1
 			{
@@ -3725,7 +3784,7 @@ udmf
 			{
 				title = "Flags";
 				type = 12;
-				flags
+				enum
 				{
 					1 = "Slide";
 					2 = "Non-exclusive";
diff --git a/extras/conf/udb/Includes/SRB222_misc.cfg b/extras/conf/udb/Includes/SRB222_misc.cfg
index e274fece69ea37d4be43cd8b7560fde69e181ee5..405d043c140995b4ed840528878cc6eda7c53346 100644
--- a/extras/conf/udb/Includes/SRB222_misc.cfg
+++ b/extras/conf/udb/Includes/SRB222_misc.cfg
@@ -229,20 +229,363 @@ Field data types:
 */
 universalfields
 {
-	sector
-	{
-	}
-
 	linedef
 	{
+		alpha
+		{
+			type = 1;
+			default = 1.0;
+		}
+		
+		comment
+		{
+			type = 2;
+			default = "";
+		}
+
+		renderstyle
+		{
+			type = 2;
+			default = "";
+		}
+		
+		stringarg0
+		{
+			type = 2;
+			default = "";
+		}
+		
+		stringarg1
+		{
+			type = 2;
+			default = "";
+		}
+		
+		executordelay
+		{
+			type = 0;
+			default = 0;
+		}
 	}
 
 	sidedef
 	{
+		comment
+		{
+			type = 2;
+			default = "";
+		}
+		
+		//light
+		//{
+		//	type = 0;
+		//	default = 0;
+		//}
+		//
+		//lightabsolute
+		//{
+		//	type = 3;
+		//	default = false;
+		//}
+		//
+		//light_top
+		//{
+		//	type = 0;
+		//	default = 0;
+		//}
+		//
+		//lightabsolute_top
+		//{
+		//	type = 3;
+		//	default = false;
+		//}
+		//
+		//light_mid
+		//{
+		//	type = 0;
+		//	default = 0;
+		//}
+		//
+		//lightabsolute_mid
+		//{
+		//	type = 3;
+		//	default = false;
+		//}
+		//
+		//light_bottom
+		//{
+		//	type = 0;
+		//	default = 0;
+		//}
+		//
+		//lightabsolute_bottom
+		//{
+		//	type = 3;
+		//	default = false;
+		//}		
+		
+		offsetx_bottom
+		{
+			type = 1;
+			default = 0.0;
+		}
+
+		offsetx_mid
+		{
+			type = 1;
+			default = 0.0;
+		}
+		
+		offsetx_top
+		{
+			type = 1;
+			default = 0.0;
+		}
+
+		offsety_bottom
+		{
+			type = 1;
+			default = 0.0;
+		}
+
+		offsety_mid
+		{
+			type = 1;
+			default = 0.0;
+		}
+		
+		offsety_top
+		{
+			type = 1;
+			default = 0.0;
+		}
+		
+		scalex_bottom
+		{
+			type = 1;
+			default = 1.0;
+		}
+		
+		scalex_mid
+		{
+			type = 1;
+			default = 1.0;
+		}
+		
+		scalex_top
+		{
+			type = 1;
+			default = 1.0;
+		}
+		
+		scaley_bottom
+		{
+			type = 1;
+			default = 1.0;
+		}
+		
+		scaley_mid
+		{
+			type = 1;
+			default = 1.0;
+		}
+		
+		scaley_top
+		{
+			type = 1;
+			default = 1.0;
+		}
 	}
 
 	thing
 	{
+		comment
+		{
+			type = 2;
+			default = "";
+		}
+		
+		pitch
+		{
+			type = 0;
+		}
+		
+		roll
+		{
+			type = 0;
+		}
+		
+		scalex
+		{
+			type = 1;
+			default = 1.0;
+		}
+		
+		scaley
+		{
+			type = 1;
+			default = 1.0;
+		}
+		
+		stringarg0
+		{
+			type = 2;
+			default = "";
+		}
+		
+		stringarg1
+		{
+			type = 2;
+			default = "";
+		}
+		
+		mobjscale
+		{
+			type = 1;
+			default = 1.0;
+			managed = false;
+		}
+	}
+	
+	sector
+	{
+		comment
+		{
+			type = 2;
+			default = "";
+		}
+
+		damagetype
+		{
+			type = 2;
+			default = "";
+		}
+
+		gravity
+		{
+			type = 1;
+			default = 1.0;
+		}
+
+		lightcolor
+		{
+			type = 0;
+			default = 0;
+		}
+
+		fadecolor
+		{
+			type = 0;
+			default = 0;
+		}
+
+		lightalpha
+		{
+			type = 0;
+			default = 25;
+		}
+
+		fadealpha
+		{
+			type = 0;
+			default = 25;
+		}
+
+		fadestart
+		{
+			type = 0;
+			default = 0;
+		}
+
+		fadeend
+		{
+			type = 0;
+			default = 31;
+		}
+
+		xpanningfloor
+		{
+			type = 1;
+			default = 0.0;
+		}
+
+		ypanningfloor
+		{
+			type = 1;
+			default = 0.0;
+		}
+
+		rotationfloor
+		{
+			type = 1;
+			default = 0.0;
+		}
+
+		xscalefloor
+		{
+			type = 1;
+			default = 1.0;
+		}
+		
+		yscalefloor
+		{
+			type = 1;
+			default = 1.0;
+		}
+
+		lightfloor
+		{
+			type = 0;
+			default = 0;
+		}
+		
+		lightfloorabsolute
+		{
+			type = 3;
+			default = false;
+		}
+
+		xpanningceiling
+		{
+			type = 1;
+			default = 0.0;
+		}
+
+		ypanningceiling
+		{
+			type = 1;
+			default = 0.0;
+		}
+
+		rotationceiling
+		{
+			type = 1;
+			default = 0.0;
+		}
+
+		xscaleceiling
+		{
+			type = 1;
+			default = 1.0;
+		}
+		
+		yscaleceiling
+		{
+			type = 1;
+			default = 1.0;
+		}
+
+		lightceiling
+		{
+			type = 0;
+			default = 0;
+		}
+		
+		lightceilingabsolute
+		{
+			type = 3;
+			default = false;
+		}
 	}
 }
 
diff --git a/extras/conf/udb/Includes/SRB222_things.cfg b/extras/conf/udb/Includes/SRB222_things.cfg
index 9eb227974dfd0b33de593a7ced83b42fab7d6738..990f8dca9f505b57f1797c49ed0d8ae003d18d5d 100644
--- a/extras/conf/udb/Includes/SRB222_things.cfg
+++ b/extras/conf/udb/Includes/SRB222_things.cfg
@@ -1127,56 +1127,63 @@ udmf
 				}
 			}
 		}
-		290
-		{
-			arrow = 0;
-			title = "Boss Escape Point";
-			width = 8;
-			height = 16;
-			sprite = "internal:eggmanend";
-		}
-		291
-		{
-			arrow = 0;
-			title = "Egg Capsule Center";
-			width = 8;
-			height = 16;
-			sprite = "internal:capsule";
-		}
-		292
-		{
-			arrow = 0;
-			title = "Boss Waypoint";
-			width = 8;
-			height = 16;
-			sprite = "internal:eggmanway";
-			arg0
-			{
-				title = "Sea Egg sequence";
+		
+		bossinvisibles
+		{
+			title = "Misc. Invisible";
+			color = 15; // White
+			
+			290
+			{
+				arrow = 0;
+				title = "Boss Escape Point";
+				width = 8;
+				height = 16;
+				sprite = "internal:eggmanend";
+			}
+			291
+			{
+				arrow = 0;
+				title = "Egg Capsule Center";
+				width = 8;
+				height = 16;
+				sprite = "internal:capsule";
+			}
+			292
+			{
+				arrow = 0;
+				title = "Boss Waypoint";
+				width = 8;
+				height = 16;
+				sprite = "internal:eggmanway";
+				arg0
+				{
+					title = "Sea Egg sequence";
+				}
+				arg1
+				{
+					title = "Brak Eggman sequence";
+				}
 			}
-			arg1
+			293
 			{
-				title = "Brak Eggman sequence";
+				title = "Metal Sonic Gather Point";
+				sprite = "internal:metal";
+				width = 8;
+				height = 16;
 			}
-		}
-		293
-		{
-			title = "Metal Sonic Gather Point";
-			sprite = "internal:metal";
-			width = 8;
-			height = 16;
-		}
-		294
-		{
-			title = "Fang Waypoint";
-			sprite = "internal:eggmanway";
-			width = 8;
-			height = 16;
-			arg0
+			294
 			{
-				title = "Center waypoint?";
-				type = 11;
-				enum = "noyes";
+				title = "Fang Waypoint";
+				sprite = "internal:eggmanway";
+				width = 8;
+				height = 16;
+				arg0
+				{
+					title = "Center waypoint?";
+					type = 11;
+					enum = "noyes";
+				}
 			}
 		}
 	}
@@ -1196,6 +1203,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1207,6 +1215,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1218,6 +1227,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1230,6 +1240,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1241,6 +1252,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1252,6 +1264,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1263,6 +1276,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1274,6 +1288,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1285,6 +1300,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1296,6 +1312,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1309,6 +1326,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1322,6 +1340,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1335,6 +1354,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1348,6 +1368,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1361,6 +1382,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1374,6 +1396,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1412,6 +1435,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1459,6 +1483,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1470,6 +1495,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1483,6 +1509,7 @@ udmf
 			arg1
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1952,7 +1979,7 @@ udmf
 	generic
 	{
 		color = 11; // Light_Cyan
-		title = "Generic Items & Hazards";
+		title = "Generic Objects ";
 
 		500
 		{
@@ -1963,6 +1990,7 @@ udmf
 			arg0
 			{
 				title = "Distance check?";
+				tooltip = "If enabled, bubbles are only spawned a player is within 2048 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -1992,19 +2020,12 @@ udmf
 				enum = "noyes";
 			}
 		}
-		520
-		{
-			title = "Bomb Sphere";
-			sprite = "SPHRD0";
-			width = 16;
-			height = 24;
-			arg0
-			{
-				title = "Float?";
-				type = 11;
-				enum = "yesno";
-			}
-		}
+	}
+	
+	hazards
+	{
+		color = 17; // Orange
+		title = "Generic Hazards";
 		521
 		{
 			title = "Spikeball";
@@ -2014,6 +2035,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -2162,6 +2184,7 @@ udmf
 			stringarg0
 			{
 				title = "Color";
+				tooltip = "Uses a SKINCOLOR_ constant.\nExample: SKINCOLOR_RED";
 			}
 		}
 		550
@@ -2240,6 +2263,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 16 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -2254,6 +2278,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 16 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -2268,6 +2293,7 @@ udmf
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 16 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -2322,7 +2348,7 @@ udmf
 		arrow = 1;
 		title = "Special Placement Patterns";
 		width = 16;
-		height = 384;
+		height = 64;
 		sprite = "RINGA0";
 
 		600
@@ -2330,6 +2356,7 @@ udmf
 			arrow = 0;
 			title = "5 Vertical Rings (Yellow Spring)";
 			sprite = "internal:ringverticalyellow";
+			height = 384;
 		}
 		601
 		{
@@ -2363,6 +2390,7 @@ udmf
 			title = "Circle of Rings (Big)";
 			sprite = "internal:circlebigring";
 			width = 192;
+			height = 384;
 			centerhitbox = true;
 		}
 		606
@@ -2378,6 +2406,7 @@ udmf
 			title = "Circle of Blue Spheres (Big)";
 			sprite = "internal:circlebigsphere";
 			width = 192;
+			height = 384;
 			centerhitbox = true;
 		}
 		608
@@ -2393,47 +2422,59 @@ udmf
 			title = "Circle of Rings and Spheres (Big)";
 			sprite = "internal:circlebigringsphere";
 			width = 192;
+			height = 384;
 			centerhitbox = true;
 		}
 		610
 		{
 			title = "Row of Items";
-			sprite = "RINGA0";
+			sprite = "internal:customrow";
+			width = 32;
+			height = 384;
 			arg0
 			{
 				title = "Number of items";
+				default = 5;
 			}
 			arg1
 			{
 				title = "Horizontal spacing";
+				default = 64;
 			}
 			arg2
 			{
 				title = "Vertical spacing";
+				default = 64;
 			}
 			stringarg0
 			{
 				title = "Object types";
+				tooltip = "A list of MT_ constants to use, separated by spaces.\nExample: MT_RING MT_BLUESPHERE";
 			}
 		}
 		611
 		{
 			title = "Circle of Items";
-			sprite = "RINGA0";
-			width = 96;
-			height = 192;
+			sprite = "internal:customcircle";
+			width = 32;
+			height = 64;
 			centerhitbox = true;
 			arg0
 			{
 				title = "Number of items";
+				default = 16;
 			}
 			arg1
 			{
 				title = "Radius";
+				renderstyle = "circle";
+				rendercolor = "#6600FF";
+				default = 128;
 			}
 			stringarg0
 			{
 				title = "Object types";
+				tooltip = "A list of MT_ constants to use, separated by spaces.\nExample: MT_RING MT_BLUESPHERE";
 			}
 		}
 	}
@@ -2456,10 +2497,13 @@ udmf
 			arg0
 			{
 				title = "Repeat speed";
+				tooltip = "In tics.";
+				default = 35;
 			}
 			stringarg0
 			{
 				title = "Sound";
+				tooltip = "Takes a DS constant.\nExample: DSAMWTR3";
 			}
 		}
 
@@ -2495,7 +2539,7 @@ udmf
 			sprite = "internal:zoom";
 			arg0
 			{
-				title = "Sequence";
+				title = "Sequence number";
 			}
 			arg1
 			{
@@ -2510,6 +2554,7 @@ udmf
 			arg0
 			{
 				title = "Radius";
+				renderstyle = "circle";
 			}
 			arg1
 			{
@@ -2547,24 +2592,28 @@ udmf
 			height = 16;
 			arg0
 			{
-				title = "Particles";
+				title = "Number of particles";
 			}
 			arg1
 			{
 				title = "Radius";
+				renderstyle = "circle";
 			}
 			arg2
 			{
 				title = "Rising speed";
+				tooltip = "In fracunits per tic.";
 			}
 			arg3
 			{
 				title = "Rotation speed";
+				tooltip = "In degrees per tic.";
 				type = 8;
 			}
 			arg4
 			{
 				title = "Spawn interval";
+				tooltip = "In tics.";
 			}
 			arg5
 			{
@@ -2575,6 +2624,11 @@ udmf
 				title = "Heights control linedef";
 				type = 15;
 			}
+			stringarg0
+			{
+				title = "Particle object type";
+				tooltip = "Uses a MT_ constant.\nExample: MT_PARTICLE\nDefaults to MT_PARTICLE.";
+			}
 		}
 		758
 		{
@@ -2770,6 +2824,7 @@ udmf
 			sprite = "GARGA1";
 			width = 16;
 			height = 40;
+			color = 6;
 			arg0
 			{
 				title = "Push behavior";
@@ -2785,6 +2840,7 @@ udmf
 			sprite = "GARGB1";
 			width = 32;
 			height = 80;
+			color = 6;
 			arg0
 			{
 				title = "Push behavior";
@@ -2893,6 +2949,7 @@ udmf
 			width = 28;
 			height = 56;
 			sprite = "BMNEA1";
+			color = 17;
 		}
 		1013
 		{
@@ -2922,6 +2979,348 @@ udmf
 		color = 2; // Green
 		title = "Castle Eggman";
 
+		macespawns
+		{
+			title = "Mace Spawnpoints";
+			color = 11;
+			
+			1104
+			{
+				title = "Mace Spawn";
+				sprite = "SMCEA0";
+				width = 17;
+				height = 34;
+				arg0
+				{
+					title = "Number of links";
+				}
+				arg1
+				{
+					title = "Number of extra spokes";
+				}
+				arg2
+				{
+					title = "Width";
+					tooltip = "Adds extra maces to both sides.";
+				}
+				arg3
+				{
+					title = "Speed";
+					tooltip = "In fracunits per tic.";
+				}
+				arg4
+				{
+					title = "Phase";
+					type = 8;
+				}
+				arg5
+				{
+					title = "Pinch";
+					type = 8;
+				}
+				arg6
+				{
+					title = "Omitted spokes";
+				}
+				arg7
+				{
+					title = "Omitted links";
+				}
+				arg8
+				{
+					title = "Flags";
+					type = 12;
+					enum = "maceflags";
+				}
+			}
+			1105
+			{
+				title = "Chain & Maces Spawn";
+				sprite = "SMCEA0";
+				width = 17;
+				height = 34;
+				arg0
+				{
+					title = "Number of links";
+				}
+				arg1
+				{
+					title = "Number of extra spokes";
+				}
+				arg2
+				{
+					title = "Width";
+					tooltip = "Adds extra chains/maces to both sides.";
+				}
+				arg3
+				{
+					title = "Speed";
+					tooltip = "In fracunits per tic.";
+				}
+				arg4
+				{
+					title = "Phase";
+					type = 8;
+				}
+				arg5
+				{
+					title = "Pinch";
+					type = 8;
+				}
+				arg6
+				{
+					title = "Omitted spokes";
+				}
+				arg7
+				{
+					title = "Omitted links";
+				}
+				arg8
+				{
+					title = "Flags";
+					type = 12;
+					enum = "maceflags";
+				}
+			}
+			1106
+			{
+				title = "Spring Ball Spawn";
+				sprite = "YSPBA0";
+				width = 17;
+				height = 34;
+				arg0
+				{
+					title = "Number of links";
+				}
+				arg1
+				{
+					title = "Number of extra spokes";
+				}
+				arg2
+				{
+					title = "Width";
+					tooltip = "Adds extra springs to both sides.";
+				}
+				arg3
+				{
+					title = "Speed";
+					tooltip = "In fracunits per tic.";
+				}
+				arg4
+				{
+					title = "Phase";
+					type = 8;
+				}
+				arg5
+				{
+					title = "Pinch";
+					type = 8;
+				}
+				arg6
+				{
+					title = "Omitted spokes";
+				}
+				arg7
+				{
+					title = "Omitted links";
+				}
+				arg8
+				{
+					title = "Flags";
+					type = 12;
+					enum
+					{
+						1 = "Red spring";
+						2 = "No sounds";
+						4 = "Player-turnable chain";
+						8 = "Swing instead of spin";
+						16 = "Make chain from end item";
+						32 = "Spawn link at origin";
+						64 = "Clip inside ground";
+						128 = "No distance check";
+					}
+				}
+			}
+			1107
+			{
+				title = "Chain Spawn";
+				sprite = "BMCHA0";
+				width = 17;
+				height = 34;
+				arg0
+				{
+					title = "Number of links";
+				}
+				arg1
+				{
+					title = "Number of extra spokes";
+				}
+				arg2
+				{
+					title = "Width";
+					tooltip = "Adds extra chains to both sides.";
+				}
+				arg3
+				{
+					title = "Speed";
+					tooltip = "In fracunits per tic.";
+				}
+				arg4
+				{
+					title = "Phase";
+					type = 8;
+				}
+				arg5
+				{
+					title = "Pinch";
+					type = 8;
+				}
+				arg6
+				{
+					title = "Omitted spokes";
+				}
+				arg7
+				{
+					title = "Omitted links";
+				}
+				arg8
+				{
+					title = "Flags";
+					type = 12;
+					enum = "maceflags";
+	
+				}
+			}
+			1108
+			{
+				arrow = 1;
+				title = "Hidden Chain Spawn";
+				sprite = "internal:chain3";
+				width = 17;
+				height = 34;
+			}
+			1109
+			{
+				title = "Firebar Spawn";
+				sprite = "BFBRA0";
+				width = 17;
+				height = 34;
+				arg0
+				{
+					title = "Number of links";
+				}
+				arg1
+				{
+					title = "Number of extra spokes";
+				}
+				arg2
+				{
+					title = "Width";
+					tooltip = "Adds extra firebars to both sides.";
+				}
+				arg3
+				{
+					title = "Speed";
+					tooltip = "In fracunits per tic.";
+				}
+				arg4
+				{
+					title = "Phase";
+					type = 8;
+				}
+				arg5
+				{
+					title = "Pinch";
+					type = 8;
+				}
+				arg6
+				{
+					title = "Omitted spokes";
+				}
+				arg7
+				{
+					title = "Omitted links";
+				}
+				arg8
+				{
+					title = "Flags";
+					type = 12;
+					enum
+					{
+						1 = "Double size";
+						2 = "No sounds";
+						4 = "Player-turnable chain";
+						8 = "Swing instead of spin";
+						16 = "Omit chain links";
+						32 = "Spawn link at origin";
+						64 = "Clip inside ground";
+						128 = "No distance check";
+					}
+				}
+			}
+			1110
+			{
+				title = "Custom Mace Spawn";
+				sprite = "SMCEA0";
+				width = 17;
+				height = 34;
+				arg0
+				{
+					title = "Number of links";
+				}
+				arg1
+				{
+					title = "Number of extra spokes";
+				}
+				arg2
+				{
+					title = "Width";
+					tooltip = "Adds extra maces to both sides.";
+				}
+				arg3
+				{
+					title = "Speed";
+					tooltip = "In fracunits per tic.";
+				}
+				arg4
+				{
+					title = "Phase";
+					type = 8;
+				}
+				arg5
+				{
+					title = "Pinch";
+					type = 8;
+				}
+				arg6
+				{
+					title = "Omitted spokes";
+				}
+				arg7
+				{
+					title = "Omitted links";
+				}
+				arg8
+				{
+					title = "Flags";
+					type = 12;
+					enum = "maceflags";
+				}
+				stringarg0
+				{
+					title = "Mace object type";
+					tooltip = "Uses a MT_ constant.\nExample: MT_BIGMACE";
+					type = 2;
+				}
+				stringarg1
+				{
+					title = "Link object type";
+					tooltip = "Uses a MT_ constant.\nExample: MT_BIGCHAIN";
+					type = 2;
+				}
+			}
+		}
+		
 		1100
 		{
 			title = "Chain (Decorative)";
@@ -2951,6 +3350,7 @@ udmf
 			sprite = "ESTAA1";
 			width = 32;
 			height = 240;
+			color = 6;
 			arg0
 			{
 				title = "Push behavior";
@@ -2971,355 +3371,36 @@ udmf
 			width = 16;
 			height = 40;
 		}
-		1104
+		1111
 		{
-			title = "Mace Spawnpoint";
-			sprite = "SMCEA0";
-			width = 17;
-			height = 34;
+			arrow = 1;
+			blocking = 2;
+			title = "Crawla Statue";
+			sprite = "CSTAA1";
+			width = 16;
+			height = 40;
+			color = 6;
 			arg0
 			{
-				title = "Number of links";
-			}
-			arg1
-			{
-				title = "Number of spokes";
-			}
-			arg2
-			{
-				title = "Width";
-			}
-			arg3
-			{
-				title = "Speed";
-			}
-			arg4
-			{
-				title = "Phase";
-				type = 8;
-			}
-			arg5
-			{
-				title = "Pinch";
-				type = 8;
-			}
-			arg6
-			{
-				title = "Omitted spokes";
-			}
-			arg7
-			{
-				title = "Omitted links";
-			}
-			arg8
-			{
-				title = "Flags";
-				type = 12;
-				enum = "maceflags";
+				title = "Push behavior";
+				type = 11;
+				enum = "pushablebehavior";
 			}
 		}
-		1105
+		1112
 		{
-			title = "Chain with Maces Spawnpoint";
-			sprite = "SMCEA0";
-			width = 17;
-			height = 34;
+			arrow = 1;
+			blocking = 2;
+			title = "Lance-a-Bot Statue";
+			sprite = "CBBSA1";
+			width = 32;
+			height = 72;
+			color = 6;
 			arg0
 			{
-				title = "Number of links";
-			}
-			arg1
-			{
-				title = "Number of spokes";
-			}
-			arg2
-			{
-				title = "Width";
-			}
-			arg3
-			{
-				title = "Speed";
-			}
-			arg4
-			{
-				title = "Phase";
-				type = 8;
-			}
-			arg5
-			{
-				title = "Pinch";
-				type = 8;
-			}
-			arg6
-			{
-				title = "Omitted spokes";
-			}
-			arg7
-			{
-				title = "Omitted links";
-			}
-			arg8
-			{
-				title = "Flags";
-				type = 12;
-				enum = "maceflags";
-			}
-		}
-		1106
-		{
-			title = "Chained Spring Spawnpoint";
-			sprite = "YSPBA0";
-			width = 17;
-			height = 34;
-			arg0
-			{
-				title = "Number of links";
-			}
-			arg1
-			{
-				title = "Number of spokes";
-			}
-			arg2
-			{
-				title = "Width";
-			}
-			arg3
-			{
-				title = "Speed";
-			}
-			arg4
-			{
-				title = "Phase";
-				type = 8;
-			}
-			arg5
-			{
-				title = "Pinch";
-				type = 8;
-			}
-			arg6
-			{
-				title = "Omitted spokes";
-			}
-			arg7
-			{
-				title = "Omitted links";
-			}
-			arg8
-			{
-				title = "Flags";
-				type = 12;
-				enum
-				{
-					1 = "Red spring";
-					2 = "No sounds";
-					4 = "Player-turnable chain";
-					8 = "Swing instead of spin";
-					16 = "Make chain from end item";
-					32 = "Spawn link at origin";
-					64 = "Clip inside ground";
-					128 = "No distance check";
-				}
-			}
-		}
-		1107
-		{
-			title = "Chain Spawnpoint";
-			sprite = "BMCHA0";
-			width = 17;
-			height = 34;
-			arg0
-			{
-				title = "Number of links";
-			}
-			arg1
-			{
-				title = "Number of spokes";
-			}
-			arg2
-			{
-				title = "Width";
-			}
-			arg3
-			{
-				title = "Speed";
-			}
-			arg4
-			{
-				title = "Phase";
-				type = 8;
-			}
-			arg5
-			{
-				title = "Pinch";
-				type = 8;
-			}
-			arg6
-			{
-				title = "Omitted spokes";
-			}
-			arg7
-			{
-				title = "Omitted links";
-			}
-			arg8
-			{
-				title = "Flags";
-				type = 12;
-				enum = "maceflags";
-
-			}
-		}
-		1108
-		{
-			arrow = 1;
-			title = "Hidden Chain Spawnpoint";
-			sprite = "internal:chain3";
-			width = 17;
-			height = 34;
-		}
-		1109
-		{
-			title = "Firebar Spawnpoint";
-			sprite = "BFBRA0";
-			width = 17;
-			height = 34;
-			arg0
-			{
-				title = "Number of links";
-			}
-			arg1
-			{
-				title = "Number of spokes";
-			}
-			arg2
-			{
-				title = "Width";
-			}
-			arg3
-			{
-				title = "Speed";
-			}
-			arg4
-			{
-				title = "Phase";
-				type = 8;
-			}
-			arg5
-			{
-				title = "Pinch";
-				type = 8;
-			}
-			arg6
-			{
-				title = "Omitted spokes";
-			}
-			arg7
-			{
-				title = "Omitted links";
-			}
-			arg8
-			{
-				title = "Flags";
-				type = 12;
-				enum
-				{
-					1 = "Double size";
-					2 = "No sounds";
-					4 = "Player-turnable chain";
-					8 = "Swing instead of spin";
-					16 = "Omit chain links";
-					32 = "Spawn link at origin";
-					64 = "Clip inside ground";
-					128 = "No distance check";
-				}
-			}
-		}
-		1110
-		{
-			title = "Custom Mace Spawnpoint";
-			sprite = "SMCEA0";
-			width = 17;
-			height = 34;
-			arg0
-			{
-				title = "Number of links";
-			}
-			arg1
-			{
-				title = "Number of spokes";
-			}
-			arg2
-			{
-				title = "Width";
-			}
-			arg3
-			{
-				title = "Speed";
-			}
-			arg4
-			{
-				title = "Phase";
-				type = 8;
-			}
-			arg5
-			{
-				title = "Pinch";
-				type = 8;
-			}
-			arg6
-			{
-				title = "Omitted spokes";
-			}
-			arg7
-			{
-				title = "Omitted links";
-			}
-			arg8
-			{
-				title = "Flags";
-				type = 12;
-				enum = "maceflags";
-			}
-			stringarg0
-			{
-				title = "Mace object type";
-				type = 2;
-			}
-			stringarg1
-			{
-				title = "Link object type";
-				type = 2;
-			}
-		}
-		1111
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Crawla Statue";
-			sprite = "CSTAA1";
-			width = 16;
-			height = 40;
-			arg0
-			{
-				title = "Push behavior";
-				type = 11;
-				enum = "pushablebehavior";
-			}
-		}
-		1112
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Lance-a-Bot Statue";
-			sprite = "CBBSA1";
-			width = 32;
-			height = 72;
-			arg0
-			{
-				title = "Push behavior";
-				type = 11;
-				enum = "pushablebehavior";
+				title = "Push behavior";
+				type = 11;
+				enum = "pushablebehavior";
 			}
 		}
 		1114
@@ -3415,6 +3496,7 @@ udmf
 			sprite = "internal:cannonball";
 			width = 8;
 			height = 16;
+			color = 17;
 		}
 		1124
 		{
@@ -3423,6 +3505,7 @@ udmf
 			sprite = "CBLLA0";
 			width = 20;
 			height = 40;
+			color = 6;
 			arg0
 			{
 				title = "Push behavior";
@@ -3443,6 +3526,7 @@ udmf
 			sprite = "LCKNC0";
 			width = 16;
 			height = 32;
+			color = 15;
 		}
 		1127
 		{
@@ -3450,6 +3534,7 @@ udmf
 			sprite = "EGR1A1";
 			width = 20;
 			height = 72;
+			color = 17;
 			arg0
 			{
 				title = "Movement";
@@ -3485,6 +3570,143 @@ udmf
 		color = 2; // Green
 		title = "Arid Canyon";
 
+		
+		cacti
+		{
+			title = "Cacti";
+			color = 17;
+			
+			1203
+			{
+				title = "Tiny Red Flower Cactus";
+				sprite = "CACTA0";
+				width = 13;
+				height = 24;
+			}
+			1204
+			{
+				title = "Small Red Flower Cactus";
+				sprite = "CACTB0";
+				width = 15;
+				height = 52;
+			}
+			1205
+			{
+				title = "Tiny Blue Flower Cactus";
+				sprite = "CACTC0";
+				width = 13;
+				height = 24;
+			}
+			1206
+			{
+				title = "Small Blue Flower Cactus";
+				sprite = "CACTD0";
+				width = 15;
+				height = 52;
+			}
+			1207
+			{
+				title = "Prickly Pear";
+				sprite = "CACTE0";
+				width = 32;
+				height = 96;
+			}
+			1208
+			{
+				title = "Barrel Cactus";
+				sprite = "CACTF0";
+				width = 20;
+				height = 128;
+			}
+			1209
+			{
+				title = "Tall Barrel Cactus";
+				sprite = "CACTG0";
+				width = 24;
+				height = 224;
+			}
+			1210
+			{
+				title = "Armed Cactus";
+				sprite = "CACTH0";
+				width = 24;
+				height = 256;
+			}
+			1211
+			{
+				title = "Ball Cactus";
+				sprite = "CACTI0";
+				width = 48;
+				height = 96;
+			}
+			1230
+			{
+				title = "Tiny Cactus";
+				sprite = "CACTJ0";
+				width = 13;
+				height = 28;
+			}
+			1231
+			{
+				title = "Small Cactus";
+				sprite = "CACTK0";
+				width = 15;
+				height = 60;
+			}
+		}
+		
+		minecarts
+		{
+			title = "Minecart";
+			color = 11;
+			
+			1219
+			{
+				title = "Minecart Spawner";
+				sprite = "MCRTCLFR";
+				width = 22;
+				height = 32;
+			}
+			1220
+			{
+				title = "Minecart Stopper";
+				sprite = "MCRTIR";
+				width = 32;
+				height = 32;
+			}
+			1221
+			{
+				title = "Minecart Saloon Door";
+				sprite = "SALDARAL";
+				width = 96;
+				height = 160;
+				arg0
+				{
+					title = "Allow non-minecart players?";
+					type = 11;
+					enum = "noyes";
+				}
+			}
+			1229
+			{
+				title = "Minecart Switch Point";
+				sprite = "internal:zoom";
+				width = 8;
+				height = 16;
+				color = 15;
+				arg0
+				{
+					title = "Type";
+					type = 11;
+					enum
+					{
+						0 = "Disable";
+						1 = "Enable";
+					}
+				}
+			}
+		}
+		
 		1200
 		{
 			title = "Tumbleweed (Big)";
@@ -3518,6 +3740,7 @@ udmf
 			sprite = "ROIAA0";
 			width = 8;
 			height = 16;
+			color = 17;
 			arg0
 			{
 				title = "Speed";
@@ -3538,86 +3761,23 @@ udmf
 				type = 2;
 			}
 		}
-		1203
+		1212
 		{
-			title = "Tiny Red Flower Cactus";
-			sprite = "CACTA0";
-			width = 13;
-			height = 24;
+			title = "Caution Sign";
+			sprite = "WWSGAR";
+			width = 22;
+			height = 64;
+			wallsprite = true;
 		}
-		1204
+		1213
 		{
-			title = "Small Red Flower Cactus";
-			sprite = "CACTB0";
-			width = 15;
-			height = 52;
+			title = "Cacti Sign";
+			sprite = "WWS2AR";
+			width = 22;
+			height = 64;
+			wallsprite = true;
 		}
-		1205
-		{
-			title = "Tiny Blue Flower Cactus";
-			sprite = "CACTC0";
-			width = 13;
-			height = 24;
-		}
-		1206
-		{
-			title = "Small Blue Flower Cactus";
-			sprite = "CACTD0";
-			width = 15;
-			height = 52;
-		}
-		1207
-		{
-			title = "Prickly Pear";
-			sprite = "CACTE0";
-			width = 32;
-			height = 96;
-		}
-		1208
-		{
-			title = "Barrel Cactus";
-			sprite = "CACTF0";
-			width = 20;
-			height = 128;
-		}
-		1209
-		{
-			title = "Tall Barrel Cactus";
-			sprite = "CACTG0";
-			width = 24;
-			height = 224;
-		}
-		1210
-		{
-			title = "Armed Cactus";
-			sprite = "CACTH0";
-			width = 24;
-			height = 256;
-		}
-		1211
-		{
-			title = "Ball Cactus";
-			sprite = "CACTI0";
-			width = 48;
-			height = 96;
-		}
-		1212
-		{
-			title = "Caution Sign";
-			sprite = "WWSGAR";
-			width = 22;
-			height = 64;
-			wallsprite = true;
-		}
-		1213
-		{
-			title = "Cacti Sign";
-			sprite = "WWS2AR";
-			width = 22;
-			height = 64;
-			wallsprite = true;
-		}
-		1214
+		1214
 		{
 			title = "Sharp Turn Sign";
 			sprite = "WWS3ALAR";
@@ -3639,6 +3799,7 @@ udmf
 			sprite = "BARRA1";
 			width = 24;
 			height = 63;
+			color = 17;
 			arg0
 			{
 				title = "Push behavior";
@@ -3652,6 +3813,7 @@ udmf
 			sprite = "REMTA0";
 			width = 64;
 			height = 40;
+			color = 17;
 		}
 		1218
 		{
@@ -3659,33 +3821,7 @@ udmf
 			sprite = "TAZDCR";
 			width = 80;
 			height = 416;
-		}
-		1219
-		{
-			title = "Minecart Spawner";
-			sprite = "MCRTCLFR";
-			width = 22;
-			height = 32;
-		}
-		1220
-		{
-			title = "Minecart Stopper";
-			sprite = "MCRTIR";
-			width = 32;
-			height = 32;
-		}
-		1221
-		{
-			title = "Minecart Saloon Door";
-			sprite = "SALDARAL";
-			width = 96;
-			height = 160;
-			arg0
-			{
-				title = "Allow non-minecart players?";
-				type = 11;
-				enum = "noyes";
-			}
+			color = 11;
 		}
 		1222
 		{
@@ -3693,6 +3829,7 @@ udmf
 			sprite = "TRAEBRBL";
 			width = 28;
 			height = 32;
+			color = 15;
 		}
 		1223
 		{
@@ -3700,6 +3837,7 @@ udmf
 			sprite = "ADSTA0";
 			width = 4;
 			height = 4;
+			color = 15;
 		}
 		1224
 		{
@@ -3707,37 +3845,7 @@ udmf
 			sprite = "STEAA0";
 			width = 4;
 			height = 4;
-		}
-		1229
-		{
-			title = "Minecart Switch Point";
-			sprite = "internal:zoom";
-			width = 8;
-			height = 16;
-			arg0
-			{
-				title = "Type";
-				type = 11;
-				enum
-				{
-					0 = "Disable";
-					1 = "Enable";
-				}
-			}
-		}
-		1230
-		{
-			title = "Tiny Cactus";
-			sprite = "CACTJ0";
-			width = 13;
-			height = 28;
-		}
-		1231
-		{
-			title = "Small Cactus";
-			sprite = "CACTK0";
-			width = 15;
-			height = 60;
+			color = 15;
 		}
 	}
 
@@ -3753,6 +3861,7 @@ udmf
 			sprite = "internal:flameh";
 			width = 16;
 			height = 40;
+			color = 17;
 			arg0
 			{
 				title = "On time";
@@ -3782,6 +3891,7 @@ udmf
 			sprite = "internal:flamev";
 			width = 16;
 			height = 40;
+			color = 17;
 			arg0
 			{
 				title = "On time";
@@ -3811,6 +3921,7 @@ udmf
 			sprite = "internal:flame2";
 			width = 16;
 			height = 24;
+			color = 17;
 		}
 		1303
 		{
@@ -3818,6 +3929,7 @@ udmf
 			sprite = "internal:flame1";
 			width = 16;
 			height = 24;
+			color = 17;
 		}
 		1304
 		{
@@ -3825,6 +3937,7 @@ udmf
 			sprite = "LFALF0";
 			width = 30;
 			height = 32;
+			color = 17;
 			arg0
 			{
 				title = "Initial delay";
@@ -3842,6 +3955,7 @@ udmf
 			sprite = "PUMIA1A5";
 			width = 30;
 			height = 60;
+			color = 11;
 			arg0
 			{
 				title = "Buoyant?";
@@ -3886,274 +4000,213 @@ udmf
 		}
 	}
 
-	botanicserenity
+	tutorial
 	{
 		color = 2; // Green
-		title = "Botanic Serenity";
-		width = 16;
-		height = 32;
-		sprite = "BSZ1A0";
-		1400
-		{
-			title = "Tall Flower (Red)";
-			sprite = "BSZ1A0";
-		}
-		1401
-		{
-			title = "Tall Flower (Purple)";
-			sprite = "BSZ1B0";
-		}
-		1402
-		{
-			title = "Tall Flower (Blue)";
-			sprite = "BSZ1C0";
-		}
-		1403
-		{
-			title = "Tall Flower (Cyan)";
-			sprite = "BSZ1D0";
-		}
-		1404
-		{
-			title = "Tall Flower (Yellow)";
-			sprite = "BSZ1E0";
-		}
-		1405
+		title = "Tutorial";
+
+		799
 		{
-			title = "Tall Flower (Orange)";
-			sprite = "BSZ1F0";
+			title = "Tutorial Plant";
+			sprite = "TUPFH0";
+			width = 40;
+			height = 144;
+			arg0
+			{
+				title = "Start frame";
+			}
 		}
-		1410
+	}
+
+	frozenhillside
+	{
+		color = 2; // Green
+		title = "Frozen Hillside";
+
+		2100
 		{
-			title = "Medium Flower (Red)";
-			sprite = "BSZ2A0";
+			title = "Ice Shard (Small)";
+			sprite = "FHZIA0";
+			width = 8;
+			height = 32;
 		}
-		1411
+		2101
 		{
-			title = "Medium Flower (Purple)";
-			sprite = "BSZ2B0";
+			title = "Ice Shard (Large)";
+			sprite = "FHZIB0";
+			width = 8;
+			height = 32;
 		}
-		1412
+		2102
 		{
-			title = "Medium Flower (Blue)";
-			sprite = "BSZ2C0";
+			title = "Crystal Tree (Aqua)";
+			sprite = "TRE3A0";
+			width = 20;
+			height = 200;
 		}
-		1413
+		2103
 		{
-			title = "Medium Flower (Cyan)";
-			sprite = "BSZ2D0";
+			title = "Crystal Tree (Pink)";
+			sprite = "TRE3B0";
+			width = 20;
+			height = 200;
 		}
-		1414
+		2104
 		{
-			title = "Medium Flower (Yellow)";
-			sprite = "BSZ2E0";
+			title = "Amy Cameo";
+			sprite = "ROSYA1";
+			width = 16;
+			height = 48;
+			color = 11;
+			arg0
+			{
+				title = "Grayscale?";
+				type = 11;
+				enum = "noyes";
+			}
 		}
-		1415
+		2105
 		{
-			title = "Medium Flower (Orange)";
-			sprite = "BSZ2F0";
+			title = "Mistletoe";
+			sprite = "XMS6A0";
+			width = 52;
+			height = 106;
 		}
-		1420
+	}
+
+	hauntedheights
+	{
+		color = 2; // Green
+		title = "Haunted Heights";
+
+		2000
 		{
-			title = "Short Flower (Red)";
-			sprite = "BSZ3A0";
+			title = "Smashing Spikeball";
+			sprite = "FMCEA0";
+			width = 18;
+			height = 28;
+			color = 17;
+			arg0
+			{
+				title = "Initial delay";
+			}
 		}
-		1421
+		2001
 		{
-			title = "Short Flower (Purple)";
-			sprite = "BSZ3B0";
+			title = "HHZ Grass";
+			sprite = "HHZMA0";
+			width = 16;
+			height = 40;
 		}
-		1422
+		2002
 		{
-			title = "Short Flower (Blue)";
-			sprite = "BSZ3C0";
+			title = "HHZ Tentacle 1";
+			sprite = "HHZMB0";
+			width = 16;
+			height = 40;
 		}
-		1423
+		2003
 		{
-			title = "Short Flower (Cyan)";
-			sprite = "BSZ3D0";
+			title = "HHZ Tentacle 2";
+			sprite = "HHZMC0";
+			width = 16;
+			height = 40;
 		}
-		1424
+		2004
 		{
-			title = "Short Flower (Yellow)";
-			sprite = "BSZ3E0";
+			title = "HHZ Stalagmite (Tall)";
+			sprite = "HHZME0";
+			width = 16;
+			height = 40;
 		}
-		1425
+		2005
 		{
-			title = "Short Flower (Orange)";
-			sprite = "BSZ3F0";
+			title = "HHZ Stalagmite (Short)";
+			sprite = "HHZMF0";
+			width = 16;
+			height = 40;
 		}
-		1430
+		2006
 		{
-			title = "Tulip (Red)";
-			sprite = "BST1A0";
+			title = "Jack-o'-lantern 1";
+			sprite = "PUMKA0";
+			width = 16;
+			height = 40;
+			arg0
+			{
+				title = "Flicker";
+				type = 11;
+				enum = "yesno";
+			}
 		}
-		1431
+		2007
 		{
-			title = "Tulip (Purple)";
-			sprite = "BST2A0";
+			title = "Jack-o'-lantern 2";
+			sprite = "PUMKB0";
+			width = 16;
+			height = 40;
+			arg0
+			{
+				title = "Flicker";
+				type = 11;
+				enum = "yesno";
+			}
 		}
-		1432
+		2008
 		{
-			title = "Tulip (Blue)";
-			sprite = "BST3A0";
+			title = "Jack-o'-lantern 3";
+			sprite = "PUMKC0";
+			width = 16;
+			height = 40;
+			arg0
+			{
+				title = "Flicker";
+				type = 11;
+				enum = "yesno";
+			}
 		}
-		1433
+		2009
 		{
-			title = "Tulip (Cyan)";
-			sprite = "BST4A0";
+			title = "Purple Mushroom";
+			sprite = "SHRMD0";
+			width = 16;
+			height = 48;
 		}
-		1434
+		2010
 		{
-			title = "Tulip (Yellow)";
-			sprite = "BST5A0";
+			title = "HHZ Tree";
+			sprite = "HHPLC0";
+			width = 12;
+			height = 40;
 		}
-		1435
+	}
+
+	azuretemple
+	{
+		color = 2; // Green
+		title = "Azure Temple";
+
+		1500
 		{
-			title = "Tulip (Orange)";
-			sprite = "BST6A0";
+			arrow = 1;
+			blocking = 2;
+			title = "Glaregoyle";
+			sprite = "BGARA1";
+			width = 16;
+			height = 40;
+			color = 17;
+			arg0
+			{
+				title = "Push behavior";
+				type = 11;
+				enum = "pushablebehavior";
+			}
+			arg1
+			{
+				title = "Starting delay";
+			}
 		}
-		1440
-		{
-			title = "Cluster (Red)";
-			sprite = "BSZ5A0";
-		}
-		1441
-		{
-			title = "Cluster (Purple)";
-			sprite = "BSZ5B0";
-		}
-		1442
-		{
-			title = "Cluster (Blue)";
-			sprite = "BSZ5C0";
-		}
-		1443
-		{
-			title = "Cluster (Cyan)";
-			sprite = "BSZ5D0";
-		}
-		1444
-		{
-			title = "Cluster (Yellow)";
-			sprite = "BSZ5E0";
-		}
-		1445
-		{
-			title = "Cluster (Orange)";
-			sprite = "BSZ5F0";
-		}
-		1450
-		{
-			title = "Bush (Red)";
-			sprite = "BSZ6A0";
-		}
-		1451
-		{
-			title = "Bush (Purple)";
-			sprite = "BSZ6B0";
-		}
-		1452
-		{
-			title = "Bush (Blue)";
-			sprite = "BSZ6C0";
-		}
-		1453
-		{
-			title = "Bush (Cyan)";
-			sprite = "BSZ6D0";
-		}
-		1454
-		{
-			title = "Bush (Yellow)";
-			sprite = "BSZ6E0";
-		}
-		1455
-		{
-			title = "Bush (Orange)";
-			sprite = "BSZ6F0";
-		}
-		1460
-		{
-			title = "Vine (Red)";
-			sprite = "BSZ7A0";
-		}
-		1461
-		{
-			title = "Vine (Purple)";
-			sprite = "BSZ7B0";
-		}
-		1462
-		{
-			title = "Vine (Blue)";
-			sprite = "BSZ7C0";
-		}
-		1463
-		{
-			title = "Vine (Cyan)";
-			sprite = "BSZ7D0";
-		}
-		1464
-		{
-			title = "Vine (Yellow)";
-			sprite = "BSZ7E0";
-		}
-		1465
-		{
-			title = "Vine (Orange)";
-			sprite = "BSZ7F0";
-		}
-		1470
-		{
-			title = "BSZ Shrub";
-			sprite = "BSZ8A0";
-		}
-		1471
-		{
-			title = "BSZ Clover";
-			sprite = "BSZ8B0";
-		}
-		1473
-		{
-			title = "Palm Tree (Big)";
-			width = 16;
-			height = 160;
-			sprite = "BSZ8D0";
-		}
-		1475
-		{
-			title = "Palm Tree (Small)";
-			width = 16;
-			height = 80;
-			sprite = "BSZ8F0";
-		}
-	}
-
-	azuretemple
-	{
-		color = 2; // Green
-		title = "Azure Temple";
-
-		1500
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Glaregoyle";
-			sprite = "BGARA1";
-			width = 16;
-			height = 40;
-			arg0
-			{
-				title = "Push behavior";
-				type = 11;
-				enum = "pushablebehavior";
-			}
-			arg1
-			{
-				title = "Starting delay";
-			}
-		}
-		1501
+		1501
 		{
 			arrow = 1;
 			blocking = 2;
@@ -4161,6 +4214,7 @@ udmf
 			sprite = "BGARA1";
 			width = 16;
 			height = 40;
+			color = 17;
 			arg0
 			{
 				title = "Push behavior";
@@ -4180,6 +4234,7 @@ udmf
 			sprite = "BGARA1";
 			width = 16;
 			height = 40;
+			color = 17;
 			arg0
 			{
 				title = "Push behavior";
@@ -4199,6 +4254,7 @@ udmf
 			sprite = "BGARA1";
 			width = 16;
 			height = 40;
+			color = 17;
 			arg0
 			{
 				title = "Push behavior";
@@ -4216,6 +4272,7 @@ udmf
 			sprite = "RCRYB0";
 			width = 24;
 			height = 32;
+			color = 11;
 		}
 		1505
 		{
@@ -4223,6 +4280,7 @@ udmf
 			sprite = "CFLMA0E0";
 			width = 8;
 			height = 32;
+			color = 17;
 		}
 		1506
 		{
@@ -4232,6 +4290,7 @@ udmf
 			sprite = "BGARD1";
 			width = 16;
 			height = 40;
+			color = 6;
 			arg0
 			{
 				title = "Push behavior";
@@ -4241,44 +4300,10 @@ udmf
 		}
 	}
 
-	dreamhill
-	{
-		color = 2; // Green
-		title = "Dream Hill";
-
-		1600
-		{
-			title = "Spring Tree";
-			sprite = "TRE6A0";
-			width = 16;
-			height = 32;
-		}
-		1601
-		{
-			title = "Shleep";
-			sprite = "SHLPA0";
-			width = 24;
-			height = 32;
-		}
-		1602
-		{
-			title = "Nightopian";
-			sprite = "NTPNA1";
-			width = 16;
-			height = 40;
-			arg0
-			{
-				title = "Can move?";
-				type = 11;
-				enum = "yesno";
-			}
-		}
-	}
-
 	nightstrk
 	{
 		color = 16; // Light Pink
-		title = "NiGHTS Track & Misc.";
+		title = "NiGHTS Track";
 		width = 8;
 		height = 4096;
 		sprite = "UNKNA0";
@@ -4299,6 +4324,7 @@ udmf
 			arg2
 			{
 				title = "Radius";
+				default = 256;
 			}
 			arg3
 			{
@@ -4337,12 +4363,21 @@ udmf
 				title = "Order";
 			}
 		}
+	}
+
+	nights
+	{
+		color = 13; // Pink
+		title = "NiGHTS Items & Misc.";
+		width = 16;
+		height = 32;
 		1703
 		{
 			title = "Ideya Drone";
 			sprite = "NDRNA1";
 			width = 16;
 			height = 56;
+			color = 16;
 			arg0
 			{
 				title = "Time limit";
@@ -4380,6 +4415,7 @@ udmf
 			sprite = "CAPSA0";
 			width = 72;
 			height = 144;
+			color = 16;
 			arg0
 			{
 				title = "Mare";
@@ -4395,20 +4431,12 @@ udmf
 			sprite = "internal:ideya";
 			width = 8;
 			height = 16;
+			color = 16;
 			arg0
 			{
 				title = "Mare";
 			}
 		}
-	}
-
-	nights
-	{
-		color = 13; // Pink
-		title = "NiGHTS Items";
-		width = 16;
-		height = 32;
-		
 		1704
 		{
 			arrow = 1;
@@ -4416,6 +4444,22 @@ udmf
 			sprite = "NBMPG3G7";
 			width = 32;
 			height = 64;
+			color = 12;
+		}
+		520
+		{
+			title = "Bomb Sphere";
+			sprite = "SPHRD0";
+			width = 16;
+			height = 24;
+			color = 17;
+			arg0
+			{
+				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
+				type = 11;
+				enum = "yesno";
+			}
 		}
 		1706
 		{
@@ -4423,9 +4467,11 @@ udmf
 			sprite = "SPHRA0";
 			width = 16;
 			height = 24;
+			color = 14;
 			arg0
 			{
 				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
@@ -4513,231 +4559,310 @@ udmf
 			width = 80;
 			height = 160;
 			centerhitbox = true;
+			color = 14;
 			arg0
 			{
 				title = "Radius";
+				default = 96;
 			}
 		}
 	}
 
-	mario
+	dreamhill
 	{
-		color = 6; // Brown
-		title = "Mario";
+		color = 2; // Green
+		title = "Dream Hill";
 
-		1800
+		1600
 		{
-			title = "Coin";
-			sprite = "COINA0";
+			title = "Spring Tree";
+			sprite = "TRE6A0";
 			width = 16;
-			height = 24;
-			arg0
-			{
-				title = "Float?";
-				type = 11;
-				enum = "yesno";
-			}
-		}
-		1801
-		{
-			arrow = 1;
-			title = "Goomba";
-			sprite = "GOOMA0";
-			width = 24;
 			height = 32;
 		}
-		1802
+		1601
 		{
-			arrow = 1;
-			title = "Goomba (Blue)";
-			sprite = "BGOMA0";
+			title = "Shleep";
+			sprite = "SHLPA0";
 			width = 24;
 			height = 32;
+			color = 9;
 		}
-		1803
-		{
-			title = "Fire Flower";
-			sprite = "FFWRB0";
-			width = 16;
-			height = 32;
-		}
-		1804
+		1602
 		{
-			title = "Koopa Shell";
-			sprite = "SHLLA1";
+			title = "Nightopian";
+			sprite = "NTPNA1";
 			width = 16;
-			height = 20;
-		}
-		1805
-		{
-			title = "Puma (Jumping Fireball)";
-			sprite = "PUMAA0";
-			width = 8;
-			height = 16;
+			height = 40;
+			color = 19;
 			arg0
 			{
-				title = "Jump strength";
+				title = "Can move?";
+				type = 11;
+				enum = "yesno";
 			}
 		}
-		1806
+	}
+
+	botanicserenity
+	{
+		color = 2; // Green
+		title = "Botanic Serenity";
+		width = 16;
+		height = 32;
+		
+		flowers
 		{
-			title = "King Bowser";
-			sprite = "KOOPA0";
-			width = 16;
-			height = 48;
-			arg0
+			title = "Flowers";
+			1400
 			{
-				title = "Death trigger tag";
-				type = 15;
+				title = "Tall Flower (Red)";
+				sprite = "BSZ1A0";
 			}
-		}
-		1807
-		{
-			title = "Axe";
-			sprite = "MAXEA0";
-			width = 8;
-			height = 16;
-			arg0
+			1401
 			{
-				title = "Death trigger tag";
-				type = 15;
+				title = "Tall Flower (Purple)";
+				sprite = "BSZ1B0";
 			}
-		}
-		1808
-		{
-			title = "Bush (Short)";
-			sprite = "MUS1A0";
-			width = 16;
-			height = 32;
-		}
-		1809
-		{
-			title = "Bush (Tall)";
-			sprite = "MUS2A0";
-			width = 16;
-			height = 32;
-		}
-		1810
-		{
-			title = "Toad";
-			sprite = "TOADA0";
-			width = 8;
-			height = 32;
-		}
-	}
-
-	christmasdisco
-	{
-		color = 2; // Green
-		title = "Christmas & Disco";
-
-		1850
-		{
-			title = "Christmas Pole";
-			sprite = "XMS1A0";
-			width = 16;
-			height = 40;
-		}
-		1851
-		{
-			title = "Candy Cane";
-			sprite = "XMS2A0";
-			width = 8;
-			height = 32;
-		}
-		1852
-		{
-			blocking = 2;
-			title = "Snowman";
-			sprite = "XMS3A0";
-			width = 16;
-			height = 64;
-			arg0
+			1402
 			{
-				title = "Push behavior";
-				type = 11;
-				enum = "pushablebehavior";
+				title = "Tall Flower (Blue)";
+				sprite = "BSZ1C0";
 			}
-		}
-		1853
-		{
-			blocking = 2;
-			title = "Snowman (With Hat)";
-			sprite = "XMS3B0";
-			width = 16;
-			height = 80;
-			arg0
+			1403
 			{
-				title = "Push behavior";
-				type = 11;
-				enum = "pushablebehavior";
+				title = "Tall Flower (Cyan)";
+				sprite = "BSZ1D0";
+			}
+			1404
+			{
+				title = "Tall Flower (Yellow)";
+				sprite = "BSZ1E0";
+			}
+			1405
+			{
+				title = "Tall Flower (Orange)";
+				sprite = "BSZ1F0";
+			}
+			1410
+			{
+				title = "Medium Flower (Red)";
+				sprite = "BSZ2A0";
+			}
+			1411
+			{
+				title = "Medium Flower (Purple)";
+				sprite = "BSZ2B0";
+			}
+			1412
+			{
+				title = "Medium Flower (Blue)";
+				sprite = "BSZ2C0";
+			}
+			1413
+			{
+				title = "Medium Flower (Cyan)";
+				sprite = "BSZ2D0";
+			}
+			1414
+			{
+				title = "Medium Flower (Yellow)";
+				sprite = "BSZ2E0";
+			}
+			1415
+			{
+				title = "Medium Flower (Orange)";
+				sprite = "BSZ2F0";
+			}
+			1420
+			{
+				title = "Short Flower (Red)";
+				sprite = "BSZ3A0";
+			}
+			1421
+			{
+				title = "Short Flower (Purple)";
+				sprite = "BSZ3B0";
+			}
+			1422
+			{
+				title = "Short Flower (Blue)";
+				sprite = "BSZ3C0";
+			}
+			1423
+			{
+				title = "Short Flower (Cyan)";
+				sprite = "BSZ3D0";
+			}
+			1424
+			{
+				title = "Short Flower (Yellow)";
+				sprite = "BSZ3E0";
+			}
+			1425
+			{
+				title = "Short Flower (Orange)";
+				sprite = "BSZ3F0";
 			}
 		}
-		1854
+		
+		tulips
 		{
-			title = "Lamp Post";
-			sprite = "XMS4A0";
-			width = 8;
-			height = 120;
+			title = "Tulips";
+			1430
+			{
+				title = "Tulip (Red)";
+				sprite = "BST1A0";
+			}
+			1431
+			{
+				title = "Tulip (Purple)";
+				sprite = "BST2A0";
+			}
+			1432
+			{
+				title = "Tulip (Blue)";
+				sprite = "BST3A0";
+			}
+			1433
+			{
+				title = "Tulip (Cyan)";
+				sprite = "BST4A0";
+			}
+			1434
+			{
+				title = "Tulip (Yellow)";
+				sprite = "BST5A0";
+			}
+			1435
+			{
+				title = "Tulip (Orange)";
+				sprite = "BST6A0";
+			}
+			1440
+			{
+				title = "Cluster (Red)";
+				sprite = "BSZ5A0";
+			}
+			1441
+			{
+				title = "Cluster (Purple)";
+				sprite = "BSZ5B0";
+			}
+			1442
+			{
+				title = "Cluster (Blue)";
+				sprite = "BSZ5C0";
+			}
+			1443
+			{
+				title = "Cluster (Cyan)";
+				sprite = "BSZ5D0";
+			}
+			1444
+			{
+				title = "Cluster (Yellow)";
+				sprite = "BSZ5E0";
+			}
+			1445
+			{
+				title = "Cluster (Orange)";
+				sprite = "BSZ5F0";
+			}
 		}
-		1855
+		
+		bushes
 		{
-			title = "Lamp Post (Snow)";
-			sprite = "XMS4B0";
-			width = 8;
-			height = 120;
+			title = "Bushes";
+			1450
+			{
+				title = "Bush (Red)";
+				sprite = "BSZ6A0";
+			}
+			1451
+			{
+				title = "Bush (Purple)";
+				sprite = "BSZ6B0";
+			}
+			1452
+			{
+				title = "Bush (Blue)";
+				sprite = "BSZ6C0";
+			}
+			1453
+			{
+				title = "Bush (Cyan)";
+				sprite = "BSZ6D0";
+			}
+			1454
+			{
+				title = "Bush (Yellow)";
+				sprite = "BSZ6E0";
+			}
+			1455
+			{
+				title = "Bush (Orange)";
+				sprite = "BSZ6F0";
+			}
 		}
-		1856
+		
+		vines
 		{
-			title = "Hanging Star";
-			sprite = "XMS5A0";
-			width = 4;
-			height = 80;
-			hangs = 1;
+			title = "Vines";
+			1460
+			{
+				title = "Vine (Red)";
+				sprite = "BSZ7A0";
+			}
+			1461
+			{
+				title = "Vine (Purple)";
+				sprite = "BSZ7B0";
+			}
+			1462
+			{
+				title = "Vine (Blue)";
+				sprite = "BSZ7C0";
+			}
+			1463
+			{
+				title = "Vine (Cyan)";
+				sprite = "BSZ7D0";
+			}
+			1464
+			{
+				title = "Vine (Yellow)";
+				sprite = "BSZ7E0";
+			}
+			1465
+			{
+				title = "Vine (Orange)";
+				sprite = "BSZ7F0";
+			}
 		}
-		1857
+		1470
 		{
-			title = "Berry Bush (Snow)";
-			sprite = "BUS1B0";
-			width = 16;
-			height = 32;
+			title = "BSZ Shrub";
+			sprite = "BSZ8A0";
 		}
-		1858
+		1471
 		{
-			title = "Bush (Snow)";
-			sprite = "BUS2B0";
-			width = 16;
-			height = 32;
+			title = "BSZ Clover";
+			sprite = "BSZ8B0";
 		}
-		1859
+		1473
 		{
-			title = "Blueberry Bush (Snow)";
-			sprite = "BUS3B0";
+			title = "Palm Tree (Big)";
 			width = 16;
-			height = 32;
+			height = 160;
+			sprite = "BSZ8D0";
 		}
-		1875
+		1475
 		{
-			title = "Disco Ball";
-			sprite = "DBALA0";
+			title = "Palm Tree (Small)";
 			width = 16;
-			height = 54;
-			hangs = 1;
-		}
-		1876
-		{
-			arrow = 1;
-			blocking = 2;
-			title = "Eggman Disco Statue";
-			sprite = "ESTAB1";
-			width = 20;
-			height = 96;
-			arg0
-			{
-				title = "Push behavior";
-				type = 11;
-				enum = "pushablebehavior";
-			}
+			height = 80;
+			sprite = "BSZ8F0";
 		}
 	}
 
@@ -4820,188 +4945,245 @@ udmf
 		}
 	}
 
-	hauntedheights
+	christmasdisco
 	{
 		color = 2; // Green
-		title = "Haunted Heights";
+		title = "Christmas & Disco";
 
-		2000
+		1850
 		{
-			title = "Smashing Spikeball";
-			sprite = "FMCEA0";
-			width = 18;
-			height = 28;
+			title = "Christmas Pole";
+			sprite = "XMS1A0";
+			width = 16;
+			height = 40;
+		}
+		1851
+		{
+			title = "Candy Cane";
+			sprite = "XMS2A0";
+			width = 8;
+			height = 32;
+		}
+		1852
+		{
+			blocking = 2;
+			title = "Snowman";
+			sprite = "XMS3A0";
+			width = 16;
+			height = 64;
+			color = 6;
 			arg0
 			{
-				title = "Initial delay";
+				title = "Push behavior";
+				type = 11;
+				enum = "pushablebehavior";
 			}
 		}
-		2001
+		1853
 		{
-			title = "HHZ Grass";
-			sprite = "HHZMA0";
+			blocking = 2;
+			title = "Snowman (With Hat)";
+			sprite = "XMS3B0";
 			width = 16;
-			height = 40;
+			height = 80;
+			color = 6;
+			arg0
+			{
+				title = "Push behavior";
+				type = 11;
+				enum = "pushablebehavior";
+			}
 		}
-		2002
+		1854
 		{
-			title = "HHZ Tentacle 1";
-			sprite = "HHZMB0";
-			width = 16;
-			height = 40;
+			title = "Lamp Post";
+			sprite = "XMS4A0";
+			width = 8;
+			height = 120;
 		}
-		2003
+		1855
 		{
-			title = "HHZ Tentacle 2";
-			sprite = "HHZMC0";
-			width = 16;
-			height = 40;
+			title = "Lamp Post (Snow)";
+			sprite = "XMS4B0";
+			width = 8;
+			height = 120;
 		}
-		2004
+		1856
 		{
-			title = "HHZ Stalagmite (Tall)";
-			sprite = "HHZME0";
+			title = "Hanging Star";
+			sprite = "XMS5A0";
+			width = 4;
+			height = 80;
+			hangs = 1;
+		}
+		1857
+		{
+			title = "Berry Bush (Snow)";
+			sprite = "BUS1B0";
 			width = 16;
-			height = 40;
+			height = 32;
 		}
-		2005
+		1858
 		{
-			title = "HHZ Stalagmite (Short)";
-			sprite = "HHZMF0";
+			title = "Bush (Snow)";
+			sprite = "BUS2B0";
 			width = 16;
-			height = 40;
+			height = 32;
 		}
-		2006
+		1859
 		{
-			title = "Jack-o'-lantern 1";
-			sprite = "PUMKA0";
+			title = "Blueberry Bush (Snow)";
+			sprite = "BUS3B0";
 			width = 16;
-			height = 40;
-			arg0
-			{
-				title = "Flicker";
-				type = 11;
-				enum = "yesno";
-			}
+			height = 32;
 		}
-		2007
+		1875
 		{
-			title = "Jack-o'-lantern 2";
-			sprite = "PUMKB0";
+			title = "Disco Ball";
+			sprite = "DBALA0";
 			width = 16;
-			height = 40;
+			height = 54;
+			hangs = 1;
+		}
+		1876
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Eggman Disco Statue";
+			sprite = "ESTAB1";
+			width = 20;
+			height = 96;
+			color = 6;
 			arg0
 			{
-				title = "Flicker";
+				title = "Push behavior";
 				type = 11;
-				enum = "yesno";
+				enum = "pushablebehavior";
 			}
 		}
-		2008
+	}
+	
+	mario
+	{
+		color = 2; // Green
+		title = "Mario";
+
+		1800
 		{
-			title = "Jack-o'-lantern 3";
-			sprite = "PUMKC0";
+			title = "Coin";
+			sprite = "COINA0";
 			width = 16;
-			height = 40;
+			height = 24;
+			color = 14;
 			arg0
 			{
-				title = "Flicker";
+				title = "Float?";
+				tooltip = "This raises the object by 24 fracunits.";
 				type = 11;
 				enum = "yesno";
 			}
 		}
-		2009
-		{
-			title = "Purple Mushroom";
-			sprite = "SHRMD0";
-			width = 16;
-			height = 48;
-		}
-		2010
+		1801
 		{
-			title = "HHZ Tree";
-			sprite = "HHPLC0";
-			width = 12;
-			height = 40;
+			arrow = 1;
+			title = "Goomba";
+			sprite = "GOOMA0";
+			width = 24;
+			height = 32;
+			color = 9;
 		}
-	}
-
-	frozenhillside
-	{
-		color = 2; // Green
-		title = "Frozen Hillside";
-
-		2100
+		1802
 		{
-			title = "Ice Shard (Small)";
-			sprite = "FHZIA0";
-			width = 8;
+			arrow = 1;
+			title = "Goomba (Blue)";
+			sprite = "BGOMA0";
+			width = 24;
 			height = 32;
+			color = 9;
 		}
-		2101
+		1803
 		{
-			title = "Ice Shard (Large)";
-			sprite = "FHZIB0";
-			width = 8;
+			title = "Fire Flower";
+			sprite = "FFWRB0";
+			width = 16;
 			height = 32;
+			color = 14;
 		}
-		2102
+		1804
 		{
-			title = "Crystal Tree (Aqua)";
-			sprite = "TRE3A0";
-			width = 20;
-			height = 200;
+			title = "Koopa Shell";
+			sprite = "SHLLA1";
+			width = 16;
+			height = 20;
+			color = 11;
 		}
-		2103
+		1805
 		{
-			title = "Crystal Tree (Pink)";
-			sprite = "TRE3B0";
-			width = 20;
-			height = 200;
+			title = "Puma (Jumping Fireball)";
+			sprite = "PUMAA0";
+			width = 8;
+			height = 16;
+			color = 17;
+			arg0
+			{
+				title = "Jump strength";
+			}
 		}
-		2104
+		1806
 		{
-			title = "Amy Cameo";
-			sprite = "ROSYA1";
+			title = "King Bowser";
+			sprite = "KOOPA0";
 			width = 16;
 			height = 48;
+			color = 4;
 			arg0
 			{
-				title = "Grayscale?";
-				type = 11;
-				enum = "noyes";
+				title = "Death trigger tag";
+				type = 15;
 			}
 		}
-		2105
-		{
-			title = "Mistletoe";
-			sprite = "XMS6A0";
-			width = 52;
-			height = 106;
-		}
-	}
-
-	tutorial
-	{
-		color = 2; // Green
-		title = "Tutorial";
-
-		799
+		1807
 		{
-			title = "Tutorial Plant";
-			sprite = "TUPFH0";
-			width = 40;
-			height = 144;
+			title = "Axe";
+			sprite = "MAXEA0";
+			width = 8;
+			height = 16;
+			color = 11;
 			arg0
 			{
-				title = "Start frame";
+				title = "Death trigger tag";
+				type = 15;
 			}
 		}
+		1808
+		{
+			title = "Bush (Short)";
+			sprite = "MUS1A0";
+			width = 16;
+			height = 32;
+			color = 2;
+		}
+		1809
+		{
+			title = "Bush (Tall)";
+			sprite = "MUS2A0";
+			width = 16;
+			height = 32;
+			color = 2;
+		}
+		1810
+		{
+			title = "Toad";
+			sprite = "TOADA0";
+			width = 8;
+			height = 32;
+			color = 2;
+		}
 	}
 
 	flickies
 	{
-		color = 2; // Green
+		color = 6; // Brown
 		title = "Flickies";
 		width = 8;
 		height = 20;
diff --git a/extras/conf/udb/SRB2-22binary.cfg b/extras/conf/udb/SRB2-22binary.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3073b76c4570cb9a8f7f62a3e675cbffc7814614
--- /dev/null
+++ b/extras/conf/udb/SRB2-22binary.cfg
@@ -0,0 +1,7183 @@
+/*********************************************************\
+	Zone Builder Game Configuration
+	For Sonic Robo Blast 2 Version 2.2
+	Contributors (alphabetical):
+	* Foxboy
+	* FuriousFox
+	* JJames19119
+	* Kalaron
+	* Kristos
+	* MascaraSnake
+	* mazmazz
+	* Morpheus
+	* Neo Chaotikal
+	* Nev3r
+	* Oogaland
+	* Rob
+	* Shadow Hog
+	* sphere
+	* SRB2-Playah
+	* SSNTails
+	* SteelT
+	* ST218
+	* toaster
+	* Viola
+\*********************************************************/
+
+// This is required to prevent accidental use of a different configuration
+type = "Doom Builder 2 Game Configuration";
+
+// This is the title to show for this game
+game = "Sonic Robo Blast 2 - 2.2 (legacy)";
+
+//GZDB specific. Don't try to load lumps that don't exist.
+basegame = "doom";
+
+// This is the simplified game engine/sourceport name
+engine = "zdoom";
+
+// When this is set to true, sectors with the same tag will light up when a line is highlighted
+linetagindicatesectors = true;
+
+// The format interface handles the map data format - DoomMapSetIO for SRB2DB2, SRB2MapSetIO for Zone Builder
+formatinterface = "DoomMapSetIO";
+	
+//Maximum safe map size check (0 means skip check)
+safeboundary = 0;
+
+//Sky textures for vanilla maps
+defaultskytextures
+{
+	SKY1 = "MAP01,MAP02,MAP03,MAP33,MAP50,MAP60,MAPF0,MAPM0";
+	SKY2 = "MAPM7,MAPMB";
+	SKY4 = "MAP04,MAP06,MAP61,MAPF6,MAPM1";
+	SKY6 = "MAP05,MAP51,MAPMA";
+	SKY7 = "MAPM2,MAPM5";
+	SKY8 = "MAP07,MAP08,MAP09,MAP52,MAP62,MAPF1";
+	SKY10 = "MAP10,MAP12,MAP53,MAP63,MAPM3";
+	SKY11 = "MAP11,MAPF7";
+	SKY13 = "MAP13,MAP64";
+	SKY14 = "MAP14";
+	SKY15 = "MAP15,MAP54";
+	SKY17 = "MAP70";
+	SKY20 = "MAP32,MAP55,MAP65,MAPF2,MAPF5";
+	SKY21 = "MAPM4";
+	SKY22 = "MAP22,MAP23,MAP25,MAP26,MAP27,MAP56,MAP66,MAPF4,MAPM6";
+	SKY30 = "MAP30";
+	SKY31 = "MAP31";
+	SKY35 = "MAP42";
+	SKY40 = "MAP41,MAP71,MAPM9";
+	SKY55 = "MAPF3,MAPM8";
+	SKY68 = "MAPF8";
+	SKY99 = "MAP57,MAPZ0";
+	SKY159 = "MAP16";
+	SKY172 = "MAP40";
+	SKY300 = "MAP72";
+	SKY301 = "MAP73";
+}
+
+// Default lump name for new map
+defaultlumpname = "MAP01";
+
+// Default testing parameters
+testparameters = "-folder \"%AF\" -file \"%AA\" \"%F\" -warp %L";
+testshortpaths = true;
+
+// Default nodebuilder configurations
+defaultsavecompiler = "zennode_normal";
+defaulttestcompiler = "zennode_fast";
+
+// Skill levels
+skills
+{
+	1 = "Normal";
+}
+
+// Skins
+skins
+{
+	Sonic;
+	Tails;
+	Knuckles;
+	Amy;
+	Fang;
+	Metalsonic;
+}
+
+// Gametypes
+gametypes
+{
+	-1 = "Single Player";
+	0 = "Co-op";
+	1 = "Competition";
+	2 = "Race";
+	3 = "Match";
+	4 = "Team Match";
+	5 = "Tag";
+	6 = "Hide and Seek";
+	7 = "CTF";
+}
+
+// Special linedefs
+soundlinedefflag = 64;	// See linedefflags
+singlesidedflag = 1;	// See linedefflags
+doublesidedflag = 4;	// See linedefflags
+impassableflag = 1;
+upperunpeggedflag = 8;
+lowerunpeggedflag = 16;
+repeatmidtextureflag = 1024;
+pegmidtextureflag = 256;
+
+// Generalized actions
+generalizedlinedefs = false;
+generalizedsectors = true;
+
+// Texture loading options
+defaultwalltexture = "GFZROCK";
+defaultfloortexture = "GFZFLR01";
+defaultceilingtexture = "F_SKY1";
+mixtexturesflats = true;
+defaulttexturescale = 1.0f;
+defaultflatscale = 1.0f;
+
+// Thing number for start position in 3D Mode
+start3dmode = 3328;
+
+
+
+
+/*
+TEXTURES AND FLAT SOURCES
+This tells Doom Builder where to find the information for textures
+and flats in the IWAD file, Addition WAD file and Map WAD file.
+
+Start and end lumps must be given in a structure (of which the
+key name doesnt matter) and any textures or flats in between them
+are loaded in either the textures category or flats category.
+
+For textures: PNAMES, TEXTURE1 and TEXTURE2 are loaded by default.
+Kalaron: and now TX_START
+*/
+
+// Texture sources
+textures
+{
+	zdoom1
+	{
+		start = "TX_START";
+		end = "TX_END";
+	}
+}
+
+// Patch sources
+patches
+{
+	standard1
+	{
+		start = "P_START";
+		end = "P_END";
+	}
+
+	standard2
+	{
+		start = "PP_START";
+		end = "PP_END";
+	}
+}
+
+// Sprite sources
+sprites
+{
+	standard1
+	{
+		start = "S_START";
+		end = "S_END";
+	}
+
+	standard2
+	{
+		start = "SS_START";
+		end = "SS_END";
+	}
+}
+
+// Flat sources
+flats
+{
+	standard1
+	{
+		start = "F_START";
+		end = "F_END";
+	}
+
+	standard2
+	{
+		start = "FF_START";
+		end = "FF_END";
+	}
+
+	standard3
+	{
+		start = "FF_START";
+		end = "F_END";
+	}
+
+	standard4
+	{
+		start = "F_START";
+		end = "FF_END";
+	}
+}
+
+
+/*
+GAME DETECT PATTERN
+Used to guess the game for which a WAD file is made.
+
+1 = One of these lumps must exist
+2 = None of these lumps must exist
+3 = All of these lumps must exist
+*/
+
+gamedetect
+{
+	EXTENDED = 2;
+
+
+	BEHAVIOR = 2;
+
+	E#M# = 2;
+
+	MAP?? = 1;
+}
+
+
+/*
+MAP LUMP NAMES
+Map lumps are loaded with the map as long as they are right after each other. When the editor
+meets a lump which is not defined in this list it will ignore the map if not satisfied.
+The order of items defines the order in which lumps will be written to WAD file on save.
+To indicate the map header lump, use ~MAP
+
+Legenda:
+required = Lump is required to exist.
+blindcopy = Lump will be copied along with the map blindly. (usefull for lumps Doom Builder doesn't use)
+nodebuild = The nodebuilder generates this lump.
+allowempty = The nodebuilder is allowed to leave this lump empty.
+script = This lump is a text-based script. Specify the filename of the script configuration to use.
+*/
+
+maplumpnames
+{
+	~MAP
+	{
+		required = true;
+		blindcopy = true;
+		nodebuild = false;
+	}
+
+	THINGS
+	{
+		required = true;
+		nodebuild = true;
+		allowempty = true;
+	}
+
+	LINEDEFS
+	{
+		required = true;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	SIDEDEFS
+	{
+		required = true;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	VERTEXES
+	{
+		required = true;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	SEGS
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	SSECTORS
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	NODES
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	SECTORS
+	{
+		required = true;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	REJECT
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	BLOCKMAP
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = true;
+	}
+}
+
+scriptlumpnames
+{
+	MAINCFG
+	{
+		script = "SOC.cfg";
+	}
+
+	OBJCTCFG
+	{
+		script = "SOC.cfg";
+	}
+
+	SOC_
+	{
+		script = "SOC.cfg";
+		isprefix = true;
+	}
+
+	LUA_
+	{
+		script = "Lua.cfg";
+		isprefix = true;
+	}
+}
+
+// DEFAULT SECTOR BRIGHTNESS LEVELS
+sectorbrightness
+{
+	255;
+	248;
+	240;
+	232;
+	224;
+	216;
+	208;
+	200;
+	192;
+	184;
+	176;
+	168;
+	160;
+	152;
+	144;
+	136;
+	128;
+	120;
+	112;
+	104;
+	96;
+	88;
+	80;
+	72;
+	64;
+	56;
+	48;
+	40;
+	32;
+	24;
+	16;
+	8;
+	0;
+}
+
+// SECTOR TYPES-----------------------------------------------------------------
+sectortypes
+{
+	0 = "Normal";
+	1 = "Damage";
+	2 = "Damage (Water)";
+	3 = "Damage (Fire)";
+	4 = "Damage (Electrical)";
+	5 = "Spikes";
+	6 = "Death Pit (Camera Tilt)";
+	7 = "Death Pit (No Camera Tilt)";
+	8 = "Instant Kill";
+	9 = "Ring Drainer (Floor Touch)";
+	10 = "Ring Drainer (Anywhere in Sector)";
+	11 = "Special Stage Damage";
+	12 = "Space Countdown";
+	13 = "Ramp Sector (double step-up/down)";
+	14 = "Non-Ramp Sector (no step-down)";
+	15 = "Bouncy FOF <deprecated>";
+	16 = "Trigger Line Ex. (Pushable Objects)";
+	32 = "Trigger Line Ex. (Anywhere, All Players)";
+	48 = "Trigger Line Ex. (Floor Touch, All Players)";
+	64 = "Trigger Line Ex. (Anywhere in Sector)";
+	80 = "Trigger Line Ex. (Floor Touch)";
+	96 = "Trigger Line Ex. (Emerald Check) <deprecated>";
+	112 = "Trigger Line Ex. (NiGHTS Mare) <deprecated>";
+	128 = "Check for Linedef Executor on FOFs";
+	144 = "Egg Capsule";
+	160 = "Special Stage Time/Spheres Parameters <deprecated>";
+	176 = "Custom Global Gravity <deprecated>";
+	512 = "Wind/Current <deprecated>";
+	1024 = "Conveyor Belt <deprecated>";
+	1280 = "Speed Pad";
+	1536 = "Flip Gravity on Jump";
+	4096 = "Star Post Activator";
+	8192 = "Exit/Special Stage Pit/Return Flag";
+	12288 = "CTF Red Team Base";
+	16384 = "CTF Blue Team Base";
+	20480 = "Fan Sector";
+	24576 = "Super Sonic Transform";
+	28672 = "Force Spin";
+	32768 = "Zoom Tube Start";
+	36864 = "Zoom Tube End";
+	40960 = "Circuit Finish Line";
+	45056 = "Rope Hang";
+	49152 = "Intangible to the Camera";
+}
+
+
+// GENERALISED SECTOR TYPES-----------------------------------------------------------------
+gen_sectortypes
+{
+	first
+	{
+		0 = "Normal";
+		1 = "Damage";
+		2 = "Damage (Water)";
+		3 = "Damage (Fire)";
+		4 = "Damage (Electrical)";
+		5 = "Spikes";
+		6 = "Death Pit (Camera Tilt)";
+		7 = "Death Pit (No Camera Tilt)";
+		8 = "Instant Kill";
+		9 = "Ring Drainer (Floor Touch)";
+		10 = "Ring Drainer (Anywhere in Sector)";
+		11 = "Special Stage Damage";
+		12 = "Space Countdown";
+		13 = "Ramp Sector (double step-up/down)";
+		14 = "Non-Ramp Sector (no step-down)";
+		15 = "Bouncy FOF <deprecated>";
+	}
+
+	second
+	{
+		0 = "Normal";
+		16 = "Trigger Line Ex. (Pushable Objects)";
+		32 = "Trigger Line Ex. (Anywhere, All Players)";
+		48 = "Trigger Line Ex. (Floor Touch, All Players)";
+		64 = "Trigger Line Ex. (Anywhere in Sector)";
+		80 = "Trigger Line Ex. (Floor Touch)";
+		96 = "Trigger Line Ex. (Emerald Check) <deprecated>";
+		112 = "Trigger Line Ex. (NiGHTS Mare) <deprecated>";
+		128 = "Check for Linedef Executor on FOFs";
+		144 = "Egg Capsule";
+		160 = "Special Stage Time/Spheres Parameters <deprecated>";
+		176 = "Custom Global Gravity <deprecated>";
+	}
+
+	third
+	{
+		0 = "Normal";
+		512 = "Wind/Current <deprecated>";
+		1024 = "Conveyor Belt <deprecated>";		 
+		1280 = "Speed Pad";
+		1536 = "Flip Gravity on Jump";
+	}
+
+	fourth
+	{
+		0 = "Normal";
+		4096 = "Star Post Activator";
+		8192 = "Exit/Special Stage Pit/Return Flag";
+		12288 = "CTF Red Team Base";
+		16384 = "CTF Blue Team Base";
+		20480 = "Fan Sector";
+		24576 = "Super Sonic Transform";
+		28672 = "Force Spin";
+		32768 = "Zoom Tube Start";
+		36864 = "Zoom Tube End";
+		40960 = "Circuit Finish Line";
+		45056 = "Rope Hang";
+		49152 = "Intangible to the Camera";
+	}
+}
+
+// LINEDEF FLAGS
+linedefflags
+{
+	1 = "[0] Impassable";
+	2 = "[1] Block Enemies";
+	4 = "[2] Double-Sided";
+	8 = "[3] Upper Unpegged";
+	16 = "[4] Lower Unpegged";
+	32 = "[5] Slope Skew (E1)";
+	64 = "[6] Not Climbable";
+	128 = "[7] No Midtexture Skew (E2)";
+	256 = "[8] Peg Midtexture (E3)";
+	512 = "[9] Solid Midtexture (E4)";
+	1024 = "[10] Repeat Midtexture (E5)";
+	2048 = "[11] Netgame Only";
+	4096 = "[12] No Netgame";
+	8192 = "[13] Effect 6";
+	16384 = "[14] Bouncy Wall";
+	32768 = "[15] Transfer Line";
+}
+
+// Linedef flags UDMF translation table
+// This is needed for copy/paste and prefabs to work properly
+// When the UDMF field name is prefixed with ! it is inverted
+linedefflagstranslation
+{
+	1 = "blocking";
+	2 = "blockmonsters";
+	4 = "twosided";
+	8 = "dontpegtop";
+	16 = "dontpegbottom";
+	32 = "secret";
+	64 = "blocksound";
+	128 = "dontdraw";
+	256 = "mapped";
+}
+
+// LINEDEF ACTIVATIONS
+linedefactivations
+{
+}
+
+// LINEDEF TYPES
+linedeftypes
+{
+	misc
+	{
+		title = "Miscellaneous";
+
+		0
+		{
+			title = "None";
+			prefix = "(0)";
+		}
+
+		1
+		{
+			title = "Per-Sector Gravity";
+			prefix = "(1)";
+			flags64text = "[6] Flip in reverse gravity";
+			flags8192text = "[13] Cancel MF2_OBJECTFLIP";
+		}
+
+		5
+		{
+			title = "Camera Scanner";
+			prefix = "(5)";
+		}
+
+		7
+		{
+			title = "Sector Flat Alignment";
+			prefix = "(7)";
+			flags2048text = "[11] Don't align floor";
+			flags4096text = "[12] Don't align ceiling";
+			flags8192text = "[13] Use texture offsets";
+		}
+
+		10
+		{
+			title = "Culling Plane";
+			prefix = "(10)";
+			flags64text = "[6] Cull only while in sector";
+		}
+
+		13
+		{
+			title = "Heat Wave Effect";
+			prefix = "(13)";
+		}
+
+	    40
+		{
+			title = "Visual Portal Between Tagged Linedefs";
+			prefix = "(40)";
+		}
+
+	    41
+		{
+			title = "Horizon Effect";
+			prefix = "(41)";
+		}
+
+		50
+		{
+			title = "Instantly Lower Floor on Level Load";
+			prefix = "(50)";
+		}
+
+		51
+		{
+			title = "Instantly Raise Ceiling on Level Load";
+			prefix = "(51)";
+		}
+
+		63
+		{
+			title = "Fake Floor/Ceiling Planes";
+			prefix = "(63)";
+		}
+
+		96
+		{
+			title = "Add Front Sector Tag to Tagged Sectors";
+			prefix = "(96)";
+			flags1024text = "[10] Offsets are target tags";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
+		}
+
+		97
+		{
+			title = "Add Tag to Front Sector";
+			prefix = "(97)";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
+		}
+
+		98
+		{
+			title = "Add Tag to Back Sector";
+			prefix = "(98)";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
+		}
+
+		99
+		{
+			title = "Add Tag to Front and Back Sectors";
+			prefix = "(99)";
+			flags8192text = "[13] Add front side offsets";
+			flags32768text = "[15] Add back side offsets";
+		}
+
+		540
+		{
+			title = "Floor Friction";
+			prefix = "(540)";
+		}
+	}
+
+	parameters
+	{
+		title = "Parameters";
+
+		2
+		{
+			title = "Custom Exit";
+			prefix = "(2)";
+			flags2text = "[1] Check emeralds";
+			flags64text = "[6] Skip score tally";
+		}
+
+		3
+		{
+			title = "Zoom Tube Parameters";
+			prefix = "(3)";
+			flags512text = "[9] Ignore player direction";
+		}
+
+		4
+		{
+			title = "Speed Pad Parameters";
+			prefix = "(4)";
+			flags512text = "[9] No teleport to center";
+			flags1024text = "[10] Force spinning frames";
+		}
+
+		8
+		{
+			title = "Special Sector Properties";
+			prefix = "(8)";
+			flags32text = "[5] Invert precipitation";
+			flags64text = "[6] Touch only ceiling";
+			flags128text = "[7] Allow opposite gravity";
+			flags256text = "[8] Touch sector edge";
+			flags512text = "[9] Touch floor or ceiling";
+		}
+
+		9
+		{
+			title = "Chain Parameters";
+			prefix = "(9)";
+			flags32text = "[5] Swing instead of spin";
+			flags128text = "[7] Make chain from end item";
+			flags64text = "[6] Player-turnable chain";
+			flags256text = "[8] Spawn link at origin";
+			flags512text = "[9] Don't clip inside ground";
+			flags1024text = "[10] No distance check";
+		}
+
+		11
+		{
+			title = "Rope Hang Parameters";
+			prefix = "(11)";
+			flags32text = "[5] Don't loop";
+			flags64text = "[6] Static";
+		}
+
+		12
+		{
+			title = "Rock Spawner Parameters";
+			prefix = "(12)";
+			flags64text = "[6] Randomize speed";
+		}
+
+		14
+		{
+			title = "Bustable Block Parameters";
+			prefix = "(14)";
+			flags32text = "[5] Particles launch from center";
+		}
+
+		15
+		{
+			title = "Fan Particle Spawner Parameters";
+			prefix = "(15)";
+		}
+
+		16
+		{
+			title = "Minecart Parameters";
+			prefix = "(16)";
+		}
+
+		64
+		{
+			title = "Continuously Appearing/Disappearing FOF";
+			prefix = "(64)";
+			flags2text = "[1] Use control sector tag";
+			flags64text = "[6] No sound effect";
+		}
+
+		76
+		{
+			title = "Make FOF Bouncy";
+			prefix = "(76)";
+		}
+	}
+
+	polyobject
+	{
+		title = "PolyObject";
+
+		20
+		{
+			title = "PolyObject First Line";
+			prefix = "(20)";
+		}
+
+		22
+		{
+			title = "PolyObject Parameters";
+			prefix = "(22)";
+			flags8text = "[3] Set translucency by X offset";
+			flags32text = "[5] Render outer sides only";
+			flags64text = "[6] Trigger linedef executor";
+			flags128text = "[7] Intangible";
+			flags256text = "[8] Stopped by pushables";
+			flags512text = "[9] Render flats";
+			flags8192text = "[13] Cut cyan flat pixels";
+		}
+
+		30
+		{
+			title = "PolyObject Waving Flag";
+			prefix = "(30)";
+		}
+
+		31
+		{
+			title = "Move PolyObject by Front Sector Displacement";
+			prefix = "(31)";
+		}
+
+		32
+		{
+			title = "Rotate PolyObject by Front Sector Displacement";
+			prefix = "(32)";
+			flags64text = "[6] Don't turn players";
+			flags512text = "[9] Turn all objects";
+		}
+	}
+
+	planemove
+	{
+		title = "Plane Movement";
+
+		52
+		{
+			title = "Continuously Falling Sector";
+			prefix = "(52)";
+			flags64text = "[6] Continuously rising";
+		}
+
+		53
+		{
+			title = "Continuous Floor/Ceiling Mover";
+			prefix = "(53)";
+		}
+
+		54
+		{
+			title = "Continuous Floor Mover";
+			prefix = "(54)";
+		}
+
+		55
+		{
+			title = "Continuous Ceiling Mover";
+			prefix = "(55)";
+		}
+
+		56
+		{
+			title = "Continuous Two-Speed Floor/Ceiling Mover";
+			prefix = "(56)";
+		}
+
+		57
+		{
+			title = "Continuous Two-Speed Floor Mover";
+			prefix = "(57)";
+		}
+
+		58
+		{
+			title = "Continuous Two-Speed Ceiling Mover";
+			prefix = "(58)";
+		}
+
+		59
+		{
+			title = "Activate Moving Platform";
+			prefix = "(59)";
+			flags64text = "[6] Move upwards at start";
+		}
+
+		60
+		{
+			title = "Activate Moving Platform (Adjustable Speed)";
+			prefix = "(60)";
+			flags64text = "[6] Move upwards at start";
+		}
+
+		61
+		{
+			title = "Crusher (Ceiling to Floor)";
+			prefix = "(61)";
+			flags512text = "[9] Double, constant speed";
+		}
+
+		62
+		{
+			title = "Crusher (Floor to Ceiling)";
+			prefix = "(62)";
+			flags512text = "[9] Double, constant speed";
+		}
+
+		66
+		{
+			title = "Move Floor by Displacement";
+			prefix = "(66)";
+			flags64text = "[6] Inverse movement";
+		}
+
+		67
+		{
+			title = "Move Ceiling by Displacement";
+			prefix = "(67)";
+			flags64text = "[6] Inverse movement";
+		}
+
+		68
+		{
+			title = "Move Floor and Ceiling by Displacement";
+			prefix = "(68)";
+			flags64text = "[6] Inverse movement";
+		}
+	}
+
+	fofsolid
+	{
+		title = "FOF (solid)";
+
+		100
+		{
+			title = "Solid, Opaque";
+			prefix = "(100)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		101
+		{
+			title = "Solid, Opaque, No Shadow";
+			prefix = "(101)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1DF";
+		}
+
+		102
+		{
+			title = "Solid, Translucent";
+			prefix = "(102)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Render insides";
+			flags128text = "[7] Only block non-players";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "195F";
+			flags643dfloorflagsadd = "7C80";
+		}
+
+		103
+		{
+			title = "Solid, Sides Only";
+			prefix = "(103)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1CF";
+		}
+
+		104
+		{
+			title = "Solid, No Sides";
+			prefix = "(104)";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1D7";
+			flags643dfloorflagsremove = "40";
+		}
+
+		105
+		{
+			title = "Solid, Invisible";
+			prefix = "(105)";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "47";
+			invisiblefof = true;
+		}
+
+		140
+		{
+			title = "Intangible from Bottom, Opaque";
+			prefix = "(140)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "200841F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		141
+		{
+			title = "Intangible from Bottom, Translucent";
+			prefix = "(141)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Render insides/block non-plr";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "200191F";
+			flags1283dfloorflagsadd = "7C80";
+			flags643dfloorflagsadd = "40";
+		}
+
+		142
+		{
+			title = "Intangible from Bottom, Translucent, No Sides";
+			prefix = "(142)";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Render insides/block non-plr";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "2001917";
+			flags1283dfloorflagsadd = "7C80";
+			flags643dfloorflagsadd = "40";
+		}
+
+		143
+		{
+			title = "Intangible from Top, Opaque";
+			prefix = "(143)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "400841F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		144
+		{
+			title = "Intangible from Top, Translucent";
+			prefix = "(144)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Render insides/block non-plr";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "400191F";
+			flags1283dfloorflagsadd = "7C80";
+			flags643dfloorflagsadd = "40";
+		}
+
+		145
+		{
+			title = "Intangible from Top, Translucent, No Sides";
+			prefix = "(145)";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Render insides/block non-plr";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "4001917";
+			flags1283dfloorflagsadd = "7C80";
+			flags643dfloorflagsadd = "40";
+		}
+
+		146
+		{
+			title = "Only Tangible from Sides";
+			prefix = "(146)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "600800F";
+		}
+	}
+
+	fofintangible
+	{
+		title = "FOF (intangible)";
+
+		120
+		{
+			title = "Water, Opaque";
+			prefix = "(120)";
+			flags2text = "[1] Make lava intangible";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			3dfloor = true;
+			3dfloorflags = "8F39";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		121
+		{
+			title = "Water, Translucent";
+			prefix = "(121)";
+			flags2text = "[1] Make lava intangible";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "9F39";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		122
+		{
+			title = "Water, Opaque, No Sides";
+			prefix = "(122)";
+			flags2text = "[1] Make lava intangible";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			3dfloor = true;
+			3dfloorflags = "F31";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		123
+		{
+			title = "Water, Translucent, No Sides";
+			prefix = "(123)";
+			flags2text = "[1] Make lava intangible";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "1F31";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		124
+		{
+			title = "Goo Water, Translucent";
+			prefix = "(124)";
+			flags2text = "[1] Make lava intangible";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "209F39";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		125
+		{
+			title = "Goo Water, Translucent, No Sides";
+			prefix = "(125)";
+			flags2text = "[1] Make lava intangible";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "201F31";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		220
+		{
+			title = "Intangible, Opaque";
+			prefix = "(220)";
+			flags8text = "[3] Slope skew sides";
+			3dfloor = true;
+			3dfloorflags = "8F19";
+		}
+
+		221
+		{
+			title = "Intangible, Translucent";
+			prefix = "(221)";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Cast shadow";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "1B59";
+			flags643dfloorflagsremove = "40";
+		}
+
+		222
+		{
+			title = "Intangible, Sides Only";
+			prefix = "(222)";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Cast shadow";
+			3dfloor = true;
+			3dfloorflags = "8249";
+			flags643dfloorflagsremove = "240";
+		}
+
+		223
+		{
+			title = "Intangible, Invisible";
+			prefix = "(223)";
+			3dfloor = true;
+			3dfloorflags = "41";
+			invisiblefof = true;
+		}
+	}
+
+	fofmoving
+	{
+		title = "FOF (moving)";
+
+		150
+		{
+			title = "Air Bobbing";
+			prefix = "(150)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		151
+		{
+			title = "Air Bobbing (Adjustable)";
+			prefix = "(151)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		152
+		{
+			title = "Reverse Air Bobbing (Adjustable)";
+			prefix = "(152)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		153
+		{
+			title = "Dynamically Sinking Platform";
+			prefix = "(153)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		160
+		{
+			title = "Water Bobbing";
+			prefix = "(160)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "4019F";
+		}
+
+		190
+		{
+			title = "Rising Platform, Solid, Opaque";
+			prefix = "(190)";
+			flags2text = "[1] Sink when stepped on";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		191
+		{
+			title = "Rising Platform, Solid, Opaque, No Shadow";
+			prefix = "(191)";
+			flags2text = "[1] Sink when stepped on";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1DF";
+		}
+
+		192
+		{
+			title = "Rising Platform, Solid, Translucent";
+			prefix = "(192)";
+			flags2text = "[1] Sink when stepped on";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "195F";
+		}
+
+		193
+		{
+			title = "Rising Platform, Solid, Invisible";
+			prefix = "(193)";
+			flags2text = "[1] Sink when stepped on";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "47";
+		}
+
+		194
+		{
+			title = "Rising Platform, Intangible from Bottom, Opaque";
+			prefix = "(194)";
+			flags2text = "[1] Sink when stepped on";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash, no shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "200841F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		195
+		{
+			title = "Rising Platform, Intangible from Bottom, Translucent";
+			prefix = "(195)";
+			flags2text = "[1] Sink when stepped on";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash, no shadow";
+			flags128text = "[7] Only block non-players";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "2009D1F";
+			flags643dfloorflagsadd = "40";
+		}
+	}
+
+	fofcrumbling
+	{
+		title = "FOF (crumbling)";
+
+		170
+		{
+			title = "Crumbling, Respawn";
+			prefix = "(170)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "10019F";
+		}
+
+		171
+		{
+			title = "Crumbling, No Respawn";
+			prefix = "(171)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "80019F";
+		}
+
+		172
+		{
+			title = "Crumbling, Respawn, Intangible from Bottom";
+			prefix = "(172)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "210841F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		173
+		{
+			title = "Crumbling, No Respawn, Intangible from Bottom";
+			prefix = "(173)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "218841F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		174
+		{
+			title = "Crumbling, Respawn, Int. from Bottom, Translucent";
+			prefix = "(174)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "210959F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		175
+		{
+			title = "Crumbling, No Respawn, Int. from Bottom, Translucent";
+			prefix = "(175)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "218959F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		176
+		{
+			title = "Crumbling, Respawn, Floating, Bobbing";
+			prefix = "(176)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "14019F";
+		}
+
+		177
+		{
+			title = "Crumbling, No Respawn, Floating, Bobbing";
+			prefix = "(177)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1C019F";
+		}
+
+		178
+		{
+			title = "Crumbling, Respawn, Floating";
+			prefix = "(178)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "14019F";
+		}
+
+		179
+		{
+			title = "Crumbling, No Respawn, Floating";
+			prefix = "(179)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1C019F";
+		}
+
+		180
+		{
+			title = "Crumbling, Respawn, Air Bobbing";
+			prefix = "(180)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "10019F";
+		}
+	}
+
+	fofspecial
+	{
+		title = "FOF (special)";
+
+		200
+		{
+			title = "Light Block";
+			prefix = "(200)";
+			3dfloor = true;
+			3dfloorflags = "20201";
+			invisiblefof = true;
+		}
+
+		201
+		{
+			title = "Half Light Block";
+			prefix = "(201)";
+			3dfloor = true;
+			3dfloorflags = "201";
+			invisiblefof = true;
+		}
+
+		202
+		{
+			title = "Fog Block";
+			prefix = "(202)";
+			3dfloor = true;
+			3dfloorflags = "3EF01";
+			invisiblefof = true;
+		}
+
+		250
+		{
+			title = "Mario Block";
+			prefix = "(250)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Invisible block";
+			flags64text = "[6] Brick block";
+			3dfloor = true;
+			3dfloorflags = "40019F";
+			flags323dfloorflagsremove = "19E";
+			flags643dfloorflagsadd = "200000";
+		}
+
+		251
+		{
+			title = "Thwomp Block";
+			prefix = "(251)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			flags512text = "[9] Custom crushing sound";
+			flags1024text = "[10] Custom speed";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		252
+		{
+			title = "Shatter Block";
+			prefix = "(252)";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Shatter only from below";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			3dfloor = true;
+			3dfloorflags = "880001D";
+			flags643dfloorflagsadd = "200002";
+		}
+
+		253
+		{
+			title = "Shatter Block, Translucent";
+			prefix = "(253)";
+			flags8text = "[3] Slope skew sides";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "880101D";
+		}
+
+		254
+		{
+			title = "Bustable Block";
+			prefix = "(254)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Strong characters only";
+			flags128text = "[7] Only block non-players";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			3dfloor = true;
+			3dfloorflags = "80001F";
+			flags643dfloorflagsadd = "20000000";
+		}
+
+		255
+		{
+			title = "Spin-Bustable Block";
+			prefix = "(255)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			3dfloor = true;
+			3dfloorflags = "1080001F";
+		}
+
+		256
+		{
+			title = "Spin-Bustable Block, Translucent";
+			prefix = "(256)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "1080101F";
+		}
+
+		257
+		{
+			title = "Quicksand";
+			prefix = "(257)";
+			flags8text = "[3] Slope skew sides";
+			flags1024text = "[10] Ripple effect";
+			3dfloor = true;
+			3dfloorflags = "1008219";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		258
+		{
+			title = "Laser";
+			prefix = "(258)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Don't damage bosses";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorflags = "959";
+		}
+
+		259
+		{
+			title = "Custom FOF";
+			prefix = "(259)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			flags8192text = "[13] Cut cyan flat pixels";
+			3dfloor = true;
+			3dfloorcustom = true;
+		}
+	}
+
+	linedeftrigger
+	{
+		title = "Linedef Executor Trigger";
+
+		300
+		{
+			title = "Continuous";
+			prefix = "(300)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		301
+		{
+			title = "Each Time";
+			prefix = "(301)";
+			flags1024text = "[10] Use faster, unordered execution";
+			flags16384text = "[14] Also trigger on exit";
+		}
+
+		302
+		{
+			title = "Once";
+			prefix = "(302)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		303
+		{
+			title = "Ring Count - Continuous";
+			prefix = "(303)";
+			flags2text = "[1] Rings greater or equal";
+			flags64text = "[6] Rings less or equal";
+			flags512text = "[9] Consider all players";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		304
+		{
+			title = "Ring Count - Once";
+			prefix = "(304)";
+			flags2text = "[1] Rings greater or equal";
+			flags64text = "[6] Rings less or equal";
+			flags512text = "[9] Consider all players";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		305
+		{
+			title = "Character Ability - Continuous";
+			prefix = "(305)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		306
+		{
+			title = "Character Ability - Each Time";
+			prefix = "(306)";
+			flags1024text = "[10] Use faster, unordered execution";
+			flags16384text = "[14] Also trigger on exit";
+		}
+
+		307
+		{
+			title = "Character Ability - Once";
+			prefix = "(307)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		308
+		{
+			title = "Race Only - Once";
+			prefix = "(308)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		309
+		{
+			title = "CTF Red Team - Continuous";
+			prefix = "(309)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		310
+		{
+			title = "CTF Red Team - Each Time";
+			prefix = "(310)";
+			flags1024text = "[10] Use faster, unordered execution";
+			flags16384text = "[14] Also trigger on exit";
+		}
+
+		311
+		{
+			title = "CTF Blue Team - Continuous";
+			prefix = "(311)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		312
+		{
+			title = "CTF Blue Team - Each Time";
+			prefix = "(312)";
+			flags1024text = "[10] Use faster, unordered execution";
+			flags16384text = "[14] Also trigger on exit";
+		}
+
+		313
+		{
+			title = "No More Enemies - Once";
+			prefix = "(313)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		314
+		{
+			title = "Number of Pushables - Continuous";
+			prefix = "(314)";
+			flags64text = "[6] Number greater or equal";
+			flags512text = "[9] Number less";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		315
+		{
+			title = "Number of Pushables - Once";
+			prefix = "(315)";
+			flags64text = "[6] Number greater or equal";
+			flags512text = "[9] Number less";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		317
+		{
+			title = "Condition Set Trigger - Continuous";
+			prefix = "(317)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		318
+		{
+			title = "Condition Set Trigger - Once";
+			prefix = "(318)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		319
+		{
+			title = "Unlockable - Continuous";
+			prefix = "(319)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		320
+		{
+			title = "Unlockable - Once";
+			prefix = "(320)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		321
+		{
+			title = "Trigger After X Calls - Continuous";
+			prefix = "(321)";
+			flags64text = "[6] Trigger more than once";
+			flags1024text = "[10] Use faster, unordered execution";
+
+		}
+
+		322
+		{
+			title = "Trigger After X Calls - Each Time";
+			prefix = "(322)";
+			flags64text = "[6] Trigger more than once";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		323
+		{
+			title = "NiGHTSerize - Each Time";
+			prefix = "(323)";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run only if player is NiGHTS";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags1024text = "[10] Use faster, unordered execution";
+			flags16384text = "[14] Run if no more mares";
+			flags32768text = "[15] Run if player is not NiGHTS";
+		}
+
+		324
+		{
+			title = "NiGHTSerize - Once";
+			prefix = "(324)";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run only if player is NiGHTS";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags1024text = "[10] Use faster, unordered execution";
+			flags16384text = "[14] Run if no more mares";
+			flags32768text = "[15] Run if player is not NiGHTS";
+		}
+
+		325
+		{
+			title = "De-NiGHTSerize - Each Time";
+			prefix = "(325)";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run if anyone is NiGHTS";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags1024text = "[10] Use faster, unordered execution";
+			flags32768text = "[15] Run if no one is NiGHTS";
+		}
+
+		326
+		{
+			title = "De-NiGHTSerize - Once";
+			prefix = "(326)";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run if anyone is NiGHTS";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags1024text = "[10] Use faster, unordered execution";
+			flags32768text = "[15] Run if no one is NiGHTS";
+		}
+
+		327
+		{
+			title = "NiGHTS Lap - Each Time";
+			prefix = "(327)";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		328
+		{
+			title = "NiGHTS Lap - Once";
+			prefix = "(328)";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		329
+		{
+			title = "Ideya Capture Touch - Each Time";
+			prefix = "(329)";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run regardless of spheres";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags1024text = "[10] Use faster, unordered execution";
+			flags16384text = "[14] Only if not enough spheres";
+			flags32768text = "[15] Run when entering Capture";
+		}
+
+		330
+		{
+			title = "Ideya Capture Touch - Once";
+			prefix = "(330)";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run regardless of spheres";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags1024text = "[10] Use faster, unordered execution";
+			flags16384text = "[14] Only if not enough spheres";
+			flags32768text = "[15] Run when entering Capture";
+		}
+
+		331
+		{
+			title = "Player Skin - Continuous";
+			prefix = "(331)";
+			flags64text = "[6] Disable for this skin";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		332
+		{
+			title = "Player Skin - Each Time";
+			prefix = "(332)";
+			flags64text = "[6] Disable for this skin";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		333
+		{
+			title = "Player Skin - Once";
+			prefix = "(333)";
+			flags64text = "[6] Disable for this skin";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		334
+		{
+			title = "Object Dye - Continuous";
+			prefix = "(334)";
+			flags64text = "[6] Disable for this color";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		335
+		{
+			title = "Object Dye - Each Time";
+			prefix = "(335)";
+			flags64text = "[6] Disable for this color";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		336
+		{
+			title = "Object Dye - Once";
+			prefix = "(336)";
+			flags64text = "[6] Disable for this color";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+
+		337
+		{
+			title = "Emerald Check - Continuous";
+			prefix = "(337)";
+		}
+
+		338
+		{
+			title = "Emerald Check - Each Time";
+			prefix = "(338)";
+		}
+
+		339
+		{
+			title = "Emerald Check - Once";
+			prefix = "(339)";
+		}
+
+		340
+		{
+			title = "NiGHTS Mare - Continuous";
+			flags2text = "[1] Mare greater or equal";
+			flags64text = "[6] Mare less or equal";
+			prefix = "(340)";
+		}
+
+		341
+		{
+			title = "NiGHTS Mare - Each Time";
+			flags2text = "[1] Mare greater or equal";
+			flags64text = "[6] Mare less or equal";
+			prefix = "(341)";
+		}
+
+		342
+		{
+			title = "NiGHTS Mare - Once";
+			flags2text = "[1] Mare greater or equal";
+			flags64text = "[6] Mare less or equal";
+			prefix = "(342)";
+		}
+
+		343
+		{
+			title = "Gravity Check - Continuous";
+			flags2text = "[1] Check temporary reverse gravity";
+			flags64text = "[6] Check for reverse gravity";
+			prefix = "(343)";
+		}
+
+		344
+		{
+			title = "Gravity Check - Each Time";
+			flags2text = "[1] Check temporary reverse gravity";
+			flags64text = "[6] Check for reverse gravity";
+			prefix = "(344)";
+		}
+
+		345
+		{
+			title = "Gravity Check - Once";
+			flags2text = "[1] Check temporary reverse gravity";
+			flags64text = "[6] Check for reverse gravity";
+			prefix = "(345)";
+		}
+
+		399
+		{
+			title = "Level Load";
+			prefix = "(399)";
+			flags1024text = "[10] Use faster, unordered execution";
+		}
+	}
+
+	linedefexecsector
+	{
+		title = "Linedef Executor (sector)";
+
+		400
+		{
+			title = "Set Tagged Sector's Floor Height/Texture";
+			prefix = "(400)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't change floor texture";
+		}
+
+		401
+		{
+			title = "Set Tagged Sector's Ceiling Height/Texture";
+			prefix = "(401)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't change ceiling texture";
+		}
+
+		402
+		{
+			title = "Copy Light Level to Tagged Sectors";
+			prefix = "(402)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		408
+		{
+			title = "Set Tagged Sector's Flats";
+			prefix = "(408)";
+			flags64text = "[6] Don't set floor flat";
+			flags512text = "[9] Don't set ceiling flat";
+		}
+
+		409
+		{
+			title = "Change Tagged Sector's Tag";
+			prefix = "(409)";
+			flags2text = "[1] Remove tag";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Add tag";
+		}
+
+		410
+		{
+			title = "Change Front Sector's Tag";
+			prefix = "(410)";
+			flags2text = "[1] Remove tag";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Add tag";
+		}
+
+		416
+		{
+			title = "Start Adjustable Flickering Light";
+			prefix = "(416)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Second level from back";
+		}
+
+		417
+		{
+			title = "Start Adjustable Pulsating Light";
+			prefix = "(417)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Second level from back";
+		}
+
+		418
+		{
+			title = "Start Adjustable Blinking Light (unsynchronized)";
+			prefix = "(418)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Second level from back";
+		}
+
+		419
+		{
+			title = "Start Adjustable Blinking Light (synchronized)";
+			prefix = "(419)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Second level from back";
+		}
+
+		420
+		{
+			title = "Fade Light Level";
+			prefix = "(420)";
+			flags8text = "[3] Set delay by backside sector";
+			flags16text = "[4] Set params by X/Y offsets";
+			flags512text = "[9] Speed = Tic Duration";
+			flags1024text = "[10] Override existing fade";
+		}
+
+		421
+		{
+			title = "Stop Lighting Effect";
+			prefix = "(421)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		435
+		{
+			title = "Change Plane Scroller Direction";
+			prefix = "(435)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		467
+		{
+			title = "Set Tagged Sector's Light Level";
+			prefix = "(467)";
+			flags8text = "[3] Set delay by backside sector";
+			flags256text = "[8] Set relative to current";
+		}
+	}
+
+	linedefexecplane
+	{
+		title = "Linedef Executor (plane movement)";
+
+		403
+		{
+			title = "Move Tagged Sector's Floor";
+			prefix = "(403)";
+			flags2text = "[1] Trigger linedef executor";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Change floor texture";
+		}
+
+		404
+		{
+			title = "Move Tagged Sector's Ceiling";
+			prefix = "(404)";
+			flags2text = "[1] Trigger linedef executor";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Change ceiling texture";
+		}
+
+		405
+		{
+			title = "Move Floor According to Front Texture Offsets";
+			prefix = "(405)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Move instantly";
+		}
+
+		407
+		{
+			title = "Move Ceiling According to Front Texture Offsets";
+			prefix = "(407)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Move instantly";
+		}
+
+		411
+		{
+			title = "Stop Plane Movement";
+			prefix = "(411)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		428
+		{
+			title = "Start Platform Movement";
+			prefix = "(428)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Move upwards at start";
+		}
+
+		429
+		{
+			title = "Crush Ceiling Once";
+			prefix = "(429)";
+			flags8text = "[3] Set delay by backside sector";
+			flags512text = "[9] Double, constant speed";
+		}
+
+		430
+		{
+			title = "Crush Floor Once";
+			prefix = "(430)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		431
+		{
+			title = "Crush Floor and Ceiling Once";
+			prefix = "(431)";
+			flags8text = "[3] Set delay by backside sector";
+			flags512text = "[9] Double, constant speed";
+		}
+	}
+
+	linedefexecplayer
+	{
+		title = "Linedef Executor (player/object)";
+
+		412
+		{
+			title = "Teleporter";
+			prefix = "(412)";
+			flags2text = "[1] Silent";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Retain angle";
+			flags256text = "[8] Relative, silent";
+			flags512text = "[9] Retain momentum";
+		}
+
+		425
+		{
+			title = "Change Object State";
+			prefix = "(425)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		426
+		{
+			title = "Stop Object";
+			prefix = "(426)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Teleport to sector center";
+		}
+
+		427
+		{
+			title = "Award Score";
+			prefix = "(427)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		432
+		{
+			title = "Enable/Disable 2D Mode";
+			prefix = "(432)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Return to 3D";
+		}
+
+		433
+		{
+			title = "Enable/Disable Gravity Flip";
+			prefix = "(433)";
+			flags2text = "[1] Force MFE_VERTICALFLIP";
+			flags8text = "[3] Set delay by backside sector";
+			flags32text = "[5] Invert current gravity";
+			flags64text = "[6] Return to normal";
+		}
+
+		434
+		{
+			title = "Award Power-Up";
+			prefix = "(434)";
+			flags2text = "[1] Use back upper texture";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] No time limit";
+		}
+
+		437
+		{
+			title = "Disable Player Control";
+			prefix = "(437)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Allow jumping";
+		}
+
+		438
+		{
+			title = "Change Object Size";
+			prefix = "(438)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		442
+		{
+			title = "Change Object Type State";
+			prefix = "(442)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		449
+		{
+			title = "Enable Bosses with Parameter";
+			prefix = "(449)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Disable bosses";
+		}
+
+		457
+		{
+			title = "Track Object's Angle";
+			prefix = "(457)";
+			flags8text = "[3] Set delay by backside sector";
+			flags128text = "[7] Don't stop after first fail";
+		}
+
+		458
+		{
+			title = "Stop Tracking Object's Angle";
+			prefix = "(458)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		460
+		{
+			title = "Award Rings";
+			prefix = "(460)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		461
+		{
+			title = "Spawn Object";
+			prefix = "(461)";
+			flags8text = "[3] Set delay by backside sector";
+			flags32text = "[5] Use line angle for object";
+			flags64text = "[6] Spawn inside a range";
+		}
+
+		462
+		{
+			title = "Stop Timer/Exit Stage in Record Attack";
+			prefix = "(462)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		463
+		{
+			title = "Dye Object";
+			prefix = "(463)";
+		}
+
+		464
+		{
+			title = "Trigger Egg Capsule";
+			prefix = "(464)";
+			flags64text = "[6] Don't end level";
+		}
+	}
+
+	linedefexecmisc
+	{
+		title = "Linedef Executor (misc.)";
+
+		413
+		{
+			title = "Change Music";
+			prefix = "(413)";
+			flags2text = "[1] Keep after death";
+			flags8text = "[3] Set delay by backside sector";
+			flags32text = "[5] Seek from current position";
+			flags64text = "[6] For everyone";
+			flags128text = "[7] Fade to custom volume";
+			flags512text = "[9] Don't loop";
+			flags16384text = "[14] Force music reload";
+		}
+
+		414
+		{
+			title = "Play Sound Effect";
+			prefix = "(414)";
+			flags2text = "[1] From calling sector";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] From nowhere for triggerer";
+			flags512text = "[9] From nowhere for everyone";
+			flags1024text = "[10] From tagged sectors";
+		}
+
+		415
+		{
+			title = "Run Script";
+			prefix = "(415)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		422
+		{
+			title = "Switch to Cut-Away View";
+			prefix = "(422)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Adjust pitch";
+		}
+
+		423
+		{
+			title = "Change Sky";
+			prefix = "(423)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] For everyone";
+		}
+
+		424
+		{
+			title = "Change Weather";
+			prefix = "(424)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] For everyone";
+		}
+
+		436
+		{
+			title = "Shatter FOF";
+			prefix = "(436)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		439
+		{
+			title = "Change Tagged Linedef's Textures";
+			prefix = "(439)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Only existing";
+			flags8192text = "[13] Use backside textures";
+		}
+
+		440
+		{
+			title = "Start Metal Sonic Race";
+			prefix = "(440)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		441
+		{
+			title = "Condition Set Trigger";
+			prefix = "(441)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		443
+		{
+			title = "Call Lua Function";
+			prefix = "(443)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		444
+		{
+			title = "Earthquake";
+			prefix = "(444)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		445
+		{
+			title = "Make FOF Disappear/Reappear";
+			prefix = "(445)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Reappear";
+		}
+
+		446
+		{
+			title = "Make FOF Crumble";
+			prefix = "(446)";
+			flags2text = "[1] Flags determine respawn";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't respawn";
+		}
+
+		447
+		{
+			title = "Change Tagged Sector's Colormap";
+			prefix = "(447)";
+			flags8text = "[3] Set delay by backside sector";
+			flags16text = "[4] Front X/Y = Alpha";
+			flags32text = "[5] Subtract Red value";
+			flags64text = "[6] Subtract Green value";
+			flags128text = "[7] Subtract Blue value";
+			flags256text = "[8] Set relative to current";
+			flags32768text = "[15] Use backside colormap";
+		}
+
+		448
+		{
+			title = "Change Skybox";
+			prefix = "(448)";
+			flags2text = "[1] Change centerpoint";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] For everyone";
+			flags512text = "[9] Don't change viewpoint";
+		}
+
+		450
+		{
+			title = "Execute Linedef Executor (specific tag)";
+			prefix = "(450)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		451
+		{
+			title = "Execute Linedef Executor (random tag in range)";
+			prefix = "(451)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		452
+		{
+			title = "Set FOF Translucency";
+			prefix = "(452)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Do not handle FF_TRANS";
+			flags256text = "[8] Set relative to current";
+		}
+
+		453
+		{
+			title = "Fade FOF";
+			prefix = "(453)";
+			flags2text = "[1] Do not handle FF_EXISTS";
+			flags8text = "[3] Set delay by backside sector";
+			flags32text = "[5] No collision during fade";
+			flags64text = "[6] Do not handle FF_TRANS";
+			flags128text = "[7] Do not handle lighting";
+			flags256text = "[8] Set relative to current";
+			flags512text = "[9] Speed = Tic Duration";
+			flags1024text = "[10] Override existing fade";
+			flags16384text = "[14] Do not handle collision";
+			flags32768text = "[15] Use exact alpha in OGL";
+		}
+
+		454
+		{
+			title = "Stop Fading FOF";
+			prefix = "(454)";
+			flags2text = "[1] Do not finalize collision";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		455
+		{
+			title = "Fade Tagged Sector's Colormap";
+			prefix = "(455)";
+			flags8text = "[3] Set delay by backside sector";
+			flags16text = "[4] Front X/Y = Alpha";
+			flags32text = "[5] Subtract Red value";
+			flags64text = "[6] Subtract Green value";
+			flags128text = "[7] Subtract Blue value";
+			flags256text = "[8] Set relative to current";
+			flags512text = "[9] Speed = Tic Duration";
+			flags1024text = "[10] Override existing fade";
+			flags16384text = "[14] Fade from invisible black";
+			flags32768text = "[15] Use backside colormap";
+		}
+
+		456
+		{
+			title = "Stop Fading Tagged Sector's Colormap";
+			prefix = "(456)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		459
+		{
+			title = "Control Text Prompt";
+			prefix = "(459)";
+			flags2text = "[1] Close text prompt";
+			flags8text = "[3] Set delay by backside sector";
+			flags32text = "[5] Run executor tag on close";
+			flags128text = "[7] Don't disable controls";
+			flags32768text = "[15] Find prompt by name";
+		}
+	}
+
+	linedefexecpoly
+	{
+		title = "Linedef Executor (polyobject)";
+
+		480
+		{
+			title = "PolyObject Door Slide";
+			prefix = "(480)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		481
+		{
+			title = "PolyObject Door Swing";
+			prefix = "(481)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		482
+		{
+			title = "Move PolyObject";
+			prefix = "(482)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		483
+		{
+			title = "Move PolyObject, Override";
+			prefix = "(483)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		484
+		{
+			title = "Rotate PolyObject Right";
+			prefix = "(484)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't turn players";
+			flags512text = "[9] Turn all objects";
+		}
+
+		485
+		{
+			title = "Rotate PolyObject Right, Override";
+			prefix = "(485)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't turn players";
+			flags512text = "[9] Turn all objects";
+		}
+
+		486
+		{
+			title = "Rotate PolyObject Left";
+			prefix = "(486)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't turn players";
+			flags512text = "[9] Turn all objects";
+		}
+
+		487
+		{
+			title = "Rotate PolyObject Left, Override";
+			prefix = "(487)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't turn players";
+			flags512text = "[9] Turn all objects";
+		}
+
+		488
+		{
+			title = "Move PolyObject by Waypoints";
+			prefix = "(488)";
+			flags8text = "[3] Set delay by backside sector";
+			flags32text = "[5] Reverse order";
+			flags128text = "[7] There and back";
+			flags256text = "[8] Return when done";
+			flags512text = "[9] Loop movement";
+		}
+
+		489
+		{
+			title = "Turn PolyObject Invisible, Intangible";
+			prefix = "(489)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Only invisible";
+		}
+
+		490
+		{
+			title = "Turn PolyObject Visible, Tangible";
+			prefix = "(490)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Only visible";
+		}
+
+		491
+		{
+			title = "Set PolyObject Translucency";
+			prefix = "(491)";
+			flags8text = "[3] Set delay by backside sector";
+			flags16text = "[4] Set raw alpha by Front X";
+			flags256text = "[8] Set relative to current";
+		}
+
+		492
+		{
+			title = "Fade PolyObject Translucency";
+			prefix = "(492)";
+			flags8text = "[3] Set delay by backside sector";
+			flags16text = "[4] Set raw alpha by Front X";
+			flags32text = "[5] No collision during fade";
+			flags256text = "[8] Set relative to current";
+			flags512text = "[9] Speed = Tic Duration";
+			flags1024text = "[10] Override existing fade";
+			flags16384text = "[14] Do not handle collision";
+		}
+	}
+
+	wallscroll
+	{
+		title = "Wall Scrolling";
+
+		500
+		{
+			title = "Scroll Front Wall Left";
+			prefix = "(500)";
+		}
+
+		501
+		{
+			title = "Scroll Front Wall Right";
+			prefix = "(501)";
+		}
+
+		502
+		{
+			title = "Scroll Tagged Walls";
+			prefix = "(502)";
+			flags128text = "[7] Use texture offsets";
+			flags256text = "[8] Scroll back side";
+		}
+
+		503
+		{
+			title = "Scroll Tagged Walls (Accelerative)";
+			prefix = "(503)";
+			flags128text = "[7] Use texture offsets";
+			flags256text = "[8] Scroll back side";
+		}
+
+		504
+		{
+			title = "Scroll Tagged Walls (Displacement)";
+			prefix = "(504)";
+			flags128text = "[7] Use texture offsets";
+			flags256text = "[8] Scroll back side";
+		}
+
+		505
+		{
+			title = "Scroll Front Wall by Front Side Offsets";
+			prefix = "(505)";
+		}
+
+		506
+		{
+			title = "Scroll Front Wall by Back Side Offsets";
+			prefix = "(506)";
+		}
+
+		507
+		{
+			title = "Scroll Back Wall by Front Side Offsets";
+			prefix = "(507)";
+		}
+
+		508
+		{
+			title = "Scroll Back Wall by Back Side Offsets";
+			prefix = "(508)";
+		}
+	}
+
+	planescroll
+	{
+		title = "Plane Scrolling";
+
+		510
+		{
+			title = "Scroll Floor Texture";
+			prefix = "(510)";
+		}
+
+		511
+		{
+			title = "Scroll Floor Texture (Accelerative)";
+			prefix = "(511)";
+		}
+
+		512
+		{
+			title = "Scroll Floor Texture (Displacement)";
+			prefix = "(512)";
+		}
+
+		513
+		{
+			title = "Scroll Ceiling Texture";
+			prefix = "(513)";
+		}
+
+		514
+		{
+			title = "Scroll Ceiling Texture (Accelerative)";
+			prefix = "(514)";
+		}
+
+		515
+		{
+			title = "Scroll Ceiling Texture (Displacement)";
+			prefix = "(515)";
+		}
+
+		520
+		{
+			title = "Carry Objects on Floor";
+			prefix = "(520)";
+			flags64text = "[6] Exclusive";
+		}
+
+		521
+		{
+			title = "Carry Objects on Floor (Accelerative)";
+			prefix = "(521)";
+			flags64text = "[6] Exclusive";
+		}
+
+		522
+		{
+			title = "Carry Objects on Floor (Displacement)";
+			prefix = "(522)";
+			flags64text = "[6] Exclusive";
+		}
+
+		523
+		{
+			title = "Carry Objects on Ceiling";
+			prefix = "(523)";
+			flags64text = "[6] Exclusive";
+		}
+
+		524
+		{
+			title = "Carry Objects on Ceiling (Accelerative)";
+			prefix = "(524)";
+			flags64text = "[6] Exclusive";
+		}
+
+		525
+		{
+			title = "Carry Objects on Ceiling (Displacement)";
+			prefix = "(525)";
+			flags64text = "[6] Exclusive";
+		}
+
+		530
+		{
+			title = "Scroll Floor Texture and Carry Objects";
+			prefix = "(530)";
+			flags64text = "[6] Exclusive";
+		}
+
+		531
+		{
+			title = "Scroll Floor Texture and Carry Objects (Accelerative)";
+			prefix = "(531)";
+			flags64text = "[6] Exclusive";
+		}
+
+		532
+		{
+			title = "Scroll Floor Texture and Carry Objects (Displacement)";
+			prefix = "(532)";
+			flags64text = "[6] Exclusive";
+		}
+
+		533
+		{
+			title = "Scroll Ceiling Texture and Carry Objects";
+			prefix = "(533)";
+			flags64text = "[6] Exclusive";
+		}
+
+		534
+		{
+			title = "Scroll Ceiling Texture and Carry Objects (Accelerative)";
+			prefix = "(534)";
+			flags64text = "[6] Exclusive";
+		}
+
+		535
+		{
+			title = "Scroll Ceiling Texture and Carry Objects (Displacement)";
+			prefix = "(535)";
+			flags64text = "[6] Exclusive";
+		}
+	}
+
+	pusher
+	{
+		title = "Pusher";
+
+		541
+		{
+			title = "Wind";
+			prefix = "(541)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Exclusive";
+		}
+
+		542
+		{
+			title = "Upwards Wind";
+			prefix = "(542)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Exclusive";
+		}
+
+		543
+		{
+			title = "Downwards Wind";
+			prefix = "(543)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Exclusive";
+		}
+
+		544
+		{
+			title = "Current";
+			prefix = "(544)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Exclusive";
+		}
+
+		545
+		{
+			title = "Upwards Current";
+			prefix = "(545)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Exclusive";
+		}
+
+		546
+		{
+			title = "Downwards Current";
+			prefix = "(546)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Exclusive";
+		}
+
+		547
+		{
+			title = "Push/Pull";
+			prefix = "(547)";
+			flags64text = "[6] Exclusive";
+		}
+	}
+
+	light
+	{
+		title = "Lighting";
+
+		600
+		{
+			title = "Floor Lighting";
+			prefix = "(600)";
+		}
+
+		601
+		{
+			title = "Ceiling Lighting";
+			prefix = "(601)";
+		}
+
+		602
+		{
+			title = "Adjustable Pulsating Light";
+			prefix = "(602)";
+		}
+
+		603
+		{
+			title = "Adjustable Flickering Light";
+			prefix = "(603)";
+		}
+
+		604
+		{
+			title = "Adjustable Blinking Light (unsynchronized)";
+			prefix = "(604)";
+		}
+
+		605
+		{
+			title = "Adjustable Blinking Light (synchronized)";
+			prefix = "(605)";
+		}
+
+		606
+		{
+			title = "Colormap";
+			prefix = "(606)";
+		}
+	}
+
+	slope
+	{
+		title = "Slope";
+
+		700
+		{
+			title = "Slope Frontside Floor";
+			prefix = "(700)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags32768text = "[15] Copy to other side";
+			slope = "regular";
+			slopeargs = 1;
+			copyslopeargs = 1;
+		}
+
+		701
+		{
+			title = "Slope Frontside Ceiling";
+			prefix = "(701)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags32768text = "[15] Copy to other side";
+			slope = "regular";
+			slopeargs = 2;
+			copyslopeargs = 4;
+		}
+
+		702
+		{
+			title = "Slope Frontside Floor and Ceiling";
+			prefix = "(702)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags32768text = "[15] Copy to other side";
+			slope = "regular";
+			slopeargs = 3;
+			copyslopeargs = 5;
+		}
+
+		703
+		{
+			title = "Slope Frontside Floor and Backside Ceiling";
+			prefix = "(703)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags32768text = "[15] Copy to other side";
+			slope = "regular";
+			slopeargs = 9;
+			copyslopeargs = 8;
+		}
+
+		704
+		{
+			title = "Slope Frontside Floor by 3 Tagged Vertex Things";
+			prefix = "(704)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags8192text = "[13] Use tag and offsets";
+			slope = "vertex";
+			slopeargs = 0;
+		}
+
+		705
+		{
+			title = "Slope Frontside Ceiling by 3 Tagged Vertex Things";
+			prefix = "(705)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags8192text = "[13] Use tag and offsets";
+			slope = "vertex";
+			slopeargs = 1;
+		}
+
+		710
+		{
+			title = "Slope Backside Floor";
+			prefix = "(710)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags32768text = "[15] Copy to other side";
+			slope = "regular";
+			slopeargs = 4;
+			copyslopeargs = 2;
+		}
+
+		711
+		{
+			title = "Slope Backside Ceiling";
+			prefix = "(711)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags32768text = "[15] Copy to other side";
+			slope = "regular";
+			slopeargs = 8;
+			copyslopeargs = 8;
+		}
+
+		712
+		{
+			title = "Slope Backside Floor and Ceiling";
+			prefix = "(712)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags32768text = "[15] Copy to other side";
+			slope = "regular";
+			slopeargs = 12;
+			copyslopeargs = 10;
+		}
+
+		713
+		{
+			title = "Slope Backside Floor and Frontside Ceiling";
+			prefix = "(713)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags32768text = "[15] Copy to other side";
+			slope = "regular";
+			slopeargs = 6;
+			copyslopeargs = 6;
+		}
+
+		714
+		{
+			title = "Slope Backside Floor by 3 Tagged Vertex Things";
+			prefix = "(714)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags8192text = "[13] Use tag and offsets";
+			slope = "vertex";
+			slopeargs = 2;
+		}
+
+		715
+		{
+			title = "Slope Backside Ceiling by 3 Tagged Vertex Things";
+			prefix = "(715)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags8192text = "[13] Use tag and offsets";
+			slope = "vertex";
+			slopeargs = 3;
+		}
+
+		720
+		{
+			title = "Copy Frontside Floor Slope from Line Tag";
+			prefix = "(720)";
+			slope = "copy";
+			slopeargs = 1;
+		}
+
+		721
+		{
+			title = "Copy Frontside Ceiling Slope from Line Tag";
+			prefix = "(721)";
+			slope = "copy";
+			slopeargs = 2;
+		}
+
+		722
+		{
+			title = "Copy Frontside Floor and Ceiling Slope from Line Tag";
+			prefix = "(722)";
+			slope = "copy";
+			slopeargs = 3;
+		}
+
+		723
+		{
+			title = "Copy Backside Floor Slope from Line Tag";
+			prefix = "(723)";
+			slope = "copy";
+			slopeargs = 4;
+		}
+
+		724
+		{
+			title = "Copy Backside Ceiling Slope from Line Tag";
+			prefix = "(724)";
+			slope = "copy";
+			slopeargs = 8;
+		}
+
+		725
+		{
+			title = "Copy Backside Floor and Ceiling Slope from Line Tag";
+			prefix = "(725)";
+			slope = "copy";
+			slopeargs = 12;
+		}
+
+		730
+		{
+			title = "Copy Frontside Floor Slope to Backside";
+			prefix = "(730)";
+			slope = "copy";
+			copyslopeargs = 1;
+		}
+
+		731
+		{
+			title = "Copy Frontside Ceiling Slope to Backside";
+			prefix = "(731)";
+			slope = "copy";
+			copyslopeargs = 4;
+		}
+
+		732
+		{
+			title = "Copy Frontside Floor and Ceiling Slope to Backside";
+			prefix = "(732)";
+			slope = "copy";
+			copyslopeargs = 5;
+		}
+
+		733
+		{
+			title = "Copy Backside Floor Slope to Frontside";
+			prefix = "(733)";
+			slope = "copy";
+			copyslopeargs = 2;
+		}
+
+		734
+		{
+			title = "Copy Backside Ceiling Slope to Frontside";
+			prefix = "(734)";
+			slope = "copy";
+			copyslopeargs = 8;
+		}
+
+		735
+		{
+			title = "Copy Backside Floor and Ceiling Slope to Frontside";
+			prefix = "(735)";
+			slope = "copy";
+			copyslopeargs = 10;
+		}
+
+		799
+		{
+			title = "Set Tagged Dynamic Slope Vertex to Front Sector Height";
+			prefix = "(799)";
+			flags64text = "[6] Use relative heights";
+		}
+	}
+
+	transwall
+	{
+		title = "Translucent Walls";
+
+		900
+		{
+			title = "90% Opaque";
+			prefix = "(900)";
+		}
+
+		901
+		{
+			title = "80% Opaque";
+			prefix = "(901)";
+		}
+
+		902
+		{
+			title = "70% Opaque";
+			prefix = "(902)";
+		}
+
+		903
+		{
+			title = "60% Opaque";
+			prefix = "(903)";
+		}
+
+		904
+		{
+			title = "50% Opaque";
+			prefix = "(904)";
+		}
+
+		905
+		{
+			title = "40% Opaque";
+			prefix = "(905)";
+		}
+
+		906
+		{
+			title = "30% Opaque";
+			prefix = "(906)";
+		}
+
+		907
+		{
+			title = "20% Opaque";
+			prefix = "(907)";
+		}
+
+		908
+		{
+			title = "10% Opaque";
+			prefix = "(908)";
+		}
+
+		909
+		{
+			title = "Fog Wall";
+			prefix = "(909)";
+		}
+
+		910
+		{
+			title = "100% Additive";
+			prefix = "(910)";
+		}
+
+		911
+		{
+			title = "90% Additive";
+			prefix = "(911)";
+		}
+
+		912
+		{
+			title = "80% Additive";
+			prefix = "(912)";
+		}
+
+		913
+		{
+			title = "70% Additive";
+			prefix = "(913)";
+		}
+
+		914
+		{
+			title = "60% Additive";
+			prefix = "(914)";
+		}
+
+		915
+		{
+			title = "50% Additive";
+			prefix = "(915)";
+		}
+
+		916
+		{
+			title = "40% Additive";
+			prefix = "(916)";
+		}
+
+		917
+		{
+			title = "30% Additive";
+			prefix = "(917)";
+		}
+
+		918
+		{
+			title = "20% Additive";
+			prefix = "(918)";
+		}
+
+		919
+		{
+			title = "10% Additive";
+			prefix = "(919)";
+		}
+
+		920
+		{
+			title = "100% Subtractive";
+			prefix = "(920)";
+		}
+
+		921
+		{
+			title = "90% Subtractive";
+			prefix = "(921)";
+		}
+
+		922
+		{
+			title = "80% Subtractive";
+			prefix = "(922)";
+		}
+
+		923
+		{
+			title = "70% Subtractive";
+			prefix = "(923)";
+		}
+
+		924
+		{
+			title = "60% Subtractive";
+			prefix = "(924)";
+		}
+
+		925
+		{
+			title = "50% Subtractive";
+			prefix = "(925)";
+		}
+
+		926
+		{
+			title = "40% Subtractive";
+			prefix = "(926)";
+		}
+
+		927
+		{
+			title = "30% Subtractive";
+			prefix = "(927)";
+		}
+
+		928
+		{
+			title = "20% Subtractive";
+			prefix = "(928)";
+		}
+
+		929
+		{
+			title = "10% Subtractive";
+			prefix = "(929)";
+		}
+
+		930
+		{
+			title = "100% Reverse Subtractive";
+			prefix = "(930)";
+		}
+
+		931
+		{
+			title = "90% Reverse Subtractive";
+			prefix = "(931)";
+		}
+
+		932
+		{
+			title = "80% Reverse Subtractive";
+			prefix = "(932)";
+		}
+
+		933
+		{
+			title = "70% Reverse Subtractive";
+			prefix = "(933)";
+		}
+
+		934
+		{
+			title = "60% Reverse Subtractive";
+			prefix = "(934)";
+		}
+
+		935
+		{
+			title = "50% Reverse Subtractive";
+			prefix = "(935)";
+		}
+
+		936
+		{
+			title = "40% Reverse Subtractive";
+			prefix = "(936)";
+		}
+
+		937
+		{
+			title = "30% Reverse Subtractive";
+			prefix = "(937)";
+		}
+
+		938
+		{
+			title = "20% Reverse Subtractive";
+			prefix = "(938)";
+		}
+
+		939
+		{
+			title = "10% Reverse Subtractive";
+			prefix = "(939)";
+		}
+
+		940
+		{
+			title = "Modulate";
+			prefix = "(940)";
+		}
+	}
+}
+
+
+// THING FLAGS
+thingflags
+{
+	1 = "[1] Extra";
+	2 = "[2] Flip";
+	4 = "[4] Special";
+	8 = "[8] Ambush";
+}
+
+// Thing flags UDMF translation table
+// This is needed for copy/paste and prefabs to work properly
+// When the UDMF field name is prefixed with ! it is inverted
+thingflagstranslation
+{
+	1 = "skill1";
+	2 = "skill2";
+	4 = "skill3";
+	8 = "ambush";
+}
+
+// THING FLAGS ERROR MASK
+// Mask for the thing flags which indicates the options
+// that make the same thing appear in the same modes
+thingflagsmask1 = 7;	// 1 + 2 + 4
+thingflagsmask2 = 0;
+
+
+// THING TYPES------------------------------------------------------------------
+// Color values: 1-Dark_Blue 2-Dark_Green 3-Turqoise 4-Dark_Red 5-Purple 6-Brown 7-Gray
+// 8-Dark_Gray 9-Blue 10-Green 11-Cyan 12-Red 13-Magenta
+// 14-Yellow 15-White 16-Pink 17-Orange 18-Gold 19-Cream
+thingtypes
+{
+	editor
+	{
+		color = 15; // White
+		arrow = 1;
+		title = "<Editor Things>";
+		error = -1;
+		width = 8;
+		height = 16;
+		sort = 1;
+
+		3328 = "3D Mode Start";
+	}
+	
+	starts
+	{
+		color = 1; // Blue
+		arrow = 1;
+		title = "Player Starts";
+		width = 16;
+		height = 48;
+		flags8text = "[8] Spawn on ceiling";
+		sprite = "PLAYA0";
+
+		1
+		{
+			title = "Player 01 Start";
+			sprite = "PLAYA0";
+		}
+		2
+		{
+			title = "Player 02 Start";
+			sprite = "PLAYA0";
+		}
+		3
+		{
+			title = "Player 03 Start";
+			sprite = "PLAYA0";
+		}
+		4
+		{
+			title = "Player 04 Start";
+			sprite = "PLAYA0";
+		}
+		5
+		{
+			title = "Player 05 Start";
+			sprite = "PLAYA0";
+		}
+		6
+		{
+			title = "Player 06 Start";
+			sprite = "PLAYA0";
+		}
+		7
+		{
+			title = "Player 07 Start";
+			sprite = "PLAYA0";
+		}
+		8
+		{
+			title = "Player 08 Start";
+			sprite = "PLAYA0";
+		}
+		9
+		{
+			title = "Player 09 Start";
+			sprite = "PLAYA0";
+		}
+		10
+		{
+			title = "Player 10 Start";
+			sprite = "PLAYA0";
+		}
+		11
+		{
+			title = "Player 11 Start";
+			sprite = "PLAYA0";
+		}
+		12
+		{
+			title = "Player 12 Start";
+			sprite = "PLAYA0";
+		}
+		13
+		{
+			title = "Player 13 Start";
+			sprite = "PLAYA0";
+		}
+		14
+		{
+			title = "Player 14 Start";
+			sprite = "PLAYA0";
+		}
+		15
+		{
+			title = "Player 15 Start";
+			sprite = "PLAYA0";
+		}
+		16
+		{
+			title = "Player 16 Start";
+			sprite = "PLAYA0";
+		}
+		17
+		{
+			title = "Player 17 Start";
+			sprite = "PLAYA0";
+		}
+		18
+		{
+			title = "Player 18 Start";
+			sprite = "PLAYA0";
+		}
+		19
+		{
+			title = "Player 19 Start";
+			sprite = "PLAYA0";
+		}
+		20
+		{
+			title = "Player 20 Start";
+			sprite = "PLAYA0";
+		}
+		21
+		{
+			title = "Player 21 Start";
+			sprite = "PLAYA0";
+		}
+		22
+		{
+			title = "Player 22 Start";
+			sprite = "PLAYA0";
+		}
+		23
+		{
+			title = "Player 23 Start";
+			sprite = "PLAYA0";
+		}
+		24
+		{
+			title = "Player 24 Start";
+			sprite = "PLAYA0";
+		}
+		25
+		{
+			title = "Player 25 Start";
+			sprite = "PLAYA0";
+		}
+		26
+		{
+			title = "Player 26 Start";
+			sprite = "PLAYA0";
+		}
+		27
+		{
+			title = "Player 27 Start";
+			sprite = "PLAYA0";
+		}
+		28
+		{
+			title = "Player 28 Start";
+			sprite = "PLAYA0";
+		}
+		29
+		{
+			title = "Player 29 Start";
+			sprite = "PLAYA0";
+		}
+		30
+		{
+			title = "Player 30 Start";
+			sprite = "PLAYA0";
+		}
+		31
+		{
+			title = "Player 31 Start";
+			sprite = "PLAYA0";
+		}
+		32
+		{
+			title = "Player 32 Start";
+			sprite = "PLAYA0";
+		}
+		33
+		{
+			title = "Match Start";
+			sprite = "NDRNA2A8";
+		}
+		34
+		{
+			title = "CTF Red Team Start";
+			sprite = "SIGNG0";
+		}
+		35
+		{
+			title = "CTF Blue Team Start";
+			sprite = "SIGNE0";
+		}
+	}
+
+	enemies
+	{
+		color = 9; // Light Blue
+		arrow = 1;
+		title = "Enemies";
+
+		100
+		{
+			title = "Crawla (Blue)";
+			sprite = "POSSA1";
+			width = 24;
+			height = 32;
+		}
+		101
+		{
+			title = "Crawla (Red)";
+			sprite = "SPOSA1";
+			width = 24;
+			height = 32;
+		}
+		102
+		{
+			title = "Stupid Dumb Unnamed RoboFish";
+			sprite = "FISHA0";
+			width = 8;
+			height = 28;
+			angletext = "Jump strength";
+			fixedrotation = 1;
+		}
+		103
+		{
+			title = "Buzz (Gold)";
+			sprite = "BUZZA1";
+			width = 28;
+			height = 40;
+			flags8text = "[8] Cannot move";
+		}
+		104
+		{
+			title = "Buzz (Red)";
+			sprite = "RBUZA1";
+			width = 28;
+			height = 40;
+			flags8text = "[8] Cannot move";
+		}
+		108
+		{
+			title = "Deton";
+			sprite = "DETNA1";
+			width = 20;
+			height = 32;
+		}
+		110
+		{
+			title = "Turret";
+			sprite = "TRETA1";
+			width = 16;
+			height = 32;
+		}
+		111
+		{
+			title = "Pop-up Turret";
+			sprite = "TURRI1";
+			width = 12;
+			height = 64;
+			angletext = "Firing delay";
+			fixedrotation = 1;
+		}
+		122
+		{
+			title = "Spring Shell (Green)";
+			sprite = "SSHLA1";
+			width = 24;
+			height = 40;
+		}
+		125
+		{
+			title = "Spring Shell (Yellow)";
+			sprite = "SSHLI1";
+			width = 24;
+			height = 40;
+		}
+		109
+		{
+			title = "Skim";
+			sprite = "SKIMA1";
+			width = 16;
+			height = 24;
+		}
+		113
+		{
+			title = "Jet Jaw";
+			sprite = "JJAWA3A7";
+			width = 12;
+			height = 20;
+		}
+		126
+		{
+			title = "Crushstacean";
+			sprite = "CRABA0";
+			width = 24;
+			height = 32;
+			flags8text = "[8] Move left from spawn";
+		}
+		138
+		{
+			title = "Banpyura";
+			sprite = "CR2BA0";
+			width = 24;
+			height = 32;
+			flags8text = "[8] Move left from spawn";
+		}
+		117
+		{
+			title = "Robo-Hood";
+			sprite = "ARCHA1";
+			width = 24;
+			height = 32;
+			flags8text = "[8] Don't jump away";
+		}
+		118
+		{
+			title = "Lance-a-Bot";
+			sprite = "CBFSA1";
+			width = 32;
+			height = 72;
+		}
+		1113
+		{
+			title = "Suspicious Lance-a-Bot Statue";
+			sprite = "CBBSA1";
+			width = 32;
+			height = 72;
+		}
+		119
+		{
+			title = "Egg Guard";
+			sprite = "ESHIA1";
+			width = 16;
+			height = 48;
+			flags1text = "[1] 90 degrees clockwise";
+			flags4text = "[4] 90 degrees counter-clockwise";
+			flags8text = "[8] Double speed";
+		}
+		115
+		{
+			title = "Bird Aircraft Strike Hazard";
+			sprite = "VLTRF1";
+			width = 12;
+			height = 24;
+		}
+		120
+		{
+			title = "Green Snapper";
+			sprite = "GSNPA1";
+			width = 24;
+			height = 24;
+		}
+		121
+		{
+			title = "Minus";
+			sprite = "MNUSA0";
+			width = 24;
+			height = 32;
+		}
+		134
+		{
+			title = "Canarivore";
+			sprite = "CANAA0";
+			width = 12;
+			height = 80;
+			hangs = 1;
+		}
+		123
+		{
+			title = "Unidus";
+			sprite = "UNIDA1";
+			width = 18;
+			height = 36;
+		}
+		135
+		{
+			title = "Pterabyte Spawner";
+			sprite = "PTERA2A8";
+			width = 24;
+			height = 48;
+			parametertext = "Spawns +1";
+			arrow = 0;
+		}
+		136
+		{
+			title = "Pyre Fly";
+			sprite = "PYREA0";
+			width = 24;
+			height = 34;
+			flags8text = "[8] Start on fire";
+		}
+		137
+		{
+			title = "Dragonbomber";
+			sprite = "DRABA1";
+			width = 28;
+			height = 48;
+		}
+		105
+		{
+			title = "Jetty-Syn Bomber";
+			sprite = "JETBB1";
+			width = 20;
+			height = 50;
+			flags8text = "[8] Cannot move";
+		}
+		106
+		{
+			title = "Jetty-Syn Gunner";
+			sprite = "JETGB1";
+			width = 20;
+			height = 48;
+			flags8text = "[8] Cannot move";
+		}
+		112
+		{
+			title = "Spincushion";
+			sprite = "SHRPA1";
+			width = 16;
+			height = 24;
+		}
+		114
+		{
+			title = "Snailer";
+			sprite = "SNLRA3A7";
+			width = 24;
+			height = 48;
+		}
+		129
+		{
+			title = "Penguinator";
+			sprite = "PENGA1";
+			width = 24;
+			height = 32;
+		}
+		130
+		{
+			title = "Pophat";
+			sprite = "POPHA1";
+			width = 24;
+			height = 32;
+		}
+		107
+		{
+			title = "Crawla Commander";
+			sprite = "CCOMA1";
+			width = 16;
+			height = 32;
+		}
+		131
+		{
+			title = "Spinbobert";
+			sprite = "SBOBB0";
+			width = 32;
+			height = 32;
+		}
+		132
+		{
+			title = "Cacolantern";
+			sprite = "CACOA0";
+			width = 32;
+			height = 32;
+			flags8text = "[8] Cannot move";
+		}
+		133
+		{
+			title = "Hangster";
+			sprite = "HBATC1";
+			width = 24;
+			height = 24;
+			hangs = 1;
+		}
+		127
+		{
+			title = "Hive Elemental";
+			sprite = "HIVEA0";
+			width = 32;
+			height = 80;
+			parametertext = "No. bees";
+		}
+		128
+		{
+			title = "Bumblebore";
+			sprite = "BUMBA1";
+			width = 16;
+			height = 32;
+			flags8text = "[8] Cannot move";
+		}
+		124
+		{
+			title = "Buggle";
+			sprite = "BBUZA1";
+			width = 20;
+			height = 24;
+		}
+		116
+		{
+			title = "Pointy";
+			sprite = "PNTYA1";
+			width = 8;
+			height = 16;
+		}
+	}
+
+	bosses
+	{
+		color = 4; // Dark Red
+		arrow = 1;
+		title = "Bosses";
+
+		200
+		{
+			title = "Egg Mobile";
+			sprite = "EGGMA1";
+			width = 36;
+			height = 84;
+			flags4text = "[4] End level on death";
+		}
+		201
+		{
+			title = "Egg Slimer";
+			sprite = "EGGNA1";
+			width = 36;
+			height = 84;
+			flags4text = "[4] End level on death";
+			flags8text = "[8] Speed up when hit";
+		}
+		202
+		{
+			title = "Sea Egg";
+			sprite = "EGGOA1";
+			width = 36;
+			height = 116;
+			flags4text = "[4] End level on death";
+		}
+		203
+		{
+			title = "Egg Colosseum";
+			sprite = "EGGPA1";
+			width = 36;
+			height = 84;
+			flags4text = "[4] End level on death";
+		}
+		204
+		{
+			title = "Fang";
+			sprite = "FANGA1";
+			width = 24;
+			height = 60;
+			flags1text = "[1] Grayscale mode";
+			flags4text = "[4] End level on death";
+			flags8text = "[8] Skip intro";
+		}
+		206
+		{
+			title = "Brak Eggman (Old)";
+			sprite = "BRAKB1";
+			width = 48;
+			height = 160;
+			flags4text = "[4] End level on death";
+		}
+		207
+		{
+			title = "Metal Sonic (Race)";
+			sprite = "METLI1";
+			width = 16;
+			height = 48;
+			flags1text = "[1] Grayscale mode";
+		}
+		208
+		{
+			title = "Metal Sonic (Battle)";
+			sprite = "METLC1";
+			width = 16;
+			height = 48;
+			flags1text = "[1] Grayscale mode";
+			flags4text = "[4] End level on death";
+		}
+		209
+		{
+			title = "Brak Eggman";
+			sprite = "BRAK01";
+			width = 48;
+			height = 160;
+			flags1text = "[1] No origin-fling death";
+			flags4text = "[4] End level on death";
+			flags8text = "[8] Electric barrier";
+		}
+		290
+		{
+			arrow = 0;
+			title = "Boss Escape Point";
+			width = 8;
+			height = 16;
+			sprite = "internal:eggmanend";
+		}
+		291
+		{
+			arrow = 0;
+			title = "Egg Capsule Center";
+			width = 8;
+			height = 16;
+			sprite = "internal:capsule";
+			angletext = "Tag";
+			fixedrotation = 1;
+			tagthing = true;
+		}
+		292
+		{
+			arrow = 0;
+			title = "Boss Waypoint";
+			width = 8;
+			height = 16;
+			flags8text = "[8] Sea Egg shooting point";
+			sprite = "internal:eggmanway";
+			angletext = "No. (Sea Egg)";
+			fixedrotation = 1;
+			flagsvaluetext = "No. (Brak)";
+			parametertext = "Next";
+		}
+		293
+		{
+			arrow = 0;
+			title = "Metal Sonic Gather Point";
+			sprite = "internal:metal";
+			width = 8;
+			height = 16;
+		}
+		294
+		{
+			arrow = 0;
+			title = "Fang Waypoint";
+			flags8text = "[8] Center waypoint";
+			sprite = "internal:eggmanway";
+			width = 8;
+			height = 16;
+		}
+	}
+
+	rings
+	{
+		color = 14; // Yellow
+		title = "Rings and Weapon Panels";
+		width = 24;
+		height = 24;
+		flags8height = 24;
+		flags8text = "[8] Float";
+		sprite = "RINGA0";
+
+		300
+		{
+			title = "Ring";
+			sprite = "RINGA0";
+			width = 16;
+		}
+		301
+		{
+			title = "Bounce Ring";
+			sprite = "RNGBA0";
+		}
+		302
+		{
+			title = "Rail Ring";
+			sprite = "RNGRA0";
+		}
+		303
+		{
+			title = "Infinity Ring";
+			sprite = "RNGIA0";
+		}
+		304
+		{
+			title = "Automatic Ring";
+			sprite = "RNGAA0";
+		}
+		305
+		{
+			title = "Explosion Ring";
+			sprite = "RNGEA0";
+		}
+		306
+		{
+			title = "Scatter Ring";
+			sprite = "RNGSA0";
+		}
+		307
+		{
+			title = "Grenade Ring";
+			sprite = "RNGGA0";
+		}
+		308
+		{
+			title = "CTF Team Ring (Red)";
+			sprite = "internal:TRNGA0R";
+			width = 16;
+		}
+		309
+		{
+			title = "CTF Team Ring (Blue)";
+			sprite = "internal:TRNGA0B";
+			width = 16;
+		}
+		330
+		{
+			title = "Bounce Ring Panel";
+			sprite = "PIKBA0";
+		}
+		331
+		{
+			title = "Rail Ring Panel";
+			sprite = "PIKRA0";
+		}
+		332
+		{
+			title = "Automatic Ring Panel";
+			sprite = "PIKAA0";
+		}
+		333
+		{
+			title = "Explosion Ring Panel";
+			sprite = "PIKEA0";
+		}
+		334
+		{
+			title = "Scatter Ring Panel";
+			sprite = "PIKSA0";
+		}
+		335
+		{
+			title = "Grenade Ring Panel";
+			sprite = "PIKGA0";
+		}
+	}
+
+	collectibles
+	{
+		color = 10; // Light Green
+		title = "Other Collectibles";
+		width = 16;
+		height = 32;
+		sort = 1;
+		sprite = "CEMGA0";
+
+		310
+		{
+			title = "CTF Red Flag";
+			sprite = "RFLGA0";
+			width = 24;
+			height = 64;
+		}
+		311
+		{
+			title = "CTF Blue Flag";
+			sprite = "BFLGA0";
+			width = 24;
+			height = 64;
+		}
+		312
+		{
+			title = "Emerald Token";
+			sprite = "TOKEA0";
+			width = 16;
+			height = 32;
+			flags8height = 24;
+			flags8text = "[8] Float";
+		}
+		313
+		{
+			title = "Chaos Emerald 1 (Green)";
+			sprite = "CEMGA0";
+		}
+		314
+		{
+			title = "Chaos Emerald 2 (Purple)";
+			sprite = "CEMGB0";
+		}
+		315
+		{
+			title = "Chaos Emerald 3 (Blue)";
+			sprite = "CEMGC0";
+		}
+		316
+		{
+			title = "Chaos Emerald 4 (Cyan)";
+			sprite = "CEMGD0";
+		}
+		317
+		{
+			title = "Chaos Emerald 5 (Orange)";
+			sprite = "CEMGE0";
+		}
+		318
+		{
+			title = "Chaos Emerald 6 (Red)";
+			sprite = "CEMGF0";
+		}
+		319
+		{
+			title = "Chaos Emerald 7 (Gray)";
+			sprite = "CEMGG0";
+		}
+		320
+		{
+			title = "Emerald Hunt Location";
+			sprite = "SHRDA0";
+			flags8height = 24;
+			flags8text = "[8] Float";
+		}
+		321
+		{
+			title = "Match Chaos Emerald Spawn";
+			sprite = "CEMGA0";
+			flags8height = 24;
+			flags8text = "[8] Float";
+		}
+		322
+		{
+			title = "Emblem";
+			sprite = "EMBMA0";
+			width = 16;
+			height = 30;
+			flags8height = 24;
+			flags8text = "[8] Float";
+			angletext = "Tag";
+			fixedrotation = 1;
+		}
+	}
+
+	boxes
+	{
+		color = 7; // Gray
+		blocking = 2;
+		title = "Monitors";
+		width = 18;
+		height = 40;
+		flags1text = "[1] Run linedef executor on pop";
+		flags4text = "[4] Random (Strong)";
+		flags8text = "[8] Random (Weak)";
+		angletext = "Tag";
+		fixedrotation = 1;
+		tagthing = true;
+
+		400
+		{
+			title = "Super Ring (10 Rings)";
+			sprite = "TVRIA0";
+		}
+		401
+		{
+			title = "Pity Shield";
+			sprite = "TVPIA0";
+		}
+		402
+		{
+			title = "Attraction Shield";
+			sprite = "TVATA0";
+		}
+		403
+		{
+			title = "Force Shield";
+			sprite = "TVFOA0";
+		}
+		404
+		{
+			title = "Armageddon Shield";
+			sprite = "TVARA0";
+		}
+		405
+		{
+			title = "Whirlwind Shield";
+			sprite = "TVWWA0";
+		}
+		406
+		{
+			title = "Elemental Shield";
+			sprite = "TVELA0";
+		}
+		407
+		{
+			title = "Super Sneakers";
+			sprite = "TVSSA0";
+		}
+		408
+		{
+			title = "Invincibility";
+			sprite = "TVIVA0";
+		}
+		409
+		{
+			title = "Extra Life";
+			sprite = "TV1UA0";
+			flags4text = "[4] Random (Strong) / 10k points";
+			flags8text = "[8] Random (Weak) / 10k points";
+		}
+		410
+		{
+			title = "Eggman";
+			sprite = "TVEGA0";
+			flags4text = "[4] Special";
+			flags8text = "[8] Ambush";
+		}
+		411
+		{
+			title = "Teleporter";
+			sprite = "TVMXA0";
+		}
+		413
+		{
+			title = "Gravity Boots";
+			sprite = "TVGVA0";
+			flags4text = "[4] Special";
+			flags8text = "[8] Ambush";
+		}
+		414
+		{
+			title = "CTF Team Ring Monitor (Red)";
+			sprite = "TRRIA0";
+			flags4text = "[4] Special";
+			flags8text = "[8] Ambush";
+		}
+		415
+		{
+			title = "CTF Team Ring Monitor (Blue)";
+			sprite = "TBRIA0";
+			flags4text = "[4] Special";
+			flags8text = "[8] Ambush";
+		}
+		416
+		{
+			title = "Recycler";
+			sprite = "TVRCA0";
+		}
+		418
+		{
+			title = "Score (1,000 Points)";
+			sprite = "TV1KA0";
+			flags4text = "[4] Special";
+			flags8text = "[8] Ambush";
+		}
+		419
+		{
+			title = "Score (10,000 Points)";
+			sprite = "TVTKA0";
+			flags4text = "[4] Special";
+			flags8text = "[8] Ambush";
+		}
+		420
+		{
+			title = "Flame Shield";
+			sprite = "TVFLA0";
+		}
+		421
+		{
+			title = "Water Shield";
+			sprite = "TVBBA0";
+		}
+		422
+		{
+			title = "Lightning Shield";
+			sprite = "TVZPA0";
+		}
+	}
+
+	boxes2
+	{
+		color = 18; // Gold
+		blocking = 2;
+		title = "Monitors (Respawning)";
+		width = 20;
+		height = 44;
+		flags1text = "[1] Run linedef executor on pop";
+		angletext = "Tag";
+		fixedrotation = 1;
+		tagthing = true;
+
+		431
+		{
+			title = "Pity Shield (Respawn)";
+			sprite = "TVPIB0";
+		}
+		432
+		{
+			title = "Attraction Shield (Respawn)";
+			sprite = "TVATB0";
+		}
+		433
+		{
+			title = "Force Shield (Respawn)";
+			sprite = "TVFOB0";
+		}
+		434
+		{
+			title = "Armageddon Shield (Respawn)";
+			sprite = "TVARB0";
+		}
+		435
+		{
+			title = "Whirlwind Shield (Respawn)";
+			sprite = "TVWWB0";
+		}
+		436
+		{
+			title = "Elemental Shield (Respawn)";
+			sprite = "TVELB0";
+		}
+		437
+		{
+			title = "Super Sneakers (Respawn)";
+			sprite = "TVSSB0";
+		}
+		438
+		{
+			title = "Invincibility (Respawn)";
+			sprite = "TVIVB0";
+		}
+		440
+		{
+			title = "Eggman (Respawn)";
+			sprite = "TVEGB0";
+		}
+		443
+		{
+			title = "Gravity Boots (Respawn)";
+			sprite = "TVGVB0";
+		}
+		450
+		{
+			title = "Flame Shield (Respawn)";
+			sprite = "TVFLB0";
+		}
+		451
+		{
+			title = "Water Shield (Respawn)";
+			sprite = "TVBBB0";
+		}
+		452
+		{
+			title = "Lightning Shield (Respawn)";
+			sprite = "TVZPB0";
+		}
+	}
+
+	generic
+	{
+		color = 11; // Light Cyan
+		title = "Generic Items & Hazards";
+
+		500
+		{
+			title = "Air Bubble Patch";
+			sprite = "BUBLE0";
+			width = 8;
+			height = 16;
+			flags8text = "[8] No distance check";
+		}
+		501
+		{
+			title = "Signpost";
+			sprite = "SIGND0";
+			width = 8;
+			height = 32;
+		}
+		502
+		{
+			arrow = 1;
+			title = "Star Post";
+			sprite = "STPTA0M0";
+			width = 64;
+			height = 128;
+			flags4text = "[4] Respawn at center";
+			angletext = "Angle/Order";
+			fixedrotation = 1;
+			parametertext = "Order";
+		}
+		520
+		{
+			title = "Bomb Sphere";
+			sprite = "SPHRD0";
+			width = 16;
+			height = 24;
+			flags8height = 24;
+			flags8text = "[8] Float";
+			unflippable = true;
+		}
+		521
+		{
+			title = "Spikeball";
+			sprite = "SPIKA0";
+			width = 12;
+			height = 8;
+			flags8height = 24;
+			flags8text = "[8] Float";
+		}
+		522
+		{
+			title = "Wall Spike";
+			sprite = "WSPKALAR";
+			width = 16;
+			height = 14;
+			arrow = 1;
+			flags1text = "[1] Start retracted";
+			flags4text = "[4] Retractable";
+			flags8text = "[8] Intangible";
+			parametertext = "Start delay";
+		}
+		523
+		{
+			title = "Spike";
+			sprite = "USPKA0";
+			width = 8;
+			height = 32;
+			flags1text = "[1] Start retracted";
+			flags4text = "[4] Retractable";
+			flags8text = "[8] Intangible";
+			angletext = "Retraction interval";
+			fixedrotation = 1;
+			parametertext = "Start delay";
+		}
+		1130
+		{
+			title = "Small Mace";
+			sprite = "SMCEA0";
+			width = 17;
+			height = 34;
+		}
+		1131
+		{
+			title = "Big Mace";
+			sprite = "BMCEA0";
+			width = 34;
+			height = 68;
+		}
+		1136
+		{
+			title = "Small Fireball";
+			sprite = "SFBRA0";
+			width = 17;
+			height = 34;
+		}
+		1137
+		{
+			title = "Large Fireball";
+			sprite = "BFBRA0";
+			width = 34;
+			height = 68;
+		}
+	}
+
+	springs
+	{
+		color = 12; // Light Red
+		title = "Springs and Fans";
+		width = 20;
+		height = 16;
+		sprite = "RSPRD2";
+
+		540
+		{
+			title = "Fan";
+			sprite = "FANSA0D0";
+			width = 16;
+			height = 8;
+			flags4text = "[4] Invisible";
+			flags8text = "[8] No distance check";
+			angletext = "Lift height";
+			fixedrotation = 1;
+		}
+		541
+		{
+			title = "Gas Jet";
+			sprite = "STEMD0";
+			flags8text = "[8] No sounds";
+			width = 32;
+		}
+		542
+		{
+			title = "Bumper";
+			sprite = "BUMPA0";
+			width = 32;
+			height = 64;
+			angletext = "Strength";
+			fixedrotation = 1;
+		}
+		543
+		{
+			title = "Balloon";
+			sprite = "BLONA0";
+			width = 32;
+			height = 64;
+			flags8text = "[8] Respawn";
+			angletext = "Color";
+			fixedrotation = 1;
+		}
+		550
+		{
+			title = "Yellow Spring";
+			sprite = "SPRYA0";
+		}
+		551
+		{
+			title = "Red Spring";
+			sprite = "SPRRA0";
+		}
+		552
+		{
+			title = "Blue Spring";
+			sprite = "SPRBA0";
+		}
+		555
+		{
+			arrow = 1;
+			title = "Diagonal Yellow Spring";
+			sprite = "YSPRD2";
+			width = 16;
+			flags4text = "[4] Ignore gravity";
+			flags8text = "[8] Rotate 22.5° CCW";
+		}
+		556
+		{
+			arrow = 1;
+			title = "Diagonal Red Spring";
+			sprite = "RSPRD2";
+			width = 16;
+			flags4text = "[4] Ignore gravity";
+			flags8text = "[8] Rotate 22.5° CCW";
+		}
+		557
+		{
+			arrow = 1;
+			title = "Diagonal Blue Spring";
+			sprite = "BSPRD2";
+			width = 16;
+			flags4text = "[4] Ignore gravity";
+			flags8text = "[8] Rotate 22.5° CCW";
+		}
+		558
+		{
+			arrow = 1;
+			title = "Horizontal Yellow Spring";
+			sprite = "SSWYD2D8";
+			flags8height = 16;
+			flags8text = "[8] Float";
+			width = 16;
+			height = 32;
+		}
+		559
+		{
+			arrow = 1;
+			title = "Horizontal Red Spring";
+			sprite = "SSWRD2D8";
+			flags8height = 16;
+			flags8text = "[8] Float";
+			width = 16;
+			height = 32;
+		}
+		560
+		{
+			arrow = 1;
+			title = "Horizontal Blue Spring";
+			sprite = "SSWBD2D8";
+			flags8height = 16;
+			flags8text = "[8] Float";
+			width = 16;
+			height = 32;
+		}
+		1134
+		{
+			title = "Yellow Spring Ball";
+			sprite = "YSPBA0";
+			width = 17;
+			height = 34;
+		}
+		1135
+		{
+			title = "Red Spring Ball";
+			sprite = "RSPBA0";
+			width = 17;
+			height = 34;
+		}
+		544
+		{
+			arrow = 1;
+			title = "Yellow Boost Panel";
+			sprite = "BSTYA0";
+			flags8text = "[8] Force spin";
+			width = 28;
+			height = 2;
+		}
+		545
+		{
+			arrow = 1;
+			title = "Red Boost Panel";
+			sprite = "BSTRA0";
+			flags8text = "[8] Force spin";
+			width = 28;
+			height = 2;
+		}
+	}
+
+	patterns
+	{
+		color = 5; // Magenta
+		arrow = 1;
+		title = "Special Placement Patterns";
+		width = 16;
+		height = 384;
+		sprite = "RINGA0";
+
+		600
+		{
+			arrow = 0;
+			title = "5 Vertical Rings (Yellow Spring)";
+			sprite = "internal:ringverticalyellow";
+		}
+		601
+		{
+			arrow = 0;
+			title = "5 Vertical Rings (Red Spring)";
+			sprite = "internal:ringverticalred";
+			height = 1024;
+		}
+		602
+		{
+			title = "5 Diagonal Rings (Yellow Spring)";
+			sprite = "RINGA0";
+			height = 32;
+		}
+		603
+		{
+			title = "10 Diagonal Rings (Red Spring)";
+			sprite = "RINGA0";
+			height = 32;
+		}
+		604
+		{
+			title = "Circle of Rings";
+			sprite = "internal:circlering";
+			width = 96;
+			height = 192;
+			unflippable = true;
+			centerHitbox = true;
+		}
+		605
+		{
+			title = "Circle of Rings (Big)";
+			sprite = "internal:circlebigring";
+			width = 192;
+			unflippable = true;
+			centerHitbox = true;
+		}
+		606
+		{
+			title = "Circle of Blue Spheres";
+			sprite = "internal:circlesphere";
+			width = 96;
+			height = 192;
+			unflippable = true;
+			centerHitbox = true;
+		}
+		607
+		{
+			title = "Circle of Blue Spheres (Big)";
+			sprite = "internal:circlebigsphere";
+			width = 192;
+			unflippable = true;
+			centerHitbox = true;
+		}
+		608
+		{
+			title = "Circle of Rings and Spheres";
+			sprite = "internal:circleringsphere";
+			width = 96;
+			height = 192;
+			unflippable = true;
+			centerHitbox = true;
+		}
+		609
+		{
+			title = "Circle of Rings and Spheres (Big)";
+			sprite = "internal:circlebigringsphere";
+			width = 192;
+			unflippable = true;
+			centerHitbox = true;
+		}
+	}
+
+	ambience
+	{
+		color = 8; // Dark Gray
+		title = "Ambience";
+		width = 8;
+		height = 16;
+		sprite = "internal:ambiance";
+
+		700
+		{
+			title = "Water Ambience A (Large)";
+		}
+
+		701
+		{
+			title = "Water Ambience B (Large)";
+		}
+
+		702
+		{
+			title = "Water Ambience C (Medium)";
+		}
+
+		703
+		{
+			title = "Water Ambience D (Medium)";
+		}
+
+		704
+		{
+			title = "Water Ambience E (Small)";
+		}
+
+		705
+		{
+			title = "Water Ambience F (Small)";
+		}
+
+		706
+		{
+			title = "Water Ambience G (Extra Large)";
+		}
+
+		707
+		{
+			title = "Water Ambience H (Extra Large)";
+		}
+
+		708
+		{
+			title = "Disco Ambience";
+		}
+
+		709
+		{
+			title = "Volcano Ambience";
+		}
+
+		710
+		{
+			title = "Machine Ambience";
+		}
+	}
+
+	invisible
+	{
+		color = 15; // White
+		title = "Misc. Invisible";
+		width = 8;
+		height = 16;
+		sprite = "UNKNA0";
+
+		750
+		{
+			title = "Slope Vertex";
+			sprite = "internal:vertexslope";
+			angletext = "Tag";
+			fixedrotation = 1;
+			parametertext = "Absolute?";
+			flagsvaluetext = "Absolute Z";
+			tagthing = true;
+		}
+
+		751
+		{
+			arrow = 1;
+			title = "Teleport Destination";
+			sprite = "internal:tele";
+		}
+
+		752
+		{
+			arrow = 1;
+			title = "Alternate View Point";
+			sprite = "internal:view";
+		}
+
+		753
+		{
+			title = "Zoom Tube Waypoint";
+			sprite = "internal:zoom";
+			angletext = "Order";
+			fixedrotation = 1;
+		}
+
+		754
+		{
+			title = "Push Point";
+			flags4text = "[4] Fades using XY";
+			flags8text = "[8] Push using XYZ";
+			sprite = "GWLGA0";
+			angletext = "Radius";
+			fixedrotation = 1;
+		}
+		755
+		{
+			title = "Pull Point";
+			flags4text = "[4] Fades using XY";
+			flags8text = "[8] Pull using XYZ";
+			sprite = "GWLRA0";
+			angletext = "Radius";
+			fixedrotation = 1;
+		}
+		756
+		{
+			title = "Blast Linedef Executor";
+			sprite = "internal:blastexec";
+			width = 32;
+			height = 16;
+			angletext = "Tag";
+			fixedrotation = 1;
+			tagthing = true;
+		}
+		757
+		{
+			title = "Fan Particle Generator";
+			sprite = "internal:fanparticles";
+			width = 8;
+			height = 16;
+			angletext = "Tag";
+			fixedrotation = 1;
+			tagthing = true;
+		}
+		758
+		{
+			title = "Object Angle Anchor";
+			sprite = "internal:view";
+		}
+		760
+		{
+			title = "PolyObject Anchor";
+			sprite = "internal:polyanchor";
+			angletext = "Tag";
+			fixedrotation = 1;
+			tagthing = true;
+			unflippable = true;
+		}
+
+		761
+		{
+			title = "PolyObject Spawn Point";
+			sprite = "internal:polycenter";
+			angletext = "Tag";
+			fixedrotation = 1;
+			tagthing = true;
+			unflippable = true;
+		}
+
+		762
+		{
+			title = "PolyObject Spawn Point (Crush)";
+			sprite = "internal:polycentercrush";
+			angletext = "Tag";
+			fixedrotation = 1;
+			tagthing = true;
+			unflippable = true;
+		}
+		780
+		{
+			title = "Skybox View Point";
+			sprite = "internal:skyb";
+			flags4text = "[4] In-map centerpoint";
+			parametertext = "ID";
+			fixedrotation = 1;
+		}
+	}
+
+	greenflower
+	{
+		color = 2; // Green
+		title = "Greenflower";
+
+		800
+		{
+			title = "GFZ Flower";
+			sprite = "FWR1A0";
+			width = 16;
+			height = 40;
+		}
+		801
+		{
+			title = "Sunflower";
+			sprite = "FWR2A0";
+			width = 16;
+			height = 96;
+		}
+		802
+		{
+			title = "Budding Flower";
+			sprite = "FWR3A0";
+			width = 8;
+			height = 32;
+		}
+		803
+		{
+			title = "Blueberry Bush";
+			sprite = "BUS3A0";
+			width = 16;
+			height = 32;
+		}
+		804
+		{
+			title = "Berry Bush";
+			sprite = "BUS1A0";
+			width = 16;
+			height = 32;
+		}
+		805
+		{
+			title = "Bush";
+			sprite = "BUS2A0";
+			width = 16;
+			height = 32;
+		}
+		806
+		{
+			title = "GFZ Tree";
+			sprite = "TRE1A0";
+			width = 20;
+			height = 128;
+		}
+		807
+		{
+			title = "GFZ Berry Tree";
+			sprite = "TRE1B0";
+			width = 20;
+			height = 128;
+		}
+		808
+		{
+			title = "GFZ Cherry Tree";
+			sprite = "TRE1C0";
+			width = 20;
+			height = 128;
+		}
+		809
+		{
+			title = "Checkered Tree";
+			sprite = "TRE2A0";
+			width = 20;
+			height = 200;
+		}
+		810
+		{
+			title = "Checkered Tree (Sunset)";
+			sprite = "TRE2B0";
+			width = 20;
+			height = 200;
+		}
+		811
+		{
+			title = "Polygon Tree";
+			sprite = "TRE4A0";
+			width = 20;
+			height = 200;
+		}
+		812
+		{
+			title = "Bush Tree";
+			sprite = "TRE5A0";
+			width = 20;
+			height = 200;
+		}
+		813
+		{
+			title = "Red Bush Tree";
+			sprite = "TRE5B0";
+			width = 20;
+			height = 200;
+		}
+	}
+
+	technohill
+	{
+		color = 2; // Green
+		title = "Techno Hill";
+
+		900
+		{
+			title = "THZ Steam Flower";
+			sprite = "THZPA0";
+			width = 8;
+			height = 32;
+		}
+		901
+		{
+			title = "Alarm";
+			sprite = "ALRMA0";
+			width = 8;
+			height = 16;
+			hangs = 1;
+		}
+		902
+		{
+			title = "THZ Spin Flower (Red)";
+			sprite = "FWR5A0";
+			width = 16;
+			height = 64;
+		}
+		903
+		{
+			title = "THZ Spin Flower (Yellow)";
+			sprite = "FWR6A0";
+			width = 16;
+			height = 64;
+		}
+		904
+		{
+			arrow = 1;
+			title = "Whistlebush";
+			sprite = "THZTA0";
+			width = 16;
+			height = 64;
+		}
+	}
+
+	deepsea
+	{
+		color = 2; // Green
+		title = "Deep Sea";
+
+		1000
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Gargoyle";
+			sprite = "GARGA1";
+			width = 16;
+			height = 40;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1009
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Gargoyle (Big)";
+			sprite = "GARGB1";
+			width = 32;
+			height = 80;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1001
+		{
+			title = "Seaweed";
+			sprite = "SEWEA0";
+			width = 24;
+			height = 56;
+		}
+		1002
+		{
+			title = "Dripping Water";
+			sprite = "DRIPD0";
+			width = 8;
+			height = 16;
+			hangs = 1;
+			angletext = "Dripping delay";
+			fixedrotation = 1;
+		}
+		1003
+		{
+			title = "Coral (Green)";
+			sprite = "CORLA0";
+			width = 29;
+			height = 40;
+		}
+		1004
+		{
+			title = "Coral (Red)";
+			sprite = "CORLB0";
+			width = 30;
+			height = 53;
+		}
+		1005
+		{
+			title = "Coral (Orange)";
+			sprite = "CORLC0";
+			width = 28;
+			height = 41;
+		}
+		1006
+		{
+			title = "Blue Crystal";
+			sprite = "BCRYA1";
+			width = 8;
+			height = 16;
+		}
+		1007
+		{
+			title = "Kelp";
+			sprite = "KELPA0";
+			width = 16;
+			height = 292;
+			flags4text = "[4] Double size";
+		}
+		1008
+		{
+			title = "Stalagmite (DSZ1)";
+			sprite = "DSTGA0";
+			width = 8;
+			height = 116;
+			flags4text = "[4] Double size";
+		}
+		1010
+		{
+			arrow = 1;
+			title = "Light Beam";
+			sprite = "LIBEARAL";
+			width = 16;
+			height = 16;
+		}
+		1011
+		{
+			title = "Stalagmite (DSZ2)";
+			sprite = "DSTGB0";
+			width = 8;
+			height = 116;
+			flags4text = "[4] Double size";
+		}
+		1012
+		{
+			arrow = 1;
+			title = "Big Floating Mine";
+			width = 28;
+			height = 56;
+			sprite = "BMNEA1";
+		}
+		1013
+		{
+			title = "Animated Kelp";
+			sprite = "ALGAA0";
+			width = 48;
+			height = 120;
+		}
+		1014
+		{
+			title = "Large Coral (Brown)";
+			sprite = "CORLD0";
+			width = 56;
+			height = 112;
+		}
+		1015
+		{
+			title = "Large Coral (Beige)";
+			sprite = "CORLE0";
+			width = 56;
+			height = 112;
+		}
+	}
+
+	castleeggman
+	{
+		color = 2; // Green
+		title = "Castle Eggman";
+
+		1100
+		{
+			title = "Chain (Decorative)";
+			sprite = "CHANA0";
+			width = 4;
+			height = 128;
+			hangs = 1;
+		}
+		1101
+		{
+			title = "Torch";
+			sprite = "FLAMA0E0";
+			width = 8;
+			height = 32;
+			flags1text = "[1] Add corona";
+		}
+		1102
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Eggman Statue";
+			sprite = "ESTAA1";
+			width = 32;
+			height = 240;
+			flags1text = "[1] Solid gold";
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1103
+		{
+			title = "CEZ Flower";
+			sprite = "FWR4A0";
+			width = 16;
+			height = 40;
+		}
+		1104
+		{
+			title = "Mace Spawnpoint";
+			sprite = "SMCEA0";
+			width = 17;
+			height = 34;
+			flags4text = "[4] No sounds";
+			flags8text = "[8] Double size";
+			angletext = "Tag";
+			parametertext = "Spokes";
+			fixedrotation = 1;
+			tagthing = true;
+		}
+		1105
+		{
+			title = "Chain with Maces Spawnpoint";
+			sprite = "SMCEA0";
+			width = 17;
+			height = 34;
+			flags4text = "[4] No sounds";
+			flags8text = "[8] Double size";
+			angletext = "Tag";
+			parametertext = "Spokes";
+			fixedrotation = 1;
+			tagthing = true;
+		}
+		1106
+		{
+			title = "Chained Spring Spawnpoint";
+			sprite = "YSPBA0";
+			width = 17;
+			height = 34;
+			flags4text = "[4] No sounds";
+			flags8text = "[8] Red spring";
+			angletext = "Tag";
+			parametertext = "Spokes";
+			fixedrotation = 1;
+			tagthing = true;
+		}
+		1107
+		{
+			title = "Chain Spawnpoint";
+			sprite = "BMCHB0";
+			width = 17;
+			height = 34;
+			flags8text = "[8] Double size";
+			angletext = "Tag";
+			parametertext = "Spokes";
+			fixedrotation = 1;
+			tagthing = true;
+		}
+		1108
+		{
+			arrow = 1;
+			title = "Hidden Chain Spawnpoint";
+			sprite = "SMCHA0";
+			width = 17;
+			height = 34;
+			flags8text = "[8] Double size";
+		}
+		1109
+		{
+			title = "Firebar Spawnpoint";
+			sprite = "BFBRA0";
+			width = 17;
+			height = 34;
+			flags4text = "[4] No sounds";
+			flags8text = "[8] Double size";
+			angletext = "Tag";
+			parametertext = "Spokes";
+			fixedrotation = 1;
+			tagthing = true;
+		}
+		1110
+		{
+			title = "Custom Mace Spawnpoint";
+			sprite = "SMCEA0";
+			width = 17;
+			height = 34;
+			flags4text = "[4] No sounds";
+			angletext = "Tag";
+			parametertext = "Spokes";
+			fixedrotation = 1;
+			tagthing = true;
+		}
+		1111
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Crawla Statue";
+			sprite = "CSTAA1";
+			width = 16;
+			height = 40;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1112
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Lance-a-Bot Statue";
+			sprite = "CBBSA1";
+			width = 32;
+			height = 72;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1114
+		{
+			title = "Pine Tree";
+			sprite = "PINEA0";
+			width = 16;
+			height = 628;
+		}
+		1115
+		{
+			title = "CEZ Shrub (Small)";
+			sprite = "CEZBA0";
+			width = 16;
+			height = 24;
+		}
+		1116
+		{
+			title = "CEZ Shrub (Large)";
+			sprite = "CEZBB0";
+			width = 32;
+			height = 48;
+		}
+		1117
+		{
+			arrow = 1;
+			title = "Pole Banner (Red)";
+			sprite = "BANRA0";
+			width = 40;
+			height = 224;
+		}
+		1118
+		{
+			arrow = 1;
+			title = "Pole Banner (Blue)";
+			sprite = "BANRA0";
+			width = 40;
+			height = 224;
+		}
+		1119
+		{
+			title = "Candle";
+			sprite = "CNDLA0";
+			width = 8;
+			height = 48;
+			flags1text = "[1] Add corona";
+		}
+		1120
+		{
+			title = "Candle Pricket";
+			sprite = "CNDLB0";
+			width = 8;
+			height = 176;
+			flags1text = "[1] Add corona";
+		}
+		1121
+		{
+			title = "Flame Holder";
+			sprite = "FLMHA0";
+			width = 24;
+			height = 80;
+			flags1text = "[1] Add corona";
+			flags4text = "[4] No flame";
+		}
+		1122
+		{
+			title = "Fire Torch";
+			sprite = "CTRCA0";
+			width = 16;
+			height = 80;
+		}
+		1123
+		{
+			title = "Cannonball Launcher";
+			sprite = "internal:cannonball";
+			width = 8;
+			height = 16;
+		}
+		1124
+		{
+			blocking = 2;
+			title = "Cannonball";
+			sprite = "CBLLA0";
+			width = 20;
+			height = 40;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1125
+		{
+			title = "Brambles";
+			sprite = "CABRALAR";
+			width = 48;
+			height = 32;
+		}
+		1126
+		{
+			title = "Invisible Lockon Object";
+			sprite = "LCKNC0";
+			width = 16;
+			height = 32;
+		}
+		1127
+		{
+			title = "Spectator Eggrobo";
+			sprite = "EGR1A1";
+			width = 20;
+			height = 72;
+			arrow = 1;
+			flags4text = "[4] Move right";
+			flags8text = "[8] Move left";
+		}
+		1128
+		{
+			arrow = 1;
+			title = "Waving Flag (Red)";
+			sprite = "CFLGA0";
+			width = 8;
+			height = 208;
+		}
+		1129
+		{
+			arrow = 1;
+			title = "Waving Flag (Blue)";
+			sprite = "CFLGA0";
+			width = 8;
+			height = 208;
+		}
+	}
+
+	aridcanyon
+	{
+		color = 2; // Green
+		title = "Arid Canyon";
+
+		1200
+		{
+			title = "Tumbleweed (Big)";
+			sprite = "BTBLA0";
+			width = 24;
+			height = 48;
+			flags8text = "[8] Moves perpetually";
+		}
+		1201
+		{
+			title = "Tumbleweed (Small)";
+			sprite = "STBLA0";
+			width = 12;
+			height = 24;
+			flags8text = "[8] Moves perpetually";
+		}
+		1202
+		{
+			arrow = 1;
+			title = "Rock Spawner";
+			sprite = "ROIAA0";
+			width = 8;
+			height = 16;
+			angletext = "Tag";
+			fixedrotation = 1;
+			tagthing = true;
+		}
+		1203
+		{
+			title = "Tiny Red Flower Cactus";
+			sprite = "CACTA0";
+			width = 13;
+			height = 24;
+		}
+		1204
+		{
+			title = "Small Red Flower Cactus";
+			sprite = "CACTB0";
+			width = 15;
+			height = 52;
+		}
+		1205
+		{
+			title = "Tiny Blue Flower Cactus";
+			sprite = "CACTC0";
+			width = 13;
+			height = 24;
+		}
+		1206
+		{
+			title = "Small Blue Flower Cactus";
+			sprite = "CACTD0";
+			width = 15;
+			height = 52;
+		}
+		1207
+		{
+			title = "Prickly Pear";
+			sprite = "CACTE0";
+			width = 32;
+			height = 96;
+		}
+		1208
+		{
+			title = "Barrel Cactus";
+			sprite = "CACTF0";
+			width = 20;
+			height = 128;
+		}
+		1209
+		{
+			title = "Tall Barrel Cactus";
+			sprite = "CACTG0";
+			width = 24;
+			height = 224;
+		}
+		1210
+		{
+			title = "Armed Cactus";
+			sprite = "CACTH0";
+			width = 24;
+			height = 256;
+		}
+		1211
+		{
+			title = "Ball Cactus";
+			sprite = "CACTI0";
+			width = 48;
+			height = 96;
+		}
+		1212
+		{
+			title = "Caution Sign";
+			sprite = "WWSGAR";
+			width = 22;
+			height = 64;
+			arrow = 1;
+		}
+		1213
+		{
+			title = "Cacti Sign";
+			sprite = "WWS2AR";
+			width = 22;
+			height = 64;
+			arrow = 1;
+		}
+		1214
+		{
+			title = "Sharp Turn Sign";
+			sprite = "WWS3ALAR";
+			width = 16;
+			height = 192;
+			arrow = 1;
+		}
+		1215
+		{
+			title = "Mine Oil Lamp";
+			sprite = "OILLA0";
+			width = 22;
+			height = 64;
+			hangs = 1;
+		}
+		1216
+		{
+			title = "TNT Barrel";
+			sprite = "BARRA1";
+			width = 24;
+			height = 63;
+			arrow = 1;
+			flags8text = "[8] Not pushable";
+		}
+		1217
+		{
+			title = "TNT Proximity Shell";
+			sprite = "REMTA0";
+			width = 64;
+			height = 40;
+		}
+		1218
+		{
+			title = "Dust Devil";
+			sprite = "TAZDCR";
+			width = 80;
+			height = 416;
+		}
+		1219
+		{
+			title = "Minecart Spawner";
+			sprite = "MCRTCLFR";
+			width = 22;
+			height = 32;
+			arrow = 1;
+		}
+		1220
+		{
+			title = "Minecart Stopper";
+			sprite = "MCRTIR";
+			width = 32;
+			height = 32;
+			arrow = 1;
+		}
+		1221
+		{
+			title = "Minecart Saloon Door";
+			sprite = "SALDARAL";
+			width = 96;
+			height = 160;
+			arrow = 1;
+			flags8text = "[8] Allow non-minecart players";
+		}
+		1222
+		{
+			title = "Train Cameo Spawner";
+			sprite = "TRAEBRBL";
+			width = 28;
+			height = 32;
+		}
+		1223
+		{
+			title = "Train Dust Spawner";
+			sprite = "ADSTA0";
+			width = 4;
+			height = 4;
+		}
+		1224
+		{
+			title = "Train Steam Spawner";
+			sprite = "STEAA0";
+			width = 4;
+			height = 4;
+		}
+		1229
+		{
+			title = "Minecart Switch Point";
+			sprite = "internal:zoom";
+			width = 8;
+			height = 16;
+			flags8text = "[8] Enable switching";
+		}
+		1230
+		{
+			title = "Tiny Cactus";
+			sprite = "CACTJ0";
+			width = 13;
+			height = 28;
+		}
+		1231
+		{
+			title = "Small Cactus";
+			sprite = "CACTK0";
+			width = 15;
+			height = 60;
+		}
+	}
+
+	redvolcano
+	{
+		color = 2; // Green
+		title = "Red Volcano";
+
+		1300
+		{
+			arrow = 1;
+			title = "Flame Jet (Horizontal)";
+			sprite = "internal:flameh";
+			width = 16;
+			height = 40;
+			flags8text = "[8] Waves vertically";
+			angletext = "On/Off time";
+			fixedrotation = 1;
+			parametertext = "Strength";
+		}
+		1301
+		{
+			title = "Flame Jet (Vertical)";
+			sprite = "internal:flamev";
+			width = 16;
+			height = 40;
+			flags8text = "[8] Shoot downwards";
+			angletext = "On/Off time";
+			fixedrotation = 1;
+			parametertext = "Strength";
+		}
+		1302
+		{
+			title = "Spinning Flame Jet (Counter-Clockwise)";
+			sprite = "internal:flame2";
+			width = 16;
+			height = 24;
+		}
+		1303
+		{
+			title = "Spinning Flame Jet (Clockwise)";
+			sprite = "internal:flame1";
+			width = 16;
+			height = 24;
+		}
+		1304
+		{
+			title = "Lavafall";
+			sprite = "LFALF0";
+			width = 30;
+			height = 32;
+			angletext = "Initial delay";
+			fixedrotation = 1;
+			hangs = 1;
+			flags8text = "[8] Double size";
+		}
+		1305
+		{
+			title = "Rollout Rock";
+			sprite = "PUMIA1A5";
+			width = 30;
+			height = 60;
+			flags8text = "[8] Non-buoyant";
+		}
+		1306
+		{
+			title = "Big Fern";
+			sprite = "JPLAB0";
+			width = 32;
+			height = 48;
+		}
+		1307
+		{
+			title = "Jungle Palm";
+			sprite = "JPLAC0";
+			width = 32;
+			height = 48;
+		}
+		1308
+		{
+			title = "Torch Flower";
+			sprite = "TFLOA0";
+			width = 14;
+			height = 110;
+		}
+		1309
+		{
+			title = "RVZ1 Wall Vine (Long)";
+			sprite = "WVINALAR";
+			width = 1;
+			height = 288;
+			arrow = 1;
+		}
+		1310
+		{
+			title = "RVZ1 Wall Vine (Short)";
+			sprite = "WVINBLBR";
+			width = 1;
+			height = 288;
+			arrow = 1;
+		}
+	}
+
+	botanicserenity
+	{
+		color = 2; // Green
+		title = "Botanic Serenity";
+		width = 16;
+		height = 32;
+		sprite = "BSZ1A0";
+		1400
+		{
+			title = "Tall Flower (Red)";
+			sprite = "BSZ1A0";
+		}
+		1401
+		{
+			title = "Tall Flower (Purple)";
+			sprite = "BSZ1B0";
+		}
+		1402
+		{
+			title = "Tall Flower (Blue)";
+			sprite = "BSZ1C0";
+		}
+		1403
+		{
+			title = "Tall Flower (Cyan)";
+			sprite = "BSZ1D0";
+		}
+		1404
+		{
+			title = "Tall Flower (Yellow)";
+			sprite = "BSZ1E0";
+		}
+		1405
+		{
+			title = "Tall Flower (Orange)";
+			sprite = "BSZ1F0";
+		}
+		1410
+		{
+			title = "Medium Flower (Red)";
+			sprite = "BSZ2A0";
+		}
+		1411
+		{
+			title = "Medium Flower (Purple)";
+			sprite = "BSZ2B0";
+		}
+		1412
+		{
+			title = "Medium Flower (Blue)";
+			sprite = "BSZ2C0";
+		}
+		1413
+		{
+			title = "Medium Flower (Cyan)";
+			sprite = "BSZ2D0";
+		}
+		1414
+		{
+			title = "Medium Flower (Yellow)";
+			sprite = "BSZ2E0";
+		}
+		1415
+		{
+			title = "Medium Flower (Orange)";
+			sprite = "BSZ2F0";
+		}
+		1420
+		{
+			title = "Short Flower (Red)";
+			sprite = "BSZ3A0";
+		}
+		1421
+		{
+			title = "Short Flower (Purple)";
+			sprite = "BSZ3B0";
+		}
+		1422
+		{
+			title = "Short Flower (Blue)";
+			sprite = "BSZ3C0";
+		}
+		1423
+		{
+			title = "Short Flower (Cyan)";
+			sprite = "BSZ3D0";
+		}
+		1424
+		{
+			title = "Short Flower (Yellow)";
+			sprite = "BSZ3E0";
+		}
+		1425
+		{
+			title = "Short Flower (Orange)";
+			sprite = "BSZ3F0";
+		}
+		1430
+		{
+			title = "Tulip (Red)";
+			sprite = "BST1A0";
+		}
+		1431
+		{
+			title = "Tulip (Purple)";
+			sprite = "BST2A0";
+		}
+		1432
+		{
+			title = "Tulip (Blue)";
+			sprite = "BST3A0";
+		}
+		1433
+		{
+			title = "Tulip (Cyan)";
+			sprite = "BST4A0";
+		}
+		1434
+		{
+			title = "Tulip (Yellow)";
+			sprite = "BST5A0";
+		}
+		1435
+		{
+			title = "Tulip (Orange)";
+			sprite = "BST6A0";
+		}
+		1440
+		{
+			title = "Cluster (Red)";
+			sprite = "BSZ5A0";
+		}
+		1441
+		{
+			title = "Cluster (Purple)";
+			sprite = "BSZ5B0";
+		}
+		1442
+		{
+			title = "Cluster (Blue)";
+			sprite = "BSZ5C0";
+		}
+		1443
+		{
+			title = "Cluster (Cyan)";
+			sprite = "BSZ5D0";
+		}
+		1444
+		{
+			title = "Cluster (Yellow)";
+			sprite = "BSZ5E0";
+		}
+		1445
+		{
+			title = "Cluster (Orange)";
+			sprite = "BSZ5F0";
+		}
+		1450
+		{
+			title = "Bush (Red)";
+			sprite = "BSZ6A0";
+		}
+		1451
+		{
+			title = "Bush (Purple)";
+			sprite = "BSZ6B0";
+		}
+		1452
+		{
+			title = "Bush (Blue)";
+			sprite = "BSZ6C0";
+		}
+		1453
+		{
+			title = "Bush (Cyan)";
+			sprite = "BSZ6D0";
+		}
+		1454
+		{
+			title = "Bush (Yellow)";
+			sprite = "BSZ6E0";
+		}
+		1455
+		{
+			title = "Bush (Orange)";
+			sprite = "BSZ6F0";
+		}
+		1460
+		{
+			title = "Vine (Red)";
+			sprite = "BSZ7A0";
+		}
+		1461
+		{
+			title = "Vine (Purple)";
+			sprite = "BSZ7B0";
+		}
+		1462
+		{
+			title = "Vine (Blue)";
+			sprite = "BSZ7C0";
+		}
+		1463
+		{
+			title = "Vine (Cyan)";
+			sprite = "BSZ7D0";
+		}
+		1464
+		{
+			title = "Vine (Yellow)";
+			sprite = "BSZ7E0";
+		}
+		1465
+		{
+			title = "Vine (Orange)";
+			sprite = "BSZ7F0";
+		}
+		1470
+		{
+			title = "BSZ Shrub";
+			sprite = "BSZ8A0";
+		}
+		1471
+		{
+			title = "BSZ Clover";
+			sprite = "BSZ8B0";
+		}
+		1473
+		{
+			title = "Palm Tree (Big)";
+			width = 16;
+			height = 160;
+			sprite = "BSZ8D0";
+		}
+		1475
+		{
+			title = "Palm Tree (Small)";
+			width = 16;
+			height = 80;
+			sprite = "BSZ8F0";
+		}
+	}
+
+	azuretemple
+	{
+		color = 2; // Green
+		title = "Azure Temple";
+
+		1500
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Glaregoyle";
+			sprite = "BGARA1";
+			width = 16;
+			height = 40;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1501
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Glaregoyle (Up)";
+			sprite = "BGARA1";
+			width = 16;
+			height = 40;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1502
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Glaregoyle (Down)";
+			sprite = "BGARA1";
+			width = 16;
+			height = 40;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1503
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Glaregoyle (Long)";
+			sprite = "BGARA1";
+			width = 16;
+			height = 40;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1504
+		{
+			title = "ATZ Target";
+			sprite = "RCRYB0";
+			width = 24;
+			height = 32;
+		}
+		1505
+		{
+			title = "Green Flame";
+			sprite = "CFLMA0E0";
+			width = 8;
+			height = 32;
+		}
+		1506
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Blue Gargoyle";
+			sprite = "BGARD1";
+			width = 16;
+			height = 40;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+	}
+
+	dreamhill
+	{
+		color = 2; // Green
+		title = "Dream Hill";
+
+		1600
+		{
+			title = "Spring Tree";
+			sprite = "TRE6A0";
+			width = 16;
+			height = 32;
+		}
+		1601
+		{
+			title = "Shleep";
+			sprite = "SHLPA0";
+			width = 24;
+			height = 32;
+		}
+		1602
+		{
+			title = "Nightopian";
+			sprite = "NTPNA1";
+			width = 16;
+			height = 40;
+		}
+	}
+
+	nightstrk
+	{
+		color = 16; // Light Pink
+		title = "NiGHTS Track & Basics";
+		width = 8;
+		height = 4096;
+		sprite = "UNKNA0";
+		fixedrotation = 1;
+
+		1700
+		{
+			title = "Axis";
+			sprite = "internal:axis1";
+			circle = 1;
+			unflippable = true;
+			ignoreZ = true;
+			flagsvaluetext = "Order";
+			angletext = "Radius/Direction";
+			parametertext = "Mare";
+		}
+		1701
+		{
+			title = "Axis Transfer";
+			sprite = "internal:axis2";
+			unflippable = true;
+			ignoreZ = true;
+			flagsvaluetext = "Order";
+			parametertext = "Mare";
+		}
+		1702
+		{
+			title = "Axis Transfer Line";
+			sprite = "internal:axis3";
+			unflippable = true;
+			ignoreZ = true;
+			flagsvaluetext = "Order";
+			parametertext = "Mare";
+		}
+		1703
+		{
+			title = "Ideya Drone";
+			sprite = "NDRNA1";
+			width = 16;
+			height = 56;
+			flags1text = "[1] Align player to middle";
+			flags4text = "[4] Align player to top";
+			flags8text = "[8] Die upon time up";
+			angletext = "Time limit";
+			fixedrotation = 1;
+			parametertext = "Height";
+		}
+		1710
+		{
+			title = "Ideya Capture";
+			sprite = "CAPSA0";
+			width = 72;
+			height = 144;
+			angletext = "Spheres";
+			parametertext = "Mare";
+		}
+	}
+
+	nights
+	{
+		color = 13; // Pink
+		title = "NiGHTS Items";
+		width = 16;
+		height = 32;
+		1704
+		{
+			arrow = 1;
+			title = "NiGHTS Bumper";
+			sprite = "NBMPG3G7";
+			width = 32;
+			height = 64;
+			unflippable = true;
+			flagsvaluetext = "Pitch";
+			angletext = "Yaw";
+		}
+		1705
+		{
+			arrow = 1;
+			title = "Hoop (Generic)";
+			sprite = "internal:nightshoop";
+			width = 80;
+			height = 160;
+			unflippable = true;
+			centerHitbox = true;
+			flagsvaluetext = "Height";
+			angletext = "Pitch/Yaw";
+			parametertext = "Degrees?";
+		}
+		1706
+		{
+			title = "Blue Sphere";
+			sprite = "SPHRA0";
+			width = 16;
+			height = 24;
+			flags8height = 24;
+			flags8text = "[8] Float";
+		}
+		1707
+		{
+			title = "Super Paraloop";
+			sprite = "NPRUA0";
+			flags4text = "[4] Bonus time only";
+			flags8text = "[8] Spawn immediately";
+		}
+		1708
+		{
+			title = "Drill Refill";
+			sprite = "NPRUB0";
+			flags4text = "[4] Bonus time only";
+			flags8text = "[8] Spawn immediately";
+		}
+		1709
+		{
+			title = "Nightopian Helper";
+			sprite = "NPRUC0";
+			flags4text = "[4] Bonus time only";
+			flags8text = "[8] Spawn immediately";
+		}
+		1711
+		{
+			title = "Extra Time";
+			sprite = "NPRUD0";
+			flags4text = "[4] Bonus time only";
+			flags8text = "[8] Spawn immediately";
+		}
+		1712
+		{
+			title = "Link Freeze";
+			sprite = "NPRUE0";
+			flags4text = "[4] Bonus time only";
+			flags8text = "[8] Spawn immediately";
+		}
+		1713
+		{
+			arrow = 1;
+			title = "Hoop (Customizable)";
+			flags1text = "[1] Radius +16";
+			flags2text = "[2] Radius +32";
+			flags4text = "[4] Radius +64";
+			flags8text = "[8] Radius +128";
+			sprite = "internal:nightshoop";
+			width = 80;
+			height = 160;
+			unflippable = true;
+			centerHitbox = true;
+			angletext = "Pitch/Yaw";
+			parametertext = "Degrees?";
+		}
+		1714
+		{
+			title = "Ideya Anchor Point";
+			sprite = "internal:axis1";
+			width = 8;
+			height = 16;
+			parametertext = "Ideya";
+		}
+	}
+
+	mario
+	{
+		color = 6; // Brown
+		title = "Mario";
+
+		1800
+		{
+			title = "Coin";
+			sprite = "COINA0";
+			width = 16;
+			height = 24;
+			flags8height = 24;
+			flags8text = "[8] Float";
+		}
+		1801
+		{
+			arrow = 1;
+			title = "Goomba";
+			sprite = "GOOMA0";
+			width = 24;
+			height = 32;
+		}
+		1802
+		{
+			arrow = 1;
+			title = "Goomba (Blue)";
+			sprite = "BGOMA0";
+			width = 24;
+			height = 32;
+		}
+		1803
+		{
+			title = "Fire Flower";
+			sprite = "FFWRB0";
+			width = 16;
+			height = 32;
+		}
+		1804
+		{
+			title = "Koopa Shell";
+			sprite = "SHLLA1";
+			width = 16;
+			height = 20;
+		}
+		1805
+		{
+			title = "Puma (Jumping Fireball)";
+			sprite = "PUMAA0";
+			width = 8;
+			height = 16;
+			angletext = "Jump strength";
+			fixedrotation = 1;
+		}
+		1806
+		{
+			title = "King Bowser";
+			sprite = "KOOPA0";
+			width = 16;
+			height = 48;
+		}
+		1807
+		{
+			title = "Axe";
+			sprite = "MAXEA0";
+			width = 8;
+			height = 16;
+		}
+		1808
+		{
+			title = "Bush (Short)";
+			sprite = "MUS1A0";
+			width = 16;
+			height = 32;
+		}
+		1809
+		{
+			title = "Bush (Tall)";
+			sprite = "MUS2A0";
+			width = 16;
+			height = 32;
+		}
+		1810
+		{
+			title = "Toad";
+			sprite = "TOADA0";
+			width = 8;
+			height = 32;
+		}
+	}
+
+	christmasdisco
+	{
+		color = 2; // Green
+		title = "Christmas & Disco";
+
+		1850
+		{
+			title = "Christmas Pole";
+			sprite = "XMS1A0";
+			width = 16;
+			height = 40;
+		}
+		1851
+		{
+			title = "Candy Cane";
+			sprite = "XMS2A0";
+			width = 8;
+			height = 32;
+		}
+		1852
+		{
+			blocking = 2;
+			title = "Snowman";
+			sprite = "XMS3A0";
+			width = 16;
+			height = 64;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1853
+		{
+			blocking = 2;
+			title = "Snowman (With Hat)";
+			sprite = "XMS3B0";
+			width = 16;
+			height = 80;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+		1854
+		{
+			title = "Lamp Post";
+			sprite = "XMS4A0";
+			width = 8;
+			height = 120;
+		}
+		1855
+		{
+			title = "Lamp Post (Snow)";
+			sprite = "XMS4B0";
+			width = 8;
+			height = 120;
+		}
+		1856
+		{
+			title = "Hanging Star";
+			sprite = "XMS5A0";
+			width = 4;
+			height = 80;
+			hangs = 1;
+		}
+		1857
+		{
+			title = "Berry Bush (Snow)";
+			sprite = "BUS1B0";
+			width = 16;
+			height = 32;
+		}
+		1858
+		{
+			title = "Bush (Snow)";
+			sprite = "BUS2B0";
+			width = 16;
+			height = 32;
+		}
+		1859
+		{
+			title = "Blueberry Bush (Snow)";
+			sprite = "BUS3B0";
+			width = 16;
+			height = 32;
+		}
+		1875
+		{
+			title = "Disco Ball";
+			sprite = "DBALA0";
+			width = 16;
+			height = 54;
+			hangs = 1;
+		}
+		1876
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Eggman Disco Statue";
+			sprite = "ESTAB1";
+			width = 20;
+			height = 96;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
+	}
+
+	stalagmites
+	{
+		color = 2; // Green
+		title = "Stalagmites";
+		width = 16;
+		height = 40;
+
+		1900
+		{
+			title = "Brown Stalagmite (Tall)";
+			sprite = "STLGA0";
+			width = 16;
+			height = 40;
+		}
+		1901
+		{
+			title = "Brown Stalagmite";
+			sprite = "STLGB0";
+			width = 16;
+			height = 40;
+		}
+		1902
+		{
+			title = "Orange Stalagmite (Tall)";
+			sprite = "STLGC0";
+			width = 16;
+			height = 40;
+		}
+		1903
+		{
+			title = "Orange Stalagmite";
+			sprite = "STLGD0";
+			width = 16;
+			height = 40;
+		}
+		1904
+		{
+			title = "Red Stalagmite (Tall)";
+			sprite = "STLGE0";
+			width = 16;
+			height = 40;
+		}
+		1905
+		{
+			title = "Red Stalagmite";
+			sprite = "STLGF0";
+			width = 16;
+			height = 40;
+		}
+		1906
+		{
+			title = "Gray Stalagmite (Tall)";
+			sprite = "STLGG0";
+			width = 24;
+			height = 96;
+		}
+		1907
+		{
+			title = "Gray Stalagmite";
+			sprite = "STLGH0";
+			width = 16;
+			height = 40;
+		}
+		1908
+		{
+			title = "Blue Stalagmite (Tall)";
+			sprite = "STLGI0";
+			width = 16;
+			height = 40;
+		}
+		1909
+		{
+			title = "Blue Stalagmite";
+			sprite = "STLGJ0";
+			width = 16;
+			height = 40;
+		}
+	}
+
+	hauntedheights
+	{
+		color = 2; // Green
+		title = "Haunted Heights";
+
+		2000
+		{
+			title = "Smashing Spikeball";
+			sprite = "FMCEA0";
+			width = 18;
+			height = 28;
+			angletext = "Initial delay";
+			fixedrotation = 1;
+		}
+		2001
+		{
+			title = "HHZ Grass";
+			sprite = "HHZMA0";
+			width = 16;
+			height = 40;
+		}
+		2002
+		{
+			title = "HHZ Tentacle 1";
+			sprite = "HHZMB0";
+			width = 16;
+			height = 40;
+		}
+		2003
+		{
+			title = "HHZ Tentacle 2";
+			sprite = "HHZMC0";
+			width = 16;
+			height = 40;
+		}
+		2004
+		{
+			title = "HHZ Stalagmite (Tall)";
+			sprite = "HHZME0";
+			width = 16;
+			height = 40;
+		}
+		2005
+		{
+			title = "HHZ Stalagmite (Short)";
+			sprite = "HHZMF0";
+			width = 16;
+			height = 40;
+		}
+		2006
+		{
+			title = "Jack-o'-lantern 1";
+			sprite = "PUMKA0";
+			width = 16;
+			height = 40;
+			flags1text = "[1] Don't flicker";
+		}
+		2007
+		{
+			title = "Jack-o'-lantern 2";
+			sprite = "PUMKB0";
+			width = 16;
+			height = 40;
+			flags1text = "[1] Don't flicker";
+		}
+		2008
+		{
+			title = "Jack-o'-lantern 3";
+			sprite = "PUMKC0";
+			width = 16;
+			height = 40;
+			flags1text = "[1] Don't flicker";
+		}
+		2009
+		{
+			title = "Purple Mushroom";
+			sprite = "SHRMD0";
+			width = 16;
+			height = 48;
+		}
+		2010
+		{
+			title = "HHZ Tree";
+			sprite = "HHPLC0";
+			width = 12;
+			height = 40;
+		}
+	}
+
+	frozenhillside
+	{
+		color = 2; // Green
+		title = "Frozen Hillside";
+
+		2100
+		{
+			title = "Ice Shard (Small)";
+			sprite = "FHZIA0";
+			width = 8;
+			height = 32;
+		}
+		2101
+		{
+			title = "Ice Shard (Large)";
+			sprite = "FHZIB0";
+			width = 8;
+			height = 32;
+		}
+		2102
+		{
+			title = "Crystal Tree (Aqua)";
+			sprite = "TRE3A0";
+			width = 20;
+			height = 200;
+		}
+		2103
+		{
+			title = "Crystal Tree (Pink)";
+			sprite = "TRE3B0";
+			width = 20;
+			height = 200;
+		}
+		2104
+		{
+			title = "Amy Cameo";
+			sprite = "ROSYA1";
+			width = 16;
+			height = 48;
+			flags1text = "[1] Grayscale mode";
+		}
+		2105
+		{
+			title = "Mistletoe";
+			sprite = "XMS6A0";
+			width = 52;
+			height = 106;
+			hangs = 1;
+		}
+	}
+
+	tutorial
+	{
+		color = 2; // Green
+		title = "Tutorial";
+
+		799
+		{
+			title = "Tutorial Plant";
+			sprite = "TUPFH0";
+			width = 40;
+			height = 144;
+			parametertext = "Start frame";
+		}
+	}
+
+	flickies
+	{
+		color = 3; // Teal
+		title = "Flickies";
+		width = 8;
+		height = 20;
+		flags1text = "[1] Move aimlessly";
+		flags4text = "[4] No movement";
+		flags8text = "[8] Hop";
+		angletext = "Radius";
+		fixedrotation = 1;
+
+		2200
+		{
+			title = "Bluebird";
+			sprite = "FL01A1";
+		}
+		2201
+		{
+			title = "Rabbit";
+			sprite = "FL02A1";
+		}
+		2202
+		{
+			title = "Chicken";
+			sprite = "FL03A1";
+		}
+		2203
+		{
+			title = "Seal";
+			sprite = "FL04A1";
+		}
+		2204
+		{
+			title = "Pig";
+			sprite = "FL05A1";
+		}
+		2205
+		{
+			title = "Chipmunk";
+			sprite = "FL06A1";
+		}
+		2206
+		{
+			title = "Penguin";
+			sprite = "FL07A1";
+		}
+		2207
+		{
+			title = "Fish";
+			sprite = "FL08A1";
+			parametertext = "Color";
+		}
+		2208
+		{
+			title = "Ram";
+			sprite = "FL09A1";
+		}
+		2209
+		{
+			title = "Puffin";
+			sprite = "FL10A1";
+		}
+		2210
+		{
+			title = "Cow";
+			sprite = "FL11A1";
+		}
+		2211
+		{
+			title = "Rat";
+			sprite = "FL12A1";
+		}
+		2212
+		{
+			title = "Bear";
+			sprite = "FL13A1";
+		}
+		2213
+		{
+			title = "Dove";
+			sprite = "FL14A1";
+		}
+		2214
+		{
+			title = "Cat";
+			sprite = "FL15A1";
+		}
+		2215
+		{
+			title = "Canary";
+			sprite = "FL16A1";
+		}
+		2216
+		{
+			title = "Spider";
+			sprite = "FS01A1";
+		}
+		2217
+		{
+			title = "Bat";
+			sprite = "FS02A0";
+		}
+	}
+}
+
+//Default things filters
+thingsfilters
+{
+
+	filter0
+	{
+		name = "Player starts";
+		category = "starts";
+		type = -1;
+	}
+
+
+	filter1
+	{
+		name = "Enemies";
+		category = "enemies";
+		type = -1;
+
+	}
+
+
+	filter2
+	{
+		name = "NiGHTS Track";
+		category = "nightstrk";
+		type = -1;
+
+	}
+
+
+	filter3
+	{
+		name = "Normal Gravity";
+		category = "";
+		type = -1;
+
+		fields
+		{
+			2 = false;
+		}
+
+	}
+
+
+	filter4
+	{
+		name = "Reverse Gravity";
+		category = "";
+		type = -1;
+
+		fields
+		{
+			2 = true;
+		}
+
+	}
+}
diff --git a/src/Makefile.d/dedicated.mk b/src/Makefile.d/dedicated.mk
new file mode 100644
index 0000000000000000000000000000000000000000..698ea553191a84775acf23c45715ba8fee6d0862
--- /dev/null
+++ b/src/Makefile.d/dedicated.mk
@@ -0,0 +1,24 @@
+makedir:=$(makedir)/Dedicated
+
+sources+=$(call List,dedicated/Sourcefile)
+
+opts+=-DDEDICATED
+
+ifdef FREEBSD
+# on FreeBSD, we have to link to libpthread explicitly
+libs+=-lpthread
+endif
+
+ifdef MINGW
+libs+=-mconsole
+endif
+
+ifndef NOTHREADS
+opts+=-DHAVE_THREADS
+sources+=dedicated/i_threads.c
+endif
+
+NOOPENMPT=1
+NOGME=1
+NOHW=1
+NOUPNP=1
diff --git a/src/Makefile.d/nix.mk b/src/Makefile.d/nix.mk
index 9adf3f0f14fdc2b052e48053cabedee48a1a7edd..d06544667118ad8cde12d7f41f286e8c9a561a1b 100644
--- a/src/Makefile.d/nix.mk
+++ b/src/Makefile.d/nix.mk
@@ -2,8 +2,6 @@
 # Makefile options for unices (linux, bsd...)
 #
 
-EXENAME?=lsdl2srb2
-
 opts+=-DUNIXCOMMON -DLUA_USE_POSIX
 # Use -rdynamic so a backtrace log shows function names
 # instead of addresses
@@ -14,7 +12,20 @@ opts+=-I/usr/X11R6/include
 libs+=-L/usr/X11R6/lib
 endif
 
+ifndef DEDICATED
+ifndef DUMMY
 SDL?=1
+DEDICATED?=0
+endif
+endif
+
+ifeq (${SDL},1)
+EXENAME?=lsdl2srb2
+endif
+
+ifeq (${DEDICATED},1)
+EXENAME?=lsrb2d
+endif
 
 # In common usage.
 ifdef LINUX
diff --git a/src/Makefile.d/platform.mk b/src/Makefile.d/platform.mk
index d19143e4cf6040dc161b201553db3942b123ee39..d9a2954f60c54b30c121c0467022c1707f720fc0 100644
--- a/src/Makefile.d/platform.mk
+++ b/src/Makefile.d/platform.mk
@@ -65,6 +65,8 @@ endif
 
 ifeq ($(SDL), 1)
 include Makefile.d/sdl.mk
+else ifeq ($(DEDICATED), 1)
+include Makefile.d/dedicated.mk
 else
 include Makefile.d/dummy.mk
 endif
diff --git a/src/Makefile.d/win32.mk b/src/Makefile.d/win32.mk
index 3e60e2416318f752a45261d14d9c349205fdb8ac..51523a212463172ebdffec37e3a798c8fad5a850 100644
--- a/src/Makefile.d/win32.mk
+++ b/src/Makefile.d/win32.mk
@@ -17,7 +17,11 @@ sources+=win32/Srb2win.rc
 opts+=-DSTDC_HEADERS
 libs+=-ladvapi32 -lkernel32 -lmsvcrt -luser32
 
+ifndef DEDICATED
+ifndef DUMMY
 SDL?=1
+endif
+endif
 
 ifndef NOHW
 opts+=-DUSE_WGL_SWAP
diff --git a/src/blua/lauxlib.h b/src/blua/lauxlib.h
index c5ea45a1cbc4c4612909107b4e3fdd5d8f88de0d..87844f4057720b36442a22ee3b419717ce756025 100644
--- a/src/blua/lauxlib.h
+++ b/src/blua/lauxlib.h
@@ -31,6 +31,26 @@ LUALIB_API void (luaL_setn) (lua_State *L, int t, int n);
 /* extra error code for `luaL_load' */
 #define LUA_ERRFILE     (LUA_ERRERR+1)
 
+/* Compiler-specific attributes and other macros */
+
+#ifdef __GNUC__ // __attribute__ ((X))
+
+	#if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && defined (__MINGW32__) // MinGW, >= GCC 4.1
+		#if 0 //defined  (__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO > 0
+			#define FUNCREPORT  __attribute__ ((format(gnu_printf, 2, 3)))
+		#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) // >= GCC 4.4
+			#define FUNCREPORT  __attribute__ ((format(ms_printf, 2, 3)))
+		#else
+			#define FUNCREPORT  __attribute__ ((format(printf, 2, 3)))
+		#endif
+	#else
+		#define FUNCREPORT  __attribute__ ((format(printf, 2, 3)))
+	#endif
+#endif
+
+#ifndef FUNCREPORT
+#define FUNCREPORT
+#endif
 
 typedef struct luaL_Reg {
   const char *name;
@@ -65,7 +85,7 @@ LUALIB_API int   (luaL_newmetatable) (lua_State *L, const char *tname);
 LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
 
 LUALIB_API void (luaL_where) (lua_State *L, int lvl);
-LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
+LUALIB_API FUNCREPORT int (luaL_error) (lua_State *L, const char *fmt, ...);
 
 LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
                                    const char *const lst[]);
diff --git a/src/console.c b/src/console.c
index 93b9fb54d022d71b0917c6a4cd351d2acda4d1e1..0d296ca74138c46564878e911b7892eb64fec145 100644
--- a/src/console.c
+++ b/src/console.c
@@ -120,7 +120,7 @@ static void CONS_backcolor_Change(void);
 #ifdef macintosh
 #define CON_BUFFERSIZE 4096 // my compiler can't handle local vars >32k
 #else
-#define CON_BUFFERSIZE 16384
+#define CON_BUFFERSIZE 32768
 #endif
 
 static char con_buffer[CON_BUFFERSIZE];
@@ -220,7 +220,7 @@ static char *bindtable[NUMINPUTS];
 static void CONS_Bind_f(void)
 {
 	size_t na;
-	char *newcmd;
+	char *newcmd = NULL;
 	//size_t newlen = 0;
 	unsigned int i;
 	INT32 key;
@@ -1339,6 +1339,8 @@ boolean CON_Responder(event_t *ev)
 
 	if (input_sel != input_cur)
 		CON_InputDelSelection();
+	if (ev->type == ev_console)
+		CON_InputAddChar(key);
 
 	return true;
 }
diff --git a/src/d_main.c b/src/d_main.c
index 663817b779657809201e0c082bd52e24b5535b09..83cb425c98bd76b8b931c91db2e1699682c20a0e 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -1295,7 +1295,7 @@ void D_SRB2Main(void)
 #endif
 
 	// for dedicated server
-#if !defined (_WINDOWS) //already check in win_main.c
+#if !defined (_WINDOWS) && !defined (DEDICATED) //already check in win_main.c
 	dedicated = M_CheckParm("-dedicated") != 0;
 #endif
 
diff --git a/src/dedicated/Sourcefile b/src/dedicated/Sourcefile
new file mode 100644
index 0000000000000000000000000000000000000000..2f5dd1a597f38268f583a9bfa1b6da84b1bff69d
--- /dev/null
+++ b/src/dedicated/Sourcefile
@@ -0,0 +1,5 @@
+i_net.c
+i_system.c
+i_main.c
+i_video.c
+i_sound.c
diff --git a/src/dedicated/i_main.c b/src/dedicated/i_main.c
new file mode 100644
index 0000000000000000000000000000000000000000..c79aea96db33d6d7a7fa849e739246f8385a13db
--- /dev/null
+++ b/src/dedicated/i_main.c
@@ -0,0 +1,189 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1993-1996 by id Software, Inc.
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2023 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  d_main.c
+/// \brief Main program, simply calls D_SRB2Main and D_SRB2Loop, the high level loop.
+
+#include "../doomdef.h"
+#include "../m_argv.h"
+#include "../d_main.h"
+#include "../m_misc.h"/* path shit */
+#include "../i_system.h"
+#include "../netcode/d_clisrv.h"
+
+#if defined (__GNUC__) || defined (__unix__)
+#include <unistd.h>
+#endif
+
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+#include <errno.h>
+#endif
+
+#include "time.h" // For log timestamps
+
+#ifdef LOGMESSAGES
+FILE *logstream = NULL;
+char logfilename[1024];
+#endif
+
+#ifndef DOXYGEN
+#ifndef O_TEXT
+#define O_TEXT 0
+#endif
+
+#ifndef O_SEQUENTIAL
+#define O_SEQUENTIAL 0
+#endif
+#endif
+
+#if defined (_WIN32)
+#include "../win32/win_dbg.h"
+typedef BOOL (WINAPI *p_IsDebuggerPresent)(VOID);
+#endif
+
+#ifdef LOGMESSAGES
+static void InitLogging(void)
+{
+	const char *logdir = NULL;
+	time_t my_time;
+	struct tm * timeinfo;
+	const char *format;
+	const char *reldir;
+	int left;
+	boolean fileabs;
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+	const char *link;
+#endif
+
+	logdir = D_Home();
+
+	my_time = time(NULL);
+	timeinfo = localtime(&my_time);
+
+	if (M_CheckParm("-logfile") && M_IsNextParm())
+	{
+		format = M_GetNextParm();
+		fileabs = M_IsPathAbsolute(format);
+	}
+	else
+	{
+		format = "log-%Y-%m-%d_%H-%M-%S.txt";
+		fileabs = false;
+	}
+
+	if (fileabs)
+	{
+		strftime(logfilename, sizeof logfilename, format, timeinfo);
+	}
+	else
+	{
+		if (M_CheckParm("-logdir") && M_IsNextParm())
+			reldir = M_GetNextParm();
+		else
+			reldir = "logs";
+
+		if (M_IsPathAbsolute(reldir))
+		{
+			left = snprintf(logfilename, sizeof logfilename,
+					"%s"PATHSEP, reldir);
+		}
+		else
+#ifdef DEFAULTDIR
+		if (logdir)
+		{
+			left = snprintf(logfilename, sizeof logfilename,
+					"%s"PATHSEP DEFAULTDIR PATHSEP"%s"PATHSEP, logdir, reldir);
+		}
+		else
+#endif/*DEFAULTDIR*/
+		{
+			left = snprintf(logfilename, sizeof logfilename,
+					"."PATHSEP"%s"PATHSEP, reldir);
+		}
+
+		strftime(&logfilename[left], sizeof logfilename - left,
+				format, timeinfo);
+	}
+
+	M_MkdirEachUntil(logfilename,
+			M_PathParts(logdir) - 1,
+			M_PathParts(logfilename) - 1, 0755);
+
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+	logstream = fopen(logfilename, "w");
+#ifdef DEFAULTDIR
+	if (logdir)
+		link = va("%s/"DEFAULTDIR"/latest-log.txt", logdir);
+	else
+#endif/*DEFAULTDIR*/
+		link = "latest-log.txt";
+	unlink(link);
+	if (symlink(logfilename, link) == -1)
+	{
+		I_OutputMsg("Error symlinking latest-log.txt: %s\n", strerror(errno));
+	}
+#else/*defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)*/
+	logstream = fopen("latest-log.txt", "wt+");
+#endif/*defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)*/
+}
+#endif
+
+
+/**	\brief	The main function
+
+	\param	argc	number of arg
+	\param	*argv	string table
+
+	\return	int
+*/
+#if defined (__GNUC__) && (__GNUC__ >= 4)
+#pragma GCC diagnostic ignored "-Wmissing-noreturn"
+#endif
+
+int main(int argc, char **argv)
+{
+	myargc = argc;
+	myargv = argv; /// \todo pull out path to exe from this string
+
+	dedicated = true;
+
+#ifdef LOGMESSAGES
+	if (!M_CheckParm("-nolog"))
+		InitLogging();
+#endif/*LOGMESSAGES*/
+
+	//I_OutputMsg("I_StartupSystem() ...\n");
+	I_StartupSystem();
+#if defined (_WIN32)
+	LoadLibraryA("exchndl.dll");
+#ifndef __MINGW32__
+	prevExceptionFilter = SetUnhandledExceptionFilter(RecordExceptionInfo);
+#endif
+#endif
+
+	// startup SRB2
+	CONS_Printf("Setting up SRB2...\n");
+	D_SRB2Main();
+#ifdef LOGMESSAGES
+	if (!M_CheckParm("-nolog"))
+		CONS_Printf("Logfile: %s\n", logfilename);
+#endif
+	CONS_Printf("Entering main game loop...\n");
+	// never return
+	D_SRB2Loop();
+
+#ifdef BUGTRAP
+	// This is safe even if BT didn't start.
+	ShutdownBugTrap();
+#endif
+
+	// return to OS
+	return 0;
+}
diff --git a/src/dedicated/i_net.c b/src/dedicated/i_net.c
new file mode 100644
index 0000000000000000000000000000000000000000..38049796e22e1980a84bb1fe3166e62827cf03ec
--- /dev/null
+++ b/src/dedicated/i_net.c
@@ -0,0 +1,7 @@
+#include "../netcode/i_net.h"
+
+boolean I_InitNetwork(void)
+{
+	// NOTE: this is no longer used.
+	return false;
+}
diff --git a/src/dedicated/i_sound.c b/src/dedicated/i_sound.c
new file mode 100644
index 0000000000000000000000000000000000000000..36e3d1ebbb28d94dfd9db178950bc906ea6e01e2
--- /dev/null
+++ b/src/dedicated/i_sound.c
@@ -0,0 +1,214 @@
+#include "../i_sound.h"
+
+UINT8 sound_started = 0;
+
+void *I_GetSfx(sfxinfo_t *sfx)
+{
+	(void)sfx;
+	return NULL;
+}
+
+void I_FreeSfx(sfxinfo_t *sfx)
+{
+	(void)sfx;
+}
+
+void I_StartupSound(void){}
+
+void I_ShutdownSound(void){}
+
+void I_UpdateSound(void){};
+
+//
+//  SFX I/O
+//
+
+INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel)
+{
+	(void)id;
+	(void)vol;
+	(void)sep;
+	(void)pitch;
+	(void)priority;
+	(void)channel;
+	return -1;
+}
+
+void I_StopSound(INT32 handle)
+{
+	(void)handle;
+}
+
+boolean I_SoundIsPlaying(INT32 handle)
+{
+	(void)handle;
+	return false;
+}
+
+void I_UpdateSoundParams(INT32 handle, UINT8 vol, UINT8 sep, UINT8 pitch)
+{
+	(void)handle;
+	(void)vol;
+	(void)sep;
+	(void)pitch;
+}
+
+void I_SetSfxVolume(UINT8 volume)
+{
+	(void)volume;
+}
+
+/// ------------------------
+//  MUSIC SYSTEM
+/// ------------------------
+
+void I_InitMusic(void){}
+
+void I_ShutdownMusic(void){}
+
+/// ------------------------
+//  MUSIC PROPERTIES
+/// ------------------------
+
+musictype_t I_SongType(void)
+{
+	return MU_NONE;
+}
+
+boolean I_SongPlaying(void)
+{
+	return false;
+}
+
+boolean I_SongPaused(void)
+{
+	return false;
+}
+
+/// ------------------------
+//  MUSIC EFFECTS
+/// ------------------------
+
+boolean I_SetSongSpeed(float speed)
+{
+	(void)speed;
+	return false;
+}
+
+/// ------------------------
+//  MUSIC SEEKING
+/// ------------------------
+
+UINT32 I_GetSongLength(void)
+{
+	return 0;
+}
+
+boolean I_SetSongLoopPoint(UINT32 looppoint)
+{
+	(void)looppoint;
+	return false;
+}
+
+UINT32 I_GetSongLoopPoint(void)
+{
+	return 0;
+}
+
+boolean I_SetSongPosition(UINT32 position)
+{
+	(void)position;
+	return false;
+}
+
+UINT32 I_GetSongPosition(void)
+{
+	return 0;
+}
+
+/// ------------------------
+//  MUSIC PLAYBACK
+/// ------------------------
+
+boolean I_LoadSong(char *data, size_t len)
+{
+	(void)data;
+	(void)len;
+	return -1;
+}
+
+void I_UnloadSong(void)
+{
+}
+
+boolean I_PlaySong(boolean looping)
+{
+	(void)looping;
+	return false;
+}
+
+void I_StopSong(void)
+{
+}
+
+void I_PauseSong(void)
+{
+}
+
+void I_ResumeSong(void)
+{
+}
+
+void I_SetMusicVolume(UINT8 volume)
+{
+	(void)volume;
+}
+
+boolean I_SetSongTrack(INT32 track)
+{
+	(void)track;
+	return false;
+}
+
+/// ------------------------
+//  MUSIC FADING
+/// ------------------------
+
+void I_SetInternalMusicVolume(UINT8 volume)
+{
+	(void)volume;
+}
+
+void I_StopFadingSong(void)
+{
+}
+
+boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void))
+{
+	(void)target_volume;
+	(void)source_volume;
+	(void)ms;
+	(void)callback;
+	return false;
+}
+
+boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void))
+{
+	(void)target_volume;
+	(void)ms;
+	(void)callback;
+	return false;
+}
+
+boolean I_FadeOutStopSong(UINT32 ms)
+{
+	(void)ms;
+	return false;
+}
+
+boolean I_FadeInPlaySong(UINT32 ms, boolean looping)
+{
+	(void)ms;
+	(void)looping;
+	return false;
+}
diff --git a/src/dedicated/i_system.c b/src/dedicated/i_system.c
new file mode 100644
index 0000000000000000000000000000000000000000..4dbaec8df9f73a5c7d04727942b7f224f4716139
--- /dev/null
+++ b/src/dedicated/i_system.c
@@ -0,0 +1,1575 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) 1993-1996 by id Software, Inc.
+// Portions Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 2014-2023 by Sonic Team Junior.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// Changes by Graue <graue@oceanbase.org> are in the public domain.
+//
+//-----------------------------------------------------------------------------
+/// \file
+/// \brief SRB2 system stuff for dedicated servers
+
+#include <signal.h>
+
+#ifdef _WIN32
+#define RPC_NO_WINDOWS_H
+#include <windows.h>
+#include "../doomtype.h"
+typedef BOOL (WINAPI *p_GetDiskFreeSpaceExA)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
+typedef BOOL (WINAPI *p_IsProcessorFeaturePresent) (DWORD);
+typedef DWORD (WINAPI *p_timeGetTime) (void);
+typedef UINT (WINAPI *p_timeEndPeriod) (UINT);
+typedef HANDLE (WINAPI *p_OpenFileMappingA) (DWORD, BOOL, LPCSTR);
+typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
+
+// This is for RtlGenRandom.
+#define SystemFunction036 NTAPI SystemFunction036
+#include <ntsecapi.h>
+#undef SystemFunction036
+
+// A little more than the minimum sleep duration on Windows.
+// May be incorrect for other platforms, but we don't currently have a way to
+// query the scheduler granularity. SDL will do what's needed to make this as
+// low as possible though.
+#define MIN_SLEEP_DURATION_MS 2.1
+
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef __GNUC__
+#include <unistd.h>
+#elif defined (_MSC_VER)
+#include <direct.h>
+#endif
+#if defined (__unix__) || defined (UNIXCOMMON)
+#include <fcntl.h>
+#endif
+
+#include <stdio.h>
+#ifdef _WIN32
+#include <conio.h>
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4214 4244)
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(default : 4214 4244)
+#endif
+
+#if defined (__unix__) || defined(__APPLE__) || (defined (UNIXCOMMON) && !defined (__HAIKU__))
+#if defined (__linux__)
+#include <sys/vfs.h>
+#else
+#include <sys/param.h>
+#include <sys/mount.h>
+/*For meminfo*/
+#include <sys/types.h>
+#ifdef FREEBSD
+#include <kvm.h>
+#endif
+#include <nlist.h>
+#include <sys/sysctl.h>
+#endif
+#endif
+
+#if defined (__linux__) || (defined (UNIXCOMMON) && !defined (__HAIKU__))
+#ifndef NOTERMIOS
+#include <termios.h>
+#include <sys/ioctl.h> // ioctl
+#define HAVE_TERMIOS
+#endif
+#endif
+
+#if defined (__unix__) || (defined (UNIXCOMMON) && !defined (__APPLE__))
+#include <errno.h>
+#include <sys/wait.h>
+#define NEWSIGNALHANDLER
+#endif
+
+#ifndef NOMUMBLE
+#ifdef __linux__ // need -lrt
+#include <sys/mman.h>
+#ifdef MAP_FAILED
+#define HAVE_SHM
+#endif
+#include <wchar.h>
+#endif
+
+#ifdef _WIN32
+#define HAVE_MUMBLE
+#define WINMUMBLE
+#elif defined (HAVE_SHM)
+#define HAVE_MUMBLE
+#endif
+#endif // NOMUMBLE
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifdef __APPLE__
+#include "macosx/mac_resources.h"
+#endif
+
+#ifndef errno
+#include <errno.h>
+#endif
+
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+#ifndef NOEXECINFO
+#include <execinfo.h>
+#endif
+#include <time.h>
+#define UNIXBACKTRACE
+#endif
+
+// Locations to directly check for srb2.pk3 in
+const char *wadDefaultPaths[] = {
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+	"/usr/local/share/games/SRB2",
+	"/usr/local/games/SRB2",
+	"/usr/share/games/SRB2",
+	"/usr/games/SRB2",
+#elif defined (_WIN32)
+	"c:\\games\\srb2",
+	"\\games\\srb2",
+#endif
+	NULL
+};
+
+// Folders to recurse through looking for srb2.pk3
+const char *wadSearchPaths[] = {
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+	"/usr/local/games",
+	"/usr/games",
+	"/usr/local",
+#elif defined (_WIN32)
+	"c:\\games",
+	"\\games",
+#endif
+	NULL
+};
+
+/**	\brief WAD file to look for
+*/
+#define WADKEYWORD1 "srb2.pk3"
+/**	\brief holds wad path
+*/
+static char returnWadPath[256];
+
+//Alam_GBC: SDL
+
+#include "../doomdef.h"
+#include "../m_misc.h"
+#include "../i_time.h"
+#include "../i_video.h"
+#include "../i_sound.h"
+#include "../i_system.h"
+#include "../i_threads.h"
+#include "../screen.h" //vid.WndParent
+#include "../netcode/d_net.h"
+#include "../netcode/commands.h"
+#include "../g_game.h"
+#include "../filesrch.h"
+
+#include "../i_joy.h"
+
+#include "../m_argv.h"
+
+#include "../r_main.h" // Frame interpolation/uncapped
+#include "../r_fps.h"
+
+#ifdef MAC_ALERT
+#include "macosx/mac_alert.h"
+#endif
+
+#include "../d_main.h"
+
+#if !defined(NOMUMBLE) && defined(HAVE_MUMBLE)
+// Mumble context string
+#include "../netcode/d_clisrv.h"
+#include "../byteptr.h"
+#endif
+
+#define MAX_EXIT_FUNCS 32
+
+UINT8 graphics_started = 0;
+
+UINT8 keyboard_started = 0;
+
+static boolean consolevent = false;
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+static boolean framebuffer = false;
+#endif
+
+static size_t num_exit_funcs;
+static void (*exit_funcs[MAX_EXIT_FUNCS])(void);
+
+static boolean is_quitting = false;
+
+#ifdef __linux__
+#define MEMINFO_FILE "/proc/meminfo"
+#define MEMTOTAL "MemTotal:"
+#define MEMAVAILABLE "MemAvailable:"
+#define MEMFREE "MemFree:"
+#define CACHED "Cached:"
+#define BUFFERS "Buffers:"
+#define SHMEM "Shmem:"
+
+/* Parse the contents of /proc/meminfo (in buf), return value of "name"
+ * (example: MemTotal) */
+static long get_entry(const char* name, const char* buf)
+{
+	long val;
+	char* hit = strstr(buf, name);
+	if (hit == NULL) {
+		return -1;
+	}
+
+	errno = 0;
+	val = strtol(hit + strlen(name), NULL, 10);
+	if (errno != 0) {
+		CONS_Alert(CONS_ERROR, M_GetText("get_entry: strtol() failed: %s\n"), strerror(errno));
+		return -1;
+	}
+	return val;
+}
+#endif
+
+size_t I_GetFreeMem(size_t *total)
+{
+#ifdef FREEBSD
+	u_int v_free_count, v_page_size, v_page_count;
+	size_t size = sizeof(v_free_count);
+	sysctlbyname("vm.stats.vm.v_free_count", &v_free_count, &size, NULL, 0);
+	size = sizeof(v_page_size);
+	sysctlbyname("vm.stats.vm.v_page_size", &v_page_size, &size, NULL, 0);
+	size = sizeof(v_page_count);
+	sysctlbyname("vm.stats.vm.v_page_count", &v_page_count, &size, NULL, 0);
+
+	if (total)
+		*total = v_page_count * v_page_size;
+	return v_free_count * v_page_size;
+#elif defined (SOLARIS)
+	/* Just guess */
+	if (total)
+		*total = 32 << 20;
+	return 32 << 20;
+#elif defined (_WIN32)
+	MEMORYSTATUS info;
+
+	info.dwLength = sizeof (MEMORYSTATUS);
+	GlobalMemoryStatus( &info );
+	if (total)
+		*total = (size_t)info.dwTotalPhys;
+	return (size_t)info.dwAvailPhys;
+#elif defined (__linux__)
+	/* Linux */
+	char buf[1024];
+	char *memTag;
+	size_t freeKBytes;
+	size_t totalKBytes;
+	INT32 n;
+	INT32 meminfo_fd = -1;
+	long Cached;
+	long MemFree;
+	long Buffers;
+	long Shmem;
+	long MemAvailable = -1;
+
+	meminfo_fd = open(MEMINFO_FILE, O_RDONLY);
+	n = read(meminfo_fd, buf, 1023);
+	close(meminfo_fd);
+
+	if (n < 0)
+	{
+		// Error
+		if (total)
+			*total = 0L;
+		return 0;
+	}
+
+	buf[n] = '\0';
+	if ((memTag = strstr(buf, MEMTOTAL)) == NULL)
+	{
+		// Error
+		if (total)
+			*total = 0L;
+		return 0;
+	}
+
+	memTag += sizeof (MEMTOTAL);
+	totalKBytes = (size_t)atoi(memTag);
+
+	if ((memTag = strstr(buf, MEMAVAILABLE)) == NULL)
+	{
+		Cached = get_entry(CACHED, buf);
+		MemFree = get_entry(MEMFREE, buf);
+		Buffers = get_entry(BUFFERS, buf);
+		Shmem = get_entry(SHMEM, buf);
+		MemAvailable = Cached + MemFree + Buffers - Shmem;
+
+		if (MemAvailable == -1)
+		{
+			// Error
+			if (total)
+				*total = 0L;
+			return 0;
+		}
+		freeKBytes = MemAvailable;
+	}
+	else
+	{
+		memTag += sizeof (MEMAVAILABLE);
+		freeKBytes = atoi(memTag);
+	}
+
+	if (total)
+		*total = totalKBytes << 10;
+	return freeKBytes << 10;
+#else
+	// Guess 48 MB.
+	if (total)
+		*total = 48<<20;
+	return 48<<20;
+#endif
+}
+
+void I_Sleep(UINT32 ms)
+{
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+	struct timespec ts = {
+		.tv_sec = ms / 1000,
+		.tv_nsec = ms % 1000 * 1000000,
+	};
+	int status;
+	do status = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts);
+	while (status == EINTR);
+#elif defined (_WIN32)
+	Sleep(ms);
+#else
+	(void)ms;
+#warning No sleep function for this system!
+#endif
+}
+
+void I_SleepDuration(precise_t duration)
+{
+#if defined(__linux__) || defined(__FreeBSD__)
+	UINT64 precision = I_GetPrecisePrecision();
+	struct timespec ts = {
+		.tv_sec = duration / precision,
+		.tv_nsec = duration * 1000000000 / precision % 1000000000,
+	};
+	int status;
+	do status = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts);
+	while (status == EINTR);
+#else
+	UINT64 precision = I_GetPrecisePrecision();
+	INT32 sleepvalue = cv_sleep.value;
+	UINT64 delaygranularity;
+	precise_t cur;
+	precise_t dest;
+
+	{
+		double gran = round(((double)(precision / 1000) * sleepvalue * MIN_SLEEP_DURATION_MS));
+		delaygranularity = (UINT64)gran;
+	}
+
+	cur = I_GetPreciseTime();
+	dest = cur + duration;
+
+	// the reason this is not dest > cur is because the precise counter may wrap
+	// two's complement arithmetic is our friend here, though!
+	// e.g. cur 0xFFFFFFFFFFFFFFFE = -2, dest 0x0000000000000001 = 1
+	// 0x0000000000000001 - 0xFFFFFFFFFFFFFFFE = 3
+	while ((INT64)(dest - cur) > 0)
+	{
+		// If our cv_sleep value exceeds the remaining sleep duration, use the
+		// hard sleep function.
+		if (sleepvalue > 0 && (dest - cur) > delaygranularity)
+		{
+			I_Sleep(sleepvalue);
+		}
+
+		// Otherwise, this is a spinloop.
+
+		cur = I_GetPreciseTime();
+	}
+#endif
+}
+
+precise_t I_GetPreciseTime(void)
+{
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+	struct timespec ts;
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+	return (precise_t)ts.tv_sec * 1000000000 + ts.tv_nsec;
+#elif defined (_WIN32)
+	LARGE_INTEGER counter;
+	QueryPerformanceCounter(&counter);
+	return (precise_t)counter.QuadPart;
+#else
+	return 0;
+#endif
+}
+
+UINT64 I_GetPrecisePrecision(void)
+{
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+	return 1000000000;
+#elif defined (_WIN32)
+	LARGE_INTEGER frequency;
+	QueryPerformanceFrequency(&frequency);
+	return (UINT64)frequency.QuadPart;
+#else
+	return 1000000;
+#endif
+}
+
+void I_GetEvent(void){}
+
+static ticcmd_t emptycmd;
+
+ticcmd_t *I_BaseTiccmd(void)
+{
+	return &emptycmd;
+}
+
+ticcmd_t *I_BaseTiccmd2(void)
+{
+	// dedicated servers don't do 2 player.
+	return NULL;
+}
+
+FUNCNORETURN static void I_QuitStatus(int status)
+{
+	if (is_quitting)
+		abort();
+
+	is_quitting = true;
+	M_SaveConfig(NULL); //save game config, cvars..
+	D_SaveBan(); // save the ban list
+	G_SaveGameData(clientGamedata); // Tails 12-08-2002
+	//added:16-02-98: when recording a demo, should exit using 'q' key,
+	//        but sometimes we forget and use 'F10'.. so save here too.
+
+	// FIXME: can a dedicated server even record demos?
+	if (demorecording)
+		G_CheckDemoStatus();
+	if (metalrecording)
+		G_StopMetalRecording(false);
+
+	D_QuitNetGame();
+	CL_AbortDownloadResume();
+	M_FreePlayerSetupColors();
+	I_ShutdownSystem();
+	W_Shutdown();
+	exit(status);
+}
+
+void I_Quit(void)
+{
+	I_QuitStatus(0);
+}
+
+void I_Error(const char *error, ...)
+{
+	va_list argptr;
+	char *buffer;
+	size_t buflen;
+
+	// Display error message in the console before we start shutting it down
+	va_start(argptr, error);
+	buflen = vsnprintf(NULL, 0, error, argptr);
+	va_end(argptr);
+
+	// do it proper with an actual malloc
+	// (stop abusing the stackbuffer ffs ヽ(。_°)ノ)
+	buffer = malloc(buflen+1);
+	va_start(argptr, error);
+	vsprintf(buffer, error, argptr);
+	va_end(argptr);
+	I_OutputMsg("\nI_Error(): %s\n", buffer);
+	free(buffer);
+	// ---
+
+	I_QuitStatus(-1);
+}
+
+void I_Tactile(FFType Type, const JoyFF_t *Effect)
+{
+	(void)Type;
+	(void)Effect;
+}
+
+void I_Tactile2(FFType Type, const JoyFF_t *Effect)
+{
+	(void)Type;
+	(void)Effect;
+}
+
+void I_JoyScale(void){}
+
+void I_JoyScale2(void){}
+
+void I_InitJoystick(void){}
+
+void I_InitJoystick2(void){}
+
+INT32 I_NumJoys(void)
+{
+	return 0;
+}
+
+const char *I_GetJoyName(INT32 joyindex)
+{
+	(void)joyindex;
+	return NULL;
+}
+
+#ifndef NOMUMBLE
+#ifdef HAVE_MUMBLE
+// Best Mumble positional audio settings:
+// Minimum distance 3.0 m
+// Bloom 175%
+// Maximum distance 80.0 m
+// Minimum volume 50%
+#define DEG2RAD (0.017453292519943295769236907684883l) // TAU/360 or PI/180
+#define MUMBLEUNIT (64.0f) // FRACUNITS in a Meter
+
+static struct {
+	UINT32 uiVersion;
+#ifdef WINMUMBLE
+	DWORD uiTick;
+#else
+	UINT32 uiTick;
+#endif
+	float fAvatarPosition[3];
+	float fAvatarFront[3];
+	float fAvatarTop[3]; // defaults to Y-is-up (only used for leaning)
+	wchar_t name[256]; // game name
+	float fCameraPosition[3];
+	float fCameraFront[3];
+	float fCameraTop[3]; // defaults to Y-is-up (only used for leaning)
+	wchar_t identity[256]; // player id
+	UINT32 context_len;
+	unsigned char context[256]; // server/team
+	wchar_t description[2048]; // game description
+} *mumble = NULL;
+#endif // HAVE_MUMBLE
+
+static void I_SetupMumble(void)
+{
+#ifdef WINMUMBLE
+	HANDLE hMap = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, L"MumbleLink");
+	if (!hMap)
+		return;
+
+	mumble = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(*mumble));
+	if (!mumble)
+		CloseHandle(hMap);
+#elif defined (HAVE_SHM)
+	int shmfd;
+	char memname[256];
+
+	snprintf(memname, 256, "/MumbleLink.%d", getuid());
+	shmfd = shm_open(memname, O_RDWR, S_IRUSR | S_IWUSR);
+
+	if(shmfd < 0)
+		return;
+
+	mumble = mmap(NULL, sizeof(*mumble), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
+	if (mumble == MAP_FAILED)
+		mumble = NULL;
+#endif
+}
+#undef WINMUMBLE
+#endif // NOMUMBLE
+
+void I_UpdateMumble(const mobj_t *mobj, const listener_t listener)
+{
+#ifdef HAVE_MUMBLE
+	// DO NOT BE DECEIVED BY THIS OLD CODE!
+	// despite being untouched for years, it still works as intended after testing.
+	// (i strongly recommend a game of hide & seek with the mumble integration; hilarities are ensured)
+	double angle;
+	fixed_t anglef;
+
+	if (!mumble)
+		return;
+
+	if(mumble->uiVersion != 2) {
+		wcsncpy(mumble->name, L"SRB2 "VERSIONSTRINGW, 256);
+		wcsncpy(mumble->description, L"Sonic Robo Blast 2 with integrated Mumble Link support.", 2048);
+		mumble->uiVersion = 2;
+	}
+	mumble->uiTick++;
+
+	if (!netgame || gamestate != GS_LEVEL) { // Zero out, but never delink.
+		mumble->fAvatarPosition[0] = mumble->fAvatarPosition[1] = mumble->fAvatarPosition[2] = 0.0f;
+		mumble->fAvatarFront[0] = 1.0f;
+		mumble->fAvatarFront[1] = mumble->fAvatarFront[2] = 0.0f;
+		mumble->fCameraPosition[0] = mumble->fCameraPosition[1] = mumble->fCameraPosition[2] = 0.0f;
+		mumble->fCameraFront[0] = 1.0f;
+		mumble->fCameraFront[1] = mumble->fCameraFront[2] = 0.0f;
+		return;
+	}
+
+	{
+		UINT8 *p = mumble->context;
+		WRITEMEM(p, server_context, 8);
+		WRITEINT16(p, gamemap);
+		mumble->context_len = (UINT32)(p - mumble->context);
+	}
+
+	if (mobj) {
+		mumble->fAvatarPosition[0] = FIXED_TO_FLOAT(mobj->x) / MUMBLEUNIT;
+		mumble->fAvatarPosition[1] = FIXED_TO_FLOAT(mobj->z) / MUMBLEUNIT;
+		mumble->fAvatarPosition[2] = FIXED_TO_FLOAT(mobj->y) / MUMBLEUNIT;
+
+		anglef = AngleFixed(mobj->angle);
+		angle = FIXED_TO_FLOAT(anglef)*DEG2RAD;
+		mumble->fAvatarFront[0] = (float)cos(angle);
+		mumble->fAvatarFront[1] = 0.0f;
+		mumble->fAvatarFront[2] = (float)sin(angle);
+	} else {
+		mumble->fAvatarPosition[0] = mumble->fAvatarPosition[1] = mumble->fAvatarPosition[2] = 0.0f;
+		mumble->fAvatarFront[0] = 1.0f;
+		mumble->fAvatarFront[1] = mumble->fAvatarFront[2] = 0.0f;
+	}
+
+	mumble->fCameraPosition[0] = FIXED_TO_FLOAT(listener.x) / MUMBLEUNIT;
+	mumble->fCameraPosition[1] = FIXED_TO_FLOAT(listener.z) / MUMBLEUNIT;
+	mumble->fCameraPosition[2] = FIXED_TO_FLOAT(listener.y) / MUMBLEUNIT;
+
+	anglef = AngleFixed(listener.angle);
+	angle = FIXED_TO_FLOAT(anglef)*DEG2RAD;
+	mumble->fCameraFront[0] = (float)cos(angle);
+	mumble->fCameraFront[1] = 0.0f;
+	mumble->fCameraFront[2] = (float)sin(angle);
+#else
+	(void)mobj;
+	(void)listener;
+#endif // HAVE_MUMBLE
+}
+
+void I_StartupMouse(void){}
+
+void I_StartupMouse2(void){}
+
+INT32 I_GetKey(void)
+{
+	return 0;
+}
+
+void I_StartupTimer(void){}
+
+void I_AddExitFunc(void (*func)())
+{
+	I_Assert(num_exit_funcs < sizeof(exit_funcs) / sizeof(exit_funcs[0]));
+	exit_funcs[num_exit_funcs++] = func;
+}
+
+void I_RemoveExitFunc(void (*func)())
+{
+	// NOTE: this isn't even used, so no need implementing this.
+	(void)func;
+}
+
+#ifdef HAVE_TERMIOS
+typedef struct
+{
+	size_t cursor;
+	char buffer[256];
+} feild_t;
+
+static feild_t tty_con;
+
+// when printing general stuff to stdout stderr (Sys_Printf)
+//   we need to disable the tty console stuff
+// this increments so we can recursively disable
+static INT32 ttycon_hide = 0;
+// some key codes that the terminal may be using
+// TTimo NOTE: I'm not sure how relevant this is
+static INT32 tty_erase;
+static INT32 tty_eof;
+
+static struct termios tty_tc;
+
+// =============================================================
+// tty console routines
+// NOTE: if the user is editing a line when something gets printed to the early console then it won't look good
+//   so we provide tty_Clear and tty_Show to be called before and after a stdout or stderr output
+// =============================================================
+
+// flush stdin, I suspect some terminals are sending a LOT of garbage
+// FIXME TTimo relevant?
+#if 0
+static inline void tty_FlushIn(void)
+{
+	char key;
+	while (read(STDIN_FILENO, &key, 1)!=-1);
+}
+#endif
+
+// do a backspace
+// TTimo NOTE: it seems on some terminals just sending '\b' is not enough
+//   so for now, in any case we send "\b \b" .. yeah well ..
+//   (there may be a way to find out if '\b' alone would work though)
+static void tty_Back(void)
+{
+	char key;
+	ssize_t d;
+	key = '\b';
+	d = write(STDOUT_FILENO, &key, 1);
+	key = ' ';
+	d = write(STDOUT_FILENO, &key, 1);
+	key = '\b';
+	d = write(STDOUT_FILENO, &key, 1);
+	(void)d;
+}
+
+static void tty_Clear(void)
+{
+	size_t i;
+	if (tty_con.cursor>0)
+	{
+		for (i=0; i<tty_con.cursor; i++)
+		{
+			tty_Back();
+		}
+	}
+
+}
+
+// clear the display of the line currently edited
+// bring cursor back to beginning of line
+static inline void tty_Hide(void)
+{
+	//I_Assert(consolevent);
+	if (ttycon_hide)
+	{
+		ttycon_hide++;
+		return;
+	}
+	tty_Clear();
+	ttycon_hide++;
+}
+
+// show the current line
+// FIXME TTimo need to position the cursor if needed??
+static inline void tty_Show(void)
+{
+	size_t i;
+	ssize_t d;
+	//I_Assert(consolevent);
+	I_Assert(ttycon_hide>0);
+	ttycon_hide--;
+	if (ttycon_hide == 0 && tty_con.cursor)
+	{
+		for (i=0; i<tty_con.cursor; i++)
+		{
+			d = write(STDOUT_FILENO, tty_con.buffer+i, 1);
+		}
+	}
+	(void)d;
+}
+
+// never exit without calling this, or your terminal will be left in a pretty bad state
+static void I_ShutdownConsole(void)
+{
+	if (consolevent)
+	{
+		I_OutputMsg("Shutdown tty console\n");
+		consolevent = false;
+		tcsetattr (STDIN_FILENO, TCSADRAIN, &tty_tc);
+	}
+}
+
+static void I_StartupConsole(void)
+{
+	struct termios tc;
+
+	// TTimo
+	// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=390 (404)
+	// then SIGTTIN or SIGTOU is emitted, if not catched, turns into a SIGSTP
+	signal(SIGTTIN, SIG_IGN);
+	signal(SIGTTOU, SIG_IGN);
+
+	consolevent = !M_CheckParm("-noconsole");
+	framebuffer = M_CheckParm("-framebuffer");
+
+	if (framebuffer)
+		consolevent = false;
+
+	if (!consolevent) return;
+
+	if (isatty(STDIN_FILENO)!=1)
+	{
+		I_OutputMsg("stdin is not a tty, tty console mode failed\n");
+		consolevent = false;
+		return;
+	}
+	memset(&tty_con, 0x00, sizeof(tty_con));
+	tcgetattr (0, &tty_tc);
+	tty_erase = tty_tc.c_cc[VERASE];
+	tty_eof = tty_tc.c_cc[VEOF];
+	tc = tty_tc;
+	/*
+	 ECHO: don't echo input characters
+	 ICANON: enable canonical mode.  This  enables  the  special
+	  characters  EOF,  EOL,  EOL2, ERASE, KILL, REPRINT,
+	  STATUS, and WERASE, and buffers by lines.
+	 ISIG: when any of the characters  INTR,  QUIT,  SUSP,  or
+	  DSUSP are received, generate the corresponding signal
+	*/
+	tc.c_lflag &= ~(ECHO | ICANON);
+	/*
+	 ISTRIP strip off bit 8
+	 INPCK enable input parity checking
+	 */
+	tc.c_iflag &= ~(ISTRIP | INPCK);
+	tc.c_cc[VMIN] = 0; //1?
+	tc.c_cc[VTIME] = 0;
+	tcsetattr (0, TCSADRAIN, &tc);
+}
+
+static void I_GetConsoleEvents(void)
+{
+	// we use this when sending back commands
+	event_t ev = {0};
+	char key = 0;
+	ssize_t d;
+
+	if (!consolevent)
+		return;
+
+	ev.type = ev_console;
+	ev.key = 0;
+	if (read(STDIN_FILENO, &key, 1) == -1 || !key)
+		return;
+
+	// we have something
+	// backspace?
+	// NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere
+	if ((key == tty_erase) || (key == 127) || (key == 8))
+	{
+		if (tty_con.cursor > 0)
+		{
+			tty_con.cursor--;
+			tty_con.buffer[tty_con.cursor] = '\0';
+			tty_Back();
+		}
+		ev.key = KEY_BACKSPACE;
+	}
+	else if (key < ' ') // check if this is a control char
+	{
+		if (key == '\n')
+		{
+			tty_Clear();
+			tty_con.cursor = 0;
+			ev.key = KEY_ENTER;
+		}
+		else return;
+	}
+	else if (tty_con.cursor < sizeof(tty_con.buffer))
+	{
+		// push regular character
+		ev.key = tty_con.buffer[tty_con.cursor] = key;
+		tty_con.cursor++;
+		// print the current line (this is differential)
+		d = write(STDOUT_FILENO, &key, 1);
+	}
+	if (ev.key) D_PostEvent(&ev);
+	//tty_FlushIn();
+	(void)d;
+}
+
+#elif defined (_WIN32)
+static BOOL I_ReadyConsole(HANDLE ci)
+{
+	DWORD gotinput;
+	if (ci == INVALID_HANDLE_VALUE) return FALSE;
+	if (WaitForSingleObject(ci,0) != WAIT_OBJECT_0) return FALSE;
+	if (GetFileType(ci) != FILE_TYPE_CHAR) return FALSE;
+	if (!GetConsoleMode(ci, &gotinput)) return FALSE;
+	return (GetNumberOfConsoleInputEvents(ci, &gotinput) && gotinput);
+}
+
+static boolean entering_con_command = false;
+
+static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co)
+{
+	event_t event;
+	CONSOLE_SCREEN_BUFFER_INFO CSBI;
+	DWORD t;
+
+	memset(&event,0x00,sizeof (event));
+
+	if (evt.bKeyDown)
+	{
+		event.type = ev_console;
+		entering_con_command = true;
+		switch (evt.wVirtualKeyCode)
+		{
+			case VK_ESCAPE:
+			case VK_TAB:
+				event.key = KEY_NULL;
+				break;
+			case VK_RETURN:
+				entering_con_command = false;
+				/* FALLTHRU */
+			default:
+				//event.key = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char
+				event.key = evt.uChar.AsciiChar;
+		}
+		if (co != INVALID_HANDLE_VALUE && GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &t))
+		{
+			if (event.key && event.key != KEY_LSHIFT && event.key != KEY_RSHIFT)
+			{
+#ifdef _UNICODE
+				WriteConsole(co, &evt.uChar.UnicodeChar, 1, &t, NULL);
+#else
+				WriteConsole(co, &evt.uChar.AsciiChar, 1 , &t, NULL);
+#endif
+			}
+			if (evt.wVirtualKeyCode == VK_BACK
+				&& GetConsoleScreenBufferInfo(co,&CSBI))
+			{
+				WriteConsoleOutputCharacterA(co, " ",1, CSBI.dwCursorPosition, &t);
+			}
+		}
+	}
+	if (event.key) D_PostEvent(&event);
+}
+
+static void I_GetConsoleEvents(void)
+{
+	HANDLE ci = GetStdHandle(STD_INPUT_HANDLE);
+	HANDLE co = GetStdHandle(STD_OUTPUT_HANDLE);
+	INPUT_RECORD input;
+	DWORD t;
+
+	while (I_ReadyConsole(ci) && ReadConsoleInput(ci, &input, 1, &t) && t)
+	{
+		switch (input.EventType)
+		{
+			case KEY_EVENT:
+				Impl_HandleKeyboardConsoleEvent(input.Event.KeyEvent, co);
+				break;
+			case MOUSE_EVENT:
+			case WINDOW_BUFFER_SIZE_EVENT:
+			case MENU_EVENT:
+			case FOCUS_EVENT:
+				break;
+		}
+	}
+}
+
+static void I_StartupConsole(void)
+{
+	HANDLE ci, co;
+	const INT32 ded = M_CheckParm("-dedicated");
+	BOOL gotConsole = FALSE;
+	if (M_CheckParm("-console") || ded)
+		gotConsole = AllocConsole();
+#ifdef _DEBUG
+	else if (M_CheckParm("-noconsole") && !ded)
+#else
+	else if (!M_CheckParm("-console") && !ded)
+#endif
+	{
+		FreeConsole();
+		gotConsole = FALSE;
+	}
+
+	if (gotConsole)
+	{
+		SetConsoleTitleA("SRB2 Console");
+		consolevent = true;
+	}
+
+	//Let get the real console HANDLE, because Mingw's Bash is bad!
+	ci = CreateFile(TEXT("CONIN$") ,               GENERIC_READ, FILE_SHARE_READ,  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+	co = CreateFile(TEXT("CONOUT$"), GENERIC_WRITE|GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+	if (ci != INVALID_HANDLE_VALUE)
+	{
+		const DWORD CM = ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT;
+		SetStdHandle(STD_INPUT_HANDLE, ci);
+		if (GetFileType(ci) == FILE_TYPE_CHAR)
+			SetConsoleMode(ci, CM); //default mode but no ENABLE_MOUSE_INPUT
+	}
+	if (co != INVALID_HANDLE_VALUE)
+	{
+		SetStdHandle(STD_OUTPUT_HANDLE, co);
+		SetStdHandle(STD_ERROR_HANDLE, co);
+	}
+}
+static inline void I_ShutdownConsole(void){}
+#else
+static void I_GetConsoleEvents(void){}
+static inline void I_StartupConsole(void)
+{
+#ifdef _DEBUG
+	consolevent = !M_CheckParm("-noconsole");
+#else
+	consolevent = M_CheckParm("-console");
+#endif
+
+	framebuffer = M_CheckParm("-framebuffer");
+
+	if (framebuffer)
+		consolevent = false;
+}
+static inline void I_ShutdownConsole(void){}
+#endif
+
+void I_OutputMsg(const char *fmt, ...)
+{
+	size_t len;
+	char *txt;
+	va_list argptr;
+
+	va_start(argptr,fmt);
+	len = vsnprintf(NULL, 0, fmt, argptr);
+	va_end(argptr);
+	txt = malloc(len+1);
+	va_start(argptr,fmt);
+	vsprintf(txt, fmt, argptr);
+	va_end(argptr);
+
+#if defined (_WIN32) && defined (_MSC_VER)
+	OutputDebugStringA(txt);
+#endif
+
+#ifdef LOGMESSAGES
+	if (logstream)
+	{
+		fwrite(txt, len, 1, logstream);
+		fflush(logstream);
+	}
+#endif
+
+#if defined (_WIN32)
+#ifdef DEBUGFILE
+	if (debugfile != stderr)
+#endif
+	{
+		HANDLE co = GetStdHandle(STD_OUTPUT_HANDLE);
+		DWORD bytesWritten;
+
+		if (co == INVALID_HANDLE_VALUE)
+		{
+			free(txt);
+			return;
+		}
+
+		if (GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &bytesWritten))
+		{
+			static COORD coordNextWrite = {0,0};
+			LPVOID oldLines = NULL;
+			INT oldLength;
+			CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+			// Save the lines that we're going to obliterate.
+			GetConsoleScreenBufferInfo(co, &csbi);
+			oldLength = csbi.dwSize.X * (csbi.dwCursorPosition.Y - coordNextWrite.Y) + csbi.dwCursorPosition.X - coordNextWrite.X;
+
+			if (oldLength > 0)
+			{
+				LPVOID blank = malloc(oldLength);
+				if (!blank)
+				{
+					free(txt);
+					return;
+				}
+				memset(blank, ' ', oldLength); // Blank out.
+				oldLines = malloc(oldLength*sizeof(TCHAR));
+				if (!oldLines)
+				{
+					free(blank);
+					free(txt);
+					return;
+				}
+
+				ReadConsoleOutputCharacter(co, oldLines, oldLength, coordNextWrite, &bytesWritten);
+
+				// Move to where we what to print - which is where we would've been,
+				// had console input not been in the way,
+				SetConsoleCursorPosition(co, coordNextWrite);
+
+				WriteConsoleA(co, blank, oldLength, &bytesWritten, NULL);
+				free(blank);
+
+				// And back to where we want to print again.
+				SetConsoleCursorPosition(co, coordNextWrite);
+			}
+
+			// Actually write the string now!
+			WriteConsoleA(co, txt, (DWORD)len, &bytesWritten, NULL);
+
+			// Next time, output where we left off.
+			GetConsoleScreenBufferInfo(co, &csbi);
+			coordNextWrite = csbi.dwCursorPosition;
+
+			// Restore what was overwritten.
+			if (oldLines && entering_con_command)
+				WriteConsole(co, oldLines, oldLength, &bytesWritten, NULL);
+			if (oldLines) free(oldLines);
+		}
+		else // Redirected to a file.
+			WriteFile(co, txt, (DWORD)len, &bytesWritten, NULL);
+	}
+#else
+#ifdef HAVE_TERMIOS
+	if (consolevent)
+	{
+		tty_Hide();
+	}
+#endif
+
+	if (!framebuffer)
+		fprintf(stderr, "%s", txt);
+#ifdef HAVE_TERMIOS
+	if (consolevent)
+	{
+		tty_Show();
+	}
+#endif
+
+	// 2004-03-03 AJR Since not all messages end in newline, some were getting displayed late.
+	if (!framebuffer)
+		fflush(stderr);
+
+#endif
+	free(txt);
+}
+
+void I_OsPolling(void)
+{
+	if (consolevent)
+		I_GetConsoleEvents();
+}
+
+FUNCNORETURN static ATTRNORETURN void quit_handler(int num)
+{
+	(void)num;
+	// FIXME: set a flag to quit later.
+	// this is risky since some functions can softlock in a signal handler: https://www.unix.com/man-page/posix/7/signal-safety/
+	I_Quit();
+}
+
+static void I_RegisterSignals (void)
+{
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+	// signal is deprecated, long live sigaction
+	struct sigaction sig;
+	sigemptyset(&sig.sa_mask);
+	sig.sa_flags = 0;
+	sig.sa_handler = quit_handler;
+	sigaction(SIGINT, &sig, NULL);
+	sig.sa_handler = quit_handler;
+	sigaction(SIGTERM, &sig, NULL);
+#else
+	signal(SIGINT, quit_handler);
+	signal(SIGBREAK, quit_handler);
+	signal(SIGTERM, quit_handler);
+#endif
+}
+
+INT32 I_StartupSystem(void)
+{
+#ifdef HAVE_THREADS
+	I_start_threads();
+	I_AddExitFunc(I_stop_threads);
+#endif
+	I_StartupConsole();
+	I_RegisterSignals();
+#ifndef NOMUMBLE
+	I_SetupMumble();
+#endif
+	return 0;
+}
+
+void I_ShutdownSystem(void)
+{
+	INT32 c;
+
+	I_ShutdownConsole();
+
+	for (c = MAX_QUIT_FUNCS-1; c >= 0; c--)
+		if (exit_funcs[c])
+			(*exit_funcs[c])();
+#ifdef LOGMESSAGES
+	if (logstream)
+	{
+		I_OutputMsg("I_ShutdownSystem(): end of logstream.\n");
+		fclose(logstream);
+		logstream = NULL;
+	}
+#endif
+}
+
+void I_GetDiskFreeSpace(INT64* freespace)
+{
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
+#if defined (SOLARIS) || defined (__HAIKU__)
+	*freespace = INT32_MAX;
+	return;
+#else // Both Linux and BSD have this, apparently.
+	struct statfs stfs;
+	if (statfs(srb2home, &stfs) == -1)
+	{
+		*freespace = INT32_MAX;
+		return;
+	}
+	*freespace = stfs.f_bavail * stfs.f_bsize;
+#endif
+#elif defined (_WIN32)
+	static p_GetDiskFreeSpaceExA pfnGetDiskFreeSpaceEx = NULL;
+	static boolean testwin95 = false;
+	ULARGE_INTEGER usedbytes, lfreespace;
+
+	if (!testwin95)
+	{
+		pfnGetDiskFreeSpaceEx = (p_GetDiskFreeSpaceExA)(LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDiskFreeSpaceExA");
+		testwin95 = true;
+	}
+	if (pfnGetDiskFreeSpaceEx)
+	{
+		if (pfnGetDiskFreeSpaceEx(srb2home, &lfreespace, &usedbytes, NULL))
+			*freespace = lfreespace.QuadPart;
+		else
+			*freespace = INT32_MAX;
+	}
+	else
+	{
+		DWORD SectorsPerCluster, BytesPerSector, NumberOfFreeClusters, TotalNumberOfClusters;
+		GetDiskFreeSpace(NULL, &SectorsPerCluster, &BytesPerSector,
+						 &NumberOfFreeClusters, &TotalNumberOfClusters);
+		*freespace = BytesPerSector*SectorsPerCluster*NumberOfFreeClusters;
+	}
+#else // Dummy for platform independent; 1GB should be enough
+	*freespace = 1024*1024*1024;
+#endif
+}
+
+char *I_GetUserName(void)
+{
+	static char username[MAXPLAYERNAME+1];
+	char *p;
+#ifdef _WIN32
+	DWORD i = MAXPLAYERNAME;
+
+	if (!GetUserNameA(username, &i))
+#endif
+	{
+		p = I_GetEnv("USER");
+		if (!p)
+		{
+			p = I_GetEnv("user");
+			if (!p)
+			{
+				p = I_GetEnv("USERNAME");
+				if (!p)
+				{
+					p = I_GetEnv("username");
+					if (!p)
+					{
+						return NULL;
+					}
+				}
+			}
+		}
+		strncpy(username, p, MAXPLAYERNAME);
+	}
+
+
+	if (strcmp(username, "") != 0)
+		return username;
+	return NULL; // dummy for platform independent version
+}
+
+INT32 I_mkdir(const char *dirname, INT32 unixright)
+{
+//[segabor]
+#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) || defined (__CYGWIN__)
+	return mkdir(dirname, unixright);
+#elif defined (_WIN32)
+	UNREFERENCED_PARAMETER(unixright); /// \todo should implement ntright under nt...
+	return CreateDirectoryA(dirname, NULL);
+#else
+	(void)dirname;
+	(void)unixright;
+	return false;
+#endif
+}
+
+const CPUInfoFlags *I_CPUInfo(void)
+{
+	return NULL;
+}
+
+static boolean isWadPathOk(const char *path)
+{
+	char *wad3path = malloc(256);
+
+	if (!wad3path)
+		return false;
+
+	sprintf(wad3path, pandf, path, WADKEYWORD1);
+
+	if (FIL_ReadFileOK(wad3path))
+	{
+		free(wad3path);
+		return true;
+	}
+
+	free(wad3path);
+	return false;
+}
+
+static void pathonly(char *s)
+{
+	size_t j;
+
+	for (j = strlen(s); j != (size_t)-1; j--)
+		if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/'))
+		{
+			if (s[j] == ':') s[j+1] = 0;
+			else s[j] = 0;
+			return;
+		}
+}
+
+static const char *searchWad(const char *searchDir)
+{
+	static char tempsw[256] = "";
+	filestatus_t fstemp;
+
+	strcpy(tempsw, WADKEYWORD1);
+	fstemp = filesearch(tempsw,searchDir,NULL,true,20);
+	if (fstemp == FS_FOUND)
+	{
+		pathonly(tempsw);
+		return tempsw;
+	}
+
+	return NULL;
+}
+
+#define CHECKWADPATH(ret) \
+do { \
+	I_OutputMsg(",%s", returnWadPath); \
+	if (isWadPathOk(returnWadPath)) \
+		return ret; \
+} while (0)
+
+#define SEARCHWAD(str) \
+do { \
+	WadPath = searchWad(str); \
+	if (WadPath) \
+		return WadPath; \
+} while (0)
+
+static const char *locateWad(void)
+{
+	const char *envstr;
+	const char *WadPath;
+	int i;
+
+	I_OutputMsg("SRB2WADDIR");
+	// does SRB2WADDIR exist?
+	if (((envstr = I_GetEnv("SRB2WADDIR")) != NULL) && isWadPathOk(envstr))
+		return envstr;
+
+#ifndef NOCWD
+	// examine current dir
+	strcpy(returnWadPath, ".");
+	CHECKWADPATH(NULL);
+#endif
+
+#ifdef __APPLE__
+	OSX_GetResourcesPath(returnWadPath);
+	CHECKWADPATH(returnWadPath);
+#endif
+
+	// examine default dirs
+	for (i = 0; wadDefaultPaths[i]; i++)
+	{
+		strcpy(returnWadPath, wadDefaultPaths[i]);
+		CHECKWADPATH(returnWadPath);
+	}
+
+#ifndef NOHOME
+	// find in $HOME
+	I_OutputMsg(",HOME");
+	if ((envstr = I_GetEnv("HOME")) != NULL)
+		SEARCHWAD(envstr);
+#endif
+
+	// search paths
+	for (i = 0; wadSearchPaths[i]; i++)
+	{
+		I_OutputMsg(", in:%s", wadSearchPaths[i]);
+		SEARCHWAD(wadSearchPaths[i]);
+	}
+
+	// if nothing was found
+	return NULL;
+}
+
+const char *I_LocateWad(void)
+{
+	const char *waddir;
+
+	I_OutputMsg("Looking for WADs in: ");
+	// FIXME: should we go for a command parameter instead for dedicated servers?
+	waddir = locateWad();
+	I_OutputMsg("\n");
+
+	if (waddir)
+	{
+		// change to the directory where we found srb2.pk3
+#if defined (_WIN32)
+		SetCurrentDirectoryA(waddir);
+#else
+		if (chdir(waddir) == -1)
+			I_OutputMsg("Couldn't change working directory\n");
+#endif
+	}
+	return waddir;
+}
+
+void I_GetJoystickEvents(void){}
+
+void I_GetJoystick2Events(void){}
+
+void I_GetMouseEvents(void){}
+
+void I_UpdateMouseGrab(void){}
+
+char *I_GetEnv(const char *name)
+{
+	return getenv(name);
+}
+
+INT32 I_PutEnv(char *name)
+{
+	return putenv(name);
+}
+
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+const char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
+size_t I_GetRandomBytes(char *destination, size_t count)
+{
+#if defined (__unix__) || defined (UNIXCOMMON) || defined(__APPLE__)
+	FILE *rndsource;
+	size_t actual_bytes;
+
+	if (!(rndsource = fopen("/dev/urandom", "r")))
+		if (!(rndsource = fopen("/dev/random", "r")))
+			actual_bytes = 0;
+
+	if (rndsource)
+	{
+		actual_bytes = fread(destination, 1, count, rndsource);
+		fclose(rndsource);
+	}
+
+	if (actual_bytes == 0)
+		I_OutputMsg("I_GetRandomBytes(): couldn't get any random bytes");
+
+	return actual_bytes;
+#elif defined (_WIN32)
+	if (RtlGenRandom(destination, count))
+		return count;
+
+	I_OutputMsg("I_GetRandomBytes(): couldn't get any random bytes");
+	return 0;
+#else
+	#warning SDL I_GetRandomBytes is not implemented on this platform.
+	return 0;
+#endif
+}
+
+void I_RegisterSysCommands(void){}
+
+char const *I_GetSysName(void)
+{
+	// reference: https://sourceforge.net/p/predef/wiki/OperatingSystems/
+#if defined(_WIN32) || defined(__CYGWIN__)
+	return "Windows";
+#elif defined(__APPLE__)
+	return "Mac OS";
+#elif defined(__linux__)
+	return "Linux";
+#elif defined(__FreeBSD__)
+	return "FreeBSD";
+#elif defined(__OpenBSD__)
+	return "OpenBSD";
+#elif defined(__NetBSD__)
+	return "NetBSD";
+#elif defined(__DragonFly__)
+	return "DragonFly BSD";
+#elif defined(__gnu_hurd__)
+	return "GNU Hurd"; // for anyone mental enough to set up an SRB2 server on GNU Hurd
+#elif defined(__hpux)
+	return "HP-UX";
+#elif defined(EPLAN9)
+	return "Plan 9";
+#elif defined(__HAIKU__)
+	return "Haiku";
+#elif defined(__BEOS__)
+	return "BeOS";
+#elif defined(__minix)
+	return "Minix";
+#elif defined(__sun)
+#if defined(__SVR4) || defined(__srv4__)
+	return "Solaris"; // this would be so cursed...
+#else
+	return "SunOS";
+#endif
+#elif defined(_AIX)
+	return "AIX";
+#elif defined(__SYLLABLE__)
+	return "SyllableOS"; // RIP SyllableOS, i still miss you ;-;
+#else
+	return "Unknown";
+#endif
+}
+
+void I_GetCursorPosition(INT32 *x, INT32 *y)
+{
+	(void)x;
+	(void)y;
+}
+
+#include "../sdl/dosstr.c"
+
diff --git a/src/dedicated/i_threads.c b/src/dedicated/i_threads.c
new file mode 100644
index 0000000000000000000000000000000000000000..6902e23a52da14cf1861df5c1f194a4ea617af17
--- /dev/null
+++ b/src/dedicated/i_threads.c
@@ -0,0 +1,359 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2020-2023 by James R.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  i_threads.c
+/// \brief Multithreading abstraction
+
+#if defined (__unix__) || (!defined(__APPLE__) && defined (UNIXCOMMON))
+
+#include <pthread.h>
+
+#include "../i_threads.h"
+#include "../doomdef.h"
+#include "../doomtype.h"
+
+typedef struct thread_s thread_t;
+
+struct thread_s
+{
+	thread_t *next;
+	void *userdata;
+	I_thread_fn func;
+	pthread_t thread;
+};
+
+// we use a linked list to avoid moving memory blocks when allocating new threads.
+static thread_t *thread_list;
+static pthread_mutex_t thread_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static void *HandleThread(void *data)
+{
+	thread_t *thread = data;
+	thread->func(thread->userdata);
+
+	pthread_mutex_lock(&thread_lock);
+	thread->func = NULL;
+	pthread_mutex_unlock(&thread_lock);
+	return NULL;
+}
+
+void I_spawn_thread(const char *name, I_thread_fn entry, void *userdata)
+{
+	thread_t *thread;
+	(void)name;
+	pthread_mutex_lock(&thread_lock);
+	thread = thread_list;
+	while (thread != NULL)
+	{
+		if (thread->func == NULL)
+		{
+			// join with the exited thread to release it's resources.
+			pthread_join(thread->thread, NULL);
+			break;
+		}
+		thread = thread->next;
+	}
+	if (thread == NULL)
+	{
+		thread = malloc(sizeof(thread_t));
+		thread->next = thread_list;
+		thread_list = thread;
+	}
+
+	thread->func = entry;
+	thread->userdata = userdata;
+	pthread_create(&thread->thread, NULL, HandleThread, thread);
+	pthread_mutex_unlock(&thread_lock);
+}
+
+int I_thread_is_stopped(void)
+{
+	thread_t *thread;
+	pthread_mutex_lock(&thread_lock);
+	thread = thread_list;
+	while (thread != NULL)
+	{
+		if (thread->func != NULL)
+		{
+			pthread_mutex_unlock(&thread_lock);
+			return false;
+		}
+		thread = thread->next;
+	}
+	pthread_mutex_unlock(&thread_lock);
+	return true;
+}
+
+void I_start_threads(void)
+{
+}
+
+void I_stop_threads(void)
+{
+	thread_t *thread = thread_list;
+	while (thread != NULL)
+	{
+		// join with all threads here, since finished threads haven't been awaited yet.
+		pthread_join(thread->thread, NULL);
+		thread = thread->next;
+	}
+}
+
+void I_lock_mutex(I_mutex *anchor)
+{
+	pthread_mutex_lock(&thread_lock);
+	if (*anchor == NULL)
+	{
+		pthread_mutexattr_t attr;
+		pthread_mutexattr_init(&attr);
+
+		// SRB2 relies on lock recursion, so we need a mutex configured for that.
+		pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+		*anchor = malloc(sizeof(pthread_mutex_t));
+		pthread_mutex_init(*anchor, &attr);
+		pthread_mutexattr_destroy(&attr);
+	}
+	pthread_mutex_unlock(&thread_lock);
+	pthread_mutex_lock(*anchor);
+}
+
+void I_unlock_mutex(I_mutex id)
+{
+	pthread_mutex_unlock(id);
+}
+
+void I_hold_cond(I_cond *cond_anchor, I_mutex mutex_id)
+{
+	I_Assert(mutex_id != NULL);
+	pthread_mutex_lock(&thread_lock);
+	if (*cond_anchor == NULL)
+	{
+		*cond_anchor = malloc(sizeof(pthread_cond_t));
+		pthread_cond_init(*cond_anchor, NULL);
+	}
+	pthread_mutex_unlock(&thread_lock);
+	pthread_cond_wait(*cond_anchor, mutex_id);
+}
+
+void I_wake_one_cond(I_cond *anchor)
+{
+	pthread_mutex_lock(&thread_lock);
+	if (*anchor == NULL)
+	{
+		*anchor = malloc(sizeof(pthread_cond_t));
+		pthread_cond_init(*anchor, NULL);
+	}
+	pthread_mutex_unlock(&thread_lock);
+	pthread_cond_signal(*anchor);
+}
+
+void I_wake_all_cond(I_cond *anchor)
+{
+	pthread_mutex_lock(&thread_lock);
+	if (*anchor == NULL)
+	{
+		*anchor = malloc(sizeof(pthread_t));
+		pthread_cond_init(*anchor, NULL);
+	}
+	pthread_mutex_unlock(&thread_lock);
+	pthread_cond_broadcast(*anchor);
+}
+#elif defined (_WIN32)
+#include <windows.h>
+
+#include "../i_threads.h"
+#include "../doomdef.h"
+#include "../doomtype.h"
+
+typedef struct thread_s thread_t;
+
+struct thread_s
+{
+	thread_t *next;
+	void *userdata;
+	I_thread_fn func;
+	HANDLE thread;
+	DWORD thread_id;
+};
+
+// we use a linked list to avoid moving memory blocks when allocating new threads.
+static thread_t *thread_list;
+static CRITICAL_SECTION thread_lock;
+
+static DWORD __stdcall HandleThread(void *data)
+{
+	thread_t *thread = data;
+	thread->func(thread->userdata);
+
+	EnterCriticalSection(&thread_lock);
+	thread->func = NULL;
+	LeaveCriticalSection(&thread_lock);
+	return 0;
+}
+
+void I_spawn_thread(const char *name, I_thread_fn entry, void *userdata)
+{
+	thread_t *thread;
+	(void)name;
+	EnterCriticalSection(&thread_lock);
+	thread = thread_list;
+	while (thread != NULL)
+	{
+		if (thread->func == NULL)
+		{
+			CloseHandle(thread->thread);
+			break;
+		}
+		thread = thread->next;
+	}
+	if (thread == NULL)
+	{
+		thread = malloc(sizeof(thread_t));
+		thread->next = thread_list;
+		thread_list = thread;
+	}
+
+	thread->func = entry;
+	thread->userdata = userdata;
+	thread->thread = CreateThread(NULL, 0, HandleThread, thread, 0, &thread->thread_id);
+	LeaveCriticalSection(&thread_lock);
+}
+
+int I_thread_is_stopped(void)
+{
+	thread_t *thread;
+	EnterCriticalSection(&thread_lock);
+	thread = thread_list;
+	while (thread != NULL)
+	{
+		if (thread->func != NULL)
+		{
+			LeaveCriticalSection(&thread_lock);
+			return false;
+		}
+		thread = thread->next;
+	}
+	LeaveCriticalSection(&thread_lock);
+	return true;
+}
+
+void I_start_threads(void)
+{
+	InitializeCriticalSection(&thread_lock);
+}
+
+void I_stop_threads(void)
+{
+	thread_t *thread = thread_list;
+	while (thread != NULL)
+	{
+		WaitForSingleObject(thread->thread, INFINITE);
+		CloseHandle(thread->thread);
+		thread = thread->next;
+	}
+	DeleteCriticalSection(&thread_lock);
+}
+
+void I_lock_mutex(I_mutex *anchor)
+{
+	EnterCriticalSection(&thread_lock);
+	if (*anchor == NULL)
+	{
+		*anchor = malloc(sizeof(CRITICAL_SECTION));
+		InitializeCriticalSection(*anchor);
+	}
+	LeaveCriticalSection(&thread_lock);
+	EnterCriticalSection(*anchor);
+}
+
+void I_unlock_mutex(I_mutex id)
+{
+	LeaveCriticalSection(id);
+}
+
+void I_hold_cond(I_cond *cond_anchor, I_mutex mutex_id)
+{
+	I_Assert(mutex_id != NULL);
+	EnterCriticalSection(&thread_lock);
+	if (*cond_anchor == NULL)
+	{
+		*cond_anchor = malloc(sizeof(CONDITION_VARIABLE));
+		InitializeConditionVariable(*cond_anchor);
+	}
+	LeaveCriticalSection(&thread_lock);
+	SleepConditionVariableCS(*cond_anchor, mutex_id, INFINITE);
+}
+
+void I_wake_one_cond(I_cond *anchor)
+{
+	EnterCriticalSection(&thread_lock);
+	if (*anchor == NULL)
+	{
+		*anchor = malloc(sizeof(CONDITION_VARIABLE));
+		InitializeConditionVariable(*anchor);
+	}
+	LeaveCriticalSection(&thread_lock);
+	WakeConditionVariable(*anchor);
+}
+
+void I_wake_all_cond(I_cond *anchor)
+{
+	EnterCriticalSection(&thread_lock);
+	if (*anchor == NULL)
+	{
+		*anchor = malloc(sizeof(CONDITION_VARIABLE));
+		InitializeConditionVariable(*anchor);
+	}
+	LeaveCriticalSection(&thread_lock);
+	WakeAllConditionVariable(*anchor);
+}
+#else
+void I_spawn_thread(const char *name, I_thread_fn entry, void *userdata)
+{
+	(void)name;
+	entry(userdata);
+}
+
+int I_thread_is_stopped(void)
+{
+}
+
+void I_start_threads(void)
+{
+}
+
+void I_stop_threads(void)
+{
+}
+
+void I_lock_mutex(I_mutex *anchor)
+{
+	(void)anchor;
+}
+
+void I_unlock_mutex(I_mutex id)
+{
+	(void)id;
+}
+
+void I_hold_cond(I_cond *cond_anchor, I_mutex mutex_id)
+{
+	(void)cond_anchor;
+	(void)mutex_id;
+}
+
+void I_wake_one_cond(I_cond *anchor)
+{
+	(void)anchor;
+}
+
+void I_wake_all_cond(I_cond *anchor)
+{
+	(void)anchor;
+}
+#endif
diff --git a/src/dedicated/i_video.c b/src/dedicated/i_video.c
new file mode 100644
index 0000000000000000000000000000000000000000..bb796b6767a1d55990209ba46cd8245377b013d9
--- /dev/null
+++ b/src/dedicated/i_video.c
@@ -0,0 +1,81 @@
+#include "../doomdef.h"
+#include "../command.h"
+#include "../i_video.h"
+
+rendermode_t rendermode = render_none;
+rendermode_t chosenrendermode = render_none;
+
+boolean highcolor = false;
+
+boolean allow_fullscreen = false;
+
+consvar_t cv_vidwait = CVAR_INIT ("vid_wait", "On", CV_SAVE, CV_OnOff, NULL);
+
+void I_StartupGraphics(void){}
+void I_ShutdownGraphics(void){}
+
+void VID_StartupOpenGL(void){}
+
+void I_SetPalette(RGBA_t *palette)
+{
+	(void)palette;
+}
+
+INT32 VID_NumModes(void)
+{
+	return 0;
+}
+
+INT32 VID_GetModeForSize(INT32 w, INT32 h)
+{
+	(void)w;
+	(void)h;
+	return 0;
+}
+
+void VID_PrepareModeList(void){}
+
+INT32 VID_SetMode(INT32 modenum)
+{
+	(void)modenum;
+	return 0;
+}
+
+boolean VID_CheckRenderer(void)
+{
+	return false;
+}
+
+void VID_CheckGLLoaded(rendermode_t oldrender)
+{
+	(void)oldrender;
+}
+
+const char *VID_GetModeName(INT32 modenum)
+{
+	(void)modenum;
+	return NULL;
+}
+
+UINT32 I_GetRefreshRate(void) { return 35; }
+
+void I_UpdateNoBlit(void){}
+
+void I_FinishUpdate(void){}
+
+void I_UpdateNoVsync(void) {}
+
+void I_WaitVBL(INT32 count)
+{
+	(void)count;
+}
+
+void I_ReadScreen(UINT8 *scr)
+{
+	(void)scr;
+}
+
+void I_BeginRead(void){}
+
+void I_EndRead(void){}
+
diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c
index fe33cfe3ef46bdfecf38b3187d3946242b601551..ecabe3576d3f15b06513315a945562076587aff5 100644
--- a/src/dummy/i_system.c
+++ b/src/dummy/i_system.c
@@ -206,5 +206,10 @@ void I_GetCursorPosition(INT32 *x, INT32 *y)
 	(void)y;
 }
 
+const char *I_GetSysName(void)
+{
+	return NULL;
+}
+
 #include "../sdl/dosstr.c"
 
diff --git a/src/g_game.c b/src/g_game.c
index 99bb0f73c8a4d39cd34721bee240d0b7b5938f68..6a99381e741544030ad9208f93860ffbc0f249d6 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -3339,7 +3339,7 @@ void G_AddPlayer(INT32 playernum)
 		p->lives = cv_startinglives.value;
 
 	if ((countplayers && !notexiting) || G_IsSpecialStage(gamemap))
-		P_DoPlayerExit(p);
+		P_DoPlayerExit(p, false);
 }
 
 boolean G_EnoughPlayersFinished(void)
@@ -3872,12 +3872,13 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
 //
 // G_UpdateVisited
 //
-static void G_UpdateVisited(gamedata_t *data, player_t *player, boolean silent)
+static void G_UpdateVisited(gamedata_t *data, player_t *player, boolean global)
 {
 	// Update visitation flags?
 	if (!demoplayback
 		&& G_CoopGametype() // Campaign mode
-		&& !stagefailed) // Did not fail the stage
+		&& !stagefailed // Did not fail the stage
+		&& (global || player->pflags & PF_FINISHED)) // Actually beat the stage
 	{
 		UINT8 earnedEmblems;
 		UINT16 totalrings = 0;
@@ -3915,12 +3916,12 @@ static void G_UpdateVisited(gamedata_t *data, player_t *player, boolean silent)
 				data->mapvisited[gamemap-1] |= MV_ALLEMERALDS;
 		}
 
-		if ((earnedEmblems = M_CompletionEmblems(data)) && !silent)
+		if ((earnedEmblems = M_CompletionEmblems(data)) && !global)
 		{
 			CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
 		}
 
-		if (silent)
+		if (global)
 		{
 			M_CheckLevelEmblems(data);
 		}
@@ -4620,6 +4621,9 @@ void G_SaveGameData(gamedata_t *data)
 
 	INT32 curmare;
 
+	if (!data)
+		return; // data struct not valid
+
 	if (!data->loaded)
 		return; // If never loaded (-nodata), don't save
 
diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index 58e16d62361a9ca3f87479ea273343c7c36065bc..55a32114a87203b5b5bd4ef451f0b8bd3e08c6fa 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -823,18 +823,13 @@ void HWR_GetRawFlat(lumpnum_t flatlumpnum)
 
 void HWR_GetLevelFlat(levelflat_t *levelflat)
 {
-	if (levelflat->type == LEVELFLAT_NONE)
+	if (levelflat->type == LEVELFLAT_NONE || levelflat->texture_id < 0)
 	{
 		HWR_SetCurrentTexture(NULL);
 		return;
 	}
 
 	INT32 texturenum = texturetranslation[levelflat->texture_id];
-	if (texturenum <= 0)
-	{
-		HWR_SetCurrentTexture(NULL);
-		return;
-	}
 
 	GLMapTexture_t *grtex = &gl_flats[texturenum];
 	GLMipmap_t *grMipmap = &grtex->mipmap;
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index fee1987683817c6183027f2095632ba8e5bdbec2..9c1a95c9316626ce5f02747eaba356b60368ee62 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -6488,7 +6488,6 @@ static CV_PossibleValue_t glfiltermode_cons_t[]= {{HWD_SET_TEXTUREFILTER_POINTSA
 CV_PossibleValue_t glanisotropicmode_cons_t[] = {{1, "MIN"}, {16, "MAX"}, {0, NULL}};
 
 consvar_t cv_glshaders = CVAR_INIT ("gr_shaders", "On", CV_SAVE, glshaders_cons_t, NULL);
-consvar_t cv_glallowshaders = CVAR_INIT ("gr_allowclientshaders", "On", CV_NETVAR, CV_OnOff, NULL);
 
 #ifdef ALAM_LIGHTING
 consvar_t cv_gldynamiclighting = CVAR_INIT ("gr_dynamiclighting", "On", CV_SAVE, CV_OnOff, NULL);
@@ -6547,7 +6546,6 @@ void HWR_AddCommands(void)
 	CV_RegisterVar(&cv_glfakecontrast);
 	CV_RegisterVar(&cv_glshearing);
 	CV_RegisterVar(&cv_glshaders);
-	CV_RegisterVar(&cv_glallowshaders);
 
 	CV_RegisterVar(&cv_glfiltermode);
 	CV_RegisterVar(&cv_glanisotropicmode);
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index bb2b837fcb299e8b0003eaa24764302d2e5bf7ee..4b199f6b3a669b51acdf7f7a45197abd465e78cd 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -620,7 +620,7 @@ static void Command_CSay_f(void)
 	DoSayCommand(0, 1, HU_CSAY);
 }
 
-static tic_t spam_tokens[MAXPLAYERS];
+static tic_t spam_tokens[MAXPLAYERS] = { 1 }; // fill the buffer with 1 so the motd can be sent.
 static tic_t spam_tics[MAXPLAYERS];
 
 /** Receives a message, processing an ::XD_SAY command.
diff --git a/src/i_system.h b/src/i_system.h
index 834dd4091487b295fd1faed9d11018e498d79b12..3f0e05d127fa4ed5ce9aa8fbe89ff109773851f4 100644
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -335,4 +335,8 @@ void I_GetCursorPosition(INT32 *x, INT32 *y);
 */
 void I_SetMouseGrab(boolean grab);
 
+/** \brief Returns the system name.
+*/
+const char *I_GetSysName(void);
+
 #endif
diff --git a/src/info.c b/src/info.c
index 964d46fa5458218065d544640194c6e975ea68d9..8dd1aac80679d17ae21b0fe43ad6b042d35ed627 100644
--- a/src/info.c
+++ b/src/info.c
@@ -21791,7 +21791,7 @@ void P_PatchInfoTables(void)
 	INT32 i;
 	char *tempname;
 
-#if NUMSPRITEFREESLOTS > 1000
+#if NUMSPRITEFREESLOTS > 9999 //tempname numbering actually starts at SPR_FIRSTFREESLOT, so the limit is actually 9999 + SPR_FIRSTFREESLOT-1, but the preprocessor doesn't understand enums, so its left at 9999 for safety
 "Update P_PatchInfoTables, you big dumb head"
 #endif
 
@@ -21799,8 +21799,8 @@ void P_PatchInfoTables(void)
 	for (i = SPR_FIRSTFREESLOT; i <= SPR_LASTFREESLOT; i++)
 	{
 		tempname = sprnames[i];
-		tempname[0] = 'F';
-		tempname[1] = (char)('0' + (char)((i-SPR_FIRSTFREESLOT+1)/100));
+		tempname[0] = (char)('0' + (char)((i-SPR_FIRSTFREESLOT+1)/1000));
+		tempname[1] = (char)('0' + (char)(((i-SPR_FIRSTFREESLOT+1)/100)%10));
 		tempname[2] = (char)('0' + (char)(((i-SPR_FIRSTFREESLOT+1)/10)%10));
 		tempname[3] = (char)('0' + (char)((i-SPR_FIRSTFREESLOT+1)%10));
 		tempname[4] = '\0';
diff --git a/src/info.h b/src/info.h
index 2362935f0629cf7304df3ea5a9f673f2aa3b3183..e8963cd3cdbdb28d2a498bb95cb63b9b5376a354 100644
--- a/src/info.h
+++ b/src/info.h
@@ -572,7 +572,7 @@ void A_ChangeHeight();
 extern int actionsoverridden[NUMACTIONS][MAX_ACTION_RECURSION];
 
 // ratio of states to sprites to mobj types is roughly 6 : 1 : 1
-#define NUMMOBJFREESLOTS 512
+#define NUMMOBJFREESLOTS 1024
 #define NUMSPRITEFREESLOTS NUMMOBJFREESLOTS
 #define NUMSTATEFREESLOTS (NUMMOBJFREESLOTS*8)
 
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index b70c63ece13cfc10daa1d621506b55a5671afa77..680c6a2c3ca9afbd48fdf7320d1b8a9e502373fb 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1584,11 +1584,12 @@ static int lib_pDoPlayerFinish(lua_State *L)
 static int lib_pDoPlayerExit(lua_State *L)
 {
 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	boolean finishedflag = lua_opttrueboolean(L, 2);
 	NOHUD
 	INLEVEL
 	if (!player)
 		return LUA_ErrInvalid(L, "player_t");
-	P_DoPlayerExit(player);
+	P_DoPlayerExit(player, finishedflag);
 	return 0;
 }
 
diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c
index 6b4b6229f68ffc869e1f6eccdd6999018d150ae0..c29eadecc625214615c2546abd2216c9b6630254 100644
--- a/src/lua_blockmaplib.c
+++ b/src/lua_blockmaplib.c
@@ -312,17 +312,12 @@ static int lib_searchBlockmap(lua_State *L)
 					continue; // our thing just found itself, so move on
 
 				funcret = lib_searchBlockmap_Objects(L, mobj, itmobj);
-				if (funcret == 2) {
-					lua_pushboolean(L, false);
-					return 1;
-				}
-				else if (funcret == 1)
-					retval = false;
-
-				if (P_MobjWasRemoved(mobj)) {
+				if (funcret == 2 || P_MobjWasRemoved(mobj)) {
 					retval = false;
 					break;
 				}
+				else if (funcret == 1)
+					retval = false;
 			}
 		}
 		while (itmobj != NULL);
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index b2730c5931f0c00db2eca2848827526661631084..767477d0bac75e7b9bada5367cb223a37a33f8cd 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -483,7 +483,7 @@ static int spriteinfo_set(lua_State *L)
 		}
 	}
 	else
-		return luaL_error(L, va("Field %s does not exist in spriteinfo_t", field));
+		return luaL_error(L, "Field %s does not exist in spriteinfo_t", field);
 
 	return 0;
 }
@@ -577,7 +577,7 @@ static int framepivot_get(lua_State *L)
 		lua_pushinteger(L, 0);
 	}
 	else
-		return luaL_error(L, va("Field %s does not exist in spriteframepivot_t", field));
+		return luaL_error(L, "Field %s does not exist in spriteframepivot_t", field);
 
 	return 1;
 }
@@ -604,7 +604,7 @@ static int framepivot_set(lua_State *L)
 	else if (fastcmp("rotaxis", field))
 		LUA_UsageWarning(L, "\"rotaxis\" is deprecated and will be removed.")
 	else
-		return luaL_error(L, va("Field %s does not exist in spriteframepivot_t", field));
+		return luaL_error(L, "Field %s does not exist in spriteframepivot_t", field);
 
 	return 0;
 }
@@ -1663,7 +1663,7 @@ static void setRamp(lua_State *L, skincolor_t* c) {
 	lua_pushnil(L);
 	for (i=0; i<COLORRAMPSIZE; i++) {
 		if (lua_objlen(L,-2)!=COLORRAMPSIZE) {
-			luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be %d entries long; got %d.", COLORRAMPSIZE, lua_objlen(L,-2));
+			luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be %d entries long; got %d.", COLORRAMPSIZE, luaL_getn(L,-2));
 			break;
 		}
 		if (lua_next(L, -2) != 0) {
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 5a3f8ad114b9f1269e3a49b76cb4aa9dd76417ad..7fd16b32b2c0806f06d2f557823aeabbc5bb4c76 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -1003,6 +1003,9 @@ static int mapthing_get(lua_State *L)
 		return 0;
 	}
 
+	if (field == (enum mapthing_e)-1)
+		return LUA_ErrInvalid(L, "fields");
+
 	switch (field)
 	{
 		case mapthing_valid:
@@ -1061,7 +1064,7 @@ static int mapthing_get(lua_State *L)
 			break;
 		default:
 			if (devparm)
-				return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
+				return luaL_error(L, "%s %s", LUA_QL("mapthing_t"), va("has no field named: %ui", field));
 			else
 				return 0;
 	}
@@ -1078,6 +1081,9 @@ static int mapthing_set(lua_State *L)
 	if (!mt)
 		return luaL_error(L, "accessed mapthing_t doesn't exist anymore.");
 
+	if (field == (enum mapthing_e)-1)
+		return LUA_ErrInvalid(L, "fields");
+
 	if (hud_running)
 		return luaL_error(L, "Do not alter mapthing_t in HUD rendering code!");
 	if (hook_cmd_running)
@@ -1135,7 +1141,7 @@ static int mapthing_set(lua_State *L)
 			mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
 			break;
 		default:
-			return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
+			return luaL_error(L, "%s %s", LUA_QL("mapthing_t"), va("has no field named: %ui", field));
 	}
 
 	return 0;
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 2aae7288349c0f1dae5b9dc15d316dead69775c8..b6ef2579c131d8de29a521615cb6a74acc94f5a0 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -1423,8 +1423,8 @@ static int power_len(lua_State *L)
 	return 1;
 }
 
-#define NOFIELD luaL_error(L, LUA_QL("ticcmd_t") " has no field named " LUA_QS, field)
-#define NOSET luaL_error(L, LUA_QL("ticcmd_t") " field " LUA_QS " should not be set directly.", ticcmd_opt[field])
+#define NOFIELD luaL_error(L, "%s %s", LUA_QL("ticcmd_t"), va("has no field named %ui", field))
+#define NOSET luaL_error(L, LUA_QL("ticcmd_t") " field %s should not be set directly.", ticcmd_opt[field])
 
 enum ticcmd_e
 {
@@ -1455,6 +1455,9 @@ static int ticcmd_get(lua_State *L)
 	if (!cmd)
 		return LUA_ErrInvalid(L, "player_t");
 
+	if (field == (enum ticcmd_e)-1)
+		return LUA_ErrInvalid(L, "fields");
+
 	switch (field)
 	{
 	case ticcmd_forwardmove:
@@ -1489,6 +1492,9 @@ static int ticcmd_set(lua_State *L)
 	if (!cmd)
 		return LUA_ErrInvalid(L, "ticcmd_t");
 
+	if (field == (enum ticcmd_e)-1)
+		return LUA_ErrInvalid(L, "fields");
+
 	if (hud_running)
 		return luaL_error(L, "Do not alter player_t in HUD rendering code!");
 
diff --git a/src/m_cheat.c b/src/m_cheat.c
index e61db2c2ee3d49e58f570567ee576ba94d3959eb..36438a47575ab2028223245096424ad04b398119 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -1098,15 +1098,23 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c
 		fixed_t fheight = P_GetSectorFloorZAt(sec, mt->x << FRACBITS, mt->y << FRACBITS);
 		mt->z = (UINT16)((player->mo->z - fheight)>>FRACBITS);
 	}
+
 	mt->angle = (INT16)(FixedInt(AngleFixed(player->mo->angle)));
 
-	mt->options = (mt->z << ZSHIFT) | (UINT16)cv_opflags.value;
+	mt->options = (UINT16)cv_opflags.value;
 	mt->scale = player->mo->scale;
 	mt->spritexscale = player->mo->spritexscale;
 	mt->spriteyscale = player->mo->spriteyscale;
 	memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
 	memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
 	mt->pitch = mt->roll = 0;
+
+	// Ignore offsets
+	if (mt->type == MT_EMBLEM)
+		mt->args[1] = 1;
+	else
+		mt->args[0] = 1;
+
 	return mt;
 }
 
diff --git a/src/m_menu.c b/src/m_menu.c
index de23acbfb46b4402261cef08c54bd972d61553ec..edbbdf2c1587a785479f8333c5ff0a8cbb997806 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -10535,7 +10535,7 @@ static void M_StartTimeAttackReplay(INT32 choice)
 // Player has selected the "REPLAY" from the time attack screen
 static void M_ReplayTimeAttack(INT32 choice)
 {
-	const char *which;
+	const char *which = NULL;
 	UINT8 error = DFILE_ERROR_NONE;
 
 	if (currentMenu == &SP_ReplayDef)
diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c
index 87f0110a9490d8a85e88c39d51d745ccede2af0d..66a30637f301942b0b78dc762b1f9c8f9a15916f 100644
--- a/src/netcode/d_netcmd.c
+++ b/src/netcode/d_netcmd.c
@@ -393,6 +393,9 @@ consvar_t cv_ps_descriptor = CVAR_INIT ("ps_descriptor", "Average", 0, ps_descri
 
 consvar_t cv_freedemocamera = CVAR_INIT("freedemocamera", "Off", CV_SAVE, CV_OnOff, NULL);
 
+// NOTE: this should be in hw_main.c, but we can't put it there as it breaks dedicated build
+consvar_t cv_glallowshaders = CVAR_INIT ("gr_allowcustomshaders", "On", CV_NETVAR, CV_OnOff, NULL);
+
 char timedemo_name[256];
 boolean timedemo_csv;
 char timedemo_csv_id[256];
@@ -526,6 +529,8 @@ void D_RegisterServerCommands(void)
 	// for master server connection
 	AddMServCommands();
 
+	CV_RegisterVar(&cv_glallowshaders);
+
 	// p_mobj.c
 	CV_RegisterVar(&cv_itemrespawntime);
 	CV_RegisterVar(&cv_itemrespawn);
@@ -3812,18 +3817,7 @@ static void Command_Version_f(void)
 #endif
 
 	// OS
-	// Would be nice to use SDL_GetPlatform for this
-#if defined (_WIN32) || defined (_WIN64)
-	CONS_Printf("Windows ");
-#elif defined(__linux__)
-	CONS_Printf("Linux ");
-#elif defined(MACOSX)
-	CONS_Printf("macOS ");
-#elif defined(UNIXCOMMON)
-	CONS_Printf("Unix (Common) ");
-#else
-	CONS_Printf("Other OS ");
-#endif
+	CONS_Printf("%s ", I_GetSysName());
 
 	// Bitness
 	if (sizeof(void*) == 4)
@@ -4069,7 +4063,7 @@ static void ExitMove_OnChange(void)
 				if (players[i].mo->target && players[i].mo->target->type == MT_SIGN)
 					P_SetTarget(&players[i].mo->target, NULL);
 
-				if (players[i].pflags & PF_FINISHED)
+				if (players[i].pflags & PF_FINISHED && !(players[i].exiting))
 					P_GiveFinishFlags(&players[i]);
 			}
 
@@ -4669,15 +4663,28 @@ static void Command_Cheats_f(void)
 			CV_ResetCheatNetVars();
 		return;
 	}
+	else if (COM_CheckParm("on"))
+	{
+		if (!(server || (IsPlayerAdmin(consoleplayer))))
+			CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
+		else
+			G_SetUsedCheats(false);
+		return;
+	}
+	
+	if (usedCheats)
+		CONS_Printf(M_GetText("Cheats are enabled, the game cannot be saved.\n"));
+	else
+		CONS_Printf(M_GetText("Cheats are disabled, the game can be saved.\n"));
 
 	if (CV_CheatsEnabled())
 	{
-		CONS_Printf(M_GetText("At least one CHEAT-marked variable has been changed -- Cheats are enabled.\n"));
+		CONS_Printf(M_GetText("At least one CHEAT-marked variable has been changed.\n"));
 		if (server || (IsPlayerAdmin(consoleplayer)))
 			CONS_Printf(M_GetText("Type CHEATS OFF to reset all cheat variables to default.\n"));
 	}
 	else
-		CONS_Printf(M_GetText("No CHEAT-marked variables are changed -- Cheats are disabled.\n"));
+		CONS_Printf(M_GetText("No CHEAT-marked variables are changed.\n"));
 }
 
 #ifdef _DEBUG
diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c
index 03ad8303e6571a477b9f107154d9ed3102ac58a9..a8a10d475d4afcc9c965573c1deaf59969c00b0f 100644
--- a/src/netcode/d_netfil.c
+++ b/src/netcode/d_netfil.c
@@ -1640,7 +1640,7 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
 #endif
 
 		// Set user agent, as some servers won't accept invalid user agents.
-		curl_easy_setopt(http_handle, CURLOPT_USERAGENT, va("Sonic Robo Blast 2/v%d.%d", VERSION, SUBVERSION));
+		curl_easy_setopt(http_handle, CURLOPT_USERAGENT, va("Sonic Robo Blast 2/%s", VERSIONSTRING));
 
 		// Authenticate if the user so wishes
 		login = CURLGetLogin(url, NULL);
diff --git a/src/netcode/i_tcp.c b/src/netcode/i_tcp.c
index 6d9a2725a3c65fb93e3f1f3b89e47dcf0464ad05..96342d5c6c55b901e2ce568aa9a62bfe330b4b3a 100644
--- a/src/netcode/i_tcp.c
+++ b/src/netcode/i_tcp.c
@@ -300,6 +300,10 @@ init_upnpc_once(struct upnpdata *upnpuserdata)
 	int upnp_error = -2;
 	int scope_id = 0;
 	int status_code = 0;
+
+	memset(&urls, 0, sizeof(struct UPNPUrls));
+	memset(&data, 0, sizeof(struct IGDdatas));
+
 	CONS_Printf(M_GetText("Looking for UPnP Internet Gateway Device\n"));
 	devlist = upnpDiscoverDevices(deviceTypes, 500, NULL, NULL, 0, false, 2, &upnp_error, 0);
 	if (devlist)
@@ -327,8 +331,6 @@ init_upnpc_once(struct upnpdata *upnpuserdata)
 			parserootdesc(descXML, descXMLsize, &data);
 			free(descXML);
 			descXML = NULL;
-			memset(&urls, 0, sizeof(struct UPNPUrls));
-			memset(&data, 0, sizeof(struct IGDdatas));
 			GetUPNPUrls(&urls, &data, dev->descURL, status_code);
 			I_AddExitFunc(I_ShutdownUPnP);
 		}
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 5314de8abfa7807fee22d2f2e08dde8d50ac33ec..a386d8ee93d75a47c24e90bf67ccdc08220bab43 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -4017,7 +4017,7 @@ static void P_DoBossVictory(mobj_t *mo)
 		{
 			if (!playeringame[i])
 				continue;
-			P_DoPlayerExit(&players[i]);
+			P_DoPlayerExit(&players[i], true);
 		}
 	}
 	else
@@ -11004,7 +11004,7 @@ void A_ForceWin(mobj_t *actor)
 	{
 		if (!playeringame[i])
 			continue;
-		P_DoPlayerExit(&players[i]);
+		P_DoPlayerExit(&players[i], true);
 	}
 }
 
diff --git a/src/p_inter.c b/src/p_inter.c
index d8765e7a2b4bde8358ba5188155a18c1c64a01df..82169bc54304782f940fdac535456d18dc047150 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2530,8 +2530,14 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 		{
 			P_SetTarget(&target->target, source);
 			source->player->numboxes++;
-			if (cv_itemrespawn.value && gametype != GT_COOP && (modifiedgame || netgame || multiplayer))
-				target->fuse = cv_itemrespawntime.value*TICRATE + 2; // Random box generation
+			// Set respawn
+			if (!(target->flags2 & MF2_DONTRESPAWN))
+			{
+				if (!(netgame || multiplayer))
+					target->fuse = atoi(cv_itemrespawntime.defaultvalue)*TICRATE + 2; 
+				else if (cv_itemrespawn.value)
+					target->fuse = cv_itemrespawntime.value*TICRATE + 2;
+			}
 		}
 
 		// Award Score Tails
diff --git a/src/p_local.h b/src/p_local.h
index 3b61b92295363a8d0ec789ce7920cb94cf417c2d..9644e7a244d434bf6f44d26d4a90e9cbf2fea656 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -189,7 +189,7 @@ void P_DoPityCheck(player_t *player);
 void P_PlayerThink(player_t *player);
 void P_PlayerAfterThink(player_t *player);
 void P_DoPlayerFinish(player_t *player);
-void P_DoPlayerExit(player_t *player);
+void P_DoPlayerExit(player_t *player, boolean finishedflag);
 void P_NightserizePlayer(player_t *player, INT32 ptime);
 
 void P_InstaThrust(mobj_t *mo, angle_t angle, fixed_t move);
diff --git a/src/p_maputl.c b/src/p_maputl.c
index 758a71ca343b86c4c07181f1e0ef31b52dc7dfdf..82b86471522ee3b2cbeebbedfb1e28c1c4438501 100644
--- a/src/p_maputl.c
+++ b/src/p_maputl.c
@@ -1095,8 +1095,6 @@ bthingit_t *P_NewBlockThingsIterator(int x1, int y1, int x2, int y2)
 		return NULL;
 
 	block = GetBlockmapBlock(x1, y1);
-	if (!block)
-		return NULL;
 
 	if (freeiters != NULL)
 	{
@@ -1188,12 +1186,12 @@ mobj_t *P_BlockThingsIteratorNext(bthingit_t *it, boolean centeronly)
 							if (!it->dynhash)
 							{
 								it->dynhashcapacity = 50;
-								Z_Calloc(it->dynhashcapacity * sizeof(it->dynhashcapacity), PU_LEVEL, &it->dynhash);
+								Z_Calloc(it->dynhashcapacity * sizeof(*it->dynhash), PU_LEVEL, &it->dynhash);
 							}
 							if (it->dynhashcount == it->dynhashcapacity)
 							{
 								it->dynhashcapacity *= 2;
-								it->dynhash = Z_Realloc(it->dynhash, it->dynhashcapacity * sizeof(it->dynhashcapacity), PU_LEVEL, &it->dynhash);
+								it->dynhash = Z_Realloc(it->dynhash, it->dynhashcapacity * sizeof(*it->dynhash), PU_LEVEL, &it->dynhash);
 							}
 							i = (int)it->dynhashcount;
 							it->dynhashcount++;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index f4fdf8ee9f56edb372f45363f561785a9d79c653..81a0941881dc84f3150a0c3971af0c95a07bfc71 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -10063,9 +10063,10 @@ static void P_MonitorFuseThink(mobj_t *mobj)
 {
 	mobj_t *newmobj;
 
-	// Special case for ALL monitors.
+	// Special case for ALL monitors outside of co-op.
 	// If a box's speed is nonzero, it's allowed to respawn as a WRM/SRM.
-	if (mobj->info->speed != 0 && (mobj->flags2 & (MF2_AMBUSH|MF2_STRONGBOX)))
+	if (!G_CoopGametype() && mobj->info->speed != 0
+		&& (mobj->flags2 & (MF2_AMBUSH|MF2_STRONGBOX)))
 	{
 		mobjtype_t spawnchance[64];
 		INT32 numchoices = 0, i = 0;
@@ -10293,6 +10294,8 @@ void P_MobjThinker(mobj_t *mobj)
 		P_SetTarget(&mobj->hnext, NULL);
 	if (mobj->hprev && P_MobjWasRemoved(mobj->hprev))
 		P_SetTarget(&mobj->hprev, NULL);
+	if (mobj->dontdrawforviewmobj && P_MobjWasRemoved(mobj->dontdrawforviewmobj))
+		P_SetTarget(&mobj->dontdrawforviewmobj, NULL);
 
 	mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG);
 
@@ -10855,10 +10858,16 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
 
 	// Set shadowscale here, before spawn hook so that Lua can change it
 	mobj->shadowscale = P_DefaultMobjShadowScale(mobj);
-
-	if (!(mobj->flags & MF_NOTHINK))
-		P_AddThinker(THINK_MOBJ, &mobj->thinker);
-
+	
+	// A monitor can't respawn if we're not in multiplayer,
+	// or if we're in co-op and it's score or a 1up
+	if (mobj->flags & MF_MONITOR && (!(netgame || multiplayer)
+	|| (G_CoopGametype()
+		&& (mobj->type == MT_1UP_BOX
+		|| mobj->type == MT_SCORE1K_BOX
+		|| mobj->type == MT_SCORE10K_BOX)
+	)))
+		mobj->flags2 |= MF2_DONTRESPAWN;
 
 	if (type == MT_PLAYER)
 	{
@@ -10869,6 +10878,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
 		va_end(args);
 	}
 
+	if (!(mobj->flags & MF_NOTHINK) || (titlemapinaction && mobj->type == MT_ALTVIEWMAN))
+		P_AddThinker(THINK_MOBJ, &mobj->thinker);
+
 	// increment mobj reference, so we don't get a dangling reference in case MobjSpawn calls P_RemoveMobj
 	mobj->thinker.references++;
 
@@ -11889,7 +11901,7 @@ void P_AfterPlayerSpawn(INT32 playernum)
 	if (CheckForReverseGravity)
 		P_CheckGravity(mobj, false);
 
-	if (p->pflags & PF_FINISHED)
+	if (p->pflags & PF_FINISHED && !(p->exiting))
 		P_GiveFinishFlags(p);
 }
 
diff --git a/src/p_setup.c b/src/p_setup.c
index 1d985beb4f07399a40607f1ec8f32f480df0e65c..a39750003bbe0769e116f69cee8552d3d6c91670 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -585,17 +585,17 @@ Ploadflat (levelflat_t *levelflat, const char *flatname, boolean resize)
 
 	// Look for a flat
 	int texturenum = R_CheckFlatNumForName(levelflat->name);
-	if (texturenum <= 0)
+	if (texturenum < 0)
 	{
 		// If we can't find a flat, try looking for a texture!
 		texturenum = R_CheckTextureNumForName(levelflat->name);
-		if (texturenum <= 0)
+		if (texturenum < 0)
 		{
 			// Use "not found" texture
 			texturenum = R_CheckTextureNumForName("REDWALL");
 
 			// Give up?
-			if (texturenum <= 0)
+			if (texturenum < 0)
 			{
 				levelflat->type = LEVELFLAT_NONE;
 				texturenum = -1;
diff --git a/src/p_spec.c b/src/p_spec.c
index 37036d095cb7f7934ebcb4473f22152312e6356c..572258165e00386a6f804b65a8fc8e561e0111e4 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -3605,7 +3605,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					{
 						if (!playeringame[i])
 							continue;
-						P_DoPlayerExit(&players[i]);
+						P_DoPlayerExit(&players[i], true);
 					}
 				}
 			}
@@ -3659,7 +3659,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					{
 						if (!playeringame[i])
 							continue;
-						P_DoPlayerExit(&players[i]);
+						P_DoPlayerExit(&players[i], true);
 					}
 				}
 			}
@@ -4421,7 +4421,7 @@ static void P_ProcessEggCapsule(player_t *player, sector_t *sector)
 	{
 		if (!playeringame[i])
 			continue;
-		P_DoPlayerExit(&players[i]);
+		P_DoPlayerExit(&players[i], true);
 	}
 }
 
@@ -4731,7 +4731,7 @@ static void P_ProcessFinishLine(player_t *player)
 			HU_DoCEcho("FINISHED!");
 		}
 
-		P_DoPlayerExit(player);
+		P_DoPlayerExit(player, true);
 	}
 }
 
diff --git a/src/p_user.c b/src/p_user.c
index 3c2b9254dd05cff186f39e6ebbf8a7e0ebc53eec..6454403624fd535af755b57c11b88f1434b2f4ed 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -893,7 +893,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 			players[i].marescore = 0;
 
 			players[i].spheres = players[i].rings = 0;
-			P_DoPlayerExit(&players[i]);
+			P_DoPlayerExit(&players[i], true);
 		}
 	}
 	else if (oldmare != player->mare)
@@ -2264,7 +2264,7 @@ void P_DoPlayerFinish(player_t *player)
 // P_DoPlayerExit
 //
 // Player exits the map via sector trigger
-void P_DoPlayerExit(player_t *player)
+void P_DoPlayerExit(player_t *player, boolean finishedflag)
 {
 	if (player->exiting)
 		return;
@@ -2285,7 +2285,11 @@ void P_DoPlayerExit(player_t *player)
 			player->exiting = (14*TICRATE)/5 + 1;
 	}
 	else
+	{
 		player->exiting = (14*TICRATE)/5 + 2; // Accidental death safeguard???
+		if (finishedflag)
+			player->pflags |= PF_FINISHED; // Give PF_FINISHED as proof of a true finish
+	}
 
 	//player->pflags &= ~PF_GLIDING;
 	if (player->climbing)
@@ -11906,7 +11910,7 @@ void P_PlayerThink(player_t *player)
 		if (((gametyperules & GTR_FRIENDLY) && cv_exitmove.value) && !G_EnoughPlayersFinished())
 			player->exiting = 0;
 		else
-			P_DoPlayerExit(player);
+			P_DoPlayerExit(player, false);
 	}
 
 	// check water content, set stuff in mobj
@@ -11955,7 +11959,7 @@ void P_PlayerThink(player_t *player)
 	}
 
 	// Synchronizes the "real" amount of time spent in the level.
-	if (!player->exiting && !stoppedclock)
+	if (!player->exiting && !(player->pflags & PF_FINISHED) && !stoppedclock)
 	{
 		if (gametyperules & GTR_RACE)
 		{
diff --git a/src/r_picformats.c b/src/r_picformats.c
index 7d7f1198d1805b24b715f78a339b07c952dd4e04..e4a59f2115d3833b3f5a76e38ff4a1cdd7d4ce30 100644
--- a/src/r_picformats.c
+++ b/src/r_picformats.c
@@ -376,7 +376,7 @@ void *Picture_PatchConvert(
 	// Write columns
 	for (INT32 x = 0; x < inwidth; x++)
 	{
-		post_t *post;
+		post_t *post = NULL;
 		size_t post_data_offset = 0;
 		boolean was_opaque = false;
 
diff --git a/src/r_segs.c b/src/r_segs.c
index 267c1d47d63ff32a56d6e6b0330783397dacfee3..453debeb6ef69840329270624f70dddae4603f49 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -441,7 +441,7 @@ static void R_DrawRepeatMaskedColumn(column_t *col, unsigned lengthcol)
 {
 	while (sprtopscreen < sprbotscreen) {
 		R_DrawMaskedColumn(col, lengthcol);
-		if ((INT64)sprtopscreen + dc_texheight*spryscale > (INT64)INT32_MAX) // prevent overflow
+		if ((INT64)sprtopscreen + (INT64)dc_texheight*spryscale > (INT64)INT32_MAX) // prevent overflow
 			sprtopscreen = INT32_MAX;
 		else
 			sprtopscreen += dc_texheight*spryscale;
diff --git a/src/r_textures.c b/src/r_textures.c
index 27a9336319bdbb71a1d0d1c4747c5a0db73fb2b2..0175a080e7cbd901bee865429660ddb3de51af9f 100644
--- a/src/r_textures.c
+++ b/src/r_textures.c
@@ -147,7 +147,7 @@ static void R_DrawFlippedColumnInCache(column_t *column, UINT8 *cache, texpatch_
 
 		if (count > 0)
 		{
-			for (; dest < cache + position + count; --source)
+			for (; dest < cache + position + count; --source, is_opaque++)
 			{
 				*dest++ = *source;
 				*is_opaque = true;
@@ -191,7 +191,7 @@ static void R_DrawBlendColumnInCache(column_t *column, UINT8 *cache, texpatch_t
 
 		if (count > 0)
 		{
-			for (; dest < cache + position + count; source++, dest++)
+			for (; dest < cache + position + count; source++, dest++, is_opaque++)
 			{
 				*dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha);
 				*is_opaque = true;
@@ -235,7 +235,7 @@ static void R_DrawBlendFlippedColumnInCache(column_t *column, UINT8 *cache, texp
 
 		if (count > 0)
 		{
-			for (; dest < cache + position + count; --source, dest++)
+			for (; dest < cache + position + count; --source, dest++, is_opaque++)
 			{
 				*dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha);
 				*is_opaque = true;
@@ -472,7 +472,7 @@ UINT8 *R_GenerateTexture(size_t texnum)
 
 	for (x = 0; x < texture->width; x++)
 	{
-		post_t *post;
+		post_t *post = NULL;
 		boolean was_opaque = false;
 
 		column_t *column = &temp_columns[x];
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index 4621700787f0c9b6395b5899730bc303a2699f1d..0f0355c6f28ad2afbd6d201f7fba46897e73f1f3 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -165,7 +165,22 @@
       <MultiProcessorCompilation>true</MultiProcessorCompilation>
       <MinimalRebuild>false</MinimalRebuild>
       <LanguageStandard>stdcpp17</LanguageStandard>
+      <PreprocessorDefinitions>HAVE_CURL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..\libs\curl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
+    <CustomBuild>
+      <Command />
+    </CustomBuild>
+    <CustomBuild>
+      <Message />
+    </CustomBuild>
+    <CustomBuild>
+      <Outputs />
+    </CustomBuild>
+    <Link>
+      <AdditionalDependencies>libcurl.dll.a;libz32.a;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\..\libs\zlib\win32;..\..\libs\curl\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
@@ -175,8 +190,51 @@
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
       <DisableSpecificWarnings>4244;4267;4146;4003</DisableSpecificWarnings>
+      <PreprocessorDefinitions>HAVE_CURL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\libs\curl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <CustomBuild>
+      <Command />
+    </CustomBuild>
+    <CustomBuild>
+      <Message />
+    </CustomBuild>
+    <CustomBuild>
+      <Outputs />
+    </CustomBuild>
+    <Link>
+      <AdditionalDependencies>libcurl.dll.a;libz32.a;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libs\zlib\win32;..\libs\curl\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <TreatWarningAsError>false</TreatWarningAsError>
     </ClCompile>
   </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <MinimalRebuild>false</MinimalRebuild>
+      <DisableSpecificWarnings>4244;4267;4146</DisableSpecificWarnings>
+      <PreprocessorDefinitions>HAVE_CURL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\libs\curl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalModuleDependencies>..\libs\curl\lib64;%(AdditionalModuleDependencies)</AdditionalModuleDependencies>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>libcurl.a;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libs\curl\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PreprocessorDefinitions>HAVE_CURL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>libcurl.a;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libs\curl\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\libs\libpng-src\projects\visualc10\libpng.vcxproj">
       <Project>{72b01aca-7a1a-4f7b-acef-2607299cf052}</Project>
@@ -314,7 +372,6 @@
     <ClInclude Include="..\netcode\server_connection.h" />
     <ClInclude Include="..\netcode\tic_command.h" />
     <ClInclude Include="..\p5prof.h" />
-    <ClInclude Include="..\p_haptic.h" />
     <ClInclude Include="..\p_local.h" />
     <ClInclude Include="..\p_maputl.h" />
     <ClInclude Include="..\p_mobj.h" />
@@ -400,6 +457,7 @@
     <ClCompile Include="..\blua\lmem.c" />
     <ClCompile Include="..\blua\lobject.c" />
     <ClCompile Include="..\blua\lopcodes.c" />
+    <ClCompile Include="..\blua\loslib.c" />
     <ClCompile Include="..\blua\lparser.c" />
     <ClCompile Include="..\blua\lstate.c" />
     <ClCompile Include="..\blua\lstring.c" />
@@ -438,6 +496,8 @@
     <ClCompile Include="..\hardware\hw_md3load.c" />
     <ClCompile Include="..\hardware\hw_model.c" />
     <ClCompile Include="..\hardware\r_opengl\r_opengl.c" />
+    <ClCompile Include="..\lua_colorlib.c" />
+    <ClCompile Include="..\r_bbox.c" />
     <ClCompile Include="..\u_list.c" />
     <ClCompile Include="..\hu_stuff.c" />
     <ClCompile Include="..\info.c" />
@@ -495,7 +555,6 @@
     <ClCompile Include="..\p_ceilng.c" />
     <ClCompile Include="..\p_enemy.c" />
     <ClCompile Include="..\p_floor.c" />
-    <ClCompile Include="..\p_haptic.c" />
     <ClCompile Include="..\p_inter.c" />
     <ClCompile Include="..\p_lights.c" />
     <ClCompile Include="..\p_map.c" />
@@ -567,7 +626,6 @@
     <ClCompile Include="IMG_xpm.c">
       <ExcludedFromBuild>true</ExcludedFromBuild>
     </ClCompile>
-    <ClCompile Include="i_gamepad.c" />
     <ClCompile Include="i_main.c" />
     <ClCompile Include="i_net.c" />
     <ClCompile Include="i_system.c" />
@@ -584,4 +642,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index 59bb76b52089708abec222a46052d89f6b25ab6f..183843018f84087b8555fbce59e638e67cfa3546 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -255,9 +255,6 @@
     <ClInclude Include="..\hardware\hw_model.h">
       <Filter>Hw_Hardware</Filter>
     </ClInclude>
-    <ClInclude Include="..\hardware\u_list.h">
-      <Filter>Hw_Hardware</Filter>
-    </ClInclude>
     <ClInclude Include="..\byteptr.h">
       <Filter>I_Interface</Filter>
     </ClInclude>
@@ -378,9 +375,6 @@
     <ClInclude Include="..\netcode\gamestate.h">
       <Filter>D_Doom</Filter>
     </ClInclude>
-    <ClInclude Include="..\netcode\http-mserv.h">
-      <Filter>I_Interface</Filter>
-    </ClInclude>
     <ClInclude Include="..\netcode\i_addrinfo.h">
       <Filter>I_Interface</Filter>
     </ClInclude>
@@ -558,12 +552,16 @@
     <ClInclude Include="..\r_fps.h">
       <Filter>R_Rend</Filter>
     </ClInclude>
-    <ClInclude Include="..\p_haptic.h">
-      <Filter>P_Play</Filter>
-    </ClInclude>
     <ClInclude Include="..\m_easing.h">
       <Filter>M_Misc</Filter>
     </ClInclude>
+    <ClInclude Include="..\d_clisrv.h" />
+    <ClInclude Include="..\d_net.h" />
+    <ClInclude Include="..\d_netcmd.h" />
+    <ClInclude Include="..\d_netfil.h" />
+    <ClInclude Include="..\u_list.h" />
+    <ClInclude Include="..\mserv.h" />
+    <ClInclude Include="..\http-mserv.h" />
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="..\tmap.nas">
@@ -753,15 +751,9 @@
     <ClCompile Include="..\hardware\hw_model.c">
       <Filter>Hw_Hardware</Filter>
     </ClCompile>
-    <ClCompile Include="..\hardware\u_list.c">
-      <Filter>Hw_Hardware</Filter>
-    </ClCompile>
     <ClCompile Include="..\filesrch.c">
       <Filter>I_Interface</Filter>
     </ClCompile>
-    <ClCompile Include="..\i_tcp.c">
-      <Filter>I_Interface</Filter>
-    </ClCompile>
     <ClCompile Include="..\lua_baselib.c">
       <Filter>LUA</Filter>
     </ClCompile>
@@ -1105,21 +1097,25 @@
     <ClCompile Include="..\lua_hudlib_drawlist.c">
       <Filter>LUA</Filter>
     </ClCompile>
-    <ClCompile Include="i_gamepad.c">
-      <Filter>SDLApp</Filter>
-    </ClCompile>
     <ClCompile Include="..\i_time.c">
       <Filter>I_Interface</Filter>
     </ClCompile>
     <ClCompile Include="..\r_fps.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
-    <ClCompile Include="..\p_haptic.c">
-      <Filter>P_Play</Filter>
-    </ClCompile>
     <ClCompile Include="..\m_easing.c">
       <Filter>M_Misc</Filter>
     </ClCompile>
+    <ClCompile Include="..\u_list.c" />
+    <ClCompile Include="..\blua\loslib.c">
+      <Filter>BLUA</Filter>
+    </ClCompile>
+    <ClCompile Include="..\lua_colorlib.c">
+      <Filter>LUA</Filter>
+    </ClCompile>
+    <ClCompile Include="..\r_bbox.c">
+      <Filter>R_Rend</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <Image Include="Srb2SDL.ico">
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 847806270f78175624fd2fabcdcf32a0b9e0e29e..f03ea6226516836612195a39022723e5df11e148 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -652,7 +652,6 @@ void I_GetConsoleEvents(void)
 	else if (tty_con.cursor < sizeof (tty_con.buffer))
 	{
 		// push regular character
-		ev.type = ev_text;
 		ev.key = tty_con.buffer[tty_con.cursor] = key;
 		tty_con.cursor++;
 		// print the current line (this is differential)
@@ -3256,4 +3255,10 @@ const CPUInfoFlags *I_CPUInfo(void)
 
 // note CPUAFFINITY code used to reside here
 void I_RegisterSysCommands(void) {}
+
+const char *I_GetSysName(void)
+{
+	return SDL_GetPlatform();
+}
+
 #endif
diff --git a/src/st_stuff.c b/src/st_stuff.c
index f3a73ce2616186574f3b390ce96dd79b7f0f717f..be676cff46d08541b56577b2cfdd4fd9f0971247 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -134,6 +134,7 @@ static patch_t *minicaps;
 static patch_t *gotrflag;
 static patch_t *gotbflag;
 static patch_t *fnshico;
+static patch_t *fireflower;
 
 hudinfo_t hudinfo[NUMHUDITEMS] =
 {
@@ -315,6 +316,8 @@ void ST_LoadGraphics(void)
 	sneakers = W_CachePatchName("TVSSICON", PU_HUDGFX);
 	gravboots = W_CachePatchName("TVGVICON", PU_HUDGFX);
 
+	fireflower = W_CachePatchName("GOTFFLOW", PU_HUDGFX);
+
 	tagico = W_CachePatchName("TAGICO", PU_HUDGFX);
 	gotrflag = W_CachePatchName("GOTRFLAG", PU_HUDGFX);
 	gotbflag = W_CachePatchName("GOTBFLAG", PU_HUDGFX);
@@ -1508,7 +1511,7 @@ static void ST_drawPowerupHUD(void)
 	UINT16 invulntime = 0;
 	INT32 offs = hudinfo[HUD_POWERUPS].x;
 	const UINT8 q = ((splitscreen && stplyr == &players[secondarydisplayplayer]) ? 1 : 0);
-	static INT32 flagoffs[2] = {0, 0}, shieldoffs[2] = {0, 0}, finishoffs[2] = {0, 0};
+	static INT32 flagoffs[2] = {0, 0}, shieldoffs[2] = {0, 0}, finishoffs[2] = {0, 0}, stackoffs[2] = {0,0};
 
 	if (F_GetPromptHideHud(hudinfo[HUD_POWERUPS].y))
 		return;
@@ -1583,6 +1586,22 @@ static void ST_drawPowerupHUD(void)
 
 	offs -= shieldoffs[q];
 
+	//Fire Flower "shield"
+	if ((stplyr->powers[pw_shield] & SH_FIREFLOWER) == SH_FIREFLOWER)
+	{
+		stackoffs[q] = ICONSEP;
+		V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, fireflower);
+	}
+	else if (stackoffs[q])
+	{
+		if (stackoffs[q] > 1)
+			stackoffs[q] = 2*stackoffs[q]/3;
+		else
+			stackoffs[q] = 0;
+	}
+
+	offs -= stackoffs[q];
+
 // ---------
 // CTF flags
 // ---------
diff --git a/src/y_inter.c b/src/y_inter.c
index 1f008eaf0363bb444b97fc24bc3e5f7872af605d..bb4fa06907785c9b193881cfde8b0c4d8bfd18f8 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -2043,7 +2043,7 @@ static void Y_AwardCoopBonuses(void)
 	y_bonus_t localbonuses[4];
 
 	// set score/total first
-	data.coop.total = players[consoleplayer].recordscore;
+	data.coop.total = (players[consoleplayer].pflags & PF_FINISHED) ? players[consoleplayer].recordscore : 0;
 	data.coop.score = players[consoleplayer].score;
 	data.coop.gotperfbonus = -1;
 	memset(data.coop.bonuses, 0, sizeof(data.coop.bonuses));
@@ -2060,7 +2060,12 @@ static void Y_AwardCoopBonuses(void)
 
 		for (j = 0; j < 4; ++j) // Set bonuses
 		{
-			(bonuses_list[bonusnum][j])(&players[i], &localbonuses[j]);
+			//Set the bonus, but only if we actually finished
+			if (players[i].pflags & PF_FINISHED)
+				(bonuses_list[bonusnum][j])(&players[i], &localbonuses[j]);
+			else
+				Y_SetNullBonus(&players[i], &localbonuses[j]);
+			
 			players[i].score += localbonuses[j].points;
 			if (players[i].score > MAXSCORE)
 				players[i].score = MAXSCORE;