diff --git a/Build/Configurations/Includes/ZDoom_misc.cfg b/Build/Configurations/Includes/ZDoom_misc.cfg
index d361fa8a..35d405af 100644
--- a/Build/Configurations/Includes/ZDoom_misc.cfg
+++ b/Build/Configurations/Includes/ZDoom_misc.cfg
@@ -713,21 +713,6 @@ universalfields
 			default = false;
 		}
 	}
-  
-  vertex 
-  {
-    zfloor 
-    {
-      type = 1;
-      default = 0.0f;
-    }
-    
-    zceiling
-    {
-      type = 1;
-      default = 0.0f;
-    }
-  }
 }
 
 
diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj
index 94638372..5b1ede18 100644
--- a/Source/Core/Builder.csproj
+++ b/Source/Core/Builder.csproj
@@ -493,7 +493,9 @@
     <Compile Include="Rendering\WorldVertex.cs" />
   </ItemGroup>
   <ItemGroup>
-    <Reference Include="SharpCompress.3.5, Version=0.8.1.0, Culture=neutral, processorArchitecture=MSIL" />
+    <Reference Include="SharpCompress.3.5, Version=0.8.1.0, Culture=neutral, processorArchitecture=MSIL">
+      <Private>False</Private>
+    </Reference>
     <Reference Include="SlimDX, Version=2.0.13.43, Culture=neutral, PublicKeyToken=b1b0c32fd1ffe4f9, processorArchitecture=x86" />
     <Reference Include="System" />
     <Reference Include="System.Core">
