From c95c26bbbcdffd87f9daafd7e4cec51c5c18c49b Mon Sep 17 00:00:00 2001
From: MaxED <j.maxed@gmail.com>
Date: Mon, 25 Aug 2014 11:15:19 +0000
Subject: [PATCH] Fixed: MapSet.GetSectorByCoordinates() returned "false" in
 some cases when a given coordinate was on top of sector's vertex. Internal:
 UDMF sector slopes and offsets are no longer stored in sector.Fields. Use
 sector.FloorSlope, sector.FloorSlopeOffset, sector.CeilingSlope and
 sector.CeilingSlopeOffset.

---
 .../Controls/SectorSlopeControl.Designer.cs   | 128 +--
 Source/Core/Controls/SectorSlopeControl.cs    |  65 +-
 .../Core/GZBuilder/Windows/ExceptionDialog.cs |   3 +-
 Source/Core/General/General.cs                |   4 +-
 Source/Core/IO/ClipboardStreamReader.cs       |   8 +-
 Source/Core/IO/ClipboardStreamWriter.cs       |  10 +
 Source/Core/IO/UniversalStreamReader.cs       |  33 +-
 Source/Core/IO/UniversalStreamWriter.cs       |  17 +
 Source/Core/Map/MapSet.cs                     |  38 +-
 Source/Core/Map/Sector.cs                     |  45 +-
 Source/Core/Resources/UDMF.cfg                |   8 +
 Source/Core/VisualModes/VisualMode.cs         |  75 +-
 .../Windows/SectorEditFormUDMF.Designer.cs    |  12 +-
 Source/Core/Windows/SectorEditFormUDMF.cs     | 740 +++++-------------
 Source/Core/Windows/SectorEditFormUDMF.resx   |  24 +
 .../Plugins/BuilderModes/BuilderModes.csproj  |   1 -
 .../VisualModes/BaseVisualMode.cs             |  11 +-
 .../VisualModes/EffectSectorSlope.cs          |  53 --
 .../BuilderModes/VisualModes/SectorData.cs    |  32 +-
 19 files changed, 535 insertions(+), 772 deletions(-)
 delete mode 100644 Source/Plugins/BuilderModes/VisualModes/EffectSectorSlope.cs

diff --git a/Source/Core/Controls/SectorSlopeControl.Designer.cs b/Source/Core/Controls/SectorSlopeControl.Designer.cs
index ebb16e845..a94ccb255 100644
--- a/Source/Core/Controls/SectorSlopeControl.Designer.cs
+++ b/Source/Core/Controls/SectorSlopeControl.Designer.cs
@@ -25,34 +25,21 @@
 		/// the contents of this method with the code editor.
 		/// </summary>
 		private void InitializeComponent() {
-			this.slopeangle = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
 			this.label23 = new System.Windows.Forms.Label();
-			this.sloperotation = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
 			this.label24 = new System.Windows.Forms.Label();
 			this.reset = new System.Windows.Forms.Button();
-			this.slopeoffset = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
 			this.label18 = new System.Windows.Forms.Label();
-			this.rotationcontrol = new CodeImp.DoomBuilder.GZBuilder.Controls.AngleControl();
 			this.angletrackbar = new System.Windows.Forms.TrackBar();
 			this.label1 = new System.Windows.Forms.Label();
 			this.pivotmodeselector = new System.Windows.Forms.ComboBox();
+			this.rotationcontrol = new CodeImp.DoomBuilder.GZBuilder.Controls.AngleControl();
+			this.slopeangle = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.sloperotation = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.slopeoffset = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
+			this.cbuselineangles = new System.Windows.Forms.CheckBox();
 			((System.ComponentModel.ISupportInitialize)(this.angletrackbar)).BeginInit();
 			this.SuspendLayout();
 			// 
-			// slopeangle
-			// 
-			this.slopeangle.AllowDecimal = true;
-			this.slopeangle.AllowNegative = true;
-			this.slopeangle.AllowRelative = true;
-			this.slopeangle.ButtonStep = 1;
-			this.slopeangle.ButtonStepFloat = 1F;
-			this.slopeangle.Location = new System.Drawing.Point(85, 78);
-			this.slopeangle.Name = "slopeangle";
-			this.slopeangle.Size = new System.Drawing.Size(82, 24);
-			this.slopeangle.StepValues = null;
-			this.slopeangle.TabIndex = 29;
-			this.slopeangle.WhenTextChanged += new System.EventHandler(this.slopeangle_WhenTextChanged);
-			// 
 			// label23
 			// 
 			this.label23.Location = new System.Drawing.Point(3, 83);
@@ -62,20 +49,6 @@
 			this.label23.Text = "Slope angle:";
 			this.label23.TextAlign = System.Drawing.ContentAlignment.TopRight;
 			// 
-			// sloperotation
-			// 
-			this.sloperotation.AllowDecimal = true;
-			this.sloperotation.AllowNegative = true;
-			this.sloperotation.AllowRelative = true;
-			this.sloperotation.ButtonStep = 1;
-			this.sloperotation.ButtonStepFloat = 1F;
-			this.sloperotation.Location = new System.Drawing.Point(85, 48);
-			this.sloperotation.Name = "sloperotation";
-			this.sloperotation.Size = new System.Drawing.Size(82, 24);
-			this.sloperotation.StepValues = null;
-			this.sloperotation.TabIndex = 27;
-			this.sloperotation.WhenTextChanged += new System.EventHandler(this.sloperotation_WhenTextChanged);
-			// 
 			// label24
 			// 
 			this.label24.Location = new System.Drawing.Point(3, 53);
@@ -95,20 +68,6 @@
 			this.reset.UseVisualStyleBackColor = true;
 			this.reset.Click += new System.EventHandler(this.reset_Click);
 			// 
-			// slopeoffset
-			// 
-			this.slopeoffset.AllowDecimal = true;
-			this.slopeoffset.AllowNegative = true;
-			this.slopeoffset.AllowRelative = true;
-			this.slopeoffset.ButtonStep = 1;
-			this.slopeoffset.ButtonStepFloat = 16F;
-			this.slopeoffset.Location = new System.Drawing.Point(85, 108);
-			this.slopeoffset.Name = "slopeoffset";
-			this.slopeoffset.Size = new System.Drawing.Size(82, 24);
-			this.slopeoffset.StepValues = null;
-			this.slopeoffset.TabIndex = 24;
-			this.slopeoffset.WhenTextChanged += new System.EventHandler(this.slopeoffset_WhenTextChanged);
-			// 
 			// label18
 			// 
 			this.label18.Location = new System.Drawing.Point(3, 113);
@@ -118,15 +77,6 @@
 			this.label18.Text = "Height offset:";
 			this.label18.TextAlign = System.Drawing.ContentAlignment.TopRight;
 			// 
-			// rotationcontrol
-			// 
-			this.rotationcontrol.Angle = 0;
-			this.rotationcontrol.Location = new System.Drawing.Point(173, 36);
-			this.rotationcontrol.Name = "rotationcontrol";
-			this.rotationcontrol.Size = new System.Drawing.Size(44, 44);
-			this.rotationcontrol.TabIndex = 56;
-			this.rotationcontrol.AngleChanged += new CodeImp.DoomBuilder.GZBuilder.Controls.AngleControl.AngleChangedDelegate(this.rotationcontrol_AngleChanged);
-			// 
 			// angletrackbar
 			// 
 			this.angletrackbar.BackColor = System.Drawing.SystemColors.ControlLightLight;
@@ -153,7 +103,7 @@
 			this.pivotmodeselector.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
 			this.pivotmodeselector.FormattingEnabled = true;
 			this.pivotmodeselector.Items.AddRange(new object[] {
-            "Origin",
+            "World origin",
             "Selection center",
             "Sector center"});
 			this.pivotmodeselector.Location = new System.Drawing.Point(85, 9);
@@ -162,10 +112,74 @@
 			this.pivotmodeselector.TabIndex = 59;
 			this.pivotmodeselector.SelectedIndexChanged += new System.EventHandler(this.pivotmodeselector_SelectedIndexChanged);
 			// 
+			// rotationcontrol
+			// 
+			this.rotationcontrol.Angle = 0;
+			this.rotationcontrol.Location = new System.Drawing.Point(173, 36);
+			this.rotationcontrol.Name = "rotationcontrol";
+			this.rotationcontrol.Size = new System.Drawing.Size(44, 44);
+			this.rotationcontrol.TabIndex = 56;
+			this.rotationcontrol.AngleChanged += new CodeImp.DoomBuilder.GZBuilder.Controls.AngleControl.AngleChangedDelegate(this.rotationcontrol_AngleChanged);
+			// 
+			// slopeangle
+			// 
+			this.slopeangle.AllowDecimal = true;
+			this.slopeangle.AllowNegative = true;
+			this.slopeangle.AllowRelative = true;
+			this.slopeangle.ButtonStep = 1;
+			this.slopeangle.ButtonStepFloat = 1F;
+			this.slopeangle.Location = new System.Drawing.Point(85, 78);
+			this.slopeangle.Name = "slopeangle";
+			this.slopeangle.Size = new System.Drawing.Size(82, 24);
+			this.slopeangle.StepValues = null;
+			this.slopeangle.TabIndex = 29;
+			this.slopeangle.WhenTextChanged += new System.EventHandler(this.slopeangle_WhenTextChanged);
+			// 
+			// sloperotation
+			// 
+			this.sloperotation.AllowDecimal = true;
+			this.sloperotation.AllowNegative = true;
+			this.sloperotation.AllowRelative = true;
+			this.sloperotation.ButtonStep = 1;
+			this.sloperotation.ButtonStepFloat = 1F;
+			this.sloperotation.Location = new System.Drawing.Point(85, 48);
+			this.sloperotation.Name = "sloperotation";
+			this.sloperotation.Size = new System.Drawing.Size(82, 24);
+			this.sloperotation.StepValues = null;
+			this.sloperotation.TabIndex = 27;
+			this.sloperotation.WhenTextChanged += new System.EventHandler(this.sloperotation_WhenTextChanged);
+			// 
+			// slopeoffset
+			// 
+			this.slopeoffset.AllowDecimal = true;
+			this.slopeoffset.AllowNegative = true;
+			this.slopeoffset.AllowRelative = true;
+			this.slopeoffset.ButtonStep = 1;
+			this.slopeoffset.ButtonStepFloat = 16F;
+			this.slopeoffset.Location = new System.Drawing.Point(85, 108);
+			this.slopeoffset.Name = "slopeoffset";
+			this.slopeoffset.Size = new System.Drawing.Size(82, 24);
+			this.slopeoffset.StepValues = null;
+			this.slopeoffset.TabIndex = 24;
+			this.slopeoffset.WhenTextChanged += new System.EventHandler(this.slopeoffset_WhenTextChanged);
+			// 
+			// cbuselineangles
+			// 
+			this.cbuselineangles.AutoSize = true;
+			this.cbuselineangles.Location = new System.Drawing.Point(226, 52);
+			this.cbuselineangles.Name = "cbuselineangles";
+			this.cbuselineangles.Size = new System.Drawing.Size(113, 17);
+			this.cbuselineangles.TabIndex = 60;
+			this.cbuselineangles.Tag = "";
+			this.cbuselineangles.Text = "Use linedef angles";
+			this.cbuselineangles.UseVisualStyleBackColor = true;
+			this.cbuselineangles.CheckedChanged += new System.EventHandler(this.cbuselineangles_CheckedChanged);
+			// 
 			// SectorSlopeControl
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+			this.Controls.Add(this.cbuselineangles);
 			this.Controls.Add(this.pivotmodeselector);
 			this.Controls.Add(this.label1);
 			this.Controls.Add(this.angletrackbar);
@@ -179,6 +193,7 @@
 			this.Controls.Add(this.label18);
 			this.Name = "SectorSlopeControl";
 			this.Size = new System.Drawing.Size(353, 169);
+			this.Load += new System.EventHandler(this.SectorSlopeControl_Load);
 			((System.ComponentModel.ISupportInitialize)(this.angletrackbar)).EndInit();
 			this.ResumeLayout(false);
 			this.PerformLayout();
@@ -198,5 +213,6 @@
 		private System.Windows.Forms.TrackBar angletrackbar;
 		private System.Windows.Forms.Label label1;
 		private System.Windows.Forms.ComboBox pivotmodeselector;
+		private System.Windows.Forms.CheckBox cbuselineangles;
 	}
 }
diff --git a/Source/Core/Controls/SectorSlopeControl.cs b/Source/Core/Controls/SectorSlopeControl.cs
index 92f046722..6ea4ffb6f 100644
--- a/Source/Core/Controls/SectorSlopeControl.cs
+++ b/Source/Core/Controls/SectorSlopeControl.cs
@@ -1,6 +1,5 @@
 using System;
 using System.Windows.Forms;
