grunk/src/world/grunk_beast/grunk_beast.gd

103 lines
2.4 KiB
GDScript

class_name GrunkBeast extends CharacterBody3D
## Grunk beast controller
#region Constants
const STALKING_SOUND_LIMIT := 20.0
#endregion
#region Exported Properties
@export var base_speed := 60.0
@export var pursuit_speed := 180.0
@export var debug_destroy: bool:
set(value):
queue_free()
#endregion
#region Member Variables
var gravity: Vector3 = (
ProjectSettings.get_setting("physics/3d/default_gravity")
* ProjectSettings.get_setting("physics/3d/default_gravity_vector")
)
var pathfinding := true
@onready var nav_agent: NavigationAgent3D = %NavAgent
@onready var nav_probe: NavigationAgent3D = %NavProbe
@onready var stalking_timer: Timer = %StalkingTimer
@onready var blackboard: Blackboard = %Blackboard
#endregion
#region Character Controller
func is_pursuing() -> bool:
return blackboard.has_value("pursuit_target")
func is_stalking() -> bool:
return false # TODO
func get_speed() -> float:
if is_pursuing():
return pursuit_speed
return base_speed
func path_shorter_than(target: Vector3, limit: float) -> bool:
var length := 0.0
var last_pos := global_position
nav_probe.target_position = target
# Fail early if the target is unreachable.
# NOTE: this call also forces a navigation path refresh! Do not remove!
if not nav_probe.is_target_reachable():
return false
var path := nav_probe.get_current_navigation_path().slice(
nav_probe.get_current_navigation_path_index()
)
if not path:
# Shouldn't be possible, but if it is it would cause problems if we didn't fail here
print_debug("Target is reachable but has no path (tell the developer!)")
return false
# Integrate along path
for waypoint: Vector3 in path:
length += last_pos.distance_to(waypoint)
if length > limit:
return false
last_pos = waypoint
return true
func _physics_process(delta: float) -> void:
var motion := Vector3.ZERO
if pathfinding and not nav_agent.is_navigation_finished():
var path_pos := nav_agent.get_next_path_position()
var relative_pos := path_pos - global_position
motion = relative_pos.normalized() * get_speed() * delta
velocity.x = motion.x
velocity.z = motion.z
if not is_on_floor():
velocity += gravity * delta
move_and_slide()
func on_sound_detected(source: Vector3) -> void:
# Check that the source isn't too far away, e.g. a sound from another room
if path_shorter_than(source, STALKING_SOUND_LIMIT):
blackboard.set_value("stalking_target", source)
stalking_timer.start()
#endregion