98 lines
2.6 KiB
GDScript
98 lines
2.6 KiB
GDScript
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()
|
|
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())
|