diff --git a/Source/Plugins/BuilderModes/Interface/MenusForm.Designer.cs b/Source/Plugins/BuilderModes/Interface/MenusForm.Designer.cs
index a77c853fa842c585e740aaa04e1239793d6bf592..ce20fce4b7f788007fdac8515f2bdd1d2961d7d7 100644
--- a/Source/Plugins/BuilderModes/Interface/MenusForm.Designer.cs
+++ b/Source/Plugins/BuilderModes/Interface/MenusForm.Designer.cs
@@ -190,7 +190,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			// toolStripMenuItem2
 			// 
 			this.toolStripMenuItem2.Name = "toolStripMenuItem2";
-			this.toolStripMenuItem2.Size = new System.Drawing.Size(146, 6);
+			this.toolStripMenuItem2.Size = new System.Drawing.Size(149, 6);
+			this.toolStripMenuItem2.Visible = false;
 			// 
 			// thingsmenu
 			// 
diff --git a/Source/Plugins/BuilderModes/Resources/Actions.cfg b/Source/Plugins/BuilderModes/Resources/Actions.cfg
index 55c9c70dd949221dd9dcdd677ffda1380e90a36a..2cd49f05678f5b9a7a3acc92d8f389035f186aab 100644
--- a/Source/Plugins/BuilderModes/Resources/Actions.cfg
+++ b/Source/Plugins/BuilderModes/Resources/Actions.cfg
@@ -844,6 +844,18 @@ pasteproperties
 	allowscroll = true;
 }
 
+//mxd
+toggleslope
+{
+	title = "Toggle Slope";
+	category = "visual";
+	description = "Toggles Slope for selected surfaces. Select or highlight upper/lower walls to add a slope. Select or highlight  floors or ceilings to remove slope.";
+	allowkeys = true;
+	allowmouse = true;
+	allowscroll = true;
+	default = 262227; //Alt-S
+}
+
 placevisualstart
 {
 	title = "Place Visual Mode Camera";
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs
index 56e49bf87a85c1b74f35d6b587fe950417847222..86186c4a12d779bbb1ef6a486d38d0a064357e5e 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs
@@ -124,16 +124,17 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
             //mxd. Modify offsets based on surface and camera angles
             if (General.Map.UDMF) {
-                float angle = 0;
-				if(GeometryType == VisualGeometryType.CEILING && level.sector.Fields.ContainsKey("rotationceiling"))
-					angle = Angle2D.DegToRad((float)level.sector.Fields["rotationceiling"].Value);// * (float)Math.PI / 180f;
-				else if(GeometryType == VisualGeometryType.FLOOR && level.sector.Fields.ContainsKey("rotationfloor"))
-					angle = Angle2D.DegToRad((float)level.sector.Fields["rotationfloor"].Value);// *(float)Math.PI / 180f;
-
-                Vector2D v = new Vector2D(offsetx, offsety).GetRotated(angle);
-                Point p = getTranslatedTextureOffset(new Point((int)Math.Round(v.x), (int)Math.Round(v.y)));
-                offsetx = p.X;
-                offsety = p.Y;
+				float angle = 0;
+
+				if(GeometryType == VisualGeometryType.CEILING)
+					angle = Angle2D.DegToRad(level.sector.Fields.GetValue("rotationceiling", 0f));
+				else
+					angle = Angle2D.DegToRad(level.sector.Fields.GetValue("rotationfloor", 0f));
+
+				Vector2D v = new Vector2D(offsetx, offsety).GetRotated(angle);
+
+				offsetx = (int)Math.Round(v.x);
+				offsety = (int)Math.Round(v.y);
             }
 
 			// Apply offsets
@@ -151,27 +152,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
             return level.sector;
         }
 
