NPC never respawning?

i’m working on a game where you run away from hostile npcs that explode when they get close enough to a player. it was all going well until i tried to implement npc respawns.

when i connect the .Died event to the respawn function, the npc never respawns. i’ve even added print statements, but the one that is supposed to print “npc is no longer alive” before promptly respawning the npc just never prints for some reason.

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

local hostileNpc = script.Parent
local personoid = hostileNpc:FindFirstChildWhichIsA("Humanoid")
local hitbox = hostileNpc.Head -- yeah
local npcRoot = hostileNpc.HumanoidRootPart
local noisemaker = hostileNpc.noisemaker -- invisible part welded to the HumanoidRootPart
local npcClone = hostileNpc:Clone()
local npcFolder = workspace:WaitForChild("current_npcs")

local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection

local lastJumpTime = 0
local isExploding = false -- flag to ensure 'explode' only runs once
npcRoot:SetNetworkOwner(nil)

-- pathfinding constants
local AGENT_CAN_JUMP = true
local AGENT_HEIGHT = hitbox.Size.Y / 2
local AGENT_RADIUS = hitbox.Size.X / 2

local PATH_COSTS = {
	roller = 0,
	Water = 100,
	DangerZone = math.huge
}

-- miscellaneous constants
local DESPAWN_TIME = 5
local EXPLOSION_BLAST_PRESSURE = 1500
local EXPLOSION_BLAST_RADIUS = 25
local EXPLOSION_DISTANCE = 25
local EXPLOSION_TIME = 15
local JUMP_INTERVAL = 2
local JUMP_THRESHOLD = 55  -- distance within which npcs start jumping randomly
local NO_PATH_THRESHOLD = 30 -- stop pathing and use Humanoid:MoveTo()
local RESPAWNS_ENABLED = true
local RESPAWN_TIME = 7
local TARGET_CHECK_INTERVAL = 0.5
local TARGET_SEARCH_RADIUS = 1150
local WAYPOINT_INTERVAL = 5

local path = PathfindingService:CreatePath({
	AgentHeight = AGENT_HEIGHT,
	AgentRadius = AGENT_RADIUS,
	AgentCanJump = AGENT_CAN_JUMP,
	Costs = PATH_COSTS
})

-- explosion & target finding
function explode()
	if isExploding then return end
	isExploding = true

	print(`starting {hostileNpc} explosion timer`)
	task.delay(EXPLOSION_TIME, function()
		print(`{hostileNpc} has exploded!`)
		noisemaker.Parent = workspace
		noisemaker.Anchored = true

		local explosion = Instance.new("Explosion")
		explosion.Position = npcRoot.Position
		explosion.Parent = workspace.Terrain
		explosion.Visible = true
		explosion.Name = `{hostileNpc} explosion!`
		explosion.BlastPressure = EXPLOSION_BLAST_PRESSURE
		explosion.BlastRadius = EXPLOSION_BLAST_RADIUS

		
		hitbox:ApplyImpulse(Vector3.new(0, 88, 0))
		personoid:ChangeState(Enum.HumanoidStateType.Dead)
		
		task.wait(DESPAWN_TIME)
		hostileNpc:Destroy()
	end)
end

function findTarget()
	local nearestPlayer = nil
	local nearestDistance = TARGET_SEARCH_RADIUS

	for _, player in pairs(Players:GetPlayers()) do
		if player.Character and player.Character:FindFirstChild("HumanoidRootPart") then
			local distance = (npcRoot.Position - player.Character.HumanoidRootPart.Position).Magnitude
			if distance < nearestDistance then
				nearestDistance = distance
				nearestPlayer = player
			end
		end
	end

	return nearestPlayer
end

-- pathfinding stuff
function handleBlockage(blockedWayPointIndex)
	if blockedWayPointIndex >= nextWaypointIndex then
		if blockedConnection then
			blockedConnection:Disconnect()
			blockedConnection = nil
		end

		local target = findTarget()
		if target and target.Character then
			local targetRoot = target.Character:FindFirstChild("HumanoidRootPart")
			if targetRoot then
				moveTo(targetRoot.Position)
			end
		end
	end
end

function handleJump()
	if tick() - lastJumpTime > JUMP_INTERVAL then
		if math.random() < 0.5 then -- 50% chance to jump
			personoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		lastJumpTime = tick()
	end
end

