Trouble with Targeting Specific Players in NPC Script

I am encountering difficulties with properly targeting specific players in my NPC script. The script is designed to identify and chase down the nearest player if they are in the TargetPlayers table. However, it seems to encounter issues when attempting to target and attack with these specific players, leading to unexpected behavior.

Upon investigation, I have observed that the script occasionally returns nil for the nearest player, even when there are valid targets present. This unexpected behavior prevents the NPC from initiating the intended actions towards the target, causing disruptions in the gameplay experience, and soft-locking the entire server. Yeah.

I have thoroughly reviewed the script, including the targeting logic and pathfinding implementation, but have been unable to identify the root cause of this issue. I am seeking guidance and advice from the community to help me resolve this problem and ensure the NPC can properly target and interact with the intended players.

Here is my code for checking the nearest player:

local SCP096 = script.Parent.Parent
local PathfindingModule = require(SCP096.PathfindingModule)

local Pathfinder = SCP096

local function FindNearestPlayer()
	if #shared.TargetPlayers ~= 0 then --// Make sure the table isn't empty
		local found = nil
		local last = math.huge
		for _, Player in pairs(shared.TargetPlayers) do --// Get all of the TargetPlayers
			local Character = Player.Character

			if Character and Character.Humanoid.Health ~= 0 then --// Make sure their character exists and isn't dead
				local distance = Player:DistanceFromCharacter(Character.HumanoidRootPart.Position)
				if distance < last then
					found = Player
					last = distance
					
					print("Nearest player found. Their name is", Player.Name) --// Debugging stuff
				end
			end
		end

		if found ~= nil then --// Everything works properly
			shared.NearestPlayer = found
			print("The nearest player is", last, "away. Their name is", found.Name)
			local Goal = found and found.Character and found.Character.HumanoidRootPart
			return Goal
		else --// Breaks the entire NPC
			print("No nearest player found.")
			return false
		end
	end
end


while true do
	local Path = PathfindingModule.new(Pathfinder)

	while true do
		local Goal = FindNearestPlayer()

		if Goal ~= false then --//Pathfind
			Path:Run(Goal)
		end
	end
end

Here are the “errors” i get:

It prints “No nearest player found.” until roblox literally force stops the script. This happens very rarely, and I have no idea what is causing it. Any help is appreciated.

Script that adds player to TargetPlayers table
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RemoteEvent = ReplicatedStorage:WaitForChild("Viewed")

local SCP096 = game.Workspace:WaitForChild("SCP-096")

local IsLookingBoolean = SCP096.IsLooking
IsLookingBoolean.Value = false

shared.TargetPlayers = {}

RemoteEvent.OnServerEvent:Connect(function(Player)
	if not table.find(shared.TargetPlayers, Player) then
		local TargetBoolean = Instance.new("BoolValue")
		TargetBoolean.Name = "Target"
		TargetBoolean.Value = true
		TargetBoolean.Parent = Player.Character

		table.insert(shared.TargetPlayers, Player)

		IsLookingBoolean.Value = true
	end
end)
1 Like

If I understood your code correctly, right here you’re getting the distance between the player’s head and the player’s HumanoidRootPart.

local distance = Player:DistanceFromCharacter(Character.HumanoidRootPart.Position)

I assume you’re trying to get the distance between the player’s character and the SCP’s character, something like this:

local distance = Player:DistanceFromCharacter(SCP096.Head.Position)

Also as for the loops at the bottom of your code, you should add some sort of wait and terminating conditions. The reason your script is exhausting the allowed execution time is because the while true do loops run indefinitely as fast as possible due to lack of wait.

Something like this?

--assuming that "SCP096" is the SCP’s character
local RunService = game:GetService("RunService")
while SCP096 and SCP096.Parent do -- maybe another "and SCP096:FindFirstChild("Humanoid") and SCP096.Humanoid.Health > 0" incase you want a health check.
	local Path = PathfindingModule.new(Pathfinder) --don’t know how I feel about creating a new PathFinding object thingy every loop but 🤷‍♂️

	--not sure what the additional nested while loop was for so I removed it, please correct me if I was wrong

	local Goal = FindNearestPlayer()

	if Goal ~= false then --//Pathfind
		Path:Run(Goal)
		-- some nifty pseudo code here
		Path.Completed:Wait
		--attack or whatever code here
	end

	RunService.Heartbeat:Wait()
end

I think you misunderstood what my problem is. The problem is not my loop getting exhausted, it only does that when this unknown problem occurs. Plus, I cannot have a wait in my loop because it would look bad to have the pathfinder run to where the character was and not where they are.

I have some code to check if a player reset the game or left, but after a seemingly random amount of kills it prints “TargetBoolean not found. Stopped pathfinding.” even when the script is disabled. After that, the next time the SCP is chasing me, right when he’s about to kill, he freezes in place and breaks the game.

Here is my code for checking if the player left the game:

local SCP096 = script.Parent.Parent

local ShouldEatBoolean = SCP096:WaitForChild("ShouldEat")
local IsTouchingTargetBoolean = SCP096:WaitForChild("IsTouchingTarget")

local TargetBoolean = nil

local function StopPathfinding()
	TargetBoolean = nil
	ShouldEatBoolean.Value = false
	IsTouchingTargetBoolean.Value = true
end

local function removePlayer(player)
	for index, p in ipairs(shared.TargetPlayers) do
		if p == player then
			print("Player", player, "removed.")
			
			table.remove(shared.TargetPlayers, index)
			break
		end
	end
end

local function hasMorePlayers()
	return #shared.TargetPlayers > 0
end

while true do
	task.wait(1)
	
	TargetBoolean = nil
	local Player = nil
	Player = shared.NearestPlayer

	if Player ~= nil then
		local Character = Player.Character
		if Character:FindFirstChild("Target") then
			TargetBoolean = Character:FindFirstChild("Target")
		end
	end

	if TargetBoolean == nil then
		print("TargetBoolean not found. Stopped pathfinding.")

		table.clear(shared.TargetPlayers)
		
		StopPathfinding()
	end

	if TargetBoolean then
		local player = Player
		if player then
			player.CharacterRemoving:Connect(function()
				print("Player reset detected. Removed from table.")

				local PlayerToRemove = Player
				removePlayer(PlayerToRemove)

				if not hasMorePlayers() then
					StopPathfinding()
				end
			end)
		end
	end
end

This seems to be what is causing the issue, but I don’t know what part of it is.

Temporarily, I recommend adding a wait() if no players are found to prevent script exhaustion.

I recommend changing your pairs(shared.TargetPlayers) to ipairs(shared.TargetPlayers), but I’m not sure if this would fix the issue.

I also noticed another issue with your script, which is in this line:

Goal will never be false, it will only be nil. Consider changing it to if Goal then.

1 Like

I will do that, but theoretically this code shouldn’t even run if there are no players: if #shared.TargetPlayers > 0 then

I changed it to ipairs and it seemed to actually be harder to break, but that could have been placebo.

And lastly, I took your advice on the if goal then.