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

local function IsServerOK()

	if GetConVar( "gmod_physiterations" ):GetInt() < 4 then
		RunConsoleCommand("gmod_physiterations", "4")

		return false
	end

	return true
end

function ENT:Initialize()
	self:PhysicsInit( SOLID_VPHYSICS )
	self:SetMoveType( MOVETYPE_VPHYSICS )
	self:SetSolid( SOLID_VPHYSICS )
	self:SetNotSolid( true )
	self:SetUseType( SIMPLE_USE )
	--self:SetRenderMode( RENDERMODE_TRANSALPHA ) -- fix broken decals
	self:AddFlags( FL_OBJECT ) -- this allows npcs to see this entity

	if not IsServerOK() then

		self:Remove()

		print("[SIMFPHYS] ERROR COULDN'T INITIALIZE VEHICLE!")
	end

	local PObj = self:GetPhysicsObject()

	if not IsValid( PObj ) then print("[SIMFPHYS] ERROR COULDN'T INITIALIZE VEHICLE! '"..self:GetModel().."' has no physics model!") self:Remove() return end

	PObj:EnableMotion( false )

	self:SetValues()

	timer.Simple( 0.1, function()
		if not IsValid( self ) then return end
		self:InitializeVehicle()
	end)
end

function ENT:PostEntityPaste( ply , ent , createdEntities )
	self:SetValues()
	
	self:SetActive( false )
	self:SetDriver( NULL )
	self:SetLightsEnabled( false )
	self:SetLampsEnabled( false )
	self:SetFogLightsEnabled( false )
	
	self:SetDriverSeat( NULL )
	self:SetFlyWheelRPM( 0 )
	self:SetThrottle( 0 )
end

function ENT:UpdateTransmitState() 
	return TRANSMIT_ALWAYS
end

function ENT:SetupView()
	local AttachmentID = self:LookupAttachment( "vehicle_driver_eyes" )
	local AttachmentID2 = self:LookupAttachment( "vehicle_passenger0_eyes" )
	
	local a_data1 = self:GetAttachment( AttachmentID )
	local a_data2 = self:GetAttachment( AttachmentID2 )
	
	local ID
	local ViewPos
	
	if a_data1 then
		ID = AttachmentID
		ViewPos = a_data1
		
	elseif a_data2 then
		ID = AttachmentID2
		ViewPos = a_data2
		
	else
		ID = false
		ViewPos = {Ang = self:LocalToWorldAngles( Angle(0, 90,0) ),Pos = self:GetPos()}
	end
	
	local ViewAng = ViewPos.Ang - Angle(0,0,self.SeatPitch)
	ViewAng:RotateAroundAxis(self:GetUp(), -90 - (self.SeatYaw or 0))
	
	local data = {
		ID = ID,
		ViewPos = ViewPos.Pos,
		ViewAng = ViewAng,
	}
	
	return data
end

function ENT:SetupEnteringAnims()
	local attachments = self:GetAttachments()
	
	self.Exitpoints = {}
	self.Enterpoints = {}
	
	for _,i in pairs(attachments) do
		local curstring = string.lower( i.name )
		
		if string.match( curstring, "exit", 1 ) then
			table.insert(self.Exitpoints, curstring)
		end
		
		if string.match( curstring, "enter", 1 ) then
			table.insert(self.Enterpoints, curstring)
		end
	end
	
	if table.Count( self.Enterpoints ) < 1 then
		self.Enterpoints = nil
	end
	
	if table.Count( self.Exitpoints ) < 1 then
		self.Exitpoints = nil
	end
end

