How do you make a part touch multiple people without touching the same person twice?

I’ve been working on a sword based game that would let you hit multiple opponents without letting it hit the same person twice

Module Script Parent (If needed)
local rp = game:GetService("ReplicatedStorage")
local basiccombat = rp:WaitForChild("BasicCombat")
local specialcombat = rp:WaitForChild("SpecialCombat")
local sound = script.Parent:WaitForChild("SwordSounds")
local handler = require(script.CombatHandler)

basiccombat.OnServerEvent:Connect(function(Player, sequence)
	local character = Player.Character
	local h = character.Humanoid
	local humrp = character.HumanoidRootPart
	
	local sword = character:FindFirstChild("Sword")
	if sword then
		local plrAnim, swordAnim, length = handler.getAnimation(h,sword.Controller, sequence)
		plrAnim:Play()
		swordAnim:Play()
		
		plrAnim:GetMarkerReachedSignal("Hit"):Connect(function()
			handler.Attack(Player,character,humrp,length,sequence)
		
		end)
		
		task.delay(length , function()
			basiccombat:FireClient(Player)
			
		end)
	end
end)
specialcombat.OnServerEvent:Connect(function(Player, sequence)
	local character = Player.Character
	local h = character.Humanoid
	local humrp = character.HumanoidRootPart

	local sword = character:FindFirstChild("Sword")
	if sword then
		local plrAnim, swordAnim, length = handler.getAnimation(h,sword.Controller, sequence)
		plrAnim:Play()
		swordAnim:Play()

		plrAnim:GetMarkerReachedSignal("Hit"):Connect(function()
			handler.Attack(Player,character,humrp,length,sequence)
		
		end)

		task.delay(length , function()
			basiccombat:FireClient(Player)

		end)
	end
end)
Module Script
local Animations = script:WaitForChild("Animations")
local plr = Animations.plr
local swrd = Animations.Sword
local meshes = script.Meshes


local rp = game:GetService("ReplicatedStorage")
local KeyProvider = game:GetService("KeyframeSequenceProvider")
local debris = game:GetService("Debris")
local VFX = rp:WaitForChild("VFX")

local SSS = game:GetService("ServerScriptService")
local dictonaryHandler = require(SSS:WaitForChild("DictonaryHandler"))
local sounds = SSS:WaitForChild("SwordSounds")
local slashes = sounds:WaitForChild("Slashes")


local handler = {

	plrsInCombat = {},
	plrStunTime = {},
	plrAnims = {
		["L"] = plr.R6_Slash3,
		["LL"] = plr.R6_Slash2,
		["LLL"] = plr.R6_Slash1
	},
--   SpinAnims = {
--	["L"] = plr.R6_Spin
--	},

	SwordAnims = {
		["L"] = swrd.R6_Sword1,
		["LL"] = swrd.R6_Sword2,
		["LLL"] = swrd.R6_Sword3
	},
	SwordHitboxes = {
		["L"] =	CFrame.Angles(0, 0, math.rad(50)),
		["LL"] = CFrame.Angles(0, 0, math.rad(90)),
		["LLL"] = CFrame.Angles(0, 0, math.rad(135))
	}

}

function handler.getAnimation(Humanoid,SwordAnimationController, sequence)
	local length = 0

	local keysequence = KeyProvider:GetKeyframeSequenceAsync(handler.plrAnims[sequence].AnimationId)
	local keyframes = keysequence:GetKeyframes()

	for i=1, #keyframes do
		local Time = keyframes[i].Time
		if Time > length then
			length = Time
		end
	end

	return Humanoid.Animator:LoadAnimation(handler.plrAnims[sequence]), SwordAnimationController.Animator:LoadAnimation(handler.SwordAnims[sequence]), length
end


function handler.Attack(Player,character,humrp,length,sequence)
	local folder = Instance.new("Folder")
	folder.Name = "SwordCombat"
	folder.Parent = workspace
	debris:AddItem(folder, .25)

	local hitbox = meshes.HitBox:Clone()
	hitbox.Size = Vector3.new(2,14,7)
	hitbox.Transparency = 0
	hitbox.CFrame = humrp.CFrame * CFrame.new(0,.5,-4) * handler.SwordHitboxes[sequence]
	hitbox.Parent = folder

	local weld1 = Instance.new("ManualWeld")
	weld1.Name = "HitboxW"
	weld1.Part0 = hitbox
	weld1.Part1 = humrp
	weld1.C0 = weld1.Part0.CFrame:ToObjectSpace(weld1.Part1.CFrame)
	weld1.Parent = weld1.Part0

	local connection
	connection = hitbox.Touched:Connect(function() end)
	local results = hitbox:GetTouchingParts()
	
	if sequence == "LLL" then
		humrp.Parent.Humanoid.Jump = true
	end
	
	local slash = slashes.OOT_Sword1:Clone()
	slash.Parent = workspace
	slash:Play()

