Blobby/src/Actors/Enemies/Beings/Flyer.gd

164 lines
5.6 KiB
GDScript

extends Actor
const PhysicsFunc = preload("res://src/Utilities/Physic/PhysicsFunc.gd")
onready var players = get_tree().get_nodes_in_group("player")
onready var vision_raycast: RayCast2D = $VisionRayCast
onready var orientation: RayCast2D = $Orientation
onready var feeler_raycast: RayCast2D = $FeelerRayCast
onready var nav_agent: NavigationAgent2D = $NavigationAgent2D
onready var tilemap: TileMap = $"../%TileMap"
onready var target_lost_timer: Timer
onready var update_navigation_timer: Timer
onready var rng = RandomNumberGenerator.new()
# Is given in blocks
export var vision_distance := 6.0
export var blindspot_angle := 20
export var loose_target_seconds := 3.0
export var patrolling := false
export var aggressive := true
export var acceleration := 50
export var patrolling_slowdown := 0.80
export var max_speed := 90
export var weight := 0.15
var path_direction: Vector2 = Vector2()
var previous_direction := Vector2()
var target: Object = null
var patrol_waypoints := []
var patrol_waypoint_index := 0
var next_waypoint: Vector2
var is_hurt := false
var has_reversed := false
var avoidance_raycasts := []
var slow_down_factor := 1.0
var detect_timer := 0.0
func _ready():
spawn_avoidance_raycasts(16, 20)
target_lost_timer = Timer.new()
target_lost_timer.set_one_shot(true)
target_lost_timer.connect("timeout", self, "lose_target")
add_child(target_lost_timer)
update_navigation_timer = Timer.new()
update_navigation_timer.connect("timeout", self, "update_navigation")
add_child(update_navigation_timer)
update_navigation_timer.start(0.3)
# TODO Hat immer den Spawn im Patrolpath
patrol_waypoints.append(global_position)
for waypoint in $PatrolPath.get_children():
patrol_waypoints.append(waypoint.global_position)
pass
func searching() -> Vector2:
slow_down_factor = patrolling_slowdown
if(aggressive && detect_timer > 0.33):
detect_player()
detect_timer = 0.0
if(patrolling && nav_agent.is_target_reached()):
next_waypoint = get_next_patrol_target()
update_navigation()
elif(patrolling):
next_waypoint = patrol_waypoints[patrol_waypoint_index]
else:
# Spawn location
return patrol_waypoints[0]
return nav_agent.get_next_location()
func get_next_patrol_target() -> Vector2:
var waypoint_count = patrol_waypoints.size()
for wp in patrol_waypoints:
if next_waypoint == wp:
patrol_waypoint_index = patrol_waypoint_index + 1 if patrol_waypoint_index < waypoint_count - 1 else 0
return patrol_waypoints[patrol_waypoint_index]
patrol_waypoint_index = 0
return patrol_waypoints[0]
func hunting() -> Vector2:
slow_down_factor = 1.0
if(detect_timer > 0.33):
detect_player()
detect_timer = 0.0
next_waypoint = players[0].global_position - Vector2(0,9)
return nav_agent.get_next_location()
func detect_player() -> void:
var player
if(players.empty()):
# print("no player found")
return
player = players[0]
#TODO Depends on height of blobby sprite since blobbys bottom and not his middle is on y=0
vision_raycast.cast_to = (player.global_position - global_position - Vector2(0,9)).normalized() * 24 * vision_distance
var ray_angle_to_facing = vision_raycast.cast_to.angle_to(orientation.cast_to)
vision_raycast.force_raycast_update()
var collider = vision_raycast.get_collider()
if(abs(ray_angle_to_facing) < PI/2-deg2rad(blindspot_angle) && collider != null && collider.is_in_group("player")):
target_lost_timer.stop()
target = collider
# print("target found")
elif(target != null && target_lost_timer.is_stopped()):
target_lost_timer.start(loose_target_seconds)
func execute_movement(delta: float) -> void:
detect_timer += delta
var next_direction = lerp(previous_direction, path_direction - global_position, 0.5)
previous_direction = next_direction
orientation.cast_to = Vector2(sign(next_direction.x),0)*50
var avoidance_obstacle_distance = average_collision_vector(avoidance_raycasts)
next_direction = next_direction.normalized() + avoidance_obstacle_distance.rotated(PI).normalized()
# TODO Make parameters more tunable
velocity = move_and_slide(PhysicsFunc.two_step_euler_vec(velocity, next_direction.normalized() * acceleration * slow_down_factor, weight, delta)
,FLOOR_NORMAL, false, 4, 0.785398,false)
velocity = velocity/max((velocity.length()/(max_speed*slow_down_factor)),1)
func average_collision_vector(var raycasts: Array) -> Vector2:
var total_distances = Vector2()
for raycast in raycasts:
if !raycast.is_colliding():
continue
var collision_point = self.to_local(raycast.get_collision_point())
total_distances += collision_point
return total_distances/raycasts.size()
func spawn_avoidance_raycasts(var raycount: int, var length: float = 24) -> void:
var direction: float = 0
while direction <= 2*PI:
var raycast: RayCast2D = RayCast2D.new()
raycast.enabled = true
raycast.exclude_parent = true
raycast.collide_with_areas = true
raycast.collide_with_bodies = true
# Layers 4, 5 & 6
raycast.collision_mask = 56
raycast.cast_to = Vector2(length, 0).rotated(direction)
add_child(raycast)
avoidance_raycasts.append(raycast)
direction += (2*PI)/raycount
# Checks the feeler ray for collisions and returns collision or null
func check_feeler(v: Vector2, _offset = Vector2(0,0)) -> Object:
var prev_position = feeler_raycast.position
feeler_raycast.position += _offset
feeler_raycast.cast_to = v
feeler_raycast.force_raycast_update()
feeler_raycast.position = prev_position
return feeler_raycast.get_collider()
func lose_target() -> void:
# print("flyer target lost")
target = null
func update_navigation() -> void:
nav_agent.set_target_location(next_waypoint)
func die() -> void:
levelState.kills += 1
queue_free()
func get_facing_direction() -> float:
return orientation.cast_to.x