NPC AI not working as intended

I have an NPC in my game that’s supposed to move only when it’s not being looked at by any player, if 1 or more players are looking at it this means it should not move, however despite checking for every player in the game it only works for the first player who joined who looks at it and if they lose vision of the NPC it will still chase the other players even if they are looking at it, I can’t seem to figure out the reason to this, why does this happen?

local character = script.Parent
local humanoid = character:FindFirstChild("Humanoid")
local root = character:FindFirstChild("HumanoidRootPart")
local detectionDistance = 100
local maxAllowedAngle = math.cos(math.rad(65))

local targetParts = {}

for i,v in pairs(character:GetChildren()) do
	if v:IsA("MeshPart") then
		table.insert(targetParts, v)
	end
end

local lookedAt = false
local speed = 35

humanoid.WalkSpeed = speed

local function isPlayerWithinDistance(player)
	local character = player.Character
	if not character then
		return false
	end

	for _, targetPart in pairs(targetParts) do
		local distance = (targetPart.Position - character.Head.Position).magnitude
		if distance <= detectionDistance then
			return true
		end
	end

	return false
end

local function isPlayerLooking(player)
	local character = player.Character
	if not character then
		return false
	end

	for _, targetPart in pairs(targetParts) do
		local direction = (targetPart.Position - character.Head.Position).unit
		local characterOrientation = character.HumanoidRootPart.CFrame.lookVector

		local dotProduct = direction:Dot(characterOrientation)

		if dotProduct >= maxAllowedAngle then
			return true
		end
	end

	return false
end

local function performRaycast(player)
	local character = player.Character
	if not character then
		return false
	end

	for _, targetPart in pairs(targetParts) do
		local raycastParams = RaycastParams.new()
		raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
		raycastParams.FilterDescendantsInstances = {player.Character}

		local raycastResult = workspace:Raycast(
			character.Head.Position,
			(targetPart.Position - character.Head.Position).unit * (targetPart.Position - character.Head.Position).magnitude,
			raycastParams
		)

		if raycastResult and raycastResult.Instance == targetPart then
			print(raycastResult)
			return true
		end
	end

	return false
end
local target2 = nil
local function checkHeadDirection()
	for _, player in pairs(game.Players:GetPlayers()) do
		if isPlayerWithinDistance(player) then
			if isPlayerLooking(player) and performRaycast(player) then
				lookedAt = true
				root.Move.Playing = false
				humanoid.WalkSpeed = 0
			else
				lookedAt = false
				if target2 then
					root.Move.Playing = true
				end
				humanoid.WalkSpeed = speed
			end
		else
			lookedAt = false
			if target2 then
				root.Move.Playing = true
			end
			humanoid.WalkSpeed = speed
		end
	end
end

local SimplePath = require(script.SimplePath)

function findNearestTorso(pos)
	local list = game.Workspace:children()
	local torso = nil
	local dist = 10000
	local temp = nil
	local human = nil
	local temp2 = nil
	local player = false
	for x = 1, #list do
		temp2 = list[x]
		if (temp2.className == "Model") and (temp2 ~= character) then
			temp = temp2:findFirstChild("HumanoidRootPart")
			human = temp2:findFirstChild("Humanoid")
			if (temp ~= nil) and (human ~= nil) and (human.Health > 0) then
				if game.Players:GetPlayerFromCharacter(temp2) and (temp.Position - pos).magnitude < dist then
					torso = temp
					dist = (temp.Position - pos).magnitude
				end
			end
		end
	end
	return torso
end

local Goal = Vector3.new()
local Path = SimplePath.new(character)

Path.Visualize = false

Path.Blocked:Connect(function()
	Path:Run(Goal)
end)

Path.WaypointReached:Connect(function()
	Path:Run(Goal)
end)

Path.Error:Connect(function(errorType)
	Path:Run(Goal)
end)

local RunService = game:GetService("RunService")
RunService.Heartbeat:Connect(function()
	checkHeadDirection()
	local target = findNearestTorso(root.Position)
	target2 = target
	if target and target.Parent:FindFirstChild("Humanoid") then
		if target.Parent.Humanoid.Health ~= 0 then
			local distance = (target.Position - root.Position).magnitude
			Goal = target.Position	
			Path:Run(Goal)
			if distance <= 3 and lookedAt == false then
				target.Parent.Humanoid.Health = 0
				root.Snap:Play()
			end
		end
	end
end)

Try this

local function checkPlayers()
	for i,player in pairs(game.Players:GetPlayers()) do
		if isPlayerLooking(player) and performRaycast(player) then return true end
	end
	return false
end

local function checkHeadDirection()
	if isPlayerWithinDistance(player) then
		if checkPlayers() then
			lookedAt = true
			root.Move.Playing = false
			humanoid.WalkSpeed = 0
		else
			lookedAt = false
			if target2 then
				root.Move.Playing = true
			end
			humanoid.WalkSpeed = speed
		end
	else
		lookedAt = false
		if target2 then
			root.Move.Playing = true
		end
		humanoid.WalkSpeed = speed
	end
end

This doesn’t seem to give the function ‘isPlayerWithinDistance’ a valid instance for player however.

Oh right, you can do this without player:

local function isPlayerWithinDistance()
	for i, player in pairs(game.Players:GetPlayers()) do
		local character = player.Character
		if not character then continue end

		for _, targetPart in pairs(targetParts) do
			local distance = (targetPart.Position - character.Head.Position).magnitude
			if distance <= detectionDistance then
				return true
			end
		end
	end
	return false
end