ghost-delivery/Enemy/enemy.gd

95 lines
2.8 KiB
GDScript

extends CharacterBody2D
@export var patrol_points: PackedVector2Array = PackedVector2Array()
@export var move_speed: float = 60.0
@export var wait_time: float = 1.0
@export var detection_fill_rate: float = 1.7
@export var detection_decay_rate: float = 0.5
@onready var animation_player: AnimationPlayer = $Cat/AnimationPlayer
@onready var sprite: Sprite2D = $Cat/Sprite2D
@onready var vision_cone: Node2D = $VisionCone2D
@onready var vision_area: Area2D = $VisionCone2D/VisionConeArea
@onready var vision_renderer: Polygon2D = $VisionCone2D/VisionConeRenderer
const NORMAL_COLOR := Color(0.95, 0.78, 0.22, 0.45)
const ALERT_COLOR := Color(0.9, 0.12, 0.12, 0.55)
var _points: Array[Vector2] = []
var _target_index: int = 0
var _wait_timer: float = 0.0
var _facing := Vector2.DOWN
var _detection: float = 0.0
var _sees_ghost: bool = false
var _ghost_in_cone: Ghost = null
func _ready() -> void:
animation_player.play("idle_sit")
if patrol_points.is_empty():
_points = [global_position]
else:
for p in patrol_points:
_points.append(global_position + p)
vision_area.body_entered.connect(_on_vision_body_entered)
vision_area.body_exited.connect(_on_vision_body_exited)
_update_cone_rotation()
func _physics_process(delta: float) -> void:
if GameManager.game_over:
velocity = Vector2.ZERO
return
_patrol(delta)
_update_detection(delta)
func _patrol(delta: float) -> void:
if _points.size() <= 1:
velocity = Vector2.ZERO
return
var target: Vector2 = _points[_target_index]
var to_target := target - global_position
if to_target.length() < 4.0:
velocity = Vector2.ZERO
if animation_player.current_animation != "idle_sit":
animation_player.play("idle_sit")
_wait_timer += delta
if _wait_timer >= wait_time:
_wait_timer = 0.0
_target_index = (_target_index + 1) % _points.size()
else:
var dir := to_target.normalized()
velocity = dir * move_speed
if animation_player.current_animation != "walk":
animation_player.play("walk")
_set_facing(dir)
move_and_slide()
func _set_facing(dir: Vector2) -> void:
if dir.length() < 0.01:
return
_facing = dir
if absf(dir.x) > 0.05:
sprite.flip_h = dir.x > 0
_update_cone_rotation()
func _update_cone_rotation() -> void:
vision_cone.global_rotation = _facing.angle() - PI / 2.0
func _update_detection(delta: float) -> void:
if _ghost_in_cone != null and _ghost_in_cone.held_item != null:
_detection += detection_fill_rate * delta
else:
_detection -= detection_decay_rate * delta
_detection = clampf(_detection, 0.0, 1.0)
vision_renderer.color = NORMAL_COLOR.lerp(ALERT_COLOR, _detection)
GameManager.report_npc_alert(get_instance_id(), _detection)
if _detection >= 1.0:
GameManager.report_caught()
func _on_vision_body_entered(body: Node2D) -> void:
if body is Ghost:
_ghost_in_cone = body
func _on_vision_body_exited(body: Node2D) -> void:
if body is Ghost:
_ghost_in_cone = null