function Engine()local V=_Tune2.Horsepower.Value/100 local B=(((_Tune2.Horsepower.Value*(_TPsi*(_Tune.CompressRatio/10)))/7.5)/2)/100 local G=_Tune.PeakRPM/1000 local F=_Tune.PeakSharpness local K=_Tune.CurveMult local E=_Tune.EqPoint/1000 function curveHP(B)B=B/1000 return(-(B-G)^2*math.min(V/G^2,K^(G/V))+V)*(B-B^F/(F*G^(F-1)))end local L=curveHP(_Tune.PeakRPM)function curvePSI(V)V=V/1000 return(-(V-G)^2*math.min(B/G^2,K^(G/B))+B)*(V-V^F/(F*G^(F-1)))end local q=curvePSI(_Tune.PeakRPM)function GetCurve(B,G)local F=math.max(curveHP(B)/(L/V),0)*100 return F,((((F*(E/B))*_Tune.Ratios[G+2])*fFD)*hpScaling)*1000 end function GetPsiCurve(V,G)local F=math.max(curvePSI(V)/(q/B),0)*100 return F,((((F*(E/V))*_Tune.Ratios[G+2])*fFD)*hpScaling)*1000 end local c={}local X={}for V,B in pairs(_Tune.Ratios)do local G={}local F={}for B=math.floor(_Tune.IdleRPM/100),math.ceil((_Tune.Redline+100)/100),1 do local K={}local E={}K.Horsepower,K.Torque=GetCurve(B*100,V-2)E.Horsepower,E.Torque=GetPsiCurve(B*100,V-2)hp1,tq1=GetCurve((B+1)*100,V-2)hp2,tq2=GetPsiCurve((B+1)*100,V-2)K.HpSlope=hp1-K.Horsepower E.HpSlope=hp2-E.Horsepower K.TqSlope=tq1-K.Torque E.TqSlope=tq2-E.Torque G[B]=K F[B]=E end table.insert(c,G)table.insert(X,F)end if _CGear==0 then _ClutchOn=false end local m=_Tune.IdleRPM if not _IsOn then m=0 _CGear=0 _ClutchOn=false _GThrot=_Tune.IdleThrottle/100 end local h=0 local I=0 for V,B in pairs(Drive)do h=h+B.RotVelocity.Magnitude I=I+1 end h=h/I if _ClutchOn then local V=math.max(math.min((h*_Tune.Ratios[_CGear+2])*fFDr,_Tune.Redline+100),m)local B=math.min(math.abs(V-_RPM)/_Tune.ClutchTol,.9)_RPM=_RPM*B+V*(1-B)else if _GThrot-_Tune.IdleThrottle/100>0 then if _RPM>_Tune.Redline then _RPM=_RPM-_Tune.RevBounce*2 else _RPM=math.min(_RPM+_Tune.RevAccel*_GThrot,_Tune.Redline+100)end else _RPM=math.max(_RPM-_Tune.RevDecay,m)end end _spLimit=(_Tune.Redline+100)/(fFDr*_Tune.Ratios[_CGear+2])if _RPM>_Tune.Redline then if _CGear<#_Tune.Ratios-2 then _RPM=_RPM-_Tune.RevBounce else _RPM=_RPM-_Tune.RevBounce*.5 end end local d=_TPsi/_TCount if _Tune.Aspiration~="\083\117\112\101\114"then local V=(((_Tune2.Horsepower.Value*(_TPsi*(_Tune.CompressRatio/10)))/7.5)/2)/100 _Boost=_Boost+(((((_HP*(_GThrot*1.2))/_Tune2.Horsepower.Value)/8-(_Boost/d)*(d/15))*((8/_Tune.TurboSize)*2))/d)*15 if _Boost<.05 then _Boost=.05 elseif _Boost>2 then _Boost=2 end else if _GThrot>sthrot then sthrot=math.min(_GThrot,sthrot+_Tune.Sensitivity)elseif _GThrot<sthrot then sthrot=math.max(_GThrot,sthrot-_Tune.Sensitivity)end _Boost=(_RPM/_Tune.Redline)*(.5+1.5*sthrot)end local U=c[_CGear+2][math.floor(math.min(_Tune.Redline,math.max(_Tune.IdleRPM,_RPM))/100)]_NH=U.Horsepower+(U.HpSlope*((_RPM-math.floor(_RPM/100))/1000))%1 _NT=U.Torque+(U.TqSlope*((_RPM-math.floor(_RPM/100))/1000))%1 if _Tune.Aspiration~="\078\097\116\117\114\097\108"then local V=X[_CGear+2][math.floor(math.min(_Tune.Redline,math.max(_Tune.IdleRPM,_RPM))/100)]_BH=V.Horsepower+(V.HpSlope*((_RPM-math.floor(_RPM/100))/1000))%1 _BT=V.Torque+(V.TqSlope*((_RPM-math.floor(_RPM/100))/1000))%1 _HP=_NH+_BH*(_Boost/2)_OutTorque=_NT+_BT*(_Boost/2)else _HP=_NH _OutTorque=_NT end local s=car.DriveSeat.CFrame.lookVector.y*cGrav if _CGear==-1 then s=-s end _OutTorque=_OutTorque*math.max(1,1+s)local w=0 local v=0 local O=0 local Y=0 for V,B in pairs(car.Wheels:GetChildren())do if B.Name=="\070\076"or B.Name=="\070\082"or B.Name=="\070"then w=w+B.RotVelocity.Magnitude v=v+1 elseif B.Name=="\082\076"or B.Name=="\082\082"or B.Name=="\082"then O=O+B.RotVelocity.Magnitude Y=Y+1 end end w=w/v O=O/Y local t=(w+O)/2 for V,B in pairs(car.Wheels:GetChildren())do local G=(CFrame.new(B.Position-(B.Arm.CFrame*cfWRot).lookVector,B.Position)*cfYRot).lookVector local F=1 local K=1 if B.Name=="\070\076"or B.Name=="\082\076"then F=-1 end if B.Name=="\070\076"or B.Name=="\070\082"then K=math.max(0,math.min(1,1+(((B.RotVelocity.Magnitude-w)/w)/(math.max(_Tune.FDiffSlipThres,1)/100))*((_Tune.FDiffLockThres-50)/50)))if _Tune.Config=="\065\087\068"then K=math.max(0,math.min(1,K*(1+(((w-t)/t)/(math.max(_Tune.CDiffSlipThres,1)/100))*((_Tune.CDiffLockThres-50)/50))))end elseif B.Name=="\082\076"or B.Name=="\082\082"then K=math.max(0,math.min(1,1+(((B.RotVelocity.Magnitude-O)/O)/(math.max(_Tune.RDiffSlipThres,1)/100))*((_Tune.RDiffLockThres-50)/50)))if _Tune.Config=="\065\087\068"then K=math.max(0,math.min(1,K*(1+(((O-t)/t)/(math.max(_Tune.CDiffSlipThres,1)/100))*((_Tune.CDiffLockThres-50)/50))))end end _TCSActive=false _ABSActive=false if _PBrake and(B.Name=="\082\082"or B.Name=="\082\076")then B["\035\065\086"].maxTorque=Vector3.new(math.abs(G.x),math.abs(G.y),math.abs(G.z))*PBrakeForce B["\035\065\086"].angularvelocity=Vector3.new()else if _GBrake==0 then local V=false for G,F in pairs(Drive)do if F==B then V=true end end if V then local V=1 if not script.Parent.IsOn.Value then V=0 end local E=1 if not _ClutchOn then E=0 end local L=_GThrot*_GThrotShift local q=_OutTorque if _Tune.Config=="\065\087\068"then local V=(_Tune.TorqueVector+1)/2 if B.Name=="\070\076"or B.Name=="\070\082"then q=q*(1-V)elseif B.Name=="\082\076"or B.Name=="\082\082"then q=q*V end end tqTCS=1 if _TCS then tqTCS=1-math.min(math.max(0,math.abs(B.RotVelocity.Magnitude*(B.Size.x/2)-B.Velocity.Magnitude)-_Tune.TCSThreshold)/_Tune.TCSGradient,1)*(1-_Tune.TCSLimit/100)end if tqTCS<1 then _TCSAmt=tqTCS _TCSActive=true end local c=1 if _CGear==-1 then c=-1 end B["\035\065\086"].maxTorque=((((((Vector3.new(math.abs(G.x),math.abs(G.y),math.abs(G.z))*q)*(1+(B.RotVelocity.Magnitude/60)^1.15))*L)*tqTCS)*K)*V)*E B["\035\065\086"].angularvelocity=((G*F)*_spLimit)*c else B["\035\065\086"].maxTorque=Vector3.new()B["\035\065\086"].angularvelocity=Vector3.new()end else local V=_GBrake local F=1 if _ABS and math.abs(B.RotVelocity.Magnitude*(B.Size.x/2)-B.Velocity.Magnitude)-_Tune.ABSThreshold>0 then F=0 end _ABSActive=F<1 if B.Name=="\070\076"or B.Name=="\070\082"or B.Name=="\070"then B["\035\065\086"].maxTorque=((Vector3.new(math.abs(G.x),math.abs(G.y),math.abs(G.z))*FBrakeForce)*V)*F else B["\035\065\086"].maxTorque=((Vector3.new(math.abs(G.x),math.abs(G.y),math.abs(G.z))*RBrakeForce)*V)*F end B["\035\065\086"].angularvelocity=Vector3.new()end end end end

