M1 Combo randomly stopping when player is holding down mouse

Hello, I am scripting a combat system similar to that of the “battleground” genre. This is the script for the basic punch (or M1) combo, where the player punches another player four times and knocks them back on the final punch. You can do the combo automatically by holding down the M1 button, however, sometimes randomly the M1 combo will stop. I’ve tried to locate exactly where or when this happens, but I can’t figure it out. I’ve put print functions after every return, and the code seems to be running all the way through. Please can somebody give me some pointers?

-- Modules

local replicatedStorage = game:GetService("ReplicatedStorage")
local modules = replicatedStorage.Modules

local hitbox = require(modules.BumperHitboxes)
local stun = require(modules.StunHandlerV2)
local attackStun = require(modules.AttackStunHandlerV2)

-- Variables

local tool = script

local anims = replicatedStorage.Animations

local cd = false

local equipped = false

local character = script.Parent
local player = game.Players:GetPlayerFromCharacter(character)
local human = character.Humanoid
local comboResetTimer

local lastComboNum

local equippedAnim

local vfx = replicatedStorage.VFX

local anim1
local anim2
local anim3
local anim4

local mouseHeld = false

-- Config

local swingAnims = {anims.Attack1, anims.Attack2, anims.Attack3, anims.Attack4}

local m1HitAnims = {anims.Hit1, anims.Hit2, anims.Hit3, anims.Hit4}

local equipAnims = {anims.Idle}

anim1 = human.Animator:LoadAnimation(swingAnims[1])
anim2 = human.Animator:LoadAnimation(swingAnims[2])
anim3 = human.Animator:LoadAnimation(swingAnims[3])
anim4 = human.Animator:LoadAnimation(swingAnims[4])

-- SFX Putting

for i, sound in pairs(script.SFX:GetChildren()) do
	sound.Parent = character.HumanoidRootPart
end

local hitSound = character.HumanoidRootPart.Punch
local hitSoundFlourish = character.HumanoidRootPart.Flourish
local swingSound = character.HumanoidRootPart.Swing

-- M1 Function

