Ai Becomes Stuck on Character Respawn

I didn’t script this entirely, my friend helped me.

Ai Script:

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

local Ai = script.Parent
local primPart = Ai.PrimaryPart
local humanoid = Ai:WaitForChild("Ai")
local AI_Config = require(ReplicatedStorage:WaitForChild("AI_Config"))

local UpdateSpeed_Event = ReplicatedStorage:WaitForChild("UpdateSpeed")

-- Disable client network ownership for all parts of the AI model
for _, part in ipairs(Ai:GetDescendants()) do
	if part:IsA("BasePart") then
		part:SetNetworkOwner(nil)
	end
end

humanoid.WalkSpeed = AI_Config.minSpeed
local lastPosition = primPart.Position
local stuckCounter = 0
local stuck = false
local chasing = false
local obstructed = true
local attacked = true
local attackCount = 0
local attackCooldown = 1

-- Get closest visible target
local function GetClosestTarget(maxDistance)
	local closest, shortestDistance = nil, maxDistance
	local forwardVector = primPart.CFrame.LookVector
	local fovAngle = math.rad(AI_Config.range)
	local blindSpotRange = AI_Config.nearRange

	for _, player in ipairs(Players:GetPlayers()) do
		local character = player.Character
		local root = character and character:FindFirstChild("HumanoidRootPart")
		local humanoidChar = character and character:FindFirstChildOfClass("Humanoid")

		if root and humanoidChar and humanoidChar.Health > 0 then
			local direction = root.Position - primPart.Position
			local dist = direction.Magnitude

			if dist <= maxDistance then
				local inBlindSpot = dist <= blindSpotRange
				local angle = math.acos(forwardVector:Dot(direction.Unit))

				if inBlindSpot or angle <= fovAngle then
					if dist < shortestDistance then
						shortestDistance = dist
						closest = character
					end
				end
			end
		end
	end

	return closest, shortestDistance
end

-- Check line-of-sight obstruction
local function CheckObstruction(target)
	if not target then return true end
	local targetRoot = target:FindFirstChild("HumanoidRootPart") or target.PrimaryPart
	if not targetRoot then return true end

	local rayParams = RaycastParams.new()
	rayParams.FilterDescendantsInstances = {Ai, target}
	rayParams.FilterType = Enum.RaycastFilterType.Exclude

	local direction = (targetRoot.Position - primPart.Position)
	local result = workspace:Raycast(primPart.Position, direction.Unit * direction.Magnitude, rayParams)

	-- If raycast hit something, obstruction exists
	return result ~= nil
end

-- Path follow logic
local function Follow(target)
	local targetRoot = target.PrimaryPart or target:FindFirstChild("HumanoidRootPart")
	if not targetRoot then return end
	
	local targetHumanoid = target:FindFirstChildWhichIsA("Humanoid")
	if not targetHumanoid or targetHumanoid.Health <= 0 then return end

	local destination = targetRoot.Position
	local path = PathfindingService:CreatePath({
		AgentRadius = 2,
		AgentHeight = 5,
		AgentCanJump = true,
		AgentJumpHeight = 7,
		AgentMaxSlope = 45,
		Costs = {Climb = 0}
	})

	local success, err = pcall(function()
		path:ComputeAsync(primPart.Position, destination)
	end)

	if success and path.Status == Enum.PathStatus.Success then
		local waypoints = path:GetWaypoints()

		if workspace:FindFirstChild("Waypoints") then
			for _, w in ipairs(workspace.Waypoints:GetChildren()) do
				w:Destroy()
			end

			for _, waypoint in ipairs(waypoints) do
				local part = Instance.new("Part")
				part.Name = "Waypoint"
				part.Shape = Enum.PartType.Ball
				part.Material = Enum.Material.Neon
				part.Size = Vector3.new(0.6, 0.6, 0.6)
				part.Position = waypoint.Position + Vector3.new(0, 2, 0)
				part.Anchored = true
				part.CanCollide = false
				part.Parent = workspace.Waypoints
			end
		end

		for _, waypoint in ipairs(waypoints) do
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				humanoid.Jump = true
			end

			local dist = (waypoint.Position - primPart.Position).Magnitude
			local timeout = math.clamp(dist / humanoid.WalkSpeed, 1, 4)

			humanoid:MoveTo(waypoint.Position)
			local reached = humanoid.MoveToFinished:Wait(timeout)
			if not reached or stuck then
				print("[AI] Stopping path following due to stuck or failure")
				break
			end
		end
	else
		warn("[AI] Pathfinding failed for " .. Ai.Name .. " to " .. target.Name .. ": " .. (err or tostring(path.Status)))
	end

	if (primPart.Position - lastPosition).Magnitude < 1 then
		stuckCounter += 1
	else
		stuckCounter = 0
	end

	stuck = stuckCounter > 10
	lastPosition = primPart.Position

	if stuck then
		warn("[AI] " .. Ai.Name .. " might be stuck at " .. tostring(primPart.Position) .. " (stuckCounter = " .. stuckCounter .. ")")
	end
