I’m working on a status effect system for our game, but when an object that applies an effect gets destroyed while an effect is running, the threads the status effect system creates are cancelled too. Is it possible to make these threads independent of the scripts in the objects in Workspace or do I need to make a new server script to fire BindableEvents at?
The code in question:
-- Return an effect's ModuleScript
function module.LookupEffect(effectName)
return script.Effects:FindFirstChild(effectName)
end
-- Apply an effect to a player. If this effect is stackable,
-- it will add the base duration of the effect on top of the currently
-- running effect.
function module:ApplyEffectTo(effectName, plr)
if not effects[plr] then
-- Create this player's effect subtable if it doesn't already exist
effects[plr] = {}
end
-- Create a new instance of the status effect
local toApply = require(module.LookupEffect(effectName)).New()
if not effects[plr][toApply.Name] then -- If this effect isn't already applied,
task.spawn(function() --!! This was just something I tried that didn't work
effects[plr][toApply.Name] = toApply
coroutine.wrap(toApply.Apply)(plr) -- Start the effect
if db then print(toApply.Name.." applied") end -- db refers to a debug mode variable
RE:FireClient(plr, effects[plr][toApply.Name])
toApply.Ended:Connect(function() -- Remove it when it finishes
if db then
print(toApply.Name.." ended")
end
effects[plr][toApply.Name] = nil
end)
end)
else
if toApply["Stackable"] == false then
if db then --!! Problem area, the thread is terminated before the Ended signal fires, leaving a dead entry in the effects list
print("Not applying because this isn't stackable")
print(effects)
end
return
end -- Return if it's not stackable
effects[plr][toApply.Name].addDuration(toApply.Duration) -- Add duration if it is
if db then
print("Applied additonal duration to effect")
end
RE:FireClient(plr, effects[plr][toApply.Name])
end
end
Structure of an effect:
local effect = {}
local RS = game:GetService("RunService")
local Signal = require(game.ReplicatedStorage.Libraries.Signal)
function effect.New(effect)
local self = {}
setmetatable(self, effect)
self.Name = effect["Name"]
self.Description = effect["Description"]
self.Image = effect["Image"]
self.Duration = effect["Duration"]
self.Sound = effect["Sound"] or 0
self.Stackable = if effect.Stackable == false then false else true
self.Apply = nil
self.applyTime = tick()
self.Ended = Signal.new()
RS.Heartbeat:Connect(function()
if tick() >= self.applyTime + self.Duration then
self.Revoke()
self.Ended:Fire()
self.Ended:DisconnectAll()
end
end)
self.addDuration = function(addTime)
self.Duration += addTime
end
self.Revoke = function()
-- We need to make sure that Ended is fired no matter how this is called
print("Revoked~!")
self.Ended:Fire()
effect.Revoke()
end
return self
end
return effect