Compose your variables instead of setting them directly

Imagine you have a item, when equipped it will slow down you by half, when unequipped this effect is undone. Now imagine you have a seperate mechanic, once you get hurt, your walkspeed is slowed down by 2 units for a second. If you get hurt and have the the item equipped at the same time, then during that 1 second slowdown you unequip your item, the resulting WalkSpeed after the effect is over is not the original walkspeed despite the fact that all factors affecting your walkspeed are gone, this can happen in many places but WalkSpeed is a common one and you have probably faced it atleast once
Example in code:

function onHurt()
	humanoid.WalkSpeed -= 2
	task.delay(1, function()
		humanoid.WalkSpeed += 2
		print(humanoid.WalkSpeed)
	end)
end

function onEquip()
	humanoid.WalkSpeed = humanoid.WalkSpeed * 0.5;
end

function onUnequip()
	humanoid.WalkSpeed = humanoid.WalkSpeed * 2;
end

onEquip()
onHurt()
task.wait(0.5)
onUnequip()
print(humanoid.WalkSpeed)

The problem is a single a number does not store the “history” of what happened to it, you cannot just reverse one change without it affecting every other change that was applied afterwards.

This is where i propose usage of what i call “StatusEffects” which is just a table basically saying
Effects player has:
Slowdown x3(applied 3 times)
Supercharge x1(applied 1 times)
Stunlocked x2(applied 2 times)

Example StatusEffect implementation (NOT BATTLETESTED):
StatusEffects.lua (1.2 KB)

Module description:

local StatusEffectsModule = require(...)
local StatusEffects = StatusEffectsModule() -- creates new status effects
local removeStatusEffect = StatusEffects.addStatusEffect("name") -- Add's status effect
removeStatusEffect(); -- removes the status effect

StatusEffects.hasStatusEffect("name") -- whenever a status effect is applied atleast once
StatusEffects.getStatusEffect("name") -- how many times a status effect is applied

-- for example
local removeSlowdown = StatusEffects.addStatusEffect("SlowDown")
local removeSlowdown2 = StatusEffects.addStatusEffect("SlowDown")

StatusEffects.getStatusEffect("name") -- is 2
StatusEffects.hasStatusEffect("name") -- true, atleast one is applied (2 in this case)

removeSlowdown()
StatusEffects.getStatusEffect("name") -- is 1
removeSlowdown()
StatusEffects.getStatusEffect("name") -- is still 1, since removeSlowdown does not remove the other slowdown you have applied
StatusEffects.hasStatusEffect("name") -- true

StatusEffects.clearStatusEffect("name") -- does clear all currently applied
StatusEffects.getStatusEffect("name")  -- 0
StatusEffects.hasStatusEffect("name") -- false

StatusEffects.statusEffectChanged:Connect(function() end) -- fired when any status changes (removeSlowdown, addStatusEffect, clearStatusEffect)

We reimplement the previous script with one function called calculateWalkSpeed which will return the new walkspeed, then hook it up to the statusEffectChanged event

After which is the reimplementation of the script at start

local StatusEffect = require(...)()

function calculateWalkSpeed()
	local baseWalkSpeed = 16;
	
	if StatusEffect.hasStatusEffect("ItemEquipped") then
		baseWalkSpeed *= 0.5;
	end
	
	if StatusEffect.hasStatusEffect("HurtSlowdown") then
		baseWalkSpeed -= 2;
	end
	
	return baseWalkSpeed
end

StatusEffect.statusEffectChanged:Connect(function()
	humanoid.WalkSpeed = calculateWalkSpeed()
	print(humanoid.WalkSpeed)
end)

function onHurt()
	local remove = StatusEffect.addStatusEffect("HurtSlowdown")
	task.delay(1, function()
		remove()
	end)
end

function onEquip()
	StatusEffect.addStatusEffect("ItemEquipped")
end

function onUnequip()
	StatusEffect.clearStatusEffect("ItemEquipped")
end

onEquip()
task.wait()
onHurt()
task.wait()
onUnequip()

If you want for the hurt slowdown to stack its as simple as changing lines harting the hurt slowdown to this

	if StatusEffect.hasStatusEffect("HurtSlowdown") then
		baseWalkSpeed -= StatusEffect.getStatusEffect("HurtSlowdown") * 2;
	end

This system instead of changing raw variables can bring you utility in many cases going outside of this example alone (think deciding whenever ui is visible on many factors, having a car’s speed be modified on many factors)

3 Likes