diff --git a/Source/Core/Builder.sln b/Source/Core/Builder.sln
index e0838665..7218a52f 100644
--- a/Source/Core/Builder.sln
+++ b/Source/Core/Builder.sln
@@ -16,6 +16,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuilderEffects", "..\Plugin
 		{818B3D10-F791-4C3F-9AF5-BB2D0079B63C} = {818B3D10-F791-4C3F-9AF5-BB2D0079B63C}
 	EndProjectSection
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StairSectorBuilder", "..\Plugins\StairSectorBuilder\StairSectorBuilder.csproj", "{3F365121-906B-409D-BB1E-37E0A78056C2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommentsPanel", "..\Plugins\CommentsPanel\CommentsPanel.csproj", "{58BD8A5B-1B48-435D-8473-A92F27D06C49}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CopyPasteSectorProperties", "..\Plugins\CopyPasteSectorProps\CopyPasteSectorProperties.csproj", "{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statistics", "..\Plugins\Statistics\Statistics.csproj", "{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagRange", "..\Plugins\TagRange\TagRange.csproj", "{F49EFF6D-51CB-4E49-8223-AAE653C5B62F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisplaneExplorer", "..\Plugins\VisplaneExplorer\VisplaneExplorer.csproj", "{CF670175-7099-4090-A330-EE25C7230139}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -88,6 +100,66 @@ Global
 		{B859BE0F-A992-476D-A642-FA8EFE94AAA5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
 		{B859BE0F-A992-476D-A642-FA8EFE94AAA5}.Release|x86.ActiveCfg = Release|x86
 		{B859BE0F-A992-476D-A642-FA8EFE94AAA5}.Release|x86.Build.0 = Release|x86
+		{3F365121-906B-409D-BB1E-37E0A78056C2}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{3F365121-906B-409D-BB1E-37E0A78056C2}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{3F365121-906B-409D-BB1E-37E0A78056C2}.Debug|Mixed Platforms.Build.0 = Debug|x86
+		{3F365121-906B-409D-BB1E-37E0A78056C2}.Debug|x86.ActiveCfg = Debug|x86
+		{3F365121-906B-409D-BB1E-37E0A78056C2}.Debug|x86.Build.0 = Debug|x86
+		{3F365121-906B-409D-BB1E-37E0A78056C2}.Release|Any CPU.ActiveCfg = Release|x86
+		{3F365121-906B-409D-BB1E-37E0A78056C2}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{3F365121-906B-409D-BB1E-37E0A78056C2}.Release|Mixed Platforms.Build.0 = Release|x86
+		{3F365121-906B-409D-BB1E-37E0A78056C2}.Release|x86.ActiveCfg = Release|x86
+		{3F365121-906B-409D-BB1E-37E0A78056C2}.Release|x86.Build.0 = Release|x86
+		{58BD8A5B-1B48-435D-8473-A92F27D06C49}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{58BD8A5B-1B48-435D-8473-A92F27D06C49}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{58BD8A5B-1B48-435D-8473-A92F27D06C49}.Debug|Mixed Platforms.Build.0 = Debug|x86
+		{58BD8A5B-1B48-435D-8473-A92F27D06C49}.Debug|x86.ActiveCfg = Debug|x86
+		{58BD8A5B-1B48-435D-8473-A92F27D06C49}.Debug|x86.Build.0 = Debug|x86
+		{58BD8A5B-1B48-435D-8473-A92F27D06C49}.Release|Any CPU.ActiveCfg = Release|x86
+		{58BD8A5B-1B48-435D-8473-A92F27D06C49}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{58BD8A5B-1B48-435D-8473-A92F27D06C49}.Release|Mixed Platforms.Build.0 = Release|x86
+		{58BD8A5B-1B48-435D-8473-A92F27D06C49}.Release|x86.ActiveCfg = Release|x86
+		{58BD8A5B-1B48-435D-8473-A92F27D06C49}.Release|x86.Build.0 = Release|x86
+		{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Debug|Mixed Platforms.Build.0 = Debug|x86
+		{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Debug|x86.ActiveCfg = Debug|x86
+		{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Debug|x86.Build.0 = Debug|x86
+		{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Release|Any CPU.ActiveCfg = Release|x86
+		{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Release|Mixed Platforms.Build.0 = Release|x86
+		{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Release|x86.ActiveCfg = Release|x86
+		{A5F93B70-18D9-4F3C-9B72-BC8B5B13998E}.Release|x86.Build.0 = Release|x86
+		{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Debug|Mixed Platforms.Build.0 = Debug|x86
+		{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Debug|x86.ActiveCfg = Debug|x86
+		{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Debug|x86.Build.0 = Debug|x86
+		{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Release|Any CPU.ActiveCfg = Release|x86
+		{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Release|Mixed Platforms.Build.0 = Release|x86
+		{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Release|x86.ActiveCfg = Release|x86
+		{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}.Release|x86.Build.0 = Release|x86
+		{F49EFF6D-51CB-4E49-8223-AAE653C5B62F}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{F49EFF6D-51CB-4E49-8223-AAE653C5B62F}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{F49EFF6D-51CB-4E49-8223-AAE653C5B62F}.Debug|Mixed Platforms.Build.0 = Debug|x86
+		{F49EFF6D-51CB-4E49-8223-AAE653C5B62F}.Debug|x86.ActiveCfg = Debug|x86
+		{F49EFF6D-51CB-4E49-8223-AAE653C5B62F}.Debug|x86.Build.0 = Debug|x86
+		{F49EFF6D-51CB-4E49-8223-AAE653C5B62F}.Release|Any CPU.ActiveCfg = Release|x86
+		{F49EFF6D-51CB-4E49-8223-AAE653C5B62F}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{F49EFF6D-51CB-4E49-8223-AAE653C5B62F}.Release|Mixed Platforms.Build.0 = Release|x86
+		{F49EFF6D-51CB-4E49-8223-AAE653C5B62F}.Release|x86.ActiveCfg = Release|x86
+		{F49EFF6D-51CB-4E49-8223-AAE653C5B62F}.Release|x86.Build.0 = Release|x86
+		{CF670175-7099-4090-A330-EE25C7230139}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{CF670175-7099-4090-A330-EE25C7230139}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{CF670175-7099-4090-A330-EE25C7230139}.Debug|Mixed Platforms.Build.0 = Debug|x86
+		{CF670175-7099-4090-A330-EE25C7230139}.Debug|x86.ActiveCfg = Debug|x86
+		{CF670175-7099-4090-A330-EE25C7230139}.Debug|x86.Build.0 = Debug|x86
+		{CF670175-7099-4090-A330-EE25C7230139}.Release|Any CPU.ActiveCfg = Release|x86
+		{CF670175-7099-4090-A330-EE25C7230139}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{CF670175-7099-4090-A330-EE25C7230139}.Release|Mixed Platforms.Build.0 = Release|x86
+		{CF670175-7099-4090-A330-EE25C7230139}.Release|x86.ActiveCfg = Release|x86
+		{CF670175-7099-4090-A330-EE25C7230139}.Release|x86.Build.0 = Release|x86
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/Source/Core/Controls/FlatSelectorControl.cs b/Source/Core/Controls/FlatSelectorControl.cs
index d9823008..71decdc6 100644
--- a/Source/Core/Controls/FlatSelectorControl.cs
+++ b/Source/Core/Controls/FlatSelectorControl.cs
@@ -41,17 +41,17 @@ namespace CodeImp.DoomBuilder.Controls
 			// Check if name is a "none" texture
 			if((imagename.Length < 1) || (imagename[0] == '-'))
 			{
-				DisplayImageSize(-1, -1); //mxd
+				DisplayImageSize(0, 0); //mxd
 				
 				// Flat required!
 				return CodeImp.DoomBuilder.Properties.Resources.MissingTexture;
 			}
 			else
 			{
-				//mxd
-				ImageData texture = General.Map.Data.GetFlatImage(imagename);
-				if(texture.ImageState == ImageLoadState.Ready) DisplayImageSize(texture.ScaledWidth, texture.ScaledHeight);
-				else DisplayImageSize(-1, -1);
+				ImageData texture = General.Map.Data.GetFlatImage(imagename); //mxd
+
+				if(string.IsNullOrEmpty(texture.FullName)) DisplayImageSize(0, 0); //mxd
+				else DisplayImageSize(texture.ScaledWidth, texture.ScaledHeight); //mxd
 				
 				// Set the image
 				return texture.GetPreview();
diff --git a/Source/Core/Controls/ImageSelectorControl.Designer.cs b/Source/Core/Controls/ImageSelectorControl.Designer.cs
index 12c97e3c..ae055645 100644
--- a/Source/Core/Controls/ImageSelectorControl.Designer.cs
+++ b/Source/Core/Controls/ImageSelectorControl.Designer.cs
@@ -64,6 +64,7 @@ namespace CodeImp.DoomBuilder.Controls
 			this.labelSize.Size = new System.Drawing.Size(48, 13);
 			this.labelSize.TabIndex = 0;
 			this.labelSize.Text = "128x128";
+			this.labelSize.Visible = false;
 			// 
 			// name
 			// 
diff --git a/Source/Core/Controls/ImageSelectorControl.cs b/Source/Core/Controls/ImageSelectorControl.cs
index 7ea31494..715ce871 100644
--- a/Source/Core/Controls/ImageSelectorControl.cs
+++ b/Source/Core/Controls/ImageSelectorControl.cs
@@ -189,7 +189,7 @@ namespace CodeImp.DoomBuilder.Controls
 
 		//mxd
 		protected void DisplayImageSize(float width, float height) {
-			if(!General.Settings.ShowTextureSizes || (width == -1 && height == -1)) {
+			if(!General.Settings.ShowTextureSizes || width == 0 || height == 0) {
 				labelSize.Visible = false;
 				return;
 			}
diff --git a/Source/Core/Controls/TextureSelectorControl.cs b/Source/Core/Controls/TextureSelectorControl.cs
index 8e9dfd01..c6ec8ac5 100644
--- a/Source/Core/Controls/TextureSelectorControl.cs
+++ b/Source/Core/Controls/TextureSelectorControl.cs
@@ -48,7 +48,7 @@ namespace CodeImp.DoomBuilder.Controls
 			// Check if name is a "none" texture
 			if((imagename.Length < 1) || (imagename[0] == '-'))
 			{
-				DisplayImageSize(-1, -1); //mxd
+				DisplayImageSize(0, 0); //mxd
 				
 				// Determine image to show
 				if(required)
@@ -58,10 +58,10 @@ namespace CodeImp.DoomBuilder.Controls
 			}
 			else
 			{
-				//mxd
-				ImageData texture = General.Map.Data.GetTextureImage(imagename);
-				if(texture.ImageState == ImageLoadState.Ready) DisplayImageSize(texture.ScaledWidth, texture.ScaledHeight);
-				else DisplayImageSize(-1, -1);
+				ImageData texture = General.Map.Data.GetTextureImage(imagename); //mxd
+
+				if(string.IsNullOrEmpty(texture.FullName)) DisplayImageSize(0, 0); //mxd
+				else DisplayImageSize(texture.ScaledWidth, texture.ScaledHeight); //mxd
 				
 				// Set the image
 				return texture.GetPreview();
diff --git a/Source/Core/Controls/VertexInfoPanel.cs b/Source/Core/Controls/VertexInfoPanel.cs
index 3783b345..b0604640 100644
--- a/Source/Core/Controls/VertexInfoPanel.cs
+++ b/Source/Core/Controls/VertexInfoPanel.cs
@@ -41,18 +41,16 @@ namespace CodeImp.DoomBuilder.Controls
 			
 			//mxd. Height offsets
 			if(General.Map.UDMF) {
-				if(v.Fields.ContainsKey("zceiling")) {
-					float zc = v.Fields.GetValue("zceiling", 0f);
-					zceiling.Text = zc.ToString("0.##");
+				if(!float.IsNaN(v.ZCeiling)) {
+					zceiling.Text = v.ZCeiling.ToString("0.##");
 					zceiling.Enabled = true;
 					labelCeilingOffset.Enabled = true;
 				} else {
 					zceiling.Text = "--";
 				}
 
-				if(v.Fields.ContainsKey("zfloor")) {
-					float zf = v.Fields.GetValue("zfloor", 0f);
-					zfloor.Text = zf.ToString("0.##");
+				if(!float.IsNaN(v.ZFloor)) {
+					zfloor.Text = v.ZFloor.ToString("0.##");
 					zfloor.Enabled = true;
 					labelFloorOffset.Enabled = true;
 				} else {
diff --git a/Source/Core/GZBuilder/Windows/TagStatisticsForm.Designer.cs b/Source/Core/GZBuilder/Windows/TagStatisticsForm.Designer.cs
index 00768d8a..45f85324 100644
--- a/Source/Core/GZBuilder/Windows/TagStatisticsForm.Designer.cs
+++ b/Source/Core/GZBuilder/Windows/TagStatisticsForm.Designer.cs
@@ -25,20 +25,21 @@
 		/// the contents of this method with the code editor.
 		/// </summary>
 		private void InitializeComponent() {
-			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle13 = new System.Windows.Forms.DataGridViewCellStyle();
-			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle14 = new System.Windows.Forms.DataGridViewCellStyle();
-			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle15 = new System.Windows.Forms.DataGridViewCellStyle();
-			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle16 = new System.Windows.Forms.DataGridViewCellStyle();
-			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle17 = new System.Windows.Forms.DataGridViewCellStyle();
-			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle18 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle5 = new System.Windows.Forms.DataGridViewCellStyle();
+			System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle6 = new System.Windows.Forms.DataGridViewCellStyle();
 			this.dataGridView = new System.Windows.Forms.DataGridView();
-			this.apply = new System.Windows.Forms.Button();
-			this.cancel = new System.Windows.Forms.Button();
 			this.TagColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.Label = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.Sectors = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.Linedefs = new System.Windows.Forms.DataGridViewTextBoxColumn();
 			this.Things = new System.Windows.Forms.DataGridViewTextBoxColumn();
+			this.apply = new System.Windows.Forms.Button();
+			this.cancel = new System.Windows.Forms.Button();
+			this.label1 = new System.Windows.Forms.Label();
 			((System.ComponentModel.ISupportInitialize)(this.dataGridView)).BeginInit();
 			this.SuspendLayout();
 			// 
@@ -54,14 +55,14 @@
 			this.dataGridView.BorderStyle = System.Windows.Forms.BorderStyle.None;
 			this.dataGridView.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.None;
 			this.dataGridView.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
-			dataGridViewCellStyle13.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
-			dataGridViewCellStyle13.BackColor = System.Drawing.SystemColors.Control;
-			dataGridViewCellStyle13.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
-			dataGridViewCellStyle13.ForeColor = System.Drawing.SystemColors.WindowText;
-			dataGridViewCellStyle13.SelectionBackColor = System.Drawing.SystemColors.Highlight;
-			dataGridViewCellStyle13.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
-			dataGridViewCellStyle13.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
-			this.dataGridView.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle13;
+			dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+			dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control;
+			dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
+			dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText;
+			dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+			dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+			dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+			this.dataGridView.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
 			this.dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
 			this.dataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
             this.TagColumn,
@@ -75,13 +76,66 @@
 			this.dataGridView.RowHeadersVisible = false;
 			this.dataGridView.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
 			this.dataGridView.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
-			this.dataGridView.Size = new System.Drawing.Size(477, 286);
+			this.dataGridView.Size = new System.Drawing.Size(477, 256);
 			this.dataGridView.TabIndex = 3;
+			this.dataGridView.CellMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dataGridView_CellMouseClick);
+			this.dataGridView.CellMouseDoubleClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dataGridView_CellMouseDoubleClick);
+			// 
+			// TagColumn
+			// 
+			this.TagColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
+			dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
+			this.TagColumn.DefaultCellStyle = dataGridViewCellStyle2;
+			this.TagColumn.HeaderText = "Tag";
+			this.TagColumn.Name = "TagColumn";
+			this.TagColumn.ReadOnly = true;
+			this.TagColumn.Width = 51;
+			// 
+			// Label
+			// 
+			this.Label.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+			dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+			this.Label.DefaultCellStyle = dataGridViewCellStyle3;
+			this.Label.HeaderText = "Label";
+			this.Label.Name = "Label";
+			// 
+			// Sectors
+			// 
+			this.Sectors.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
+			dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
+			this.Sectors.DefaultCellStyle = dataGridViewCellStyle4;
+			this.Sectors.HeaderText = "Sectors";
+			this.Sectors.Name = "Sectors";
+			this.Sectors.ReadOnly = true;
+			this.Sectors.Resizable = System.Windows.Forms.DataGridViewTriState.True;
+			this.Sectors.Width = 68;
+			// 
+			// Linedefs
+			// 
+			this.Linedefs.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
+			dataGridViewCellStyle5.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
+			this.Linedefs.DefaultCellStyle = dataGridViewCellStyle5;
+			this.Linedefs.HeaderText = "Linedefs";
+			this.Linedefs.Name = "Linedefs";
+			this.Linedefs.ReadOnly = true;
+			this.Linedefs.Resizable = System.Windows.Forms.DataGridViewTriState.True;
+			this.Linedefs.Width = 72;
+			// 
+			// Things
+			// 
+			this.Things.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
+			dataGridViewCellStyle6.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
+			this.Things.DefaultCellStyle = dataGridViewCellStyle6;
+			this.Things.HeaderText = "Things";
+			this.Things.Name = "Things";
+			this.Things.ReadOnly = true;
+			this.Things.Resizable = System.Windows.Forms.DataGridViewTriState.True;
+			this.Things.Width = 64;
 			// 
 			// apply
 			// 
 			this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-			this.apply.Location = new System.Drawing.Point(399, 304);
+			this.apply.Location = new System.Drawing.Point(399, 319);
 			this.apply.Name = "apply";
 			this.apply.Size = new System.Drawing.Size(90, 23);
 			this.apply.TabIndex = 4;
@@ -93,7 +147,7 @@
 			// 
 			this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
 			this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-			this.cancel.Location = new System.Drawing.Point(303, 304);
+			this.cancel.Location = new System.Drawing.Point(303, 319);
 			this.cancel.Name = "cancel";
 			this.cancel.Size = new System.Drawing.Size(90, 23);
 			this.cancel.TabIndex = 5;
@@ -101,56 +155,17 @@
 			this.cancel.UseVisualStyleBackColor = true;
 			this.cancel.Click += new System.EventHandler(this.cancel_Click);
 			// 
-			// TagColumn
+			// label1
 			// 
-			this.TagColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
-			dataGridViewCellStyle14.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
-			this.TagColumn.DefaultCellStyle = dataGridViewCellStyle14;
-			this.TagColumn.HeaderText = "Tag";
-			this.TagColumn.Name = "TagColumn";
-			this.TagColumn.ReadOnly = true;
-			this.TagColumn.Width = 51;
-			// 
-			// Label
-			// 
-			this.Label.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
-			dataGridViewCellStyle15.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
-			this.Label.DefaultCellStyle = dataGridViewCellStyle15;
-			this.Label.HeaderText = "Label";
-			this.Label.Name = "Label";
-			// 
-			// Sectors
-			// 
-			this.Sectors.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
-			dataGridViewCellStyle16.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
-			this.Sectors.DefaultCellStyle = dataGridViewCellStyle16;
-			this.Sectors.HeaderText = "Sectors";
-			this.Sectors.Name = "Sectors";
-			this.Sectors.ReadOnly = true;
-			this.Sectors.Resizable = System.Windows.Forms.DataGridViewTriState.True;
-			this.Sectors.Width = 68;
-			// 
-			// Linedefs
-			// 
-			this.Linedefs.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
-			dataGridViewCellStyle17.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
-			this.Linedefs.DefaultCellStyle = dataGridViewCellStyle17;
-			this.Linedefs.HeaderText = "Linedefs";
-			this.Linedefs.Name = "Linedefs";
-			this.Linedefs.ReadOnly = true;
-			this.Linedefs.Resizable = System.Windows.Forms.DataGridViewTriState.True;
-			this.Linedefs.Width = 72;
-			// 
-			// Things
-			// 
-			this.Things.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader;
-			dataGridViewCellStyle18.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
-			this.Things.DefaultCellStyle = dataGridViewCellStyle18;
-			this.Things.HeaderText = "Things";
-			this.Things.Name = "Things";
-			this.Things.ReadOnly = true;
-			this.Things.Resizable = System.Windows.Forms.DataGridViewTriState.True;
-			this.Things.Width = 64;
+			this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+			this.label1.AutoSize = true;
+			this.label1.Location = new System.Drawing.Point(12, 278);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(472, 26);
+			this.label1.TabIndex = 6;
+			this.label1.Text = "Double click on a cell in Sectors, Linedefs or Things column to select map elemen" +
+				"ts with given tag.\r\nRight click to open Properties form for map elements with gi" +
+				"ven tag.";
 			// 
 			// TagStatisticsForm
 			// 
@@ -158,7 +173,8 @@
 			this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
 			this.CancelButton = this.cancel;
-			this.ClientSize = new System.Drawing.Size(501, 333);
+			this.ClientSize = new System.Drawing.Size(501, 348);
+			this.Controls.Add(this.label1);
 			this.Controls.Add(this.cancel);
 			this.Controls.Add(this.apply);
 			this.Controls.Add(this.dataGridView);
@@ -170,6 +186,7 @@
 			this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.TagStatisticsForm_FormClosing);
 			((System.ComponentModel.ISupportInitialize)(this.dataGridView)).EndInit();
 			this.ResumeLayout(false);
+			this.PerformLayout();
 
 		}
 
@@ -183,5 +200,6 @@
 		private System.Windows.Forms.DataGridViewTextBoxColumn Sectors;
 		private System.Windows.Forms.DataGridViewTextBoxColumn Linedefs;
 		private System.Windows.Forms.DataGridViewTextBoxColumn Things;
+		private System.Windows.Forms.Label label1;
 	}
 }
\ No newline at end of file
diff --git a/Source/Core/GZBuilder/Windows/TagStatisticsForm.cs b/Source/Core/GZBuilder/Windows/TagStatisticsForm.cs
index bfbfa47a..7a6961f4 100644
--- a/Source/Core/GZBuilder/Windows/TagStatisticsForm.cs
+++ b/Source/Core/GZBuilder/Windows/TagStatisticsForm.cs
@@ -4,6 +4,8 @@ using System.ComponentModel;
 using System.Drawing;
 using System.Windows.Forms;
 using CodeImp.DoomBuilder.Map;
+using CodeImp.DoomBuilder.Geometry;
+using CodeImp.DoomBuilder.Editing;
 
 namespace CodeImp.DoomBuilder.GZBuilder.Windows
 {
@@ -22,6 +24,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.Windows
 				this.Location = location;
 			}
 
+			setup();
+		}
+
+		private void setup() {
 			//collect all tags
 			List<int> tags = new List<int>();
 			Dictionary<int, int> sectorsCountByTag = new Dictionary<int, int>();
@@ -32,7 +38,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.Windows
 			foreach(Sector s in General.Map.Map.Sectors) {
 				if(s.Tag == 0) continue;
 				if(!tags.Contains(s.Tag)) tags.Add(s.Tag);
-					
+
 				if(!sectorsCountByTag.ContainsKey(s.Tag))
 					sectorsCountByTag.Add(s.Tag, 1);
 				else
@@ -44,13 +50,13 @@ namespace CodeImp.DoomBuilder.GZBuilder.Windows
 				foreach(Linedef l in General.Map.Map.Linedefs) {
 					if(l.Tag == 0) continue;
 					if(!tags.Contains(l.Tag)) tags.Add(l.Tag);
-						
+
 					if(!linedefsCountByTag.ContainsKey(l.Tag))
 						linedefsCountByTag.Add(l.Tag, 1);
 					else
 						linedefsCountByTag[l.Tag] += 1;
 				}
-			}else{
+			} else {
 				Linedefs.Visible = false;
 			}
 
@@ -59,19 +65,20 @@ namespace CodeImp.DoomBuilder.GZBuilder.Windows
 				foreach(Thing t in General.Map.Map.Things) {
 					if(t.Tag == 0) continue;
 					if(!tags.Contains(t.Tag)) tags.Add(t.Tag);
-						
+
 					if(!thingsCountByTag.ContainsKey(t.Tag))
 						thingsCountByTag.Add(t.Tag, 1);
 					else
 						thingsCountByTag[t.Tag] += 1;
 				}
-			}else{
+			} else {
 				Things.Visible = false;
 			}
 
 			//create rows
-			foreach(int tag in tags){
-				addRow(tag, 
+			dataGridView.Rows.Clear();
+			foreach(int tag in tags) {
+				addRow(tag,
 					General.Map.Options.TagLabels.ContainsKey(tag) ? General.Map.Options.TagLabels[tag] : string.Empty,
 					sectorsCountByTag.ContainsKey(tag) ? sectorsCountByTag[tag] : 0,
 					linedefsCountByTag.ContainsKey(tag) ? linedefsCountByTag[tag] : 0,
@@ -108,6 +115,67 @@ namespace CodeImp.DoomBuilder.GZBuilder.Windows
 			dataGridView.Rows.Add(row);
 		}
 
+		private List<Sector> getSectorsWithTag(int tag, int count) {
+			List<Sector> list = new List<Sector>();
+			foreach(Sector s in General.Map.Map.Sectors) {
+				if(s.Tag == tag) {
+					list.Add(s);
+					if(list.Count == count) break;
+				}
+			}
+
+			return list;
+		}
+
+		private List<Linedef> getLinedefsWithTag(int tag, int count) {
+			List<Linedef> list = new List<Linedef>();
+			foreach(Linedef l in General.Map.Map.Linedefs) {
+				if(l.Tag == tag) {
+					list.Add(l);
+					if(list.Count == count) break;
+				}
+			}
+
+			return list;
+		}
+
+		private List<Thing> getThingsWithTag(int tag, int count) {
+			List<Thing> list = new List<Thing>();
+			foreach(Thing t in General.Map.Map.Things) {
+				if(t.Tag == tag) {
+					list.Add(t);
+					if(list.Count == count) break;
+				}
+			}
+
+			return list;
+		}
+
+		private void showSelection(List<Vector2D> points) {
+			RectangleF area = MapSet.CreateEmptyArea();
+			
+			// Make a view area from the points
+			foreach(Vector2D p in points) area = MapSet.IncreaseArea(area, p);
+
+			// Make the area square, using the largest side
+			if(area.Width > area.Height) {
+				float delta = area.Width - area.Height;
+				area.Y -= delta * 0.5f;
+				area.Height += delta;
+			} else {
+				float delta = area.Height - area.Width;
+				area.X -= delta * 0.5f;
+				area.Width += delta;
+			}
+
+			// Add padding
+			area.Inflate(100f, 100f);
+
+			// Zoom to area
+			ClassicMode editmode = (General.Editing.Mode as ClassicMode);
+			editmode.CenterOnArea(area, 0.6f);
+		}
+
 //events
 		private void apply_Click(object sender, EventArgs e) {
 			//refill TagLabels with table data
@@ -127,6 +195,102 @@ namespace CodeImp.DoomBuilder.GZBuilder.Windows
 			this.Close();
 		}
 
+		private void dataGridView_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e) {
+			//open properties window
+			if(e.ColumnIndex > 1 && e.Button == MouseButtons.Right) {
+				dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Selected = true;
+				int tag = (int)dataGridView.Rows[e.RowIndex].Cells[0].Value;
+
+				if(e.ColumnIndex == 2) { //sectors
+					List<Sector> list = getSectorsWithTag(tag, (int)dataGridView.Rows[e.RowIndex].Cells[2].Value);
+					if(list.Count > 0) {
+						General.MainWindow.ShowEditSectors(list);
+						General.Map.Map.Update();
+						setup();
+					}
+				} else if(e.ColumnIndex == 3) { //linedefs
+					List<Linedef> list = getLinedefsWithTag(tag, (int)dataGridView.Rows[e.RowIndex].Cells[3].Value);
+					if(list.Count > 0) {
+						General.MainWindow.ShowEditLinedefs(list);
+						General.Map.Map.Update();
+						setup();
+					}
+				} else if(e.ColumnIndex == 4) { //things
+					List<Thing> list = getThingsWithTag(tag, (int)dataGridView.Rows[e.RowIndex].Cells[4].Value);
+					if(list.Count > 0) {
+						General.MainWindow.ShowEditThings(list);
+						General.Map.Map.Update();
+						setup();
+					}
+				}
+			}
+		}
+
+		private void dataGridView_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e) {
+			if(e.ColumnIndex < 2) return;
+			int tag = (int)dataGridView.Rows[e.RowIndex].Cells[0].Value;
+
+			if(e.ColumnIndex == 2) { //sectors
+				List<Sector> list = getSectorsWithTag(tag, (int)dataGridView.Rows[e.RowIndex].Cells[2].Value);
+				if(list.Count > 0) {
+					General.Map.Map.ClearSelectedSectors();
+					General.Map.Map.ClearSelectedLinedefs();
+
+					List<Vector2D> points = new List<Vector2D>();
+					foreach(Sector s in list) {
+						s.Selected = true;
+
+						foreach(Sidedef sd in s.Sidedefs) {
+							points.Add(sd.Line.Start.Position);
+							points.Add(sd.Line.End.Position);
+						}
+					}
+					
+					General.Map.Map.Update();
+					General.Editing.ChangeMode("SectorsMode");
+					showSelection(points);
+				}
+			} else if(e.ColumnIndex == 3) { //linedefs
+				List<Linedef> list = getLinedefsWithTag(tag, (int)dataGridView.Rows[e.RowIndex].Cells[3].Value);
+				if(list.Count > 0) {
+					General.Map.Map.ClearSelectedSectors();
+					General.Map.Map.ClearSelectedLinedefs();
+
+					List<Vector2D> points = new List<Vector2D>();
+					foreach(Linedef l in list) {
+						l.Selected = true;
+						points.Add(l.Start.Position);
+						points.Add(l.End.Position);
+					}
+
+					General.Map.Map.Update();
+					General.Editing.ChangeMode("LinedefsMode");
+					showSelection(points);
+				}
+			} else if(e.ColumnIndex == 4) { //things
+				List<Thing> list = getThingsWithTag(tag, (int)dataGridView.Rows[e.RowIndex].Cells[4].Value);
+				if(list.Count > 0) {
+					General.Map.Map.ClearSelectedThings();
+
+					List<Vector2D> points = new List<Vector2D>();
+					foreach(Thing t in list) {
+						t.Selected = true;
+						
+						Vector2D p = (Vector2D)t.Position;
+						points.Add(p);
+						points.Add(p + new Vector2D(t.Size * 2.0f, t.Size * 2.0f));
+						points.Add(p + new Vector2D(t.Size * 2.0f, -t.Size * 2.0f));
+						points.Add(p + new Vector2D(-t.Size * 2.0f, t.Size * 2.0f));
+						points.Add(p + new Vector2D(-t.Size * 2.0f, -t.Size * 2.0f));
+					}
+
+					General.Map.Map.Update();
+					General.Editing.ChangeMode("ThingsMode");
+					showSelection(points);
+				}
+			}
+		}
+
 		private void TagStatisticsForm_FormClosing(object sender, FormClosingEventArgs e) {
 			size = this.Size;
 			location = this.Location;
diff --git a/Source/Core/IO/UniversalStreamReader.cs b/Source/Core/IO/UniversalStreamReader.cs
index f4f292a8..f66f8245 100644
--- a/Source/Core/IO/UniversalStreamReader.cs
+++ b/Source/Core/IO/UniversalStreamReader.cs
@@ -391,6 +391,10 @@ namespace CodeImp.DoomBuilder.IO
 				Vertex v = map.CreateVertex(new Vector2D(x, y));
 				if(v != null)
 				{
+					//mxd. zoffsets
+					v.ZCeiling = GetCollectionEntry<float>(c, "zceiling", false, float.NaN, where); //mxd
+					v.ZFloor = GetCollectionEntry<float>(c, "zfloor", false, float.NaN, where); //mxd
+					
 					// Custom fields
 					ReadCustomFields(c, v, "vertex");
 
diff --git a/Source/Core/IO/UniversalStreamWriter.cs b/Source/Core/IO/UniversalStreamWriter.cs
index 06933f86..901278ff 100644
--- a/Source/Core/IO/UniversalStreamWriter.cs
+++ b/Source/Core/IO/UniversalStreamWriter.cs
@@ -158,6 +158,8 @@ namespace CodeImp.DoomBuilder.IO
 				UniversalCollection coll = new UniversalCollection();
 				coll.Add("x", v.Position.x);
 				coll.Add("y", v.Position.y);
+				if(!float.IsNaN(v.ZCeiling)) coll.Add("zceiling", v.ZCeiling); //mxd
+				if(!float.IsNaN(v.ZFloor)) coll.Add("zfloor", v.ZFloor); //mxd
 				coll.Comment = v.Index.ToString();
 
 				// Add custom fields
diff --git a/Source/Core/Map/Vertex.cs b/Source/Core/Map/Vertex.cs
index dacbd470..c7222839 100644
--- a/Source/Core/Map/Vertex.cs
+++ b/Source/Core/Map/Vertex.cs
@@ -45,6 +45,10 @@ namespace CodeImp.DoomBuilder.Map
 		// Position
 		private Vector2D pos;
 
+		//mxd. Height
+		private float zfloor;
+		private float zceiling;
+
 		// References
 		private LinkedList<Linedef> linedefs;
 		
@@ -61,6 +65,24 @@ namespace CodeImp.DoomBuilder.Map
 		public Vector2D Position { get { return pos; } }
 		internal Vertex Clone { get { return clone; } set { clone = value; } }
 		internal int SerializedIndex { get { return serializedindex; } set { serializedindex = value; } }
+		public float ZCeiling {	//mxd
+			get { return zceiling; }
+			set {
+				if(zceiling != value) {
+					BeforeFieldsChange();
+					zceiling = value;
+				}
+			}
+		}
+		public float ZFloor { //mxd
+			get { return zfloor; }
+			set {
+				if(zfloor != value) {
+					BeforeFieldsChange();
+					zfloor = value;
+				}
+			}
+		}
 
 		#endregion
 
@@ -74,6 +96,8 @@ namespace CodeImp.DoomBuilder.Map
 			this.linedefs = new LinkedList<Linedef>();
 			this.listindex = listindex;
 			this.pos = pos;
+			this.zceiling = float.NaN; //mxd
+			this.zfloor = float.NaN; //mxd
 			
 			if(map == General.Map.Map)
 				General.Map.UndoRedo.RecAddVertex(this);
@@ -161,6 +185,8 @@ namespace CodeImp.DoomBuilder.Map
 			base.ReadWrite(s);
 			
 			s.rwVector2D(ref pos);
+			s.rwFloat(ref zceiling); //mxd
+			s.rwFloat(ref zfloor); //mxd
 			
 			if(s.IsWriting)
 			{
@@ -195,6 +221,8 @@ namespace CodeImp.DoomBuilder.Map
 			
 			// Copy properties
 			v.pos = pos;
+			v.zceiling = zceiling; //mxd
+			v.zfloor = zfloor; //mxd
 			base.CopyPropertiesTo(v);
 		}
 		
diff --git a/Source/Core/Resources/UDMF.cfg b/Source/Core/Resources/UDMF.cfg
index deaf963b..e46ba40c 100644
--- a/Source/Core/Resources/UDMF.cfg
+++ b/Source/Core/Resources/UDMF.cfg
@@ -10,6 +10,8 @@ managedfields
 	{
 		x;
 		y;
+		zceiling;
+		zfloor;
 	}
 	
 	linedef
diff --git a/Source/Core/Windows/IMainForm.cs b/Source/Core/Windows/IMainForm.cs
index 7f22fc0b..86c57fe6 100644
--- a/Source/Core/Windows/IMainForm.cs
+++ b/Source/Core/Windows/IMainForm.cs
@@ -40,6 +40,9 @@ namespace CodeImp.DoomBuilder.Windows
 		MouseButtons MouseButtons { get; }
 		bool IsActiveWindow { get; }
 		RenderTargetControl Display { get; }
+
+		//mxd. Events
+		event EventHandler OnEditFormValuesChanged;
 		
 		// Methods
 		void DisplayReady();
diff --git a/Source/Core/Windows/LinedefEditForm.Designer.cs b/Source/Core/Windows/LinedefEditForm.Designer.cs
index 8a6dc6c9..321faab2 100644
--- a/Source/Core/Windows/LinedefEditForm.Designer.cs
+++ b/Source/Core/Windows/LinedefEditForm.Designer.cs
@@ -924,7 +924,7 @@ namespace CodeImp.DoomBuilder.Windows
 			// 
 			// pfcFrontOffsetBottom
 			// 
-			this.pfcFrontOffsetBottom.AllowDecimal = false;
+			this.pfcFrontOffsetBottom.AllowDecimal = true;
 			this.pfcFrontOffsetBottom.ButtonStep = 16F;
 			this.pfcFrontOffsetBottom.DefaultValue = 0F;
 			this.pfcFrontOffsetBottom.Field1 = "offsetx_bottom";
@@ -937,7 +937,7 @@ namespace CodeImp.DoomBuilder.Windows
 			// 
 			// pfcFrontOffsetMid
 			// 
-			this.pfcFrontOffsetMid.AllowDecimal = false;
+			this.pfcFrontOffsetMid.AllowDecimal = true;
 			this.pfcFrontOffsetMid.ButtonStep = 16F;
 			this.pfcFrontOffsetMid.DefaultValue = 0F;
 			this.pfcFrontOffsetMid.Field1 = "offsetx_mid";
@@ -950,7 +950,7 @@ namespace CodeImp.DoomBuilder.Windows
 			// 
 			// pfcFrontOffsetTop
 			// 
-			this.pfcFrontOffsetTop.AllowDecimal = false;
+			this.pfcFrontOffsetTop.AllowDecimal = true;
 			this.pfcFrontOffsetTop.ButtonStep = 16F;
 			this.pfcFrontOffsetTop.DefaultValue = 0F;
 			this.pfcFrontOffsetTop.Field1 = "offsetx_top";
@@ -1244,7 +1244,7 @@ namespace CodeImp.DoomBuilder.Windows
 			// 
 			// pfcBackOffsetBottom
 			// 
-			this.pfcBackOffsetBottom.AllowDecimal = false;
+			this.pfcBackOffsetBottom.AllowDecimal = true;
 			this.pfcBackOffsetBottom.ButtonStep = 16F;
 			this.pfcBackOffsetBottom.DefaultValue = 0F;
 			this.pfcBackOffsetBottom.Field1 = "offsetx_bottom";
@@ -1257,7 +1257,7 @@ namespace CodeImp.DoomBuilder.Windows
 			// 
 			// pfcBackOffsetMid
 			// 
-			this.pfcBackOffsetMid.AllowDecimal = false;
+			this.pfcBackOffsetMid.AllowDecimal = true;
 			this.pfcBackOffsetMid.ButtonStep = 16F;
 			this.pfcBackOffsetMid.DefaultValue = 0F;
 			this.pfcBackOffsetMid.Field1 = "offsetx_mid";
@@ -1270,7 +1270,7 @@ namespace CodeImp.DoomBuilder.Windows
 			// 
 			// pfcBackOffsetTop
 			// 
-			this.pfcBackOffsetTop.AllowDecimal = false;
+			this.pfcBackOffsetTop.AllowDecimal = true;
 			this.pfcBackOffsetTop.ButtonStep = 16F;
 			this.pfcBackOffsetTop.DefaultValue = 0F;
 			this.pfcBackOffsetTop.Field1 = "offsetx_top";
diff --git a/Source/Core/Windows/MainForm.cs b/Source/Core/Windows/MainForm.cs
index e9508a0b..ab5f9737 100644
--- a/Source/Core/Windows/MainForm.cs
+++ b/Source/Core/Windows/MainForm.cs
@@ -101,6 +101,12 @@ namespace CodeImp.DoomBuilder.Windows
 
 		#endregion
 
+		#region ================== mxd. Events
+
+		public event EventHandler OnEditFormValuesChanged; //mxd
+
+		#endregion
+
 		#region ================== Variables
 
 		// Position/size
@@ -2960,6 +2966,7 @@ namespace CodeImp.DoomBuilder.Windows
 			// Show sector edit dialog
 			VertexEditForm f = new VertexEditForm();
 			f.Setup(vertices, allowPositionChange);
+			f.OnValuesChanged += new EventHandler(EditForm_OnValuesChanged);
 			result = f.ShowDialog(this);
 			f.Dispose();
 
@@ -3015,6 +3022,12 @@ namespace CodeImp.DoomBuilder.Windows
 			return result;
 		}
 
+		//mxd
+		private void EditForm_OnValuesChanged(object sender, EventArgs e) {
+			if(OnEditFormValuesChanged != null)
+				OnEditFormValuesChanged(sender, e);
+		}
+
 		#endregion
 
 		#region ================== Message Pump
diff --git a/Source/Core/Windows/PreferencesForm.Designer.cs b/Source/Core/Windows/PreferencesForm.Designer.cs
index 89692d98..43048fd9 100644
--- a/Source/Core/Windows/PreferencesForm.Designer.cs
+++ b/Source/Core/Windows/PreferencesForm.Designer.cs
@@ -57,26 +57,27 @@ namespace CodeImp.DoomBuilder.Windows
 			this.defaultviewmode = new System.Windows.Forms.ComboBox();
 			this.keyusedlabel = new System.Windows.Forms.Label();
 			this.colorsgroup1 = new System.Windows.Forms.GroupBox();
+			this.color3dFloors = new CodeImp.DoomBuilder.Controls.ColorControl();
 			this.label23 = new System.Windows.Forms.Label();
 			this.numSectorsLabel = new System.Windows.Forms.Label();
 			this.colorNewSectors = new CodeImp.DoomBuilder.Controls.ColorControl();
 			this.colorInfo = new CodeImp.DoomBuilder.Controls.ColorControl();
-			this.cbStretchModels = new System.Windows.Forms.CheckBox();
 			this.colorMD3 = new CodeImp.DoomBuilder.Controls.ColorControl();
-			this.label2 = new System.Windows.Forms.Label();
+			this.doublesidedalpha = new Dotnetrix.Controls.TrackBar();
 			this.colorgrid64 = new CodeImp.DoomBuilder.Controls.ColorControl();
-			this.squarethings = new System.Windows.Forms.CheckBox();
 			this.colorgrid = new CodeImp.DoomBuilder.Controls.ColorControl();
-			this.doublesidedalphalabel = new System.Windows.Forms.Label();
 			this.colorindication = new CodeImp.DoomBuilder.Controls.ColorControl();
-			this.qualitydisplay = new System.Windows.Forms.CheckBox();
 			this.colorbackcolor = new CodeImp.DoomBuilder.Controls.ColorControl();
+			this.label2 = new System.Windows.Forms.Label();
+			this.doublesidedalphalabel = new System.Windows.Forms.Label();
 			this.colorselection = new CodeImp.DoomBuilder.Controls.ColorControl();
 			this.colorvertices = new CodeImp.DoomBuilder.Controls.ColorControl();
-			this.doublesidedalpha = new Dotnetrix.Controls.TrackBar();
 			this.colorhighlight = new CodeImp.DoomBuilder.Controls.ColorControl();
 			this.colorlinedefs = new CodeImp.DoomBuilder.Controls.ColorControl();
 			this.tbNumSectors = new Dotnetrix.Controls.TrackBar();
+			this.cbStretchModels = new System.Windows.Forms.CheckBox();
+			this.squarethings = new System.Windows.Forms.CheckBox();
+			this.qualitydisplay = new System.Windows.Forms.CheckBox();
 			this.cancel = new System.Windows.Forms.Button();
 			this.apply = new System.Windows.Forms.Button();
 			this.tabs = new System.Windows.Forms.TabControl();
@@ -118,13 +119,13 @@ namespace CodeImp.DoomBuilder.Windows
 			this.columncontrolaction = new System.Windows.Forms.ColumnHeader();
 			this.columncontrolkey = new System.Windows.Forms.ColumnHeader();
 			this.actioncontrolpanel = new System.Windows.Forms.GroupBox();
+			this.actiondescription = new System.Windows.Forms.TextBox();
 			this.keyusedlist = new System.Windows.Forms.ListBox();
 			this.disregardshiftlabel = new System.Windows.Forms.Label();
 			this.actioncontrol = new System.Windows.Forms.ComboBox();
 			this.actiontitle = new System.Windows.Forms.Label();
 			this.actioncontrolclear = new System.Windows.Forms.Button();
 			this.actionkey = new System.Windows.Forms.TextBox();
-			this.actiondescription = new System.Windows.Forms.Label();
 			this.tabcolors = new System.Windows.Forms.TabPage();
 			this.appearancegroup1 = new System.Windows.Forms.GroupBox();
 			this.cbLoadGameGldefs = new System.Windows.Forms.CheckBox();
@@ -163,7 +164,6 @@ namespace CodeImp.DoomBuilder.Windows
 			this.label16 = new System.Windows.Forms.Label();
 			this.pasteoptions = new CodeImp.DoomBuilder.Controls.PasteOptionsControl();
 			this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
-			this.color3dFloors = new CodeImp.DoomBuilder.Controls.ColorControl();
 			label7 = new System.Windows.Forms.Label();
 			label6 = new System.Windows.Forms.Label();
 			label5 = new System.Windows.Forms.Label();
@@ -508,6 +508,18 @@ namespace CodeImp.DoomBuilder.Windows
 			this.colorsgroup1.Text = " Display ";
 			this.colorsgroup1.Visible = false;
 			// 
+			// color3dFloors
+			// 
+			this.color3dFloors.BackColor = System.Drawing.Color.Transparent;
+			this.color3dFloors.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.color3dFloors.Label = "3D Floors:";
+			this.color3dFloors.Location = new System.Drawing.Point(15, 286);
+			this.color3dFloors.MaximumSize = new System.Drawing.Size(10000, 23);
+			this.color3dFloors.MinimumSize = new System.Drawing.Size(100, 23);
+			this.color3dFloors.Name = "color3dFloors";
+			this.color3dFloors.Size = new System.Drawing.Size(168, 23);
+			this.color3dFloors.TabIndex = 24;
+			// 
 			// label23
 			// 
 			this.label23.AutoSize = true;
@@ -551,18 +563,6 @@ namespace CodeImp.DoomBuilder.Windows
 			this.colorInfo.Size = new System.Drawing.Size(168, 23);
 			this.colorInfo.TabIndex = 19;
 			// 
-			// cbStretchModels
-			// 
-			this.cbStretchModels.AutoSize = true;
-			this.cbStretchModels.Location = new System.Drawing.Point(236, 170);
-			this.cbStretchModels.Name = "cbStretchModels";
-			this.cbStretchModels.Size = new System.Drawing.Size(167, 18);
-			this.cbStretchModels.TabIndex = 18;
-			this.cbStretchModels.Text = "Scale models in visual modes";
-			this.toolTip1.SetToolTip(this.cbStretchModels, "If enabled, heights of models will be scaled down by 15% in Visual mode \r\nto mimi" +
-					"ck GZDoom\'s way of rendering.");
-			this.cbStretchModels.UseVisualStyleBackColor = true;
-			// 
 			// colorMD3
 			// 
 			this.colorMD3.BackColor = System.Drawing.Color.Transparent;
@@ -575,15 +575,15 @@ namespace CodeImp.DoomBuilder.Windows
 			this.colorMD3.Size = new System.Drawing.Size(168, 23);
 			this.colorMD3.TabIndex = 17;
 			// 
-			// label2
+			// doublesidedalpha
 			// 
-			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(14, 405);
-			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(147, 14);
-			this.label2.TabIndex = 14;
-			this.label2.Text = "Passable lines transparency:";
-			this.label2.TextAlign = System.Drawing.ContentAlignment.TopRight;
+			this.doublesidedalpha.LargeChange = 3;
+			this.doublesidedalpha.Location = new System.Drawing.Point(11, 426);
+			this.doublesidedalpha.Name = "doublesidedalpha";
+			this.doublesidedalpha.Size = new System.Drawing.Size(130, 45);
+			this.doublesidedalpha.TabIndex = 2;
+			this.doublesidedalpha.TickStyle = System.Windows.Forms.TickStyle.TopLeft;
+			this.doublesidedalpha.ValueChanged += new System.EventHandler(this.doublesidedalpha_ValueChanged);
 			// 
 			// colorgrid64
 			// 
@@ -597,16 +597,6 @@ namespace CodeImp.DoomBuilder.Windows
 			this.colorgrid64.Size = new System.Drawing.Size(168, 23);
 			this.colorgrid64.TabIndex = 9;
 			// 
-			// squarethings
-			// 
-			this.squarethings.AutoSize = true;
-			this.squarethings.Location = new System.Drawing.Point(25, 170);
-			this.squarethings.Name = "squarethings";
-			this.squarethings.Size = new System.Drawing.Size(93, 18);
-			this.squarethings.TabIndex = 8;
-			this.squarethings.Text = "Square things";
-			this.squarethings.UseVisualStyleBackColor = true;
-			// 
 			// colorgrid
 			// 
 			this.colorgrid.BackColor = System.Drawing.Color.Transparent;
@@ -619,15 +609,6 @@ namespace CodeImp.DoomBuilder.Windows
 			this.colorgrid.Size = new System.Drawing.Size(168, 23);
 			this.colorgrid.TabIndex = 8;
 			// 
-			// doublesidedalphalabel
-			// 
-			this.doublesidedalphalabel.AutoSize = true;
-			this.doublesidedalphalabel.Location = new System.Drawing.Point(147, 438);
-			this.doublesidedalphalabel.Name = "doublesidedalphalabel";
-			this.doublesidedalphalabel.Size = new System.Drawing.Size(23, 14);
-			this.doublesidedalphalabel.TabIndex = 16;
-			this.doublesidedalphalabel.Text = "0%";
-			// 
 			// colorindication
 			// 
 			this.colorindication.BackColor = System.Drawing.Color.Transparent;
@@ -640,16 +621,6 @@ namespace CodeImp.DoomBuilder.Windows
 			this.colorindication.Size = new System.Drawing.Size(168, 23);
 			this.colorindication.TabIndex = 7;
 			// 
-			// qualitydisplay
-			// 
-			this.qualitydisplay.AutoSize = true;
-			this.qualitydisplay.Location = new System.Drawing.Point(25, 191);
-			this.qualitydisplay.Name = "qualitydisplay";
-			this.qualitydisplay.Size = new System.Drawing.Size(130, 18);
-			this.qualitydisplay.TabIndex = 7;
-			this.qualitydisplay.Text = "High quality rendering";
-			this.qualitydisplay.UseVisualStyleBackColor = true;
-			// 
 			// colorbackcolor
 			// 
 			this.colorbackcolor.BackColor = System.Drawing.Color.Transparent;
@@ -662,6 +633,25 @@ namespace CodeImp.DoomBuilder.Windows
 			this.colorbackcolor.Size = new System.Drawing.Size(168, 23);
 			this.colorbackcolor.TabIndex = 0;
 			// 
+			// label2
+			// 
+			this.label2.AutoSize = true;
+			this.label2.Location = new System.Drawing.Point(14, 405);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(147, 14);
+			this.label2.TabIndex = 14;
+			this.label2.Text = "Passable lines transparency:";
+			this.label2.TextAlign = System.Drawing.ContentAlignment.TopRight;
+			// 
+			// doublesidedalphalabel
+			// 
+			this.doublesidedalphalabel.AutoSize = true;
+			this.doublesidedalphalabel.Location = new System.Drawing.Point(147, 438);
+			this.doublesidedalphalabel.Name = "doublesidedalphalabel";
+			this.doublesidedalphalabel.Size = new System.Drawing.Size(23, 14);
+			this.doublesidedalphalabel.TabIndex = 16;
+			this.doublesidedalphalabel.Text = "0%";
+			// 
 			// colorselection
 			// 
 			this.colorselection.BackColor = System.Drawing.Color.Transparent;
@@ -686,16 +676,6 @@ namespace CodeImp.DoomBuilder.Windows
 			this.colorvertices.Size = new System.Drawing.Size(168, 23);
 			this.colorvertices.TabIndex = 1;
 			// 
-			// doublesidedalpha
-			// 
-			this.doublesidedalpha.LargeChange = 3;
-			this.doublesidedalpha.Location = new System.Drawing.Point(11, 426);
-			this.doublesidedalpha.Name = "doublesidedalpha";
-			this.doublesidedalpha.Size = new System.Drawing.Size(130, 45);
-			this.doublesidedalpha.TabIndex = 2;
-			this.doublesidedalpha.TickStyle = System.Windows.Forms.TickStyle.TopLeft;
-			this.doublesidedalpha.ValueChanged += new System.EventHandler(this.doublesidedalpha_ValueChanged);
-			// 
 			// colorhighlight
 			// 
 			this.colorhighlight.BackColor = System.Drawing.Color.Transparent;
@@ -732,6 +712,38 @@ namespace CodeImp.DoomBuilder.Windows
 			this.toolTip1.SetToolTip(this.tbNumSectors, "Will draw selected number of newly created sectors using \"New sector\" color");
 			this.tbNumSectors.ValueChanged += new System.EventHandler(this.tbNumSectors_ValueChanged);
 			// 
+			// cbStretchModels
+			// 
+			this.cbStretchModels.AutoSize = true;
+			this.cbStretchModels.Location = new System.Drawing.Point(236, 170);
+			this.cbStretchModels.Name = "cbStretchModels";
+			this.cbStretchModels.Size = new System.Drawing.Size(167, 18);
+			this.cbStretchModels.TabIndex = 18;
+			this.cbStretchModels.Text = "Scale models in visual modes";
+			this.toolTip1.SetToolTip(this.cbStretchModels, "If enabled, heights of models will be scaled down by 15% in Visual mode \r\nto mimi" +
+					"ck GZDoom\'s way of rendering.");
+			this.cbStretchModels.UseVisualStyleBackColor = true;
+			// 
+			// squarethings
+			// 
+			this.squarethings.AutoSize = true;
+			this.squarethings.Location = new System.Drawing.Point(25, 170);
+			this.squarethings.Name = "squarethings";
+			this.squarethings.Size = new System.Drawing.Size(93, 18);
+			this.squarethings.TabIndex = 8;
+			this.squarethings.Text = "Square things";
+			this.squarethings.UseVisualStyleBackColor = true;
+			// 
+			// qualitydisplay
+			// 
+			this.qualitydisplay.AutoSize = true;
+			this.qualitydisplay.Location = new System.Drawing.Point(25, 191);
+			this.qualitydisplay.Name = "qualitydisplay";
+			this.qualitydisplay.Size = new System.Drawing.Size(130, 18);
+			this.qualitydisplay.TabIndex = 7;
+			this.qualitydisplay.Text = "High quality rendering";
+			this.qualitydisplay.UseVisualStyleBackColor = true;
+			// 
 			// cancel
 			// 
 			this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
@@ -1193,6 +1205,7 @@ namespace CodeImp.DoomBuilder.Windows
 			// 
 			this.actioncontrolpanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
 						| System.Windows.Forms.AnchorStyles.Right)));
+			this.actioncontrolpanel.Controls.Add(this.actiondescription);
 			this.actioncontrolpanel.Controls.Add(this.keyusedlist);
 			this.actioncontrolpanel.Controls.Add(this.keyusedlabel);
 			this.actioncontrolpanel.Controls.Add(this.disregardshiftlabel);
@@ -1202,7 +1215,6 @@ namespace CodeImp.DoomBuilder.Windows
 			this.actioncontrolpanel.Controls.Add(this.actioncontrolclear);
 			this.actioncontrolpanel.Controls.Add(label6);
 			this.actioncontrolpanel.Controls.Add(this.actionkey);
-			this.actioncontrolpanel.Controls.Add(this.actiondescription);
 			this.actioncontrolpanel.Controls.Add(label5);
 			this.actioncontrolpanel.Enabled = false;
 			this.actioncontrolpanel.Location = new System.Drawing.Point(377, 12);
@@ -1213,6 +1225,16 @@ namespace CodeImp.DoomBuilder.Windows
 			this.actioncontrolpanel.TabStop = false;
 			this.actioncontrolpanel.Text = " Action control ";
 			// 
+			// actiondescription
+			// 
+			this.actiondescription.BorderStyle = System.Windows.Forms.BorderStyle.None;
+			this.actiondescription.Location = new System.Drawing.Point(20, 47);
+			this.actiondescription.Multiline = true;
+			this.actiondescription.Name = "actiondescription";
+			this.actiondescription.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+			this.actiondescription.Size = new System.Drawing.Size(256, 72);
+			this.actiondescription.TabIndex = 12;
+			// 
 			// keyusedlist
 			// 
 			this.keyusedlist.BackColor = System.Drawing.SystemColors.Control;
@@ -1223,7 +1245,7 @@ namespace CodeImp.DoomBuilder.Windows
 			this.keyusedlist.Location = new System.Drawing.Point(23, 307);
 			this.keyusedlist.Name = "keyusedlist";
 			this.keyusedlist.SelectionMode = System.Windows.Forms.SelectionMode.None;
-			this.keyusedlist.Size = new System.Drawing.Size(233, 115);
+			this.keyusedlist.Size = new System.Drawing.Size(238, 115);
 			this.keyusedlist.Sorted = true;
 			this.keyusedlist.TabIndex = 11;
 			this.keyusedlist.Visible = false;
@@ -1247,7 +1269,7 @@ namespace CodeImp.DoomBuilder.Windows
 			this.actioncontrol.ImeMode = System.Windows.Forms.ImeMode.Off;
 			this.actioncontrol.Location = new System.Drawing.Point(23, 190);
 			this.actioncontrol.Name = "actioncontrol";
-			this.actioncontrol.Size = new System.Drawing.Size(233, 22);
+			this.actioncontrol.Size = new System.Drawing.Size(238, 22);
 			this.actioncontrol.TabIndex = 8;
 			this.actioncontrol.TabStop = false;
 			this.actioncontrol.SelectedIndexChanged += new System.EventHandler(this.actioncontrol_SelectedIndexChanged);
@@ -1284,15 +1306,6 @@ namespace CodeImp.DoomBuilder.Windows
 			this.actionkey.TabStop = false;
 			this.actionkey.KeyDown += new System.Windows.Forms.KeyEventHandler(this.actionkey_KeyDown);
 			// 
-			// actiondescription
-			// 
-			this.actiondescription.AutoEllipsis = true;
-			this.actiondescription.Location = new System.Drawing.Point(20, 50);
-			this.actiondescription.Name = "actiondescription";
-			this.actiondescription.Size = new System.Drawing.Size(245, 71);
-			this.actiondescription.TabIndex = 3;
-			this.actiondescription.UseMnemonic = false;
-			// 
 			// tabcolors
 			// 
 			this.tabcolors.Controls.Add(this.appearancegroup1);
@@ -1523,7 +1536,7 @@ namespace CodeImp.DoomBuilder.Windows
 			this.scripttabwidth.AllowDecimal = false;
 			this.scripttabwidth.AllowNegative = false;
 			this.scripttabwidth.AllowRelative = false;
-			this.scripttabwidth.ButtonStep = 2;
+			this.scripttabwidth.ButtonStep = 2F;
 			this.scripttabwidth.Location = new System.Drawing.Point(259, 155);
 			this.scripttabwidth.Name = "scripttabwidth";
 			this.scripttabwidth.Size = new System.Drawing.Size(71, 24);
@@ -1754,18 +1767,6 @@ namespace CodeImp.DoomBuilder.Windows
 			this.pasteoptions.Size = new System.Drawing.Size(666, 427);
 			this.pasteoptions.TabIndex = 0;
 			// 
-			// color3dFloors
-			// 
-			this.color3dFloors.BackColor = System.Drawing.Color.Transparent;
-			this.color3dFloors.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
-			this.color3dFloors.Label = "3D Floors:";
-			this.color3dFloors.Location = new System.Drawing.Point(15, 286);
-			this.color3dFloors.MaximumSize = new System.Drawing.Size(10000, 23);
-			this.color3dFloors.MinimumSize = new System.Drawing.Size(100, 23);
-			this.color3dFloors.Name = "color3dFloors";
-			this.color3dFloors.Size = new System.Drawing.Size(168, 23);
-			this.color3dFloors.TabIndex = 24;
-			// 
 			// PreferencesForm
 			// 
 			this.AcceptButton = this.apply;
@@ -1843,7 +1844,6 @@ namespace CodeImp.DoomBuilder.Windows
 		private System.Windows.Forms.Label actiontitle;
 		private System.Windows.Forms.Button actioncontrolclear;
 		private System.Windows.Forms.TextBox actionkey;
-		private System.Windows.Forms.Label actiondescription;
 		private System.Windows.Forms.TabPage tabcolors;
 		private CodeImp.DoomBuilder.Controls.ColorControl colorselection;
 		private CodeImp.DoomBuilder.Controls.ColorControl colorhighlight;
@@ -1956,5 +1956,6 @@ namespace CodeImp.DoomBuilder.Windows
 		private System.Windows.Forms.TextBox tbFilterActions;
 		private System.Windows.Forms.Label label24;
 		private CodeImp.DoomBuilder.Controls.ColorControl color3dFloors;
+		private System.Windows.Forms.TextBox actiondescription;
 	}
 }
\ No newline at end of file
diff --git a/Source/Core/Windows/SectorEditForm.Designer.cs b/Source/Core/Windows/SectorEditForm.Designer.cs
index b8a3134d..c314c612 100644
--- a/Source/Core/Windows/SectorEditForm.Designer.cs
+++ b/Source/Core/Windows/SectorEditForm.Designer.cs
@@ -31,17 +31,17 @@ namespace CodeImp.DoomBuilder.Windows
 			System.Windows.Forms.Label label1;
 			System.Windows.Forms.Label label3;
 			System.Windows.Forms.GroupBox groupeffect;
-			System.Windows.Forms.Label label9;
 			System.Windows.Forms.Label label8;
+			System.Windows.Forms.Label label9;
 			System.Windows.Forms.GroupBox groupfloorceiling;
 			System.Windows.Forms.Label label6;
 			System.Windows.Forms.Label label5;
 			System.Windows.Forms.Label label2;
 			System.Windows.Forms.Label label4;
-			this.tagSelector = new CodeImp.DoomBuilder.GZBuilder.Controls.TagSelector();
-			this.brightness = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
 			this.browseeffect = new System.Windows.Forms.Button();
 			this.effect = new CodeImp.DoomBuilder.Controls.ActionSelectorControl();
+			this.tagSelector = new CodeImp.DoomBuilder.GZBuilder.Controls.TagSelector();
+			this.brightness = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
 			this.ceilingheight = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
 			this.sectorheightlabel = new System.Windows.Forms.Label();
 			this.sectorheight = new System.Windows.Forms.Label();
@@ -56,8 +56,8 @@ namespace CodeImp.DoomBuilder.Windows
 			label1 = new System.Windows.Forms.Label();
 			label3 = new System.Windows.Forms.Label();
 			groupeffect = new System.Windows.Forms.GroupBox();
-			label9 = new System.Windows.Forms.Label();
 			label8 = new System.Windows.Forms.Label();
+			label9 = new System.Windows.Forms.Label();
 			groupfloorceiling = new System.Windows.Forms.GroupBox();
 			label6 = new System.Windows.Forms.Label();
 			label5 = new System.Windows.Forms.Label();
@@ -86,15 +86,9 @@ namespace CodeImp.DoomBuilder.Windows
 			label3.Text = "Ceiling";
 			label3.TextAlign = System.Drawing.ContentAlignment.TopCenter;
 			// 
-			// tagSelector
-			// 
-			this.tagSelector.Location = new System.Drawing.Point(47, 52);
-			this.tagSelector.Name = "tagSelector";
-			this.tagSelector.Size = new System.Drawing.Size(384, 34);
-			this.tagSelector.TabIndex = 0;
-			// 
 			// groupeffect
 			// 
+			groupeffect.BackColor = System.Drawing.Color.Transparent;
 			groupeffect.Controls.Add(this.browseeffect);
 			groupeffect.Controls.Add(this.effect);
 			groupeffect.Controls.Add(label8);
@@ -106,18 +100,6 @@ namespace CodeImp.DoomBuilder.Windows
 			groupeffect.TabStop = false;
 			groupeffect.Text = "Effect and Identification";
 			// 
-			// brightness
-			// 
-			this.brightness.AllowDecimal = false;
-			this.brightness.AllowNegative = false;
-			this.brightness.AllowRelative = true;
-			this.brightness.ButtonStep = 8F;
-			this.brightness.Location = new System.Drawing.Point(99, 124);
-			this.brightness.Name = "brightness";
-			this.brightness.Size = new System.Drawing.Size(73, 24);
-			this.brightness.StepValues = null;
-			this.brightness.TabIndex = 24;
-			// 
 			// browseeffect
 			// 
 			this.browseeffect.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
@@ -131,15 +113,6 @@ namespace CodeImp.DoomBuilder.Windows
 			this.browseeffect.UseVisualStyleBackColor = true;
 			this.browseeffect.Click += new System.EventHandler(this.browseeffect_Click);
 			// 
-			// label9
-			// 
-			label9.AutoSize = true;
-			label9.Location = new System.Drawing.Point(31, 129);
-			label9.Name = "label9";
-			label9.Size = new System.Drawing.Size(62, 14);
-			label9.TabIndex = 2;
-			label9.Text = "Brightness:";
-			// 
 			// effect
 			// 
 			this.effect.BackColor = System.Drawing.Color.Transparent;
@@ -161,8 +134,25 @@ namespace CodeImp.DoomBuilder.Windows
 			label8.TabIndex = 0;
 			label8.Text = "Special:";
 			// 
+			// tagSelector
+			// 
+			this.tagSelector.Location = new System.Drawing.Point(47, 52);
+			this.tagSelector.Name = "tagSelector";
+			this.tagSelector.Size = new System.Drawing.Size(384, 34);
+			this.tagSelector.TabIndex = 0;
+			// 
+			// label9
+			// 
+			label9.AutoSize = true;
+			label9.Location = new System.Drawing.Point(31, 129);
+			label9.Name = "label9";
+			label9.Size = new System.Drawing.Size(62, 14);
+			label9.TabIndex = 2;
+			label9.Text = "Brightness:";
+			// 
 			// groupfloorceiling
 			// 
+			groupfloorceiling.BackColor = System.Drawing.Color.Transparent;
 			groupfloorceiling.Controls.Add(this.brightness);
 			groupfloorceiling.Controls.Add(this.ceilingheight);
 			groupfloorceiling.Controls.Add(label6);
@@ -182,6 +172,18 @@ namespace CodeImp.DoomBuilder.Windows
 			groupfloorceiling.TabStop = false;
 			groupfloorceiling.Text = "Floor and Ceiling ";
 			// 
+			// brightness
+			// 
+			this.brightness.AllowDecimal = false;
+			this.brightness.AllowNegative = false;
+			this.brightness.AllowRelative = true;
+			this.brightness.ButtonStep = 8F;
+			this.brightness.Location = new System.Drawing.Point(99, 124);
+			this.brightness.Name = "brightness";
+			this.brightness.Size = new System.Drawing.Size(73, 24);
+			this.brightness.StepValues = null;
+			this.brightness.TabIndex = 24;
+			// 
 			// ceilingheight
 			// 
 			this.ceilingheight.AllowDecimal = false;
@@ -319,7 +321,7 @@ namespace CodeImp.DoomBuilder.Windows
 			// 
 			// panel1
 			// 
-			this.panel1.BackColor = System.Drawing.SystemColors.ControlLightLight;
+			this.panel1.BackColor = System.Drawing.SystemColors.Window;
 			this.panel1.Controls.Add(groupfloorceiling);
 			this.panel1.Controls.Add(groupeffect);
 			this.panel1.Location = new System.Drawing.Point(12, 10);
diff --git a/Source/Core/Windows/SectorEditFormUDMF.Designer.cs b/Source/Core/Windows/SectorEditFormUDMF.Designer.cs
index 7c0fa20c..a119152c 100644
--- a/Source/Core/Windows/SectorEditFormUDMF.Designer.cs
+++ b/Source/Core/Windows/SectorEditFormUDMF.Designer.cs
@@ -630,7 +630,7 @@
 			// 
 			// floorOffsets
 			// 
-			this.floorOffsets.AllowDecimal = false;
+			this.floorOffsets.AllowDecimal = true;
 			this.floorOffsets.ButtonStep = 16F;
 			this.floorOffsets.DefaultValue = 0F;
 			this.floorOffsets.Field1 = "xpanningfloor";
@@ -800,7 +800,7 @@
 			// 
 			// ceilOffsets
 			// 
-			this.ceilOffsets.AllowDecimal = false;
+			this.ceilOffsets.AllowDecimal = true;
 			this.ceilOffsets.ButtonStep = 16F;
 			this.ceilOffsets.DefaultValue = 0F;
 			this.ceilOffsets.Field1 = "xpanningceiling";
diff --git a/Source/Core/Windows/SectorEditFormUDMF.resx b/Source/Core/Windows/SectorEditFormUDMF.resx
index 90add833..53aec627 100644
--- a/Source/Core/Windows/SectorEditFormUDMF.resx
+++ b/Source/Core/Windows/SectorEditFormUDMF.resx
@@ -138,21 +138,6 @@
   <metadata name="label8.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>False</value>
   </metadata>
-  <metadata name="label14.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>False</value>
-  </metadata>
-  <metadata name="label9.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>False</value>
-  </metadata>
-  <metadata name="label13.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>False</value>
-  </metadata>
-  <metadata name="label2.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>False</value>
-  </metadata>
-  <metadata name="label8.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>False</value>
-  </metadata>
   <metadata name="groupfloorceiling.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>False</value>
   </metadata>
@@ -162,12 +147,6 @@
   <metadata name="label5.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>False</value>
   </metadata>
-  <metadata name="label6.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>False</value>
-  </metadata>
-  <metadata name="label5.GenerateMember" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>False</value>
-  </metadata>
   <metadata name="tabproperties.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
@@ -177,7 +156,4 @@
   <metadata name="fieldslist.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
-  <metadata name="fieldslist.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>True</value>
-  </metadata>
 </root>
\ No newline at end of file
diff --git a/Source/Core/Windows/VertexEditForm.Designer.cs b/Source/Core/Windows/VertexEditForm.Designer.cs
index 89c8131f..8854b378 100644
--- a/Source/Core/Windows/VertexEditForm.Designer.cs
+++ b/Source/Core/Windows/VertexEditForm.Designer.cs
@@ -44,6 +44,8 @@ namespace CodeImp.DoomBuilder.Windows
 			this.fieldslist = new CodeImp.DoomBuilder.Controls.FieldsEditorControl();
 			this.cancel = new System.Windows.Forms.Button();
 			this.apply = new System.Windows.Forms.Button();
+			this.clearZCeiling = new System.Windows.Forms.Button();
+			this.clearZFloor = new System.Windows.Forms.Button();
 			tabproperties = new System.Windows.Forms.TabPage();
 			label2 = new System.Windows.Forms.Label();
 			label3 = new System.Windows.Forms.Label();
@@ -87,13 +89,15 @@ namespace CodeImp.DoomBuilder.Windows
 			// 
 			// panelHeightControls
 			// 
+			this.panelHeightControls.Controls.Add(this.clearZFloor);
+			this.panelHeightControls.Controls.Add(this.clearZCeiling);
 			this.panelHeightControls.Controls.Add(this.zceiling);
 			this.panelHeightControls.Controls.Add(this.zfloor);
 			this.panelHeightControls.Controls.Add(label2);
 			this.panelHeightControls.Controls.Add(label3);
 			this.panelHeightControls.Location = new System.Drawing.Point(48, 73);
 			this.panelHeightControls.Name = "panelHeightControls";
-			this.panelHeightControls.Size = new System.Drawing.Size(308, 100);
+			this.panelHeightControls.Size = new System.Drawing.Size(361, 100);
 			this.panelHeightControls.TabIndex = 30;
 			// 
 			// zceiling
@@ -107,6 +111,7 @@ namespace CodeImp.DoomBuilder.Windows
 			this.zceiling.Size = new System.Drawing.Size(120, 24);
 			this.zceiling.StepValues = null;
 			this.zceiling.TabIndex = 28;
+			this.zceiling.WhenTextChanged += new System.EventHandler(this.zceiling_WhenTextChanged);
 			// 
 			// zfloor
 			// 
@@ -119,6 +124,7 @@ namespace CodeImp.DoomBuilder.Windows
 			this.zfloor.Size = new System.Drawing.Size(120, 24);
 			this.zfloor.StepValues = null;
 			this.zfloor.TabIndex = 29;
+			this.zfloor.WhenTextChanged += new System.EventHandler(this.zfloor_WhenTextChanged);
 			// 
 			// label2
 			// 
@@ -149,6 +155,7 @@ namespace CodeImp.DoomBuilder.Windows
 			this.positiony.Size = new System.Drawing.Size(120, 24);
 			this.positiony.StepValues = null;
 			this.positiony.TabIndex = 25;
+			this.positiony.WhenTextChanged += new System.EventHandler(this.positiony_WhenTextChanged);
 			// 
 			// positionx
 			// 
@@ -161,6 +168,7 @@ namespace CodeImp.DoomBuilder.Windows
 			this.positionx.Size = new System.Drawing.Size(120, 24);
 			this.positionx.StepValues = null;
 			this.positionx.TabIndex = 24;
+			this.positionx.WhenTextChanged += new System.EventHandler(this.positionx_WhenTextChanged);
 			// 
 			// label1
 			// 
@@ -228,6 +236,7 @@ namespace CodeImp.DoomBuilder.Windows
 			this.fieldslist.TypeColumnVisible = true;
 			this.fieldslist.TypeColumnWidth = 100;
 			this.fieldslist.ValueColumnVisible = true;
+			this.fieldslist.OnFieldValueChanged += new CodeImp.DoomBuilder.Controls.FieldsEditorControl.SingleFieldNameEvent(this.fieldslist_OnFieldValueChanged);
 			// 
 			// cancel
 			// 
@@ -252,6 +261,26 @@ namespace CodeImp.DoomBuilder.Windows
 			this.apply.UseVisualStyleBackColor = true;
 			this.apply.Click += new System.EventHandler(this.apply_Click);
 			// 
+			// clearZCeiling
+			// 
+			this.clearZCeiling.Image = global::CodeImp.DoomBuilder.Properties.Resources.SearchClear;
+			this.clearZCeiling.Location = new System.Drawing.Point(314, 0);
+			this.clearZCeiling.Name = "clearZCeiling";
+			this.clearZCeiling.Size = new System.Drawing.Size(26, 24);
+			this.clearZCeiling.TabIndex = 30;
+			this.clearZCeiling.UseVisualStyleBackColor = true;
+			this.clearZCeiling.Click += new System.EventHandler(this.clearZCeiling_Click);
+			// 
+			// clearZFloor
+			// 
+			this.clearZFloor.Image = global::CodeImp.DoomBuilder.Properties.Resources.SearchClear;
+			this.clearZFloor.Location = new System.Drawing.Point(314, 32);
+			this.clearZFloor.Name = "clearZFloor";
+			this.clearZFloor.Size = new System.Drawing.Size(26, 24);
+			this.clearZFloor.TabIndex = 31;
+			this.clearZFloor.UseVisualStyleBackColor = true;
+			this.clearZFloor.Click += new System.EventHandler(this.clearZFloor_Click);
+			// 
 			// VertexEditForm
 			// 
 			this.AcceptButton = this.apply;
@@ -295,5 +324,7 @@ namespace CodeImp.DoomBuilder.Windows
 		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox zfloor;
 		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox zceiling;
 		private System.Windows.Forms.Panel panelHeightControls;
+		private System.Windows.Forms.Button clearZFloor;
+		private System.Windows.Forms.Button clearZCeiling;
 	}
 }
\ No newline at end of file
diff --git a/Source/Core/Windows/VertexEditForm.cs b/Source/Core/Windows/VertexEditForm.cs
index 9b79f29a..d12a0b0c 100644
--- a/Source/Core/Windows/VertexEditForm.cs
+++ b/Source/Core/Windows/VertexEditForm.cs
@@ -31,18 +31,46 @@ namespace CodeImp.DoomBuilder.Windows
 	{
 		#region ================== Constants
 
-		#endregion
-
-		#region ================== Variables
-
-		private ICollection<Vertex> vertices;
+		private const string CLEAR_VALUE = "Unused"; //mxd
 
 		#endregion
 
-		#region ================== Properties
+		#region ================== Events
+
+		public event EventHandler OnValuesChanged; //mxd
 
 		#endregion
 		
+		#region ================== Variables
+
+		private ICollection<Vertex> vertices;
+		private bool blockUpdate; //mxd
+		private List<VertexProperties> vertexProps; //mxd
+
+		private struct VertexProperties //mxd
+		{
+			public float X;
+			public float Y;
+			public float ZCeiling;
+			public float ZFloor;
+
+			public VertexProperties(Vertex v) {
+				X = v.Position.x;
+				Y = v.Position.y;
+				ZCeiling = v.ZCeiling;
+				ZFloor = v.ZFloor;
+			}
+
+		}
+
+		#endregion
+
+		#region ================== mxd. Properties
+
+		public ICollection<Vertex> Selection { get { return vertices; } }
+
+		#endregion
+
 		#region ================== Constructor
 
 		// Constructor
@@ -81,9 +109,12 @@ namespace CodeImp.DoomBuilder.Windows
 		// This sets up the form to edit the given vertices
 		public void Setup(ICollection<Vertex> vertices, bool allowPositionChange)
 		{
+			blockUpdate = true; //mxd
+			
 			// Keep this list
 			this.vertices = vertices;
 			if(vertices.Count > 1) this.Text = "Edit Vertices (" + vertices.Count + ")";
+			vertexProps = new List<VertexProperties>(); //mxd
 
 			////////////////////////////////////////////////////////////////////////
 			// Set all options to the first vertex properties
@@ -115,80 +146,173 @@ namespace CodeImp.DoomBuilder.Windows
 				if(positiony.Text != v.Position.y.ToString()) positiony.Text = "";
 
 				// Custom fields
+				v.Fields.BeforeFieldsChange();//mxd
 				fieldslist.SetValues(v.Fields, false);
+
+				//mxd. Store initial properties
+				vertexProps.Add(new VertexProperties(v));
 			}
 
 			//mxd. Height offsets
 			if(General.Map.UDMF) {
-				zceiling.Text = vc.Fields.GetValue("zceiling", 0f).ToString();
-				zfloor.Text = vc.Fields.GetValue("zfloor", 0f).ToString();
+				zceiling.Text = (float.IsNaN(vc.ZCeiling) ? CLEAR_VALUE : vc.ZCeiling.ToString());
+				zfloor.Text = (float.IsNaN(vc.ZFloor) ? CLEAR_VALUE : vc.ZFloor.ToString());
 
 				foreach(Vertex v in vertices) {
-					if(zceiling.Text != v.Fields.GetValue("zceiling", 0f).ToString())
-						zceiling.Text = "";
-					if(zfloor.Text != v.Fields.GetValue("zfloor", 0f).ToString())
-						zfloor.Text = "";
+					string zc = (float.IsNaN(v.ZCeiling) ? CLEAR_VALUE : v.ZCeiling.ToString());
+					string zf = (float.IsNaN(v.ZFloor) ? CLEAR_VALUE : v.ZFloor.ToString());
+
+					if(zceiling.Text != zc)	zceiling.Text = "";
+					if(zfloor.Text != zf) zfloor.Text = "";
 				}
 			}
+
+			//mxd. Make undo
+			string undodesc = "vertex";
+			if(vertices.Count > 1) undodesc = vertices.Count + " vertices";
+			General.Map.UndoRedo.CreateUndo("Edit " + undodesc);
+
+			blockUpdate = false; //mxd
 		}
 		
 		#endregion
-		
+
+		#region ================== mxd. Control Events
+
+		private void positionx_WhenTextChanged(object sender, EventArgs e) {
+			if(blockUpdate) return;
+
+			//restore values
+			if(string.IsNullOrEmpty(positionx.Text)) {
+				int i = 0;
+
+				// Apply position
+				foreach(Vertex v in vertices)
+					v.Move(new Vector2D(vertexProps[i++].X, v.Position.y));
+			} else { //update values
+				// Verify the coordinates
+				float px = positionx.GetResultFloat(0.0f);
+				if(px < General.Map.FormatInterface.MinCoordinate) {
+					positionx.Text = General.Map.FormatInterface.MinCoordinate.ToString();
+					return;
+				} else if(px > General.Map.FormatInterface.MaxCoordinate) {
+					positionx.Text = General.Map.FormatInterface.MaxCoordinate.ToString();
+					return;
+				}
+
+				// Apply position
+				foreach(Vertex v in vertices)
+					v.Move(new Vector2D(px, v.Position.y));
+			}
+
+			General.Map.IsChanged = true;
+			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
+		}
+
+		private void positiony_WhenTextChanged(object sender, EventArgs e) {
+			if(blockUpdate) return;
+
+			//restore values
+			if(string.IsNullOrEmpty(positiony.Text)) {
+				int i = 0;
+
+				// Apply position
+				foreach(Vertex v in vertices)
+					v.Move(new Vector2D(v.Position.x, vertexProps[i++].Y));
+			} else { //update values
+				// Verify the coordinates
+				float py = positiony.GetResultFloat(0.0f);
+				if(py < General.Map.FormatInterface.MinCoordinate) {
+					positiony.Text = General.Map.FormatInterface.MinCoordinate.ToString();
+					return;
+				} else if(py > General.Map.FormatInterface.MaxCoordinate) {
+					positiony.Text = General.Map.FormatInterface.MaxCoordinate.ToString();
+					return;
+				}
+
+				// Apply position
+				foreach(Vertex v in vertices)
+					v.Move(new Vector2D(v.Position.x, py));
+			}
+
+			General.Map.IsChanged = true;
+			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
+		}
+
+		private void zceiling_WhenTextChanged(object sender, EventArgs e) {
+			if(blockUpdate) return;
+
+			//restore values
+			if(string.IsNullOrEmpty(zceiling.Text)) {
+				int i = 0;
+
+				foreach(Vertex v in vertices)
+					v.ZCeiling = vertexProps[i++].ZCeiling;
+			//clear values
+			} else if(zceiling.Text == CLEAR_VALUE) { 
+				foreach(Vertex v in vertices)
+					v.ZCeiling = float.NaN;
+			//update values
+			} else { 
+				foreach(Vertex v in vertices)
+					v.ZCeiling = zceiling.GetResultFloat(v.ZCeiling);
+			}
+
+			General.Map.IsChanged = true;
+			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
+		}
+
+		private void zfloor_WhenTextChanged(object sender, EventArgs e) {
+			if(blockUpdate) return;
+
+			//restore values
+			if(string.IsNullOrEmpty(zfloor.Text)) {
+				int i = 0;
+
+				foreach(Vertex v in vertices)
+					v.ZFloor = vertexProps[i++].ZFloor;
+			//clear values
+			}else if(zfloor.Text == CLEAR_VALUE){
+				foreach(Vertex v in vertices)
+					v.ZFloor = float.NaN;
+			//update values
+			} else { 
+				foreach(Vertex v in vertices) 
+					v.ZFloor = zfloor.GetResultFloat(v.ZFloor);
+			}
+
+			General.Map.IsChanged = true;
+			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
+		}
+
+		private void fieldslist_OnFieldValueChanged(string fieldname) {
+			if(blockUpdate) return;
+
+			foreach(Vertex v in vertices)
+				fieldslist.Apply(v.Fields);
+
+			General.Map.IsChanged = true;
+			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
+		}
+
+		//mxd
+		private void clearZFloor_Click(object sender, EventArgs e) {
+			zfloor.Text = CLEAR_VALUE;
+		}
+
+		//mxd
+		private void clearZCeiling_Click(object sender, EventArgs e) {
+			zceiling.Text = CLEAR_VALUE;
+		}
+
+		#endregion
+
 		#region ================== Events
 
 		// OK clicked
 		private void apply_Click(object sender, EventArgs e)
 		{
-			string undodesc = "vertex";
-
-			// Verify the coordinates
-			if((positionx.GetResultFloat(0.0f) < General.Map.FormatInterface.MinCoordinate) || (positionx.GetResultFloat(0.0f) > General.Map.FormatInterface.MaxCoordinate) ||
-			   (positiony.GetResultFloat(0.0f) < General.Map.FormatInterface.MinCoordinate) || (positiony.GetResultFloat(0.0f) > General.Map.FormatInterface.MaxCoordinate))
-			{
-				General.ShowWarningMessage("Vertex coordinates must be between " + General.Map.FormatInterface.MinCoordinate + " and " + General.Map.FormatInterface.MaxCoordinate + ".", MessageBoxButtons.OK);
-				return;
-			}
-			
-			// Make undo
-			if(vertices.Count > 1) undodesc = vertices.Count + " vertices";
-			General.Map.UndoRedo.CreateUndo("Edit " + undodesc);
-
-			// Go for all vertices
-			foreach(Vertex v in vertices)
-			{
-				// Apply position
-				Vector2D p = new Vector2D();
-				p.x = General.Clamp(positionx.GetResultFloat(v.Position.x), (float)General.Map.FormatInterface.MinCoordinate, (float)General.Map.FormatInterface.MaxCoordinate);
-				p.y = General.Clamp(positiony.GetResultFloat(v.Position.y), (float)General.Map.FormatInterface.MinCoordinate, (float)General.Map.FormatInterface.MaxCoordinate);
-				v.Move(p);
-
-				// Custom fields
-				fieldslist.Apply(v.Fields);
-			}
-
-			//mxd. Height offsets
-			if(General.Map.UDMF) {
-				foreach(Vertex v in vertices) {
-					if(string.IsNullOrEmpty(zceiling.Text)) {
-						if(v.Fields.ContainsKey("zceiling"))
-							v.Fields.Remove("zceiling");
-					} else {
-						float oldCeil = v.Fields.GetValue("zceiling", 0f);
-						UDMFTools.SetFloat(v.Fields, "zceiling", zceiling.GetResultFloat(oldCeil), 0, false);
-					}
-
-					if(string.IsNullOrEmpty(zfloor.Text)) {
-						if(v.Fields.ContainsKey("zfloor"))
-							v.Fields.Remove("zfloor");
-					} else {
-						float oldFloor = v.Fields.GetValue("zfloor", 0f);
-						UDMFTools.SetFloat(v.Fields, "zfloor", zfloor.GetResultFloat(oldFloor), 0, false);
-					}
-				}
-			}
-			
 			// Done
-			General.Map.IsChanged = true;
 			this.DialogResult = DialogResult.OK;
 			this.Close();
 		}
@@ -196,7 +320,10 @@ namespace CodeImp.DoomBuilder.Windows
 		// Cancel clicked
 		private void cancel_Click(object sender, EventArgs e)
 		{
-			// Just close
+			//mxd. perform undo
+			General.Map.UndoRedo.PerformUndo();
+			
+			// And close
 			this.DialogResult = DialogResult.Cancel;
 			this.Close();
 		}
diff --git a/Source/Core/Windows/VertexEditForm.resx b/Source/Core/Windows/VertexEditForm.resx
index 1452fde7..2c1cbc9a 100644
--- a/Source/Core/Windows/VertexEditForm.resx
+++ b/Source/Core/Windows/VertexEditForm.resx
@@ -150,6 +150,9 @@
   <metadata name="fieldslist.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
+  <metadata name="fieldslist.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
   <metadata name="cancel.Locked" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>True</value>
   </metadata>
diff --git a/Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs b/Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs
index 85e5bd83..ba37f83b 100644
--- a/Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/VerticesMode.cs
@@ -397,16 +397,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				{
 					if(General.Interface.IsActiveWindow)
 					{
-						// Show line edit dialog
+						//mxd. Show realtime vertex edit dialog
+						General.Interface.OnEditFormValuesChanged += new EventHandler(vertexEditForm_OnValuesChanged);
 						General.Interface.ShowEditVertices(selected);
-						General.Map.Map.Update();
 
 						// When a single vertex was selected, deselect it now
 						if(selected.Count == 1) General.Map.Map.ClearSelectedVertices();
-
-						// Update entire display
-						General.Map.Renderer2D.Update3dFloorTagsList(); //mxd
-						General.Interface.RedrawDisplay();
 					}
 				}
 			}
@@ -414,6 +410,14 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			editpressed = false;
 			base.OnEditEnd();
 		}
+
+		//mxd
+		private void vertexEditForm_OnValuesChanged(object sender, EventArgs e) {
+			// Update entire display
+			General.Map.Map.Update();
+			General.Map.Renderer2D.Update3dFloorTagsList();
+			General.Interface.RedrawDisplay();
+		}
 		
 		// Mouse moves
 		public override void OnMouseMove(MouseEventArgs e)
@@ -808,8 +812,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				}
 			}
 
-			List<Linedef> changedLines = new List<Linedef>();
-
 			// Go for all vertices that need to be removed
 			foreach(Vertex v in selected) {
 				// Not already removed automatically?
@@ -832,13 +834,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 							}
 						}
 
-						if(!dontMerge) {
-							if(ld1.Start == v) ld1.SetStartVertex(v2);
-							else ld1.SetEndVertex(v2);
-							ld2.Dispose();
-
-							if(!changedLines.Contains(ld1)) changedLines.Add(ld1);
-						}
+						if(!dontMerge) mergeLines(selected, ld1, ld2, v);
 					}
 
 					// Trash vertex
@@ -850,53 +846,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			General.Map.Map.Update();
 			General.Map.IsChanged = true;
 
-			//redraw changed lines
-			foreach(Linedef l in changedLines) {
-				if(l.IsDisposed) continue;
-				drawLine(l.Start.Position, l.End.Position);
-
-				if(l.IsDisposed) continue;
-				drawLine(l.Start.Position, l.End.Position);
-			}
-
-			//redraw all affected sectors
-			List<Linedef> redrawnLines = new List<Linedef>();
-			foreach(Sector sector in affectedSectors) {
-				if(sector.IsDisposed) continue;
-
-				List<Linedef> lines = new List<Linedef>();
-				foreach(Sidedef side in sector.Sidedefs) {
-					if(!lines.Contains(side.Line)) 
-						lines.Add(side.Line);
-				}
-
-				if(sector.Triangles.Vertices.Count == 0) {
-					sector.Dispose();
-				}
-
-				foreach(Linedef line in lines) {
-					if(line.IsDisposed || redrawnLines.Contains(line)) continue;
-
-					if(line.Front == null && line.Back != null) {
-						//Flip linedef
-						line.FlipVertices();
-						line.FlipSidedefs();
-						line.SetFlag(General.Map.Config.ImpassableFlag, true);
-					} else if(line.Front != null && line.Back == null) {
-						line.SetFlag(General.Map.Config.ImpassableFlag, true);
-					}
-
-					line.ApplySidedFlags();
-
-					drawLine(line.Start.Position, line.End.Position);
-					redrawnLines.Add(line);
-				}
-			}
-
-			// Update cache values
-			General.Map.Map.Update();
-			General.Map.IsChanged = true;
-
 			// Invoke a new mousemove so that the highlighted item updates
 			MouseEventArgs e = new MouseEventArgs(MouseButtons.None, 0, (int)mousepos.x, (int)mousepos.y, 0);
 			OnMouseMove(e);
@@ -905,6 +854,45 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			General.Interface.RedrawDisplay();
 		}
 
+		//mxd
+		private void mergeLines(ICollection<Vertex> selected, Linedef ld1, Linedef ld2, Vertex v) {
+			Vertex v1 = (ld1.Start == v) ? ld1.End : ld1.Start;
+			Vertex v2 = (ld2.Start == v) ? ld2.End : ld2.Start;
+
+			if(ld1.Start == v) ld1.SetStartVertex(v2);
+			else ld1.SetEndVertex(v2);
+			ld2.Dispose();
+			bool redraw = true;
+
+			if(selected.Contains(v2) && v2.Linedefs.Count == 2) {
+				Linedef[] lines = new Linedef[2];
+				v2.Linedefs.CopyTo(lines, 0);
+				Linedef other = lines[0] == ld2 ? lines[1] :lines[0];
+
+				mergeLines(selected, ld1, other, v2);
+				v2.Dispose();
+				redraw = false;
+			}
+
+			if(selected.Contains(v1) && v1.Linedefs.Count == 2) {
+				Linedef[] lines = new Linedef[2];
+				v1.Linedefs.CopyTo(lines, 0);
+				Linedef other = lines[0] == ld1 ? lines[1] : lines[0];
+
+				mergeLines(selected, other, ld1, v1);
+				v1.Dispose();
+				redraw = false;
+			}
+
+			if(redraw) {
+				Vector2D start = ld1.Start.Position;
+				Vector2D end = ld1.End.Position;
+				ld1.Dispose();
+				drawLine(start, end);
+			}
+		}
+
+		//mxd
 		private void drawLine(Vector2D start, Vector2D end) {
 			DrawnVertex dv1 = new DrawnVertex();
 			DrawnVertex dv2 = new DrawnVertex();
diff --git a/Source/Plugins/BuilderModes/General/CopyStructures.cs b/Source/Plugins/BuilderModes/General/CopyStructures.cs
index 0a085d5b..ba47ef8f 100644
--- a/Source/Plugins/BuilderModes/General/CopyStructures.cs
+++ b/Source/Plugins/BuilderModes/General/CopyStructures.cs
@@ -27,14 +27,21 @@ namespace CodeImp.DoomBuilder.BuilderModes
 	public class VertexProperties
 	{
 		private UniFields fields;
+		private float zceiling; //mxd
+		private float zfloor; //mxd
 
 		public VertexProperties(Vertex v)
 		{
 			fields = new UniFields(v.Fields);
+			zceiling = v.ZCeiling; //mxd
+			zfloor = v.ZFloor; //mxd
 		}
 
 		public void Apply(Vertex v)
 		{
+			v.ZCeiling = zceiling; //mxd
+			v.ZFloor = zfloor; //mxd
+			
 			v.Fields.BeforeFieldsChange();
 			v.Fields.Clear();
 			foreach(KeyValuePair<string, UniValue> uv in fields)
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
index 1c244c5b..0fccdd3f 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
@@ -1664,10 +1664,35 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		public void EndEdit()
 		{
 			PreActionNoChange();
+
+			//mxd
+			General.Interface.OnEditFormValuesChanged += new EventHandler(Interface_OnEditFormValuesChanged);
+
 			GetTargetEventReceiver(false).OnEditEnd();
 			PostAction();
 		}
 
+		//mxd
+		private void Interface_OnEditFormValuesChanged(object sender, EventArgs e) {
+			// Reset changed flags
+			foreach(KeyValuePair<Sector, VisualSector> vs in allsectors) {
+				BaseVisualSector bvs = (vs.Value as BaseVisualSector);
+				foreach(VisualFloor vf in bvs.ExtraFloors)
+					vf.Changed = false;
+				foreach(VisualCeiling vc in bvs.ExtraCeilings)
+					vc.Changed = false;
+				foreach(VisualFloor vf in bvs.ExtraBackFloors)
+					vf.Changed = false; 
+				foreach(VisualCeiling vc in bvs.ExtraBackCeilings)
+					vc.Changed = false; 
+				bvs.Floor.Changed = false;
+				bvs.Ceiling.Changed = false;
+			}
+
+			UpdateChangedObjects();
+			ShowTargetInfo();
+		}
+
 		[BeginAction("raisesector8")]
 		public void RaiseSector8()
 		{
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualVertex.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualVertex.cs
index 284a70ec..16bcf963 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualVertex.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualVertex.cs
@@ -3,7 +3,7 @@ using CodeImp.DoomBuilder.VisualModes;
 using CodeImp.DoomBuilder.Map;
 using CodeImp.DoomBuilder.Geometry;
 using System.Windows.Forms;
-using CodeImp.DoomBuilder.Types;
+using CodeImp.DoomBuilder.Windows;
 
 namespace CodeImp.DoomBuilder.BuilderModes
 {
@@ -36,20 +36,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		//this updates the handle itself
 		public override void Update() {
 			if(!changed) return;
-
-			string key = ceilingVertex ? "zceiling" : "zfloor";
-			float z = 0f;
+			float z = ceilingVertex ? vertex.ZCeiling : vertex.ZFloor;
 
 			int height = getSectorHeight();
-			if(vertex.Fields.ContainsKey(key)) {
-				z = vertex.Fields.GetValue(key, 0f);
-
-				if(z == height && neighboursHaveSameHeight(height)) {  //don't create garbage data!
-					vertex.Fields.Remove(key);
-					haveOffset = false;
-				} else {
-					haveOffset = true;
-				}
+			if(!float.IsNaN(z)) {
+				haveOffset = (z != height || !neighboursHaveSameHeight(height));
 			} else {
 				z = height;
 				haveOffset = false;
@@ -90,7 +81,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			}
 		}
 
-		//get the most apropriate height from sectors
+		//get the most appropriate height from sectors
 		private int getSectorHeight() {
 			int height = 0;
 
@@ -275,11 +266,18 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		//Delete key pressed - remove zoffset field
 		public virtual void OnDelete() {
-			string key = ceilingVertex ? "zceiling" : "zfloor";
+			if(ceilingVertex) {
+				if(float.IsNaN(vertex.ZCeiling)) return;
+				vertex.ZCeiling = float.NaN;
+
+				//update affected sectors
+				updateGeometry(vertex);
+				changed = true;
+				mode.ShowTargetInfo();
+			} else {
+				if(float.IsNaN(vertex.ZFloor)) return;
+				vertex.ZFloor = float.NaN;
 
-			if(vertex.Fields.ContainsKey(key)) {
-				vertex.Fields.Remove(key);
-				
 				//update affected sectors
 				updateGeometry(vertex);
 				changed = true;
@@ -291,45 +289,49 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		public virtual void OnEditEnd() {
 			if(General.Interface.IsActiveWindow) {
 				List<Vertex> verts = mode.GetSelectedVertices();
-				DialogResult result = General.Interface.ShowEditVertices(verts, false);
-				
-				if(result == DialogResult.OK) {
-					foreach(Vertex v in verts) {
-						// Update what must be updated
-						updateGeometry(v);
-						if(v.Index == vertex.Index) {
-							changed = true;
-							Update();
-						}
-					}
-				}
+				General.Interface.OnEditFormValuesChanged += new System.EventHandler(Interface_OnEditFormValuesChanged);
+				General.Interface.ShowEditVertices(verts, false);
 			}
 		}
 
+		//mxd
+		private void Interface_OnEditFormValuesChanged(object sender, System.EventArgs e) {
+			VertexEditForm form = sender as VertexEditForm;
+			if(form == null) return;
+
+			// Update what must be updated
+			foreach(Vertex v in form.Selection) 
+				updateGeometry(v);
+
+			changed = true;
+			Update();
+		}
+
 		// Raise/lower thing
 		public virtual void OnChangeTargetHeight(int amount) {
 			if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
 				undoticket = mode.CreateUndo("Change vertex height");
 
-			//change vertex zoffset
-			vertex.Fields.BeforeFieldsChange();
-
-			string key = ceilingVertex ? "zceiling" : "zfloor";
-			float val = 0f;
-
-			//get value
-			if(!vertex.Fields.ContainsKey(key)) {
-				val = getSectorHeight();
-				vertex.Fields.Add(key, new UniValue(UniversalType.Float, val));
+			if(ceilingVertex) {
+				vertex.ZCeiling = (float.IsNaN(vertex.ZCeiling) ? getSectorHeight() + amount : vertex.ZCeiling + amount);
+				
+				if(vertex.ZCeiling == getSectorHeight()) {
+					vertex.ZCeiling = float.NaN;
+					mode.SetActionResult("Cleared vertex height.");
+				} else {
+					mode.SetActionResult("Changed vertex height to " + vertex.ZCeiling + ".");
+				}
 			} else {
-				val = (float)vertex.Fields[key].Value;
+				vertex.ZFloor = (float.IsNaN(vertex.ZFloor) ? getSectorHeight() + amount : vertex.ZFloor + amount);
+				
+				if(vertex.ZFloor == getSectorHeight()) {
+					vertex.ZFloor = float.NaN;
+					mode.SetActionResult("Cleared vertex height.");
+				} else {
+					mode.SetActionResult("Changed vertex height to " + vertex.ZFloor + ".");
+				}
 			}
 
-			//update value
-			vertex.Fields[key].Value = val + amount;
-
-			mode.SetActionResult("Changed vertex height to " + (int)(val + amount) + ".");
-
 			// Update what must be updated
 			updateGeometry(vertex);
 			changed = true;
diff --git a/Source/Plugins/BuilderModes/VisualModes/EffectUDMFVertexOffset.cs b/Source/Plugins/BuilderModes/VisualModes/EffectUDMFVertexOffset.cs
index 9b248e6a..207c8921 100644
--- a/Source/Plugins/BuilderModes/VisualModes/EffectUDMFVertexOffset.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/EffectUDMFVertexOffset.cs
@@ -30,18 +30,18 @@ namespace CodeImp.DoomBuilder.BuilderModes
                 ceilingVerts[index] = new Vector3D(v.Position);
 
                 //check ceiling
-				if(v.Fields.ContainsKey("zceiling")) {
+				if(!float.IsNaN(v.ZCeiling)) {
 					//vertex offset is absolute
-					ceilingVerts[index].z = (float)v.Fields["zceiling"].Value;
+					ceilingVerts[index].z = v.ZCeiling;
 					ceilingChanged = true;
 				} else {
 					ceilingVerts[index].z = data.Ceiling.plane.GetZ(v.Position);
 				}
 
                 //and floor
-				if(v.Fields.ContainsKey("zfloor")) {
+				if(!float.IsNaN(v.ZFloor)) {
 					//vertex offset is absolute
-					floorVerts[index].z = (float)v.Fields["zfloor"].Value;
+					floorVerts[index].z = v.ZFloor;
 					floorChanged = true;
 				} else {
 					floorVerts[index].z = data.Floor.plane.GetZ(v.Position);
diff --git a/Source/Plugins/CopyPasteSectorProps/BuilderPlug.cs b/Source/Plugins/CopyPasteSectorProps/BuilderPlug.cs
index d421a86e..024dc811 100644
--- a/Source/Plugins/CopyPasteSectorProps/BuilderPlug.cs
+++ b/Source/Plugins/CopyPasteSectorProps/BuilderPlug.cs
@@ -16,27 +16,12 @@
 
 #region ================== Namespaces
 
-using System;
 using System.Collections;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Globalization;
-using System.Text;
-using System.Windows.Forms;
-using System.IO;
-using System.Reflection;
 using CodeImp.DoomBuilder.Windows;
-using CodeImp.DoomBuilder.IO;
 using CodeImp.DoomBuilder.Map;
-using CodeImp.DoomBuilder.Rendering;
-using CodeImp.DoomBuilder.Geometry;
-using System.Drawing;
-using CodeImp.DoomBuilder.Editing;
 using CodeImp.DoomBuilder.Plugins;
 using CodeImp.DoomBuilder.Actions;
-using CodeImp.DoomBuilder.Types;
-using CodeImp.DoomBuilder.Config;
-using CodeImp.DoomBuilder.Data;
 
 #endregion
 
@@ -69,7 +54,7 @@ namespace CodeImp.DoomBuilder.CopyPasteSectorProps
         private int brightness;
         private int effect;
         private int tag;
-		private UniFields fields;
+		//private UniFields fields;
 		
         // This is set to true to know that we copied sector properties.
 		// If this is false, the variables above are uninitialized.
diff --git a/Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProperties.csproj b/Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProperties.csproj
index 0e25a357..a4e27908 100644
--- a/Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProperties.csproj
+++ b/Source/Plugins/CopyPasteSectorProps/CopyPasteSectorProperties.csproj
@@ -34,6 +34,7 @@
     <Reference Include="Builder, Version=2.0.0.0, Culture=neutral, processorArchitecture=x86">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\..\..\Build\Builder.exe</HintPath>
+      <Private>False</Private>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core">
diff --git a/Source/Plugins/StairSectorBuilder/BuilderPlug.cs b/Source/Plugins/StairSectorBuilder/BuilderPlug.cs
new file mode 100644
index 00000000..b3448be5
--- /dev/null
+++ b/Source/Plugins/StairSectorBuilder/BuilderPlug.cs
@@ -0,0 +1,184 @@
+
+#region ================== Copyright (c) 2009 Boris Iwanski
+
+/*
+ * Copyright (c) 2009 Boris Iwanski
+ * This program is released under GNU General Public License
+ * 
+ * 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.
+ * 
+ */
+
+#endregion
+
+#region ================== Namespaces
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Globalization;
+using System.Text;
+using System.Windows.Forms;
+using System.IO;
+using System.Reflection;
+using CodeImp.DoomBuilder.Windows;
+using CodeImp.DoomBuilder.IO;
+using CodeImp.DoomBuilder.Map;
+using CodeImp.DoomBuilder.Rendering;
+using CodeImp.DoomBuilder.Geometry;
+using System.Drawing;
+using CodeImp.DoomBuilder.Editing;
+using CodeImp.DoomBuilder.Plugins;
+using CodeImp.DoomBuilder.Actions;
+using CodeImp.DoomBuilder.Types;
+using CodeImp.DoomBuilder.Config;
+using CodeImp.DoomBuilder.Data;
+
+#endregion
+
+namespace CodeImp.DoomBuilder.StairSectorBuilderMode
+{
+	//
+	// MANDATORY: The plug!
+	// This is an important class to the Doom Builder core. Every plugin must
+	// have exactly 1 class that inherits from Plug. When the plugin is loaded,
+	// this class is instantiated and used to receive events from the core.
+	// Make sure the class is public, because only public classes can be seen
+	// by the core.
+	//
+
+	public class BuilderPlug : Plug
+	{
+        public struct Prefab
+        {
+            public string name;
+
+            // General
+            public int numberofsectors;
+            public int innervertexmultiplier;
+            public int outervertexmultiplier;
+            public int stairtype;
+
+            // Straight stairs
+            public int sectordepth;
+            public int spacing;
+            public bool frontside;
+            public bool singlesectors;
+            public bool singledirection;
+			public bool distinctbaseheights;
+
+            // Auto curve
+            public int flipping;
+
+            // Catmull Rom spline
+            public int numberofcontrolpoints;
+
+            // Height info
+            public bool applyfloormod;
+            public int floormod;
+			public int floorbase;
+            public bool applyceilingmod;
+            public int ceilingmod;
+			public int ceilingbase;
+
+            // Textures
+            public bool applyfloortexture;
+            public string floortexture;
+            public bool applyceilingtexture;
+            public string ceilingtexture;
+
+            public bool applyuppertexture;
+            public string uppertexture;
+			public bool upperunpegged;
+			public bool applymiddletexture;
+			public string middletexture;
+            public bool applylowertexture;
+            public string lowertexture;
+			public bool lowerunpegged;
+        }
+
+		// Static instance. We can't use a real static class, because BuilderPlug must
+		// be instantiated by the core, so we keep a static reference. (this technique
+		// should be familiar to object-oriented programmers)
+		private static BuilderPlug me;
+
+		// Static property to access the BuilderPlug
+		public static BuilderPlug Me { get { return me; } }
+
+        private List<Prefab> prefabs;
+
+        public List<Prefab> Prefabs { get { return prefabs; } }
+
+		// This plugin relies on some functionality that wasn't there in older versions
+		public override int MinimumRevision { get { return 1310; } }
+
+		// This event is called when the plugin is initialized
+		public override void OnInitialize()
+		{
+			base.OnInitialize();
+
+			// This binds the methods in this class that have the BeginAction
+			// and EndAction attributes with their actions. Without this, the
+			// attributes are useless. Note that in classes derived from EditMode
+			// this is not needed, because they are bound automatically when the
+			// editing mode is engaged.
+            General.Actions.BindMethods(this);
+
+            prefabs = new List<Prefab>();
+
+			// TODO: Add DB2 version check so that old DB2 versions won't crash
+			// General.ErrorLogger.Add(ErrorType.Error, "zomg!");
+
+			// Keep a static reference
+            me = this;
+		}
+
+		// This is called when the plugin is terminated
+		public override void Dispose()
+		{
+			base.Dispose();
+
+			// This must be called to remove bound methods for actions.
+            General.Actions.UnbindMethods(this);
+        }
+
+        #region ================== Actions
+
+		[BeginAction("selectsectorsoutline")]
+		public void SelectSectorsOutline()
+		{
+			// Must have sectors selected. Having sectors selected implies
+			// that there are also linedefs selected
+			if (General.Map.Map.SelectedSectorsCount == 0) return;
+
+			// Get the list of selected sectors since it'll be empty after
+			// switching to linedefs mode
+			List<Sector> sectors = new List<Sector>(General.Map.Map.GetSelectedSectors(true));
+
+			General.Editing.ChangeMode("LinedefsMode");
+
+			// Go through all selected linedefs and unselect/unmark all which
+			// have a selected sector on both sides
+			foreach (Linedef ld in General.Map.Map.GetSelectedLinedefs(true))
+			{
+				if (sectors.Contains(ld.Front.Sector) && ld.Back != null && sectors.Contains(ld.Back.Sector))
+				{
+					ld.Selected = false;
+					ld.Marked = false;
+				}
+			}
+
+			General.Interface.RedrawDisplay();
+		}
+
+        #endregion
+
+		#region ================== Methods
+
+		#endregion
+	}
+}
diff --git a/Source/Plugins/StairSectorBuilder/Properties/AssemblyInfo.cs b/Source/Plugins/StairSectorBuilder/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..d110b4e1
--- /dev/null
+++ b/Source/Plugins/StairSectorBuilder/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Управление общими сведениями о сборке осуществляется с помощью 
+// набора атрибутов. Измените значения этих атрибутов, чтобы изменить сведения,
+// связанные со сборкой.
+[assembly: AssemblyTitle("StairSectorBuilder")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("StairSectorBuilder")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Параметр ComVisible со значением FALSE делает типы в сборке невидимыми 
+// для COM-компонентов.  Если требуется обратиться к типу в этой сборке через 
+// COM, задайте атрибуту ComVisible значение TRUE для этого типа.
+[assembly: ComVisible(false)]
+
+// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM
+[assembly: Guid("a3340ba1-665f-4414-8cbb-a0271aa32137")]
+
+// Сведения о версии сборки состоят из следующих четырех значений:
+//
+//      Основной номер версии
+//      Дополнительный номер версии 
+//      Номер построения
+//      Редакция
+//
+// Можно задать все значения или принять номер построения и номер редакции по умолчанию, 
+// используя "*", как показано ниже:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Source/Plugins/StairSectorBuilder/Properties/Resources.Designer.cs b/Source/Plugins/StairSectorBuilder/Properties/Resources.Designer.cs
new file mode 100644
index 00000000..5f476774
--- /dev/null
+++ b/Source/Plugins/StairSectorBuilder/Properties/Resources.Designer.cs
@@ -0,0 +1,77 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     Этот код создан программой.
+//     Исполняемая версия:2.0.50727.4927
+//
+//     Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае
+//     повторной генерации кода.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace CodeImp.DoomBuilder.StairSectorBuilder.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   Класс ресурса со строгой типизацией для поиска локализованных строк и т.д.
+    /// </summary>
+    // Этот класс создан автоматически классом StronglyTypedResourceBuilder
+    // с помощью такого средства, как ResGen или Visual Studio.
+    // Чтобы добавить или удалить член, измените файл .ResX и снова запустите ResGen
+    // с параметром /str или перестройте свой проект VS.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   Возвращает кэшированный экземпляр ResourceManager, использованный этим классом.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CodeImp.DoomBuilder.StairSectorBuilder.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Перезаписывает свойство CurrentUICulture текущего потока для всех
+        ///   обращений к ресурсу с помощью этого класса ресурса со строгой типизацией.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        internal static byte[] Actions {
+            get {
+                object obj = ResourceManager.GetObject("Actions", resourceCulture);
+                return ((byte[])(obj));
+            }
+        }
+        
+        internal static System.Drawing.Bitmap StairIcon {
+            get {
+                object obj = ResourceManager.GetObject("StairIcon", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+    }
+}
diff --git a/Source/Plugins/StairSectorBuilder/Properties/Resources.resx b/Source/Plugins/StairSectorBuilder/Properties/Resources.resx
new file mode 100644
index 00000000..409f8525
--- /dev/null
+++ b/Source/Plugins/StairSectorBuilder/Properties/Resources.resx
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="Actions" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\Actions.cfg;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="StairIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\StairIcon.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+</root>
\ No newline at end of file
diff --git a/Source/Plugins/StairSectorBuilder/Resources/Actions.cfg b/Source/Plugins/StairSectorBuilder/Resources/Actions.cfg
new file mode 100644
index 00000000..cf26f6e1
--- /dev/null
+++ b/Source/Plugins/StairSectorBuilder/Resources/Actions.cfg
@@ -0,0 +1,42 @@
+
+//
+// This file defines which actions there are, what description they have and
+// some behaviour options. The Doom Builder core will bind to these actions
+// with delegates (function pointers) where you use the BeginAction and
+// EndAction attributes. This file must be named Actions.cfg and must be
+// included in the plugin project as "Embedded Resource".
+//
+
+//
+// Options:
+//
+// allowkeys:       Allows the user to bind standard keys to this action.
+// allowmouse:      Allows the user to bind mouse buttons to this action.
+// allowscroll:     Allows the user to bind the scrollwheel to this action.
+// disregardshift:  This action will trigger regardless if Shift or Control is used.
+// repeat:          BeginAction will be called for automatic key repetition.
+// default:         Default key is only used when the action is loaded for the first
+//                  time and the default key is not used by any other action.
+//
+// allowkeys and allowmouse are true by default, the others are false by default.
+//
+
+stairsectorbuildermode
+{
+	title = "Stair Sector Builder Mode";
+	category = "modes";
+	description = "Switches to the stair sector builder mode.";
+	allowkeys = true;
+	allowmouse = true;
+	allowscroll = true;
+}
+
+selectsectorsoutline
+{
+	title = "Select Sectors Outline";
+	category = "sectors";
+	description = "Selects the outline of all marked sectors, deselecting lines that are shared.";
+	allowkeys = true;
+	allowmouse = true;
+	allowscroll = true;
+}
\ No newline at end of file
diff --git a/Source/Plugins/StairSectorBuilder/Resources/StairIcon.png b/Source/Plugins/StairSectorBuilder/Resources/StairIcon.png
new file mode 100644
index 00000000..4db91e94
Binary files /dev/null and b/Source/Plugins/StairSectorBuilder/Resources/StairIcon.png differ
diff --git a/Source/Plugins/StairSectorBuilder/StairSectorBuilder.csproj b/Source/Plugins/StairSectorBuilder/StairSectorBuilder.csproj
new file mode 100644
index 00000000..7081f258
--- /dev/null
+++ b/Source/Plugins/StairSectorBuilder/StairSectorBuilder.csproj
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{3F365121-906B-409D-BB1E-37E0A78056C2}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>CodeImp.DoomBuilder.StairSectorBuilder</RootNamespace>
+    <AssemblyName>StairSectorBuilder</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\..\..\Build\Plugins\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <PlatformTarget>x86</PlatformTarget>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\..\..\Build\Plugins\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <PlatformTarget>x86</PlatformTarget>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Windows.Forms" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="BuilderPlug.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <Compile Include="StairSectorBuilderForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="StairSectorBuilderForm.designer.cs">
+      <DependentUpon>StairSectorBuilderForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="StairSectorBuilderMode.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+    <EmbeddedResource Include="StairSectorBuilderForm.resx">
+      <DependentUpon>StairSectorBuilderForm.cs</DependentUpon>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Resources\Actions.cfg" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Resources\StairIcon.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\Builder.csproj">
+      <Project>{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}</Project>
+      <Name>Builder</Name>
+      <Private>False</Private>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="ClassDiagram1.cd" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/Source/Plugins/StairSectorBuilder/StairSectorBuilder.csproj.user b/Source/Plugins/StairSectorBuilder/StairSectorBuilder.csproj.user
new file mode 100644
index 00000000..7ff3943f
--- /dev/null
+++ b/Source/Plugins/StairSectorBuilder/StairSectorBuilder.csproj.user
@@ -0,0 +1 @@
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
\ No newline at end of file
diff --git a/Source/Plugins/StairSectorBuilder/StairSectorBuilderForm.cs b/Source/Plugins/StairSectorBuilder/StairSectorBuilderForm.cs
new file mode 100644
index 00000000..6f2bca33
--- /dev/null
+++ b/Source/Plugins/StairSectorBuilder/StairSectorBuilderForm.cs
@@ -0,0 +1,892 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using CodeImp.DoomBuilder.Controls;
+using CodeImp.DoomBuilder.Editing;
+using CodeImp.DoomBuilder.Map;
+using CodeImp.DoomBuilder.Data;
+using CodeImp.DoomBuilder.IO;
+using CodeImp.DoomBuilder.Windows;
+
+
+namespace CodeImp.DoomBuilder.StairSectorBuilderMode
+{
+	public partial class StairSectorBuilderForm : DelayedForm
+	{
+		private bool fullyloaded = false;
+		private bool loadingprefab = false;
+		private int stepmultiplier = 1;
+		private int originalfloorbase;
+		private int originalceilingbase;
+
+
+		#region ================== Properties
+
+		public int OriginalFloorBase
+		{
+			set { originalfloorbase = value; }
+			get { return originalfloorbase; }
+		}
+
+		public int OriginalCeilingBase
+		{
+			set { originalceilingbase = value; }
+			get { return originalceilingbase; }
+		}
+
+		public int StepMultiplier
+		{
+			set { stepmultiplier = value; }
+		}
+
+		public int StairType
+		{
+			get { return tabcontrol.SelectedIndex; }
+		}
+
+		public uint NumberOfSectors
+		{
+			// get { return (uint)System.Convert.ToUInt32(numberofsectors.Text); }
+			get { return (uint)numberofsectors.GetResult(1); }
+			set { numberofsectors.Text = value.ToString(); }
+		}
+
+		public uint SectorDepth
+		{
+			// get { return (uint)System.Convert.ToUInt32(sectordepth.Text); }
+			get { return (uint)sectordepth.GetResult(32); }
+			set { sectordepth.Text = value.ToString(); }
+		}
+
+        public int Spacing
+        {
+            get { return spacing.GetResult(0); }
+            set { spacing.Text = value.ToString(); }
+        }
+
+		public int InnerVertexMultiplier
+		{
+			get { return autocurveinnervertexmultiplier.GetResult(1); }
+			set { autocurveinnervertexmultiplier.Text = value.ToString(); }
+		}
+
+		public int OuterVertexMultiplier
+		{
+			get { return autocurveoutervertexmultiplier.GetResult(1); }
+			set { autocurveoutervertexmultiplier.Text = value.ToString(); }
+		}
+
+		public bool SideFront
+		{
+			get { return sidefront.Checked; }
+            set { sidefront.Checked = value; }
+		}
+
+		public CheckBox DistinctBaseHeights
+		{
+			get { return distinctbaseheights; }
+			set { distinctbaseheights = value; }
+		}
+
+		public CheckBox SingleSectors
+		{
+			get { return singlesectors; }
+			set { singlesectors = value; }
+		}
+
+		public CheckBox SingleDirection
+		{
+			get { return singledirection; }
+			set { singledirection = value; }
+		}
+
+		public TabControl Tabs
+		{
+			get { return tabcontrol; }
+		}
+
+		public int NumControlPoints
+		{
+			get { return numberofcontrolpoints.GetResult(1) + 2; }
+            set { numberofcontrolpoints.Text = value.ToString(); }
+		}
+
+        public bool FloorHeight
+        {
+            get { return floorheightmodification.Checked; }
+            set { floorheightmodification.Checked = value; }
+        }
+
+		public bool CeilingHeight
+		{
+			get { return ceilingheightmodification.Checked; }
+            set { ceilingheightmodification.Checked = value; }
+		}
+
+
+        public int FloorHeightModification
+        {
+            get { return floorheightmod.GetResult(0); }
+            set { floorheightmod.Text = value.ToString(); }
+        }
+
+        public int CeilingHeightModification
+        {
+            get { return ceilingheightmod.GetResult(0); }
+            set { ceilingheightmod.Text = value.ToString(); }
+        }
+
+        public bool FloorFlat
+        {
+            get { return floorflat.Checked; }
+            set { floorflat.Checked = value; }
+        }
+
+        public string FloorFlatTexture
+        {
+            get { return floorflattexture.TextureName; }
+            set { floorflattexture.TextureName = value; }
+        }
+
+        public bool CeilingFlat
+        {
+            get { return ceilingflat.Checked; }
+            set { ceilingflat.Checked = value; }
+        }
+
+        public string CeilingFlatTexture
+        {
+            get { return ceilingflattexture.TextureName; }
+            set { ceilingflattexture.TextureName = value; }
+        }
+
+        public bool UpperTexture
+        {
+            get { return uppertexture.Checked; }
+            set { uppertexture.Checked = value; }
+        }
+
+		public bool MiddleTexture
+		{
+			get { return middletexture.Checked; }
+			set { middletexture.Checked = value; }
+		}
+
+        public bool LowerTexture
+        {
+            get { return lowertexture.Checked; }
+            set { lowertexture.Checked = value; }
+        }
+
+        public string UpperTextureTexture
+        {
+            get { return uppertexturetexture.TextureName; }
+            set { uppertexturetexture.TextureName = value; }
+        }
+
+		public string MiddleTextureTexture
+		{
+			get { return middletexturetexture.TextureName; }
+			set { middletexturetexture.TextureName = value; }
+		}
+
+        public string LowerTextureTexture
+        {
+            get { return lowertexturetexture.TextureName; }
+            set { lowertexturetexture.TextureName = value; }
+        }
+
+		public int FloorBase
+		{
+			set { floorbase.Text = value.ToString(); }
+			get { return floorbase.GetResult(0); }
+		}
+
+		public int CeilingBase
+		{
+			set { ceilingbase.Text = value.ToString(); }
+			get { return ceilingbase.GetResult(0); }
+		}
+
+		public bool FullyLoaded
+		{
+			get { return fullyloaded; }
+		}
+
+		public int Flipping
+		{
+			get { return autocurveflipping.SelectedIndex; }
+			set { autocurveflipping.SelectedIndex = value; }
+		}
+
+		public bool UpperUnpegged
+		{
+			get { return upperunpegged.Checked; }
+			set { upperunpegged.Checked = value; }
+		}
+
+		public bool LowerUnpegged
+		{
+			get { return lowerunpegged.Checked; }
+			set { lowerunpegged.Checked = value; }
+		}
+
+		#endregion
+
+		public StairSectorBuilderForm()
+		{
+			InitializeComponent();
+
+            foreach (BuilderPlug.Prefab p in BuilderPlug.Me.Prefabs)
+            {
+                ListViewItem lvi = new ListViewItem();
+                ListViewItem.ListViewSubItem lvisi = new ListViewItem.ListViewSubItem();
+
+                lvi.Text = p.name;
+                lvisi.Text = tabcontrol.TabPages[p.stairtype].Text;
+
+                lvi.SubItems.Add(lvisi);
+                prefabs.Items.Add(lvi);
+            }
+		}
+
+        // This shows the window
+        public void Show(Form owner)
+        {
+            // Position at left-top of owner
+            this.Location = new Point(owner.Location.X + 20, owner.Location.Y + 90);
+
+			// Set the default name for the prefab
+			for (int i = 1; i < int.MaxValue; i++)
+			{
+				string defname = "Prefab #" + i.ToString();
+				bool validname = true;
+
+				foreach (BuilderPlug.Prefab p in BuilderPlug.Me.Prefabs)
+				{
+					if (p.name == defname)
+					{
+						validname = false;
+						break;
+					}
+				}
+
+				if (validname)
+				{
+					prefabname.Text = defname;
+					break;
+				}
+			}
+
+            // Show window
+            base.Show(owner);
+        }
+
+		private void ComputeHeights()
+		{
+			if (!fullyloaded) return;
+
+			if (floorbase.Enabled == false)
+			{
+				floorfirst.Text = "--";
+				floorlast.Text = "--";
+			}
+			else
+			{
+				floorfirst.Text = (Int32.Parse(floorbase.Text) + Int32.Parse(floorheightmod.Text)).ToString();
+				floorlast.Text = (Int32.Parse(floorbase.Text) + Int32.Parse(floorheightmod.Text) * (Int32.Parse(numberofsectors.Text) * stepmultiplier)).ToString();
+			}
+
+			if (ceilingbase.Enabled == false)
+			{
+				ceilingfirst.Text = "--";
+				ceilinglast.Text = "--";
+			}
+			else
+			{
+				ceilingfirst.Text = (Int32.Parse(ceilingbase.Text) + Int32.Parse(ceilingheightmod.Text)).ToString();
+				ceilinglast.Text = (Int32.Parse(ceilingbase.Text) + Int32.Parse(ceilingheightmod.Text) * (Int32.Parse(numberofsectors.Text) * stepmultiplier)).ToString();
+			}
+		}
+
+		// Wrap redrawing display so that it will not get called multiple
+		// times while loading a prefab
+		private void DoRedrawDisplay()
+		{
+			if (loadingprefab == false) General.Interface.RedrawDisplay();
+		}
+
+		private void btnOK_Click(object sender, EventArgs e)
+		{
+			SavePrefab("[Previous]", true, 0);
+
+			General.Editing.AcceptMode();
+		}
+
+		private void btnCancel_Click(object sender, EventArgs e)
+		{
+			General.Editing.CancelMode();
+		}
+
+		private void tbSectorDepth_TextChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void rdbFront_CheckedChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void rdbBack_CheckedChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void tabcontrol_SelectedIndexChanged(object sender, EventArgs e)
+		{
+			if (General.Map.Map.GetSelectedLinedefs(true).Count == 1)
+			{
+				tabcontrol.SelectedTab = tabPage1;
+			}
+
+			if (tabcontrol.SelectedTab != tabPage1)
+			{
+				floorbase.Enabled = true;
+				ceilingbase.Enabled = true;
+			}
+			else
+			{
+				if (distinctbaseheights.Checked == true)
+				{
+					floorbase.Enabled = false;
+					ceilingbase.Enabled = false;
+				}
+				else
+				{
+					floorbase.Enabled = true;
+					ceilingbase.Enabled = true;
+				}
+			}
+
+			DoRedrawDisplay();
+			ComputeHeights();
+		}
+
+		private void tbAutoNumSectors_TextChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void numberofsectors_WhenTextChanged(object sender, EventArgs e)
+		{
+			if (numberofsectors.Text == "" || numberofsectors.GetResult(1) == 0) numberofsectors.Text = "1";
+			ComputeHeights();
+			DoRedrawDisplay();
+		}
+
+		private void sectordepth_WhenTextChanged(object sender, EventArgs e)
+		{
+			if (sectordepth.GetResult(32) == 0) sectordepth.Text = "1";
+			DoRedrawDisplay();
+		}
+
+		private void StairSectorBuilderForm_Load(object sender, EventArgs e)
+		{
+			sectordepth.Text = "32";
+            spacing.Text = "0";
+			numberofsectors.Text = "1";
+			autocurveinnervertexmultiplier.Text = "1";
+			autocurveoutervertexmultiplier.Text = "1";
+			splineinnervertexmultiplier.Text = "1";
+			splineoutervertexmultiplier.Text = "1";
+			numberofcontrolpoints.Text = "1";
+            floorheightmod.Text = "0";
+            ceilingheightmod.Text = "0";
+			floorbase.Text = "0";
+			ceilingbase.Text = "0";
+			autocurveflipping.SelectedIndex = 0;
+			MiddleTextureTexture = "-";
+
+			fullyloaded = true;
+
+			ComputeHeights();
+		}
+
+		private void autocurveinnervertexmultiplier_WhenTextChanged(object sender, EventArgs e)
+		{
+			if (autocurveinnervertexmultiplier.GetResult(1) == 0) autocurveinnervertexmultiplier.Text = "1";
+
+			if(splineinnervertexmultiplier.Text != autocurveinnervertexmultiplier.Text)
+				splineinnervertexmultiplier.Text = autocurveinnervertexmultiplier.Text;
+
+			DoRedrawDisplay();
+		}
+
+		private void autocurveoutervertexmultiplier_WhenTextChanged(object sender, EventArgs e)
+		{
+			if (autocurveoutervertexmultiplier.GetResult(1) == 0) autocurveoutervertexmultiplier.Text = "1";
+
+			if(splineoutervertexmultiplier.Text != autocurveoutervertexmultiplier.Text)
+				splineoutervertexmultiplier.Text = autocurveoutervertexmultiplier.Text;
+
+			DoRedrawDisplay();
+
+		}
+
+		private void splineinnervertexmultiplier_WhenTextChanged(object sender, EventArgs e)
+		{
+			if (splineinnervertexmultiplier.GetResult(1) == 0) splineinnervertexmultiplier.Text = "1";
+
+			if(autocurveinnervertexmultiplier.Text != splineinnervertexmultiplier.Text)
+				autocurveinnervertexmultiplier.Text = splineinnervertexmultiplier.Text;
+
+			DoRedrawDisplay();
+		}
+
+		private void splineoutervertexmultiplier_WhenTextChanged(object sender, EventArgs e)
+		{
+			if (splineoutervertexmultiplier.GetResult(1) == 0) splineoutervertexmultiplier.Text = "1";
+
+			if(splineoutervertexmultiplier.Text != autocurveoutervertexmultiplier.Text)
+				autocurveoutervertexmultiplier.Text = splineoutervertexmultiplier.Text;
+
+			DoRedrawDisplay();
+		}
+
+		private void onecontrolpoint_CheckedChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void StairSectorBuilderForm_Shown(object sender, EventArgs e)
+		{
+			if (General.Map.Map.GetSelectedLinedefs(true).Count == 1 || General.Map.Map.SelectedSectorsCount > 0)
+			{
+				tabcontrol.TabPages.Remove(tabPage2);
+				tabcontrol.TabPages.Remove(tabPage3);
+			}
+
+			if (General.Map.Map.SelectedSectorsCount > 0)
+			{
+				singledirection.Checked = false;
+				singledirection.Enabled = false;
+
+				singlesectors.Checked = true;
+				singlesectors.Enabled = false;
+			}
+		}
+
+		private void singlesectors_CheckedChanged(object sender, EventArgs e)
+		{
+			if (singlesectors.Checked)
+				singledirection.Enabled = true;
+			else
+				singledirection.Enabled = false;
+
+			DoRedrawDisplay();
+		}
+
+		private void numberofcontrolpoints_WhenTextChanged(object sender, EventArgs e)
+		{
+			if (numberofcontrolpoints.GetResult(1) == 0) numberofcontrolpoints.Text = "1";
+			DoRedrawDisplay();
+		}
+
+		private void sidefront_CheckedChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void sideback_CheckedChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void singledirection_CheckedChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+        private void floorheightmod_WhenTextChanged(object sender, EventArgs e)
+        {
+            if (floorheightmod.GetResult(0) == 0) floorheightmod.Text = "0";
+			ComputeHeights();
+        }
+
+        private void ceilingheightmod_WhenTextChanged(object sender, EventArgs e)
+        {
+            if (ceilingheightmod.GetResult(0) == 0) ceilingheightmod.Text = "0";
+			ComputeHeights();
+        }
+
+        private void floorflat_CheckedChanged(object sender, EventArgs e)
+        {
+            if (floorflat.Checked)
+                floorflattexture.Enabled = true;
+            else
+                floorflattexture.Enabled = false;
+        }
+
+        private void ceilingflat_CheckedChanged(object sender, EventArgs e)
+        {
+            if (ceilingflat.Checked)
+                ceilingflattexture.Enabled = true;
+            else
+                ceilingflattexture.Enabled = false;
+        }
+
+        private void spacing_WhenTextChanged(object sender, EventArgs e)
+        {
+            if (spacing.GetResult(0) == 0) spacing.Text = "0";
+            DoRedrawDisplay();
+        }
+
+        private void uppertexture_CheckedChanged(object sender, EventArgs e)
+        {
+			if (uppertexture.Checked)
+			{
+				uppertexturetexture.Enabled = true;
+				upperunpegged.Enabled = true;
+			}
+			else
+			{
+				uppertexturetexture.Enabled = false;
+				upperunpegged.Enabled = false;
+			}
+        }
+
+		private void middletexture_CheckedChanged(object sender, EventArgs e)
+		{
+			if (middletexture.Checked)
+				middletexturetexture.Enabled = true;
+			else
+				middletexturetexture.Enabled = false;
+		}
+
+        private void lowertexture_CheckedChanged(object sender, EventArgs e)
+        {
+			if (lowertexture.Checked)
+			{
+				lowertexturetexture.Enabled = true;
+				lowerunpegged.Enabled = true;
+			}
+			else
+			{
+				lowertexturetexture.Enabled = false;
+				lowerunpegged.Enabled = false;
+			}
+        }
+
+		private void firstisback_CheckedChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void StairSectorBuilderForm_FormClosing(object sender, FormClosingEventArgs e)
+		{
+			// User closing the window?
+			if (e.CloseReason == CloseReason.UserClosing)
+			{
+				// Just cancel
+				General.Editing.CancelMode();
+				e.Cancel = true;
+			}
+		}
+
+		private void lastisback_CheckedChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void acflipping1_CheckedChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void acflipping2_CheckedChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void acflipping3_CheckedChanged(object sender, EventArgs e)
+		{
+			DoRedrawDisplay();
+		}
+
+		private void floorbase_WhenTextChanged(object sender, EventArgs e)
+		{
+			if (floorbase.GetResult(0) == 0) floorbase.Text = "0";
+			ComputeHeights();
+		}
+
+		private void ceilingbase_WhenTextChanged(object sender, EventArgs e)
+		{
+			if (ceilingbase.GetResult(0) == 0) ceilingbase.Text = "0";
+			ComputeHeights();
+		}
+
+        private void prefabsave_Click(object sender, EventArgs e)
+        {
+			string name = prefabname.Text.Trim();
+
+			if (name == "[Previous]")
+				MessageBox.Show(Owner, "The prefab name \"[Previous]\" is reserved and can not be overwritten.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+			else
+				SavePrefab(prefabname.Text.Trim(), false, -1);
+		}
+
+		private void SavePrefab(string name, bool forceoverwrite, int position)
+		{
+            int overwrite = -1;
+
+            // Prefab name may not be empty
+            if (name == "")
+            {
+                MessageBox.Show(this.Owner, "Please enter a name for the prefab", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+                return;
+            }
+
+            // Check if there's already a prefab with the given name
+			for (int i = 0; i < BuilderPlug.Me.Prefabs.Count; i++)
+			{
+				BuilderPlug.Prefab p = BuilderPlug.Me.Prefabs[i];
+
+				if (p.name == name)
+				{
+					if (forceoverwrite == false && MessageBox.Show(this.Owner, "A prefab with that name already exists. Overwrite?", "", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
+						return;
+					else
+						overwrite = i;
+				}
+			}
+
+            ListViewItem lvi = new ListViewItem();
+            ListViewItem.ListViewSubItem lvisi = new ListViewItem.ListViewSubItem();
+
+            lvi.Text = name;
+            lvisi.Text = tabcontrol.TabPages[tabcontrol.SelectedIndex].Text;
+
+            lvi.SubItems.Add(lvisi);
+
+            BuilderPlug.Prefab pf = new BuilderPlug.Prefab();
+
+            pf.name = name;
+
+            pf.numberofsectors = (int)NumberOfSectors;
+            pf.outervertexmultiplier = OuterVertexMultiplier;
+            pf.innervertexmultiplier = InnerVertexMultiplier;
+
+            pf.stairtype = tabcontrol.SelectedIndex;
+
+            // Straight stairs
+            pf.sectordepth = (int)SectorDepth;
+            pf.spacing = (int)Spacing;
+            pf.frontside = SideFront;
+            pf.singlesectors = SingleSectors.Checked;
+            pf.singledirection = SingleDirection.Checked;
+			pf.distinctbaseheights = DistinctBaseHeights.Checked;
+
+            // Auto curve
+			pf.flipping = Flipping;
+
+            // Catmull Rom spline
+            pf.numberofcontrolpoints = NumControlPoints - 2;
+
+            // Height info
+            pf.applyfloormod = FloorHeight;
+            pf.floormod = FloorHeightModification;
+			//pf.floorbase = FloorBase;
+            pf.applyceilingmod = CeilingHeight;
+            pf.ceilingmod = CeilingHeightModification;
+			//pf.ceilingbase = CeilingBase;
+
+            // Textures
+            pf.applyfloortexture = FloorFlat;
+            pf.floortexture = FloorFlatTexture;
+
+            pf.applyceilingtexture = CeilingFlat;
+            pf.ceilingtexture = CeilingFlatTexture;
+
+            pf.applyuppertexture = UpperTexture;
+            pf.uppertexture = UpperTextureTexture;
+			pf.upperunpegged = UpperUnpegged;
+
+			pf.applymiddletexture = MiddleTexture;
+			pf.middletexture = MiddleTextureTexture;
+
+            pf.applylowertexture = LowerTexture;
+            pf.lowertexture = LowerTextureTexture;
+			pf.lowerunpegged = LowerUnpegged;
+
+			if (overwrite == -1)
+			{
+				if (position == -1)
+				{
+					BuilderPlug.Me.Prefabs.Add(pf);
+					prefabs.Items.Add(lvi);
+				}
+				else
+				{
+					BuilderPlug.Me.Prefabs.Insert(position, pf);
+					prefabs.Items.Insert(position, lvi);
+				}
+			}
+			else
+			{
+				BuilderPlug.Me.Prefabs.RemoveAt(overwrite);
+				BuilderPlug.Me.Prefabs.Insert(overwrite, pf);
+
+				prefabs.Items.RemoveAt(overwrite);
+				prefabs.Items.Insert(overwrite, lvi);
+			}
+        }
+
+        private void prefabload_Click(object sender, EventArgs e)
+        {
+			if (prefabs.SelectedIndices.Count == 0) return;
+
+			loadingprefab = true;
+
+            BuilderPlug.Prefab p = BuilderPlug.Me.Prefabs[prefabs.SelectedIndices[0]];
+			
+            prefabname.Text = p.name;
+
+            NumberOfSectors = (uint)p.numberofsectors;
+            OuterVertexMultiplier = p.outervertexmultiplier;
+            InnerVertexMultiplier = p.innervertexmultiplier;
+
+            tabcontrol.SelectedIndex = p.stairtype;
+
+            // Straight stairs
+            SectorDepth = (uint)p.sectordepth;
+            Spacing = p.spacing;
+            SideFront = p.frontside;
+            SingleSectors.Checked = p.singlesectors;
+            SingleDirection.Checked = p.singledirection;
+			DistinctBaseHeights.Checked = p.distinctbaseheights;
+
+            // Auto curve TODO
+			Flipping = p.flipping;
+
+            // Catmull Rom spline
+            NumControlPoints = p.numberofcontrolpoints;
+
+            // Height info
+            FloorHeight = p.applyfloormod;
+            FloorHeightModification = p.floormod;
+			//FloorBase = p.floorbase;
+            CeilingHeight = p.applyceilingmod;
+            CeilingHeightModification = p.ceilingmod;
+			//CeilingBase = p.ceilingbase;
+
+            // Textures
+            FloorFlat = p.applyfloortexture;
+            FloorFlatTexture = p.floortexture;
+            CeilingFlat = p.applyceilingtexture;
+            CeilingFlatTexture = p.ceilingtexture;
+
+            UpperTexture = p.applyuppertexture;
+            UpperTextureTexture = p.uppertexture;
+			UpperUnpegged = p.upperunpegged;
+
+			MiddleTexture = p.applymiddletexture;
+			MiddleTextureTexture = p.middletexture;
+
+            LowerTexture = p.applylowertexture;
+            LowerTextureTexture = p.lowertexture;
+			LowerUnpegged = p.lowerunpegged;
+
+			loadingprefab = false;
+
+			DoRedrawDisplay();
+        }
+
+		private void prefabdelete_Click(object sender, EventArgs e)
+		{
+			if (prefabs.SelectedIndices.Count == 0) return;
+
+			BuilderPlug.Me.Prefabs.RemoveAt(prefabs.SelectedIndices[0]);
+			prefabs.Items.RemoveAt(prefabs.SelectedIndices[0]);
+		}
+
+		private void distinctbaseheights_CheckedChanged(object sender, EventArgs e)
+		{
+			if (distinctbaseheights.Checked == true)
+			{
+				floorbase.Enabled = false;
+				ceilingbase.Enabled = false;
+			}
+			else
+			{
+				if(floorheightmodification.Checked) floorbase.Enabled = true;
+				if(ceilingheightmodification.Checked) ceilingbase.Enabled = true;
+			}
+
+			ComputeHeights();
+		}
+
+		private void autocurveflipping_SelectedIndexChanged(object sender, EventArgs e)
+		{
+			if (autocurveflipping.SelectedIndex != splineflipping.SelectedIndex)
+				splineflipping.SelectedIndex = autocurveflipping.SelectedIndex;
+
+			DoRedrawDisplay();
+		}
+
+		private void splineflipping_SelectedIndexChanged(object sender, EventArgs e)
+		{
+			if (splineflipping.SelectedIndex != autocurveflipping.SelectedIndex)
+				autocurveflipping.SelectedIndex = splineflipping.SelectedIndex;
+
+			DoRedrawDisplay();
+		}
+
+		private void floorheightmodification_CheckedChanged(object sender, EventArgs e)
+		{
+			if (floorheightmodification.Checked)
+			{
+				floorheightmod.Enabled = true;
+
+				if (StairType != 0 || distinctbaseheights.Checked == false)	floorbase.Enabled = true;
+			}
+			else
+			{
+				floorheightmod.Enabled = false;
+
+				if (StairType != 0 || distinctbaseheights.Checked == false)	floorbase.Enabled = false;
+			}
+		}
+
+		private void ceilingheightmodification_CheckedChanged(object sender, EventArgs e)
+		{
+			if (ceilingheightmodification.Checked)
+			{
+				ceilingheightmod.Enabled = true;
+
+				if (StairType != 0 || distinctbaseheights.Checked == false) ceilingbase.Enabled = true;
+			}
+			else
+			{
+				ceilingheightmod.Enabled = false;
+
+				if (StairType != 0 || distinctbaseheights.Checked == false) ceilingbase.Enabled = false;
+			}
+		}
+
+		private void floorbasegetter_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+		{
+			floorbase.Text = originalfloorbase.ToString();
+		}
+
+		private void ceilingbasegetter_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+		{
+			ceilingbase.Text = originalceilingbase.ToString();
+		}
+	}
+}
diff --git a/Source/Plugins/StairSectorBuilder/StairSectorBuilderForm.designer.cs b/Source/Plugins/StairSectorBuilder/StairSectorBuilderForm.designer.cs
new file mode 100644
index 00000000..9a24f16a
--- /dev/null
+++ b/Source/Plugins/StairSectorBuilder/StairSectorBuilderForm.designer.cs
@@ -0,0 +1,1151 @@
+namespace CodeImp.DoomBuilder.StairSectorBuilderMode
+{
+	partial class StairSectorBuilderForm
+	{
+		/// <summary>
+		/// Erforderliche Designervariable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary>
+		/// Verwendete Ressourcen bereinigen.
+		/// </summary>
+		/// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Vom Windows Form-Designer generierter Code
+
+		/// <summary>
+		/// Erforderliche Methode für die Designerunterstützung.
+		/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			this.btnOK = new System.Windows.Forms.Button();
+			this.btnCancel = new System.Windows.Forms.Button();
+			this.groupBox1 = new System.Windows.Forms.GroupBox();
+			this.numberofsectors = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.label4 = new System.Windows.Forms.Label();
+			this.tabcontrol = new System.Windows.Forms.TabControl();
+			this.tabPage1 = new System.Windows.Forms.TabPage();
+			this.distinctbaseheights = new System.Windows.Forms.CheckBox();
+			this.spacing = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.label11 = new System.Windows.Forms.Label();
+			this.groupBox3 = new System.Windows.Forms.GroupBox();
+			this.singledirection = new System.Windows.Forms.CheckBox();
+			this.singlesectors = new System.Windows.Forms.CheckBox();
+			this.groupBox2 = new System.Windows.Forms.GroupBox();
+			this.sideback = new System.Windows.Forms.RadioButton();
+			this.sidefront = new System.Windows.Forms.RadioButton();
+			this.sectordepth = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.label1 = new System.Windows.Forms.Label();
+			this.tabPage2 = new System.Windows.Forms.TabPage();
+			this.autocurveflipping = new System.Windows.Forms.ComboBox();
+			this.label12 = new System.Windows.Forms.Label();
+			this.autocurveoutervertexmultiplier = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.autocurveinnervertexmultiplier = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.label6 = new System.Windows.Forms.Label();
+			this.label5 = new System.Windows.Forms.Label();
+			this.tabPage3 = new System.Windows.Forms.TabPage();
+			this.splineflipping = new System.Windows.Forms.ComboBox();
+			this.label13 = new System.Windows.Forms.Label();
+			this.splineoutervertexmultiplier = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.splineinnervertexmultiplier = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.label16 = new System.Windows.Forms.Label();
+			this.label17 = new System.Windows.Forms.Label();
+			this.numberofcontrolpoints = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.label10 = new System.Windows.Forms.Label();
+			this.groupBox4 = new System.Windows.Forms.GroupBox();
+			this.floorflattexture = new CodeImp.DoomBuilder.Controls.FlatSelectorControl();
+			this.floorflat = new System.Windows.Forms.CheckBox();
+			this.ceilingflat = new System.Windows.Forms.CheckBox();
+			this.groupBox5 = new System.Windows.Forms.GroupBox();
+			this.ceilingflattexture = new CodeImp.DoomBuilder.Controls.FlatSelectorControl();
+			this.groupBox6 = new System.Windows.Forms.GroupBox();
+			this.label14 = new System.Windows.Forms.Label();
+			this.floorbase = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.floorheightmodification = new System.Windows.Forms.CheckBox();
+			this.floorlast = new System.Windows.Forms.Label();
+			this.floorfirst = new System.Windows.Forms.Label();
+			this.label7 = new System.Windows.Forms.Label();
+			this.label3 = new System.Windows.Forms.Label();
+			this.floorheightmod = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.label = new System.Windows.Forms.Label();
+			this.groupBox7 = new System.Windows.Forms.GroupBox();
+			this.lowerunpegged = new System.Windows.Forms.CheckBox();
+			this.lowertexturetexture = new CodeImp.DoomBuilder.Controls.TextureSelectorControl();
+			this.lowertexture = new System.Windows.Forms.CheckBox();
+			this.groupBox8 = new System.Windows.Forms.GroupBox();
+			this.upperunpegged = new System.Windows.Forms.CheckBox();
+			this.uppertexturetexture = new CodeImp.DoomBuilder.Controls.TextureSelectorControl();
+			this.uppertexture = new System.Windows.Forms.CheckBox();
+			this.ceilinglast = new System.Windows.Forms.Label();
+			this.ceilingfirst = new System.Windows.Forms.Label();
+			this.label8 = new System.Windows.Forms.Label();
+			this.label9 = new System.Windows.Forms.Label();
+			this.ceilingheightmod = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.label2 = new System.Windows.Forms.Label();
+			this.groupBox9 = new System.Windows.Forms.GroupBox();
+			this.label15 = new System.Windows.Forms.Label();
+			this.ceilingbase = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.ceilingheightmodification = new System.Windows.Forms.CheckBox();
+			this.groupBox11 = new System.Windows.Forms.GroupBox();
+			this.prefabload = new System.Windows.Forms.Button();
+			this.prefabdelete = new System.Windows.Forms.Button();
+			this.prefabsave = new System.Windows.Forms.Button();
+			this.prefabname = new System.Windows.Forms.TextBox();
+			this.prefabs = new System.Windows.Forms.ListView();
+			this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
+			this.columnHeader2 = new System.Windows.Forms.ColumnHeader();
+			this.groupBox10 = new System.Windows.Forms.GroupBox();
+			this.middletexturetexture = new CodeImp.DoomBuilder.Controls.TextureSelectorControl();
+			this.middletexture = new System.Windows.Forms.CheckBox();
+			this.groupBox1.SuspendLayout();
+			this.tabcontrol.SuspendLayout();
+			this.tabPage1.SuspendLayout();
+			this.groupBox3.SuspendLayout();
+			this.groupBox2.SuspendLayout();
+			this.tabPage2.SuspendLayout();
+			this.tabPage3.SuspendLayout();
+			this.groupBox4.SuspendLayout();
+			this.groupBox5.SuspendLayout();
+			this.groupBox6.SuspendLayout();
+			this.groupBox7.SuspendLayout();
+			this.groupBox8.SuspendLayout();
+			this.groupBox9.SuspendLayout();
+			this.groupBox11.SuspendLayout();
+			this.groupBox10.SuspendLayout();
+			this.SuspendLayout();
+			// 
+			// btnOK
+			// 
+			this.btnOK.Location = new System.Drawing.Point(333, 532);
+			this.btnOK.Name = "btnOK";
+			this.btnOK.Size = new System.Drawing.Size(112, 27);
+			this.btnOK.TabIndex = 4;
+			this.btnOK.Text = "OK";
+			this.btnOK.UseVisualStyleBackColor = true;
+			this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
+			// 
+			// btnCancel
+			// 
+			this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+			this.btnCancel.Location = new System.Drawing.Point(448, 532);
+			this.btnCancel.Name = "btnCancel";
+			this.btnCancel.Size = new System.Drawing.Size(112, 27);
+			this.btnCancel.TabIndex = 5;
+			this.btnCancel.Text = "Cancel";
+			this.btnCancel.UseVisualStyleBackColor = true;
+			this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
+			// 
+			// groupBox1
+			// 
+			this.groupBox1.Controls.Add(this.numberofsectors);
+			this.groupBox1.Controls.Add(this.label4);
+			this.groupBox1.Location = new System.Drawing.Point(12, 9);
+			this.groupBox1.Name = "groupBox1";
+			this.groupBox1.Size = new System.Drawing.Size(220, 50);
+			this.groupBox1.TabIndex = 9;
+			this.groupBox1.TabStop = false;
+			this.groupBox1.Text = "General";
+			// 
+			// numberofsectors
+			// 
+			this.numberofsectors.AllowDecimal = false;
+			this.numberofsectors.AllowNegative = false;
+			this.numberofsectors.AllowRelative = false;
+			this.numberofsectors.ButtonStep = 1;
+			this.numberofsectors.Location = new System.Drawing.Point(112, 17);
+			this.numberofsectors.Name = "numberofsectors";
+			this.numberofsectors.Size = new System.Drawing.Size(56, 24);
+			this.numberofsectors.StepValues = null;
+			this.numberofsectors.TabIndex = 1;
+			this.numberofsectors.WhenTextChanged += new System.EventHandler(this.numberofsectors_WhenTextChanged);
+			// 
+			// label4
+			// 
+			this.label4.AutoSize = true;
+			this.label4.Location = new System.Drawing.Point(9, 22);
+			this.label4.Name = "label4";
+			this.label4.Size = new System.Drawing.Size(97, 14);
+			this.label4.TabIndex = 0;
+			this.label4.Text = "Number of sectors";
+			// 
+			// tabcontrol
+			// 
+			this.tabcontrol.Controls.Add(this.tabPage1);
+			this.tabcontrol.Controls.Add(this.tabPage2);
+			this.tabcontrol.Controls.Add(this.tabPage3);
+			this.tabcontrol.Location = new System.Drawing.Point(12, 65);
+			this.tabcontrol.Name = "tabcontrol";
+			this.tabcontrol.SelectedIndex = 0;
+			this.tabcontrol.Size = new System.Drawing.Size(220, 271);
+			this.tabcontrol.TabIndex = 10;
+			this.tabcontrol.SelectedIndexChanged += new System.EventHandler(this.tabcontrol_SelectedIndexChanged);
+			// 
+			// tabPage1
+			// 
+			this.tabPage1.Controls.Add(this.distinctbaseheights);
+			this.tabPage1.Controls.Add(this.spacing);
+			this.tabPage1.Controls.Add(this.label11);
+			this.tabPage1.Controls.Add(this.groupBox3);
+			this.tabPage1.Controls.Add(this.groupBox2);
+			this.tabPage1.Controls.Add(this.sectordepth);
+			this.tabPage1.Controls.Add(this.label1);
+			this.tabPage1.Location = new System.Drawing.Point(4, 23);
+			this.tabPage1.Name = "tabPage1";
+			this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
+			this.tabPage1.Size = new System.Drawing.Size(212, 244);
+			this.tabPage1.TabIndex = 0;
+			this.tabPage1.Text = "Straight";
+			this.tabPage1.UseVisualStyleBackColor = true;
+			// 
+			// distinctbaseheights
+			// 
+			this.distinctbaseheights.AutoSize = true;
+			this.distinctbaseheights.Location = new System.Drawing.Point(10, 70);
+			this.distinctbaseheights.Name = "distinctbaseheights";
+			this.distinctbaseheights.Size = new System.Drawing.Size(126, 18);
+			this.distinctbaseheights.TabIndex = 6;
+			this.distinctbaseheights.Text = "Distinct base heights";
+			this.distinctbaseheights.UseVisualStyleBackColor = true;
+			this.distinctbaseheights.CheckedChanged += new System.EventHandler(this.distinctbaseheights_CheckedChanged);
+			// 
+			// spacing
+			// 
+			this.spacing.AllowDecimal = false;
+			this.spacing.AllowNegative = false;
+			this.spacing.AllowRelative = false;
+			this.spacing.ButtonStep = 8;
+			this.spacing.Location = new System.Drawing.Point(81, 33);
+			this.spacing.Name = "spacing";
+			this.spacing.Size = new System.Drawing.Size(56, 24);
+			this.spacing.StepValues = null;
+			this.spacing.TabIndex = 5;
+			this.spacing.WhenTextChanged += new System.EventHandler(this.spacing_WhenTextChanged);
+			// 
+			// label11
+			// 
+			this.label11.AutoSize = true;
+			this.label11.Location = new System.Drawing.Point(7, 37);
+			this.label11.Name = "label11";
+			this.label11.Size = new System.Drawing.Size(46, 14);
+			this.label11.TabIndex = 4;
+			this.label11.Text = "Spacing";
+			// 
+			// groupBox3
+			// 
+			this.groupBox3.Controls.Add(this.singledirection);
+			this.groupBox3.Controls.Add(this.singlesectors);
+			this.groupBox3.Location = new System.Drawing.Point(82, 94);
+			this.groupBox3.Name = "groupBox3";
+			this.groupBox3.Size = new System.Drawing.Size(124, 79);
+			this.groupBox3.TabIndex = 3;
+			this.groupBox3.TabStop = false;
+			this.groupBox3.Text = "Appearance";
+			// 
+			// singledirection
+			// 
+			this.singledirection.AutoSize = true;
+			this.singledirection.Enabled = false;
+			this.singledirection.Location = new System.Drawing.Point(7, 46);
+			this.singledirection.Name = "singledirection";
+			this.singledirection.Size = new System.Drawing.Size(99, 18);
+			this.singledirection.TabIndex = 1;
+			this.singledirection.Text = "Single direction";
+			this.singledirection.UseVisualStyleBackColor = true;
+			this.singledirection.CheckedChanged += new System.EventHandler(this.singledirection_CheckedChanged);
+			// 
+			// singlesectors
+			// 
+			this.singlesectors.AutoSize = true;
+			this.singlesectors.Enabled = false;
+			this.singlesectors.Location = new System.Drawing.Point(7, 22);
+			this.singlesectors.Name = "singlesectors";
+			this.singlesectors.Size = new System.Drawing.Size(95, 18);
+			this.singlesectors.TabIndex = 0;
+			this.singlesectors.Text = "Single sectors";
+			this.singlesectors.UseVisualStyleBackColor = true;
+			this.singlesectors.CheckedChanged += new System.EventHandler(this.singlesectors_CheckedChanged);
+			// 
+			// groupBox2
+			// 
+			this.groupBox2.Controls.Add(this.sideback);
+			this.groupBox2.Controls.Add(this.sidefront);
+			this.groupBox2.Location = new System.Drawing.Point(11, 94);
+			this.groupBox2.Name = "groupBox2";
+			this.groupBox2.Size = new System.Drawing.Size(65, 79);
+			this.groupBox2.TabIndex = 2;
+			this.groupBox2.TabStop = false;
+			this.groupBox2.Text = "Side";
+			// 
+			// sideback
+			// 
+			this.sideback.AutoSize = true;
+			this.sideback.Location = new System.Drawing.Point(7, 46);
+			this.sideback.Name = "sideback";
+			this.sideback.Size = new System.Drawing.Size(49, 18);
+			this.sideback.TabIndex = 1;
+			this.sideback.TabStop = true;
+			this.sideback.Text = "Back";
+			this.sideback.UseVisualStyleBackColor = true;
+			this.sideback.CheckedChanged += new System.EventHandler(this.sideback_CheckedChanged);
+			// 
+			// sidefront
+			// 
+			this.sidefront.AutoSize = true;
+			this.sidefront.Checked = true;
+			this.sidefront.Location = new System.Drawing.Point(7, 22);
+			this.sidefront.Name = "sidefront";
+			this.sidefront.Size = new System.Drawing.Size(50, 18);
+			this.sidefront.TabIndex = 0;
+			this.sidefront.TabStop = true;
+			this.sidefront.Text = "Front";
+			this.sidefront.UseVisualStyleBackColor = true;
+			this.sidefront.CheckedChanged += new System.EventHandler(this.sidefront_CheckedChanged);
+			// 
+			// sectordepth
+			// 
+			this.sectordepth.AllowDecimal = false;
+			this.sectordepth.AllowNegative = false;
+			this.sectordepth.AllowRelative = false;
+			this.sectordepth.ButtonStep = 8;
+			this.sectordepth.Location = new System.Drawing.Point(81, 3);
+			this.sectordepth.Name = "sectordepth";
+			this.sectordepth.Size = new System.Drawing.Size(56, 24);
+			this.sectordepth.StepValues = null;
+			this.sectordepth.TabIndex = 1;
+			this.sectordepth.WhenTextChanged += new System.EventHandler(this.sectordepth_WhenTextChanged);
+			// 
+			// label1
+			// 
+			this.label1.AutoSize = true;
+			this.label1.Location = new System.Drawing.Point(7, 8);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(69, 14);
+			this.label1.TabIndex = 0;
+			this.label1.Text = "Sector depth";
+			// 
+			// tabPage2
+			// 
+			this.tabPage2.Controls.Add(this.autocurveflipping);
+			this.tabPage2.Controls.Add(this.label12);
+			this.tabPage2.Controls.Add(this.autocurveoutervertexmultiplier);
+			this.tabPage2.Controls.Add(this.autocurveinnervertexmultiplier);
+			this.tabPage2.Controls.Add(this.label6);
+			this.tabPage2.Controls.Add(this.label5);
+			this.tabPage2.Location = new System.Drawing.Point(4, 23);
+			this.tabPage2.Name = "tabPage2";
+			this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
+			this.tabPage2.Size = new System.Drawing.Size(212, 244);
+			this.tabPage2.TabIndex = 1;
+			this.tabPage2.Text = "Auto curve";
+			this.tabPage2.UseVisualStyleBackColor = true;
+			// 
+			// autocurveflipping
+			// 
+			this.autocurveflipping.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.autocurveflipping.FormattingEnabled = true;
+			this.autocurveflipping.Items.AddRange(new object[] {
+            "None",
+            "Source direction",
+            "Destination direction"});
+			this.autocurveflipping.Location = new System.Drawing.Point(55, 9);
+			this.autocurveflipping.Name = "autocurveflipping";
+			this.autocurveflipping.Size = new System.Drawing.Size(151, 22);
+			this.autocurveflipping.TabIndex = 11;
+			this.autocurveflipping.SelectedIndexChanged += new System.EventHandler(this.autocurveflipping_SelectedIndexChanged);
+			// 
+			// label12
+			// 
+			this.label12.AutoSize = true;
+			this.label12.Location = new System.Drawing.Point(6, 12);
+			this.label12.Name = "label12";
+			this.label12.Size = new System.Drawing.Size(43, 14);
+			this.label12.TabIndex = 10;
+			this.label12.Text = "Flipping";
+			// 
+			// autocurveoutervertexmultiplier
+			// 
+			this.autocurveoutervertexmultiplier.AllowDecimal = false;
+			this.autocurveoutervertexmultiplier.AllowNegative = false;
+			this.autocurveoutervertexmultiplier.AllowRelative = false;
+			this.autocurveoutervertexmultiplier.ButtonStep = 1;
+			this.autocurveoutervertexmultiplier.Location = new System.Drawing.Point(128, 72);
+			this.autocurveoutervertexmultiplier.Name = "autocurveoutervertexmultiplier";
+			this.autocurveoutervertexmultiplier.Size = new System.Drawing.Size(56, 24);
+			this.autocurveoutervertexmultiplier.StepValues = null;
+			this.autocurveoutervertexmultiplier.TabIndex = 9;
+			this.autocurveoutervertexmultiplier.WhenTextChanged += new System.EventHandler(this.autocurveoutervertexmultiplier_WhenTextChanged);
+			// 
+			// autocurveinnervertexmultiplier
+			// 
+			this.autocurveinnervertexmultiplier.AllowDecimal = false;
+			this.autocurveinnervertexmultiplier.AllowNegative = false;
+			this.autocurveinnervertexmultiplier.AllowRelative = false;
+			this.autocurveinnervertexmultiplier.ButtonStep = 1;
+			this.autocurveinnervertexmultiplier.Location = new System.Drawing.Point(128, 45);
+			this.autocurveinnervertexmultiplier.Name = "autocurveinnervertexmultiplier";
+			this.autocurveinnervertexmultiplier.Size = new System.Drawing.Size(56, 24);
+			this.autocurveinnervertexmultiplier.StepValues = null;
+			this.autocurveinnervertexmultiplier.TabIndex = 8;
+			this.autocurveinnervertexmultiplier.WhenTextChanged += new System.EventHandler(this.autocurveinnervertexmultiplier_WhenTextChanged);
+			// 
+			// label6
+			// 
+			this.label6.AutoSize = true;
+			this.label6.Location = new System.Drawing.Point(6, 77);
+			this.label6.Name = "label6";
+			this.label6.Size = new System.Drawing.Size(112, 14);
+			this.label6.TabIndex = 7;
+			this.label6.Text = "Outer vertex multiplier";
+			// 
+			// label5
+			// 
+			this.label5.AutoSize = true;
+			this.label5.Location = new System.Drawing.Point(6, 50);
+			this.label5.Name = "label5";
+			this.label5.Size = new System.Drawing.Size(109, 14);
+			this.label5.TabIndex = 6;
+			this.label5.Text = "Inner vertex multiplier";
+			// 
+			// tabPage3
+			// 
+			this.tabPage3.Controls.Add(this.splineflipping);
+			this.tabPage3.Controls.Add(this.label13);
+			this.tabPage3.Controls.Add(this.splineoutervertexmultiplier);
+			this.tabPage3.Controls.Add(this.splineinnervertexmultiplier);
+			this.tabPage3.Controls.Add(this.label16);
+			this.tabPage3.Controls.Add(this.label17);
+			this.tabPage3.Controls.Add(this.numberofcontrolpoints);
+			this.tabPage3.Controls.Add(this.label10);
+			this.tabPage3.Location = new System.Drawing.Point(4, 23);
+			this.tabPage3.Name = "tabPage3";
+			this.tabPage3.Padding = new System.Windows.Forms.Padding(3);
+			this.tabPage3.Size = new System.Drawing.Size(212, 244);
+			this.tabPage3.TabIndex = 2;
+			this.tabPage3.Text = "Catmull Rom";
+			this.tabPage3.UseVisualStyleBackColor = true;
+			// 
+			// splineflipping
+			// 
+			this.splineflipping.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.splineflipping.FormattingEnabled = true;
+			this.splineflipping.Items.AddRange(new object[] {
+            "None",
+            "Source direction",
+            "Destination direction"});
+			this.splineflipping.Location = new System.Drawing.Point(55, 9);
+			this.splineflipping.Name = "splineflipping";
+			this.splineflipping.Size = new System.Drawing.Size(151, 22);
+			this.splineflipping.TabIndex = 13;
+			this.splineflipping.SelectedIndexChanged += new System.EventHandler(this.splineflipping_SelectedIndexChanged);
+			// 
+			// label13
+			// 
+			this.label13.AutoSize = true;
+			this.label13.Location = new System.Drawing.Point(6, 12);
+			this.label13.Name = "label13";
+			this.label13.Size = new System.Drawing.Size(43, 14);
+			this.label13.TabIndex = 12;
+			this.label13.Text = "Flipping";
+			// 
+			// splineoutervertexmultiplier
+			// 
+			this.splineoutervertexmultiplier.AllowDecimal = false;
+			this.splineoutervertexmultiplier.AllowNegative = false;
+			this.splineoutervertexmultiplier.AllowRelative = false;
+			this.splineoutervertexmultiplier.ButtonStep = 1;
+			this.splineoutervertexmultiplier.Location = new System.Drawing.Point(128, 72);
+			this.splineoutervertexmultiplier.Name = "splineoutervertexmultiplier";
+			this.splineoutervertexmultiplier.Size = new System.Drawing.Size(56, 24);
+			this.splineoutervertexmultiplier.StepValues = null;
+			this.splineoutervertexmultiplier.TabIndex = 9;
+			this.splineoutervertexmultiplier.WhenTextChanged += new System.EventHandler(this.splineoutervertexmultiplier_WhenTextChanged);
+			// 
+			// splineinnervertexmultiplier
+			// 
+			this.splineinnervertexmultiplier.AllowDecimal = false;
+			this.splineinnervertexmultiplier.AllowNegative = false;
+			this.splineinnervertexmultiplier.AllowRelative = false;
+			this.splineinnervertexmultiplier.ButtonStep = 1;
+			this.splineinnervertexmultiplier.Location = new System.Drawing.Point(128, 45);
+			this.splineinnervertexmultiplier.Name = "splineinnervertexmultiplier";
+			this.splineinnervertexmultiplier.Size = new System.Drawing.Size(56, 24);
+			this.splineinnervertexmultiplier.StepValues = null;
+			this.splineinnervertexmultiplier.TabIndex = 8;
+			this.splineinnervertexmultiplier.WhenTextChanged += new System.EventHandler(this.splineinnervertexmultiplier_WhenTextChanged);
+			// 
+			// label16
+			// 
+			this.label16.AutoSize = true;
+			this.label16.Location = new System.Drawing.Point(6, 77);
+			this.label16.Name = "label16";
+			this.label16.Size = new System.Drawing.Size(112, 14);
+			this.label16.TabIndex = 7;
+			this.label16.Text = "Outer vertex multiplier";
+			// 
+			// label17
+			// 
+			this.label17.AutoSize = true;
+			this.label17.Location = new System.Drawing.Point(6, 50);
+			this.label17.Name = "label17";
+			this.label17.Size = new System.Drawing.Size(109, 14);
+			this.label17.TabIndex = 6;
+			this.label17.Text = "Inner vertex multiplier";
+			// 
+			// numberofcontrolpoints
+			// 
+			this.numberofcontrolpoints.AllowDecimal = false;
+			this.numberofcontrolpoints.AllowNegative = false;
+			this.numberofcontrolpoints.AllowRelative = false;
+			this.numberofcontrolpoints.ButtonStep = 1;
+			this.numberofcontrolpoints.Location = new System.Drawing.Point(128, 99);
+			this.numberofcontrolpoints.Name = "numberofcontrolpoints";
+			this.numberofcontrolpoints.Size = new System.Drawing.Size(56, 24);
+			this.numberofcontrolpoints.StepValues = null;
+			this.numberofcontrolpoints.TabIndex = 3;
+			this.numberofcontrolpoints.WhenTextChanged += new System.EventHandler(this.numberofcontrolpoints_WhenTextChanged);
+			// 
+			// label10
+			// 
+			this.label10.AutoSize = true;
+			this.label10.Location = new System.Drawing.Point(6, 104);
+			this.label10.Name = "label10";
+			this.label10.Size = new System.Drawing.Size(73, 14);
+			this.label10.TabIndex = 2;
+			this.label10.Text = "Control points";
+			// 
+			// groupBox4
+			// 
+			this.groupBox4.Controls.Add(this.floorflattexture);
+			this.groupBox4.Controls.Add(this.floorflat);
+			this.groupBox4.Location = new System.Drawing.Point(12, 343);
+			this.groupBox4.Name = "groupBox4";
+			this.groupBox4.Size = new System.Drawing.Size(105, 183);
+			this.groupBox4.TabIndex = 11;
+			this.groupBox4.TabStop = false;
+			// 
+			// floorflattexture
+			// 
+			this.floorflattexture.Enabled = false;
+			this.floorflattexture.Location = new System.Drawing.Point(8, 23);
+			this.floorflattexture.Name = "floorflattexture";
+			this.floorflattexture.Size = new System.Drawing.Size(90, 127);
+			this.floorflattexture.TabIndex = 1;
+			this.floorflattexture.TextureName = "";
+			// 
+			// floorflat
+			// 
+			this.floorflat.AutoSize = true;
+			this.floorflat.Location = new System.Drawing.Point(9, -1);
+			this.floorflat.Name = "floorflat";
+			this.floorflat.Size = new System.Drawing.Size(68, 18);
+			this.floorflat.TabIndex = 0;
+			this.floorflat.Text = "Floor flat";
+			this.floorflat.UseVisualStyleBackColor = true;
+			this.floorflat.CheckedChanged += new System.EventHandler(this.floorflat_CheckedChanged);
+			// 
+			// ceilingflat
+			// 
+			this.ceilingflat.AutoSize = true;
+			this.ceilingflat.Location = new System.Drawing.Point(9, -1);
+			this.ceilingflat.Name = "ceilingflat";
+			this.ceilingflat.Size = new System.Drawing.Size(75, 18);
+			this.ceilingflat.TabIndex = 0;
+			this.ceilingflat.Text = "Ceiling flat";
+			this.ceilingflat.UseVisualStyleBackColor = true;
+			this.ceilingflat.CheckedChanged += new System.EventHandler(this.ceilingflat_CheckedChanged);
+			// 
+			// groupBox5
+			// 
+			this.groupBox5.Controls.Add(this.ceilingflattexture);
+			this.groupBox5.Controls.Add(this.ceilingflat);
+			this.groupBox5.Location = new System.Drawing.Point(123, 343);
+			this.groupBox5.Name = "groupBox5";
+			this.groupBox5.Size = new System.Drawing.Size(105, 183);
+			this.groupBox5.TabIndex = 12;
+			this.groupBox5.TabStop = false;
+			// 
+			// ceilingflattexture
+			// 
+			this.ceilingflattexture.Enabled = false;
+			this.ceilingflattexture.Location = new System.Drawing.Point(8, 23);
+			this.ceilingflattexture.Name = "ceilingflattexture";
+			this.ceilingflattexture.Size = new System.Drawing.Size(90, 127);
+			this.ceilingflattexture.TabIndex = 2;
+			this.ceilingflattexture.TextureName = "";
+			// 
+			// groupBox6
+			// 
+			this.groupBox6.Controls.Add(this.label14);
+			this.groupBox6.Controls.Add(this.floorbase);
+			this.groupBox6.Controls.Add(this.floorheightmodification);
+			this.groupBox6.Controls.Add(this.floorlast);
+			this.groupBox6.Controls.Add(this.floorfirst);
+			this.groupBox6.Controls.Add(this.label7);
+			this.groupBox6.Controls.Add(this.label3);
+			this.groupBox6.Controls.Add(this.floorheightmod);
+			this.groupBox6.Controls.Add(this.label);
+			this.groupBox6.Location = new System.Drawing.Point(238, 203);
+			this.groupBox6.Name = "groupBox6";
+			this.groupBox6.Size = new System.Drawing.Size(110, 134);
+			this.groupBox6.TabIndex = 13;
+			this.groupBox6.TabStop = false;
+			this.groupBox6.Text = "c";
+			// 
+			// label14
+			// 
+			this.label14.AutoSize = true;
+			this.label14.Location = new System.Drawing.Point(4, 53);
+			this.label14.Name = "label14";
+			this.label14.Size = new System.Drawing.Size(32, 14);
+			this.label14.TabIndex = 17;
+			this.label14.Text = "Base";
+			// 
+			// floorbase
+			// 
+			this.floorbase.AllowDecimal = false;
+			this.floorbase.AllowNegative = true;
+			this.floorbase.AllowRelative = false;
+			this.floorbase.ButtonStep = 8;
+			this.floorbase.Location = new System.Drawing.Point(45, 48);
+			this.floorbase.Name = "floorbase";
+			this.floorbase.Size = new System.Drawing.Size(56, 24);
+			this.floorbase.StepValues = null;
+			this.floorbase.TabIndex = 16;
+			this.floorbase.WhenTextChanged += new System.EventHandler(this.floorbase_WhenTextChanged);
+			// 
+			// floorheightmodification
+			// 
+			this.floorheightmodification.AutoSize = true;
+			this.floorheightmodification.Checked = true;
+			this.floorheightmodification.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.floorheightmodification.Location = new System.Drawing.Point(9, -1);
+			this.floorheightmodification.Name = "floorheightmodification";
+			this.floorheightmodification.Size = new System.Drawing.Size(82, 18);
+			this.floorheightmodification.TabIndex = 14;
+			this.floorheightmodification.Text = "Floor height";
+			this.floorheightmodification.UseVisualStyleBackColor = true;
+			this.floorheightmodification.CheckedChanged += new System.EventHandler(this.floorheightmodification_CheckedChanged);
+			// 
+			// floorlast
+			// 
+			this.floorlast.AutoSize = true;
+			this.floorlast.Location = new System.Drawing.Point(41, 108);
+			this.floorlast.Name = "floorlast";
+			this.floorlast.Size = new System.Drawing.Size(46, 14);
+			this.floorlast.TabIndex = 11;
+			this.floorlast.Text = "floorlast";
+			// 
+			// floorfirst
+			// 
+			this.floorfirst.AutoSize = true;
+			this.floorfirst.Location = new System.Drawing.Point(41, 81);
+			this.floorfirst.Name = "floorfirst";
+			this.floorfirst.Size = new System.Drawing.Size(48, 14);
+			this.floorfirst.TabIndex = 10;
+			this.floorfirst.Text = "floorfirst";
+			// 
+			// label7
+			// 
+			this.label7.AutoSize = true;
+			this.label7.Location = new System.Drawing.Point(4, 108);
+			this.label7.Name = "label7";
+			this.label7.Size = new System.Drawing.Size(31, 14);
+			this.label7.TabIndex = 7;
+			this.label7.Text = "Last:";
+			// 
+			// label3
+			// 
+			this.label3.AutoSize = true;
+			this.label3.Location = new System.Drawing.Point(4, 81);
+			this.label3.Name = "label3";
+			this.label3.Size = new System.Drawing.Size(31, 14);
+			this.label3.TabIndex = 6;
+			this.label3.Text = "First:";
+			// 
+			// floorheightmod
+			// 
+			this.floorheightmod.AllowDecimal = false;
+			this.floorheightmod.AllowNegative = true;
+			this.floorheightmod.AllowRelative = false;
+			this.floorheightmod.ButtonStep = 8;
+			this.floorheightmod.Location = new System.Drawing.Point(45, 22);
+			this.floorheightmod.Name = "floorheightmod";
+			this.floorheightmod.Size = new System.Drawing.Size(56, 24);
+			this.floorheightmod.StepValues = null;
+			this.floorheightmod.TabIndex = 3;
+			this.floorheightmod.WhenTextChanged += new System.EventHandler(this.floorheightmod_WhenTextChanged);
+			// 
+			// label
+			// 
+			this.label.AutoSize = true;
+			this.label.Location = new System.Drawing.Point(4, 27);
+			this.label.Name = "label";
+			this.label.Size = new System.Drawing.Size(39, 14);
+			this.label.TabIndex = 2;
+			this.label.Text = "Modify";
+			// 
+			// groupBox7
+			// 
+			this.groupBox7.Controls.Add(this.lowerunpegged);
+			this.groupBox7.Controls.Add(this.lowertexturetexture);
+			this.groupBox7.Controls.Add(this.lowertexture);
+			this.groupBox7.Location = new System.Drawing.Point(456, 342);
+			this.groupBox7.Name = "groupBox7";
+			this.groupBox7.Size = new System.Drawing.Size(105, 183);
+			this.groupBox7.TabIndex = 14;
+			this.groupBox7.TabStop = false;
+			// 
+			// lowerunpegged
+			// 
+			this.lowerunpegged.AutoSize = true;
+			this.lowerunpegged.Checked = true;
+			this.lowerunpegged.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.lowerunpegged.Location = new System.Drawing.Point(9, 156);
+			this.lowerunpegged.Name = "lowerunpegged";
+			this.lowerunpegged.Size = new System.Drawing.Size(75, 18);
+			this.lowerunpegged.TabIndex = 3;
+			this.lowerunpegged.Text = "Unpegged";
+			this.lowerunpegged.UseVisualStyleBackColor = true;
+			// 
+			// lowertexturetexture
+			// 
+			this.lowertexturetexture.Location = new System.Drawing.Point(8, 23);
+			this.lowertexturetexture.Name = "lowertexturetexture";
+			this.lowertexturetexture.Required = false;
+			this.lowertexturetexture.Size = new System.Drawing.Size(90, 127);
+			this.lowertexturetexture.TabIndex = 2;
+			this.lowertexturetexture.TextureName = "";
+			// 
+			// lowertexture
+			// 
+			this.lowertexture.AutoSize = true;
+			this.lowertexture.Checked = true;
+			this.lowertexture.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.lowertexture.Location = new System.Drawing.Point(9, -1);
+			this.lowertexture.Name = "lowertexture";
+			this.lowertexture.Size = new System.Drawing.Size(95, 18);
+			this.lowertexture.TabIndex = 0;
+			this.lowertexture.Text = "Lower texture";
+			this.lowertexture.UseVisualStyleBackColor = true;
+			this.lowertexture.CheckedChanged += new System.EventHandler(this.lowertexture_CheckedChanged);
+			// 
+			// groupBox8
+			// 
+			this.groupBox8.Controls.Add(this.upperunpegged);
+			this.groupBox8.Controls.Add(this.uppertexturetexture);
+			this.groupBox8.Controls.Add(this.uppertexture);
+			this.groupBox8.Location = new System.Drawing.Point(234, 343);
+			this.groupBox8.Name = "groupBox8";
+			this.groupBox8.Size = new System.Drawing.Size(105, 183);
+			this.groupBox8.TabIndex = 13;
+			this.groupBox8.TabStop = false;
+			// 
+			// upperunpegged
+			// 
+			this.upperunpegged.AutoSize = true;
+			this.upperunpegged.Checked = true;
+			this.upperunpegged.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.upperunpegged.Location = new System.Drawing.Point(9, 156);
+			this.upperunpegged.Name = "upperunpegged";
+			this.upperunpegged.Size = new System.Drawing.Size(75, 18);
+			this.upperunpegged.TabIndex = 4;
+			this.upperunpegged.Text = "Unpegged";
+			this.upperunpegged.UseVisualStyleBackColor = true;
+			// 
+			// uppertexturetexture
+			// 
+			this.uppertexturetexture.Location = new System.Drawing.Point(8, 23);
+			this.uppertexturetexture.Name = "uppertexturetexture";
+			this.uppertexturetexture.Required = false;
+			this.uppertexturetexture.Size = new System.Drawing.Size(90, 127);
+			this.uppertexturetexture.TabIndex = 1;
+			this.uppertexturetexture.TextureName = "";
+			// 
+			// uppertexture
+			// 
+			this.uppertexture.AutoSize = true;
+			this.uppertexture.Checked = true;
+			this.uppertexture.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.uppertexture.Location = new System.Drawing.Point(9, -1);
+			this.uppertexture.Name = "uppertexture";
+			this.uppertexture.Size = new System.Drawing.Size(92, 18);
+			this.uppertexture.TabIndex = 0;
+			this.uppertexture.Text = "Upper texture";
+			this.uppertexture.UseVisualStyleBackColor = true;
+			this.uppertexture.CheckedChanged += new System.EventHandler(this.uppertexture_CheckedChanged);
+			// 
+			// ceilinglast
+			// 
+			this.ceilinglast.AutoSize = true;
+			this.ceilinglast.Location = new System.Drawing.Point(41, 108);
+			this.ceilinglast.Name = "ceilinglast";
+			this.ceilinglast.Size = new System.Drawing.Size(54, 14);
+			this.ceilinglast.TabIndex = 24;
+			this.ceilinglast.Text = "ceilinglast";
+			// 
+			// ceilingfirst
+			// 
+			this.ceilingfirst.AutoSize = true;
+			this.ceilingfirst.Location = new System.Drawing.Point(41, 81);
+			this.ceilingfirst.Name = "ceilingfirst";
+			this.ceilingfirst.Size = new System.Drawing.Size(56, 14);
+			this.ceilingfirst.TabIndex = 23;
+			this.ceilingfirst.Text = "ceilingfirst";
+			// 
+			// label8
+			// 
+			this.label8.AutoSize = true;
+			this.label8.Location = new System.Drawing.Point(4, 108);
+			this.label8.Name = "label8";
+			this.label8.Size = new System.Drawing.Size(31, 14);
+			this.label8.TabIndex = 22;
+			this.label8.Text = "Last:";
+			// 
+			// label9
+			// 
+			this.label9.AutoSize = true;
+			this.label9.Location = new System.Drawing.Point(4, 81);
+			this.label9.Name = "label9";
+			this.label9.Size = new System.Drawing.Size(31, 14);
+			this.label9.TabIndex = 21;
+			this.label9.Text = "First:";
+			// 
+			// ceilingheightmod
+			// 
+			this.ceilingheightmod.AllowDecimal = false;
+			this.ceilingheightmod.AllowNegative = true;
+			this.ceilingheightmod.AllowRelative = false;
+			this.ceilingheightmod.ButtonStep = 8;
+			this.ceilingheightmod.Location = new System.Drawing.Point(45, 22);
+			this.ceilingheightmod.Name = "ceilingheightmod";
+			this.ceilingheightmod.Size = new System.Drawing.Size(56, 24);
+			this.ceilingheightmod.StepValues = null;
+			this.ceilingheightmod.TabIndex = 20;
+			this.ceilingheightmod.WhenTextChanged += new System.EventHandler(this.ceilingheightmod_WhenTextChanged);
+			// 
+			// label2
+			// 
+			this.label2.AutoSize = true;
+			this.label2.Location = new System.Drawing.Point(4, 27);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(39, 14);
+			this.label2.TabIndex = 19;
+			this.label2.Text = "Modify";
+			// 
+			// groupBox9
+			// 
+			this.groupBox9.Controls.Add(this.label15);
+			this.groupBox9.Controls.Add(this.ceilingbase);
+			this.groupBox9.Controls.Add(this.ceilingheightmodification);
+			this.groupBox9.Controls.Add(this.ceilingheightmod);
+			this.groupBox9.Controls.Add(this.label2);
+			this.groupBox9.Controls.Add(this.ceilinglast);
+			this.groupBox9.Controls.Add(this.label9);
+			this.groupBox9.Controls.Add(this.ceilingfirst);
+			this.groupBox9.Controls.Add(this.label8);
+			this.groupBox9.Location = new System.Drawing.Point(354, 203);
+			this.groupBox9.Name = "groupBox9";
+			this.groupBox9.Size = new System.Drawing.Size(110, 134);
+			this.groupBox9.TabIndex = 18;
+			this.groupBox9.TabStop = false;
+			this.groupBox9.Text = "c";
+			// 
+			// label15
+			// 
+			this.label15.AutoSize = true;
+			this.label15.Location = new System.Drawing.Point(4, 53);
+			this.label15.Name = "label15";
+			this.label15.Size = new System.Drawing.Size(32, 14);
+			this.label15.TabIndex = 27;
+			this.label15.Text = "Base";
+			// 
+			// ceilingbase
+			// 
+			this.ceilingbase.AllowDecimal = false;
+			this.ceilingbase.AllowNegative = true;
+			this.ceilingbase.AllowRelative = false;
+			this.ceilingbase.ButtonStep = 8;
+			this.ceilingbase.Location = new System.Drawing.Point(45, 48);
+			this.ceilingbase.Name = "ceilingbase";
+			this.ceilingbase.Size = new System.Drawing.Size(56, 24);
+			this.ceilingbase.StepValues = null;
+			this.ceilingbase.TabIndex = 26;
+			this.ceilingbase.WhenTextChanged += new System.EventHandler(this.ceilingbase_WhenTextChanged);
+			// 
+			// ceilingheightmodification
+			// 
+			this.ceilingheightmodification.AutoSize = true;
+			this.ceilingheightmodification.Checked = true;
+			this.ceilingheightmodification.CheckState = System.Windows.Forms.CheckState.Checked;
+			this.ceilingheightmodification.Location = new System.Drawing.Point(9, -1);
+			this.ceilingheightmodification.Name = "ceilingheightmodification";
+			this.ceilingheightmodification.Size = new System.Drawing.Size(89, 18);
+			this.ceilingheightmodification.TabIndex = 14;
+			this.ceilingheightmodification.Text = "Ceiling height";
+			this.ceilingheightmodification.UseVisualStyleBackColor = true;
+			this.ceilingheightmodification.CheckedChanged += new System.EventHandler(this.ceilingheightmodification_CheckedChanged);
+			// 
+			// groupBox11
+			// 
+			this.groupBox11.Controls.Add(this.prefabload);
+			this.groupBox11.Controls.Add(this.prefabdelete);
+			this.groupBox11.Controls.Add(this.prefabsave);
+			this.groupBox11.Controls.Add(this.prefabname);
+			this.groupBox11.Controls.Add(this.prefabs);
+			this.groupBox11.Location = new System.Drawing.Point(238, 9);
+			this.groupBox11.Name = "groupBox11";
+			this.groupBox11.Size = new System.Drawing.Size(323, 187);
+			this.groupBox11.TabIndex = 24;
+			this.groupBox11.TabStop = false;
+			this.groupBox11.Text = "Prefabs";
+			// 
+			// prefabload
+			// 
+			this.prefabload.Location = new System.Drawing.Point(80, 156);
+			this.prefabload.Name = "prefabload";
+			this.prefabload.Size = new System.Drawing.Size(65, 23);
+			this.prefabload.TabIndex = 28;
+			this.prefabload.Text = "Load";
+			this.prefabload.UseVisualStyleBackColor = true;
+			this.prefabload.Click += new System.EventHandler(this.prefabload_Click);
+			// 
+			// prefabdelete
+			// 
+			this.prefabdelete.Location = new System.Drawing.Point(151, 156);
+			this.prefabdelete.Name = "prefabdelete";
+			this.prefabdelete.Size = new System.Drawing.Size(65, 23);
+			this.prefabdelete.TabIndex = 27;
+			this.prefabdelete.Text = "Delete";
+			this.prefabdelete.UseVisualStyleBackColor = true;
+			this.prefabdelete.Click += new System.EventHandler(this.prefabdelete_Click);
+			// 
+			// prefabsave
+			// 
+			this.prefabsave.Location = new System.Drawing.Point(9, 156);
+			this.prefabsave.Name = "prefabsave";
+			this.prefabsave.Size = new System.Drawing.Size(65, 23);
+			this.prefabsave.TabIndex = 26;
+			this.prefabsave.Text = "Save";
+			this.prefabsave.UseVisualStyleBackColor = true;
+			this.prefabsave.Click += new System.EventHandler(this.prefabsave_Click);
+			// 
+			// prefabname
+			// 
+			this.prefabname.Location = new System.Drawing.Point(10, 19);
+			this.prefabname.Name = "prefabname";
+			this.prefabname.Size = new System.Drawing.Size(306, 20);
+			this.prefabname.TabIndex = 25;
+			// 
+			// prefabs
+			// 
+			this.prefabs.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+            this.columnHeader1,
+            this.columnHeader2});
+			this.prefabs.FullRowSelect = true;
+			this.prefabs.Location = new System.Drawing.Point(9, 41);
+			this.prefabs.MultiSelect = false;
+			this.prefabs.Name = "prefabs";
+			this.prefabs.Size = new System.Drawing.Size(307, 109);
+			this.prefabs.TabIndex = 24;
+			this.prefabs.UseCompatibleStateImageBehavior = false;
+			this.prefabs.View = System.Windows.Forms.View.Details;
+			// 
+			// columnHeader1
+			// 
+			this.columnHeader1.Text = "Name";
+			this.columnHeader1.Width = 200;
+			// 
+			// columnHeader2
+			// 
+			this.columnHeader2.Text = "Type";
+			this.columnHeader2.Width = 80;
+			// 
+			// groupBox10
+			// 
+			this.groupBox10.Controls.Add(this.middletexturetexture);
+			this.groupBox10.Controls.Add(this.middletexture);
+			this.groupBox10.Location = new System.Drawing.Point(345, 343);
+			this.groupBox10.Name = "groupBox10";
+			this.groupBox10.Size = new System.Drawing.Size(105, 183);
+			this.groupBox10.TabIndex = 25;
+			this.groupBox10.TabStop = false;
+			// 
+			// middletexturetexture
+			// 
+			this.middletexturetexture.Enabled = false;
+			this.middletexturetexture.Location = new System.Drawing.Point(8, 23);
+			this.middletexturetexture.Name = "middletexturetexture";
+			this.middletexturetexture.Required = false;
+			this.middletexturetexture.Size = new System.Drawing.Size(90, 127);
+			this.middletexturetexture.TabIndex = 2;
+			this.middletexturetexture.TextureName = "";
+			// 
+			// middletexture
+			// 
+			this.middletexture.AutoSize = true;
+			this.middletexture.Location = new System.Drawing.Point(9, -1);
+			this.middletexture.Name = "middletexture";
+			this.middletexture.Size = new System.Drawing.Size(93, 18);
+			this.middletexture.TabIndex = 0;
+			this.middletexture.Text = "Middle texture";
+			this.middletexture.UseVisualStyleBackColor = true;
+			this.middletexture.CheckedChanged += new System.EventHandler(this.middletexture_CheckedChanged);
+			// 
+			// StairSectorBuilderForm
+			// 
+			this.AcceptButton = this.btnOK;
+			this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+			this.CancelButton = this.btnCancel;
+			this.ClientSize = new System.Drawing.Size(573, 570);
+			this.Controls.Add(this.groupBox10);
+			this.Controls.Add(this.groupBox11);
+			this.Controls.Add(this.groupBox9);
+			this.Controls.Add(this.groupBox7);
+			this.Controls.Add(this.groupBox8);
+			this.Controls.Add(this.groupBox6);
+			this.Controls.Add(this.groupBox5);
+			this.Controls.Add(this.groupBox4);
+			this.Controls.Add(this.tabcontrol);
+			this.Controls.Add(this.groupBox1);
+			this.Controls.Add(this.btnCancel);
+			this.Controls.Add(this.btnOK);
+			this.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
+			this.MaximizeBox = false;
+			this.MinimizeBox = false;
+			this.Name = "StairSectorBuilderForm";
+			this.Opacity = 0;
+			this.ShowIcon = false;
+			this.ShowInTaskbar = false;
+			this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+			this.Text = "Stair Sector Builder";
+			this.Load += new System.EventHandler(this.StairSectorBuilderForm_Load);
+			this.Shown += new System.EventHandler(this.StairSectorBuilderForm_Shown);
+			this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.StairSectorBuilderForm_FormClosing);
+			this.groupBox1.ResumeLayout(false);
+			this.groupBox1.PerformLayout();
+			this.tabcontrol.ResumeLayout(false);
+			this.tabPage1.ResumeLayout(false);
+			this.tabPage1.PerformLayout();
+			this.groupBox3.ResumeLayout(false);
+			this.groupBox3.PerformLayout();
+			this.groupBox2.ResumeLayout(false);
+			this.groupBox2.PerformLayout();
+			this.tabPage2.ResumeLayout(false);
+			this.tabPage2.PerformLayout();
+			this.tabPage3.ResumeLayout(false);
+			this.tabPage3.PerformLayout();
+			this.groupBox4.ResumeLayout(false);
+			this.groupBox4.PerformLayout();
+			this.groupBox5.ResumeLayout(false);
+			this.groupBox5.PerformLayout();
+			this.groupBox6.ResumeLayout(false);
+			this.groupBox6.PerformLayout();
+			this.groupBox7.ResumeLayout(false);
+			this.groupBox7.PerformLayout();
+			this.groupBox8.ResumeLayout(false);
+			this.groupBox8.PerformLayout();
+			this.groupBox9.ResumeLayout(false);
+			this.groupBox9.PerformLayout();
+			this.groupBox11.ResumeLayout(false);
+			this.groupBox11.PerformLayout();
+			this.groupBox10.ResumeLayout(false);
+			this.groupBox10.PerformLayout();
+			this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+		private System.Windows.Forms.Button btnOK;
+		private System.Windows.Forms.Button btnCancel;
+		private System.Windows.Forms.GroupBox groupBox1;
+		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox numberofsectors;
+		private System.Windows.Forms.Label label4;
+		private System.Windows.Forms.TabControl tabcontrol;
+		private System.Windows.Forms.TabPage tabPage1;
+		private System.Windows.Forms.TabPage tabPage2;
+		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox sectordepth;
+		private System.Windows.Forms.Label label1;
+		private System.Windows.Forms.GroupBox groupBox2;
+		private System.Windows.Forms.RadioButton sideback;
+		private System.Windows.Forms.RadioButton sidefront;
+		private System.Windows.Forms.GroupBox groupBox3;
+		private System.Windows.Forms.CheckBox singledirection;
+		private System.Windows.Forms.CheckBox singlesectors;
+		private System.Windows.Forms.GroupBox groupBox4;
+		private System.Windows.Forms.CheckBox floorflat;
+		private CodeImp.DoomBuilder.Controls.FlatSelectorControl floorflattexture;
+		private System.Windows.Forms.GroupBox groupBox5;
+		private System.Windows.Forms.CheckBox ceilingflat;
+		private System.Windows.Forms.TabPage tabPage3;
+		private System.Windows.Forms.GroupBox groupBox6;
+		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox floorheightmod;
+		private System.Windows.Forms.Label label;
+		private CodeImp.DoomBuilder.Controls.FlatSelectorControl ceilingflattexture;
+		private System.Windows.Forms.Label label3;
+		private System.Windows.Forms.Label floorlast;
+		private System.Windows.Forms.Label floorfirst;
+		private System.Windows.Forms.Label label7;
+		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox numberofcontrolpoints;
+		private System.Windows.Forms.Label label10;
+        private System.Windows.Forms.CheckBox floorheightmodification;
+        private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox spacing;
+        private System.Windows.Forms.Label label11;
+        private System.Windows.Forms.GroupBox groupBox7;
+        private System.Windows.Forms.CheckBox lowertexture;
+        private System.Windows.Forms.GroupBox groupBox8;
+        private System.Windows.Forms.CheckBox uppertexture;
+        private CodeImp.DoomBuilder.Controls.TextureSelectorControl lowertexturetexture;
+		private CodeImp.DoomBuilder.Controls.TextureSelectorControl uppertexturetexture;
+		private System.Windows.Forms.Label ceilinglast;
+		private System.Windows.Forms.Label ceilingfirst;
+		private System.Windows.Forms.Label label8;
+		private System.Windows.Forms.Label label9;
+		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox ceilingheightmod;
+		private System.Windows.Forms.Label label2;
+		private System.Windows.Forms.GroupBox groupBox9;
+		private System.Windows.Forms.CheckBox ceilingheightmodification;
+		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox floorbase;
+        private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox ceilingbase;
+        private System.Windows.Forms.GroupBox groupBox11;
+        private System.Windows.Forms.Button prefabload;
+        private System.Windows.Forms.Button prefabdelete;
+        private System.Windows.Forms.Button prefabsave;
+        private System.Windows.Forms.TextBox prefabname;
+        private System.Windows.Forms.ListView prefabs;
+        private System.Windows.Forms.ColumnHeader columnHeader1;
+        private System.Windows.Forms.ColumnHeader columnHeader2;
+		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox autocurveoutervertexmultiplier;
+		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox autocurveinnervertexmultiplier;
+		private System.Windows.Forms.Label label6;
+		private System.Windows.Forms.Label label5;
+		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox splineoutervertexmultiplier;
+		private CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox splineinnervertexmultiplier;
+		private System.Windows.Forms.Label label16;
+		private System.Windows.Forms.Label label17;
+		private System.Windows.Forms.CheckBox distinctbaseheights;
+		private System.Windows.Forms.Label label12;
+		private System.Windows.Forms.ComboBox autocurveflipping;
+		private System.Windows.Forms.ComboBox splineflipping;
+		private System.Windows.Forms.Label label13;
+		private System.Windows.Forms.Label label14;
+		private System.Windows.Forms.Label label15;
+		private System.Windows.Forms.CheckBox lowerunpegged;
+		private System.Windows.Forms.CheckBox upperunpegged;
+		private System.Windows.Forms.GroupBox groupBox10;
+		private CodeImp.DoomBuilder.Controls.TextureSelectorControl middletexturetexture;
+		private System.Windows.Forms.CheckBox middletexture;
+	}
+}
\ No newline at end of file
diff --git a/Source/Plugins/StairSectorBuilder/StairSectorBuilderForm.resx b/Source/Plugins/StairSectorBuilder/StairSectorBuilderForm.resx
new file mode 100644
index 00000000..ff31a6db
--- /dev/null
+++ b/Source/Plugins/StairSectorBuilder/StairSectorBuilderForm.resx
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/Source/Plugins/StairSectorBuilder/StairSectorBuilderMode.cs b/Source/Plugins/StairSectorBuilder/StairSectorBuilderMode.cs
new file mode 100644
index 00000000..cc3ce80e
--- /dev/null
+++ b/Source/Plugins/StairSectorBuilder/StairSectorBuilderMode.cs
@@ -0,0 +1,1671 @@
+
+#region ================== Copyright (c) 2007 Pascal vd Heiden
+
+/*
+ * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
+ * This program is released under GNU General Public License
+ * 
+ * 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.
+ * 
+ */
+
+#endregion
+
+#region ================== Namespaces
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Globalization;
+using System.Text;
+using System.Windows.Forms;
+using System.IO;
+using System.Reflection;
+using System.Drawing;
+using CodeImp.DoomBuilder.Windows;
+using CodeImp.DoomBuilder.IO;
+using CodeImp.DoomBuilder.Map;
+using CodeImp.DoomBuilder.Rendering;
+using CodeImp.DoomBuilder.Geometry;
+using CodeImp.DoomBuilder.Editing;
+using CodeImp.DoomBuilder.Data;
+using CodeImp.DoomBuilder.Actions;
+using CodeImp.DoomBuilder.Types;
+using CodeImp.DoomBuilder.Config;
+using CodeImp.DoomBuilder.Controls;
+
+#endregion
+
+namespace CodeImp.DoomBuilder.StairSectorBuilderMode
+{
+	//[EditMode(DisplayName = "Stair Sector Builder",
+	//          Volatile = true)]
+
+	[EditMode(DisplayName = "Stair Sector Builder Mode",
+			  SwitchAction = "stairsectorbuildermode",		// Action name used to switch to this mode
+			  ButtonImage = "StairIcon.png",	// Image resource name for the button
+			  ButtonOrder = int.MinValue + 500,	// Position of the button (lower is more to the left)
+			  ButtonGroup = "000_editing",
+			  UseByDefault = true,
+			  SafeStartMode = false,
+              Volatile = true )]
+
+	public sealed class StairSectorBuilderMode : ClassicMode
+	{
+		#region ================== Constants
+
+		private const float LINE_THICKNESS = 0.6f;
+		private const float CONTROLPOINT_SIZE = 10.0f;
+		private const int INNER_SPLINE = 0;
+		private const int OUTER_SPLINE = 1;
+
+		#endregion
+
+		#region ================== Variables
+
+		private StairSectorBuilderForm stairsectorbuilderform;
+        private List<StairInfo> stairsectors;
+		// private List<Vector2D> controlpoints;
+		// private List<Vector2D> tangents;
+		private List<CatmullRomSplineGroup> catmullromsplinegroups;
+		private int numcontrolpoints;
+		private ControlpointPointer selectedcontrolpoint;
+		private bool linesconnected;
+		private List<ConnectedLinedefs> connectedlinedefs;
+		private bool baseheightset = false;
+		private int oldflipping = -1;
+		private List<List<Vector2D>> exactsplines;
+
+		#endregion
+
+		#region ================== Structures
+
+		private struct CatmullRomSplineData
+		{
+			public Line2D line;
+			public List<Vector2D> controlpoints;
+			public List<Vector2D> tangents;
+		}
+
+		private struct CatmullRomSplineGroup
+		{
+			public CatmullRomSplineData[] splines;
+			public Linedef sourcelinedef;
+		}
+
+		private struct ControlpointPointer
+		{
+			public int crsg;
+			public int crsd;
+			public int cp;
+		}
+
+		private struct ConnectedLinedefs
+		{
+			public bool closed;
+			public int sector;
+			public List<Linedef> linedefs;
+			public Linedef firstlinedef;
+		}
+
+        private struct StairInfo
+        {
+            public int floorheight;
+            public int ceilingheight;
+            public List<List<Vector2D>> sectors;
+        }
+
+
+		#endregion
+
+		#region ================== Properties
+
+		// Just keep the base mode button checked
+		public override string EditModeButtonName { get { return General.Editing.PreviousStableMode.Name; } }
+
+		#endregion
+
+		#region ================== Constructor / Disposer
+
+		#endregion
+
+		#region ================== Methods
+
+		private void InitializeCatmullRomSplines()
+		{
+			List<Linedef> sourceld = new List<Linedef>(General.Map.Map.GetSelectedLinedefs(true));
+			Line2D innerline, outerline;
+			CatmullRomSplineData innerspline, outerspline;
+			CatmullRomSplineGroup splinegroup;
+			
+			numcontrolpoints = stairsectorbuilderform.NumControlPoints;
+
+			if (General.Map.Map.GetSelectedLinedefs(true).Count <= 0)
+			{
+				return;
+			}
+
+			if (catmullromsplinegroups == null)
+				catmullromsplinegroups = new List<CatmullRomSplineGroup>();
+			else
+				catmullromsplinegroups.Clear();
+
+			for (int l1 = 0; l1 < sourceld.Count - 1; l1++)
+			{
+				int l2 = l1 + 1;
+				Vector2D s1, s2, e1, e2;
+
+				s1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].End.Position : sourceld[l1].Start.Position;
+				e1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].Start.Position : sourceld[l1].End.Position;
+				s2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].End.Position : sourceld[l2].Start.Position;
+				e2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].Start.Position : sourceld[l2].End.Position;
+
+				//innerline = new Line2D(sourceld[l2].Start.Position, sourceld[l1].Start.Position);
+				//outerline = new Line2D(sourceld[l1].End.Position, sourceld[l2].End.Position);
+
+				innerline = new Line2D(s2, s1);
+				outerline = new Line2D(e1, e2);
+
+				innerspline = new CatmullRomSplineData();
+				innerspline.controlpoints = new List<Vector2D>();
+
+				innerspline.line = innerline;
+				innerspline.controlpoints.Add(new Vector2D(innerline.v1));
+
+				for (int k = 1; k <= numcontrolpoints - 2; k++)
+					innerspline.controlpoints.Add(new Vector2D(innerline.GetCoordinatesAt(1.0f / (numcontrolpoints - 1) * k)));
+
+				innerspline.controlpoints.Add(new Vector2D(innerline.v2));
+
+				ComputeTangents(ref innerspline);
+
+				outerspline = new CatmullRomSplineData();
+				outerspline.controlpoints = new List<Vector2D>();
+
+				outerspline.line = outerline;
+				outerspline.controlpoints.Add(new Vector2D(outerline.v1));
+
+				for (int k = 1; k <= numcontrolpoints - 2; k++)
+					outerspline.controlpoints.Add(new Vector2D(outerline.GetCoordinatesAt(1.0f / (numcontrolpoints - 1) * k)));
+
+				outerspline.controlpoints.Add(new Vector2D(outerline.v2));
+
+				ComputeTangents(ref outerspline);
+
+				splinegroup = new CatmullRomSplineGroup();
+				splinegroup.splines = new CatmullRomSplineData[2];
+				splinegroup.splines[INNER_SPLINE] = innerspline;
+				splinegroup.splines[OUTER_SPLINE] = outerspline;
+				splinegroup.sourcelinedef = sourceld[l1];
+
+				catmullromsplinegroups.Add(splinegroup);
+			}
+		}
+
+		// Modifies the number of control points on the splines
+		public void ModifyControlpointNumber(int num)
+		{
+			if (catmullromsplinegroups == null) return;
+
+			for(int crsg=0; crsg < catmullromsplinegroups.Count;crsg++) {
+				for (int s = 0; s < 2; s++)
+				{
+					List<Vector2D> verts = GenerateCatmullRom(catmullromsplinegroups[crsg].splines[s], num-1);
+
+					catmullromsplinegroups[crsg].splines[s].controlpoints.Clear();
+
+					foreach (Vector2D v in verts)
+					{
+						catmullromsplinegroups[crsg].splines[s].controlpoints.Add(v);
+					}
+
+					// catmullromsplinegroups[crsg].splines[s].controlpoints.Add(catmullromsplinegroups[crsg].splines[s].line.v1);
+
+					// New tangents of the control points have to be calculated
+					ComputeTangents(ref catmullromsplinegroups[crsg].splines[s]);
+				}
+			}
+		}
+
+		// Create the sector data depending on the selected tab
+		public void UpdateVertexData()
+		{
+			// Straight stair
+			if (stairsectorbuilderform.Tabs.SelectedIndex == 0)
+			{
+				if (General.Map.Map.SelectedSectorsCount == 0)
+				{
+					stairsectors = CreateStraightStairSectorsFromLines(new List<Linedef>(General.Map.Map.GetSelectedLinedefs(true)));
+				}
+				else
+				{
+					stairsectors = CreateStraightStairSectorsFromSectors(new List<Sector>(General.Map.Map.GetSelectedSectors(true)));
+				}
+			}
+			// Curved stair
+			else if (stairsectorbuilderform.Tabs.SelectedIndex == 1)
+			{
+				stairsectors = CreateCurvedStairSectors();
+			}
+			// Catmull Rom stair
+			else if (stairsectorbuilderform.Tabs.SelectedIndex == 2)
+			{
+				if (oldflipping != stairsectorbuilderform.Flipping)
+				{
+					oldflipping = stairsectorbuilderform.Flipping;
+					InitializeCatmullRomSplines();
+				}
+
+				exactsplines = new List<List<Vector2D>>();
+                stairsectors = CreateCatmullRomStairSectors();
+			}
+
+			if (stairsectors.Count > 0)
+			{
+				stairsectorbuilderform.OriginalCeilingBase = stairsectors[0].ceilingheight;
+				stairsectorbuilderform.OriginalFloorBase = stairsectors[0].floorheight;
+			}
+		}
+
+		private bool CheckConnectedLines()
+		{
+			//List<Linedef> sourceld = new List<Linedef>();
+
+			//if (General.Map.Map.GetSelectedLinedefs(true).Count <= 0 && General.Map.Map.SelectedSectorsCount <= 0)
+			//{
+			//    return false;
+			//}
+
+			// connectedlinedefs = new List<ConnectedLinedefs>();
+
+			//foreach (Linedef ld in General.Map.Map.GetSelectedLinedefs(true))
+			//{
+			//    sourceld.Add(ld);
+			//}
+
+			// GetConnectedLines(ref sourceld, -1);
+
+			return true;
+		}
+
+		private void GetConnectedLines(ref List<Linedef> sourceld, int sector)
+		{
+			List<Linedef> connectedld = new List<Linedef>();
+			Linedef currentld = null;
+
+			connectedlinedefs = new List<ConnectedLinedefs>();
+
+			while (sourceld.Count > 0)
+			{
+				// List is empty
+				if (connectedld.Count == 0)
+				{
+					connectedld.Add(sourceld[0]);
+					currentld = sourceld[0];
+					sourceld.RemoveAt(0);
+				}
+
+				// Find all connected linedefs starting from the start of the current linedef
+				while (currentld != null)
+				{
+					bool found = false;
+
+					foreach (Linedef ld in sourceld)
+					{
+						// if (currentld.Start.Linedefs.Contains(ld))
+						if(ld.Start == currentld.Start || ld.End == currentld.Start)
+						{
+							// add the connected linedef to the beginning of the list
+							connectedld.Insert(0, ld);
+							currentld = ld;
+							sourceld.Remove(ld);
+							found = true;
+							break;
+						}
+					}
+
+					if (found == false) currentld = null;
+				}
+
+				currentld = connectedld[connectedld.Count - 1];
+
+				// Find all connected linedefs starting from the end of the current linedef
+				while (currentld != null)
+				{
+					bool found = false;
+
+					foreach (Linedef ld in sourceld)
+					{
+						// if (currentld.End.Linedefs.Contains(ld))
+						if (ld.Start == currentld.End || ld.End == currentld.End)
+						{
+							// add the connected linedef to the end of the list
+							connectedld.Add(ld);
+							currentld = ld;
+							sourceld.Remove(ld);
+							found = true;
+							break;
+						}
+					}
+
+					if (found == false) currentld = null;
+				}
+
+				ConnectedLinedefs cld = new ConnectedLinedefs();
+				cld.linedefs = new List<Linedef>();
+
+				cld.sector = sector;
+
+				foreach (Linedef ld in connectedld)
+				{
+					cld.linedefs.Add(ld);
+				}
+
+				if (General.Map.Map.SelectedLinedefsCount > 0)
+				{
+					foreach (Linedef ld in General.Map.Map.GetSelectedLinedefs(true))
+					{
+						if (cld.linedefs.Contains(ld))
+						{
+							cld.firstlinedef = ld;
+							break;
+						}
+					}
+
+					// if(connectedvertices[0] == cld.firstlinedef.End.Position && connectedvertices[connectedvertices.Count-1] == cld.firstlinedef.Start.Position) {
+				}
+				else
+				{
+					cld.closed = true;
+					cld.firstlinedef = cld.linedefs[cld.linedefs.Count-1];
+				}
+
+				connectedlinedefs.Add(cld);
+				connectedld.Clear();
+			}
+		}
+
+		// Computes the tangents of the control points of a Catmull Rom spline
+		private void ComputeTangents(ref CatmullRomSplineData spline)
+		{
+			float f = 2.0f;
+			int i;
+
+			if (spline.tangents == null)
+				spline.tangents = new List<Vector2D>();
+			else
+				spline.tangents.Clear();
+
+			// Special case for the first control point
+			// spline.tangents.Add(new Vector2D((spline.controlpoints[1] - spline.controlpoints[0]) / f));
+			spline.tangents.Add(new Vector2D(spline.controlpoints[1] - spline.controlpoints[0]));
+
+			for (i=1; i < spline.controlpoints.Count - 1; i++)
+			{
+				spline.tangents.Add(new Vector2D((spline.controlpoints[i + 1] - spline.controlpoints[i - 1]) / f));
+			}
+
+			// Special case for the last control point
+			//spline.tangents.Add(new Vector2D((spline.controlpoints[i] - spline.controlpoints[i - 1]) / f));
+			spline.tangents.Add(new Vector2D(spline.controlpoints[i] - spline.controlpoints[i - 1]));
+		}
+
+		// Creates sectors for a curved stair
+        private List<StairInfo> CreateCurvedStairSectors()
+		{
+			List<List<Vector2D>> secs = new List<List<Vector2D>>();
+			List<Vector2D> newsec;
+			List<DrawnVertex> dvl = new List<DrawnVertex>();
+			List<Vector2D> innervertices, outervertices;
+			Line2D innerline, outerline;
+			float innerangle, outerangle;
+			int innervertexmultiplier = stairsectorbuilderform.InnerVertexMultiplier;
+			int outervertexmultiplier = stairsectorbuilderform.OuterVertexMultiplier;
+			bool clockwise;
+			int numsteps = (int)stairsectorbuilderform.NumberOfSectors;
+			uint depth = stairsectorbuilderform.SectorDepth;
+			List<Linedef> sourceld = new List<Linedef>();
+            List<StairInfo> stairinfo = new List<StairInfo>();
+            StairInfo si = new StairInfo();
+            List<List<Vector2D>> sisecs = new List<List<Vector2D>>();
+
+			secs.Clear();
+
+			if (General.Map.Map.GetSelectedLinedefs(true).Count <= 0)
+			{
+				return null;
+			}
+
+			// Add all selected linedefs to a source linedef list
+			foreach (Linedef ld in General.Map.Map.GetSelectedLinedefs(true))
+			{
+				sourceld.Add(ld);
+			}
+
+			// Go through all source linedefs
+			for (int l1 = 0; l1 < sourceld.Count - 1; l1++)
+			{
+				// A curve will always be made in the order the lines were selected
+				int l2 = l1 + 1;
+				Vector2D s1, s2, e1, e2;
+				float distance = 128;
+				bool fixedcurve = true;
+
+				s1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].End.Position : sourceld[l1].Start.Position;
+				e1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].Start.Position : sourceld[l1].End.Position;
+				s2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].End.Position : sourceld[l2].Start.Position;
+				e2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].Start.Position : sourceld[l2].End.Position;
+
+				if (Vector2D.Distance(s1, s2) < Vector2D.Distance(e1, e2))
+				{
+					clockwise = true;
+					innerline = new Line2D(s2, s1);
+					outerline = new Line2D(e1, e2);
+				}
+				else // Counter clockwise
+				{
+					clockwise = false;
+					innerline = new Line2D(e1, e2);
+					outerline = new Line2D(s2, s1);
+				}
+
+				// Compute the angle of the inner and outer line
+				if (sourceld[l1].Angle == sourceld[l2].Angle && !(stairsectorbuilderform.Flipping == 1) && !(stairsectorbuilderform.Flipping == 2))
+				{
+					innerangle = 1;
+					outerangle = 1;
+					distance = 0;
+					fixedcurve = false;
+				}
+				else
+				{
+					if (clockwise)
+					{
+						innerangle = outerangle = sourceld[l1].Angle - sourceld[l2].Angle;
+						if (innerangle < 0.0f) innerangle += Angle2D.PI2;
+						if (outerangle < 0.0f) outerangle += Angle2D.PI2;
+					}
+					else
+					{
+						innerangle = outerangle = sourceld[l2].Angle - sourceld[l1].Angle;
+						if (innerangle < 0.0f) innerangle += Angle2D.PI2;
+						if (outerangle < 0.0f) outerangle += Angle2D.PI2;
+					}
+
+					if (stairsectorbuilderform.Flipping != 0)
+					{
+						if (sourceld[l1].Angle == sourceld[l2].Angle)
+						{
+							if (stairsectorbuilderform.Flipping == 1)
+							{
+								innerangle = Math.Abs(innerangle - Angle2D.PI);
+								outerangle = Math.Abs(outerangle - Angle2D.PI);
+							}
+							else if (stairsectorbuilderform.Flipping == 2)
+							{
+								innerangle -= Angle2D.PI;
+								outerangle -= Angle2D.PI;
+							}
+						}
+						else
+						{
+							innerangle = Math.Abs(innerangle - Angle2D.PI2);
+							outerangle = Math.Abs(outerangle - Angle2D.PI2);
+						}
+					}
+				}
+
+				// Generate the vertices on the curve, and add the start and end vertices of the inner line to the list
+				innervertices = GenerateCurve(innerline, numsteps * innervertexmultiplier - 1, innerangle, false, distance, fixedcurve);
+				innervertices.Insert(0, innerline.v1);
+				innervertices.Add(innerline.v2);
+
+				// Generate the vertices on the curve, and add the start and end vertices of the outer line to the list
+				outervertices = GenerateCurve(outerline, numsteps * outervertexmultiplier - 1, outerangle, true, distance, fixedcurve);
+				outervertices.Insert(0, outerline.v1);
+				outervertices.Add(outerline.v2);
+
+                // If the vertices were created in counter-clockwise order turn them into clockwise order
+                if (!clockwise)
+                {
+                    List<Vector2D> tmpvertices;
+                    int tmpmultiplier;
+
+                    tmpvertices = innervertices;
+                    tmpmultiplier = innervertexmultiplier;
+
+                    innervertices = outervertices;
+                    outervertices = tmpvertices;
+
+                    innervertexmultiplier = outervertexmultiplier;
+                    outervertexmultiplier = tmpmultiplier;
+                }
+
+				// Create the sectors from the created vertices
+			    for (int i = 0; i < numsteps; i++)
+			    {
+					newsec = new List<Vector2D>();
+
+					// Depending on the outer and innter vertex multipliers more
+					// vertices from the curve have to be added to the new sector
+                    for (int k = 0; k <= outervertexmultiplier; k++)
+                    {
+                        newsec.Add(outervertices[i * outervertexmultiplier + k]);
+                    }
+
+					// Depending on the outer and innter vertex multipliers more
+					// vertices from the curve have to be added to the new sector
+                    for (int k = 0; k <= innervertexmultiplier; k++)
+                    {
+                        newsec.Add(innervertices[(numsteps - 1 - i) * innervertexmultiplier + k]);
+                    }
+
+					// Don't forget the add the first vertex again, to actually have
+					// a whole sector
+                    newsec.Add(outervertices[i * outervertexmultiplier]);
+
+					sisecs.Add(newsec);
+			    }
+			}
+
+			GetSetTextureAndHeightInfo(sourceld[0], out si);
+
+            si.sectors = sisecs;
+
+            stairinfo.Add(si);
+
+			stairsectorbuilderform.StepMultiplier = sourceld.Count - 1;
+
+			return stairinfo;
+		}
+
+		private List<Vector2D> GenerateCurve(Line2D line, int vertices, float angle, bool backwards)
+		{
+			return GenerateCurve(line, vertices, angle, backwards, 128, true);
+		}
+
+		// This generates the vertices to split the line with, from start to end
+		private List<Vector2D> GenerateCurve(Line2D line, int vertices, float angle, bool backwards, float distance, bool fixedcurve)
+		{
+
+			// Make list
+			List<Vector2D> points = new List<Vector2D>(vertices);
+
+			//Added by Anders Åstrand 2008-05-18
+			//The formulas used are taken from http://mathworld.wolfram.com/CircularSegment.html
+			//c and theta are known (length of line and angle parameter). d, R and h are
+			//calculated from those two
+			//If the curve is not supposed to be a circular segment it's simply deformed to fit
+			//the value set for distance.
+
+			//The vertices are generated to be evenly distributed (by angle) along the curve
+			//and lastly they are rotated and moved to fit with the original line
+
+			//calculate some identities of a circle segment (refer to the graph in the url above)
+			double c = line.GetLength();
+			double theta = angle;
+
+			double d = (c / Math.Tan(theta / 2)) / 2;
+			double R = d / Math.Cos(theta / 2);
+			double h = R - d;
+
+			double yDeform = fixedcurve ? 1 : distance / h;
+			if (backwards)
+				yDeform = -yDeform;
+
+			double a, x, y;
+			Vector2D vertex;
+
+			for (int v = 1; v <= vertices; v++)
+			{
+				//calculate the angle for this vertex
+				//the curve starts at PI/2 - theta/2 and is segmented into vertices+1 segments
+				//this assumes the line is horisontal and on y = 0, the point is rotated and moved later
+
+				a = (Math.PI - theta) / 2 + v * (theta / (vertices + 1));
+
+				//calculate the coordinates of the point, and distort the y coordinate
+				//using the deform factor calculated above
+				x = Math.Cos(a) * R;
+				y = (Math.Sin(a) * R - d) * yDeform;
+
+				//rotate and transform to fit original line
+				vertex = new Vector2D((float)x, (float)y).GetRotated(line.GetAngle() + Angle2D.PIHALF);
+				vertex = vertex.GetTransformed(line.GetCoordinatesAt(0.5f).x, line.GetCoordinatesAt(0.5f).y, 1, 1);
+
+				points.Add(vertex);
+			}
+
+
+			// Done
+			return points;
+		}
+
+
+		// Creates stair sectors from two Catmull Rom splines
+        private List<StairInfo> CreateCatmullRomStairSectors()
+		{
+			List<List<Vector2D>> secs = new List<List<Vector2D>>();
+			List<Vector2D> newsec;
+			List<Vector2D> innervertices, outervertices;
+			int innervertexmultiplier = stairsectorbuilderform.InnerVertexMultiplier;
+			int outervertexmultiplier = stairsectorbuilderform.OuterVertexMultiplier;
+			int numsteps = (int)stairsectorbuilderform.NumberOfSectors;
+			List<List<Vector2D>> sisecs = new List<List<Vector2D>>();
+
+            List<StairInfo> stairinfo = new List<StairInfo>();
+            StairInfo si = new StairInfo();
+
+			secs.Clear();
+
+			foreach(CatmullRomSplineGroup crsg in catmullromsplinegroups)
+			{
+				// Generate the vertices for both the inner and outer spline
+				innervertices = GenerateCatmullRom(crsg.splines[INNER_SPLINE], numsteps * innervertexmultiplier);
+				outervertices = GenerateCatmullRom(crsg.splines[OUTER_SPLINE], numsteps * outervertexmultiplier);
+
+				// Create the data to build complete sectors from the splines
+				for (int i = 0; i < numsteps; i++)
+				{
+					newsec = new List<Vector2D>();
+
+					// Depending on the outer and innter vertex multipliers more
+					// vertices from the splines have to be added to the new sector
+					for (int k = 0; k <= outervertexmultiplier; k++)
+					{
+						newsec.Add(outervertices[i * outervertexmultiplier + k]);
+					}
+
+					for (int k = 0; k <= innervertexmultiplier; k++)
+					{
+						newsec.Add(innervertices[(numsteps - 1 - i) * innervertexmultiplier + k]);
+					}
+
+					// Don't forget the add the first vertex again, to actually have
+					// a whole sector
+					newsec.Add(outervertices[i * outervertexmultiplier]);
+
+					sisecs.Add(newsec);
+				}
+			}
+
+			GetSetTextureAndHeightInfo(catmullromsplinegroups[0].sourcelinedef, out si);
+
+			si.sectors = sisecs;
+
+			stairinfo.Add(si);
+
+			return stairinfo;
+		}
+
+		private List<StairInfo> CreateStraightStairSectorsFromSectors(List<Sector> selectedsectors)
+		{
+			List<StairInfo> stairinfo = new List<StairInfo>();
+
+			foreach (Sector s in selectedsectors)
+			{
+				List<Linedef> linedefs = new List<Linedef>();
+				List<Linedef> fliplinedefs = new List<Linedef>();
+
+				foreach (Sidedef sd in s.Sidedefs)
+				{
+					if (sd.Line.Front.Sector.Index != s.Index)
+					{
+						fliplinedefs.Add(sd.Line);
+						sd.Line.FlipVertices();
+						sd.Line.FlipSidedefs();
+					}
+
+					linedefs.Add(sd.Line);
+				}
+
+				stairinfo.AddRange(CreateStraightStairSectorsFromLines(linedefs));
+
+				foreach (Linedef ld in fliplinedefs)
+				{
+					ld.FlipSidedefs();
+					ld.FlipVertices();
+				}
+			}
+
+			return stairinfo;
+		}
+
+		private List<StairInfo> CreateStraightStairSectorsFromLines(List<Linedef> selectedlinedefs)
+		{
+            List<StairInfo> stairinfo = new List<StairInfo>();
+			uint numsteps = stairsectorbuilderform.NumberOfSectors;
+			uint depth = stairsectorbuilderform.SectorDepth;
+            int spacing = stairsectorbuilderform.Spacing;
+			List<Linedef> sourceld = new List<Linedef>(selectedlinedefs);
+            StairInfo si = new StairInfo();
+
+			GetConnectedLines(ref selectedlinedefs, -1);
+
+            // Build an independend set of steps from each selected line
+			if (!stairsectorbuilderform.SingleSectors.Checked)
+			{
+				Vector2D direction = new Vector2D();
+				Vector2D v1, v2;
+
+                // Go through each selected line
+				for (int k = 0; k < sourceld.Count; k++)
+				{
+                    List<List<Vector2D>> sisecs = new List<List<Vector2D>>();
+
+                    // Get the direction to build the stair to. "Flip" the vector depending on the selected build direction modifier
+                    direction = sourceld[k].Line.GetPerpendicular().GetNormal() * (stairsectorbuilderform.SideFront ? -1 : 1);
+
+					for (int i = 0; i < numsteps; i++)
+					{
+						List<DrawnVertex> vertices = new List<DrawnVertex>();
+						Vector2D v3, v4;
+						List<Vector2D> newsec = new List<Vector2D>();
+
+                        // Compute the position of the vertices that form the line opposite the source line
+                        v1 = sourceld[k].Start.Position + direction * depth * i;
+                        v2 = sourceld[k].End.Position + direction * depth * i;
+						v3 = v1 + direction * depth;
+                        v4 = v2 + direction * depth;
+
+                        // Apply spacing to each vertex
+                        if (spacing > 0)
+                        {
+                            v1 += (direction * spacing) * i;
+                            v2 += (direction * spacing) * i;
+                            v3 += (direction * spacing) * i;
+                            v4 += (direction * spacing) * i;
+
+                        }
+
+                        // v1 is added twice so it will actually draw a complete sector
+						newsec.Add(v1);
+						newsec.Add(v3);
+						newsec.Add(v4);
+						newsec.Add(v2);
+						newsec.Add(v1);
+
+						sisecs.Add(newsec);
+					}
+
+					GetSetTextureAndHeightInfo(sourceld[k], out si);
+
+                    si.sectors = sisecs;
+
+                    stairinfo.Add(si);
+				}
+			}
+			else if(stairsectorbuilderform.SingleSectors.Checked)
+			{
+				Vector2D direction = new Vector2D();
+				Vector2D globaldirection = new Vector2D();
+				bool closed = false;
+
+				foreach (ConnectedLinedefs cld in connectedlinedefs)
+				{
+					List<Vector2D> connectedvertices = new List<Vector2D>();
+					List<Vector2D> connecteddirections = new List<Vector2D>();
+					List<double> connectedlength = new List<double>();
+                    List<List<Vector2D>> sisecs = new List<List<Vector2D>>();
+
+					globaldirection = Vector2D.FromAngle(cld.firstlinedef.Angle + Angle2D.DegToRad(stairsectorbuilderform.SideFront ? -90.0f : 90.0f));
+
+					for(int i=0; i < cld.linedefs.Count; i++)
+					{
+						if (cld.sector >= 0)
+						{
+							if (cld.linedefs[i].Front.Sector.Index == cld.sector)
+							{
+								if (!connectedvertices.Contains(cld.linedefs[i].End.Position))
+									connectedvertices.Add(cld.linedefs[i].End.Position);
+
+								if (!connectedvertices.Contains(cld.linedefs[i].Start.Position))
+									connectedvertices.Add(cld.linedefs[i].Start.Position);
+
+							}
+							else
+							{
+								if (!connectedvertices.Contains(cld.linedefs[i].Start.Position))
+									connectedvertices.Add(cld.linedefs[i].Start.Position);
+
+								if (!connectedvertices.Contains(cld.linedefs[i].End.Position))
+									connectedvertices.Add(cld.linedefs[i].End.Position);
+							}
+						}
+						else
+						{
+							if (!connectedvertices.Contains(cld.linedefs[i].Start.Position))
+								connectedvertices.Add(cld.linedefs[i].Start.Position);
+
+							if (!connectedvertices.Contains(cld.linedefs[i].End.Position))
+								connectedvertices.Add(cld.linedefs[i].End.Position);
+						}
+					}
+
+					if (cld.sector >= 0)
+					{
+						closed = true;
+					}
+					else
+					{
+						if (connectedvertices[0] == cld.firstlinedef.End.Position && connectedvertices[connectedvertices.Count - 1] == cld.firstlinedef.Start.Position)
+							closed = true;
+					}
+				
+					for (int i = 0; i < connectedvertices.Count; i++)
+					{
+						if (i == 0 && closed == false)
+						{
+							connecteddirections.Add(Vector2D.FromAngle(Vector2D.GetAngle(connectedvertices[0], connectedvertices[1]) - Angle2D.DegToRad(stairsectorbuilderform.SideFront ? -90.0f : 90.0f)));
+							connectedlength.Add(depth);
+						}
+						else if (i == connectedvertices.Count - 1 && closed == false)
+						{
+							connecteddirections.Add(Vector2D.FromAngle(Vector2D.GetAngle(connectedvertices[i], connectedvertices[i - 1]) + Angle2D.DegToRad(stairsectorbuilderform.SideFront ? -90.0f : 90.0f)));
+							connectedlength.Add(depth);
+						}
+						else
+						{
+							Vector2D v1;
+							Vector2D v2;
+
+							if (closed == true && i == 0)
+							{
+								v1 = new Line2D(connectedvertices[1], connectedvertices[0]).GetPerpendicular();
+								v2 = new Line2D(connectedvertices[0], connectedvertices[connectedvertices.Count - 1]).GetPerpendicular();
+							}
+							else if(closed == true && i == connectedvertices.Count - 1)
+							{
+								v1 = new Line2D(connectedvertices[0], connectedvertices[i]).GetPerpendicular();
+								v2 = new Line2D(connectedvertices[i], connectedvertices[i - 1]).GetPerpendicular();
+							}
+							else
+							{
+								v1 = new Line2D(connectedvertices[i + 1], connectedvertices[i]).GetPerpendicular();
+								v2 = new Line2D(connectedvertices[i], connectedvertices[i - 1]).GetPerpendicular();
+							}
+
+							double a = (v1.GetNormal() + v2.GetNormal()).GetAngle();
+							double b = a - v1.GetNormal().GetAngle();
+
+							double opl = Math.Tan(b) * (double)depth;
+							double distance = Math.Sqrt(depth * depth + opl * opl);
+							Vector2D v3 = Vector2D.FromAngle((float)a);
+
+							connecteddirections.Add(Vector2D.FromAngle(v3.GetAngle() + Angle2D.DegToRad(stairsectorbuilderform.SideFront ? 0.0f : 180.0f)));
+							connectedlength.Add(distance);
+						}
+					}
+
+					for (int i = 0; i < numsteps; i++)
+					{
+						List<Vector2D> newsec = new List<Vector2D>();
+						float length;
+
+						if (closed == false)
+						{
+							for (int k = 0; k < connectedvertices.Count; k++)
+							{
+								if (!stairsectorbuilderform.SingleDirection.Checked)
+								{
+									direction = connecteddirections[k];
+									length = (float)connectedlength[k];
+								}
+								else
+								{
+									direction = globaldirection;
+									length = depth;
+								}
+
+                                newsec.Add(connectedvertices[k] + direction * length * i + (direction * spacing) * i);
+							}
+						}
+
+						for (int k = connectedvertices.Count - 1; k >= 0; k--)
+						{
+							if (!stairsectorbuilderform.SingleDirection.Checked)
+							{
+								direction = connecteddirections[k];
+								length = (float)connectedlength[k];
+							}
+							else
+							{
+								direction = globaldirection;
+								length = depth;
+							}
+
+                            newsec.Add(connectedvertices[k] + direction * length * (i + 1) + (direction * spacing) * i);
+						}
+
+						if(closed == false)
+                            newsec.Add(connectedvertices[0] + direction * depth * i + (direction * spacing) * i);
+						else
+							newsec.Add(newsec[0]);
+
+						// If the steps are drawn on the back side the vertices are in counter-clockwise
+						// order, so reverse their order
+						if (!stairsectorbuilderform.SideFront) newsec.Reverse();
+
+						sisecs.Add(newsec);
+					}
+
+					GetSetTextureAndHeightInfo(cld.firstlinedef, out si);
+
+                    si.sectors = sisecs;
+
+                    stairinfo.Add(si);
+				}
+			}
+
+			stairsectorbuilderform.StepMultiplier = 1;
+
+			return stairinfo;
+		}
+
+		// Generats the Catmull Rom spline from the given control points and their tangents.
+		// Returns a list of Vector2D.
+		// The line between two control points is a "section". There is always one section
+		// less than there are control points.
+		// A "hop" is the distance between two to-be-created vertices. The distance is not
+		// in map units, but a relative indication where on the line between two control
+		// points the vertex will be.
+		// First it is calculated in which section the vertex will be in, then the relative
+		// position on the line between the control points of this section is calculated.
+		// This value will be from 0.0 to 0.99999...
+		// A workaround has to be done for the last vertex, which will inevitably be exactly
+		// on the last control point, thus computing the wrong section and relative position
+		private List<Vector2D> GenerateCatmullRom(CatmullRomSplineData crsd, int numverts)
+		{
+			List<Vector2D> vertices = new List<Vector2D>();
+			int sections = crsd.controlpoints.Count - 1;
+			double hop = (double)sections / numverts;
+			float distance = 0.0f;
+			float unithop = 0;
+			List<float> cpdistance = new List<float>();
+
+			// Compute the length of the whole spline and the length of the parts on the
+			// spline between the control points
+			for (int i = 0; i < crsd.controlpoints.Count-1; i++)
+			{
+				int h = (int)Vector2D.Distance(crsd.controlpoints[i], crsd.controlpoints[i + 1]);
+				float dist = 0;
+				List<Vector2D> dc = new List<Vector2D>();
+
+				Vector2D p0 = crsd.controlpoints[i];
+				Vector2D p1 = crsd.controlpoints[i + 1];
+				Vector2D t0 = crsd.tangents[i];
+				Vector2D t1 = crsd.tangents[i + 1];
+
+				for (int k = 0; k <= h; k++)
+				{
+					dc.Add(Tools.HermiteSpline(p0, t0, p1, t1, (float)(((float)k/(float)h))));
+				}
+
+				for (int k = 0; k < h; k++)
+				{
+					dist += Vector2D.Distance(dc[k], dc[k + 1]);
+				}
+
+				distance += dist;
+
+				cpdistance.Add(dist);
+
+				exactsplines.Add(dc);
+			}
+
+			unithop = distance / numverts;
+
+			for (int i = 0; i <= numverts; i++)
+			{
+				int s = 0;
+				float u;
+				float distancefromstart = i * unithop;
+				float max = 0.0f;
+
+				// Find out what section the vertex is in
+				while (max < distancefromstart)
+				{
+					max += cpdistance[s];
+					if (max < distancefromstart) s++;
+
+					// Work around for rounding errors
+					if (s > cpdistance.Count-1)
+					{
+						s = cpdistance.Count - 1;
+						max = distancefromstart;
+					}
+				}
+
+				// Compute the u component with special cases for the first
+				// and last vertex
+				if (distancefromstart == 0)
+					u = 0.0f;
+				else if (distancefromstart == distance)
+					u = 1.0f;
+				else
+					u = 1.0f - ((max - distancefromstart) / cpdistance[s]);
+
+				Vector2D p0 = crsd.controlpoints[s];
+				Vector2D p1 = crsd.controlpoints[s + 1];
+				Vector2D t0 = crsd.tangents[s];
+				Vector2D t1 = crsd.tangents[s + 1];
+				vertices.Add(Tools.HermiteSpline(p0, t0, p1, t1, u));
+			}
+
+			// OLD CODE
+			//for (int x = 0; x <= numverts; x++)
+			//{
+			//    // Compute the section the vertex will be in
+			//    int s = (int)(x * hop);
+			//    double u;
+
+			//    // Workaround for the last vertex
+			//    if ((x * hop) - s < 0.00001f && s == sections)
+			//    {
+			//        u = 1.0f;
+			//        s--;
+			//    }
+			//    else
+			//    {
+			//        //u = (summe - x * unithop) / Vector2D.Distance(crsd.controlpoints[cs], crsd.controlpoints[cs+1]);
+			//        u = (x * hop) - s;
+			//    }
+
+			//    if (u > 1.0f) u = 1.0f;
+
+			//    Vector2D p0 = crsd.controlpoints[s];
+			//    Vector2D p1 = crsd.controlpoints[s + 1];
+			//    Vector2D t0 = crsd.tangents[s];
+			//    Vector2D t1 = crsd.tangents[s + 1];
+			//    vertices.Add(Tools.HermiteSpline(p0, t0, p1, t1, (float)u));
+			//}
+
+			return vertices;
+		}
+
+		// Turns a position into a DrawnVertex and returns it
+		private DrawnVertex SectorVertex(float x, float y)
+		{
+			DrawnVertex v = new DrawnVertex();
+
+			v.stitch = true;
+			v.stitchline = true;
+			v.pos = new Vector2D((float)Math.Round(x, General.Map.FormatInterface.VertexDecimals), (float)Math.Round(y, General.Map.FormatInterface.VertexDecimals));
+
+			return v;
+		}
+
+		private DrawnVertex SectorVertex(Vector2D v)
+		{
+			return SectorVertex(v.x, v.y);
+		}
+
+		// Remember info for floor and ceiling height. The info is retrieved
+		// from either front or back of the source line, depending in what
+		// direction the it should build
+		// Also set the upper/lower textures to use if they aren't already
+		private void GetSetTextureAndHeightInfo(Linedef ld, out StairInfo siout)
+		{
+			StairInfo si = new StairInfo();
+			Sidedef primary;
+			Sidedef secondary;
+
+			GetSetBaseHeights(ld);
+
+			if (stairsectorbuilderform.SideFront)
+			{
+				if (ld.Front == null)
+				{
+					primary = ld.Back;
+					secondary = ld.Front;
+				}
+				else
+				{
+					primary = ld.Front;
+					secondary = ld.Back;
+				}
+			}
+			else
+			{
+				if (ld.Back == null)
+				{
+					primary = ld.Front;
+					secondary = ld.Back;
+				}
+				else
+				{
+					primary = ld.Back;
+					secondary = ld.Front;
+				}
+			}
+
+			si.ceilingheight = primary.Sector.CeilHeight;
+			si.floorheight = primary.Sector.FloorHeight;
+
+			if (stairsectorbuilderform.UpperTextureTexture == "")
+				stairsectorbuilderform.UpperTextureTexture = (secondary == null) ? primary.MiddleTexture : primary.HighTexture;
+
+			if (stairsectorbuilderform.LowerTextureTexture == "")
+				stairsectorbuilderform.LowerTextureTexture = (secondary == null) ? primary.MiddleTexture : primary.LowTexture;
+
+			siout = si;
+		}
+
+		private void GetSetBaseHeights(Linedef ld)
+		{
+			if (baseheightset) return;
+
+			baseheightset = true;
+
+			if (stairsectorbuilderform.SideFront)
+			{
+				if (ld.Back == null)
+				{
+					stairsectorbuilderform.CeilingBase = ld.Front.Sector.CeilHeight;
+					stairsectorbuilderform.FloorBase = ld.Front.Sector.FloorHeight;
+				}
+				else
+				{
+					stairsectorbuilderform.CeilingBase = ld.Back.Sector.CeilHeight;
+					stairsectorbuilderform.FloorBase = ld.Back.Sector.FloorHeight;
+				}
+			}
+			else
+			{
+				stairsectorbuilderform.CeilingBase = ld.Front.Sector.CeilHeight;
+				stairsectorbuilderform.FloorBase = ld.Front.Sector.FloorHeight;
+			}
+		}
+
+		#endregion
+
+		#region ================== Events
+
+		public override void OnHelp()
+		{
+			General.ShowHelp("e_curvelinedefs.html");
+		}
+
+		// Cancelled
+		public override void OnCancel()
+		{
+			// Cancel base class
+			base.OnCancel();
+
+			// stairsectorbuilderform.Close();
+
+			// Return to base mode
+			General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
+		}
+
+		// Mode engages
+		public override void OnEngage()
+		{
+			base.OnEngage();
+
+            // If no lines are selected nothing can be done, so exit this mode immediately
+            if (General.Map.Map.SelectedLinedefsCount == 0 && General.Map.Map.SelectedSectorsCount == 0)
+            {
+                General.Interface.DisplayStatus(StatusType.Warning, "No lines or sectors selected.");
+                General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
+                return;
+            }
+
+			renderer.SetPresentation(Presentation.Standard);
+
+			linesconnected = CheckConnectedLines();
+
+			LoadPrefabs();
+
+            // stairsectorbuilderform = BuilderPlug.Me.StairSectorBuilderForm;
+
+            stairsectorbuilderform = new StairSectorBuilderForm();
+
+			InitializeCatmullRomSplines();
+
+			selectedcontrolpoint.cp = -1;
+			selectedcontrolpoint.crsd = -1;
+			selectedcontrolpoint.crsg = -1;
+
+			stairsectorbuilderform.SingleSectors.Enabled = linesconnected ? true : false;
+			stairsectorbuilderform.DistinctBaseHeights.Checked = General.Map.Map.SelectedLinedefsCount == 1 ? false : true;
+
+
+			// Show toolbox window
+            // stairsectorbuilderform.Show();
+            stairsectorbuilderform.Show((Form)General.Interface);
+		}
+
+		// Disenagaging
+		public override void OnDisengage()
+		{
+			base.OnDisengage();
+
+			SavePrefabs();
+
+			// Sort of work around so that the DB2 window won't lose focus
+			General.Interface.Focus();
+
+			if(stairsectorbuilderform != null)
+			    stairsectorbuilderform.Hide();
+		}
+
+		// This applies the curves and returns to the base mode
+		public override void OnAccept()
+		{
+			string uppertexture = stairsectorbuilderform.UpperTextureTexture;
+			string lowertexture = stairsectorbuilderform.LowerTextureTexture;
+			string middletexture = stairsectorbuilderform.MiddleTextureTexture;
+			string olddefaulttexture = "-";
+			List<Vertex> newvertices = new List<Vertex>();
+			List<Linedef> newlinedefs = new List<Linedef>();
+			List<Sector> allnewsectors = new List<Sector>();
+
+			if (stairsectors == null) return;
+
+			// Create undo
+			General.Map.UndoRedo.CreateUndo("Build stair sectors");
+
+            // Get the default settings and remember the default texture
+            General.Settings.FindDefaultDrawSettings();
+
+			if (stairsectorbuilderform.MiddleTexture)
+			{
+				olddefaulttexture = General.Settings.DefaultTexture;
+				General.Settings.DefaultTexture = "-";
+			}
+
+    		// This creates the geometry. Uses exactly the same data used
+			// for the preview
+			try
+			{
+				foreach (StairInfo si in stairsectors)
+				{
+					int stepcounter = 1;
+
+					foreach (List<Vector2D> lv in si.sectors)
+					{
+						List<Sector> oldsectors = new List<Sector>(General.Map.Map.Sectors);
+						List<Sector> newsectors = new List<Sector>();
+						List<DrawnVertex> vertices = new List<DrawnVertex>();
+
+						for (int i = 0; i < lv.Count; i++)
+						{
+							vertices.Add(SectorVertex(lv[i]));
+						}
+
+						// Draw the new sector
+						if (!Tools.DrawLines(vertices)) throw new Exception("Failed drawing lines");
+
+						// Update newly created sector(s) with the given textures and heights
+						foreach (Sector s in General.Map.Map.Sectors)
+						{
+							if (!oldsectors.Contains(s))
+							{
+								allnewsectors.Add(s);
+
+								// Apply floor heights if necessary
+								if (stairsectorbuilderform.FloorHeight)
+								{
+									// Should each stair use individual base height or the global one?
+									if (stairsectorbuilderform.DistinctBaseHeights.Checked && stairsectorbuilderform.StairType == 0)
+										s.FloorHeight = si.floorheight + stairsectorbuilderform.FloorHeightModification * stepcounter;
+									else
+										s.FloorHeight = stairsectorbuilderform.FloorBase + stairsectorbuilderform.FloorHeightModification * stepcounter;
+								}
+
+								// Apply ceiling heights if necessary
+								if (stairsectorbuilderform.CeilingHeight)
+								{
+									// Should each stair use individual base height or the global one?
+									if (stairsectorbuilderform.DistinctBaseHeights.Checked && stairsectorbuilderform.StairType == 0)
+										s.CeilHeight = si.ceilingheight + stairsectorbuilderform.CeilingHeightModification * stepcounter;
+									else
+										s.CeilHeight = stairsectorbuilderform.CeilingBase + stairsectorbuilderform.CeilingHeightModification * stepcounter;
+								}
+
+								// Apply floor and ceiling textures if necessary
+								if (stairsectorbuilderform.FloorFlat)
+									s.SetFloorTexture(stairsectorbuilderform.FloorFlatTexture);
+
+								if (stairsectorbuilderform.CeilingFlat)
+									s.SetCeilTexture(stairsectorbuilderform.CeilingFlatTexture);
+
+								// Fix missing textures
+								foreach (Sidedef sd in s.Sidedefs)
+								{
+									if (!newlinedefs.Contains(sd.Line)) newlinedefs.Add(sd.Line);
+
+									if (stairsectorbuilderform.UpperTexture)
+									{
+										if (sd.Line.Back != null && sd.Line.Back.HighRequired()) sd.Line.Back.SetTextureHigh(uppertexture);
+										if (sd.Line.Front != null && sd.Line.Front.HighRequired()) sd.Line.Front.SetTextureHigh(uppertexture);
+										if (stairsectorbuilderform.UpperUnpegged) sd.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, true);
+									}
+
+									if (stairsectorbuilderform.LowerTexture)
+									{
+										if (sd.Line.Front != null && sd.Line.Front.LowRequired()) sd.Line.Front.SetTextureLow(lowertexture);
+										if (sd.Line.Back != null && sd.Line.Back.LowRequired()) sd.Line.Back.SetTextureLow(lowertexture);
+										if (stairsectorbuilderform.LowerUnpegged) sd.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, true);
+									}
+
+									if (stairsectorbuilderform.MiddleTexture)
+									{
+										if (sd.Line.Front != null && sd.Line.Front.MiddleRequired()) sd.Line.Front.SetTextureMid(middletexture);
+										if (sd.Line.Back != null && sd.Line.Back.MiddleRequired()) sd.Line.Back.SetTextureMid(middletexture);
+									}
+								}
+							}
+						}
+
+						stepcounter++;
+					}
+				}
+
+				if (stairsectorbuilderform.MiddleTexture)
+					General.Settings.DefaultTexture = olddefaulttexture;
+
+				// Snap to map format accuracy
+				General.Map.Map.SnapAllToAccuracy();
+
+				// Clean the map up. Merge all vertices that share the same position and
+				// remove looped linedefs
+				foreach (Sector s in allnewsectors)
+				{
+					if (s.Sidedefs == null) continue;
+
+					foreach (Sidedef sd in s.Sidedefs)
+					{
+						if (!newvertices.Contains(sd.Line.Start)) newvertices.Add(sd.Line.Start);
+						if (!newvertices.Contains(sd.Line.End)) newvertices.Add(sd.Line.End);
+					}
+				}
+
+				General.Map.Map.BeginAddRemove();
+				MapSet.JoinVertices(newvertices, newvertices, false, MapSet.STITCH_DISTANCE);
+				General.Map.Map.EndAddRemove();
+
+				MapSet.RemoveLoopedLinedefs(newlinedefs);
+
+				// Update textures
+				General.Map.Data.UpdateUsedTextures();
+
+				// Update caches
+				General.Map.Map.Update();
+				General.Map.IsChanged = true;
+			}
+			catch (Exception e)
+			{
+				General.Interface.DisplayStatus(StatusType.Warning, e.ToString());
+				General.Map.UndoRedo.WithdrawUndo();
+			}
+
+			// Return to base mode
+			General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
+		}
+
+		// Redrawing display
+		public override void OnRedrawDisplay()
+		{
+			if (!stairsectorbuilderform.FullyLoaded) return;
+
+			base.OnRedrawDisplay();
+
+			renderer.RedrawSurface();
+
+			// Render lines
+			if (renderer.StartPlotter(true))
+			{
+				renderer.PlotLinedefSet(General.Map.Map.Linedefs);
+				renderer.PlotVerticesSet(General.Map.Map.Vertices);
+				renderer.Finish();
+			}
+
+			// Render things
+			if (renderer.StartThings(true))
+			{
+				renderer.RenderThingSet(General.Map.Map.Things, 1.0f);
+				renderer.Finish();
+			}
+
+			// Render overlay
+			if (renderer.StartOverlay(true))
+			{
+				if (stairsectorbuilderform.Tabs.SelectedIndex == 2 && numcontrolpoints != stairsectorbuilderform.NumControlPoints)
+				{
+					ModifyControlpointNumber(stairsectorbuilderform.NumControlPoints);
+					numcontrolpoints = stairsectorbuilderform.NumControlPoints;
+				}
+
+                // Create sector info based on the settings...
+				UpdateVertexData();
+
+				// Render the control points if the Catmull Rom spline tab is selected
+				if (stairsectorbuilderform.Tabs.SelectedIndex == 2)
+				{
+					foreach (List<Vector2D> lv in exactsplines)
+					{
+						for (int i = 0; i < lv.Count - 1; i++)
+						{
+							renderer.RenderLine(lv[i], lv[i + 1], LINE_THICKNESS, new PixelColor(255, 128, 128, 128), true);
+						}
+					}
+
+					foreach(CatmullRomSplineGroup crsg in catmullromsplinegroups)
+					{
+						CatmullRomSplineData[] crsd = new CatmullRomSplineData[2] { crsg.splines[INNER_SPLINE], crsg.splines[OUTER_SPLINE] };
+
+						foreach(CatmullRomSplineData c in crsd)
+						{
+							for (int i = 0; i < c.controlpoints.Count; i++)
+							{
+								RectangleF rect = new RectangleF(c.controlpoints[i].x - CONTROLPOINT_SIZE / 2, c.controlpoints[i].y - CONTROLPOINT_SIZE / 2, 10.0f, 10.0f);
+
+								if(i == 0) renderer.RenderRectangle(rect, 2, new PixelColor(128, 255, 0, 0), true);
+								else if (i == c.controlpoints.Count -1) renderer.RenderRectangle(rect, 2, new PixelColor(128, 0, 255, 0), true);
+								else renderer.RenderRectangle(rect, 2, General.Colors.Indication, true);
+								//renderer.RenderLine(c.controlpoints[i], c.controlpoints[i] + c.tangents[i], LINE_THICKNESS, new PixelColor(255, 128, 128, 128), true);
+							}
+						}
+					}
+				}
+
+				// ... and draw the preview
+				foreach (StairInfo si in stairsectors)
+				{
+					foreach (List<Vector2D> lv in si.sectors)
+					{
+						for (int i = 0; i < lv.Count - 1; i++)
+						{
+							renderer.RenderLine(lv[i], lv[i + 1], LINE_THICKNESS, General.Colors.Highlight, true);
+
+							RectangleF rect = new RectangleF(lv[i].x - CONTROLPOINT_SIZE / 2, lv[i].y - CONTROLPOINT_SIZE / 2, 10.0f, 10.0f);
+							renderer.RenderRectangle(rect, 2, new PixelColor(128, 255, 0, 0), true);
+						}
+					}
+				}
+			
+				renderer.Finish();
+			}
+
+			renderer.Present();
+		}
+
+		protected override void OnSelectBegin()
+		{
+			base.OnSelectBegin();
+
+			if (stairsectorbuilderform.Tabs.SelectedIndex != 2) return;
+
+			// Go through all Catmull Rom Spline Groups
+			for (int crsg = 0; crsg < catmullromsplinegroups.Count; crsg++)
+			{
+				// Check inner and outer splines
+				for (int crsd = 0; crsd < 2; crsd++)
+				{
+					// Check all control points except the first and last, they belong to the linedefs
+					// and may not be moved
+					for (int cp = 1; cp < catmullromsplinegroups[crsg].splines[crsd].controlpoints.Count-1; cp++)
+					{
+						Vector2D p = catmullromsplinegroups[crsg].splines[crsd].controlpoints[cp];
+
+						// If the mouse is inside a control point, set the info about that control point
+						// in the selectedcontrolpoint struct
+						if (mousemappos.x >= p.x - CONTROLPOINT_SIZE / 2 && mousemappos.x <= p.x + CONTROLPOINT_SIZE / 2 && mousemappos.y >= p.y - CONTROLPOINT_SIZE / 2 && mousemappos.y <= p.y + CONTROLPOINT_SIZE / 2)
+						{
+							selectedcontrolpoint.cp = cp;
+							selectedcontrolpoint.crsd = crsd;
+							selectedcontrolpoint.crsg = crsg;
+						}
+					}
+				}
+			}
+		}
+
+		// When selected button is released
+		protected override void OnSelectEnd()
+		{
+			base.OnSelectEnd();
+
+			// No control point is selected
+			selectedcontrolpoint.cp = -1;
+			selectedcontrolpoint.crsd = -1;
+			selectedcontrolpoint.crsg = -1;
+
+			// Redraw
+			General.Map.Map.Update();
+			General.Interface.RedrawDisplay();
+		}
+
+		// Mouse moves
+		public override void OnMouseMove(MouseEventArgs e)
+		{
+			base.OnMouseMove(e);
+
+			// If a control point is selected move it around
+			if (selectedcontrolpoint.cp != -1)
+			{
+				if(General.Interface.ShiftState ^ General.Interface.SnapToGrid)
+				{
+					catmullromsplinegroups[selectedcontrolpoint.crsg].splines[selectedcontrolpoint.crsd].controlpoints[selectedcontrolpoint.cp] = new Vector2D(General.Map.Grid.SnappedToGrid(mousemappos).x, General.Map.Grid.SnappedToGrid(mousemappos).y);
+				}
+				else
+				{
+					catmullromsplinegroups[selectedcontrolpoint.crsg].splines[selectedcontrolpoint.crsd].controlpoints[selectedcontrolpoint.cp] = new Vector2D(mousemappos.x, mousemappos.y);
+				}
+
+				// New tangents of the control points have to be calculated
+				ComputeTangents(ref catmullromsplinegroups[selectedcontrolpoint.crsg].splines[selectedcontrolpoint.crsd]);
+
+				General.Interface.RedrawDisplay();
+			}
+		}
+
+		public static void SavePrefabs()
+		{
+			ListDictionary prefabdata = new ListDictionary();
+			int counter = 1;
+
+			foreach(BuilderPlug.Prefab p in BuilderPlug.Me.Prefabs)
+			{
+				ListDictionary data = new ListDictionary();
+				data.Add("name", p.name);
+				data.Add("numberofsectors", p.numberofsectors);
+				data.Add("outervertexmultiplier", p.outervertexmultiplier);
+				data.Add("innervertexmultiplier", p.innervertexmultiplier);
+				data.Add("stairtype", p.stairtype);
+				data.Add("sectordepth", p.sectordepth);
+				data.Add("spacing", p.spacing);
+				data.Add("frontside", p.frontside);
+				data.Add("singlesectors", p.singlesectors);
+				data.Add("singledirection", p.singledirection);
+				data.Add("distinctbaseheights", p.distinctbaseheights);
+				data.Add("flipping", p.flipping);
+				data.Add("numberofcontrolpoints", p.numberofcontrolpoints);
+				data.Add("applyfloormod", p.applyfloormod);
+				data.Add("floormod", p.floormod);
+				//data.Add("floorbase", p.floorbase);
+				data.Add("applyceilingmod", p.applyceilingmod);
+				data.Add("ceilingmod", p.ceilingmod);
+				//data.Add("ceilingbase", p.ceilingbase);
+				data.Add("applyfloortexture", p.applyfloortexture);
+				data.Add("floortexture", p.floortexture);
+				data.Add("applyceilingtexture", p.applyceilingtexture);
+				data.Add("ceilingtexture", p.ceilingtexture);
+				data.Add("applyuppertexture", p.applyuppertexture);
+				data.Add("uppertexture", p.uppertexture);
+				data.Add("upperunpegged", p.upperunpegged);
+				data.Add("applymiddletexture", p.applymiddletexture);
+				data.Add("middletexture", p.middletexture);
+				data.Add("applylowertexture", p.applylowertexture);
+				data.Add("lowertexture", p.lowertexture);
+				data.Add("lowerunpegged", p.lowerunpegged);
+
+				prefabdata.Add("prefab" + counter.ToString(), data);
+
+				counter++;
+			}
+
+			General.Map.Options.WritePluginSetting("prefabs", prefabdata);
+		}
+
+		private void LoadPrefabs()
+		{
+			ListDictionary prefabdata = new ListDictionary();
+
+			// load the light info from the .dbs
+			prefabdata = (ListDictionary)General.Map.Options.ReadPluginSetting("prefabs", new ListDictionary());
+
+			BuilderPlug.Me.Prefabs.Clear();
+
+			foreach (DictionaryEntry prefabentry in prefabdata)
+			{
+				BuilderPlug.Prefab p = new BuilderPlug.Prefab();
+
+				foreach (DictionaryEntry entry in (ListDictionary)prefabentry.Value)
+				{
+					if ((string)entry.Key == "name") p.name = (string)entry.Value;
+					if ((string)entry.Key == "numberofsectors") p.numberofsectors = (int)entry.Value;
+					if ((string)entry.Key == "outervertexmultiplier") p.outervertexmultiplier = (int)entry.Value;
+					if ((string)entry.Key == "innervertexmultiplier") p.innervertexmultiplier = (int)entry.Value;
+					if ((string)entry.Key == "stairtype") p.stairtype = (int)entry.Value;
+					if ((string)entry.Key == "sectordepth") p.sectordepth = (int)entry.Value;
+					if ((string)entry.Key == "spacing") p.spacing = (int)entry.Value;
+					if ((string)entry.Key == "frontside") p.frontside = (bool)entry.Value;
+					if ((string)entry.Key == "singlesectors") p.singlesectors = (bool)entry.Value;
+					if ((string)entry.Key == "singledirection") p.singledirection = (bool)entry.Value;
+					if ((string)entry.Key == "distinctbaseheights") p.distinctbaseheights = (bool)entry.Value;
+					if ((string)entry.Key == "flipping") p.flipping = (int)entry.Value;
+					if ((string)entry.Key == "numberofcontrolpoints") p.numberofcontrolpoints = (int)entry.Value;
+					if ((string)entry.Key == "applyfloormod") p.applyfloormod = (bool)entry.Value;
+					if ((string)entry.Key == "floormod") p.floormod = (int)entry.Value;
+					//if ((string)entry.Key == "floorbase") p.floorbase = (int)entry.Value;
+					if ((string)entry.Key == "applyceilingmod") p.applyceilingmod = (bool)entry.Value;
+					if ((string)entry.Key == "ceilingmod") p.ceilingmod = (int)entry.Value;
+					//if ((string)entry.Key == "ceilingbase") p.ceilingbase = (int)entry.Value;
+					if ((string)entry.Key == "applyfloortexture") p.applyfloortexture = (bool)entry.Value;
+					if ((string)entry.Key == "floortexture") p.floortexture = (string)entry.Value;
+					if ((string)entry.Key == "applyceilingtexture") p.applyceilingtexture = (bool)entry.Value;
+					if ((string)entry.Key == "ceilingtexture") p.ceilingtexture = (string)entry.Value;
+					if ((string)entry.Key == "applyuppertexture") p.applyuppertexture = (bool)entry.Value;
+					if ((string)entry.Key == "upperunpegged") p.upperunpegged = (bool)entry.Value;
+					if ((string)entry.Key == "uppertexture") p.uppertexture = (string)entry.Value;
+					if ((string)entry.Key == "applymiddletexture") p.applymiddletexture = (bool)entry.Value;
+					if ((string)entry.Key == "middletexture") p.middletexture = (string)entry.Value;
+					if ((string)entry.Key == "applylowertexture") p.applylowertexture = (bool)entry.Value;
+					if ((string)entry.Key == "lowertexture") p.lowertexture = (string)entry.Value;
+					if ((string)entry.Key == "lowerunpegged") p.lowerunpegged = (bool)entry.Value;
+				}
+
+				BuilderPlug.Me.Prefabs.Add(p);
+			}
+		}
+
+		#endregion
+	}
+}
diff --git a/Source/Plugins/Statistics/Statistics.csproj b/Source/Plugins/Statistics/Statistics.csproj
index 666bb6b6..692ab1cd 100644
--- a/Source/Plugins/Statistics/Statistics.csproj
+++ b/Source/Plugins/Statistics/Statistics.csproj
@@ -3,7 +3,7 @@
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>8.0.50727</ProductVersion>
+    <ProductVersion>9.0.30729</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{FBC0A503-9152-4BE2-9B5C-128FFD0B0D3F}</ProjectGuid>
     <OutputType>Library</OutputType>
@@ -55,6 +55,7 @@
     <ProjectReference Include="..\..\Core\Builder.csproj">
       <Project>{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}</Project>
       <Name>Builder</Name>
+      <Private>False</Private>
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
diff --git a/Source/Plugins/TagRange/TagRange.csproj b/Source/Plugins/TagRange/TagRange.csproj
index 23fe8294..2dd081ac 100644
--- a/Source/Plugins/TagRange/TagRange.csproj
+++ b/Source/Plugins/TagRange/TagRange.csproj
@@ -88,6 +88,7 @@
     <ProjectReference Include="..\..\Core\Builder.csproj">
       <Project>{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}</Project>
       <Name>Builder</Name>
+      <Private>False</Private>
     </ProjectReference>
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />