diff --git a/src/nodes/level.cpp b/src/nodes/level.cpp index d145eaf..47a1998 100644 --- a/src/nodes/level.cpp +++ b/src/nodes/level.cpp @@ -52,7 +52,7 @@ namespace rl::inline node if (this->active()) [[likely]] { godot::Point2 mouse_pos{ this->get_global_mouse_position() }; - this->draw_circle(mouse_pos, 10, { "DARK_CYAN" }); + this->draw_circle(mouse_pos, 5, { "DARK_CYAN" }); } } @@ -70,16 +70,21 @@ namespace rl::inline node void Level::on_character_spawn_projectile(godot::Node* obj) { godot::Node2D* node{ gdcast(obj) }; - rl::Projectile* proj{ m_projectile_spawner->spawn_projectile() }; - - proj->set_position(node->get_global_position()); - proj->set_rotation(node->get_rotation() - godot::Math::deg_to_rad(45.0)); + Projectile* projectile{ m_projectile_spawner->spawn_projectile() }; + if (projectile != nullptr) + { + projectile->set_position(node->get_global_position()); + projectile->set_rotation(node->get_rotation() - godot::Math::deg_to_rad(45.0)); - rl::Character* character{ gdcast(node) }; - if (character != nullptr) - proj->set_velocity(godot::Vector2{ 0, -1 }.rotated(character->get_global_rotation())); + Character* character{ gdcast(node) }; + if (character != nullptr) + { + projectile->set_velocity( + godot::Vector2(0, -1).rotated(character->get_global_rotation())); + } - this->add_child(proj); + this->add_child(projectile); + } } [[signal_slot]] diff --git a/src/nodes/projectile_spawner.cpp b/src/nodes/projectile_spawner.cpp index 8786cea..d62cb1f 100644 --- a/src/nodes/projectile_spawner.cpp +++ b/src/nodes/projectile_spawner.cpp @@ -1,16 +1,47 @@ #include "nodes/projectile_spawner.hpp" +#include "util/bind.hpp" + namespace rl::inline node { - void ProjectileSpawner::_ready() + [[nodiscard]] + Projectile* ProjectileSpawner::spawn_projectile() { - m_scene.set_path(path::scene::Bullet); + auto elapsed{ clock_t::now() - m_prev_spawn_time }; + if (elapsed < m_spawn_delay) + return nullptr; + else + { + Projectile* projectile{ m_scene.instantiate() }; + m_prev_spawn_time = clock_t::now(); + return projectile; + } } - [[nodiscard]] - Projectile* ProjectileSpawner::spawn_projectile() + [[property]] + double ProjectileSpawner::get_fire_rate() const + { + return m_fire_rate; + } + + [[property]] + void ProjectileSpawner::set_fire_rate(double fire_rate) + { + m_fire_rate = fire_rate; + m_spawn_delay = ProjectileSpawner::calculate_spawn_delay(m_fire_rate); + } + + ProjectileSpawner::millisec_t ProjectileSpawner::calculate_spawn_delay(double fire_rate) + { + // converts fire rate (shots per second) to the time delay between shots in ms. + // the multiplication by 100 is just to offset the rounding errors by shifting + // the decimal place to the right a few places before dividing. + return (1000ms * 100) / static_cast(fire_rate * 100); + } + + void ProjectileSpawner::_bind_methods() { - Projectile* projectile{ m_scene.instantiate() }; - return projectile; + bind_member_function(ProjectileSpawner, get_fire_rate); + bind_member_function(ProjectileSpawner, set_fire_rate); } } diff --git a/src/nodes/projectile_spawner.hpp b/src/nodes/projectile_spawner.hpp index d599983..aeb9ff3 100644 --- a/src/nodes/projectile_spawner.hpp +++ b/src/nodes/projectile_spawner.hpp @@ -1,14 +1,19 @@ #pragma once #include "nodes/projectile.hpp" +#include "util/bind.hpp" #include "util/constants.hpp" #include "util/scene.hpp" +#include + #include #include namespace rl::inline node { + using namespace std::chrono_literals; + class ProjectileSpawner : public godot::Node2D { GDCLASS(ProjectileSpawner, godot::Node2D); @@ -17,15 +22,27 @@ namespace rl::inline node ProjectileSpawner() = default; ~ProjectileSpawner() = default; - void _ready() override; Projectile* spawn_projectile(); protected: - static void _bind_methods() - { - } + [[property]] double get_fire_rate() const; + [[property]] void set_fire_rate(double fire_rate); - protected: - resource::preload::scene m_scene; + static void _bind_methods(); + + private: + using clock_t = std::chrono::high_resolution_clock; + using millisec_t = std::chrono::milliseconds; + static millisec_t calculate_spawn_delay(double fire_rate); + + private: + // number of prjectiles per second + double m_fire_rate{ 10.0 }; + // time delay between shots (ms). multiplication by 100 is just to offset rounding errors. + millisec_t m_spawn_delay{ ProjectileSpawner::calculate_spawn_delay(m_fire_rate) }; + // the time point that keeps track of when the last projectile was spawned. + clock_t::time_point m_prev_spawn_time{ clock_t::now() }; + // preloaded packed scene that will be instantiated per spawn + resource::preload::scene m_scene{ path::scene::Bullet }; }; }