Important Combat System Bug

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    I’m making a combat system for my indie fighting game. I’m using a localscript for detecting the inputs, and a server script for hitboxes, vfx, and damage.

  2. What is the issue? Include screenshots / videos if possible!
    When I punch someone, for some reason they die after a few seconds, even if they’ve only been punched once.

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

  • I have read through the documentation for the TakeDamage() event.
  • I have asked ChatGPT (who, as always, is terrible at luau)
  • I have looked for people with the same issue as me on the dev forum.
  • I have tried different methods of taking away health.

Even after all of that, I’m getting the same result.

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

CombatHandler (server)

local CollectionService = game:GetService("CollectionService")
local TweenService = game:GetService("TweenService")
local CombatRemote = game.ReplicatedStorage.Remotes.Combat
local Debris = game:GetService("Debris")

local combatTimers = {}

-- Handle InCombat tagging and timeout
local function addInCombatTag(character)
	if not CollectionService:HasTag(character, "InCombat") then
		CollectionService:AddTag(character, "InCombat")
	end

	if combatTimers[character] then
		task.cancel(combatTimers[character])
	end

	combatTimers[character] = task.delay(10, function()
		if character and character:IsDescendantOf(workspace) and CollectionService:HasTag(character, "InCombat") then
			CollectionService:RemoveTag(character, "InCombat")
		end
		combatTimers[character] = nil
	end)
end

-- Make DamageMarker float up and fade out
local function createFloatingDamage(damageMarker)
	if not damageMarker or not damageMarker:IsA("BillboardGui") then return end

	local tweenInfo = TweenInfo.new(0.8, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)

	-- Move up
	local startPosition = damageMarker.StudsOffset
	local goalPosition = startPosition + Vector3.new(0, 2, 0)
	local moveTween = TweenService:Create(damageMarker, tweenInfo, {StudsOffset = goalPosition})

	-- Fade out text and UIStroke
	local damageLabel = damageMarker:FindFirstChild("Damage")
	if damageLabel and damageLabel:IsA("TextLabel") then
		local fadeTextTween = TweenService:Create(damageLabel, tweenInfo, {TextTransparency = 1})

		local stroke = damageLabel:FindFirstChildOfClass("UIStroke")
		if stroke then
			local fadeStrokeTween = TweenService:Create(stroke, tweenInfo, {Transparency = 1})
			moveTween:Play()
			fadeTextTween:Play()
			fadeStrokeTween:Play()
		else
			moveTween:Play()
			fadeTextTween:Play()
		end

		-- Clean up after
		Debris:AddItem(damageMarker, 1.5)
	end
end

