diff --git a/assets/models/weapons/rifle.blend b/assets/models/weapons/rifle.blend index a31e7a9f..e19ac1b4 100644 Binary files a/assets/models/weapons/rifle.blend and b/assets/models/weapons/rifle.blend differ diff --git a/assets/models/weapons/rifle.blend1 b/assets/models/weapons/rifle.blend1 index a2ea4e99..763a9d42 100644 Binary files a/assets/models/weapons/rifle.blend1 and b/assets/models/weapons/rifle.blend1 differ diff --git a/modules/wave_survival/player_body.cpp b/modules/wave_survival/player_body.cpp index 16f0ca83..a628bb01 100644 --- a/modules/wave_survival/player_body.cpp +++ b/modules/wave_survival/player_body.cpp @@ -1,6 +1,8 @@ #include "player_body.h" #include "macros.h" #include "player_input.h" +#include "weapon_base.h" +#include "weapon_inventory.h" PlayerBody *PlayerBody::singleton_instance{ nullptr }; @@ -13,6 +15,7 @@ void PlayerBody::ready() { input->connect(PlayerInput::sig_look_input, callable_mp(this, &self_type::on_look_input)); input->connect(PlayerInput::sig_jump, callable_mp(this, &self_type::on_jump_input)); input->connect(PlayerInput::sig_run, callable_mp(this, &self_type::on_run_input)); + this->weapons = cast_to(get_node(NodePath("%WeaponInventory"))); } void PlayerBody::process(double delta) { @@ -85,5 +88,5 @@ PlayerBody *PlayerBody::get_singleton() { } bool PlayerBody::get_is_running() const { - return this->try_running && this->movement_input.y > 0.f; + return this->try_running && this->movement_input.y > 0.f && this->weapons->get_current_weapon()->allows_running(); } diff --git a/modules/wave_survival/player_body.h b/modules/wave_survival/player_body.h index 090c76be..fc2a4bcc 100644 --- a/modules/wave_survival/player_body.h +++ b/modules/wave_survival/player_body.h @@ -2,6 +2,7 @@ #define PLAYER_BODY_H #include "scene/3d/physics/character_body_3d.h" +class WeaponInventory; class PlayerBody : public CharacterBody3D { GDCLASS(PlayerBody, CharacterBody3D); @@ -32,6 +33,8 @@ private: float acceleration{ 40.f }; float jump_strength{ 3.5f }; Vector2 movement_input{}; + + WeaponInventory *weapons; }; #endif // !PLAYER_BODY_H diff --git a/modules/wave_survival/register_types.cpp b/modules/wave_survival/register_types.cpp index 62c5c8f2..f44af9b3 100644 --- a/modules/wave_survival/register_types.cpp +++ b/modules/wave_survival/register_types.cpp @@ -5,6 +5,7 @@ #include "wave_survival/player_camera.h" #include "wave_survival/player_input.h" #include "wave_survival/weapon_base.h" +#include "wave_survival/weapon_inventory.h" #include "wave_survival/weapons/rifle.h" void initialize_wave_survival_module(ModuleInitializationLevel p_level) { @@ -16,6 +17,7 @@ void initialize_wave_survival_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(PlayerCamera); GDREGISTER_RUNTIME_CLASS(WeaponBase); GDREGISTER_CLASS(Rifle); + GDREGISTER_CLASS(WeaponInventory); } void uninitialize_wave_survival_module(ModuleInitializationLevel p_level) { diff --git a/modules/wave_survival/weapon_base.h b/modules/wave_survival/weapon_base.h index 065f8966..cdcc1549 100644 --- a/modules/wave_survival/weapon_base.h +++ b/modules/wave_survival/weapon_base.h @@ -22,6 +22,10 @@ public: void set_body(PlayerBody *body); PlayerBody *get_body() const; + virtual bool allows_running() const { return true; } + virtual bool allows_jumping() const { return true; } + virtual void notify_selected() {} + private: AnimationPlayer *anim{ nullptr }; PlayerCamera *camera{ nullptr }; diff --git a/modules/wave_survival/weapon_inventory.cpp b/modules/wave_survival/weapon_inventory.cpp index 4160c73e..3525fe8b 100644 --- a/modules/wave_survival/weapon_inventory.cpp +++ b/modules/wave_survival/weapon_inventory.cpp @@ -1,10 +1,99 @@ #include "weapon_inventory.h" +#include "macros.h" +#include "weapon_base.h" -void WeaponInventory::_bind_methods() {} +void WeaponInventory::_bind_methods() { + BIND_HPROPERTY(Variant::OBJECT, fallback_weapon, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"); +} void WeaponInventory::on_switch_input() { this->current = (this->current + 1) % 1; this->select_weapon(this->weapons[this->current]); } +void WeaponInventory::ready() { + // initialize the fallback weapon + this->weapon_parent = cast_to(get_node(NodePath("%PlayerCamera"))); + if (this->fallback_weapon_scene.is_valid()) { + Node *fallback_as_node{ this->fallback_weapon_scene->instantiate() }; + this->weapon_parent->add_child(fallback_as_node); + if ((this->fallback_weapon = cast_to(fallback_as_node))) { + this->select_weapon(this->fallback_weapon); + } else if(fallback_as_node != nullptr) { + fallback_as_node->queue_free(); + } + } + if (this->fallback_weapon == nullptr) { + print_error("WeaponInventory::ready(): fallback weapon is invalid, expect unintended behaviour."); + } +} +void WeaponInventory::_notification(int what) { + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + switch (what) { + default: + return; + case NOTIFICATION_READY: + ready(); + return; + } +} + +void WeaponInventory::set_fallback_weapon(Ref scene) { + this->fallback_weapon_scene = scene; +} + +Ref WeaponInventory::get_fallback_weapon() const { + return this->fallback_weapon_scene; +} + +void WeaponInventory::select_weapon(WeaponBase *new_weapon) { + if (new_weapon == nullptr) { + new_weapon = this->fallback_weapon; + } + if (this->current_weapon != nullptr) { + this->current_weapon->set_process_mode(PROCESS_MODE_DISABLED); + this->current_weapon->set_visible(false); + } + this->current_weapon = new_weapon; + if (new_weapon != nullptr) { + this->current_weapon->set_process_mode(PROCESS_MODE_PAUSABLE); + this->current_weapon->set_visible(true); + this->current_weapon->notify_selected(); + if (new_weapon == this->weapons[0]) { + this->current = 0; + } else if (new_weapon == this->weapons[1]) { + this->current = 1; + } else { + print_error("WeaponInventory::select_weapon: Attempt to select weapon that is not in inventory"); + } + } +} + +WeaponBase *WeaponInventory::get_current_weapon() const { + return this->current_weapon; +} + +void WeaponInventory::pickup_weapon(Ref weapon_scene) { + Node *weapon_as_node{ weapon_scene->instantiate() }; + if (WeaponBase *weapon{ cast_to(weapon_as_node) }) { + if (this->weapons[0] == nullptr) { + this->weapons[0] = weapon; + select_weapon(weapon); + return; + } else if (this->weapons[1] == nullptr) { + this->weapons[1] = weapon; + select_weapon(weapon); + return; + } else { + this->current_weapon->queue_free(); + this->weapons[this->current] = weapon; + this->current_weapon = nullptr; + select_weapon(weapon); + } + } else if (weapon_as_node != nullptr) { + weapon_as_node->queue_free(); + } +} diff --git a/modules/wave_survival/weapon_inventory.h b/modules/wave_survival/weapon_inventory.h index 0487e38b..3c4f572f 100644 --- a/modules/wave_survival/weapon_inventory.h +++ b/modules/wave_survival/weapon_inventory.h @@ -10,8 +10,9 @@ class WeaponInventory : public Node { GDCLASS(WeaponInventory, Node); static void _bind_methods(); void on_switch_input(); + void stow_current_weapon(); void ready(); - void select_weapon(WeaponBase *next); + protected: void _notification(int what); @@ -19,15 +20,19 @@ public: void set_fallback_weapon(Ref scene); Ref get_fallback_weapon() const; + void select_weapon(WeaponBase *next); WeaponBase *get_current_weapon() const; + void pickup_weapon(Ref weapon_scene); + private: + Node3D *weapon_parent{ nullptr }; unsigned current{ 0 }; LocalVector weapons{ nullptr, nullptr }; WeaponBase *current_weapon{ nullptr }; WeaponBase *fallback_weapon{ nullptr }; - Ref default_weapon_scene{}; + Ref fallback_weapon_scene{}; }; #endif // !WEAPON_INVENTORY_H diff --git a/modules/wave_survival/weapons/rifle.cpp b/modules/wave_survival/weapons/rifle.cpp index 24e198f5..a1ef5360 100644 --- a/modules/wave_survival/weapons/rifle.cpp +++ b/modules/wave_survival/weapons/rifle.cpp @@ -39,6 +39,8 @@ void Rifle::ready() { get_anim()->animation_set_next("fire_aim", "aim"); get_anim()->animation_set_next("aim_to_hip", "RESET"); get_anim()->animation_set_next("fire_hip", "RESET"); + get_anim()->animation_set_next("equip", "RESET"); + get_anim()->play("equip"); } void Rifle::process(double delta) { @@ -74,3 +76,12 @@ void Rifle::_notification(int what) { return; } } + +bool Rifle::allows_running() const { + String const animation{ get_anim()->get_current_animation() }; + return animation.is_empty(); +} + +void Rifle::notify_selected() { + get_anim()->play("equip"); +} diff --git a/modules/wave_survival/weapons/rifle.h b/modules/wave_survival/weapons/rifle.h index b02e478c..87aef8cb 100644 --- a/modules/wave_survival/weapons/rifle.h +++ b/modules/wave_survival/weapons/rifle.h @@ -17,6 +17,9 @@ class Rifle : public WeaponBase { public: void _notification(int what); + virtual bool allows_running() const override; + virtual void notify_selected() override; + private: float ads_factor{ 0.5f }; bool request_alt_mode{ false }; diff --git a/project/assets/models/weapons/rifle.glb b/project/assets/models/weapons/rifle.glb index 98a0bb8f..45bee5ca 100644 Binary files a/project/assets/models/weapons/rifle.glb and b/project/assets/models/weapons/rifle.glb differ diff --git a/project/objects/player.tscn b/project/objects/player.tscn index c4225bd0..53fea981 100644 --- a/project/objects/player.tscn +++ b/project/objects/player.tscn @@ -1,6 +1,4 @@ -[gd_scene load_steps=3 format=3 uid="uid://snjgu4yp5swd"] - -[ext_resource type="PackedScene" uid="uid://ce40pq785yoyi" path="res://objects/weapons/rifle.tscn" id="1_eqqp1"] +[gd_scene load_steps=2 format=3 uid="uid://snjgu4yp5swd"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_bxedw"] @@ -18,7 +16,4 @@ unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 1.60811, 0) fov = 60.0 -[node name="Rifle" parent="PlayerCamera" instance=ExtResource("1_eqqp1")] - -[editable path="PlayerCamera/Rifle"] -[editable path="PlayerCamera/Rifle/rifle"] +[node name="WeaponInventory" type="WeaponInventory" parent="."]