Cruise Control function doesn't behave as expected

To start this off, I didn’t make this script, this is altering a scripted system that a friend made and he is no longer around (left the platform and deleted all social channels) to assist me in altering his creation.

So here is the issue, when you activate the cruise control, the script adjusts the torque, throttle, and more to allow for a constant state of speed. However in doing this, below a certain threshold speed (dependent on your gear ratios and max speed setting) the script doesn’t adjust parameters correctly enough to maintain speed. Rather it has no power and the vehicle stops.

If the player presses the accelerator during this it will accelerate to the speed set when you activate cruise but you have to hold the throttle to maintain speed. It should be the other way around.

I believe I know where this happens, it is the line that goes as follows:

local engineChange 	= cruiseControl.Value and 0.0 or (thr * tq * gmul) + engineDrag + engineFeedback

A breakdown is this: engineChange is a setting applied to the vehicle in motion, if the Cruise Control value is true, it adjusts that setting to 0 which cuts the acceleration or something to 0 and thus the vehicle cannot accelerate nor decelerate. At least this is what is supposed to be the case. I am genuinely at witts end trying to figure out why its behaving so oddly below a certain speed.

Here is the full script:

--------------------------------------------------------------------------------------------------
local coast					= 0.01	-- Affects coasting.
--local brakeMultiplier 		= 2	-- Affects braking power.
--------------------------------------------------------------------------------------------------
-- Changing anything below is likely going to break the car, only advanced scripters should continue.
local UIS 				= game:GetService("UserInputService")
local shoulderDeadzone	= 0.05
local stickDeadzone		= 0.25
local P					= script.Parent
local player 			= game.Players.LocalPlayer
wait(0.2)
local CarObject 		= script.CarObject.Value
local seat				= CarObject.VehicleSeat
local exhausts			= CarObject.Exhausts
local vars				= seat.ATSVariables
local fuelVars			= seat.FuelVariables
local powerwheels		= {}
local steerMotors		= {}
for i,v in pairs(CarObject.Wheels:GetChildren()) do
	if (v.Wheel:FindFirstChild("BAV")) and (v.Wheel.BAV.P > 0) then
		table.insert(powerwheels, v.Wheel)
	end
	if (v:FindFirstChild("Steer")) then
		table.insert(steerMotors, v.Steer.Motor)
	end
end
local wheelRadius		= powerwheels[1].Size.x / 2
local automatic			= vars.AutomaticMode
local redlineTimer		= 0.0
local engineRPM			= vars.RPM
local wheelRPM			= 0.0
local gearRatios		= {-0.3, 0.0, 1.0}
local currentGear		= vars.Gear
local smoothThrottle	= vars.SmoothThrottle
local cruiseControl		= vars.CruiseControl
local clutch			= vars.Clutch
local steeringAngle		= vars.SteeringAngle
local steeringSpeed		= vars.SteeringSpeed
local maxSpeed			= vars.MaxSpeed
local brakePower		= vars.BrakePower
local afmTrsh			= fuelVars.AFMTrsh
local fuel				= fuelVars.Fuel
local fuelDriveDrain	= fuelVars.FuelDriveDrain
local fuelIdleDrain		= fuelVars.FuelIdleDrain
local instconsum		= fuelVars.Instconsum
local speed				= fuelVars.Speed
local fuelSysActivator  = fuelVars.FuelSystemActivator

-------------------------------------------------------------------------------------------------
-- DisableDetectorFunctions and varables
function Stop()
	for i,v in pairs(CarObject.Wheels:GetChildren()) do
		if (v:FindFirstChild("BAV")) then
			v.BAV.angularvelocity = Vector3.new()
		end
	end
	vars.Gear.Value = 0
	vars.RPM.Value = 0
	vars.SmoothThrottle.Value = 0
	SetExhaustsEnabled(false)
end

seat.ChildRemoved:connect(function(child)
	if (child.Name == "SeatWeld") then
		Stop()
		--script:Destroy()
	end		
end)

P.Changed:connect(function(property)
	if (property == "Disabled") and (P.Disabled == true) then
		Stop()
	end
end)


--------------------------------------------------------------------------------------------------
-- Methods
function SetExhaustsEnabled(bool)
	for i,v in ipairs(exhausts:GetChildren()) do
		if v.Name=="Emitter" then
			v.SmokeFumes.Enabled = bool
		end
	end
end
function Clamp(val, Min, Max) return math.min( math.max(val, Min), Max) end