function ENT:InitializeVehicle()
	if not IsValid( self ) then return end
	
	local physObj = self:GetPhysicsObject()
	
	if not IsValid( physObj ) then return end
	
	if self.LightsTable then
		local vehiclelist = list.Get( "simfphys_lights" )[self.LightsTable] or false
		if vehiclelist then
			if vehiclelist.PoseParameters then
				self.LightsPP = vehiclelist.PoseParameters
			end
			
			if vehiclelist.BodyGroups then
				self:SetBodygroup(vehiclelist.BodyGroups.Off[1], vehiclelist.BodyGroups.Off[2] )
			end
		end
	end
	
	physObj:SetDragCoefficient( self.AirFriction or -250 )
	physObj:SetMass( self.Mass * 0.75 )
	
	if self.Inertia then
		physObj:SetInertia( self.Inertia ) 
	end

	local tanksize = self.FuelTankSize and self.FuelTankSize or 65
	local fueltype = self.FuelType and self.FuelType or FUELTYPE_PETROL

	self:SetMaxFuel( tanksize )
	self:SetFuel( self:GetMaxFuel() )
	self:SetFuelType( fueltype )
	self:SetFuelPos( self.FuelFillPos and self.FuelFillPos or Vector(0,0,0) )

	if fueltype ~= FUELTYPE_NONE then
		self:AddFuelTank()
	end

	local View = self:SetupView()
	
	self.DriverSeat = self:AddDriverSeat( self:WorldToLocal( View.ViewPos ), self:WorldToLocalAngles( View.ViewAng ) )
	self.DriverSeat:SetParent( NULL )
	self.DriverSeat:SetMoveType( MOVETYPE_NONE )
	self.DriverSeat:Spawn()
	self.DriverSeat:Activate()
	self.DriverSeat:SetPos( View.ViewPos + self.DriverSeat:GetUp() * (-34 + self.SeatOffset.z) + self.DriverSeat:GetRight() * (self.SeatOffset.y) + self.DriverSeat:GetForward() * (-6 + self.SeatOffset.x) )
	
	if View.ID ~= false then
		self:SetupEnteringAnims()
		self.DriverSeat:SetParent( self , View.ID )
	else
		self.DriverSeat:SetParent( self )
	end

	self.DriverSeat.fphysSeat = true
	self.DriverSeat.base = self

	if self.PassengerSeats then
		for i = 1, table.Count( self.PassengerSeats ) do
			self.pSeat[i] = self:AddPassengerSeat( self.PassengerSeats[i].pos, self.PassengerSeats[i].ang )
			self.pSeat[i].fphysSeat = true
			self.pSeat[i].base = self
		end
	end

	if istable(WireLib) then
		local passengersSeats = istable( self.pSeat ) and self.pSeat or {}
		WireLib.TriggerOutput(self, "PassengerSeats", passengersSeats )
		
		WireLib.TriggerOutput(self, "DriverSeat", self.DriverSeat )
	end

	if self.Attachments then
		for i = 1, table.Count( self.Attachments ) do
			local prop = ents.Create( "gmod_sent_vehicle_fphysics_attachment" )
			prop:SetModel( self.Attachments[i].model )			
			prop:SetMaterial( self.Attachments[i].material )
			prop:SetRenderMode( RENDERMODE_TRANSALPHA )
			prop:SetPos( self:LocalToWorld( self.Attachments[i].pos ) )
			prop:SetAngles( self:LocalToWorldAngles( self.Attachments[i].ang ) )
			prop:SetOwner( self )
			prop:Spawn()
			prop:Activate()
			prop:DrawShadow( true )
			prop:SetNotSolid( true )
			prop:SetParent( self )
			prop.DoNotDuplicate = true
			simfphys.SetOwner( self.EntityOwner, prop )
			
			if self.Attachments[i].skin then
				prop:SetSkin( self.Attachments[i].skin )
			end
			
			if self.Attachments[i].bodygroups then
				for b = 1, table.Count( self.Attachments[i].bodygroups ) do
					prop:SetBodygroup(b, self.Attachments[i].bodygroups[b] )
				end
			end
			
			if self.Attachments[i].useVehicleColor == true then
				self.ColorableProps[i] = prop
				prop:SetColor( self:GetColor() )
			else
				prop:SetColor( self.Attachments[i].color or Color(255,255,255,255) )
			end
			
			self:DeleteOnRemove( prop )
		end
	end

	self:GetVehicleData()
end

function ENT:GetVehicleData()	
	self:SetPoseParameter("vehicle_steer",1) 
	self:SetPoseParameter("vehicle_wheel_fl_height",1) 
	self:SetPoseParameter("vehicle_wheel_fr_height",1) 
	self:SetPoseParameter("vehicle_wheel_rl_height",1) 
	self:SetPoseParameter("vehicle_wheel_rr_height",1)

	timer.Simple( 0.15, function()
		if not IsValid(self) then return end
		self.posepositions["Pose0_Steerangle"] = self.CustomWheels and Angle(0,0,0) or self:GetAttachment( self:LookupAttachment( "wheel_fl" ) ).Ang
		self.posepositions["Pose0_Pos_FL"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosFL ) or self:GetAttachment( self:LookupAttachment( "wheel_fl" ) ).Pos
		self.posepositions["Pose0_Pos_FR"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosFR ) or self:GetAttachment( self:LookupAttachment( "wheel_fr" ) ).Pos
		self.posepositions["Pose0_Pos_RL"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosRL ) or self:GetAttachment( self:LookupAttachment( "wheel_rl" ) ).Pos
		self.posepositions["Pose0_Pos_RR"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosRR ) or self:GetAttachment( self:LookupAttachment( "wheel_rr" ) ).Pos	
		
		self:WriteVehicleDataTable()
	end )
