diff --git a/assets/models/weapons/revolver.blend b/assets/models/weapons/revolver.blend index b2047441..cd625729 100644 Binary files a/assets/models/weapons/revolver.blend and b/assets/models/weapons/revolver.blend differ diff --git a/assets/models/weapons/revolver.blend1 b/assets/models/weapons/revolver.blend1 index 2f832370..e9c6d426 100644 Binary files a/assets/models/weapons/revolver.blend1 and b/assets/models/weapons/revolver.blend1 differ diff --git a/modules/wave_survival/register_types.cpp b/modules/wave_survival/register_types.cpp index bf6b5723..7f8dc63d 100644 --- a/modules/wave_survival/register_types.cpp +++ b/modules/wave_survival/register_types.cpp @@ -19,6 +19,7 @@ #include "wave_survival/state_machine.h" #include "wave_survival/weapon_base.h" #include "wave_survival/weapon_inventory.h" +#include "wave_survival/weapons/revolver.h" #include "wave_survival/weapons/rifle.h" void initialize_wave_survival_module(ModuleInitializationLevel p_level) { @@ -47,6 +48,7 @@ void initialize_wave_survival_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(DamageBox); GDREGISTER_CLASS(PlayerInteractor); GDREGISTER_CLASS(Interactable); + GDREGISTER_CLASS(Revolver); } void uninitialize_wave_survival_module(ModuleInitializationLevel p_level) { diff --git a/modules/wave_survival/weapon_base.cpp b/modules/wave_survival/weapon_base.cpp index 7e466b62..604122a9 100644 --- a/modules/wave_survival/weapon_base.cpp +++ b/modules/wave_survival/weapon_base.cpp @@ -13,9 +13,6 @@ void WeaponBase::ready() { this->input = cast_to(get_parent()->get_node(NodePath("%PlayerInput"))); this->camera = cast_to(get_parent()); this->body = cast_to(get_parent()->get_owner()); - if (this->anim) { - this->anim->play("RESET"); - } } void WeaponBase::_notification(int what) { @@ -30,6 +27,20 @@ void WeaponBase::_notification(int what) { } } +PackedStringArray WeaponBase::get_configuration_warnings() const { + PackedStringArray warnings{ super_type::get_configuration_warnings() }; + + if (this->anim == nullptr) { + warnings.push_back("Weapons need something to animate them.\nRemember to configure the 'anim' property on this weapon"); + } + + return warnings; +} + +bool WeaponBase::is_animating() const { + return !get_anim()->get_current_animation().is_empty() || !get_anim()->get_queue().is_empty(); +} + void WeaponBase::set_anim(AnimationPlayer *anim) { this->anim = anim; } diff --git a/modules/wave_survival/weapon_base.h b/modules/wave_survival/weapon_base.h index 810da0c4..4d46558a 100644 --- a/modules/wave_survival/weapon_base.h +++ b/modules/wave_survival/weapon_base.h @@ -14,8 +14,10 @@ class WeaponBase : public Node3D { protected: void _notification(int what); + virtual PackedStringArray get_configuration_warnings() const override; public: + bool is_animating() const; void set_anim(AnimationPlayer *player); AnimationPlayer *get_anim() const; PlayerInput *get_input() const; @@ -24,7 +26,7 @@ public: void set_body(PlayerBody *body); PlayerBody *get_body() const; - virtual bool allows_running() const { return true; } + virtual bool allows_running() const { return false; } virtual bool allows_jumping() const { return true; } virtual void notify_selected() {} diff --git a/modules/wave_survival/weapons/revolver.cpp b/modules/wave_survival/weapons/revolver.cpp new file mode 100644 index 00000000..5d65324a --- /dev/null +++ b/modules/wave_survival/weapons/revolver.cpp @@ -0,0 +1,66 @@ +#include "revolver.h" +#include "scene/animation/animation_player.h" +#include "wave_survival/player_input.h" + +void Revolver::_bind_methods() { +} + +void Revolver::play_equip_anim() { + get_anim()->play("equip", 0.0); + get_anim()->queue("idle_double"); + get_anim()->advance(0.0); +} + +void Revolver::shoot() { + if (!is_animating()) { + this->muzzle->shoot(); + if (this->alt_requested) { + get_anim()->queue("fire_single"); + get_anim()->queue("idle_single"); + } else { + get_anim()->queue("fire_double"); + get_anim()->queue("idle_double"); + } + } +} + +void Revolver::on_primary_fire(bool pressed) { + if (pressed) { + shoot(); + } +} + +void Revolver::on_alt_mode(bool pressed) { + this->alt_requested = pressed; +} + +void Revolver::ready() { + this->muzzle = cast_to(get_node(NodePath("%HitscanMuzzle"))); + 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 Revolver::process(double delta) { +} + +void Revolver::_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; + } +} + +void Revolver::notify_selected() { + play_equip_anim(); +} diff --git a/modules/wave_survival/weapons/revolver.h b/modules/wave_survival/weapons/revolver.h new file mode 100644 index 00000000..4790ea2a --- /dev/null +++ b/modules/wave_survival/weapons/revolver.h @@ -0,0 +1,27 @@ +#ifndef WEAPONS_REVOLVER_H +#define WEAPONS_REVOLVER_H + +#include "wave_survival/hitscan_muzzle.h" +#include "wave_survival/weapon_base.h" + +class Revolver : public WeaponBase { + GDCLASS(Revolver, WeaponBase); + static void _bind_methods(); + void play_equip_anim(); + void shoot(); + void on_primary_fire(bool pressed); + void on_alt_mode(bool pressed); + void ready(); + void process(double delta); + +protected: + void _notification(int what); + virtual void notify_selected() override; + +private: + bool alt_requested{ false }; + bool alt_active{ false }; + HitscanMuzzle *muzzle{ nullptr }; +}; + +#endif // !WEAPONS_REVOLVER_H diff --git a/modules/wave_survival/weapons/rifle.cpp b/modules/wave_survival/weapons/rifle.cpp index 92bf6060..383f7f88 100644 --- a/modules/wave_survival/weapons/rifle.cpp +++ b/modules/wave_survival/weapons/rifle.cpp @@ -12,24 +12,24 @@ void Rifle::_bind_methods() { BIND_PROPERTY(Variant::FLOAT, recoil_time); } -void Rifle::queue_start_aim() { +void Rifle::queue_enter_alt() { get_anim()->queue("hip_to_aim"); get_anim()->queue("aim"); } -void Rifle::queue_stop_ads_anim() { +void Rifle::queue_exit_alt() { get_anim()->queue("aim_to_hip"); get_anim()->queue("hip"); } -void Rifle::queue_start_run_anim() { +void Rifle::queue_enter_run() { this->running = true; get_anim()->clear_queue(); get_anim()->queue("hip_to_run"); get_anim()->queue("run"); } -void Rifle::stop_run_anim() { +void Rifle::exit_run() { this->running = false; get_anim()->clear_queue(); if (this->request_alt_mode) { @@ -55,6 +55,7 @@ void Rifle::shoot() { 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) { @@ -90,8 +91,11 @@ void Rifle::ready() { 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") { @@ -102,21 +106,23 @@ void Rifle::process(double delta) { 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_start_aim(); + queue_enter_alt(); } else { - queue_stop_ads_anim(); + queue_exit_alt(); } } else if (this->running != run_requested) { if (run_requested && !is_animating()) { - queue_start_run_anim(); + queue_enter_run(); } else if (!run_requested) { - stop_run_anim(); + exit_run(); } } - - if (current == "fire_hip" || current == "fire_aim") { + // 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); } @@ -139,6 +145,14 @@ void Rifle::_notification(int what) { } } +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; @@ -149,11 +163,7 @@ bool Rifle::run_requested() const { } void Rifle::notify_selected() { - get_anim()->play("equip"); -} - -bool Rifle::is_animating() const { - return !get_anim()->get_current_animation().is_empty() || !get_anim()->get_queue().is_empty(); + play_equip_anim(); } void Rifle::set_ads_factor(float value) { diff --git a/modules/wave_survival/weapons/rifle.h b/modules/wave_survival/weapons/rifle.h index 69f8367e..a0e3559a 100644 --- a/modules/wave_survival/weapons/rifle.h +++ b/modules/wave_survival/weapons/rifle.h @@ -9,10 +9,10 @@ class HitscanMuzzle; class Rifle : public WeaponBase { GDCLASS(Rifle, WeaponBase); static void _bind_methods(); - void queue_start_aim(); - void queue_stop_ads_anim(); - void queue_start_run_anim(); - void stop_run_anim(); + void queue_enter_alt(); + void queue_exit_alt(); + void queue_enter_run(); + void exit_run(); void shoot(); void play_equip_anim(); void on_primary_fire(bool down); @@ -24,11 +24,11 @@ class Rifle : public WeaponBase { public: void _notification(int what); + virtual PackedStringArray get_configuration_warnings() const override; virtual bool allows_running() const override; bool run_requested() const; virtual void notify_selected() override; - bool is_animating() const; void set_ads_factor(float value); float get_ads_factor() const; diff --git a/project/assets/models/weapons/revolver.blend b/project/assets/models/weapons/revolver.blend index ad6b646d..567029e0 100644 Binary files a/project/assets/models/weapons/revolver.blend and b/project/assets/models/weapons/revolver.blend differ diff --git a/project/assets/models/weapons/revolver.blend.import b/project/assets/models/weapons/revolver.blend.import index e458a392..7da71d86 100644 --- a/project/assets/models/weapons/revolver.blend.import +++ b/project/assets/models/weapons/revolver.blend.import @@ -37,7 +37,25 @@ import_script/path="" materials/extract=0 materials/extract_format=0 materials/extract_path="" -_subresources={} +_subresources={ +"materials": { +"body": { +"use_external/enabled": true, +"use_external/fallback_path": "res://assets/materials/weapons/arms.tres", +"use_external/path": "uid://dgl8ygpyta7b0" +}, +"metal": { +"use_external/enabled": true, +"use_external/fallback_path": "res://assets/materials/weapons/metal.tres", +"use_external/path": "uid://cey2t4uje80f3" +}, +"wood": { +"use_external/enabled": true, +"use_external/fallback_path": "res://assets/materials/weapons/wood.tres", +"use_external/path": "uid://bvy3xngb287wd" +} +} +} blender/nodes/visible=0 blender/nodes/active_collection_only=false blender/nodes/punctual_lights=true diff --git a/project/assets/models/weapons/revolver.blend1 b/project/assets/models/weapons/revolver.blend1 index 9256836d..3f3b6fdf 100644 Binary files a/project/assets/models/weapons/revolver.blend1 and b/project/assets/models/weapons/revolver.blend1 differ diff --git a/project/assets/models/weapons/rifle.blend b/project/assets/models/weapons/rifle.blend index 25fbf2f2..421f2e29 100644 Binary files a/project/assets/models/weapons/rifle.blend and b/project/assets/models/weapons/rifle.blend differ diff --git a/project/assets/models/weapons/rifle.blend1 b/project/assets/models/weapons/rifle.blend1 index eb331000..da53ce64 100644 Binary files a/project/assets/models/weapons/rifle.blend1 and b/project/assets/models/weapons/rifle.blend1 differ diff --git a/project/objects/player.tscn b/project/objects/player.tscn index 1b5fc8d1..2ec79cf5 100644 --- a/project/objects/player.tscn +++ b/project/objects/player.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=6 format=3 uid="uid://snjgu4yp5swd"] -[ext_resource type="PackedScene" uid="uid://ce40pq785yoyi" path="res://objects/weapons/rifle.tscn" id="1_eqqp1"] +[ext_resource type="PackedScene" uid="uid://cfgwif53qypko" path="res://objects/weapons/revolver.tscn" id="1_eqqp1"] [sub_resource type="SphereShape3D" id="SphereShape3D_eqqp1"] radius = 0.2 diff --git a/project/objects/weapons/revolver.tscn b/project/objects/weapons/revolver.tscn new file mode 100644 index 00000000..e63c2aaf --- /dev/null +++ b/project/objects/weapons/revolver.tscn @@ -0,0 +1,29 @@ +[gd_scene load_steps=2 format=3 uid="uid://cfgwif53qypko"] + +[ext_resource type="PackedScene" uid="uid://bkw6pt33nbn2" path="res://assets/models/weapons/revolver.blend" id="1_5ynga"] + +[node name="Revolver" type="Revolver" node_paths=PackedStringArray("anim")] +anim = NodePath("revolver/AnimationPlayer") + +[node name="revolver" parent="." instance=ExtResource("1_5ynga")] + +[node name="Body" parent="revolver/Character/Skeleton3D" index="0"] +layers = 2 + +[node name="Cube" parent="revolver/Character/Skeleton3D" index="1"] +layers = 2 + +[node name="BoneAttachment3D" type="BoneAttachment3D" parent="revolver/Character/Skeleton3D" index="2"] +transform = Transform3D(1, -6.350722e-17, 4.732016e-17, 4.732016e-17, 0.95822614, 0.28601173, -6.350722e-17, -0.28601173, 0.95822614, -1.1196792e-16, -0.03667751, 0.009908612) +bone_name = "root" +bone_idx = 39 + +[node name="HitscanMuzzle" type="HitscanMuzzle" parent="revolver/Character/Skeleton3D/BoneAttachment3D"] +unique_name_in_owner = true +transform = Transform3D(1, 6.3507215e-17, 4.732015e-17, -6.350722e-17, 0.28601167, 0.958226, 4.7320157e-17, -0.958226, 0.28601167, 1.0408685e-17, 0.13487417, -0.03951955) +target_position = Vector3(0, 200, 0) +collision_mask = 6 +collide_with_areas = true +spread = 0.03 + +[editable path="revolver"] diff --git a/project/objects/weapons/rifle.tscn b/project/objects/weapons/rifle.tscn index e13fa75b..fe6777f2 100644 --- a/project/objects/weapons/rifle.tscn +++ b/project/objects/weapons/rifle.tscn @@ -20,13 +20,14 @@ bone_idx = 39 [node name="HitscanMuzzle" type="HitscanMuzzle" parent="rifle/Character/Skeleton3D/BoneAttachment3D"] unique_name_in_owner = true -transform = Transform3D(1, 0, 0, 0, 0.99999833, -0.0017453281, 0, 0.0017453281, 0.99999833, 1.4540284e-26, 0, 0.027612558) -target_position = Vector3(0, 100, 0) +transform = Transform3D(1, 0, 0, 0, 0.9999968, 0.0024609112, 0, -0.0024609112, 0.9999968, 1.1099746e-16, 0.053034816, 0.03427495) +target_position = Vector3(0, 200, 0) collision_mask = 6 collide_with_areas = true spread = 0.003 +damage = 3 -[node name="AnimationPlayer" parent="rifle" index="1"] +[node name="AnimationPlayer" parent="rifle" index="2"] playback_default_blend_time = 0.1 [editable path="rifle"]