local function M1(plr, mouseDown)
	
	if mouseDown == true then
		mouseHeld = true
	else
		mouseHeld = false
		return
	end
	
	if plr ~= player then return end
	
	if cd == true then
		wait(0.1)

		if mouseHeld == true then
			M1(plr, true)
			return
		end
		return
	end
	
	if character:GetAttribute("Stunned") == true then
		wait(0.1)

		if mouseHeld == true then
			M1(plr, true)
			return
		end
		return
	end
	
	if character:GetAttribute("AttackStun") == true then
		wait(0.1)

		if mouseHeld == true then
			M1(plr, true)
			return
		end
		return
	end
	
	if character:GetAttribute("DashAttacking") == true then
		if mouseHeld == true then
			repeat
				wait()
			until character:GetAttribute("DashAttacking") == false
			wait(0.1)
			M1(plr, true)
			return
		end
		return
	end
	
	if character:GetAttribute("AttackStunAlt") == true then

		if mouseHeld == true then
			wait(0.1)
			M1(plr, true)
			return
		end
		return
	end
	
	cd = true
	
	character:SetAttribute("AttackStunAlt", true)
	
	-- Animations & Combo
	
	local currentComboNum = tool:GetAttribute("ComboNum")
	
	task.spawn(function()
		
		if currentComboNum == 1 then
			tool:SetAttribute("ComboNum", currentComboNum + 1)
			anim1:AdjustWeight(0.0001, 0.0001)
			anim1:Play()
			
			local trail = Instance.new("Trail")
			trail.Transparency = NumberSequence.new(0.5, 1)
			trail.Lifetime = 0.25
			trail.FaceCamera = true
			
			local attachment1 = Instance.new("Attachment")

			attachment1.Parent = character["Right Arm"]
			attachment1.WorldPosition = character["Right Arm"].Position
			trail.Parent = character["Right Arm"]
			trail.Attachment0 = character["Right Arm"].RightGripAttachment
			trail.Attachment1 = attachment1
			
			task.spawn(function()
				task.wait(0.3)
				character:SetAttribute("AttackStunAlt", false)
				attachment1:Destroy()
			end)
			
		elseif currentComboNum == 2 then
			tool:SetAttribute("ComboNum", currentComboNum + 1)
			anim2:AdjustWeight(10, 0.0001)
			anim2:Play()
			anim1:Stop()
			
			local trail = Instance.new("Trail")
			trail.Transparency = NumberSequence.new(0.5, 1)
			trail.Lifetime = 0.25
			trail.FaceCamera = true

			local attachment1 = Instance.new("Attachment")

			attachment1.Parent = character["Left Arm"]
			attachment1.WorldPosition = character["Left Arm"].Position
			trail.Parent = character["Left Arm"]
			trail.Attachment0 = character["Left Arm"].LeftGripAttachment
			trail.Attachment1 = attachment1

			task.spawn(function()
				task.wait(0.3)
				character:SetAttribute("AttackStunAlt", false)
				attachment1:Destroy()
			end)
			
		elseif currentComboNum == 3 then
			tool:SetAttribute("ComboNum", currentComboNum + 1)
			anim3:AdjustWeight(30, 0.0001)
			anim3:Play()
			anim2:Stop()
			
			local trail = Instance.new("Trail")
			trail.Transparency = NumberSequence.new(0.5, 1)
			trail.Lifetime = 0.25
			trail.FaceCamera = true

			local attachment1 = Instance.new("Attachment")

			attachment1.Parent = character["Right Arm"]
			attachment1.WorldPosition = character["Right Arm"].Position
			trail.Parent = character["Right Arm"]
			trail.Attachment0 = character["Right Arm"].RightGripAttachment
			trail.Attachment1 = attachment1

			task.spawn(function()
				task.wait(0.3)
				character:SetAttribute("AttackStunAlt", false)
				attachment1:Destroy()
			end)
			
		else
			task.spawn(function()
				task.wait(0.4)
				attackStun.Stun(human, 0.5)
			end)
			tool:SetAttribute("ComboNum", 1)
			anim4:AdjustWeight(40, 0.0001)
			anim4:Play()
			anim3:Stop()
			
			local trail = Instance.new("Trail")
			trail.Transparency = NumberSequence.new(0.5, 1)
			trail.Lifetime = 0.25
			trail.FaceCamera = true

			local attachment1 = Instance.new("Attachment")

			attachment1.Parent = character["Left Arm"]
			attachment1.WorldPosition = character["Left Arm"].Position
			trail.Parent = character["Left Arm"]
			trail.Attachment0 = character["Left Arm"].LeftGripAttachment
			trail.Attachment1 = attachment1

			task.spawn(function()
				task.wait(0.3)
				character:SetAttribute("AttackStunAlt", false)
				attachment1:Destroy()
			end)
		end
	end)
	
	local currentComboNum = tool:GetAttribute("ComboNum")
	
	lastComboNum = tool:GetAttribute("ComboNum")
	
	-- Hitbox & Damage
	
	task.spawn(function()
		local HitTable = {}
		wait(0.05)
		swingSound:Play()
		wait(0.1)
		local hitParts = hitbox:Create(player, Vector3.new(6,6,6), character.HumanoidRootPart.CFrame.LookVector * 3)
		for i, part in pairs(hitParts) do
			if not part:HasTag("VFX") and part.Name ~= "Hitbox" then
				if part.Parent:FindFirstChild("Humanoid") then
					if not table.find(HitTable, part.Parent) then
						if part.Parent ~= character then

							if character:GetAttribute("Stunned") == true then return end

							if part.Parent:GetAttribute("Ragdolled") == true then return end

							if part.Parent:GetAttribute("Blocking") == true then
								local ObjectSpace = part.Parent.HumanoidRootPart.CFrame:inverse() * character.HumanoidRootPart.CFrame
								local MaxDistance = 100
								if ObjectSpace.Z < 0 and (character.HumanoidRootPart.CFrame.p - part.Parent.HumanoidRootPart.CFrame.p).magnitude <= MaxDistance then
									part.Parent.Humanoid.Animator:LoadAnimation(anims.Blockhit):Play()
									character.HumanoidRootPart.BlockHit:Play()
									return
								end
							end

							table.insert(HitTable, part.Parent)

							part.Parent:SetAttribute("Blocking", false)
							part.Parent.Humanoid:TakeDamage(tool:GetAttribute("Damage"))

							local animations = part.Parent.Humanoid.Animator:GetPlayingAnimationTracks()

							for i, anim in pairs(animations) do
								if anim == "Block" then
									anim:Stop()
								end
							end

							if currentComboNum == 1 then
								hitSoundFlourish:Play()

								stun.Stun(part.Parent.Humanoid, 1.2)

								local slide = Instance.new("BodyVelocity")
								slide.MaxForce = Vector3.new(1,1,1) * 30000
								slide.Velocity = character.HumanoidRootPart.CFrame.lookVector * 45 + Vector3.new(0, 0, 0)
								slide.Parent = part.Parent.HumanoidRootPart

								task.spawn(function()
									wait(0.2)
									slide:Destroy()
								end)

								task.spawn(function()
									part.Parent.Ragdoll.Value = true
									task.wait(1.2)
									part.Parent.Ragdoll.Value = false
								end)

								-- VFX

								task.spawn(function()

									local trail = Instance.new("Trail")
									trail.Transparency = NumberSequence.new(0.5, 1)
									trail.Lifetime = 0.25
									trail.FaceCamera = true

									local trail2 = Instance.new("Trail")
									trail2.Transparency = NumberSequence.new(0.5, 1)
									trail2.Lifetime = 0.25
									trail2.FaceCamera = true

									local trail3 = Instance.new("Trail")
									trail3.Transparency = NumberSequence.new(0.5, 1)
									trail3.Lifetime = 0.25
									trail3.FaceCamera = true

									local trail4 = Instance.new("Trail")
									trail4.Transparency = NumberSequence.new(0.5, 1)
									trail4.Lifetime = 0.25
									trail4.FaceCamera = true

									local attachment1 = Instance.new("Attachment")
									local attachment2 = Instance.new("Attachment")
									local attachment3 = Instance.new("Attachment")
									local attachment4 = Instance.new("Attachment")

									attachment1.Parent = part.Parent["Left Arm"]
									attachment1.WorldPosition = part.Parent["Left Arm"].Position
									trail.Parent = part.Parent["Left Arm"]
									trail.Attachment0 = part.Parent["Left Arm"].LeftGripAttachment
									trail.Attachment1 = attachment1

									attachment2.Parent = part.Parent["Right Arm"]
									attachment2.WorldPosition = part.Parent["Right Arm"].Position
									trail2.Parent = part.Parent["Right Arm"]
									trail2.Attachment0 = part.Parent["Right Arm"].RightGripAttachment
									trail2.Attachment1 = attachment2

									attachment3.Parent = part.Parent["Right Leg"]
									attachment3.WorldPosition = part.Parent["Right Leg"].Position
									trail3.Parent = part.Parent["Right Leg"]
									trail3.Attachment0 = part.Parent["Right Leg"].RightFootAttachment
									trail3.Attachment1 = attachment3

									attachment4.Parent = part.Parent["Left Leg"]
									attachment4.WorldPosition = part.Parent["Left Leg"].Position
									trail4.Parent = part.Parent["Left Leg"]
									trail4.Attachment0 = part.Parent["Left Leg"].LeftFootAttachment
									trail4.Attachment1 = attachment4

									wait(1.2)

									trail.Enabled = false
									trail2.Enabled = false
									trail3.Enabled = false
									trail4.Enabled = false

									wait(1)

									attachment1:Destroy()
									trail:Destroy()
									attachment2:Destroy()
									trail2:Destroy()
									attachment3:Destroy()
									trail3:Destroy()
									attachment4:Destroy()
									trail4:Destroy()
								end)

								-- Knockback on flourish

							elseif currentComboNum == 2 then
								hitSound:Play()
								local anim = part.Parent.Humanoid.Animator:LoadAnimation(anims.Hit1)
								anim:Play()
								stun.Stun(part.Parent.Humanoid, 0.7)
							elseif currentComboNum == 3 then
								hitSound:Play()
								local anim = part.Parent.Humanoid.Animator:LoadAnimation(anims.Hit2)
								anim:Play()
								stun.Stun(part.Parent.Humanoid, 0.7)
							else
								hitSound:Play()
								local anim = part.Parent.Humanoid.Animator:LoadAnimation(anims.Hit3)
								anim:Play()
								stun.Stun(part.Parent.Humanoid, 0.7)
							end

							-- VFX

							local punchParticle = vfx.PunchParticle:Clone()

							punchParticle.Parent = part.Parent.Torso
							punchParticle:Emit(1)

							task.spawn(function()
								wait(0.3)
								punchParticle:Destroy()
							end)
						end
					end
				end
			end
		end
		HitTable = {}
	end)
	
	-- Cooldowns
	
	if currentComboNum == 1 then
		wait(tool:GetAttribute("ComboCooldown"))
	else
		wait(tool:GetAttribute("M1Cooldown"))
	end
	
	cd = false
	
	wait(0.1)
	
	if mouseHeld == true then
		M1(plr, true)
		return
	end
	
	-- Combo Reset
	
	wait(2.5)
	
	if currentComboNum == lastComboNum then
		tool:SetAttribute("ComboNum", 1)
	end
	
end

replicatedStorage.Events.M1.OnServerEvent:Connect(M1)

Sorry for spaghetti code, I’m not the most advanced scripter.