diff --git a/Source/Core/Editing/CopyPasteManager.cs b/Source/Core/Editing/CopyPasteManager.cs
index 6274d4b20f775545f87de406665909e936c7e433..665d03a531bf509e7ad97cb1f7df0c33c40f7192 100755
--- a/Source/Core/Editing/CopyPasteManager.cs
+++ b/Source/Core/Editing/CopyPasteManager.cs
@@ -225,7 +225,7 @@ namespace CodeImp.DoomBuilder.Editing
 		}
 		
 		// This performs the copy. Returns false when copy was cancelled.
-		private static bool DoCopySelection(string desc)
+		public static bool DoCopySelection(string desc)
 		{
 			// Check if possible to copy/paste
 			if(General.Editing.Mode.Attributes.AllowCopyPaste)
@@ -285,7 +285,7 @@ namespace CodeImp.DoomBuilder.Editing
 		}
 		
 		// This performs the paste. Returns false when paste was cancelled.
-		private static void DoPasteSelection(PasteOptions options)
+		public static void DoPasteSelection(PasteOptions options)
 		{
 			// Check if possible to copy/paste
 			if(General.Editing.Mode.Attributes.AllowCopyPaste)
diff --git a/Source/Plugins/3DFloorMode/Resources/Actions.cfg b/Source/Plugins/3DFloorMode/Resources/Actions.cfg
index 75eb7127060990444087a5f4e81294b73e364a6b..1529ef290ab7e632a8e489551b495ccd50aab755 100644
--- a/Source/Plugins/3DFloorMode/Resources/Actions.cfg
+++ b/Source/Plugins/3DFloorMode/Resources/Actions.cfg
@@ -155,4 +155,14 @@ select3dfloorcontrolsector
 	allowkeys = true;
 	allowmouse = true;
 	allowscroll = true;
+}
+
+duplicate3dfloorgeometry
+{
+	title = "Duplicate and paste geometry";
+	category = "threedfloorplugin";
+	description = "Duplicates and pastes selected geometry and its 3D floors";
+	allowkeys = true;
+	allowmouse = true;
+	allowscroll = true;
 }
\ No newline at end of file
diff --git a/Source/Plugins/3DFloorMode/ThreeDFloor.cs b/Source/Plugins/3DFloorMode/ThreeDFloor.cs
index 61cd17296f775689cd4d67f2da0855b13d629595..9e622f7e2af0d415b009e9a5379217fe71715088 100644
--- a/Source/Plugins/3DFloorMode/ThreeDFloor.cs
+++ b/Source/Plugins/3DFloorMode/ThreeDFloor.cs
@@ -218,6 +218,13 @@ namespace CodeImp.DoomBuilder.ThreeDFloorMode
 		}
 
 		public bool CreateGeometry(List<int> tagblacklist)