function GetInput()
	local inputs = {0.0, 0.0, 0.0} -- Forward, Brake, Steering
	if (UIS:GetGamepadConnected(Enum.UserInputType.Gamepad1)) then
		local GP1state = UIS:GetGamepadState(Enum.UserInputType.Gamepad1)
		for i,v in pairs(GP1state) do
			if (v.KeyCode == Enum.KeyCode.ButtonR2) then
				if (currentGear.Value >= 0) then
					inputs[1] = (v.Position.z > shoulderDeadzone) and v.Position.z or 0.0
				else
					inputs[2] = (v.Position.z > shoulderDeadzone) and v.Position.z or 0.0
				end
			elseif (v.KeyCode == Enum.KeyCode.ButtonL2) then
				if (currentGear.Value >= 0) then
					inputs[2] = (v.Position.z > shoulderDeadzone) and v.Position.z or 0.0
				else
					inputs[1] = (v.Position.z > shoulderDeadzone) and v.Position.z or 0.0
				end
			elseif (v.KeyCode == Enum.KeyCode.Thumbstick1) then
				inputs[3] = (math.abs(v.Position.x) > stickDeadzone) and v.Position.x or 0.0
			end
		end
	end

	local wKey = (UIS:IsKeyDown(Enum.KeyCode.W) or UIS:IsKeyDown(Enum.KeyCode.Up)) and 1 or 0
	local sKey = (UIS:IsKeyDown(Enum.KeyCode.S) or UIS:IsKeyDown(Enum.KeyCode.Down)) and 1 or 0
	local aKey = (UIS:IsKeyDown(Enum.KeyCode.A) or UIS:IsKeyDown(Enum.KeyCode.Left)) and 1 or 0
	local dKey = (UIS:IsKeyDown(Enum.KeyCode.D) or UIS:IsKeyDown(Enum.KeyCode.Right)) and 1 or 0
	
	if (currentGear.Value >= 0) then
		inputs[1] = math.max(wKey, inputs[1])
		inputs[2] = math.max(sKey, inputs[2])
	else
		inputs[1] = math.max(sKey, inputs[1])
		inputs[2] = math.max(wKey, inputs[2])
	end
	local keySteer = dKey - aKey
	if (math.abs(keySteer) > math.abs(inputs[3])) then
		inputs[3] = keySteer
	end
	return inputs
end

function SetThrottle(newThrottle)
	newThrottle = newThrottle / wheelRadius -- Fix
	for i,v in pairs(powerwheels) do
		local dir = seat.CFrame.lookVector
		
		if (v.Parent:FindFirstChild("Steer")) then
			dir = (v.Parent.Steer.CFrame * CFrame.Angles(math.pi/2, 0, 0)).lookVector
		end
		v.BAV.angularvelocity = dir * newThrottle	
	end
end

function SetGear(gear) currentGear.Value = Clamp(gear, -1, 1) end
function GetGearRatio() return gearRatios[currentGear.Value+2] end
function ToEngineRPM(_wheelRPM) return (gearRatios[currentGear.Value+2] == 0) and 0 or _wheelRPM / gearRatios[currentGear.Value+2] end
function ToWheelRPM(_engineRPM) return _engineRPM * gearRatios[currentGear.Value+2] end

function GetWheelSpeed()
	local wspd = 0.0
	for i,v in pairs(powerwheels) do
		local v = powerwheels[i]
		if (v.RotVelocity.magnitude > 0.0) then
			local side = (v.CFrame * CFrame.Angles(math.pi / 1.8, 0, 0)).lookVector.unit
			local rVel = (v.RotVelocity:Dot(side) * v.RotVelocity.unit).magnitude
			wspd = wspd + rVel
		end
	end
	wspd = wspd / #powerwheels * wheelRadius 
	vars.WheelSpeed.Value = wspd
	return wspd
end

exhausts_debounce = false
function SetExhausts(transparency)
	coroutine.resume(coroutine.create(function()	
		if exhausts_debounce then return end
		exhausts_debounce = true
		for i,v in ipairs(exhausts:GetChildren()) do
			if v.Name=="Emitter" then
				v.SmokeFumes.Transparency = NumberSequence.new{
					NumberSequenceKeypoint.new(0,transparency,0),
					NumberSequenceKeypoint.new(1,1,0)
				}
			end
		end
		wait(3)
		exhausts_debounce = false
	end))