end

function ENT:ResetJoystick()
	self.PressedKeys["joystick_steer_left"] = 0
	self.PressedKeys["joystick_steer_right"] = 0
	self.PressedKeys["joystick_brake"] = 0
	self.PressedKeys["joystick_throttle"] = 0
	self.PressedKeys["joystick_gearup"] = 0
	self.PressedKeys["joystick_geardown"] = 0
	self.PressedKeys["joystick_handbrake"] = 0
	self.PressedKeys["joystick_clutch"] = 0
	self.PressedKeys["joystick_air_w"] = 0
	self.PressedKeys["joystick_air_a"] = 0
	self.PressedKeys["joystick_air_s"] = 0
	self.PressedKeys["joystick_air_d"] = 0
end

function ENT:SetValues()
	if istable( WireLib ) then
		self:createWireIO()
	end
	
	self:SetGear( 2 )

	self.WheelOnGroundDelay = 0
	self.SmoothAng = 0
	self.Steer = 0
	self.EngineIsOn = 0
	self.EngineTorque = 0
	
	self.pSeat = {}
	self.exfx = {}
	self.Wheels = {}
	self.Elastics = {}
	self.GhostWheels = {}
	self.PressedKeys = {}
	self:ResetJoystick()
	
	self.ColorableProps = {}
	self.posepositions = {}
	
	self.HandBrakePower = 0
	self.DriveWheelsOnGround = 0
	self.WheelRPM = 0
	self.EngineRPM = 0
	self.RpmDiff = 0
	self.Torque = 0
	self.CurrentGear = 2
	self.GearUpPressed = 0
	self.GearDownPressed = 0
	self.RPM_DIFFERENCE = 0
	self.exprpmdiff = 0
	self.OldLockBrakes = 0
	self.ThrottleDelay = 0
	self.Brake = 0
	self.HandBrake = 0
	self.AutoClutch = 0
	self.NextShift = 0
	self.ForwardSpeed = 0
	self.EngineWasOn = 0
	self.SmoothTurbo = 0
	self.SmoothBlower = 0
	self.cc_speed = 0
	self.LightsActivated = false

	self.VehicleData = {}
	for i = 1, 6 do
		self.VehicleData[ "spin_"..i ] = 0
		self.VehicleData[ "SurfaceMul_"..i ] = 1
		self.VehicleData[ "onGround_"..i ] = 0
	end
	
	self.VehicleData[ "Steer" ] = 0
end

