#include "player_detector.h" #include "sound_event_patchboard.h" String PlayerDetector::sig_awareness_changed{ "awareness_changed" }; void PlayerDetector::_bind_methods() { ADD_SIGNAL(MethodInfo(sig_awareness_changed, PropertyInfo(Variant::BOOL, "aware"))); } // check if there is geometry between detector and player bool PlayerDetector::line_of_sight_exists() const { PhysicsDirectSpaceState3D::RayParameters params{ this->ray_params }; params.from = get_global_position(); params.to = PlayerBody::get_singleton()->get_global_position(); PhysicsDirectSpaceState3D *space{ get_world_3d()->get_direct_space_state() }; PhysicsDirectSpaceState3D::RayResult result{}; space->intersect_ray(params, result); return result.collider == nullptr; } // Check if the player is in a bounded area in front of the detector and unobscured. // As all tests are required to pass, we do them in increasing order of complexity, to minimize unneeded resource use. bool PlayerDetector::check() const { Vector3 const forward{ get_global_basis().get_column(2) }; Vector3 const position{ get_global_position() }; Vector3 const target{ PlayerBody::get_singleton()->get_global_position() + Vector3{ 0.f, 1.5f, 0.f } }; // check if the target is in a view cone if (forward.dot(target - position) < this->min_sight_dot) { return false; } // check if the target is in range if (position.distance_squared_to(target) > this->max_sight_range * this->max_sight_range) { return false; } return line_of_sight_exists(); } void PlayerDetector::ready() { SoundEventPatchboard::get_singleton()->connect(SoundEventPatchboard::sig_sound_triggered, callable_mp(this, &self_type::on_player_sound)); this->ray_params.exclude.insert(PlayerBody::get_singleton()->get_rid()); } void PlayerDetector::process(double delta) { if (this->query_timer > 0.0) { this->query_timer -= delta; } else { this->query_timer = this->query_time; bool const new_awareness{ check() }; if (new_awareness != this->aware_of_player) { set_aware_of_player(new_awareness); } } } void PlayerDetector::on_player_sound(Vector3 at, float range) { if (get_global_position().distance_squared_to(at) <= range * range && line_of_sight_exists()) { set_aware_of_player(true); } } void PlayerDetector::set_aware_of_player(bool value) { emit_signal(sig_awareness_changed, value); this->aware_of_player = value; } void PlayerDetector::_notification(int what) { if (Engine::get_singleton()->is_editor_hint()) { return; } switch (what) { default: return; case NOTIFICATION_READY: set_process(true); ready(); return; case NOTIFICATION_PROCESS: process(get_process_delta_time()); return; } } bool PlayerDetector::is_aware_of_player() const { return this->aware_of_player; }