I made a hurtbox with GetPartsInPart which is supposed to hurt another player but I keep getting an error at line 10 every time for this
local Players = game:GetService("Players")
local timealive = script.Parent.TimeAlive.Value
local damage = script.Parent.Damage.Value
while task.wait(timealive) do
local deb = false
local objectsInSpace = workspace:GetPartsInPart(script.Parent)
for i, v in pairs(objectsInSpace) do
if not v:IsA("BasePart") then return end
if v.Parent:FindFirstChild("Humanoid") then
if deb == true then continue end
deb = true
local player = Players:GetPlayerFromCharacter(v.Parent)
if player.Name == script.Parent.SpawnedBy.Value then
print("same person!")
else
player.Character:WaitForChild("Humanoid").Health = player.Character:WaitForChild("Humanoid").Health - damage
player.Character:FindFirstChild("Attributes"):FindFirstChild("Stunned").Value = true
task.wait(timealive / 2)
player.Character:FindFirstChild("Attributes"):FindFirstChild("Stunned").Value = false
end
end
end
end
I’ve tried multiple things to fix it but the glitch keeps happening. What’s weirder is that one player will always have it working and one player will always have it broken.
To achieve precision, it must be run at every step.
Debounce must be per player. This is possibly the reason why it works for one player but not for another.
And there are other problematic issues.
local Players = game:GetService("Players")
local timealive = script.Parent.TimeAlive.Value
local damage = script.Parent.Damage.Value
local playerDebounces = {}
game:GetService("RunService").Heartbeat:Connect(function()
local objectsInSpace = workspace:GetPartsInPart(script.Parent)
for i, v in pairs(objectsInSpace) do
if not v:IsA("BasePart") then continue end -- `continue` to keep processing other parts
local character = v.Parent
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
local player = Players:GetPlayerFromCharacter(character)
if player then
if player.Name == script.Parent.SpawnedBy.Value then
print("same person!")
continue
end
if playerDebounces[player.UserId] then
continue
end
playerDebounces[player.UserId] = true
humanoid.Health = humanoid.Health - damage
local attributesFolder = character:FindFirstChild("Attributes")
if attributesFolder then
local stunnedAttribute = attributesFolder:FindFirstChild("Stunned")
if stunnedAttribute then
stunnedAttribute.Value = true
task.spawn(function() -- to avoid blocking the current thread
task.wait(timealive / 2)
if stunnedAttribute and stunnedAttribute.Parent then -- check if the attribute still exists
stunnedAttribute.Value = false
end
playerDebounces[player.UserId] = nil
end)
else
playerDebounces[player.UserId] = nil
end
else
playerDebounces[player.UserId] = nil
end
end
end
end
end)
Used Heartbeat because I assumed it is server-side. I think it would work better if it were client-side.
This code is not exactly what you need, but rather an idea of how it should be. On one hand, your current code isn’t very suitable for the task at hand. On the other hand, I don’t have enough information about how your game works, so I can’t provide the exact code that would solve your problem.
I see. It goes from a client side to a module script to a server script i believe, although i havent looked in a bit. Ill try to make the debounce client side so only plrs have cd instead of everyone. If that doesnt work i will try your solution.
Its the thing that clones the hurtbox and sets it at the player. I do it so I can run it at anytime like if someone like presses E or something i can activate the hurtbox. I would check exactly how it works but im out rn so I cant.
I would not make the cooldown client-sided as an exploiter could bypass it. I suggest keeping it server-sided.
As @lumizk shared in their code, they provided a flexible way for players to each have their own cooldown. The trick is to keep track of the players within it and then remove them afterwards.
local IndividualCoolDowns = {} -- A table referencing players currently on cooldown. Think of it like a grocery list, but rather then vegetables it's players. Okay that sounds weird uhhh moving along now.
local CoolDownTime = 5 -- How long each player's in cooldown for.
local function CoolDownPlayer(nm)
IndividualCoolDowns[nm] = true -- Add their name to the list.
delay(CoolDownTime, function() -- After a delay...
IndividualCoolDowns[nm] = nil -- Remove them from the list. Setting their reference to nil mitigates potential memory leaks.
end)
end
local function IsPlayerInCooldown(nm)
return IndividualCoolDowns[nm] ~= nil -- Returns truthy or falsey if the player's name's present in the list.
-- It's good practice in my opinion to check against the exact data type and not always resort to "not". I can expand upon this if inquired about it further.
end
Part.Touched:Connect(function(hit)
local plyr = game.Players:GetPlayerFromCharacter(hit.Parent) -- Grab the player if it's their character
if plyr ~= nil and not IsPlayerInCoolDown(plyr.Name) then -- Was it a player and are they not already in the list?
CoolDownPlayer(plyr.Name) -- Run the function to add them.
-- Additional code logic.
end
end)
It keeps things evened out and makes sure everybody – not just one player – can interact with XYZ thing. =)
EDIT
I should also note that this can be used in ModuleScripts too.
Replace the end section with this:
local function AttachTouchedLogic(prt)
if not prt:IsA("BasePart") then
return
end
prt.Touched:Connect(function(hit)
local plyr = game.Players:GetPlayerFromCharacter(hit.Parent) -- Grab the player if it's their character
if plyr ~= nil and not IsPlayerInCoolDown(plyr.Name) then -- Was it a player and are they not already in the list?
CoolDownPlayer(plyr.Name) -- Run the function to add them.
-- Additional code logic.
end
end)
end
return AttachTouchedLogic
Call require on that ModuleScript and it’ll do just that trick.
Alright I’m at my computer and how it works is that a local script gets a mouse1 or mobile input then goes to a server script to then activate the module script which spawns the hurtbox where it needs to be. Then that hurtbox has the script I have posted in it. I don’t think you will need to rework anything but just incase you needed to know! Also your comment about cooldowns being hackable if client side was helpful because I had recently change other scripts in my game to do that!
Edit: The first response, which conveniently is also the first one i tried worked. The only change I made was:
I wouldn’t recommend doing while wait(n) do as it’s relying on the fact that task.wait and wait return a truthy value. I would suggest instead making the condition a boolean and then running the sleeper function. Since RunService.Heartbeat runs after physics had concluded that cycle that makes it a better method to use. RunService | Documentation - Roblox Creator Hub
If the issue’s that there needs to be some time between each time it execute the code logic, this can be mitigated with Heartbeat passing the delta time (the amount of time between each cycle)
local TimeTracker = 0
RunService.Heartbeat:Connect(function(DeltaTime)
TimeTracker = TimeTracker + DeltaTime
local RoundedTime = math.floor(TimeTracker / timealive)
if RoundedTime >= 1 then
for i = 1, RoundedTime do
-- code logic
end
TimeTracker = TimeTracker - (RoundedTime * timealive)
end
end)
This would wait an X amount of time before executing the code’s instructions and run it the amount of times needed if Heartbeat took unexpectedly longer than usual to fire.