285 lines
7.4 KiB
Lua
285 lines
7.4 KiB
Lua
AddCSLuaFile()
|
|
DEFINE_BASECLASS( "base_wire_entity" )
|
|
ENT.PrintName = "Wire Clutch"
|
|
ENT.Purpose = "Allows rotational friction to be varied dynamically"
|
|
ENT.WireDebugName = "Clutch"
|
|
|
|
if CLIENT then return end -- No more client
|
|
|
|
function ENT:Initialize()
|
|
self:PhysicsInit( SOLID_VPHYSICS )
|
|
self:SetMoveType( MOVETYPE_VPHYSICS )
|
|
self:SetSolid( SOLID_VPHYSICS )
|
|
|
|
self.Inputs = Wire_CreateInputs( self, { "Friction" } )
|
|
--self.Outputs = Wire_CreateOutputs( self, { "Welded" } )
|
|
|
|
self.clutch_friction = 0
|
|
self.clutch_ballsockets = {} -- Table of constraints as keys
|
|
|
|
self:UpdateOverlay()
|
|
end
|
|
|
|
function ENT:UpdateOverlay()
|
|
local text = "Friction: " .. tostring( self.clutch_friction ) .. "\n"
|
|
|
|
local num_constraints = table.Count( self.clutch_ballsockets )
|
|
if num_constraints > 0 then
|
|
text = text .. "Links: " .. tostring( num_constraints )
|
|
else
|
|
text = text .. "Unlinked"
|
|
end
|
|
|
|
self:SetOverlayText( text )
|
|
end
|
|
|
|
|
|
--[[-------------------------------------------------------
|
|
-- Constraint functions --
|
|
Functions for handling clutch constraints
|
|
---------------------------------------------------------]]
|
|
|
|
function ENT:ClutchExists( Ent1, Ent2 )
|
|
for k, _ in pairs( self.clutch_ballsockets ) do
|
|
if ( Ent1 == k.Ent1 and Ent2 == k.Ent2 ) or
|
|
( Ent1 == k.Ent2 and Ent2 == k.Ent1 ) then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
|
|
-- Returns an array with each entry as a table containing Ent1, Ent2
|
|
function ENT:GetConstrainedPairs()
|
|
local ConstrainedPairs = {}
|
|
for k, _ in pairs( self.clutch_ballsockets ) do
|
|
if IsValid( k ) then
|
|
table.insert( ConstrainedPairs, {Ent1 = k.Ent1, Ent2 = k.Ent2} )
|
|
else
|
|
self.clutch_ballsockets[k] = nil
|
|
end
|
|
end
|
|
return ConstrainedPairs
|
|
end
|
|
|
|
|
|
local function NewBallSocket( Ent1, Ent2, friction )
|
|
if not IsValid( Ent1 ) or not IsValid( Ent2 ) then return false end
|
|
|
|
local ballsocket = constraint.AdvBallsocket( Ent1, Ent2, 0, 0,
|
|
Vector(0,0,0), Vector(0,0,0), 0, 0,
|
|
-180, -180, -180, 180, 180, 180,
|
|
friction, friction, friction, 1, 0 )
|
|
|
|
if ballsocket then
|
|
-- Prevent ball socket from being affected by dupe/remove functions
|
|
ballsocket.Type = ""
|
|
end
|
|
|
|
return ballsocket
|
|
end
|
|
|
|
|
|
-- Register a new clutch association with the controller
|
|
function ENT:AddClutch( Ent1, Ent2, friction )
|
|
local ballsocket = NewBallSocket( Ent1, Ent2, friction or self.clutch_friction )
|
|
|
|
if ballsocket then
|
|
self.clutch_ballsockets[ballsocket] = true
|
|
ballsocket:CallOnRemove( "WireClutchRemove", function()
|
|
if self.clutch_ballsockets[ballsocket] then
|
|
-- The table value is still true so something unknown killed the ballsocket
|
|
-- Set the table so that nothing else runs into issues
|
|
self.clutch_ballsockets[ballsocket] = nil
|
|
self:UpdateOverlay()
|
|
-- Wait a frame so nothing bad happens, then rebuild it
|
|
timer.Simple(0, function()
|
|
if self:IsValid() then
|
|
self:AddClutch( Ent1, Ent2, friction )
|
|
end
|
|
end)
|
|
end
|
|
end)
|
|
end
|
|
|
|
self:UpdateOverlay()
|
|
return ballsocket
|
|
end
|
|
|
|
|
|
-- Remove a new clutch association from the controller
|
|
function ENT:RemoveClutch( const )
|
|
self.clutch_ballsockets[const] = nil
|
|
|
|
if IsValid( const ) then
|
|
const:Remove()
|
|
end
|
|
|
|
self:UpdateOverlay()
|
|
end
|
|
|
|
|
|
function ENT:SetClutchFriction( const, friction )
|
|
-- There seems to be no direct way to edit constraint friction, so we must create a new ball socket constraint
|
|
self.clutch_ballsockets[const] = nil
|
|
|
|
if IsValid( const ) then
|
|
local Ent1 = const.Ent1
|
|
local Ent2 = const.Ent2
|
|
|
|
const:Remove()
|
|
|
|
local newconst = NewBallSocket( Ent1, Ent2, friction )
|
|
if newconst then
|
|
self.clutch_ballsockets[newconst] = true
|
|
end
|
|
|
|
else
|
|
print("Wire Clutch: Attempted to set friction on invalid constraint")
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
|
|
function ENT:OnRemove()
|
|
|
|
for k, v in pairs( self.clutch_ballsockets ) do
|
|
|
|
self:RemoveClutch( k )
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
--[[-------------------------------------------------------
|
|
-- Main controller functions --
|
|
Handle controller tables, wire input
|
|
---------------------------------------------------------]]
|
|
-- Used for setting/restoring entity mass when creating the clutch constraint
|
|
local function SaveMass( MassTable, ent )
|
|
if IsValid( ent ) and !MassTable[ent] then
|
|
local Phys = ent:GetPhysicsObject()
|
|
if IsValid( Phys ) then
|
|
MassTable[ent] = Phys:GetMass()
|
|
Phys:SetMass(1)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function RestoreMass( MassTable )
|
|
for k, v in pairs( MassTable ) do
|
|
k:GetPhysicsObject():SetMass( v )
|
|
end
|
|
end
|
|
|
|
|
|
-- Set friction on all constrained ents, called by input or timer (if delayed)
|
|
function ENT:UpdateFriction()
|
|
-- Set masses to 1 - this will prevents friction from varying depending on mass
|
|
local MassTable = {}
|
|
|
|
-- Create a table copy so when we start ammending self.clutch_ballsockets, it won't affect this loop
|
|
local clutch_ballsockets = table.Copy( self.clutch_ballsockets )
|
|
|
|
-- Update all registered ball socket constraints
|
|
local numconstraints = 0 -- Used to calculate the delay between inputs
|
|
|
|
for k, _ in pairs( clutch_ballsockets ) do
|
|
if not IsValid( k ) then
|
|
self:RemoveClutch( k )
|
|
|
|
else
|
|
SaveMass( MassTable, k.Ent1 )
|
|
SaveMass( MassTable, k.Ent2 )
|
|
|
|
self:SetClutchFriction( k, self.clutch_friction )
|
|
numconstraints = numconstraints + 1
|
|
|
|
end
|
|
end
|
|
|
|
RestoreMass( MassTable )
|
|
self:UpdateOverlay()
|
|
|
|
return numconstraints
|
|
end
|
|
|
|
|
|
-- Called when the clutch input delay timer finishes
|
|
local function ClutchDelayEnd( ent )
|
|
ent.ClutchDelay = nil
|
|
|
|
if ent.delayed_clutch_friction then
|
|
ent:TriggerInput( "Friction", ent.delayed_clutch_friction )
|
|
ent.delayed_clutch_friction = nil
|
|
end
|
|
end
|
|
|
|
|
|
function ENT:TriggerInput( iname, value )
|
|
if iname == "Friction" then
|
|
if !self.ClutchDelay then
|
|
self.clutch_friction = value
|
|
|
|
-- Create a delay to avoid server lag
|
|
local numconstraints = self:UpdateFriction()
|
|
local maxrate = math.max( GetConVarNumber( "wire_clutch_maxrate", 20 ), 1 )
|
|
local Delay = numconstraints / maxrate
|
|
|
|
self.ClutchDelay = true
|
|
timer.Create( "wire_clutch_delay_" .. tostring(self:EntIndex()), Delay, 0, function() ClutchDelayEnd(self) end )
|
|
|
|
else
|
|
-- This should only happen if an error prevents the ClutchDelayEnd function from being called
|
|
if !timer.Exists( "wire_clutch_delay_" .. tostring(self:EntIndex())) then
|
|
self.ClutchDelay = false
|
|
end
|
|
|
|
-- Store new friction value so it can be updated after the delay
|
|
self.delayed_clutch_friction = value
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
--[[-------------------------------------------------------
|
|
-- Adv Duplicator Support --
|
|
Linked entities are stored and recalled by their EntIndexes
|
|
---------------------------------------------------------]]
|
|
function ENT:BuildDupeInfo()
|
|
local info = BaseClass.BuildDupeInfo(self) or {}
|
|
info.constrained_pairs = {}
|
|
|
|
for k, v in pairs( self:GetConstrainedPairs() ) do
|
|
info.constrained_pairs[k] = {}
|
|
info.constrained_pairs[k].Ent1 = v.Ent1:EntIndex() or 0
|
|
info.constrained_pairs[k].Ent2 = v.Ent2:EntIndex() or 0
|
|
end
|
|
|
|
return info
|
|
end
|
|
|
|
function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID)
|
|
BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID)
|
|
|
|
local Ent1, Ent2
|
|
for _, v in pairs( info.constrained_pairs ) do
|
|
Ent1 = GetEntByID(v.Ent1)
|
|
Ent2 = GetEntByID(v.Ent2, game.GetWorld())
|
|
|
|
if IsValid(Ent1) and
|
|
Ent1 ~= Ent2 and
|
|
hook.Run( "CanTool", ply, WireLib.dummytrace(Ent1), "ballsocket_adv" ) and
|
|
hook.Run( "CanTool", ply, WireLib.dummytrace(Ent2), "ballsocket_adv" ) then
|
|
self:AddClutch( Ent1, Ent2 )
|
|
end
|
|
end
|
|
end
|
|
|
|
duplicator.RegisterEntityClass("gmod_wire_clutch", WireLib.MakeWireEnt, "Data")
|