157 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			157 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "enemy_wretched.h"
 | 
						|
#include "scene/animation/animation_player.h"
 | 
						|
#include "wave_survival/macros.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() {
 | 
						|
	BIND_PROPERTY(Variant::FLOAT, chase_speed);
 | 
						|
}
 | 
						|
 | 
						|
void EnemyWretched::on_child_entered(Node *node) {
 | 
						|
	if (StateMachine * fsm{ cast_to<StateMachine>(node) }) {
 | 
						|
		this->fsm = fsm;
 | 
						|
	}
 | 
						|
	if (node->has_node(NodePath("AnimationPlayer"))) {
 | 
						|
		this->anim = cast_to<AnimationPlayer>(node->get_node(NodePath("AnimationPlayer")));
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void EnemyWretched::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:
 | 
						|
			if (!is_ready()) {
 | 
						|
				connect("child_entered_tree", callable_mp(this, &self_type::on_child_entered));
 | 
						|
			}
 | 
						|
			return;
 | 
						|
		case NOTIFICATION_READY:
 | 
						|
			ready();
 | 
						|
			return;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void EnemyWretched::set_chase_speed(float speed) {
 | 
						|
	this->chase_speed = speed;
 | 
						|
}
 | 
						|
 | 
						|
float EnemyWretched::get_chase_speed() const {
 | 
						|
	return this->chase_speed;
 | 
						|
}
 | 
						|
 | 
						|
AnimationPlayer *EnemyWretched::get_anim() const {
 | 
						|
	return this->anim;
 | 
						|
}
 | 
						|
 | 
						|
void WretchedState::set_target(Node *node) {
 | 
						|
	this->target = cast_to<EnemyWretched>(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::on_velocity_calculated(Vector3 direction) {
 | 
						|
	get_target()->set_movement_direction(Vector2{ direction.x, direction.z } / get_target()->get_movement_speed());
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
	}
 | 
						|
	get_nav()->connect("velocity_computed", this->mp_on_velocity_calculated);
 | 
						|
}
 | 
						|
 | 
						|
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()).normalized() };
 | 
						|
		if (get_nav()->get_avoidance_enabled()) {
 | 
						|
			get_nav()->set_velocity(direction);
 | 
						|
		} else {
 | 
						|
			on_velocity_calculated(direction);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void WretchedPatrolState::exit_state() {
 | 
						|
	get_nav()->disconnect("velocity_computed", this->mp_on_velocity_calculated);
 | 
						|
}
 | 
						|
 | 
						|
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() {
 | 
						|
	get_target()->set_movement_speed(get_target()->get_chase_speed());
 | 
						|
	get_nav()->set_max_speed(get_target()->get_chase_speed());
 | 
						|
	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");
 | 
						|
	get_target()->set_movement_direction(Vector2());
 | 
						|
}
 | 
						|
 | 
						|
String WretchedAttackState::get_next_state() const {
 | 
						|
	if (get_anim()->get_current_animation().is_empty()) {
 | 
						|
		return WretchedChaseState::get_class_static();
 | 
						|
	}
 | 
						|
	return get_class();
 | 
						|
}
 |