coroutine.wrap(function()
		for _, object in pairs(results) do
			if not object:IsDescendantOf(character) then
				local Eh = object.Parent:FindFirstChild("Humanoid")
				local Ehmrp = object.Parent:FindFirstChild("HumanoidRootPart")
				if Eh and Ehmrp and Eh.Health > 0 then
					hitbox:Destroy()
					
					
					if handler.plrsInCombat[character] or dictonaryHandler.find(character, "Stunned") then
						return
					end	
					if connection then
						connection:Disconnect()
					end
					
					if sequence == "LLL" then

							Eh:TakeDamage(15)
							Eh.Jump = true
					else
							Eh:TakeDamage(10)
					end
					


					local Speed = 40
					local Force = 8000

					local TotalForce = Force
					local KnockBack = Ehmrp:FindFirstChild("BodyVelocity")
					
					if KnockBack.Velocity == 1167 then
						return
					else
						KnockBack.MaxForce = Vector3.new(TotalForce,TotalForce,TotalForce)
						KnockBack.Velocity = character:FindFirstChild("HumanoidRootPart").CFrame.LookVector * Speed
						wait(.1)
						KnockBack.MaxForce = Vector3.new(0,0,0)
					end
				

					



					Eh.Died:Connect(function()
						if not Eh:GetAttribute("Dead") then
							Eh:SetAttribute("Dead", true)
							local leaderstats = Player.leaderstats
							print(Player,"Killed",Eh.Parent)
							leaderstats.Kills.Value = leaderstats.Kills.Value + 1
						end
					end)





					coroutine.wrap(function()
						for _, plr in pairs(game.Players:GetChildren()) do
							VFX:FireClient(plr,"SwordVFX","vfx",Ehmrp)
							
						end
					end)()

					local duration

					if string.len(sequence) < 4 then
						duration = length * 2
					else
						duration = length * 3
					end
					if not handler.plrsInCombat[Eh.Parent] then
						handler.plrsInCombat[Eh.Parent] = os.clock()
						handler.plrsInCombat[Eh.Parent] = duration
						dictonaryHandler.add(Eh.Parent, "Stunned")
						
						Eh.WalkSpeed = 20
						slash:Destroy()
					else
						handler.plrsInCombat[Eh.Parent] = os.clock()
						handler.plrStunTime[Eh.Parent] = duration
						
						Eh.WalkSpeed = 20
						slash:Destroy()
					end
				end
			end
		end
	end)()


	task.delay(.25, function()
		if connection then
			connection:Disconnect()
			slash:Destroy()
		end
	end)
end
return handler