-        //mxd. Modify texture offsets based on camera angle (so "movetextureleft" action always moves texture more or less "left" etc.)
-        protected Point getTranslatedTextureOffset(Point p) {
-            Point tp = new Point();
-            int camAngle = (int)Angle2D.RadToDeg(General.Map.VisualCamera.AngleXY);// * 180f / (float)Math.PI);
-
-            if (camAngle > 315 || camAngle < 46) {
-                tp = p;
-            } else if (camAngle > 225) {
-                tp.Y = p.X;
-                tp.X = -p.Y;
-            } else if (camAngle > 135) {
-                tp.X = -p.X;
-                tp.Y = -p.Y;
-            }else{
-                tp.Y = -p.X;
-                tp.X = p.Y;
-            }
-
-            return tp;
-        }
-
 		//mxd
 		protected void onTextureChanged() {
 			if(level.sector == this.Sector.Sector) {
@@ -206,7 +186,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		// Unused
 		public virtual void OnEditBegin() { }
-		public virtual void OnTextureAlign(bool alignx, bool aligny) { }
+		//public virtual void OnTextureAlign(bool alignx, bool aligny) { }
 		public virtual void OnToggleUpperUnpegged() { }
 		public virtual void OnToggleLowerUnpegged() { }
 		public virtual void OnResetTextureOffset() { }
@@ -387,6 +367,41 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				}
 			}
 		}
+
+		//mxd. Auto-align texture offsets
+		public virtual void OnTextureAlign(bool alignx, bool aligny) {
+			if(!General.Map.UDMF) return;
+
+			//create undo
+			string rest = string.Empty;
+			if(alignx && aligny) rest = "(X and Y)";
+			else if(alignx)	rest = "(X)";
+			else rest = "(Y)";
+
+			mode.CreateUndo("Auto-align textures " + rest);
+			mode.SetActionResult("Auto-aligned textures " + rest + ".");
+
+			//get selection
+			List<VisualGeometry> selection = mode.GetSelectedSurfaces();
+
+			//align textures on slopes
+			foreach(VisualGeometry vg in selection) {
+				if(vg.GeometryType == VisualGeometryType.FLOOR || vg.GeometryType == VisualGeometryType.CEILING) {
+					if(vg.GeometryType == VisualGeometryType.FLOOR)
+						((VisualFloor)vg).AlignTexture(alignx, aligny);
+					else
+						((VisualCeiling)vg).AlignTexture(alignx, aligny);
+
+					vg.Sector.Sector.UpdateNeeded = true;
+					vg.Sector.Sector.UpdateCache();
+				}
+			}
+
+			// Map is changed
+			General.Map.Map.Update();
+			General.Map.IsChanged = true;
+			General.Interface.RefreshInfo();
+		}
 		
 		// Copy properties
 		public virtual void OnCopyProperties()
@@ -506,24 +521,49 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		}
 
 		// Texture offset change
-		public virtual void OnChangeTextureOffset(int horizontal, int vertical)
+		public virtual void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection)
 		{
 			//mxd
-            if (General.Map.UDMF) {
-                if ((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
-                    undoticket = mode.CreateUndo("Change texture offsets");
+            if (!General.Map.UDMF) {
+				General.ShowErrorMessage("Floor/ceiling texture offsets cannot be changed in this map format!", MessageBoxButtons.OK);
+				return;
+			}
 
-                // Apply offsets
-                MoveTextureOffset(new Point(-horizontal, -vertical));
+			if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
+				undoticket = mode.CreateUndo("Change texture offsets");
 
-                mode.SetActionResult("Changed texture offsets by " + -horizontal + ", " + -vertical + ".");
+			//mxd
+			if(doSurfaceAngleCorrection) {
+				Point p = new Point(horizontal, vertical);
+				float angle = Angle2D.RadToDeg(General.Map.VisualCamera.AngleXY);
+				if(GeometryType == VisualGeometryType.CEILING) {
+					angle += level.sector.Fields.GetValue("rotationceiling", 0f);
+				} else
+					angle += level.sector.Fields.GetValue("rotationfloor", 0f);
+
+				angle = General.ClampAngle(angle);
+
+				if(angle > 315 || angle < 46) {
+
+				} else if(angle > 225) {
+					vertical = p.X;
+					horizontal = -p.Y;
+				} else if(angle > 135) {
+					horizontal = -p.X;
+					vertical = -p.Y;
+				} else {
+					vertical = -p.X;
+					horizontal = p.Y;
+				}
+			}
 
-                // Update sector geometry
-                Sector.UpdateSectorGeometry(false);
-                Sector.Rebuild();
-            } else {
-                General.ShowErrorMessage("Floor/ceiling texture offsets cannot be changed in this map format!", MessageBoxButtons.OK);
-            }
+			// Apply offsets
+			MoveTextureOffset(new Point(-horizontal, -vertical));
+			mode.SetActionResult("Changed texture offsets by " + (-horizontal) + ", " + (-vertical) + ".");
+
+			// Update sector geometry
+			Sector.UpdateSectorGeometry(false);
+			Sector.Rebuild();
 		}
 		
 		#endregion
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs
index 785a1452f36b68037cfbd728461b39101db55175..dbbbbce40f1596459806e51c287a61d1471dd840 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs
@@ -756,11 +756,17 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			}
 		}
 		
