Need Help On A Script For Sword Combat System

Guys, I’m trying to make a sword combat system but I have a problem with a script.

local debounce1 = 1
UserInputService.InputBegan:Connect(function(input, gameProccessed)
	local char = tool.Parent
	humanoid = char:WaitForChild("Humanoid")

	heavySlice1 = humanoid:LoadAnimation(tool:WaitForChild("HeavySlice1")) 
	heavySlice2 = humanoid:LoadAnimation(tool:WaitForChild("HeavySlice2")) 
	
	if input.UserInputType == Enum.UserInputType.MouseButton2 then
		if debounce1 == 1 then
			local waitTime = math.max(heavySlice1.Length, HeavyCooldownTime)
			if not isAttacking then
				isAttacking = true -- Disable repeated attack attempts during animation
				canDamageHeavy = true
				heavySlice1:Play() -- Play animation
				wait(waitTime) -- Wait until animation is complete or cooldown is finished whichever is longer
				isAttacking = false -- Re-Enable attack attempts
				debounce = 2
			end
		elseif debounce1 == 2 then
			local waitTime = math.max(heavySlice2.Length, HeavyCooldownTime)
			if not isAttacking then
				isAttacking = true -- Disable repeated attack attempts during animation
				canDamageHeavy = true
				heavySlice2:Play() -- Play animation
				wait(waitTime) -- Wait until animation is complete or cooldown is finished whichever is longer
				isAttacking = false -- Re-Enable attack attempts
				debounce = 2
			end
		end
	end
end)

The problem is that the sword animation doesn’t play and the otherHumanoid doesn’t take damage.
Here is the full script:

-- Settings

local CooldownTime = 0.5
local HeavyCooldownTime = 2
local Damage = 30
local HeavyDamage = 50

-- Services 

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local UserInputService = game:GetService("UserInputService")

-- Tool Variables 

local tool = script.Parent
local swordBlade = tool.Handle

-- Animations, Humanoid, Player 

local humanoid, attack1, attack2, attack3, attack4, attack5, heavySlice1, heavySlice2, equip, player

-- Bool Values

local canDamage = true
local canDamageHeavy = true
local isAttacking = false

-- Equip Function
local function onEquip()
	-- Set humanoid, animations and player variables
	local char = tool.Parent
	humanoid = char:WaitForChild("Humanoid")
	player = Players:GetPlayerFromCharacter(char)
	equip = humanoid:LoadAnimation(tool:WaitForChild("Equip"))
	equip:Play()
end

-- Detect Hit Function 

local function onDetectHit(otherPart)
	local partParent = otherPart.Parent
	local otherHumanoid = partParent:FindFirstChildWhichIsA("Humanoid")
	-- Make sure blade doesn't harm controlling player
	
	if otherHumanoid and otherHumanoid == humanoid then
		return
	elseif otherHumanoid and isAttacking and canDamage then
		canDamage = false
		otherHumanoid:TakeDamage(Damage)
	elseif otherHumanoid and isAttacking and canDamageHeavy then
		canDamageHeavy = false
		otherHumanoid:TakeDamage(HeavyDamage)
	end
end

-- On Attack Function


local debounce = 1
local function onAttack()
	
	local char = tool.Parent
	humanoid = char:WaitForChild("Humanoid")
	
	attack1 = humanoid:LoadAnimation(tool:WaitForChild("Attack1")) 
	attack2 = humanoid:LoadAnimation(tool:WaitForChild("Attack2")) 
	attack3 = humanoid:LoadAnimation(tool:WaitForChild("Attack3")) 
	attack4 = humanoid:LoadAnimation(tool:WaitForChild("Attack4")) 
	attack5 = humanoid:LoadAnimation(tool:WaitForChild("Attack5")) 
	
	if debounce == 1 then
		-- Attack 1
		local waitTime = math.max(attack1.Length, CooldownTime)
		if not isAttacking then
			isAttacking = true -- Disable repeated attack attempts during animation
			canDamage = true
			attack1:Play() -- Play animation
			wait(waitTime) -- Wait until animation is complete or cooldown is finished whichever is longer
			isAttacking = false -- Re-Enable attack attempts
			debounce = 2
		end
	elseif debounce == 2 then
		-- Attack 2
		local waitTime = math.max(attack2.Length, CooldownTime)
		if not isAttacking then
			isAttacking = true -- Disable repeated attack attempts during animation
			canDamage = true
			attack2:Play() -- Play animation
			wait(waitTime) -- Wait until animation is complete or cooldown is finished whichever is longer
			isAttacking = false -- Re-Enable attack attempts
			debounce = 3
		end
	elseif debounce == 3 then
		-- Attack 3
		local waitTime = math.max(attack3.Length, CooldownTime)
		if not isAttacking then
			isAttacking = true -- Disable repeated attack attempts during animation
			canDamage = true
			attack3:Play() -- Play animation
			wait(waitTime) -- Wait until animation is complete or cooldown is finished whichever is longer
			isAttacking = false -- Re-Enable attack attempts
			debounce = 4
		end
	elseif debounce == 4 then
		-- Attack 4
		local waitTime = math.max(attack4.Length, CooldownTime)
		if not isAttacking then
			isAttacking = true -- Disable repeated attack attempts during animation
			canDamage = true
			attack4:Play() -- Play animation
			wait(waitTime) -- Wait until animation is complete or cooldown is finished whichever is longer
			isAttacking = false -- Re-Enable attack attempts
			debounce = 5
		end
	elseif debounce == 5 then
		-- Attack 5
		local waitTime = math.max(attack5.Length, CooldownTime)
		if not isAttacking then
			isAttacking = true -- Disable repeated attack attempts during animation
			canDamage = true
			attack5:Play() -- Play animation
			wait(waitTime) -- Wait until animation is complete or cooldown is finished whichever is longer
			isAttacking = false -- Re-Enable attack attempts
			debounce = 1
		end
	end
