--[[ Title: Adv. Duplicator 2 Module Desc: Provides advanced duplication functionality for the Adv. Dupe 2 tool. Author: TB Version: 1.0 ]] require "duplicator" AdvDupe2.duplicator = {} AdvDupe2.JobManager = {} AdvDupe2.JobManager.PastingHook = false AdvDupe2.JobManager.Queue = {} local constraints = {Weld=true, Axis=true, Ballsocket=true, Elastic=true, Hydraulic=true, Motor=true, Muscle=true, Pulley=true, Rope=true, Slider=true, Winch=true} local serializable = { [TYPE_BOOL] = true, [TYPE_NUMBER] = true, [TYPE_VECTOR] = true, [TYPE_ANGLE] = true, [TYPE_TABLE] = true, [TYPE_STRING] = true } local function CopyClassArgTable(tab) local done = {} local function recursiveCopy(oldtable) local newtable = {} done[oldtable] = newtable for k, v in pairs(oldtable) do local varType = TypeID(v) if serializable[varType] then if varType == TYPE_TABLE then if done[v] then newtable[k] = done[v] else newtable[k] = recursiveCopy(v) end else newtable[k] = v end -- else -- print("[AdvDupe2] ClassArg table with key \"" .. tostring(k) .. "\" has unsupported value of type \"".. type(v) .."\"!\n") end end return newtable end return recursiveCopy(tab) end local function doSpawnHooks(ply, ent) local class = ent:GetClass() if class == 'prop_effect' then gamemode.Call('PlayerSpawnedEffect', ply, ent:GetModel(), ent) elseif class == 'prop_physics' or class:find('prop_dynamic', 1, false) then gamemode.Call('PlayerSpawnedProp', ply, ent:GetModel(), ent) else gamemode.Call('PlayerSpawnedSENT', ply, ent) end end --[[ Name: CopyEntTable Desc: Returns a copy of the passed entity's table Params: Ent Returns: enttable ]] /*--------------------------------------------------------- Returns a copy of the passed entity's table ---------------------------------------------------------*/ local function CopyEntTable( Ent, Offset ) -- Filter duplicator blocked entities out. if Ent.DoNotDuplicate then return nil end if(not IsValid(Ent:GetPhysicsObject()))then return nil end local Tab = {} if Ent.PreEntityCopy then local status, valid = pcall(Ent.PreEntityCopy, Ent) if(not status)then print("AD2 PreEntityCopy Error: "..tostring(valid)) end end local EntityClass = duplicator.FindEntityClass( Ent:GetClass() ) local EntTable = Ent:GetTable() if EntityClass then for iNumber, Key in pairs( EntityClass.Args ) do -- Ignore keys from old system if Key~="Pos" and Key~="Model" and Key~="Ang" and Key~="Angle" and Key~="ang" and Key~="angle" and Key~="pos" and Key~="position" and Key~="model" then local varType=TypeID(EntTable[Key]) if serializable[varType] then if varType == TYPE_TABLE then Tab[Key] = CopyClassArgTable(EntTable[Key]) else Tab[Key] = EntTable[Key] end -- elseif varType ~= TYPE_NIL then -- print("[AdvDupe2] Entity ClassArg \"" .. Key .. "\" of type \"".. Ent:GetClass() .."\" has unsupported value of type \"".. type(EntTable[ Key ]) .."\"!\n") end end end end Tab.BoneMods = table.Copy( Ent.BoneMods ) if(Ent.EntityMods)then Tab.EntityMods = table.Copy(Ent.EntityMods) end if Ent.PostEntityCopy then local status, valid = pcall(Ent.PostEntityCopy, Ent) if(not status)then print("AD2 PostEntityCopy Error: "..tostring(valid)) end end Tab.Pos = Ent:GetPos() Tab.Class = Ent:GetClass() Tab.Model = Ent:GetModel() Tab.Skin = Ent:GetSkin() Tab.CollisionGroup = Ent:GetCollisionGroup() Tab.ModelScale = Ent:GetModelScale() if(Tab.Skin==0)then Tab.Skin = nil end if(Tab.ModelScale == 1)then Tab.ModelScale = nil end if(Tab.Class == "gmod_cameraprop")then Tab.key = Ent:GetNetworkedInt("key") end -- Allow the entity to override the class -- This is a hack for the jeep, since it's real class is different from the one it reports as -- (It reports a different class to avoid compatibility problems) if Ent.ClassOverride then Tab.Class = Ent.ClassOverride end Tab.PhysicsObjects = {} -- Physics Objects local PhysObj for Bone = 0, Ent:GetPhysicsObjectCount()-1 do PhysObj = Ent:GetPhysicsObjectNum( Bone ) if IsValid(PhysObj) then Tab.PhysicsObjects[ Bone ] = Tab.PhysicsObjects[ Bone ] or {} if(PhysObj:IsMoveable())then Tab.PhysicsObjects[ Bone ].Frozen = true end PhysObj:EnableMotion(false) Tab.PhysicsObjects[ Bone ].Pos = PhysObj:GetPos() - Tab.Pos Tab.PhysicsObjects[ Bone ].Angle = PhysObj:GetAngles() end end Tab.PhysicsObjects[0].Pos = Tab.Pos - Offset Tab.Pos = nil if(Tab.Class~="prop_physics")then if(not Tab.BuildDupeInfo)then Tab.BuildDupeInfo = {} end Tab.BuildDupeInfo.IsNPC = Ent:IsNPC() Tab.BuildDupeInfo.IsVehicle = Ent:IsVehicle() end if( IsValid(Ent:GetParent()) ) then if(not Tab.BuildDupeInfo)then Tab.BuildDupeInfo = {} end Tab.PhysicsObjects[ 0 ].Angle = Ent:GetAngles() Tab.BuildDupeInfo.DupeParentID = Ent:GetParent():EntIndex() end -- Flexes local FlexNum = Ent:GetFlexNum() Tab.Flex = Tab.Flex or {} local weight local flexes for i = 0, FlexNum do weight = Ent:GetFlexWeight( i ) if(weight~=0)then Tab.Flex[ i ] = weight flexes = true end end if(flexes or Ent:GetFlexScale()~=1)then Tab.FlexScale = Ent:GetFlexScale() else Tab.Flex = nil end -- Body Groups Tab.BodyG = {} for k, v in pairs(Ent:GetBodyGroups()) do if ( Ent:GetBodygroup( v.id ) > 0 ) then Tab.BodyG[ v.id ] = Ent:GetBodygroup( v.id ) end end if(table.Count(Tab.BodyG)==0)then Tab.BodyG = nil end -- Bone Manipulator if ( Ent:HasBoneManipulations() ) then Tab.BoneManip = {} local t local s local a local p for i=0, Ent:GetBoneCount() do t={} s = Ent:GetManipulateBoneScale( i ) a = Ent:GetManipulateBoneAngles( i ) p = Ent:GetManipulateBonePosition( i ) if ( s != Vector( 1, 1, 1 ) ) then t[ 's' ] = s end if ( a != Angle( 0, 0, 0 ) ) then t[ 'a' ] = a end if ( p != Vector( 0, 0, 0 ) ) then t[ 'p' ] = p end if ( t['s'] or t['a'] or t['p'] ) then Tab.BoneManip[ i ] = t end end end if Ent.GetNetworkVars then Tab.DT = Ent:GetNetworkVars() end // Make this function on your SENT if you want to modify the // returned table specifically for your entity. if Ent.OnEntityCopyTableFinish then local status, valid = pcall(Ent.OnEntityCopyTableFinish, Ent, Tab) if(not status)then print("AD2 OnEntityCopyTableFinish Error: "..tostring(valid)) end end return Tab end --[[ Name: CopyConstraintTable Desc: Create a table for constraints Params:
Constraints Returns:
Constraints,
Entities ]] local function CopyConstraintTable( Const, Offset ) if(Const==nil)then return nil, {} end -- Filter duplicator blocked constraints out. if Const.DoNotDuplicate then return nil, {} end local Type = duplicator.ConstraintType[ Const.Type ] if(not Type)then return nil, {} end local Constraint = {} local Entities = {} Const.Constraint = nil Const.OnDieFunctions=nil Constraint.Entity={} for k, key in pairs( Type.Args ) do if(key~="pl" and not string.find(key, "Ent") and not string.find(key, "Bone"))then Constraint[key] = Const[ key ] end end if((Const["Ent"] and Const["Ent"]:IsWorld()) or IsValid(Const["Ent"]))then Constraint.Entity[ 1 ] = {} Constraint.Entity[ 1 ].Index = Const["Ent"]:EntIndex() if(not Const["Ent"]:IsWorld())then table.insert( Entities, Const["Ent"] ) end Constraint.Type = Const.Type if(Const.BuildDupeInfo)then Constraint.BuildDupeInfo = table.Copy(Const.BuildDupeInfo) end else local ent for i=1,4 do ent = "Ent"..i if((Const[ent] and Const[ent]:IsWorld()) or IsValid(Const[ent]))then Constraint.Entity[ i ] = {} Constraint.Entity[ i ].Index = Const[ent]:EntIndex() Constraint.Entity[ i ].Bone = Const[ "Bone"..i ] Constraint.Entity[ i ].Length = Const[ "Length"..i ] Constraint.Entity[ i ].World = Const[ "World"..i ] if Const[ ent ]:IsWorld() then Constraint.Entity[ i ].World = true if ( Const[ "LPos"..i ] ) then if(i~= 4 and i~=2)then if(Const["Ent2"])then Constraint.Entity[ i ].LPos = Const[ "LPos"..i ] - Const["Ent2"]:GetPos() Constraint[ "LPos"..i ] = Const[ "LPos"..i ] - Const["Ent2"]:GetPos() elseif(Const["Ent4"])then Constraint.Entity[ i ].LPos = Const[ "LPos"..i ] - Const["Ent4"]:GetPos() Constraint[ "LPos"..i ] = Const[ "LPos"..i ] - Const["Ent4"]:GetPos() end elseif(Const["Ent1"])then Constraint.Entity[ i ].LPos = Const[ "LPos"..i ] - Const["Ent1"]:GetPos() Constraint[ "LPos"..i ] = Const[ "LPos"..i ] - Const["Ent1"]:GetPos() end else Constraint.Entity[ i ].LPos = Offset Constraint[ "LPos"..i ] = Offset end else Constraint.Entity[ i ].LPos = Const[ "LPos"..i ] Constraint.Entity[ i ].WPos = Const[ "WPos"..i ] end if(not Const[ent]:IsWorld())then table.insert( Entities, Const[ent] ) end end if(Const["WPos"..i])then if(not Const["Ent1"]:IsWorld())then Constraint["WPos"..i] = Const[ "WPos"..i ] - Const["Ent1"]:GetPos() else Constraint["WPos"..i] = Const[ "WPos"..i ] - Const["Ent4"]:GetPos() end end end Constraint.Type = Const.Type if(Const.BuildDupeInfo)then Constraint.BuildDupeInfo = table.Copy(Const.BuildDupeInfo) end end return Constraint, Entities end --[[ Name: Copy Desc: Copy an entity and all entities constrained Params: Entity Returns:
Entities,
Constraints ]] local function Copy( Ent, EntTable, ConstraintTable, Offset ) local index = Ent:EntIndex() if EntTable[index] then return EntTable, ConstraintTable end local EntData = CopyEntTable(Ent, Offset) if EntData == nil then return EntTable, ConstraintTable end EntTable[index] = EntData if Ent.Constraints then for k, Constraint in pairs( Ent.Constraints ) do if Constraint:IsValid() then index = Constraint:GetCreationID() if index and not ConstraintTable[index] then local ConstTable, EntTab = CopyConstraintTable( table.Copy(Constraint:GetTable()), Offset ) ConstraintTable[index] = ConstTable for j,e in pairs(EntTab) do if e and ( e:IsWorld() or e:IsValid() ) then Copy( e, EntTable, ConstraintTable, Offset ) end end end end end end do -- Wiremod Wire Connections if istable(Ent.Inputs) then for k, v in pairs(Ent.Inputs) do if isentity(v.Src) and v.Src:IsValid() then Copy( v.Src, EntTable, ConstraintTable, Offset ) end end end if istable(Ent.Outputs) then for k, v in pairs(Ent.Outputs) do if istable(v.Connected) then for k, v in pairs(v.Connected) do if isentity(v.Entity) and v.Entity:IsValid() then Copy( v.Entity, EntTable, ConstraintTable, Offset ) end end end end end end do -- Parented stuff local parent = Ent:GetParent() if IsValid(parent) then Copy(parent, EntTable, ConstraintTable, Offset) end for k, child in pairs(Ent:GetChildren()) do Copy(child, EntTable, ConstraintTable, Offset) end end for k,v in pairs(EntTable[Ent:EntIndex()].PhysicsObjects)do Ent:GetPhysicsObjectNum(k):EnableMotion(v.Frozen) end return EntTable, ConstraintTable end AdvDupe2.duplicator.Copy = Copy --[[ Name: AreaCopy Desc: Copy based on a box Params: Entity Returns:
Entities,
Constraints ]] function AdvDupe2.duplicator.AreaCopy( Entities, Offset, CopyOutside ) local Constraints, EntTable, ConstraintTable = {}, {}, {} local index, add, AddEnts, AddConstrs, ConstTable, EntTab for _,Ent in pairs(Entities) do index = Ent:EntIndex() EntTable[index] = CopyEntTable(Ent, Offset) if(EntTable[index]~=nil)then if ( not constraint.HasConstraints( Ent ) ) then for k,v in pairs(EntTable[Ent:EntIndex()].PhysicsObjects)do Ent:GetPhysicsObjectNum(k):EnableMotion(v.Frozen) end else for k,v in pairs(Ent.Constraints)do -- Filter duplicator blocked constraints out. if v.DoNotDuplicate then continue end index = v:GetCreationID() if(index and not Constraints[index])then Constraints[index] = v end end for k,v in pairs(EntTable[Ent:EntIndex()].PhysicsObjects)do Ent:GetPhysicsObjectNum(k):EnableMotion(v.Frozen) end end end end for _, Constraint in pairs( Constraints ) do ConstTable, EntTab = CopyConstraintTable( table.Copy(Constraint:GetTable()), Offset ) //If the entity is constrained to an entity outside of the area box, don't copy the constraint. if(not CopyOutside)then add = true for k,v in pairs(EntTab)do if(not Entities[v:EntIndex()])then add=false end end if(add)then ConstraintTable[_] = ConstTable end else //Copy entities and constraints outside of the box that are constrained to entities inside the box ConstraintTable[_] = ConstTable for k,v in pairs(EntTab)do Copy(v, EntTable, ConstraintTable, Offset) end end end return EntTable, ConstraintTable end --[[ Name: CreateConstraintFromTable Desc: Creates a constraint from a given table Params:
Constraint,
EntityList,
EntityTable Returns: CreatedConstraint ]] local function CreateConstraintFromTable(Constraint, EntityList, EntityTable, Player, DontEnable) local Factory = duplicator.ConstraintType[ Constraint.Type ] if not Factory then return end if Factory.Tool and Constraint.Entity and Constraint.Entity[1] then local override = hook.Run('CanTool', Player, { Entity = Constraint.Entity[1] }, Factory.Tool) if override == false then return end end local first --Ent1 or Ent in the constraint's table local second --Any other Ent that is not Ent1 or Ent -- Build the argument list for the Constraint's spawn function local Args = {} local Val for k, Key in pairs( Factory.Args ) do Val = Constraint[ Key ] if Key == "pl" or Key == "ply" then Val = Player end for i=1, 4 do if ( Constraint.Entity and Constraint.Entity[ i ] ) then if Key == "Ent"..i or Key == "Ent" then if ( Constraint.Entity[ i ].World ) then Val = game.GetWorld() else Val = EntityList[ Constraint.Entity[ i ].Index ] if not IsValid(Val) then if(Player)then Player:ChatPrint("DUPLICATOR: ERROR, "..Constraint.Type.." Constraint could not find an entity!") else print("DUPLICATOR: ERROR, "..Constraint.Type.." Constraint could not find an entity!") end return else if(IsValid(Val:GetPhysicsObject()))then Val:GetPhysicsObject():EnableMotion(false) end --Important for perfect duplication --Get which entity is which so we can reposition them before constraining if(Key== "Ent" or Key == "Ent1")then first=Val firstindex = Constraint.Entity[ i ].Index else second=Val secondindex = Constraint.Entity[ i ].Index end end end end if Key == "Bone"..i or Key == "Bone" then Val = Constraint.Entity[ i ].Bone or 0 end if Key == "LPos"..i then if (Constraint.Entity[i].World and Constraint.Entity[i].LPos)then if(i==2 or i==4)then Val = Constraint.Entity[i].LPos + EntityList[Constraint.Entity[1].Index]:GetPos() elseif(i==1)then if(Constraint.Entity[2])then Val = Constraint.Entity[i].LPos + EntityList[Constraint.Entity[2].Index]:GetPos() else Val = Constraint.Entity[i].LPos + EntityList[Constraint.Entity[4].Index]:GetPos() end end elseif( Constraint.Entity[i].LPos ) then Val = Constraint.Entity[ i ].LPos end end if Key == "Length"..i then Val = Constraint.Entity[ i ].Length end end if Key == "WPos"..i then if(not Constraint.Entity[1].World)then Val = Constraint["WPos"..i] + EntityList[Constraint.Entity[1].Index]:GetPos() else Val = Constraint["WPos"..i] + EntityList[Constraint.Entity[4].Index]:GetPos() end end end -- If there's a missing argument then unpack will stop sending at that argument Val = Val or false table.insert( Args, Val ) end local Bone1 local Bone1Index local ReEnableFirst local Bone2 local Bone2Index local ReEnableSecond if(Constraint.BuildDupeInfo)then if first ~= nil and second ~= nil and not second:IsWorld() and Constraint.BuildDupeInfo.EntityPos ~= nil then local SecondPhys = second:GetPhysicsObject() if IsValid(SecondPhys) then if not DontEnable then ReEnableSecond = SecondPhys:IsMoveable() end SecondPhys:EnableMotion(false) second:SetPos(first:GetPos()-Constraint.BuildDupeInfo.EntityPos) if(Constraint.BuildDupeInfo.Bone2) then Bone2Index = Constraint.BuildDupeInfo.Bone2 Bone2 = second:GetPhysicsObjectNum(Bone2Index) if IsValid(Bone2) then Bone2:EnableMotion(false) Bone2:SetPos(second:GetPos() + Constraint.BuildDupeInfo.Bone2Pos) Bone2:SetAngles(Constraint.BuildDupeInfo.Bone2Angle) end end end end if first ~= nil and Constraint.BuildDupeInfo.Ent1Ang ~= nil then local FirstPhys = first:GetPhysicsObject() if IsValid(FirstPhys) then if not DontEnable then ReEnableFirst = FirstPhys:IsMoveable() end FirstPhys:EnableMotion(false) first:SetAngles(Constraint.BuildDupeInfo.Ent1Ang) if(Constraint.BuildDupeInfo.Bone1) then Bone1Index = Constraint.BuildDupeInfo.Bone1 Bone1 = first:GetPhysicsObjectNum(Bone1Index) if IsValid(Bone1) then Bone1:EnableMotion(false) Bone1:SetPos(first:GetPos() + Constraint.BuildDupeInfo.Bone1Pos) Bone1:SetAngles(Constraint.BuildDupeInfo.Bone1Angle) end end end end if second ~= nil and Constraint.BuildDupeInfo.Ent2Ang ~= nil then second:SetAngles(Constraint.BuildDupeInfo.Ent2Ang) end if second ~= nil and Constraint.BuildDupeInfo.Ent4Ang ~= nil then second:SetAngles(Constraint.BuildDupeInfo.Ent4Ang) end end local status, Ent = pcall( Factory.Func, unpack(Args)) if not status or not Ent then if(Player)then AdvDupe2.Notify(Player, "ERROR, Failed to create "..Constraint.Type.." Constraint!", NOTIFY_ERROR) else print("DUPLICATOR: ERROR, Failed to create "..Constraint.Type.." Constraint!") end return end Ent.BuildDupeInfo = table.Copy(Constraint.BuildDupeInfo) //Move the entities back after constraining them if(EntityTable)then if(first~=nil)then first:SetPos(EntityTable[firstindex].BuildDupeInfo.PosReset) first:SetAngles(EntityTable[firstindex].BuildDupeInfo.AngleReset) if(IsValid(Bone1) and Bone1Index~=0)then Bone1:SetPos(EntityTable[firstindex].BuildDupeInfo.PosReset + EntityTable[firstindex].BuildDupeInfo.PhysicsObjects[Bone1Index].Pos) Bone1:SetAngles(EntityTable[firstindex].PhysicsObjects[Bone1Index].Angle) end local FirstPhys = first:GetPhysicsObject() if IsValid(FirstPhys) then if ReEnableFirst then FirstPhys:EnableMotion(true) end end end if(second~=nil)then second:SetPos(EntityTable[secondindex].BuildDupeInfo.PosReset) second:SetAngles(EntityTable[secondindex].BuildDupeInfo.AngleReset) if(IsValid(Bone2) and Bone2Index~=0)then Bone2:SetPos(EntityTable[secondindex].BuildDupeInfo.PosReset + EntityTable[secondindex].BuildDupeInfo.PhysicsObjects[Bone2Index].Pos) Bone2:SetAngles(EntityTable[secondindex].PhysicsObjects[Bone2Index].Angle) end local SecondPhys = second:GetPhysicsObject() if IsValid(SecondPhys) then if ReEnableSecond then SecondPhys:EnableMotion(true) end end end end if(Ent and Ent.length)then Ent.length = Constraint["length"] end //Fix for weird bug with ropes return Ent end local function ApplyEntityModifiers( Player, Ent ) if not Ent.EntityMods then return end if Ent.EntityMods.trail then Ent.EntityMods.trail.EndSize = math.Clamp(tonumber(Ent.EntityMods.trail.EndSize) or 0, 0, 1024) Ent.EntityMods.trail.StartSize = math.Clamp(tonumber(Ent.EntityMods.trail.StartSize) or 0, 0, 1024) end local status, error for Type, Data in SortedPairs( Ent.EntityMods ) do local ModFunction = duplicator.EntityModifiers[ Type ] if ( ModFunction ) then status, error = pcall(ModFunction, Player, Ent, Data ) if(not status)then if(Player)then Player:ChatPrint('Error applying entity modifer, "'..tostring(Type)..'". ERROR: '..error) else print('Error applying entity modifer, "'..tostring(Type)..'". ERROR: '..error) end end end end if(Ent.EntityMods["mass"] and duplicator.EntityModifiers["mass"])then status, error = pcall(duplicator.EntityModifiers["mass"], Player, Ent, Ent.EntityMods["mass"] ) if(not status)then if(Player)then Player:ChatPrint('Error applying entity modifer, "mass". ERROR: '..error) else print('Error applying entity modifer, "'..tostring(Type)..'". ERROR: '..error) end end end end local function ApplyBoneModifiers( Player, Ent ) if(not Ent.BoneMods or not Ent.PhysicsObjects)then return end local status, error, PhysObject for Type, ModFunction in pairs( duplicator.BoneModifiers ) do for Bone, Args in pairs( Ent.PhysicsObjects ) do if ( Ent.BoneMods[ Bone ] and Ent.BoneMods[ Bone ][ Type ] ) then PhysObject = Ent:GetPhysicsObjectNum( Bone ) if ( Ent.PhysicsObjects[ Bone ] ) then status, error = pcall(ModFunction, Player, Ent, Bone, PhysObject, Ent.BoneMods[ Bone ][ Type ] ) if(not status)then Player:ChatPrint('Error applying bone modifer, "'..tostring(Type)..'". ERROR: '..error) end end end end end end --[[ Name: DoGenericPhysics Desc: Applies bone data, generically. Params: Player,
data Returns: Entity,
data ]] local function DoGenericPhysics( Entity, data, Player ) if (not data) then return end if (not data.PhysicsObjects) then return end local Phys if(Player)then for Bone, Args in pairs( data.PhysicsObjects ) do Phys = Entity:GetPhysicsObjectNum(Bone) if ( IsValid(Phys) ) then Phys:SetPos( Args.Pos ) Phys:SetAngles( Args.Angle ) Phys:EnableMotion( false ) Player:AddFrozenPhysicsObject( Entity, Phys ) end end else for Bone, Args in pairs( data.PhysicsObjects ) do Phys = Entity:GetPhysicsObjectNum(Bone) if ( IsValid(Phys) ) then Phys:SetPos( Args.Pos ) Phys:SetAngles( Args.Angle ) Phys:EnableMotion( false ) end end end end local function reportclass(ply,class) umsg.Start("AdvDupe2_ReportClass", ply) umsg.String(class) umsg.End() end local function reportmodel(ply,model) umsg.Start("AdvDupe2_ReportModel", ply) umsg.String(model) umsg.End() end --[[ Name: GenericDuplicatorFunction Desc: Override the default duplicator's GenericDuplicatorFunction function Params: Player,
data Returns: Entity ]] local function GenericDuplicatorFunction( data, Player ) local Entity = ents.Create( data.Class ) if ( not IsValid(Entity) ) then if(Player)then reportclass(Player,data.Class) else print("Advanced Duplicator 2 Invalid Class: "..data.Class) end return nil end if( not util.IsValidModel(data.Model) and not file.Exists( data.Model, "GAME" ) )then if(Player)then reportmodel(Player,data.Model) else print("Advanced Duplicator 2 Invalid Model: "..data.Model) end return nil end duplicator.DoGeneric( Entity, data ) Entity:Spawn() Entity:Activate() DoGenericPhysics( Entity, data, Player ) -- APG.entGhost(Entity, true, true) -- timer.Simple(0.1, function() -- if IsValid(Entity) then APG.entUnGhost(Entity) end -- end) table.Add( Entity:GetTable(), data ) return Entity end --[[ Name: MakeProp Desc: Make prop without spawn effects Params: Player, Pos, Ang, Model,
PhysicsObject,
Data Returns: Prop ]] local function MakeProp(Player, Pos, Ang, Model, PhysicsObject, Data) if( not util.IsValidModel(Model) and not file.Exists( Data.Model, "GAME" ) )then if(Player)then reportmodel(Player,Data.Model) else print("Advanced Duplicator 2 Invalid Model: "..Model) end return nil end Data.Pos = Pos Data.Angle = Ang Data.Model = Model Data.Frozen = true // Make sure this is allowed if( Player )then if ( not gamemode.Call( "PlayerSpawnProp", Player, Model ) ) then return false end end local Prop = ents.Create( "prop_physics" ) if not IsValid(Prop) then return false end duplicator.DoGeneric( Prop, Data ) Prop:Spawn() Prop:Activate() DoGenericPhysics( Prop, Data, Player ) if(Data.Flex)then duplicator.DoFlex( Prop, Data.Flex, Data.FlexScale ) end -- APG.entGhost(Prop, true, true) -- timer.Simple(0.1, function() -- if IsValid(Prop) then APG.entUnGhost(Prop) end -- end) return Prop end local function MakeDynProp(Player, Pos, Ang, Model, PhysicsObject, Data) if( not util.IsValidModel(Model) and not file.Exists( Data.Model, "GAME" ) )then if(Player)then reportmodel(Player,Data.Model) else print("Advanced Duplicator 2 Invalid Model: "..Model) end return nil end Data.Pos = Pos Data.Angle = Ang Data.Model = Model Data.Frozen = true // Make sure this is allowed if( Player )then if ( not gamemode.Call( "PlayerSpawnProp", Player, Model ) ) then return false end end local Prop = ents.Create( "prop_dynamic_override" ) if not IsValid(Prop) then return false end duplicator.DoGeneric( Prop, Data ) Prop:Spawn() Prop:PhysicsInitStatic(SOLID_VPHYSICS) Prop:Activate() DoGenericPhysics( Prop, Data, Player ) if(Data.Flex)then duplicator.DoFlex( Prop, Data.Flex, Data.FlexScale ) end -- APG.entGhost(Prop, true, true) -- timer.Simple(0.1, function() -- if IsValid(Prop) then APG.entUnGhost(Prop) end -- end) return Prop end local function RestoreBodyGroups( ent, BodyG ) for k, v in pairs( BodyG ) do ent:SetBodygroup( k, v ) end end --[[ Name: CreateEntityFromTable Desc: Creates an entity from a given table Params:
EntTable, Player Returns: nil ]] local function IsAllowed(Player, Class, EntityClass) if ( scripted_ents.GetMember( Class, "DoNotDuplicate" ) ) then return false end if ( IsValid( Player ) and !Player:IsAdmin()) then if !duplicator.IsAllowed(Class) then return false end if ( !scripted_ents.GetMember( Class, "Spawnable" ) and not EntityClass ) then return false end if ( scripted_ents.GetMember( Class, "AdminOnly" ) ) then return false end end return true end local function CreateEntityFromTable(EntTable, Player) local classToSearch = EntTable.Class:find('prop_dynamic', 1, false) and 'prop_physics' or EntTable.Class local EntityClass = duplicator.FindEntityClass( classToSearch ) if not IsAllowed(Player, EntTable.Class, EntityClass) then Player:ChatPrint([[Entity Class Black listed, "]]..EntTable.Class..[["]]) return nil end local sent = false local status, valid local GENERIC = false // This class is unregistered. Instead of failing try using a generic // Duplication function to make a new copy. if (not EntityClass) then GENERIC = true sent = true if(EntTable.Class=="prop_effect")then sent = gamemode.Call( "PlayerSpawnEffect", Player, EntTable.Model) else sent = gamemode.Call( "PlayerSpawnSENT", Player, EntTable.Class) end if(sent==false)then print("Advanced Duplicator 2: Creation rejected for class, : "..EntTable.Class) return nil else sent = true end if IsAllowed(Player, EntTable.Class, EntityClass) then status, valid = pcall(GenericDuplicatorFunction, EntTable, Player ) else print("Advanced Duplicator 2: ENTITY CLASS IS BLACKLISTED, CLASS NAME: "..EntTable.Class) return nil end end if(not GENERIC)then // Build the argument list for the Entitie's spawn function local ArgList = {} local Arg for iNumber, Key in pairs( EntityClass.Args ) do Arg = nil // Translate keys from old system if ( Key == "pos" or Key == "position" ) then Key = "Pos" end if ( Key == "ang" or Key == "Ang" or Key == "angle" ) then Key = "Angle" end if ( Key == "model" ) then Key = "Model" end if ( Key == "VehicleTable" and EntTable[Key] and EntTable[Key].KeyValues)then EntTable[Key].KeyValues = {vehiclescript=EntTable[Key].KeyValues.vehiclescript, limitview=EntTable[Key].KeyValues.limitview} end Arg = EntTable[ Key ] // Special keys if ( Key == "Data" ) then Arg = EntTable end ArgList[ iNumber ] = Arg end // Create and return the entity if(EntTable.Class=="prop_physics")then valid = MakeProp(Player, unpack(ArgList, 1, #EntityClass.Args)) //Create prop_physics like this because if the model doesn't exist it will cause elseif(EntTable.Class:find("prop_dynamic", 1, false)) then valid = MakeDynProp(Player, unpack(ArgList, 1, #EntityClass.Args)) elseif IsAllowed(Player, EntTable.Class, EntityClass) then //Create sents using their spawn function with the arguments we stored earlier sent = true if (EntTable.Class:find("prop_dynamic", 1, false)) then sent = gamemode.Call( "PlayerSpawnProp", Player, EntTable.Model ) ~= false elseif(not EntTable.BuildDupeInfo.IsVehicle and not EntTable.BuildDupeInfo.IsNPC and EntTable.Class ~= "prop_ragdoll" and EntTable.Class ~= "prop_effect") then //These four are auto done sent = hook.Call("PlayerSpawnSENT", nil, Player, EntTable.Class) end if not sent then if EntTable.Class:sub(1, 5) == 'gmod_' then local tool = EntTable.Class:sub(6) local override = hook.Run('CanTool', Player, { Entity = NULL }, tool) if override ~= false then sent = true end end local tool = scripted_ents.GetMember(EntTable.Class, 'Tool') if tool then local override = hook.Run('CanTool', Player, { Entity = NULL }, tool) if override ~= false then sent = true end end end if(sent==false)then print("Advanced Duplicator 2: Creation rejected for class, : "..EntTable.Class) return nil else sent = true end status,valid = pcall( EntityClass.Func, Player, unpack(ArgList, 1, #EntityClass.Args) ) else print("Advanced Duplicator 2: ENTITY CLASS IS BLACKLISTED, CLASS NAME: "..EntTable.Class) return nil end end //If its a valid entity send it back to the entities list so we can constrain it if( status~=false and IsValid(valid) )then for _, ply in pairs(player.GetAll()) do FPP.calculateCanTouch(ply, valid) end if(sent)then local iNumPhysObjects = valid:GetPhysicsObjectCount() local PhysObj if(Player)then for Bone = 0, iNumPhysObjects-1 do PhysObj = valid:GetPhysicsObjectNum( Bone ) if IsValid(PhysObj) then PhysObj:EnableMotion(false) Player:AddFrozenPhysicsObject( valid, PhysObj ) end end else for Bone = 0, iNumPhysObjects-1 do PhysObj = valid:GetPhysicsObjectNum( Bone ) if IsValid(PhysObj) then PhysObj:EnableMotion(false) end end end if(EntTable.Skin)then valid:SetSkin(EntTable.Skin) end if ( EntTable.BodyG ) then RestoreBodyGroups( valid, EntTable.BodyG ) end if valid.RestoreNetworkVars then valid:RestoreNetworkVars(EntTable.DT) end -- if GENERIC then -- if(EntTable.Class=="prop_effect")then -- gamemode.Call("PlayerSpawnedEffect", Player, valid:GetModel(), valid) -- else -- gamemode.Call("PlayerSpawnedSENT", Player, valid) -- end -- end elseif(Player)then -- gamemode.Call( "PlayerSpawnedProp", Player, valid:GetModel(), valid ) end return valid else if(valid==false)then return false else return nil end end end --[[ Name: Paste Desc: Override the default duplicator's paste function Params: Player,
Entities,
Constraints Returns:
Entities,
Constraints ]] function AdvDupe2.duplicator.Paste( Player, EntityList, ConstraintList, Position, AngleOffset, OrigPos, Parenting ) local CreatedEntities = {} -- -- Create entities -- local proppos DisablePropCreateEffect = true for k, v in pairs( EntityList ) do if(not v.BuildDupeInfo)then v.BuildDupeInfo={} end v.BuildDupeInfo.PhysicsObjects = table.Copy(v.PhysicsObjects) proppos = v.PhysicsObjects[0].Pos v.BuildDupeInfo.PhysicsObjects[0].Pos = Vector(0,0,0) if( OrigPos )then for i,p in pairs(v.BuildDupeInfo.PhysicsObjects) do v.PhysicsObjects[i].Pos = p.Pos + proppos + OrigPos v.PhysicsObjects[i].Frozen = true end v.Pos = v.PhysicsObjects[0].Pos v.Angle = v.PhysicsObjects[0].Angle v.BuildDupeInfo.PosReset = v.Pos v.BuildDupeInfo.AngleReset = v.Angle else for i,p in pairs(v.BuildDupeInfo.PhysicsObjects)do v.PhysicsObjects[i].Pos, v.PhysicsObjects[i].Angle = LocalToWorld(p.Pos + proppos, p.Angle, Position, AngleOffset) v.PhysicsObjects[i].Frozen = true end v.Pos = v.PhysicsObjects[0].Pos v.BuildDupeInfo.PosReset = v.Pos v.Angle = v.PhysicsObjects[0].Angle v.BuildDupeInfo.AngleReset = v.Angle end AdvDupe2.SpawningEntity = true local Ent = CreateEntityFromTable(v, Player) AdvDupe2.SpawningEntity = false if Ent then if(Player)then Player:AddCleanup( "AdvDupe2", Ent ) end Ent.BoneMods = table.Copy( v.BoneMods ) Ent.EntityMods = table.Copy( v.EntityMods ) Ent.PhysicsObjects = table.Copy( v.PhysicsObjects ) if(v.CollisionGroup)then Ent:SetCollisionGroup(v.CollisionGroup) end if(Ent.OnDuplicated)then Ent:OnDuplicated(v) end ApplyEntityModifiers( Player, Ent ) ApplyBoneModifiers( Player, Ent ) Ent.SolidMod = not Ent:IsSolid() Ent:SetNotSolid(true) doSpawnHooks(Player, Ent) elseif(Ent==false)then Ent = nil --ConstraintList = {} --break else Ent = nil end CreatedEntities[k] = Ent end local CreatedConstraints = {} local Entity -- -- Create constraints -- for k, Constraint in pairs( ConstraintList ) do Entity = CreateConstraintFromTable( Constraint, CreatedEntities, EntityList, Player ) if(IsValid(Entity))then table.insert( CreatedConstraints, Entity ) end end if(Player)then undo.Create "AdvDupe2_Paste" for _,v in pairs( CreatedEntities ) do --If the entity has a PostEntityPaste function tell it to use it now if v.PostEntityPaste then local status, valid = pcall(v.PostEntityPaste, v, Player, v, CreatedEntities) if(not status)then print("AD2 PostEntityPaste Error: "..tostring(valid)) end end v:GetPhysicsObject():EnableMotion(false) if(EntityList[_].BuildDupeInfo.DupeParentID and Parenting)then v:SetParent(CreatedEntities[EntityList[_].BuildDupeInfo.DupeParentID]) end v:SetNotSolid( v.SolidMod ) undo.AddEntity( v ) end undo.SetPlayer( Player ) undo.Finish() //if(Tool)then AdvDupe2.FinishPasting(Player, true) end else for _,v in pairs( CreatedEntities ) do --If the entity has a PostEntityPaste function tell it to use it now if v.PostEntityPaste then local status, valid = pcall(v.PostEntityPaste, v, Player, v, CreatedEntities) if(not status)then print("AD2 PostEntityPaste Error: "..tostring(valid)) end end v:GetPhysicsObject():EnableMotion(false) if(EntityList[_].BuildDupeInfo.DupeParentID and Parenting)then v:SetParent(CreatedEntities[EntityList[_].BuildDupeInfo.DupeParentID]) end v:SetNotSolid( v.SolidMod ) end end DisablePropCreateEffect = nil hook.Call("AdvDupe_FinishPasting", nil, {{EntityList=EntityList, CreatedEntities=CreatedEntities, ConstraintList=ConstraintList, CreatedConstraints=CreatedConstraints, HitPos=OrigPos or Position, Player=Player}}, 1) return CreatedEntities, CreatedConstraints end local function AdvDupe2_Spawn() local Queue = AdvDupe2.JobManager.Queue[AdvDupe2.JobManager.CurrentPlayer] if(not Queue or not IsValid(Queue.Player))then if Queue then table.remove(AdvDupe2.JobManager.Queue, AdvDupe2.JobManager.CurrentPlayer) end if(#AdvDupe2.JobManager.Queue==0)then hook.Remove("Tick", "AdvDupe2_Spawning") DisablePropCreateEffect = nil AdvDupe2.JobManager.PastingHook = false end return end if(Queue.Entity)then if(Queue.Current==1)then AdvDupe2.InitProgressBar(Queue.Player,"Pasting:") Queue.Player.AdvDupe2.Queued = false end local newpos if(Queue.Current>#Queue.SortedEntities)then Queue.Entity = false Queue.Constraint = true Queue.Current = 1 return end if(not Queue.SortedEntities[Queue.Current])then Queue.Current = Queue.Current+1 return end local k = Queue.SortedEntities[Queue.Current] local v = Queue.EntityList[k] if(not v.BuildDupeInfo)then v.BuildDupeInfo={} end if(v.LocalPos)then for i,p in pairs(v.PhysicsObjects) do v.PhysicsObjects[i] = {Pos=v.LocalPos, Angle=v.LocalAngle} end end v.BuildDupeInfo.PhysicsObjects = table.Copy(v.PhysicsObjects) proppos = v.PhysicsObjects[0].Pos v.BuildDupeInfo.PhysicsObjects[0].Pos = Vector(0,0,0) if( Queue.OrigPos )then for i,p in pairs(v.BuildDupeInfo.PhysicsObjects) do v.PhysicsObjects[i].Pos = p.Pos + proppos + Queue.OrigPos v.PhysicsObjects[i].Frozen = true end v.Pos = v.PhysicsObjects[0].Pos v.Angle = v.PhysicsObjects[0].Angle v.BuildDupeInfo.PosReset = v.Pos v.BuildDupeInfo.AngleReset = v.Angle else for i,p in pairs(v.BuildDupeInfo.PhysicsObjects)do v.PhysicsObjects[i].Pos, v.PhysicsObjects[i].Angle = LocalToWorld(p.Pos + proppos, p.Angle, Queue.PositionOffset, Queue.AngleOffset) v.PhysicsObjects[i].Frozen = true end v.Pos = v.PhysicsObjects[0].Pos v.BuildDupeInfo.PosReset = v.Pos v.Angle = v.PhysicsObjects[0].Angle v.BuildDupeInfo.AngleReset = v.Angle end AdvDupe2.SpawningEntity = true local Ent = CreateEntityFromTable(v, Queue.Player) AdvDupe2.SpawningEntity = false if Ent then local colData = v.EntityMods and v.EntityMods.colour if colData then if colData.Color then Ent:SetColor(colData.Color) end if colData.RenderFX then Ent:SetRenderFX(colData.RenderFX) end if colData.RenderMode then Ent:SetRenderMode(colData.RenderMode) end Ent.APG_oldColor = colData.Color end Queue.Player:AddCleanup( "AdvDupe2", Ent ) Ent.BoneMods = table.Copy( v.BoneMods ) Ent.EntityMods = table.Copy( v.EntityMods ) Ent.PhysicsObjects = table.Copy( v.PhysicsObjects ) Ent.SolidMod = not Ent:IsSolid() local Phys = Ent:GetPhysicsObject() if(IsValid(Phys))then Phys:EnableMotion(false) end if(not Queue.DisableProtection)then Ent:SetNotSolid(true) end if(v.CollisionGroup)then Ent:SetCollisionGroup(v.CollisionGroup) end if(Ent.OnDuplicated)then Ent:OnDuplicated(v) end doSpawnHooks(Queue.Player, Ent) elseif(Ent==false)then Ent = nil else Ent = nil end Queue.CreatedEntities[ k ] = Ent AdvDupe2.UpdateProgressBar(Queue.Player, math.floor((Queue.Percent*Queue.Current)*100)) Queue.Current = Queue.Current+1 if(Queue.Current>#Queue.SortedEntities)then for _,Ent in pairs(Queue.CreatedEntities)do ApplyEntityModifiers( Queue.Player, Ent ) ApplyBoneModifiers( Queue.Player, Ent ) --If the entity has a PostEntityPaste function tell it to use it now if Ent.PostEntityPaste then local status, valid = pcall(Ent.PostEntityPaste, Ent, Queue.Player, Ent, Queue.CreatedEntities) if(not status)then print("AD2 PostEntityPaste Error: "..tostring(valid)) end end end Queue.Entity = false Queue.Constraint = true Queue.Current = 1 end if(#AdvDupe2.JobManager.Queue>=AdvDupe2.JobManager.CurrentPlayer+1)then AdvDupe2.JobManager.CurrentPlayer = AdvDupe2.JobManager.CurrentPlayer+1 else AdvDupe2.JobManager.CurrentPlayer = 1 end else if(#Queue.ConstraintList>0)then if(#AdvDupe2.JobManager.Queue==0)then hook.Remove("Tick", "AdvDupe2_Spawning") DisablePropCreateEffect = nil AdvDupe2.JobManager.PastingHook = false end if(not Queue.ConstraintList[Queue.Current])then Queue.Current = Queue.Current+1 return end local Entity = CreateConstraintFromTable( Queue.ConstraintList[Queue.Current], Queue.CreatedEntities, Queue.EntityList, Queue.Player, true ) if IsValid(Entity) then table.insert( Queue.CreatedConstraints, Entity ) end elseif(table.Count(Queue.ConstraintList)>0)then local tbl = {} for k,v in pairs(Queue.ConstraintList)do table.insert(tbl, v) end Queue.ConstraintList = tbl Queue.Current=0 end AdvDupe2.UpdateProgressBar(Queue.Player, math.floor((Queue.Percent*(Queue.Current+Queue.Plus))*100)) Queue.Current = Queue.Current+1 if(Queue.Current>#Queue.ConstraintList)then local unfreeze = tobool(Queue.Player:GetInfo("advdupe2_paste_unfreeze")) or false local preservefrozenstate = tobool(Queue.Player:GetInfo("advdupe2_preserve_freeze")) or false //Remove the undo for stopping pasting local undos = undo.GetTable()[Queue.Player:UniqueID()] local str = "AdvDupe2_"..Queue.Player:UniqueID() for i=#undos, 1, -1 do if(undos[i] and undos[i].Name == str)then undos[i] = nil -- Undo module netmessage net.Start( "Undo_Undone" ) net.WriteInt( i, 16 ) net.Send( Queue.Player ) break end end undo.Create "AdvDupe2" local phys local edit local mass for _,v in pairs( Queue.CreatedEntities ) do if(not IsValid(v))then v = nil else edit = true if(Queue.EntityList[_].BuildDupeInfo.DupeParentID~=nil and Queue.Parenting)then v:SetParent(Queue.CreatedEntities[Queue.EntityList[_].BuildDupeInfo.DupeParentID]) if(v.Constraints~=nil)then for i,c in pairs(v.Constraints)do if(c and constraints[c.Type])then edit=false break end end end if(edit and IsValid(v:GetPhysicsObject()))then mass = v:GetPhysicsObject():GetMass() v:PhysicsInitShadow(false, false) v:SetCollisionGroup(COLLISION_GROUP_WORLD) v:GetPhysicsObject():EnableMotion(false) v:GetPhysicsObject():Sleep() v:GetPhysicsObject():SetMass(mass) end else edit=false end if(unfreeze)then for i=0, v:GetPhysicsObjectCount() do phys = v:GetPhysicsObjectNum(i) if(IsValid(phys))then phys:EnableMotion(true) //Unfreeze the entitiy and all of its objects phys:Wake() end end elseif(preservefrozenstate)then for i=0, v:GetPhysicsObjectCount() do phys = v:GetPhysicsObjectNum(i) if(IsValid(phys))then if(Queue.EntityList[_].BuildDupeInfo.PhysicsObjects[i].Frozen)then phys:EnableMotion(true) //Restore the entity and all of its objects to their original frozen state phys:Wake() else Queue.Player:AddFrozenPhysicsObject( v, phys ) end end end else for i=0, v:GetPhysicsObjectCount() do phys = v:GetPhysicsObjectNum(i) if(IsValid(phys))then if(phys:IsMoveable())then phys:EnableMotion(false) //Freeze the entitiy and all of its objects Queue.Player:AddFrozenPhysicsObject( v, phys ) end end end end if v:GetClass() == 'prop_dynamic' and not IsValid(v:GetPhysicsObject()) then v:PhysicsInitStatic(SOLID_VPHYSICS) v:Activate() elseif(not edit or not Queue.DisableParents)then v:SetNotSolid(v.SolidMod) end undo.AddEntity( v ) end end undo.SetCustomUndoText("Undone "..(Queue.Name or "Advanced Duplication")) undo.SetPlayer( Queue.Player ) undo.Finish() if Queue.Perma and IsValid(Queue.Player) and Queue.Player:query(L.permissions_permaprops) then permaprops.markPerma(Queue.CreatedEntities) end hook.Call("AdvDupe_FinishPasting", nil, {{EntityList=Queue.EntityList, CreatedEntities=Queue.CreatedEntities, ConstraintList=Queue.ConstraintList, CreatedConstraints=Queue.CreatedConstraints, HitPos=Queue.PositionOffset, Player=Queue.Player}}, 1) AdvDupe2.FinishPasting(Queue.Player, true) table.remove(AdvDupe2.JobManager.Queue, AdvDupe2.JobManager.CurrentPlayer) if(#AdvDupe2.JobManager.Queue==0)then hook.Remove("Tick", "AdvDupe2_Spawning") DisablePropCreateEffect = nil AdvDupe2.JobManager.PastingHook = false end end if(#AdvDupe2.JobManager.Queue>=AdvDupe2.JobManager.CurrentPlayer+1)then AdvDupe2.JobManager.CurrentPlayer = AdvDupe2.JobManager.CurrentPlayer+1 else AdvDupe2.JobManager.CurrentPlayer = 1 end end end local ticktotal = 0 local function ErrorCatchSpawning() ticktotal = ticktotal + AdvDupe2.SpawnRate while ticktotal >= 1 do ticktotal = ticktotal - 1 local status, error = pcall(AdvDupe2_Spawn) if(not status)then //PUT ERROR LOGGING HERE if(not AdvDupe2.JobManager.Queue)then print("[AdvDupe2Notify]\t"..error) AdvDupe2.JobManager.Queue = {} return end local Queue = AdvDupe2.JobManager.Queue[AdvDupe2.JobManager.CurrentPlayer] if(not Queue)then print("[AdvDupe2Notify]\t"..error) return end if(IsValid(Queue.Player))then AdvDupe2.Notify(Queue.Player, error) local undos = undo.GetTable()[Queue.Player:UniqueID()] local str = "AdvDupe2_"..Queue.Player:UniqueID() for i=#undos, 1, -1 do if(undos[i] and undos[i].Name == str)then undos[i] = nil -- Undo module netmessage net.Start( "Undo_Undone" ) net.WriteInt( i, 16 ) net.Send( Queue.Player ) break end end else print("[AdvDupe2Notify]\t"..error) end for k,v in pairs(Queue.CreatedEntities)do if(IsValid(v))then v:Remove() end end if(IsValid(Queue.Player))then AdvDupe2.FinishPasting(Queue.Player, true) end table.remove(AdvDupe2.JobManager.Queue, AdvDupe2.JobManager.CurrentPlayer) if(#AdvDupe2.JobManager.Queue==0)then hook.Remove("Tick", "AdvDupe2_Spawning") DisablePropCreateEffect = nil AdvDupe2.JobManager.PastingHook = false else if(#Queue