-using CodeImp.DoomBuilder.Geometry;
 
 namespace CodeImp.DoomBuilder.Controls
 {
@@ -20,13 +19,14 @@ namespace CodeImp.DoomBuilder.Controls
 
 		#region ================== Events
 
-		public event EventHandler OnValuesChanged; //mxd
+		public event EventHandler OnValuesChanged;
+		public event EventHandler OnUseLineAnglesChanged;
 
 		#endregion
 
 		#region ================== Variables
 
-		private static SlopePivotMode pivotmode = SlopePivotMode.LOCAL;
+		private static SlopePivotMode pivotmode = SlopePivotMode.ORIGIN; //DBG SlopePivotMode.LOCAL
 		internal SlopePivotMode PivotMode { get { return pivotmode; } }
 
 		private bool blockUpdate;
@@ -36,15 +36,16 @@ namespace CodeImp.DoomBuilder.Controls
 		private float anglez;
 		private float offset;
 
-		public float AngleXY { get { return anglexy; } }
-		public float AngleZ { get { return anglez; } }
+		public float AngleXY { get { return anglexy; } } //in dergrees
+		public float AngleZ { get { return anglez; } } //in dergrees, add 90
 		public float Offset { get { return offset; } }
+		public StepsList StepValues { set { sloperotation.StepValues = value; } }
+		public bool UseLineAngles { get { return cbuselineangles.Checked; } set { blockUpdate = true; cbuselineangles.Checked = value; blockUpdate = false; } }
 
 		#endregion
 
 		public SectorSlopeControl() {
 			InitializeComponent();
-			pivotmodeselector.SelectedIndex = (int) pivotmode;
 		}
 
 		#region ================== Methods
@@ -55,10 +56,19 @@ namespace CodeImp.DoomBuilder.Controls
 				this.anglexy = anglexy;
 				this.anglez = anglez;
 				this.offset = offset;
+
+				//dbg
+				//Console.WriteLine("SetValues: anglexy=" + this.anglexy + "; anglez=" + this.anglez + "; offset=" + this.offset + "[first time]");
 			} else {
+				//dbg
+				//Console.WriteLine("SetValues: this.anglexy=" + this.anglexy + "; anglexy = " + anglexy + "; this.anglez=" + this.anglez + "; anglez = " + anglez + "; this.offset=" + this.offset + "; offset = " + offset + "[before]");
+
 				if(!float.IsNaN(this.anglexy) && this.anglexy != anglexy) this.anglexy = float.NaN;
 				if(!float.IsNaN(this.anglez) && this.anglez != anglez) this.anglez = float.NaN;
 				if(!float.IsNaN(this.offset) && this.offset != offset) this.offset = float.NaN;
+
+				//dbg
+				//Console.WriteLine("SetValues: this.anglexy=" + this.anglexy + "; anglexy = " + anglexy + "; this.anglez=" + this.anglez + "; anglez = " + anglez + "; this.offset=" + this.offset + "; offset = " + offset + "[after]");
 			}
 		}
 
@@ -70,22 +80,32 @@ namespace CodeImp.DoomBuilder.Controls
 				rotationcontrol.Angle = 0;
 			} else {
 				sloperotation.Text = anglexy.ToString();
-				rotationcontrol.Angle = (int)Math.Round(anglexy); //(int)Math.Round(Angle2D.RadToDeg(this.anglexy));
+				rotationcontrol.Angle = (int)Math.Round(anglexy + 90); //(int)Math.Round(Angle2D.RadToDeg(this.anglexy));
 			}
 
 			if(float.IsNaN(anglez)) {
 				slopeangle.Text = "";
 				angletrackbar.Value = 0;
 			} else {
+				//clamp value to [-85 .. 85]
+				//anglez = General.Clamp((anglez + 90) % 90 - 90, angletrackbar.Minimum, angletrackbar.Maximum);
+				anglez = General.Clamp(anglez, angletrackbar.Minimum, angletrackbar.Maximum);
+
 				slopeangle.Text = anglez.ToString();
-				angletrackbar.Value = General.Clamp((int)Math.Round(anglez - 90), angletrackbar.Minimum, angletrackbar.Maximum);
+				//angletrackbar.Value = (int)Math.Round(anglez);
+				angletrackbar.Value = (int)General.Clamp(anglez, angletrackbar.Minimum, angletrackbar.Maximum);
 			}
 
-			slopeoffset.Text = float.IsNaN(this.offset) ? "" : this.offset.ToString();
+			slopeoffset.Text = float.IsNaN(offset) ? "" : offset.ToString();
 
 			blockUpdate = false;
 		}
 
+		/*public void ClearOffset() {
+			offset = float.NaN;
+			slopeoffset.Text = string.Empty;
+		}*/
+
 		#endregion
 
 		#region ================== Events
@@ -94,8 +114,8 @@ namespace CodeImp.DoomBuilder.Controls
 			if(blockUpdate) return;
 			blockUpdate = true;
 
-			anglexy = sloperotation.GetResultFloat(0f); //Angle2D.DegToRad(sloperotation.GetResultFloat(0f));
-			rotationcontrol.Angle = (int)Math.Round(sloperotation.GetResultFloat(0f));
+			anglexy = General.ClampAngle(sloperotation.GetResultFloat(0f));
+			rotationcontrol.Angle = (int)Math.Round(anglexy + 90);
 
 			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
 			blockUpdate = false;
@@ -105,8 +125,8 @@ namespace CodeImp.DoomBuilder.Controls
 			if(blockUpdate) return;
 			blockUpdate = true;
 
-			sloperotation.Text = rotationcontrol.Angle.ToString();
-			anglexy = Angle2D.DegToRad(rotationcontrol.Angle);
+			anglexy = General.ClampAngle(rotationcontrol.Angle - 90);
+			sloperotation.Text = anglexy.ToString();
 
 			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
 			blockUpdate = false;
@@ -116,9 +136,8 @@ namespace CodeImp.DoomBuilder.Controls
 			if(blockUpdate) return;
 			blockUpdate = true;
 
-			int anglezdeg = General.Clamp((int)Math.Round(slopeangle.GetResultFloat(0f)), angletrackbar.Minimum, angletrackbar.Maximum);
-			angletrackbar.Value = anglezdeg;
-			anglez = Angle2D.DegToRad(anglezdeg - 90);
+			anglez = General.Clamp((int)Math.Round(slopeangle.GetResultFloat(0f)), angletrackbar.Minimum, angletrackbar.Maximum);
+			angletrackbar.Value = (int)anglez;
 
 			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
 			blockUpdate = false;
@@ -129,7 +148,7 @@ namespace CodeImp.DoomBuilder.Controls
 			blockUpdate = true;
 
 			slopeangle.Text = angletrackbar.Value.ToString();
-			anglez = Angle2D.DegToRad(angletrackbar.Value);
+			anglez = angletrackbar.Value;
 
 			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
 			blockUpdate = false;
@@ -144,7 +163,7 @@ namespace CodeImp.DoomBuilder.Controls
 			blockUpdate = true;
 
 			sloperotation.Text = "0";
-			rotationcontrol.Angle = 0;
+			rotationcontrol.Angle = 90;
 			slopeangle.Text = "0";
 			angletrackbar.Value = 0;
 			slopeoffset.Text = "0";
@@ -157,9 +176,19 @@ namespace CodeImp.DoomBuilder.Controls
 		}
 
 		private void pivotmodeselector_SelectedIndexChanged(object sender, EventArgs e) {
+			if(blockUpdate) return;
 			pivotmode = (SlopePivotMode)pivotmodeselector.SelectedIndex;
 		}
 
+		private void SectorSlopeControl_Load(object sender, EventArgs e) {
+			pivotmodeselector.SelectedIndex = (int)pivotmode;
+		}
+
+		private void cbuselineangles_CheckedChanged(object sender, EventArgs e) {
+			if(blockUpdate) return;
+			if(OnUseLineAnglesChanged != null) OnUseLineAnglesChanged(this, EventArgs.Empty);
+		}
+
 		#endregion
 
 	}
diff --git a/Source/Core/GZBuilder/Windows/ExceptionDialog.cs b/Source/Core/GZBuilder/Windows/ExceptionDialog.cs
index 8aca47aa3..6f7697f4b 100644
--- a/Source/Core/GZBuilder/Windows/ExceptionDialog.cs
+++ b/Source/Core/GZBuilder/Windows/ExceptionDialog.cs
@@ -98,7 +98,8 @@ namespace CodeImp.DoomBuilder.GZBuilder.Windows
 								  "Deal with it",
 								  "Error 47",
 								  "YOU DIED",
-								  "Thanks, Obama"
+								  "Thanks, Obama",
+								  "The God Of Exceptions Demands MORE Exceptions!"
 							  };
 			this.Text = titles[new Random().Next(0, titles.Length - 1)];
 		}