end

-- Heavy Hit Function

local debounce1 = 1
UserInputService.InputBegan:Connect(function(input, gameProccessed)
	local char = tool.Parent
	humanoid = char:WaitForChild("Humanoid")

	heavySlice1 = humanoid:LoadAnimation(tool:WaitForChild("HeavySlice1")) 
	heavySlice2 = humanoid:LoadAnimation(tool:WaitForChild("HeavySlice2")) 
	
	if input.UserInputType == Enum.UserInputType.MouseButton2 then
		if debounce1 == 1 then
			local waitTime = math.max(heavySlice1.Length, HeavyCooldownTime)
			if not isAttacking then
				isAttacking = true -- Disable repeated attack attempts during animation
				canDamageHeavy = true
				heavySlice1:Play() -- Play animation
				wait(waitTime) -- Wait until animation is complete or cooldown is finished whichever is longer
				isAttacking = false -- Re-Enable attack attempts
				debounce1 = 2
			end
		elseif debounce1 == 2 then
			local waitTime = math.max(heavySlice2.Length, HeavyCooldownTime)
			if not isAttacking then
				isAttacking = true -- Disable repeated attack attempts during animation
				canDamageHeavy = true
				heavySlice2:Play() -- Play animation
				wait(waitTime) -- Wait until animation is complete or cooldown is finished whichever is longer
				isAttacking = false -- Re-Enable attack attempts
				debounce1 = 2
			end
		end
	end
end)

-- Connections

tool.Equipped:Connect(onEquip)
tool.Activated:Connect(onAttack)
swordBlade.Touched:Connect(onDetectHit)

I tried getting help from Roblox Studio Communities.

1 Like

what is the problem?

I noticed you’re assigning debounce = 2 and not debounce1 = 2 same with the end of the block of code under elseif debouce1 == 2 then

1 Like

I did change it but it still doesn’t work.

what exactly doesn’t work? your original post doesn’t say what the bug is

I edited the post. You can see now why.

But the other humanoid can take damage from MouseButton1. What I mean is that the other humanoid can’t take heavy damage and the heavy slice animations don’t play.

Something like this may work, I haven’t tested it so you might wanna debug it if it doesn’t work properly. The combo system you tried to pull off could’ve been simplified so much. Another flaw in the system you’ve made is that you’re doing hit calculations on the client. I would personally do hit calculations on the client to improve hit precision but you’re doing the actual damage handling and other checks on the client. 1) Exploiters can abuse that and demolish everyone in the server as .Touched() relies on the client’s position. 2) Damage won’t replicate to others as you’re only damaging them on your client. You should read more into Roblox Networking (Remote Events and Functions) Remote Functions and Events
Exploiting Explained
Exploiting Explained

-- Client
	
	local Players = game:GetService("Players")
	local UserInputService = game:GetService("UserInputService")
	local ReplicatedStorage = game:GetService("ReplicatedStorage")
	
	local onPlayerAttack = ReplicatedStorage:WaitForChild("OnPlayerAttack")
	
	local player = Players.LocalPlayer
	
	local character = player.Character or player.CharacterAdded:Wait()
	local humanoid = character:WaitForChild("Humanoid")
	
	local tool = script.Parent -- path to tool
	
	local mouse = player:GetMouse()
	
	-- I recommend loading all the tracks onto the humanoid during runtime, this makes sure that there is no
	-- loading delay or anything else of the sort
	local equipTrack = humanoid:LoadAnimation(tool:WaitForChild("Equip")) 
	
	
	local function onEquip()
		equipTrack:Play()
	end
	
	local function onAttack(state)
		onPlayerAttack:FireServer(state)
	end
	
	
	tool.Equipped:Connect(onEquip)
	mouse.Button1Down:Connect(function()
		onAttack("Light")
	end)
	
	mouse.Button2Down:Connect(function()
		onAttack("Heavy")
	end)
