Abilities System Not Working!

Hello devs!

I am making an abilities system in my game where the player can equip three abilities (with keybinds F, G, and H) at a time. There is a bug right now where if you do an ability with keybind G for example, unequip the ability, and equip another ability (it will have keybind G), and actually press G, it will do the ability that you have equipped AND the one that you previously unequipped.

Here is my code right now.

ClientFunctions Script (StarterPlayerScripts):

local Players = game:GetService("Players")

local UI = {
	Players.LocalPlayer.PlayerGui:WaitForChild("ShopUI"),
	Players.LocalPlayer.PlayerGui:WaitForChild("InventoryUI"),
	Players.LocalPlayer.PlayerGui:WaitForChild("AbilitiesUI"),
	Players.LocalPlayer.PlayerGui:WaitForChild("VIPui"),
}

_G.HideAllUI = function(exception)
	for i, gui in UI do
		if gui.Name == exception then continue end
		gui.Enabled = false
	end
end

_G.UpdateAbilityKeybinds = function()
	local player = game.Players.LocalPlayer
	local equippedAbilities = player.EquippedAbilities:GetChildren()
	local keybinds = { Enum.KeyCode.F, Enum.KeyCode.G, Enum.KeyCode.H } -- Default keybinds
	local keyStringMap = {}

	-- Clear existing keybinds
	for _, abilityUI in pairs(player.PlayerGui.BottomBar.Frame:GetChildren()) do
		if abilityUI:IsA("Frame") and abilityUI:FindFirstChild("Keystroke") then
			abilityUI.Keystroke.Value = "Unknown" -- Reset to default value
		end
	end

	-- Assign keybinds to equipped abilities
	for i, ability in ipairs(equippedAbilities) do
		if ability:IsA("StringValue") and ability.Name ~= "Keystroke" then
			local key = keybinds[i] or Enum.KeyCode.Unknown
			local keyString = key.Name
			keyStringMap[ability.Name] = keyString

			local abilityUI = player.PlayerGui.BottomBar.Frame:FindFirstChild(ability.Name)
			if abilityUI and abilityUI:IsA("Frame") then
				local keystrokeValue = abilityUI:FindFirstChild("Keystroke")
				if keystrokeValue then
					keystrokeValue.Value = keyString
				end
			end
		end
	end

	return keyStringMap
end

Dash (StarterPlayerScripts → Abilities):

local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local UpgradeItems = require(ReplicatedStorage.Modules.UpgradeItems)
local player = game.Players.LocalPlayer

local DashAnimation = script:WaitForChild('Animation')

task.wait(2.1)

-- Settings
local velocity = 30000 -- dash speed
local debounce = false -- debounce, do not set to true
local cooldown = UpgradeItems.Dash.Cooldown
local duration = UpgradeItems.Dash.StartingTime

local currentAbilityConnection

-- Get the TextLabel
local textLabel = player:WaitForChild("PlayerGui"):WaitForChild("BottomBar"):WaitForChild("Frame"):WaitForChild("Dash"):WaitForChild("TextLabel")

local function updateKeyDisplay()
	local keyStringMap = _G.UpdateAbilityKeybinds()
	local keyString = keyStringMap["Dash"] or "Unknown"
	local textLabel = player:WaitForChild("PlayerGui"):WaitForChild("BottomBar"):WaitForChild("Frame"):WaitForChild("Dash"):WaitForChild("TextLabel")
	textLabel.Text = keyString
end

local function updateCooldownText(remainingTime)
	textLabel.Text = string.format("%.1f", remainingTime)
end

