How to make health system for player

Godot Version

<stable4.2>

Question

<Im using hitbox and hurtbox class as well as node based state machine for player. Id like to call player_take_damage(amount: int): in script attached to state node however, im facing problems such as “amount not declared” or “too few arguments”.

I have this function inside player script which will be called, whenever player hit by enemy projectile.

func player_in_hurt_state(amount: int):
	state_in_hurt = true
	print("player take damage")
	health = max(0, health - amount)
	if health == 0:
		state_in_death = true

hurtbox:

elif owner.has_method("player_in_hurt_state") and hitbox.is_in_group("enemy_projectile"):
		owner.player_in_hurt_state(hitbox.enemy_damage()) #Player Enter Hurt State If Hit By Enemy Projectile

I want to make player to take damage after finishing certain state for some enemies. to do this, i tried to call player.func player_in_hurt_state(amount) but receives errors, since state node have no idea what damage values are.

1 Like

right now, i made a variable call var player_take_damage = false and decided to call this from enemy script by player.player_take_damage = true, and inside specific state that I want player to take damage, if player.player_take_damage: player.health -= 10.
Right now this works, but I feel really dumb to do it this way. Is there any other smarter way to do this?

is there any way to call player_in_hurt_state(amount: int) from state node script?

In the first case, you’re passing an undeclared (!) variable amount to your function:

player_in_hurt_state(amount) # undeclared variable

In the second case, you’re simply not providing the required arguments:

player_in_hurt_state() # no arguments!

Godot will not magically come up with a value for that variable, so you have to either declare it in the script you’re calling the method from, or point to another script with a variable that holds this value:

var amount = 10 # declared in the calling script
var amount = get_node("<PathToNode>").damage # declared in other Node
player_in_hurt_state(amount)
1 Like

If damage variable is written in hitbox class script, what should the path to node be?

my hitbox class script(Damage values are set within the editor of each enemy node):

@icon("res://entities/node/HitBox.svg")
class_name HitBox
extends Area2D

@export var damage := 10
@export_range(0, 1) var crit_chance: float = 0.05

func _ready():
	randomize()

func bullet_damage() -> int:
	if damage == 0:
		return 0
	else:
		var crit_multiplier = crit(crit_chance)
		var random_multiplier = 1.0
		if crit_multiplier == 2.0:
			random_multiplier = randomness()
		var final_damage = damage * crit_multiplier * random_multiplier
		return int(max(final_damage, damage))  

func enemy_damage() -> int:
	if damage == 0:
		return 0
	else:
		return damage

That depends on how your scene tree looks like! From what I understand, you have a player node with a script attached to it that contains the player_in_hurt_state function and the hurtbox is a child node of the player? Assuming the hitbox you use there is the correct one, the code you posted above should actually work (and not throw any errors). Can you share the full hurtbox script?

1 Like
@icon("res://entities/node/HurtBox.svg")
class_name HurtBox
extends Area2D

func _ready() -> void:
	connect("area_entered", Callable(self, "_on_area_entered"))

func _on_area_entered(hitbox: HitBox) -> void:
	if owner.has_method("take_damage") and hitbox.is_in_group("bullet"):
		owner.take_damage(hitbox.bullet_damage()) #Enemy Take Damage By Bullet
	elif owner.has_method("grab_player") and hitbox.is_in_group("player"):
		owner.grab_player() #Enemy Grab Player If Hit By Player
	elif owner.has_method("grab_by_enemies") and hitbox.is_in_group("enemies"):
		owner.grab_by_enemies() #Player Enter Grab State If Hit By Enemies
	elif owner.has_method("player_in_hurt_state") and hitbox.is_in_group("enemy_projectile"):
		owner.player_in_hurt_state(hitbox.enemy_damage()) #Player Enter Hurt State If Hit By Enemy Projectile


Enemy scene tree is just like player’s one. Hitbox class is child of characterbody2d.

Correct me if I’m wrong, but your code is checking if the hitbox is in a group called “enemy_projectile” so I’d expect that it’s not actually the enemy hitting your player, but their bullets? In that case, the script attached to the hitbox of that bullet needs to contain an enemy_damage function. Is that the case?

1 Like

yes for now. it was previously hitbox_is_in_group(“enemies”). but i deleted cuz i didnt want player to take damage immediately when hit by enemies.

@icon("res://entities/node/HitBox.svg")
class_name HitBox
extends Area2D

@export var damage := 10
@export_range(0, 1) var crit_chance: float = 0.05

func _ready():
	randomize()

func bullet_damage() -> int:
	if damage == 0:
		return 0
	else:
		var crit_multiplier = crit(crit_chance)
		var random_multiplier = 1.0
		if crit_multiplier == 2.0:
			random_multiplier = randomness()
		var final_damage = damage * crit_multiplier * random_multiplier
		return int(max(final_damage, damage))  

func enemy_damage() -> int:
	if damage == 0:
		return 0
	else:
		return damage

func crit(chance) -> float:
	var num = randf_range(0, 1)
	if num < chance:
		return 2.0
	else:
		return 1.0

func randomness():
	return randf_range(0.85, 1.15)

Mhm… So are you still seeing any errors right now? Have you tried printing the result of your call to enemy_damage and made sure it’s the correct number?

1 Like

Since HitBox is the class, it tried by var amount = HitBox.damage but i get an error cannot find member "damage" in base "HitBox"

You cannot do that on the base class itself, it has to be an instance of the class! Doing print(hitbox.damage) inside the elif from your hurtbox script that you have posted in your initial post should work.

1 Like

print(hitbox.damage) will print the damage value. but what is this do with amount

If hitbox.damage is the correct value, then all you have to do is supply that as an argument to player_in_hurt_state and everything should work. Do you still get any errors?

1 Like

how exactly can I utilize this as an argument ?

Like to any other function: player_in_hurt_state(hitbox.damage)

1 Like

i get an error “hitbox” not declared

That makes no sense. You just told me you did print(hitbox.damage) in the line before that and it worked. Have you made sure you really get that error in that line and not somewhere else?

1 Like

var hitbox = HitBox
player.player_in_hurt_state(hitbox.damage)

Now i get an error "Invalid get index ‘damage’(on base : GDscript)

You’re still interacting with the base class, not an instance. Look, all I just told you is to change your Player’s Hurtbox script to this:

class_name HurtBox
extends Area2D

func _ready() -> void:
	connect("area_entered", Callable(self, "_on_area_entered"))

func _on_area_entered(hitbox: HitBox) -> void:
	 # ... (I left out some code here for readability)
	elif owner.has_method("player_in_hurt_state") and hitbox.is_in_group("enemy_projectile"):
		print(hitbox.damage) # you said this prints the right number
		owner.player_in_hurt_state(hitbox.damage) # then this should work also!
1 Like