Attack from different angles (doesn't damage!)

The player in my game can attack in 4 sides. It essentially has 4 animations. I was able to successfully implement the damage mechanics in the right attack, but I am now struggling to do the same for the other attacks.


func _ready():
	...
	%SwordHitRight.set_deferred("monitoring", false)  # extra safety
	%SwordHitLeft.set_deferred("monitoring", false)  # extra safety
	%SwordHitTop.set_deferred("monitoring", false)  # extra safety
	%SwordHitBottom.set_deferred("monitoring", false)  # extra safety

	...

	if angle_in_degrees >= 315 or angle_in_degrees < 45:
		%SwordHitRight.monitoring = true
		%Bluwee.play_attack()  # Mouse is above the player
	elif angle_in_degrees >= 45 and angle_in_degrees < 135:
		%SwordHitBottom.monitoring = true
		%Bluwee.play_down_attack()  # Mouse is to the right of the player
	elif angle_in_degrees >= 225 and angle_in_degrees < 315:
		%SwordHitTop.monitoring = true
		%Bluwee.play_up_attack()  # Mouse is below the player
	elif angle_in_degrees >= 135 and angle_in_degrees < 225:
		%SwordHitBottom.monitoring = true
		%Bluwee.play_left_attack()
...

func handle_attack_animation():
	...
	%SwordHitRight.monitoring = false
	%SwordHitLeft.monitoring = false
	%SwordHitTop.monitoring = false
	%SwordHitBottom.monitoring = false

...

func _on_attack_hit():
	var current_anim = %AnimationPlayer.current_animation
	print("Attack Hit during: ", current_anim)  # Debug which animation fired
	
	for area in %SwordHitRight.get_overlapping_areas():  # Check ALL current overlaps
		if area.is_in_group("hurtbox"):
			var target = area.get_parent()
			if target.has_method("take_damage"):
				target.take_damage(GlobalVariables.player_attack_damage / 2.0)  # Half damage per swing
				print("RIGHT hit ", target.name, " | Frame: ", $AnimationPlayer.current_animation_position)
			else:
				print("RIGHT: target missing take_damage() method")
			
	for area in %SwordHitLeft.get_overlapping_areas():
		if area.is_in_group("hurtbox"):
			var target = area.get_parent()
			if target.has_method("take_damage"):
				target.take_damage(GlobalVariables.player_attack_damage / 2.0)
				print("LEFT hit ", target.name, " | Frame: ", $AnimationPlayer.current_animation_position)
			else:
				print("LEFT: target missing take_damage() method")

Sheep.gd code:

extends CharacterBody2D

var health = 50.0

func _ready():
	var sheep_anim = $AnimationPlayer
	sheep_anim.play("idle")
	$Hurtbox.add_to_group("hurtbox")

func take_damage(amount):
	health -= amount
	print("Sheep took ", amount, " damage, health remaining: ", health)
	%ProgressBar.value = health
	if health <= 0:
		die()

func die():
	print("Sheep has died.")
	
	# Spawn the Dead scene at sheep's position
	var dead_scene = load("res://dead.tscn")
	var dead_instance = dead_scene.instantiate()
	dead_instance.position = position
	get_parent().add_child(dead_instance)
	
	queue_free()  # Remove the sheep from the scene

I have no idea why it isn’t damaging the sheep. Someone please help me out.

is _on_attack_hit connected to some signal? Do you get any errors or warnings? What prints?

Do you have a sheep hurt box? Then you could implement some sort of code like

func _on_area_entered(body: node2D):
 if body.name == "Player_attack_box" // use the real name for it instead
  take_damage(amount)

I fixed it by adding this code:

func apply_attack_damage_right():
	for area in %SwordHitRight.get_overlapping_areas():
		if area.is_in_group("hurtbox"):
			var target = area.get_parent()
			if target.has_method("take_damage"):
				target.take_damage(GlobalVariables.player_attack_damage / 2.0)
				print("RIGHT hit ", target.name)

func apply_attack_damage_left():
	print("left attack function called!")
	for area in %SwordHitLeft.get_overlapping_areas():
		if area.is_in_group("hurtbox"):
			var target = area.get_parent()
			if target.has_method("take_damage"):
				target.take_damage(GlobalVariables.player_attack_damage / 2.0)
				print("LEFT hit ", target.name)

func apply_attack_damage_up():
	for area in %SwordHitTop.get_overlapping_areas():
		if area.is_in_group("hurtbox"):
			var target = area.get_parent()
			if target.has_method("take_damage"):
				target.take_damage(GlobalVariables.player_attack_damage / 2.0)
				print("UP hit ", target.name)

func apply_attack_damage_down():
	for area in %SwordHitBottom.get_overlapping_areas():
		if area.is_in_group("hurtbox"):
			var target = area.get_parent()
			if target.has_method("take_damage"):
				target.take_damage(GlobalVariables.player_attack_damage / 2.0)
				print("DOWN hit ", target.name)

But the left attack still doesn’t work, I assume it’s because the left animation is just the flip of the right animation, but I still don’t see why it wouldn’t work. Thing is it does print out the “left attack function called!”

Your functions are almost identical, you should try to make a more generic function that just takes in a direction of the hit and applies the logic to it. It will be much easier to debug later too.
Here’s an example:

func apply_attack_damage(direction: String) -> void:
	var sword_hit_node = {
		"right": %SwordHitRight,
		"left": %SwordHitLeft,
		"up": %SwordHitTop,
		"down": %SwordHitBottom,
	}.get(direction, null)

	if sword_hit_node == null:
		push_warning("Invalid attack direction: %s" % direction)
		return

	for area in sword_hit_node.get_overlapping_areas():
		if area.is_in_group("hurtbox"):
			var target = area.get_parent()
			if target.has_method("take_damage"):
				target.take_damage(GlobalVariables.player_attack_damage / 2.0)
				print("%s hit %s" % [direction.upper(), target.name])

In this case, you can just call:

apply_attack_damage("right")
apply_attack_damage("left")
apply_attack_damage("up")
apply_attack_damage("down")
etc.
2 Likes

well, ye, but the thing is, I call the function in the animation frame itself yk? In the method track.