function ENT:WriteVehicleDataTable()	
	self:SetPoseParameter("vehicle_steer",0) 
	self:SetPoseParameter("vehicle_wheel_fl_height",0) 
	self:SetPoseParameter("vehicle_wheel_fr_height",0) 
	self:SetPoseParameter("vehicle_wheel_rl_height",0) 
	self:SetPoseParameter("vehicle_wheel_rr_height",0)
	
	timer.Simple( 0.15, function()
		if not IsValid(self) then return end
		self.posepositions["Pose1_Steerangle"] = self.CustomWheels and Angle(0,0,0) or self:GetAttachment( self:LookupAttachment( "wheel_fl" ) ).Ang
		self.posepositions["Pose1_Pos_FL"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosFL ) or self:GetAttachment( self:LookupAttachment( "wheel_fl" ) ).Pos
		self.posepositions["Pose1_Pos_FR"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosFR ) or self:GetAttachment( self:LookupAttachment( "wheel_fr" ) ).Pos
		self.posepositions["Pose1_Pos_RL"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosRL ) or self:GetAttachment( self:LookupAttachment( "wheel_rl" ) ).Pos
		self.posepositions["Pose1_Pos_RR"] = self.CustomWheels and self:LocalToWorld( self.CustomWheelPosRR ) or self:GetAttachment( self:LookupAttachment( "wheel_rr" ) ).Pos
		self.posepositions["PoseL_Pos_FL"] = self:WorldToLocal( self.posepositions.Pose1_Pos_FL )
		self.posepositions["PoseL_Pos_FR"] = self:WorldToLocal( self.posepositions.Pose1_Pos_FR )
		self.posepositions["PoseL_Pos_RL"] = self:WorldToLocal( self.posepositions.Pose1_Pos_RL )
		self.posepositions["PoseL_Pos_RR"] = self:WorldToLocal( self.posepositions.Pose1_Pos_RR )
		
		self.VehicleData["suspensiontravel_fl"] = self.CustomWheels and self.FrontHeight or math.Round( (self.posepositions.Pose0_Pos_FL - self.posepositions.Pose1_Pos_FL):Length() , 2)
		self.VehicleData["suspensiontravel_fr"] = self.CustomWheels and self.FrontHeight or math.Round( (self.posepositions.Pose0_Pos_FR - self.posepositions.Pose1_Pos_FR):Length() , 2)
		self.VehicleData["suspensiontravel_rl"] = self.CustomWheels and self.RearHeight or math.Round( (self.posepositions.Pose0_Pos_RL - self.posepositions.Pose1_Pos_RL):Length() , 2)
		self.VehicleData["suspensiontravel_rr"] = self.CustomWheels and self.RearHeight or math.Round( (self.posepositions.Pose0_Pos_RR - self.posepositions.Pose1_Pos_RR):Length() , 2)
		
		local Figure1 = math.Round( math.acos( math.Clamp(self.posepositions.Pose0_Steerangle:Up():Dot(self.posepositions.Pose1_Steerangle:Up()),-1,1) ) * (180 / math.pi) , 2)
		local Figure2 = math.Round( math.acos( math.Clamp(self.posepositions.Pose0_Steerangle:Forward():Dot(self.posepositions.Pose1_Steerangle:Forward()),-1,1) ) * (180 / math.pi) , 2)
		local Figure3 = math.Round( math.acos( math.Clamp(self.posepositions.Pose0_Steerangle:Right():Dot(self.posepositions.Pose1_Steerangle:Right()),-1,1) ) * (180 / math.pi) , 2)
		self.VehicleData["steerangle"] = self.CustomWheels and self.CustomSteerAngle or math.max(Figure1,Figure2,Figure3)
		
		local pFL = self.posepositions.Pose0_Pos_FL
		local pFR = self.posepositions.Pose0_Pos_FR
		local pRL = self.posepositions.Pose0_Pos_RL
		local pRR = self.posepositions.Pose0_Pos_RR
		local pAngL = self:WorldToLocalAngles( ((pFL + pFR) / 2 - (pRL + pRR) / 2):Angle() )
		pAngL.r = 0
		pAngL.p = 0
		
		self.VehicleData["LocalAngForward"] = pAngL
		
		local yAngL = self.VehicleData.LocalAngForward - Angle(0,90,0)
		yAngL:Normalize() 
		
		self.VehicleData["LocalAngRight"] = yAngL
		self.VehicleData[ "pp_spin_1" ] = "vehicle_wheel_fl_spin"
		self.VehicleData[ "pp_spin_2" ] = "vehicle_wheel_fr_spin"
		self.VehicleData[ "pp_spin_3" ] = "vehicle_wheel_rl_spin"
		self.VehicleData[ "pp_spin_4" ] = "vehicle_wheel_rr_spin"
		
		self.Turbo = CreateSound(self, "")
		self.Blower = CreateSound(self, "")
		self.BlowerWhine = CreateSound(self, "")
		self.BlowOff = CreateSound(self, "")

		local Health = math.floor(self.MaxHealth ~= -1 and self.MaxHealth or (1000 + self:GetPhysicsObject():GetMass() / 3))
		self:SetMaxHealth( Health )
		self:SetCurHealth( Health )

		self:SetAITEAM( self.AITEAM )

		self:SetFastSteerAngle(self.FastSteeringAngle / self.VehicleData["steerangle"])
		self:SetNotSolid( false )
		self:SetupVehicle()
	end )
end

