--[[ | 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/ --]] if ( SERVER ) then -- If you're a server admin and you want your physics to spazz out less you can -- use the convar. The higher you set it the more accurate physics will be. -- This is set to 4 by default, since we are a physics mod. CreateConVar( "gmod_physiterations", "4", { FCVAR_REPLICATED, FCVAR_ARCHIVE }, "Improves physics accuracy at the expense of performance." ) end module( "constraint", package.seeall ) -- Clients don't need this module. if ( CLIENT ) then return end -- I think 128 constraints is around the max that causes the crash -- So at this number we'll refuse to add more to the system local MAX_CONSTRAINTS_PER_SYSTEM = 100 local CurrentSystem = nil local SystemLookup = {} hook.Add( "EntityRemoved", "Constraint Library - ConstraintRemoved", function( Ent ) local System = SystemLookup[ Ent ] if ( !IsValid( System ) ) then return end System.__ConstraintCount = ( System.__ConstraintCount or 0 ) - 1 if System.__ConstraintCount <= 0 then System.__BadConstraintSystem = true System:Remove() end end ) local function ConstraintCreated( Constraint ) assert( IsValid( CurrentSystem ) ) SystemLookup[ Constraint ] = CurrentSystem CurrentSystem.__ConstraintCount = ( CurrentSystem.__ConstraintCount or 0 ) + 1 end --[[---------------------------------------------------------------------- CreateConstraintSystem ------------------------------------------------------------------------]] local function CreateConstraintSystem() local iterations = GetConVarNumber( "gmod_physiterations" ) local csystem = ents.Create( "phys_constraintsystem" ) if ( !IsValid( csystem ) ) then return end csystem:SetKeyValue( "additionaliterations", iterations ) csystem:Spawn() csystem:Activate() csystem.__ConstraintCount = 0 return csystem end --[[---------------------------------------------------------------------- FindOrCreateConstraintSystem Takes 2 entities. If the entities don't have a constraint system associated with them it creates one and associates it with them. It then returns the constraint system ------------------------------------------------------------------------]] local function FindOrCreateConstraintSystem( Ent1, Ent2 ) local System = nil Ent2 = Ent2 or Ent1 -- Does Ent1 have a constraint system? if ( !Ent1:IsWorld() && IsValid( Ent1.ConstraintSystem ) && !Ent1.ConstraintSystem.__BadConstraintSystem ) then System = Ent1.ConstraintSystem end -- Don't add to this system - we have too many constraints on it already. if ( IsValid( System ) && ( System.__ConstraintCount or 0 ) >= MAX_CONSTRAINTS_PER_SYSTEM ) then System = nil end -- Does Ent2 have a constraint system? if ( !IsValid( System ) && !Ent2:IsWorld() && IsValid( Ent2.ConstraintSystem ) && !Ent2.ConstraintSystem.__BadConstraintSystem ) then System = Ent2.ConstraintSystem end -- Don't add to this system - we have too many constraints on it already. if ( IsValid( System ) && ( System.__ConstraintCount or 0 ) >= MAX_CONSTRAINTS_PER_SYSTEM ) then System = nil end -- No constraint system yet (Or they're both full) - make a new one if ( !IsValid( System ) ) then --Msg( "New Constrant System\n" ) System = CreateConstraintSystem() end Ent1.ConstraintSystem = System Ent2.ConstraintSystem = System return System end --[[---------------------------------------------------------------------- onStartConstraint( Ent1, Ent2 ) Should be called before creating a constraint ------------------------------------------------------------------------]] local function onStartConstraint( Ent1, Ent2 ) -- Get constraint system CurrentSystem = FindOrCreateConstraintSystem( Ent1, Ent2 ) -- Any constraints called after this call will use this system SetPhysConstraintSystem( CurrentSystem ) end --[[---------------------------------------------------------------------- onFinishConstraint( Ent1, Ent2 ) Should be called before creating a constraint ------------------------------------------------------------------------]] local function onFinishConstraint( Ent1, Ent2 ) -- Turn off constraint system override CurrentSystem = nil SetPhysConstraintSystem( NULL ) end local function SetPhysicsCollisions( Ent, b ) if ( !IsValid( Ent ) or !IsValid( Ent:GetPhysicsObject() ) ) then return end Ent:GetPhysicsObject():EnableCollisions( b ) end --[[---------------------------------------------------------------------- RemoveConstraints( Ent, Type ) Removes all constraints of type from entity ------------------------------------------------------------------------]] function RemoveConstraints( Ent, Type ) if ( !Ent.Constraints ) then return end local c = Ent.Constraints local i = 0 for k, v in pairs( c ) do if ( !IsValid( v ) ) then c[ k ] = nil elseif ( v.Type == Type ) then -- Make sure physics collisions are on! -- If we don't the unconstrained objects will fall through the world forever. SetPhysicsCollisions( v.Ent1, true ) SetPhysicsCollisions( v.Ent2, true ) c[ k ] = nil v:Remove() i = i + 1 end end if ( table.IsEmpty( c ) ) then -- Update the network var and clear the constraints table. Ent:IsConstrained() end local bool = i != 0 return bool, i end --[[---------------------------------------------------------------------- RemoveAll( Ent ) Removes all constraints from entity ------------------------------------------------------------------------]] function RemoveAll( Ent ) if ( !Ent.Constraints ) then return end local c = Ent.Constraints local i = 0 for k, v in pairs( c ) do if ( IsValid( v ) ) then -- Make sure physics collisions are on! -- If we don't the unconstrained objects will fall through the world forever. SetPhysicsCollisions( v.Ent1, true ) SetPhysicsCollisions( v.Ent2, true ) v:Remove() i = i + 1 end end -- Update the network var and clear the constraints table. Ent:IsConstrained() local bool = i != 0 return bool, i end --[[---------------------------------------------------------------------- Find( Ent1, Ent2, Type, Bone1, Bone2 ) Returns a constraint of given type between the two entities, if one exists ------------------------------------------------------------------------]] function Find( Ent1, Ent2, Type, Bone1, Bone2 ) if ( !Ent1.Constraints ) then return end for k, v in pairs( Ent1.Constraints ) do if ( IsValid( v ) && v.Type == Type ) then if ( v.Ent1 == Ent1 && v.Ent2 == Ent2 && v.Bone1 == Bone1 && v.Bone2 == Bone2 ) then return v end if ( v.Ent2 == Ent1 && v.Ent1 == Ent2 && v.Bone2 == Bone1 && v.Bone1 == Bone2 ) then return v end end end return nil end --[[---------------------------------------------------------------------- CanConstrain( Ent, Bone ) Returns false if we shouldn't be constraining this entity ------------------------------------------------------------------------]] function CanConstrain( Ent, Bone ) if ( !Ent ) then return false end if ( !isnumber( Bone ) ) then return false end if ( !Ent:IsWorld() && !Ent:IsValid() ) then return false end if ( !IsValid( Ent:GetPhysicsObjectNum( Bone ) ) ) then return false end return true end --[[---------------------------------------------------------------------- CalcElasticConsts( ... ) This attempts to scale the elastic constraints such as the winch to keep a stable but responsive constraint.. ------------------------------------------------------------------------]] local function CalcElasticConsts( phys1, phys2, ent1, Ent2, fixed ) local minMass = 0 if ( ent1:IsWorld() ) then minMass = phys2:GetMass() elseif ( Ent2:IsWorld() ) then minMass = phys1:GetMass() else minMass = math.min( phys1:GetMass(), phys2:GetMass() ) end -- const, damp local const = minMass * 100 local damp = const * 0.2 if ( !fixed ) then const = minMass * 50 damp = const * 0.1 end return const, damp end --[[---------------------------------------------------------------------- CreateKeyframeRope( ... ) Creates a rope without any constraint ------------------------------------------------------------------------]] function CreateKeyframeRope( Pos, width, material, Constraint, Ent1, LPos1, Bone1, Ent2, LPos2, Bone2, kv ) -- No rope if 0 or minus if ( width <= 0 ) then return end -- Clamp the rope to a sensible width width = math.Clamp( width, 0.2, 100 ) local rope = ents.Create( "keyframe_rope" ) if ( !IsValid( rope ) ) then return end rope:SetPos( Pos ) rope:SetKeyValue( "Width", width ) if ( isstring( material ) ) then -- Avoid materials with this shader, it either caused crashes or severe graphical glitches local mat = Material( material ) if ( mat && !string.find( mat:GetShader():lower(), "spritecard", nil, true ) && !string.find( mat:GetShader():lower(), "shadow", nil, true ) ) then rope:SetKeyValue( "RopeMaterial", material ) end end -- Attachment point 1 rope:SetEntity( "StartEntity", Ent1 ) rope:SetKeyValue( "StartOffset", tostring( LPos1 ) ) rope:SetKeyValue( "StartBone", Bone1 ) -- Attachment point 2 rope:SetEntity( "EndEntity", Ent2 ) rope:SetKeyValue( "EndOffset", tostring( LPos2 ) ) rope:SetKeyValue( "EndBone", Bone2 ) if ( kv ) then for k, v in pairs( kv ) do rope:SetKeyValue( k, tostring( v ) ) end end rope:Spawn() rope:Activate() -- Delete the rope if the attachments get killed Ent1:DeleteOnRemove( rope ) Ent2:DeleteOnRemove( rope ) if ( IsValid( Constraint ) ) then Constraint:DeleteOnRemove( rope ) end return rope end --[[---------------------------------------------------------------------- AddConstraintTable( Ent, Constraint, Ent2, Ent3, Ent4 ) Stores info about the constraints on the entity's table ------------------------------------------------------------------------]] function AddConstraintTable( Ent, Constraint, Ent2, Ent3, Ent4 ) if ( !IsValid( Constraint ) ) then return end if ( IsValid( Ent ) || ( Ent && Ent:IsWorld() ) ) then Ent.Constraints = Ent.Constraints or {} table.insert( Ent.Constraints, Constraint ) Ent:DeleteOnRemove( Constraint ) end if ( Ent2 && Ent2 != Ent ) then AddConstraintTable( Ent2, Constraint, Ent3, Ent4 ) end end --[[---------------------------------------------------------------------- AddConstraintTableNoDelete( Ent, Constraint, Ent2, Ent3, Ent4 ) Stores info about the constraints on the entity's table ------------------------------------------------------------------------]] function AddConstraintTableNoDelete( Ent, Constraint, Ent2, Ent3, Ent4 ) if ( !IsValid( Constraint ) ) then return end if ( IsValid( Ent ) || ( Ent && Ent:IsWorld() ) ) then Ent.Constraints = Ent.Constraints or {} table.insert( Ent.Constraints, Constraint ) end if ( Ent2 && Ent2 != Ent ) then AddConstraintTableNoDelete( Ent2, Constraint, Ent3, Ent4 ) end end --[[---------------------------------------------------------------------- Weld( ... ) Creates a solid weld constraint ------------------------------------------------------------------------]] function Weld( Ent1, Ent2, Bone1, Bone2, forcelimit, nocollide, deleteonbreak ) if ( Ent1 == Ent2 && Bone1 == Bone2 ) then return false end if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end if ( Find( Ent1, Ent2, "Weld", Bone1, Bone2 ) ) then -- A weld already exists between these two physics objects. -- There's totally no point in re-creating it. It doesn't make -- the weld any stronger - that's just an urban legend. return false end -- Don't weld World to objects, weld objects to World! -- Prevents crazy physics on some props if ( Ent1:IsWorld() ) then Ent1 = Ent2 Ent2 = game.GetWorld() end local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) onStartConstraint( Ent1, Ent2 ) -- Create the constraint local Constraint = ents.Create( "phys_constraint" ) ConstraintCreated( Constraint ) if ( forcelimit ) then Constraint:SetKeyValue( "forcelimit", forcelimit ) end if ( nocollide ) then Constraint:SetKeyValue( "spawnflags", 1 ) end Constraint:SetPhysConstraintObjects( Phys2, Phys1 ) Constraint:Spawn() Constraint:Activate() onFinishConstraint( Ent1, Ent2 ) AddConstraintTable( Ent1, Constraint, Ent2 ) -- Optionally delete Ent1 when the weld is broken -- This is to fix bug #310 if ( deleteonbreak ) then Ent2:DeleteOnRemove( Ent1 ) end -- Make a constraints table local ctable = { Type = "Weld", Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, forcelimit = forcelimit, nocollide = nocollide, deleteonbreak = deleteonbreak } Constraint:SetTable( ctable ) Phys1:Wake() Phys2:Wake() return Constraint end duplicator.RegisterConstraint( "Weld", Weld, "Ent1", "Ent2", "Bone1", "Bone2", "forcelimit", "nocollide", "deleteonbreak" ) --[[---------------------------------------------------------------------- Rope( ... ) Creates a rope constraint - with rope! ------------------------------------------------------------------------]] function Rope( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, length, addlength, forcelimit, width, material, rigid, color ) if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) local WPos1 = Phys1:LocalToWorld( LPos1 ) local WPos2 = Phys2:LocalToWorld( LPos2 ) local addlength = math.Clamp( addlength or 0, -56756, 56756 ) local Constraint = nil -- Make Constraint if ( Phys1 != Phys2 ) then onStartConstraint( Ent1, Ent2 ) -- Create the constraint Constraint = ents.Create( "phys_lengthconstraint" ) ConstraintCreated( Constraint ) Constraint:SetPos( WPos1 ) Constraint:SetKeyValue( "attachpoint", tostring( WPos2 ) ) Constraint:SetKeyValue( "minlength", "0.0" ) Constraint:SetKeyValue( "length", length + addlength ) if ( forcelimit ) then Constraint:SetKeyValue( "forcelimit", forcelimit ) end if ( rigid ) then Constraint:SetKeyValue( "spawnflags", 2 ) end Constraint:SetPhysConstraintObjects( Phys1, Phys2 ) Constraint:Spawn() Constraint:Activate() onFinishConstraint( Ent1, Ent2 ) end -- Make Rope local kv = { Length = length + addlength, Collide = 1 } if ( rigid ) then kv.Type = 2 end local rope = CreateKeyframeRope( WPos1, width, material, Constraint, Ent1, LPos1, Bone1, Ent2, LPos2, Bone2, kv ) if ( IsValid( rope ) && color ) then rope:SetColor( color ) end -- What the fuck if ( !Constraint ) then Constraint, rope = rope, nil end local ctable = { Type = "Rope", Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, LPos1 = LPos1, LPos2 = LPos2, length = length, addlength = addlength, forcelimit = forcelimit, width = width, material = material, rigid = rigid, color = color } if ( IsValid( Constraint ) ) then Constraint:SetTable( ctable ) AddConstraintTable( Ent1, Constraint, Ent2 ) end return Constraint, rope end duplicator.RegisterConstraint( "Rope", Rope, "Ent1", "Ent2", "Bone1", "Bone2", "LPos1", "LPos2", "length", "addlength", "forcelimit", "width", "material", "rigid", "color" ) --[[---------------------------------------------------------------------- Elastic( ... ) Creates an elastic constraint ------------------------------------------------------------------------]] function Elastic( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, constant, damping, rdamping, material, width, stretchonly, color ) if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) local WPos1 = Phys1:LocalToWorld( LPos1 ) local WPos2 = Phys2:LocalToWorld( LPos2 ) local Constraint = nil local rope = nil -- Make Constraint if ( Phys1 != Phys2 ) then onStartConstraint( Ent1, Ent2 ) Constraint = ents.Create( "phys_spring" ) ConstraintCreated( Constraint ) Constraint:SetPos( WPos1 ) Constraint:SetKeyValue( "springaxis", tostring( WPos2 ) ) Constraint:SetKeyValue( "constant", constant ) Constraint:SetKeyValue( "damping", damping ) Constraint:SetKeyValue( "relativedamping", rdamping ) Constraint:SetPhysConstraintObjects( Phys1, Phys2 ) if ( stretchonly == 1 or stretchonly == true ) then Constraint:SetKeyValue( "spawnflags", 1 ) end Constraint:Spawn() Constraint:Activate() onFinishConstraint( Ent1, Ent2 ) AddConstraintTable( Ent1, Constraint, Ent2 ) local ctable = { Type = "Elastic", Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, LPos1 = LPos1, LPos2 = LPos2, constant = constant, damping = damping, rdamping = rdamping, material = material, width = width, length = ( WPos1 - WPos2 ):Length(), stretchonly = stretchonly, color = color } Constraint:SetTable( ctable ) -- Make Rope local kv = { Collide = 1, Type = 0 } rope = CreateKeyframeRope( WPos1, width, material, Constraint, Ent1, LPos1, Bone1, Ent2, LPos2, Bone2, kv ) if ( IsValid( rope ) && color ) then rope:SetColor( color ) end end return Constraint, rope end duplicator.RegisterConstraint( "Elastic", Elastic, "Ent1", "Ent2", "Bone1", "Bone2", "LPos1", "LPos2", "constant", "damping", "rdamping", "material", "width", "stretchonly", "color" ) --[[---------------------------------------------------------------------- Keepupright( ... ) Creates a KeepUpright constraint ------------------------------------------------------------------------]] function Keepupright( Ent, Ang, Bone, angularlimit ) if ( !CanConstrain( Ent, Bone ) ) then return false end -- This was once here. Is there any specific reason this was the case? --if ( Ent:GetClass() != "prop_physics" && Ent:GetClass() != "prop_ragdoll" ) then return false end if ( Ent:IsPlayer() || Ent:IsWorld() ) then return false end if ( !angularlimit or angularlimit < 0 ) then return end local Phys = Ent:GetPhysicsObjectNum( Bone ) -- Remove any KU's already on entity RemoveConstraints( Ent, "Keepupright" ) onStartConstraint( Ent ) local Constraint = ents.Create( "phys_keepupright" ) ConstraintCreated( Constraint ) Constraint:SetAngles( Ang ) Constraint:SetKeyValue( "angularlimit", angularlimit ) Constraint:SetPhysConstraintObjects( Phys, Phys ) Constraint:Spawn() Constraint:Activate() onFinishConstraint( Ent ) AddConstraintTable( Ent, Constraint ) local ctable = { Type = "Keepupright", Ent1 = Ent, Ang = Ang, Bone = Bone, angularlimit = angularlimit } Constraint:SetTable( ctable ) -- -- This is a hack to keep the KeepUpright context menu in sync.. -- Ent:SetNWBool( "IsUpright", true ) return Constraint end duplicator.RegisterConstraint( "Keepupright", Keepupright, "Ent1", "Ang", "Bone", "angularlimit" ) function CreateStaticAnchorPoint( pos ) -- Creates an invisible frozen, not interactive prop. local anchor = ents.Create( "gmod_anchor" ) if ( !IsValid( anchor ) ) then return end anchor:SetPos( pos ) anchor:Spawn() anchor:Activate() return anchor, anchor:GetPhysicsObject(), 0, vector_origin end --[[---------------------------------------------------------------------- Slider( ... ) Creates a slider constraint ------------------------------------------------------------------------]] function Slider( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, width, material, color ) -- TODO: If we get rid of sliders we can get rid of gmod_anchor too! if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) local WPos1 = Phys1:LocalToWorld( LPos1 ) local WPos2 = Phys2:LocalToWorld( LPos2 ) local StaticAnchor = nil -- Make Constraint if ( Phys1 == Phys2 ) then return end -- Start World Hack. -- Attaching a slider to the world makes it really sucks so we make -- a prop and attach to that. if ( Ent1:IsWorld() ) then Ent1, Phys1, Bone1, LPos1 = CreateStaticAnchorPoint( WPos1 ) StaticAnchor = Ent1 end if ( Ent2:IsWorld() ) then Ent2, Phys2, Bone2, LPos2 = CreateStaticAnchorPoint( WPos2 ) StaticAnchor = Ent2 end -- End World Hack. onStartConstraint( Ent1, Ent2 ) local Constraint = ents.Create( "phys_slideconstraint" ) ConstraintCreated( Constraint ) Constraint:SetPos( WPos1 ) Constraint:SetKeyValue( "slideaxis", tostring( WPos2 ) ) Constraint:SetPhysConstraintObjects( Phys1, Phys2 ) Constraint:Spawn() Constraint:Activate() onFinishConstraint( Ent1, Ent2 ) AddConstraintTable( Ent1, Constraint, Ent2 ) -- Make Rope local kv = { Collide = 0, Type = 2, Subdiv = 1, } local rope = CreateKeyframeRope( WPos1, width, material, Constraint, Ent1, LPos1, Bone1, Ent2, LPos2, Bone2, kv ) if ( IsValid( rope ) && color ) then rope:SetColor( color ) end -- If we have a static anchor - delete it when we die. if ( StaticAnchor ) then Constraint:DeleteOnRemove( StaticAnchor ) end local ctable = { Type = "Slider", Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, LPos1 = LPos1, LPos2 = LPos2, width = width, material = material, color = color } Constraint:SetTable( ctable ) return Constraint, rope end duplicator.RegisterConstraint( "Slider", Slider, "Ent1", "Ent2", "Bone1", "Bone2", "LPos1", "LPos2", "width", "material", "color" ) --[[---------------------------------------------------------------------- Axis( ... ) Creates an axis constraint ------------------------------------------------------------------------]] function Axis( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, forcelimit, torquelimit, friction, nocollide, LocalAxis, DontAddTable ) if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) local WPos1 = Phys1:LocalToWorld( LPos1 ) local WPos2 = Phys2:LocalToWorld( LPos2 ) if ( Phys1 == Phys2 ) then return false end -- If we have a LocalAxis, use that if ( LocalAxis ) then WPos2 = Phys1:LocalToWorld( LocalAxis ) end onStartConstraint( Ent1, Ent2 ) local Constraint = ents.Create( "phys_hinge" ) ConstraintCreated( Constraint ) Constraint:SetPos( WPos1 ) Constraint:SetKeyValue( "hingeaxis", tostring( WPos2 ) ) if ( forcelimit && forcelimit > 0 ) then Constraint:SetKeyValue( "forcelimit", forcelimit ) end if ( torquelimit && torquelimit > 0 ) then Constraint:SetKeyValue( "torquelimit", torquelimit ) end if ( friction && friction > 0 ) then Constraint:SetKeyValue( "hingefriction", friction ) end if ( nocollide && nocollide > 0 ) then Constraint:SetKeyValue( "spawnflags", 1 ) end Constraint:SetPhysConstraintObjects( Phys1, Phys2 ) Constraint:Spawn() Constraint:Activate() onFinishConstraint( Ent1, Ent2 ) if ( !DontAddTable ) then AddConstraintTable( Ent1, Constraint, Ent2 ) end local ctable = { Type = "Axis", Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, LPos1 = LPos1, LPos2 = LPos2, forcelimit = forcelimit, torquelimit = torquelimit, friction = friction, nocollide = nocollide, LocalAxis = Phys1:WorldToLocal( WPos2 ) } Constraint:SetTable( ctable ) return Constraint end duplicator.RegisterConstraint( "Axis", Axis, "Ent1", "Ent2", "Bone1", "Bone2", "LPos1", "LPos2", "forcelimit", "torquelimit", "friction", "nocollide", "LocalAxis", "DontAddTable" ) --[[---------------------------------------------------------------------- AdvBallsocket( ... ) Creates an advanced ballsocket (ragdoll) constraint ------------------------------------------------------------------------]] function AdvBallsocket( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, forcelimit, torquelimit, xmin, ymin, zmin, xmax, ymax, zmax, xfric, yfric, zfric, onlyrotation, nocollide ) if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) local WPos1 = Phys1:LocalToWorld( LPos1 ) -- local WPos2 = Phys2:LocalToWorld( LPos2 ) if ( Phys1 == Phys2 ) then return false end -- Make Constraint onStartConstraint( Ent1, Ent2 ) local flags = 0 if ( onlyrotation && onlyrotation > 0 ) then flags = flags + 2 end if ( nocollide && nocollide > 0 ) then flags = flags + 1 end local Constraint = ents.Create( "phys_ragdollconstraint" ) ConstraintCreated( Constraint ) Constraint:SetPos( WPos1 ) Constraint:SetKeyValue( "xmin", xmin ) Constraint:SetKeyValue( "xmax", xmax ) Constraint:SetKeyValue( "ymin", ymin ) Constraint:SetKeyValue( "ymax", ymax ) Constraint:SetKeyValue( "zmin", zmin ) Constraint:SetKeyValue( "zmax", zmax ) if ( xfric && xfric > 0 ) then Constraint:SetKeyValue( "xfriction", xfric ) end if ( yfric && yfric > 0 ) then Constraint:SetKeyValue( "yfriction", yfric ) end if ( zfric && zfric > 0 ) then Constraint:SetKeyValue( "zfriction", zfric ) end if ( forcelimit && forcelimit > 0 ) then Constraint:SetKeyValue( "forcelimit", forcelimit ) end if ( torquelimit && torquelimit > 0 ) then Constraint:SetKeyValue( "torquelimit", torquelimit ) end Constraint:SetKeyValue( "spawnflags", flags ) Constraint:SetPhysConstraintObjects( Phys1, Phys2 ) Constraint:Spawn() Constraint:Activate() onFinishConstraint( Ent1, Ent2 ) AddConstraintTable( Ent1, Constraint, Ent2 ) local ctable = { Type = "AdvBallsocket", Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, LPos1 = LPos1, LPos2 = LPos2, forcelimit = forcelimit, torquelimit = torquelimit, xmin = xmin, ymin = ymin, zmin = zmin, xmax = xmax, ymax = ymax, zmax = zmax, xfric = xfric, yfric = yfric, zfric = zfric, onlyrotation = onlyrotation, nocollide = nocollide } Constraint:SetTable( ctable ) return Constraint end duplicator.RegisterConstraint( "AdvBallsocket", AdvBallsocket, "Ent1", "Ent2", "Bone1", "Bone2", "LPos1", "LPos2", "forcelimit", "torquelimit", "xmin", "ymin", "zmin", "xmax", "ymax", "zmax", "xfric", "yfric", "zfric", "onlyrotation", "nocollide" ) --[[---------------------------------------------------------------------- NoCollide( ... ) Creates an nocollide `constraint' ------------------------------------------------------------------------]] function NoCollide( Ent1, Ent2, Bone1, Bone2 ) if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) if ( Phys1 == Phys2 ) then return false end if ( Find( Ent1, Ent2, "NoCollide", Bone1, Bone2 ) ) then return false end -- Make Constraint local constr = ents.Create( "logic_collision_pair" ) if ( !IsValid( constr ) ) then return end constr:SetKeyValue( "startdisabled", 1 ) constr:SetPhysConstraintObjects( Phys1, Phys2 ) constr:Spawn() constr:Activate() constr:Input( "DisableCollisions", nil, nil, nil ) AddConstraintTable( Ent1, constr, Ent2 ) local ctable = { Type = "NoCollide", Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, } constr:SetTable( ctable ) return constr end duplicator.RegisterConstraint( "NoCollide", NoCollide, "Ent1", "Ent2", "Bone1", "Bone2" ) --[[---------------------------------------------------------------------- MotorControl( pl, motor, onoff, dir ) Numpad controls for the motor constraints ------------------------------------------------------------------------]] local function MotorControl( pl, motor, onoff, dir ) if ( !IsValid( motor ) ) then return false end local activate = false if ( motor.toggle == 1 ) then -- Toggle mode, only do something when the key is pressed -- if the motor is off, turn it on, and vice-versa. -- This only happens if the same key as the current -- direction is pressed, otherwise the direction is changed -- with the motor being left on. if ( onoff ) then if ( motor.direction == dir or !motor.is_on ) then -- Direction is the same, Activate if the motor is off -- Deactivate if the motor is on. motor.is_on = !motor.is_on activate = motor.is_on else -- Change of direction, make sure it's activated activate = true end else return end else -- normal mode: activate is based on the key status -- (down = on, up = off) activate = onoff end if ( activate ) then motor:Fire( "Scale", dir ) -- This makes the direction change motor:Fire( "Activate" ) -- Turn on the motor else -- Turn off the motor, mimicking the wheel tool motor:Fire( "Scale", 0 ) motor:Fire( "Activate" ) -- For some reason Deactivate causes the torque axis to be fucked up, or rather.. -- Reinitialized without taking into account possibly new rotation of the "parent" prop -- motor:Fire( "Deactivate" ) end motor.direction = dir return true end numpad.Register( "MotorControl", MotorControl ) --[[---------------------------------------------------------------------- Motor( ... ) Creates a motor constraint ------------------------------------------------------------------------]] function Motor( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, friction, torque, forcetime, nocollide, toggle, pl, forcelimit, numpadkey_fwd, numpadkey_bwd, direction, LocalAxis ) if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end -- Get information we're about to use local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) local WPos1 = Phys1:LocalToWorld( LPos1 ) local WPos2 = Phys2:LocalToWorld( LPos2 ) if ( Phys1 == Phys2 ) then return false end if ( LocalAxis ) then WPos2 = Phys1:LocalToWorld( LocalAxis ) end -- The true at the end stops it adding the axis table to the entity's count stuff. local axis = Axis( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, forcelimit, 0, friction, nocollide, LocalAxis, true ) -- Delete the axis when either object dies Ent1:DeleteOnRemove( axis ) Ent2:DeleteOnRemove( axis ) -- Create the constraint onStartConstraint( Ent1, Ent2 ) local Constraint = ents.Create( "phys_torque" ) ConstraintCreated( Constraint ) Constraint:SetPos( WPos1 ) Constraint:SetKeyValue( "axis", tostring( WPos2 ) ) Constraint:SetKeyValue( "force", torque ) Constraint:SetKeyValue( "forcetime", forcetime ) Constraint:SetKeyValue( "spawnflags", 4 ) Constraint:SetPhysConstraintObjects( Phys1, Phys1 ) Constraint:Spawn() Constraint:Activate() onFinishConstraint( Ent1, Ent2 ) AddConstraintTableNoDelete( Ent1, Constraint, Ent2 ) direction = direction or 1 LocalAxis = Phys1:WorldToLocal( WPos2 ) -- Delete the phys_torque too! axis:DeleteOnRemove( Constraint ) -- Delete the axis constrain if phys_torque is deleted, with something like Motor tools reload Constraint:DeleteOnRemove( axis ) local ctable = { Type = "Motor", Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, LPos1 = LPos1, LPos2 = LPos2, friction = friction, torque = torque, forcetime = forcetime, nocollide = nocollide, toggle = toggle, pl = pl, forcelimit = forcelimit, forcescale = 0, direction = direction, is_on = false, numpadkey_fwd = numpadkey_fwd, numpadkey_bwd = numpadkey_bwd, LocalAxis = LocalAxis } Constraint:SetTable( ctable ) if ( numpadkey_fwd ) then numpad.OnDown( pl, numpadkey_fwd, "MotorControl", Constraint, true, 1 ) numpad.OnUp( pl, numpadkey_fwd, "MotorControl", Constraint, false, 1 ) end if ( numpadkey_bwd ) then numpad.OnDown( pl, numpadkey_bwd, "MotorControl", Constraint, true, -1 ) numpad.OnUp( pl, numpadkey_bwd, "MotorControl", Constraint, false, -1 ) end return Constraint, axis end duplicator.RegisterConstraint( "Motor", Motor, "Ent1", "Ent2", "Bone1", "Bone2", "LPos1", "LPos2", "friction", "torque", "forcetime", "nocollide", "toggle", "pl", "forcelimit", "numpadkey_fwd", "numpadkey_bwd", "direction", "LocalAxis" ) --[[---------------------------------------------------------------------- Pulley( ... ) Creates a pulley constraint ------------------------------------------------------------------------]] function Pulley( Ent1, Ent4, Bone1, Bone4, LPos1, LPos4, WPos2, WPos3, forcelimit, rigid, width, material, color ) if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent4, Bone4 ) ) then return false end local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys4 = Ent4:GetPhysicsObjectNum( Bone4 ) local WPos1 = Phys1:LocalToWorld( LPos1 ) local WPos4 = Phys4:LocalToWorld( LPos4 ) if ( Phys1 == Phys4 ) then return false end -- Make Constraint onStartConstraint( Ent1, Ent4 ) local Constraint = ents.Create( "phys_pulleyconstraint" ) ConstraintCreated( Constraint ) Constraint:SetPos( WPos2 ) Constraint:SetKeyValue( "position2", tostring( WPos3 ) ) Constraint:SetKeyValue( "ObjOffset1", tostring( LPos1 ) ) Constraint:SetKeyValue( "ObjOffset2", tostring( LPos4 ) ) Constraint:SetKeyValue( "forcelimit", forcelimit ) Constraint:SetKeyValue( "addlength", ( WPos3 - WPos4 ):Length() ) if ( rigid ) then Constraint:SetKeyValue( "spawnflags", 2 ) end Constraint:SetPhysConstraintObjects( Phys1, Phys4 ) Constraint:Spawn() Constraint:Activate() onFinishConstraint( Ent1, Ent4 ) AddConstraintTable( Ent1, Constraint, Ent4 ) local ctable = { Type = "Pulley", Ent1 = Ent1, Ent4 = Ent4, Bone1 = Bone1, Bone4 = Bone4, LPos1 = LPos1, LPos4 = LPos4, WPos2 = WPos2, WPos3 = WPos3, forcelimit = forcelimit, rigid = rigid, width = width, material = material, color = color } Constraint:SetTable( ctable ) -- make Rope local World = game.GetWorld() local kv = { Collide = 1, Type = 2, Subdiv = 1, } local rope1 = CreateKeyframeRope( WPos1, width, material, Constraint, Ent1, LPos1, Bone1, World, WPos2, 0, kv ) local rope2 = CreateKeyframeRope( WPos1, width, material, Constraint, World, WPos3, 0, World, WPos2, 0, kv ) local rope3 = CreateKeyframeRope( WPos1, width, material, Constraint, World, WPos3, 0, Ent4, LPos4, Bone4, kv ) if ( color ) then if ( IsValid( rope1 ) ) then rope1:SetColor( color ) end if ( IsValid( rope2 ) ) then rope2:SetColor( color ) end if ( IsValid( rope3 ) ) then rope3:SetColor( color ) end end return Constraint, rope1, rope2, rope3 end duplicator.RegisterConstraint( "Pulley", Pulley, "Ent1", "Ent4", "Bone1", "Bone4", "LPos1", "LPos4", "WPos2", "WPos3", "forcelimit", "rigid", "width", "material", "color" ) --[[---------------------------------------------------------------------- Ballsocket( ... ) Creates a Ballsocket constraint ------------------------------------------------------------------------]] function Ballsocket( Ent1, Ent2, Bone1, Bone2, LPos, forcelimit, torquelimit, nocollide ) if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end -- Get information we're about to use local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) local WPos = Phys2:LocalToWorld( LPos ) if ( Phys1 == Phys2 ) then return false end onStartConstraint( Ent1, Ent2 ) local Constraint = ents.Create( "phys_ballsocket" ) ConstraintCreated( Constraint ) Constraint:SetPos( WPos ) if ( forcelimit && forcelimit > 0 ) then Constraint:SetKeyValue( "forcelimit", forcelimit ) end if ( torquelimit && torquelimit > 0 ) then Constraint:SetKeyValue( "torquelimit", torquelimit ) end if ( nocollide && nocollide > 0 ) then Constraint:SetKeyValue( "spawnflags", 1 ) end Constraint:SetPhysConstraintObjects( Phys1, Phys2 ) Constraint:Spawn() Constraint:Activate() onFinishConstraint( Ent1, Ent2 ) AddConstraintTable( Ent1, Constraint, Ent2 ) local ctable = { Type = "Ballsocket", Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, LPos = LPos, forcelimit = forcelimit, torquelimit = torquelimit, nocollide = nocollide } Constraint:SetTable( ctable ) return Constraint end duplicator.RegisterConstraint( "Ballsocket", Ballsocket, "Ent1", "Ent2", "Bone1", "Bone2", "LPos", "forcelimit", "torquelimit", "nocollide" ) --[[---------------------------------------------------------------------- Winch( ... ) Creates a Winch constraint ------------------------------------------------------------------------]] function Winch( pl, Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, width, fwd_bind, bwd_bind, fwd_speed, bwd_speed, material, toggle, color ) if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) -- local WPos1 = Phys1:LocalToWorld( LPos1 ) -- local WPos2 = Phys2:LocalToWorld( LPos2 ) if ( Phys1 == Phys2 ) then return false end local const, dampen = CalcElasticConsts( Phys1, Phys2, Ent1, Ent2, false ) local Constraint, rope = Elastic( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, const, dampen, 0, material, width, true, color ) if ( !Constraint ) then return nil, rope end local ctable = { Type = "Winch", pl = pl, Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, LPos1 = LPos1, LPos2 = LPos2, width = width, fwd_bind = fwd_bind, bwd_bind = bwd_bind, fwd_speed = fwd_speed, bwd_speed = bwd_speed, material = material, toggle = toggle, color = color } Constraint:SetTable( ctable ) -- Attach our Controller to the Elastic constraint local controller = ents.Create( "gmod_winch_controller" ) controller:SetConstraint( Constraint ) controller:SetRope( rope ) controller:Spawn() Constraint:DeleteOnRemove( controller ) Ent1:DeleteOnRemove( controller ) Ent2:DeleteOnRemove( controller ) if ( toggle ) then numpad.OnDown( pl, fwd_bind, "WinchToggle", controller, 1 ) numpad.OnDown( pl, bwd_bind, "WinchToggle", controller, -1 ) else numpad.OnDown( pl, fwd_bind, "WinchOn", controller, 1 ) numpad.OnUp( pl, fwd_bind, "WinchOff", controller ) numpad.OnDown( pl, bwd_bind, "WinchOn", controller, -1 ) numpad.OnUp( pl, bwd_bind, "WinchOff", controller ) end return Constraint, rope, controller end duplicator.RegisterConstraint( "Winch", Winch, "pl", "Ent1", "Ent2", "Bone1", "Bone2", "LPos1", "LPos2", "width", "fwd_bind", "bwd_bind", "fwd_speed", "bwd_speed", "material", "toggle", "color" ) --[[---------------------------------------------------------------------- Hydraulic( ... ) Creates a Hydraulic constraint ------------------------------------------------------------------------]] function Hydraulic( pl, Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, Length1, Length2, width, key, fixed, speed, material, toggle, color ) if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) -- local WPos1 = Phys1:LocalToWorld( LPos1 ) -- local WPos2 = Phys2:LocalToWorld( LPos2 ) if ( Phys1 == Phys2 ) then return false end if ( toggle == nil ) then toggle = true end -- Retain original behavior local const, dampn = CalcElasticConsts( Phys1, Phys2, Ent1, Ent2, tobool( fixed ) ) local Constraint, rope = Elastic( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, const, dampn, 0, material, width, false, color ) local ctable = { Type = "Hydraulic", pl = pl, Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, LPos1 = LPos1, LPos2 = LPos2, Length1 = Length1, Length2 = Length2, width = width, key = key, fixed = fixed, fwd_speed = speed, bwd_speed = speed, toggle = toggle, material = material, color = color } Constraint:SetTable( ctable ) if ( Constraint && Constraint != rope ) then local slider if ( fixed == 1 ) then slider = Slider( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, 0 ) slider:SetTable( {} ) Constraint:DeleteOnRemove( slider ) end local controller = ents.Create( "gmod_winch_controller" ) if ( Length2 > Length1 ) then controller:SetKeyValue( "minlength", Length1 ) controller:SetKeyValue( "maxlength", Length2 ) else controller:SetKeyValue( "minlength", Length2 ) controller:SetKeyValue( "maxlength", Length1 ) end controller:SetConstraint( Constraint ) controller:Spawn() Ent1:DeleteOnRemove( controller ) Ent2:DeleteOnRemove( controller ) Constraint:DeleteOnRemove( controller ) if ( toggle ) then numpad.OnDown( pl, key, "HydraulicToggle", controller ) else numpad.OnUp( pl, key, "HydraulicDir", controller, -1 ) numpad.OnDown( pl, key, "HydraulicDir", controller, 1 ) end return Constraint, rope, controller, slider else return Constraint, rope end end duplicator.RegisterConstraint( "Hydraulic", Hydraulic, "pl", "Ent1", "Ent2", "Bone1", "Bone2", "LPos1", "LPos2", "Length1", "Length2", "width", "key", "fixed", "fwd_speed", "material", "toggle", "color" ) --[[---------------------------------------------------------------------- Muscle( ... ) Creates a Muscle constraint ------------------------------------------------------------------------]] function Muscle( pl, Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, Length1, Length2, width, key, fixed, period, amplitude, starton, material, color ) if ( !CanConstrain( Ent1, Bone1 ) ) then return false end if ( !CanConstrain( Ent2, Bone2 ) ) then return false end local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) -- local WPos1 = Phys1:LocalToWorld( LPos1 ) -- local WPos2 = Phys2:LocalToWorld( LPos2 ) if ( Phys1 == Phys2 ) then return false end local const, dampn = CalcElasticConsts( Phys1, Phys2, Ent1, Ent2, tobool( fixed ) ) local Constraint, rope = Elastic( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, const, dampn, 0, material, width, false, color ) if ( !Constraint ) then return false end local ctable = { Type = "Muscle", pl = pl, Ent1 = Ent1, Ent2 = Ent2, Bone1 = Bone1, Bone2 = Bone2, LPos1 = LPos1, LPos2 = LPos2, Length1 = Length1, Length2 = Length2, width = width, key = key, fixed = fixed, period = period, amplitude = amplitude, toggle = true, starton = starton, material = material, color = color } Constraint:SetTable( ctable ) local slider = nil if ( fixed == 1 ) then slider = Slider( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, 0 ) slider:SetTable( {} ) -- Remove data for duplicator Constraint:DeleteOnRemove( slider ) end local controller = ents.Create( "gmod_winch_controller" ) if ( Length2 > Length1 ) then controller:SetKeyValue( "minlength", Length1 ) controller:SetKeyValue( "maxlength", Length2 ) else controller:SetKeyValue( "minlength", Length2 ) controller:SetKeyValue( "maxlength", Length1 ) end controller:SetKeyValue( "type", 1 ) controller:SetConstraint( Constraint ) controller:Spawn() Ent1:DeleteOnRemove( controller ) Ent2:DeleteOnRemove( controller ) Constraint:DeleteOnRemove( controller ) numpad.OnDown( pl, key, "MuscleToggle", controller ) if ( starton ) then controller:SetDirection( 1 ) end return Constraint, rope, controller, slider end duplicator.RegisterConstraint( "Muscle", Muscle, "pl", "Ent1", "Ent2", "Bone1", "Bone2", "LPos1", "LPos2", "Length1", "Length2", "width", "key", "fixed", "period", "amplitude", "starton", "material", "color" ) --[[---------------------------------------------------------------------- Returns true if this entity has valid constraints ------------------------------------------------------------------------]] function HasConstraints( ent ) if ( !ent ) then return false end if ( !ent.Constraints ) then return false end local count = 0 for key, Constraint in pairs( ent.Constraints ) do if ( !IsValid( Constraint ) ) then ent.Constraints[ key ] = nil else count = count + 1 end end return count != 0 end --[[---------------------------------------------------------------------- Returns this entities constraints table This is for the future, because ideally the constraints table will eventually look like this - and we won't have to build it every time. ------------------------------------------------------------------------]] function GetTable( ent ) if ( !HasConstraints( ent ) ) then return {} end local RetTable = {} for key, ConstraintEntity in pairs( ent.Constraints ) do local con = {} table.Merge( con, ConstraintEntity:GetTable() ) con.Constraint = ConstraintEntity con.Entity = {} for i = 1, 6 do if ( con[ "Ent" .. i ] && ( con[ "Ent" .. i ]:IsWorld() or con[ "Ent" .. i ]:IsValid() ) ) then con.Entity[ i ] = {} con.Entity[ i ].Index = con[ "Ent" .. i ]:EntIndex() con.Entity[ i ].Entity = con[ "Ent" .. i ] con.Entity[ i ].Bone = con[ "Bone" .. i ] con.Entity[ i ].LPos = con[ "LPos" .. i ] con.Entity[ i ].WPos = con[ "WPos" .. i ] con.Entity[ i ].Length = con[ "Length" .. i ] con.Entity[ i ].World = con[ "Ent" .. i ]:IsWorld() end end table.insert( RetTable, con ) end return RetTable end --[[---------------------------------------------------------------------- Make this entity forget any constraints it knows about ------------------------------------------------------------------------]] function ForgetConstraints( ent ) ent.Constraints = {} end --[[---------------------------------------------------------------------- Returns a list of constraints, by name ------------------------------------------------------------------------]] function FindConstraints( ent, name ) local ConTable = GetTable( ent ) local Found = {} for k, con in ipairs( ConTable ) do if ( con.Type == name ) then table.insert( Found, con ) end end return Found end --[[---------------------------------------------------------------------- Returns the first constraint found by name ------------------------------------------------------------------------]] function FindConstraint( ent, name ) local ConTable = GetTable( ent ) for k, con in ipairs( ConTable ) do if ( con.Type == name ) then return con end end return nil end --[[---------------------------------------------------------------------- Returns the first constraint found by name ------------------------------------------------------------------------]] function FindConstraintEntity( ent, name ) local ConTable = GetTable( ent ) for k, con in ipairs( ConTable ) do if ( con.Type == name ) then return con.Constraint end end return NULL end --[[---------------------------------------------------------------------- Returns a table of all the entities constrained to ent ------------------------------------------------------------------------]] function GetAllConstrainedEntities( ent, ResultTable ) local ResultTable = ResultTable or {} if ( !IsValid( ent ) && !ent:IsWorld() ) then return end if ( ResultTable[ ent ] ) then return end ResultTable[ ent ] = ent local ConTable = GetTable( ent ) for k, con in ipairs( ConTable ) do for EntNum, Ent in pairs( con.Entity ) do if ( !Ent.Entity:IsWorld() ) then GetAllConstrainedEntities( Ent.Entity, ResultTable ) end end end return ResultTable end