mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 05:43:46 +03:00
855 lines
26 KiB
Lua
855 lines
26 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/
|
|
--]]
|
|
|
|
TOOL.Category = "simfphys"
|
|
TOOL.Name = "#Vehicle Duplicator"
|
|
TOOL.Command = nil
|
|
TOOL.ConfigName = ""
|
|
|
|
if SERVER then
|
|
util.AddNetworkString( "sphys_dupe" )
|
|
|
|
local recv_interval_start = 0 -- first second of current interval
|
|
local recv_interval_len = 1 -- in seconds, length of interval before resetting recv_interval_(start,count)
|
|
local recv_interval_count = 0 -- current count of messages received in interval
|
|
local recv_max = 500 -- maximum number of messages to receive during one interval
|
|
local recv_stop = 0 -- is the net.Receive stopped (activates short-circuit)
|
|
local recv_delay_after_stop = 3600 -- how long to wait (in seconds) before allowing messages again
|
|
|
|
net.Receive("sphys_dupe", function( length, ply )
|
|
if (recv_stop == 1) then -- if stopped,
|
|
return -- short-circuit
|
|
end
|
|
-- check if a new interval has come
|
|
if (recv_interval_start + recv_interval_len < os.time()) then
|
|
recv_interval_start = os.time() -- reset time
|
|
recv_interval_count = 0 -- reset message count
|
|
end
|
|
recv_interval_count = recv_interval_count + 1
|
|
-- check if below threshold
|
|
if (recv_interval_count > recv_max) then
|
|
-- if over threshold, activate short-circuit
|
|
recv_stop = 1
|
|
-- and create a timer to deactivate the short-circuit in recv_delay_after_stop seconds
|
|
timer.Simple(recv_delay_after_stop, function()
|
|
recv_stop = 0
|
|
end)
|
|
-- warn server about attack
|
|
PrintMessage(HUD_PRINTTALK, "WARNING: " .. ply:Nick() .. " [" .. ply:SteamID() .. "] is attacking sphys_dupe. simfphys duplicator is disabled for " .. tostring(recv_delay_after_stop) .. " seconds.")
|
|
end
|
|
|
|
ply.TOOLMemory = net.ReadTable()
|
|
ply:SelectWeapon( "gmod_tool" )
|
|
end)
|
|
end
|
|
|
|
if CLIENT then
|
|
language.Add( "tool.simfphysduplicator.name", "Vehicle Duplicator" )
|
|
language.Add( "tool.simfphysduplicator.desc", "Copy, Paste or Save your Vehicles" )
|
|
language.Add( "tool.simfphysduplicator.0", "Left click to spawn or update. Right click to copy" )
|
|
language.Add( "tool.simfphysduplicator.1", "Left click to spawn or update. Right click to copy" )
|
|
|
|
local selecteditem = nil
|
|
local TOOLMemory = {}
|
|
|
|
net.Receive("sphys_dupe", function( length )
|
|
TOOLMemory = net.ReadTable()
|
|
end)
|
|
|
|
local function GetSaves( panel )
|
|
local saved_vehicles = file.Find("saved_vehicles/*.txt", "DATA")
|
|
local index = 0
|
|
local highlight = false
|
|
local offset = 22
|
|
|
|
for k,v in pairs(saved_vehicles) do
|
|
local printname = v
|
|
|
|
if not selecteditem then
|
|
selecteditem = printname
|
|
end
|
|
|
|
local Button = vgui.Create( "DButton", panel )
|
|
Button:SetText( printname )
|
|
Button:SetTextColor( Color( 255, 255, 255 ) )
|
|
Button:SetPos( 0,index * offset)
|
|
Button:SetSize( 280, offset )
|
|
Button.highlight = highlight
|
|
Button.printname = printname
|
|
Button.Paint = function( self, w, h )
|
|
|
|
local c_selected = Color( 128, 185, 128, 255 )
|
|
local c_normal = self.highlight and Color( 108, 111, 114, 200 ) or Color( 77, 80, 82, 200 )
|
|
local c_hovered = Color( 41, 128, 185, 255 )
|
|
local c_ = (selecteditem == self.printname) and c_selected or (self:IsHovered() and c_hovered or c_normal)
|
|
|
|
draw.RoundedBox( 5, 1, 1, w - 2, h - 1, c_ )
|
|
end
|
|
Button.DoClick = function( self )
|
|
selecteditem = self.printname
|
|
end
|
|
|
|
index = index + 1
|
|
highlight = not highlight
|
|
end
|
|
end
|
|
|
|
function TOOL.BuildCPanel( panel )
|
|
if not file.Exists( "saved_vehicles", "DATA" ) then
|
|
file.CreateDir( "saved_vehicles" )
|
|
end
|
|
|
|
local Frame = vgui.Create( "DFrame", panel )
|
|
Frame:SetPos( 10, 30 )
|
|
Frame:SetSize( 280, 320 )
|
|
Frame:SetTitle( "" )
|
|
Frame:SetVisible( true )
|
|
Frame:ShowCloseButton( false )
|
|
Frame:SetDraggable( false )
|
|
Frame.Paint = function( self, w, h )
|
|
draw.RoundedBox( 5, 0, 0, w, h, Color( 115, 115, 115, 255 ) )
|
|
draw.RoundedBox( 5, 1, 1, w - 2, h - 2, Color( 234, 234, 234, 255 ) )
|
|
end
|
|
|
|
local ScrollPanel = vgui.Create( "DScrollPanel", Frame )
|
|
ScrollPanel:SetSize( 280, 320 )
|
|
ScrollPanel:SetPos( 0, 0 )
|
|
|
|
GetSaves( ScrollPanel )
|
|
|
|
local Button = vgui.Create( "DButton", panel )
|
|
Button:SetText( "Save" )
|
|
Button:SetPos( 10, 350)
|
|
Button:SetSize( 280, 20 )
|
|
Button.DoClick = function( self )
|
|
if isstring(TOOLMemory.SpawnName) then
|
|
local Frame = vgui.Create( "DFrame" )
|
|
Frame:SetPos( gui.MouseX() - 100, gui.MouseY() - 30 )
|
|
Frame:SetSize( 280, 50 )
|
|
Frame:SetTitle( "Save As..." )
|
|
Frame:SetVisible( true )
|
|
Frame:ShowCloseButton( true )
|
|
Frame:MakePopup()
|
|
Frame:SetDraggable( true )
|
|
|
|
local TextEntry = vgui.Create( "DTextEntry", Frame )
|
|
TextEntry:SetPos( 5, 25 )
|
|
TextEntry:SetSize( 270, 20 )
|
|
|
|
TextEntry.OnEnter = function()
|
|
local Name = TextEntry:GetValue()
|
|
|
|
if Name ~= "" then
|
|
local DataString = ""
|
|
|
|
for k,v in pairs(TOOLMemory) do
|
|
if k == "SubMaterials" then
|
|
local mats = ""
|
|
local first = true
|
|
for k, v in pairs( v ) do
|
|
if first then
|
|
first = false
|
|
mats = mats..v
|
|
else
|
|
mats = mats..","..v
|
|
end
|
|
end
|
|
DataString = DataString..k.."="..mats.."#"
|
|
else
|
|
DataString = DataString..k.."="..tostring( v ).."#"
|
|
end
|
|
end
|
|
|
|
local words = string.Explode( "", DataString )
|
|
local shit = {}
|
|
|
|
for k, v in pairs( words ) do
|
|
shit[k] = string.char( string.byte( v ) + 20 )
|
|
end
|
|
|
|
file.Write("saved_vehicles/"..Name..".txt", string.Implode("",shit) )
|
|
|
|
ScrollPanel:Clear()
|
|
selecteditem = Name..".txt"
|
|
GetSaves( ScrollPanel )
|
|
end
|
|
|
|
Frame:Close()
|
|
end
|
|
end
|
|
end
|
|
|
|
local Button = vgui.Create( "DButton", panel )
|
|
Button:SetText( "Load" )
|
|
Button:SetPos( 10, 370)
|
|
Button:SetSize( 280, 20 )
|
|
Button.DoClick = function( self )
|
|
if isstring(selecteditem) then
|
|
if not file.Exists( "saved_vehicles/"..selecteditem, "DATA" ) then
|
|
ScrollPanel:Clear()
|
|
selecteditem = nil
|
|
GetSaves( ScrollPanel )
|
|
|
|
return
|
|
end
|
|
|
|
local DataString = file.Read( "saved_vehicles/"..selecteditem, "DATA" )
|
|
|
|
local words = string.Explode( "", DataString )
|
|
local shit = {}
|
|
|
|
for k, v in pairs( words ) do
|
|
shit[k] = string.char( string.byte( v ) - 20 )
|
|
end
|
|
|
|
local Data = string.Explode( "#", string.Implode("",shit) )
|
|
|
|
table.Empty( TOOLMemory )
|
|
|
|
for _,v in pairs(Data) do
|
|
local Var = string.Explode( "=", v )
|
|
local name = Var[1]
|
|
local variable = Var[2]
|
|
|
|
if name and variable then
|
|
if name == "SubMaterials" then
|
|
TOOLMemory[name] = {}
|
|
|
|
local submats = string.Explode( ",", variable )
|
|
for i = 0, (table.Count( submats ) - 1) do
|
|
TOOLMemory[name][i] = submats[i+1]
|
|
end
|
|
else
|
|
TOOLMemory[name] = variable
|
|
end
|
|
end
|
|
end
|
|
|
|
net.Start("sphys_dupe")
|
|
net.WriteTable( TOOLMemory )
|
|
net.SendToServer()
|
|
end
|
|
end
|
|
|
|
local Button = vgui.Create( "DButton", panel )
|
|
Button:SetText( "Delete" )
|
|
Button:SetPos( 10, 430)
|
|
Button:SetSize( 280, 20 )
|
|
Button.DoClick = function( self )
|
|
|
|
if isstring(selecteditem) then
|
|
file.Delete( "saved_vehicles/"..selecteditem )
|
|
end
|
|
|
|
ScrollPanel:Clear()
|
|
selecteditem = nil
|
|
GetSaves( ScrollPanel )
|
|
end
|
|
|
|
local Button = vgui.Create( "DButton", panel )
|
|
Button:SetText( "Refresh" )
|
|
Button:SetPos( 10, 390)
|
|
Button:SetSize( 280, 20 )
|
|
Button.DoClick = function( self )
|
|
ScrollPanel:Clear()
|
|
selecteditem = nil
|
|
GetSaves( ScrollPanel )
|
|
end
|
|
end
|
|
end
|
|
|
|
local function ValidateModel( model )
|
|
local v_list = list.Get( "simfphys_vehicles" )
|
|
for listname, _ in pairs( v_list ) do
|
|
if v_list[listname].Members.CustomWheels then
|
|
local FrontWheel = v_list[listname].Members.CustomWheelModel
|
|
local RearWheel = v_list[listname].Members.CustomWheelModel_R
|
|
|
|
if FrontWheel then
|
|
FrontWheel = string.lower( FrontWheel )
|
|
end
|
|
|
|
if RearWheel then
|
|
RearWheel = string.lower( RearWheel )
|
|
end
|
|
|
|
if model == FrontWheel or model == RearWheel then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
local list = list.Get( "simfphys_Wheels" )[model]
|
|
|
|
if list then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function TOOL:GetVehicleData( ent, ply )
|
|
if not IsValid(ent) then return end
|
|
if not istable(ply.TOOLMemory) then ply.TOOLMemory = {} end
|
|
|
|
table.Empty( ply.TOOLMemory )
|
|
|
|
ply.TOOLMemory.SpawnName = ent:GetSpawn_List()
|
|
ply.TOOLMemory.SteerSpeed = ent:GetSteerSpeed()
|
|
ply.TOOLMemory.SteerFadeSpeed = ent:GetFastSteerConeFadeSpeed()
|
|
ply.TOOLMemory.SteerAngFast = ent:GetFastSteerAngle()
|
|
ply.TOOLMemory.SoundPreset = ent:GetEngineSoundPreset()
|
|
ply.TOOLMemory.IdleRPM = ent:GetIdleRPM()
|
|
ply.TOOLMemory.MaxRPM = ent:GetLimitRPM()
|
|
ply.TOOLMemory.PowerStart = ent:GetPowerBandStart()
|
|
ply.TOOLMemory.PowerEnd = ent:GetPowerBandEnd()
|
|
ply.TOOLMemory.PeakTorque = ent:GetMaxTorque()
|
|
ply.TOOLMemory.HasTurbo = ent:GetTurboCharged()
|
|
ply.TOOLMemory.HasBlower = ent:GetSuperCharged()
|
|
ply.TOOLMemory.HasRevLimiter = ent:GetRevlimiter()
|
|
ply.TOOLMemory.HasBulletProofTires = ent:GetBulletProofTires()
|
|
ply.TOOLMemory.MaxTraction = ent:GetMaxTraction()
|
|
ply.TOOLMemory.GripOffset = ent:GetTractionBias()
|
|
ply.TOOLMemory.BrakePower = ent:GetBrakePower()
|
|
ply.TOOLMemory.PowerDistribution = ent:GetPowerDistribution()
|
|
ply.TOOLMemory.Efficiency = ent:GetEfficiency()
|
|
ply.TOOLMemory.HornSound = ent.snd_horn
|
|
ply.TOOLMemory.HasBackfire = ent:GetBackFire()
|
|
ply.TOOLMemory.DoesntStall = ent:GetDoNotStall()
|
|
ply.TOOLMemory.SoundOverride = ent:GetSoundoverride()
|
|
|
|
ply.TOOLMemory.FrontHeight = ent:GetFrontSuspensionHeight()
|
|
ply.TOOLMemory.RearHeight = ent:GetRearSuspensionHeight()
|
|
|
|
ply.TOOLMemory.Camber = ent.Camber or 0
|
|
|
|
if ent.FrontDampingOverride and ent.FrontConstantOverride and ent.RearDampingOverride and ent.RearConstantOverride then
|
|
ply.TOOLMemory.FrontDampingOverride = ent.FrontDampingOverride
|
|
ply.TOOLMemory.FrontConstantOverride = ent.FrontConstantOverride
|
|
ply.TOOLMemory.RearDampingOverride = ent.RearDampingOverride
|
|
ply.TOOLMemory.RearConstantOverride = ent.RearConstantOverride
|
|
end
|
|
|
|
if ent.CustomWheels then
|
|
if ent.GhostWheels then
|
|
if IsValid(ent.GhostWheels[1]) then
|
|
ply.TOOLMemory.FrontWheelOverride = ent.GhostWheels[1]:GetModel()
|
|
elseif IsValid(ent.GhostWheels[2]) then
|
|
ply.TOOLMemory.FrontWheelOverride = ent.GhostWheels[2]:GetModel()
|
|
end
|
|
|
|
if IsValid(ent.GhostWheels[3]) then
|
|
ply.TOOLMemory.RearWheelOverride = ent.GhostWheels[3]:GetModel()
|
|
elseif IsValid(ent.GhostWheels[4]) then
|
|
ply.TOOLMemory.RearWheelOverride = ent.GhostWheels[4]:GetModel()
|
|
end
|
|
end
|
|
end
|
|
|
|
local tsc = ent:GetTireSmokeColor()
|
|
ply.TOOLMemory.TireSmokeColor = tsc.r..","..tsc.g..","..tsc.b
|
|
|
|
local Gears = ""
|
|
for _,v in pairs(ent.Gears) do
|
|
Gears = Gears..v..","
|
|
end
|
|
|
|
local c = ent:GetColor()
|
|
ply.TOOLMemory.Color = c.r..","..c.g..","..c.b..","..c.a
|
|
|
|
local bodygroups = {}
|
|
for k,v in pairs(ent:GetBodyGroups()) do
|
|
bodygroups[k] = ent:GetBodygroup( k )
|
|
end
|
|
|
|
ply.TOOLMemory.BodyGroups = string.Implode( ",", bodygroups)
|
|
|
|
ply.TOOLMemory.Skin = ent:GetSkin()
|
|
|
|
ply.TOOLMemory.Gears = Gears
|
|
ply.TOOLMemory.FinalGear = ent:GetDifferentialGear()
|
|
|
|
if ent.WheelTool_Foffset then
|
|
ply.TOOLMemory.WheelTool_Foffset = ent.WheelTool_Foffset
|
|
end
|
|
|
|
if ent.WheelTool_Roffset then
|
|
ply.TOOLMemory.WheelTool_Roffset = ent.WheelTool_Roffset
|
|
end
|
|
|
|
if ent.snd_blowoff then
|
|
ply.TOOLMemory.snd_blowoff = ent.snd_blowoff
|
|
end
|
|
|
|
if ent.snd_spool then
|
|
ply.TOOLMemory.snd_spool = ent.snd_spool
|
|
end
|
|
|
|
if ent.snd_bloweron then
|
|
ply.TOOLMemory.snd_bloweron = ent.snd_bloweron
|
|
end
|
|
|
|
if ent.snd_bloweroff then
|
|
ply.TOOLMemory.snd_bloweroff = ent.snd_bloweroff
|
|
end
|
|
|
|
ply.TOOLMemory.backfiresound = ent:GetBackfireSound()
|
|
|
|
ply.TOOLMemory.SubMaterials = {}
|
|
for i = 0, (table.Count( ent:GetMaterials() ) - 1) do
|
|
ply.TOOLMemory.SubMaterials[i] = ent:GetSubMaterial( i )
|
|
end
|
|
|
|
if not IsValid( ply ) then return end
|
|
|
|
net.Start("sphys_dupe")
|
|
net.WriteTable( ply.TOOLMemory )
|
|
net.Send( ply )
|
|
end
|
|
|
|
local function GetRight( ent, index, WheelPos )
|
|
local Steer = ent:GetTransformedDirection()
|
|
|
|
local Right = ent.Right
|
|
|
|
if WheelPos.IsFrontWheel then
|
|
Right = (IsValid( ent.SteerMaster ) and Steer.Right or ent.Right) * (WheelPos.IsRightWheel and 1 or -1)
|
|
else
|
|
Right = (IsValid( ent.SteerMaster ) and Steer.Right2 or ent.Right) * (WheelPos.IsRightWheel and 1 or -1)
|
|
end
|
|
|
|
return Right
|
|
end
|
|
|
|
local function SetWheelOffset( ent, offset_front, offset_rear )
|
|
if not IsValid( ent ) then return end
|
|
|
|
ent.WheelTool_Foffset = offset_front
|
|
ent.WheelTool_Roffset = offset_rear
|
|
|
|
if not istable( ent.Wheels ) or not istable( ent.GhostWheels ) then return end
|
|
|
|
for i = 1, table.Count( ent.GhostWheels ) do
|
|
local Wheel = ent.Wheels[ i ]
|
|
local WheelModel = ent.GhostWheels[i]
|
|
local WheelPos = ent:LogicWheelPos( i )
|
|
|
|
if IsValid( Wheel ) and IsValid( WheelModel ) then
|
|
local Pos = Wheel:GetPos()
|
|
local Right = GetRight( ent, i, WheelPos )
|
|
local offset = WheelPos.IsFrontWheel and offset_front or offset_rear
|
|
|
|
WheelModel:SetParent( nil )
|
|
|
|
local physObj = WheelModel:GetPhysicsObject()
|
|
if IsValid( physObj ) then
|
|
physObj:EnableMotion( false )
|
|
end
|
|
|
|
WheelModel:SetPos( Pos + Right * offset )
|
|
WheelModel:SetParent( Wheel )
|
|
end
|
|
end
|
|
end
|
|
|
|
local function ApplyWheel(ent, data)
|
|
ent.CustomWheelAngleOffset = data[2]
|
|
ent.CustomWheelAngleOffset_R = data[4]
|
|
|
|
timer.Simple( 0.05, function()
|
|
if not IsValid( ent ) then return end
|
|
for i = 1, table.Count( ent.GhostWheels ) do
|
|
local Wheel = ent.GhostWheels[i]
|
|
|
|
if IsValid( Wheel ) then
|
|
local isfrontwheel = (i == 1 or i == 2)
|
|
local swap_y = (i == 2 or i == 4 or i == 6)
|
|
|
|
local angleoffset = isfrontwheel and ent.CustomWheelAngleOffset or ent.CustomWheelAngleOffset_R
|
|
|
|
local model = isfrontwheel and data[1] or data[3]
|
|
|
|
local fAng = ent:LocalToWorldAngles( ent.VehicleData.LocalAngForward )
|
|
local rAng = ent:LocalToWorldAngles( ent.VehicleData.LocalAngRight )
|
|
|
|
local Forward = fAng:Forward()
|
|
local Right = swap_y and -rAng:Forward() or rAng:Forward()
|
|
local Up = ent:GetUp()
|
|
|
|
local Camber = data[5] or 0
|
|
|
|
local ghostAng = Right:Angle()
|
|
local mirAng = swap_y and 1 or -1
|
|
ghostAng:RotateAroundAxis(Forward,angleoffset.p * mirAng)
|
|
ghostAng:RotateAroundAxis(Right,angleoffset.r * mirAng)
|
|
ghostAng:RotateAroundAxis(Up,-angleoffset.y)
|
|
|
|
ghostAng:RotateAroundAxis(Forward, Camber * mirAng)
|
|
|
|
Wheel:SetModelScale( 1 )
|
|
Wheel:SetModel( model )
|
|
Wheel:SetAngles( ghostAng )
|
|
|
|
timer.Simple( 0.05, function()
|
|
if not IsValid(Wheel) or not IsValid( ent ) then return end
|
|
local wheelsize = Wheel:OBBMaxs() - Wheel:OBBMins()
|
|
local radius = isfrontwheel and ent.FrontWheelRadius or ent.RearWheelRadius
|
|
local size = (radius * 2) / math.max(wheelsize.x,wheelsize.y,wheelsize.z)
|
|
|
|
Wheel:SetModelScale( size )
|
|
end)
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function GetAngleFromSpawnlist( model )
|
|
if not model then print("invalid model") return Angle(0,0,0) end
|
|
|
|
model = string.lower( model )
|
|
|
|
local v_list = list.Get( "simfphys_vehicles" )
|
|
for listname, _ in pairs( v_list ) do
|
|
if v_list[listname].Members.CustomWheels then
|
|
local FrontWheel = v_list[listname].Members.CustomWheelModel
|
|
local RearWheel = v_list[listname].Members.CustomWheelModel_R
|
|
|
|
if FrontWheel then
|
|
FrontWheel = string.lower( FrontWheel )
|
|
end
|
|
|
|
if RearWheel then
|
|
RearWheel = string.lower( RearWheel )
|
|
end
|
|
|
|
if model == FrontWheel or model == RearWheel then
|
|
local Angleoffset = v_list[listname].Members.CustomWheelAngleOffset
|
|
if (Angleoffset) then
|
|
return Angleoffset
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local list = list.Get( "simfphys_Wheels" )[model]
|
|
local output = list and list.Angle or Angle(0,0,0)
|
|
|
|
return output
|
|
end
|
|
|
|
function TOOL:LeftClick( trace )
|
|
if CLIENT then return true end
|
|
|
|
local Ent = trace.Entity
|
|
|
|
local ply = self:GetOwner()
|
|
|
|
if not istable(ply.TOOLMemory) then return end
|
|
|
|
local vname = ply.TOOLMemory.SpawnName
|
|
local Update = false
|
|
local VehicleList = list.Get( "simfphys_vehicles" )
|
|
local vehicle = VehicleList[ vname ]
|
|
|
|
if not vehicle then return false end
|
|
|
|
ply.LockRightClick = true
|
|
timer.Simple( 0.6, function() if IsValid( ply ) then ply.LockRightClick = false end end )
|
|
|
|
local SpawnPos = trace.HitPos + Vector(0,0,25) + (vehicle.SpawnOffset or Vector(0,0,0))
|
|
|
|
local SpawnAng = self:GetOwner():EyeAngles()
|
|
SpawnAng.pitch = 0
|
|
SpawnAng.yaw = SpawnAng.yaw + 180 + (vehicle.SpawnAngleOffset and vehicle.SpawnAngleOffset or 0)
|
|
SpawnAng.roll = 0
|
|
|
|
if simfphys.IsCar( Ent ) then
|
|
if vname ~= Ent:GetSpawn_List() then
|
|
ply:PrintMessage( HUD_PRINTTALK, vname.." is not compatible with "..Ent:GetSpawn_List() )
|
|
return
|
|
end
|
|
Update = true
|
|
else
|
|
Ent = simfphys.SpawnVehicle( ply, SpawnPos, SpawnAng, vehicle.Model, vehicle.Class, vname, vehicle )
|
|
end
|
|
|
|
if not IsValid( Ent ) then return end
|
|
|
|
undo.Create( "Vehicle" )
|
|
undo.SetPlayer( ply )
|
|
undo.AddEntity( Ent )
|
|
undo.SetCustomUndoText( "Undone " .. vehicle.Name )
|
|
undo.Finish( "Vehicle (" .. tostring( vehicle.Name ) .. ")" )
|
|
|
|
ply:AddCleanup( "vehicles", Ent )
|
|
|
|
timer.Simple( 0.5, function()
|
|
if not IsValid(Ent) then return end
|
|
|
|
local tsc = string.Explode( ",", ply.TOOLMemory.TireSmokeColor )
|
|
Ent:SetTireSmokeColor( Vector( tonumber(tsc[1]), tonumber(tsc[2]), tonumber(tsc[3]) ) )
|
|
|
|
Ent.Turbocharged = tobool( ply.TOOLMemory.HasTurbo )
|
|
Ent.Supercharged = tobool( ply.TOOLMemory.HasBlower )
|
|
|
|
Ent:SetEngineSoundPreset( math.Clamp( tonumber( ply.TOOLMemory.SoundPreset ), -1, 23) )
|
|
Ent:SetMaxTorque( math.Clamp( tonumber( ply.TOOLMemory.PeakTorque ), 20, 1000) )
|
|
Ent:SetDifferentialGear( math.Clamp( tonumber( ply.TOOLMemory.FinalGear ),0.2, 6 ) )
|
|
|
|
Ent:SetSteerSpeed( math.Clamp( tonumber( ply.TOOLMemory.SteerSpeed ), 1, 16 ) )
|
|
Ent:SetFastSteerAngle( math.Clamp( tonumber( ply.TOOLMemory.SteerAngFast ), 0, 1) )
|
|
Ent:SetFastSteerConeFadeSpeed( math.Clamp( tonumber( ply.TOOLMemory.SteerFadeSpeed ), 1, 5000 ) )
|
|
|
|
Ent:SetEfficiency( math.Clamp( tonumber( ply.TOOLMemory.Efficiency ) ,0.2,4) )
|
|
Ent:SetMaxTraction( math.Clamp( tonumber( ply.TOOLMemory.MaxTraction ) , 5,1000) )
|
|
Ent:SetTractionBias( math.Clamp( tonumber( ply.TOOLMemory.GripOffset ),-0.99,0.99) )
|
|
Ent:SetPowerDistribution( math.Clamp( tonumber( ply.TOOLMemory.PowerDistribution ) ,-1,1) )
|
|
|
|
Ent:SetBackFire( tobool( ply.TOOLMemory.HasBackfire ) )
|
|
Ent:SetDoNotStall( tobool( ply.TOOLMemory.DoesntStall ) )
|
|
|
|
Ent:SetIdleRPM( math.Clamp( tonumber( ply.TOOLMemory.IdleRPM ),1,25000) )
|
|
Ent:SetLimitRPM( math.Clamp( tonumber( ply.TOOLMemory.MaxRPM ),4,25000) )
|
|
Ent:SetRevlimiter( tobool( ply.TOOLMemory.HasRevLimiter ) )
|
|
Ent:SetPowerBandEnd( math.Clamp( tonumber( ply.TOOLMemory.PowerEnd ), 3, 25000) )
|
|
Ent:SetPowerBandStart( math.Clamp( tonumber( ply.TOOLMemory.PowerStart ) ,2 ,25000) )
|
|
|
|
Ent:SetTurboCharged( Ent.Turbocharged )
|
|
Ent:SetSuperCharged( Ent.Supercharged )
|
|
Ent:SetBrakePower( math.Clamp( tonumber( ply.TOOLMemory.BrakePower ), 0.1, 500) )
|
|
|
|
Ent:SetSoundoverride( ply.TOOLMemory.SoundOverride or "" )
|
|
|
|
Ent:SetLights_List( Ent.LightsTable or "no_lights" )
|
|
|
|
Ent:SetBulletProofTires( tobool( ply.TOOLMemory.HasBulletProofTires ) )
|
|
|
|
Ent.snd_horn = ply.TOOLMemory.HornSound
|
|
|
|
Ent.snd_blowoff = ply.TOOLMemory.snd_blowoff
|
|
Ent.snd_spool = ply.TOOLMemory.snd_spool
|
|
Ent.snd_bloweron = ply.TOOLMemory.snd_bloweron
|
|
Ent.snd_bloweroff = ply.TOOLMemory.snd_bloweroff
|
|
|
|
Ent:SetBackfireSound( ply.TOOLMemory.backfiresound or "" )
|
|
|
|
local Gears = {}
|
|
local Data = string.Explode( ",", ply.TOOLMemory.Gears )
|
|
for i = 1, table.Count( Data ) do
|
|
local gRatio = tonumber( Data[i] )
|
|
|
|
if isnumber( gRatio ) then
|
|
if i == 1 then
|
|
Gears[i] = math.Clamp( gRatio, -5, -0.001)
|
|
|
|
elseif i == 2 then
|
|
Gears[i] = 0
|
|
|
|
else
|
|
Gears[i] = math.Clamp( gRatio, 0.001, 5)
|
|
end
|
|
end
|
|
end
|
|
Ent.Gears = Gears
|
|
|
|
if istable( ply.TOOLMemory.SubMaterials ) then
|
|
for i = 0, table.Count( ply.TOOLMemory.SubMaterials ) do
|
|
Ent:SetSubMaterial( i, ply.TOOLMemory.SubMaterials[i] )
|
|
end
|
|
end
|
|
|
|
if ply.TOOLMemory.FrontDampingOverride and ply.TOOLMemory.FrontConstantOverride and ply.TOOLMemory.RearDampingOverride and ply.TOOLMemory.RearConstantOverride then
|
|
Ent.FrontDampingOverride = tonumber( ply.TOOLMemory.FrontDampingOverride )
|
|
Ent.FrontConstantOverride = tonumber( ply.TOOLMemory.FrontConstantOverride )
|
|
Ent.RearDampingOverride = tonumber( ply.TOOLMemory.RearDampingOverride )
|
|
Ent.RearConstantOverride = tonumber( ply.TOOLMemory.RearConstantOverride )
|
|
|
|
local data = {
|
|
[1] = {Ent.FrontConstantOverride,Ent.FrontDampingOverride},
|
|
[2] = {Ent.FrontConstantOverride,Ent.FrontDampingOverride},
|
|
[3] = {Ent.RearConstantOverride,Ent.RearDampingOverride},
|
|
[4] = {Ent.RearConstantOverride,Ent.RearDampingOverride},
|
|
[5] = {Ent.RearConstantOverride,Ent.RearDampingOverride},
|
|
[6] = {Ent.RearConstantOverride,Ent.RearDampingOverride}
|
|
}
|
|
|
|
local elastics = Ent.Elastics
|
|
if elastics then
|
|
for i = 1, table.Count( elastics ) do
|
|
local elastic = elastics[i]
|
|
if Ent.StrengthenSuspension == true then
|
|
if IsValid( elastic ) then
|
|
elastic:Fire( "SetSpringConstant", data[i][1] * 0.5, 0 )
|
|
elastic:Fire( "SetSpringDamping", data[i][2] * 0.5, 0 )
|
|
end
|
|
local elastic2 = elastics[i * 10]
|
|
if IsValid( elastic2 ) then
|
|
elastic2:Fire( "SetSpringConstant", data[i][1] * 0.5, 0 )
|
|
elastic2:Fire( "SetSpringDamping", data[i][2] * 0.5, 0 )
|
|
end
|
|
else
|
|
if IsValid( elastic ) then
|
|
elastic:Fire( "SetSpringConstant", data[i][1], 0 )
|
|
elastic:Fire( "SetSpringDamping", data[i][2], 0 )
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
Ent:SetFrontSuspensionHeight( tonumber( ply.TOOLMemory.FrontHeight ) )
|
|
Ent:SetRearSuspensionHeight( tonumber( ply.TOOLMemory.RearHeight ) )
|
|
|
|
local groups = string.Explode( ",", ply.TOOLMemory.BodyGroups)
|
|
for i = 1, table.Count( groups ) do
|
|
Ent:SetBodygroup(i, tonumber(groups[i]) )
|
|
end
|
|
|
|
Ent:SetSkin( ply.TOOLMemory.Skin )
|
|
|
|
local c = string.Explode( ",", ply.TOOLMemory.Color )
|
|
local Color = Color( tonumber(c[1]), tonumber(c[2]), tonumber(c[3]), tonumber(c[4]) )
|
|
|
|
local dot = Color.r * Color.g * Color.b * Color.a
|
|
Ent.OldColor = dot
|
|
Ent:SetColor( Color )
|
|
|
|
local data = {
|
|
Color = Color,
|
|
RenderMode = 0,
|
|
RenderFX = 0
|
|
}
|
|
duplicator.StoreEntityModifier( Ent, "colour", data )
|
|
|
|
if Update then
|
|
local PhysObj = Ent:GetPhysicsObject()
|
|
if not IsValid( PhysObj ) then return end
|
|
|
|
local freezeWhenDone = PhysObj:IsMotionEnabled()
|
|
local freezeWheels = {}
|
|
PhysObj:EnableMotion( false )
|
|
Ent:SetNotSolid( true )
|
|
|
|
local ResetPos = Ent:GetPos()
|
|
local ResetAng = Ent:GetAngles()
|
|
|
|
Ent:SetPos( ResetPos + Vector(0,0,30) )
|
|
Ent:SetAngles( Angle(0,ResetAng.y,0) )
|
|
|
|
for i = 1, table.Count( Ent.Wheels ) do
|
|
local Wheel = Ent.Wheels[ i ]
|
|
if IsValid( Wheel ) then
|
|
local wPObj = Wheel:GetPhysicsObject()
|
|
|
|
if IsValid( wPObj ) then
|
|
freezeWheels[ i ] = {}
|
|
freezeWheels[ i ].dofreeze = wPObj:IsMotionEnabled()
|
|
freezeWheels[ i ].pos = Wheel:GetPos()
|
|
freezeWheels[ i ].ang = Wheel:GetAngles()
|
|
Wheel:SetNotSolid( true )
|
|
wPObj:EnableMotion( true )
|
|
wPObj:Wake()
|
|
end
|
|
end
|
|
end
|
|
|
|
timer.Simple( 0.5, function()
|
|
if not IsValid( Ent ) then return end
|
|
if not IsValid( PhysObj ) then return end
|
|
|
|
PhysObj:EnableMotion( freezeWhenDone )
|
|
Ent:SetNotSolid( false )
|
|
Ent:SetPos( ResetPos )
|
|
Ent:SetAngles( ResetAng )
|
|
|
|
for i = 1, table.Count( freezeWheels ) do
|
|
local Wheel = Ent.Wheels[ i ]
|
|
if IsValid( Wheel ) then
|
|
local wPObj = Wheel:GetPhysicsObject()
|
|
|
|
Wheel:SetNotSolid( false )
|
|
|
|
if IsValid( wPObj ) then
|
|
wPObj:EnableMotion( freezeWheels[i].dofreeze )
|
|
end
|
|
|
|
Wheel:SetPos( freezeWheels[ i ].pos )
|
|
Wheel:SetAngles( freezeWheels[ i ].ang )
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
if Ent.CustomWheels then
|
|
if Ent.GhostWheels then
|
|
timer.Simple( Update and 0.25 or 0, function()
|
|
if not IsValid( Ent ) then return end
|
|
if ply.TOOLMemory.WheelTool_Foffset and ply.TOOLMemory.WheelTool_Roffset then
|
|
SetWheelOffset( Ent, ply.TOOLMemory.WheelTool_Foffset, ply.TOOLMemory.WheelTool_Roffset )
|
|
end
|
|
|
|
if not ply.TOOLMemory.FrontWheelOverride and not ply.TOOLMemory.RearWheelOverride then return end
|
|
|
|
local front_model = ply.TOOLMemory.FrontWheelOverride or vehicle.Members.CustomWheelModel
|
|
local front_angle = GetAngleFromSpawnlist(front_model)
|
|
|
|
local camber = ply.TOOLMemory.Camber or 0
|
|
local rear_model = ply.TOOLMemory.RearWheelOverride or (vehicle.Members.CustomWheelModel_R and vehicle.Members.CustomWheelModel_R or front_model)
|
|
local rear_angle = GetAngleFromSpawnlist(rear_model)
|
|
|
|
if not front_model or not rear_model or not front_angle or not rear_angle then return end
|
|
|
|
if ValidateModel( front_model ) and ValidateModel( rear_model ) then
|
|
Ent.Camber = camber
|
|
ApplyWheel(Ent, {front_model,front_angle,rear_model,rear_angle,camber})
|
|
else
|
|
ply:PrintMessage( HUD_PRINTTALK, "selected wheel does not exist on the server")
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
end)
|
|
|
|
return true
|
|
end
|
|
|
|
function TOOL:RightClick( trace )
|
|
if CLIENT then return true end
|
|
|
|
local ent = trace.Entity
|
|
local ply = self:GetOwner()
|
|
|
|
if ply.LockRightClick then ply:PrintMessage( HUD_PRINTTALK, "Duplicator is busy") return end
|
|
|
|
if not istable(ply.TOOLMemory) then
|
|
ply.TOOLMemory = {}
|
|
end
|
|
|
|
if not IsValid(ent) then
|
|
table.Empty( ply.TOOLMemory )
|
|
|
|
net.Start("sphys_dupe")
|
|
net.WriteTable( ply.TOOLMemory )
|
|
net.Send( ply )
|
|
|
|
return false
|
|
end
|
|
|
|
if not simfphys.IsCar( ent ) then return false end
|
|
|
|
self:GetVehicleData( ent, ply )
|
|
|
|
return true
|
|
end
|
|
|
|
function TOOL:Reload( trace )
|
|
return false
|
|
end
|