diff --git a/src/projectile_pool.cpp b/src/projectile_pool.cpp new file mode 100644 index 0000000..b306741 --- /dev/null +++ b/src/projectile_pool.cpp @@ -0,0 +1,70 @@ +#include "projectile_pool.hpp" +#include "projectile.hpp" +#include +#include + +namespace godot { +void ProjectilePool::_bind_methods() { +#define CLASSNAME ProjectilePool +} + +void ProjectilePool::_exit_tree() { + if(this->data.is_valid()) { + for(Node3D *node : this->inactive) { + node->queue_free(); + } + } +} + +void ProjectilePool::set_data(Ref data) { + if(!this->data.is_null()) { + this->active.clear(); + for(Node3D *node : this->inactive) { + node->queue_free(); + } + } + this->data = data; + if(!data.is_valid()) + return; + this->count = this->data->get_projectile_count() * this->data->get_rounds_per_second() * 2; + for(size_t i{0}; i < this->count; ++i) + this->inactive.push_back(this->instantiate_new()); +} + +Node3D *ProjectilePool::claim_projectile() { + if(inactive.is_empty()) { + size_t const new_count = this->count + this->data->get_projectile_count() * this->data->get_rounds_per_second() * 2; + for(size_t i{this->count}; i < new_count; ++i) + this->inactive.push_back(this->instantiate_new()); + } + Node3D *const ret{this->inactive[0]}; + this->inactive.remove_at(0); + this->active.push_back(ret); + return ret; +} + +void ProjectilePool::return_projectile(Node3D *node) { + if(this->active.has(node)) { + this->active.erase(node); + this->inactive.push_back(node); + node->get_parent()->remove_child(node); + } else { + // leftover from previous weapon, free + node->queue_free(); + } +} + +Node3D *ProjectilePool::instantiate_new() const { + Ref scene = this->data->get_projectile_scene(); + if(!scene.is_valid() || !ClassDB::is_parent_class("Node3D", scene->get_state()->get_node_type(0))) + return nullptr; + Node3D *node = Object::cast_to(this->data->get_projectile_scene()->instantiate()); + IProjectile *projectile = dynamic_cast(node); + if(projectile != nullptr) { + projectile->set_weapon_data(this->data); + } else { + UtilityFunctions::push_warning("Projectile scene ", scene->get_path(), "' root node does not implement IProjectile"); + } + return node; +} +} diff --git a/src/projectile_pool.hpp b/src/projectile_pool.hpp new file mode 100644 index 0000000..6bf127f --- /dev/null +++ b/src/projectile_pool.hpp @@ -0,0 +1,30 @@ +#ifndef PROJECTILE_POOL_HPP +#define PROJECTILE_POOL_HPP + +#include "weapon_data.hpp" +#include +#include + +namespace godot { +class ProjectilePool : public Node { + GDCLASS(ProjectilePool, Node); + static void _bind_methods(); +public: + virtual void _exit_tree() override; + void set_data(Ref data); + Ref get_data() const; + + Node3D *claim_projectile(); + void return_projectile(Node3D *node); + +protected: + Node3D *instantiate_new() const; +private: + Ref data; + size_t count; + Vector active{}; + Vector inactive{}; +}; +} + +#endif // !PROJECTILE_POOL_HPP