#include "Zombie.h" #include #include #include #include using namespace godot; namespace blitz { static constexpr float SPEED = 2.0f; static constexpr float ATTACK_RANGE = 2.0f; void Zombie::_bind_methods() { ClassDB::bind_method(D_METHOD("set_m_PlayerPath", "m_PlayerPath"), &Zombie::set_m_PlayerPath); ClassDB::bind_method(D_METHOD("get_m_PlayerPath"), &Zombie::get_m_PlayerPath); ClassDB::bind_method(D_METHOD("hit_finished"), &Zombie::hit_finished); ClassDB::bind_method(D_METHOD("_on_area_3d_a_zombie_head_shot_hit", "m_Health"), &Zombie::_on_area_3d_a_zombie_head_shot_hit); ClassDB::bind_method(D_METHOD("_on_area_3d_a_zombie_body_shot_hit", "m_Health"), &Zombie::_on_area_3d_a_zombie_body_shot_hit); ClassDB::bind_method(D_METHOD("_on_timer_timeout"), &Zombie::_on_timer_timeout); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "m_PlayerPath"), "set_m_PlayerPath", "get_m_PlayerPath"); } Zombie::Zombie() {} Zombie::~Zombie() {} void Zombie::_ready() { #if DEBUG_ENABLED if (Engine::get_singleton()->is_editor_hint()) { return; } #endif godot::Node* scene_root = Object::cast_to(get_tree()->get_root()->get_child(0)); if (scene_root) { m_Player = Object::cast_to(scene_root->find_child("FirstPersonPlayer", true)); DEV_ASSERT(m_Player); } else { ERR_PRINT("Scene root not found or invalid."); } m_NavigationAgent = Object::cast_to(find_child("NavigationAgent3D")); DEV_ASSERT(m_NavigationAgent); m_AnimationTree = Object::cast_to(find_child("AnimationTree")); DEV_ASSERT(m_AnimationTree); m_StateMachine = Object::cast_to(m_AnimationTree->get("parameters/playback")); DEV_ASSERT(m_StateMachine); this->set_velocity(Vector3(0, 0, 0)); m_HeadCollision = Object::cast_to(find_child("HeadArea3D")); DEV_ASSERT(m_HeadCollision); m_HeadCollision->connect("a_ZombieHeadShotHit", Callable(this, "_on_area_3d_a_zombie_head_shot_hit")); connect_collision_shapes(m_BodyPartsCollision); m_Timer = memnew(Timer); add_child(m_Timer); m_Timer->connect("timeout", callable_mp(this, &Zombie::_on_timer_timeout)); } void Zombie::_process(float a_Delta) { #if DEBUG_ENABLED if (Engine::get_singleton()->is_editor_hint()) { return; } #endif if (m_StateMachine->get_current_node().match("Run")) { m_NavigationAgent->set_target_position(m_Player->get_global_position()); m_Velocity = (m_NavigationAgent->get_next_path_position() - this->get_global_position()).normalized() * SPEED; this->set_velocity(m_Velocity); look_at(Vector3(this->get_global_position().x + this->get_velocity().x, this->get_global_position().y, this->get_global_position().z + this->get_velocity().z)); } else if (m_StateMachine->get_current_node().match("Attack")) { this->set_velocity(Vector3(0, 0, 0)); look_at(Vector3(m_Player->get_global_position().x + this->get_velocity().x, this->get_global_position().y, m_Player->get_global_position().z + this->get_velocity().z)); } m_AnimationTree->set("parameters/conditions/run", !_target_in_range()); m_AnimationTree->set("parameters/conditions/attack", _target_in_range()); move_and_slide(); } bool Zombie::_target_in_range() { return this->get_global_position().distance_to(m_Player->get_global_position()) < ATTACK_RANGE; } void Zombie::set_m_PlayerPath(const NodePath& path) { m_PlayerPath = path; } NodePath Zombie::get_m_PlayerPath() const { return m_PlayerPath; } void Zombie::hit_finished() { if (this->get_global_position().distance_to(m_Player->get_global_position()) < (ATTACK_RANGE + 1.0)) { Vector3 dir = this->get_global_position().direction_to(m_Player->get_global_position()); m_Player->hit(dir); } } void Zombie::apply_damage(int dam) { m_Health -= dam; if (m_Health <= 0) { m_AnimationTree->set("parameters/conditions/die", true); this->set_velocity(Vector3(0, 0, 0)); m_Timer->set_wait_time(4.0); m_Timer->start(); } } void Zombie::_on_area_3d_a_zombie_head_shot_hit(int dam) { apply_damage(dam); } void Zombie::_on_area_3d_a_zombie_body_shot_hit(int dam) { apply_damage(dam); } void Zombie::connect_collision_shapes(const std::vector& body_parts) { for (const StringName& part : body_parts) { StringName area_name = String(part) + "Area3D"; BoneCollisionShape* collision_shape = Object::cast_to(find_child(area_name)); DEV_ASSERT(collision_shape); collision_shape->connect("a_ZombieBodyShotHit", Callable(this, "_on_area_3d_a_zombie_body_shot_hit")); } } void Zombie::_on_timer_timeout() { queue_free(); } } // namespace blitz