Unable to check if player has tool

I was trying to add on to my NPC system, where the NPC would chase a player and kill it. My goal was that if the player has a tool in their inventory, the NPC wouldn’t chase the player, but ignore it. I got to a point, where it just stands still and doesn’t do anything. Here’s the script.

wait(2)

local PathfindingService = game:GetService("PathfindingService")

local npc = script.Parent
local humanoid = npc:WaitForChild("Humanoid")
local hrp = npc:WaitForChild("HumanoidRootPart")
hrp:SetNetworkOwner(nil)

local walkAnim = humanoid.Animator:LoadAnimation(script.Walk)
local attackAnim = humanoid.Animator:LoadAnimation(script.Attack)

local RANGE = 60
local DAMAGE = 100
local REQUIRED_TOOL_NAME = "Tazer" -- Replace with the name of the required tool
local COOLDOWN = 5 -- Cooldown in seconds

-- Enum for AI states
local AIState = {
	Patrol = "Patrol",
	Attack = "Attack",
	Cooldown = "Cooldown",
}

-- Helper function to check if the player has the required tool
local function hasRequiredTool(player)
	local character = player.Character
	if character and character:FindFirstChild(REQUIRED_TOOL_NAME) then
		return true
	end
	return false
end

local function canSeeTarget(target)
	local orgin = hrp.Position
	local direction = (target.HumanoidRootPart.Position - hrp.Position).Unit * RANGE
	local ray = workspace:Raycast(orgin, direction)

	if ray and ray.Instance then
		local hitPart = ray.Instance
		local character = hitPart.Parent

		-- Check if the hit part or its ancestor is the target
		while character do
			if character == target then
				-- Check if the player has the required tool
				return hasRequiredTool(target)
			end

			character = character.Parent
		end

		return false
	else
		return false
	end
end

local function findTarget()
	local players = game.Players:GetPlayers()
	local maxDistance = RANGE
	local nearestTarget

	for i, player in pairs(players) do
		if player.Character and player.Character:IsA("Model") then
			local target = player.Character
			local distance = (hrp.Position - target.HumanoidRootPart.Position).Magnitude

			if distance < maxDistance and canSeeTarget(target) then
				nearestTarget = target
				maxDistance = distance
			end
		end
	end

	return nearestTarget
end

local function attack(target)
	local distance = (hrp.Position - target.HumanoidRootPart.Position).Magnitude

	if distance > 5 then
		humanoid:MoveTo(target.HumanoidRootPart.Position)
	else
		npc.Head.AttackSound:Play()
		attackAnim:Play()
		target.Humanoid.Health -= DAMAGE
		wait(0.5)
	end
end

local function mainLoop()
	local currentState = AIState.Patrol
	local cooldownEndTime = 0

	while true do
		local target = findTarget()

		if target then
			if target.Humanoid.Health > 0 then
				if currentState == AIState.Patrol then
					currentState = AIState.Attack
				elseif currentState == AIState.Attack then
					attack(target)
					currentState = AIState.Cooldown
					cooldownEndTime = tick() + COOLDOWN
				elseif currentState == AIState.Cooldown then
					if tick() >= cooldownEndTime then
						currentState = AIState.Patrol
					end
				end
			end
		else
			currentState = AIState.Patrol
		end

		wait(0.2)
	end
end

mainLoop()

1 Like

Your code only checks if the player is holding it, so you might want to check if it’s in their backpack too.

I’m not sure how to do that, could you explain?

You can check if the desired tool is in the player’s inventory. You can go about this by using for pairs but i think there is a better solution to these kinds of things.

EDIT: Fors is the best solution i think, create a table of all the items of that type and you can go about your day like such, you can also check how many of those items are in the player’s inventory using this method.

1 Like

Use :FindFirstChild on the player’s backpack.

Code:

wait(2)

local PathfindingService = game:GetService("PathfindingService")

local npc = script.Parent
local humanoid = npc:WaitForChild("Humanoid")
local hrp = npc:WaitForChild("HumanoidRootPart")
hrp:SetNetworkOwner(nil)

local walkAnim = humanoid.Animator:LoadAnimation(script.Walk)
local attackAnim = humanoid.Animator:LoadAnimation(script.Attack)

local RANGE = 60
local DAMAGE = 100
local REQUIRED_TOOL_NAME = "Tazer" -- Replace with the name of the required tool
local COOLDOWN = 5 -- Cooldown in seconds

-- Enum for AI states
local AIState = {
	Patrol = "Patrol",
	Attack = "Attack",
	Cooldown = "Cooldown",
}

-- Helper function to check if the player has the required tool
local function hasRequiredTool(player)
	local character = player.Character
	if (character and character:FindFirstChild(REQUIRED_TOOL_NAME)) or player.Backpack:FindFirstChild(REQUIRED_TOOL_NAME) then
		return true
	end
	return false
end

local function canSeeTarget(target)
	local orgin = hrp.Position
	local direction = (target.HumanoidRootPart.Position - hrp.Position).Unit * RANGE
	local ray = workspace:Raycast(orgin, direction)

	if ray and ray.Instance then
		local hitPart = ray.Instance
		local character = hitPart.Parent

		-- Check if the hit part or its ancestor is the target
		while character do
			if character == target then
				-- Check if the player has the required tool
				return hasRequiredTool(target)
			end

			character = character.Parent
		end

		return false
	else
		return false
	end
end

