i’ve got a function that is supposed to handle pathfinding and make tagged npcs spawn an item after they die. this seems fairly simple. however, the implementation presents an unexpected challenge. when an NPC dies, the event will inexplicably trigger up to 1,000 times (per NPC) and i have no idea why.
local function setup(obj)
local entity_root = obj.PrimaryPart
local personoid = obj:FindFirstChildWhichIsA("Humanoid")
local path = path_module.new(obj)
target = find_nearest(entity_root.Position)
entity_root:SetNetworkOwner(nil)
if target and target.Character then
plr_root = target.Character.PrimaryPart
path:Run(plr_root.Position)
personoid.Died:Connect(function()
print(`{obj} is DEAD`)
path:Destroy() -- destroy the path, as we no longer need it
end)
end
end
You might wanna show how you call the function, like if you are doing it in a for loop. But there’s an easy fix aswell, you can just do :Once() which will only fire once and never again if that’s what you want. (I assume you don’t want it to fire only one time though). So could you show how the function (setup) is being called?
-- handle pathfinding for every tagged NPC
local services = {
CollectionService = game:GetService("CollectionService");
ServerStorage = game:GetService("ServerStorage");
Players = game:GetService("Players");
RunService = game:GetService("RunService");
}
local path_module = require(services.ServerStorage.SimplePath)
local enemy_tag = "PATHING_ENTITY"
local target, plr_root, died_connection
local function find_nearest(position)
local nearest_plr
local shortest_dist = math.huge
for _, player in ipairs(services.Players:GetPlayers()) do
local character = player.Character
if character and character:FindFirstChildWhichIsA("Humanoid").Health > 0 then
local dist = (character.PrimaryPart.Position - position).Magnitude
if dist < shortest_dist then
shortest_dist = dist
nearest_plr = player
end
end
end
return nearest_plr
end
function run_path()
local function setup(obj)
local entity_root = obj.PrimaryPart
local personoid = obj:FindFirstChildWhichIsA("Humanoid")
local path = path_module.new(obj)
target = find_nearest(entity_root.Position)
entity_root:SetNetworkOwner(nil)
if target and target.Character then
plr_root = target.Character.PrimaryPart
path:Run(plr_root.Position)
died_connection = personoid.Died:Connect(function()
print(`{obj} is DEAD`)
path:Destroy()
died_connection:Disconnect() -- added this but that didn't help
end)
end
end
local enemies = services.CollectionService:GetTagged(enemy_tag)
for _, hostile_entity in ipairs(enemies) do
if not hostile_entity:IsA("Model") then continue end
setup(hostile_entity)
end
services.CollectionService:GetInstanceAddedSignal(enemy_tag):Connect(function(obj)
setup(obj)
end)
end
local timer = 0
local update_interval = 0.25 -- update every 0.25 seconds
local function update_path(dt)
timer = timer + dt
if timer >= update_interval then
timer = timer - update_interval
run_path()
end
end
task.delay(4, function()
services.RunService.Heartbeat:Connect(update_path)
end)
local services = {
CollectionService = game:GetService("CollectionService");
ServerStorage = game:GetService("ServerStorage");
Players = game:GetService("Players");
RunService = game:GetService("RunService");
}
local path_module = require(services.ServerStorage.SimplePath)
local enemy_tag = "PATHING_ENTITY"
local target, plr_root, died_connection
local function find_nearest(position)
local nearest_plr
local shortest_dist = math.huge
for _, player in ipairs(services.Players:GetPlayers()) do
local character = player.Character
if character and character:FindFirstChildWhichIsA("Humanoid").Health > 0 then
local dist = (character.PrimaryPart.Position - position).Magnitude
if dist < shortest_dist then
shortest_dist = dist
nearest_plr = player
end
end
end
return nearest_plr
end
function run_path()
local function setup(obj)
if obj:FindFirstChild("prepped") then return end
local prepped = Instance.new("BoolValue")
prepped.Name = "prepped"
prepped.Value = true
prepped.Parent = obj
local entity_root = obj.PrimaryPart
local personoid = obj:FindFirstChildWhichIsA("Humanoid")
local path = path_module.new(obj)
target = find_nearest(entity_root.Position)
entity_root:SetNetworkOwner(nil)
if target and target.Character then
plr_root = target.Character.PrimaryPart
path:Run(plr_root.Position)
died_connection = personoid.Died:Connect(function()
print(`{obj} is DEAD`)
path:Destroy()
died_connection:Disconnect() -- added this but that didn't help
end)
end
end
local enemies = services.CollectionService:GetTagged(enemy_tag)
for _, hostile_entity in ipairs(enemies) do
if not hostile_entity:IsA("Model") then continue end
setup(hostile_entity)
end
services.CollectionService:GetInstanceAddedSignal(enemy_tag):Connect(function(obj)
setup(obj)
end)
end
local timer = 0
local update_interval = 0.25 -- update every 0.25 seconds
local function update_path(dt)
timer = timer + dt
if timer >= update_interval then
timer = timer - update_interval
run_path()
end
end
task.delay(4, function()
services.RunService.Heartbeat:Connect(update_path)
end)
died_connection = personoid.Died:Connect(function()
print(`{obj} is DEAD`)
path:Destroy()
died_connection:Disconnect() -- added this but that didn't help
end)
function run_path()
local function setup(obj)
local entity_root = obj.PrimaryPart
local path = path_module.new(obj)
target = find_nearest(entity_root.Position)
entity_root:SetNetworkOwner(nil)
if target and target.Character then
plr_root = target.Character.PrimaryPart
path:Run(plr_root.Position)
end
end
local enemies = services.CollectionService:GetTagged(enemy_tag)
for _, hostile_entity in ipairs(enemies) do
if not hostile_entity:IsA("Model") then continue end
setup(hostile_entity)
end
services.CollectionService:GetInstanceAddedSignal(enemy_tag):Connect(function(obj)
setup(obj)
local personoid = obj:FindFirstChildWhichIsA("Humanoid")
died_connection = personoid.Died:Connect(function()
print(`{obj} is DEAD`)
died_connection:Disconnect()
end)
end)
end
local services = {
CollectionService = game:GetService("CollectionService");
ServerStorage = game:GetService("ServerStorage");
Players = game:GetService("Players");
RunService = game:GetService("RunService");
}
local path_module = require(services.ServerStorage.SimplePath)
local enemy_tag = "PATHING_ENTITY"
local target, plr_root, died_connection
local function find_nearest(position)
local nearest_plr
local shortest_dist = math.huge
for _, player in ipairs(services.Players:GetPlayers()) do
local character = player.Character
if character and character:FindFirstChildWhichIsA("Humanoid").Health > 0 then
local dist = (character.PrimaryPart.Position - position).Magnitude
if dist < shortest_dist then
shortest_dist = dist
nearest_plr = player
end
end
end
return nearest_plr
end
function run_path()
local function setup(obj)
local entity_root = obj.PrimaryPart
local personoid = obj:FindFirstChildWhichIsA("Humanoid")
local path = path_module.new(obj)
target = find_nearest(entity_root.Position)
entity_root:SetNetworkOwner(nil)
if target and target.Character then
plr_root = target.Character.PrimaryPart
path:Run(plr_root.Position)
if obj:FindFirstChild("prepped") then return end
local prepped = Instance.new("BoolValue")
prepped.Name = "prepped"
prepped.Value = true
prepped.Parent = obj
died_connection = personoid.Died:Connect(function()
print(`{obj} is DEAD`)
path:Destroy()
died_connection:Disconnect() -- added this but that didn't help
end)
end
end
local enemies = services.CollectionService:GetTagged(enemy_tag)
for _, hostile_entity in ipairs(enemies) do
if not hostile_entity:IsA("Model") then continue end
setup(hostile_entity)
end
services.CollectionService:GetInstanceAddedSignal(enemy_tag):Connect(function(obj)
setup(obj)
end)
end
local timer = 0
local update_interval = 0.25 -- update every 0.25 seconds
local function update_path(dt)
timer = timer + dt
if timer >= update_interval then
timer = timer - update_interval
run_path()
end
end
task.delay(4, function()
services.RunService.Heartbeat:Connect(update_path)
end)
dude literally just do this if disconnecting the signal doesn’t help
local Died = false
died_connection = personoid.Died:Connect(function()
if Died then
return
end
Died = true
print(`{obj} is DEAD`)
path:Destroy()
died_connection:Disconnect() -- added this but that didn't help
end)
This wouldn’t work. You’re literally setting the variable to false before the connection—if the function runs again the connection will simply connect again. This is why I used an instance which exists in the DataModel and can be tracked.
Is run_path() being called twice in your script? As others have mentioned the setup function may be running more than once on your NPCs resulting in the .Died signal firing multiple times. I suggest putting a print statement on where you suspect it may be running more than once and try working from there.
I’ve looked at your script. The best way to do this is to put your death event handler in the initialization section of your script so it gets ran once. Also, as others have suggested, use :Once() instead of :Connect(). The reason behind this is at instance of the NPC can only die once, so once the Died event fires, it’s not needed anymore. So when the NPC dies, you spawn in the item at the location where the NPC met their end.
As a suggestion, if you’re going for something like an adventure type game or a MMORPG like World of Warcraft, you can do what they do: Start a particle emitter on the corpse. So when a player clicks or taps on that corpse, a window opens showing the item or items on that corpse. The NPC would have a loot table containing all items that are possible, and a few are randomly chosen from that list. The items are represented as ID numbers that are indices into the main loot table (which can be huge) which contains the definitions of all items in the game. When the item is transferred into your inventory, the ID number is added to a list for the player. There’s more so if you’re interested, let me know.
local services = {
CollectionService = game:GetService("CollectionService");
ServerStorage = game:GetService("ServerStorage");
Players = game:GetService("Players");
RunService = game:GetService("RunService");
}
local path_module = require(services.ServerStorage.SimplePath)
local enemy_tag = "PATHING_ENTITY"
local target, plr_root, died_connection
local function find_nearest(position)
local nearest_plr
local shortest_dist = math.huge
for _, player in ipairs(services.Players:GetPlayers()) do
local character = player.Character
if character and character:FindFirstChildWhichIsA("Humanoid").Health > 0 then
local dist = (character.PrimaryPart.Position - position).Magnitude
if dist < shortest_dist then
shortest_dist = dist
nearest_plr = player
end
end
end
return nearest_plr
end
function run_path()
local function setup(obj)
local entity_root = obj.PrimaryPart
local personoid = obj:FindFirstChildWhichIsA("Humanoid")
local path = path_module.new(obj)
target = find_nearest(entity_root.Position)
entity_root:SetNetworkOwner(nil)
if target and target.Character then
plr_root = target.Character.PrimaryPart
path:Run(plr_root.Position)
end)
else
path:Destroy()
end
end
local enemies = services.CollectionService:GetTagged(enemy_tag)
for _, hostile_entity in ipairs(enemies) do
if not hostile_entity:IsA("Model") then continue end
setup(hostile_entity)
end
services.CollectionService:GetInstanceAddedSignal(enemy_tag):Connect(function(obj)
setup(obj)
end)
end
local timer = 0
local update_interval = 0.25 -- update every 0.25 seconds
local function update_path(dt)
timer = timer + dt
if timer >= update_interval then
timer = timer - update_interval
run_path()
end
end
task.delay(4, function()
services.RunService.Heartbeat:Connect(update_path)
end)