local function Dash()
	if not player.Abilities:FindFirstChild("Dash") then return end

	local character = player.Character
	if character and not debounce then
		debounce = true

		local humanoid = character.Humanoid
		local HRP = character.HumanoidRootPart
		local loadedAnimation = humanoid.Animator:LoadAnimation(DashAnimation)

		local dashDirection = nil
		local moveDirection = humanoid.MoveDirection
		local lookVector = HRP.CFrame.LookVector
		local minusVelocity = -velocity

		local isOnGround = humanoid.FloorMaterial ~= Enum.Material.Air and humanoid.FloorMaterial ~= Enum.Material.Water

		if isOnGround then
			if moveDirection == Vector3.new(0,0,0) then
				dashDirection = HRP.Position + Vector3.new(lookVector.X, 0, lookVector.Z)
			else
				dashDirection = HRP.Position + Vector3.new(moveDirection.X, 0, moveDirection.Z)
			end

			local bodyGyro = Instance.new("BodyGyro")
			bodyGyro.Parent = HRP
			bodyGyro.MaxTorque = Vector3.new(math.huge, math.huge, math.huge)
			bodyGyro.D = 0
			bodyGyro.P = 500000
			bodyGyro.CFrame = CFrame.lookAt(HRP.Position, dashDirection)

			script.DashSound:Play()

			local trail = script.Trail:Clone()
			trail.Attachment0 = HRP:WaitForChild("TrailAttachment0")
			trail.Attachment1 = HRP:WaitForChild("TrailAttachment1")
			trail.Parent = HRP

			local attachment = Instance.new("Attachment")
			attachment.Parent = HRP

			local vectorForce = Instance.new("VectorForce")
			vectorForce.Parent = HRP
			vectorForce.Attachment0 = attachment
			vectorForce.Force = Vector3.new(0, 0, minusVelocity)

			loadedAnimation:Play()
			humanoid.AutoRotate = false

			wait(duration)

			humanoid.AutoRotate = true

			vectorForce:Destroy()
			bodyGyro:Destroy()
			attachment:Destroy()
			trail:Destroy()
			loadedAnimation:Stop()
		end

		-- Cooldown timer
		local remainingCooldown = cooldown
		while remainingCooldown > 0 do
			updateCooldownText(remainingCooldown)
			wait(0.1)
			remainingCooldown = remainingCooldown - 0.1
		end

		updateKeyDisplay()

		debounce = false
	end
end

local function updateKey()
	local keyStringMap = _G.UpdateAbilityKeybinds()
	local keyString = keyStringMap["Dash"]
	if keyString then
		return Enum.KeyCode[keyString] or Enum.KeyCode.Unknown
	else
		return Enum.KeyCode.Unknown
	end
end

local function connectInputToAbility(key)
	if currentAbilityConnection then
		currentAbilityConnection:Disconnect()
	end

	currentAbilityConnection = UserInputService.InputBegan:Connect(function(input, gameProcessed)
		if not gameProcessed and input.KeyCode == key then
			Dash()
		end
	end)
end

-- Initial Key Display
updateKeyDisplay()

local key = updateKey()
connectInputToAbility(key)

player.EquippedAbilities.ChildAdded:Connect(function()
	key = updateKey()
	updateKeyDisplay()
	connectInputToAbility(key)
end)

player.EquippedAbilities.ChildRemoved:Connect(function()
	key = updateKey()
	updateKeyDisplay()
	connectInputToAbility(key)
end)

player.PlayerGui.BottomBar.Frame.Dash.Activated:Connect(function()
	Dash()
end)

High Jump (StarterPlayerScripts → Abilities):

local player = game.Players.LocalPlayer
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local UpgradeItems = require(ReplicatedStorage.Modules.UpgradeItems)

local jumpSettings = UpgradeItems["High Jump"]
local cooldown = jumpSettings.Cooldown
local duration = jumpSettings.StartingTime
local debounce = false

task.wait(2.1)

local currentAbilityConnection

local jumpButton = player:WaitForChild("PlayerGui"):WaitForChild("BottomBar"):WaitForChild("Frame"):WaitForChild("High Jump")
local textLabel = jumpButton:WaitForChild("TextLabel")

