Files
wnsrc/lua/advdupe2/sv_clipboard.lua
lifestorm 6a58f406b1 Upload
2024-08-04 23:54:45 +03:00

1626 lines
46 KiB
Lua

--[[
| 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. 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 debugConvar = GetConVar("AdvDupe2_DebugInfo")
local gtSetupTable = {
SERIAL = {
[TYPE_BOOL] = true,
[TYPE_ANGLE] = true,
[TYPE_TABLE] = true,
[TYPE_NUMBER] = true,
[TYPE_VECTOR] = true,
[TYPE_STRING] = true
},
CONSTRAINT = {
Weld = true,
Axis = true,
Rope = true,
Motor = true,
Winch = true,
Muscle = true,
Pulley = true,
Slider = true,
Elastic = true,
Hydraulic = true,
Ballsocket = true
},
COMPARE = {
V1 = Vector(1, 1, 1),
A0 = Angle (0, 0, 0),
V0 = Vector(0, 0, 0)
},
POS = {
pos = true,
Pos = true,
position = true,
Position = true
},
ANG = {
ang = true,
Ang = true,
angle = true,
Angle = true
},
MODEL = {
model = true,
Model = true
},
PLAYER = {
pl = true,
ply = true
},
ENT1 = {
Ent = true,
Ent1 = true,
},
TVEHICLE = {
VehicleTable = true
},
SPECIAL = {
Data = 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 gtSetupTable.SERIAL[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
if debugConvar:GetBool() then
print("[AdvDupe2] ClassArg table with key \"" .. tostring(k) .. "\" has unsupported value of type \"".. type(v) .."\"!")
end
end
end
return newtable
end
return recursiveCopy(tab)
end
--[[
Name: CopyEntTable
Desc: Returns a copy of the passed entity's table
Params: <entity> Ent
Returns: <table> enttable
]]
--[[---------------------------------------------------------
Returns a copy of the passed entity's table
---------------------------------------------------------]]
function AdvDupe2.duplicator.IsCopyable(Ent)
return not Ent.DoNotDuplicate and duplicator.IsAllowed(Ent:GetClass()) and IsValid(Ent:GetPhysicsObject())
end
local function CopyEntTable(Ent, Offset)
-- Filter duplicator blocked entities out.
if not AdvDupe2.duplicator.IsCopyable(Ent) 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
if gtSetupTable.SPECIAL[Key] then
Tab = CopyClassArgTable(EntTable)
end
-- Ignore keys from old system
if (not gtSetupTable.POS[Key] and
not gtSetupTable.ANG[Key] and
not gtSetupTable.MODEL[Key]) then
local varType = TypeID(EntTable[Key])
if gtSetupTable.SERIAL[varType] then
if varType == TYPE_TABLE then
Tab[Key] = CopyClassArgTable(EntTable[Key])
else
Tab[Key] = EntTable[Key]
end
elseif varType ~= TYPE_NIL and debugConvar:GetBool() 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(next(Tab.BodyG) == nil)then
Tab.BodyG = nil
end
-- Bone Manipulator
if (Ent:HasBoneManipulations()) then
Tab.BoneManip = {}
local c = gtSetupTable.COMPARE
for i = 0, Ent:GetBoneCount() do
local s = Ent:GetManipulateBoneScale(i)
s = ((s ~= c.V1) and s or nil)
local a = Ent:GetManipulateBoneAngles(i)
a = ((a ~= c.A0) and a or nil)
local p = Ent:GetManipulateBonePosition(i)
p = ((p ~= c.V0) and p or nil)
-- Avoid making a vector just to compare it
if (s or a or p) then
Tab.BoneManip[i] = {s = s, a = a, p = p}
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: <table> Constraints
Returns: <table> Constraints, <table> 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
for i = 1, 4 do
local ent = "Ent" .. i
local lpos = "LPos" .. i
local wpos = "WPos" .. 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]) then
if (i ~= 4 and i ~= 2) then
if (Const["Ent2"]) then
Constraint.Entity[i].LPos = Const[lpos] - Const["Ent2"]:GetPos()
Constraint[lpos] = Const[lpos] - Const["Ent2"]:GetPos()
elseif (Const["Ent4"]) then
Constraint.Entity[i].LPos = Const[lpos] - Const["Ent4"]:GetPos()
Constraint[lpos] = Const[lpos] - Const["Ent4"]:GetPos()
end
elseif (Const["Ent1"]) then
Constraint.Entity[i].LPos = Const[lpos] - Const["Ent1"]:GetPos()
Constraint[lpos] = Const[lpos] - Const["Ent1"]:GetPos()
end
else
Constraint.Entity[i].LPos = Offset
Constraint[lpos] = Offset
end
else
Constraint.Entity[i].LPos = Const[lpos]
Constraint.Entity[i].WPos = Const[wpos]
end
if (not Const[ent]:IsWorld()) then table.insert(Entities, Const[ent]) end
end
if (Const[wpos]) then
if (not Const["Ent1"]:IsWorld()) then
Constraint[wpos] = Const[wpos] - Const["Ent1"]:GetPos()
else
Constraint[wpos] = Const[wpos] - 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> Entity
Returns: <table> Entities, <table> Constraints
]]
local function Copy(ply, Ent, EntTable, ConstraintTable, Offset)
local function RecursiveCopy(Ent)
local index = Ent:EntIndex()
if EntTable[index] then return end
local cantool = Ent.CPPICanTool
if cantool and not cantool(Ent, ply, "advdupe2") then return end
local EntData = CopyEntTable(Ent, Offset)
if EntData == nil then return 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
RecursiveCopy(e)
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
RecursiveCopy(v.Src)
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
RecursiveCopy(v.Entity)
end
end
end
end
end
end
do -- Parented stuff
local parent = Ent:GetParent()
if IsValid(parent) then RecursiveCopy(parent) end
for k, child in pairs(Ent:GetChildren()) do
RecursiveCopy(child)
end
end
for k, v in pairs(EntData.PhysicsObjects) do
Ent:GetPhysicsObjectNum(k):EnableMotion(v.Frozen)
end
end
RecursiveCopy(Ent)
return EntTable, ConstraintTable
end
AdvDupe2.duplicator.Copy = Copy
--[[
Name: AreaCopy
Desc: Copy based on a box
Returns: <table> Entities, <table> Constraints
]]
function AdvDupe2.duplicator.AreaCopy(ply, 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 not v.DoNotDuplicate then
index = v:GetCreationID()
if (index and not Constraints[index]) then
Constraints[index] = v
end
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(ply, v, EntTable, ConstraintTable, Offset)
end
end
end
return EntTable, ConstraintTable
end
--[[
Name: CreateConstraintFromTable
Desc: Creates a constraint from a given table
Params: <table>Constraint, <table> EntityList, <table> EntityTable
Returns: <entity> CreatedConstraint
]]
local function CreateConstraintFromTable(Constraint, EntityList, EntityTable, Player, DontEnable)
local Factory = duplicator.ConstraintType[Constraint.Type]
if not Factory then return end
local first, firstindex -- Ent1 or Ent in the constraint's table
local second, secondindex -- Any other Ent that is not Ent1 or Ent
local Args = {} -- Build the argument list for the Constraint's spawn function
for k, Key in ipairs(Factory.Args) do
local Val = Constraint[Key]
if gtSetupTable.PLAYER[Key] 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 (gtSetupTable.ENT1[Key]) 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[Key] + EntityList[Constraint.Entity[1].Index]:GetPos()
else
Val = Constraint[Key] + EntityList[Constraint.Entity[4].Index]:GetPos()
end
end
end
Args[k] = Val
end
local Bone1, Bone1Index, ReEnableFirst
local Bone2, Bone2Index, ReEnableSecond
local buildInfo = Constraint.BuildDupeInfo
if (buildInfo) then
if first ~= nil and second ~= nil and not second:IsWorld() and buildInfo.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() - buildInfo.EntityPos)
if (buildInfo.Bone2) then
Bone2Index = buildInfo.Bone2
Bone2 = second:GetPhysicsObjectNum(Bone2Index)
if IsValid(Bone2) then
Bone2:EnableMotion(false)
Bone2:SetPos(second:GetPos() + buildInfo.Bone2Pos)
Bone2:SetAngles(buildInfo.Bone2Angle)
end
end
end
end
if first ~= nil and not first:IsWorld() and buildInfo.Ent1Ang ~= nil then
local FirstPhys = first:GetPhysicsObject()
if IsValid(FirstPhys) then
if not DontEnable then ReEnableFirst = FirstPhys:IsMoveable() end
FirstPhys:EnableMotion(false)
first:SetAngles(buildInfo.Ent1Ang)
if (buildInfo.Bone1) then
Bone1Index = buildInfo.Bone1
Bone1 = first:GetPhysicsObjectNum(Bone1Index)
if IsValid(Bone1) then
Bone1:EnableMotion(false)
Bone1:SetPos(first:GetPos() + buildInfo.Bone1Pos)
Bone1:SetAngles(buildInfo.Bone1Angle)
end
end
end
end
if second ~= nil and not second:IsWorld() then
if buildInfo.Ent2Ang ~= nil then
second:SetAngles(buildInfo.Ent2Ang)
elseif buildInfo.Ent4Ang ~= nil then
second:SetAngles(buildInfo.Ent4Ang)
end
end
end
local ok, Ent = pcall(Factory.Func, unpack(Args, 1, #Factory.Args))
if not ok 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(buildInfo)
-- Move the entities back after constraining them. No point in moving the world though.
if (EntityTable) then
local fEnt = EntityTable[firstindex]
local sEnt = EntityTable[secondindex]
if (first ~= nil and not first:IsWorld()) then
first:SetPos(fEnt.BuildDupeInfo.PosReset)
first:SetAngles(fEnt.BuildDupeInfo.AngleReset)
if (IsValid(Bone1) and Bone1Index ~= 0) then
Bone1:SetPos(fEnt.BuildDupeInfo.PosReset +
fEnt.BuildDupeInfo.PhysicsObjects[Bone1Index].Pos)
Bone1:SetAngles(fEnt.PhysicsObjects[Bone1Index].Angle)
end
local FirstPhys = first:GetPhysicsObject()
if IsValid(FirstPhys) then
if ReEnableFirst then
FirstPhys:EnableMotion(true)
end
end
end
if (second ~= nil and not second:IsWorld()) then
second:SetPos(sEnt.BuildDupeInfo.PosReset)
second:SetAngles(sEnt.BuildDupeInfo.AngleReset)
if (IsValid(Bone2) and Bone2Index ~= 0) then
Bone2:SetPos(sEnt.BuildDupeInfo.PosReset +
sEnt.BuildDupeInfo.PhysicsObjects[Bone2Index].Pos)
Bone2:SetAngles(sEnt.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
for Type, Data in SortedPairs(Ent.EntityMods) do
local ModFunction = duplicator.EntityModifiers[Type]
if (ModFunction) then
local ok, err = pcall(ModFunction, Player, Ent, Data)
if (not ok) then
if (Player) then
Player:ChatPrint('Error applying entity modifer, "' .. tostring(Type) .. '". ERROR: ' .. err)
else
print('Error applying entity modifer, "' .. tostring(Type) .. '". ERROR: ' .. err)
end
end
end
end
if (Ent.EntityMods["mass"] and duplicator.EntityModifiers["mass"]) then
local ok, err = pcall(duplicator.EntityModifiers["mass"], Player, Ent, Ent.EntityMods["mass"])
if (not ok) then
if (Player) then
Player:ChatPrint('Error applying entity modifer, "mass". ERROR: ' .. err)
else
print('Error applying entity modifer, "' .. tostring(Type) .. '". ERROR: ' .. err)
end
end
end
if(Ent.EntityMods["buoyancy"] and duplicator.EntityModifiers["buoyancy"]) then
local ok, err = pcall(duplicator.EntityModifiers["buoyancy"], Player, Ent, Ent.EntityMods["buoyancy"])
if (not ok) then
if (Player) then
Player:ChatPrint('Error applying entity modifer, "buoyancy". ERROR: ' .. err)
else
print('Error applying entity modifer, "' .. tostring(Type) .. '". ERROR: ' .. err)
end
end
end
end
local function ApplyBoneModifiers(Player, Ent)
if (not Ent.BoneMods or not Ent.PhysicsObjects) then return end
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
local PhysObj = Ent:GetPhysicsObjectNum(Bone)
if (Ent.PhysicsObjects[Bone]) then
local ok, err = pcall(ModFunction, Player, Ent, Bone, PhysObj, Ent.BoneMods[Bone][Type])
if (not ok) then
Player:ChatPrint('Error applying bone modifer, "' .. tostring(Type) .. '". ERROR: ' .. err)
end
end
end
end
end
end
--[[
Name: DoGenericPhysics
Desc: Applies bone data, generically.
Params: <player> Player, <table> data
Returns: <entity> Entity, <table> 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)
net.Start("AdvDupe2_ReportClass")
net.WriteString(class)
net.Send(ply)
end
local function reportmodel(ply, model)
net.Start("AdvDupe2_ReportModel")
net.WriteString(model)
net.Send(ply)
end
--[[
Name: GenericDuplicatorFunction
Desc: Override the default duplicator's GenericDuplicatorFunction function
Params: <table> data, <player> Player
Returns: <entity> 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)
if (Player) then Entity:SetCreator(Player) end
Entity:Spawn()
Entity:Activate()
DoGenericPhysics(Entity, data, Player)
table.Add(Entity:GetTable(), data)
return Entity
end
--[[
Name: MakeProp
Desc: Make prop without spawn effects
Params: <player> Player, <vector> Pos, <angle> Ang, <string> Model, <table> PhysicsObject, <table> Data
Returns: <entity> Prop
]]
local function MakeProp(Player, Pos, Ang, Model, PhysicsObject, Data)
if Data.ModelScale then Data.ModelScale = math.Clamp(Data.ModelScale, 1e-5, 1e5) end
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)
if (Player) then Prop:SetCreator(Player) end
Prop:Spawn()
Prop:Activate()
DoGenericPhysics(Prop, Data, Player)
if (Data.Flex) then
duplicator.DoFlex(Prop, Data.Flex, Data.FlexScale)
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: <table> EntTable, <player> Player
Returns: nil
]]
local function IsAllowed(Player, Class, EntityClass)
if (scripted_ents.GetMember(Class, "DoNotDuplicate")) then return false end
if (IsValid(Player) and not Player:IsAdmin()) then
if not duplicator.IsAllowed(Class) then return false end
if (not 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)
hook.Run("AdvDupe2_PreCreateEntity", EntTable, Player)
local EntityClass = duplicator.FindEntityClass(EntTable.Class)
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
local CreatedEntities = {}
-- 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 Player then
if(EntTable.Class=="prop_effect")then
sent = gamemode.Call( "PlayerSpawnEffect", Player, EntTable.Model)
else
sent = gamemode.Call( "PlayerSpawnSENT", Player, EntTable.Class)
end
else
sent = true
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, Arg = {}
for iNumber, Key in pairs(EntityClass.Args) do
Arg = nil
-- Translate keys from old system
if (gtSetupTable.POS[Key]) then Key = "Pos" end
if (gtSetupTable.ANG[Key]) then Key = "Angle" end
if (gtSetupTable.MODEL[Key]) then Key = "Model" end
if (gtSetupTable.TVEHICLE[Key] and EntTable[Key] and EntTable[Key].KeyValues) then
EntTable[Key].KeyValues = {
limitview = EntTable[Key].KeyValues.limitview,
vehiclescript = EntTable[Key].KeyValues.vehiclescript
}
end
Arg = EntTable[Key]
-- Special keys
if (gtSetupTable.SPECIAL[Key]) 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 IsAllowed(Player, EntTable.Class, EntityClass) then
-- Create sents using their spawn function with the arguments we stored earlier
sent = true
if Player then
if (not EntTable.BuildDupeInfo.IsVehicle and not EntTable.BuildDupeInfo.IsNPC and EntTable.Class ~= "prop_ragdoll" and EntTable.Class ~= "prop_effect") then
sent = hook.Call("PlayerSpawnSENT", nil, Player, EntTable.Class)
end
else
sent = true
end
if (sent == false) then
print("Advanced Duplicator 2: Creation rejected for class, : " .. EntTable.Class)
return nil
else
sent = true
end
hook.Add( "OnEntityCreated", "AdvDupe2_GetLastEntitiesCreated", function( ent )
table.insert( CreatedEntities, ent )
end )
status, valid = xpcall(EntityClass.Func, ErrorNoHaltWithStack, Player, unpack(ArgList, 1, #EntityClass.Args))
hook.Remove( "OnEntityCreated", "AdvDupe2_GetLastEntitiesCreated" )
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
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 and Player 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 (status == false) then
print("Advanced Duplicator 2: Error creating entity, removing last created entities")
for _, CreatedEntity in pairs(CreatedEntities) do
SafeRemoveEntity(CreatedEntity)
end
end
if (valid == false) then
return false
else
return nil
end
end
end
--[[
Name: Paste
Desc: Override the default duplicator's paste function
Params: <player> Player, <table> Entities, <table> Constraints
Returns: <table> Entities, <table> 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)
elseif (Ent == false) then
Ent = nil
-- ConstraintList = {}
-- break
else
Ent = nil
end
CreatedEntities[k] = Ent
end
local CreatedConstraints, 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")
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
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 Queue.Revision < 1 and v.LocalPos then
for i, _ in pairs(v.PhysicsObjects) do
v.PhysicsObjects[i] = {Pos = v.LocalPos, Angle = v.LocalAngle}
end
end
v.BuildDupeInfo.PhysicsObjects = table.Copy(v.PhysicsObjects)
local 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
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
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 (next(Queue.ConstraintList) ~= nil) 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 undotxt = Queue.Name and ("AdvDupe2 ("..Queue.Name..")") or "AdvDupe2"
local undos = undo.GetTable()[Queue.Player:UniqueID()]
for i = #undos, 1, -1 do
if (undos[i] and undos[i].Name == undotxt) then
undos[i] = nil
-- Undo module netmessage
net.Start("Undo_Undone")
net.WriteInt(i, 16)
net.Send(Queue.Player)
break
end
end
undo.Create(undotxt)
local phys, edit, mass
for k, v in pairs(Queue.CreatedEntities) do
if (not IsValid(v)) then
v = nil
else
edit = true
if (Queue.EntityList[k].BuildDupeInfo.DupeParentID ~= nil and Queue.Parenting) then
v:SetParent(Queue.CreatedEntities[Queue.EntityList[k].BuildDupeInfo.DupeParentID])
if (v.Constraints ~= nil) then
for i, c in pairs(v.Constraints) do
if (c and gtSetupTable.CONSTRAINT[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[k].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 (not edit or not Queue.DisableParents) then
v:SetNotSolid(v.SolidMod)
end
undo.AddEntity(v)
end
end
undo.SetPlayer(Queue.Player)
undo.Finish(undotxt)
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 + math.max(GetConVarNumber("AdvDupe2_SpawnRate"), 0.01)
while ticktotal >= 1 do
ticktotal = ticktotal - 1
local status, err = pcall(AdvDupe2_Spawn)
if (not status) then
-- PUT ERROR LOGGING HERE
if (not AdvDupe2.JobManager.Queue) then
print("[AdvDupe2Notify]\t" .. err)
AdvDupe2.JobManager.Queue = {}
return
end
local Queue = AdvDupe2.JobManager.Queue[AdvDupe2.JobManager.CurrentPlayer]
if (not Queue) then
print("[AdvDupe2Notify]\t" .. err)
return
end
if (IsValid(Queue.Player)) then
AdvDupe2.Notify(Queue.Player, err)
local undos = undo.GetTable()[Queue.Player:UniqueID()]
local undotxt = Queue.Name and ("AdvDupe2 ("..Queue.Name..")") or "AdvDupe2"
for i = #undos, 1, -1 do
if (undos[i] and undos[i].Name == undotxt) 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" .. err)
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 < AdvDupe2.JobManager.CurrentPlayer) then
AdvDupe2.JobManager.CurrentPlayer = 1
end
end
end
end
end
local function RemoveSpawnedEntities(tbl, i)
if (not AdvDupe2.JobManager.Queue[i]) then return end -- Without this some errors come up, double check the errors without this line
for k, v in pairs(AdvDupe2.JobManager.Queue[i].CreatedEntities) do
if (IsValid(v)) then v:Remove() end
end
AdvDupe2.FinishPasting(AdvDupe2.JobManager.Queue[i].Player, false)
table.remove(AdvDupe2.JobManager.Queue, i)
if (#AdvDupe2.JobManager.Queue == 0) then
hook.Remove("Tick", "AdvDupe2_Spawning")
DisablePropCreateEffect = nil
AdvDupe2.JobManager.PastingHook = false
end
end
function AdvDupe2.InitPastingQueue(Player, PositionOffset, AngleOffset, OrigPos, Constrs, Parenting, DisableParents, DisableProtection)
local i = #AdvDupe2.JobManager.Queue + 1
local Queue = {
Player = Player,
SortedEntities = {},
EntityList = table.Copy(Player.AdvDupe2.Entities),
Current = 1,
Name = Player.AdvDupe2.Name,
Entity = true,
Constraint = false,
Parenting = Parenting,
DisableParents = DisableParents,
DisableProtection = DisableProtection,
CreatedEntities = {},
CreatedConstraints = {},
PositionOffset = PositionOffset or Vector(0, 0, 0),
AngleOffset = AngleOffset or Angle(0, 0, 0),
Revision = Player.AdvDupe2.Revision,
}
AdvDupe2.JobManager.Queue[i] = Queue
if (Constrs) then
Queue.ConstraintList = table.Copy(Player.AdvDupe2.Constraints)
else
Queue.ConstraintList = {}
end
Queue.OrigPos = OrigPos
for k, v in pairs(Player.AdvDupe2.Entities) do
table.insert(Queue.SortedEntities, k)
end
if (Player.AdvDupe2.Name) then
print(
"[AdvDupe2NotifyPaste]\t Player: " .. Player:Nick() .. " Pasted File, " .. Player.AdvDupe2.Name .. " with, " ..
#Queue.SortedEntities .. " Entities and " .. #Player.AdvDupe2.Constraints .. " Constraints.")
else
print("[AdvDupe2NotifyPaste]\t Player: " .. Player:Nick() .. " Pasted, " .. #Queue.SortedEntities ..
" Entities and " .. #Player.AdvDupe2.Constraints .. " Constraints.")
end
Queue.Plus = #Queue.SortedEntities
Queue.Percent = 1 / (#Queue.SortedEntities + #Queue.ConstraintList)
AdvDupe2.InitProgressBar(Player, "Queued:")
Player.AdvDupe2.Queued = true
if (not AdvDupe2.JobManager.PastingHook) then
DisablePropCreateEffect = true
hook.Add("Tick", "AdvDupe2_Spawning", ErrorCatchSpawning)
AdvDupe2.JobManager.PastingHook = true
AdvDupe2.JobManager.CurrentPlayer = 1
end
local undotxt = Player.AdvDupe2.Name and ("AdvDupe2 ("..Player.AdvDupe2.Name..")") or "AdvDupe2"
undo.Create(undotxt)
undo.SetPlayer(Player)
undo.AddFunction(RemoveSpawnedEntities, i)
undo.Finish(undotxt)
end