-		// Auto-align texture X offsets
+		// Auto-align texture offsets
 		public virtual void OnTextureAlign(bool alignx, bool aligny)
 		{
-			mode.CreateUndo("Auto-align textures");
-			mode.SetActionResult("Auto-aligned textures.");
+			//mxd
+			string rest = string.Empty;
+			if(alignx && aligny) rest = "(X and Y)";
+			else if(alignx)	rest = "(X)";
+			else rest = "(Y)";
+
+			mode.CreateUndo("Auto-align textures " + rest);
+			mode.SetActionResult("Auto-aligned textures " + rest + ".");
 			
 			// Make sure the texture is loaded (we need the texture size)
 			if(!base.Texture.IsImageLoaded) base.Texture.LoadImage();
@@ -1068,7 +1074,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		}
 		
 		// Texture offset change
-		public virtual void OnChangeTextureOffset(int horizontal, int vertical)
+		public virtual void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection)
 		{
 			if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
 				undoticket = mode.CreateUndo("Change texture offsets");
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
index 125472f86c49ceaac3b39ed28678ee7abdac6f84..76397e7009c702d176df9c19bf6b3159f3eb8703 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
@@ -1229,7 +1229,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				{
 					if(!donesides.ContainsKey((i as BaseVisualGeometrySidedef).Sidedef))
 					{
-						i.OnChangeTextureOffset(dx, dy);
+						i.OnChangeTextureOffset(dx, dy, false);
 						donesides.Add((i as BaseVisualGeometrySidedef).Sidedef, 0);
 					}
 				}
@@ -1247,7 +1247,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 				{
 					if(!donesectors.ContainsKey((i as BaseVisualGeometrySector).Sector.Sector))
 					{
-						i.OnChangeTextureOffset(dx, dy);
+						i.OnChangeTextureOffset(dx, dy, false);
 						donesectors.Add((i as BaseVisualGeometrySector).Sector.Sector, 0);
 					}
 				}
@@ -2015,7 +2015,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
             PreAction(UndoGroup.TextureOffsetChange);
 			List<IVisualEventReceiver> objs = GetSelectedObjects(true, true, false, false);
-            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(-1, 0);
+            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(-1, 0, true);
             PostAction();
 		}
 
@@ -2024,7 +2024,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
             PreAction(UndoGroup.TextureOffsetChange);
 			List<IVisualEventReceiver> objs = GetSelectedObjects(true, true, false, false);
-            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(1, 0);
+            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(1, 0, true);
             PostAction();
 		}
 
@@ -2033,7 +2033,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
             PreAction(UndoGroup.TextureOffsetChange);
 			List<IVisualEventReceiver> objs = GetSelectedObjects(true, true, false, false);
-            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, -1);
+            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, -1, true);
             PostAction();
 		}
 
@@ -2042,7 +2042,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
             PreAction(UndoGroup.TextureOffsetChange);
 			List<IVisualEventReceiver> objs = GetSelectedObjects(true, true, false, false);
-            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, 1);
+            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, 1, true);
             PostAction();
 		}
 
@@ -2051,7 +2051,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
             PreAction(UndoGroup.TextureOffsetChange);
 			List<IVisualEventReceiver> objs = GetSelectedObjects(true, true, false, false);
-            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(-8, 0);
+            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(-8, 0, true);
             PostAction();
 		}
 
@@ -2060,7 +2060,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
             PreAction(UndoGroup.TextureOffsetChange);
 			List<IVisualEventReceiver> objs = GetSelectedObjects(true, true, false, false);
-            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(8, 0);
+            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(8, 0, true);
             PostAction();
 		}
 
@@ -2069,7 +2069,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
             PreAction(UndoGroup.TextureOffsetChange);
 			List<IVisualEventReceiver> objs = GetSelectedObjects(true, true, false, false);