-- Main Combat Remote Event
CombatRemote.OnServerEvent:Connect(function(plr, arg)
	local character = plr.Character
	if not character or not character:FindFirstChild("HumanoidRootPart") then return end

	if arg == "M1" then
		local hitRange = 5
		local damage = 5

		for _, target in ipairs(workspace:GetChildren()) do
			if target:IsA("Model") and target:FindFirstChild("Humanoid") and target ~= character then
				local targetHRP = target:FindFirstChild("HumanoidRootPart")
				if targetHRP then
					local distance = (character.HumanoidRootPart.Position - targetHRP.Position).Magnitude
					if distance <= hitRange then
						-- Always damage if in range

						addInCombatTag(character)
						addInCombatTag(target)

						if CollectionService:HasTag(target, "Block") then
							-- Blocked hit
							local blockEffectTemplate = game.ReplicatedStorage.Effects.Block:FindFirstChild("Attachment")
							if blockEffectTemplate then
								local blockEffect = blockEffectTemplate:Clone()
								blockEffect.Parent = targetHRP

								-- DamageMarker Text
								local damageMarker = blockEffect:FindFirstChild("DamageMarker")
								if damageMarker and damageMarker:FindFirstChild("Damage") then
									damageMarker.Damage.Text = "-Blocked-"
									createFloatingDamage(damageMarker)
								end

								-- Block sound
								local blockSoundTemplate = game.ReplicatedStorage.Effects.Block:FindFirstChild("Block")
								if blockSoundTemplate then
									local blockSound = blockSoundTemplate:Clone()
									blockSound.Parent = targetHRP
									blockSound:Play()
									Debris:AddItem(blockSound, 3)
								end

								Debris:AddItem(blockEffect, 3)

								task.spawn(function()
									for _, emitter in ipairs(blockEffect:GetChildren()) do
										if emitter:IsA("ParticleEmitter") then
											emitter:Emit(emitter:GetAttribute("EmitCount") or 5)
										end
									end
								end)
							end

						else
							-- Normal hit
							target.Humanoid:TakeDamage(damage)

							local hitEffectTemplate = game.ReplicatedStorage.Effects.Hit:FindFirstChild("Attachment")
							if hitEffectTemplate then
								local hitEffect = hitEffectTemplate:Clone()
								hitEffect.Parent = targetHRP

								-- DamageMarker Text
								local damageMarker = hitEffect:FindFirstChild("DamageMarker")
								if damageMarker and damageMarker:FindFirstChild("Damage") then
									damageMarker.Damage.Text = "-" .. tostring(damage)
									createFloatingDamage(damageMarker)
								end

								-- Hit sound
								local hitSoundTemplate = game.ReplicatedStorage.Effects.Hit:FindFirstChild("Hit")
								if hitSoundTemplate then
									local hitSound = hitSoundTemplate:Clone()
									hitSound.Parent = targetHRP
									hitSound:Play()
									Debris:AddItem(hitSound, 3)
								end

								Debris:AddItem(hitEffect, 3)

								task.spawn(function()
									for _, emitter in ipairs(hitEffect:GetChildren()) do
										if emitter:IsA("ParticleEmitter") then
											emitter:Emit(emitter:GetAttribute("EmitCount") or 5)
										end
									end
								end)
							end
						end
					end
				end
			end
		end

	elseif arg == "BlockOn" then
		if not CollectionService:HasTag(character, "Block") then
			CollectionService:AddTag(character, "Block")
		end

	elseif arg == "BlockOff" then
		if CollectionService:HasTag(character, "Block") then
			CollectionService:RemoveTag(character, "Block")
		end
	end
end)

CombatClient (LocalScript)

local plr = game.Players.LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local uis = game:GetService("UserInputService")
local m1debounce = false
local blockdebounce = false
local m1Left = char:WaitForChild("Humanoid"):LoadAnimation(game.ReplicatedStorage.Animations.M1Left)
local m1Right = char:WaitForChild("Humanoid"):LoadAnimation(game.ReplicatedStorage.Animations.M1Right)
local Block = char:WaitForChild("Humanoid"):LoadAnimation(game.ReplicatedStorage.Animations.Block)
local currentPunch = 1
local currentAnim

uis.InputBegan:Connect(function(i, e)
	if e then return end
	
	if i.UserInputType == Enum.UserInputType.MouseButton1 and m1debounce == false then
		m1debounce = true
		
		print("m1")
		if currentPunch == 1 then
			m1Left:Play()
			currentAnim = m1Left
			currentPunch = 2
		elseif currentPunch == 2 then
			m1Right:Play()
			currentAnim = m1Right
			currentPunch = 1
		end
		
		currentAnim:GetMarkerReachedSignal("Hit"):Connect(function()
			game.ReplicatedStorage.Remotes.Combat:FireServer("M1")
		end)
		task.delay(0.7, function()
			m1debounce = false
		end)
	end
	
	if i.KeyCode == Enum.KeyCode.F and blockdebounce == false then
		game.ReplicatedStorage.Remotes.Combat:FireServer("BlockOn")
		char:WaitForChild("Humanoid").WalkSpeed = 6
		Block:Play()
	end 
end)

uis.InputEnded:Connect(function(i, e)
	if e then return end
	if i.KeyCode == Enum.KeyCode.F and blockdebounce == false then
		game.ReplicatedStorage.Remotes.Combat:FireServer("BlockOff")
		blockdebounce = true
		char:WaitForChild("Humanoid").WalkSpeed = 32
		Block:Stop()
		task.delay(1, function()
			blockdebounce = false
		end)
	end
end)

I will be thankful for any help, as this is a very bad bug to have in a fighting game.

Add a print statement right before this line, see if it’s being called multiple times or something else is going wrong.

will do, thanks for the suggestion

thanks so much for the help! when i punched, this was in the output:

  07:17:26.296  m1  -  Client - ClientCombat:18
  07:17:26.496   ▶ Damaging! (x5)  -  Server - CombatHandler:114