-- Server Code
	
	local COMBO_WINDOW = 0.35 -- Set to how long the player has to left click before the combo is over
	local DAMAGE = 30
	local HEAVY_DAMAGE = 50
	
	
	local ReplicatedStorage = game:GetService("ReplicatedStorage")
	local Players = game:GetService("Players")
	
	local onPlayerAttack = ReplicatedStorage:WaitForChild("OnPlayerAttack")

	local attackDebounce = false
	
	
	
	local combo = {}
	local previous = {}
	local current = {}
	local timeBetweenAttack = {}
	
	local animsFolder = script:WaitForChild("AnimationFolder")

	local heavyAnimations = {
		animsFolder:WaitForChild("HeavySlice1"), 
		animsFolder:WaitForChild("HeavySlice2"),
	}
	
	local lightAnimations = {
		animsFolder:WaitForChild("Attack1"), 
		animsFolder:WaitForChild("Attack2"),
		animsFolder:WaitForChild("Attack3"), 
		animsFolder:WaitForChild("Attack4"),
		animsFolder:WaitForChild("Attack5"),
	}
	
	
	local function handleDamage(hit, humanoidWhoAttacked)
		local hitCharacter = hit:FindFirstAncestorWhichIsA("Model")
		if (hitCharacter) then
			local hitHumanoid = hitCharacter:FindFirstChildOfClass("Humanoid")
			
			if (hitHumanoid) then
				if (hitHumanoid ~= humanoidWhoAttacked) then
					if (hitHumanoid.Health > 0) then
						hitHumanoid:TakeDamage(DAMAGE)
					end
				end
			end
		end
	end
	
	onPlayerAttack.OnServerEvent:Connect(function(player, state)
		-- Doing sanity checks as the client could send any information and potentially break the script
		if (state ~= nil) then
			if (typeof(state) == "string") then
				if (string.lower(state) == "light" or string.lower(state) == "heavy") then
					local character = player.Character
					local humanoid = character.Humanoid
					local root = character.HumanoidRootPart

					local tool = character:FindFirstChildOfClass("Tool")
				
					local touchConnection = nil
					
	
					current[player] = tick()
					if (combo[player] == 0) then
						timeBetweenAttack[player] = -COMBO_WINDOW
					end
					
					if (current[player] - previous[player] <= timeBetweenAttack[player] + COMBO_WINDOW) then
						if (combo[player] < #heavyAnimations) then
							-- Attack
							combo[player] += 1
							
							local animTrack = nil
							

							if (humanoid.Health > 0 and tool) then
								
								if (state == "light") then
									timeBetweenAttack = lightAnimations[combo[player] + 1].Length
									animTrack = lightAnimations[combo[player]]
						
								elseif (state == "heavy") then
									animTrack = heavyAnimations[combo[player]]
									timeBetweenAttack = heavyAnimations[combo[player] + 1].Length
								end

								local playingTrack = humanoid:LoadAnimation(animTrack)
								playingTrack:Play()
								
								
								touchConnection = tool.Handle.Touched:Connect(function(hit)
									handleDamage(hit, humanoid)
								end)


							end
						else
							-- Player went thorugh all their combos
							combo[player] = 0
						end

					else
						-- Reset combo as player didn't click within the time window
						combo[player] = 0
					end


					previous = tick()
					
					if (touchConnection ~= nil) then
						touchConnection:Disconnect()
					end
				end
				
				
			end
		end
		
	end)
1 Like

I tried it, it seems professional but I get an “error” (I don’t know how we call it) and the animations don’t work.

Would you like to get access to the place?

Or

Add me on discord, so I can show you live: SilentDev#8569

1 Like

The script that has the comment (–Server) should be placed in ServerScriptService. You’d then create a folder named “AnimationFolder” and place your animations in that folder. The local script can go inside of your tool or character, I prefer placing it in StarterCharacterScript for ultimate organisation reasons.

1 Like

I did what you said but it still doesn’t work.

I did some changes, I forgot to do something on the server. It should work now, recopy the server script.

Recopy what Server Script? Also if it doesn’t work, is there any other way we can contact so I can show you with sharescreen? Because I will need to ask you some questions too.

Yeah my discord is KongoBongo#7272