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

312 lines
9.1 KiB
Lua

WireToolSetup.setCategory( "Physics" )
WireToolSetup.open( "clutch", "Clutch", "gmod_wire_clutch", nil, "Clutchs" )
if CLIENT then
language.Add( "Tool.wire_clutch.name", "Clutch Tool (Wire)" )
language.Add( "Tool.wire_clutch.desc", "Control rotational friction between props" )
TOOL.Information = {
{ name = "left_0", stage = 0, text = "Place/Select a clutch controller" },
{ name = "right_0", stage = 0, text = "Select an entity to apply the clutch to" },
{ name = "reload_0", stage = 0, text = "Remove clutch from entity/deselect controller" },
{ name = "right_1", stage = 1, text = "Right click on the second entity you want the clutch to apply to" },
}
end
WireToolSetup.BaseLang()
WireToolSetup.SetupMax( 8 )
if SERVER then
CreateConVar( "wire_clutch_maxlinks", 10 ) -- how many constraints can be added per controller
CreateConVar( "wire_clutch_maxrate", 40 ) -- how many constraints/sec may be changed per controller
end
TOOL.ClientConVar[ "model" ] = "models/jaanus/wiretool/wiretool_siren.mdl"
cleanup.Register( "wire_clutch" )
/*---------------------------------------------------------
-- Server Usermessages --
Send entity tables for the DrawHUD display
---------------------------------------------------------*/
local Send_Links
if SERVER then
// Send info: constraints associated with the selected clutch controller
Send_Links = function( ply, constrained_pairs )
umsg.Start( "wire_clutch_links", ply )
local num_constraints = #constrained_pairs
umsg.Short( num_constraints )
for k, v in pairs( constrained_pairs ) do
umsg.Entity( v.Ent1 )
umsg.Entity( v.Ent2 )
end
umsg.End()
end
end
/*---------------------------------------------------------
-- Client Usermessages --
Receive entity tables for the DrawHUD display
---------------------------------------------------------*/
if CLIENT then
local Linked_Ents = {} -- Table of constrained ents, with Ent1 as k and Ent2 as v
local Unique_Ents = {} -- Table of entities as keys
// Receive stage 0 info
local function Receive_links( um )
table.Empty( Linked_Ents )
local num_constraints = um:ReadShort() or 0
if num_constraints ~= 0 then
for i = 1, num_constraints do
local Ent1 = um:ReadEntity()
local Ent2 = um:ReadEntity()
table.insert( Linked_Ents, {Ent1 = Ent1, Ent2 = Ent2} )
Unique_Ents[Ent1] = true
Unique_Ents[Ent2] = true
end
end
end
usermessage.Hook( "wire_clutch_links", Receive_links )
/*---------------------------------------------------------
-- DrawHUD --
Display clutch constraints associated with a controller
---------------------------------------------------------*/
local function InView( pos2D )
if pos2D.x > 0 and pos2D.y > 0 and pos2D.x < ScrW() and pos2D.y < ScrH() then
return true
end
return false
end
// Client function for drawing a line to represent constraint to world
local function DrawBaseLine( pos, viewpos )
local dist = math.Clamp( viewpos:Distance( pos ), 50, 5000 )
local linelength = 3000 / dist
local pos2D = pos:ToScreen()
local pos1 = { x = pos2D.x + linelength, y = pos2D.y }
local pos2 = { x = pos2D.x - linelength, y = pos2D.y }
surface.DrawLine( pos1.x, pos1.y, pos2.x, pos2.y )
end
// Client function for drawing a circle around the currently selected controller
local function DrawSelectCircle( pos, viewpos )
local pos2D = pos:ToScreen()
if InView( pos2D ) then
surface.DrawCircle( pos2D.x, pos2D.y, 7, Color(255, 100, 100, 255 ) )
end
end
function TOOL:DrawHUD()
local DrawnEnts = {} -- Used to keep track of which ents already have a circle
local controller = self:GetWeapon():GetNWEntity( "WireClutchController" )
if !IsValid( controller ) then return end
// Draw circle around the controller
local viewpos = LocalPlayer():GetViewModel():GetPos()
local controllerpos = controller:LocalToWorld( controller:OBBCenter() )
DrawSelectCircle( controllerpos, viewpos )
local numconstraints_0 = #Linked_Ents
if numconstraints_0 ~= 0 then
// Draw lines between each pair of constrained ents
surface.SetDrawColor( 100, 255, 100, 255 )
// Check whether each entity/position can be drawn
for k, v in pairs( Linked_Ents ) do
local basepos
local pos1, pos2
local IsValid1 = IsValid( v.Ent1 )
local IsValid2 = IsValid( v.Ent2 )
if IsValid1 then pos1 = v.Ent1:GetPos():ToScreen() end
if IsValid2 then pos2 = v.Ent2:GetPos():ToScreen() end
if !IsValid1 and !IsValid2 then
table.remove( Linked_Ents, k )
elseif v.Ent1:IsWorld() then
basepos = v.Ent2:GetPos() + Vector(0, 0, -30)
pos1 = basepos:ToScreen()
elseif v.Ent2:IsWorld() then
basepos = v.Ent1:GetPos() + Vector(0, 0, -30)
pos2 = basepos:ToScreen()
end
if pos1 and pos2 then
if InView( pos1 ) and InView( pos2 ) then
surface.DrawLine( pos1.x, pos1.y, pos2.x, pos2.y )
if !DrawnEnts[v.Ent1] and IsValid1 then
surface.DrawCircle( pos1.x, pos1.y, 5, Color(100, 255, 100, 255 ) )
DrawnEnts[v.Ent1] = true
end
if !DrawnEnts[v.Ent2] and IsValid2 then
surface.DrawCircle( pos2.x, pos2.y, 5, Color(100, 255, 100, 255 ) )
DrawnEnts[v.Ent2] = true
end
if basepos then
DrawBaseLine( basepos, viewpos )
end
end
end
end
end
end
end
if SERVER then
function TOOL:SelectController( controller )
self.controller = controller
self:GetWeapon():SetNWEntity( "WireClutchController", controller or Entity(0) ) -- Must use null entity since nil won't send
// Send constraint from the controller to the client
local constrained_pairs = {}
if IsValid( controller ) then
constrained_pairs = controller:GetConstrainedPairs()
end
Send_Links( self:GetOwner(), constrained_pairs )
end
function TOOL:PostMake(ent)
self:SelectController(ent)
end
function TOOL:LeftClick_Update( trace )
self:PostMake(trace.Entity)
end
end
/*---------------------------------------------------------
-- Right click --
Associates ents with the currently selected controller
---------------------------------------------------------*/
function TOOL:RightClick( trace )
if CLIENT then return true end
local ply = self:GetOwner()
local stage = self:NumObjects()
if !IsValid( self.controller ) then
ply:PrintMessage( HUD_PRINTTALK, "Select a clutch controller with left click first" )
return
end
if ( !IsValid( trace.Entity ) and !trace.Entity:IsWorld() ) or trace.Entity:IsPlayer() then return end
// First click: select the first entity
if stage == 0 then
if trace.Entity:IsWorld() then
ply:PrintMessage( HUD_PRINTTALK, "Select a valid entity" )
return
end
// Check that we won't be going over the max number of links allowed
local maxlinks = GetConVarNumber( "wire_clutch_maxlinks", 10 )
if table.Count( self.controller.clutch_ballsockets ) >= maxlinks then
ply:PrintMessage( HUD_PRINTTALK, "A maximum of " .. tostring( maxlinks ) .. " links are allowed per clutch controller" )
return
end
// Store this entity for use later
local Phys = trace.Entity:GetPhysicsObjectNum( trace.PhysicsBone )
self:SetObject( 1, trace.Entity, trace.HitPos, Phys, trace.PhysicsBone, trace.HitNormal )
self:SetStage(1)
// Second click: select the second entity, and update the controller
else
local Ent1, Ent2 = self:GetEnt(1), trace.Entity
if Ent1 == Ent2 then
ply:PrintMessage( HUD_PRINTTALK, "Select a different entity" )
return false
end
// Check that these ents aren't already registered on this controller
if self.controller:ClutchExists( Ent1, Ent2 ) then
ply:PrintMessage( HUD_PRINTTALK, "Entities have already been registered to this controller!" )
return true
end
// Add this constraint to the clutch controller
self.controller:AddClutch( Ent1, Ent2 )
WireLib.AddNotify( ply, "Entities registered with clutch controller", NOTIFY_GENERIC, 7 )
// Update client
Send_Links( ply, self.controller:GetConstrainedPairs() )
self:ClearObjects()
self:SetStage(0)
end
return true
end
/*---------------------------------------------------------
-- Reload --
Remove clutch association between current controller and
the traced entity
Removes all current selections if hits world
---------------------------------------------------------*/
function TOOL:Reload( trace )
local stage = self:NumObjects()
if stage == 1 then
self:ClearObjects()
self:SetStage(0)
return
// Remove clutch associations with this entity
elseif IsValid( self.controller ) then
if trace.Entity:IsWorld() then
self:ClearObjects()
self:SetStage(0)
self.controller = nil
else
for k, v in pairs( self.controller.clutch_ballsockets ) do
if k.Ent1 == trace.Entity or k.Ent2 == trace.Entity then
self.controller:RemoveClutch( k )
end
end
end
// Update client with new constraint info
self:SelectController( self.controller )
end
return true
end
function TOOL:Holster()
self:ClearObjects()
self:SetStage(0)
self:ReleaseGhostEntity()
end
function TOOL.BuildCPanel( panel )
panel:AddControl( "Header", { Text = "#Tool.wire_clutch.name", Description = "#Tool.wire_clutch.desc" } )
WireDermaExts.ModelSelect(panel, "wire_clutch_model", list.Get( "Wire_Misc_Tools_Models" ), 1)
end
if CLIENT then return end