Jump to content

[Template] Timed Buffs/Debuffs


Recommended Posts

Here is a template for a simple Timed Buff system. You can put (most of) that code somewhere in the root of your entity's Lua code. It may seem like a lot of code, but most of it is boilerplate code, to make sure your buffs aren't applied multiple times and their changes don't persist through save/load.

Read all the code-comments thoroughly, so you understand how it works and how to use it. It works by applying some code immediately to apply the buff, and then setting up some code to be called after a certain amount of time to remove the buff again. This means that consecutive applications of the buff will have to be handled, and in the code below I just cancel the old task, so when applying a buff to an entity that already has that buff, it replaces the buff completely. So, if you make a stun and you apply it twice, then you don't want the first stun's task to remove the stun before the second stun has run out. It also makes sure to remove the buff before the buffed entity is saved, so any buffed variables that are saved get unbuffed BEFORE they are saved. That way we avoid duplicate buffs across game sessions.

-- This function is rather complicated. You don't need to change anything in here.
local function ApplyBuff(buffData, instToBuff)
	local buffUniqueName = buffData.uniqueName
	-- Cancel any existing duplicate timed buff.
	if instToBuff[buffUniqueName.."Task"] ~= nil then
		instToBuff[buffUniqueName.."Task"] = nil
	-- Make sure that our buff is removed BEFORE the entity is saved (the server is closed or player quits).
	-- This is only necessary if you change variables which are saved, like current health, health-penalty, etc.
	-- Without it, you could apply a max-health buff, quit before it ran out, rejoin, and you would have kept
	-- the extra max health!
	local buffOnSave = function(self, inst, data)
		if inst[buffUniqueName.."Task"] ~= nil then
			inst[buffUniqueName.."Task"] = nil
	if instToBuff.OnSave ~= nil then
		local oldOnSave = instToBuff.OnSave
		instToBuff.OnSave = function(self, inst, data)
			buffOnSave(self, inst, data)
			oldOnSave(self, inst, data)
		instToBuff.OnSave = buffOnSave
	-- Apply the buff.
	-- Set up removal after a certain amount of time.
	instToBuff[buffUniqueName.."Task"] = instToBuff:DoTaskInTime(buffData.duration, function(inst)
		inst[buffUniqueName.."Task"] = nil

-- Now to make some buffs and put them in a Buffs-dictionary.
local Buffs = {}

-- This function creates a new buff-data object and adds it to the Buffs-dictionary
-- with the uniqueName as key.
local function CreateBuff(uniqueName, duration, applyFunction, removeFunction)
	local newBuff = {}
	newBuff.uniqueName = uniqueName
	newBuff.duration = duration
	newBuff.applyFunction = applyFunction
	newBuff.removeFunction = removeFunction
	Buffs[uniqueName] = newBuff

-- This is an example of how to use the CreateBuff() function to create a new buff.
CreateBuff("MyFantasticBuff", 5.0,
		-- Apply all your buff code here
		-- Remember to always check whether the components you want to change are actually present on the
		-- entity before you try to mess with them.
		-- Revert your buff code here
		-- Remember to always check whether the components you want to change are actually present on the
		-- entity before you try to mess with them.

-- Now we can apply our new buff to any entity like this:
-- Parameters: <buff data>, <entity to apply buff to>
ApplyBuff(Buffs["MyFantasticBuff"], inst)

So, where would you call this ApplyBuff function?

Maybe when you attack with a certain weapon (so, in an attack-listener function), you either just call ApplyBuff(Buffs["MyFantasticBuff"], inst) on the entity you attack, or you do a FindEntities-call to find e.g. every player around you (for a positive area buff) or every entity with a locomotor component which is NOT a player (for a negative area buff) or something, and then call ApplyBuff("SomeBuffName", inst) on all of them.

If you do AoE-effects, remember to set proper filters on your FindEntities-calls

If you want to apply your buff to anything around you all the time, take a look at my Auras tutorial. You can combine it with this approach.

Now for the things you can put in under the comments saying "Apply all your buff code here" and "Revert your buff code here". These are just a few examples to give you the idea of how this works.

Change Movement Speed

-- Add +20% speed boost in all situations
inst.components.locomotor:SetExternalSpeedMultiplier(inst, "myuniquemodifierkey", 1.20)

To remove it again, remove the multiplier.

-- Remove speed bonus.
inst.components.locomotor:RemoveExternalSpeedMultiplier(inst, "myuniquemodifierkey")

 Change Max Health

-- Store the current maxhealth in a variable.
inst.normalmaxhealth = inst.components.health.maxhealth
-- Add 50 to max health
inst.components.health:SetMaxHealth(inst.components.health.maxhealth + 50)
-- Changing the max health does not change the current health.
-- If you want to give the character the extra health immediately, do the following.
inst.components.health:DoDelta(50, false)

To remove it again, remove the multiplier.

-- Set the max health back to normal, using the value we stored above.

 Add a damage modifier

-- Add 10% damage bonus.
inst.components.combat.externaldamagemultipliers:SetModifier(inst, 1.10, "myuniquemodifierkey")

To remove it again, remove the multiplier.

-- Remove damage bonus.
inst.components.combat.externaldamagemultipliers:RemoveModifier(inst, "myuniquemodifierkey")

Substitute myuniquemodifierkey with a unique modifer key (a string) of your own.

Edited by Ultroman
  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Create New...