NPC pathfinding AI Dynamically

32 Views Asked by At

My NPC goes to my old position and then computes a new path to my position ater it reached the last waypoint.

local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local PathfindingService = game:GetService("PathfindingService")

local mom = script.Parent
local humanoid = mom:WaitForChild("Humanoid")
local torso = mom:WaitForChild("Torso")
humanoid.WalkSpeed = 20

local startingPart = Workspace:WaitForChild("StartingPart")
local roomPart = Workspace:WaitForChild("RoomPart")
local stopPart = Workspace.stopPart

local stopMoving = false
local blacklistedPlayers = {}
local nearStartingPart = false

local path = nil
local waypoints = nil
local visualize = true
local folder = nil

if visualize then
    folder = Instance.new("Folder")
    folder.Name = "Waypoints"
    folder.Parent = workspace
end

local function isPlayerBlacklisted(player)
    for _, blacklistedPlayer in ipairs(blacklistedPlayers) do
        if player == blacklistedPlayer then
            return true
        end
    end
    return false
end

local function addToBlacklist(player)
    table.insert(blacklistedPlayers, player)
end

local function removeFromBlacklist(player)
    for i, blacklistedPlayer in ipairs(blacklistedPlayers) do
        if player == blacklistedPlayer then
            table.remove(blacklistedPlayers, i)
            break
        end
    end
end

local function moveMom(direction)
    if not stopMoving then
        humanoid:Move(direction)
    else
        humanoid:Move(Vector3.new())
    end
end

local function goToPart(part)
    print("Mom is going to", part.Name)

    path = nil

    if visualize then
        for _, child in ipairs(folder:GetChildren()) do
            child:Destroy()
        end
    end

    path = PathfindingService:CreatePath({
        AgentRadius = 2,
        AgentHeight = 5,
        AgentCanJump = true,
        AgentJumpHeight = 10,
    })
    path:ComputeAsync(torso.Position, part.Position)
    waypoints = path:GetWaypoints()

    if visualize then
        for i, waypoint in ipairs(waypoints) do
            local part = Instance.new("Part")
            part.Shape = Enum.PartType.Ball
            part.Color = Color3.fromRGB(255, 90, 61)
            part.Material = Enum.Material.Neon
            part.CFrame = CFrame.new(waypoint.Position)
            part.Name = i
            part.Anchored = true
            part.Size = Vector3.new(1, 1, 1)
            part.CanCollide = false
            part.Parent = folder
        end
    end

    while true do
        for i, waypoint in ipairs(waypoints) do
            if stopMoving or not path:IsValid() then
                return
            end

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

local function nearStopPart()
    local momTorso = mom:FindFirstChild("Torso")
    if momTorso then
        return (momTorso.Position - stopPart.Position).Magnitude < 5
    end
    return false
end

local function shouldStopMoving(catchPlayerFunction)
    for _, playerToCheck in ipairs(Players:GetPlayers()) do
        if playerToCheck.Character and playerToCheck.Character:FindFirstChild("Humanoid") and not playerToCheck.Character:FindFirstChild("Humanoid").SeatPart then
            print(playerToCheck.Name, "unsat while going to StartingPart. Mom will turn back.")

            removeFromBlacklist(playerToCheck)
            humanoid:MoveTo(torso.Position)
            stopMoving = true

            local anyAlivePlayers = false
            for _, player in ipairs(Players:GetPlayers()) do
                if player.Character and player.Character:FindFirstChild("Humanoid") and player.Character:FindFirstChild("Humanoid").Health > 0 then
                    anyAlivePlayers = true
                    if player.Character:FindFirstChild("Humanoid").SeatPart then
                        print(player.Name, "is sitting at RoomPart. Mom will not kill.")
                    else
                        print(player.Name, "unsat at RoomPart. Mom will kill.")
                        catchPlayerFunction(player)
                    end
                end
            end

            if anyAlivePlayers and not nearStartingPart and not nearStopPart() then
                print("Mom is going to RoomPart")
                goToPart(roomPart)
                wait(5)
                print("Finished waiting at RoomPart.")
            end

            local waitStartTime = tick()
            while stopMoving do
                wait(0.1)
                if tick() - waitStartTime > 5 then
                    print("Timeout: Mom waiting to stop moving.")
                    break
                end
            end

            return true
        end
    end
    return false
end

local function updatePath(player)
    -- Update the path to the player's current position
    if not path or not path:IsA("Path") then
        path = PathfindingService:CreatePath({
            AgentRadius = 2,
            AgentHeight = 5,
            AgentCanJump = true,
            AgentJumpHeight = 10,
        })
    end

    local playerRootPart = player.Character:FindFirstChild("HumanoidRootPart")
    if playerRootPart then
        local playerPosition = playerRootPart.Position
        local momPosition = torso.Position

        path:ComputeAsync(momPosition, playerPosition)
        waypoints = path:GetWaypoints()

        if visualize then
            for _, waypoint in ipairs(waypoints) do
                local part = Instance.new("Part")
                part.Shape = Enum.PartType.Ball
                part.Color = Color3.fromRGB(98, 87, 255)
                part.Material = Enum.Material.Neon
                part.CFrame = CFrame.new(waypoint.Position)
                part.Name = "Waypoint"
                part.Anchored = true
                part.Size = Vector3.new(1, 1, 1)
                part.CanCollide = false
                part.Parent = folder
            end
        end
    end