-            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, -8);
+            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, -8, true);
             PostAction();
 		}
 
@@ -2078,7 +2078,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
             PreAction(UndoGroup.TextureOffsetChange);
 			List<IVisualEventReceiver> objs = GetSelectedObjects(true, true, false, false);
-            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, 8);
+            foreach (IVisualEventReceiver i in objs) i.OnChangeTextureOffset(0, 8, true);
             PostAction();
 		}
 
@@ -2474,6 +2474,129 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			UpdateChangedObjects();
 			ShowTargetInfo();
 		}
+
+		//mxd
+		[BeginAction("toggleslope")]
+		public void ToggleSlope() {
+			List<VisualGeometry> selection = GetSelectedSurfaces();
+
+			if(selection.Count == 0) {
+				General.Interface.DisplayStatus(StatusType.Warning, "Toggle Slope action requires selected surfaces!");
+				return;
+			}
+
+			bool update = false;
+			List<BaseVisualSector> toUpdate = new List<BaseVisualSector>();
+			General.Map.UndoRedo.CreateUndo("Toggle Slope");
+
+			//check selection
+			foreach(VisualGeometry vg in selection) {
+				update = false;
+
+				//assign/remove action
+				if(vg.GeometryType == VisualGeometryType.WALL_BOTTOM) {
+					if(vg.Sidedef.Line.Action == 0 || (vg.Sidedef.Line.Action == 181 && vg.Sidedef.Line.Args[0] == 0)) {
+						//check if the sector already has floor slopes
+						foreach(Sidedef side in vg.Sidedef.Sector.Sidedefs) {
+							if(side == vg.Sidedef || side.Line.Action != 181)
+								continue;
+
+							int arg = (side == side.Line.Front ? 1 : 2);
+
+							if(side.Line.Args[0] == arg) {
+								//if only floor is affected, remove action
+								if(side.Line.Args[1] == 0)
+									side.Line.Action = 0;
+								else //clear floor alignment
+									side.Line.Args[0] = 0;
+							}
+						}
+
+						//set action
+						vg.Sidedef.Line.Action = 181;
+						vg.Sidedef.Line.Args[0] = (vg.Sidedef == vg.Sidedef.Line.Front ? 1 : 2);
+						update = true;
+					}
+				} else if(vg.GeometryType == VisualGeometryType.WALL_UPPER) {
+					if(vg.Sidedef.Line.Action == 0 || (vg.Sidedef.Line.Action == 181 && vg.Sidedef.Line.Args[1] == 0)) {
+						//check if the sector already has ceiling slopes
+						foreach(Sidedef side in vg.Sidedef.Sector.Sidedefs) {
+							if(side == vg.Sidedef || side.Line.Action != 181)
+								continue;
+
+							int arg = (side == side.Line.Front ? 1 : 2);
+
+							if(side.Line.Args[1] == arg) {
+								//if only ceiling is affected, remove action
+								if(side.Line.Args[0] == 0)
+									side.Line.Action = 0;
+								else //clear ceiling alignment
+									side.Line.Args[1] = 0;
+							}
+						}
+
+						//set action
+						vg.Sidedef.Line.Action = 181;
+						vg.Sidedef.Line.Args[1] = (vg.Sidedef == vg.Sidedef.Line.Front ? 1 : 2);
+						update = true;
+					}
+				} else if(vg.GeometryType == VisualGeometryType.CEILING) {
+					//check if the sector has ceiling slopes
+					foreach(Sidedef side in vg.Sector.Sector.Sidedefs) {
+						if(side.Line.Action != 181)
+							continue;
+
+						int arg = (side == side.Line.Front ? 1 : 2);
+
+						if(side.Line.Args[1] == arg) {
+							//if only ceiling is affected, remove action
+							if(side.Line.Args[0] == 0)
+								side.Line.Action = 0;
+							else //clear ceiling alignment
+								side.Line.Args[1] = 0;
+
+							update = true;
+						}
+					}
+				} else if(vg.GeometryType == VisualGeometryType.FLOOR) {
+					//check if the sector has floor slopes
+					foreach(Sidedef side in vg.Sector.Sector.Sidedefs) {
+						if(side.Line.Action != 181)
+							continue;
+
+						int arg = (side == side.Line.Front ? 1 : 2);
+
+						if(side.Line.Args[0] == arg) {
+							//if only floor is affected, remove action
+							if(side.Line.Args[1] == 0)
+								side.Line.Action = 0;
+							else //clear floor alignment
+								side.Line.Args[0] = 0;
+
+							update = true;
+						}
+					}
+				}
+
+				//add to update list
+				if(update)
+					toUpdate.Add(vg.Sector as BaseVisualSector);
+			}
+
+			//update changed geometry
+			if(toUpdate.Count > 0) {
+				RebuildElementData();
+
+				foreach(BaseVisualSector vs in toUpdate)
+					vs.UpdateSectorGeometry(true);
+
+				UpdateChangedObjects();
+				ClearSelection();
+				ShowTargetInfo();
+			}
+
+			General.Interface.DisplayStatus(StatusType.Action, "Toggled Slope for " + toUpdate.Count + (toUpdate.Count == 1 ? " surface." : " surfaces."));
+		}
 		
 		#endregion
 
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs
index 93c66881b48c502abcf40c70578e7005a48ccc86..3cc4af74d8027fa9bd4c21ce732b5efcbc064084 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs
@@ -438,7 +438,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		public virtual void OnEditBegin() { }
 		public virtual void OnMouseMove(MouseEventArgs e) { }
 		public virtual void OnChangeTargetBrightness(bool up) { }
