--[[ | 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