trenches/src/enemy.cpp

136 lines
4.7 KiB
C++
Raw Normal View History

2024-12-06 16:12:06 +00:00
#include "enemy.hpp"
2024-12-09 22:55:11 +00:00
#include "utils/godot_macros.hpp"
#include <godot_cpp/classes/physics_direct_space_state3d.hpp>
#include <godot_cpp/classes/physics_ray_query_parameters3d.hpp>
#include <godot_cpp/classes/timer.hpp>
#include <godot_cpp/classes/world3d.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
2024-12-06 16:12:06 +00:00
2024-12-09 22:55:11 +00:00
void Enemy::_bind_methods() {
#define CLASSNAME Enemy
GDFUNCTION_ARGS(notice_player, "player");
GDPROPERTY(update_interval, gd::Variant::FLOAT);
}
2024-12-06 16:12:06 +00:00
void Enemy::_ready() {
this->anim_tree = this->get_node<PlayerAnimTree>("CharacterModel/AnimationTree");
2024-12-09 22:55:11 +00:00
this->agent = this->get_node<gd::NavigationAgent3D>("%NavigationAgent3D");
gd::Timer *timer{memnew(gd::Timer)};
this->add_child(timer);
timer->start(this->update_interval);
timer->connect("timeout", callable_mp(this, &Enemy::update));
}
void Enemy::update() {
if(this->current_action_fn != nullptr)
this->current_action_fn = (ActionFn)(this->*current_action_fn)();
}
void Enemy::chase_enter() {
if(this->player != nullptr)
this->agent->set_target_position(this->player->get_global_position());
}
void Enemy::chase() {
2024-12-10 19:05:13 +00:00
bool const at_end{!this->agent->is_navigation_finished()};
this->anim_tree->set_lock_running(at_end);
this->anim_tree->set_aim_weapon(!at_end);
if(at_end) {
2024-12-09 22:55:11 +00:00
gd::Vector3 const global_pos{this->get_global_position()};
gd::Vector3 const target{global_pos * 2 - this->agent->get_next_path_position()};
this->look_at({target.x, global_pos.y, target.z});
2024-12-10 19:05:13 +00:00
} else if(this->get_global_position().distance_to(this->player->get_global_position()) >= this->agent->get_target_desired_distance())
2024-12-09 22:55:11 +00:00
this->chase_enter(); // repath
}
2024-12-10 19:05:13 +00:00
Enemy::ActionFn Enemy::wait_line_of_sight() {
if(this->can_see_player)
return (ActionFn)&Enemy::take_aim;
else
return (ActionFn)&Enemy::wait_line_of_sight;
}
Enemy::ActionFn Enemy::take_aim() {
2024-12-09 22:55:11 +00:00
this->anim_tree->set_aim_weapon(true);
2024-12-10 19:05:13 +00:00
if(this->anim_tree->get_current_state().begins_with("Aim"))
return (ActionFn)&Enemy::hit;
else
return(ActionFn)&Enemy::take_aim;
2024-12-09 22:55:11 +00:00
}
Enemy::ActionFn Enemy::miss() {
if(this->can_see_player) {
gd::Basis const basis{this->get_global_basis()};
2024-12-10 19:05:13 +00:00
this->look_at(this->get_global_position() * 2 - this->player->get_global_position() + basis.get_column(0) * 0.6f);
2024-12-09 22:55:11 +00:00
this->anim_tree->set_aim_weapon(true);
this->anim_tree->set_fire_weapon();
2024-12-10 19:05:13 +00:00
++this->missed_shots;
return (ActionFn)&Enemy::wait_end_of_shot;
2024-12-09 22:55:11 +00:00
} else {
this->chase();
}
return (ActionFn)&Enemy::miss;
}
Enemy::ActionFn Enemy::hit() {
2024-12-10 19:05:13 +00:00
if(this->can_see_player) {
2024-12-09 22:55:11 +00:00
this->look_at(this->get_global_position() * 2 - this->player->get_global_position());
this->anim_tree->set_aim_weapon(true);
this->anim_tree->set_fire_weapon();
this->missed_shots = 0;
2024-12-10 19:05:13 +00:00
return (ActionFn)&Enemy::wait_end_of_shot;
2024-12-09 22:55:11 +00:00
} else {
this->chase();
}
return (ActionFn)&Enemy::hit;
}
2024-12-10 19:05:13 +00:00
Enemy::ActionFn Enemy::wait_end_of_shot() {
if(this->anim_tree->get_current_state().begins_with("Fire") || this->anim_tree->get_fire_weapon()) // last shot still going
return (ActionFn)&Enemy::wait_end_of_shot;
else
return this->missed_shots >= SHOTS_BEFORE_HIT ? (ActionFn)&Enemy::hit : (ActionFn)&Enemy::miss;
}
2024-12-09 22:55:11 +00:00
void Enemy::_physics_process(double delta) {
this->update_can_see_player();
gd::Basis const basis{this->get_global_basis()};
gd::Vector3 const motion{this->anim_tree->get_root_motion_position()};
this->set_velocity({
basis.get_column(0) * motion.x +
basis.get_column(1) * motion.y +
basis.get_column(2) * motion.z
2024-12-10 19:05:13 +00:00
});\
2024-12-09 22:55:11 +00:00
this->move_and_slide();
2024-12-06 16:12:06 +00:00
}
void Enemy::damage() {
this->anim_tree->death_animation();
this->set_collision_mask(0x0);
this->set_collision_layer(0x0);
}
2024-12-09 22:55:11 +00:00
void Enemy::notice_player(Player *player) {
this->player = player;
2024-12-10 19:05:13 +00:00
this->current_action_fn = (ActionFn)&Enemy::wait_line_of_sight;
2024-12-09 22:55:11 +00:00
}
void Enemy::update_can_see_player() {
if(this->player == nullptr)
return;
gd::Vector3 origin{this->get_global_position() + gd::Vector3{0.f, 1.8f, 0.f}};
gd::PhysicsDirectSpaceState3D *space{this->get_world_3d()->get_direct_space_state()};
gd::Ref<gd::PhysicsRayQueryParameters3D> query{gd::PhysicsRayQueryParameters3D::create(origin, this->player->get_global_position() + gd::Vector3{0.f, 1.8f, 0.f})};
gd::Dictionary dict{space->intersect_ray(query)};
this->can_see_player = (dict.is_empty() || gd::Object::cast_to<Node>(dict["collider"]) == this->player);
}
void Enemy::set_update_interval(float time) {
this->update_interval = time;
}
float Enemy::get_update_interval() const {
return this->update_interval;
}