humanoid:TakeDamage() firing several times in succession for some reason

I’m trying to make an object that disables items and deals periodic damage when a Start part is touched, and re-enabling the items and stopping the periodic damage when a Stop part is touched. However, when the effect is re-enabled, previously has touched a Start and Stop part, and then touches the Start part again, the humanoid:TakeDamage() occurs 2-3 times in a row. I know that my code is a bit messy, but I’ve added debounces to everything so I don’t understand why it does this.

local players = game:GetService("Players")
players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(char)
		local db = false
		script.Parent.Start.Touched:Connect(function(hit)
			local hum = hit.Parent
			if hum and db == false then
				db = true
				script.Parent.Enabled.Value = true
			end
		end)

		for _, d in pairs(script.Parent.Stop:GetChildren()) do
			if d:IsA("BasePart") then
				d.Touched:Connect(function(hit)
					local hum = hit.Parent
					if hum and db == true then
						db = false
						script.Parent.Enabled.Value = false
					end
				end)
			end
		end
		local h = player.Character.Humanoid
		if script.Parent.Enabled.Value then
			local EQ = player.Character:GetChildren()
			for _, v in pairs(EQ) do
				if v:IsA("Tool") and v.Name ~= "Noclip" and v.Name ~= "Heal"  then
					print(v.Name .. "1")
					v.Parent = player.Backpack
				end
			end

		end
		local playerInv = player.Backpack:GetChildren()
		script.Parent.Enabled.Changed:Connect(function(changed)
			print("changed")
			for _, v in pairs (playerInv) do
				if v:IsA("Tool") and v.Name ~= "Noclip" and v.Name ~= "Heal" then
					local toolScripts = v:GetDescendants()
					print(v.Name)
					for _, b in pairs(toolScripts) do
						if b:IsA("Script") or v:IsA("LocalScript") then
							b.Disabled = true
						end
					end
					if v:IsA("Tool") and script.Parent.Enabled.Value == false then
						for _, b in pairs(playerInv) do
							if b:IsA("Tool") then
								local toolScripts = v:GetDescendants()
								print(v.Name)
								for _, e in pairs(toolScripts) do
									if e:IsA("Script") or v:IsA("LocalScript") then
										e.Disabled = false
									end
								end
							end
						end
					end

				end
			end
			local ddb = false
			while true do
				wait(0.1)
				if script.Parent.Enabled.Value and ddb == false then
					wait(5)
					ddb = true
					h.Health -=10
					wait(0.1)
					ddb = false

				end
			end
		end)
	end)
end)

image
Above is the hiearchy for the object. Server script is used in order to avoid clashing with other game scripts

Could it be that when you check

