#Attack overlap protection failing

1 messages · Page 1 of 1 (latest)

rotund nova
#

So ive created a M1 and M2 system, but i have an issue. My system blocks m1s from being used during m2s and m2s from being used during m1s, but the problem is, if you use m2 very quickly after using m1 it takes the cooldown and causes a desync between the GUI and the actually cooldown. I dont want it to go on cooldown unless its actually used..

#

ComboLogicHeavy (for the m2s)

#

Client side M1Punch

#
-- LocalScript (StarterPlayerScripts)
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")

local PunchEvent = ReplicatedStorage:WaitForChild("PunchEvent") -- RemoteEvent

-- Local animations (client will receive which one to play)
local ComboAnimations = {
    "rbxassetid://180426354",
    "rbxassetid://507766388",
    "rbxassetid://507767714",
    "rbxassetid://507768375"
}

-- input-side debounce (responsiveness)
local localCooldown = 0.15
local lastLocal = 0

UserInputService.InputBegan:Connect(function(input, gp)
    if gp then return end
    if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end

    local now = tick()
    if now - lastLocal < localCooldown then
        -- too fast locally; ignore to avoid spamming server
        return
    end
    lastLocal = now

    -- Ask server to attempt a punch. Server decides if allowed.
    PunchEvent:FireServer("RequestPunch")
end)

-- Server will tell us which animation to play with "PlayAnim" message
local currentTrack
PunchEvent.OnClientEvent:Connect(function(action, payload)
    if action == "PlayAnim" and payload and payload.AnimationId then
        -- Play animation locally for responsiveness
        if currentTrack and currentTrack.IsPlaying then
            currentTrack:Stop()
            currentTrack:Destroy()
        end

        local anim = Instance.new("Animation")
        anim.AnimationId = payload.AnimationId
        local track = humanoid:LoadAnimation(anim)
        track.Priority = Enum.AnimationPriority.Action
        track.Looped = false
        track:Play()
        currentTrack = track

        -- optional debug
        -- print("Playing animation", payload.AnimationId)
    end
end)
#

Client side m2 punch

-- LocalScript (StarterPlayerScripts)
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")

local PunchEvent = ReplicatedStorage:WaitForChild("HeavyPunchEvent") -- RemoteEvent

-- Local animations (client will receive which one to play)
local ComboAnimations = {
    "rbxassetid://74528414412028", --- Heavy punch 1
}

-- input-side debounce (responsiveness)
local localCooldownHeavy = 3
local lastLocalH = 0

UserInputService.InputBegan:Connect(function(input, gp)
    if gp then return end
    if input.UserInputType ~= Enum.UserInputType.MouseButton2 then return end

    local now = tick()
    if now - lastLocalH < localCooldownHeavy then
        -- too fast locally; ignore to avoid spamming server
        return
    end
    lastLocalH = now

    -- Ask server to attempt a punch. Server decides if allowed.
    PunchEvent:FireServer("RequestHeavyPunch")
end)

-- Server will tell us which animation to play with "PlayAnim" message
local currentTrack
PunchEvent.OnClientEvent:Connect(function(action, payload)
    if action == "PlayAnim" and payload and payload.AnimationId then
        -- Play animation locally for responsiveness
        if currentTrack and currentTrack.IsPlaying then
            currentTrack:Stop()
            currentTrack:Destroy()
        end

        local anim = Instance.new("Animation")
        anim.AnimationId = payload.AnimationId
        local track = humanoid:LoadAnimation(anim)
        track.Priority = Enum.AnimationPriority.Action
        track.Looped = false
        track:Play()
        currentTrack = track

        -- optional debug
        -- print("Playing Heavy animation", payload.AnimationId)
    end
end)
#

But im pretty certain this will end up being an issue with more moves or just something I need to learn how to fix

#

oh also, this is the action state module script


-- ModuleScript: ReplicatedStorage/ActionState.lua
local ActionState = {}

-- player -> { isBusy = false, currentAction = nil }
local states = {}

function ActionState:Get(player)
    local s = states[player]
    if not s then
        s = { isBusy = false, currentAction = nil }
        states[player] = s
    end
    return s
end

function ActionState:SetBusy(player, actionName, busy)
    local s = self:Get(player)
    s.isBusy = busy
    s.currentAction = busy and actionName or nil
end

function ActionState:IsBusy(player)
    local s = self:Get(player)
    return s.isBusy
end

game:GetService("Players").PlayerRemoving:Connect(function(p)
    states[p] = nil
end)

return ActionState
#

and this is the GUI script

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local PlayerGui = player:WaitForChild("PlayerGui")

local HeavyPunchEvent = ReplicatedStorage:WaitForChild("HeavyPunchEvent")

-- GUI references
local gui = PlayerGui:WaitForChild("HeavyPunchGUI")
local frame = gui:WaitForChild("CooldownFrame")
local text = frame:WaitForChild("CooldownText")
local fill = frame:FindFirstChild("FillFrame")
if not fill then
    fill = Instance.new("Frame")
    fill.Name = "FillFrame"
    fill.BackgroundColor3 = Color3.fromRGB(255, 0, 0)
    fill.Size = UDim2.new(1, 0, 0, 0) -- start empty
    fill.Position = UDim2.new(0, 0, 1, 0) -- bottom of frame
    fill.AnchorPoint = Vector2.new(0, 1)  -- bottom anchor
    fill.BorderSizePixel = 0
    fill.Parent = frame