local function updateKeyDisplay()
	local keyStringMap = _G.UpdateAbilityKeybinds()
	local keyString = keyStringMap["High Jump"] or "Unknown"
	local textLabel = player:WaitForChild("PlayerGui"):WaitForChild("BottomBar"):WaitForChild("Frame"):WaitForChild("High Jump"):WaitForChild("TextLabel")
	textLabel.Text = keyString
end

local function updateCooldownText(remainingTime)
	textLabel.Text = string.format("%.1f", remainingTime)
end

local function activateJump()
	if not player.Abilities:FindFirstChild("High Jump") then return end

	if not debounce then
		debounce = true
		local character = player.Character
		if character then
			ReplicatedStorage.PlayerJump:FireServer(character, true)
			wait(duration)
			ReplicatedStorage.PlayerJump:FireServer(character, false)

			local remainingCooldown = cooldown
			while remainingCooldown > 0 do
				updateCooldownText(remainingCooldown)
				wait(0.1)
				remainingCooldown = remainingCooldown - 0.1
			end

			updateKeyDisplay()
			debounce = false
		end
	end
end

local function updateKey()
	local keyStringMap = _G.UpdateAbilityKeybinds()
	local keyString = keyStringMap["High Jump"]
	if keyString then
		return Enum.KeyCode[keyString] or Enum.KeyCode.Unknown
	else
		return Enum.KeyCode.Unknown
	end
end

local function connectInputToAbility(key)
	if currentAbilityConnection then
		currentAbilityConnection:Disconnect()
	end

	currentAbilityConnection = UserInputService.InputBegan:Connect(function(input, gameProcessed)
		if not gameProcessed and input.KeyCode == key then
			activateJump()
		end
	end)
end

-- Initial Key Display
updateKeyDisplay()

local key = updateKey()
connectInputToAbility(key)

player.EquippedAbilities.ChildAdded:Connect(function()
	key = updateKey()
	updateKeyDisplay()
	connectInputToAbility(key)
end)

player.EquippedAbilities.ChildRemoved:Connect(function()
	key = updateKey()
	updateKeyDisplay()
	connectInputToAbility(key)
end)

jumpButton.Activated:Connect(activateJump)

Invisibility (StarterPlayerScripts → Abilities):

local player = game.Players.LocalPlayer
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local UpgradeItems = require(ReplicatedStorage.Modules.UpgradeItems)

local jumpSettings = UpgradeItems["High Jump"]
local cooldown = jumpSettings.Cooldown
local duration = jumpSettings.StartingTime
local debounce = false

task.wait(2.1)

local currentAbilityConnection

local jumpButton = player:WaitForChild("PlayerGui"):WaitForChild("BottomBar"):WaitForChild("Frame"):WaitForChild("High Jump")
local textLabel = jumpButton:WaitForChild("TextLabel")

local function updateKeyDisplay()
	local keyStringMap = _G.UpdateAbilityKeybinds()
	local keyString = keyStringMap["High Jump"] or "Unknown"
	local textLabel = player:WaitForChild("PlayerGui"):WaitForChild("BottomBar"):WaitForChild("Frame"):WaitForChild("High Jump"):WaitForChild("TextLabel")
	textLabel.Text = keyString
end

local function updateCooldownText(remainingTime)
	textLabel.Text = string.format("%.1f", remainingTime)
end

local function activateJump()
	if not player.Abilities:FindFirstChild("High Jump") then return end

	if not debounce then
		debounce = true
		local character = player.Character
		if character then
			ReplicatedStorage.PlayerJump:FireServer(character, true)
			wait(duration)
			ReplicatedStorage.PlayerJump:FireServer(character, false)

			local remainingCooldown = cooldown
			while remainingCooldown > 0 do
				updateCooldownText(remainingCooldown)
				wait(0.1)
				remainingCooldown = remainingCooldown - 0.1
			end

			updateKeyDisplay()
			debounce = false
		end
	end
end

local function updateKey()
	local keyStringMap = _G.UpdateAbilityKeybinds()
	local keyString = keyStringMap["High Jump"]
	if keyString then
		return Enum.KeyCode[keyString] or Enum.KeyCode.Unknown
	else
		return Enum.KeyCode.Unknown
	end