-		public virtual void OnChangeTextureOffset(int horizontal, int vertical) { }
+		public virtual void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection) { }
 		public virtual void OnSelectTexture() { }
 		public virtual void OnCopyTexture() { }
 		public virtual void OnPasteTexture() { }
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualVertex.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualVertex.cs
index 10ba209d86a566017dd0ca8863e1b021c4181a81..bccbbbc10ebb8810d5adb147fa2773673d1e2601 100644
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualVertex.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualVertex.cs
@@ -204,7 +204,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		public virtual void OnEditBegin() { }
 		public virtual void OnMouseMove(MouseEventArgs e) { }
 		public virtual void OnChangeTargetBrightness(bool up) { }
-		public virtual void OnChangeTextureOffset(int horizontal, int vertical) { }
+		public virtual void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection) { }
 		public virtual void OnSelectTexture() { }
 		public virtual void OnCopyTexture() { }
 		public virtual void OnPasteTexture() { }
diff --git a/Source/Plugins/BuilderModes/VisualModes/IVisualEventReceiver.cs b/Source/Plugins/BuilderModes/VisualModes/IVisualEventReceiver.cs
index bbf0b4c582c38f1b90af0125154173a011d16cbd..a6efd89cfbe535e211968e3fab7d7ddf5ad405a2 100644
--- a/Source/Plugins/BuilderModes/VisualModes/IVisualEventReceiver.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/IVisualEventReceiver.cs
@@ -32,7 +32,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		void OnMouseMove(MouseEventArgs e);
 		void OnChangeTargetHeight(int amount);
 		void OnChangeTargetBrightness(bool up);
-		void OnChangeTextureOffset(int horizontal, int vertical);
+		void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection);
 		void OnResetTextureOffset();
 		void OnSelectTexture();
 		void OnCopyTexture();
diff --git a/Source/Plugins/BuilderModes/VisualModes/NullVisualEventReceiver.cs b/Source/Plugins/BuilderModes/VisualModes/NullVisualEventReceiver.cs
index 021fbc5ca1d522bbfefe0bbfd037ecd599e733a9..701ae46ab599038a18d5f31a5126a40a0a6244b8 100644
--- a/Source/Plugins/BuilderModes/VisualModes/NullVisualEventReceiver.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/NullVisualEventReceiver.cs
@@ -57,7 +57,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
 		}
 
-		public void OnChangeTextureOffset(int horizontal, int vertical)
+		public void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection)
 		{
 		}
 
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs b/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs
index 49684fde8128c299c5e55c731fa130df47fa00da..01f6c0ba60f0b82b533f70b9c41aea05a94577e7 100644
--- a/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs
@@ -179,7 +179,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			s.Fields.BeforeFieldsChange();
             float oldx = s.Fields.GetValue("xpanningceiling", 0.0f);
             float oldy = s.Fields.GetValue("ypanningceiling", 0.0f);
