extends Node enum State { IDLE, CHASE, ATTACK, RETURN } func _physics_process(delta: float) -> void: _process_group(delta, EnemyData) _process_group(delta, BossData) func _process_group(delta: float, data_source: Node) -> void: for entity in data_source.entities: if not is_instance_valid(entity) or not data_source.is_alive(entity): continue var data: Dictionary = data_source.entities[entity] var state: int = data["state"] match state: State.IDLE: entity.velocity.x = 0 entity.velocity.z = 0 State.CHASE: _chase(entity, data, data_source) State.ATTACK: _attack(entity, data, data_source, delta) State.RETURN: _return_to_spawn(entity, data, data_source, delta) func _chase(entity: Node, data: Dictionary, data_source: Node) -> void: if not is_instance_valid(data["target"]): data["state"] = State.RETURN return var base: EnemyStats = data_source.get_base(entity) var attack_range: float = base.attack_range var dist: float = entity.global_position.distance_to(data["target"].global_position) if dist <= attack_range: data["state"] = State.ATTACK return var nav_agent: NavigationAgent3D = entity.get_node_or_null("NavigationAgent3D") if not nav_agent: return nav_agent.target_position = data["target"].global_position var next_pos := nav_agent.get_next_path_position() var direction: Vector3 = (next_pos - entity.global_position).normalized() direction.y = 0 entity.velocity.x = direction.x * base.speed entity.velocity.z = direction.z * base.speed func _attack(entity: Node, data: Dictionary, data_source: Node, delta: float) -> void: data["attack_timer"] -= delta if not is_instance_valid(data["target"]): data["state"] = State.RETURN return var base: EnemyStats = data_source.get_base(entity) var dist: float = entity.global_position.distance_to(data["target"].global_position) if dist > base.attack_range: data["state"] = State.CHASE return if data["attack_timer"] <= 0: data["attack_timer"] = base.attack_cooldown EventBus.damage_requested.emit(entity, data["target"], base.attack_damage) entity.velocity.x = 0 entity.velocity.z = 0 func _return_to_spawn(entity: Node, data: Dictionary, data_source: Node, delta: float) -> void: var spawn_pos: Vector3 = data["spawn_position"] var dist: float = entity.global_position.distance_to(spawn_pos) if dist < 1.0: data["state"] = State.IDLE entity.velocity.x = 0 entity.velocity.z = 0 return var base: EnemyStats = data_source.get_base(entity) var nav_agent: NavigationAgent3D = entity.get_node_or_null("NavigationAgent3D") if not nav_agent: return nav_agent.target_position = spawn_pos var next_pos := nav_agent.get_next_path_position() var direction: Vector3 = (next_pos - entity.global_position).normalized() direction.y = 0 entity.velocity.x = direction.x * base.speed entity.velocity.z = direction.z * base.speed _regenerate(entity, data, data_source, delta) func _regenerate(entity: Node, data: Dictionary, data_source: Node, delta: float) -> void: var health: float = data["health"] var max_health: float = data["max_health"] if health < max_health: var base: EnemyStats = data_source.get_base(entity) var rate: float = base.regen_fast if health >= max_health * 0.99: rate = base.regen_slow health = min(health + max_health * rate * delta, max_health) data_source.set_health(entity, health)