local function findTarget()
	local players = game.Players:GetPlayers()
	local maxDistance = RANGE
	local nearestTarget

	for i, player in pairs(players) do
		if player.Character and player.Character:IsA("Model") then
			local target = player.Character
			local distance = (hrp.Position - target.HumanoidRootPart.Position).Magnitude

			if distance < maxDistance and canSeeTarget(target) then
				nearestTarget = target
				maxDistance = distance
			end
		end
	end

	return nearestTarget
end

local function attack(target)
	local distance = (hrp.Position - target.HumanoidRootPart.Position).Magnitude

	if distance > 5 then
		humanoid:MoveTo(target.HumanoidRootPart.Position)
	else
		npc.Head.AttackSound:Play()
		attackAnim:Play()
		target.Humanoid.Health -= DAMAGE
		task.wait(0.5)
	end
end

local function mainLoop()
	local currentState = AIState.Patrol
	local cooldownEndTime = 0

	while true do
		local target = findTarget()

		if target then
			if target.Humanoid.Health > 0 then
				if currentState == AIState.Patrol then
					currentState = AIState.Attack
				elseif currentState == AIState.Attack then
					attack(target)
					currentState = AIState.Cooldown
					cooldownEndTime = tick() + COOLDOWN
				elseif currentState == AIState.Cooldown then
					if tick() >= cooldownEndTime then
						currentState = AIState.Patrol
					end
				end
			end
		else
			currentState = AIState.Patrol
		end

		task.wait(0.2)
	end
end

mainLoop()

You can use :FindFirstChild.

what if he wants to check for multiple of the same tool tho?

What do you mean by multiple of the same tool? You mean different tools?

The OP said they wanted to check if a player has tool, not the amount of tools they have.

no i mean what if he wants to check the amount of items of the same type? could be useful for development further on so i think it is a better solution.

If they wanted to, they would’ve put it in the post or notified me. Also, a loop is worse for performance because you’ll also have to keep a counter of how many times the tool was found (which is not needed for the game, and why would you even need to track that?).

“Character is not a valid member of Model “Workspace.austintmrw””

Well character is not a valid descendant of the player’s character in workspace, thats why.

Fixed, it was an error with your original script:

wait(2)

local PathfindingService = game:GetService("PathfindingService")

local npc = script.Parent
local humanoid = npc:WaitForChild("Humanoid")
local hrp = npc:WaitForChild("HumanoidRootPart")
hrp:SetNetworkOwner(nil)

local walkAnim = humanoid.Animator:LoadAnimation(script.Walk)
local attackAnim = humanoid.Animator:LoadAnimation(script.Attack)

local RANGE = 60
local DAMAGE = 100
local REQUIRED_TOOL_NAME = "Tazer" -- Replace with the name of the required tool
local COOLDOWN = 5 -- Cooldown in seconds

-- Enum for AI states
local AIState = {
	Patrol = "Patrol",
	Attack = "Attack",
	Cooldown = "Cooldown",
}

-- Helper function to check if the player has the required tool
local function hasRequiredTool(player)
	local character = player.Character
	if (character and character:FindFirstChild(REQUIRED_TOOL_NAME)) or player.Backpack:FindFirstChild(REQUIRED_TOOL_NAME) then
		return true
	end
	return false
end

local function canSeeTarget(player)
	local target = player.Character
	local orgin = hrp.Position
	local direction = (target.HumanoidRootPart.Position - hrp.Position).Unit * RANGE
	local ray = workspace:Raycast(orgin, direction)

	if ray and ray.Instance then
		local hitPart = ray.Instance
		local character = hitPart.Parent

		-- Check if the hit part or its ancestor is the target
		while character do
			if character == target then
				-- Check if the player has the required tool
				return hasRequiredTool(player)
			end

			character = character.Parent
		end

		return false
	else
		return false
	end
end

local function findTarget()
	local players = game.Players:GetPlayers()
	local maxDistance = RANGE
	local nearestTarget

	for i, player in pairs(players) do
		if player.Character and player.Character:IsA("Model") then
			local target = player.Character
			local distance = (hrp.Position - target.HumanoidRootPart.Position).Magnitude

			if distance < maxDistance and canSeeTarget(player) then
				nearestTarget = target
				maxDistance = distance
			end
		end
	end

	return nearestTarget
end

local function attack(target)
	local distance = (hrp.Position - target.HumanoidRootPart.Position).Magnitude

	if distance > 5 then
		humanoid:MoveTo(target.HumanoidRootPart.Position)
	else
		npc.Head.AttackSound:Play()
		attackAnim:Play()
		target.Humanoid.Health -= DAMAGE
		task.wait(0.5)
	end
end

local function mainLoop()
	local currentState = AIState.Patrol
	local cooldownEndTime = 0

	while true do
		local target = findTarget()

		if target then
			if target.Humanoid.Health > 0 then
				if currentState == AIState.Patrol then
					currentState = AIState.Attack
				elseif currentState == AIState.Attack then
					attack(target)
					currentState = AIState.Cooldown
					cooldownEndTime = tick() + COOLDOWN
				elseif currentState == AIState.Cooldown then
					if tick() >= cooldownEndTime then
						currentState = AIState.Patrol
					end
				end
			end
		else
			currentState = AIState.Patrol
		end

		task.wait(0.2)
	end
end

mainLoop()

I would assume that your looking for the character through the character right now.

The NPC is just standing still, it isn’t working and is just frozen still.

Did this happen before I changed the code? Try the original code you sent and tell me if that problem happens.

It did, i’m not sure why that happened though.