extends CharacterBody3D @onready var anim_player: AnimationPlayer = $cow/AnimationPlayer @export var dest_array:Array[Marker3D] @onready var navigation_agent: NavigationAgent3D = $NavigationAgent3D @onready var idle_timer: Timer = $Timer enum NPCSTATE { IDLE, WALKING } var cur_state : NPCSTATE var cur_destination: Marker3D var speed = 4 @onready var audio_moo: AudioStreamPlayer3D = $AudioMoo @onready var moo_timer: Timer = $MooTimer var one_time_each : bool = false func _ready() -> void: _set_state( NPCSTATE.IDLE ) func _on_timer_timeout() -> void: decide_next_state() func _set_state(new_state:NPCSTATE): cur_state = new_state match cur_state: NPCSTATE.IDLE: idle_timer.start( randf_range(3, 5) ) anim_player.play("metarig|metarig|walking", -1, .7 ) NPCSTATE.WALKING: anim_player.play("metarig|metarig|walking", -1, 1.7 ) func _physics_process(delta: float) -> void: match cur_state: NPCSTATE.IDLE: pass NPCSTATE.WALKING: var next_path_pos:Vector3 = navigation_agent.get_next_path_position() var new_vel : Vector3 = global_position.direction_to( next_path_pos ) * speed velocity = new_vel var look_at_target : Vector3 = Vector3( next_path_pos.x, global_position.y, next_path_pos.z ) if not global_position.is_equal_approx( look_at_target ): look_at( look_at_target ) move_and_slide() func decide_next_state(): if cur_state == NPCSTATE.IDLE: cur_destination = dest_array.pick_random() navigation_agent.target_position = cur_destination.global_position _set_state( NPCSTATE.WALKING ) func _on_navigation_agent_3d_navigation_finished() -> void: _set_state( NPCSTATE.IDLE ) func _on_moo_timer_timeout() -> void: moo_timer.wait_time = randf_range(40, 60) audio_moo.pitch_scale = randf_range( .8, 1.2 ) audio_moo.play()