-            xy = getTranslatedTextureOffset(xy);
             s.Fields["xpanningceiling"] = new UniValue(UniversalType.Float, oldx + (float)xy.X);
             s.Fields["ypanningceiling"] = new UniValue(UniversalType.Float, oldy + (float)xy.Y);
             s.UpdateNeeded = true;
@@ -376,6 +375,96 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					vs.Ceiling.SelectNeighbours(select, withSameTexture, withSameHeight);
 			}
 		}
+
+		//mxd
+		public void AlignTexture(bool alignx, bool aligny) {
+			if(!General.Map.UDMF) return;
+
+			float slopeAngle = level.plane.Normal.GetAngleZ() - Angle2D.PIHALF;
+
+			if(slopeAngle == 0)	return; //it's a horizontal plane
+
+			//find slope source linedef
+			Linedef slopeSource = null;
+			bool isFront = false;
+
+			foreach(Sidedef side in Sector.Sector.Sidedefs) {
+				if(side.Line.Action == 181) {
+					if(side.Line.Args[1] == 1 && side.Line.Front != null && side.Line.Front == side) {
+						slopeSource = side.Line;
+						isFront = true;
+						break;
+					} else if(side.Line.Args[1] == 2 && side.Line.Back != null && side.Line.Back == side) {
+						slopeSource = side.Line;
+						break;
+					}
+				}
+			}
+
+			if(slopeSource == null)	return;
+
+			Sector.Sector.Fields.BeforeFieldsChange();
+
+			//match rotation
+			float sourceAngle = (float)Math.Round(General.ClampAngle(isFront ? -Angle2D.RadToDeg(slopeSource.Angle) + 90 : -Angle2D.RadToDeg(slopeSource.Angle) - 90), 1);
+
+			if((isFront && slopeSource.Front.Sector.CeilHeight < slopeSource.Back.Sector.CeilHeight) ||
+				(!isFront && slopeSource.Front.Sector.CeilHeight > slopeSource.Back.Sector.CeilHeight)) {
+				sourceAngle = General.ClampAngle(sourceAngle + 180);
+			}
+
+			if(sourceAngle != 0) {
+				if(!Sector.Sector.Fields.ContainsKey("rotationceiling"))
+					Sector.Sector.Fields.Add("rotationceiling", new UniValue(UniversalType.Float, sourceAngle));
+				else
+					Sector.Sector.Fields["rotationceiling"].Value = sourceAngle;
+			} else if(Sector.Sector.Fields.ContainsKey("rotationceiling")) {
+				Sector.Sector.Fields.Remove("rotationceiling");
+			}
+
+			//update scaleY
+			float scaleX = Sector.Sector.Fields.GetValue("xscaleceiling", 1.0f);
+			float scaleY = (float)Math.Round(scaleX * (1 / (float)Math.Cos(slopeAngle)), 2);
+
+			if(aligny) {
+				if(Sector.Sector.Fields.ContainsKey("yscaleceiling"))
+					Sector.Sector.Fields["yscaleceiling"].Value = scaleY;
+				else
+					Sector.Sector.Fields.Add("yscaleceiling", new UniValue(UniversalType.Float, scaleY));
+			}
+
+			//update texture offsets
+			Vector2D offset;
+			if((isFront && slopeSource.Front.Sector.CeilHeight > slopeSource.Back.Sector.CeilHeight) ||
+			  (!isFront && slopeSource.Front.Sector.CeilHeight < slopeSource.Back.Sector.CeilHeight)) {
+				offset = slopeSource.End.Position;
+			} else {
+				offset = slopeSource.Start.Position;
+			}
+
+			offset = offset.GetRotated(Angle2D.DegToRad(sourceAngle));
+
+			if(alignx) {
+				if(Texture != null)	offset.x %= Texture.Width / scaleX;
+
+				if(Sector.Sector.Fields.ContainsKey("xpanningceiling"))
+					Sector.Sector.Fields["xpanningceiling"].Value = (float)Math.Round(-offset.x);
+				else
+					Sector.Sector.Fields.Add("xpanningceiling", new UniValue(UniversalType.Float, (float)Math.Round(-offset.x)));
+			}
+
+			if(aligny) {
+				if(Texture != null)	offset.y %= Texture.Height / scaleY;
+
+				if(Sector.Sector.Fields.ContainsKey("ypanningceiling"))
+					Sector.Sector.Fields["ypanningceiling"].Value = (float)Math.Round(offset.y);
+				else
+					Sector.Sector.Fields.Add("ypanningceiling", new UniValue(UniversalType.Float, (float)Math.Round(offset.y)));
+			}
+
+			//update geometry
+			Sector.UpdateSectorGeometry(false);
+		}
 		
 		#endregion
 	}
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs b/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs
index ed0083ca2018cf3dd4ff6e4d2475e95499f2b308..f6f1f9c650442e5f42587a3ec534fc71c80341a0 100644
--- a/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs
@@ -87,20 +87,14 @@ namespace CodeImp.DoomBuilder.BuilderModes
 										  s.Fields.GetValue("yscalefloor", 1.0f));
 			
 			//Load floor texture
