How do I make status effects over time?

4.2

I want to make an enemy able to apply a status effect on the player for a variable time with a variable value example

that I can apply burn with a damage of 8 for 3 seconds.
The problem is that I don’t know how to make the created timer call the burn() method with the given value.
p.s: I’m not an English speaker and I used the translator so maybe everything won’t be understood very well.

1 Like

Show the code for your burn() method.

1 Like

Func burn(timer,value):
if timer.left_time % 1 == 0:
call(“damage”)
My idea is create a timer with other function that lasts a while that is the parameter that the enemy passes to it and that the timer created is the one used by the burn() function, I don’t know if it is understood

Then all you need is burn.bind() to get a function with the parameters set, and give that to the timer finish signal, if I’m understanding it right.

I think you do not understand me.
I plan to create the timers dynamically, that is, create them every time a state is applied, but I don’t know how to make it so that when the state is applied, it is called in the physical processes function.
the burn() function with the timer path and the value but I am not achieving it

Well, after you create a timer, you have to set the time, start it, give it a function to call when the time ends and add it to the node tree. Which part is giving you trouble exactly? Show more about your setup.

Something like these ? (I haven’t try it)

var counter = 0;
var timers = {}

func apply_effect(duration_between_damage, repeat_time, damage):
	var timer = Timer.new()
	timer.wait_time = duration_between_damage
	timer.autostart = true
	timer.timeout += burn.bind(damage)
	timer.timeout += time_out_timer_damage.bind(counter)
	timers[counter] = {repeat = repeat_time, timer = timer}
	add_child(timer)
	counter += 1

func time_out_timer_damage(id):
	timers[id].repeat -= 1
	if (timers[id].repeat == 0):
		timers[id].timer.call_deferred("queue_free")
		timers.erase(id)

func burn(damage):
	pass
1 Like

Yeah. I think that should work.

I think that could work. but I was also testing and it occurred to me that the effects could be a class that is saved in an array and that has a function with its behavior. What happens is that I don’t know how to make a class call a method (for example damage() )of the node in which that class is stored

Just store the object it should affect as a var, then use it as the base of the call. Say you add a parameter to your apply_effect() function called apply_to of type Node, you pass the node it should affect as that parameter, store it in your class, then on the burn() function you call apply_to.damage()
When you call a function like this, expecting it to be implemented in a different object, this is called the Interface Pattern, in case you need to look up more details on how to use this.

1 Like

Find an error in my previous post, i forget to add the time_out_timer_damage function to timer timeout.

if you want to call a specific function you can use a Callable like so :

var counter = 0;
var timers = {}

func apply_effect(duration_between_damage : float, repeat_time : int, damage : float, callable : Callable):
	var timer = Timer.new()
	timer.wait_time = duration_between_damage
	timer.autostart = true
	timer.timeout += time_out_timer_damage.bind(counter) 
	timers[counter] = {repeat = repeat_time, timer = timer, callable = callable, damage = damage}
	add_child(timer)
	counter += 1

func time_out_timer_damage(id):
	timers[id].callable.call(timers[id].damage)
	timers[id].repeat -= 1
	if (timers[id].repeat == 0):
		timers[id].timer.call_deferred("queue_free")
		timers.erase(id)

And you use something like this to call your function :

func other_func():
	var callable = Callable(Class, "function in class")
	apply_effect( 1, 3, 8, callable)
1 Like

Thanks but isn’t there a way to automatically get the node in which it is saved when the _init() method is called?

Why don’t use a custom Timer ? You define what do this effect in a script for each type.

You can proceed like this :
#1 Create a script effect like this one :

Effect.gd script
class_name Effect extends Timer

var duration_between_damage
var repeat_time
var damage

func _ready():
	wait_time = duration_between_damage
	autostart = true
	timeout.connect(time_out_timer_damage)


func time_out_timer_damage():
	_apply_damage()
	repeat_time -= 1
	if (repeat_time == 0):
		call_deferred("queue_free")


func _apply_damage():
	# calculation, and type effect here (override it when inherit)
	get_parent().damage(damage)

#2 Create your differents effects with new script who inherit from effect :

Effect exemple (Effect_Burn.gd)
class_name Burn extends Effect


func _apply_damage():
	# calculation, and type effect here
	get_parent().damage(damage)

#3 to apply effect to a node just create an effect and add it as a child of this node :

Add burn effect to a node
func apply_burn():
	var effect = Burn.new()
	effect.duration_between_damage = 1.0
	effect.repeat_time = 3
	effect.damage = 8
	$Node.add_child(effect);

if you are adding an object to a node, you have a variable at some point with that node, right?

How to write code ? For explain

https://commonmark.org/help/

you can do this by instantiating a script when your player takes a burning damage, that script will essentially be attached to a Node so you will be loading up that node and adding that as the child of your player node. you can then immediately run the burning script using _ready() or in that _ready() function you can create a new Timer which will periodically run a method every second.