Here is an example that allows you to apply status effects any time you apply a tag to a Humanoid that matches a dictionary in StatusEffectsList
. You can reference the Humanoid’s specific table in Humanoids
to track which statuses are currently running. This is quite a bit more basic than using a module system like ECS.
This example uses a debounce as to not apply multiple of the same status effect at the same time. If you want stacking status effects, you’d have to remove the debounce and make the :GetInstanceRemovedSignal(i)
call ProcessStatusEffects()
with specifying the exact status effect that triggered the event instead of doing all available statuses.
local CollectionService = game:GetService("CollectionService")
local HumanCount = 0
local Humanoids = {} --Format: ["Human1"] = {Humanoid Instance, {StatusEffectsTable}}
local StatusEffectsList = {
["Poison"] = {
["TickTime"] = 2,
["Damage"] = 5,
["Length"] = 10,
["Processing"] = false, --Debounce
},
["MovementPenalty"] = {
["IncreaseIncrement"] = -10,
["Length"] = 10,
["Processing"] = false, --Debounce
},
["MovementBoost"] = {
["IncreaseIncrement"] = 10,
["Length"] = 10,
["Processing"] = false, --Debounce
},
}
function UpdateHumanTable(Human) --Puts the Human into the Humanoid table with the corresponding Status Effects. Returns the table.
if Human then
if Human:IsA("Humanoid") then
--Dupe check
local ExistingHumanTable = nil
if Human:GetAttribute("IndexName") then
ExistingHumanTable = Humanoids[Human:GetAttribute("IndexName")]
end
for i,v in pairs (Humanoids) do
if table.find(v,Human) then --Makes sure the Humanoid isn't already in one of the subtables in case the attribute check fails for some reason.
ExistingHumanTable = v
end
end
--Dupe check
if ExistingHumanTable == nil then --Create a table if there isn't one already.
HumanCount += 1
local IndexName = "Human" ..tostring(HumanCount)
Human:SetAttribute("IndexName", IndexName) --Add an attribute so we know that it's already in the table so it can find it during the dupe check.
local FetchedEffects = {}
for i,v in ipairs (CollectionService:GetTags(Human)) do
if StatusEffectsList[v] then
FetchedEffects[v] = StatusEffectsList[v] --Inserts the StatusEffects into the temporary table which will go into the specific Humanoid table
end
end
Humanoids[IndexName] = {Human, FetchedEffects}
return Humanoids[IndexName]
else --Update an existing table
for i,v in pairs (CollectionService:GetTags(Human)) do
if StatusEffectsList[v] then
if not ExistingHumanTable[2][v] then --If there already isn't an effect in the table,
ExistingHumanTable[2][v] = StatusEffectsList[v] --Inserts the StatusEffects into the pre-existing table specific to the Humanoid
end
end
end
for i,v in pairs (ExistingHumanTable[2]) do
if not table.find(CollectionService:GetTags(Human),i) then --If the player does not have a specific status effect tag,
v = nil --Removes the status from the table
end
end
return ExistingHumanTable
end
end
end
end
function TagHuman(Human, Tags)
if Human then
if typeof(Tags) == "table" then
for i,v in ipairs (Tags) do
if typeof(v) == "string" then
CollectionService:AddTag(Human,v)
end
end
elseif typeof(Tags) == "string" then
CollectionService:AddTag(Human,Tags)
end
end
end
function UntagHuman(Human, Tags)
if Human then
if typeof(Tags) == "table" then
for i,v in ipairs (Tags) do
if typeof(v) == "string" then
CollectionService:RemoveTag(Human,v)
end
end
elseif typeof(Tags) == "string" then
CollectionService:RemoveTag(Human,Tags)
end
end
end
function ProcessStatusEffects(HumanTable) --HumanTable Format: {Humanoid Instance, {StatusEffectsTable}}
if typeof(HumanTable) == "table" then
local HumanoidInstance = HumanTable[1]
local StatusEffects = HumanTable[2]
if not HumanoidInstance or typeof(StatusEffects) ~= "table" then warn("Humanoid/Status Effects missing") return end
for i,v in pairs (StatusEffects) do
if typeof(v) == "table" then
if v["Processing"] == false or v["Processing"] == nil then --Checks the debounce
v["Processing"] = true --Sets the debounce
if i == "Poison" then --Apply the effects here by checking the type of status effect (i).
local TimeRan = 0
local MaxTime = v["Length"]
task.spawn(function()
while TimeRan < MaxTime do
TimeRan +=1
task.wait(1)
end
end)
while v do
if TimeRan < MaxTime then
if HumanoidInstance then
HumanoidInstance.Health -= v["Damage"]
print("Damaging humanoid...")
end
else
break
end
task.wait(v["TickTime"])
end
UntagHuman(HumanoidInstance, i)
elseif i == "MovementPenalty" or i == "MovementBoost" then
HumanoidInstance.WalkSpeed += v["IncreaseIncrement"]
print("Humanoid's speed adjusted!")
wait (v["Length"])
if HumanoidInstance then
HumanoidInstance.WalkSpeed -= v["IncreaseIncrement"]
end
print("Humanoid speed back to normal.")
UntagHuman(HumanoidInstance, i)
end
end
end
end
else
warn("Missing or improperly formatted humanoid table.")
end
end
for i,v in pairs (StatusEffectsList) do
CollectionService:GetInstanceAddedSignal(i):Connect(function(Object)
if Object:IsA("Humanoid") then
local FetchedHumanTable = UpdateHumanTable(Object)
task.spawn(function()
ProcessStatusEffects(FetchedHumanTable)
end)
end
end)
CollectionService:GetInstanceRemovedSignal(i):Connect(function(Object)
if Object:IsA("Humanoid") then
local FetchedHumanTable = UpdateHumanTable(Object)
task.spawn(function()
ProcessStatusEffects(FetchedHumanTable)
end)
end
end)
end
--Now you just need to tag a humanoid whenever you want to apply a status effect. You can use the TagHuman function from above or the CollectionService:AddTag() function which achieves the same thing.
local TestHuman = game.Workspace.Robloxian.Humanoid
wait(1)
TagHuman(TestHuman,{"Poison", "MovementPenalty"}) --Will apply poison damage every 2 seconds for a total length of 10 seconds and a movement status effect that decreases walk speed.
wait(1)
TagHuman(TestHuman, "Poison") --This won't do anything since there's a debounce in place.