function Engine()
	--Horsepower Curve

	local HP=_Tune2.Horsepower.Value/100
	local HP_B=((_Tune2.Horsepower.Value*((_TPsi)*(_Tune.CompressRatio/10))/7.5)/2)/100
	local Peak=_Tune.PeakRPM/1000
	local Sharpness=_Tune.PeakSharpness
	local CurveMult=_Tune.CurveMult
	local EQ=_Tune.EqPoint/1000

	--Horsepower Curve

	function curveHP(RPM)
		RPM=RPM/1000
		return ((-(RPM-Peak)^2)*math.min(HP/(Peak^2),CurveMult^(Peak/HP))+HP)*(RPM-((RPM^Sharpness)/(Sharpness*Peak^(Sharpness-1))))
	end

	local PeakCurveHP = curveHP(_Tune.PeakRPM)

	function curvePSI(RPM)
		RPM=RPM/1000
		return ((-(RPM-Peak)^2)*math.min(HP_B/(Peak^2),CurveMult^(Peak/HP_B))+HP_B)*(RPM-((RPM^Sharpness)/(Sharpness*Peak^(Sharpness-1))))
	end
	local PeakCurvePSI = curvePSI(_Tune.PeakRPM)

	--Plot Current Horsepower
	function GetCurve(x,gear)
		local hp=(math.max(curveHP(x)/(PeakCurveHP/HP),0))*100
		return hp,((hp*(EQ/x))*_Tune.Ratios[gear+2]*fFD*hpScaling)*1000
	end

	--Plot Current Boost (addition to Horsepower)
	function GetPsiCurve(x,gear)
		local hp=(math.max(curvePSI(x)/(PeakCurvePSI/HP_B),0))*100
		return hp,((hp*(EQ/x))*_Tune.Ratios[gear+2]*fFD*hpScaling)*1000
	end	

	--Output Cache	
	local HPCache = {}
	local PSICache = {}

	for gear,ratio in pairs(_Tune.Ratios) do
		local nhpPlot = {}
		local bhpPlot = {}
		for rpm = math.floor(_Tune.IdleRPM/100),math.ceil((_Tune.Redline+100)/100) do
			local ntqPlot = {}
			local btqPlot = {}
			ntqPlot.Horsepower,ntqPlot.Torque = GetCurve(rpm*100,gear-2)
			btqPlot.Horsepower,btqPlot.Torque = GetPsiCurve(rpm*100,gear-2)
			hp1,tq1 = GetCurve((rpm+1)*100,gear-2)
			hp2,tq2 = GetPsiCurve((rpm+1)*100,gear-2)
			ntqPlot.HpSlope = (hp1 - ntqPlot.Horsepower)
			btqPlot.HpSlope = (hp2 - btqPlot.Horsepower)
			ntqPlot.TqSlope = (tq1 - ntqPlot.Torque)
			btqPlot.TqSlope = (tq2 - btqPlot.Torque)
			nhpPlot[rpm] = ntqPlot
			bhpPlot[rpm] = btqPlot
		end
		table.insert(HPCache,nhpPlot)
		table.insert(PSICache,bhpPlot)
	end
	--Neutral Gear
	if _CGear==0 then _ClutchOn = false end

	--Car Is Off
	local revMin = _Tune.IdleRPM
	if not _IsOn then 
		revMin = 0 
		_CGear = 0
		_ClutchOn = false
		_GThrot = _Tune.IdleThrottle/100
	end

	--Determine RPM
	local maxSpin=0
	local maxCount=0
	for i,v in pairs(Drive) do maxSpin = maxSpin + v.RotVelocity.Magnitude maxCount = maxCount + 1 end
	maxSpin=maxSpin/maxCount

	if _ClutchOn then
		local aRPM = math.max(math.min(maxSpin*_Tune.Ratios[_CGear+2]*fFDr,_Tune.Redline+100),revMin)
		local clutchP = math.min(math.abs(aRPM-_RPM)/_Tune.ClutchTol,.9)
		_RPM = _RPM*clutchP  +  aRPM*(1-clutchP)
	else
		if _GThrot-(_Tune.IdleThrottle/100)>0 then
			if _RPM>_Tune.Redline then
				_RPM = _RPM-_Tune.RevBounce*2
			else
				_RPM = math.min(_RPM+_Tune.RevAccel*_GThrot,_Tune.Redline+100)
			end
		else
			_RPM = math.max(_RPM-_Tune.RevDecay,revMin)
		end
	end

	--Rev Limiter
	_spLimit = (_Tune.Redline+100)/(fFDr*_Tune.Ratios[_CGear+2])
	if _RPM>_Tune.Redline then 
		if _CGear<#_Tune.Ratios-2 then
			_RPM = _RPM-_Tune.RevBounce
		else
			_RPM = _RPM-_Tune.RevBounce*.5
		end
	end

	local TPsi = _TPsi/_TCount

	if _Tune.Aspiration ~= "Super" then 
		local HP2_B=((_Tune2.Horsepower.Value*((_TPsi)*(_Tune.CompressRatio/10))/7.5)/2)/100
		_Boost = _Boost + ((((((_HP*(_GThrot*1.2)/_Tune2.Horsepower.Value)/8)-(((_Boost/TPsi*(TPsi/15)))))*((8/_Tune.TurboSize)*2))/TPsi)*15)
		if _Boost < 0.05 then _Boost = 0.05 elseif _Boost > 2 then _Boost = 2 end
	else
		if _GThrot>sthrot then
			sthrot=math.min(_GThrot,sthrot+_Tune.Sensitivity)
		elseif _GThrot<sthrot then
			sthrot=math.max(_GThrot,sthrot-_Tune.Sensitivity)
		end
		_Boost = (_RPM/_Tune.Redline)*(.5+(1.5*sthrot))
	end


	local cTq = HPCache[_CGear+2][math.floor(math.min(_Tune.Redline,math.max(_Tune.IdleRPM,_RPM))/100)]
	_NH = cTq.Horsepower+(cTq.HpSlope*((_RPM-math.floor(_RPM/100))/1000)%1)
	_NT = cTq.Torque+(cTq.TqSlope*((_RPM-math.floor(_RPM/100))/1000)%1)
	if _Tune.Aspiration ~= "Natural" then
		local bTq = PSICache[_CGear+2][math.floor(math.min(_Tune.Redline,math.max(_Tune.IdleRPM,_RPM))/100)]
		_BH = bTq.Horsepower+(bTq.HpSlope*((_RPM-math.floor(_RPM/100))/1000)%1)
		_BT = bTq.Torque+(bTq.TqSlope*((_RPM-math.floor(_RPM/100))/1000)%1)
		_HP = _NH + (_BH*(_Boost/2))
		_OutTorque = _NT + (_BT*(_Boost/2))
	else
		_HP = _NH
		_OutTorque = _NT
	end


	local iComp =(car.DriveSeat.CFrame.lookVector.y)*cGrav
	if _CGear==-1 then iComp=-iComp end
	_OutTorque = _OutTorque*math.max(1,(1+iComp))

	--Average Rotational Speed Calculation
	local fwspeed=0
	local fwcount=0
	local rwspeed=0
	local rwcount=0

	for i,v in pairs(car.Wheels:GetChildren()) do
		if v.Name=="FL" or v.Name=="FR" or v.Name == "F" then
			fwspeed=fwspeed+v.RotVelocity.Magnitude
			fwcount=fwcount+1
		elseif v.Name=="RL" or v.Name=="RR" or v.Name == "R" then
			rwspeed=rwspeed+v.RotVelocity.Magnitude
			rwcount=rwcount+1
		end
	end
	fwspeed=fwspeed/fwcount
	rwspeed=rwspeed/rwcount	
	local cwspeed=(fwspeed+rwspeed)/2

	--Update Wheels
	for i,v in pairs(car.Wheels:GetChildren()) do
		--Reference Wheel Orientation
		local Ref=(CFrame.new(v.Position-((v.Arm.CFrame*cfWRot).lookVector),v.Position)*cfYRot).lookVector
		local aRef=1
		local diffMult=1
		if v.Name=="FL" or v.Name=="RL" then aRef=-1 end

		--Differential/Torque-Vectoring
		if v.Name=="FL" or v.Name=="FR" then
			diffMult=math.max(0,math.min(1,1+((((v.RotVelocity.Magnitude-fwspeed)/fwspeed)/(math.max(_Tune.FDiffSlipThres,1)/100))*((_Tune.FDiffLockThres-50)/50))))
			if _Tune.Config == "AWD" then
				diffMult=math.max(0,math.min(1,diffMult*(1+((((fwspeed-cwspeed)/cwspeed)/(math.max(_Tune.CDiffSlipThres,1)/100))*((_Tune.CDiffLockThres-50)/50)))))
			end
		elseif v.Name=="RL" or v.Name=="RR" then
			diffMult=math.max(0,math.min(1,1+((((v.RotVelocity.Magnitude-rwspeed)/rwspeed)/(math.max(_Tune.RDiffSlipThres,1)/100))*((_Tune.RDiffLockThres-50)/50))))
			if _Tune.Config == "AWD" then
				diffMult=math.max(0,math.min(1,diffMult*(1+((((rwspeed-cwspeed)/cwspeed)/(math.max(_Tune.CDiffSlipThres,1)/100))*((_Tune.CDiffLockThres-50)/50)))))
			end
		end

		_TCSActive = false
		_ABSActive = false
		--Output
		if _PBrake and (v.Name=="RR" or v.Name=="RL") then
			--PBrake
			v["#AV"].maxTorque=Vector3.new(math.abs(Ref.x),math.abs(Ref.y),math.abs(Ref.z))*PBrakeForce
			v["#AV"].angularvelocity=Vector3.new()
		else
			--Apply Power
			if _GBrake==0 then
				local driven = false
				for _,a in pairs(Drive) do if a==v then driven = true end end
				if driven then
					local on=1
					if not script.Parent.IsOn.Value then on=0 end
					local clutch=1
					if not _ClutchOn then clutch=0 end
					local throt = _GThrot * _GThrotShift
					local tq = _OutTorque

					--Apply AWD Vectoring
					if _Tune.Config == "AWD" then
						local bias = (_Tune.TorqueVector+1)/2
						if v.Name=="FL" or v.Name=="FR" then
							tq = tq*(1-bias)
						elseif v.Name=="RL" or v.Name=="RR" then
							tq = tq*bias
						end
					end

					--Apply TCS
					tqTCS = 1
					if _TCS then
						tqTCS = 1-(math.min(math.max(0,math.abs(v.RotVelocity.Magnitude*(v.Size.x/2) - v.Velocity.Magnitude)-_Tune.TCSThreshold)/_Tune.TCSGradient,1)*(1-(_Tune.TCSLimit/100)))
					end
					if tqTCS < 1 then
						_TCSAmt = tqTCS
						_TCSActive = true
					end

					--Update Forces
					local dir=1
					if _CGear==-1 then dir = -1 end
					v["#AV"].maxTorque=Vector3.new(math.abs(Ref.x),math.abs(Ref.y),math.abs(Ref.z))*tq*(1+(v.RotVelocity.Magnitude/60)^1.15)*throt*tqTCS*diffMult*on*clutch
					v["#AV"].angularvelocity=Ref*aRef*_spLimit*dir
				else
					v["#AV"].maxTorque=Vector3.new()
					v["#AV"].angularvelocity=Vector3.new()
				end

				--Brakes
			else
				local brake = _GBrake

				--Apply ABS
				local tqABS = 1
				if _ABS and math.abs(v.RotVelocity.Magnitude*(v.Size.x/2) - v.Velocity.Magnitude)-_Tune.ABSThreshold>0 then
					tqABS = 0
				end
				_ABSActive = (tqABS<1)

				--Update Forces
				if v.Name=="FL" or v.Name=="FR" or v.Name=="F" then
					v["#AV"].maxTorque=Vector3.new(math.abs(Ref.x),math.abs(Ref.y),math.abs(Ref.z))*FBrakeForce*brake*tqABS
				else
					v["#AV"].maxTorque=Vector3.new(math.abs(Ref.x),math.abs(Ref.y),math.abs(Ref.z))*RBrakeForce*brake*tqABS
				end
				v["#AV"].angularvelocity=Vector3.new()
			end
		end
	end
end
