Combat Hitbox Issue

  1. What do you want to achieve?
    I’m trying to make client to server to client combat

  2. What is the issue?
    I’m trying to make the characters attribute State change to Attack and back, I’m having issues with setting the attribute back to default on the server.
    I’m having trouble with the server script when setting the attribute back, because it will return early. Also if the player doesn’t hit anything in the module script, the State attribute wont change at all.

module script

if the hitbox never hits anything the remote never fires and the state Attribute doesn’t change, I’ve tried to move the remote outside the loop and I’ve also tried putting a attacklist if hitbox is a humanoid and firing that during the heartbeat instead but I also had some issues with that

local module = {}
local tweenservice = game:GetService("TweenService")

module.Melee = {
	BaseDamage = 10,
	Attack = function(player)
		local character = player.Character
		local torso = character.HumanoidRootPart
		
		local state = character:GetAttribute("State")
		
		if state ~= "Idle" then return end
		
		local attackevent = game.ReplicatedStorage.Events.AttackEvent
		local hits = false
		
		local overlap = OverlapParams.new()
		overlap.FilterDescendantsInstances = {character}
		overlap.FilterType = Enum.RaycastFilterType.Exclude
		
		local heart = game:GetService("RunService").Heartbeat:Connect(function()
			
			local hitbox = workspace:GetPartBoundsInBox(torso.CFrame + torso.CFrame.LookVector * 4, Vector3.new(5,4,5), overlap)
			
			for i,v in hitbox do
				
				local opponent = v.Parent:FindFirstChild("Humanoid")
				
				if opponent then
					if hits then return end
					
					local opponentstate = v.Parent:GetAttribute("State")
					
					if opponentstate == "Evade" then return end
					
					attackevent:FireServer(v.Parent, state)
					
					hits = true
				end
			end
		end)
		
		task.wait(.5)
		heart:Disconnect()
	end,
	
}

server script

the script will return early if it’s a rig npc and the code to set the attribute back wont run because it returns early, So I’d have to put multiple Character Set Attributes separately if it’s a rig, or player or if it hit nothing.

attackevent.OnServerEvent:Connect(function(player, opponentcharacter, localstate)
	local character = player.Character
	local torso = character.HumanoidRootPart

	local playerstate = character:GetAttribute("State")
	local opponentstate = opponentcharacter:GetAttribute("State")

	local opponenttorso = opponentcharacter.HumanoidRootPart
	print(player, opponentcharacter)

	if playerstate ~= localstate then return end

	if playerstate ~= "Idle" then return end
	
	character:SetAttribute("State", "Attack")
	
	if opponentcharacter.Name == "Rig" then opponentcharacter.Humanoid:TakeDamage(10) return end
	
	if player.Team == players:GetPlayerFromCharacter(opponentcharacter).Team then return end

	if opponentstate == "Evade" then print("Cant hit") return end

	if (opponenttorso.Position - torso.Position).Magnitude >= 12 then
		print("more than 12 distance")
		return
	end

	opponentcharacter.Humanoid:TakeDamage(10)
	task.wait(.5)
	character:SetAttribute("State", "Idle") -- wont work if it's a rig npc
end)

I’ve tried doing in the server script if opponentcharacter exists then do the checks and outside of the if statement I’d set the characters State Attribute then wait and set it back to default, but I’ve also had problems with that.

If I tried doing this in a local script it would be easier but wont replicate, what I’m trying to do is this.

example

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local uips = game:GetService("UserInputService")

uips.InputBegan:Connect(function(input)
	
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		
		local state = character:GetAttribute("State")
		
		if state ~= "Idle" then return end
		
		character:SetAttribute("State", "Attack")
		
		local torso = character.HumanoidRootPart
		
		local overlap = OverlapParams.new()
		overlap.FilterDescendantsInstances = {character}
		overlap.FilterType = Enum.RaycastFilterType.Exclude
		
		local heart = game:GetService("RunService").Heartbeat:Connect(function()
			
			local hitbox = workspace:GetPartBoundsInBox(torso.CFrame + torso.CFrame.LookVector *4, Vector3.new(5,4,5), overlap)
			
			for i,v in hitbox do
				
				if v.Parent:FindFirstChild("Humanoid") then
					
					v.Parent:FindFirstChild("Humanoid"):TakeDamage(10)
				end
				
			end
		end)
		
		task.wait(.5)
		heart:Disconnect()
		character:SetAttribute("State", "Idle") -- back to default
	end
end)

I’ve tried doing the attacklist again and I’m running into issues, I’ve simplified my code so I can show the problem easier. The server works fine if the task.wait() is 1 but when I do task.wait(.5) attack works even if the state isnt idle.

You can see in the video I press twice and it prints it cant hit but it still does

Local Script

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local uips = game:GetService("UserInputService")
local remote = game.ReplicatedStorage.RemoteEvent


uips.InputBegan:Connect(function(input)
	
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		
		local state = character:GetAttribute("State")
		local torso = character.HumanoidRootPart
		
		if state ~= "Idle" then print("Cant hit") return end
		
		local overlap = OverlapParams.new()
		overlap.FilterDescendantsInstances = {character}
		
		overlap.FilterType = Enum.RaycastFilterType.Exclude
		
		local attacklist = {}
		
		local heart = game:GetService("RunService").Heartbeat:Connect(function()
			
			local hitbox = workspace:GetPartBoundsInBox(torso.CFrame + torso.CFrame.LookVector*4, Vector3.new(5,4,5), overlap)
			
			for i,v in hitbox do
				local opponent = v.Parent:FindFirstChild("Humanoid")
				
				if opponent then
					attacklist[opponent.Parent.Name] = opponent.Parent
				end
			end
			
			
			remote:FireServer(attacklist)
		end)
		
		task.wait(1)
		heart:Disconnect()
	end
end)

Server Script

local remote = game.ReplicatedStorage.RemoteEvent
local players = game:GetService("Players")

players.PlayerAdded:Connect(function(newplayer)
	local character = newplayer.Character or newplayer.CharacterAdded:Wait()
	
	character:SetAttribute("State", "Idle")
end)

remote.OnServerEvent:Connect(function(player, attacklist)
	local character = player.Character
	local state = character:GetAttribute("State")
	
	if state ~= "Idle" then return end
	print("attacking", state)
	
	for i,v in attacklist do
		local opponentstate = v:GetAttribute("State")
		
		if opponentstate == "Evade" then return end
		
		v.Humanoid:TakeDamage(10)
	end
	
	character:SetAttribute("State", "Attack")
	task.wait(.5) -- doesn't work if it's .5 but works if it's 1
	character:SetAttribute("State", "Idle")
end)