end
function Main(deltaTime)
	-- Prerequisites
	redlineTimer = redlineTimer - deltaTime
	local inputs = GetInput()
	if fuel.Value<=0 then
		inputs[1] = 0
		inputs[2] = 0
		fuel.Value = 0
		Stop()
	end
	fuel.Value = (fuelSysActivator.Value and (inputs[1]==1 or inputs[2]==1)) 	and fuel.Value - fuelDriveDrain.Value 	or fuel.Value
	fuel.Value = (fuelSysActivator.Value and (inputs[1]==0 and inputs[2]==0))	and fuel.Value - fuelIdleDrain.Value 	or fuel.Value

	local brk = inputs[2] * brakePower.Value --brakeMultiplier
	vars.TrueBrake.Value = brk
	local thr = (redlineTimer <= 0) and inputs[1] * math.max(clutch.Value, 0.1) or 0.0
	smoothThrottle.Value = 0.8 * smoothThrottle.Value + 0.2 * thr
	local trueSpeed = seat.Velocity.magnitude
	local wheelSpeed = GetWheelSpeed()
	clutch.Value = math.min(clutch.Value + deltaTime * 2, 1.0)
	local connection = (currentGear.Value == 0) and 0.0 or clutch.Value
	-- Steering
	local steerInfluence = 1 - math.min(trueSpeed / 300, 0.5)
	--print(trueSpeed,trueSpeed / 300)
	for i,v in pairs(steerMotors) do
		v.DesiredAngle = (inputs[3] * steeringSpeed.Value * (steerInfluence))*steeringAngle.Value
	end
	-- Redline
	if (engineRPM.Value >= 1.0) then redlineTimer = 0.05 end		
	-- Gearing
	if (automatic.Value) and (currentGear.Value == 0) and (brk > 0.0) then
		SetGear(-1)
		local t = brk
		--brk = throttle
		thr = t
	end	
	if (automatic.Value) and (currentGear.Value == 0) and (thr > 0.1) then SetGear(1) end
	if (automatic.Value) and (thr < 0.01) and (engineRPM.Value < 0.35) and (currentGear.Value == 1 or currentGear.Value == -1) then SetGear(0) end
	if (automatic.Value) then
		if (engineRPM.Value > 0.8 and currentGear.Value > 0 and currentGear.Value < 6) then
			SetGear(currentGear.Value + 1)
		elseif (engineRPM.Value < 0.35 and currentGear.Value > 1) then
			SetGear(currentGear.Value - 1)			
		end
	end
	
	-- Drivetrain
	local engineFeedback	= (currentGear.Value ~= 0) and (wheelSpeed - math.abs(ToWheelRPM(engineRPM.Value) * maxSpeed.Value)) * 0.05 or 0.0
	local engineDrag 		= (currentGear.Value == 0) and (thr < 0.5) and -0.5 or 0.0
	
	local tq			= ((engineRPM.Value^2-(engineRPM.Value-0.05)^3) * 1.35 + 0.7) * seat.Torque * 0.01
	local gmul			= (currentGear.Value ~= 0) and math.abs(1/GetGearRatio()) or 7
	local engineChange 	= cruiseControl.Value and 0.0 or (thr * tq * gmul) + engineDrag + engineFeedback
	print(engineChange)
	engineRPM.Value 	= Clamp(engineRPM.Value + engineChange * deltaTime, 0.1, 1.0)
	
	-- External forces / braking
	local finalDrag 	= (currentGear.Value == 0) and 0.0 or Clamp(currentGear.Value, -1, 1) * -coast * math.abs(1/GetGearRatio())
	local finalBrake 	= 1 - brk	
	wheelRPM = ((currentGear.Value == 0) and wheelRPM * 0.01 or finalBrake * ((1-connection) * wheelRPM + connection * ToWheelRPM(engineRPM.Value) + (finalDrag) * deltaTime))
	local finalExhausts = finalBrake<0 and 0.925 or 0.975
	SetThrottle(wheelRPM * maxSpeed.Value)
	SetExhausts(finalExhausts)
end

currentGear.Changed:connect(function() clutch.Value = 0.0 end)

-- Main loop
local success, message
while true do
	success, message = pcall(Main, wait())
	if (not success) then break end
	if engineRPM.Value ~= 0 then
		seat.Engine:FireServer("Rev",engineRPM.Value)
	end
	if smoothThrottle.Value ~= 0 then
		seat.Trot:FireServer(smoothThrottle.Value)
	end
end
print("ATS error: "..message)
1 Like