feat: start of firing enemies
This commit is contained in:
parent
7a6aefc44c
commit
2054474c01
12 changed files with 212 additions and 14 deletions
118
src/enemy.cpp
118
src/enemy.cpp
|
|
@ -1,9 +1,102 @@
|
|||
#include "enemy.hpp"
|
||||
#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>
|
||||
|
||||
void Enemy::_bind_methods() {}
|
||||
void Enemy::_bind_methods() {
|
||||
#define CLASSNAME Enemy
|
||||
GDFUNCTION_ARGS(notice_player, "player");
|
||||
GDPROPERTY(update_interval, gd::Variant::FLOAT);
|
||||
}
|
||||
|
||||
void Enemy::_ready() {
|
||||
this->anim_tree = this->get_node<PlayerAnimTree>("CharacterModel/AnimationTree");
|
||||
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() {
|
||||
bool const is_chasing{!this->agent->is_navigation_finished()};
|
||||
this->anim_tree->set_lock_running(is_chasing);
|
||||
this->anim_tree->set_aim_weapon(!is_chasing);
|
||||
if(is_chasing) {
|
||||
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});
|
||||
}
|
||||
if(this->get_global_position().distance_to(this->player->get_global_position()) >= this->agent->get_target_desired_distance())
|
||||
this->chase_enter(); // repath
|
||||
}
|
||||
|
||||
Enemy::ActionFn Enemy::miss_enter() {
|
||||
this->anim_tree->set_aim_weapon(true);
|
||||
return (ActionFn)&Enemy::miss;
|
||||
}
|
||||
|
||||
Enemy::ActionFn Enemy::miss() {
|
||||
if(this->anim_tree->get_current_state().begins_with("Fire") || this->anim_tree->get_fire_weapon()) // last shot still going
|
||||
return (ActionFn)&Enemy::miss;
|
||||
if(this->can_see_player) {
|
||||
gd::Basis const basis{this->get_global_basis()};
|
||||
this->look_at(this->get_global_position() * 2 - this->player->get_global_position() + basis.get_column(0));
|
||||
this->anim_tree->set_aim_weapon(true);
|
||||
this->anim_tree->set_fire_weapon();
|
||||
if(this->anim_tree->get_fire_weapon()) {
|
||||
gd::UtilityFunctions::print("!!! miss fired");
|
||||
return ++this->missed_shots > SHOTS_BEFORE_HIT ? (ActionFn)&Enemy::hit_enter : (ActionFn)&Enemy::miss_enter;
|
||||
}
|
||||
} else {
|
||||
this->chase();
|
||||
}
|
||||
return (ActionFn)&Enemy::miss;
|
||||
}
|
||||
|
||||
Enemy::ActionFn Enemy::hit_enter() {
|
||||
this->anim_tree->set_aim_weapon(true);
|
||||
return (ActionFn)&Enemy::hit;
|
||||
}
|
||||
|
||||
Enemy::ActionFn Enemy::hit() {
|
||||
if(this->anim_tree->get_current_state().begins_with("Fire") || this->anim_tree->get_fire_weapon()) // last shot still going
|
||||
return (ActionFn)&Enemy::hit;
|
||||
else if(this->can_see_player) {
|
||||
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;
|
||||
return (ActionFn)&Enemy::miss_enter;
|
||||
} else {
|
||||
this->chase();
|
||||
}
|
||||
return (ActionFn)&Enemy::hit;
|
||||
}
|
||||
|
||||
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
|
||||
});
|
||||
this->move_and_slide();
|
||||
}
|
||||
|
||||
void Enemy::damage() {
|
||||
|
|
@ -11,3 +104,26 @@ void Enemy::damage() {
|
|||
this->set_collision_mask(0x0);
|
||||
this->set_collision_layer(0x0);
|
||||
}
|
||||
|
||||
void Enemy::notice_player(Player *player) {
|
||||
this->player = player;
|
||||
this->current_action_fn = (ActionFn)&Enemy::miss_enter;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,46 @@
|
|||
#define ENEMY_HPP
|
||||
|
||||
#include "damageable_entity.hpp"
|
||||
#include "player.hpp"
|
||||
#include "player_anim_tree.hpp"
|
||||
#include "utils/godot_macros.hpp"
|
||||
#include <godot_cpp/classes/character_body3d.hpp>
|
||||
#include <godot_cpp/classes/navigation_agent3d.hpp>
|
||||
namespace gd = godot;
|
||||
|
||||
class Enemy : public gd::CharacterBody3D, public DamageableEntity {
|
||||
GDCLASS(Enemy, gd::CharacterBody3D);
|
||||
static void _bind_methods();
|
||||
typedef void *(Enemy::*ActionFn_)();
|
||||
typedef ActionFn_ (Enemy::*ActionFn)();
|
||||
public:
|
||||
virtual void _ready() override;
|
||||
void update();
|
||||
void chase_enter();
|
||||
void chase();
|
||||
ActionFn miss_enter();
|
||||
ActionFn miss();
|
||||
ActionFn hit_enter();
|
||||
ActionFn hit();
|
||||
ActionFn stab_enter();
|
||||
ActionFn stab();
|
||||
virtual void _physics_process(double delta) override;
|
||||
virtual void damage() override;
|
||||
|
||||
void notice_player(Player *player);
|
||||
void update_can_see_player();
|
||||
|
||||
void set_update_interval(float time);
|
||||
float get_update_interval() const;
|
||||
private:
|
||||
int const SHOTS_BEFORE_HIT{2};
|
||||
|
||||
int missed_shots{0};
|
||||
double update_interval{0.2};
|
||||
ActionFn current_action_fn{nullptr};
|
||||
bool can_see_player{false};
|
||||
Player *player{nullptr};
|
||||
gd::NavigationAgent3D *agent{nullptr};
|
||||
PlayerAnimTree *anim_tree{nullptr};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ void PlayerAnimTree::_bind_methods() {
|
|||
GDPROPERTY(target_turn_speed, gd::Variant::FLOAT);
|
||||
GDPROPERTY(is_walking, gd::Variant::FLOAT);
|
||||
GDPROPERTY(walk_speed, gd::Variant::FLOAT);
|
||||
GDPROPERTY(lock_running, gd::Variant::BOOL);
|
||||
GDFUNCTION(get_is_running);
|
||||
GDPROPERTY(aim_weapon, gd::Variant::BOOL);
|
||||
GDFUNCTION(get_fire_weapon);
|
||||
|
|
@ -66,12 +67,20 @@ float PlayerAnimTree::get_walk_speed() const {
|
|||
return this->walk_speed;
|
||||
}
|
||||
|
||||
void PlayerAnimTree::set_lock_running(bool value) {
|
||||
this->lock_running = value;
|
||||
}
|
||||
|
||||
bool PlayerAnimTree::get_lock_running() const {
|
||||
return this->lock_running;
|
||||
}
|
||||
|
||||
void PlayerAnimTree::set_is_running() {
|
||||
this->running_time = this->RUN_PARAM_DECAY;
|
||||
}
|
||||
|
||||
bool PlayerAnimTree::get_is_running() const {
|
||||
return this->running_time > 0.0;
|
||||
return this->lock_running || this->running_time > 0.0;
|
||||
}
|
||||
|
||||
void PlayerAnimTree::set_aim_weapon(bool value) {
|
||||
|
|
@ -97,7 +106,7 @@ void PlayerAnimTree::set_stab() {
|
|||
}
|
||||
|
||||
bool PlayerAnimTree::get_stab() {
|
||||
bool const is_set{this->fire_weapon > 0.0};
|
||||
bool const is_set{this->stab > 0.0};
|
||||
this->stab = 0.0;
|
||||
return is_set;
|
||||
}
|
||||
|
|
@ -111,6 +120,10 @@ bool PlayerAnimTree::match_tags(Tags tags) const {
|
|||
return (this->current_tags & tags) != Tags::None;
|
||||
}
|
||||
|
||||
gd::StringName const &PlayerAnimTree::get_current_state() const {
|
||||
return this->last_known_anim;
|
||||
}
|
||||
|
||||
void PlayerAnimTree::update_tags(gd::StringName const &anim) {
|
||||
if(anim != this->last_known_anim && this->fsm->get_travel_path().size() <= 1) {
|
||||
this->last_known_anim = anim;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ public:
|
|||
bool get_is_walking() const;
|
||||
void set_walk_speed(float value);
|
||||
float get_walk_speed() const;
|
||||
void set_lock_running(bool value);
|
||||
bool get_lock_running() const;
|
||||
void set_is_running();
|
||||
bool get_is_running() const;
|
||||
void set_aim_weapon(bool value);
|
||||
|
|
@ -36,6 +38,7 @@ public:
|
|||
bool get_stab();
|
||||
void death_animation();
|
||||
bool match_tags(Tags tags) const;
|
||||
gd::StringName const &get_current_state() const;
|
||||
private:
|
||||
void update_tags(gd::StringName const &anim);
|
||||
void commit_turn_speed();
|
||||
|
|
@ -50,6 +53,7 @@ private:
|
|||
float turn_speed{0.f}; //!< blend position of turn animation (-1 to 1). Moved towards target_turn_speed every frame.
|
||||
float target_turn_speed{0.f}; //!< target blend position of turn animation.
|
||||
bool is_walking{false}; //!< set to true if the walk animation should be playing.
|
||||
bool lock_running{false}; //!< lock animation into running instead of walking.
|
||||
float walk_speed{0.f}; //!< blend amount between RESET/Rest animation and walk animation in walk state.
|
||||
double running_time{0.0}; //!< time in seconds to keep running for.
|
||||
bool aim_weapon{false}; //!< set to true to play the aim animation.
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level)
|
|||
utils::godot_cpp_utils_register_types();
|
||||
GDREGISTER_CLASS(Player);
|
||||
GDREGISTER_CLASS(PlayerAnimTree);
|
||||
GDREGISTER_CLASS(Enemy);
|
||||
GDREGISTER_RUNTIME_CLASS(Enemy);
|
||||
GDREGISTER_CLASS(HitscanMuzzle);
|
||||
GDREGISTER_RUNTIME_CLASS(CameraEffects);
|
||||
GDREGISTER_RUNTIME_CLASS(CameraEffectSource);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue