162 lines
5.5 KiB
GDScript
162 lines
5.5 KiB
GDScript
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
|
|
@export var time_to_lose_target = 1.0
|
|
|
|
@onready var target_lost_timer = time_to_lose_target
|
|
|
|
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
|