--[[
	Title: Adv. Dupe 2 Tool

	Desc: Defines the AD2 tool and assorted functionalities.

	Author: TB

	Version: 1.0
]]

TOOL.Category = "Construction"
TOOL.Name = "#Tool.advdupe2.name"
cleanup.Register( "AdvDupe2" )
require( "controlpanel" )

if(SERVER) then
	CreateConVar("sbox_maxgmod_contr_spawners",5)

	local phys_constraint_system_types = {
		Weld          = true,
		Rope          = true,
		Elastic       = true,
		Slider        = true,
		Axis          = true,
		AdvBallsocket = true,
		Motor         = true,
		Pulley        = true,
		Ballsocket    = true,
		Winch         = true,
		Hydraulic     = true,
		WireMotor     = true,
		WireHydraulic = true
	}
	--Orders constraints so that the dupe uses as little constraint systems as possible
	local function GroupConstraintOrder( ply, constraints )
		--First separate the nocollides, sorted, and unsorted constraints
		local sorted, unsorted = {}, {}
		for k, v in pairs(constraints) do
			if phys_constraint_system_types[v.Type] then
				sorted[#sorted+1] = v
			else
				unsorted[#unsorted+1] = v
			end
		end

		local sortingSystems = {}
		local fullSystems = {}
		local function buildSystems(input)
			while next(input) ~= nil do
				for k, v in pairs(input) do
					for systemi, system in pairs(sortingSystems) do
						for _, target in pairs(system) do
							for x = 1, 4 do
								if v.Entity[x] then
									for y = 1, 4 do
										if target.Entity[y] and v.Entity[x].Index == target.Entity[y].Index then
											system[#system + 1] = v
											if #system == 100 then
												fullSystems[#fullSystems + 1] = system
												table.remove(sortingSystems, systemi)
											end
											input[k] = nil
											goto super_loopbreak
										end
									end
								end
							end
						end
					end
				end

				--Normally skipped by the goto unless no cluster is found. If so, make a new one.
				local k = next(input)
				sortingSystems[#sortingSystems + 1] = {input[k]}
				input[k] = nil

				::super_loopbreak::
			end
		end
		buildSystems(sorted)

		local ret = {}
		for _, system in pairs(fullSystems) do
			for _, v in pairs(system) do
				ret[#ret + 1] = v
			end
		end
		for _, system in pairs(sortingSystems) do
			for _, v in pairs(system) do
				ret[#ret + 1] = v
			end
		end
		for k, v in pairs(unsorted) do
			ret[#ret + 1] = v
		end

		if #fullSystems ~= 0 then
			ply:ChatPrint("DUPLICATOR: WARNING, Number of constraints exceeds 100: (".. #ret .."). Constraint sorting might not work as expected.")
		end

		return ret
	end

	local function CreationConstraintOrder( constraints )
		local ret = {}
		for k, v in pairs( constraints ) do
			ret[#ret + 1] = k
		end
		table.sort(ret)
		for i = 1, #ret do
			ret[i] = constraints[ret[i]]
		end
		return ret
	end

	local function GetSortedConstraints( ply, constraints )
		if ply:GetInfo("advdupe2_sort_constraints") ~= "0" then
			return GroupConstraintOrder( ply, constraints )
		else
			return CreationConstraintOrder( constraints )
		end
	end

	local areacopy_classblacklist = {
		gmod_anchor = true
	}

	local function PlayerCanDupeCPPI(ply, ent)
		if not AdvDupe2.duplicator.IsCopyable(ent) or areacopy_classblacklist[ent:GetClass()] then return false end
		return ent:CPPIGetOwner()==ply
	end

	-- Code from WireLib.CanTool
	local zero = Vector(0, 0, 0)
	local norm = Vector(1, 0, 0)

	local tr = { ---@type TraceResult
		Hit = true, HitNonWorld = true, HitNoDraw = false, HitSky = false, AllSolid = true,
		HitNormal = zero, Normal = norm,

		Fraction = 1, FractionLeftSolid = 0,
		HitBox = 0, HitGroup = 0, HitTexture = "**studio**",
		MatType = 0, PhysicsBone = 0, SurfaceProps = 0, DispFlags = 0, Contents = 0,

		Entity = NULL, HitPos = zero, StartPos = zero,
	}

	local function PlayerCanDupeTool(ply, ent)
		if not AdvDupe2.duplicator.IsCopyable(ent) or areacopy_classblacklist[ent:GetClass()] then return false end

		local pos = ent:GetPos()
		tr.Entity, tr.HitPos, tr.StartPos = ent, pos, pos

		return hook.Run( "CanTool", ply, tr, "advdupe2" ) ~= false
	end

	--Find all the entities in a box, given the adjacent corners and the player
	local function FindInBox(min, max, ply)
		local PPCheck = (tobool(ply:GetInfo("advdupe2_copy_only_mine")) and ply.CPPIGetOwner~=nil) and PlayerCanDupeCPPI or PlayerCanDupeTool
		local EntTable = {}

		for _, ent in ipairs(ents.FindInBox(min, max)) do
			if PPCheck(ply, ent) then
				EntTable[ent:EntIndex()] = ent
			end
		end

		return EntTable
	end

	--[[
		Name: GetDupeAngleOffset
		Desc: Retrieves duplication angle offsets from player
		Returns: <angle> Created angle
	]]
	local function GetDupeAngleOffset(ply)
		local p = math.Clamp(ply:GetInfoNum("advdupe2_offset_pitch", 0), -180, 180)
		local y = math.Clamp(ply:GetInfoNum("advdupe2_offset_yaw"  , 0), -180, 180)
		local r = math.Clamp(ply:GetInfoNum("advdupe2_offset_roll" , 0), -180, 180)
		return Angle(p, y, r)
	end

	--[[
		Name: GetDupeElevation
		Desc: Retrieves duplication Z elevation
		Returns: <number> Dupe elevation
	]]
	local function GetDupeElevation(ply)
		local con = ply:GetInfoNum("advdupe2_offset_z", 0)
		local enz = (tonumber(ply.AdvDupe2.HeadEnt.Z) or 0)
		return math.Clamp(con + enz, -32000, 32000)
	end

	--[[
		Name: LeftClick
		Desc: Defines the tool's behavior when the player left-clicks.
		Params: <trace> trace
		Returns: <boolean> success
	]]
	local messages = {
		["Pasting"] = "pasting.",
		["Downloading"] = "downloading.",
		["Uploading"] = "uploading."
	}
	function TOOL:LeftClick( trace )
		if(not trace) then return false end

		local ply = self:GetOwner()
		local dupe = ply.AdvDupe2

		if not (dupe and dupe.Entities) then return false end

		for key, msg in pairs(messages) do
			if dupe[key] then
				AdvDupe2.Notify(ply, "Advanced Duplicator 2 is busy " .. msg, NOTIFY_ERROR)
				return false
			end
		end

		dupe.Angle = GetDupeAngleOffset(ply)
		dupe.Position = Vector(trace.HitPos)
		dupe.Position.z = dupe.Position.z + GetDupeElevation(ply)

		if(tobool(ply:GetInfo("advdupe2_offset_world"))) then
			dupe.Angle = dupe.Angle - dupe.Entities[dupe.HeadEnt.Index].PhysicsObjects[0].Angle
		end

		dupe.Pasting = true
		AdvDupe2.Notify(ply,"Pasting...")
		local origin
		if(tobool(ply:GetInfo("advdupe2_original_origin"))) then
			origin = dupe.HeadEnt.Pos
		end

		AdvDupe2.InitPastingQueue(ply, dupe.Position, dupe.Angle, origin,
			tobool(ply:GetInfo("advdupe2_paste_constraints")),
			tobool(ply:GetInfo("advdupe2_paste_parents")),
			tobool(ply:GetInfo("advdupe2_paste_disparents")),
			tobool(ply:GetInfo("advdupe2_paste_protectoveride")))

		return true
	end

	--[[
		Name: RightClick
		Desc: Defines the tool's behavior when the player right-clicks.
		Params: <trace> trace
		Returns: <boolean> success
	]]
	function TOOL:RightClick( trace )
		local ply = self:GetOwner()
		local dupe = ply.AdvDupe2

		if not dupe then dupe = {}; ply.AdvDupe2 = dupe end

		if(dupe.Pasting or dupe.Downloading) then
			AdvDupe2.Notify(ply,"Advanced Duplicator 2 is busy.", NOTIFY_ERROR)
			return false
		end

		--Set Area Copy on or off
		if( ply:KeyDown(IN_SPEED) and not ply:KeyDown(IN_WALK) ) then
			if(self:GetStage()==0) then
				AdvDupe2.DrawSelectBox(ply)
				self:SetStage(1)
				return false
			elseif(self:GetStage()==1) then
				AdvDupe2.RemoveSelectBox(ply)
				self:SetStage(0)
				return false
			end
		end

		if(not trace or not trace.Hit) then return false end

		local Entities, Constraints, AddOne
		local HeadEnt = {}
		--If area copy is on
		if(self:GetStage()==1) then
			local area_size = math.Clamp(tonumber(ply:GetInfo("advdupe2_area_copy_size")) or 50, 0, 30720)
			local Pos = trace.HitNonWorld and trace.Entity:GetPos() or trace.HitPos
			local T = (Vector(area_size,area_size,area_size)+Pos)
			local B = (Vector(-area_size,-area_size,-area_size)+Pos)

			local Ents = FindInBox(B,T, ply)
			local _, Ent = next(Ents)
			if not Ent then
				self:SetStage(0)
				AdvDupe2.RemoveSelectBox(ply)
				return true
			end

			Ent = trace.HitNonWorld and trace.Entity or Ent
			HeadEnt.Index = Ent:EntIndex()
			HeadEnt.Pos = Ent:GetPos()

			Entities, Constraints = AdvDupe2.duplicator.AreaCopy(ply, Ents, HeadEnt.Pos, tobool(ply:GetInfo("advdupe2_copy_outside")))

			self:SetStage(0)
			AdvDupe2.RemoveSelectBox(ply)
		elseif trace.HitNonWorld then	--Area Copy is off
			-- Filter duplicator blocked entities out.
			if not AdvDupe2.duplicator.IsCopyable( trace.Entity ) then
				return false
			end

			--If Alt is being held, add a prop to the dupe
			if(ply:KeyDown(IN_WALK) and dupe.Entities~=nil and next(dupe.Entities)~=nil) then
				Entities = dupe.Entities
				Constraints = dupe.Constraints
				HeadEnt = dupe.HeadEnt

				AdvDupe2.duplicator.Copy( ply, trace.Entity, Entities, Constraints, HeadEnt.Pos)

				--Only add the one ghost
				AddOne = Entities[trace.Entity:EntIndex()]
			else
				Entities = {}
				Constraints = {}
				HeadEnt.Index = trace.Entity:EntIndex()
				HeadEnt.Pos = trace.HitPos

				AdvDupe2.duplicator.Copy( ply, trace.Entity, Entities, Constraints, trace.HitPos )
			end
		else --Non valid entity or clicked the world
			if dupe.Entities then
				--clear the dupe
				net.Start("AdvDupe2_RemoveGhosts")
				net.Send(ply)
				dupe.Entities = nil
				dupe.Constraints = nil
				net.Start("AdvDupe2_ResetDupeInfo")
				net.Send(ply)
				AdvDupe2.ResetOffsets(ply)
				return true
			else
				--select all owned props
				Entities = {}
				local PPCheck = (tobool(ply:GetInfo("advdupe2_copy_only_mine")) and CPPI~=nil) and PlayerCanDupeCPPI or PlayerCanDupeTool
				for _, ent in ents.Iterator() do
					if PPCheck( ply, ent ) then
						Entities[ent:EntIndex()] = ent
					end
				end

				local _, Ent = next(Entities)
				if not Ent then
				net.Start("AdvDupe2_RemoveGhosts")
				net.Send(ply)
					return true
				end

				HeadEnt.Index = Ent:EntIndex()
				HeadEnt.Pos = Ent:GetPos()

				Entities, Constraints = AdvDupe2.duplicator.AreaCopy(ply, Entities, HeadEnt.Pos, tobool(ply:GetInfo("advdupe2_copy_outside")))
			end
		end

		if not HeadEnt.Z then
			local WorldTrace = util.TraceLine({
				mask   = MASK_NPCWORLDSTATIC,
				start  = HeadEnt.Pos + Vector(0,0,1),
				endpos = HeadEnt.Pos-Vector(0,0,50000)
			})

			HeadEnt.Z = WorldTrace.Hit and math.abs(HeadEnt.Pos.Z - WorldTrace.HitPos.Z) or 0
		end

		dupe.HeadEnt = HeadEnt
		dupe.Entities = Entities
		dupe.Constraints = GetSortedConstraints(ply, Constraints)
		dupe.Revision = AdvDupe2.CodecRevision

		net.Start("AdvDupe2_SetDupeInfo")
			net.WriteString("")
			net.WriteString(ply:Nick())
			net.WriteString(os.date("%d %B %Y"))
			net.WriteString(os.date("%I:%M %p"))
			net.WriteString("")
			net.WriteString("")
			net.WriteString(table.Count(dupe.Entities))
			net.WriteString(#dupe.Constraints)
		net.Send(ply)

		if AddOne then
			AdvDupe2.SendGhost(ply, AddOne)
		else
			AdvDupe2.SendGhosts(ply)
		end

		AdvDupe2.ResetOffsets(ply)

		return true
	end

	--Checks table, re-draws loading bar, and recreates ghosts when tool is pulled out
	function TOOL:Deploy()
		local ply = self:GetOwner()
		local dupe = ply.AdvDupe2

		if not dupe then dupe = {}; ply.AdvDupe2 = dupe end

		if(not dupe.Entities) then return end

		net.Start("AdvDupe2_StartGhosting")
		net.Send(ply)

		if(dupe.Queued) then
			AdvDupe2.InitProgressBar(ply, "Queued: ")
			return
		end

		if(dupe.Pasting) then
			AdvDupe2.InitProgressBar(ply, "Pasting: ")
			return
		else
			if(dupe.Uploading) then
				AdvDupe2.InitProgressBar(ply, "Uploading: ")
				return
			elseif(dupe.Downloading) then
				AdvDupe2.InitProgressBar(ply, "Saving: ")
				return
			end
		end

	end

	--Removes progress bar
	function TOOL:Holster()
		AdvDupe2.RemoveProgressBar(self:GetOwner())
	end

	--[[
		Name: Reload
		Desc: Creates an Advance Contraption Spawner.
		Params: <trace> trace
		Returns: <boolean> success
	]]
	function TOOL:Reload( trace )
		if(not trace.Hit) then return false end

		local ply = self:GetOwner()
		local dupe = ply.AdvDupe2

		if not dupe then dupe = {}; ply.AdvDupe2 = dupe end

		if(self:GetStage()==1) then
			local areasize = math.Clamp(tonumber(ply:GetInfo("advdupe2_area_copy_size")) or 50, 0, 30720)
			net.Start("AdvDupe2_CanAutoSave")
				net.WriteVector(trace.HitPos)
				net.WriteFloat(areasize)
				if(trace.Entity) then
					net.WriteUInt(trace.Entity:EntIndex(), 16)
				else
					net.WriteUInt(0, 16)
				end
			net.Send(ply)
			self:SetStage(0)
			AdvDupe2.RemoveSelectBox(ply)
			dupe.TempAutoSavePos = trace.HitPos
			dupe.TempAutoSaveSize = areasize
			dupe.TempAutoSaveOutSide = tobool(ply:GetInfo("advdupe2_copy_outside"))
			return true
		end

		--If a contraption spawner was clicked then update it with the current settings
		if(trace.Entity:GetClass()=="gmod_contr_spawner") then
			local delay = tonumber(ply:GetInfo("advdupe2_contr_spawner_delay"))
			local undo_delay = tonumber(ply:GetInfo("advdupe2_contr_spawner_undo_delay"))
			local min
			local max
			if(not delay) then
				delay = tonumber(GetConVarString("AdvDupe2_MinContraptionSpawnDelay")) or 0.2
			else
				if(not game.SinglePlayer()) then
					min = tonumber(GetConVarString("AdvDupe2_MinContraptionSpawnDelay")) or 0.2
					if (delay < min) then
						delay = min
					end
				elseif(delay<0) then
					delay = 0
				end
			end

			if(not undo_delay) then
				undo_delay = tonumber(GetConVarString("AdvDupe2_MinContraptionUndoDelay"))
			else
				if(not game.SinglePlayer()) then
					min = tonumber(GetConVarString("AdvDupe2_MinContraptionUndoDelay")) or 0.1
					max = tonumber(GetConVarString("AdvDupe2_MaxContraptionUndoDelay")) or 60
					if(undo_delay < min) then
						undo_delay = min
					elseif(undo_delay > max) then
						undo_delay = max
					end
				elseif(undo_delay < 0) then
					undo_delay = 0
				end
			end

			trace.Entity:GetTable():SetOptions(
				ply, delay, undo_delay,
				tonumber(ply:GetInfo("advdupe2_contr_spawner_key")),
				tonumber(ply:GetInfo("advdupe2_contr_spawner_undo_key")),
				tonumber(ply:GetInfo("advdupe2_contr_spawner_disgrav")) or 0,
				tonumber(ply:GetInfo("advdupe2_contr_spawner_disdrag")) or 0,
				tonumber(ply:GetInfo("advdupe2_contr_spawner_addvel")) or 1 )

			return true
		end

		--Create a contraption spawner
		if dupe and dupe.Entities then
			local headent = dupe.Entities[dupe.HeadEnt.Index]
			local Pos, Ang

			if(headent) then
				if(tobool(ply:GetInfo("advdupe2_original_origin"))) then
					Pos = dupe.HeadEnt.Pos + headent.PhysicsObjects[0].Pos
					Ang = headent.PhysicsObjects[0].Angle
				else
					local EntAngle = headent.PhysicsObjects[0].Angle
					if(tobool(ply:GetInfo("advdupe2_offset_world"))) then EntAngle = Angle(0,0,0) end
					trace.HitPos.Z = trace.HitPos.Z + GetDupeElevation(ply)
					Pos, Ang = LocalToWorld(headent.PhysicsObjects[0].Pos, EntAngle, trace.HitPos, GetDupeAngleOffset(ply))
				end
			else
				AdvDupe2.Notify(ply, "Invalid head entity to spawn contraption spawner.")
				return false
			end

			if(headent.Class=="gmod_contr_spawner") then
				AdvDupe2.Notify(ply, "Cannot make a contraption spawner from a contraption spawner.")
				return false
			end


			local spawner = MakeContraptionSpawner(
				ply, Pos, Ang, dupe.HeadEnt.Index,
				table.Copy(dupe.Entities),
				table.Copy(dupe.Constraints),
				tonumber(ply:GetInfo("advdupe2_contr_spawner_delay")),
				tonumber(ply:GetInfo("advdupe2_contr_spawner_undo_delay")), headent.Model,
				tonumber(ply:GetInfo("advdupe2_contr_spawner_key")),
				tonumber(ply:GetInfo("advdupe2_contr_spawner_undo_key")),
				tonumber(ply:GetInfo("advdupe2_contr_spawner_disgrav")) or 0,
				tonumber(ply:GetInfo("advdupe2_contr_spawner_disdrag")) or 0,
				tonumber(ply:GetInfo("advdupe2_contr_spawner_addvel")) or 1,
				tonumber(ply:GetInfo("advdupe2_contr_spawner_hideprops")) or 0)

			if not spawner then
				return false
			end

			local undotxt = "AdvDupe2: Spawner ["..tostring(spawner:EntIndex()).."]"..(dupe.Name and "("..tostring(dupe.Name)..")" or "")

			ply:AddCleanup( "AdvDupe2", spawner )
			undo.Create( undotxt )
				undo.AddEntity( spawner )
				undo.SetPlayer( ply )
			undo.Finish()

			return true
		end
	end

	--Called to clean up the tool when pasting is finished or undo during pasting
	function AdvDupe2.FinishPasting(Player, Paste)
		Player.AdvDupe2.Pasting=false
		AdvDupe2.RemoveProgressBar(Player)
		if(Paste) then AdvDupe2.Notify(Player,"Finished Pasting!") end
	end

	--function for creating a contraption spawner
	function MakeContraptionSpawner( ply, Pos, Ang, HeadEnt, EntityTable, ConstraintTable, delay, undo_delay, model, key, undo_key, disgrav, disdrag, addvel, hideprops)

		if not ply:CheckLimit("gmod_contr_spawners") then return nil end

		if(not game.SinglePlayer()) then
			if(table.Count(EntityTable)>tonumber(GetConVarString("AdvDupe2_MaxContraptionEntities"))) then
				AdvDupe2.Notify(ply,"Contraption Spawner exceeds the maximum amount of "..GetConVarString("AdvDupe2_MaxContraptionEntities").." entities for a spawner!",NOTIFY_ERROR)
				return false
			end
			if(#ConstraintTable>tonumber(GetConVarString("AdvDupe2_MaxContraptionConstraints"))) then
				AdvDupe2.Notify(ply,"Contraption Spawner exceeds the maximum amount of "..GetConVarString("AdvDupe2_MaxContraptionConstraints").." constraints for a spawner!",NOTIFY_ERROR)
				return false
			end
		end

		local spawner = ents.Create("gmod_contr_spawner")
		if not IsValid(spawner) then return end

		spawner:SetPos(Pos)
		spawner:SetAngles(Ang)
		spawner:SetModel(model)
		spawner:SetRenderMode(RENDERMODE_TRANSALPHA)
		spawner:SetCreator(ply)
		spawner:Spawn()

		duplicator.ApplyEntityModifiers(ply, spawner)

		if IsValid(spawner:GetPhysicsObject()) then
			spawner:GetPhysicsObject():EnableMotion(false)
		end

		local min
		local max
		if(not delay) then
			delay = tonumber(GetConVarString("AdvDupe2_MinContraptionSpawnDelay")) or 0.2
		else
			if(not game.SinglePlayer()) then
				min = tonumber(GetConVarString("AdvDupe2_MinContraptionSpawnDelay")) or 0.2
				if (delay < min) then
					delay = min
				end
			elseif(delay<0) then
				delay = 0
			end
		end

		if(not undo_delay) then
			undo_delay = tonumber(GetConVarString("AdvDupe2_MinContraptionUndoDelay"))
		else
			if(not game.SinglePlayer()) then
				min = tonumber(GetConVarString("AdvDupe2_MinContraptionUndoDelay")) or 0.1
				max = tonumber(GetConVarString("AdvDupe2_MaxContraptionUndoDelay")) or 60
				if(undo_delay < min) then
					undo_delay = min
				elseif(undo_delay > max) then
					undo_delay = max
				end
			elseif(undo_delay < 0) then
				undo_delay = 0
			end
		end

		-- Set options
		spawner:SetPlayer(ply)
		spawner:GetTable():SetOptions(ply, delay, undo_delay, key, undo_key, disgrav, disdrag, addvel, hideprops)

		local tbl = {
			ply        = ply,
			delay      = delay,
			undo_delay = undo_delay,
			disgrav    = disgrav,
			disdrag    = disdrag,
			addvel     = addvel,
			hideprops  = hideprops
		}
		table.Merge(spawner:GetTable(), tbl)
		spawner:SetDupeInfo(HeadEnt, EntityTable, ConstraintTable)
		spawner:AddGhosts(ply)

		ply:AddCount("gmod_contr_spawners", spawner)
		ply:AddCleanup("gmod_contr_spawner", spawner)
		return spawner
	end

	duplicator.RegisterEntityClass("gmod_contr_spawner", MakeContraptionSpawner,
		"Pos", "Ang", "HeadEnt", "EntityTable", "ConstraintTable", "delay",
		"undo_delay", "model", "key", "undo_key", "disgrav", "disdrag", "addvel", "hideprops")

	function AdvDupe2.InitProgressBar(ply,label)
		net.Start("AdvDupe2_InitProgressBar")
			net.WriteString(label)
		net.Send(ply)
	end

	function AdvDupe2.DrawSelectBox(ply)
		net.Start("AdvDupe2_DrawSelectBox")
		net.Send(ply)
	end

	function AdvDupe2.RemoveSelectBox(ply)
		net.Start("AdvDupe2_RemoveSelectBox")
		net.Send(ply)
	end

	function AdvDupe2.UpdateProgressBar(ply,percent)
		net.Start("AdvDupe2_UpdateProgressBar")
			net.WriteFloat(percent)
		net.Send(ply)
	end

	function AdvDupe2.RemoveProgressBar(ply)
		net.Start("AdvDupe2_RemoveProgressBar")
		net.Send(ply)
	end

	--Reset the offsets of height, pitch, yaw, and roll back to default
	function AdvDupe2.ResetOffsets(ply, keep)

		if(not keep) then
			ply.AdvDupe2.Name = nil
		end
		net.Start("AdvDupe2_ResetOffsets")
		net.Send(ply)
	end

	net.Receive("AdvDupe2_CanAutoSave", function(len, ply, len2)

		local desc = net.ReadString()
		local ent = net.ReadInt(16)
		local dupe = ply.AdvDupe2

		if(ent~=0) then
			dupe.AutoSaveEnt = ent
			if(ply:GetInfo("advdupe2_auto_save_contraption")=="1") then
				dupe.AutoSaveEnt = ents.GetByIndex( dupe.AutoSaveEnt )
			end
		else
			if(ply:GetInfo("advdupe2_auto_save_contraption")=="1") then
				AdvDupe2.Notify(ply, "No entity selected to auto save contraption.", NOTIFY_ERROR)
				return
			end
			dupe.AutoSaveEnt = nil
		end

		dupe.AutoSavePos = dupe.TempAutoSavePos
		dupe.AutoSaveSize = dupe.TempAutoSaveSize
		dupe.AutoSaveOutSide = dupe.TempAutoSaveOutSide
		dupe.AutoSaveContr = ply:GetInfo("advdupe2_auto_save_contraption")=="1"
		dupe.AutoSaveDesc = desc

		local time = math.Clamp(tonumber(ply:GetInfo("advdupe2_auto_save_time")) or 2, 2, 30)
		if(game.SinglePlayer()) then
			dupe.AutoSavePath = net.ReadString()
		end

		AdvDupe2.Notify(ply, "Your area will be auto saved every "..(time*60).." seconds.")
		local name = "AdvDupe2_AutoSave_"..ply:UniqueID()
		if(timer.Exists(name)) then
			timer.Adjust(name, time*60, 0)
			return
		end
		timer.Create(name, time*60, 0, function()
			if(not IsValid(ply)) then
				timer.Remove(name)
				return
			end

			local dupe = ply.AdvDupe2
			if(dupe.Downloading) then
				AdvDupe2.Notify(ply, "Skipping auto save, tool is busy.", NOTIFY_ERROR)
				return
			end

			local Tab = {Entities={}, Constraints={}, HeadEnt={}}

			if(dupe.AutoSaveContr) then
				if(not IsValid(dupe.AutoSaveEnt)) then
					timer.Remove(name)
					AdvDupe2.Notify(ply, "Head entity for auto save no longer valid; stopping auto save.", NOTIFY_ERROR)
					return
				end

				Tab.HeadEnt.Index = dupe.AutoSaveEnt:EntIndex()
				Tab.HeadEnt.Pos = dupe.AutoSaveEnt:GetPos()

				local WorldTrace = util.TraceLine({
					mask   = MASK_NPCWORLDSTATIC,
					start  = Tab.HeadEnt.Pos + Vector(0,0,1),
					endpos = Tab.HeadEnt.Pos - Vector(0,0,50000)
				})

				Tab.HeadEnt.Z = WorldTrace.Hit and math.abs(Tab.HeadEnt.Pos.Z - WorldTrace.HitPos.Z) or 0
				AdvDupe2.duplicator.Copy( ply, dupe.AutoSaveEnt, Tab.Entities, Tab.Constraints, Tab.HeadEnt.Pos )
			else
				local i = dupe.AutoSaveSize
				local Pos = dupe.AutoSavePos
				local T = Vector( i, i, i); T:Add(Pos)
				local B = Vector(-i,-i,-i); B:Add(Pos)

				local Entities = FindInBox(B,T, ply)
				local _, HeadEnt = next(Entities)
				if not HeadEnt then
					AdvDupe2.Notify(ply, "Area Auto Save copied 0 entities; be sure to turn it off.", NOTIFY_ERROR)
					return
				end

				if(dupe.AutoSaveEnt and Entities[dupe.AutoSaveEnt]) then
					Tab.HeadEnt.Index = dupe.AutoSaveEnt
				else
					Tab.HeadEnt.Index = HeadEnt:EntIndex()
				end
				Tab.HeadEnt.Pos = HeadEnt:GetPos()

				local WorldTrace = util.TraceLine({
					mask   = MASK_NPCWORLDSTATIC,
					start  = Tab.HeadEnt.Pos + Vector(0,0,1),
					endpos = Tab.HeadEnt.Pos - Vector(0,0,50000)
				})

				Tab.HeadEnt.Z = WorldTrace.Hit and math.abs(Tab.HeadEnt.Pos.Z - WorldTrace.HitPos.Z) or 0
				Tab.Entities, Tab.Constraints = AdvDupe2.duplicator.AreaCopy(ply, Entities, Tab.HeadEnt.Pos, dupe.AutoSaveOutSide)
			end
			Tab.Constraints = GetSortedConstraints(ply, Tab.Constraints)
			Tab.Description = dupe.AutoSaveDesc

			AdvDupe2.Encode( Tab, AdvDupe2.GenerateDupeStamp(ply), function(data)
				AdvDupe2.SendToClient(ply, data, true)
			end)
			dupe.FileMod = CurTime()+tonumber(GetConVarString("AdvDupe2_FileModificationDelay"))
		end)
		timer.Start(name)
	end)

	concommand.Add("AdvDupe2_SetStage", function(ply, cmd, args)
		ply:GetTool("advdupe2"):SetStage(1)
	end)

	concommand.Add("AdvDupe2_RemoveAutoSave", function(ply, cmd, args)
		timer.Remove("AdvDupe2_AutoSave_"..ply:UniqueID())
	end)

	concommand.Add("AdvDupe2_SaveMap", function(ply, cmd, args)
		if not ply:IsSuperAdmin() then
			AdvDupe2.Notify(ply, "You do not have permission to this function.", NOTIFY_ERROR)
			return
		end

		local Entities = {}

		for _, v in ents.Iterator() do
			if not v:CreatedByMap() and AdvDupe2.duplicator.IsCopyable(v) then
				Entities[v:EntIndex()] = v
			end
		end

		local _, HeadEnt = next(Entities)
		if not HeadEnt then AdvDupe2.Notify(ply, "There is nothing to save!", NOTIFY_ERROR) return end

		local Tab = {Entities = {}, Constraints = {}, HeadEnt = {}, Description = ""}
		Tab.HeadEnt.Index = HeadEnt:EntIndex()
		Tab.HeadEnt.Pos = HeadEnt:GetPos()

		local WorldTrace = util.TraceLine({
			mask   = MASK_NPCWORLDSTATIC,
			start  = Tab.HeadEnt.Pos + Vector(0, 0, 1),
			endpos = Tab.HeadEnt.Pos - Vector(0, 0, 50000)
		})

		Tab.HeadEnt.Z = WorldTrace.Hit and math.abs(Tab.HeadEnt.Pos.Z - WorldTrace.HitPos.Z) or 0
		Tab.Entities, Tab.Constraints = AdvDupe2.duplicator.AreaCopy(ply, Entities, Tab.HeadEnt.Pos, true)
		Tab.Constraints = GetSortedConstraints(ply, Tab.Constraints)
		Tab.Map = true

		AdvDupe2.Encode(Tab, AdvDupe2.GenerateDupeStamp(ply), function(data)
			if #data > AdvDupe2.MaxDupeSize then
				AdvDupe2.Notify(ply, "Copied duplicator filesize is too big!", NOTIFY_ERROR)
				return
			end

			local map = game.GetMap()
			file.CreateDir("advdupe2/maps/" .. map)

			local savename = AdvDupe2.SanitizeFilename(args[1] and #args[1] > 0 and args[1] or util.DateStamp())
			file.Write("advdupe2/maps/" .. map .. "/" .. savename .. ".txt", data)

			AdvDupe2.Notify(ply, "Map save, saved successfully.")
		end)
	end)
end

if(CLIENT) then

	function TOOL:LeftClick(trace)
		if(trace and AdvDupe2.HeadGhost) then
			return true
		end
		return false
	end

	function TOOL:RightClick(trace)
		if( self:GetOwner():KeyDown(IN_SPEED) and not self:GetOwner():KeyDown(IN_WALK) ) then
			return false
		end
		return true
	end

	--Removes progress bar and removes ghosts when tool is put away
	function TOOL:ReleaseGhostEntity()
		AdvDupe2.RemoveGhosts()
		AdvDupe2.RemoveSelectBox()
		if(AdvDupe2.Rotation) then
			hook.Remove("PlayerBindPress", "AdvDupe2_BindPress")
			hook.Remove("CreateMove", "AdvDupe2_MouseControl")
		end
		return
	end

	function TOOL:Reload( trace )
		if(trace and (AdvDupe2.HeadGhost or self:GetStage() == 1)) then
			return true
		end
		return false
	end

	--Take control of the mouse wheel bind so the player can modify the height of the dupe
	local function MouseWheelScrolled(ply, bind, pressed)

		if(bind == "invprev") then
			if(ply:GetTool("advdupe2"):GetStage() == 1) then
				local size = math.min(tonumber(ply:GetInfo("advdupe2_area_copy_size")) + 25, 30720)
				RunConsoleCommand("advdupe2_area_copy_size",size)
			else
				local Z = tonumber(ply:GetInfo("advdupe2_offset_z")) + 5
				RunConsoleCommand("advdupe2_offset_z",Z)
			end
			return true
		elseif(bind == "invnext") then
			if(ply:GetTool("advdupe2"):GetStage() == 1) then
				local size = math.max(tonumber(ply:GetInfo("advdupe2_area_copy_size")) - 25, 25)
				RunConsoleCommand("advdupe2_area_copy_size",size)
			else
				local Z = tonumber(ply:GetInfo("advdupe2_offset_z")) - 5
				RunConsoleCommand("advdupe2_offset_z",Z)
			end
			return true
		end

		GAMEMODE:PlayerBindPress(ply, bind, pressed)
	end

	local YawTo = 0
	local BsAng = Angle()

	local function GetRotationSign(ply)
		local VY = tonumber(ply:GetInfo("advdupe2_offset_yaw")) or 0
		BsAng:Zero(); BsAng:RotateAroundAxis(BsAng:Up(), VY)
		local PR = ply:GetRight()
		local DP = BsAng:Right():Dot(PR)
		local DR = BsAng:Forward():Dot(PR)
		if(math.abs(DR) > math.abs(DP)) then -- Roll priority
			if(DR >= 0) then return -1, 1 else return  1, -1 end
		else -- Pitch axis takes priority. Normal X-Y map
			if(DP >= 0) then return  1, 1 else return -1, -1 end
		end
	end

	local function MouseControl( cmd )
		local ply = LocalPlayer()
		local X =  cmd:GetMouseX() / 20
		local Y = -cmd:GetMouseY() / 20
		local ru = ply:KeyDown(IN_SPEED)
		local mm = input.IsMouseDown(MOUSE_MIDDLE)

		if(mm) then
			if(ru) then
				YawTo = 0 -- Reset total integrated yaw
				RunConsoleCommand("advdupe2_offset_pitch", 0)
				RunConsoleCommand("advdupe2_offset_yaw"  , 0)
				RunConsoleCommand("advdupe2_offset_roll" , 0)
			else
				if(Y ~= 0) then
					local VR = tonumber(ply:GetInfo("advdupe2_offset_roll"))  or 0
					local VP = tonumber(ply:GetInfo("advdupe2_offset_pitch")) or 0
					local SP, SR, P, R = GetRotationSign(ply)
					if(SP ~= SR) then
						P = math.NormalizeAngle(VP + X * SR)
						R = math.NormalizeAngle(VR + Y * SP)
					else
						P = math.NormalizeAngle(VP + Y * SP)
						R = math.NormalizeAngle(VR + X * SR)
					end
					RunConsoleCommand("advdupe2_offset_pitch", P)
					RunConsoleCommand("advdupe2_offset_roll" , R)
				end
			end
		else
			if(X ~= 0) then
				VY = tonumber(ply:GetInfo("advdupe2_offset_yaw")) or 0
				if(ru) then
					YawTo = YawTo + X -- Integrate the mouse on the X value from the mouse
					RunConsoleCommand("advdupe2_offset_yaw", math.SnapTo(math.NormalizeAngle(YawTo), 45))
				else
					YawTo = VY + X -- Update the last yaw with the current value from the mouse
					RunConsoleCommand("advdupe2_offset_yaw", math.NormalizeAngle(YawTo))
				end
			end
		end
	end

	--Checks binds to modify dupes position and angles
	function TOOL:Think()

		if AdvDupe2.HeadGhost then
			AdvDupe2.UpdateGhosts()
		end

		if(LocalPlayer():KeyDown(IN_USE)) then
			if(not AdvDupe2.Rotation) then
				hook.Add("PlayerBindPress", "AdvDupe2_BindPress", MouseWheelScrolled)
				hook.Add("CreateMove", "AdvDupe2_MouseControl", MouseControl)
				AdvDupe2.Rotation = true
			end
		else
			if(AdvDupe2.Rotation) then
				AdvDupe2.Rotation = false
				hook.Remove("PlayerBindPress", "AdvDupe2_BindPress")
				hook.Remove("CreateMove", "AdvDupe2_MouseControl")
			end
		end
	end

	--Hinder the player from looking to modify offsets with the mouse
	function TOOL:FreezeMovement()
		return AdvDupe2.Rotation
	end

	TOOL.Information = {
		{ name = "left" },
		{ name = "right", stage = 0 },
		{ name = "right_world", stage = 0, icon2 = "icon16/world.png" },
		{ name = "right_shift", stage = 0, icon2 = "gui/info" },
		{ name = "right_alt", stage = 1 },
		{ name = "reload_alt", stage = 1 },
		{ name = "right_shift_alt", stage = 1, icon2 = "gui/info" },
	}

	language.Add( "Tool.advdupe2.name",				"Advanced Duplicator 2" )
	language.Add( "Tool.advdupe2.desc",				"Duplicate things." )
	language.Add( "Tool.advdupe2.left",				"Paste" )
	language.Add( "Tool.advdupe2.right",			"Copy" )
	language.Add( "Tool.advdupe2.right_world",		"Hit the world to select/deselect all" )
	language.Add( "Tool.advdupe2.right_shift",		"Press Shift to area copy" )
	language.Add( "Tool.advdupe2.right_alt",		"Copy an area" )
	language.Add( "Tool.advdupe2.reload_alt",		"Autosave an area" )
	language.Add( "Tool.advdupe2.right_shift_alt",	"Press Shift to cancel" )
	language.Add( "Undone_AdvDupe2",				"Undone AdvDupe2 paste" )
	language.Add( "Cleanup_AdvDupe2",				"AdvDupe2 Duplications" )
	language.Add( "Cleaned_AdvDupe2",				"Cleaned up all AdvDupe2 Duplications" )
	language.Add( "SBoxLimit_AdvDupe2",				"You've reached the AdvDupe2 Duplicator limit!" )

	CreateClientConVar("advdupe2_offset_world", 0, false, true)
	CreateClientConVar("advdupe2_offset_z", 0, false, true)
	CreateClientConVar("advdupe2_offset_pitch", 0, false, true)
	CreateClientConVar("advdupe2_offset_yaw", 0, false, true)
	CreateClientConVar("advdupe2_offset_roll", 0, false, true)
	CreateClientConVar("advdupe2_original_origin", 0, false, true)
	CreateClientConVar("advdupe2_paste_constraints", 1, false, true)
	CreateClientConVar("advdupe2_sort_constraints", 1, true, true)
	CreateClientConVar("advdupe2_paste_parents", 1, false, true)
	CreateClientConVar("advdupe2_paste_unfreeze", 0, false, true)
	CreateClientConVar("advdupe2_preserve_freeze", 0, false, true)
	CreateClientConVar("advdupe2_copy_outside", 0, false, true)
	CreateClientConVar("advdupe2_copy_only_mine", 1, false, true)
	CreateClientConVar("advdupe2_limit_ghost", 100, false, true, nil, 0, 100)
	CreateClientConVar("advdupe2_ghost_rate", 5, true, true, nil, 1, 50)
	CreateClientConVar("advdupe2_area_copy_size", 300, false, true)
	CreateClientConVar("advdupe2_auto_save_contraption", 0, false, true)
	CreateClientConVar("advdupe2_auto_save_overwrite", 1, false, true)
	CreateClientConVar("advdupe2_auto_save_time", 2, false, true)

	--Contraption Spawner
	CreateClientConVar("advdupe2_contr_spawner_key", -1, false, true)
	CreateClientConVar("advdupe2_contr_spawner_undo_key", -1, false, true)
	CreateClientConVar("advdupe2_contr_spawner_delay", 0, false, true)
	CreateClientConVar("advdupe2_contr_spawner_undo_delay", 10, false, true)
	CreateClientConVar("advdupe2_contr_spawner_disgrav", 0, false, true)
	CreateClientConVar("advdupe2_contr_spawner_disdrag", 0, false, true)
	CreateClientConVar("advdupe2_contr_spawner_addvel", 1, false, true)
	CreateClientConVar("advdupe2_contr_spawner_hideprops", 0, false, true)

	--Experimental
	CreateClientConVar("advdupe2_paste_disparents", 0, false, true)
	CreateClientConVar("advdupe2_paste_protectoveride", 1, false, true)
	CreateClientConVar("advdupe2_debug_openfile", 1, false, true)

	local function BuildCPanel(CPanel)
		CPanel:ClearControls()

		local FileBrowser = vgui.Create("advdupe2_browser")
		CPanel:AddItem(FileBrowser)
		FileBrowser:SetSize(CPanel:GetWide(), 405)
		AdvDupe2.FileBrowser = FileBrowser

		local Check = vgui.Create("DCheckBoxLabel")

		Check:SetText( "Paste at original position" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_original_origin" )
		Check:SetValue( 0 )
		Check:SetToolTip("Paste at the position originally copied")
		CPanel:AddItem(Check)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Paste with constraints" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_paste_constraints" )
		Check:SetValue( 1 )
		Check:SetToolTip("Paste with or without constraints")
		CPanel:AddItem(Check)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Paste with parenting" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_paste_parents" )
		Check:SetValue( 1 )
		Check:SetToolTip("Paste with or without parenting")
		CPanel:AddItem(Check)

		local Check_1 = vgui.Create("DCheckBoxLabel")
		local Check_2 = vgui.Create("DCheckBoxLabel")

		Check_1:SetText( "Unfreeze all after paste" )
		Check_1:SetDark(true)
		Check_1:SetConVar( "advdupe2_paste_unfreeze" )
		Check_1:SetValue( 0 )
		Check_1.OnChange = function()
			if(Check_1:GetChecked() and Check_2:GetChecked()) then
				Check_2:SetValue(0)
			end
		end
		Check_1:SetToolTip("Unfreeze all props after pasting")
		CPanel:AddItem(Check_1)

		Check_2:SetText( "Preserve frozen state after paste" )
		Check_2:SetDark(true)
		Check_2:SetConVar( "advdupe2_preserve_freeze" )
		Check_2:SetValue( 0 )
		Check_2.OnChange = function()
			if(Check_2:GetChecked() and Check_1:GetChecked()) then
				Check_1:SetValue(0)
			end
		end
		Check_2:SetToolTip("Makes props have the same frozen state as when they were copied")
		CPanel:AddItem(Check_2)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Area copy constrained props outside of box" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_copy_outside" )
		Check:SetValue( 0 )
		Check:SetToolTip("Copy entities outside of the area copy that are constrained to entities insde")
		CPanel:AddItem(Check)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "World/Area copy only your own props" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_copy_only_mine" )
		Check:SetValue( 1 )
		Check:SetToolTip("Copy entities outside of the area copy that are constrained to entities insde")
		CPanel:AddItem(Check)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Sort constraints by their connections" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_sort_constraints" )
		Check:SetValue( GetConVarNumber("advdupe2_sort_constraints") )
		Check:SetToolTip( "Orders constraints so that they build a rigid constraint system." )
		CPanel:AddItem(Check)

		-- Ghost Percentage
		local NumSlider = vgui.Create( "DNumSlider" )
		NumSlider:SetText( "Ghost Percentage:" )
		NumSlider.Label:SetDark(true)
		NumSlider:SetMin( 0 )
		NumSlider:SetMax( 100 )
		NumSlider:SetDecimals( 0 )
		NumSlider:SetConVar( "advdupe2_limit_ghost" )
		NumSlider:SetToolTip("Change the percent of ghosts to spawn")
		--If these funcs are not here, problems occur for each
		local func = NumSlider.Slider.OnMouseReleased
		NumSlider.Slider.OnMouseReleased = function(self, mcode) func(self, mcode) AdvDupe2.StartGhosting() end
		local func2 = NumSlider.Slider.Knob.OnMouseReleased
		NumSlider.Slider.Knob.OnMouseReleased = function(self, mcode) func2(self, mcode) AdvDupe2.StartGhosting() end
		local func3 = NumSlider.Wang.Panel.OnLoseFocus
		NumSlider.Wang.Panel.OnLoseFocus = function(txtBox) func3(txtBox) AdvDupe2.StartGhosting() end
		CPanel:AddItem(NumSlider)

		-- Ghost Rate
		NumSlider = vgui.Create( "DNumSlider" )
		NumSlider:SetText( "Ghost speed:" )
		NumSlider.Label:SetDark(true)
		NumSlider:SetMin( 1 )
		NumSlider:SetMax( 50 )
		NumSlider:SetDecimals( 0 )
		NumSlider:SetConVar( "advdupe2_ghost_rate" )
		NumSlider:SetToolTip("Change how quickly the ghosts are generated")
		CPanel:AddItem(NumSlider)

		-- Area Copy Size
		NumSlider = vgui.Create( "DNumSlider" )
		NumSlider:SetText( "Area Copy Size:" )
		NumSlider.Label:SetDark(true)
		NumSlider:SetMin( 0 )
		NumSlider:SetMax( 30720 )
		NumSlider:SetDecimals( 0 )
		NumSlider:SetConVar( "advdupe2_area_copy_size" )
		NumSlider:SetToolTip("Change the size of the area copy")
		CPanel:AddItem(NumSlider)

		local Category1 = vgui.Create("DCollapsibleCategory")
		CPanel:AddItem(Category1)
		Category1:SetLabel("Offsets")
		Category1:SetExpanded(0)

		local parent = FileBrowser:GetParent():GetParent():GetParent():GetParent()
		--[[Offsets]]--
		local CategoryContent1 = vgui.Create( "DPanelList" )
		CategoryContent1:SetAutoSize( true )
		CategoryContent1:SetDrawBackground( false )
		CategoryContent1:SetSpacing( 1 )
		CategoryContent1:SetPadding( 2 )
		CategoryContent1.OnMouseWheeled = function(self, dlta) parent:OnMouseWheeled(dlta) end		--Fix the damned mouse not scrolling when it's over the catagories

		Category1:SetContents( CategoryContent1 )

		NumSlider = vgui.Create( "DNumSlider" )
		NumSlider:SetText( "Height Offset" )
		NumSlider.Label:SetDark(true)
		NumSlider:SetMin( -2500 )
		NumSlider:SetMax( 2500 )
		NumSlider:SetDefaultValue( 0 )
		NumSlider:SetDecimals( 3 )
		NumSlider:SetConVar("advdupe2_offset_z")
		NumSlider:SetToolTip("Changes the dupe Z offset")
		CategoryContent1:AddItem(NumSlider)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Use World Angles" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_offset_world" )
		Check:SetValue( 0 )
		Check:SetToolTip("Use world angles for the offset instead of the main entity")
		CategoryContent1:AddItem(Check)

		NumSlider = vgui.Create( "DNumSlider" )
		NumSlider:SetText( "Pitch Offset" )
		NumSlider.Label:SetDark(true)
		NumSlider:SetMin( -180 )
		NumSlider:SetMax( 180 )
		NumSlider:SetDefaultValue( 0 )
		NumSlider:SetDecimals( 3 )
		NumSlider:SetToolTip("Changes the dupe pitch offset")
		NumSlider:SetConVar("advdupe2_offset_pitch")
		CategoryContent1:AddItem(NumSlider)

		NumSlider = vgui.Create( "DNumSlider" )
		NumSlider:SetText( "Yaw Offset" )
		NumSlider.Label:SetDark(true)
		NumSlider:SetMin( -180 )
		NumSlider:SetMax( 180 )
		NumSlider:SetDefaultValue( 0 )
		NumSlider:SetDecimals( 3 )
		NumSlider:SetToolTip("Changes the dupe yaw offset")
		NumSlider:SetConVar("advdupe2_offset_yaw")
		CategoryContent1:AddItem(NumSlider)

		NumSlider = vgui.Create( "DNumSlider" )
		NumSlider:SetText( "Roll Offset" )
		NumSlider.Label:SetDark(true)
		NumSlider:SetMin( -180 )
		NumSlider:SetMax( 180 )
		NumSlider:SetDefaultValue( 0 )
		NumSlider:SetDecimals( 3 )
		NumSlider:SetToolTip("Changes the dupe roll offset")
		NumSlider:SetConVar("advdupe2_offset_roll")
		CategoryContent1:AddItem(NumSlider)

		local Btn = vgui.Create("DButton")
		Btn:SetText("Reset")
		Btn.DoClick = function()
			RunConsoleCommand("advdupe2_offset_z", 0)
			RunConsoleCommand("advdupe2_offset_pitch", 0)
			RunConsoleCommand("advdupe2_offset_yaw", 0)
			RunConsoleCommand("advdupe2_offset_roll", 0)
		end
		CategoryContent1:AddItem(Btn)


		--[[Dupe Information]]--
		local Category2 = vgui.Create("DCollapsibleCategory")
		CPanel:AddItem(Category2)
		Category2:SetLabel("Dupe Information")
		Category2:SetExpanded(0)

		local CategoryContent2 = vgui.Create( "DPanelList" )
		CategoryContent2:SetAutoSize( true )
		CategoryContent2:SetDrawBackground( false )
		CategoryContent2:SetSpacing( 3 )
		CategoryContent2:SetPadding( 2 )
		Category2:SetContents( CategoryContent2 )
		CategoryContent2.OnMouseWheeled = function(self, dlta) parent:OnMouseWheeled(dlta) end

		AdvDupe2.Info = {}

		local lbl = vgui.Create( "DLabel" )
		lbl:SetText(AdvDupe2.InfoText.File or "File: ")
		lbl:SetDark(true)
		CategoryContent2:AddItem(lbl)
		AdvDupe2.Info.File = lbl

		lbl = vgui.Create( "DLabel" )
		lbl:SetText(AdvDupe2.InfoText.Creator or "Creator:")
		lbl:SetDark(true)
		CategoryContent2:AddItem(lbl)
		AdvDupe2.Info.Creator = lbl

		lbl = vgui.Create( "DLabel" )
		lbl:SetText(AdvDupe2.InfoText.Date or "Date:")
		lbl:SetDark(true)
		CategoryContent2:AddItem(lbl)
		AdvDupe2.Info.Date = lbl

		lbl = vgui.Create( "DLabel" )
		lbl:SetText(AdvDupe2.InfoText.Time or "Time:")
		lbl:SetDark(true)
		CategoryContent2:AddItem(lbl)
		AdvDupe2.Info.Time = lbl

		lbl = vgui.Create( "DLabel" )
		lbl:SetText(AdvDupe2.InfoText.Size or "Size:")
		lbl:SetDark(true)
		CategoryContent2:AddItem(lbl)
		AdvDupe2.Info.Size = lbl

		lbl = vgui.Create( "DLabel" )
		lbl:SetText(AdvDupe2.InfoText.Desc or "Desc:")
		lbl:SetDark(true)
		CategoryContent2:AddItem(lbl)
		AdvDupe2.Info.Desc = lbl

		lbl = vgui.Create( "DLabel" )
		lbl:SetText(AdvDupe2.InfoText.Entities or "Entities:")
		lbl:SetDark(true)
		CategoryContent2:AddItem(lbl)
		AdvDupe2.Info.Entities = lbl

		lbl = vgui.Create( "DLabel" )
		lbl:SetText(AdvDupe2.InfoText.Constraints or "Constraints:")
		lbl:SetDark(true)
		CategoryContent2:AddItem(lbl)
		AdvDupe2.Info.Constraints = lbl

	--[[Contraption Spawner]]--
		local Category3 = vgui.Create("DCollapsibleCategory")
		CPanel:AddItem(Category3)
		Category3:SetLabel("Contraption Spawner")
		Category3:SetExpanded(0)

		local CategoryContent3 = vgui.Create( "DPanelList" )
		CategoryContent3:SetAutoSize( true )
		CategoryContent3:SetDrawBackground( false )
		CategoryContent3:SetSpacing( 3 )
		CategoryContent3:SetPadding( 2 )
		Category3:SetContents( CategoryContent3 )
		CategoryContent3.OnMouseWheeled = function(self, dlta) parent:OnMouseWheeled(dlta) end

		local ctrl = vgui.Create( "CtrlNumPad" )
		ctrl:SetConVar1( "advdupe2_contr_spawner_key" )
		ctrl:SetConVar2( "advdupe2_contr_spawner_undo_key" )
		ctrl:SetLabel1( "Spawn Key")
		ctrl:SetLabel2( "Undo Key" )
		CategoryContent3:AddItem(ctrl)

		NumSlider = vgui.Create( "DNumSlider" )
		NumSlider:SetText( "Spawn Delay" )
		NumSlider.Label:SetDark(true)
		if(game.SinglePlayer()) then
			NumSlider:SetMin( 0 )
		else
			local min = tonumber(GetConVarString("AdvDupe2_MinContraptionSpawnDelay")) or 0.2
			if(tonumber(LocalPlayer():GetInfo("advdupe2_contr_spawner_delay"))<min) then
				RunConsoleCommand("advdupe2_contr_spawner_delay", tostring(min))
			end
			NumSlider:SetMin( min )
		end
		NumSlider:SetMax(60)
		NumSlider:SetDecimals( 1 )
		NumSlider:SetConVar("advdupe2_contr_spawner_delay")
		CategoryContent3:AddItem(NumSlider)

		NumSlider = vgui.Create( "DNumSlider" )
		NumSlider:SetText( "Undo Delay" )
		NumSlider.Label:SetDark(true)
		if(game.SinglePlayer()) then
			NumSlider:SetMin( 0 )
			NumSlider:SetMax( 60 )
		else
			local min = tonumber(GetConVarString("AdvDupe2_MinContraptionUndoDelay")) or 10
			local max = tonumber(GetConVarString("AdvDupe2_MaxContraptionUndoDelay")) or 60
			if(tonumber(LocalPlayer():GetInfo("advdupe2_contr_spawner_undo_delay")) < min) then
				RunConsoleCommand("advdupe2_contr_spawner_undo_delay", tostring(min))
			elseif(tonumber(LocalPlayer():GetInfo("advdupe2_contr_spawner_undo_delay")) > max) then
				RunConsoleCommand("advdupe2_contr_spawner_undo_delay", tostring(max))
			end
			NumSlider:SetMin( min )
			NumSlider:SetMax( max )
		end
		NumSlider:SetDecimals( 1 )
		NumSlider:SetConVar("advdupe2_contr_spawner_undo_delay")
		CategoryContent3:AddItem(NumSlider)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Disable gravity for all spawned props" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_contr_spawner_disgrav" )
		Check:SetValue( 0 )
		CategoryContent3:AddItem(Check)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Disable drag for all spawned props" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_contr_spawner_disdrag" )
		Check:SetValue( 0 )
		CategoryContent3:AddItem(Check)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Add spawner's velocity to contraption" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_contr_spawner_addvel" )
		Check:SetValue( 1 )
		CategoryContent3:AddItem(Check)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Disable drawing spawner props" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_contr_spawner_hideprops" )
		Check:SetValue( 0 )
		CategoryContent3:AddItem(Check)

		--[[Area Auto Save]]--
		local Category4 = vgui.Create("DCollapsibleCategory")
		CPanel:AddItem(Category4)
		Category4:SetLabel("Area Auto Save")
		Category4:SetExpanded(0)

		local CategoryContent4 = vgui.Create( "DPanelList" )
		CategoryContent4:SetAutoSize( true )
		CategoryContent4:SetDrawBackground( false )
		CategoryContent4:SetSpacing( 3 )
		CategoryContent4:SetPadding( 2 )
		Category4:SetContents( CategoryContent4 )
		CategoryContent4.OnMouseWheeled = function(self, dlta) parent:OnMouseWheeled(dlta) end

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Only copy contraption" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_auto_save_contraption" )
		Check:SetValue( 0 )
		Check:SetToolTip("Only copy a contraption instead of an area")
		CategoryContent4:AddItem(Check)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Overwrite File" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_auto_save_overwrite" )
		Check:SetValue( 1 )
		Check:SetToolTip("Overwrite the file instead of creating a new one everytime")
		CategoryContent4:AddItem(Check)

		NumSlider = vgui.Create( "DNumSlider" )
		NumSlider:SetText( "Minutes to Save:" )
		NumSlider.Label:SetDark(true)
		NumSlider:SetMin( 2 )
		NumSlider:SetMax( 30 )
		NumSlider:SetDecimals( 0 )
		NumSlider:SetConVar( "advdupe2_auto_save_time" )
		NumSlider:SetToolTip("Interval time to save in minutes")
		CategoryContent4:AddItem(NumSlider)

		local pnl = vgui.Create("Panel")
		pnl:SetWide(CPanel:GetWide()-40)
		pnl:SetTall(75)
		pnl:SetPos(0, 50)
		CategoryContent4:AddItem(pnl)

		local label = vgui.Create("DLabel", pnl)
		label:SetText("Directory: ")
		label:SizeToContents()
		label:SetDark(true)
		label:SetPos(5,7)

		AdvDupe2.AutoSavePath = ""
		local txtbox = vgui.Create("DTextEntry", pnl)
		txtbox:SetWide(pnl:GetWide()-100)
		txtbox:SetPos(60, 5)
		txtbox:SetUpdateOnType(true)
		txtbox.OnTextChanged = function(self)
			self:SetValue(AdvDupe2.AutoSavePath)
		end

		local btn = vgui.Create("DImageButton", pnl)
		local x, y = txtbox:GetPos()
		btn:SetPos(x + txtbox:GetWide() + 5, 7)
		btn:SetMaterial("icon16/folder_explore.png")
		btn:SizeToContents()
		btn:SetToolTip("Browse")
		btn.DoClick = function()
			local ScrollBar = parent.VBar
			ScrollBar:AnimateTo(0, 1, 0, 0.2)

			FileBrowser.Submit:SetMaterial("icon16/disk.png")
			FileBrowser.Submit:SetTooltip("Directory for Area Auto Save")
			if(FileBrowser.FileName:GetValue()=="Folder_Name...") then
				FileBrowser.FileName:SetValue("File_Name...")
			end
			FileBrowser.Desc:SetVisible(true)
			FileBrowser.Info:SetVisible(false)
			FileBrowser.FileName:SetVisible(true)
			FileBrowser.FileName:SelectAllOnFocus(true)
			FileBrowser.FileName:OnMousePressed()
			FileBrowser.FileName:RequestFocus()
			FileBrowser.Expanding=true
			FileBrowser:Slide(true)
			FileBrowser.Submit.DoClick = function()
				local name = FileBrowser.FileName:GetValue()
				if(name=="" or name=="File_Name...") then
					AdvDupe2.Notify("Name field is blank.", NOTIFY_ERROR)
					FileBrowser.FileName:SelectAllOnFocus(true)
					FileBrowser.FileName:OnGetFocus()
					FileBrowser.FileName:RequestFocus()
					return
				end
				local desc = FileBrowser.Desc:GetValue()
				if(desc=="Description...") then desc="" end

				if(not IsValid(FileBrowser.Browser.pnlCanvas.m_pSelectedItem) or FileBrowser.Browser.pnlCanvas.m_pSelectedItem.Derma.ClassName~="advdupe2_browser_folder") then
					AdvDupe2.Notify("Folder to save Area Auto Save not selected.", NOTIFY_ERROR)
					return
				end

				FileBrowser.AutoSaveNode = FileBrowser.Browser.pnlCanvas.m_pSelectedItem
				txtbox:SetValue(FileBrowser:GetFullPath(FileBrowser.Browser.pnlCanvas.m_pSelectedItem)..name)
				AdvDupe2.AutoSavePath = txtbox:GetValue()
				txtbox:SetToolTip(txtbox:GetValue())
				AdvDupe2.AutoSaveDesc = desc

				FileBrowser:Slide(false)
				ScrollBar:AnimateTo(ScrollBar.CanvasSize, 1, 0, 0.2)

				RunConsoleCommand("AdvDupe2_SetStage")
				hook.Add("HUDPaint", "AdvDupe2_DrawSelectionBox", AdvDupe2.DrawSelectionBox)
			end
			FileBrowser.FileName.OnEnter = function()
				FileBrowser.FileName:KillFocus()
				FileBrowser.Desc:SelectAllOnFocus(true)
				FileBrowser.Desc.OnMousePressed()
				FileBrowser.Desc:RequestFocus()
			end
			FileBrowser.Desc.OnEnter = FileBrowser.Submit.DoClick
		end

		btn = vgui.Create("DButton", pnl)
		btn:SetSize(50, 35)
		btn:SetPos(pnl:GetWide()/4-10, 30)
		btn:SetText("Show")
		btn.DoClick = function()
			if(AdvDupe2.AutoSavePos) then
				RunConsoleCommand("advdupe2_area_copy_size", AdvDupe2.AutoSaveSize)
				LocalPlayer():SetEyeAngles( (AdvDupe2.AutoSavePos - LocalPlayer():GetShootPos()):Angle() )
				RunConsoleCommand("AdvDupe2_SetStage")
				hook.Add("HUDPaint", "AdvDupe2_DrawSelectionBox", AdvDupe2.DrawSelectionBox)
			end
		end

		btn = vgui.Create("DButton", pnl)
		btn:SetSize(50, 35)
		btn:SetPos((pnl:GetWide()/4)*3-40, 30)
		btn:SetText("Turn Off")
		btn:SetDisabled(true)
		btn.DoClick = function(self)
			RunConsoleCommand("AdvDupe2_RemoveAutoSave")
			self:SetDisabled(true)
			AdvDupe2.AutoSavePos = nil
		end
		AdvDupe2.OffButton = btn


		--[[Experimental Section]]--
		local Category5 = vgui.Create("DCollapsibleCategory")
		CPanel:AddItem(Category5)
		Category5:SetLabel("Experimental Section")
		Category5:SetExpanded(0)

		local CategoryContent5 = vgui.Create( "DPanelList" )
		CategoryContent5:SetAutoSize( true )
		CategoryContent5:SetDrawBackground( false )
		CategoryContent5:SetSpacing( 3 )
		CategoryContent5:SetPadding( 2 )
		Category5:SetContents( CategoryContent5 )
		CategoryContent5.OnMouseWheeled = function(self, dlta) parent:OnMouseWheeled(dlta) end

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Disable parented props physics interaction" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_paste_disparents" )
		Check:SetValue( 0 )
		CategoryContent5:AddItem(Check)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Disable Dupe Spawn Protection" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_paste_protectoveride" )
		Check:SetValue( 1 )
		Check:SetToolTip("Check this if you things don't look right after pasting.")
		CategoryContent5:AddItem(Check)

		Check = vgui.Create("DCheckBoxLabel")
		Check:SetText( "Open file after Saving" )
		Check:SetDark(true)
		Check:SetConVar( "advdupe2_debug_openfile" )
		Check:SetValue( 1 )
		Check:SetToolTip("Check this if you want your files to be opened after saving them.")
		CategoryContent5:AddItem(Check)

		--[[Save Map]]--
		if(LocalPlayer():IsAdmin()) then
			local Category6 = vgui.Create("DCollapsibleCategory")
			CPanel:AddItem(Category6)
			Category6:SetLabel("Save Map")
			Category6:SetExpanded(0)

			local CategoryContent6 = vgui.Create( "DPanelList" )
			CategoryContent6:SetAutoSize( true )
			CategoryContent6:SetDrawBackground( false )
			CategoryContent6:SetSpacing( 3 )
			CategoryContent6:SetPadding( 2 )
			Category6:SetContents( CategoryContent6 )
			CategoryContent6.OnMouseWheeled = function(self, dlta) parent:OnMouseWheeled(dlta) end

			pnl = vgui.Create("Panel")
			pnl:SetWide(CPanel:GetWide()-40)
			pnl:SetTall(75)
			pnl:SetPos(0, 50)
			CategoryContent6:AddItem(pnl)

			label = vgui.Create("DLabel", pnl)
			label:SetText("File Name: ")
			label:SizeToContents()
			label:SetDark(true)
			label:SetPos(5,7)

			AdvDupe2.AutoSavePath = ""

			local txtbox2 = vgui.Create("DTextEntry", pnl)
			txtbox2:SetWide(pnl:GetWide()-100)
			txtbox2:SetPos(60, 5)

			local btn2 = vgui.Create("DImageButton", pnl)
			x, y = txtbox2:GetPos()
			btn2:SetPos(x + txtbox2:GetWide() + 5, 7)
			btn2:SetMaterial("icon16/disk.png")
			btn2:SizeToContents()
			btn2:SetToolTip("Save Map")
			btn2.DoClick = 	function()
				RunConsoleCommand("AdvDupe2_SaveMap", txtbox2:GetValue())
			end
			txtbox2.OnEnter = function()
				btn2:DoClick()
			end
		end
	end

	function TOOL.BuildCPanel(panel)
		panel:ClearControls()
		panel:AddControl("Header", {
			Text = "Advanced Duplicator 2",
			Description = "Duplicate stuff."
		})
		local function tryToBuild()
			local CPanel = controlpanel.Get("advdupe2")
			if CPanel and CPanel:GetWide()>16 then
				BuildCPanel(CPanel)
			else
				timer.Simple(0.1,tryToBuild)
			end
		end
		tryToBuild()
	end

	local StColor  = {r=130, g=25, b=40, a=255}
	local NoColor  = {r=25, g=100, b=40, a=255}
	local CurColor = {r=25, g=100, b=40, a=255}
	local CWhite   = Color(255,255,255,255)
	surface.CreateFont ("AD2Font", {font="Arial", size=40, weight=1000}) ---Remember to use gm_clearfonts
	surface.CreateFont ("AD2TitleFont", {font="Arial", size=24, weight=1000})

	function TOOL:DrawToolScreen()
		if(not AdvDupe2) then return true end

		local text = "Ready"
		local state, co = false
		local ply = LocalPlayer()

		if(AdvDupe2.Preview) then
			text = "Preview"
		end
		if(AdvDupe2.ProgressBar.Text) then
			state = true
			text = AdvDupe2.ProgressBar.Text
		end

		cam.Start2D()

			surface.SetDrawColor(32, 32, 32, 255)
			surface.DrawRect(0, 0, 256, 256)

			if(state) then
				co = StColor
			else
				co = NoColor
			end

			local rate = FrameTime() * 160
			CurColor.r = math.Approach( CurColor.r, co.r, rate )
			CurColor.g = math.Approach( CurColor.g, co.g, rate )

			surface.SetDrawColor(CurColor)
			surface.DrawRect(13, 13, 230, 230)

			surface.SetTextColor( 255, 255, 255, 255 )

			draw.SimpleText("Advanced Duplicator 2", "AD2TitleFont", 128, 50, CWhite, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP)
			draw.SimpleText(text, "AD2Font", 128, 128, CWhite, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
			if(state) then
				draw.RoundedBox( 6, 32, 178, 192, 28, Color( 255, 255, 255, 150 ) )
				draw.RoundedBox( 6, 34, 180, 188*(AdvDupe2.ProgressBar.Percent / 100), 24, Color( 0, 255, 0, 255 ) )
			elseif(ply:KeyDown(IN_USE)) then
				local font, align = "AD2TitleFont", TEXT_ALIGN_BOTTOM
				draw.SimpleText("H: "..ply:GetInfo("advdupe2_offset_z")    , font, 20,  210, CWhite, TEXT_ALIGN_LEFT , align)
				draw.SimpleText("P: "..ply:GetInfo("advdupe2_offset_pitch"), font, 236, 210, CWhite, TEXT_ALIGN_RIGHT, align)
				draw.SimpleText("Y: "..ply:GetInfo("advdupe2_offset_yaw")  , font, 20 , 240, CWhite, TEXT_ALIGN_LEFT , align)
				draw.SimpleText("R: "..ply:GetInfo("advdupe2_offset_roll") , font, 236, 240, CWhite, TEXT_ALIGN_RIGHT, align)
			end

		cam.End2D()
	end


	local function FindInBox(min, max)
		local EntTable = {}

		for _, ent in ipairs(ents.FindInBox(min, max)) do
			EntTable[ent:EntIndex()] = ent
		end

		return EntTable
	end


	local GreenSelected = Color(0, 255, 0, 255)
	function AdvDupe2.DrawSelectionBox()

		local TraceRes = util.TraceLine(util.GetPlayerTrace(LocalPlayer()))
		local i = math.Clamp(tonumber(LocalPlayer():GetInfo("advdupe2_area_copy_size")) or 50, 0, 30720)

		--Bottom Points
		local B1 = (Vector(-i,-i,-i) + TraceRes.HitPos)
		local B2 = (Vector(-i, i,-i) + TraceRes.HitPos)
		local B3 = (Vector( i, i,-i) + TraceRes.HitPos)
		local B4 = (Vector( i,-i,-i) + TraceRes.HitPos)

		--Top Points
		local T1 = (Vector(-i,-i, i) + TraceRes.HitPos):ToScreen()
		local T2 = (Vector(-i, i, i) + TraceRes.HitPos):ToScreen()
		local T3 = (Vector( i, i, i) + TraceRes.HitPos):ToScreen()
		local T4 = (Vector( i,-i, i) + TraceRes.HitPos):ToScreen()

		if(not AdvDupe2.LastUpdate or CurTime()>=AdvDupe2.LastUpdate) then

			if AdvDupe2.ColorEntities then
				for k,v in pairs(AdvDupe2.EntityColors)do
					local ent = AdvDupe2.ColorEntities[k]
					if(IsValid(ent)) then
						AdvDupe2.ColorEntities[k]:SetColor(v)
					end
				end
			end

			local Entities = FindInBox(B1, (Vector(i,i,i)+TraceRes.HitPos), LocalPlayer())
			AdvDupe2.ColorEntities = Entities
			AdvDupe2.EntityColors = {}
			for k,v in pairs(Entities)do
				AdvDupe2.EntityColors[k] = v:GetColor()
				v:SetColor(GreenSelected)
			end
			AdvDupe2.LastUpdate = CurTime()+0.25

		end

		local tracedata = {}
		tracedata.mask = MASK_NPCWORLDSTATIC
		local WorldTrace

		tracedata.start = B1+Vector(0,0,i*2)
		tracedata.endpos = B1
		WorldTrace = util.TraceLine( tracedata )
		B1 = WorldTrace.HitPos:ToScreen()
		tracedata.start = B2+Vector(0,0,i*2)
		tracedata.endpos = B2
		WorldTrace = util.TraceLine( tracedata )
		B2 = WorldTrace.HitPos:ToScreen()
		tracedata.start = B3+Vector(0,0,i*2)
		tracedata.endpos = B3
		WorldTrace = util.TraceLine( tracedata )
		B3 = WorldTrace.HitPos:ToScreen()
		tracedata.start = B4+Vector(0,0,i*2)
		tracedata.endpos = B4
		WorldTrace = util.TraceLine( tracedata )
		B4 = WorldTrace.HitPos:ToScreen()

		surface.SetDrawColor( 0, 255, 0, 255 )

		--Draw Sides
		surface.DrawLine(B1.x, B1.y, T1.x, T1.y)
		surface.DrawLine(B2.x, B2.y, T2.x, T2.y)
		surface.DrawLine(B3.x, B3.y, T3.x, T3.y)
		surface.DrawLine(B4.x, B4.y, T4.x, T4.y)

		--Draw Bottom
		surface.DrawLine(B1.x, B1.y, B2.x, B2.y)
		surface.DrawLine(B2.x, B2.y, B3.x, B3.y)
		surface.DrawLine(B3.x, B3.y, B4.x, B4.y)
		surface.DrawLine(B4.x, B4.y, B1.x, B1.y)

		--Draw Top
		surface.DrawLine(T1.x, T1.y, T2.x, T2.y)
		surface.DrawLine(T2.x, T2.y, T3.x, T3.y)
		surface.DrawLine(T3.x, T3.y, T4.x, T4.y)
		surface.DrawLine(T4.x, T4.y, T1.x, T1.y)

	end

	net.Receive("AdvDupe2_DrawSelectBox", function()
		hook.Add("HUDPaint", "AdvDupe2_DrawSelectionBox", AdvDupe2.DrawSelectionBox)
	end)

	function AdvDupe2.RemoveSelectBox()
		hook.Remove("HUDPaint", "AdvDupe2_DrawSelectionBox")
		if AdvDupe2.ColorEntities then
			for k,v in pairs(AdvDupe2.EntityColors)do
				if(not IsValid(AdvDupe2.ColorEntities[k])) then
					AdvDupe2.ColorEntities[k]=nil
				else
					AdvDupe2.ColorEntities[k]:SetColor(v)
				end
			end
			AdvDupe2.ColorEntities={}
			AdvDupe2.EntityColors={}
		end
	end
	net.Receive("AdvDupe2_RemoveSelectBox",function()
		AdvDupe2.RemoveSelectBox()
	end)

	function AdvDupe2.InitProgressBar(label)
		AdvDupe2.ProgressBar = {}
		AdvDupe2.ProgressBar.Text = label
		AdvDupe2.ProgressBar.Percent = 0
		AdvDupe2.BusyBar = true
	end
	net.Receive("AdvDupe2_InitProgressBar", function()
		AdvDupe2.InitProgressBar(net.ReadString())
	end)

	net.Receive("AdvDupe2_UpdateProgressBar", function()
		AdvDupe2.ProgressBar.Percent = net.ReadFloat()
	end)

	function AdvDupe2.RemoveProgressBar()
		AdvDupe2.ProgressBar = {}
		AdvDupe2.BusyBar = false
		if(AdvDupe2.Ghosting) then
			AdvDupe2.InitProgressBar("Ghosting: ")
			AdvDupe2.BusyBar = false
			AdvDupe2.ProgressBar.Percent = AdvDupe2.CurrentGhost/AdvDupe2.TotalGhosts*100
		end
	end
	net.Receive("AdvDupe2_RemoveProgressBar", function()
		AdvDupe2.RemoveProgressBar()
	end)

	net.Receive("AdvDupe2_ResetOffsets", function()
		RunConsoleCommand("advdupe2_original_origin", "0")
		RunConsoleCommand("advdupe2_paste_constraints","1")
		RunConsoleCommand("advdupe2_offset_z","0")
		RunConsoleCommand("advdupe2_offset_pitch","0")
		RunConsoleCommand("advdupe2_offset_yaw","0")
		RunConsoleCommand("advdupe2_offset_roll","0")
		RunConsoleCommand("advdupe2_paste_parents","1")
		RunConsoleCommand("advdupe2_paste_disparents","0")
	end)

	net.Receive("AdvDupe2_ReportModel", function()
		print("Advanced Duplicator 2: Invalid Model: "..net.ReadString())
	end)

	net.Receive("AdvDupe2_ReportClass", function()
		print("Advanced Duplicator 2: Invalid Class: "..net.ReadString())
	end)

	net.Receive("AdvDupe2_ResetDupeInfo", function()
		if not AdvDupe2.Info then return end
		AdvDupe2.Info.File:SetText("File:")
		AdvDupe2.Info.Creator:SetText("Creator:")
		AdvDupe2.Info.Date:SetText("Date:")
		AdvDupe2.Info.Time:SetText("Time:")
		AdvDupe2.Info.Size:SetText("Size:")
		AdvDupe2.Info.Desc:SetText("Desc:")
		AdvDupe2.Info.Entities:SetText("Entities:")
		AdvDupe2.Info.Constraints:SetText("Constraints:")
	end)

	net.Receive("AdvDupe2_CanAutoSave", function()
		if(AdvDupe2.AutoSavePath~="") then
			AdvDupe2.AutoSavePos = net.ReadVector()
			AdvDupe2.AutoSaveSize = net.ReadFloat()
			local ent = net.ReadUInt(16)
			AdvDupe2.OffButton:SetDisabled(false)
			net.Start("AdvDupe2_CanAutoSave")
				net.WriteString(AdvDupe2.AutoSaveDesc)
				net.WriteInt(ent, 16)
				if(game.SinglePlayer()) then
					net.WriteString(string.sub(AdvDupe2.AutoSavePath, 10, #AdvDupe2.AutoSavePath))
				end
			net.SendToServer()
		else
			AdvDupe2.Notify("Select a directory for the Area Auto Save.", NOTIFY_ERROR)
		end
	end)

	net.Receive("AdvDupe2_SetDupeInfo", function(len, ply, len2)
		if AdvDupe2.Info then
			AdvDupe2.Info.File:SetText("File: "..net.ReadString())
			AdvDupe2.Info.Creator:SetText("Creator: "..net.ReadString())
			AdvDupe2.Info.Date:SetText("Date: "..net.ReadString())
			AdvDupe2.Info.Time:SetText("Time: "..net.ReadString())
			AdvDupe2.Info.Size:SetText("Size: "..net.ReadString())
			AdvDupe2.Info.Desc:SetText("Desc: "..net.ReadString())
			AdvDupe2.Info.Entities:SetText("Entities: "..net.ReadString())
			AdvDupe2.Info.Constraints:SetText("Constraints: "..net.ReadString())
		else
			AdvDupe2.InfoText.File = "File: "..net.ReadString()
			AdvDupe2.InfoText.Creator = "Creator: "..net.ReadString()
			AdvDupe2.InfoText.Date = "Date: "..net.ReadString()
			AdvDupe2.InfoText.Time = "Time: "..net.ReadString()
			AdvDupe2.InfoText.Size = "Size: "..net.ReadString()
			AdvDupe2.InfoText.Desc = "Desc: "..net.ReadString()
			AdvDupe2.InfoText.Entities = "Entities: "..net.ReadString()
			AdvDupe2.InfoText.Constraints = "Constraints: "..net.ReadString()
		end
	end)
end