-			//if((s.FloorTexture.Length > 0) && (s.FloorTexture[0] != '-')) {
-				base.Texture = General.Map.Data.GetFlatImage(s.LongFloorTexture);
-				if(base.Texture == null) {
-					base.Texture = General.Map.Data.MissingTexture3D;
-					setuponloadedtexture = s.LongFloorTexture;
-				} else {
-					if(!base.Texture.IsImageLoaded)
-						setuponloadedtexture = s.LongFloorTexture;
-				}
-			/*} else {
-				// Use missing texture
+			base.Texture = General.Map.Data.GetFlatImage(s.LongFloorTexture);
+			if(base.Texture == null) {
 				base.Texture = General.Map.Data.MissingTexture3D;
-				setuponloadedtexture = 0;
-			}*/
+				setuponloadedtexture = s.LongFloorTexture;
+			} else {
+				if(!base.Texture.IsImageLoaded)
+					setuponloadedtexture = s.LongFloorTexture;
+			}
 
 			// Determine texture scale
 			if(base.Texture.IsImageLoaded)
@@ -178,7 +172,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
             s.Fields.BeforeFieldsChange();
             float oldx = s.Fields.GetValue("xpanningfloor", 0.0f);
             float oldy = s.Fields.GetValue("ypanningfloor", 0.0f);
-            xy = getTranslatedTextureOffset(xy);
             s.Fields["xpanningfloor"] = new UniValue(UniversalType.Float, oldx + (float)xy.X);
             s.Fields["ypanningfloor"] = new UniValue(UniversalType.Float, oldy + (float)xy.Y);
             s.UpdateNeeded = true;
@@ -339,6 +332,95 @@ namespace CodeImp.DoomBuilder.BuilderModes
 					vs.Floor.SelectNeighbours(select, withSameTexture, withSameHeight);
 			}
 		}
