extends Unit class_name Enemy @export var player: Player @export var chasing_range := 30.0 @export var attack_range := 3.0 @export var enemy_reward_gold = 100 @export var attacks := ["Unarmed_Melee_Attack_Punch_A", "Unarmed_Melee_Attack_Punch_B", "Unarmed_Melee_Attack_Kick"] const ANIM_IWR_PARAM := "parameters/IWR/blend_position" @onready var navigation_agent: NavigationAgent3D = $NavigationAgent3D func _ready() -> void: health = maximum_health stamina = maximum_stamina reward_gold = enemy_reward_gold state_changed.connect(_on_state_changed) func _physics_process(delta: float) -> void: # Guards if state == States.dead or player == null: return if player.state == States.dead: state = States.idle return # Apply gravity velocity.y += -gravity * delta # Zustandsableitung var distance_to_player := global_position.distance_to(player.global_position) var desired_state := _determine_state(distance_to_player) if desired_state != state: state = desired_state # Navigation immer auf Spieler ausrichten navigation_agent.set_target_position(player.global_position) # Bewegungs-Update je nach Zustand match state: States.chasing: _update_chasing_navigation() States.attacking, States.idle: _stop_horizontal_movement() _: pass move_and_slide() func _determine_state(distance: float) -> States: if distance <= attack_range: return States.attacking elif distance <= chasing_range: return States.chasing else: return States.idle func _update_chasing_navigation() -> void: if navigation_agent.is_navigation_finished() or navigation_agent.is_target_reached(): _stop_horizontal_movement() return var next_path_position: Vector3 = navigation_agent.get_next_path_position() # Prüfen, ob die Position unterschiedlich ist, bevor look_at aufgerufen wird if not model.global_position.is_equal_approx(next_path_position): model.look_at(next_path_position, Vector3.UP) # Nur horizontale Bewegung steuern, vertikale Komponente beibehalten var to_next := next_path_position - global_position to_next.y = 0.0 var desired := to_next.normalized() * speed var vy := velocity.y velocity.x = desired.x velocity.z = desired.z velocity.y = vy # Animation (IWR) nach lokaler Geschwindigkeit blenden var v_local: Vector3 = velocity * model.transform.basis anim_tree.set(ANIM_IWR_PARAM, Vector2(v_local.x, -v_local.z) / speed) func _stop_horizontal_movement() -> void: var vy := velocity.y velocity = Vector3.ZERO velocity.y = vy func _on_state_changed(_old_state: States, new_state: States) -> void: name_changed.emit(unit_name, States.keys()[new_state]) match new_state: States.attacking: if can_spend_stamina(attack_cost): spend_stamina(attack_cost) if attacks.size() > 0: anim_state.travel(attacks.pick_random())