#include "enemy_wretched.h" #include "scene/animation/animation_player.h" #include "wave_survival/npc_unit.h" #include "wave_survival/patrol_path.h" #include "wave_survival/player_body.h" #include "wave_survival/state_machine.h" void EnemyWretched::_bind_methods() { } void EnemyWretched::on_child_entered(Node *node) { if (StateMachine * fsm{ cast_to(node) }) { this->fsm = fsm; } if (node->has_node(NodePath("AnimationPlayer"))) { this->anim = cast_to(node->get_node(NodePath("AnimationPlayer"))); } } void EnemyWretched::ready() { print_line("ready"); fsm->add_state(memnew(WretchedPatrolState)); fsm->add_state(memnew(WretchedChaseState)); fsm->add_state(memnew(WretchedAttackState)); } void EnemyWretched::_notification(int what) { if (Engine::get_singleton()->is_editor_hint()) { return; } switch (what) { default: return; case NOTIFICATION_ENTER_TREE: connect("child_entered_tree", callable_mp(this, &self_type::on_child_entered)); return; case NOTIFICATION_READY: ready(); return; } } AnimationPlayer *EnemyWretched::get_anim() const { return this->anim; } void WretchedState::set_target(Node *node) { this->target = cast_to(node); } EnemyWretched *WretchedState::get_target() const { return this->target; } NpcUnit *WretchedState::get_unit() const { return this->target->get_unit(); } NavigationAgent3D *WretchedState::get_nav() const { return this->target->get_nav(); } AnimationPlayer *WretchedState::get_anim() const { return this->target->get_anim(); } void WretchedPatrolState::set_patrol_target(Vector3 path_point) { get_nav()->set_target_position(path_point + get_target()->get_unit_offset_3d()); } void WretchedPatrolState::enter_state() { this->path = get_target()->get_unit()->get_patrol_path(); float const max_speed{ get_unit()->get_patrol_speed() }; get_target()->set_movement_speed(max_speed); get_nav()->set_max_speed(max_speed); if (this->path) { Vector3 const nav_target{ this->path->get_closest_point(get_target()->get_global_position(), &this->path_point) }; set_patrol_target(nav_target); } } void WretchedPatrolState::process(double delta) { if (this->path) { if (get_nav()->is_navigation_finished()) { this->path_point += 1; set_patrol_target(this->path->point_at(this->path_point)); } Vector3 const direction{ get_target()->get_global_position().direction_to(get_nav()->get_next_path_position()) }; get_target()->set_movement_direction(Vector2{ direction.x, direction.z }.normalized()); } } String WretchedPatrolState::get_next_state() const { if (get_target()->get_unit()->is_aware_of_player()) { return WretchedChaseState::get_class_static(); } return get_class(); } void WretchedChaseState::enter_state() { // TODO: replace this with a setting somewhere get_target()->set_movement_speed(get_unit()->get_patrol_speed() * 2.f); get_nav()->set_max_speed(get_unit()->get_patrol_speed() * 2.f); get_nav()->set_target_position(PlayerBody::get_singleton()->get_global_position()); get_anim()->play("ready"); // TODO: replace this with "run" } void WretchedChaseState::process(double delta) { // TODO: optimize this with some checks to avoid re-pathing every frame get_nav()->set_target_position(PlayerBody::get_singleton()->get_global_position()); Vector3 const direction{ get_target()->get_global_position().direction_to(get_nav()->get_next_path_position()) }; get_target()->set_movement_direction(Vector2{ direction.x, direction.z }.normalized()); } String WretchedChaseState::get_next_state() const { if (get_target()->get_global_position().distance_to(PlayerBody::get_singleton()->get_global_position()) < 2.f) { return WretchedAttackState::get_class_static(); } return get_class(); } void WretchedAttackState::enter_state() { get_anim()->play("attack"); } String WretchedAttackState::get_next_state() const { if (get_anim()->get_current_animation().is_empty()) { return WretchedChaseState::get_class_static(); } return get_class(); }