+		{
+			int newtag;
+
+			return CreateGeometry(tagblacklist, false, out newtag);
+		}
+
+		public bool CreateGeometry(List<int> tagblacklist, bool forcenewtag, out int newtag)
 		{
 			List<DrawnVertex> drawnvertices = new List<DrawnVertex>();
 			List<Vertex> vertices = new List<Vertex>();
@@ -225,6 +232,8 @@ namespace CodeImp.DoomBuilder.ThreeDFloorMode
 			Vector3D slopebottomthingpos = new Vector3D(0, 0, 0);
 			Line2D slopeline = new Line2D(0, 0, 0, 0);
 
+			newtag = -1;
+
 			drawnvertices = BuilderPlug.Me.ControlSectorArea.GetNewControlSectorVertices();
 
 			if (Tools.DrawLines(drawnvertices) == false)
@@ -257,9 +266,9 @@ namespace CodeImp.DoomBuilder.ThreeDFloorMode
 			// With multiple tag support in UDMF only one tag is needed, so bind it right away
 			if (General.Map.UDMF == true)
 			{
-				if (isnew)
+				if (isnew || forcenewtag)
 				{
-					udmftag = BuilderPlug.Me.ControlSectorArea.GetNewSectorTag(tagblacklist);
+					newtag = udmftag = BuilderPlug.Me.ControlSectorArea.GetNewSectorTag(tagblacklist);
 					tagblacklist.Add(udmftag);
 				}					
 
diff --git a/Source/Plugins/3DFloorMode/ThreeDFloorMode.cs b/Source/Plugins/3DFloorMode/ThreeDFloorMode.cs
index 3813e3ebdadcf19139d1cd0c9215d0713fc49d4f..a620029a4ce06c9997f87cee40505c5107077ebe 100644
--- a/Source/Plugins/3DFloorMode/ThreeDFloorMode.cs
+++ b/Source/Plugins/3DFloorMode/ThreeDFloorMode.cs
@@ -40,6 +40,7 @@ using CodeImp.DoomBuilder.Types;
 using CodeImp.DoomBuilder.BuilderModes;
 using CodeImp.DoomBuilder.BuilderModes.Interface;
 using CodeImp.DoomBuilder.Controls;
+using CodeImp.DoomBuilder.Config;
 // using CodeImp.DoomBuilder.GZBuilder.Geometry;
 
 #endregion
@@ -1176,7 +1177,29 @@ namespace CodeImp.DoomBuilder.ThreeDFloorMode
 				SelectSector(highlighted, true, true);
 			}
 
-			return base.OnCopyBegin();
+			General.Map.Map.MarkAllSelectedGeometry(true, false, true, true, false);
+
+			return General.Map.Map.GetMarkedSectors(true).Count > 0;
+		}
+
+		public override bool OnPasteBegin(PasteOptions options)
+		{
+			return true;
+		}
+
+		// This is called when something was pasted.
+		public override void OnPasteEnd(PasteOptions options)
+		{
+			General.Map.Map.ClearAllSelected();
+			General.Map.Map.SelectMarkedGeometry(true, true);
+			General.Map.Renderer2D.UpdateExtraFloorFlag(); //mxd
+
+			// Switch to EditSelectionMode
+			EditSelectionMode editmode = new EditSelectionMode();
+			editmode.Pasting = true;
+			editmode.UpdateSlopes = true;
+			editmode.PasteOptions = options;
+			General.Editing.ChangeMode(editmode);
 		}
 
 		// When undo is used
@@ -1460,6 +1483,98 @@ namespace CodeImp.DoomBuilder.ThreeDFloorMode
 			General.Interface.DisplayStatus(StatusType.Info, String.Format("3D floor control sector selected. {0} sector(s) selected.", General.Map.Map.GetSelectedSectors(true).Count));
 		}
 
+		[BeginAction("duplicate3dfloorgeometry")]
+		public void Duplicate3DFloorGeometry()
+		{
+			List<Sector> selectedsectors;
+			List<ThreeDFloor> duplicatethreedfloors;
+			Dictionary<int, int> tagreplacements = new Dictionary<int, int>();
+			List<int> tagblacklist = new List<int>();
+
+			// No selection made? But we have a highlight!
+			if ((General.Map.Map.GetSelectedSectors(true).Count == 0) && (highlighted != null))
+			{
+				// Make the highlight the selection
+				SelectSector(highlighted, true, true);
+			}
+
+			selectedsectors = General.Map.Map.GetSelectedSectors(true).ToList();
+
+			// Get the 3D floors we need to duplicate
+			duplicatethreedfloors = BuilderPlug.GetThreeDFloors(selectedsectors);
+
+			// Create a list of all tags used by the control sectors. This is necessary so that
+			// tags that will be assigned to not yet existing geometry will not be used
+			foreach (ThreeDFloor tdf in threedfloors)
+				foreach (int tag in tdf.Tags)
+					if (!tagblacklist.Contains(tag))
+						tagblacklist.Add(tag);
+
+			if (duplicatethreedfloors.Count == 0)
+				return;
+
+			General.Map.UndoRedo.CreateUndo("Duplicate 3D floor control sectors before pasting");
+
+			// Create a new control sector for each 3D floor that needs to be duplicated. Force it to generate
+			// a new tag, and store the old (current) and new tag
+			foreach (ThreeDFloor tdf in duplicatethreedfloors)
+			{
+				int newtag;
+				int oldtag = tdf.UDMFTag;
+
+				if(tdf.CreateGeometry(new List<int>(), true, out newtag))
+				{
+					tagreplacements[oldtag] = newtag;
+				}
+				else
+				{
+					General.Interface.DisplayStatus(StatusType.Warning, "Could not create 3D floor control sector geometry");
+					General.Map.UndoRedo.WithdrawUndo();
+					return;
+				}
+			}
+
+			// Replace the old tags of the selected sectors with the new tags
+			foreach (Sector s in selectedsectors)
+			{
+				foreach (int oldtag in tagreplacements.Keys)
+				{
+					if (s.Tags.Contains(oldtag))
+					{
+						s.Tags.Remove(oldtag);
+						s.Tags.Add(tagreplacements[oldtag]);
+					}
+				}
+			}
+
+			// Store the selected sectors (with the new tags) in the clipboard
+			if(!CopyPasteManager.DoCopySelection("3D floor test copy!"))
+			{
+				General.Interface.DisplayStatus(StatusType.Warning, "Something failed trying to copy the selection");
+				General.Map.UndoRedo.WithdrawUndo();
+				return;
+			}
+
+			// Now set the tags of the selected sectors back to the old tags
+			foreach(Sector s in selectedsectors)
+			{
+				foreach(int oldtag in tagreplacements.Keys)
+				{
+					if(s.Tags.Contains(tagreplacements[oldtag]))
+					{
+						s.Tags.Remove(tagreplacements[oldtag]);
+						s.Tags.Add(oldtag);
+					}
+				}
+			}
+
+			// For this operation we have to make sure the tags and actions are not changed, no matter
+			// what the user preference is, otherwise it will not work
+			PasteOptions po = new PasteOptions() { ChangeTags = 0, RemoveActions = false };
+
+			CopyPasteManager.DoPasteSelection(po);
+		}
+
 		#endregion
 	}
 }
