#include "enemy_world_state.hpp" #include "entity_health.hpp" #include "goap/goal.hpp" #include "godot_cpp/variant/utility_functions.hpp" #include "unit.hpp" #include "utils/godot_macros.hpp" #include void EnemyWorldState::_bind_methods() { #define CLASSNAME EnemyWorldState GDPROPERTY_HINTED(editor_available_goals, gd::Variant::ARRAY, gd::PROPERTY_HINT_ARRAY_TYPE, GDRESOURCETYPE("Goal")); } void EnemyWorldState::_ready() { this->awareness_area = this->get_node("%AwarenessArea"); this->awareness_area->connect("body_entered", callable_mp(this, &EnemyWorldState::on_awareness_entered)); this->awareness_area->connect("body_exited", callable_mp(this, &EnemyWorldState::on_awareness_exited)); this->health = this->get_node("%EntityHealth"); if(this->health == nullptr) this->health->connect("damage", callable_mp(this, &EnemyWorldState::on_damaged)); } void EnemyWorldState::on_awareness_entered(gd::Node3D *node) { gd::UtilityFunctions::print("1) object entered awareness"); Unit *unit{gd::Object::cast_to(node)}; if(unit == nullptr) return; if(unit == this->parent_unit) return; gd::UtilityFunctions::print("2) object was Unit"); this->known_enemies.push_back(unit); this->try_set_target(this->select_target_from_known()); } void EnemyWorldState::on_awareness_exited(gd::Node3D *node) { Unit *unit{gd::Object::cast_to(node)}; this->known_enemies.erase(unit); } void EnemyWorldState::on_damaged(int remaining, int delta, Unit *source) { float highest_priority{0.f}; Unit *highest_priority_unit{this->select_target_from_known_with_priority(&highest_priority)}; float const priority{this->calculate_priority(source)}; if(priority >= highest_priority) this->try_set_target(source); } Unit *EnemyWorldState::select_target_from_known_with_priority(float *out_priority) { Unit *out{nullptr}; *out_priority = -INFINITY; for(Unit *unit : this->known_enemies) { float const priority{this->calculate_priority(unit)}; if(priority > *out_priority) { out = unit; *out_priority = priority; } } return out; } Unit *EnemyWorldState::select_target_from_known() { float dummy{}; return this->select_target_from_known_with_priority(&dummy); } float EnemyWorldState::calculate_priority(Unit *target) { return target->get_global_position().distance_squared_to(this->parent_unit->get_global_position()); } gd::Ref EnemyWorldState::get_goal_for_target(Unit *unit) { gd::Node3D *store{this->target_node}; this->target_node = unit; for(gd::Ref const &goal : this->available_goals) { if(goal->check_requirements_met(this)) { this->target_node = store; return goal; } } this->target_node = store; return nullptr; } void EnemyWorldState::try_set_target(Unit *unit) { gd::UtilityFunctions::print("3) selecting goal"); gd::Ref goal{this->get_goal_for_target(unit)}; if(!goal.is_valid()) return; gd::UtilityFunctions::print("4) selected goal ", goal->get_path()); this->parent_unit->set_target_goal(unit, goal); } void EnemyWorldState::set_editor_available_goals(gd::Array array) { this->available_goals.clear(); while(!array.is_empty()) { gd::Ref goal{array.pop_back()}; this->available_goals.push_back(goal); } } gd::Array EnemyWorldState::get_editor_available_goals() const { gd::Array a{}; for(gd::Ref const &goal : this->available_goals) a.push_back(goal); return a; }