Need help with NPCs with Moods/Needs

  1. What do you want to achieve? Keep it simple and clear!
    I’m trying to make an NPC system with moods/needs such as hunger, thirst, cleanliness etc… The NPC will walk to the part/object that’ll fulfill that need and then if all needs are fine they’ll just walk around randomly

  2. What is the issue? Include screenshots / videos if possible!
    No idea on where to start, and pathfinding seems to be broken for me. I haven’t had to use pathfinding until now and it’s like there’s nothing about it.

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I’ve tried to make it, but it the NPC keeps going in a straight line and not to the destination. I even tried a script right off the developer hub and it still didn’t work. Also checked YT and dev forums / other sites and found nothing.

The main module will start everything up, so it’ll make the moods with the moods module and then will go to the pathfinding module.

main prisoner module

local prisonerHandler = {}

local moods = require(script.moodInterface)
local pathfinding = require(script.pathfinding)

task.spawn(function()
	for _, prisoner in pairs(workspace.PrisonOperator_Main.TestingPlot.Prisoners:GetChildren()) do
		local hrp:Part = prisoner:FindFirstChild("HumanoidRootPart")
		hrp:SetNetworkOwner(nil)
		moods:CreateMoods(prisoner)
		wait(0.25)
		pathfinding:Start(prisoner)
	end	
end)

return prisonerHandler

pathfinding submodule

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

local path:Path = PathfindingService:CreatePath({
	Costs = {
		Water = 20
	}
})

local TEST_DESTINATION = Vector3.new(workspace.PrisonOperator_Main.TestingPlot.Showers.ShowerA.Position)

local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection

local function followPath(destination, character, humanoid)
	-- Compute the path
	local success, errorMessage = pcall(function()
		path:ComputeAsync(character.PrimaryPart.Position, destination)
	end)

	if success and path.Status == Enum.PathStatus.Success then
		-- Get the path waypoints
		waypoints = path:GetWaypoints()
		
		for i, waypoint:PathWaypoint in pairs(waypoints) do

			local part = Instance.new("Part")
			part.Shape = Enum.PartType.Ball
			part.Color = Color3.fromRGB(255,0,0)
			part.Anchored = true
			part.Transparency = 0.5
			part.CanCollide = false
			part.Position = waypoint.Position
			part.Parent = workspace.Waypoints

			character.Humanoid.WalkSpeed = character:GetAttribute("WalkSpeed")
			character.Humanoid:MoveTo(waypoint.Position)
			character.Humanoid.MoveToFinished:Wait()
			game.Debris:AddItem(part, 2)
		end

		-- Detect if path becomes blocked
		blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
			-- Check if the obstacle is further down the path
			if blockedWaypointIndex >= nextWaypointIndex then
				-- Stop detecting path blockage until path is re-computed
				blockedConnection:Disconnect()
				-- Call function to re-compute new path
				followPath(destination)
			end
		end)

		-- Detect when movement to next waypoint is complete
		if not reachedConnection then
			reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
				if reached and nextWaypointIndex < #waypoints then
					-- Increase waypoint index and move to next waypoint
					nextWaypointIndex += 1
					humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
				else
					reachedConnection:Disconnect()
					blockedConnection:Disconnect()
				end
			end)
		end

		-- Initially move to second waypoint (first waypoint is path start; skip it)
		nextWaypointIndex = 2
		humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
	else
		warn("Path not computed!", errorMessage)
	end
end

local pathfinding = {}

function pathfinding:Start(prisoner:Model)
	followPath(TEST_DESTINATION, prisoner, prisoner:FindFirstChildOfClass("Humanoid"))
end

return pathfinding

mood module

local ServerStorage = game:GetService("ServerStorage")
local ScriptAssets = ServerStorage.ScriptAssets

local moodModule = {}

function moodModule:StartMoodDecay(Character:Model, moodInterface)
	task.spawn(function()
		while true do
			wait(0.25) -- change to 5 later
			local moodsFolder = Character:FindFirstChild("Moods")
			for _, mood:IntValue in pairs(moodsFolder:GetChildren()) do
				if mood.Name ~= "Happiness" then
					if mood.Value > 0 then
						mood.Value -= math.random(1,5)
					else
						mood.Value = 0
					end
				end
			end
			moodModule:LoadMoodInterface(moodInterface, moodsFolder)
		end
	end)
end

function moodModule:CreateMoods(Character:Model)
	if Character:FindFirstChildOfClass("Humanoid") ~= nil then
		local moodInterface = ScriptAssets.MoodInterface:Clone()
		local moodsFolder = ScriptAssets.Moods:Clone() 

		moodsFolder.Parent = Character
		moodInterface.Parent = Character:FindFirstChild("HumanoidRootPart")

		moodModule:StartMoodDecay(Character, moodInterface)
	end
end

function moodModule:LoadMoodInterface(moodInterface:BillboardGui, moodsFolder:Configuration)
	moodInterface.Frame.BladderLabel.Text = moodsFolder.Bladder.Value
	moodInterface.Frame.CleanLabel.Text = moodsFolder.Cleanliness.Value
end

return moodModule

mb if something is wrong, it’s my first time posting, i’ll try to respond as fast as possible if you have any questions

2 Likes

solved it myself sigh it was a difficult fix

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.