Files
wnsrc/lua/includes/modules/duplicator.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

1035 lines
25 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/
--]]
--[[---------------------------------------------------------
Duplicator module,
to add new constraints or entity classes use...
duplicator.RegisterConstraint( "name", funct, ... )
duplicator.RegisterEntityClass( "class", funct, ... )
-----------------------------------------------------------]]
module( "duplicator", package.seeall )
--
-- When saving or loading all coordinates are saved relative to these
--
local LocalPos = Vector( 0, 0, 0 )
local LocalAng = Angle( 0, 0, 0 )
--
-- Should be set to the player that is creating/copying stuff. Can be nil.
--
local ActionPlayer = nil
--
-- The physics object Saver/Loader
--
local PhysicsObject =
{
Save = function( data, phys )
data.Pos = phys:GetPos()
data.Angle = phys:GetAngles()
data.Frozen = !phys:IsMoveable()
if ( !phys:IsGravityEnabled() ) then data.NoGrav = true end
if ( phys:IsAsleep() ) then data.Sleep = true end
data.Pos, data.Angle = WorldToLocal( data.Pos, data.Angle, LocalPos, LocalAng )
end,
Load = function( data, phys )
if ( isvector( data.Pos ) and isangle( data.Angle ) ) then
local pos, ang = LocalToWorld( data.Pos, data.Angle, LocalPos, LocalAng )
phys:SetPos( pos )
phys:SetAngles( ang )
end
-- Let's not Wake or put anything to sleep for now
--[[
if ( data.Sleep ) then
if ( IsValid( phys ) ) then phys:Sleep() end
else
phys:Wake()
end
]]
if ( data.Frozen ) then
phys:EnableMotion( false )
-- If we're being created by a player then add these to their frozen list so they can unfreeze them all
if ( IsValid( ActionPlayer ) ) then
ActionPlayer:AddFrozenPhysicsObject( phys:GetEntity(), phys )
end
end
if ( data.NoGrav ) then phys:EnableGravity( false ) end
end,
}
--
-- Entity physics saver
--
local EntityPhysics =
{
--
-- Loop each bone, calling PhysicsObject.Save
--
Save = function( data, Entity )
local num = Entity:GetPhysicsObjectCount()
for objectid = 0, num-1 do
local obj = Entity:GetPhysicsObjectNum( objectid )
if ( !IsValid( obj ) ) then continue end
data[ objectid ] = {}
PhysicsObject.Save( data[ objectid ], obj )
end
end,
--
-- Loop each bone, calling PhysicsObject.Load
--
Load = function( data, Entity )
if ( !istable( data ) ) then return end
for objectid, objectdata in pairs( data ) do
local Phys = Entity:GetPhysicsObjectNum( objectid )
if ( !IsValid( Phys ) ) then continue end
PhysicsObject.Load( objectdata, Phys )
end
end,
}
--
-- Entity saver
--
local EntitySaver =
{
--
-- Called on each entity when saving
--
Save = function( data, ent )
--
-- Merge the entities actual table with the table we're saving
-- this is terrible behaviour - but it's what we've always done.
--
if ( ent.PreEntityCopy ) then ent:PreEntityCopy() end
table.Merge( data, ent:GetTable() )
if ( ent.PostEntityCopy ) then ent:PostEntityCopy() end
--
-- Set so me generic variables that pretty much all entities
-- would like to save.
--
data.Pos = ent:GetPos()
data.Angle = ent:GetAngles()
data.Class = ent:GetClass()
data.Model = ent:GetModel()
data.Skin = ent:GetSkin()
data.Mins, data.Maxs = ent:GetCollisionBounds()
data.ColGroup = ent:GetCollisionGroup()
data.Name = ent:GetName()
data.WorkshopID = ent:GetWorkshopID()
data.CurHealth = ent:Health()
data.MaxHealth = ent:GetMaxHealth()
data.Persistent = ent:GetPersistent()
data.Pos, data.Angle = WorldToLocal( data.Pos, data.Angle, LocalPos, LocalAng )
data.ModelScale = ent:GetModelScale()
if ( data.ModelScale == 1 ) then data.ModelScale = nil end
-- This is useful for addons to determine if the entity was map spawned or not
if ( ent:CreatedByMap() ) then
data.MapCreationID = ent:MapCreationID()
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)
if ( ent.ClassOverride ) then data.Class = ent.ClassOverride end
-- Save the physics
data.PhysicsObjects = data.PhysicsObjects or {}
EntityPhysics.Save( data.PhysicsObjects, ent )
-- Flexes
data.FlexScale = ent:GetFlexScale()
for i = 0, ent:GetFlexNum() do
local w = ent:GetFlexWeight( i )
if ( w != 0 ) then
data.Flex = data.Flex or {}
data.Flex[ i ] = w
end
end
-- Body Groups
local bg = ent:GetBodyGroups()
if ( bg ) then
for k, v in pairs( bg ) do
--
-- If it has a non default setting, save it.
--
if ( ent:GetBodygroup( v.id ) > 0 ) then
data.BodyG = data.BodyG or {}
data.BodyG[ v.id ] = ent:GetBodygroup( v.id )
end
end
end
-- Non Sandbox tool set color and materials
if ( ent:GetColor() != color_white ) then data._DuplicatedColor = ent:GetColor() end
if ( ent:GetMaterial() != "" ) then data._DuplicatedMaterial = ent:GetMaterial() end
-- Sub materials
local subMaterials = {}
for i = 0, 31 do
local mat = ent:GetSubMaterial( i )
if ( mat:len() > 0 ) then
subMaterials[ i ] = mat
end
end
if ( !table.IsEmpty( subMaterials ) ) then data._DuplicatedSubMaterials = subMaterials end
-- Bone Manipulations
if ( ent:HasBoneManipulations() ) then
data.BoneManip = {}
for i = 0, ent:GetBoneCount() do
local t = {}
local s = ent:GetManipulateBoneScale( i )
local a = ent:GetManipulateBoneAngles( i )
local p = ent:GetManipulateBonePosition( i )
if ( s != Vector( 1, 1, 1 ) ) then t[ 's' ] = s end -- scale
if ( a != angle_zero ) then t[ 'a' ] = a end -- angle
if ( p != vector_origin ) then t[ 'p' ] = p end -- position
if ( !table.IsEmpty( t ) ) then
data.BoneManip[ i ] = t
end
end
end
--
-- Store networks vars/DT vars (assigned using SetupDataTables)
--
if ( ent.GetNetworkVars ) then
data.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
ent:OnEntityCopyTableFinish( data )
end
--
-- Exclude this crap
--
for k, v in pairs( data ) do
if ( isfunction( v ) ) then
data[k] = nil
end
end
data.OnDieFunctions = nil
data.AutomaticFrameAdvance = nil
data.BaseClass = nil
end,
--
-- Fill in the data!
--
Load = function( data, ent )
if ( !data ) then return end
-- We do the second check for models because apparently setting the model on an NPC causes some position changes
-- And to prevent NPCs going into T-pose briefly upon duplicating
if ( data.Model and data.Model != ent:GetModel() ) then ent:SetModel( data.Model ) end
if ( data.Angle ) then ent:SetAngles( data.Angle ) end
if ( data.Pos ) then ent:SetPos( data.Pos ) end
if ( data.Skin ) then ent:SetSkin( data.Skin ) end
if ( data.Flex ) then DoFlex( ent, data.Flex, data.FlexScale ) end
if ( data.BoneManip ) then DoBoneManipulator( ent, data.BoneManip ) end
if ( data.ModelScale ) then ent:SetModelScale( data.ModelScale, 0 ) end
if ( data.ColGroup ) then ent:SetCollisionGroup( data.ColGroup ) end
if ( data.Name ) then ent:SetName( data.Name ) end
if ( data.Persistent ) then ent:SetPersistent( data.Persistent ) end
if ( data._DuplicatedColor ) then ent:SetColor( data._DuplicatedColor ) end
if ( data._DuplicatedMaterial ) then ent:SetMaterial( data._DuplicatedMaterial ) end
-- Sub materials
if ( data._DuplicatedSubMaterials ) then
for id, mat in pairs( data._DuplicatedSubMaterials ) do
ent:SetSubMaterial( id, mat )
end
end
-- Body Groups
if ( data.BodyG ) then
for k, v in pairs( data.BodyG ) do
ent:SetBodygroup( k, v )
end
end
--
-- Restore NetworkVars/DataTable variables (the SetupDataTables values)
--
if ( ent.RestoreNetworkVars ) then
ent:RestoreNetworkVars( data.DT )
end
end,
}
local DuplicateAllowed = {}
--
-- Allow this entity to be duplicated
--
function Allow( classname )
DuplicateAllowed[ classname ] = true
end
--
-- Disallow this entity to be duplicated
--
function Disallow( classname )
DuplicateAllowed[ classname ] = false
end
--
-- Returns true if we can copy/paste this entity
--
function IsAllowed( classname )
return DuplicateAllowed[ classname ]
end
ConstraintType = ConstraintType or {}
--
-- When a copy is copied it will be translated according to these
-- If you set them - make sure to set them back to 0 0 0!
--
function SetLocalPos( v ) LocalPos = v * 1 end
function SetLocalAng( v ) LocalAng = v * 1 end
--[[---------------------------------------------------------
Register a constraint to be duplicated
-----------------------------------------------------------]]
function RegisterConstraint( _name_, _function_, ... )
ConstraintType[ _name_ ] = {}
ConstraintType[ _name_ ].Func = _function_
ConstraintType[ _name_ ].Args = { ... }
end
EntityClasses = EntityClasses or {}
--[[---------------------------------------------------------
Register an entity's class, to allow it to be duplicated
-----------------------------------------------------------]]
function RegisterEntityClass( _name_, _function_, ... )
EntityClasses[ _name_ ] = {}
EntityClasses[ _name_ ].Func = _function_
EntityClasses[ _name_ ].Args = {...}
Allow( _name_ )
end
--[[---------------------------------------------------------
Returns an entity class factory
-----------------------------------------------------------]]
function FindEntityClass( _name_ )
if ( !_name_ ) then return end
return EntityClasses[ _name_ ]
end
BoneModifiers = BoneModifiers or {}
EntityModifiers = EntityModifiers or {}
function RegisterBoneModifier( _name_, _function_ ) BoneModifiers[ _name_ ] = _function_ end
function RegisterEntityModifier( _name_, _function_ ) EntityModifiers[ _name_ ] = _function_ end
--
-- Try to work out which workshop addons are used by this dupe. This is far from perfect.
--
function FigureOutRequiredAddons( Dupe )
local addons = {}
for _, ent in pairs( Dupe.Entities ) do
for id, addon in pairs( engine.GetAddons() ) do
-- Model
if ( ent.Model and file.Exists( ent.Model, addon.title ) ) then
addons[ addon.wsid ] = true
end
-- Material override
if ( ent._DuplicatedMaterial and file.Exists( "materials/" .. ent._DuplicatedMaterial .. ".vmt", addon.title ) ) then
addons[ addon.wsid ] = true
end
end
end
Dupe.RequiredAddons = table.GetKeys( addons )
end
if ( CLIENT ) then return end
--[[---------------------------------------------------------
Restore's the flex data
-----------------------------------------------------------]]
function DoFlex( ent, Flex, Scale )
if ( !Flex ) then return end
if ( !IsValid( ent ) ) then return end
for k, v in pairs( Flex ) do
ent:SetFlexWeight( k, v )
end
if ( Scale ) then
ent:SetFlexScale( Scale )
end
end
--[[---------------------------------------------------------
Restore's the bone's data
-----------------------------------------------------------]]
function DoBoneManipulator( ent, Bones )
if ( !Bones ) then return end
if ( !IsValid( ent ) ) then return end
for k, v in pairs( Bones ) do
if ( v.s ) then ent:ManipulateBoneScale( k, v.s ) end
if ( v.a ) then ent:ManipulateBoneAngles( k, v.a ) end
if ( v.p ) then ent:ManipulateBonePosition( k, v.p ) end
end
end
--[[---------------------------------------------------------
Generic function for duplicating stuff
-----------------------------------------------------------]]
function GenericDuplicatorFunction( Player, data )
if ( !IsAllowed( data.Class ) ) then
-- MsgN( "duplicator: ", data.Class, " isn't allowed to be duplicated!" )
return
end
--
-- Is this entity 'admin only'?
--
if ( IsValid( Player ) and !Player:IsAdmin() ) then
if ( !scripted_ents.GetMember( data.Class, "Spawnable" ) ) then return end
if ( scripted_ents.GetMember( data.Class, "AdminOnly" ) ) then return end
end
local Entity = ents.Create( data.Class )
if ( !IsValid( Entity ) ) then return end
-- TODO: Entity not found - maybe spawn a prop_physics with their model?
DoGeneric( Entity, data )
Entity:Spawn()
Entity:Activate()
EntityPhysics.Load( data.PhysicsObjects, Entity )
table.Merge( Entity:GetTable(), data )
return Entity
end
--[[---------------------------------------------------------
Automates the process of adding crap the EntityMods table
-----------------------------------------------------------]]
function StoreEntityModifier( Entity, Type, Data )
if ( !IsValid( Entity ) ) then return end
Entity.EntityMods = Entity.EntityMods or {}
-- Copy the data
local NewData = Entity.EntityMods[ Type ] or {}
table.Merge( NewData, Data )
Entity.EntityMods[ Type ] = NewData
end
--[[---------------------------------------------------------
Clear entity modification
-----------------------------------------------------------]]
function ClearEntityModifier( Entity, Type )
if ( !IsValid( Entity ) ) then return end
Entity.EntityMods = Entity.EntityMods or {}
Entity.EntityMods[ Type ] = nil
end
--[[---------------------------------------------------------
Automates the process of adding crap the BoneMods table
-----------------------------------------------------------]]
function StoreBoneModifier( Entity, BoneID, Type, Data )
if ( !IsValid( Entity ) ) then return end
-- Copy the data
NewData = {}
table.Merge( NewData, Data )
-- Add it to the entity
Entity.BoneMods = Entity.BoneMods or {}
Entity.BoneMods[ BoneID ] = Entity.BoneMods[ BoneID ] or {}
Entity.BoneMods[ BoneID ][ Type ] = NewData
end
--[[---------------------------------------------------------
Returns a copy of the passed entity's table
-----------------------------------------------------------]]
function CopyEntTable( Ent )
local output = {}
EntitySaver.Save( output, Ent )
return output
end
--
-- Work out the AABB size
--
function WorkoutSize( Ents )
local mins = Vector( -1, -1, -1 )
local maxs = Vector( 1, 1, 1 )
for k, v in pairs( Ents ) do
if ( !v.Mins or !v.Maxs ) then continue end
if ( !v.Angle or !v.Pos ) then continue end
--
-- Rotate according to the entity!
--
local mi = v.Mins
local ma = v.Maxs
-- There has to be a better way
local t1 = LocalToWorld( Vector( mi.x, mi.y, mi.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
local t2 = LocalToWorld( Vector( ma.x, mi.y, mi.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
local t3 = LocalToWorld( Vector( mi.x, ma.y, mi.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
local t4 = LocalToWorld( Vector( ma.x, ma.y, mi.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
local b1 = LocalToWorld( Vector( mi.x, mi.y, ma.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
local b2 = LocalToWorld( Vector( ma.x, mi.y, ma.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
local b3 = LocalToWorld( Vector( mi.x, ma.y, ma.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
local b4 = LocalToWorld( Vector( ma.x, ma.y, ma.z ), Angle( 0, 0, 0 ), v.Pos, v.Angle )
mins.x = math.min( mins.x, t1.x, t2.x, t3.x, t4.x, b1.x, b2.x, b3.x, b4.x )
mins.y = math.min( mins.y, t1.y, t2.y, t3.y, t4.y, b1.y, b2.y, b3.y, b4.y )
mins.z = math.min( mins.z, t1.z, t2.z, t3.z, t4.z, b1.z, b2.z, b3.z, b4.z )
maxs.x = math.max( maxs.x, t1.x, t2.x, t3.x, t4.x, b1.x, b2.x, b3.x, b4.x )
maxs.y = math.max( maxs.y, t1.y, t2.y, t3.y, t4.y, b1.y, b2.y, b3.y, b4.y )
maxs.z = math.max( maxs.z, t1.z, t2.z, t3.z, t4.z, b1.z, b2.z, b3.z, b4.z )
end
return mins, maxs
end
--[[---------------------------------------------------------
Copy this entity, and all of its constraints and entities
and put them in a table.
-----------------------------------------------------------]]
function Copy( Ent, AddToTable )
local Ents = {}
local Constraints = {}
GetAllConstrainedEntitiesAndConstraints( Ent, Ents, Constraints )
local EntTables = {}
if ( AddToTable != nil ) then EntTables = AddToTable.Entities or {} end
for k, v in pairs( Ents ) do
EntTables[ k ] = CopyEntTable( v )
end
local ConstraintTables = {}
if ( AddToTable != nil ) then ConstraintTables = AddToTable.Constraints or {} end
for k, v in pairs( Constraints ) do
ConstraintTables[ k ] = v
end
local mins, maxs = WorkoutSize( EntTables )
return {
Entities = EntTables,
Constraints = ConstraintTables,
Mins = mins,
Maxs = maxs
}
end
function CopyEnts( Ents )
local Ret = { Entities = {}, Constraints = {} }
for k, v in pairs( Ents ) do
Ret = Copy( v, Ret )
end
return Ret
end
--[[---------------------------------------------------------
Create an entity from a table.
-----------------------------------------------------------]]
function CreateEntityFromTable( Player, EntTable )
-- Get rid of stored outputs, they are being abused
-- Do it here, so that entities can store new ones on creation
EntTable.m_tOutputs = nil
--
-- Convert position/angle to `local`
--
if ( EntTable.Pos and EntTable.Angle ) then
EntTable.Pos, EntTable.Angle = LocalToWorld( EntTable.Pos, EntTable.Angle, LocalPos, LocalAng )
end
local EntityClass = FindEntityClass( EntTable.Class )
-- This class is unregistered. Instead of failing try using a generic
-- Duplication function to make a new copy..
if ( !EntityClass ) then
return GenericDuplicatorFunction( Player, EntTable )
end
-- Build the argument list
local ArgList = {}
for iNumber, Key in pairs( EntityClass.Args ) do
local 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
Arg = EntTable[ Key ]
-- Special keys
if ( Key == "Data" ) then Arg = EntTable end
-- If there's a missing argument then unpack will stop sending at that argument so send it as `false`
if ( Arg == nil ) then Arg = false end
ArgList[ iNumber ] = Arg
end
-- Create and return the entity
return EntityClass.Func( Player, unpack( ArgList ) )
end
--[[---------------------------------------------------------
Make a constraint from a constraint table
-----------------------------------------------------------]]
function CreateConstraintFromTable( Constraint, EntityList, Player )
local Factory = ConstraintType[ Constraint.Type ]
if ( !Factory ) then return end
local Args = {}
for k, Key in pairs( Factory.Args ) do
local Val = Constraint[ Key ]
for i = 1, 6 do
if ( Constraint.Entity[ i ] ) then
if ( Key == "Ent" .. i ) then
Val = EntityList[ Constraint.Entity[ i ].Index ]
if ( Constraint.Entity[ i ].World ) then
Val = game.GetWorld()
end
end
if ( Key == "Bone" .. i ) then Val = Constraint.Entity[ i ].Bone or 0 end
if ( Key == "LPos" .. i ) then Val = Constraint.Entity[ i ].LPos end
if ( Key == "WPos" .. i ) then Val = Constraint.Entity[ i ].WPos end
if ( Key == "Length" .. i ) then Val = Constraint.Entity[ i ].Length or 0 end
end
end
-- A little hack to give the duped constraints the correct player object
if ( Key:lower() == "pl" or Key:lower() == "ply" or Key:lower() == "player" ) then Val = Player end
-- If there's a missing argument then unpack will stop sending at that argument
if ( Val == nil ) then Val = false end
table.insert( Args, Val )
end
return Factory.Func( unpack( Args ) )
end
--[[---------------------------------------------------------
Given entity list and constranit list, create all entities
and return their tables
-----------------------------------------------------------]]
function Paste( Player, entityList, constraintList )
--
-- Store the player
--
local oldplayer = ActionPlayer
ActionPlayer = Player
--
-- Copy the table - because we're gonna be changing some stuff on it.
--
local EntityList = table.Copy( entityList )
local ConstraintList = table.Copy( constraintList )
local CreatedEntities = {}
--
-- Create the Entities
--
for k, v in pairs( EntityList ) do
local e = nil
local b = ProtectedCall( function() e = CreateEntityFromTable( Player, v ) end )
if ( !b ) then continue end
if ( IsValid( e ) ) then
--
-- Call this here ( as well as before :Spawn) because Spawn/Init might have stomped the values
--
if ( e.RestoreNetworkVars ) then
e:RestoreNetworkVars( v.DT )
end
if ( e.OnDuplicated ) then
e:OnDuplicated( v )
end
end
CreatedEntities[ k ] = e
if ( CreatedEntities[ k ] ) then
CreatedEntities[ k ].BoneMods = table.Copy( v.BoneMods )
CreatedEntities[ k ].EntityMods = table.Copy( v.EntityMods )
CreatedEntities[ k ].PhysicsObjects = table.Copy( v.PhysicsObjects )
else
CreatedEntities[ k ] = nil
end
end
--
-- Apply modifiers to the created entities
--
for EntID, Ent in pairs( CreatedEntities ) do
ApplyEntityModifiers( Player, Ent )
ApplyBoneModifiers( Player, Ent )
if ( Ent.PostEntityPaste ) then
Ent:PostEntityPaste( Player or NULL, Ent, CreatedEntities )
end
end
local CreatedConstraints = {}
--
-- Create constraints
--
for k, Constraint in pairs( ConstraintList ) do
local Entity = nil
ProtectedCall( function() Entity = CreateConstraintFromTable( Constraint, CreatedEntities, Player ) end )
if ( IsValid( Entity ) ) then
table.insert( CreatedConstraints, Entity )
end
end
ActionPlayer = oldplayer
return CreatedEntities, CreatedConstraints
end
--[[---------------------------------------------------------
Applies entity modifiers
-----------------------------------------------------------]]
function ApplyEntityModifiers( Player, Ent )
if ( !Ent ) then return end
if ( !Ent.EntityMods ) then return end
for Type, ModFunction in pairs( EntityModifiers ) do
if ( Ent.EntityMods[ Type ] ) then
ModFunction( Player, Ent, Ent.EntityMods[ Type ] )
end
end
end
--[[---------------------------------------------------------
Applies Bone Modifiers
-----------------------------------------------------------]]
function ApplyBoneModifiers( Player, Ent )
if ( !Ent ) then return end
if ( !Ent.PhysicsObjects ) then return end
if ( !Ent.BoneMods ) then return end
--
-- Loop every Bone on the entity
--
for Bone, Types in pairs( Ent.BoneMods ) do
-- The physics object isn't valid, skip it.
if ( !Ent.PhysicsObjects[ Bone ] ) then continue end
-- Loop through each modifier on this bone
for Type, Data in pairs( Types ) do
-- Find and all the function
local ModFunction = BoneModifiers[ Type ]
if ( ModFunction ) then
ModFunction( Player, Ent, Bone, Ent:GetPhysicsObjectNum( Bone ), Data )
end
end
end
end
--
-- Returns all constrained Entities and constraints
-- This is kind of in the wrong place.
--
-- This function will accept the world entity to save constrains, but will not actually save the world entity itself
--
function GetAllConstrainedEntitiesAndConstraints( ent, EntTable, ConstraintTable )
if ( !IsValid( ent ) and !ent:IsWorld() ) then return end
-- Translate the class name
local classname = ent:GetClass()
if ( ent.ClassOverride ) then classname = ent.ClassOverride end
-- Is the entity in the dupe whitelist?
if ( !IsAllowed( classname ) and !ent:IsWorld() ) then
-- MsgN( "duplicator: ", classname, " isn't allowed to be duplicated!" )
return
end
-- Entity doesn't want to be duplicated.
if ( ent.DoNotDuplicate ) then return end
if ( !ent:IsWorld() ) then EntTable[ ent:EntIndex() ] = ent end
if ( !constraint.HasConstraints( ent ) ) then return end
local ConTable = constraint.GetTable( ent )
for key, constr in pairs( ConTable ) do
local index = constr.Constraint:GetCreationID()
if ( !ConstraintTable[ index ] ) then
-- Add constraint to the constraints table
ConstraintTable[ index ] = constr
-- Run the Function for any ents attached to this constraint
for _, ConstrainedEnt in pairs( constr.Entity ) do
if ( !ConstrainedEnt.Entity:IsWorld() ) then
GetAllConstrainedEntitiesAndConstraints( ConstrainedEnt.Entity, EntTable, ConstraintTable )
end
end
end
end
return EntTable, ConstraintTable
end
--
-- Return true if this entity should be removed when RemoveMapCreatedEntities is called
-- We don't want to remove all entities.
--
local function ShouldMapEntityBeRemoved( ent, classname )
if ( classname == "prop_physics" ) then return true end
if ( classname == "prop_physics_multiplayer" ) then return true end
if ( classname == "prop_ragdoll" ) then return true end
if ( ent:IsNPC() ) then return true end
if ( IsAllowed( classname ) ) then return true end
return false
end
--
-- Help to remove certain map created entities before creating the saved entities
-- This is obviously so we don't get duplicate props everywhere. It should be called
-- before calling Paste.
--
function RemoveMapCreatedEntities()
for k, v in ipairs( ents.GetAll() ) do
if ( v:CreatedByMap() and ShouldMapEntityBeRemoved( v, v:GetClass() ) ) then
v:Remove()
end
end
end
--
-- BACKWARDS COMPATIBILITY - PHASE OUT, RENAME?
--
function DoGenericPhysics( Entity, Player, data )
if ( !data or !data.PhysicsObjects ) then return end
EntityPhysics.Load( data.PhysicsObjects, Entity )
end
function DoGeneric( ent, data )
EntitySaver.Load( data, ent )
end