extends State class_name EnemyState @export var enemy : CharacterBody3D @export var move_speed : float = 3 @export var body_turn_speed : float = 3 @export var turret_speed : float = 6.0 @export var can_see : bool = true # indicates whether the enemy is able to see things in the state @export var poi_investigate : bool = false @export var poi_change_to_search : bool = false @export var poi_update_waypoint_immediately : bool = false var move_target var look_target func enemy_has_target(): if enemy.player_last_seen != null: return true if enemy.point_of_interest != null: return true return false func update_move_target(): if enemy.player_last_seen != null: return enemy.player_last_seen elif enemy.point_of_interest != null: return enemy.point_of_interest["point"] else: return enemy.global_position func attack_on_sight(): if can_see: if enemy.is_player_in_area() and enemy.is_player_visible(): enemy.cache_player_pos() Transitioned.emit(self,"attack") if enemy.turret_look.is_colliding(): if enemy.turret_look.get_collider() is Player: enemy.cache_player_pos() Transitioned.emit(self,"attack") func search_on_lost_target(): if !enemy.is_player_visible(): Transitioned.emit(self,"attack") func move_target_adj(position): return Vector3(position.x,enemy.global_position.y,position.z) func search_for_suspicious_sounds(): if enemy.point_of_interest != null: enemy.player_last_seen = null Transitioned.emit(self,"search") func rotate_to_face2D(object,target,target_offset_angle,delta,turn_speed): #to allow both nodes and positions to be passed to this function, test the target and use Vector3 coords var target_transformed if target == null: target_transformed = object.global_position elif target is Vector3: target_transformed = target elif target is Node3D: target_transformed = target.global_position var pos2d = Vector2(object.global_position.x,object.global_position.z) var target_pos2d = Vector2(target_transformed.x,target_transformed.z) var direction = (pos2d - target_pos2d) return lerp_angle(object.global_rotation.y,atan2(direction.x,direction.y) + deg_to_rad(target_offset_angle),delta * turn_speed) func rotate_to_face3D(object : Node3D,target,target_offset_angle : Vector3,delta : float,turn_speed : float): if target == null: return var target_positon if target is Vector3: target_positon = target else: target_positon = target.global_position var desired_rotation = object.global_transform.looking_at(target_positon,Vector3.UP).basis.get_euler() var current_rotation = object.global_rotation #Interpolate each axis current_rotation.x = lerp_angle(current_rotation.x,desired_rotation.x + target_offset_angle.x,delta * turn_speed) current_rotation.y = lerp_angle(current_rotation.y,desired_rotation.y + target_offset_angle.y,delta * turn_speed) current_rotation.z = lerp_angle(current_rotation.z,desired_rotation.z + target_offset_angle.z,delta * turn_speed) #clamp pitch var max_downward_pitch = deg_to_rad(85) var min_upward_pitch = deg_to_rad(-45) current_rotation.x = clamp(current_rotation.x,min_upward_pitch, max_downward_pitch) object.global_rotation = current_rotation func move_to_nav_point(delta): var destination = enemy.nav_agent.get_next_path_position() var local_destination = destination - enemy.global_position var new_velocity = local_destination.normalized() * move_speed enemy.nav_agent.set_velocity(new_velocity) enemy.global_rotation.y = rotate_to_face2D(enemy,destination,0,delta,body_turn_speed) func velocity_computed(safe_velocity): enemy.velocity = enemy.velocity.move_toward(safe_velocity,.25) func turret_look2D(delta): enemy.turret_look.global_rotation.y = rotate_to_face2D(enemy.turret_look,look_target,0,delta,turret_speed) func turret_scan(target_offset : Vector3,delta): rotate_to_face3D(enemy.turret_look,look_target,target_offset,delta,turret_speed) func turret_look3D(delta): rotate_to_face3D(enemy.turret_look,look_target,Vector3.ZERO,delta,turret_speed) func turret_scan_look(target,scan_direction,delta): enemy.turret_look.global_rotation.y = rotate_to_face2D(enemy.turret_look,target,scan_direction,delta,turret_speed) func investigate_points_of_interest(point,loudness,max_distance): if poi_investigate: var distance_to_point = enemy.global_position.distance_to(point) if distance_to_point <= max_distance: var perceived_loudness = 1/distance_to_point * loudness var add_point = {"point" : point, "loudness" : perceived_loudness} enemy.points_of_interest.append(add_point) if poi_change_to_search: Transitioned.emit(self,"search") if poi_update_waypoint_immediately: get_new_point_of_interest() func has_points_to_investigate(): if enemy.player_last_seen != null: return true elif enemy.points_of_interest.size() > 0: return true else: return false func get_new_point_of_interest(): if enemy.player_last_seen != null: move_target = enemy.player_last_seen enemy.player_last_seen = null enemy.nav_agent.set_target_position(move_target) elif enemy.points_of_interest.size() > 0: var point_of_interest = enemy.points_of_interest.pop_back() move_target = point_of_interest["point"] enemy.nav_agent.set_target_position(move_target) func clear_points_of_interest(): enemy.points_of_interest = [] func debug_marker(target_pos): if enemy.debug_tools: enemy.debug_tracker.visible = true enemy.debug_tracker.global_position = target_pos else: enemy.debug_tracker.visible = false func update_minimap(priority,duration,color): SignalBus.emit_signal("ui_minimap_point",enemy,enemy.global_position,1,duration,color)