end



local cooldownTime = 5
local canPunch = true
local lastPunch = 0

local UserInputService = game:GetService("UserInputService")
UserInputService.InputBegan:Connect(function(input, gp)
    if gp then return end
    if input.UserInputType ~= Enum.UserInputType.MouseButton2 then return end
    if not canPunch then return end

    canPunch = false
    lastPunch = tick()
    HeavyPunchEvent:FireServer("RequestHeavyPunch")
    text.Text = "Cooling..."
    -- Animate fill bar
    coroutine.wrap(function()
        local elapsed = 0
        while elapsed < cooldownTime do
            elapsed = tick() - lastPunch
            local pct = math.clamp(elapsed / cooldownTime, 0, 1)

            fill.Size = UDim2.new(1, 0, pct, 0) -- height grows bottom→top

            text.Text = string.format("%.1f", math.max(cooldownTime - elapsed, 0))
            task.wait(0.03)
        end

        -- Reset
        fill.Size = UDim2.new(1, 0, 0, 0) -- hide fill
        text.Text = "Heavy Punch"
        canPunch = true
    end)()
end)
#

Im figuring its a very easy issue

#

but im tired and cant figure it out lol

ancient wing
#

wait the bug is

#

if u spam your mouse too much it causes it to punch again?

#

kinda confused

rotund nova
# ancient wing kinda confused

Ok, I have a 4 string attack linked to m1, each attack cycles to the next, they cant be used during the Heavy punch which is linked to m2. this part works fine

The issue lies with m2 the heavy punch, if I press m2 during m1 it doesnt trigger which is correct, but if I click it too fast for some reason it still takes the cooldown and triggers the gui, but also mismatches the cooldown as well for some reason

#

basically if I click too fast it steals my m2 cooldown whilst still blocking it from triggering like it should

ancient wing
#

the way your doing it kinda makes it hard to find it

#

u should concentrate this more

#

as in

#

have something like a MoveHandler module

#

that handles each move

#

because seperate scripts for each move like this its normal to run into sync problems and trouble debugging

#

also do not load animations every time u play them

#

load once then play that over and over again

rotund nova
ancient wing
#

i mean have a structure for each move

#

and a single module that runs the move

#

moves can have their own modules yes but it should still be tied to the base module for structure

#

that way you can also share cooldowns etc

rotund nova
#

So like a module that accepts the move request?

ancient wing
#

let me write an example

#

gimme a minute

rotund nova
#

aye aye, thank you!

ancient wing
#

aight

#
--- MoveHandler module
local module = {}

local MoveModules = {
    Punch = require(script.Punch),
    HeavyPunch = require(script.HeavyPunch)
}

local ClientState =  nil

function module.BindMove(MoveName)
    MoveModules[MoveName].Bind(ClientState)
end

function module.ClearConnections()
    for _, Connection in ClientState.Connections do
        Connection:Disconnect()
    end
    ClientState.Connections = {}
end


function module.CharacterAdded()
    ClientState = {
        State = "Idle",
        Binds = {},
        Cooldowns = {
            M1Cooldown = false
        }
    }
    
    module.BindMove("Punch")
    module.BindMove("HeavyPunch")
end

function module.CharacterDied()
    module.ClearConnections()
end

return module

#
-- Punch module
local module = {}

local UserInputService = game:GetService("UserInputService")

function module.Bind(ClientState)
    local Connection = UserInputService.InputBegan:Connect(function()
        if ClientState.State == "Idle" and ClientState.Cooldowns.M1Cooldown == false then
            ClientState.State = "Punching"
            ClientState.Cooldowns.M1Cooldown = true
            -- do punchy stuff
            
            task.wait(0.5)
            
            ClientState.State = "Idle"
            ClientState.Cooldowns.M1Cooldown = false
        end
    end)
    
    table.insert(ClientState.Connections, Connection)
end

return module

#

something like this would make it easier long term,

rotund nova
#

OK i think I get it, and all of this in replicated storage

ancient wing
#

and this way you can share data between each move

#

yea and u run it with a local script

rotund nova
#

Very cool, this looks a lot easier long term

ancient wing
#

you should write something like this that fits more with your games combat

rotund nova
#

ive been brute forcing it for like 5 hours lol

#

rewriting stuff over and over for new changes 😂

ancient wing
#

thats how u learn

rotund nova
#

Yes that is true. Thank you very much!

ancient wing
#

ur welcome

#

ofc the thing with this is u also have to make sure it syncs with the server well

#

many people alternatively use value instances since they replicate automatically

#

however if u want to replicate urself you can do it with remotes

rotund nova
#

I see I see, ill see how this works first then if needed ill look into value instances (im going to bed soon its 6 am lol)

ancient wing
#

ye alr