class_name NonPlayerCharacter extends CharacterBody3D @export var reacts_to_player:bool = true : set (value): reacts_to_player = _set_reacts_to_player(value) get: return reacts_to_player @export var behaviour:BehaviorTree = null: set (value): behaviour = value if is_instance_valid(bt_player): bt_player.behavior_tree = behaviour get: return behaviour #@export var behaviour_blackboard_plan:BlackboardPlan = null: #set (value): #behaviour_blackboard_plan = value #if is_instance_valid(bt_player): #bt_player.blackboard_plan = behaviour_blackboard_plan #get: # return behaviour_blackboard_plan @onready var geometry: Node3D = null @onready var player_detection: Area3D = %PlayerDetection @onready var default_geometry: Node3D = %DefaultGeometry @onready var animation_player: AnimationPlayer = %AnimationPlayer @onready var navigation_agent: NavigationAgent3D = %NavigationAgent @onready var bt_player: BTPlayer = %BTPlayer const SPEED = 2.0 const JUMP_VELOCITY = 4.5 var tool_damage:bool = false var tracking_node:Node3D = null var navigation_active:bool = false var navigation_query_parameters:NavigationPathQueryParameters3D = NavigationPathQueryParameters3D.new() var navigation_query_result:NavigationPathQueryResult3D = NavigationPathQueryResult3D.new() func _set_reacts_to_player(value:bool) -> bool: if player_detection != null: player_detection.monitoring = value return value func _ready() -> void: player_detection.monitoring = reacts_to_player geometry = find_child("Geometry") if geometry == null: geometry = default_geometry else: default_geometry.visible = false animation_player.set_root("Geometry") if is_instance_valid(bt_player): bt_player.behavior_tree = behaviour func _physics_process(delta: float) -> void: if navigation_active: var next_position:Vector3 = navigation_agent.get_next_path_position() var current_path:PackedVector3Array = navigation_agent.get_current_navigation_path() var path_length:float = 0 for i in range(1, current_path.size()): path_length = path_length + (current_path[i] - current_path[i - 1]).length() if DebugSystem.debug_npc == self: DebugDraw3D.draw_point_path(current_path, DebugDraw3D.POINT_TYPE_SPHERE, 0.02) if is_nan(next_position.length()): breakpoint var vector_to_target:Vector3 = next_position - global_position var distance:float = vector_to_target.length() if is_nan(distance): breakpoint var path_remainder:float = path_length / (SPEED * delta) if navigation_agent.is_navigation_finished() or path_remainder <= navigation_agent.target_desired_distance: print ("Target reached. Distance: %s" % navigation_agent.distance_to_target()) navigation_active = false else: var direction = (vector_to_target) / distance basis = Basis.looking_at(-direction) velocity = direction * SPEED * (minf(1.0, path_remainder * 0.9)) # print ("scale %f" % (path_remainder)) move_and_slide() func _on_player_detection_body_entered(body: Node3D) -> void: if body is Player: tracking_node = body func _on_player_detection_body_exited(body: Node3D) -> void: if body == tracking_node: tracking_node = null func is_target_navigatable(target_position: Vector3) -> bool: navigation_query_parameters.map = get_world_3d().navigation_map navigation_query_parameters.start_position = global_position navigation_query_parameters.target_position = target_position navigation_query_parameters.navigation_layers = navigation_agent.navigation_layers if NavigationServer3D.map_get_iteration_id(navigation_query_parameters.map) == 0: return false NavigationServer3D.query_path(navigation_query_parameters, navigation_query_result) var query_path: PackedVector3Array = navigation_query_result.path if query_path.size() > 0 and (query_path[query_path.size() - 1] - target_position).length() <= navigation_agent.target_desired_distance: return true return false func navigate_to(target_position: Vector3) -> void: navigation_agent.target_position = target_position navigation_active = not navigation_agent.is_navigation_finished() func is_navigation_finished() -> bool: return navigation_agent.is_navigation_finished() func is_navigation_target_reached() -> bool: return navigation_agent.is_target_reached()