Hey, so I’m currently making a status effect system in, and I was wondering if the base code I wrote for it can be improved:
local StatusEffectManager = {}
local Players = game:GetService("Players")
local activeEffects = {} --table for holding active status effects for each target
local uiRefs = {} --track the UI elements for each target
------------------------------------------------------------------------------------------------------------------
--//Apply a status effect to a target
function StatusEffectManager.ApplyEffect(target, effectName, params)
if not activeEffects[target] then
activeEffects[target] = {}
uiRefs[target] = {}
end
--load the status effect module
local effectModule = require(script.StatusEffects:FindFirstChild(effectName))
if not effectModule then
warn("Effect module for " .. effectName .. " not found!")
return
end
local effects = activeEffects[target]
local uiElements = uiRefs[target]
if effects[effectName] then
--effect is already applied so extend the duration
local activeEffect = effects[effectName]
activeEffect.Params.Duration = (activeEffect.Params.Duration or 0) + (params.Duration or 5) --add new duration
--update the UI countdown
if uiElements[effectName] then
uiElements[effectName].Timer.Text = string.format("%.1f", activeEffect.Params.Duration) .. "s"
end
print(effectName .. " duration extended for " .. target.Name)
return
end
--initialize the effect
local effect = {
Name = effectName,
Params = params,
Module = effectModule,
Target = target,
}
if effectModule.OnApply then
effectModule.OnApply(target, params)
end
local plr = Players:FindFirstChild(target.Name)
if plr then --only create ui's for player oviously ^^
StatusEffectManager.CreateEffectUI(plr, effectName, params.Duration)
end
effects[effectName] = effect
end
--//Remove a status effect from a target
function StatusEffectManager.RemoveEffect(target, effectName)
if not activeEffects[target] or not activeEffects[target][effectName] then
warn("Effect " .. effectName .. " is not active on this target!")
return
end
local effect = activeEffects[target][effectName]
if effect.Module.OnRemove then
effect.Module.OnRemove(effect.Target, effect.Params)
end
StatusEffectManager.RemoveEffectUI(target, effectName)
activeEffects[target][effectName] = nil
end
--//Update all active effects (tick system)
function StatusEffectManager.Tick(deltaTime)
for target, effects in activeEffects do
for name, effect in effects do
if effect.Module.OnTick then
local isFinished = effect.Module.OnTick(effect.Target, deltaTime, effect.Params)
if isFinished then
StatusEffectManager.RemoveEffect(target, name)
end
if uiRefs[target] and uiRefs[target][name] then
uiRefs[target][name].Timer.Text = string.format("%.1f", effect.Params.Duration) .. "s"
end
end
end
end
end
--//Create a UI element for an effect
function StatusEffectManager.CreateEffectUI(player, effectName, duration)
local playerGui = player:FindFirstChild("PlayerGui")
if not playerGui then return end
local effectHolder = playerGui:FindFirstChild("StatusEffectHolder")
--creating the UI frame for the effects
local effectModule = require(script.StatusEffects:FindFirstChild(effectName))
local uiPreset = effectModule.UiPreset()
local effectFrame = script.ExampleFrame:Clone()
effectFrame.Name = effectName
effectFrame.Parent = effectHolder.Holder
effectFrame.BackgroundColor3 = uiPreset.BackgroundColor
effectFrame.UIStroke.Color = uiPreset.UiStrokeColor
effectFrame.BackgroundImage.Image = uiPreset.Image
effectFrame.BackgroundImage.ImageTransparency = uiPreset.ImageTransparency
effectFrame.BackgroundImage.Size = uiPreset.ImageSize
effectFrame.BackgroundImage.Position = uiPreset.ImagePosition
local nameLabel = effectFrame.EffectName
nameLabel.Text = uiPreset.Titel
local timerLabel = effectFrame.Timer
timerLabel.Text = string.format("%.1f", duration) .. "s"
--track the UI elements and stuff
if not uiRefs[player] then
uiRefs[player] = {}
end
uiRefs[player.Character][effectName] = {
Frame = effectFrame,
Timer = timerLabel,
}
end
--//remove a UI frame thing for an effect
function StatusEffectManager.RemoveEffectUI(player, effectName)
if uiRefs[player] and uiRefs[player][effectName] then
local frame = uiRefs[player][effectName].Frame
if frame then
frame:Destroy()
end
uiRefs[player][effectName] = nil
end
end
--//remove all active status effects from a target
function StatusEffectManager.RemoveAllEffects(target)
if not activeEffects[target] then
warn("No active effects to remove for this target!")
return
end
local effects = activeEffects[target]
--go through all active effects and remove them
for effectName, effect in pairs(effects) do
if effect.Module.OnRemove then
effect.Module.OnRemove(effect.Target, effect.Params)
end
--remove associated UI
StatusEffectManager.RemoveEffectUI(target, effectName)
end
--clear the active effects table for the target
activeEffects[target] = nil
end
return StatusEffectManager
I’m still also not sure if it’s actually better to just leave out the pairs or Ipairs for the loops because someone once said that it is actually better to let just Roblox do that.
What I mean as an example is this:
--//Update all active effects (tick system)
function StatusEffectManager.Tick(deltaTime)
for target, effects in activeEffects do
for name, effect in effects do
if effect.Module.OnTick then
local isFinished = effect.Module.OnTick(effect.Target, deltaTime, effect.Params)
if isFinished then
StatusEffectManager.RemoveEffect(target, name)
end
if uiRefs[target] and uiRefs[target][name] then
uiRefs[target][name].Timer.Text = string.format("%.1f", effect.Params.Duration) .. "s"
end
end
end
end
end
or
--//Update all active effects (tick system)
function StatusEffectManager.Tick(deltaTime)
for target, effects in pairs(activeEffects) do
for name, effect in pairs(effects) do
if effect.Module.OnTick then
local isFinished = effect.Module.OnTick(effect.Target, deltaTime, effect.Params)
if isFinished then
StatusEffectManager.RemoveEffect(target, name)
end
if uiRefs[target] and uiRefs[target][name] then
uiRefs[target][name].Timer.Text = string.format("%.1f", effect.Params.Duration) .. "s"
end
end
end
end
end