script.Parent.Enabled.Changed:Connect(function(changed)

and then change the debounce which changes the Enabled.Value it fires that function again?

The script provided doesn’t even contain a call to ‘TakeDamage()’.

There’s some pretty major refactoring you may want to your code that should fix a lot of the existing issues.

Generally, I don’t advice connecting directly to a playerAdded function. Instead I like this method.

local function playerAdded(player: Player)
	...
end

for _, player in ipairs(Players:GetPlayers()) do
	task.spawn(playerAdded, player)
end

Players.PlayerAdded:Connect(playerAdded)

That’s not really important in this case. You’re using a script that has a .Touched event which fires repeatedly. You’re using playerAdded and creating touched signals over and over again.

For me, the easiest way to just simply get a player from a part is this. Trust me, It’s dead simple. I’ll walk you through it:

-- Services
local CollectionService = game:GetService("CollectionService")
local Players = game:GetService("Players")

-- main
local Model = script.Parent
local Enabled = Model:WaitForChild("Enabled")

-- stateful stuff
local debounce = 0 -- we are going to use tick as a debounce

local function setTimeout(time: number) -- Crazyman32 method
	local now = tick()
	debounce = now

	task.delay(time, function()
		if debounce == now then
			debounce = 0
		end
	end)
end

local whitelistedTools = {"Noclip", "Heal"}

local function toggleScripts(tool: Tool, disabled: boolean)
	for _, obj in ipairs(tool:GetChildren()) do
		if obj:IsA("LuaSourceContainer") then
			obj.Disabled = disabled
		end
	end
end

local function toggleScriptInTools(disabled: boolean, ...) -- expected to be instances
	local function getTool(root: Instance)
		assert(typeof(root) == "Instance", ("%s is not an Instance!"):format(root.Name))
		for _, tool in ipairs(root:GetChildren()) do
			if tool:IsA("Tool") then
				if not table.find(tool.Name, whitelistedTools) then
					toggleScripts(tool, disabled)
				end
			end
		end
	end
	
	for _, root in ipairs({...}) do
		getTool(root)
	end
end

local function findPlayer(hit: Instance) -- touched will always have a basepart
	local model = hit:FindFirstAncestorWhichIsA("Model") -- keep in mind, that characters will always be a model, if it finds a model it will make sure a humanoid exists.
	if model then -- what's great about this is we can use the model to find the player from the character
		local humanoid = model:FindFirstChildWhichIsA("Humanoid") -- don't use the recursive argument for this
		if humanoid and humanoid.Health > 0 then
			local player = Players:GetPlayerFromCharacter(model)
			if player then return player, model, humanoid end -- finally can say there's a player
		end
	end
end

CollectionService:GetInstanceAddedSignal("Humanoid"):Connect(function(humanoid: Humanoid)
	local player, model = findPlayer(humanoid)
	if player then
		toggleScriptInTools(true, model, player) -- if you have scripts that should be disabled i'd recommend adding a table to verify that
	end
end)

CollectionService:GetInstanceRemovedSignal("Humanoid"):Connect(function(humanoid: Humanoid)
	local player, model = findPlayer(humanoid)
	if player then
		toggleScriptInTools(false, model, player)
	end
end)

local function onTouched(hit: BasePart)
	if debounce ~= 0 then return end
	local player, _, humanoid = findPlayer(hit) -- find the player
	if player and humanoid then
		CollectionService:AddTag(humanoid, "Humanoid")
		setTimeout(5)
	end
end

Model.Start.Touched:Connect(onTouched)

local function connectStopped(stop: BasePart) -- this makes it dead simple when you want to use a childadded thing, it looks pretty clean	
	assert(typeof(stop) == "Instance", ("%s is not an instance, how did that happen?"):format(stop.Name))
	assert(stop:IsA("BasePart"), ("%s is not a BasePart!"):format(stop.Name))
	
	local function stopOnTouched(hit: BasePart)
		local player, _, humanoid = findPlayer(hit) -- find the player

		if player and humanoid then
			CollectionService:RemoveTag(humanoid, "Humanoid")
		end
	end	
	
	stop.Touched:Connect(stopOnTouched)
end

for _, stop in ipairs(Model.Stop:GetChildren()) do
	if stop:IsA("BasePart") then -- this is so the script doesn't break if you accidentally threw in a non-basepart lol
		connectStopped(stop)
	end
end

Model.Stop.ChildAdded:Connect(connectStopped) -- script won't break because connections spawn on a new thread

task.spawn(function()
	while task.wait(5) do
		if Enabled.Value then
			for _, tagged in ipairs(CollectionService:GetTagged("Humanoid")) do
				tagged:TakeDamage(10)
			end
		end
	end
end)
1 Like

it’s h.Health -= 10, but regardless it still deals damage several times in succession

This is because every single time a player dies it creates a new damage dealer without disconnecting the old one. Disconnect the old one when the player dies.

The player doesn’t die, it only does so whenever “Enabled” is re-enabled after previously being disabled. Nonetheless putting the code below fixed my problem, so thanks!

if script.Parent.Enabled.Value == false then
break 
end

okay i actually finished my solution. check it out. Anyway here’s what it should look like.

https://i.gyazo.com/7aa3b18bc3d6953469dca751b7266343.mp4

1 Like