--[[ | 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/ --]] AddCSLuaFile( "shared.lua" ) AddCSLuaFile( "cl_init.lua" ) AddCSLuaFile( "cl_tiresounds.lua" ) include("shared.lua") include("spawn.lua") include("simfunc.lua") include("numpads.lua") include("damage.lua") function ENT:AlignView( ply ) if not IsValid( ply ) then return end timer.Simple( 0, function() if not IsValid( ply ) or not IsValid( self ) then return end local Ang = Angle(0,90,0) local pod = ply:GetVehicle() local MouseAim = ply:lvsMouseAim() and self:GetDriver() == ply if MouseAim and IsValid( pod ) then Ang = pod:LocalToWorldAngles( Angle(0,90,0) ) Ang.r = 0 end ply:SetEyeAngles( Ang ) end) end function ENT:TakeCollisionDamage( damage, attacker ) end local function EntityLookup(CreatedEntities) return function(id, default) if id == nil then return default end if id == 0 then return game.GetWorld() end local ent = CreatedEntities[id] or (isnumber(id) and ents.GetByIndex(id)) if IsValid(ent) then return ent else return default end end end function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) if istable( WireLib ) then WireLib.ApplyDupeInfo(ply, ent, info, GetEntByID) end end function ENT:PreEntityCopy() if istable( WireLib ) then duplicator.StoreEntityModifier( self, "WireDupeInfo", WireLib.BuildDupeInfo(self) ) end end function ENT:PostEntityPaste(Player,Ent,CreatedEntities) if istable( WireLib ) then if Ent.EntityMods and Ent.EntityMods.WireDupeInfo then WireLib.ApplyDupeInfo(Player, Ent, Ent.EntityMods.WireDupeInfo, EntityLookup(CreatedEntities)) end end end function ENT:OnSpawn() end function ENT:OnTick() end function ENT:OnDelete() end function ENT:OnDestroyed() end function ENT:OnRepaired() end function ENT:Think() local Time = CurTime() self:PhysicsThink() self:OnTick() hook.Run( "simfphysOnTick", self, Time ) self.NextTick = self.NextTick or 0 if self.NextTick < Time then self.NextTick = Time + 0.025 if IsValid( self.DriverSeat ) then local Driver = self.DriverSeat:GetDriver() Driver = IsValid( self.RemoteDriver ) and self.RemoteDriver or Driver local OldDriver = self:GetDriver() if OldDriver ~= Driver then if self:GetlvsLockedStatus() then self:UnLock() end self:SetDriver( Driver ) local HadDriver = IsValid( OldDriver ) local HasDriver = IsValid( Driver ) if HasDriver then self:SetActive( true ) self:SetupControls( Driver ) if Driver:GetInfoNum( "cl_simfphys_autostart", 1 ) > 0 then self:StartEngine() end else if self.ems then self.ems:Stop() end if self.horn then self.horn:Stop() end if self.PressedKeys then for k,v in pairs( self.PressedKeys ) do if isbool( v ) then self.PressedKeys[k] = false end end end if self.keys then for i = 1, table.Count( self.keys ) do numpad.Remove( self.keys[i] ) end end if HadDriver then if OldDriver:GetInfoNum( "cl_simfphys_autostart", 1 ) > 0 then self:StopEngine() self:SetActive( false ) else self:ResetJoystick() if not self:EngineActive() then self:SetActive( false ) end end else self:SetActive( false ) self:StopEngine() end end end end if self:IsInitialized() then self:SetColors() self:SimulateVehicle( Time ) self:ControlLighting( Time ) self:ControlHorn() if istable( WireLib ) then self:UpdateWireOutputs() end self.NextWaterCheck = self.NextWaterCheck or 0 if self.NextWaterCheck < Time then self.NextWaterCheck = Time + 0.2 self:WaterPhysics() end if self:GetActive() then self:SetPhysics( ((math.abs(self.ForwardSpeed) < 50) and (self.Brake > 0 or self.HandBrake > 0)) ) else self:SetPhysics( true ) end end end self:NextThink( Time ) return true end function ENT:ControlHorn() local HornVol = self.HornKeyIsDown and 1 or 0 self.HornVolume = self.HornVolume and self.HornVolume + math.Clamp(HornVol - self.HornVolume,-0.45,0.8) or 0 if self.horn then if self.HornVolume <= 0 then if self.horn then self.horn:Stop() self.horn = nil end else self.horn:ChangeVolume( self.HornVolume ^ 2 ) end end end function ENT:createWireIO() self.Inputs = WireLib.CreateInputs( self,{"Eject Driver","Eject Passengers","Lock","Engine Start","Engine Stop","Engine Toggle","Steer","Throttle","Gear Up","Gear Down","Set Gear","Clutch","Handbrake","Brake/Reverse"} ) --self.Inputs = WireLib.CreateSpecialInputs(self, { "blah" }, { "NORMAL" }) self.Outputs = WireLib.CreateSpecialOutputs( self, { "Active","Health","RPM","Torque","DriverSeat","PassengerSeats","Driver","Gear","Ratio","Lights Enabled","Highbeams Enabled","Foglights Enabled","Sirens Enabled","Turn Signals Enabled","Remaining Fuel" }, { "NORMAL","NORMAL","NORMAL","NORMAL","ENTITY","ARRAY","ENTITY","NORMAL","NORMAL","NORMAL","NORMAL","NORMAL","NORMAL","NORMAL","NORMAL" } ) end function ENT:TriggerInput( name, value ) if name == "Engine Start" then if value >= 1 then self:SetActive( true ) self:StartEngine() end end if name == "Engine Stop" then if value >= 1 then self:SetActive( false ) self:StopEngine() end end if name == "Engine Toggle" then if value >= 1 then if self:GetActive() then if not self:EngineActive() then self:StartEngine() else self:StopEngine() self:SetActive( false ) end else self:SetActive( true ) self:StartEngine() end end end if name == "Lock" then if value >= 1 then self:Lock() else self:UnLock() end end if name == "Eject Driver" then local Driver = self:GetDriver() if IsValid( Driver ) then Driver:ExitVehicle() end end if name == "Eject Passengers" then if istable( self.pSeat ) then for i = 1, table.Count( self.pSeat ) do if IsValid( self.pSeat[i] ) then local Driver = self.pSeat[i]:GetDriver() if IsValid( Driver ) then Driver:ExitVehicle() end end end end end if name == "Steer" then self:SteerVehicle( math.Clamp( value, -1 , 1) * self.VehicleData["steerangle"] ) for i = 1, table.Count(self.Wheels) do local Wheel = self.Wheels[i] if IsValid( Wheel ) then Wheel:PhysWake() end end end if name == "Throttle" then self.PressedKeys["joystick_throttle"] = math.Clamp( value, 0, 1 ) end if name == "Brake/Reverse" then self.PressedKeys["joystick_brake"] = math.Clamp( value, 0, 1 ) end if name == "Gear Up" then if value >= 1 then self.CurrentGear = math.Clamp(self.CurrentGear + 1,1,table.Count( self.Gears ) ) self:SetGear( self.CurrentGear ) end end if name == "Gear Down" then if value >= 1 then self.CurrentGear = math.Clamp(self.CurrentGear - 1,1,table.Count( self.Gears ) ) self:SetGear( self.CurrentGear ) end end if name == "Set Gear" then self:ForceGear( math.Round( value, 0 ) ) end if name == "Clutch" then self.PressedKeys["joystick_clutch"] = math.Clamp( value, 0, 1 ) end if name == "Handbrake" then self.PressedKeys["joystick_handbrake"] = (value > 0) and 1 or 0 end end function ENT:ForceGear( desGear ) self.CurrentGear = math.Clamp( math.Round( desGear, 0 ),1,table.Count( self.Gears ) ) self:SetGear( self.CurrentGear ) end function ENT:UpdateWireOutputs() WireLib.TriggerOutput(self, "Active", self:EngineActive() and 1 or 0 ) WireLib.TriggerOutput(self, "Health", self:GetCurHealth() ) WireLib.TriggerOutput(self, "Driver", self:GetDriver() ) WireLib.TriggerOutput(self, "Torque", self.Torque ) WireLib.TriggerOutput(self, "RPM", self:GetEngineRPM() ) WireLib.TriggerOutput(self, "Gear", self:GetGear() ) WireLib.TriggerOutput(self, "Ratio",self:GetGear() == 2 and 0 or (self.GearRatio or 0) ) WireLib.TriggerOutput(self, "Lights Enabled", self:GetLightsEnabled() and 1 or 0 ) WireLib.TriggerOutput(self, "Highbeams Enabled", self:GetLampsEnabled() and 1 or 0 ) WireLib.TriggerOutput(self, "Foglights Enabled", self:GetFogLightsEnabled() and 1 or 0 ) WireLib.TriggerOutput(self, "Sirens Enabled", self:GetEMSEnabled() and 1 or 0 ) WireLib.TriggerOutput(self, "Turn Signals Enabled", self:GetTSEnabled()) WireLib.TriggerOutput(self, "Remaining Fuel", self:GetFuel()) end function ENT:OnActiveChanged( name, old, new) if new == old then return end if not self:IsInitialized() then return end local TurboCharged = self:GetTurboCharged() local SuperCharged = self:GetSuperCharged() if new == true then self.HandBrakePower = self:GetMaxTraction() + 20 - self:GetTractionBias() * self:GetMaxTraction() if self:GetEMSEnabled() then if self.ems then self.ems:Play() end end if TurboCharged then self.Turbo = CreateSound(self, self.snd_spool or "simulated_vehicles/turbo_spin.wav") self.Turbo:PlayEx(0,0) end if SuperCharged then self.Blower = CreateSound(self, self.snd_bloweroff or "simulated_vehicles/blower_spin.wav") self.BlowerWhine = CreateSound(self, self.snd_bloweron or "simulated_vehicles/blower_gearwhine.wav") self.Blower:PlayEx(0,0) self.BlowerWhine:PlayEx(0,0) end else self:StopEngine() if TurboCharged then self.Turbo:Stop() end if SuperCharged then self.Blower:Stop() self.BlowerWhine:Stop() end self:SetIsBraking( false ) end if istable( self.Wheels ) then for i = 1, table.Count( self.Wheels ) do local Wheel = self.Wheels[ i ] if IsValid(Wheel) then Wheel:SetOnGround( 0 ) end end end end function ENT:OnHealthChanged( name, OldHealth, NewHealth) if NewHealth == OldHealth or NewHealth < OldHealth then return end local MaxHealth = self:GetMaxHealth() if NewHealth > (MaxHealth * 0.6) then self:SetOnFire( false ) self:SetOnSmoke( false ) end if NewHealth > (MaxHealth * 0.3) then self:SetOnFire( false ) if NewHealth <= (MaxHealth * 0.6) then self:SetOnSmoke( true ) end end end function ENT:OnThrottleChanged( name, old, new) if new == old then return end local Health = self:GetCurHealth() local MaxHealth = self:GetMaxHealth() local Active = self:EngineActive() if new == 1 then if Health < MaxHealth * 0.6 then if Active then if math.Round(math.random(0,4),0) == 1 then self:DamagedStall() end end end end if new == 0 then if self:GetTurboCharged() then if (self.SmoothTurbo > 350) then local Volume = math.Clamp( ((self.SmoothTurbo - 300) / 150) ,0, 1) * 0.5 self.SmoothTurbo = 0 self.BlowOff:Stop() self.BlowOff = CreateSound(self, self.snd_blowoff or "simulated_vehicles/turbo_blowoff.ogg") self.BlowOff:PlayEx(Volume,100) end end end end function ENT:WaterPhysics() if self:WaterLevel() <= 1 then self.IsInWater = false return end if self:GetDoNotStall() then self:SetOnFire( false ) self:SetOnSmoke( false ) return end if not self.IsInWater then if self:EngineActive() then self:EmitSound( "vehicles/jetski/jetski_off.wav" ) end self.IsInWater = true self.EngineIsOn = 0 self.EngineRPM = 0 self:SetFlyWheelRPM( 0 ) self:SetOnFire( false ) self:SetOnSmoke( false ) end local phys = self:GetPhysicsObject() phys:ApplyForceCenter( -self:GetVelocity() * 0.5 * phys:GetMass() ) end function ENT:SetColors() if self.ColorableProps then local Color = self:GetColor() local dot = Color.r * Color.g * Color.b * Color.a if dot ~= self.OldColor then for i, prop in pairs( self.ColorableProps ) do if IsValid(prop) then prop:SetColor( Color ) prop:SetRenderMode( self:GetRenderMode() ) end end self.OldColor = dot end end end function ENT:ControlLighting( curtime ) if (self.NextLightCheck or 0) < curtime then if self.LightsActivated ~= self.DoCheck then self.DoCheck = self.LightsActivated if self.LightsActivated then self:SetLightsEnabled(true) end end end end function ENT:SetTSInternal(mode) self.TSMode = mode end function ENT:GetTSEnabled() if self.TSMode != nil then return self.TSMode else return 0 end end function ENT:GetEngineData() local LimitRPM = math.max(self:GetLimitRPM(),4) local Powerbandend = math.Clamp(self:GetPowerBandEnd(),3,LimitRPM - 1) local Powerbandstart = math.Clamp(self:GetPowerBandStart(),2,Powerbandend - 1) local IdleRPM = math.Clamp(self:GetIdleRPM(),1,Powerbandstart - 1) local Data = { IdleRPM = IdleRPM, Powerbandstart = Powerbandstart, Powerbandend = Powerbandend, LimitRPM = LimitRPM, } return Data end function ENT:SimulateVehicle( curtime ) local Active = self:GetActive() local EngineData = self:GetEngineData() local LimitRPM = EngineData.LimitRPM local Powerbandend = EngineData.Powerbandend local Powerbandstart = EngineData.Powerbandstart local IdleRPM = EngineData.IdleRPM self.Forward = self:LocalToWorldAngles( self.VehicleData.LocalAngForward ):Forward() self.Right = self:LocalToWorldAngles( self.VehicleData.LocalAngRight ):Forward() self.Up = self:GetUp() self.Vel = self:GetVelocity() self.VelNorm = self.Vel:GetNormalized() self.MoveDir = math.acos( math.Clamp( self.Forward:Dot(self.VelNorm) ,-1,1) ) * (180 / math.pi) self.ForwardSpeed = math.cos(self.MoveDir * (math.pi / 180)) * self.Vel:Length() if self.poseon then self.cpose = self.cpose or self.LightsPP.min local anglestep = math.abs(math.max(self.LightsPP.max or self.LightsPP.min)) / 3 self.cpose = self.cpose + math.Clamp(self.poseon - self.cpose,-anglestep,anglestep) self:SetPoseParameter(self.LightsPP.name, self.cpose) end self:SetPoseParameter("vehicle_guage", (math.abs(self.ForwardSpeed) * 0.0568182 * 0.75) / (self.SpeedoMax or 120)) if self.RPMGaugePP then local flywheelrpm = self:GetFlyWheelRPM() local rpm if self:GetRevlimiter() then local throttle = self:GetThrottle() local maxrpm = self:GetLimitRPM() local revlimiter = (maxrpm > 2500) and (throttle > 0) rpm = math.Round(((flywheelrpm >= maxrpm - 200) and revlimiter) and math.Round(flywheelrpm - 200 + math.sin(curtime * 50) * 600,0) or flywheelrpm,0) else rpm = flywheelrpm end self:SetPoseParameter(self.RPMGaugePP, rpm / self.RPMGaugeMax) end if Active then local ply = self:GetDriver() local IsValidDriver = IsValid( ply ) local GearUp = self.PressedKeys["M1"] and 1 or self.PressedKeys["joystick_gearup"] local GearDown = self.PressedKeys["M2"] and 1 or self.PressedKeys["joystick_geardown"] local W = self.PressedKeys["W"] and 1 or 0 local A = self.PressedKeys["A"] and 1 or self.PressedKeys["joystick_steer_left"] local S = self.PressedKeys["S"] and 1 or 0 local D = self.PressedKeys["D"] and 1 or self.PressedKeys["joystick_steer_right"] if IsValidDriver then self:PlayerSteerVehicle( ply, A, D ) end local aW = self.PressedKeys["aW"] and 1 or self.PressedKeys["joystick_air_w"] local aA = self.PressedKeys["aA"] and 1 or self.PressedKeys["joystick_air_a"] local aS = self.PressedKeys["aS"] and 1 or self.PressedKeys["joystick_air_s"] local aD = self.PressedKeys["aD"] and 1 or self.PressedKeys["joystick_air_d"] local cruise = self:GetIsCruiseModeOn() local k_sanic = IsValidDriver and ply:GetInfoNum( "cl_simfphys_sanic", 0 ) or 1 local sanicmode = isnumber( k_sanic ) and k_sanic or 0 local k_Shift = self.PressedKeys["Shift"] local Shift = (sanicmode == 1) and (k_Shift and 0 or 1) or (k_Shift and 1 or 0) local sportsmode = IsValidDriver and ply:GetInfoNum( "cl_simfphys_sport", 0 ) or 1 local k_auto = IsValidDriver and ply:GetInfoNum( "cl_simfphys_auto", 0 ) or 1 local transmode = (k_auto == 1) local Alt = self.PressedKeys["Alt"] and 1 or 0 local Space = self.PressedKeys["Space"] and 1 or self.PressedKeys["joystick_handbrake"] if cruise then if (self.PressedKeys["joystick_gearup"] + self.PressedKeys["joystick_geardown"] + self.PressedKeys["joystick_handbrake"] + self.PressedKeys["joystick_throttle"] + self.PressedKeys["joystick_clutch"] + self.PressedKeys["joystick_brake"]) > 0 then self:SetIsCruiseModeOn( false ) end if k_Shift then self.cc_speed = math.Round(self:GetVelocity():Length(),0) + 70 end if Alt == 1 then self.cc_speed = math.Round(self:GetVelocity():Length(),0) - 25 end end self:SimulateTransmission(W,S,Shift,Alt,Space,GearUp,GearDown,transmode,IdleRPM,Powerbandstart,Powerbandend,sportsmode,cruise,curtime) self:SimulateEngine( IdleRPM, LimitRPM, Powerbandstart, Powerbandend, curtime ) self:SimulateWheels( math.max(Space,Alt), LimitRPM ) self:SimulateAirControls( aW, aS, aA, aD ) if self.WheelOnGroundDelay < curtime then self:WheelOnGround() self.WheelOnGroundDelay = curtime + 0.15 end else self:SetWheelVelocity( 0 ) for i = 1, table.Count( self.Wheels ) do local Wheel = self.Wheels[i] if not IsValid( Wheel ) or Wheel:GetRPM() == 0 then continue end Wheel:SetRPM( 0 ) end end if self.CustomWheels then self:PhysicalSteer() end end function ENT:SetupControls( ply ) self:ResetJoystick() if self.keys then for i = 1, table.Count( self.keys ) do numpad.Remove( self.keys[i] ) end end if IsValid(ply) then self.cl_SteerSettings = { Overwrite = (ply:GetInfoNum( "cl_simfphys_overwrite", 0 ) >= 1), TurnSpeed = ply:GetInfoNum( "cl_simfphys_steerspeed", 8 ), fadespeed = ply:GetInfoNum( "cl_simfphys_fadespeed", 535 ), fastspeedangle = ply:GetInfoNum( "cl_simfphys_steerangfast", 10 ), smoothsteer = (ply:GetInfoNum( "cl_simfphys_smoothsteer", 0 ) >= 1), } local W = ply:GetInfoNum( "cl_simfphys_keyforward", 0 ) local A = ply:GetInfoNum( "cl_simfphys_keyleft", 0 ) local S = ply:GetInfoNum( "cl_simfphys_keyreverse", 0 ) local D = ply:GetInfoNum( "cl_simfphys_keyright", 0 ) local aW = ply:GetInfoNum( "cl_simfphys_key_air_forward", 0 ) local aA = ply:GetInfoNum( "cl_simfphys_key_air_left", 0 ) local aS = ply:GetInfoNum( "cl_simfphys_key_air_reverse", 0 ) local aD = ply:GetInfoNum( "cl_simfphys_key_air_right", 0 ) local GearUp = ply:GetInfoNum( "cl_simfphys_keygearup", 0 ) local GearDown = ply:GetInfoNum( "cl_simfphys_keygeardown", 0 ) local R = ply:GetInfoNum( "cl_simfphys_cruisecontrol", 0 ) local F = ply:GetInfoNum( "cl_simfphys_lights", 0 ) local V = ply:GetInfoNum( "cl_simfphys_foglights", 0 ) local H = ply:GetInfoNum( "cl_simfphys_keyhorn", 0 ) local I = ply:GetInfoNum( "cl_simfphys_keyengine", 0 ) local Shift = ply:GetInfoNum( "cl_simfphys_keywot", 0 ) local Alt = ply:GetInfoNum( "cl_simfphys_keyclutch", 0 ) local Space = ply:GetInfoNum( "cl_simfphys_keyhandbrake", 0 ) local lock = ply:GetInfoNum( "cl_simfphys_key_lock", 0 ) local w_dn = numpad.OnDown( ply, W, "k_forward",self, true ) local w_up = numpad.OnUp( ply, W, "k_forward",self, false ) local s_dn = numpad.OnDown( ply, S, "k_reverse",self, true ) local s_up = numpad.OnUp( ply, S, "k_reverse",self, false ) local a_dn = numpad.OnDown( ply, A, "k_left",self, true ) local a_up = numpad.OnUp( ply, A, "k_left",self, false ) local d_dn = numpad.OnDown( ply, D, "k_right",self, true ) local d_up = numpad.OnUp( ply, D, "k_right",self, false ) local aw_dn = numpad.OnDown( ply, aW, "k_a_forward",self, true ) local aw_up = numpad.OnUp( ply, aW, "k_a_forward",self, false ) local as_dn = numpad.OnDown( ply, aS, "k_a_reverse",self, true ) local as_up = numpad.OnUp( ply, aS, "k_a_reverse",self, false ) local aa_dn = numpad.OnDown( ply, aA, "k_a_left",self, true ) local aa_up = numpad.OnUp( ply, aA, "k_a_left",self, false ) local ad_dn = numpad.OnDown( ply, aD, "k_a_right",self, true ) local ad_up = numpad.OnUp( ply, aD, "k_a_right",self, false ) local gup_dn = numpad.OnDown( ply, GearUp, "k_gup",self, true ) local gup_up = numpad.OnUp( ply, GearUp, "k_gup",self, false ) local gdn_dn = numpad.OnDown( ply, GearDown, "k_gdn",self, true ) local gdn_up = numpad.OnUp( ply, GearDown, "k_gdn",self, false ) local shift_dn = numpad.OnDown( ply, Shift, "k_wot",self, true ) local shift_up = numpad.OnUp( ply, Shift, "k_wot",self, false ) local alt_dn = numpad.OnDown( ply, Alt, "k_clutch",self, true ) local alt_up = numpad.OnUp( ply, Alt, "k_clutch",self, false ) local space_dn = numpad.OnDown( ply, Space, "k_hbrk",self, true ) local space_up = numpad.OnUp( ply, Space, "k_hbrk",self, false ) local k_cruise = numpad.OnDown( ply, R, "k_ccon",self, true ) local k_lights_dn = numpad.OnDown( ply, F, "k_lgts",self, true ) local k_lights_up = numpad.OnUp( ply, F, "k_lgts",self, false ) local k_flights_dn = numpad.OnDown( ply, V, "k_flgts",self, true ) local k_flights_up = numpad.OnUp( ply, V, "k_flgts",self, false ) local k_horn_dn = numpad.OnDown( ply, H, "k_hrn",self, true ) local k_horn_up = numpad.OnUp( ply, H, "k_hrn",self, false ) local k_engine_dn = numpad.OnDown( ply, I, "k_eng",self, true ) local k_engine_up = numpad.OnUp( ply, I, "k_eng",self, false ) self.keys = { w_dn,w_up, s_dn,s_up, a_dn,a_up, d_dn,d_up, aw_dn,aw_up, as_dn,as_up, aa_dn,aa_up, ad_dn,ad_up, gup_dn,gup_up, gdn_dn,gdn_up, shift_dn,shift_up, alt_dn,alt_up, space_dn,space_up, k_cruise, k_lights_dn,k_lights_up, k_horn_dn,k_horn_up, k_flights_dn,k_flights_up, k_engine_dn,k_engine_up, } end end function ENT:PhysicalSteer() if IsValid(self.SteerMaster) then local physobj = self.SteerMaster:GetPhysicsObject() if not IsValid(physobj) then return end if physobj:IsMotionEnabled() then physobj:EnableMotion(false) end self.SteerMaster:SetAngles( self:LocalToWorldAngles( Angle(0,math.Clamp(-self.VehicleData[ "Steer" ],-self.CustomSteerAngle,self.CustomSteerAngle),0) ) ) end if IsValid(self.SteerMaster2) then local physobj = self.SteerMaster2:GetPhysicsObject() if not IsValid(physobj) then return end if physobj:IsMotionEnabled() then physobj:EnableMotion(false) end self.SteerMaster2:SetAngles( self:LocalToWorldAngles( Angle(0,math.Clamp(self.VehicleData[ "Steer" ],-self.CustomSteerAngle,self.CustomSteerAngle),0) ) ) end end function ENT:EngineActive() return (self.EngineIsOn == 1) end function ENT:IsDriveWheelsOnGround() return (self.DriveWheelsOnGround == 1) end function ENT:GetRPM() local RPM = self.EngineRPM and self.EngineRPM or 0 return RPM end function ENT:GetEngineRPM() local flywheelrpm = self:GetRPM() local rpm if self:GetRevlimiter() then local throttle = self:GetThrottle() local maxrpm = self:GetLimitRPM() local revlimiter = (maxrpm > 2500) and (throttle > 0) rpm = math.Round(((flywheelrpm >= maxrpm - 200) and revlimiter) and math.Round(flywheelrpm - 200 + math.sin(CurTime()* 50) * 600,0) or flywheelrpm,0) else rpm = flywheelrpm end return rpm end function ENT:GetDiffGear() return math.max(self:GetDifferentialGear(),0.01) end function ENT:DamagedStall() if not self:GetActive() then return end local rtimer = 0.8 timer.Simple( rtimer, function() if not IsValid( self ) then return end net.Start( "simfphys_backfire" ) net.WriteEntity( self ) net.Broadcast() end) self:StallAndRestart( rtimer, true ) end function ENT:StopEngine() if self:EngineActive() then if hook.Run( "simfphysOnEngine", self, false, bIgnoreSettings ) then return end self:EmitSound( "vehicles/jetski/jetski_off.wav" ) self.EngineRPM = 0 self.EngineIsOn = 0 self:SetFlyWheelRPM( 0 ) self:SetIsCruiseModeOn( false ) end end function ENT:CanStart() local FuelSystemOK = true if simfphys.Fuel then FuelSystemOK = self:GetFuel() > 0 end local canstart = self:GetCurHealth() > (self:GetMaxHealth() * 0.1) and FuelSystemOK return canstart end function ENT:StartEngine( bIgnoreSettings ) if not self:CanStart() then return end if not self:EngineActive() then if hook.Run( "simfphysOnEngine", self, true, bIgnoreSettings ) then return end if not bIgnoreSettings then self.CurrentGear = 2 end if not self.IsInWater then self.EngineRPM = self:GetEngineData().IdleRPM self.EngineIsOn = 1 else if self:GetDoNotStall() then self.EngineRPM = self:GetEngineData().IdleRPM self.EngineIsOn = 1 end end end end function ENT:StallAndRestart( nTimer, bIgnoreSettings ) nTimer = nTimer or 1 self:StopEngine() local ply = self:GetDriver() if IsValid(ply) and not bIgnoreSettings then if ply:GetInfoNum( "cl_simfphys_autostart", 1 ) <= 0 then return end end timer.Simple( nTimer, function() if not IsValid(self) then return end self:StartEngine( bIgnoreSettings ) end) end function ENT:PlayerSteerVehicle( ply, left, right ) if IsValid( ply ) then local CounterSteeringEnabled = (ply:GetInfoNum( "cl_simfphys_ctenable", 0 ) or 1) == 1 local CounterSteeringMul = math.Clamp(ply:GetInfoNum( "cl_simfphys_ctmul", 0 ) or 0.7,0.1,2) local MaxHelpAngle = math.Clamp(ply:GetInfoNum( "cl_simfphys_ctang", 0 ) or 15,1,90) local Ang = self.MoveDir local TurnSpeed local fadespeed local fastspeedangle local extrasmooth = false if istable(self.cl_SteerSettings) and self.cl_SteerSettings.Overwrite then TurnSpeed = self.cl_SteerSettings.TurnSpeed fadespeed = self.cl_SteerSettings.fadespeed fastspeedangle = self.cl_SteerSettings.fastspeedangle extrasmooth = self.cl_SteerSettings.smoothsteer else TurnSpeed = self:GetSteerSpeed() fadespeed = self:GetFastSteerConeFadeSpeed() fastspeedangle = self:GetFastSteerAngle() * self.VehicleData["steerangle"] end local SlowSteeringRate = (Ang > 20) and ((math.Clamp((self.ForwardSpeed - 150) / 25,0,1) == 1) and 60 or self.VehicleData["steerangle"]) or self.VehicleData["steerangle"] local FastSteeringAngle = math.Clamp(fastspeedangle,1,SlowSteeringRate) local FastSteeringRate = FastSteeringAngle + ((Ang > (FastSteeringAngle-1)) and 1 or 0) * math.min(Ang,90 - FastSteeringAngle) local Ratio = 1 - math.Clamp((math.abs(self.ForwardSpeed) - fadespeed) / 25,0,1) local SteerRate = FastSteeringRate + (SlowSteeringRate - FastSteeringRate) * Ratio local Steer = ((left + right) > 0 and (right - left) or self:GetMouseSteer()) * SteerRate local LocalDrift = math.acos( math.Clamp( self.Right:Dot(self.VelNorm) ,-1,1) ) * (180 / math.pi) - 90 local CounterSteer = CounterSteeringEnabled and (math.Clamp(LocalDrift * CounterSteeringMul * (((left + right) == 0) and 1 or 0),-MaxHelpAngle,MaxHelpAngle) * ((self.ForwardSpeed > 50) and 1 or 0)) or 0 local Rate = extrasmooth and math.max( (math.abs(self.SmoothAng) / self.VehicleData["steerangle"]) ^ 1.5 * TurnSpeed, math.max(1 - self.ForwardSpeed / 2000,0.05) ) or TurnSpeed self.SmoothAng = self.SmoothAng + math.Clamp((Steer - CounterSteer) - self.SmoothAng,-Rate,Rate) self:SteerVehicle( self.SmoothAng ) end end function ENT:SteerVehicle( steer ) self.VehicleData[ "Steer" ] = steer self:SetVehicleSteer( steer / self.VehicleData["steerangle"] ) end function ENT:ForceLightsOff() local vehiclelist = list.Get( "simfphys_lights" )[self.LightsTable] or false if not vehiclelist then return end if vehiclelist.Animation then if self.LightsActivated then self.LightsActivated = false self.LampsActivated = false self:SetLightsEnabled(false) self:SetLampsEnabled(false) end end end function ENT:EnteringSequence( ply ) local LinkedDoorAnims = istable(self.ModelInfo) and istable(self.ModelInfo.LinkDoorAnims) if not istable(self.Enterpoints) and not LinkedDoorAnims then return end local sequence local pos local dist if LinkedDoorAnims then for i,_ in pairs( self.ModelInfo.LinkDoorAnims ) do local seq_ = self.ModelInfo.LinkDoorAnims[ i ].enter local a_pos = self:GetAttachment( self:LookupAttachment( i ) ).Pos local a_dist = (ply:GetPos() - a_pos):Length() if not sequence then sequence = seq_ pos = a_pos dist = a_dist else if a_dist < dist then sequence = seq_ pos = a_pos dist = a_dist end end end else for i = 1, table.Count( self.Enterpoints ) do local a_ = self.Enterpoints[ i ] local a_pos = self:GetAttachment( self:LookupAttachment( a_ ) ).Pos local a_dist = (ply:GetPos() - a_pos):Length() if i == 1 then sequence = a_ pos = a_pos dist = a_dist else if (a_dist < dist) then sequence = a_ pos = a_pos dist = a_dist end end end end self:PlayAnimation( sequence ) self:ForceLightsOff() end function ENT:GetMouseSteer() local Driver = self:GetDriver() if not IsValid( Driver ) or not Driver.lvsMouseAim or not Driver:lvsMouseAim() then return 0 end local TargetAngle = Driver:EyeAngles() local pod = self:GetDriverSeat() if not IsValid( pod ) then return end local ang = self:GetAngles() ang.y = pod:GetAngles().y + 90 local Forward = ang:Right() local View = pod:WorldToLocalAngles( TargetAngle ):Forward() local Reversed = false if self:AngleBetweenNormal( View, ang:Forward() ) < 90 then Reversed = self:GetGear() == 1 end local LocalAngSteer = (self:AngleBetweenNormal( View, ang:Right() ) - 90) / self.MouseSteerAngle local Steer = (math.min( math.abs( LocalAngSteer ), 1 ) ^ self.MouseSteerExponent * self:Sign( LocalAngSteer )) return -Steer end function ENT:SetPhysics( enable ) if enable then if not self.PhysicsEnabled then for i = 1, table.Count( self.Wheels ) do local Wheel = self.Wheels[i] if IsValid(Wheel) then Wheel:GetPhysicsObject():SetMaterial("jeeptire") end end self.PhysicsEnabled = true end else if self.PhysicsEnabled ~= false then for i = 1, table.Count( self.Wheels ) do local Wheel = self.Wheels[i] if IsValid(Wheel) then Wheel:GetPhysicsObject():SetMaterial("friction_00") if Wheel:GetPhysicsObject():GetMaterial() ~= "friction_00" then print("(SIMFPHYS) ERROR! MISSING ''friction_00'' PHYSPROP-MATERIAL!!! THIS SHOULD NEVER HAPPEN!! CLEAN YOUR GMOD!! DON'T USE CONTENT OF GAMES YOU DON'T OWN!! DON'T EVEN BOTHER REPORTING THIS ISSUE, BECAUSE ONLY YOU CAN FIX THIS AS THIS IS AN ISSUE WITH YOUR GAME!!!!") sound.Play( "common/bugreporter_failed.wav", self:GetPos() ) self:Remove() break end end end self.PhysicsEnabled = false end end end function ENT:SetSuspension( index , bIsDamaged ) local bIsDamaged = bIsDamaged or false local h_mod = index <= 2 and self:GetFrontSuspensionHeight() or self:GetRearSuspensionHeight() local heights = { [1] = self.FrontHeight + self.VehicleData.suspensiontravel_fl * -h_mod, [2] = self.FrontHeight + self.VehicleData.suspensiontravel_fr * -h_mod, [3] = self.RearHeight + self.VehicleData.suspensiontravel_rl * -h_mod, [4] = self.RearHeight + self.VehicleData.suspensiontravel_rr * -h_mod, [5] = self.RearHeight + self.VehicleData.suspensiontravel_rl * -h_mod, [6] = self.RearHeight + self.VehicleData.suspensiontravel_rr * -h_mod } local Wheel = self.Wheels[index] if not IsValid(Wheel) then return end local subRadius = bIsDamaged and Wheel.dRadius or 0 local newheight = heights[index] + subRadius local Elastic = self.Elastics[index] if IsValid(Elastic) then Elastic:Fire( "SetSpringLength", newheight ) end if self.StrengthenSuspension == true then local Elastic2 = self.Elastics[index * 10] if IsValid(Elastic2) then Elastic2:Fire( "SetSpringLength", newheight ) end end end function ENT:OnFrontSuspensionHeightChanged( name, old, new ) if old == new then return end if not self.CustomWheels and new > 0 then new = 0 end if not self:IsInitialized() then return end if IsValid(self.Wheels[1]) then local Elastic = self.Elastics[1] if IsValid(Elastic) then Elastic:Fire( "SetSpringLength", self.FrontHeight + self.VehicleData.suspensiontravel_fl * -new ) end if self.StrengthenSuspension == true then local Elastic2 = self.Elastics[10] if IsValid(Elastic2) then Elastic2:Fire( "SetSpringLength", self.FrontHeight + self.VehicleData.suspensiontravel_fl * -new ) end end end if IsValid(self.Wheels[2]) then local Elastic = self.Elastics[2] if IsValid(Elastic) then Elastic:Fire( "SetSpringLength", self.FrontHeight + self.VehicleData.suspensiontravel_fr * -new ) end if self.StrengthenSuspension == true then local Elastic2 = self.Elastics[20] if (IsValid(Elastic2)) then Elastic2:Fire( "SetSpringLength", self.FrontHeight + self.VehicleData.suspensiontravel_fr * -new ) end end end end function ENT:OnRearSuspensionHeightChanged( name, old, new ) if old == new then return end if not self.CustomWheels and new > 0 then new = 0 end if not self:IsInitialized() then return end if IsValid(self.Wheels[3]) then local Elastic = self.Elastics[3] if IsValid(Elastic) then Elastic:Fire( "SetSpringLength", self.RearHeight + self.VehicleData.suspensiontravel_rl * -new ) end if self.StrengthenSuspension == true then local Elastic2 = self.Elastics[30] if IsValid(Elastic2) then Elastic2:Fire( "SetSpringLength", self.RearHeight + self.VehicleData.suspensiontravel_rl * -new ) end end end if IsValid(self.Wheels[4]) then local Elastic = self.Elastics[4] if IsValid(Elastic) then Elastic:Fire( "SetSpringLength", self.RearHeight + self.VehicleData.suspensiontravel_rr * -new ) end if self.StrengthenSuspension == true then local Elastic2 = self.Elastics[40] if IsValid(Elastic2) then Elastic2:Fire( "SetSpringLength", self.RearHeight + self.VehicleData.suspensiontravel_rr * -new ) end end end if IsValid(self.Wheels[5]) then local Elastic = self.Elastics[5] if IsValid(Elastic) then Elastic:Fire( "SetSpringLength", self.RearHeight + self.VehicleData.suspensiontravel_rl * -new ) end if self.StrengthenSuspension == true then local Elastic2 = self.Elastics[50] if IsValid(Elastic2) then Elastic2:Fire( "SetSpringLength", self.RearHeight + self.VehicleData.suspensiontravel_rl * -new ) end end end if IsValid(self.Wheels[6]) then local Elastic = self.Elastics[6] if IsValid(Elastic) then Elastic:Fire( "SetSpringLength", self.RearHeight + self.VehicleData.suspensiontravel_rr * -new ) end if self.StrengthenSuspension == true then local Elastic2 = self.Elastics[60] if IsValid(Elastic2) then Elastic2:Fire( "SetSpringLength", self.RearHeight + self.VehicleData.suspensiontravel_rr * -new ) end end end end function ENT:OnTurboCharged( name, old, new ) if old == new then return end local Active = self:GetActive() if new == true and Active then self.Turbo:Stop() self.Turbo = CreateSound(self, self.snd_spool or "simulated_vehicles/turbo_spin.wav") self.Turbo:PlayEx(0,0) elseif new == false then if self.Turbo then self.Turbo:Stop() end end end function ENT:OnSuperCharged( name, old, new ) if old == new then return end local Active = self:GetActive() if new == true and Active then self.Blower:Stop() self.BlowerWhine:Stop() self.Blower = CreateSound(self, self.snd_bloweroff or "simulated_vehicles/blower_spin.wav") self.BlowerWhine = CreateSound(self, self.snd_bloweron or "simulated_vehicles/blower_gearwhine.wav") self.Blower:PlayEx(0,0) self.BlowerWhine:PlayEx(0,0) elseif new == false then if self.Blower then self.Blower:Stop() end if self.BlowerWhine then self.BlowerWhine:Stop() end end end function ENT:OnRemove() if self.Wheels then for i = 1, table.Count( self.Wheels ) do local Ent = self.Wheels[ i ] if IsValid(Ent) then Ent:Remove() end end end if self.keys then for i = 1, table.Count( self.keys ) do numpad.Remove( self.keys[i] ) end end if self.Turbo then self.Turbo:Stop() end if self.Blower then self.Blower:Stop() end if self.BlowerWhine then self.BlowerWhine:Stop() end if self.horn then self.horn:Stop() end if self.ems then self.ems:Stop() end self:OnDelete() hook.Run( "simfphysOnDelete", self ) end function ENT:PlayPP( On ) self.poseon = On and self.LightsPP.max or self.LightsPP.min end function ENT:SetOnFire( bOn ) if bOn == self:OnFire() then return end if hook.Run( "simfphysOnFire", self, bOn ) then return end self:SetNWBool( "OnFire", bOn ) if bOn then self:DamagedStall() end end function ENT:SetOnSmoke( bOn ) if bOn == self:OnSmoke() then return end if hook.Run( "simfphysOnSmoke", self, bOn ) then return end self:SetNWBool( "OnSmoke", bOn ) if bOn then self:DamagedStall() end end function ENT:SetMaxHP( nHealth ) self:SetMaxHealth( nHealth ) end function ENT:SetMaxHealth( nHealth ) self:SetNWFloat( "MaxHealth", nHealth ) end function ENT:SetMaxFuel( nFuel ) self:SetNWFloat( "MaxFuel", nFuel ) end function ENT:SetFuel( nFuel ) self:SetNWFloat( "Fuel", math.Clamp( nFuel,0,self:GetMaxFuel() ) ) end function ENT:SetFuelUse( nFuel ) self:SetNWFloat( "FuelUse", nFuel ) end function ENT:SetFuelType( fueltype ) self:SetNWInt( "FuelType", fueltype ) end function ENT:SetFuelPos( vPos ) self:SetFuelPortPosition( vPos ) end function ENT:OnMaintenance() net.Start( "simfphys_lightsfixall" ) net.WriteEntity( self ) net.Broadcast() self:OnRepaired() local Fuel = self:GetFuel() local MaxFuel = self:GetMaxFuel() if Fuel ~= MaxFuel then self:SetFuel( MaxFuel ) self:OnRefueled() end if istable(self.Wheels) then for i = 1, table.Count( self.Wheels ) do local Wheel = self.Wheels[ i ] if IsValid(Wheel) then Wheel:SetDamaged( false ) end end end hook.Run( "simfphysOnRepaired", self ) end function ENT:AddFuelTank( fueltype ) if IsValid( self:GetFuelTank() ) then return end local FuelTank = ents.Create( "gmod_sent_vehicle_fphysics_fueltank" ) if not IsValid( FuelTank ) then self:Remove() print("LVS: Failed to create fueltank entity. Vehicle terminated.") return end FuelTank:SetPos( self:GetPos() ) FuelTank:SetAngles( self:GetAngles() ) FuelTank:Spawn() FuelTank:Activate() FuelTank:SetParent( self ) FuelTank:SetBase( self ) self:SetFuelTank( FuelTank ) self:DeleteOnRemove( FuelTank ) self:TransferCPPI( FuelTank ) end function ENT:OnRefueled() local FuelTank = self:GetFuelTank() if not IsValid( FuelTank ) then return end FuelTank:EmitSound( "vehicles/jetski/jetski_no_gas_start.wav" ) end function ENT:SetRoadkillAttacker( ply ) local T = CurTime() if (self._nextSetAttacker or 0) > T then return end self._nextSetAttacker = T + 1 self:SetPhysicsAttacker( ply, 1.1 ) end