#Attack overlap protection failing
1 messages · Page 1 of 1 (latest)
ComboLogic (for the m1s)
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)
If I dont click too fast it works fine, its only if I click VERY fast
But im pretty certain this will end up being an issue with more moves or just something I need to learn how to fix
Example of cooldown mismatch
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
wait the bug is
if u spam your mouse too much it causes it to punch again?
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
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
How would a movehandler module work? I tried to link the actionstate to this module and it seems to work.
do you mean the content of each move to its own module?
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
So like a module that accepts the move request?
aye aye, thank you!
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,
OK i think I get it, and all of this in replicated storage
and this way you can share data between each move
yea and u run it with a local script
Very cool, this looks a lot easier long term
you should write something like this that fits more with your games combat
ive been brute forcing it for like 5 hours lol
rewriting stuff over and over for new changes 😂
thats how u learn
Yes that is true. Thank you very much!
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
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)
ye alr