Weapon randomly one shots?

Ok. This is becoming annoying. I have made this script (below) which organises a sword (not a tool its attached to your arm with motor6d). Every once in a while it randomly one shots. I think its because of debounce not working, yet ive added many more things to make sure it runs ONCE PER SWING. Im really not sure what to do. Sorry for large amount of code but i wanted to make sure people understand my entire code. If anyone could point out a problem or anything it would be appreciated. This script is under each of the players sword

local function hitBox(character)
	print(debounce, swing)
	task.wait(0.25)
	sound:Play()

	print("so next line glitches it")
	local physicalBox = Instance.new("Part", character)
	print("Made")
	physicalBox.Name = "Hitbox"
	physicalBox.Transparency = 1
	physicalBox.Anchored = false
	physicalBox.CanCollide = false
	physicalBox.Size = Vector3.new(5.5, 4, 5.5)
	physicalBox.Massless = true
	physicalBox.CFrame = character.HumanoidRootPart.CFrame * CFrame.new(0, 0, -3.5)
	physicalBox.Parent = character.HumanoidRootPart
	local weld = Instance.new("WeldConstraint")
	weld.Part0 = character.HumanoidRootPart
	weld.Part1 = physicalBox
	weld.Parent = character.HumanoidRootPart
	game.Debris:AddItem(weld, 0.5)
	game.Debris:AddItem(physicalBox, 0.5)
	local debounce = false
	local affected = {} -- Track players already hit during a single swing

	physicalBox.Touched:Connect(function(hit)
		if debounce then
			return
		end
		debounce = true (regular debounce)
		damageCount += 1 -- incase it runs twice(which it still does)
		wait()
		if damageCount > 1 then return end
		if hit.Parent and hit.Parent:IsA("Model") and hit.Parent:FindFirstChild("Humanoid") then
			if hit.Parent == character or character:WaitForChild("IsRagdoll").Value == true then
				debounce = false -- reset debounce
				damageCount = 0
				return
			end
			local player = game.Players:FindFirstChild(hit.Parent.Name)
			if player and player:FindFirstChild("Bool") and player.Bool.Invincible.Value == true then
				debounce = false -- Reset debounce before returning
				damageCount = 0
				return
			end
                        -- makes sure player hasnt been hit in this swing (still doesnt have an effect)

			if table.find(affected, player.Name) then 
				debounce = false -- Reset debounce before returning
				damageCount = 0
				return
			end
			table.insert(affected, player.Name)
			-- Apply damage
			local humanoid = hit.Parent:FindFirstChild("Humanoid")
			if humanoid then
				humanoid:TakeDamage(12)
			end
			-- Update stats and apply ragdoll effect
			local attacker = game.Players:FindFirstChild(character.Name)
			if attacker and attacker:FindFirstChild("leaderstats") then
				attacker.leaderstats.Hits.Value += 1
			end
			swordRagdoll(hit.Parent, character)
		else
			debounce = false -- reset
			damageCount = 0 -- reset
		end
	end)
end

script.Parent.SwordCombatRemote.OnServerEvent:Connect(function(player)
	if swingCheck == false then
		swingCheck = true -- stops swing being used again
		swingCount += 1
		if swingCount > 1 then return end
		local char = player.Character
		if char.IsRagdoll.Value == true then
			swingCount = 0
			swingCheck = false
			return
		end
		local function stopSwing()
			wait()
			swingCheck = false
			swingCount = 0
			debounce = false
			damageCount = 0
		end
		local Hum = char:WaitForChild("Humanoid")
		if Combo == 1 then
			playAnim = Hum:LoadAnimation(anim)
			playAnim:Play()
			Combo = 2		
			hitBox(char)
		elseif Combo == 2 then
			playAnim = Hum:LoadAnimation(anim2)
			playAnim:Play()
			Combo = 1		
			hitBox(char)
		end
		playAnim.Stopped:Connect(stopSwing)
	end
end)

6 Likes

Delete and disconnect the touch function when you hit someone, cause it seems like this can hit only one person.

4 Likes

how does that work? i’ve only just properly attempted to start coding a game so i’m not sure

2 Likes

Found the problem, change the following:

if table.find(affected, player.Name) then 
				debounce = false -- Reset debounce before returning
				damageCount = 0
				return
			end

To this:

if table.find(affected, player.Name) ~= nil then 
				debounce = false -- Reset debounce before returning
				damageCount = 0
				return
			end

Explanation:
Table.find returns nil if the subject is not found, and likewise if there is something it will return the number of the instance found. Using an if statement without operators is not advised in this case as it does not know what its looking for (or the condition its looking for to base on the premise of “true” or “false”), by adding that condition; ~= (means if the table.find does not return nil meaning the PLAYER has been AFFECTED), it will run the code and return.
Conclusion: I hope this helps, if not I will look into the code furthermore and see if I can see the real problem.

1 Like

Put the function as a variable

local TouchEvent = function()
      --your function
End

Then use

TouchEvent:Disconnect()
1 Like

I’ll see if this works give me about 15 minutes. If not i’ll get back to you

2 Likes

would this just make it so touch event won’t run until reassigned?

1 Like

I’m suspecting you may be hitting the same person.

I do feel like @awesome12734568 answer may be better.

1 Like

Also a question I would like to ask, Is this an AOE kind of damage, or it damages the first player it comes into contact with.
If it is the second then disconnect the function instead of returning.
Reference: RBXScriptConnection | Documentation - Roblox Creator Hub
Or just do what @RocketSlither said.

1 Like

Damages first player it comes in contact with. Also what is aoe

1 Like

AOE: Area of effect, basically means the “attack” affects all players in the vicinity.

1 Like

What would be better; AOE or first contact

1 Like

Then disconnect the function instead of returning it.

1 Like