end

local function AttemptAttack(target)
	if not attacked then return end
	attacked = false

	local targetHumanoid = target:FindFirstChildWhichIsA("Humanoid")
	if not targetHumanoid or target:FindFirstChildWhichIsA("ForceField") then
		attacked = true
		return
	end

	targetHumanoid:TakeDamage(AI_Config.damage)
	attackCount = math.clamp(attackCount + 1, 0, 3)
	humanoid.WalkSpeed = math.max(AI_Config.minSpeed, AI_Config.maxSpeed - attackCount)

	local player = Players:GetPlayerFromCharacter(target)
	if player then
		UpdateSpeed_Event:FireClient(player, false, true)
	end

	print(string.format("[AI] Attacked %s! Attack count: %d, AI WalkSpeed: %.2f", target.Name, attackCount, humanoid.WalkSpeed))

	if targetHumanoid.Health > 0 then
		local boost = math.floor((targetHumanoid.MaxHealth - targetHumanoid.Health) / 10 + 10)
		targetHumanoid.WalkSpeed += boost

		local ff = Instance.new("ForceField")
		ff.Visible = false
		ff.Parent = target

		ff.Destroying:Once(function()
			targetHumanoid.WalkSpeed -= boost
			if player then
				UpdateSpeed_Event:FireClient(player, true, false)
			end
		end)

		Debris:AddItem(ff, 5)
	end

	task.delay(attackCooldown, function()
		attacked = true
		print("[AI] Attack cooldown ended, can attack again.")
	end)
end

RunService.Stepped:Connect(function()
	local target = GetClosestTarget(AI_Config.range)
	if target then
		obstructed = CheckObstruction(target)
	else
		obstructed = true
	end
	print(string.format("[AI] Obstructed: %s, Chasing: %s, Stuck: %s", tostring(obstructed), tostring(chasing), tostring(stuck)))
end)

task.spawn(function()
	while true do
		task.wait(0.05)
		if stuck then
			local posStr = string.format("(%.2f, %.2f, %.2f)", primPart.Position.X, primPart.Position.Y, primPart.Position.Z)
			warn("[AI] " .. Ai.Name .. " might be stuck at " .. posStr)
			local target = GetClosestTarget(AI_Config.range)
			if target then
				Follow(target)
			end
		end
	end
end)

task.spawn(function()
	while task.wait() do
		if chasing then
			humanoid.WalkSpeed = math.min(humanoid.WalkSpeed + AI_Config.speedIncreaseRate, AI_Config.maxSpeed)
		else
			humanoid.WalkSpeed = math.max(humanoid.WalkSpeed - AI_Config.speedIncreaseRate, AI_Config.minSpeed)
		end
	end
end)