diff --git a/Source/Plugins/BuilderModes/ClassicModes/BaseClassicMode.cs b/Source/Plugins/BuilderModes/ClassicModes/BaseClassicMode.cs
index cd3849336fdbb894e2d327c881f38fc7776a70c7..85d866c2bfb52f12e211c097b2a7ac424711c892 100755
--- a/Source/Plugins/BuilderModes/ClassicModes/BaseClassicMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/BaseClassicMode.cs
@@ -100,6 +100,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			// Switch to EditSelectionMode
 			EditSelectionMode editmode = new EditSelectionMode();
 			editmode.Pasting = true;
+			editmode.UpdateSlopes = false;
 			editmode.PasteOptions = options;
 			General.Editing.ChangeMode(editmode);
 		}
diff --git a/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
index b65bdcf2d852c603e81daee781e778ad35e8708f..4127904c303bbaec33430b3bf49f6be929ebed6a 100755
--- a/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
@@ -212,7 +212,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 		// Options
 		private bool snaptogrid;		// SHIFT to toggle
-		private bool snaptonearest;		// CTRL to enable
+		private bool snaptonearest;     // CTRL to enable
+
+		private bool updateslopes;
 		
 		#endregion
 
@@ -222,6 +224,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		
 		public bool Pasting { get { return pasting; } set { pasting = value; } }
 		public PasteOptions PasteOptions { get { return pasteoptions; } set { pasteoptions = value.Copy(); } }
+		
+		public bool UpdateSlopes { get { return updateslopes; } set { updateslopes = value; } }
 
 		//mxd. Modification
 		internal bool UsePrecisePosition { get { return usepreciseposition; } set { usepreciseposition = value; } }
@@ -246,6 +250,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		{
 			// Initialize
 			mode = ModifyMode.None;
+			updateslopes = true;
 		}
 
 		//mxd. Another constructor. Used indirectly from ImportObjAsTerrainMode.OnAccept.
@@ -254,6 +259,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 			// Initialize
 			this.pasting = pasting;
 			this.mode = ModifyMode.None;
+			this.updateslopes = true;
 		}
 
 		// Disposer
@@ -1591,7 +1597,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 
 					// Create cache of 3D floor control sectors that reference the selected sectors. Only do it if not pasting, since the slopes
 					// will only be updated when not pasting, since it'd otherwise screw up the original slopes 
-					if (!pasting)
+					if (updateslopes)
 					{
 						foreach (Linedef ld in General.Map.Map.Linedefs)
 						{
@@ -1646,7 +1652,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 						}
 
 						// Update the slopes of 3D floor control sectors. Only do it if not pasting, since it'd otherwise screw up the original slopes 
-						if (!pasting && controlsectors.ContainsKey(s))
+						if (updateslopes && controlsectors.ContainsKey(s))
 						{
 							foreach (Sector cs in controlsectors[s])
 							{
@@ -1699,7 +1705,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
 						}
 
 						// Update the slopes of 3D floor control sectors. Only do it if not pasting, since it'd otherwise screw up the original slopes 
-						if (!pasting && controlsectors.ContainsKey(s))
+						if (updateslopes && controlsectors.ContainsKey(s))
 						{
 							foreach (Sector cs in controlsectors[s])
 							{