Depends on the type of attack, it all comes to what kind of attack you want:
Single Swing - First Player Contact probably
A raybeam - AOE probably

1 Like

The issue seems to stem from the debounce logic, specifically where it interacts with other parts of the script, leading to inconsistent behavior and allowing multiple damage applications. Below, I’ll break down potential issues and suggest fixes:

Key Problems Identified:

  1. Duplicate debounce Variable:

    • The local debounce inside the Touched event handler conflicts with the debounce declared at a higher scope. This creates separate states, which can result in unexpected behavior.
  2. Race Conditions:

    • Multiple executions of the Touched event handler due to simultaneous or rapid collisions could bypass the intended safeguards.
  3. damageCount Increment Logic:

    • While damageCount is used to limit damage, its checks and resets (if damageCount > 1) may not always prevent multiple damage applications due to timing issues.
  4. Touched Events Are Triggered by Anything:

    • The Touched event might be triggered by unintended objects (e.g., parts other than players), which could lead to unexpected behavior.

Fixes and Optimizations:

1. Global Debounce

Use a single debounce for the entire hitBox function and reset it after the swing ends. This ensures that the state is consistent across all parts of the function.

2. Better Player Filtering

Avoid relying on table.find for every swing. Instead, use a dictionary (affected[player.Name] = true) for faster lookups.

3. Handle Touched Robustly

Filter out unwanted touches using stricter conditions, such as checking for a valid Humanoid.

4. Stop Damage Race Conditions

Include safeguards to ensure damage is applied only once per swing.

Here’s the improved code:

local function hitBox(character)
	print(debounce, swing)
	task.wait(0.25)
	sound:Play()

	local physicalBox = Instance.new("Part", character)
	physicalBox.Name = "Hitbox"
	physicalBox.Transparency = 1
	physicalBox.Anchored = false
	physicalBox.CanCollide = false
	physicalBox.Size = Vector3.new(5.5, 4, 5.5)
	physicalBox.Massless = true
	physicalBox.CFrame = character.HumanoidRootPart.CFrame * CFrame.new(0, 0, -3.5)
	physicalBox.Parent = character.HumanoidRootPart

	local weld = Instance.new("WeldConstraint")
	weld.Part0 = character.HumanoidRootPart
	weld.Part1 = physicalBox
	weld.Parent = character.HumanoidRootPart
	game.Debris:AddItem(weld, 0.5)
	game.Debris:AddItem(physicalBox, 0.5)

	-- Use a global debounce for this hitbox
	if debounce then return end
	debounce = true
	local affected = {}

	physicalBox.Touched:Connect(function(hit)
		-- Ensure debounce is active
		if not debounce then return end

		-- Filter valid hits
		if hit.Parent and hit.Parent:IsA("Model") and hit.Parent:FindFirstChild("Humanoid") then
			if hit.Parent == character or character:FindFirstChild("IsRagdoll").Value then
				return
			end

			local player = game.Players:GetPlayerFromCharacter(hit.Parent)
			if not player or (player:FindFirstChild("Bool") and player.Bool.Invincible.Value) then
				return
			end

			-- Prevent duplicate hits for this swing
			if affected[player.Name] then
				return
			end
			affected[player.Name] = true

			-- Apply damage
			local humanoid = hit.Parent:FindFirstChild("Humanoid")
			if humanoid then
				humanoid:TakeDamage(12)
			end

			-- Update stats and apply ragdoll
			local attacker = game.Players:GetPlayerFromCharacter(character)
			if attacker and attacker:FindFirstChild("leaderstats") then
				attacker.leaderstats.Hits.Value += 1
			end
			swordRagdoll(hit.Parent, character)
		end
	end)

	-- Reset debounce after 0.5 seconds
	task.delay(0.5, function()
		debounce = false
	end)
end

script.Parent.SwordCombatRemote.OnServerEvent:Connect(function(player)
	if swingCheck then return end
	swingCheck = true

	local char = player.Character
	if char.IsRagdoll.Value then
		swingCheck = false
		return
	end

	local function stopSwing()
		swingCheck = false
		debounce = false
	end

	local Hum = char:WaitForChild("Humanoid")
	if Combo == 1 then
		local playAnim = Hum:LoadAnimation(anim)
		playAnim:Play()
		Combo = 2
		hitBox(char)
	elseif Combo == 2 then
		local playAnim = Hum:LoadAnimation(anim2)
		playAnim:Play()
		Combo = 1
		hitBox(char)
	end

	playAnim.Stopped:Connect(stopSwing)
end)

What Changed?

  1. Global Debounce: debounce is now global to the hitBox function and resets only after the swing ends.
  2. Dictionary for affected: Faster lookups for players hit during the swing.
  3. Stricter Filtering: Ensures only valid player models are processed.
  4. Reset Timing: Debounce resets consistently using task.delay, ensuring predictable behavior.
1 Like

Ok thanks, i’ll stick with single hit

1 Like

If a global debounce variable is called outside of the local function, it would affect all other debounces for other players doing the attack as they would all interfere with each other causing multiple functions to call upon a variable accessed by them.

i think he meant define the variable in a different place but i’m not sure

Help i can only swing once now after using ur code. Fixed, was cause playAnim was within an if statement and had local before it

  1. Global Debounce: debounce is now global to the hitBox function and resets only after the swing ends.
    Use a single debounce for the entire hitBox function and reset it after the swing ends. This ensures that the state is consistent across all parts of the function.

You’re using a server Script I am assuming since your calling a remote function, this script is handling all other player’s “swings”, making the debounce global, and by global he means to define the variable outside the functions, this allows that function and other functions to access it, however let’s say 2 players are swinging, and one of them hit a player first, then the function would change the debounce, tricking the other hitbox function thinking it too has also been hit since the debounce would be true/false.