function handleWaypoint(reached)
	if reached and nextWaypointIndex < #waypoints then
		nextWaypointIndex = math.min(nextWaypointIndex + WAYPOINT_INTERVAL, #waypoints)
		personoid:MoveTo(waypoints[nextWaypointIndex].Position)
	else
		if blockedConnection then
			blockedConnection:Disconnect()
			blockedConnection = nil
		end
	end
end

function moveTo(destination) -- main pathing function
	local distance = (npcRoot.Position - destination).Magnitude

	if distance <= NO_PATH_THRESHOLD then
		personoid:MoveTo(destination)
		return
	end

	local success, failure = pcall(function()
		path:ComputeAsync(npcRoot.Position, destination)
	end)

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

		if blockedConnection then
			blockedConnection:Disconnect()
		end
		blockedConnection = path.Blocked:Connect(handleBlockage)

		if not reachedConnection then
			reachedConnection = personoid.MoveToFinished:Connect(handleWaypoint)
		end

		nextWaypointIndex = 2 -- start at the second waypoint if it exists
		personoid:MoveTo(waypoints[nextWaypointIndex].Position)
	else
		warn(`{hostileNpc} failed to create a path: {failure}`)
	end
end

-- handle npc death
function respawnNPC()
	if not RESPAWNS_ENABLED then return end
	print(`respawn function called`) -- this prints

	task.delay(RESPAWN_TIME, function()
		print(`{hostileNpc} is no longer alive`) -- this never prints
		npcClone.Parent = npcFolder
		npcClone:MakeJoints()
	end)
end

-- tie everything together
function mainLoop()
	local target = findTarget()
	if target and target.Character then
		local targetRoot = target.Character:FindFirstChild("HumanoidRootPart")
		if targetRoot then
			local distance = (npcRoot.Position - targetRoot.Position).Magnitude
			if distance <= JUMP_THRESHOLD then
				handleJump()
			end

			if distance <= EXPLOSION_DISTANCE then
				explode()
			end
			moveTo(targetRoot.Position)
		end
	end
end

personoid.Died:Connect(respawnNPC) -- never respawns the npc

while task.wait(TARGET_CHECK_INTERVAL) do
	mainLoop()
end

1 Like

I’m unsure whether your NPC died by explosion in this case, but your despawn time is smaller than your respawn time, meaning your hostileNpc will delete itself (including this script that handles respawning). Try to troubleshoot by increasing the despawn time or decreasing the respawn time.

i did find that the npc only respawns if the respawn time is less than the despawn time

2 Likes

Hmm. It does respawn to me.

try this rig:
explosive_rig.rbxm (23.4 KB)

I think all I changed was personoid.Died:Connect() to personoid.Died:Once().

Nevermind, it seems the problem was already solved.

1 Like

An alternative to the response above would be to make one manager script that simply clones every NPC after 7 seconds (or your desired respawn time) once they die.

This could look like:

local npcFolder = workspace["current_npcs"]
local RESPAWN_TIME = 7

for i, humanoid in pairs(npcFolder:GetDescendants()) do
  if humanoid:IsA("Humanoid") then
      humanoid.Died:Connect(function()
        task.wait(RESPAWN_TIME)
        local newNPC = humanoid.Parent:Clone()
        newNPC.Parent = npcFolder
        newNPC:MakeJoints()
    end)
  end
end

Although this might not be the smartest decision performance-wise.

thank you all for taking the time to reply, but after a bit of troubleshooting, i managed to fix the problem:

function explode()
	if isExploding then return end
	isExploding = true

	print(`starting explosion timer for {hostileNpc}`)
	task.delay(EXPLOSION_TIME, function()
		print(`{hostileNpc} has exploded!`)
		noisemaker.Parent = workspace
		noisemaker.Anchored = true

		local explosion = Instance.new("Explosion")
		explosion.Position = npcRoot.Position
		explosion.Parent = workspace.Terrain
		explosion.Visible = true
		explosion.Name = `kablooey`
		explosion.BlastPressure = EXPLOSION_BLAST_PRESSURE
		explosion.BlastRadius = EXPLOSION_BLAST_RADIUS
		
		hitbox:ApplyImpulse(Vector3.new(0, 88, 0))
		personoid:ChangeState(Enum.HumanoidStateType.Dead)

		task.delay(RESPAWN_TIME, function()
			npcClone.Parent = hostileNpc.Parent
			npcClone:MakeJoints()
			hostileNpc:Destroy()
		end)
	end)
end
1 Like

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