dobrograd-13-06-2022/garrysmod/addons/feature-wire/lua/entities/gmod_wire_clutch.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

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")