NPC pathfinding malfunctions

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!

I have a module script with two functions. I want my NPC to travel randomly but when a remote is fired itll directly stop the random path traveling IMMEDIATELY and head to me. When the walkTo function is called it sets a variable TargetSpotted to true. And the random path traveling is a while true do loop which first checks if TargetSpotted is true to break the loop immediately, and this leads to my issue.

  1. What is the issue? Include screenshots / videos if possible!

So basically there is a remote that when called, it is supposed to call the npc to me and stop the random path traveling IMMEDIATELY. Except, for some reason, the NPC finishes traveling a waypoint, goes back, or does whatever the heck in the video shown, instead of going DIRECTLY to me like supposed to. Why is this? Can someone break down why and know how to fix?

Video:
robloxapp-20241222-2058443.wmv (583.1 KB)

Output of what happened in video:

this is my MODULE script im using (Ignore block comments)

local module = {}

local PathfindingService = game:GetService("PathfindingService")

local Path = PathfindingService:CreatePath()

local TargetSpotted = false

module.walkTo = function(character : Model, endpos: Part)
	TargetSpotted = true

	
	local humanoid: Humanoid = character.Humanoid
	local humanoidRootPart = character.HumanoidRootPart

	local maxRetries = 5
	local currentRetry = 1
	local Success, Result


	local reachedEvent
	local pathBlockedEvent

	repeat 

		Success, Result = pcall(Path.ComputeAsync, Path, humanoidRootPart.Position, endpos)

	until Success or currentRetry == maxRetries


	if not Success then
		print(Result)
		return
	end

	if Path.Status ~= Enum.PathStatus.Success then
		print("Path could not be generated.")
		return
	end


	local Waypoints = Path:GetWaypoints()
	local CurrentWaypointIndex = 1 -- dw ik 

	for i, v in Waypoints do
		local Sphere = Instance.new("Part")
		Sphere.Shape = Enum.PartType.Ball
		Sphere.Position = v.Position
		Sphere.Anchored = true
		Sphere.CanCollide = false
		Sphere.BrickColor = BrickColor.new("Baby blue")
		Sphere.Material = Enum.Material.SmoothPlastic
		Sphere.Parent = workspace
	end

	-- R u teasing me rn lol
	-- lololol gg ez
	local function MoveToNextWaypoint()
		CurrentWaypointIndex += 1

		humanoid:MoveTo(Waypoints[CurrentWaypointIndex].Position)
		if Waypoints[CurrentWaypointIndex].Action == Enum.PathWaypointAction.Jump then
			humanoid.Jump = true
		end
	end

	local function onReached(reached)
		if reached and CurrentWaypointIndex < #Waypoints then
			MoveToNextWaypoint()
			print("Next waypoint")	
		else
			reachedEvent:Disconnect()
			reachedEvent = nil 
			pathBlockedEvent:Disconnect()
			pathBlockedEvent = nil
		end
	end

	if reachedEvent == nil then
		reachedEvent = humanoid.MoveToFinished:Connect(onReached)
	end

	pathBlockedEvent = Path.Blocked:Connect(function(waypointBlocked)

		if waypointBlocked > CurrentWaypointIndex then
			pathBlockedEvent:Disconnect()

			-- We r gonna assign it a new connection anyway cuz this function is gonna be called again
			-- when u recurse the function pathBlockedEvent is gona be assigned a new value

			print("Generating new path...")
		end




	end)

	MoveToNextWaypoint()
end



module.RandomTraveling = function(character: Model, WaypointsFolder : Folder)
	local humanoid = character.Humanoid
	local humanoidRootPart = character.HumanoidRootPart
	local WaypointsFolder = workspace.Waypoints

	-- i defined it here but we dont need it cuz we're only using it here

	while true do
		if TargetSpotted then
			print("Target Spotted, breaking the random traveling")
			break
		end
		
		local children = WaypointsFolder:GetChildren() -- array of waypoints
		for index = 1, #children do -- length of the array of waypoints
			local randomWaypoint = children[math.random(1, #children)] -- random index

			humanoid:MoveTo(randomWaypoint.Position)
			humanoid.MoveToFinished:Wait()
		end
	end
end


return module



The script calling the module if needed:

local PathModule = require(game.ReplicatedStorage.PathModule)
local CallAgentRemote = game.ReplicatedStorage.CallAgent

CallAgentRemote.OnServerEvent:Connect(function(player : Player)
	PathModule.walkTo(workspace.Agent, player.Character.HumanoidRootPart.Position)

end)

PathModule.RandomTraveling(workspace.Agent, workspace.Waypoints)

I believe your problem lies with your usage of MoveToFinished:Wait().

The script will wait until the humanoid has reached a waypoint (its current waypoint trajectory) before going to you.

Setting TargetSpotted—which should be camelCase to align with your naming convention—does stop your while loop, but not its inner numerical for loop. Since no code exists beyond this loop, move your check to within it to stay more up-to-date with the state of TargetSpotted

while true do
	local waypoints = WaypointsFolder:GetChildren()

	for _ = 1, #waypoints do
        if targetSpotted then
			print("Target Spotted, breaking the random travelling.")

			break
		end

        -- Ensure the NPC travels to a different waypoint.
		local randomWaypoint = table.remove(waypoints, math.random(#waypoints))

		humanoid:MoveTo(randomWaypoint.Position)
		humanoid.MoveToFinished:Wait()
	end
end

With more complex logic, to avoid having to place duplicate checks at yielding points, you can encapsulate your logic in a to-be-cancelled thread:

function module.randomTraveling(character: Model, WaypointsFolder: Folder) -- You misspelt "travelling".
    if module.randomTravellingThread then
        return
    end

    module.randomTravellingThread = task.defer(function()
        while true do
			local waypoints = WaypointsFolder:GetChildren()
		
			for _ = 1, #waypoints do
				local randomWaypoint = table.remove(waypoints, math.random(#waypoints))
		
				humanoid:MoveTo(randomWaypoint.Position)
				humanoid.MoveToFinished:Wait()
			end
		end
    end)
end
function module.walkTo(character: Model, endpos: Part)
    if module.randomTravellingThread then
        task.cancel(module.randomTravellingThread)
    end

    -- ...
end