task.delay(2, function()
	while task.wait() do
		local target, distance = GetClosestTarget(AI_Config.range)
		if target then
			local humanoidChar = target:FindFirstChildWhichIsA("Humanoid")
			if humanoidChar and humanoidChar.Health > 0 then
				chasing = distance <= AI_Config.chaseRange
				print(string.format("[AI] Target: %s, Distance: %.2f, Chasing: %s", target.Name, distance, tostring(chasing)))
				Follow(target)
				if distance <= AI_Config.attackRange then
					AttemptAttack(target)
				end
			else
				chasing = false
			end
		else
			chasing = false
			print("[AI] No target found, idle.")
			humanoid:MoveTo(primPart.Position)
		end
	end
end)

I do apologies if the script is unorganised.

AI_Config Module:

return {
	range = math.huge,
	nearRange = 80,
	chaseRange = 130,

	minSpeed = 5,
	maxSpeed = 29,
	speedIncreaseRate = 1,

	attackRange = 5,
	attackCount = 0,
	damage = 30,
}


Doing too much. Classic coding mistake. To fix this you remove all of the confusing nonsense from your code and refine it down to what you need so you can figure out why your script doesn’t work. I’ll guide you in the right direction and neaten up your script a bit.

local Players = game:GetService("Players")

local PathfindingService = game:GetService("PathfindingService")

local RunService = game:GetService("RunService")

local Debris = game:GetService("Debris")

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Ai = script.Parent

local primPart = Ai.PrimaryPart

local humanoid = Ai:WaitForChild("Ai")

local AI_Config = require(ReplicatedStorage:WaitForChild("AI_Config"))

local UpdateSpeed_Event = ReplicatedStorage:WaitForChild("UpdateSpeed")

-- Disable client network ownership for all parts of the AI model

for _, part in ipairs(Ai:GetDescendants()) do

if part:IsA("BasePart") then

part:SetNetworkOwner(nil)

end

end

humanoid.WalkSpeed = AI_Config.minSpeed

local lastPosition = primPart.Position

local attacked = true

local attackCount = 0

local attackCooldown = 1

-- Get closest visible target

local function GetClosestTarget(maxDistance)

local closest, shortestDistance = nil, maxDistance

local forwardVector = primPart.CFrame.LookVector

local fovAngle = math.rad(AI_Config.range)

local blindSpotRange = AI_Config.nearRange

for _, player in ipairs(Players:GetPlayers()) do

local character = player.Character

local root = character and character:FindFirstChild("HumanoidRootPart")

local humanoidChar = character and character:FindFirstChildOfClass("Humanoid")

if root and humanoidChar and humanoidChar.Health > 0 then

local direction = root.Position - primPart.Position

local dist = direction.Magnitude

if dist <= maxDistance then

local inBlindSpot = dist <= blindSpotRange

local angle = math.acos(forwardVector:Dot(direction.Unit))

if inBlindSpot or angle <= fovAngle then

if dist < shortestDistance then

shortestDistance = dist

closest = character

end

end

end

end

end

return closest, shortestDistance

end

-- Check line-of-sight obstruction

local function CheckObstruction(target)

if not target then return true end

local targetRoot = target:FindFirstChild("HumanoidRootPart") or target.PrimaryPart

if not targetRoot then return true end

local rayParams = RaycastParams.new()

rayParams.FilterDescendantsInstances = {Ai, target}

rayParams.FilterType = Enum.RaycastFilterType.Exclude

local direction = (targetRoot.Position - primPart.Position)

local result = workspace:Raycast(primPart.Position, direction.Unit * direction.Magnitude, rayParams)

-- If raycast hit something, obstruction exists

return result ~= nil

end

-- Path follow logic

local function Follow(target)

local targetRoot = target.PrimaryPart or target:FindFirstChild("HumanoidRootPart")

if not targetRoot then return end

local targetHumanoid = target:FindFirstChildWhichIsA("Humanoid")

if not targetHumanoid or targetHumanoid.Health <= 0 then return end

local destination = targetRoot.Position

local path = PathfindingService:CreatePath({

AgentRadius = 2,

AgentHeight = 5,

AgentCanJump = true,

AgentJumpHeight = 7,

AgentMaxSlope = 45,

Costs = {Climb = 0}

})

local success, err = pcall(function()

path:ComputeAsync(primPart.Position, destination)

end)

if success and path.Status == Enum.PathStatus.Success then

