142 lines
4.1 KiB
GDScript
142 lines
4.1 KiB
GDScript
extends Area3D
|
|
class_name Alien
|
|
|
|
const DEATH_SCENE : PackedScene = preload("res://entities/alien/alien_death_effect/alien_death_effect.tscn") as PackedScene
|
|
const DEATH_SCENE_TIME : float = 10.
|
|
|
|
const COLLISION_HEIGHT = 5
|
|
const COLLISION_RADIUS = 1.5
|
|
const NEIGHBOR_DETECTION_MARGIN = 0.5
|
|
|
|
const ATTACK_DISTANCE = 6.
|
|
|
|
const NEIGHBOR_ROTATION = 0.5
|
|
const NEIGHBOR_BUMP_FORCE = 4. * AlienHordeManager.CALCULATE_SCATTER_FREQUENCY
|
|
const NEIGHBOR_BLOCKED_SPEED_FACTOR = 0.5
|
|
const DIRECTION_DRIFT_RANGE = PI/8
|
|
const DAMAGE_EFFECT_DURATION : float = 0.5
|
|
|
|
const DEFAULT_HP : float = 20.
|
|
const DEFAULT_DAMAGE_RATE : float = 1.
|
|
|
|
signal died
|
|
|
|
enum State {RUNNING, ATTACKING, DEAD}
|
|
|
|
@export var size : float = 1.0
|
|
@export var speed : float = 10.
|
|
@export var turn_speed : float = 20.
|
|
|
|
@onready var damage_rate : float = size * DEFAULT_DAMAGE_RATE
|
|
@onready var hp : float = size * DEFAULT_HP
|
|
var state : State = State.RUNNING
|
|
var damage_effect_last : float = 0.
|
|
|
|
var id : int = 0
|
|
var target : Node3D
|
|
|
|
@onready var collision_shape : CollisionShape3D = generate_collision_shape()
|
|
|
|
func _ready():
|
|
monitoring = false
|
|
|
|
func generate_collision_shape() -> CollisionShape3D:
|
|
var new_collision_shape : CollisionShape3D= CollisionShape3D.new()
|
|
var cylinder_shape : CylinderShape3D = CylinderShape3D.new()
|
|
|
|
cylinder_shape.height = COLLISION_HEIGHT
|
|
cylinder_shape.radius = COLLISION_RADIUS
|
|
|
|
new_collision_shape.shape = cylinder_shape
|
|
|
|
new_collision_shape.position = Vector3(0,COLLISION_HEIGHT/2.,0)
|
|
|
|
add_child(new_collision_shape)
|
|
|
|
return new_collision_shape
|
|
|
|
func move(
|
|
delta: float,
|
|
calculate_direction: bool,
|
|
calculate_scatter : bool
|
|
):
|
|
var target_angle = rotation.y
|
|
var current_speed = speed
|
|
|
|
if calculate_direction and target:
|
|
target_angle = calculate_target_angle()
|
|
|
|
if calculate_scatter:
|
|
var collision_right = has_neighbor(1)
|
|
var collision_left = has_neighbor(-1)
|
|
|
|
if collision_left and not collision_right:
|
|
target_angle += - NEIGHBOR_ROTATION
|
|
position += Vector3.RIGHT.rotated(Vector3.UP, rotation.y) * NEIGHBOR_BUMP_FORCE * delta
|
|
elif collision_right and not collision_left:
|
|
target_angle += NEIGHBOR_ROTATION
|
|
position += Vector3.LEFT.rotated(Vector3.UP, rotation.y) * NEIGHBOR_BUMP_FORCE * delta
|
|
elif collision_left and collision_right:
|
|
current_speed = speed * get_random().randf_range(0., NEIGHBOR_BLOCKED_SPEED_FACTOR)
|
|
|
|
rotation.y = lerp_angle(
|
|
rotation.y,
|
|
target_angle,
|
|
min(1, delta*turn_speed)
|
|
)
|
|
|
|
position += Vector3.FORWARD.rotated(Vector3.UP, rotation.y) * current_speed * delta
|
|
|
|
func calculate_target_angle() -> float:
|
|
|
|
var target_angle : float = (
|
|
Vector2(global_position.z,global_position.x)
|
|
- Vector2(target.global_position.z,target.global_position.x)
|
|
).normalized().angle()
|
|
|
|
# Random drift in direction
|
|
target_angle += get_random().randf_range(-DIRECTION_DRIFT_RANGE, DIRECTION_DRIFT_RANGE)
|
|
|
|
return target_angle
|
|
|
|
|
|
|
|
func has_neighbor(direction = 1) -> bool:
|
|
var space_state = get_world_3d().direct_space_state
|
|
|
|
var query = PhysicsRayQueryParameters3D.create(
|
|
global_position,
|
|
global_position + Vector3(
|
|
direction * (COLLISION_RADIUS + NEIGHBOR_DETECTION_MARGIN), 0,0
|
|
).rotated(Vector3.UP, rotation.y)
|
|
)
|
|
query.collide_with_areas = true
|
|
query.exclude.append(get_rid())
|
|
|
|
var result = space_state.intersect_ray(query)
|
|
|
|
return result != {}
|
|
|
|
func attack_target(delta : float):
|
|
if target and target.has_method("hit"):
|
|
target.hit(delta * damage_rate)
|
|
|
|
func hit(damage = 1.):
|
|
hp = max(0, hp - damage)
|
|
damage_effect_last = DAMAGE_EFFECT_DURATION
|
|
if hp == 0:
|
|
die()
|
|
|
|
func die():
|
|
died.emit()
|
|
state = State.DEAD
|
|
collision_shape.disabled = true
|
|
add_child(DEATH_SCENE.instantiate())
|
|
await get_tree().create_timer(DEATH_SCENE_TIME).timeout
|
|
queue_free()
|
|
|
|
func get_random() -> RandomNumberGenerator:
|
|
var random : RandomNumberGenerator = RandomNumberGenerator.new()
|
|
random.seed = id
|
|
|
|
return random |