How to change enemy's state while keeping other duplicated enemy in certain state

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

Hello Godot community. I am making 2D platformer.
I have made an enemy which has four different states (Idle, Chase, Attack, Death)
Enemy has PlayerDetector Area2D and Hitbox Area2D. Once Player entered PlayerDetector Area2D, Enemy will be in Chase State. As soon as Player exited PlayerDetector Area2D, Enemy will return to Idle State. If player entered Hitbox Area2D, Enemy will be in Attack State. What I would like to do is, when one enemy is in Attack State, change all other duplicated enemies to Idle State. How could I achieve this? I would appreciate if you could kindly teach me.

What I have tried so far:
① Disable Player’s PlayerDetector Area2D, so enemy will no longer be in Chase State.
But this caused a problem that all of the enemies will be in Idle State, as soon as Player’s playerDetector Area2D is disabled, since it will trigger Enemy’s PlayerDetector Area2D exited function.

② send a signal has_player == true and enable enemy’s Chase State only if has_player == false. This funnily worked only if duplicated enemies are in Idle State. If enemies are already in Chase State, they would not return to Idle State.

Enemy Script:

extends KinematicBody2D

var max_hp = 100
var current_hp= max_hp
var defense = 0

var speed = 100
var chase_speed = 170
var gravity = 800
var direction = 1
var velocity = Vector2.ZERO

var has_player = false
var has_trigger = false

onready var _animation_player: AnimationPlayer = $Position2D/CatSkin/AnimationPlayer
onready var sprite = get_node("Position2D/CatSkin")
onready var flashTimer = $flashTimer
onready var player = get_node("../Player")
onready var EnemyCollision = $EnemyCollision
onready var label = $Label
onready var bullet = get_node("../Bullet02")

signal button_mashing_start()


enum state {IDLE, CHASE, GRAB, FINISH, DEATH}
var enemy_state = state.IDLE


func _ready():
	enemy_state = state.IDLE
	$Position2D/CatSkin/AnimationPlayer.connect("animation_finisehd", self, "on_animation_finished")
	Autoload.connect("button_mashing_cleared", self, "button_mashing_cleared_signal_recieved")
	Autoload.connect("button_mashing_failed", self, "button_mashing_failed_signal_recieved")
	Autoload.connect("has_triggered", self, "has_triggered_signal_recieved")
	current_hp = max_hp
	pass

func _physics_process(delta):
	
	match enemy_state:
		state.IDLE:
			label.text="IDLE"
			_animation_player.play("Cat Idle")
			if velocity.x>0:
				$Position2D.scale.x=1
			elif velocity.x<0:
				$Position2D.scale.x=-1
			
			velocity.y += gravity * delta
			velocity.x = speed * direction
			
			if EnemyCollision.is_colliding():
				velocity += EnemyCollision.get_push_vector() * delta * 100
			velocity = move_and_slide(velocity, Vector2.UP)
			
			if is_on_wall() or not $Position2D/FloorRay.is_colliding() and is_on_floor():
				direction = direction * -1
				flip()
				
				velocity.y += gravity * delta
				velocity.x = speed * direction

		state.CHASE:
			label.text="CHASE"
			_animation_player.play("Cat Chase")
			
			if velocity.x>0:
				$Position2D.scale.x=1
			elif velocity.x<0:
				$Position2D.scale.x=-1
	
			velocity = Vector2(sign(player.global_position.x - global_position.x), 0).normalized()
			
			
			if not is_on_floor():
				velocity.y += gravity * delta
			
			if EnemyCollision.is_colliding():
				velocity += EnemyCollision.get_push_vector() * delta * 100
			velocity = move_and_slide(velocity * chase_speed)

		state.GRAB:
			label.text="GRAB01"
			_animation_player.play("Cat Grab Attack")
			
			velocity.y = 0
			velocity.x = 0 * direction 
			velocity = move_and_slide(velocity, Vector2.UP)
			emit_signal("button_mashing_start")

		state.FINISH:
			label.text="GRAB02"
			_animation_player.play("Cat Grab Attack 02")
			
			velocity.y = 0
			velocity.x = 0 * direction
			velocity = move_and_slide(velocity, Vector2.UP)

		state.DEATH:
			label.text="DEATH"
			_animation_player.play("Cat Death")
			
			has_player = true
			velocity.x = 0 * direction 
			velocity = move_and_slide(velocity, Vector2.UP)
			Autoload.emit_signal("screen_shake")
			$HitboxCat/CollisionShape2D.set_deferred("disabled", true)
			$HitboxCat/CollisionShape2D2.set_deferred("disabled", true)
			$HurtboxCat/CollisionShape2D.set_deferred("disabled", true)
			$HurtboxCat/CollisionShape2D2.set_deferred("disabled", true)





func _on_DeathSound_finished():
	queue_free()


func _on_AnimationPlayer_animation_finished(anim_name):
	print("Grab02 End!")
	Autoload.emit_signal("grab_finished", self)
	enemy_state = state.CHASE
	has_player = false

func flip(): #Flip Sprite
	if direction>0:
		$Position2D.scale.x=1
	elif direction<0:
		$Position2D.scale.x=-1


func _on_HurtboxCat_area_entered(area):
	if area.name == "Bullet02":
		print("I got hit!")
		$HitSound.play()
		
		var base_damage = area.damage
		self.current_hp -= base_damage
		if self.current_hp <= 0:
			print("IM DEAD!")
			enemy_state = state.DEATH
			$DeathSound.play()

func _on_HitboxCat_area_entered(area):
		if area.name == "HurtboxPlayer":
				enemy_state = state.GRAB
				print("Grab01!")
				pass


func _on_PlayerDetector_area_entered(area: Area2D): #Player Search Area. Chase when Player entered
	if area.name == "PlayerDetector":
		if has_player == false:
			print("found you !")
			enemy_state = state.CHASE
		else:
			enemy_state = state.IDLE

func _on_PlayerDetector_area_exited(area: Area2D): #Player Search Area. Stop Chase when Player exited
	if area.name == "PlayerDetector":
		if has_player == false:
			print("lost you !")
			enemy_state = state.IDLE

func has_triggered_signal_recieved():
	has_player = true
	print("has_player=false")
:bust_in_silhouette: Reply From: Asthmar

if all the duplicate enemies are under one parent node then once one enemy enters the chase state you can call a function that does something like this.

 for i in $Enemyholder.get_children: ## getting all the enemies
      if i.state != chase: ## excluding the enemy that is chasing player
            i.state = idle #### setting the state to idle and you can maybe disable collision of the enemy that are supposed to be idle here too

Thank you Asthmar.
I tried as you suggested, but I receive an error “Invalid get index ‘get_children’ (on base: ‘null instance’)”

amaou310 | 2022-09-02 05:42

Never mind. I think I solved this.
I used autoload. I made enemy to emit signal(“has_player”) when collided with player. Enemy will then receive signal(“has_player”) and do if enemy_state != state.Attack: disable PlayerDetector Area2D. And enabled PlayerDetector Area2D when state.Attack finished.

Thank you so much Asthmar for the help. much appreciated

amaou310 | 2022-09-02 09:29

You’re welcome, Glad you found a solution! Also, I think I see why my code didn’t work I forgot parentheses after get_children it should have been get_children().

Asthmar | 2022-09-02 11:47