function ENT:SetupVehicle()
	local BaseMass = self:GetPhysicsObject():GetMass()
	local MassCenterOffset = self.CustomMassCenter or Vector(0,0,0)
	local BaseMassCenter = self:LocalToWorld( self:GetPhysicsObject():GetMassCenter() - MassCenterOffset )
	
	local OffsetMass = BaseMass * 0.25
	local CenterWheels = (self.posepositions["Pose1_Pos_FL"] + self.posepositions["Pose1_Pos_FR"] + self.posepositions["Pose1_Pos_RL"] + self.posepositions["Pose1_Pos_RR"]) / 4
	
	local Sub = CenterWheels - BaseMassCenter
	local Dir = Sub:GetNormalized()
	local Dist = Sub:Length()
	local DistAdd = BaseMass * Dist / OffsetMass

	local OffsetMassCenter = BaseMassCenter + Dir * (Dist + DistAdd)
	
	self.MassOffset = ents.Create( "prop_physics" )
	self.MassOffset:SetModel( "models/hunter/plates/plate.mdl" )
	self.MassOffset:SetPos( OffsetMassCenter )
	self.MassOffset:SetAngles( Angle(0,0,0) )
	self.MassOffset:Spawn()
	self.MassOffset:Activate()
	self.MassOffset:GetPhysicsObject():EnableMotion(false)
	self.MassOffset:GetPhysicsObject():SetMass( OffsetMass )
	self.MassOffset:GetPhysicsObject():EnableDrag( false ) 
	self.MassOffset:SetOwner( self )
	self.MassOffset:DrawShadow( false )
	self.MassOffset:SetNotSolid( true )
	self.MassOffset:SetNoDraw( true )
	self.MassOffset.DoNotDuplicate = true
	simfphys.SetOwner( self.EntityOwner, self.MassOffset )
	
	local weld = constraint.Weld(self.MassOffset,self, 0, 0, 0,true, true) 
	weld.DoNotDuplicate = true
	
	local ballsack = constraint.AdvBallsocket(self.MassOffset, self,0,0,Vector(0,0,0),Vector(0,0,0),0,0, -0.01, -0.01, -0.01, 0.01, 0.01, 0.01, 0, 0, 0, 0, 1)
	ballsack.DoNotDuplicate = true
	
	if self.CustomWheels then
		if self.CustomWheelModel then
			if not file.Exists( self.CustomWheelModel, "GAME" ) then 
				if IsValid( self.EntityOwner ) then
					self.EntityOwner:PrintMessage( HUD_PRINTTALK, "ERROR: \""..self.CustomWheelModel.."\" does not exist! Removing vehicle. (Class: "..self:GetSpawn_List()..")")
				end
				self:Remove()
				return
			end
		
			if self.SteerFront ~= false then
				self.SteerMaster = ents.Create( "prop_physics" )
				self.SteerMaster:SetModel( self.CustomWheelModel )
				self.SteerMaster:SetPos( self:GetPos() )
				self.SteerMaster:SetAngles( self:GetAngles() )
				self.SteerMaster:Spawn()
				self.SteerMaster:Activate()
				
				local pobj = self.SteerMaster:GetPhysicsObject()
				
				if IsValid(pobj) then
					pobj:EnableMotion(false)
				else
					if IsValid( self.EntityOwner ) then
						self.EntityOwner:PrintMessage( HUD_PRINTTALK, "ERROR: \""..self.CustomWheelModel.."\" doesn't have an collision model! Removing vehicle. (Class: "..self:GetSpawn_List()..")")
					end
					self.SteerMaster:Remove()
					self:Remove()
					
					return
				end
				
				self.SteerMaster:SetOwner( self )
				self.SteerMaster:DrawShadow( false )
				self.SteerMaster:SetNotSolid( true )
				self.SteerMaster:SetNoDraw( true )
				self.SteerMaster.DoNotDuplicate = true
				self:DeleteOnRemove( self.SteerMaster )
				simfphys.SetOwner( self.EntityOwner, self.SteerMaster )
			end
			
			if self.SteerRear then
				self.SteerMaster2 = ents.Create( "prop_physics" )
				self.SteerMaster2:SetModel( self.CustomWheelModel )
				self.SteerMaster2:SetPos( self:GetPos() )
				self.SteerMaster2:SetAngles( self:GetAngles() )
				self.SteerMaster2:Spawn()
				self.SteerMaster2:Activate()
				
				local pobj = self.SteerMaster2:GetPhysicsObject()
				if IsValid(pobj) then
					pobj:EnableMotion(false)
				else
					if IsValid( self.EntityOwner ) then
						self.EntityOwner:PrintMessage( HUD_PRINTTALK, "ERROR: \""..self.CustomWheelModel.."\" doesn't have an collision model! Removing vehicle. (Class: "..self:GetSpawn_List()..")")
					end
					self.SteerMaster2:Remove()
					self:Remove()
					return
				end
				
				self.SteerMaster2:SetOwner( self )
				self.SteerMaster2:DrawShadow( false )
				self.SteerMaster2:SetNotSolid( true )
				self.SteerMaster2:SetNoDraw( true )
				self.SteerMaster2.DoNotDuplicate = true
				self:DeleteOnRemove( self.SteerMaster2 )
				simfphys.SetOwner( self.EntityOwner, self.SteerMaster2 )
			end
			
			local radius = IsValid(self.SteerMaster) and (self.SteerMaster:OBBMaxs() - self.SteerMaster:OBBMins()) or (self.SteerMaster2:OBBMaxs() - self.SteerMaster2:OBBMins())
			self.FrontWheelRadius = self.FrontWheelRadius or math.max( radius.x, radius.y, radius.z ) * 0.5
			self.RearWheelRadius = self.RearWheelRadius or self.FrontWheelRadius
			
			self:CreateWheel(1, WheelFL, self:LocalToWorld( self.CustomWheelPosFL ), self.FrontHeight, self.FrontWheelRadius, false , self:LocalToWorld( self.CustomWheelPosFL + Vector(0,0,self.CustomSuspensionTravel * 0.5) ),self.CustomSuspensionTravel, self.FrontConstant, self.FrontDamping, self.FrontRelativeDamping)
			self:CreateWheel(2, WheelFR, self:LocalToWorld( self.CustomWheelPosFR ), self.FrontHeight, self.FrontWheelRadius, true , self:LocalToWorld( self.CustomWheelPosFR + Vector(0,0,self.CustomSuspensionTravel * 0.5) ),self.CustomSuspensionTravel, self.FrontConstant, self.FrontDamping, self.FrontRelativeDamping)
			self:CreateWheel(3, WheelRL, self:LocalToWorld( self.CustomWheelPosRL ), self.RearHeight, self.RearWheelRadius, false , self:LocalToWorld( self.CustomWheelPosRL + Vector(0,0,self.CustomSuspensionTravel * 0.5) ),self.CustomSuspensionTravel, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
			self:CreateWheel(4, WheelRR, self:LocalToWorld( self.CustomWheelPosRR ), self.RearHeight, self.RearWheelRadius, true , self:LocalToWorld( self.CustomWheelPosRR + Vector(0,0,self.CustomSuspensionTravel * 0.5) ), self.CustomSuspensionTravel, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
			
			if self.CustomWheelPosML then
				self:CreateWheel(5, WheelML, self:LocalToWorld( self.CustomWheelPosML ), self.RearHeight, self.RearWheelRadius, false , self:LocalToWorld( self.CustomWheelPosML + Vector(0,0,self.CustomSuspensionTravel * 0.5) ),self.CustomSuspensionTravel, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
			end
			
			if self.CustomWheelPosMR then
				self:CreateWheel(6, WheelMR, self:LocalToWorld( self.CustomWheelPosMR ), self.RearHeight, self.RearWheelRadius, true , self:LocalToWorld( self.CustomWheelPosMR + Vector(0,0,self.CustomSuspensionTravel * 0.5) ), self.CustomSuspensionTravel, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
			end
		else
			if IsValid( self.EntityOwner ) then
				self.EntityOwner:PrintMessage( HUD_PRINTTALK, "ERROR: no wheel model defined. Removing vehicle. (Class: "..self:GetSpawn_List()..")")
			end
			self:Remove()
		end
	else
		self:CreateWheel(1, WheelFL, self:GetAttachment( self:LookupAttachment( "wheel_fl" ) ).Pos, self.FrontHeight, self.FrontWheelRadius, false , self.posepositions.Pose1_Pos_FL, self.VehicleData.suspensiontravel_fl, self.FrontConstant, self.FrontDamping, self.FrontRelativeDamping)
		self:CreateWheel(2, WheelFR, self:GetAttachment( self:LookupAttachment( "wheel_fr" ) ).Pos, self.FrontHeight, self.FrontWheelRadius, true , self.posepositions.Pose1_Pos_FR, self.VehicleData.suspensiontravel_fr, self.FrontConstant, self.FrontDamping, self.FrontRelativeDamping)
		self:CreateWheel(3, WheelRL, self:GetAttachment( self:LookupAttachment( "wheel_rl" ) ).Pos, self.RearHeight, self.RearWheelRadius, false , self.posepositions.Pose1_Pos_RL, self.VehicleData.suspensiontravel_rl, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
		self:CreateWheel(4, WheelRR, self:GetAttachment( self:LookupAttachment( "wheel_rr" ) ).Pos, self.RearHeight, self.RearWheelRadius, true , self.posepositions.Pose1_Pos_RR, self.VehicleData.suspensiontravel_rr, self.RearConstant, self.RearDamping, self.RearRelativeDamping)
	end
	
	timer.Simple( 0.01, function()		
		if not istable( self.Wheels ) then return end
		
		for i = 1, table.Count( self.Wheels ) do
			local Ent = self.Wheels[ i ]
			local PhysObj = Ent:GetPhysicsObject()
			
			if IsValid( PhysObj ) then
				PhysObj:EnableMotion( true )
			end
		end
		
		timer.Simple( 0.1, function()
			if not IsValid( self ) then return end
			
			self:GetPhysicsObject():EnableMotion(true)
			
			local PhysObj = self.MassOffset:GetPhysicsObject()
			if IsValid( PhysObj ) then
				PhysObj:EnableMotion(true)
			end
		end )
	end )

	if istable( self.GibModels ) then
		for _, modelName in ipairs( self.GibModels ) do
			util.PrecacheModel( modelName )
		end
	end

	self:OnSpawn()
	hook.Run( "simfphysOnSpawn", self )

	self:SetlvsReady( true )

	self.VehicleData["filter"] = self:GetCrosshairFilterEnts()
end

function ENT:CreateWheel(index, name, attachmentpos, height, radius, swap_y , poseposition, suspensiontravel, constant, damping, rdamping)
	local fAng = self:LocalToWorldAngles( self.VehicleData.LocalAngForward )
	local rAng = self:LocalToWorldAngles( self.VehicleData.LocalAngRight )
	
	local Forward = fAng:Forward() 
	local Right = swap_y and -rAng:Forward() or rAng:Forward()
	local Up = self:GetUp()
	
	local RopeLength = 150
	local LimiterLength = 60
	local LimiterRopeLength = math.sqrt( (suspensiontravel * 0.5) ^ 2 + LimiterLength ^ 2 )
	local WheelMass = self.Mass / 32
	
	if self.FrontWheelMass and (index == 1 or index == 2) then
		WheelMass = self.FrontWheelMass
	end
	if self.RearWheelMass and (index == 3 or index == 4 or index == 5 or index == 6) then
		WheelMass = self.RearWheelMass
	end

	self.name = ents.Create( "gmod_sent_vehicle_fphysics_wheel" )
	self.name:SetPos( attachmentpos - Up * height)
	self.name:SetAngles( fAng )
	self.name:Spawn()
	self.name:Activate()
	self.name:PhysicsInitSphere( radius, "jeeptire" )
	self.name:SetCollisionBounds( Vector(-radius,-radius,-radius), Vector(radius,radius,radius) )
	self.name:GetPhysicsObject():EnableMotion(false)
	self.name:GetPhysicsObject():SetMass( WheelMass ) 
	self.name:SetBaseEnt( self )
	simfphys.SetOwner( self.EntityOwner, self.name )
	self.name.EntityOwner = self.EntityOwner
	self.name.Index = index
	self.name.Radius = radius
	self.name:SetRadius( radius )

	if self.CustomWheels then
		local Model = (self.CustomWheelModel_R and (index == 3 or index == 4 or index == 5 or index == 6)) and self.CustomWheelModel_R or self.CustomWheelModel
		local ghostAng = Right:Angle()
		local mirAng = swap_y and 1 or -1
		ghostAng:RotateAroundAxis(Forward,self.CustomWheelAngleOffset.p * mirAng)
		ghostAng:RotateAroundAxis(Right,self.CustomWheelAngleOffset.r * mirAng)
		ghostAng:RotateAroundAxis(Up,-self.CustomWheelAngleOffset.y)
		
		local Camber = self.CustomWheelCamber or 0
		ghostAng:RotateAroundAxis(Forward, Camber * mirAng)
		
		self.GhostWheels[index] = ents.Create( "gmod_sent_vehicle_fphysics_attachment" )
		self.GhostWheels[index]:SetModel( Model )
		self.GhostWheels[index]:SetPos( self.name:GetPos() )
		self.GhostWheels[index]:SetAngles( ghostAng )
		self.GhostWheels[index]:SetOwner( self )
		self.GhostWheels[index]:Spawn()
		self.GhostWheels[index]:Activate()
		self.GhostWheels[index]:SetNotSolid( true )
		self.GhostWheels[index].DoNotDuplicate = true
		self.GhostWheels[index]:SetParent( self.name )
		self:DeleteOnRemove( self.GhostWheels[index] )
		simfphys.SetOwner( self.EntityOwner, self.GhostWheels[index] )
		
		self.GhostWheels[index]:SetRenderMode( RENDERMODE_TRANSALPHA )
		
		if self.ModelInfo then
			if self.ModelInfo.WheelColor then
				self.GhostWheels[index]:SetColor( self.ModelInfo.WheelColor )
			end
		end
		
		self.name.GhostEnt = self.GhostWheels[index]
		
		local nocollide = constraint.NoCollide(self,self.name,0,0)
		nocollide.DoNotDuplicate = true
	end

	local targetentity = self
	if self.CustomWheels then
		if index == 1 or index == 2 then
			targetentity = self.SteerMaster or self
		end
		if index == 3 or index == 4 then
			targetentity = self.SteerMaster2 or self
		end
	end
	
	local Ballsocket = constraint.AdvBallsocket(targetentity,self.name,0,0,Vector(0,0,0),Vector(0,0,0),0,0, -0.01, -0.01, -0.01, 0.01, 0.01, 0.01, 0, 0, 0, 1, 1) 
	local Rope1 = constraint.Rope(self,self.name,0,0,self:WorldToLocal( self.name:GetPos() + Forward * RopeLength * 0.5 + Right * RopeLength), Vector(0,0,0), Vector(RopeLength * 0.5,RopeLength,0):Length(), 0, 0, 0,"cable/cable2", true )
	local Rope2 = constraint.Rope(self,self.name,0,0,self:WorldToLocal( self.name:GetPos() - Forward * RopeLength * 0.5 + Right * RopeLength), Vector(0,0,0), Vector(RopeLength * 0.5,RopeLength,0):Length(), 0, 0, 0,"cable/cable2", true )

	if self.StrengthenSuspension == true then
		local Rope3 = constraint.Rope(self,self.name,0,0,self:WorldToLocal( poseposition - Up * suspensiontravel * 0.5 + Right * LimiterLength), Vector(0,0,0),LimiterRopeLength * 0.99, 0, 0, 0,"cable/cable2", false )
		local Rope4 = constraint.Rope(self,self.name,0,0,self:WorldToLocal( poseposition - Up * suspensiontravel * 0.5 - Right * LimiterLength), Vector(0,0,0),LimiterRopeLength * 1, 0, 0, 0,"cable/cable2", false )
		local elastic1 = constraint.Elastic(self.name, self, 0, 0, Vector(0,0,height), self:WorldToLocal( self.name:GetPos() ), constant * 0.5, damping * 0.5, rdamping * 0.5,"cable/cable2",0, false)
		local elastic2 = constraint.Elastic(self.name, self, 0, 0, Vector(0,0,height), self:WorldToLocal( self.name:GetPos() ), constant * 0.5, damping * 0.5, rdamping * 0.5,"cable/cable2",0, false)
		
		Rope3.DoNotDuplicate = true
		Rope4.DoNotDuplicate = true
		elastic1.DoNotDuplicate = true
		elastic2.DoNotDuplicate = true
		self.Elastics[index] = elastic1
		self.Elastics[index * 10] = elastic2
	else
		local Rope3 = constraint.Rope(self,self.name,0,0,self:WorldToLocal( poseposition - Up * suspensiontravel * 0.5 + Right * LimiterLength), Vector(0,0,0),LimiterRopeLength, 0, 0, 0,"cable/cable2", false )
		local elastic = constraint.Elastic(self.name, self, 0, 0, Vector(0,0,height), self:WorldToLocal( self.name:GetPos() ), constant, damping, rdamping,"cable/cable2",0, false) 
		
		Rope3.DoNotDuplicate = true
		elastic.DoNotDuplicate = true
		self.Elastics[index] = elastic
	end
	
	self.Wheels[index] = self.name
	
	Ballsocket.DoNotDuplicate = true
	Rope1.DoNotDuplicate = true
	Rope2.DoNotDuplicate = true
	
	if index == 2 then
		if IsValid( self.Wheels[ 1 ] ) and IsValid( self.Wheels[ 2 ] ) then
			local nocollide = constraint.NoCollide( self.Wheels[ 1 ], self.Wheels[ 2 ], 0, 0 )
			nocollide.DoNotDuplicate = true
		end
		
	elseif index == 4 then
		if IsValid( self.Wheels[ 3 ] ) and IsValid( self.Wheels[ 4 ] ) then
			local nocollide = constraint.NoCollide( self.Wheels[ 3 ], self.Wheels[ 4 ], 0, 0 )
			nocollide.DoNotDuplicate = true
		end
		
	elseif index == 6 then
		if IsValid( self.Wheels[ 5 ] ) and IsValid( self.Wheels[ 6 ] ) then
			local nocollide = constraint.NoCollide( self.Wheels[ 5 ], self.Wheels[ 6 ], 0, 0 )
			nocollide.DoNotDuplicate = true
		end
	end
end