Issue with tracking custom user input cooldown

I have a script that does things to a player depending on what key they press.
there are 3 different keys that do different things (they are all in the same function). I have a Gui for them that shows the time left before the cool down for said key is over.

It overall works fine. EXCEPT for when you spam it or use more than one key at once. Then it just does the new key you pressed and forgets about finishing the cool down timer for the other key.

How can I fix this without using multiple scripts?

The script is pretty long so if anyone wants to see it here.


-- Game Services
local UserInputService = game:GetService("UserInputService")
local RepStorage = game:GetService("ReplicatedStorage")
local players = game:GetService("Players")

-- Character or player variables
local Player = game:GetService('Players').LocalPlayer
local character = players.LocalPlayer.Character
local root = character:WaitForChild('HumanoidRootPart')
local humanoid = character:WaitForChild("Humanoid")

-- Gui Variables
local frame = script.Parent.Frame
local PassivePB = frame.Passive:FindFirstChild("ProgressBar")
local AssistivePB = frame.Assistive:FindFirstChild("ProgressBar")
local SpecialPB = frame.SpecialMove:FindFirstChild("ProgressBar")

-- Function Variables
local doubleJumpEnabled = false
local SpecialMoveReady = true
local Can_Dash = true
local dash = true

-- Configurables
local cooldown = 60
local PassiveCooldown = 5
local AssistiveCooldwn = 25
local length = 15
local Dash_Normal = 15

-- PASSIVE
humanoid.StateChanged:Connect(function(oldState, newState)
	if newState == Enum.HumanoidStateType.Jumping then
		if not doubleJumpEnabled then 
			wait(.2)
			if humanoid:GetState() == Enum.HumanoidStateType.Freefall then
				doubleJumpEnabled = true 
				local	Ani = humanoid:LoadAnimation(RepStorage:FindFirstChild("Animation"))
				Ani:Play()
			end
		end
	elseif newState == Enum.HumanoidStateType.Landed then	
		doubleJumpEnabled = false
	end
end)


UserInputService.InputBegan:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.Space then
		if doubleJumpEnabled then
			if humanoid:GetState() ~= Enum.HumanoidStateType.Jumping then
				humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
				spawn(function()
					PassivePB.Size = UDim2.new(0,0,1,0)
					doubleJumpEnabled = false 
					script.Disabled = true
					for cycle = 1, 150 do
						PassivePB.Size = UDim2.new(0,cycle,1,0)
						wait(PassiveCooldown/150)
					end
					script.Disabled = false					
				end)
			end
		end				
		
		
	elseif input.KeyCode == Enum.KeyCode.E and Can_Dash == true then
		Can_Dash = false
		AssistivePB.Size = UDim2.new(0,0,1,0)

		local i = Instance.new('BodyPosition')
		i.MaxForce = Vector3.new(1000000,0,1000000) -- y-component is 0 because we don't want them to fly
		i.P = 100000
		i.D = 2000
		i.Position = (root.CFrame*CFrame.new(0,0,-Dash_Normal)).Position --get 20 units in front of the player
		i.Parent = root
		wait(.2)
		i:Destroy()

		for i = 1, 150 do
			AssistivePB.Size = UDim2.new(0,i,1,0)
			wait(AssistiveCooldwn/150)
		end

		Can_Dash = true		
		
	elseif input.KeyCode == Enum.KeyCode.RightShift and SpecialMoveReady == true then
		SpecialMoveReady = false
		humanoid.WalkSpeed = 20

		for i = 150, 0 , -1 do
			SpecialPB.Size = UDim2.new(0,i,1,0)
			wait(length/150)
		end
		humanoid.WalkSpeed = 16
		for i = 0, 150 do
			SpecialPB.Size = UDim2.new(0,i,1,0)
			wait(cooldown/150)
		end		

		SpecialMoveReady = true
	end
end)

The issue here is that on every jump you are spawning a new thread to handle the cooldown wait, which means that any subsequent jump’s disregard the wait in the other thread.

A reasonably accurate way to track cool down’s can be through the use of tick(). Here’s how it might look like:

  • We set up a table to hold the cooldown for some specific thing (key) (i.e double jumping in your case, with the value of the key being tick(), i.e the timestamp in seconds when the last input was recorded. (We use tick() as a placeholder to represent ‘now’ as in, when the script runs)
  • The user provides input! We have to check here if their tick() - CooldownTable['NameOfCooldown'] is less than CooldownTime.
  • If it is, we return and don’t let them do the thing they attempted to do. If it’s been equal to or longer than the wait time, they can do the thing.

Here’s what it might very roughly look like (for demo purposes only):

local UIS = game:GetService('UserInputService')

local CooldownTable = {
    ["Example1"] = {
        Duration = 5, LastInput = tick()
    };
}

UIS.InputBegan:Connect(function(input)
    if input.KeyCode == Enum.KeyCode.Space then
        if tick() - CooldownTable["Example1"]["LastInput"] >= CooldownTable["Example1"]["Duration"] then
            CooldownTable["Example1"]["LastInput"] = tick() -- Reset their LastInput to 'now' because they just pressed it successfully
            -- They can do the action!
        else
            -- Cooldown not expired yet
        end
    end
end)

Hope that helps!

1 Like