end

local function catchPlayer(player)
    print("Mom is dynamically checking if player is sitting before chasing", player.Name)

    while player.Character and player.Character:FindFirstChild("Humanoid") and player.Character.Humanoid.Health > 0 do
        if player.Character:FindFirstChild("Humanoid").SeatPart then
            print(player.Name, "is sitting. Mom will not chase.")
            break
        end

        updatePath(player)

        for _, waypoint in ipairs(waypoints) do
            humanoid:MoveTo(waypoint.Position)
            humanoid.MoveToFinished:Wait()
        end

        local distanceToPlayer = (torso.Position - player.Character:WaitForChild("HumanoidRootPart").Position).Magnitude
        if distanceToPlayer < 1 then
            print("Touching player:", player.Name)

            if not isPlayerBlacklisted(player) then
                if player.Character:FindFirstChild("Humanoid").Health > 0 then
                    player.Character.Humanoid.Health = 0
                    print("Player killed:", player.Name)

                    addToBlacklist(player)
                    print(player.Name, "added to the blacklist.")
                end
            end
            break
        end

        wait(0.1)
    end
end

while true do
    wait(math.random(5, 10))

    local nonSittingPlayers = {}
    for _, player in ipairs(Players:GetPlayers()) do
        if player.Character and player.Character:FindFirstChild("Humanoid") and not player.Character:FindFirstChild("Humanoid").SeatPart then
            if not isPlayerBlacklisted(player) then
                table.insert(nonSittingPlayers, player)
            end
        end
    end

    if #nonSittingPlayers > 0 then
        print("Non-sitting players detected. Trying to catch them.")
        for _, player in ipairs(nonSittingPlayers) do
            catchPlayer(player)
        end
    end

    local anyAlivePlayers = false
    for _, player in ipairs(Players:GetPlayers()) do
        if player.Character and player.Character:FindFirstChild("Humanoid") and player.Character:FindFirstChild("Humanoid").Health > 0 then
            anyAlivePlayers = true
        end
    end

    if not anyAlivePlayers then
        print("No living players found, going to StartingPart and ending script.")
        goToPart(startingPart)
        break
    end

    if not nearStartingPart and not nearStopPart() then
        goToPart(roomPart)
    end

    local sittingPlayers = false
    for _, player in ipairs(Players:GetPlayers()) do
        if player.Character and player.Character:FindFirstChild("Humanoid") and player.Character.Humanoid.SeatPart then
            sittingPlayers = true
            break
        end
    end

    if nearStopPart() then
        print("Mom is near stopPart, stopping all functions related to going to RoomPart.")
    else
        if sittingPlayers then
            print("Waiting at RoomPart for 5 seconds.")

            local startTime = tick()

            while tick() - startTime < 5 do
                local allSitting = true

                for _, player in ipairs(Players:GetPlayers()) do
                    if player.Character and player.Character:FindFirstChild("Humanoid") and not player.Character:FindFirstChild("Humanoid").SeatPart then
                        allSitting = false
                        break
                    end
                end

                if not allSitting or shouldStopMoving(catchPlayer) then
                    print("Not all players are sitting or someone unsat. Aborting wait at RoomPart.")
                    break
                end

                print(tick() - startTime)
                wait(0.1)
            end

            local allSitting = true
            for _, player in ipairs(Players:GetPlayers()) do
                if player.Character and player.Character:FindFirstChild("Humanoid") and not player.Character:FindFirstChild("Humanoid").SeatPart then
                    allSitting = false
                    break
                end
            end

            if allSitting then
                print("All players are still sitting. Going to StartingPart.")
                goToPart(startingPart)

                while torso.Position ~= startingPart.Position do
                    wait(0.1)

                    if shouldStopMoving(catchPlayer) then
                        break
                    end
                end

                if torso.Position == startingPart.Position then
                    print("Mom reached StartingPart. Ending script.")
                    break
                end
            else
                print("Not all players are sitting. Skipping going to StartingPart.")
            end
        else
            print("No sitting players. Skipping wait at RoomPart.")
        end
    end

    for _, player in ipairs(Players:GetPlayers()) do
        if player.Character and player.Character:FindFirstChild("Humanoid") and not player.Character:FindFirstChild("Humanoid").SeatPart then
            print(player.Name, "unsat at RoomPart. Mom will kill.")
            catchPlayer(player)
        end
    end

    print("Finished waiting at RoomPart.")
    stopMoving = false

    local originalDestination = startingPart.Position
    while humanoid.WalkToPoint ~= Vector3.new() and not nearStartingPart and not nearStopPart() do
        wait(0.1)

        if shouldStopMoving(catchPlayer) then
            break
        end
    end

    if not stopMoving and not nearStartingPart and not nearStopPart() then
        goToPart(startingPart)
    end
end

Video: https://vimeo.com/888253706?share=copy

I tried visualising waypoints and adding checks but I still cant figure it out. I also tried removing: humanoid.MoveToFinished:Wait() but that breaks the script.

0

There are 0 best solutions below