diff --git a/project/demos/ability_test_ui/abilities/ability_000.gd b/project/demos/ability_test_ui/abilities/ability_000.gd new file mode 100644 index 0000000..c075ee2 --- /dev/null +++ b/project/demos/ability_test_ui/abilities/ability_000.gd @@ -0,0 +1 @@ +extends Ability diff --git a/project/demos/ability_test_ui/main.gd b/project/demos/ability_test_ui/main.gd new file mode 100644 index 0000000..e07a0bf --- /dev/null +++ b/project/demos/ability_test_ui/main.gd @@ -0,0 +1 @@ +extends Control diff --git a/project/demos/ability_test_ui/main.tscn b/project/demos/ability_test_ui/main.tscn new file mode 100644 index 0000000..c318785 --- /dev/null +++ b/project/demos/ability_test_ui/main.tscn @@ -0,0 +1,14 @@ +[gd_scene load_steps=2 format=3 uid="uid://ye6lunjs1ns2"] + +[ext_resource type="Script" path="res://demos/ability_test_ui/main.gd" id="1_8w5uo"] + +[node name="Main" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_8w5uo") + +[node name="AbilityContainer" type="AbilityContainer" parent="."] diff --git a/src/register_types.cpp b/src/register_types.cpp index bd6b968..2d6e202 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -10,6 +10,10 @@ #include /// ggs stuff +#include "system/ability/ability.h" +#include "system/ability/ability_container.h" +#include "system/ability/ability_grant.h" +#include "system/ability/ability_queue.h" #include "system/attribute/attribute.h" #include "system/attribute/attribute_container.h" #include "system/attribute/attribute_effect.h" @@ -44,6 +48,9 @@ void initialize_ggs_module(ModuleInitializationLevel p_level) if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) { /// registering classes + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); @@ -55,6 +62,8 @@ void initialize_ggs_module(ModuleInitializationLevel p_level) ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_internal_class(); + ClassDB::register_internal_class(); /// registering settings ggs::AttributeProjectSettings::setup(); diff --git a/src/system/ability/ability.cpp b/src/system/ability/ability.cpp new file mode 100644 index 0000000..40f429b --- /dev/null +++ b/src/system/ability/ability.cpp @@ -0,0 +1,228 @@ +#include "ability.h" + +using namespace ggs; + +void Ability::_bind_methods() +{ + /// binds methods + ClassDB::bind_method(D_METHOD("get_ability_name"), &Ability::get_ability_name); + ClassDB::bind_method(D_METHOD("get_cooldown"), &Ability::get_cooldown); + ClassDB::bind_method(D_METHOD("get_tags_added_on_activation"), &Ability::get_tags_added_on_activation); + ClassDB::bind_method(D_METHOD("get_tags_added_on_cooldown_end"), &Ability::get_tags_added_on_cooldown_end); + ClassDB::bind_method(D_METHOD("get_tags_added_on_cooldown_start"), &Ability::get_tags_added_on_cooldown_start); + ClassDB::bind_method(D_METHOD("get_tags_added_on_grant"), &Ability::get_tags_added_on_grant); + ClassDB::bind_method(D_METHOD("get_tags_removed_on_activation"), &Ability::get_tags_removed_on_activation); + ClassDB::bind_method(D_METHOD("get_tags_removed_on_block"), &Ability::get_tags_removed_on_block); + ClassDB::bind_method(D_METHOD("get_tags_removed_on_cancel"), &Ability::get_tags_removed_on_cancel); + ClassDB::bind_method(D_METHOD("get_tags_removed_on_cooldown_end"), &Ability::get_tags_removed_on_cooldown_end); + ClassDB::bind_method(D_METHOD("get_tags_removed_on_cooldown_start"), &Ability::get_tags_removed_on_cooldown_start); + ClassDB::bind_method(D_METHOD("get_tags_required_to_activate"), &Ability::get_tags_required_to_activate); + ClassDB::bind_method(D_METHOD("get_tags_required_to_block"), &Ability::get_tags_required_to_block); + ClassDB::bind_method(D_METHOD("get_tags_required_to_cancel"), &Ability::get_tags_required_to_cancel); + ClassDB::bind_method(D_METHOD("get_tags_required_to_grant"), &Ability::get_tags_required_to_grant); + ClassDB::bind_method(D_METHOD("has_cooldown"), &Ability::has_cooldown); + ClassDB::bind_method(D_METHOD("set_ability_name", "name"), &Ability::set_ability_name); + ClassDB::bind_method(D_METHOD("set_cooldown", "cooldown"), &Ability::set_cooldown); + ClassDB::bind_method(D_METHOD("set_tags_added_on_activation", "tags"), &Ability::set_tags_added_on_activation); + ClassDB::bind_method(D_METHOD("set_tags_added_on_cooldown_end", "tags"), &Ability::set_tags_added_on_cooldown_end); + ClassDB::bind_method(D_METHOD("set_tags_added_on_cooldown_start", "tags"), &Ability::set_tags_added_on_cooldown_start); + ClassDB::bind_method(D_METHOD("set_tags_added_on_grant", "tags"), &Ability::set_tags_added_on_grant); + ClassDB::bind_method(D_METHOD("set_tags_removed_on_activation", "tags"), &Ability::set_tags_removed_on_activation); + ClassDB::bind_method(D_METHOD("set_tags_removed_on_block", "tags"), &Ability::set_tags_removed_on_block); + ClassDB::bind_method(D_METHOD("set_tags_removed_on_cancel", "tags"), &Ability::set_tags_removed_on_cancel); + ClassDB::bind_method(D_METHOD("set_tags_removed_on_cooldown_end", "tags"), &Ability::set_tags_removed_on_cooldown_end); + ClassDB::bind_method(D_METHOD("set_tags_removed_on_cooldown_start", "tags"), &Ability::set_tags_removed_on_cooldown_start); + ClassDB::bind_method(D_METHOD("set_tags_required_to_activate", "tags"), &Ability::set_tags_required_to_activate); + ClassDB::bind_method(D_METHOD("set_tags_required_to_block", "tags"), &Ability::set_tags_required_to_block); + ClassDB::bind_method(D_METHOD("set_tags_required_to_cancel", "tags"), &Ability::set_tags_required_to_cancel); + ClassDB::bind_method(D_METHOD("set_tags_required_to_grant", "tags"), &Ability::set_tags_required_to_grant); + + /// binds properties + + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "ability_name"), "set_ability_name", "get_ability_name"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cooldown"), "set_cooldown", "get_cooldown"); + ADD_GROUP("Tags", "tags_"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_added_on_cooldown_end"), "set_tags_added_on_cooldown_end", "get_tags_added_on_cooldown_end"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_added_on_cooldown_start"), "set_tags_added_on_cooldown_start", "get_tags_added_on_cooldown_start"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_added_on_grant"), "set_tags_added_on_grant", "get_tags_added_on_grant"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_added_on_activation"), "set_tags_added_on_activation", "get_tags_added_on_activation"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_removed_on_cooldown_end"), "set_tags_removed_on_cooldown_end", "get_tags_removed_on_cooldown_end"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_removed_on_cooldown_start"), "set_tags_removed_on_cooldown_start", "get_tags_removed_on_cooldown_start"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_removed_on_activation"), "set_tags_removed_on_activation", "get_tags_removed_on_activation"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_removed_on_block"), "set_tags_removed_on_block", "get_tags_removed_on_block"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_removed_on_cancel"), "set_tags_removed_on_cancel", "get_tags_removed_on_cancel"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_required_to_activate"), "set_tags_required_to_activate", "get_tags_required_to_activate"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_required_to_block"), "set_tags_required_to_block", "get_tags_required_to_block"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_required_to_cancel"), "set_tags_required_to_cancel", "get_tags_required_to_cancel"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "tags_required_to_grant"), "set_tags_required_to_grant", "get_tags_required_to_grant"); + + /// binds signals +} + +Ability::Ability() +{ +} + +Ability::~Ability() +{ +} + +bool Ability::has_cooldown() const +{ + return cooldown - 1.0f > 0.05f; +} + +StringName Ability::get_ability_name() const +{ + if (ability_name.length() == 0) + { + return StringName("ggs.ability." + String::num(get_rid().get_id())); + } + + return ability_name; +} + +float Ability::get_cooldown() const +{ + return cooldown; +} + +PackedStringArray Ability::get_tags_added_on_cooldown_end() const +{ + return tags_added_on_cooldown_end; +} + +PackedStringArray Ability::get_tags_added_on_cooldown_start() const +{ + return tags_added_on_cooldown_start; +} + +PackedStringArray Ability::get_tags_added_on_grant() const +{ + return tags_added_on_grant; +} + +PackedStringArray Ability::get_tags_added_on_activation() const +{ + return tags_added_on_activation; +} + +PackedStringArray Ability::get_tags_removed_on_cooldown_end() const +{ + return tags_removed_on_cooldown_end; +} + +PackedStringArray Ability::get_tags_removed_on_cooldown_start() const +{ + return tags_removed_on_cooldown_start; +} + +PackedStringArray Ability::get_tags_removed_on_activation() const +{ + return tags_removed_on_activation; +} + +PackedStringArray Ability::get_tags_removed_on_block() const +{ + return tags_removed_on_block; +} + +PackedStringArray Ability::get_tags_removed_on_cancel() const +{ + return tags_removed_on_cancel; +} + +PackedStringArray Ability::get_tags_required_to_activate() const +{ + return tags_required_to_activate; +} + +PackedStringArray Ability::get_tags_required_to_block() const +{ + return tags_required_to_block; +} + +PackedStringArray Ability::get_tags_required_to_cancel() const +{ + return tags_required_to_cancel; +} + +PackedStringArray Ability::get_tags_required_to_grant() const +{ + return tags_required_to_grant; +} + +void Ability::set_ability_name(const StringName &p_name) +{ + ability_name = p_name; +} + +void Ability::set_cooldown(const float p_cooldown) +{ + cooldown = p_cooldown; +} + +void Ability::set_tags_added_on_cooldown_end(const PackedStringArray &p_tags) +{ + tags_added_on_cooldown_end = p_tags; +} + +void Ability::set_tags_added_on_cooldown_start(const PackedStringArray &p_tags) +{ + tags_added_on_cooldown_start = p_tags; +} + +void Ability::set_tags_added_on_grant(const PackedStringArray &p_tags) +{ + tags_added_on_grant = p_tags; +} + +void Ability::set_tags_added_on_activation(const PackedStringArray &p_tags) +{ + tags_added_on_activation = p_tags; +} + +void Ability::set_tags_removed_on_cooldown_end(const PackedStringArray &p_tags) +{ + tags_removed_on_cooldown_end = p_tags; +} + +void Ability::set_tags_removed_on_cooldown_start(const PackedStringArray &p_tags) +{ + tags_removed_on_cooldown_start = p_tags; +} + +void Ability::set_tags_removed_on_activation(const PackedStringArray &p_tags) +{ + tags_removed_on_activation = p_tags; +} + +void Ability::set_tags_removed_on_block(const PackedStringArray &p_tags) +{ + tags_removed_on_block = p_tags; +} + +void Ability::set_tags_removed_on_cancel(const PackedStringArray &p_tags) +{ + tags_removed_on_cancel = p_tags; +} + +void Ability::set_tags_required_to_activate(const PackedStringArray &p_tags) +{ + tags_required_to_activate = p_tags; +} + +void Ability::set_tags_required_to_block(const PackedStringArray &p_tags) +{ + tags_required_to_block = p_tags; +} + +void Ability::set_tags_required_to_cancel(const PackedStringArray &p_tags) +{ + tags_required_to_cancel = p_tags; +} + +void Ability::set_tags_required_to_grant(const PackedStringArray &p_tags) +{ + tags_required_to_grant = p_tags; +} diff --git a/src/system/ability/ability.h b/src/system/ability/ability.h new file mode 100644 index 0000000..302e50c --- /dev/null +++ b/src/system/ability/ability.h @@ -0,0 +1,148 @@ +#ifndef GGS_ABILITY_H +#define GGS_ABILITY_H + +#include + +using namespace godot; + +namespace ggs +{ + class Ability : public Resource + { + GDCLASS(Ability, Resource); + + protected: + static void _bind_methods(); + /// @brief The name of the ability. + StringName ability_name; + /// @brief The ability cooldown. Set to 0 to disable. + float cooldown; + /// @brief Tags that are added to the user when the ability's cooldown ends. + PackedStringArray tags_added_on_cooldown_end; + /// @brief Tags that are added to the user when the ability's cooldown starts. + PackedStringArray tags_added_on_cooldown_start; + /// @brief The tags that are added to the user when this ability is granted. + PackedStringArray tags_added_on_grant; + /// @brief The tags that are added to the user when this ability is activated. + PackedStringArray tags_added_on_activation; + /// @brief The tags that are removed from the user when this ability's cooldown ends. + PackedStringArray tags_removed_on_cooldown_end; + /// @brief The tags that are removed from the user when this ability's cooldown starts. + PackedStringArray tags_removed_on_cooldown_start; + /// @brief The tags that are removed from the user when this ability is activated. + PackedStringArray tags_removed_on_activation; + /// @brief The tags that are removed from the user when this ability is blocked. + PackedStringArray tags_removed_on_block; + /// @brief The tags that are removed from the user when this ability is cancelled. + PackedStringArray tags_removed_on_cancel; + /// @brief The tags that are required for this ability to be activated. + PackedStringArray tags_required_to_activate; + /// @brief The tags which will block this ability from being activated. + PackedStringArray tags_required_to_block; + /// @brief The tags that are required for this ability to be cancelled. + PackedStringArray tags_required_to_cancel; + /// @brief The tags that are required for this ability to be granted. + PackedStringArray tags_required_to_grant; + + public: + /// @brief Default constructor. + Ability(); + /// @brief Default destructor. + ~Ability(); + /// @brief Returns if the ability has a cooldown. + /// @return True if the ability has a cooldown, false otherwise. + bool has_cooldown() const; + /// @brief Gets the ability name. + /// @return The ability name. + StringName get_ability_name() const; + /// @brief Gets the ability cooldown. + /// @return The ability cooldown. + float get_cooldown() const; + /// @brief Gets the tags that are added to the user when the ability's cooldown ends. + /// @return The tags that are added to the user when the ability's cooldown ends. + PackedStringArray get_tags_added_on_cooldown_end() const; + /// @brief Gets the tags that are added to the user when the ability's cooldown starts. + /// @return The tags that are added to the user when the ability's cooldown starts. + PackedStringArray get_tags_added_on_cooldown_start() const; + /// @brief Gets the tags that are added to the user when this ability is granted. + /// @return The tags that are added to the user when this ability is granted. + PackedStringArray get_tags_added_on_grant() const; + /// @brief Gets the tags that are added to the user when this ability is activated. + /// @return The tags that are added to the user when this ability is activated. + PackedStringArray get_tags_added_on_activation() const; + /// @brief Gets the tags that are removed from the user when this ability's cooldown ends. + /// @return The tags that are removed from the user when this ability's cooldown ends. + PackedStringArray get_tags_removed_on_cooldown_end() const; + /// @brief Gets the tags that are removed from the user when this ability's cooldown starts. + /// @return The tags that are removed from the user when this ability's cooldown starts. + PackedStringArray get_tags_removed_on_cooldown_start() const; + /// @brief Gets the tags that are removed from the user when this ability is activated. + /// @return The tags that are removed from the user when this ability is activated. + PackedStringArray get_tags_removed_on_activation() const; + /// @brief Gets the tags that are removed from the user when this ability is blocked. + /// @return The tags that are removed from the user when this ability is blocked. + PackedStringArray get_tags_removed_on_block() const; + /// @brief Gets the tags that are removed from the user when this ability is cancelled. + /// @return The tags that are removed from the user when this ability is cancelled. + PackedStringArray get_tags_removed_on_cancel() const; + /// @brief Gets the tags that are required for this ability to be activated. + /// @return The tags that are required for this ability to be activated. + PackedStringArray get_tags_required_to_activate() const; + /// @brief Gets the tags which will block this ability from being activated. + /// @return The tags which will block this ability from being activated. + PackedStringArray get_tags_required_to_block() const; + /// @brief Gets the tags that are required for this ability to be cancelled. + /// @return The tags that are required for this ability to be cancelled. + PackedStringArray get_tags_required_to_cancel() const; + /// @brief Gets the tags that are required for this ability to be granted. + /// @return The tags that are required for this ability to be granted. + PackedStringArray get_tags_required_to_grant() const; + /// @brief Sets the tags that are required for this ability to be activated. + /// @param p_name The name of the ability. + void set_ability_name(const StringName &p_name); + /// @brief Sets the ability cooldown. + /// @param p_cooldown The ability cooldown. + void set_cooldown(const float p_cooldown); + /// @brief Sets the tags that are added to the user when the ability's cooldown ends. + /// @param p_tags The tags that are added to the user when the ability's cooldown ends. + void set_tags_added_on_cooldown_end(const PackedStringArray &p_tags); + /// @brief Sets the tags that are added to the user when the ability's cooldown starts. + /// @param p_tags The tags that are added to the user when the ability's cooldown starts. + void set_tags_added_on_cooldown_start(const PackedStringArray &p_tags); + /// @brief Sets the tags that are added to the user when this ability is granted. + /// @param p_tags The tags that are added to the user when this ability is granted. + void set_tags_added_on_grant(const PackedStringArray &p_tags); + /// @brief Sets the tags that are added to the user when this ability is activated. + /// @param p_tags The tags that are added to the user when this ability is activated. + void set_tags_added_on_activation(const PackedStringArray &p_tags); + /// @brief Sets the tags that are removed from the user when this ability's cooldown ends. + /// @param p_tags The tags that are removed from the user when this ability's cooldown ends. + void set_tags_removed_on_cooldown_end(const PackedStringArray &p_tags); + /// @brief Sets the tags that are removed from the user when this ability's cooldown starts. + /// @param p_tags The tags that are removed from the user when this ability's cooldown starts. + void set_tags_removed_on_cooldown_start(const PackedStringArray &p_tags); + /// @brief Sets the tags that are removed from the user when this ability is activated. + /// @param p_tags The tags that are removed from the user when this ability is activated. + void set_tags_removed_on_activation(const PackedStringArray &p_tags); + /// @brief Sets the tags that are removed from the user when this ability is blocked. + /// @param p_tags The tags that are removed from the user when this ability is blocked. + void set_tags_removed_on_block(const PackedStringArray &p_tags); + /// @brief Sets the tags that are removed from the user when this ability is cancelled. + /// @param p_tags The tags that are removed from the user when this ability is cancelled. + void set_tags_removed_on_cancel(const PackedStringArray &p_tags); + /// @brief Sets the tags that are required for this ability to be activated. + /// @param p_tags The tags that are required for this ability to be activated. + void set_tags_required_to_activate(const PackedStringArray &p_tags); + /// @brief Sets the tags which will block this ability from being activated. + /// @param p_tags The tags which will block this ability from being activated. + void set_tags_required_to_block(const PackedStringArray &p_tags); + /// @brief Sets the tags that are required for this ability to be cancelled. + /// @param p_tags The tags that are required for this ability to be cancelled. + void set_tags_required_to_cancel(const PackedStringArray &p_tags); + /// @brief Sets the tags that are required for this ability to be granted. + /// @param p_tags The tags that are required for this ability to be granted. + void set_tags_required_to_grant(const PackedStringArray &p_tags); + }; +} + +#endif \ No newline at end of file diff --git a/src/system/ability/ability_container.cpp b/src/system/ability/ability_container.cpp new file mode 100644 index 0000000..671304e --- /dev/null +++ b/src/system/ability/ability_container.cpp @@ -0,0 +1,540 @@ +#include + +#include "ability_container.h" +#include "system/tag/tag_manager.h" + +using namespace ggs; + +TypedArray AbilityContainer::_get_abilities() const +{ + return _abilities; +} + +void AbilityContainer::_set_abilities(TypedArray p_abilities) +{ + _abilities = p_abilities; +} + +void AbilityContainer::_handle_ability_enqueued(Ability *p_ability) +{ + if (p_ability != nullptr) + { + tags.add_tags(p_ability->get_tags_added_on_cooldown_start()); + emit_signal("ability_enqueued", Ref(p_ability)); + } +} + +void AbilityContainer::_handle_ability_dequeued(Ability *p_ability) +{ + if (p_ability != nullptr) + { + tags.remove_tags(p_ability->get_tags_removed_on_cooldown_end()); + emit_signal("ability_dequeued", Ref(p_ability)); + } +} + +void AbilityContainer::_bind_methods() +{ + /// binds methods + ClassDB::bind_method(D_METHOD("_get_abilities"), &AbilityContainer::_get_abilities); + ClassDB::bind_method(D_METHOD("_set_abilities", "abilities"), &AbilityContainer::_set_abilities); + ClassDB::bind_method(D_METHOD("activate_by_name", "ability_name"), &AbilityContainer::activate_by_name); + ClassDB::bind_method(D_METHOD("activate", "ability"), &AbilityContainer::activate); + ClassDB::bind_method(D_METHOD("add_ability", "ability"), &AbilityContainer::add_ability); + ClassDB::bind_method(D_METHOD("block_by_name", "ability_name"), &AbilityContainer::block_by_name); + ClassDB::bind_method(D_METHOD("block", "ability"), &AbilityContainer::block); + ClassDB::bind_method(D_METHOD("can_activate_by_name", "ability_name"), &AbilityContainer::can_activate_by_name); + ClassDB::bind_method(D_METHOD("can_activate", "ability"), &AbilityContainer::can_activate); + ClassDB::bind_method(D_METHOD("can_block_by_name", "ability_name"), &AbilityContainer::can_block_by_name); + ClassDB::bind_method(D_METHOD("can_block", "ability"), &AbilityContainer::can_block); + ClassDB::bind_method(D_METHOD("can_cancel_by_name", "ability_name"), &AbilityContainer::can_cancel_by_name); + ClassDB::bind_method(D_METHOD("can_cancel", "ability"), &AbilityContainer::can_cancel); + ClassDB::bind_method(D_METHOD("can_grant", "ability"), &AbilityContainer::can_grant); + ClassDB::bind_method(D_METHOD("cancel_by_name", "ability_name"), &AbilityContainer::cancel_by_name); + ClassDB::bind_method(D_METHOD("cancel", "ability"), &AbilityContainer::cancel); + ClassDB::bind_method(D_METHOD("get_abilities"), &AbilityContainer::get_abilities); + ClassDB::bind_method(D_METHOD("get_ability_owner"), &AbilityContainer::get_ability_owner); + ClassDB::bind_method(D_METHOD("get_ability", "ability_name"), &AbilityContainer::get_ability); + ClassDB::bind_method(D_METHOD("grant_abilities"), &AbilityContainer::grant_abilities); + ClassDB::bind_method(D_METHOD("grant_ability", "ability"), &AbilityContainer::grant_ability); + ClassDB::bind_method(D_METHOD("has_ability_owner"), &AbilityContainer::has_ability_owner); + ClassDB::bind_method(D_METHOD("has_ability", "ability"), &AbilityContainer::has_ability); + ClassDB::bind_method(D_METHOD("set_abilities", "abilities"), &AbilityContainer::set_abilities); + ClassDB::bind_method(D_METHOD("set_ability_owner", "ability_owner"), &AbilityContainer::set_ability_owner); + ClassDB::bind_method(D_METHOD("revoke_by_name", "ability_name"), &AbilityContainer::revoke_by_name); + ClassDB::bind_method(D_METHOD("revoke", "ability"), &AbilityContainer::revoke); + ClassDB::bind_method(D_METHOD("revoke_all"), &AbilityContainer::revoke_all); + + ClassDB::bind_method(D_METHOD("_handle_ability_enqueued", "ability"), &AbilityContainer::_handle_ability_enqueued); + ClassDB::bind_method(D_METHOD("_handle_ability_dequeued", "ability"), &AbilityContainer::_handle_ability_dequeued); + + /// binds properties + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "abilities", PROPERTY_HINT_RESOURCE_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Ability")), "_set_abilities", "_get_abilities"); + + /// binds signals + ADD_SIGNAL(MethodInfo("ability_activated", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); + ADD_SIGNAL(MethodInfo("ability_activation_fail", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); + ADD_SIGNAL(MethodInfo("ability_added", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); + ADD_SIGNAL(MethodInfo("ability_block_fail", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); + ADD_SIGNAL(MethodInfo("ability_blocked", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); + ADD_SIGNAL(MethodInfo("ability_cancellation_failed", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); + ADD_SIGNAL(MethodInfo("ability_cancelled", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); + ADD_SIGNAL(MethodInfo("ability_dequeued", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); + ADD_SIGNAL(MethodInfo("ability_enqueued", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); + ADD_SIGNAL(MethodInfo("ability_granted", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); +} + +AbilityGrant *AbilityContainer::get_ability_grant(const StringName &p_ability_name) const +{ + for (int i = 0; i < abilities.size(); i++) + { + Variant variant = abilities[i]; + AbilityGrant *ability_grant = cast_to(variant); + Ref ability = Ref(ability_grant->get_ability()); + + if (ability->get_ability_name() == p_ability_name) + { + return ability_grant; + } + } + + return nullptr; +} + +AbilityGrant *AbilityContainer::get_ability_grant(Ability *p_ability) const +{ + if (p_ability != nullptr) + { + return get_ability_grant(p_ability->get_ability_name()); + } + + return nullptr; +} + +void AbilityContainer::revoke_ability_grant(AbilityGrant *p_ability_grant) +{ + Ref ability = Ref(p_ability_grant->get_ability()); + + if (ability != nullptr && ability.is_valid()) + { + Ability *ability_ptr = ability.ptr(); + + if (ability_queue->can_dequeue(ability_ptr)) + { + ability_queue->dequeue(ability_ptr); + emit_signal("ability_dequeued", ability); + } + + p_ability_grant->set_grant(false); + tags.remove_tags(ability_ptr->get_tags_added_on_activation()); + tags.remove_tags(ability_ptr->get_tags_added_on_grant()); + tags.remove_tags(ability_ptr->get_tags_added_on_cooldown_end()); + tags.remove_tags(ability_ptr->get_tags_added_on_cooldown_start()); + } +} + +void AbilityContainer::setup_tag_dictionary() +{ + if (has_ability_owner()) + { + tags.set_tags(TagManager::get_singleton()->get_tags(ability_owner)); + } +} + +AbilityContainer::AbilityContainer() +{ + abilities = TypedArray(); + ability_queue = memnew(AbilityQueue); +} + +AbilityContainer::~AbilityContainer() +{ +} + +void AbilityContainer::_process(float delta) +{ + ability_queue->process(delta); +} + +void AbilityContainer::_ready() +{ + if (!Engine::get_singleton()->is_editor_hint()) + { + set_abilities(_abilities); + setup_tag_dictionary(); + grant_abilities(); + + ability_queue->connect("ability_enqueued", Callable(this, "_handle_ability_enqueued")); + ability_queue->connect("ability_dequeued", Callable(this, "_handle_ability_dequeued")); + } +} + +void AbilityContainer::activate(Ability *p_ability) +{ + if (p_ability != nullptr) + { + activate_by_name(p_ability->get_ability_name()); + } +} + +void AbilityContainer::activate_by_name(const StringName &p_ability_name) +{ + AbilityGrant *ability_grant = get_ability_grant(p_ability_name); + + if (ability_grant != nullptr) + { + Ref ability = Ref(ability_grant->get_ability()); + + if (ability != nullptr && ability.is_valid() && can_activate_by_name(p_ability_name)) + { + if (ability->has_method("activate")) + { + ability->call("activate", this, ability_owner); + } + + tags.add_tags(ability->get_tags_added_on_activation()); + + emit_signal("ability_activated", ability); + + tags.remove_tags(ability->get_tags_removed_on_activation()); + + Ability *ability_ptr = ability.ptr(); + + if (ability_queue->can_enqueue(ability_ptr)) + { + ability_queue->enqueue(ability_ptr); + emit_signal("ability_enqueued", ability); + } + } + else + { + emit_signal("ability_activation_fail", ability); + } + } +} + +void AbilityContainer::add_ability(Ability *p_ability) +{ + if (!has_ability(p_ability)) + { + AbilityGrant ability_grant = AbilityGrant(); + ability_grant.set_ability(p_ability); + abilities.append(ability_grant); + emit_signal("ability_added", Ref(p_ability)); + } +} + +void AbilityContainer::block(Ability *p_ability) +{ + if (p_ability != nullptr) + { + block_by_name(p_ability->get_ability_name()); + } +} + +void AbilityContainer::block_by_name(const StringName &p_ability_name) +{ + AbilityGrant *ability_grant = get_ability_grant(p_ability_name); + + if (ability_grant != nullptr) + { + Ref ability = Ref(ability_grant->get_ability()); + + if (ability != nullptr && ability.is_valid() && can_block_by_name(p_ability_name)) + { + if (ability->has_method("block")) + { + ability->call("block", this, ability_owner); + } + + emit_signal("ability_blocked", ability); + + Ability *ability_ptr = ability.ptr(); + + if (ability_queue->can_dequeue(ability_ptr)) + { + ability_queue->dequeue(ability_ptr); + emit_signal("ability_dequeued", ability); + } + + tags.remove_tags(ability->get_tags_removed_on_block()); + } + else + { + emit_signal("ability_block_fail", ability); + } + } +} + +bool AbilityContainer::can_activate(Ability *p_ability) const +{ + if (p_ability != nullptr) + { + return can_activate_by_name(p_ability->get_ability_name()); + } + + return false; +} + +bool AbilityContainer::can_activate_by_name(const StringName &p_ability_name) const +{ + AbilityGrant *ability_grant = get_ability_grant(p_ability_name); + + if (ability_grant != nullptr && has_ability_owner()) + { + return tags.has_all(ability_grant->get_ability()->get_tags_required_to_activate()); + } + + return false; +} + +bool AbilityContainer::can_block(Ability *p_ability) const +{ + if (p_ability != nullptr) + { + return can_block_by_name(p_ability->get_ability_name()); + } + + return false; +} + +bool AbilityContainer::can_block_by_name(const StringName &p_ability_name) const +{ + AbilityGrant *ability_grant = get_ability_grant(p_ability_name); + + if (ability_grant != nullptr && has_ability_owner()) + { + return !tags.has_some(ability_grant->get_ability()->get_tags_required_to_block()); + } + + return false; +} + +bool AbilityContainer::can_cancel(Ability *p_ability) const +{ + if (p_ability != nullptr) + { + return can_cancel_by_name(p_ability->get_ability_name()); + } + + return false; +} + +bool AbilityContainer::can_cancel_by_name(const StringName &p_ability_name) const +{ + AbilityGrant *ability_grant = get_ability_grant(p_ability_name); + + if (ability_grant != nullptr && has_ability_owner()) + { + return !tags.has_some(ability_grant->get_ability()->get_tags_required_to_cancel()); + } + + return false; +} + +bool AbilityContainer::can_grant(Ability *p_ability) const +{ + AbilityGrant *ability_grant = get_ability_grant(p_ability); + + if (ability_grant != nullptr && has_ability_owner()) + { + } + + return false; +} + +bool AbilityContainer::can_revoke(Ability *p_ability) const +{ + return has_ability(p_ability) && !ability_queue->has_enqueued_ability(p_ability); +} + +void AbilityContainer::cancel(Ability *p_ability) +{ + if (p_ability != nullptr) + { + cancel_by_name(p_ability->get_ability_name()); + } +} + +void AbilityContainer::cancel_by_name(const StringName &p_ability_name) +{ + if (can_cancel_by_name(p_ability_name)) + { + AbilityGrant *ability_grant = get_ability_grant(p_ability_name); + + if (ability_grant != nullptr) + { + Ref ability = Ref(ability_grant->get_ability()); + + if (ability != nullptr && ability.is_valid() && can_cancel_by_name(p_ability_name)) + { + if (ability->has_method("cancel")) + { + ability->call("cancel", this, ability_owner); + } + + Ability *ability_ptr = ability.ptr(); + + if (ability_queue->can_dequeue(ability_ptr)) + { + ability_queue->dequeue(ability_ptr); + emit_signal("ability_dequeued", ability); + } + + emit_signal("ability_cancelled", ability); + + tags.remove_tags(ability->get_tags_removed_on_cancel()); + } + else + { + emit_signal("ability_cancellation_failed", ability); + } + } + } +} + +bool AbilityContainer::has_ability(Ability *p_ability) const +{ + return get_ability_grant(p_ability) != nullptr; +} + +bool AbilityContainer::has_ability_owner() const +{ + return ability_owner != nullptr; +} + +Ref AbilityContainer::get_ability(const StringName &p_ability_name) const +{ + AbilityGrant *ability_grant = get_ability_grant(p_ability_name); + + if (ability_grant != nullptr) + { + return Ref(ability_grant->get_ability()); + } + + return nullptr; +} + +TypedArray AbilityContainer::get_abilities() const +{ + TypedArray output = TypedArray(); + + for (int i = 0; i < abilities.size(); i++) + { + Variant variant = abilities[i]; + AbilityGrant *ability_grant = cast_to(variant); + + if (ability_grant != nullptr) + { + Ref ability = Ref(ability_grant->get_ability()); + + if (ability != nullptr && ability.is_valid()) + { + output.append(ability); + } + } + } + + return output; +} + +Node *AbilityContainer::get_ability_owner() const +{ + return ability_owner; +} + +void AbilityContainer::grant_ability(Ability *p_ability) +{ + if (can_grant(p_ability)) + { + AbilityGrant *ability_grant = get_ability_grant(p_ability); + + if (ability_grant != nullptr) + { + ability_grant->set_grant(true); + emit_signal("ability_granted", Ref(p_ability)); + tags.add_tags(ability_grant->get_ability()->get_tags_added_on_grant()); + } + } +} + +void AbilityContainer::grant_abilities() +{ + for (int i = 0; i < abilities.size(); i++) + { + Variant variant = abilities[i]; + AbilityGrant *ability_grant = cast_to(variant); + + if (ability_grant != nullptr) + { + Ref ability = ability_grant->get_ability(); + + ability_grant->set_grant(tags.has_all(ability->get_tags_required_to_grant())); + + tags.add_tags(ability->get_tags_added_on_grant()); + + if (ability_grant->get_grant()) + { + emit_signal("ability_granted", ability); + } + } + } +} + +void AbilityContainer::revoke(Ability *p_ability) +{ + if (p_ability != nullptr) + { + revoke_by_name(p_ability->get_ability_name()); + } +} + +void AbilityContainer::revoke_by_name(const StringName &p_ability_name) +{ + AbilityGrant *ability_grant = get_ability_grant(p_ability_name); + + if (ability_grant != nullptr) + { + revoke_ability_grant(ability_grant); + } +} + +void AbilityContainer::revoke_all() +{ + for (int i = 0; i < abilities.size(); i++) + { + Variant variant = abilities[i]; + AbilityGrant *ability_grant = cast_to(variant); + + if (ability_grant != nullptr) + { + revoke_ability_grant(ability_grant); + } + } +} + +void AbilityContainer::set_abilities(TypedArray p_abilities) +{ + WARN_PRINT(vformat("Setting abilities")); + for (int i = 0; i < p_abilities.size(); i++) + { + AbilityGrant ability_grant = AbilityGrant(); + Variant ability_variant = p_abilities[i]; + Ability *ability = cast_to(ability_variant); + + ability_grant.set_ability(ability); + abilities.append(ability_grant); + + if (can_grant(ability)) + { + ability_grant.set_grant(true); + } + } +} + +void AbilityContainer::set_ability_owner(Node *p_ability_owner) +{ + ability_owner = p_ability_owner; + setup_tag_dictionary(); + grant_abilities(); +} diff --git a/src/system/ability/ability_container.h b/src/system/ability/ability_container.h new file mode 100644 index 0000000..974a61e --- /dev/null +++ b/src/system/ability/ability_container.h @@ -0,0 +1,154 @@ +#ifndef GGS_ABILITY_ABILITY_CONTAINER_H +#define GGS_ABILITY_ABILITY_CONTAINER_H + +#include + +#include "ability.h" +#include "ability_grant.h" +#include "ability_queue.h" +#include "system/tag/tag_dictionary.h" + +using namespace godot; + +namespace ggs +{ + class AbilityContainer : public Node + { + GDCLASS(AbilityContainer, Node); + + private: + // note: doing this because the normal set_abilities must accept a TypedArray + TypedArray _abilities; + TypedArray _get_abilities() const; + void _set_abilities(TypedArray p_abilities); + void _handle_ability_enqueued(Ability *p_ability); + void _handle_ability_dequeued(Ability *p_ability); + + protected: + /// @brief Binds methods to Godot. + static void _bind_methods(); + /// @brief The ability queue. + AbilityQueue *ability_queue; + /// @brief The abilities that are contained. + TypedArray abilities; + /// @brief The owner of the abilities. + Node *ability_owner; + /// @brief Gets the ability grant. + /// @param p_ability The ability to get the grant for. + /// @return The ability grant. + AbilityGrant *get_ability_grant(const StringName &p_ability_name) const; + /// @brief Gets the ability grant. + /// @param p_ability The ability to get the grant for. + /// @return The ability grant. + AbilityGrant *get_ability_grant(Ability *p_ability) const; + /// @brief Revokes an ability grant. + /// @param p_ability_grant The ability grant to revoke. + void revoke_ability_grant(AbilityGrant *p_ability_grant); + /// @brief Gets the tag dictionary of the owner. + void setup_tag_dictionary(); + /// @brief The tags that the owner has. + TagDictionary tags; + + public: + /// @brief Default constructor. + AbilityContainer(); + /// @brief Default destructor. + ~AbilityContainer(); + /// @brief The process function from Godot. + /// @param delta The time since the last frame. + void _process(float delta); + /// @brief Override of the ready function from Godot. + void _ready() override; + /// @brief Activates an ability. + /// @param p_ability The ability to activate. + void activate(Ability *p_ability); + /// @brief Activates an ability by name. + /// @param p_ability_name The name of the ability to activate. + void activate_by_name(const StringName &p_ability_name); + /// @brief Blocks an ability. + /// @param p_ability The ability to block. + void block(Ability *p_ability); + /// @brief Blocks an ability by name. + /// @param p_ability_name The name of the ability to block. + void block_by_name(const StringName &p_ability_name); + /// @brief Adds an ability. + /// @param p_ability The ability to add. + void add_ability(Ability *p_ability); + /// @brief Returns if the container can activate an ability. + /// @param p_ability The ability to check for. + /// @return True if the container can activate the ability, false otherwise. + bool can_activate(Ability *p_ability) const; + /// @brief Returns if the container can activate an ability by name. + /// @param p_ability_name The name of the ability to check for. + /// @return True if the container can activate the ability, false otherwise. + bool can_activate_by_name(const StringName &p_ability_name) const; + /// @brief Returns if the container can block an ability. + /// @param p_ability The ability to check for. + /// @return True if the container can block the ability, false otherwise. + bool can_block(Ability *p_ability) const; + /// @brief Returns if the container can block an ability by name. + /// @param p_ability_name The name of the ability to check for. + /// @return True if the container can block the ability, false otherwise. + bool can_block_by_name(const StringName &p_ability_name) const; + /// @brief Returns if the container can cancel an ability. + /// @param p_ability The ability to check for. + /// @return True if the container can cancel the ability, false otherwise. + bool can_cancel(Ability *p_ability) const; + /// @brief Returns if the container can cancel an ability by name. + /// @param p_ability_name The name of the ability to check for. + /// @return True if the container can cancel the ability, false otherwise. + bool can_cancel_by_name(const StringName &p_ability_name) const; + /// @brief Returns if the container can grant an ability. + /// @param p_ability The ability to check for. + /// @return True if the container can grant the ability, false otherwise. + bool can_grant(Ability *p_ability) const; + /// @brief Returns if the container can revoke a granted ability. + /// @param p_ability The ability to check for. + /// @return True if the container can revoke the ability, false otherwise. + bool can_revoke(Ability *p_ability) const; + /// @brief Cancels an ability. + /// @param p_ability The ability to cancel. + void cancel(Ability *p_ability); + /// @brief Cancels an ability by name. + /// @param p_ability_name The name of the ability to cancel. + void cancel_by_name(const StringName &p_ability_name); + /// @brief Checks if the container has an ability. + /// @param p_ability The ability to check for. + /// @return True if the container has the ability, false otherwise. + bool has_ability(Ability *p_ability) const; + /// @brief Returns if the container has an ability owner. + /// @return True if the container has an ability owner, false otherwise. + bool has_ability_owner() const; + /// @brief Gets an ability by name. + /// @param p_ability_name the name of the ability. + /// @return The ability with the given name. + Ref get_ability(const StringName &p_ability_name) const; + /// @brief Gets the abilities. + /// @return The abilities. + TypedArray get_abilities() const; + /// @brief Returns the owner of the abilities. + /// @return The owner of the abilities. + Node *get_ability_owner() const; + /// @brief Grants an ability. + /// @param p_ability + void grant_ability(Ability *p_ability); + /// @brief Grants the abilities owned by the container. + void grant_abilities(); + /// @brief Revokes an ability. + /// @param p_ability The ability to revoke. + void revoke(Ability *p_ability); + /// @brief Revokes an ability by name. + /// @param p_ability_name The name of the ability to revoke. + void revoke_by_name(const StringName &p_ability_name); + /// @brief Revokes all abilities in the container if possible. + void revoke_all(); + /// @brief Set the abilities. + /// @param p_abilities The abilities. + void set_abilities(TypedArray p_abilities); + /// @brief Sets the owner of the abilities. + /// @param p_ability_owner The owner of the abilities. + void set_ability_owner(Node *p_ability_owner); + }; +} + +#endif \ No newline at end of file diff --git a/src/system/ability/ability_grant.cpp b/src/system/ability/ability_grant.cpp new file mode 100644 index 0000000..ad14228 --- /dev/null +++ b/src/system/ability/ability_grant.cpp @@ -0,0 +1,44 @@ +#include "ability_grant.h" + +using namespace ggs; + +void AbilityGrant::_bind_methods() +{ + /// binds methods + ClassDB::bind_method(D_METHOD("get_grant"), &AbilityGrant::get_grant); + ClassDB::bind_method(D_METHOD("get_ability"), &AbilityGrant::get_ability); + ClassDB::bind_method(D_METHOD("set_grant", "grant"), &AbilityGrant::set_grant); + ClassDB::bind_method(D_METHOD("set_ability", "ability"), &AbilityGrant::set_ability); + + /// binds properties + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "grant"), "set_grant", "get_grant"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"), "set_ability", "get_ability"); +} + +AbilityGrant::AbilityGrant() +{ +} + +AbilityGrant::~AbilityGrant() +{ +} + +Ref AbilityGrant::get_ability() const +{ + return ability; +} + +bool AbilityGrant::get_grant() const +{ + return granted; +} + +void AbilityGrant::set_grant(bool p_grant) +{ + granted = p_grant; +} + +void AbilityGrant::set_ability(Ref p_ability) +{ + ability = p_ability; +} diff --git a/src/system/ability/ability_grant.h b/src/system/ability/ability_grant.h new file mode 100644 index 0000000..2befd9d --- /dev/null +++ b/src/system/ability/ability_grant.h @@ -0,0 +1,45 @@ +#ifndef GGS_ABILITY_GRANT_H +#define GGS_ABILITY_GRANT_H + +#include + +#include "ability.h" + +using namespace godot; + +namespace ggs +{ + class AbilityGrant : public RefCounted + { + GDCLASS(AbilityGrant, RefCounted); + + protected: + static void _bind_methods(); + /// @brief The ability that was granted. True if the ability was granted, false otherwise. + bool granted; + /// @brief The ability that is contained. + Ref ability; + + public: + operator Variant() const { return Variant((Object *)this); } + + /// @brief Default constructor. + AbilityGrant(); + /// @brief Default destructor. + ~AbilityGrant(); + /// @brief Gets the ability. + /// @return The ability. + Ref get_ability() const; + /// @brief Gets the ability grant. + /// @return True if the ability was granted, false otherwise. + bool get_grant() const; + /// @brief Sets the ability grant. + /// @param p_grant True if the ability was granted, false otherwise. + void set_grant(bool p_grant); + /// @brief Gets the ability. + /// @param p_ability The ability. + void set_ability(Ref p_ability); + }; +} + +#endif diff --git a/src/system/ability/ability_queue.cpp b/src/system/ability/ability_queue.cpp new file mode 100644 index 0000000..6a507c3 --- /dev/null +++ b/src/system/ability/ability_queue.cpp @@ -0,0 +1,142 @@ +#include "ability_queue.h" + +using namespace ggs; + +void AbilityQueueItem::_bind_methods() +{ + /// binds methods + ClassDB::bind_method(D_METHOD("can_be_dequeued"), &AbilityQueueItem::can_be_dequeued); + ClassDB::bind_method(D_METHOD("increment_lifetime", "tick"), &AbilityQueueItem::increment_lifetime); + ClassDB::bind_method(D_METHOD("get_ability"), &AbilityQueueItem::get_ability); + ClassDB::bind_method(D_METHOD("set_ability", "ability"), &AbilityQueueItem::set_ability); +} + +AbilityQueueItem::AbilityQueueItem() +{ +} + +AbilityQueueItem::~AbilityQueueItem() +{ +} + +bool AbilityQueueItem::can_be_dequeued() const +{ + return lifetime >= ability->get_cooldown(); +} + +void AbilityQueueItem::increment_lifetime(float tick) +{ + lifetime += tick; +} + +Ability *AbilityQueueItem::get_ability() const +{ + return ability; +} + +void AbilityQueueItem::set_ability(Ability *p_ability) +{ + ability = p_ability; + lifetime = p_ability->get_cooldown(); +} + +void AbilityQueue::_bind_methods() +{ + /// binds methods + ClassDB::bind_method(D_METHOD("can_enqueue", "ability"), &AbilityQueue::can_enqueue); + ClassDB::bind_method(D_METHOD("can_dequeue", "ability"), &AbilityQueue::can_dequeue); + ClassDB::bind_method(D_METHOD("dequeue", "ability"), &AbilityQueue::dequeue); + ClassDB::bind_method(D_METHOD("enqueue", "ability"), &AbilityQueue::enqueue); + ClassDB::bind_method(D_METHOD("process", "delta"), &AbilityQueue::process); + + /// signals bindings + ADD_SIGNAL(MethodInfo("ability_enqueued", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); + ADD_SIGNAL(MethodInfo("ability_dequeued", PropertyInfo(Variant::OBJECT, "ability", PROPERTY_HINT_RESOURCE_TYPE, "Ability"))); +} + +AbilityQueue::AbilityQueue() +{ + queue = TypedArray(); +} + +AbilityQueue::~AbilityQueue() +{ +} + +bool AbilityQueue::can_enqueue(Ability *p_ability) +{ + return p_ability != nullptr && p_ability->get_cooldown() - 1.0f > 0.05f; +} + +bool AbilityQueue::can_dequeue(Ability *p_ability) +{ + for (int i = queue.size() - 1; i >= 0; i--) + { + AbilityQueueItem *item = cast_to(queue[i]); + + if (item != nullptr && item->get_ability()->get_ability_name() == p_ability->get_ability_name()) + { + return true; + } + } + + return false; +} + +void AbilityQueue::dequeue(Ability *p_ability) +{ + for (int i = queue.size() - 1; i >= 0; i--) + { + AbilityQueueItem *item = cast_to(queue[i]); + + if (item != nullptr && item->get_ability()->get_ability_name() == p_ability->get_ability_name()) + { + queue.remove_at(i); + emit_signal("ability_dequeued", p_ability); + } + } +} + +void AbilityQueue::enqueue(Ability *p_ability) +{ + if (can_enqueue(p_ability)) + { + AbilityQueueItem *item = memnew(AbilityQueueItem); + item->set_ability(p_ability); + queue.push_back(item); + emit_signal("ability_enqueued", p_ability); + } +} + +bool AbilityQueue::has_enqueued_ability(Ability *p_ability) +{ + for (int i = queue.size() - 1; i >= 0; i--) + { + AbilityQueueItem *item = cast_to(queue[i]); + + if (item != nullptr && item->get_ability()->get_ability_name() == p_ability->get_ability_name()) + { + return true; + } + } + + return false; +} + +void AbilityQueue::process(float delta) +{ + for (int i = queue.size() - 1; i >= 0; i--) + { + AbilityQueueItem *item = cast_to(queue[i]); + + if (item != nullptr) + { + item->increment_lifetime(delta); + + if (item->can_be_dequeued()) + { + queue.remove_at(i); + } + } + } +} \ No newline at end of file diff --git a/src/system/ability/ability_queue.h b/src/system/ability/ability_queue.h new file mode 100644 index 0000000..cfc2410 --- /dev/null +++ b/src/system/ability/ability_queue.h @@ -0,0 +1,86 @@ +#ifndef GGS_ABILITY_QUEUE_H +#define GGS_ABILITY_QUEUE_H + +#include + +#include "ability.h" + +using namespace godot; + +namespace ggs +{ + /// @brief An item in the ability queue. + class AbilityQueueItem : public RefCounted + { + GDCLASS(AbilityQueueItem, RefCounted); + + protected: + /// @brief Binds methods to Godot. + static void _bind_methods(); + /// @brief How many times the ability has been called. + float lifetime; + /// @brief The ability. + Ability *ability; + + public: + /// @brief Default constructor. + AbilityQueueItem(); + /// @brief Default destructor. + ~AbilityQueueItem(); + + /// @brief Returns if the ability can be dequeued (it reached it's lifetime). + /// @return True if the ability can be dequeued, false otherwise. + bool can_be_dequeued() const; + /// @brief Increments the lifetime of the ability. + /// @param tick The amount to increment the lifetime by. + void increment_lifetime(float tick); + /// @brief Returns the ability. + /// @return The ability. + Ability *get_ability() const; + /// @brief Sets the ability. + /// @param p_ability The ability. + void set_ability(Ability *p_ability); + }; + + /// @brief The ability queue. + class AbilityQueue : public RefCounted + { + GDCLASS(AbilityQueue, RefCounted); + + protected: + /// @brief Binds methods to Godot. + static void _bind_methods(); + /// @brief The queue of abilities. + TypedArray queue; + + public: + /// @brief Default constructor. + AbilityQueue(); + /// @brief Default destructor. + ~AbilityQueue(); + + /// @brief Returns if the ability can be enqueued. + /// @param p_ability The ability to check. + /// @return True if the ability can be enqueued, false otherwise. + bool can_enqueue(Ability *p_ability); + /// @brief Returns if the ability can be dequeued. + /// @param p_ability The ability to check. + /// @return True if the ability can be dequeued, false otherwise. + bool can_dequeue(Ability *p_ability); + /// @brief Dequeues an ability. + /// @param p_ability The ability to dequeue. + void dequeue(Ability *p_ability); + /// @brief Enqueues an ability. + /// @param p_ability The ability to enqueue. + void enqueue(Ability *p_ability); + /// @brief Returns if the ability is enqueued. + /// @param p_ability The ability to check. + /// @return True if the ability is enqueued, false otherwise. + bool has_enqueued_ability(Ability *p_ability); + /// @brief The process function. + /// @param delta The time since the last frame. + void process(float delta); + }; +} + +#endif