+
+		//mxd
+		public void AlignTexture(bool alignx, bool aligny) {
+			if(!General.Map.UDMF) return;
+
+			float slopeAngle = level.plane.Normal.GetAngleZ() - Angle2D.PIHALF;
+
+			if(slopeAngle == 0)	return; //it's a horizontal plane
+
+			//find slope source linedef
+			Linedef slopeSource = null;
+			bool isFront = false;
+
+			foreach(Sidedef side in Sector.Sector.Sidedefs) {
+				if(side.Line.Action == 181) {
+					if(side.Line.Args[0] == 1 && side.Line.Front != null && side.Line.Front == side) {
+						slopeSource = side.Line;
+						isFront = true;
+						break;
+					} else if(side.Line.Args[0] == 2 && side.Line.Back != null && side.Line.Back == side) {
+						slopeSource = side.Line;
+						break;
+					}
+				}
+			}
+
+			if(slopeSource == null)	return;
+
+			Sector.Sector.Fields.BeforeFieldsChange();
+
+			float sourceAngle = (float)Math.Round(General.ClampAngle(isFront ? -Angle2D.RadToDeg(slopeSource.Angle) + 90 : -Angle2D.RadToDeg(slopeSource.Angle) - 90), 1);
+
+			if((isFront && slopeSource.Front.Sector.FloorHeight > slopeSource.Back.Sector.FloorHeight) ||
+				(!isFront && slopeSource.Front.Sector.FloorHeight < slopeSource.Back.Sector.FloorHeight)) {
+				sourceAngle = General.ClampAngle(sourceAngle + 180);
+			}
+
+			if(sourceAngle != 0) {
+				if(!Sector.Sector.Fields.ContainsKey("rotationfloor"))
+					Sector.Sector.Fields.Add("rotationfloor", new UniValue(UniversalType.Float, sourceAngle));
+				else
+					Sector.Sector.Fields["rotationfloor"].Value = sourceAngle;
+			} else if(Sector.Sector.Fields.ContainsKey("rotationfloor")) {
+				Sector.Sector.Fields.Remove("rotationfloor");
+			}
+
+			//update scaleY
+			float scaleX = Sector.Sector.Fields.GetValue("xscalefloor", 1.0f);
+			float scaleY = (float)Math.Round(scaleX * (1 / (float)Math.Cos(slopeAngle)), 2);
+
+			if(aligny) {
+				if(Sector.Sector.Fields.ContainsKey("yscalefloor"))
+					Sector.Sector.Fields["yscalefloor"].Value = scaleY;
+				else
+					Sector.Sector.Fields.Add("yscalefloor", new UniValue(UniversalType.Float, scaleY));
+			}
+
+			//update texture offsets
+			Vector2D offset;
+			if((isFront && slopeSource.Front.Sector.FloorHeight < slopeSource.Back.Sector.FloorHeight) ||
+				(!isFront && slopeSource.Front.Sector.FloorHeight > slopeSource.Back.Sector.FloorHeight)) {
+				offset = slopeSource.End.Position;
+			} else {
+				offset = slopeSource.Start.Position;
+			}
+
+			offset = offset.GetRotated(Angle2D.DegToRad(sourceAngle));
+
+			if(alignx) {
+				if(Texture != null)	offset.x %= Texture.Width / scaleX;
+
+				if(Sector.Sector.Fields.ContainsKey("xpanningfloor"))
+					Sector.Sector.Fields["xpanningfloor"].Value = (float)Math.Round(-offset.x);
+				else
+					Sector.Sector.Fields.Add("xpanningfloor", new UniValue(UniversalType.Float, (float)Math.Round(-offset.x)));
+			}
+
+			if(aligny) {
+				if(Texture != null) offset.y %= Texture.Height / scaleY;
+
+				if(Sector.Sector.Fields.ContainsKey("ypanningfloor"))
+					Sector.Sector.Fields["ypanningfloor"].Value = (float)Math.Round(offset.y);
+				else
+					Sector.Sector.Fields.Add("ypanningfloor", new UniValue(UniversalType.Float, (float)Math.Round(offset.y)));
+			}
+
+			//update geometry
+			Sector.UpdateSectorGeometry(false);
+		}
 		
 		#endregion
 	}
diff --git a/Source/Plugins/ColorPicker/Windows/LightColorPicker.cs b/Source/Plugins/ColorPicker/Windows/LightColorPicker.cs
index 6f25b28177a508f486ae2c155ea1a360240a506c..9b373aa98140093a85d16b7e3974b639f94d8631 100644
--- a/Source/Plugins/ColorPicker/Windows/LightColorPicker.cs
+++ b/Source/Plugins/ColorPicker/Windows/LightColorPicker.cs
@@ -339,6 +339,7 @@ namespace CodeImp.DoomBuilder.ColorPicker.Windows {
 
         private void colorPickerControl1_OnOkPressed(object sender, EventArgs e) {
 			this.DialogResult = DialogResult.OK;
+			General.Interface.RefreshInfo();
 			Close();
         }
 
diff --git a/Source/Plugins/UMDFControls/Windows/UDMFControlsForm.cs b/Source/Plugins/UMDFControls/Windows/UDMFControlsForm.cs
index 4ac34f61fd743551c692b60ab30098b355776c07..958583c445eb5c5feb21f987a54cca8fd625f945 100644
--- a/Source/Plugins/UMDFControls/Windows/UDMFControlsForm.cs
+++ b/Source/Plugins/UMDFControls/Windows/UDMFControlsForm.cs
@@ -514,6 +514,7 @@ namespace CodeImp.DoomBuilder.UDMFControls
             }
 
             removeDefaultValues();
+			General.Interface.RefreshInfo();
 			this.DialogResult = DialogResult.OK;
             Close();
         }