#include "rifle.h" #include "scene/animation/animation_player.h" #include "wave_survival/hitscan_muzzle.h" #include "wave_survival/macros.h" #include "wave_survival/player_body.h" #include "wave_survival/player_input.h" void Rifle::_bind_methods() { BIND_PROPERTY(Variant::FLOAT, ads_factor); BIND_PROPERTY(Variant::FLOAT, run_factor); BIND_PROPERTY(Variant::FLOAT, recoil_force); BIND_PROPERTY(Variant::FLOAT, recoil_time); } void Rifle::queue_enter_alt() { get_anim()->queue("hip_to_aim"); get_anim()->queue("aim"); } void Rifle::queue_exit_alt() { get_anim()->queue("aim_to_hip"); get_anim()->queue("hip"); } void Rifle::queue_enter_run() { this->running = true; get_anim()->clear_queue(); get_anim()->queue("hip_to_run"); get_anim()->queue("run"); } void Rifle::exit_run() { this->running = false; get_anim()->clear_queue(); if (this->request_alt_mode) { get_anim()->play("run_to_aim", 0.0); get_anim()->queue("aim"); } else { get_anim()->play("run_to_hip", 0.0); get_anim()->queue("hip"); } } void Rifle::shoot() { if (!is_animating()) { this->muzzle->shoot(); if (this->request_alt_mode) { get_anim()->queue("fire_aim"); } else { get_anim()->queue("fire_hip"); } } } void Rifle::play_equip_anim() { get_anim()->play("equip", 0.0); get_anim()->queue("hip"); get_anim()->advance(0.0); } void Rifle::on_primary_fire(bool pressed) { if (pressed) { shoot(); } } void Rifle::on_alt_mode(bool alt_mode) { this->request_alt_mode = alt_mode; } void Rifle::on_animation_changed(String new_animation) { if (new_animation == "aim") { this->in_alt_mode = true; get_camera()->set_fov_factor(this->ads_factor); } else if (new_animation == "hip") { this->in_alt_mode = false; get_camera()->set_fov_factor(1.0); } else if (new_animation == "run") { get_camera()->set_fov_factor(this->run_factor); } } void Rifle::ready() { this->muzzle = cast_to(get_node(NodePath("%HitscanMuzzle"))); get_anim()->connect("current_animation_changed", callable_mp(this, &self_type::on_animation_changed)); get_input()->connect(PlayerInput::sig_primary_fire, callable_mp(this, &self_type::on_primary_fire)); get_input()->connect(PlayerInput::sig_alt_mode, callable_mp(this, &self_type::on_alt_mode)); play_equip_anim(); } void Rifle::process(double delta) { String const current{ get_anim()->get_current_animation() }; bool run_requested{ this->run_requested() }; // track animation progress double const animation_time{ get_anim()->get_current_animation_position() }; // percentually float const progress{ float(CLAMP(animation_time / get_anim()->get_current_animation_length(), 0.0, 1.0)) }; // lerp the current FOV factor depending on the ongoing animation if (current == "hip_to_aim") { get_camera()->set_fov_factor(Math::lerp(1.f, this->ads_factor, progress)); } else if (current == "aim_to_hip") { get_camera()->set_fov_factor(Math::lerp(this->ads_factor, 1.f, progress)); } else if (current == "run_to_aim") { get_camera()->set_fov_factor(Math::lerp(this->run_factor, this->ads_factor, progress)); } else if (current == "hip_to_run") { get_camera()->set_fov_factor(Math::lerp(1.f, this->run_factor, progress)); } else if (current == "run_to_hip") { get_camera()->set_fov_factor(Math::lerp(this->run_factor, 1.f, progress)); // animation is not one of the transitory ones ( x_to_y ), // check if there is a request for a transitory animation } else if (this->request_alt_mode != this->in_alt_mode && !is_animating()) { if (this->request_alt_mode) { queue_enter_alt(); } else { queue_exit_alt(); } } else if (this->running != run_requested) { if (run_requested && !is_animating()) { queue_enter_run(); } else if (!run_requested) { exit_run(); } } // apply fire recoil else if (current == "fire_hip" || current == "fire_aim") { double t{ animation_time / this->recoil_time }; get_camera()->recoil(Math::lerp((double)this->recoil_force, 0.0, CLAMP(t, 0.0, 1.0)) * delta); } } void Rifle::_notification(int what) { if (Engine::get_singleton()->is_editor_hint()) { return; } switch (what) { default: return; case NOTIFICATION_READY: set_process(true); ready(); return; case NOTIFICATION_PROCESS: process(get_process_delta_time()); return; } } PackedStringArray Rifle::get_configuration_warnings() const { PackedStringArray warnings{ super_type::get_configuration_warnings() }; if (get_node(NodePath("%HitscanMuzzle")) == nullptr) { warnings.push_back("Rifle needs a hitscan muzzle.\nConsider adding a node called '%HitscanMuzzle' of type HitscanMuzzle"); } return warnings; } bool Rifle::allows_running() const { String const animation{ get_anim()->get_current_animation() }; return animation == "run" && !this->request_alt_mode; } bool Rifle::run_requested() const { return get_body()->get_wants_to_run() && !this->request_alt_mode; } void Rifle::notify_selected() { play_equip_anim(); } void Rifle::set_ads_factor(float value) { this->ads_factor = value; } float Rifle::get_ads_factor() const { return this->ads_factor; } void Rifle::set_run_factor(float value) { this->run_factor = value; } float Rifle::get_run_factor() const { return this->run_factor; } void Rifle::set_recoil_force(float value) { this->recoil_force = value; } float Rifle::get_recoil_force() const { return this->recoil_force; } void Rifle::set_recoil_time(float value) { this->recoil_time = value; } float Rifle::get_recoil_time() const { return this->recoil_time; }