I’m encountering a strange issue in my game where, when the attacker uses an ability on a victim, the victim can also trigger the double-tap mechanic and activate a variant of the ability. This behavior is unintended and creates problems for the gameplay.
What I want to achieve is to ensure that only the attacker, who initiates the ability, can use the double-tap input to activate the variant. The victim should not be able to trigger this mechanic under any circumstances.
I need help fixing this issue to make the game mechanics function as intended.
I’m using knit.
Attack Controller ( Client )
local rp = game:GetService("ReplicatedStorage")
local Uis = game:GetService("UserInputService")
local knit = require(rp.Game.Shared.Packages.Knit)
local plr = game.Players.LocalPlayer
local AttackController = knit.CreateController {
Name = script.Name,
CurrentCooldowns = {},
Connections = {},
Skills = {
Skill1 = 1,
Skill2 = 2,
Skill3 = 3,
Skill4 = 4,
},
Keys = {
[Enum.KeyCode.One] = "Skill1",
[Enum.KeyCode.Two] = "Skill2",
[Enum.KeyCode.Three] = "Skill3",
[Enum.KeyCode.Four] = "Skill4",
[Enum.KeyCode.G] = "Awaken"
},
Console = {
[Enum.KeyCode.ButtonL1] = "Skill1",
[Enum.KeyCode.ButtonL2] = "Skill2",
[Enum.KeyCode.ButtonR1] = "Skill3",
[Enum.KeyCode.ButtonR2] = "Skill4",
[Enum.KeyCode.ButtonX] = "Awaken"
},
}
function lerp(a, b, t)
return a + (b - a) * t
end
function UIEquip(overlay, bool)
overlay.Visible = bool
end
function AttackController:Setup()
for i,v in pairs(self.Connections) do
v:Disconnect()
end
local plrgui = plr.PlayerGui
local backpack = plr.Backpack
local Main = plrgui:WaitForChild("Main")
local AwakeningFrame = Main:WaitForChild("AwakeningFrame")
local energyBar = AwakeningFrame.ProgressBar.MainBar.Bar
local Hotbar = Main:WaitForChild("Hotbar")
local Grid = Hotbar:WaitForChild("Grid")
local Info = require(rp.Info).Characters[plr:GetAttribute("Character")]
local function Writethistextforme(label, text)
task.spawn(function()
for i = 1, #text do
label.Text = string.sub(text, 1, i)
task.wait(.05)
end
end)
end
plr.AttributeChanged:Connect(function()
energyBar:TweenSize(UDim2.new(plr:GetAttribute("Ultimate")/100,0,1,0), Enum.EasingDirection.Out, Enum.EasingStyle.Sine, .2, true)
energyBar.ImageColor3 = Info.Color
end)
if plr.CharacterAppearanceLoaded and plr:HasAppearanceLoaded() then
if plr:GetAttributeChangedSignal("Character") then
Writethistextforme(AwakeningFrame.AwakeningName, Info.UltimateName)
end
end
Writethistextforme(AwakeningFrame.AwakeningName, Info.UltimateName)
Writethistextforme(AwakeningFrame.AwakeningName.AwakeningName, Info.UltimateName)
energyBar:TweenSize(UDim2.new(plr:GetAttribute("Ultimate")/100,0,1,0), Enum.EasingDirection.Out, Enum.EasingStyle.Sine, .2, true)
energyBar.ImageColor3 = Info.Color
for _, v in Grid:GetChildren() do
if v:IsA("ImageButton") then
v:Destroy()
end
end
local function AddSlot(num, infos)
local Slot = script.SlotFrame:Clone()
Slot.ToolName.Text = infos.Name
Slot.LayoutOrder = num
Slot.Number.Text = tostring(num)
Slot.Name = "Skill"..tostring(num)
Slot.Parent = Grid
if infos.twiceInfo then
Slot.Reuse.Visible = true
Slot.Reuse.Reuse.Visible = true
else
Slot.Reuse.Visible = false
Slot.Reuse.Reuse.Visible = false
end
end
for i,v in pairs(Info.Base) do
local slotnum = self.Skills[i]
if slotnum then
AddSlot(slotnum, v)
end
end
local Uis = game:GetService("UserInputService")
local plr = game.Players.LocalPlayer
self.Connections[#self.Connections + 1] = Uis.InputBegan:Connect(function(input, proc)
if proc then return end
self.KeyPressTimes = self.KeyPressTimes or {}
local keycode = input.KeyCode
local skill = self.Keys[keycode] or self.Console[keycode]
local autoActivate = plr.Data.Settings["Auto Equip"].Value
if skill and Info.Base[skill] then
if not autoActivate then
if self.SelectedSkill == skill and self.Equipped then
local hotbar = plr.PlayerGui.Main.Hotbar.Grid
local lastKeyButton = hotbar:FindFirstChild(self.SelectedSkill)
if lastKeyButton then
local overlay = lastKeyButton:FindFirstChild("Overlay")
if overlay then
overlay.Visible = false
self.Equipped = false
plr.Character:SetAttribute("CheckActivated", nil)
end
end
return
end
if self.LastKey then
local hotbar = plr.PlayerGui.Main.Hotbar.Grid
local lastKeyButton = hotbar:FindFirstChild(self.LastKey)
if lastKeyButton then
local overlay = lastKeyButton:FindFirstChild("Overlay")
if overlay then
overlay.Visible = false
plr.Character:SetAttribute("CheckActivated", nil)
end
end
end
self.SelectedSkill = skill
self.LastKey = skill
local hotbar = plr.PlayerGui.Main.Hotbar.Grid
local selectedSkillButton = hotbar:FindFirstChild(self.SelectedSkill)
if selectedSkillButton then
local overlay = selectedSkillButton:FindFirstChild("Overlay")
if overlay then
overlay.Visible = true
self.Equipped = true
plr.Character:SetAttribute("CheckActivated", true)
end
end
else
self.SelectedSkill = skill
self.Equipped = false
local hotbar = plr.PlayerGui.Main.Hotbar.Grid
local lastKeyButton = hotbar:FindFirstChild(self.SelectedSkill)
self.KeyPressTimes[keycode] = self.KeyPressTimes[keycode] or 0
local currentTime = tick()
if Info and Info.Base[self.SelectedSkill]["twiceInfo"] and knit.Player.Character then
local character = knit.Player.Character
if character:IsDescendantOf(workspace.Mobs) then
local isDoubleTap = (currentTime - self.KeyPressTimes[keycode] <= Info.Base[self.SelectedSkill]["twiceInfo"]["interval"])
self.KeyPressTimes[keycode] = currentTime
if isDoubleTap then
knit.GetService("AttackService").Attack:Fire(self.SelectedSkill, nil, isDoubleTap)
return
end
end
end
knit.GetService("AttackService").Attack:Fire(self.SelectedSkill, nil)
if lastKeyButton then
local overlay = lastKeyButton:FindFirstChild("Overlay")
if overlay then
overlay.Visible = false
self.Equipped = false
plr.Character:SetAttribute("CheckActivated", nil)
end
end
end
end
if Uis:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) and self.SelectedSkill then
if self.Equipped then
self.KeyPressTimes[keycode] = self.KeyPressTimes[keycode] or 0
local currentTime = tick()
if Info.Base[self.SelectedSkill]["twiceInfo"] then
local isDoubleTap = (currentTime - self.KeyPressTimes[keycode] <= Info.Base[self.SelectedSkill]["twiceInfo"]["interval"])
self.KeyPressTimes[keycode] = currentTime
if isDoubleTap then
knit.GetService("AttackService").Attack:Fire(self.SelectedSkill, nil, isDoubleTap)
return
end
end
knit.GetService("AttackService").Attack:Fire(self.SelectedSkill)
end
end
if skill == "Awaken" and plr:GetAttribute("Ultimate") >= plr:GetAttribute("MaxUltimate") then
knit.GetService("AttackService").Awaken:Fire()
-- Function to update awakened or base skills
local function updateAwakenedSkills()
if plr:GetAttribute("IsUltimate") then
for _, v in Grid:GetChildren() do
if v:IsA("ImageButton") then
v:Destroy()
end
end
for i, v in pairs(Info.Awakened) do
local slotnum = self.Skills[i]
if slotnum then
AddSlot(slotnum, v)
end
end
else
for _, v in Grid:GetChildren() do
if v:IsA("ImageButton") then
v:Destroy()
end
end
for i, v in pairs(Info.Base) do
local slotnum = self.Skills[i]
if slotnum then
AddSlot(slotnum, v)
end
end
end
end
-- Reset Ultimate attributes
plr:SetAttribute("Ultimate", 0)
plr:SetAttribute("MaxUltimate", 100)
-- Connect to the AttributeChanged event to monitor IsUltimate changes
plr:GetAttributeChangedSignal("IsUltimate"):Connect(function()
updateAwakenedSkills()
end)
-- Initial skill update
updateAwakenedSkills()
print("yes awaken")
-- Update energy bar
local energyBar = AwakeningFrame.ProgressBar.MainBar.Bar
energyBar.ImageColor3 = Info.Color
energyBar:TweenSize(
UDim2.new(plr:GetAttribute("Ultimate") / 100, 0, 1, 0),
Enum.EasingDirection.Out,
Enum.EasingStyle.Sine,
0.2,
true
)
end
end)
end
function AttackController:KnitStart()
knit.GetService("CooldownService").Event:Connect(function(player, skill, duration)
local Hotbar = player.PlayerGui:WaitForChild("Main"):WaitForChild("Hotbar"):WaitForChild("Grid")
for i,v in pairs(Hotbar:GetChildren()) do
if v and v.Parent and v.Name == skill then-- wat
local cd = script.SlotFrame.Cooldown:Clone()
cd.Parent = v
cd.Visible = true
game:GetService("TweenService"):Create(cd, TweenInfo.new(duration, Enum.EasingStyle.Linear, Enum.EasingDirection.Out), {
Size = UDim2.new(1,0,0,0)
}):Play()
task.delay(duration, function()
end)
table.insert(self.CurrentCooldowns, cd)
end
end
end)
knit.GetService("CooldownService").CooldownsCleared:Connect(function(plr, attack)
local Hotbar = knit.Player.PlayerGui:WaitForChild("Main"):WaitForChild("Hotbar"):WaitForChild("Grid")
for _, v in pairs(Hotbar:GetChildren()) do
if v:IsA("ImageButton") then
local cooldownInstance = v:FindFirstChild("Cooldown")
if cooldownInstance then
cooldownInstance:Destroy()
end
end
end
end)
knit.GetService("CooldownService").specificCD:Connect(function(plr, attack)
local Hotbar = knit.Player.PlayerGui:WaitForChild("Main"):WaitForChild("Hotbar"):WaitForChild("Grid")
local attackButton = Hotbar:FindFirstChild(attack)
if attackButton and attackButton:IsA("ImageButton") then
local cooldownInstance = attackButton:FindFirstChild("Cooldown")
if cooldownInstance then
cooldownInstance:Destroy()
end
end
end)
plr.CharacterAdded:Connect(function()
self:Setup()
end)
if plr.Character then
self:Setup()
end
end
return AttackController
Attack Service ( Server )
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local Knit = require(ReplicatedStorage.Game.Shared.Packages.Knit)
local Info = require(ReplicatedStorage.Info)
local Skills = {}
for _, module in pairs(ServerScriptService.Main.Skills:GetDescendants()) do
if module:IsA("ModuleScript") then
Skills[module.Parent.Name .. module.Name] = require(module)
end
end
local AttackService = Knit.CreateService {
Name = script.Name,
Client = {
Attack = Knit.CreateSignal(),
Awaken = Knit.CreateSignal(),
},
}
local doubleTapped = {}
local function executeSkill(skill, char, skillType, isDoubleTapped)
if skill and typeof(skill[skillType]) == "function" then
task.spawn(function()
skill[skillType](skill, char, skillType, isDoubleTapped)
end)
return true
end
return false
end
function AttackService:KnitStart()
self.Client.Awaken:Connect(function(player)
if Knit.ActionCheck(player.Character, {}, false) then
return
end
local char = player.Character
if not char then return end
player:SetAttribute("IsUltimate", true)
Knit.GetService("CooldownService"):RemoveCooldowns(char)
local awakeningSkill = Skills["Awakening" .. "Awaken"]
if awakeningSkill then
if executeSkill(awakeningSkill, char, "Fired", false) then
print("Awakening skill triggered")
end
end
end)
self.Client.Attack:Connect(function(player, skill, skillType, IsDoubleTap)
local char = player.Character or player.CharacterAdded:Wait()
if not char or not player:HasAppearanceLoaded() then
warn("Character not ready or appearance not loaded")
return
end
local characterKey = player:GetAttribute("Character")
if not characterKey then
warn("Player character attribute is missing")
return
end
local baseSkill = Skills[characterKey .. tostring(skill)]
local ultimateSkill = Skills["Awakening" .. tostring(skill)]
if baseSkill and not player:GetAttribute("IsUltimate") then
local skillInfo = Info.Characters[characterKey] and Info.Characters[characterKey].Base[skill]
if skillInfo and skillInfo["Active"] then
executeSkill(baseSkill, char, "Fired", IsDoubleTap)
end
end
if ultimateSkill and player:GetAttribute("IsUltimate") then
local awakenedInfo = Info.Characters[characterKey] and Info.Characters[characterKey].Awakened[skill]
if awakenedInfo and awakenedInfo["Active"] then
executeSkill(ultimateSkill, char, "Fired", IsDoubleTap)
end
print("Ultimate skill triggered")
end
end)
end
return AttackService
It might be long so here the double tap logic
else
self.SelectedSkill = skill
self.Equipped = false
local hotbar = plr.PlayerGui.Main.Hotbar.Grid
local lastKeyButton = hotbar:FindFirstChild(self.SelectedSkill)
self.KeyPressTimes[keycode] = self.KeyPressTimes[keycode] or 0
local currentTime = tick()
if Info and Info.Base[self.SelectedSkill]["twiceInfo"] and knit.Player.Character then
local character = knit.Player.Character
if character:IsDescendantOf(workspace.Mobs) then
local isDoubleTap = (currentTime - self.KeyPressTimes[keycode] <= Info.Base[self.SelectedSkill]["twiceInfo"]["interval"])
self.KeyPressTimes[keycode] = currentTime
if isDoubleTap then
knit.GetService("AttackService").Attack:Fire(self.SelectedSkill, nil, isDoubleTap)
return
end
end
end
knit.GetService("AttackService").Attack:Fire(self.SelectedSkill, nil)
if lastKeyButton then
local overlay = lastKeyButton:FindFirstChild("Overlay")
if overlay then
overlay.Visible = false
self.Equipped = false
plr.Character:SetAttribute("CheckActivated", nil)
end
end
end
end
As shown in the video, I’m attacking another player, and when I switch to my alternate account, it seems like the victim (the one being attacked) can still double-tap to trigger the ability. This behavior should not be happening. I want to ensure that only the attacker (the one who initiated the attack) can double-tap to activate the ability, and the victim should not be able to trigger it.
Can you help me fix this?