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

1679 lines
47 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/
--]]
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