diff --git a/Source/Core/General/General.cs b/Source/Core/General/General.cs
index ef6699aca..05b4438a7 100644
--- a/Source/Core/General/General.cs
+++ b/Source/Core/General/General.cs
@@ -2032,8 +2032,8 @@ namespace CodeImp.DoomBuilder
 				dlg.ShowDialog();
 			} catch(Exception exc) {
 				try {
-					MessageBox.Show("Could not write the error to the event log. Reason: "
-						+ exc.Message + "\n\nOriginal message:\n" + exceptionmsg, "Fatal Non-UI Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
+					MessageBox.Show("Could not write the error to the event log.\nReason: "
+						+ exc.Message + "\n\nInitial exception:\n" + exceptionmsg, "Fatal Non-UI Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
 				} finally {
 					Application.Exit();
 				}
diff --git a/Source/Core/IO/ClipboardStreamReader.cs b/Source/Core/IO/ClipboardStreamReader.cs
index eb0aa2cc1..8a2949f1f 100644
--- a/Source/Core/IO/ClipboardStreamReader.cs
+++ b/Source/Core/IO/ClipboardStreamReader.cs
@@ -102,6 +102,12 @@ namespace CodeImp.DoomBuilder.IO
 				string tfloor = ReadString(reader);
 				string tceil = ReadString(reader);
 
+				//mxd. Slopes
+				float foffset = reader.ReadSingle();
+				Vector3D fslope = new Vector3D(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
+				float coffset = reader.ReadSingle();
+				Vector3D cslope = new Vector3D(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
+
 				//flags
 				Dictionary<string, bool> stringflags = new Dictionary<string, bool>(StringComparer.Ordinal);
 				int numFlags = reader.ReadInt32();
@@ -118,7 +124,7 @@ namespace CodeImp.DoomBuilder.IO
 				Dictionary<string, UniValue> fields = ReadCustomFields(reader);
 				Sector s = map.CreateSector();
 				if(s != null) {
-					s.Update(hfloor, hceil, tfloor, tceil, effect, stringflags, tag, bright);
+					s.Update(hfloor, hceil, tfloor, tceil, effect, stringflags, tag, bright, foffset, fslope, coffset, cslope);
 
 					// Add custom fields
 					s.Fields.BeforeFieldsChange();
diff --git a/Source/Core/IO/ClipboardStreamWriter.cs b/Source/Core/IO/ClipboardStreamWriter.cs
index 37fcfb051..4d7c1b7eb 100644
--- a/Source/Core/IO/ClipboardStreamWriter.cs
+++ b/Source/Core/IO/ClipboardStreamWriter.cs
@@ -188,6 +188,16 @@ namespace CodeImp.DoomBuilder.IO
 				writer.Write(s.CeilTexture.Length);
 				writer.Write(s.CeilTexture.ToCharArray());
 
+				//mxd. Slopes
+				writer.Write(s.FloorSlopeOffset);
+				writer.Write(s.FloorSlope.x);
+				writer.Write(s.FloorSlope.y);
+				writer.Write(s.FloorSlope.z);
+				writer.Write(s.CeilingSlopeOffset);
+				writer.Write(s.CeilingSlope.x);
+				writer.Write(s.CeilingSlope.y);
+				writer.Write(s.CeilingSlope.z);
+
 				AddFlags(s.Flags, writer);
 				AddCustomFields(s.Fields, "sector", writer);
 			}
diff --git a/Source/Core/IO/UniversalStreamReader.cs b/Source/Core/IO/UniversalStreamReader.cs
index 5e4812289..2bf2c87a2 100644
--- a/Source/Core/IO/UniversalStreamReader.cs
+++ b/Source/Core/IO/UniversalStreamReader.cs
@@ -379,24 +379,35 @@ namespace CodeImp.DoomBuilder.IO
 				// Read fields
 				UniversalCollection c = collections[i];
 				string where = "sector " + i;
-				int hfloor = GetCollectionEntry<int>(c, "heightfloor", false, 0, where);
-				int hceil = GetCollectionEntry<int>(c, "heightceiling", false, 0, where);
-				string tfloor = GetCollectionEntry<string>(c, "texturefloor", true, "-", where);
-				string tceil = GetCollectionEntry<string>(c, "textureceiling", true, "-", where);
-				int bright = GetCollectionEntry<int>(c, "lightlevel", false, 160, where);
-				int special = GetCollectionEntry<int>(c, "special", false, 0, where);
-				int tag = GetCollectionEntry<int>(c, "id", false, 0, where);
-
-				//mxd. Flags
+				int hfloor = GetCollectionEntry(c, "heightfloor", false, 0, where);
+				int hceil = GetCollectionEntry(c, "heightceiling", false, 0, where);
+				string tfloor = GetCollectionEntry(c, "texturefloor", true, "-", where);
+				string tceil = GetCollectionEntry(c, "textureceiling", true, "-", where);
+				int bright = GetCollectionEntry(c, "lightlevel", false, 160, where);
+				int special = GetCollectionEntry(c, "special", false, 0, where);
+				int tag = GetCollectionEntry(c, "id", false, 0, where);
+
+				//mxd. Read slopes
+				float fslopex = GetCollectionEntry(c, "floorplane_a", false, 0.0f, where);
+				float fslopey = GetCollectionEntry(c, "floorplane_b", false, 0.0f, where);
+				float fslopez = GetCollectionEntry(c, "floorplane_c", false, 0.0f, where);
+				float foffset = GetCollectionEntry(c, "floorplane_d", false, float.NaN, where);
+
+				float cslopex = GetCollectionEntry(c, "ceilingplane_a", false, 0.0f, where);
+				float cslopey = GetCollectionEntry(c, "ceilingplane_b", false, 0.0f, where);
+				float cslopez = GetCollectionEntry(c, "ceilingplane_c", false, 0.0f, where);
+				float coffset = GetCollectionEntry(c, "ceilingplane_d", false, float.NaN, where);
+
+				//mxd. Read flags
 				Dictionary<string, bool> stringflags = new Dictionary<string, bool>(StringComparer.Ordinal);
 				foreach(KeyValuePair<string, string> flag in General.Map.Config.SectorFlags)
-					stringflags[flag.Key] = GetCollectionEntry<bool>(c, flag.Key, false, false, where);
+					stringflags[flag.Key] = GetCollectionEntry(c, flag.Key, false, false, where);
 
 				// Create new item
 				Sector s = map.CreateSector();
 				if(s != null)
 				{
-					s.Update(hfloor, hceil, tfloor, tceil, special, stringflags, tag, bright);
+					s.Update(hfloor, hceil, tfloor, tceil, special, stringflags, tag, bright, foffset, new Vector3D(fslopex, fslopey, fslopez).GetNormal(), coffset, new Vector3D(cslopex, cslopey, cslopez).GetNormal());
 
 					// Custom fields
 					ReadCustomFields(c, s, "sector");
diff --git a/Source/Core/IO/UniversalStreamWriter.cs b/Source/Core/IO/UniversalStreamWriter.cs
index 3aa048c41..f0e93a9d6 100644
--- a/Source/Core/IO/UniversalStreamWriter.cs
+++ b/Source/Core/IO/UniversalStreamWriter.cs
@@ -270,6 +270,23 @@ namespace CodeImp.DoomBuilder.IO
 				if(s.Tag != 0) coll.Add("id", s.Tag);
 				coll.Comment = s.Index.ToString();
 
+				//mxd. Slopes
+				if (s.FloorSlope.GetLengthSq() > 0) 
+				{
+					coll.Add("floorplane_a", Math.Round(s.FloorSlope.x, 3));
+					coll.Add("floorplane_b", Math.Round(s.FloorSlope.y, 3));
+					coll.Add("floorplane_c", Math.Round(s.FloorSlope.z, 3));
+					coll.Add("floorplane_d", Math.Round(s.FloorSlopeOffset, 3));
+				}
+
+				if (s.CeilingSlope.GetLengthSq() > 0) 
+				{
+					coll.Add("ceilingplane_a", Math.Round(s.CeilingSlope.x, 3));
+					coll.Add("ceilingplane_b", Math.Round(s.CeilingSlope.y, 3));
+					coll.Add("ceilingplane_c", Math.Round(s.CeilingSlope.z, 3));
+					coll.Add("ceilingplane_d", Math.Round(s.CeilingSlopeOffset, 3));
+				}
+
 				//mxd. Flags
 				foreach(KeyValuePair<string, bool> flag in s.Flags)
 					if(flag.Value) coll.Add(flag.Key, flag.Value);
diff --git a/Source/Core/Map/MapSet.cs b/Source/Core/Map/MapSet.cs
index 7737cc846..cbafc7779 100644
--- a/Source/Core/Map/MapSet.cs
+++ b/Source/Core/Map/MapSet.cs
@@ -27,6 +27,7 @@ using CodeImp.DoomBuilder.Geometry;
 using CodeImp.DoomBuilder.IO;
 using CodeImp.DoomBuilder.Types;
 using CodeImp.DoomBuilder.Windows;
+using CodeImp.DoomBuilder.VisualModes;
 
 #endregion
 
@@ -3018,25 +3019,26 @@ namespace CodeImp.DoomBuilder.Map
 
 		//mxd
 		/// <summary>This returns a sector if given coordinates are inside one.</summary>
-		public Sector GetSectorByCoordinates(Vector2D pos) {
-			List<Linedef> lines = new List<Linedef>(10);
-
-			foreach (Sector s in sectors) {
-				if(pos.x < s.BBox.Left || pos.x > s.BBox.Right || pos.y < s.BBox.Top || pos.y > s.BBox.Bottom) continue;
-				foreach (Sidedef side in s.Sidedefs) lines.Add(side.Line);
-			}
-
-			Linedef nl = NearestLinedef(lines, pos);
-			if(nl != null) {
-				// Check what side of line we are at
-				if(nl.SideOfLine(pos) < 0f) {
-					// Front side
-					if(nl.Front != null) return nl.Front.Sector;
-				} else {
-					// Back side
-					if(nl.Back != null) return nl.Back.Sector;
-				}
+		public Sector GetSectorByCoordinates(Vector2D pos) 
+		{
+			foreach (Sector s in sectors) 
+			{
+				if (s.Intersect(pos)) return s;
+			}
+			return null;
+		}
+
+		//mxd
+		/// <summary>This returns a sector if given coordinates are inside one.</summary>
+		public Sector GetSectorByCoordinates(Vector2D pos, VisualBlockMap blockmap) 
+		{
+			// Find nearest sectors using the blockmap
+			List<Sector> possiblesectors = blockmap.GetBlock(blockmap.GetBlockCoordinates(pos)).Sectors;
+			foreach(Sector s in possiblesectors) 
+			{
+				if(s.Intersect(pos)) return s;
 			}
+
 			return null;
 		}
 
diff --git a/Source/Core/Map/Sector.cs b/Source/Core/Map/Sector.cs
index 71353a592..d299e1094 100644
--- a/Source/Core/Map/Sector.cs
+++ b/Source/Core/Map/Sector.cs
@@ -72,12 +72,18 @@ namespace CodeImp.DoomBuilder.Map
 		private Triangulation triangles;
 		private FlatVertex[] flatvertices;
 		private ReadOnlyCollection<LabelPositionInfo> labels;
-		private SurfaceEntryCollection surfaceentries;
+		private readonly SurfaceEntryCollection surfaceentries;
 
 		//mxd. Rendering
-		protected Color4 fogColor; //mxd
-		protected bool hasFogColor; //mxd
-		protected bool useOutsideFog; //mxd
+		private Color4 fogColor;
+		private bool hasFogColor;
+		private bool useOutsideFog;
+
+		//mxd. Slopes
+		private Vector3D floorslope;
+		private float flooroffset;
+		private Vector3D ceilslope;
+		private float ceiloffset;
 		
 		#endregion
 
@@ -113,6 +119,12 @@ namespace CodeImp.DoomBuilder.Map
 		public bool HasFogColor { get { return hasFogColor; } }
 		public bool UsesOutsideFog { get { return useOutsideFog; } }
 
+		//mxd. Slopes
+		public Vector3D FloorSlope { get { return floorslope; } set { BeforePropsChange(); floorslope = value; updateneeded = true; } }
+		public float FloorSlopeOffset { get { return flooroffset; } set { BeforePropsChange(); flooroffset = value; updateneeded = true; } }
+		public Vector3D CeilingSlope { get { return ceilslope; } set { BeforePropsChange(); ceilslope = value; updateneeded = true; } }
+		public float CeilingSlopeOffset { get { return ceiloffset; } set { BeforePropsChange(); ceiloffset = value; updateneeded = true; } }
+
 		#endregion
 
 		#region ================== Constructor / Disposer
@@ -229,6 +241,12 @@ namespace CodeImp.DoomBuilder.Map
 			s.rwInt(ref effect);
 			s.rwInt(ref tag);
 			s.rwInt(ref brightness);
+
+			//mxd. Slopes
+			s.rwFloat(ref flooroffset);
+			s.rwVector3D(ref floorslope);
+			s.rwFloat(ref ceiloffset);
+			s.rwVector3D(ref ceilslope);
 		}
 		
 		// After deserialization
@@ -255,6 +273,10 @@ namespace CodeImp.DoomBuilder.Map
 			s.tag = tag;
 			s.flags = new Dictionary<string, bool>(flags); //mxd
 			s.brightness = brightness;
+			s.flooroffset = flooroffset; //mxd
+			s.floorslope = floorslope; //mxd
+			s.ceiloffset = ceiloffset; //mxd
+			s.ceilslope = ceilslope; //mxd
 			s.updateneeded = true;
 			base.CopyPropertiesTo(s);
 		}
@@ -445,8 +467,10 @@ namespace CodeImp.DoomBuilder.Map
 		}
 		
 		// This checks if the given point is inside the sector polygon
-		public bool Intersect(Vector2D p)
+		public bool Intersect(Vector2D p) 
 		{
+			if (MapSet.GetCSFieldBits(p, bbox) != 0) return false; //mxd. Check bounding box
+			
 			uint c = 0;
 			
 			// Go for all sidedefs
@@ -455,6 +479,9 @@ namespace CodeImp.DoomBuilder.Map
 				// Get vertices
 				Vector2D v1 = sd.Line.Start.Position;
 				Vector2D v2 = sd.Line.End.Position;
+
+				//mxd. On top of a vertex?
+				if (p == v1 || p == v2) return true;
 				
 				// Determine min/max values
 				float miny = Math.Min(v1.y, v2.y);
@@ -553,11 +580,11 @@ namespace CodeImp.DoomBuilder.Map
 		//mxd. This updates all properties (Doom/Hexen version)
 		public void Update(int hfloor, int hceil, string tfloor, string tceil, int effect, int tag, int brightness) 
 		{
-			Update(hfloor, hceil, tfloor, tceil, effect, new Dictionary<string, bool>(StringComparer.Ordinal), tag, brightness);
+			Update(hfloor, hceil, tfloor, tceil, effect, new Dictionary<string, bool>(StringComparer.Ordinal), tag, brightness, 0, new Vector3D(), 0, new Vector3D());
 		}
 
 		//mxd. This updates all properties (UDMF version)
-		public void Update(int hfloor, int hceil, string tfloor, string tceil, int effect, Dictionary<string, bool> flags, int tag, int brightness)
+		public void Update(int hfloor, int hceil, string tfloor, string tceil, int effect, Dictionary<string, bool> flags, int tag, int brightness, float flooroffset, Vector3D floorslope, float ceiloffset, Vector3D ceilslope)
 		{
 			BeforePropsChange();
 			
@@ -570,6 +597,10 @@ namespace CodeImp.DoomBuilder.Map
 			this.tag = tag;
 			this.flags = new Dictionary<string, bool>(flags); //mxd
 			this.brightness = brightness;
+			this.flooroffset = flooroffset; //mxd
+			this.floorslope = floorslope; //mxd
+			this.ceiloffset = ceiloffset; //mxd
+			this.ceilslope = ceilslope; //mxd
 			updateneeded = true;
 		}
 
diff --git a/Source/Core/Resources/UDMF.cfg b/Source/Core/Resources/UDMF.cfg
index 6f876788c..adf670c96 100644
--- a/Source/Core/Resources/UDMF.cfg
+++ b/Source/Core/Resources/UDMF.cfg
@@ -48,6 +48,14 @@ managedfields
 		lightlevel;
 		special;
 		id;
+		floorplane_a;
+		floorplane_b;
+		floorplane_c;
+		floorplane_d;
+		ceilingplane_a;
+		ceilingplane_b;
+		ceilingplane_c;
+		ceilingplane_d;
 	}
 	
 	thing
diff --git a/Source/Core/VisualModes/VisualMode.cs b/Source/Core/VisualModes/VisualMode.cs
index 173f59de6..385292101 100644
--- a/Source/Core/VisualModes/VisualMode.cs
+++ b/Source/Core/VisualModes/VisualMode.cs
@@ -42,9 +42,6 @@ namespace CodeImp.DoomBuilder.VisualModes
 		#endregion
 
 		#region ================== Variables
-
-		// 3D Mode thing
-		protected Thing modething;
 		
 		// Graphics
 		protected IRenderer3D renderer;
@@ -65,6 +62,7 @@ namespace CodeImp.DoomBuilder.VisualModes
 		private List<VisualThing> selectedVisualThings;
 		private List<VisualSector> selectedVisualSectors;
 		protected Dictionary<Vertex, VisualVertexPair> vertices;
+		private static Vector2D initialcameraposition;
 		//used in "Play From Here" Action
 		private Thing playerStart;
 		private Vector3D playerStartPosition;
@@ -101,7 +99,6 @@ namespace CodeImp.DoomBuilder.VisualModes
 		{
 			// Initialize
 			this.renderer = General.Map.Renderer3D;
-			//this.renderer3d = (Renderer3D)General.Map.Renderer3D;
 			this.blockmap = new VisualBlockMap();
 			this.allsectors = new Dictionary<Sector, VisualSector>(General.Map.Map.Sectors.Count);
 			this.allthings = new Dictionary<Thing,VisualThing>(General.Map.Map.Things.Count);
@@ -118,35 +115,11 @@ namespace CodeImp.DoomBuilder.VisualModes
 			//mxd. Synch camera position to cursor position or center of the screen in 2d-mode
 			if (General.Settings.GZSynchCameras && General.Editing.Mode is ClassicMode) {
 				ClassicMode oldmode = General.Editing.Mode as ClassicMode;
-				Vector2D pos2d;
-
-				if (oldmode.IsMouseInside)
-					pos2d = new Vector2D(oldmode.MouseMapPos.x, oldmode.MouseMapPos.y);
-				 else
-					pos2d = new Vector2D(General.Map.CRenderer2D.Viewport.Left + General.Map.CRenderer2D.Viewport.Width / 2.0f, General.Map.CRenderer2D.Viewport.Top + General.Map.CRenderer2D.Viewport.Height / 2.0f);
 
-				//if position is inside sector - adjust camera.z accordingly
-				Sector sector = General.Map.Map.GetSectorByCoordinates(pos2d);
-
-				float posz = General.Map.VisualCamera.Position.z;
-				if(sector != null) {
-					int sectorHeight = sector.CeilHeight - sector.FloorHeight;
-					if (General.Map.VisualCamera.Position.z < sector.FloorHeight + 41) {
-						if (sectorHeight < 41) {
-							posz = sector.FloorHeight + sectorHeight / 2;
-						} else {
-							posz = sector.FloorHeight + 41; // same as in doom
-						}
-					} else if(General.Map.VisualCamera.Position.z > sector.CeilHeight) {
-						if(sectorHeight < 41) {
-							posz = sector.FloorHeight + sectorHeight / 2;
-						} else {
-							posz = sector.CeilHeight - 4;
-						}
-					}
-				}
-
-				General.Map.VisualCamera.Position = new Vector3D(pos2d.x, pos2d.y, posz);
+				if(oldmode.IsMouseInside)
+					initialcameraposition = new Vector2D(oldmode.MouseMapPos.x, oldmode.MouseMapPos.y);
+				else
+					initialcameraposition = new Vector2D(General.Map.CRenderer2D.Viewport.Left + General.Map.CRenderer2D.Viewport.Width / 2.0f, General.Map.CRenderer2D.Viewport.Top + General.Map.CRenderer2D.Viewport.Height / 2.0f);
 			}
 		}
 		
@@ -185,13 +158,43 @@ namespace CodeImp.DoomBuilder.VisualModes
 		{
 			base.OnEngage();
 			
-			General.Map.VisualCamera.PositionAtThing();
-			
 			// Update the used textures
 			General.Map.Data.UpdateUsedTextures();
 			
 			// Fill the blockmap
 			FillBlockMap();
+
+			//mxd. Synch camera position to cursor position or center of the screen in 2d-mode
+			if(General.Settings.GZSynchCameras) 
+			{
+				//if position is inside sector - adjust camera.z accordingly
+				Sector sector = General.Map.Map.GetSectorByCoordinates(initialcameraposition, blockmap);
+
+				float posz = General.Map.VisualCamera.Position.z;
+				if(sector != null) 
+				{
+					int sectorHeight = sector.CeilHeight - sector.FloorHeight;
+					if(General.Map.VisualCamera.Position.z < sector.FloorHeight + 41) 
+					{
+						if(sectorHeight < 41)
+							posz = sector.FloorHeight + sectorHeight / 2;
+						else
+							posz = sector.FloorHeight + 41; // same as in doom
+					} 
+					else if(General.Map.VisualCamera.Position.z > sector.CeilHeight) 
+					{
+						if(sectorHeight < 41)
+							posz = sector.FloorHeight + sectorHeight / 2;
+						else
+							posz = sector.CeilHeight - 4;
+					}
+				}
+				General.Map.VisualCamera.Position = new Vector3D(initialcameraposition.x, initialcameraposition.y, posz);
+			} 
+			else 
+			{
+				General.Map.VisualCamera.PositionAtThing();
+			}
 			
 			// Start special input mode
 			General.Interface.EnableProcessing();
@@ -285,7 +288,7 @@ namespace CodeImp.DoomBuilder.VisualModes
 
 				//now check if camera is located inside a sector
 				Vector3D camPos = General.Map.VisualCamera.Position;
-				Sector s = General.Map.Map.GetSectorByCoordinates(new Vector2D(camPos.x, camPos.y));
+				Sector s = General.Map.Map.GetSectorByCoordinates(new Vector2D(camPos.x, camPos.y), blockmap);
 
 				if (s == null) {
 					General.MainWindow.DisplayStatus(StatusType.Warning, "Can't test from current position: cursor is not inside sector!");
@@ -1109,7 +1112,7 @@ namespace CodeImp.DoomBuilder.VisualModes
 			//show form...
 			CenterOnCoordinatesForm form = new CenterOnCoordinatesForm();
 			if(form.ShowDialog() == DialogResult.OK) {
-				Sector s = General.Map.Map.GetSectorByCoordinates(form.Coordinates);
+				Sector s = General.Map.Map.GetSectorByCoordinates(form.Coordinates, blockmap);
 
 				if (s == null) {
 					General.Map.VisualCamera.Position = form.Coordinates;
diff --git a/Source/Core/Windows/SectorEditFormUDMF.Designer.cs b/Source/Core/Windows/SectorEditFormUDMF.Designer.cs
index ee9ff4352..2a772a52a 100644
--- a/Source/Core/Windows/SectorEditFormUDMF.Designer.cs
+++ b/Source/Core/Windows/SectorEditFormUDMF.Designer.cs
@@ -475,10 +475,10 @@
 			this.cbUseFloorLineAngles.AutoSize = true;
 			this.cbUseFloorLineAngles.Location = new System.Drawing.Point(236, 147);
 			this.cbUseFloorLineAngles.Name = "cbUseFloorLineAngles";
-			this.cbUseFloorLineAngles.Size = new System.Drawing.Size(99, 18);
+			this.cbUseFloorLineAngles.Size = new System.Drawing.Size(115, 18);
 			this.cbUseFloorLineAngles.TabIndex = 57;
 			this.cbUseFloorLineAngles.Tag = "";
-			this.cbUseFloorLineAngles.Text = "Use line angles";
+			this.cbUseFloorLineAngles.Text = "Use linedef angles";
 			this.cbUseFloorLineAngles.UseVisualStyleBackColor = true;
 			this.cbUseFloorLineAngles.CheckedChanged += new System.EventHandler(this.cbUseFloorLineAngles_CheckedChanged);
 			// 
@@ -671,10 +671,10 @@
 			this.cbUseCeilLineAngles.AutoSize = true;
 			this.cbUseCeilLineAngles.Location = new System.Drawing.Point(236, 147);
 			this.cbUseCeilLineAngles.Name = "cbUseCeilLineAngles";
-			this.cbUseCeilLineAngles.Size = new System.Drawing.Size(99, 18);
+			this.cbUseCeilLineAngles.Size = new System.Drawing.Size(115, 18);
 			this.cbUseCeilLineAngles.TabIndex = 56;
 			this.cbUseCeilLineAngles.Tag = "";
-			this.cbUseCeilLineAngles.Text = "Use line angles";
+			this.cbUseCeilLineAngles.Text = "Use linedef angles";
 			this.cbUseCeilLineAngles.UseVisualStyleBackColor = true;
 			this.cbUseCeilLineAngles.CheckedChanged += new System.EventHandler(this.cbUseCeilLineAngles_CheckedChanged);
 			// 
@@ -866,7 +866,9 @@
 			this.floorslopecontrol.Name = "floorslopecontrol";
 			this.floorslopecontrol.Size = new System.Drawing.Size(431, 178);
 			this.floorslopecontrol.TabIndex = 0;
+			this.floorslopecontrol.UseLineAngles = false;
 			this.floorslopecontrol.OnValuesChanged += new System.EventHandler(this.floorslopecontrol_OnValuesChanged);
+			this.floorslopecontrol.OnUseLineAnglesChanged += new System.EventHandler(this.floorslopecontrol_OnUseLineAnglesChanged);
 			// 
 			// groupBox4
 			// 
@@ -884,7 +886,9 @@
 			this.ceilingslopecontrol.Name = "ceilingslopecontrol";
 			this.ceilingslopecontrol.Size = new System.Drawing.Size(431, 178);
 			this.ceilingslopecontrol.TabIndex = 1;
+			this.ceilingslopecontrol.UseLineAngles = false;
 			this.ceilingslopecontrol.OnValuesChanged += new System.EventHandler(this.ceilingslopecontrol_OnValuesChanged);
+			this.ceilingslopecontrol.OnUseLineAnglesChanged += new System.EventHandler(this.ceilingslopecontrol_OnUseLineAnglesChanged);
 			// 
 			// tabcustom
 			// 
diff --git a/Source/Core/Windows/SectorEditFormUDMF.cs b/Source/Core/Windows/SectorEditFormUDMF.cs
index df68cb531..f310ed820 100644
--- a/Source/Core/Windows/SectorEditFormUDMF.cs
+++ b/Source/Core/Windows/SectorEditFormUDMF.cs
@@ -6,6 +6,7 @@ using CodeImp.DoomBuilder.GZBuilder.Tools;
 using CodeImp.DoomBuilder.Map;
 using CodeImp.DoomBuilder.Types;
 using CodeImp.DoomBuilder.Geometry;
+using CodeImp.DoomBuilder.Controls;
 
 namespace CodeImp.DoomBuilder.Windows
 {
@@ -20,31 +21,31 @@ namespace CodeImp.DoomBuilder.Windows
 		#region ================== Variables
 
 		private ICollection<Sector> sectors;
-		private List<SectorProperties> sectorProps; //mxd
-		private bool blockUpdate; //mxd
-		private StepsList angleSteps; //mxd
+		private Dictionary<Sector, SectorProperties> sectorprops; //mxd
+		private bool blockupdate; //mxd
+		private StepsList anglesteps; //mxd
 
 		//mxd. Persistent settings
 		private static bool linkCeilingScale;
 		private static bool linkFloorScale;
 		private static bool useFloorLineAngles;
 		private static bool useCeilLineAngles;
+		private static bool useFloorSlopeLineAngles;
+		private static bool useCeilSlopeLineAngles;
 
 		//mxd. Window setup stuff
 		private static Point location = Point.Empty;
-		private static int activeTab;
-
-		//mxd. Slope stuff
-		private static readonly string[] floorslopekeys = { "floorplane_a", "floorplane_b", "floorplane_c", "floorplane_d" };
-		private static readonly string[] ceilslopekeys = { "ceilingplane_a", "ceilingplane_b", "ceilingplane_c", "ceilingplane_d" };
-		
+		private static int activetab;
+	
 		//mxd. Slope pivots
-		//private Vector3D ceilslopenormal;
-		//private Vector3D floorslopenormal;
 		private Vector3D globalceilslopepivot;
 		private Vector3D globalfloorslopepivot;
-		private List<Vector3D> ceilslopepivots;
-		private List<Vector3D> floorslopepivots;
+		private Dictionary<Sector, Vector3D> ceilslopepivots;
+		private Dictionary<Sector, Vector3D> floorslopepivots;
+
+		#endregion
+
+		#region ================== Structs
 
 		private struct SectorProperties //mxd
 		{
@@ -79,17 +80,13 @@ namespace CodeImp.DoomBuilder.Windows
 			public readonly int FloorBrightness;
 			public readonly bool FloorLightAbsoulte;
 
-			//UDMF floor slope
-			public readonly float FloorSlopeX;
-			public readonly float FloorSlopeY;
-			public readonly float FloorSlopeZ;
+			//UDMF slopes. Angles are in degrees
+			public readonly float FloorSlopeAngleXY;
+			public readonly float FloorSlopeAngleZ;
 			public readonly float FloorSlopeOffset;
-
-			//UDMF ceiling slope
-			public readonly float CeilSlopeX;
-			public readonly float CeilSlopeY;
-			public readonly float CeilSlopeZ;
-			public readonly float CeilSlopeOffset;
+			public readonly float CeilingSlopeAngleXY;
+			public readonly float CeilingSlopeAngleZ;
+			public readonly float CeilingSlopeOffset;
 
 			public SectorProperties(Sector s) {
 				Brightness = s.Brightness;
@@ -122,17 +119,24 @@ namespace CodeImp.DoomBuilder.Windows
 				FloorBrightness = s.Fields.GetValue("lightfloor", 0);
 				FloorLightAbsoulte = s.Fields.GetValue("lightfloorabsolute", false);
 
-				//UDMF Ceiling slope
-				CeilSlopeX = UDMFTools.GetFloat(s.Fields, "ceilingplane_a", 0f);
-				CeilSlopeY = UDMFTools.GetFloat(s.Fields, "ceilingplane_b", 0f);
-				CeilSlopeZ = UDMFTools.GetFloat(s.Fields, "ceilingplane_c", 0f);
-				CeilSlopeOffset = UDMFTools.GetFloat(s.Fields, "ceilingplane_d", 0f);
-
-				//UDMF Floor slope
-				FloorSlopeX = UDMFTools.GetFloat(s.Fields, "floorplane_a", 0f);
-				FloorSlopeY = UDMFTools.GetFloat(s.Fields, "floorplane_b", 0f);
-				FloorSlopeZ = UDMFTools.GetFloat(s.Fields, "floorplane_c", 0f);
-				FloorSlopeOffset = UDMFTools.GetFloat(s.Fields, "floorplane_d", 0f);
+				//UDMF slopes
+				if (s.FloorSlope.GetLengthSq() > 0) {
+					FloorSlopeAngleXY = (float)Math.Round(Angle2D.RadToDeg(s.FloorSlope.GetAngleXY()), 3);
+					FloorSlopeAngleZ = (float)Math.Round(Angle2D.RadToDeg(s.FloorSlope.GetAngleZ()), 3);
+				} else {
+					FloorSlopeAngleXY = 0;
+					FloorSlopeAngleZ = 0;
+				}
+				FloorSlopeOffset = s.FloorSlopeOffset;
+
+				if (s.CeilingSlope.GetLengthSq() > 0) {
+					CeilingSlopeAngleXY = (float)Math.Round(Angle2D.RadToDeg(s.CeilingSlope.GetAngleXY()), 3);
+					CeilingSlopeAngleZ = (float)Math.Round(Angle2D.RadToDeg(s.CeilingSlope.GetAngleZ()), 3);
+				} else {
+					CeilingSlopeAngleXY = 0;
+					CeilingSlopeAngleZ = 0;
+				}
+				CeilingSlopeOffset = s.CeilingSlopeOffset;
 			}
 		}
 
@@ -151,7 +155,7 @@ namespace CodeImp.DoomBuilder.Windows
 			if(location != Point.Empty) {
 				this.StartPosition = FormStartPosition.Manual;
 				this.Location = location;
-				if(activeTab > 0) tabs.SelectTab(activeTab);
+				if(activetab > 0) tabs.SelectTab(activetab);
 			}
 
 			// Fill flags list
@@ -187,13 +191,8 @@ namespace CodeImp.DoomBuilder.Windows
 			cbUseCeilLineAngles.Checked = useCeilLineAngles;
 			cbUseFloorLineAngles.Checked = useFloorLineAngles;
 
-			// Slopes stuff
-			//ceilslopenormal = new Vector3D(0f, 0f, -1f);
-			//floorslopenormal = new Vector3D(0f, 0f, 1f);
-			globalceilslopepivot = new Vector3D();
-			globalfloorslopepivot = new Vector3D();
-
-			//TODO: set stored pivot mode
+			floorslopecontrol.UseLineAngles = useFloorSlopeLineAngles;
+			ceilingslopecontrol.UseLineAngles = useCeilSlopeLineAngles;
 		}
 
 		#endregion
@@ -202,12 +201,12 @@ namespace CodeImp.DoomBuilder.Windows
 
 		// This sets up the form to edit the given sectors
 		public void Setup(ICollection<Sector> sectors) {
-			blockUpdate = true; //mxd
+			blockupdate = true; //mxd
 
 			// Keep this list
 			this.sectors = sectors;
 			if(sectors.Count > 1) this.Text = "Edit Sectors (" + sectors.Count + ")";
-			sectorProps = new List<SectorProperties>(); //mxd
+			sectorprops = new Dictionary<Sector, SectorProperties>(sectors.Count); //mxd
 
 			//mxd. Make undo
 			string undodesc = "sector";
@@ -286,24 +285,19 @@ namespace CodeImp.DoomBuilder.Windows
 			lightColor.SetValueFrom(sc.Fields);
 
 			//Slopes
-			SetSlopeValues(sc.Fields, true);
-			/*float ceilslopex = sc.Fields.GetValue("ceilingplane_a", 0f);
-			float ceilslopey = sc.Fields.GetValue("ceilingplane_b", 0f);
-			float ceilslopez = sc.Fields.GetValue("ceilingplane_c", 0f);
-			float ceilslopeoffset = sc.Fields.GetValue("ceilingplane_d", 0f);
-
-			Vector3D ceilnormal = new Vector3D(ceilslopex, ceilslopey, ceilslopez);
-			ceilingslopecontrol.SetValues( (float)Math.Round(Angle2D.RadToDeg(ceilnormal.GetAngleXY()), 1),
-				(float)Math.Round(Angle2D.RadToDeg(ceilnormal.GetAngleZ()), 1), ceilslopeoffset, true);
-
-			float floorslopex = sc.Fields.GetValue("floorplane_a", 0f);
-			float floorslopey = sc.Fields.GetValue("floorplane_b", 0f);
-			float floorslopez = sc.Fields.GetValue("floorplane_c", 0f);
-			float floorslopeoffset = sc.Fields.GetValue("floorplane_d", 0f);
+			if (sc.CeilingSlope.GetLengthSq() > 0) {
+				ceilingslopecontrol.SetValues((float) Math.Round(Angle2D.RadToDeg(sc.CeilingSlope.GetAngleXY()), 3),
+				                              (float) -Math.Round(Angle2D.RadToDeg(sc.CeilingSlope.GetAngleZ()) + 90, 3), -sc.CeilingSlopeOffset, true);
+			} else {
+				ceilingslopecontrol.SetValues(0f, 0f, -sc.CeilingSlopeOffset, true);
+			}
 
-			Vector3D floornormal = new Vector3D(floorslopex, floorslopey, floorslopez);
-			floorslopecontrol.SetValues((float)Math.Round(Angle2D.RadToDeg(floornormal.GetAngleXY()), 1),
-				(float)Math.Round(Angle2D.RadToDeg(floornormal.GetAngleZ()), 1), floorslopeoffset, true);*/
+			if (sc.FloorSlope.GetLengthSq() > 0) {
+				floorslopecontrol.SetValues((float) Math.Round(Angle2D.RadToDeg(sc.FloorSlope.GetAngleXY()), 3),
+				                            (float) Math.Round(Angle2D.RadToDeg(sc.FloorSlope.GetAngleZ()) + 90, 3), -sc.FloorSlopeOffset, true);
+			} else {
+				floorslopecontrol.SetValues(0f, 0f, -sc.FloorSlopeOffset, true);
+			}
 
 			// Action
 			tagSelector.Setup(UniversalType.SectorTag); //mxd
@@ -312,10 +306,11 @@ namespace CodeImp.DoomBuilder.Windows
 			// Custom fields
 			fieldslist.SetValues(sc.Fields, true);
 
-			angleSteps = new StepsList();
+			anglesteps = new StepsList();
 
-			floorslopepivots = new List<Vector3D>(sectors.Count);
-			ceilslopepivots = new List<Vector3D>(sectors.Count);
+			//mxd. Slope pivots
+			floorslopepivots = new Dictionary<Sector, Vector3D>(sectors.Count);
+			ceilslopepivots = new Dictionary<Sector, Vector3D>(sectors.Count);
 
 			////////////////////////////////////////////////////////////////////////
 			// Now go for all sectors and change the options when a setting is different
@@ -323,6 +318,10 @@ namespace CodeImp.DoomBuilder.Windows
 
 			// Go for all sectors
 			foreach(Sector s in sectors) {
+				//mxd. Store initial properties
+				SectorProperties sp = new SectorProperties(s);
+				sectorprops.Add(s, sp);
+
 				// Flags
 				foreach(CheckBox c in flags.Checkboxes) {
 					if(c.CheckState == CheckState.Indeterminate) continue; //mxd
@@ -402,16 +401,8 @@ namespace CodeImp.DoomBuilder.Windows
 				lightColor.SetValueFrom(s.Fields);
 
 				//Slopes
-				SetSlopeValues(s.Fields, false);
-				/*if(s.Fields.GetValue("ceilingplane_a", 0f).ToString() != ceilslopex.Text) ceilslopex.Text = "";
-				if(s.Fields.GetValue("ceilingplane_b", 0f).ToString() != ceilslopey.Text) ceilslopey.Text = "";
-				if(s.Fields.GetValue("ceilingplane_c", 0f).ToString() != ceilslopez.Text) ceilslopez.Text = "";
-				if(s.Fields.GetValue("ceilingplane_d", 0f).ToString() != ceilslopeoffset.Text) ceilslopeoffset.Text = "";
-
-				if(s.Fields.GetValue("floorplane_a", 0f).ToString() != floorslopex.Text) floorslopex.Text = "";
-				if(s.Fields.GetValue("floorplane_b", 0f).ToString() != floorslopey.Text) floorslopey.Text = "";
-				if(s.Fields.GetValue("floorplane_c", 0f).ToString() != floorslopez.Text) floorslopez.Text = "";
-				if(s.Fields.GetValue("floorplane_d", 0f).ToString() != floorslopeoffset.Text) floorslopeoffset.Text = "";*/
+				ceilingslopecontrol.SetValues(sp.CeilingSlopeAngleXY, -(sp.CeilingSlopeAngleZ + 90), sp.CeilingSlopeOffset, false);
+				floorslopecontrol.SetValues(sp.FloorSlopeAngleXY, sp.FloorSlopeAngleZ + 90, -sp.FloorSlopeOffset, false);
 
 				// Action
 				if(s.Tag != sc.Tag) tagSelector.ClearTag(); //mxd
@@ -420,9 +411,6 @@ namespace CodeImp.DoomBuilder.Windows
 				s.Fields.BeforeFieldsChange(); //mxd
 				fieldslist.SetValues(s.Fields, false);
 
-				//mxd. Store initial properties
-				sectorProps.Add(new SectorProperties(s));
-
 				//mxd. Angle steps
 				int angle;
 				foreach(Sidedef side in s.Sidedefs){
@@ -431,47 +419,23 @@ namespace CodeImp.DoomBuilder.Windows
 					else
 						angle = General.ClampAngle(90 - side.Line.AngleDeg);
 
-					if(!angleSteps.Contains(angle)) angleSteps.Add(angle);
+					if(!anglesteps.Contains(angle)) anglesteps.Add(angle);
 				}
 
 				//Slope pivots
-				Vector3D ceilpivot = new Vector3D(s.BBox.X + s.BBox.Width / 2, s.BBox.Y + s.BBox.Height / 2, s.CeilHeight);
 				Vector3D floorpivot = new Vector3D(s.BBox.X + s.BBox.Width / 2, s.BBox.Y + s.BBox.Height / 2, s.FloorHeight);
+				Vector3D ceilpivot = new Vector3D(s.BBox.X + s.BBox.Width / 2, s.BBox.Y + s.BBox.Height / 2, s.CeilHeight);
 
 				globalfloorslopepivot += floorpivot;
 				globalceilslopepivot += ceilpivot;
 
-				floorslopepivots.Add(floorpivot);
-				ceilslopepivots.Add(ceilpivot);
+				floorslopepivots.Add(s, floorpivot);
+				ceilslopepivots.Add(s, ceilpivot);
 			}
 
 			globalfloorslopepivot /= sectors.Count;
 			globalceilslopepivot /= sectors.Count;
 
-			//mxd. Set ceiling slope controls
-			/*if (!string.IsNullOrEmpty(ceilslopex.Text) && !string.IsNullOrEmpty(ceilslopey.Text) && !string.IsNullOrEmpty(ceilslopez.Text)) {
-				Vector3D v = new Vector3D(ceilslopex.GetResultFloat(0f), ceilslopey.GetResultFloat(0f), ceilslopez.GetResultFloat(0f));
-				if (v.x != 0 || v.y != 0 || v.z != 0) {
-					ceilslopeangle.Value = (int) Math.Round(Angle2D.RadToDeg(v.GetAngleXY()));
-					ceilsloperoll.Value = (int) Math.Round(Angle2D.RadToDeg(v.GetAngleZ()));
-					ceilslopeanglelabel.Text = ceilslopeangle.Value + "\u00B0";
-					ceilsloperolllabel.Text = ceilsloperoll.Value + "\u00B0";
-				}
-			}
-
-			//mxd. Set floor slope controls
-			if(!string.IsNullOrEmpty(floorslopex.Text) && !string.IsNullOrEmpty(floorslopey.Text) && !string.IsNullOrEmpty(floorslopez.Text)) {
-				Vector3D v = new Vector3D(floorslopex.GetResultFloat(0f), floorslopey.GetResultFloat(0f), floorslopez.GetResultFloat(0f));
-				if(v.x != 0 || v.y != 0 || v.z != 0) {
-					//ceilslopeangle.Value = General.ClampAngle((int)Math.Round(Angle2D.RadToDeg(v.GetAngleXY())));
-					//ceilsloperoll.Value = (int)Math.Round(Angle2D.RadToDeg(v.GetAngleZ()));
-					//ceilslopeanglelabel.Text = ceilslopeangle.Value + "\u00B0";
-					//ceilsloperolllabel.Text = ceilsloperoll.Value + "\u00B0";
-					floorsloperotation.Text = ((int)Math.Round(Angle2D.RadToDeg(v.GetAngleXY()))).ToString();
-					floorslopeangle.Text = ((int)Math.Round(Angle2D.RadToDeg(v.GetAngleZ()))).ToString();
-				}
-			}*/
-
 			//mxd. Update slope controls
 			#if DEBUG
 			ceilingslopecontrol.UpdateControls();
@@ -482,66 +446,34 @@ namespace CodeImp.DoomBuilder.Windows
 			UpdateSectorHeight();
 
 			//mxd. Angle steps
-			angleSteps.Sort();
-			if(useCeilLineAngles) ceilRotation.StepValues = angleSteps;
-			if(useFloorLineAngles) floorRotation.StepValues = angleSteps;
+			anglesteps.Sort();
+			if(useCeilLineAngles) ceilRotation.StepValues = anglesteps;
+			if(useFloorLineAngles) floorRotation.StepValues = anglesteps;
+			if(useCeilSlopeLineAngles) ceilingslopecontrol.StepValues = anglesteps;
+			if(useFloorSlopeLineAngles) floorslopecontrol.StepValues = anglesteps;
 
-			blockUpdate = false; //mxd
-		}
-
-		//mxd
-		private void SetSlopeValues(UniFields source, bool first) {
-			#if !DEBUG
-			return;
-			#endif
-
-
-			float x = source.GetValue("ceilingplane_a", 0f);
-			float y = source.GetValue("ceilingplane_b", 0f);
-			float z = source.GetValue("ceilingplane_c", 0f);
-			float offset = source.GetValue("ceilingplane_d", 0f);
-
-			Vector3D ceilnormal = new Vector3D(x, y, z);
-			ceilingslopecontrol.SetValues((float)Math.Round(Angle2D.RadToDeg(ceilnormal.GetAngleXY()), 1),
-				(float)Math.Round(Angle2D.RadToDeg(ceilnormal.GetAngleZ()), 1), offset, first);
-
-			x = source.GetValue("floorplane_a", 0f);
-			y = source.GetValue("floorplane_b", 0f);
-			z = source.GetValue("floorplane_c", 0f);
-			offset = source.GetValue("floorplane_d", 0f);
-
-			Vector3D floornormal = new Vector3D(x, y, z);
-			floorslopecontrol.SetValues((float)Math.Round(Angle2D.RadToDeg(floornormal.GetAngleXY()), 1),
-				(float)Math.Round(Angle2D.RadToDeg(floornormal.GetAngleZ()), 1), offset, first);
+			blockupdate = false; //mxd
 		}
 
 		// This updates the sector height field
 		private void UpdateSectorHeight() {
-			int delta = 0;
-			int index = -1; //mxd
-			int i = 0; //mxd
+			int delta = int.MinValue;
 
 			// Check all selected sectors
 			foreach(Sector s in sectors) {
-				if(index == -1) {
+				if(delta == int.MinValue) {
 					// First sector in list
 					delta = s.CeilHeight - s.FloorHeight;
-					index = i; //mxd
 				} else if(delta != (s.CeilHeight - s.FloorHeight)) {
 					// We can't show heights because the delta
 					// heights for the sectors is different
-					index = -1;
+					delta = int.MinValue;
 					break;
 				}
-
-				i++;
 			}
 
-			if(index > -1) {
-				int fh = floorheight.GetResult(sectorProps[index].FloorHeight); //mxd
-				int ch = ceilingheight.GetResult(sectorProps[index].CeilHeight); //mxd
-				int height = ch - fh;
-				sectorheight.Text = height.ToString();
+			if(delta != int.MinValue) {
+				sectorheight.Text = delta.ToString();
 				sectorheight.Visible = true;
 				sectorheightlabel.Visible = true;
 			} else {
@@ -623,19 +555,6 @@ namespace CodeImp.DoomBuilder.Windows
 					float val = General.Clamp(desaturation.GetResultFloat(s.Fields.GetValue("desaturation", 0f)), 0f, 1f);
 					UDMFTools.SetFloat(s.Fields, "desaturation", val, 0f);
 				}
-
-				//clear slope props if all values are 0
-				if(UDMFTools.GetFloat(s.Fields, ceilslopekeys[0]) == 0 && UDMFTools.GetFloat(s.Fields, ceilslopekeys[1]) == 0
-					&& UDMFTools.GetFloat(s.Fields, ceilslopekeys[2]) == 0) {
-					UDMFTools.ClearFields(s.Fields, ceilslopekeys);
-					s.UpdateNeeded = true;
-				}
-
-				if(UDMFTools.GetFloat(s.Fields, floorslopekeys[0]) == 0 && UDMFTools.GetFloat(s.Fields, floorslopekeys[1]) == 0
-					&& UDMFTools.GetFloat(s.Fields, floorslopekeys[2]) == 0) {
-					UDMFTools.ClearFields(s.Fields, floorslopekeys);
-					s.UpdateNeeded = true;
-				}
 			}
 
 			// Update the used textures
@@ -646,6 +565,8 @@ namespace CodeImp.DoomBuilder.Windows
 			linkFloorScale = floorScale.LinkValues;
 			useCeilLineAngles = cbUseCeilLineAngles.Checked;
 			useFloorLineAngles = cbUseFloorLineAngles.Checked;
+			useCeilSlopeLineAngles = ceilingslopecontrol.UseLineAngles;
+			useFloorSlopeLineAngles = floorslopecontrol.UseLineAngles;
 
 			// Done
 			General.Map.IsChanged = true;
@@ -670,7 +591,7 @@ namespace CodeImp.DoomBuilder.Windows
 		//mxd
 		private void SectorEditFormUDMF_FormClosing(object sender, FormClosingEventArgs e) {
 			location = this.Location;
-			activeTab = tabs.SelectedIndex;
+			activetab = tabs.SelectedIndex;
 		}
 
 		private void SectorEditFormUDMF_HelpRequested(object sender, HelpEventArgs hlpevent) {
@@ -691,11 +612,11 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void cbUseCeilLineAngles_CheckedChanged(object sender, EventArgs e) {
-			ceilRotation.StepValues = (cbUseCeilLineAngles.Checked ? angleSteps : null);
+			ceilRotation.StepValues = (cbUseCeilLineAngles.Checked ? anglesteps : null);
 		}
 
 		private void cbUseFloorLineAngles_CheckedChanged(object sender, EventArgs e) {
-			floorRotation.StepValues = (cbUseFloorLineAngles.Checked ? angleSteps : null);
+			floorRotation.StepValues = (cbUseFloorLineAngles.Checked ? anglesteps : null);
 		}
 
 		#endregion
@@ -703,17 +624,16 @@ namespace CodeImp.DoomBuilder.Windows
 		#region mxd. Sector Realtime events
 
 		private void ceilingheight_WhenTextChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			//restore values
 			if(string.IsNullOrEmpty(ceilingheight.Text)) {
 				foreach(Sector s in sectors)
-					s.CeilHeight = sectorProps[i++].CeilHeight;
+					s.CeilHeight = sectorprops[s].CeilHeight;
 			//update values
 			} else {
 				foreach(Sector s in sectors)
-					s.CeilHeight = ceilingheight.GetResult(sectorProps[i++].CeilHeight);
+					s.CeilHeight = ceilingheight.GetResult(sectorprops[s].CeilHeight);
 			}
 
 			UpdateSectorHeight();
@@ -723,17 +643,16 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void floorheight_WhenTextChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			//restore values
 			if(string.IsNullOrEmpty(floorheight.Text)) {
 				foreach(Sector s in sectors)
-					s.FloorHeight = sectorProps[i++].FloorHeight;
+					s.FloorHeight = sectorprops[s].FloorHeight;
 			//update values
 			} else {
 				foreach(Sector s in sectors)
-					s.FloorHeight = floorheight.GetResult(sectorProps[i++].FloorHeight);
+					s.FloorHeight = floorheight.GetResult(sectorprops[s].FloorHeight);
 			}
 
 			UpdateSectorHeight();
@@ -743,13 +662,12 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void brightness_WhenTextChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			//restore values
 			if(string.IsNullOrEmpty(brightness.Text)) {
 				foreach(Sector s in sectors)
-					s.Brightness = sectorProps[i++].Brightness;
+					s.Brightness = sectorprops[s].Brightness;
 			//update values
 			} else {
 				//clamp value?
@@ -761,7 +679,7 @@ namespace CodeImp.DoomBuilder.Windows
 				}
 
 				foreach(Sector s in sectors)
-					s.Brightness = General.Clamp(brightness.GetResult(sectorProps[i++].Brightness), General.Map.FormatInterface.MinBrightness, General.Map.FormatInterface.MaxBrightness);
+					s.Brightness = General.Clamp(brightness.GetResult(sectorprops[s].Brightness), General.Map.FormatInterface.MinBrightness, General.Map.FormatInterface.MaxBrightness);
 			}
 
 			General.Map.IsChanged = true;
@@ -769,14 +687,12 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void ceilingtex_OnValueChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
+			if(blockupdate)	return;
 
 			//restore values
 			if(string.IsNullOrEmpty(ceilingtex.TextureName)) {
-				int i = 0;
-
 				foreach(Sector s in sectors)
-					s.SetCeilTexture(sectorProps[i++].CeilTexture);
+					s.SetCeilTexture(sectorprops[s].CeilTexture);
 			//update values
 			} else {
 				foreach(Sector s in sectors)
@@ -791,14 +707,12 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void floortex_OnValueChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
+			if(blockupdate)	return;
 
 			//restore values
 			if(string.IsNullOrEmpty(floortex.TextureName)) {
-				int i = 0;
-
 				foreach(Sector s in sectors)
-					s.SetFloorTexture(sectorProps[i++].FloorTexture);
+					s.SetFloorTexture(sectorprops[s].FloorTexture);
 			//update values
 			} else {
 				foreach(Sector s in sectors)
@@ -814,20 +728,18 @@ namespace CodeImp.DoomBuilder.Windows
 
 		private void floorRotation_WhenTextChanged(object sender, EventArgs e) {
 			floorAngleControl.Angle = (int)General.ClampAngle(360 - floorRotation.GetResultFloat(0));
-
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			//restore values
 			if(string.IsNullOrEmpty(floorRotation.Text)) {
 				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "rotationfloor", sectorProps[i++].FloorRotation, 0f);
+					UDMFTools.SetFloat(s.Fields, "rotationfloor", sectorprops[s].FloorRotation, 0f);
 					s.UpdateNeeded = true;
 				}
 			//update values
 			} else {
 				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "rotationfloor", floorRotation.GetResultFloat(sectorProps[i++].FloorRotation), 0f);
+					UDMFTools.SetFloat(s.Fields, "rotationfloor", floorRotation.GetResultFloat(sectorprops[s].FloorRotation), 0f);
 					s.UpdateNeeded = true;
 				}
 			}
@@ -838,20 +750,18 @@ namespace CodeImp.DoomBuilder.Windows
 
 		private void ceilRotation_WhenTextChanged(object sender, EventArgs e) {
 			ceilAngleControl.Angle = (int)General.ClampAngle(360 - ceilRotation.GetResultFloat(0));
-
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			//restore values
 			if(string.IsNullOrEmpty(ceilRotation.Text)) {
 				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "rotationceiling", sectorProps[i++].CeilRotation, 0f);
+					UDMFTools.SetFloat(s.Fields, "rotationceiling", sectorprops[s].CeilRotation, 0f);
 					s.UpdateNeeded = true;
 				}
 			//update values
 			} else {
 				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "rotationceiling", ceilRotation.GetResultFloat(sectorProps[i++].CeilRotation), 0f);
+					UDMFTools.SetFloat(s.Fields, "rotationceiling", ceilRotation.GetResultFloat(sectorprops[s].CeilRotation), 0f);
 					s.UpdateNeeded = true;
 				}
 			}
@@ -861,11 +771,10 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void lightColor_OnValueChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			foreach(Sector s in sectors) {
-				lightColor.ApplyTo(s.Fields, sectorProps[i++].LightColor);
+				lightColor.ApplyTo(s.Fields, sectorprops[s].LightColor);
 				s.UpdateNeeded = true;
 			}
 
@@ -874,11 +783,10 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void fadeColor_OnValueChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			foreach(Sector s in sectors) {
-				fadeColor.ApplyTo(s.Fields, sectorProps[i++].FadeColor);
+				fadeColor.ApplyTo(s.Fields, sectorprops[s].FadeColor);
 				s.UpdateNeeded = true;
 			}
 
@@ -891,11 +799,10 @@ namespace CodeImp.DoomBuilder.Windows
 		#region mxd. Ceiling/Floor realtime events
 
 		private void ceilOffsets_OnValuesChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			foreach(Sector s in sectors) {
-				ceilOffsets.ApplyTo(s.Fields, General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset, sectorProps[i].CeilOffsetX, sectorProps[i++].CeilOffsetY);
+				ceilOffsets.ApplyTo(s.Fields, General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset, sectorprops[s].CeilOffsetX, sectorprops[s].CeilOffsetY);
 				s.UpdateNeeded = true;
 			}
 
@@ -904,11 +811,10 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void floorOffsets_OnValuesChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			foreach(Sector s in sectors) {
-				floorOffsets.ApplyTo(s.Fields, General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset, sectorProps[i].FloorOffsetX, sectorProps[i++].FloorOffsetY);
+				floorOffsets.ApplyTo(s.Fields, General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset, sectorprops[s].FloorOffsetX, sectorprops[s].FloorOffsetY);
 				s.UpdateNeeded = true;
 			}
 
@@ -917,12 +823,10 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void ceilScale_OnValuesChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
-
-			int i = 0;
+			if(blockupdate)	return;
 
 			foreach(Sector s in sectors) {
-				ceilScale.ApplyTo(s.Fields, General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset, sectorProps[i].CeilScaleX, sectorProps[i++].CeilScaleY);
+				ceilScale.ApplyTo(s.Fields, General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset, sectorprops[s].CeilScaleX, sectorprops[s].CeilScaleY);
 				s.UpdateNeeded = true;
 			}
 
@@ -931,11 +835,10 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void floorScale_OnValuesChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			foreach(Sector s in sectors) {
-				floorScale.ApplyTo(s.Fields, General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset, sectorProps[i].FloorScaleX, sectorProps[i++].FloorScaleY);
+				floorScale.ApplyTo(s.Fields, General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset, sectorprops[s].FloorScaleX, sectorprops[s].FloorScaleY);
 				s.UpdateNeeded = true;
 			}
 
@@ -944,13 +847,12 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void ceilBrightness_WhenTextChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			//restore values
 			if(string.IsNullOrEmpty(ceilBrightness.Text)) {
 				foreach(Sector s in sectors) {
-					UDMFTools.SetInteger(s.Fields, "lightceiling", sectorProps[i++].CeilBrightness, 0);
+					UDMFTools.SetInteger(s.Fields, "lightceiling", sectorprops[s].CeilBrightness, 0);
 					s.UpdateNeeded = true;
 				}
 			//update values
@@ -963,7 +865,7 @@ namespace CodeImp.DoomBuilder.Windows
 						absolute = true;
 					}
 
-					int value = General.Clamp(ceilBrightness.GetResult(sectorProps[i++].CeilBrightness), (absolute ? 0 : -255), 255);
+					int value = General.Clamp(ceilBrightness.GetResult(sectorprops[s].CeilBrightness), (absolute ? 0 : -255), 255);
 					UDMFTools.SetInteger(s.Fields, "lightceiling", value, 0);
 					s.UpdateNeeded = true;
 				}
@@ -974,13 +876,12 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void floorBrightness_WhenTextChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
-			int i = 0;
+			if(blockupdate)	return;
 
 			//restore values
 			if(string.IsNullOrEmpty(floorBrightness.Text)) {
 				foreach(Sector s in sectors) {
-					UDMFTools.SetInteger(s.Fields, "lightfloor", sectorProps[i++].FloorBrightness, 0);
+					UDMFTools.SetInteger(s.Fields, "lightfloor", sectorprops[s].FloorBrightness, 0);
 					s.UpdateNeeded = true;
 				}
 			//update values
@@ -993,7 +894,7 @@ namespace CodeImp.DoomBuilder.Windows
 						absolute = true;
 					}
 
-					int value = General.Clamp(floorBrightness.GetResult(sectorProps[i++].FloorBrightness), (absolute ? 0 : -255), 255);
+					int value = General.Clamp(floorBrightness.GetResult(sectorprops[s].FloorBrightness), (absolute ? 0 : -255), 255);
 					UDMFTools.SetInteger(s.Fields, "lightfloor", value, 0);
 					s.UpdateNeeded = true;
 				}
@@ -1004,7 +905,7 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void ceilLightAbsolute_CheckedChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
+			if(blockupdate)	return;
 
 			if(ceilLightAbsolute.Checked) {
 				foreach(Sector s in sectors) {
@@ -1012,18 +913,14 @@ namespace CodeImp.DoomBuilder.Windows
 					s.UpdateNeeded = true;
 				}
 			} else if(ceilLightAbsolute.CheckState == CheckState.Indeterminate) {
-				int i = 0;
-				
 				foreach(Sector s in sectors) {
-					if(sectorProps[i].CeilLightAbsoulte) {
+					if(sectorprops[s].CeilLightAbsoulte) {
 						s.Fields["lightceilingabsolute"] = new UniValue(UniversalType.Boolean, true);
 						s.UpdateNeeded = true;
 					}else if(s.Fields.ContainsKey("lightceilingabsolute")) {
 						s.Fields.Remove("lightceilingabsolute");
 						s.UpdateNeeded = true;
 					}
-
-					i++;
 				}
 			} else {
 				foreach(Sector s in sectors) {
@@ -1039,7 +936,7 @@ namespace CodeImp.DoomBuilder.Windows
 		}
 
 		private void floorLightAbsolute_CheckedChanged(object sender, EventArgs e) {
-			if(blockUpdate)	return;
+			if(blockupdate)	return;
 
 			if(floorLightAbsolute.Checked){
 				foreach(Sector s in sectors) {
@@ -1047,18 +944,14 @@ namespace CodeImp.DoomBuilder.Windows
 					s.UpdateNeeded = true;
 				}
 			} else if(floorLightAbsolute.CheckState == CheckState.Indeterminate) {
-				int i = 0;
-
 				foreach(Sector s in sectors) {
-					if(sectorProps[i].FloorLightAbsoulte) {
+					if(sectorprops[s].FloorLightAbsoulte) {
 						s.Fields["lightfloorabsolute"] = new UniValue(UniversalType.Boolean, true);
 						s.UpdateNeeded = true;
 					} else if(s.Fields.ContainsKey("lightfloorabsolute")) {
 						s.Fields.Remove("lightfloorabsolute");
 						s.UpdateNeeded = true;
 					}
-
-					i++;
 				}
 			} else {
 				foreach(Sector s in sectors) {
@@ -1077,349 +970,108 @@ namespace CodeImp.DoomBuilder.Windows
 
 		#region mxd. Slopes realtime events
 
-		/*private void resetfloorslope_Click(object sender, EventArgs e) {
-			foreach(Sector s in sectors) {
-				UDMFTools.ClearFields(s.Fields, floorslopekeys);
-				s.UpdateNeeded = true;
-			}
-
-			blockUpdate = true;
-			floorslopex.Text = "0";
-			floorslopey.Text = "0";
-			floorslopez.Text = "0";
-			floorslopeoffset.Text = "0";
-			blockUpdate = false;
-
-			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
-		}*/
-
-		private void floorslopex_WhenTextChanged(object sender, EventArgs e) {
-			/*if(blockUpdate) return;
-			int i = 0;
-
-			//restore values
-			if(string.IsNullOrEmpty(floorslopex.Text)) {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "floorplane_a", sectorProps[i++].FloorSlopeX, float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			//update values
-			} else {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "floorplane_a", floorslopex.GetResultFloat(sectorProps[i++].FloorSlopeX), float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			}
-
-			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);*/
-		}
+		private void ceilingslopecontrol_OnValuesChanged(object sender, EventArgs e) 
+		{
+			if(blockupdate) return;
 
-		private void floorslopey_WhenTextChanged(object sender, EventArgs e) {
-			/*if(blockUpdate) return;
+			float anglexy, anglez;
 			int i = 0;
 
-			//restore values
-			if(string.IsNullOrEmpty(floorslopey.Text)) {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "floorplane_b", sectorProps[i++].FloorSlopeY, float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			//update values
-			} else {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "floorplane_b", floorslopey.GetResultFloat(sectorProps[i++].FloorSlopeY), float.MinValue);
-					s.UpdateNeeded = true;
-				}
+			//Set or restore values
+			foreach(Sector s in sectors) 
+			{
+				anglexy = (float.IsNaN(ceilingslopecontrol.AngleXY) ? sectorprops[s].CeilingSlopeAngleXY : ceilingslopecontrol.AngleXY);
+				anglez = (float.IsNaN(ceilingslopecontrol.AngleZ) ? sectorprops[s].CeilingSlopeAngleZ : ceilingslopecontrol.AngleZ + 90);
+				
+				/*if (s.CeilingSlope.GetLengthSq() > 0) {
+					s.CeilingSlopeOffset = SetSlopeOffset(ceilingslopecontrol.Offset, sectorprops[i].CeilingSlopeOffset, i);
+				} else {
+					s.CeilingSlopeOffset = s.CeilHeight;
+				}*/
+				
+				s.CeilingSlope = Vector3D.FromAngleXYZ(Angle2D.DegToRad(anglexy) + Angle2D.PI, Angle2D.DegToRad(-anglez));
+				s.CeilingSlopeOffset = SetSlopeOffset(s, -ceilingslopecontrol.Offset, ceilingslopecontrol.PivotMode, false);
+				s.UpdateNeeded = true;
+				i++;
 			}
 
 			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);*/
+			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
 		}
 
-		private void floorslopez_WhenTextChanged(object sender, EventArgs e) {
-			/*if(blockUpdate) return;
-			int i = 0;
-
-			//restore values
-			if(string.IsNullOrEmpty(floorslopez.Text)) {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "floorplane_c", sectorProps[i++].FloorSlopeZ, float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			//update values
-			} else {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "floorplane_c", floorslopez.GetResultFloat(sectorProps[i++].FloorSlopeZ), float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			}
-
-			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);*/
-		}
+		private void floorslopecontrol_OnValuesChanged(object sender, EventArgs e) 
+		{
+			if(blockupdate) return;
 
-		private void floorslopeoffset_WhenTextChanged(object sender, EventArgs e) {
-			/*if(blockUpdate) return;
+			float anglexy, anglez;
 			int i = 0;
 
-			//restore values
-			if(string.IsNullOrEmpty(floorslopeoffset.Text)) {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "floorplane_d", sectorProps[i++].FloorSlopeOffset, float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			//update values
-			} else {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "floorplane_d", floorslopeoffset.GetResultFloat(sectorProps[i++].FloorSlopeOffset), float.MinValue);
-					s.UpdateNeeded = true;
-				}
+			//Set or restore values
+			foreach(Sector s in sectors)
+			{
+				anglexy = (float.IsNaN(floorslopecontrol.AngleXY) ? sectorprops[s].FloorSlopeAngleXY : floorslopecontrol.AngleXY);
+				anglez = (float.IsNaN(floorslopecontrol.AngleZ) ? sectorprops[s].FloorSlopeAngleZ : floorslopecontrol.AngleZ + 90);
+				s.FloorSlope = Vector3D.FromAngleXYZ(Angle2D.DegToRad(anglexy) + Angle2D.PI, Angle2D.DegToRad(anglez));
+				s.FloorSlopeOffset = SetSlopeOffset(s, -floorslopecontrol.Offset, floorslopecontrol.PivotMode, true);
+				s.UpdateNeeded = true;
+				i++;
 			}
 
 			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);*/
-		}
-
-		/*private void floorsloperotation_WhenTextChanged(object sender, EventArgs e) {
-			if(blockUpdate) return;
-			float anglexy = floorsloperotation.GetResultFloat(float.NaN);
-			float anglez = floorslopeangle.GetResultFloat(float.NaN);
-			if(float.IsNaN(anglexy) || float.IsNaN(anglez)) return;
-
-			applySlopeTransform(Angle2D.DegToRad(anglexy), Angle2D.DegToRad(anglez), floorslopekeys);
-		}*/
-
-		/*private void floorslopeangle_WhenTextChanged(object sender, EventArgs e) {
-			if(blockUpdate) return;
-			float anglexy = floorsloperotation.GetResultFloat(float.NaN);
-			float anglez = floorslopeangle.GetResultFloat(float.NaN);
-			if(float.IsNaN(anglexy) || float.IsNaN(anglez)) return;
-
-			applySlopeTransform(Angle2D.DegToRad(anglexy), Angle2D.DegToRad(anglez), floorslopekeys);
-		}*/
-
-		private void ceilingslopecontrol_OnValuesChanged(object sender, EventArgs e) 
-		{
-			if(blockUpdate) return;
-
-			float anglexy = ceilingslopecontrol.AngleXY;
-			float anglez = ceilingslopecontrol.AngleZ;
-			if(float.IsNaN(anglexy) || float.IsNaN(anglez)) return;
-
-			applySlopeTransform(Angle2D.DegToRad(anglexy), Angle2D.DegToRad(anglez), ceilingslopecontrol.Offset, ceilslopekeys);
-		}
-
-		private void floorslopecontrol_OnValuesChanged(object sender, EventArgs e) 
-		{
-			if(blockUpdate) return;
-
-			float anglexy = floorslopecontrol.AngleXY;
-			float anglez = floorslopecontrol.AngleZ;
-			if(float.IsNaN(anglexy) || float.IsNaN(anglez)) return;
-
-			applySlopeTransform(Angle2D.DegToRad(anglexy), Angle2D.DegToRad(anglez), floorslopecontrol.Offset, floorslopekeys);
+			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
 		}
 
-		private void applySlopeTransform(float anglexy, float anglez, float offset, string[] keys) 
+		private float SetSlopeOffset(Sector target, float offset, SlopePivotMode mode, bool floor) 
 		{
-			Vector3D v = Vector3D.FromAngleXYZ(anglexy + Angle2D.PI, anglez);
-
-			//restore or set values
-			if(v.x == 0 && v.y == 0 && v.z == 0) 
+			float validoffset;
+			if (float.IsNaN(offset)) 
 			{
-				foreach(Sector s in sectors) 
+				float storedoffset = (floor ? sectorprops[target].FloorSlopeOffset : sectorprops[target].CeilingSlopeOffset);
+				if (float.IsNaN(storedoffset)) 
 				{
-					UDMFTools.ClearFields(s.Fields, keys);
-					s.UpdateNeeded = true;
+					//return an offset based on sector's floor/ceiling height
+					validoffset = (floor ? target.FloorHeight : target.CeilHeight);
+				} 
+				else 
+				{
+					//restore initial value
+					validoffset = storedoffset;
 				}
 			} 
 			else 
 			{
-				foreach(Sector s in sectors) 
-				{
-					UDMFTools.SetFloat(s.Fields, keys[0], v.x, float.MinValue);
-					UDMFTools.SetFloat(s.Fields, keys[1], v.y, float.MinValue);
-					UDMFTools.SetFloat(s.Fields, keys[2], v.z, float.MinValue);
-					UDMFTools.SetFloat(s.Fields, keys[3], offset, float.MinValue);
-					//TODO: set offset based on current SlopePivotMode
-					s.UpdateNeeded = true;
-				}
-			}
-
-			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
-		}
-
-		#endregion
-
-		#region mxd. Ceiling slope realtime events
-
-		/*private void resetceilslope_Click(object sender, EventArgs e) {
-			foreach(Sector s in sectors) {
-				UDMFTools.ClearFields(s.Fields, ceilslopekeys);
-				s.UpdateNeeded = true;
-			}
-
-			blockUpdate = true;
-			ceilslopex.Text = "0";
-			ceilslopey.Text = "0";
-			ceilslopez.Text = "0";
-			ceilslopeoffset.Text = "0";
-			ceilslopeangle.Value = 0;
-			ceilslopeanglelabel.Text = "0";
-			ceilsloperoll.Value = 0;
-			ceilsloperolllabel.Text = "0";
-			blockUpdate = false;
-
-			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
-		}*/
-
-		/*private void ceilslopex_WhenTextChanged(object sender, EventArgs e) {
-			if(blockUpdate) return;
-			int i = 0;
-
-			//restore values
-			if(string.IsNullOrEmpty(ceilslopex.Text)) {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "ceilingplane_a", sectorProps[i++].CeilSlopeX, float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			//update values
-			} else {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "ceilingplane_a", ceilslopex.GetResultFloat(sectorProps[i++].CeilSlopeX), float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			}
-
-			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
-		}*/
-
-		/*private void ceilslopey_WhenTextChanged(object sender, EventArgs e) {
-			if(blockUpdate) return;
-			int i = 0;
-
-			//restore values
-			if(string.IsNullOrEmpty(ceilslopey.Text)) {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "ceilingplane_b", sectorProps[i++].CeilSlopeY, float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			//update values
-			} else {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "ceilingplane_b", ceilslopey.GetResultFloat(sectorProps[i++].CeilSlopeY), float.MinValue);
-					s.UpdateNeeded = true;
-				}
+				//use current value
+				validoffset = offset;
 			}
+			
+			switch(mode) {
+				case SlopePivotMode.GLOBAL: //rotate around the center of selection 
+					//TODO: implement!
+					return validoffset;
 
-			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
-		}*/
+				case SlopePivotMode.LOCAL: //rotate around sector's bounding box center
+					//TODO: implement!
+					return validoffset;
 
-		/*private void ceilslopez_WhenTextChanged(object sender, EventArgs e) {
-			if(blockUpdate) return;
-			int i = 0;
+				case SlopePivotMode.ORIGIN: //rotate around world origin (0, 0)
+					return validoffset;
 
-			//restore values
-			if(string.IsNullOrEmpty(ceilslopez.Text)) {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "ceilingplane_c", sectorProps[i++].CeilSlopeZ, float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			//update values
-			} else {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "ceilingplane_c", ceilslopez.GetResultFloat(sectorProps[i++].CeilSlopeZ), float.MinValue);
-					s.UpdateNeeded = true;
-				}
+				default:
+					throw new NotImplementedException("SectorEditFormUDMF.SetSlopeOffset: Got unknown SlopePivotMode (" + (int)mode + ")");
 			}
+		}
 
-			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
-		}*/
-
-		/*private void ceilslopeoffset_WhenTextChanged(object sender, EventArgs e) {
-			if(blockUpdate) return;
-			int i = 0;
-
-			//restore values
-			if(string.IsNullOrEmpty(ceilslopeoffset.Text)) {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "ceilingplane_d", sectorProps[i++].CeilSlopeOffset, float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			//update values
-			} else {
-				foreach(Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, "ceilingplane_d", ceilslopeoffset.GetResultFloat(sectorProps[i++].CeilSlopeOffset), float.MinValue);
-					s.UpdateNeeded = true;
-				}
-			}
+		private void ceilingslopecontrol_OnUseLineAnglesChanged(object sender, EventArgs e) 
+		{
+			ceilingslopecontrol.StepValues = (ceilingslopecontrol.UseLineAngles ? anglesteps : null);
+		}
 
-			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
-		}*/
+		private void floorslopecontrol_OnUseLineAnglesChanged(object sender, EventArgs e) 
+		{
+			floorslopecontrol.StepValues = (floorslopecontrol.UseLineAngles ? anglesteps : null);
+		}
 
 		#endregion
 
-		/*private void ceilslopeangle_Scroll(object sender, EventArgs e) {
-			if(blockUpdate) return;
-
-			//ceilslopeanglelabel.Text = ceilslopeangle.Value + "\u00B0";
-			//applySlopeValues(Angle2D.DegToRad(ceilslopeangle.Value), Angle2D.DegToRad(ceilsloperoll.Value), ceilslopekeys);
-
-			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
-		}*/
-
-		/*private void ceilsloperoll_Scroll(object sender, EventArgs e) {
-			if(blockUpdate) return;
-
-			//ceilsloperolllabel.Text = ceilsloperoll.Value + "\u00B0";
-			//applySlopeValues(Angle2D.DegToRad(ceilslopeangle.Value), Angle2D.DegToRad(ceilsloperoll.Value), ceilslopekeys);
-
-			General.Map.IsChanged = true;
-			if(OnValuesChanged != null) OnValuesChanged(this, EventArgs.Empty);
-		}*/
-
-		//TODO: remove this
-		/*private void applySlopeValues(float anglexy, float anglez, string[] keys) {
-			Vector3D v = Vector3D.FromAngleXYZ(anglexy, anglez); //normalize?
-
-			//restore or set values
-			if (v.x == 0 && v.y == 0 && v.z == 0) {
-				foreach(Sector s in sectors) {
-					UDMFTools.ClearFields(s.Fields, keys);
-					s.UpdateNeeded = true;
-				}
-
-				//update offset text
-				blockUpdate = true;
-				ceilslopeoffset.Text = "0";
-				blockUpdate = false;
-
-			} else {
-				foreach (Sector s in sectors) {
-					UDMFTools.SetFloat(s.Fields, keys[0], v.x, float.MinValue);
-					UDMFTools.SetFloat(s.Fields, keys[1], v.y, float.MinValue);
-					UDMFTools.SetFloat(s.Fields, keys[2], v.z, float.MinValue);
-					//TODO: set offset based on current SlopePivotMode
-					s.UpdateNeeded = true;
-				}
-			}
-
-			//update xyz text
-			blockUpdate = true;
-			ceilslopex.Text = v.x.ToString();
-			ceilslopey.Text = v.y.ToString();
-			ceilslopez.Text = v.z.ToString();
-			blockUpdate = false;
-		}*/
-
 	}
 }
diff --git a/Source/Core/Windows/SectorEditFormUDMF.resx b/Source/Core/Windows/SectorEditFormUDMF.resx
index 53aec6270..90add8337 100644
--- a/Source/Core/Windows/SectorEditFormUDMF.resx
+++ b/Source/Core/Windows/SectorEditFormUDMF.resx
@@ -138,6 +138,21 @@
   <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>
@@ -147,6 +162,12 @@
   <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>
@@ -156,4 +177,7 @@
   <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/Plugins/BuilderModes/BuilderModes.csproj b/Source/Plugins/BuilderModes/BuilderModes.csproj
index 9e94e8c95..8620c88e6 100644
--- a/Source/Plugins/BuilderModes/BuilderModes.csproj
+++ b/Source/Plugins/BuilderModes/BuilderModes.csproj
@@ -360,7 +360,6 @@
     <Compile Include="VisualModes\EffectCopySlope.cs" />
     <Compile Include="VisualModes\EffectLineSlope.cs" />
     <Compile Include="VisualModes\EffectPlaneCopySlope.cs" />
-    <Compile Include="VisualModes\EffectSectorSlope.cs" />
     <Compile Include="VisualModes\EffectThingLineSlope.cs" />
     <Compile Include="VisualModes\EffectThingSlope.cs" />
     <Compile Include="VisualModes\EffectThingVertexSlope.cs" />
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
index 080bd3080..f978e4222 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
@@ -759,16 +759,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			// Find sectors with 3 vertices, because they can be sloped
 			foreach(Sector s in General.Map.Map.Sectors)
 			{
-				// ========== Sector slope, Thing vertex slope, vertices with UDMF vertex offsets ==========
-				if (General.Map.UDMF) 
-				{
-					bool havesectorfloorslope = !(!s.Fields.ContainsKey("floorplane_a") || !s.Fields.ContainsKey("floorplane_b") || !s.Fields.ContainsKey("floorplane_c") || !s.Fields.ContainsKey("floorplane_d"));
-					bool havesectorceilingslope = !(!s.Fields.ContainsKey("ceilingplane_a") || !s.Fields.ContainsKey("ceilingplane_b") || !s.Fields.ContainsKey("ceilingplane_c") || !s.Fields.ContainsKey("ceilingplane_d"));
-
-					if(havesectorfloorslope) GetSectorData(s).AddEffectSectorSlope(false);
-					if(havesectorceilingslope) GetSectorData(s).AddEffectSectorSlope(true);
-				}
-
+				// ========== Thing vertex slope, vertices with UDMF vertex offsets ==========
 				if(s.Sidedefs.Count == 3)
 				{
 					if(General.Map.UDMF) //mxd
diff --git a/Source/Plugins/BuilderModes/VisualModes/EffectSectorSlope.cs b/Source/Plugins/BuilderModes/VisualModes/EffectSectorSlope.cs
deleted file mode 100644
index 3df3351d6..000000000
--- a/Source/Plugins/BuilderModes/VisualModes/EffectSectorSlope.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using CodeImp.DoomBuilder.Geometry;
-
-namespace CodeImp.DoomBuilder.BuilderModes
-{
-	internal class EffectSectorSlope : SectorEffect
-	{
-		private readonly bool ceilingslope;
-		
-		public EffectSectorSlope(SectorData data, bool ceilingslope) : base(data) 
-		{
-			this.ceilingslope = ceilingslope;
-
-			// New effect added: This sector needs an update!
-			if(data.Mode.VisualSectorExists(data.Sector)) 
-			{
-				BaseVisualSector vs = (BaseVisualSector)data.Mode.GetVisualSector(data.Sector);
-				vs.UpdateSectorGeometry(true);
-			}
-		}
-
-		// This makes sure we are updated
-		public override void Update() 
-		{
-			if (ceilingslope) {
-				float a = data.Sector.Fields.GetValue("ceilingplane_a", 0f);
-				float b = data.Sector.Fields.GetValue("ceilingplane_b", 0f);
-				float c = data.Sector.Fields.GetValue("ceilingplane_c", 0f);
-				float d = data.Sector.Fields.GetValue("ceilingplane_d", 0f);
-
-				Vector3D normal = new Vector3D(a, b, c).GetNormal();
-				if (normal.x != 0 || normal.y != 0 || normal.z != 0) 
-				{
-					if(normal.z > 0) normal = -normal; //flip the plane if it's facing the wrong direction
-					data.Ceiling.plane = new Plane(normal, d);
-				}
-			} 
-			else 
-			{
-				float a = data.Sector.Fields.GetValue("floorplane_a", 0f);
-				float b = data.Sector.Fields.GetValue("floorplane_b", 0f);
-				float c = data.Sector.Fields.GetValue("floorplane_c", 0f);
-				float d = data.Sector.Fields.GetValue("floorplane_d", 0f);
-				
-				Vector3D normal = new Vector3D(a, b, c).GetNormal();
-				if (normal.x != 0 || normal.y != 0 || normal.z != 0) 
-				{
-					if(normal.z < 0) normal = -normal; //flip the plane if it's facing the wrong direction
-					data.Floor.plane = new Plane(normal, d);
-				}
-			}
-		}
-	}
-}
diff --git a/Source/Plugins/BuilderModes/VisualModes/SectorData.cs b/Source/Plugins/BuilderModes/VisualModes/SectorData.cs
index dda52a55a..667c4d8d0 100644
--- a/Source/Plugins/BuilderModes/VisualModes/SectorData.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/SectorData.cs
@@ -114,13 +114,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			alleffects.Add(e);
 		}
 
-		//mxd. Sector slope effect
-		public void AddEffectSectorSlope(bool ceilingslope) 
-		{
-			EffectSectorSlope e = new EffectSectorSlope(this, ceilingslope);
-			alleffects.Add(e);
-		}
-
 		//mxd. Plane copy slope effect
 		public void AddEffectPlaneClopySlope(Linedef sourcelinedef, bool front) 
 		{
@@ -208,9 +201,28 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// This sets up the basic floor and ceiling, as they would be in normal Doom circumstances
 		private void BasicSetup()
 		{
-			// Normal (flat) floor and ceiling planes
-			floor.plane = new Plane(new Vector3D(0, 0, 1), -sector.FloorHeight);
-			ceiling.plane = new Plane(new Vector3D(0, 0, -1), sector.CeilHeight);
+			//mxd
+			if(sector.FloorSlope.GetLengthSq() > 0) 
+			{
+				// Sloped plane
+				floor.plane = new Plane(sector.FloorSlope, sector.FloorSlopeOffset);
+			} 
+			else 
+			{
+				// Normal (flat) floor plane
+				floor.plane = new Plane(new Vector3D(0, 0, 1), -sector.FloorHeight);
+			}
+
+			if(sector.CeilingSlope.GetLengthSq() > 0) 
+			{
+				// Sloped plane
+				ceiling.plane = new Plane(sector.CeilingSlope, sector.CeilingSlopeOffset);
+			} 
+			else 
+			{
+				// Normal (flat) ceiling plane
+				ceiling.plane = new Plane(new Vector3D(0, 0, -1), sector.CeilHeight);
+			}
 			
 			// Fetch ZDoom fields
 			int color = sector.Fields.GetValue("lightcolor", -1);
-- 
GitLab