The status effect system works like this:
-
Create an OOP object (StatusEffect) – the constructor needs the effect name, the target, and an optional table containing attributes for the effect (e.g.
{["Duration"] = 10}
), such as a custom duration or damage per tick. -
Set the table of status effect attributes in the StatusEffect. Compare the parameters we passed into a giant dictionary of default status effect parameters stored in another ModuleScript. Add any missing attributes (e.g. if we only passed in a table containing a custom duration for a status effect that also had a damage per tick attribute) to a table that is then set to the StatusEffect’s Params.
-
CollectionService tags the target with the effect name. If this tag is removed before the effect times out, remove the effect (managed by the function in Step 4)
-
Call the function. After a few checks, call the function with the same name that we set the StatusEffect’s name to. All the possible functions are stored in the same ModuleScript as the dictionary of status effects.
The issue is that I am repeating myself a tremendous amount. About 80% of the code in the other specific functions is the same, and with only 10 status effects, the ModuleScript is already over 1000 lines long. How should I change this? Make a separate OOP Module for each status effect?
Here is an example function:
--[[
BURNING:
]]--
function statusEffectList.burning(player, params, giver)
-- Apply the particles and other visuals
local damagePerTick, tickTime, duration = params["Damage"], params["Tick"], params["Duration"]
-- Timer --
local currentTime = 0
task.spawn(function()
while currentTime <= duration and CollectionService:HasTag(player, "Burning") do
currentTime += 0.1
task.wait(0.1)
end
end)
-- Reset duration if burning is applied again to the same player --
-- This section is also really bad because of the if statements
local resetDurationConnection;
resetDurationConnection = StatusEffectResetDuration.Event:Connect(function(newPlayer, newEffectName, newParams, newGiver)
if newPlayer == player and newEffectName == "Burning" then
if typeof(newParams) ~= "table" then
warn("The new params aren't in a table")
return
end
currentTime = 0 -- Reset our timer
if newParams["Tick"] < tickTime then -- Change the damage tick if it's lower
tickTime = newParams["Tick"]
end
if newParams["Damage"] > damagePerTick then
damagePerTick = newParams["Damage"]
end
if newParams["Duration"] ~= duration then -- If the new duration is different, set it as the new one
duration = newParams["Duration"]
end
if newGiver then
if newGiver ~= giver then -- Replace the previous player who applied the effect
giver = newGiver
end
end
end
end)
-- Deal damage --
-- Yields the script until the effect times out or gets removed
while currentTime <= duration and CollectionService:HasTag(player, "Burning") do
if player then
-- Code for damage
end
task.wait(tickTime)
end
StatusEffectEnded:Fire(player, "Burning") -- Signals to the StatusEffect object to remove CollectionService tags
-- Remove effects and visuals
-- Disconnect the listener once we're done --
if resetDurationConnection then
resetDurationConnection:Disconnect()
end
end