In a function how do I return more then one value

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Dragon20C

for an example this is what I have

func take_damage(move,attacker):
	randomize()
	critical = 1
	var critical_chance = randi() % 100 + 1
	#var critical_chance = 5
	if critical_chance <= 6.25:
		critical = 2
		
	var type : float = Get_effectiveness(move.type,type_1) * Get_effectiveness(move.type,type_2)
	var modifier : float = rand_range(0.85,1.0) * type * critical
	var a : float = (2.0 * attacker.level / 5.0 + 2.0)
	var b : float = (a * attacker.attack * move.power / defence) / 50.0
	var c : float = (b + 2.0) * modifier
	var damage = int(c)
	current_health -= damage
	print(str(attacker.name) + " = " + str(damage))
	return critical > 1

This function currently returns if a critical hit happens but I would also like to turn other variables like if current_health is <= 0 and other stats, I saw in unity something about returning a function or was it a class, cant remember anyways whats the preferred way for godot/gdscript

:bust_in_silhouette: Reply From: AlexTheRegent

Just return multiple values as Array or Dictionary. Something like this:

return [critical, current_health]

Then you can get critical and current_health as

var result = take_damage(move, attacker)  
var critical = result[0]  
var current_health = result[1]
:bust_in_silhouette: Reply From: timothybrentwood

If you want to return multiple values you can either create an Object to handle those multiple values (not preferred), return an Array (less preferred) or return a Dictionary. (Vectors work as well if you’re dealing with numbers.)

I would suggest that you let the attacker handle the logic of determining the damage and at the end emitting a signal with the amount of damage and the unit that is supposed to be damaged.
Then you could print something like “Attacker uses Move, it was a critical strike!” in the attacker’s logic and “Target takes x damage” when the target takes the damage. I think separating the logic in this manner will allow you to avoid the need to return multiple variables in the first place.

As it stands your take_damage() function is quite overloaded: it determines if it was a critical, uses the attacker’s stats to determine the amount of damage in conjunction with the target’s defense (very brittle), prints the amount of damage done, and finally applies the damage to the target. If I was reading code and came across a function called take_damage() I would expect it to have 1-2 inputs (amount and maybe damage class/type) and I would apply my defense to that amount then reduce my health (which would probably be the responsibility of a separate function so it can handle UI stuff).

So your suggesting I should try to think outside the box and not make it like this, this function is in a resource script where I can make multiple enemys but I could do it that way but I would still be adding a function that adds the stats and defence stats together, a object sounds good though I don’t really know how an object is presented in code.
Thanks for the tip to make it better but for now I just want things to work my way and then I will improve it with people’s comments of course, thanks though I appreciate it!

Dragon20C | 2021-05-06 18:08

Absolutely man! Don’t get me wrong what you’re doing works and it’s a prototype. It’s good to delegate tasks to different objects from the get go though. I’m spitballing but I would do something like this:

signal damage_target(amount, target)
# how you handle this part is really up to how your game is structured 
func start_battle(enemies):
    for enemy in enemies:
        enemy.connect("damage_target", self, "determine_damage")

func use_move_on_target(move, target):
    var critical_multiplier = determine_critical()
    var damage = determine_move_damage(move)
    damage = int(damage * critical multiplier)
    print_move(move, critical > 1)
    emit_signal("damage_target", damage, target)

func determine_critical()
    randomize()
    critical = 1
    var critical_chance = randi() % 100 + 1
    #var critical_chance = 5
    if critical_chance <= 6.25:
        critical = 2
    return critical

func determine_move_damage(move)
    var type : float = Get_effectiveness(move.type,type_1) * Get_effectiveness(move.type,type_2)
    var modifier : float = rand_range(0.85,1.0) * type
    var a : float = (2.0 * self.level / 5.0 + 2.0)
    var b : float = (a * self.attack * move.power) / 50.0
    var c : float = (b + 2.0) * modifier
    var damage = c
    return damage

func print_move(move, is_critical):
    var to_print = self.name + " uses " + move.name 
    if is_critical:
        to_print += ", it was a critical hit!"
    else:
        to_print += "."
    print(to_print)

# called when the damage_target signal is emitted
func determine_damage(amount, target):
    if target == self:
        apply_damage(amount)

func apply_damage(amount)
    var reduced_by_defence = int(amount / self.defence)
    self.current_health -= reduced_by_defence
    var damage_message = str(self.name) + " took " + str(reduced_by_defence) + " damage."
    if self.current_health <= 0:
        print(damage_message + " " + str(self.name), " has died.")
    else:
        print(damage_message)

timothybrentwood | 2021-05-06 18:47

I see, I don’t usually use signals or I mainly use them for simple tasks but I can try this, thanks!

Dragon20C | 2021-05-06 18:51