local waypoints = path:GetWaypoints()

if workspace:FindFirstChild("Waypoints") then

for _, w in ipairs(workspace.Waypoints:GetChildren()) do

w:Destroy()

end

for _, waypoint in ipairs(waypoints) do

local part = Instance.new("Part")

part.Name = "Waypoint"

part.Shape = Enum.PartType.Ball

part.Material = Enum.Material.Neon

part.Size = Vector3.new(0.6, 0.6, 0.6)

part.Position = waypoint.Position + Vector3.new(0, 2, 0)

part.Anchored = true

part.CanCollide = false

part.Parent = workspace.Waypoints

end

end

for _, waypoint in ipairs(waypoints) do

if waypoint.Action == Enum.PathWaypointAction.Jump then

humanoid.Jump = true

end

local dist = (waypoint.Position - primPart.Position).Magnitude

local timeout = math.clamp(dist / humanoid.WalkSpeed, 1, 4)

humanoid:MoveTo(waypoint.Position)

local reached = humanoid.MoveToFinished:Wait(timeout)

if not reached then

print("[AI] Stopping path following due to the humanoid not reaching its destination")

break

end

end

else

warn("[AI] Pathfinding failed for " .. Ai.Name .. " to " .. target.Name .. ": " .. (err or tostring(path.Status)))

end

if (primPart.Position - lastPosition).Magnitude < 1 then

print("Clearly stuck")

end

lastPosition = primPart.Position

end

local function AttemptAttack(target)

if not attacked then return end

attacked = false

local targetHumanoid = target:FindFirstChildWhichIsA("Humanoid")

if not targetHumanoid or target:FindFirstChildWhichIsA("ForceField") then

attacked = true

return

end

targetHumanoid:TakeDamage(AI_Config.damage)

attackCount = math.clamp(attackCount + 1, 0, 3)

humanoid.WalkSpeed = math.max(AI_Config.minSpeed, AI_Config.maxSpeed - attackCount)

local player = Players:GetPlayerFromCharacter(target)

if player then

UpdateSpeed_Event:FireClient(player, false, true)

end

print(string.format("[AI] Attacked %s! Attack count: %d, AI WalkSpeed: %.2f", target.Name, attackCount, humanoid.WalkSpeed))

if targetHumanoid.Health > 0 then

local boost = math.floor((targetHumanoid.MaxHealth - targetHumanoid.Health) / 10 + 10)

targetHumanoid.WalkSpeed += boost

local ff = Instance.new("ForceField")

ff.Visible = false

ff.Parent = target

ff.Destroying:Once(function()

targetHumanoid.WalkSpeed -= boost

if player then

UpdateSpeed_Event:FireClient(player, true, false)

end

end)

Debris:AddItem(ff, 5)

end

task.delay(attackCooldown, function()

attacked = true

print("[AI] Attack cooldown ended, can attack again.")

end)

end

RunService.Stepped:Connect(function()

local target = GetClosestTarget(AI_Config.range)

if target then

print("The target exists")

end

end)

task.spawn(function()

while task.wait() do

if chasing then

humanoid.WalkSpeed = math.min(humanoid.WalkSpeed + AI_Config.speedIncreaseRate, AI_Config.maxSpeed)

else

humanoid.WalkSpeed = math.max(humanoid.WalkSpeed - AI_Config.speedIncreaseRate, AI_Config.minSpeed)

end

end

end)

task.delay(2, function()

while task.wait() do

local target, distance = GetClosestTarget(AI_Config.range)

if target then

local humanoidChar = target:FindFirstChildWhichIsA("Humanoid")

if humanoidChar and humanoidChar.Health > 0 then

chasing = distance <= AI_Config.chaseRange

print(string.format("[AI] Target: %s, Distance: %.2f, Chasing: %s", target.Name, distance, tostring(chasing)))

Follow(target)

if distance <= AI_Config.attackRange then

AttemptAttack(target)

end

else

chasing = false

end

else

chasing = false

print("[AI] No target found, idle.")

humanoid:MoveTo(primPart.Position)

end

end

end)