end

local function connectInputToAbility(key)
	if currentAbilityConnection then
		currentAbilityConnection:Disconnect()
	end

	currentAbilityConnection = UserInputService.InputBegan:Connect(function(input, gameProcessed)
		if not gameProcessed and input.KeyCode == key then
			activateJump()
		end
	end)
end

-- Initial Key Display
updateKeyDisplay()

local key = updateKey()
connectInputToAbility(key)

player.EquippedAbilities.ChildAdded:Connect(function()
	key = updateKey()
	updateKeyDisplay()
	connectInputToAbility(key)
end)

player.EquippedAbilities.ChildRemoved:Connect(function()
	key = updateKey()
	updateKeyDisplay()
	connectInputToAbility(key)
end)

jumpButton.Activated:Connect(activateJump)

Shield (StarterPlayerScripts → Abilities):

local player = game.Players.LocalPlayer
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local UpgradeItems = require(ReplicatedStorage.Modules.UpgradeItems)

local shieldSettings = UpgradeItems["Shield"]
local cooldown = shieldSettings.Cooldown
local duration = shieldSettings.StartingTime
local debounce = false

task.wait(2.1)

local currentAbilityConnection

local shieldButton = player:WaitForChild("PlayerGui"):WaitForChild("BottomBar"):WaitForChild("Frame"):WaitForChild("Shield")
local textLabel = shieldButton:WaitForChild("TextLabel")

local function updateKeyDisplay()
	local keyStringMap = _G.UpdateAbilityKeybinds()
	local keyString = keyStringMap["Shield"] or "Unknown"
	local textLabel = player:WaitForChild("PlayerGui"):WaitForChild("BottomBar"):WaitForChild("Frame"):WaitForChild("Shield"):WaitForChild("TextLabel")
	textLabel.Text = keyString
end

local function updateCooldownText(remainingTime)
	textLabel.Text = string.format("%.1f", remainingTime)
end

local function activateShield()
	if not player.Abilities:FindFirstChild("Shield") then return end

	if not debounce then
		debounce = true
		local character = player.Character
		if character then
			ReplicatedStorage.GiveShield:FireServer(character)
			wait(duration)
			ReplicatedStorage.DestroyShield:FireServer(character)

			local remainingCooldown = cooldown
			while remainingCooldown > 0 do
				updateCooldownText(remainingCooldown)
				wait(0.1)
				remainingCooldown = remainingCooldown - 0.1
			end

			updateKeyDisplay()
			debounce = false
		end
	end
end

local function updateKey()
	local keyStringMap = _G.UpdateAbilityKeybinds()
	local keyString = keyStringMap["Shield"]
	if keyString then
		return Enum.KeyCode[keyString] or Enum.KeyCode.Unknown
	else
		return Enum.KeyCode.Unknown
	end
end

local function connectInputToAbility(key)
	if currentAbilityConnection then
		currentAbilityConnection:Disconnect()
	end

	currentAbilityConnection = UserInputService.InputBegan:Connect(function(input, gameProcessed)
		if not gameProcessed and input.KeyCode == key then
			activateShield()
		end
	end)
end

-- Initial Key Display
updateKeyDisplay()

local key = updateKey()
connectInputToAbility(key)

player.EquippedAbilities.ChildAdded:Connect(function()
	key = updateKey()
	updateKeyDisplay()
	connectInputToAbility(key)
end)

player.EquippedAbilities.ChildRemoved:Connect(function()
	key = updateKey()
	updateKeyDisplay()
	connectInputToAbility(key)
end)

shieldButton.Activated:Connect(activateShield)

If you need any more code snippets please ask!

Thanks
-DarkPurpleElixr

2 Likes

The way I would handle it is to add an Attribute under the Player instance for each of the keybinds as either a text or number type. When the player changes the assigned ability, update the value in the Attribute. On the key press, check the relevant Attribute value and run whichever ability.