This is the module script that handles the sword physics (damage, hitbox size, etc) and I closest solution I’ve found is putting a break after the first end (what I mean is below but, this causes the hitbox to only hit the first person and ignore anyone else. Anyone know an alternative to better suit my situation?

Example
						handler.plrsInCombat[Eh.Parent] = os.clock()
						handler.plrsInCombat[Eh.Parent] = duration
						dictonaryHandler.add(Eh.Parent, "Stunned")
						
						Eh.WalkSpeed = 20
						slash:Destroy()
					else
						handler.plrsInCombat[Eh.Parent] = os.clock()
						handler.plrStunTime[Eh.Parent] = duration
						
						Eh.WalkSpeed = 20
						slash:Destroy()
					end
                break -- only one person is hit once.
				end

If anything else is needed I’ll gradly provide

1 Like

Couldn’t you use a table to effectively just create a debounce?

You could use table yo check if enemy already hit
Example:

local AlreadyHit = {}
Hitbox.Touched: Connect (function(Hit)
local humanoid = Hit.Parent:FindFirstChild("Humanoid")
if humanoid and not table.find(AlreadyHit,humanoid) then
       table.insert(AlreadyHit,humanoid)
       humanoid:TakeDamage(10)
    end
end)
2 Likes

This sort of works. I’ll try my best to word this, It adds to the table but, when i make it remove it from the table (or use table.clear) it just makes the player take damage again like usual making the whole table redundant.

Edited Module

Changes are more towards the bottom

local plr = Animations.plr
local swrd = Animations.Sword
local meshes = script.Meshes


local rp = game:GetService("ReplicatedStorage")
local KeyProvider = game:GetService("KeyframeSequenceProvider")
local debris = game:GetService("Debris")
local VFX = rp:WaitForChild("VFX")

local SSS = game:GetService("ServerScriptService")
local dictonaryHandler = require(SSS:WaitForChild("DictonaryHandler"))
local sounds = SSS:WaitForChild("SwordSounds")
local slashes = sounds:WaitForChild("Slashes")

local AlreadyHit = {}

local handler = {

	plrsInCombat = {},
	plrStunTime = {},
	plrAnims = {
		["L"] = plr.R6_Slash3,
		["LL"] = plr.R6_Slash2,
		["LLL"] = plr.R6_Slash1
	},
--   SpinAnims = {
--	["L"] = plr.R6_Spin
--	},

	SwordAnims = {
		["L"] = swrd.R6_Sword1,
		["LL"] = swrd.R6_Sword2,
		["LLL"] = swrd.R6_Sword3
	},
	SwordHitboxes = {
		["L"] =	CFrame.Angles(0, 0, math.rad(50)),
		["LL"] = CFrame.Angles(0, 0, math.rad(90)),
		["LLL"] = CFrame.Angles(0, 0, math.rad(135))
	}
	
}

function handler.getAnimation(Humanoid,SwordAnimationController, sequence)
	local length = 0

	local keysequence = KeyProvider:GetKeyframeSequenceAsync(handler.plrAnims[sequence].AnimationId)
	local keyframes = keysequence:GetKeyframes()

	for i=1, #keyframes do
		local Time = keyframes[i].Time
		if Time > length then
			length = Time
		end
	end

	return Humanoid.Animator:LoadAnimation(handler.plrAnims[sequence]), SwordAnimationController.Animator:LoadAnimation(handler.SwordAnims[sequence]), length
end


function handler.Attack(Player,character,humrp,length,sequence)
	local folder = Instance.new("Folder")
	folder.Name = "SwordCombat"
	folder.Parent = workspace
	debris:AddItem(folder, .25)

	local hitbox = meshes.HitBox:Clone()
	hitbox.Size = Vector3.new(2,14,7)
	hitbox.Transparency = 0
	hitbox.CFrame = humrp.CFrame * CFrame.new(0,.5,-4) * handler.SwordHitboxes[sequence]
	hitbox.Parent = folder

	local weld1 = Instance.new("ManualWeld")
	weld1.Name = "HitboxW"
	weld1.Part0 = hitbox
	weld1.Part1 = humrp
	weld1.C0 = weld1.Part0.CFrame:ToObjectSpace(weld1.Part1.CFrame)
	weld1.Parent = weld1.Part0

	local connection
	connection = hitbox.Touched:Connect(function() end)
	local results = hitbox:GetTouchingParts()
	
	if sequence == "LLL" then
		humrp.Parent.Humanoid.Jump = true
	end
	
	local slash = slashes.OOT_Sword1:Clone()
	slash.Parent = workspace
	slash:Play()
	
	
	
	
	coroutine.wrap(function()
		for _, object in pairs(results) do
			if not object:IsDescendantOf(character) then
				local Eh = object.Parent:FindFirstChild("Humanoid")
				local Ehmrp = object.Parent:FindFirstChild("HumanoidRootPart")
				if Eh and Ehmrp and Eh.Health > 0 then
					hitbox:Destroy()

					if Eh and not table.find(AlreadyHit,Eh) then
						if sequence == "LLL" then
							table.insert(AlreadyHit,Eh)
							Eh:TakeDamage(15)
							Eh.Jump = true

						else
							table.insert(AlreadyHit,Eh)
							Eh:TakeDamage(10)
							print(AlreadyHit)
							print(Eh.Health)

						end	
						
						local Speed = 40
						local Force = 8000

						local TotalForce = Force
						local KnockBack = Ehmrp:FindFirstChild("BodyVelocity")

						if KnockBack.Velocity == 1167 then
							return
						else
							KnockBack.MaxForce = Vector3.new(TotalForce,TotalForce,TotalForce)
							KnockBack.Velocity = character:FindFirstChild("HumanoidRootPart").CFrame.LookVector * Speed
							wait(.1)
							KnockBack.MaxForce = Vector3.new(0,0,0)
						end



						
						coroutine.wrap(function()
							for _, plr in pairs(game.Players:GetChildren()) do
								VFX:FireClient(plr,"SwordVFX","vfx",Ehmrp)

							end
						end)()
					end
					
					if handler.plrsInCombat[character] or dictonaryHandler.find(character, "Stunned") then
						return
					end	
					if connection then
						connection:Disconnect()
					end
					
					
					



					






					Eh.Died:Connect(function()
						if not Eh:GetAttribute("Dead") then
							Eh:SetAttribute("Dead", true)
							local leaderstats = Player.leaderstats
							print(Player,"Killed",Eh.Parent)
							leaderstats.Kills.Value = leaderstats.Kills.Value + 1
						end
					end)




					
					

					local duration

					if string.len(sequence) < 4 then
						duration = length * 2
					else
						duration = length * 3
					end
					if not handler.plrsInCombat[Eh.Parent] then
						handler.plrsInCombat[Eh.Parent] = os.clock()
						handler.plrsInCombat[Eh.Parent] = duration
						dictonaryHandler.add(Eh.Parent, "Stunned")

						Eh.WalkSpeed = 20
						slash:Destroy()
						
					else
						handler.plrsInCombat[Eh.Parent] = os.clock()
						handler.plrStunTime[Eh.Parent] = duration

						Eh.WalkSpeed = 20
						slash:Destroy()
					end
					wait(1)
           table.clear(AlreadyHit)
				end
			end
		end
	end)()
	

	task.delay(.25, function()
		if connection then
			connection:Disconnect()
			slash:Destroy()
		end
	end)
end



return handler

Did you make a cooldown before you clear the table?

yes, it just makes the humanoid take damage over and over but, just slightly delayed

Make the AlreadyHit table in the function not outside
This should fix it if i understand your combat system right