--[[ | This file was obtained through the combined efforts | of Madbluntz & Plymouth Antiquarian Society. | | Credits: lifestorm, Gregory Wayne Rossel JR., | Maloy, DrPepper10 @ RIP, Atle! | | Visit for more: https://plymouth.thetwilightzone.ru/ --]] --[[ 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 ents.Iterator() do local pos = ent:GetPos() if (pos.X>=min.X) and (pos.X<=max.X) and (pos.Y>=min.Y) and (pos.Y<=max.Y) and (pos.Z>=min.Z) and (pos.Z<=max.Z) and PPCheck( ply, ent ) then EntTable[ent:EntIndex()] = ent end end return EntTable end --[[ Name: GetDupeAngleOffset Desc: Retrieves duplication angle offsets from player Returns: 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: 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 Returns: success ]] 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 if(dupe.Pasting or dupe.Downloading) then AdvDupe2.Notify(ply,"Advanced Duplicator 2 is busy.",NOTIFY_ERROR) return false 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 Returns: 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, "Opening: ") 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 Returns: 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) ply:AddCleanup( "AdvDupe2", spawner ) undo.Create("gmod_contr_spawner") 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, 1) 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:IsAdmin()) 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 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 if(not file.IsDir("advdupe2_maps", "DATA")) then file.CreateDir("advdupe2_maps") end file.Write("advdupe2_maps/"..args[1]..".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 language.Add( "Tool.advdupe2.name", "Advanced Duplicator 2" ) language.Add( "Tool.advdupe2.desc", "Duplicate things." ) language.Add( "Tool.advdupe2.0", "Primary: Paste, Secondary: Copy, Secondary+World: Select/Deselect All, Secondary+Shift: Area copy." ) language.Add( "Tool.advdupe2.1", "Primary: Paste, Secondary: Copy an area, Reload: Autosave an area, Secondary+Shift: 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) 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) 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) 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")) 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() if(txtbox2:GetValue()=="") then return end 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, ply) local EntTable = {} for _,ent in ents.Iterator() do local pos = ent:GetPos() if (pos.X>=min.X) and (pos.X<=max.X) and (pos.Y>=min.Y) and (pos.Y<=max.Y) and (pos.Z>=min.Z) and (pos.Z<=max.Z) then --if(ent:GetClass()~="C_BaseFlexclass") then EntTable[ent:EntIndex()] = ent --end end 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