From 29fd8d6f2a7a6a9f931f6b8b4f587bab10495483 Mon Sep 17 00:00:00 2001 From: octod Date: Fri, 12 Jan 2024 10:32:46 +0100 Subject: [PATCH] feat: adds BreakType::BREAK_RESET_COUNTER --- project/demos/attributes_test_ui/main.gd | 55 +++- src/system/attribute/attribute_container.cpp | 264 ++++++++++-------- src/system/attribute/attribute_container.h | 29 +- src/system/attribute/attribute_effect.cpp | 8 +- src/system/attribute/attribute_effect.h | 2 +- .../attribute/attribute_effect_condition.cpp | 1 + .../attribute/attribute_effect_condition.h | 1 + 7 files changed, 206 insertions(+), 154 deletions(-) diff --git a/project/demos/attributes_test_ui/main.gd b/project/demos/attributes_test_ui/main.gd index 1b2a3fa..cf1267a 100644 --- a/project/demos/attributes_test_ui/main.gd +++ b/project/demos/attributes_test_ui/main.gd @@ -8,8 +8,8 @@ class BuffDex extends AttributeEffect: affected_amount = 10 application_type = AttributeEffect.ADD_BUFF affected_attribute = "attributes.dexterity" - - + + class BuffStr extends AttributeEffect: func _init() -> void: affected_amount = 5 @@ -47,23 +47,45 @@ class StaminaIncrease extends AttributeEffect: class DebuffCondition extends AttributeEffectCondition: func get_break_type(attribute_effect: AttributeEffect, attribute_container: AttributeContainer) -> AttributeEffectCondition.BreakType: - var attribute = attribute_container.get_attribute(attribute_effect.affected_attribute) - - if attribute != null and attribute.buff == 0: - return AttributeEffectCondition.BREAK - - return AttributeEffectCondition.NO_BREAK + # if the effect lasted for more than 2 seconds, we will start to debuff it + if attribute_container.count_timeouts(attribute_effect) > 2: + if (attribute_container.get_attribute(attribute_effect.affected_attribute).buff > 0): + return AttributeEffectCondition.NO_BREAK + + return AttributeEffectCondition.BREAK_RESET_COUNTER class DebuffDex extends AttributeEffect: func _init() -> void: - affected_amount = 1 + affected_amount = 5 application_type = AttributeEffect.SUBTRACT_BUFF affected_attribute = "attributes.dexterity" life_cycle = AttributeEffect.INFINITE_TIME_BASED conditions.append(DebuffCondition.new()) +class DebuffStr extends AttributeEffect: + func _init() -> void: + application_type = AttributeEffect.SUBTRACT_BUFF + affected_attribute = "attributes.strength" + life_cycle = AttributeEffect.INFINITE_TIME_BASED + conditions.append(DebuffCondition.new()) + + func calculate_affected_amount(container: AttributeContainer) -> float: + var attribute = container.get_attribute(affected_attribute) + + if attribute != null: + ## loses 5% each second + var value_to_debuff = (attribute.value / 100) * 5 + + if value_to_debuff > 0: + return value_to_debuff + else: + return 1.0 + + return affected_amount + + func make_gameplay_effect(attribute_effect: AttributeEffect) -> GameplayEffect: var x = GameplayEffect.new() @@ -73,16 +95,21 @@ func make_gameplay_effect(attribute_effect: AttributeEffect) -> GameplayEffect: return x +func _handle_attribute_changed(attribute: Attribute) -> void: + print(attribute.tag_name + " has changed") + print_label() + + func _ready() -> void: print_label() add_child((make_gameplay_effect(DebuffDex.new()))) + add_child((make_gameplay_effect(DebuffStr.new()))) + attribute_container.attribute_changed.connect(_handle_attribute_changed) func _input(event: InputEvent) -> void: if event is InputEventKey and event.is_pressed(): var k = event.as_text_keycode() - var m = false - var x = Node.new() if k == "1": add_child(make_gameplay_effect(SpeedIncrease.new())) @@ -105,9 +132,9 @@ func print_attribute(attribute: Attribute) -> String: return "tag_name: {0}; value: {1}; maximum_value: {2}; buff: {3};".format({ 0: attribute.tag_name, - 1: attribute.value, - 2: attribute.max_value, - 3: attribute.buff + 1: int(attribute.value), + 2: int(attribute.max_value), + 3: int(attribute.buff) }) diff --git a/src/system/attribute/attribute_container.cpp b/src/system/attribute/attribute_container.cpp index 05c6812..aeb9cb9 100644 --- a/src/system/attribute/attribute_container.cpp +++ b/src/system/attribute/attribute_container.cpp @@ -23,26 +23,12 @@ void AttributeContainer::_handle_attribute_owner_received_node(Node *p_node) } } -void AttributeContainer::_handle_timer_timeout(Timer *timer, Ref &attribute_effect, Ref &attribute) -{ - int count = increase_attribute_effect_timer_count(timer->get_instance_id()); - - if ((attribute_effect->get_life_cycle() == AttributeEffect::LifeCycle::INFINITE_TIME_BASED && should_break_attribute_effect(attribute_effect)) || (should_break_attribute_effect(attribute_effect) || count >= attribute_effect->get_applications_count())) - { - timeouts_count.erase(timer->get_instance_id()); - timer->stop(); - timer->call_deferred("queue_free"); - return; - } - - apply_attribute_effect(attribute_effect, attribute); -} - void AttributeContainer::_bind_methods() { /// methods binding ClassDB::bind_method(D_METHOD("_handle_attribute_owner_received_node", "node"), &AttributeContainer::_handle_attribute_owner_received_node); ClassDB::bind_method(D_METHOD("apply_effect", "effect"), &AttributeContainer::apply_effect); + ClassDB::bind_method(D_METHOD("count_timeouts", "attribute_effect"), &AttributeContainer::count_timeouts); ClassDB::bind_method(D_METHOD("ensure_attributes", "attributes"), &AttributeContainer::ensure_attributes); ClassDB::bind_method(D_METHOD("get_attribute", "attribute_name"), &AttributeContainer::get_attribute); ClassDB::bind_method(D_METHOD("get_attributes_owner"), &AttributeContainer::get_attributes_owner); @@ -71,62 +57,65 @@ void AttributeContainer::_bind_methods() ADD_SIGNAL(MethodInfo("attribute_value_subtracted", PropertyInfo(Variant::OBJECT, "attribute", PROPERTY_HINT_RESOURCE_TYPE, "Attribute"), PropertyInfo(Variant::FLOAT, "value_amount"))); } -void AttributeContainer::apply_attribute_effect(Ref &attribute_effect, Ref &attribute) +void AttributeContainer::apply_attribute_effect(AttributeEffect *attribute_effect, Ref &attribute) { - if (!are_attribute_conditions_met(attribute_effect)) - { - emit_signal("attribute_effect_conditions_not_met", Ref(attribute_effect)); - return; - } + float affected_amount = attribute_effect->_calculate_affected_amount(this); switch (attribute_effect->get_application_type()) { case AttributeEffect::ApplicationType::ADD_BUFF: { - attribute->add_buff(attribute_effect->get_affected_amount()); - emit_signal("attribute_buff_added", Ref(attribute), attribute_effect->get_affected_amount()); + attribute->add_buff(affected_amount); + emit_signal("attribute_buff_added", Ref(attribute), affected_amount); + emit_signal("attribute_changed", Ref(attribute)); break; } case AttributeEffect::ApplicationType::ADD_VALUE: { - attribute->add_value(attribute_effect->get_affected_amount()); - emit_signal("attribute_value_added", Ref(attribute), attribute_effect->get_affected_amount()); + attribute->add_value(affected_amount); + emit_signal("attribute_value_added", Ref(attribute), affected_amount); + emit_signal("attribute_changed", Ref(attribute)); break; } case AttributeEffect::ApplicationType::ADD_VALUE_OR_BUFF: { - attribute->add_value_or_buff(attribute_effect->get_affected_amount()); - emit_signal("attribute_value_added", Ref(attribute), attribute_effect->get_affected_amount()); + attribute->add_value_or_buff(affected_amount); + emit_signal("attribute_value_added", Ref(attribute), affected_amount); + emit_signal("attribute_changed", Ref(attribute)); break; } case AttributeEffect::ApplicationType::SUBTRACT_BUFF: { - attribute->subtract_buff(attribute_effect->get_affected_amount()); - emit_signal("attribute_buff_subtracted", Ref(attribute), attribute_effect->get_affected_amount()); + attribute->subtract_buff(affected_amount); + emit_signal("attribute_buff_subtracted", Ref(attribute), affected_amount); + emit_signal("attribute_changed", Ref(attribute)); break; } case AttributeEffect::ApplicationType::SUBTRACT_VALUE: { - attribute->subtract_value(attribute_effect->get_affected_amount()); - emit_signal("attribute_value_subtracted", Ref(attribute), attribute_effect->get_affected_amount()); + attribute->subtract_value(affected_amount); + emit_signal("attribute_value_subtracted", Ref(attribute), affected_amount); + emit_signal("attribute_changed", Ref(attribute)); break; } case AttributeEffect::ApplicationType::SET_BUFF: { - attribute->set_buff(attribute_effect->get_affected_amount()); - emit_signal("attribute_buff_set", Ref(attribute), attribute_effect->get_affected_amount()); + attribute->set_buff(affected_amount); + emit_signal("attribute_buff_set", Ref(attribute), affected_amount); + emit_signal("attribute_changed", Ref(attribute)); break; } case AttributeEffect::ApplicationType::SET_VALUE: { - attribute->set_value(attribute_effect->get_affected_amount()); - emit_signal("attribute_value_set", Ref(attribute), attribute_effect->get_affected_amount()); + attribute->set_value(affected_amount); + emit_signal("attribute_value_set", Ref(attribute), affected_amount); + emit_signal("attribute_changed", Ref(attribute)); break; } } } -bool AttributeContainer::are_attribute_conditions_met(Ref &attribute_effect) +bool AttributeContainer::are_attribute_conditions_met(AttributeEffect *attribute_effect) { if (attribute_effect->get_conditions().size() > 0) { @@ -135,7 +124,7 @@ bool AttributeContainer::are_attribute_conditions_met(Ref &attr Variant condition = attribute_effect->get_conditions()[i]; Ref attribute_effect_condition = Ref(condition); - if (!attribute_effect_condition->_should_apply_effect(attribute_effect.ptr(), this)) + if (!attribute_effect_condition->_should_apply_effect(attribute_effect, this)) { return false; } @@ -145,8 +134,10 @@ bool AttributeContainer::are_attribute_conditions_met(Ref &attr return true; } -int AttributeContainer::increase_attribute_effect_timer_count(int p_id) +int AttributeContainer::increase_attribute_effect_timer_count(AttributeEffect *p_attribute_effect) { + int p_id = p_attribute_effect->get_instance_id(); + if (timeouts_count.has(p_id)) { int count = timeouts_count[p_id]; @@ -161,76 +152,6 @@ int AttributeContainer::increase_attribute_effect_timer_count(int p_id) return timeouts_count[p_id]; } -Timer *AttributeContainer::setup_attribute_effect_timer(Ref &attribute_effect, Ref &attribute) -{ - Timer *timer = memnew(Timer); - - timer->set_autostart(false); - timer->set_one_shot(false); - timer->set_wait_time(1.0f); - - add_child(timer); - - Callable callable = Callable(this, "_handle_timer_timeout"); - Array arguments = Array(); - - arguments.append(timer); - arguments.append(attribute_effect); - arguments.append(attribute); - - callable.bindv(arguments); - - timer->connect("timeout", callable); - - return timer; -} - -bool AttributeContainer::should_break_attribute_effect(Ref &attribute_effect) -{ - switch (attribute_effect->get_break(this)) - { - case AttributeEffectCondition::BreakType::BREAK: - { - emit_signal("attribute_effect_blocked", Ref(attribute_effect)); - return true; - } - case AttributeEffectCondition::BreakType::BREAK_REMOVE_ATTRIBUTE_EFFECT: - { - ongoing_effects.erase(attribute_effect); - emit_signal("attribute_effect_removed", Ref(attribute_effect)); - return true; - } - case AttributeEffectCondition::BreakType::BREAK_REMOVE_ATTRIBUTE_EFFECT_OF_THIS_TYPE: - { - for (int x = ongoing_effects.size() - 1; x >= 0; x--) - { - Variant ongoing_effect_variant = ongoing_effects.operator[](x); - AttributeEffect *ongoing_effect = cast_to(ongoing_effect_variant); - - if (ongoing_effect != nullptr && ongoing_effect->get_class() == attribute_effect->get_class()) - { - ongoing_effects.erase(ongoing_effect); - emit_signal("attribute_effect_removed", Ref(ongoing_effect)); - } - } - - return true; - } - case AttributeEffectCondition::BreakType::BREAK_REMOVE_ANY_ATTRIBUTE_EFFECT: - { - ongoing_effects.clear(); - emit_signal("all_effects_removed"); - return true; - } - case AttributeEffectCondition::BreakType::NO_BREAK: - { - return false; - } - } - - return false; -} - AttributeContainer::AttributeContainer() { } @@ -256,6 +177,96 @@ AttributeContainer::~AttributeContainer() { } +void AttributeContainer::_process(float delta) +{ + if (Engine::get_singleton()->is_editor_hint()) + { + return; + } + + _process_delta += delta; + + /// get if a second has passed since the last process + /// note: this will have to be balanced once multiplayer api will kick in. The check should be made taking in consideration the average ping of the players. + if (_process_delta - 1.0f >= 0.05f) + { + _process_delta = 0.0f; + + for (int i = ongoing_effects.size() - 1; i >= 0; i--) + { + Variant effect_variant = ongoing_effects[i]; + AttributeEffect *attribute_effect = cast_to(effect_variant); + + if (attribute_effect == nullptr) + { + WARN_PRINT("ERR/GGS/S/A/AC::_p_000"); + continue; + } + + Ref attribute_effect_ref = Ref(attribute_effect); + Ref attribute_ref = get_attribute(attribute_effect->get_affected_attribute()); + + int attribute_effect_application_count = increase_attribute_effect_timer_count(attribute_effect); + + if (attribute_effect->get_life_cycle() == AttributeEffect::LifeCycle::TIME_BASED && attribute_effect_application_count >= attribute_effect->get_applications_count()) + { + ongoing_effects.remove_at(i); + emit_signal("attribute_effect_removed", attribute_effect); + continue; + } + + switch (attribute_effect->get_break(this)) + { + case AttributeEffectCondition::BreakType::NO_BREAK: + { + if (are_attribute_conditions_met(attribute_effect)) + { + apply_attribute_effect(attribute_effect, attribute_ref); + } + else + { + emit_signal("attribute_effect_conditions_not_met", attribute_effect); + } + break; + } + case AttributeEffectCondition::BreakType::BREAK: + { + emit_signal("attribute_effect_blocked", attribute_effect); + break; + } + case AttributeEffectCondition::BreakType::BREAK_RESET_COUNTER: + { + emit_signal("attribute_effect_blocked", attribute_effect); + timeouts_count[attribute_effect->get_instance_id()] = 0; + break; + } + case AttributeEffectCondition::BreakType::BREAK_REMOVE_ANY_ATTRIBUTE_EFFECT: + { + for (int j = ongoing_effects.size() - 1; j >= 0; j--) + { + Variant ongoing_effect_variant = ongoing_effects[j]; + AttributeEffect *ongoing_effect = cast_to(ongoing_effect_variant); + + if (ongoing_effect != nullptr && ongoing_effect->get_rid() == attribute_effect->get_rid()) + { + ongoing_effects.erase(ongoing_effect); + } + } + emit_signal("attribute_effect_removed", attribute_effect); + break; + } + case AttributeEffectCondition::BreakType::BREAK_REMOVE_ATTRIBUTE_EFFECT: + { + emit_signal("attribute_effect_blocked", attribute_effect); + ongoing_effects.remove_at(i); + emit_signal("attribute_effect_removed", attribute_effect); + break; + } + } + } + } +} + void AttributeContainer::_ready() { if (attributes_owner == nullptr) @@ -275,35 +286,50 @@ void AttributeContainer::apply_effect(GameplayEffect *p_game_effect) Variant effect_variant = effects[i]; AttributeEffect *attribute_effect = cast_to(effect_variant); - if (attribute_effect != nullptr) + if (attribute_effect == nullptr) { - ongoing_effects.push_back(attribute_effect); + WARN_PRINT("ERR/GGS/S/A/AC::AE_000"); + continue; } if (!has_attribute(attribute_effect->get_affected_attribute())) { + WARN_PRINT("ERR/GGS/S/A/AC::AE_001"); continue; } Ref attribute_ref = get_attribute(attribute_effect->get_affected_attribute()); - Ref attribute_effect_ref = Ref(attribute_effect); - - if (should_break_attribute_effect(attribute_effect_ref)) - { - continue; - } - if (attribute_effect_ref->get_life_cycle() == AttributeEffect::LifeCycle::ONE_TIME) + if (attribute_effect->get_life_cycle() == AttributeEffect::LifeCycle::ONE_TIME) { - apply_attribute_effect(attribute_effect_ref, attribute_ref); + if (are_attribute_conditions_met(attribute_effect)) + { + apply_attribute_effect(attribute_effect, attribute_ref); + } + else + { + emit_signal("attribute_effect_conditions_not_met", attribute_effect); + } } else { - setup_attribute_effect_timer(attribute_effect_ref, attribute_ref)->start(); + ongoing_effects.push_back(attribute_effect); } } } +int AttributeContainer::count_timeouts(AttributeEffect *p_effect) +{ + int p_id = p_effect->get_instance_id(); + + if (timeouts_count.has(p_id)) + { + return timeouts_count[p_id]; + } + + return 0; +} + void AttributeContainer::ensure_attributes(PackedStringArray p_attributes) { if (Engine::get_singleton()->is_editor_hint()) diff --git a/src/system/attribute/attribute_container.h b/src/system/attribute/attribute_container.h index 78ac20c..80bbd8a 100644 --- a/src/system/attribute/attribute_container.h +++ b/src/system/attribute/attribute_container.h @@ -17,12 +17,11 @@ namespace ggs GDCLASS(AttributeContainer, Node); private: + float _process_delta; + /// @brief Handles the given gampeplay effect. /// @param p_node The node that received the gampeplay effect. void _handle_attribute_owner_received_node(Node *p_node); - /// @brief Called when the timer used to count the number of times the given effect has been applied times out. - /// @param p_id The timer id. - void _handle_timer_timeout(Timer *timer, Ref &attribute_effect, Ref &attribute); protected: friend class GGSAttributeContainerInspectorEditor; @@ -39,23 +38,14 @@ namespace ggs /// @brief Applies the given attribute effect. /// @param attribute_effect The attribute effect to apply. /// @param attribute The attribute to apply the effect to. - void apply_attribute_effect(Ref &attribute_effect, Ref &attribute); + void apply_attribute_effect(AttributeEffect *attribute_effect, Ref &attribute); /// @brief Checks if the attribute effect conditions are met. /// @param attribute_effect The attribute effect to check. /// @return True if the attribute effect conditions are met, false otherwise. - bool are_attribute_conditions_met(Ref &attribute_effect); + bool are_attribute_conditions_met(AttributeEffect *attribute_effect); /// @brief Counts the number of times the given effect has been applied. - /// @param p_id - int increase_attribute_effect_timer_count(int p_id); - /// @brief Setups the timer used to count the number of times the given effect has been applied. - /// @return The timer used to count the number of times the given effect has been applied. - Timer *setup_attribute_effect_timer(Ref &attribute_effect, Ref &attribute); - /// @brief Returns true if the attribute effect should be broken, false otherwise. - /// @param attribute_effect The attribute effect to check. - /// @param p_game_effect The gameplay effect that contains the attribute effect. - /// @param break_effect The boolean that will be set to true if the effect should be broken. - /// @return - bool should_break_attribute_effect(Ref &attribute_effect); + /// @param p_id The id of the attribute effect. + int increase_attribute_effect_timer_count(AttributeEffect *p_attribute_effect); public: /// @brief Default constructor. @@ -72,12 +62,19 @@ namespace ggs /// @brief Destructor. ~AttributeContainer(); + /// @brief Handles the process of the node. + /// @param delta + void _process(float delta); /// @brief Called when the node enters the scene tree for the first time. void _ready() override; /// @brief Applies the given effect. /// @param p_effect The effect to activate. void apply_effect(GameplayEffect *p_effect); + /// @brief Returns how many times the given effect has been applied. + /// @param p_effect The effect to count. + /// @return The number of times the given effect has been applied. + int count_timeouts(AttributeEffect *p_effect); /// @brief Ensures that the container has the attributes defined in the given array. /// @param p_attributes The attributes to ensure. void ensure_attributes(PackedStringArray p_attributes); diff --git a/src/system/attribute/attribute_effect.cpp b/src/system/attribute/attribute_effect.cpp index c976c7e..aede334 100644 --- a/src/system/attribute/attribute_effect.cpp +++ b/src/system/attribute/attribute_effect.cpp @@ -7,7 +7,7 @@ void AttributeEffect::_bind_methods() /// binds methods /// TODO: remember to change it once godot-cpp will allow virtual methods for gdscript ClassDB::bind_method(D_METHOD("_calculate_affected_amount"), &AttributeEffect::_calculate_affected_amount); - /// + /// ClassDB::bind_method(D_METHOD("are_conditions_met", "attribute_container"), &AttributeEffect::are_conditions_met); ClassDB::bind_method(D_METHOD("get_affected_amount"), &AttributeEffect::get_affected_amount); ClassDB::bind_method(D_METHOD("get_affected_attribute"), &AttributeEffect::get_affected_attribute); @@ -91,11 +91,11 @@ bool AttributeEffect::are_conditions_met(AttributeContainer *p_attribute_contain return true; } -float AttributeEffect::_calculate_affected_amount() +float AttributeEffect::_calculate_affected_amount(AttributeContainer *attribute_container) { if (has_method("calculate_affected_amount")) { - return call("calculate_affected_amount"); + return call("calculate_affected_amount", attribute_container); } return affected_amount; @@ -133,7 +133,7 @@ PackedFloat32Array AttributeEffect::get_applications() const float AttributeEffect::get_affected_amount() { - return _calculate_affected_amount(); + return affected_amount; } StringName AttributeEffect::get_affected_attribute() const diff --git a/src/system/attribute/attribute_effect.h b/src/system/attribute/attribute_effect.h index a225926..01a2f1c 100644 --- a/src/system/attribute/attribute_effect.h +++ b/src/system/attribute/attribute_effect.h @@ -70,7 +70,7 @@ namespace ggs /// @return Returns true if the attribute effect can be applied, false otherwise. bool are_conditions_met(AttributeContainer *p_attribute_container); /// @brief Calculates the affected amount of the attribute effect. - virtual float _calculate_affected_amount(); + virtual float _calculate_affected_amount(AttributeContainer *attribute_container); /// @brief Returns the applications of this attribute effect. Each item in the array is how the attribute effect will be applied. /// @return PackedFloat32Array get_applications() const; diff --git a/src/system/attribute/attribute_effect_condition.cpp b/src/system/attribute/attribute_effect_condition.cpp index dd08877..8af58a6 100644 --- a/src/system/attribute/attribute_effect_condition.cpp +++ b/src/system/attribute/attribute_effect_condition.cpp @@ -10,6 +10,7 @@ void AttributeEffectCondition::_bind_methods() BIND_ENUM_CONSTANT(NO_BREAK); BIND_ENUM_CONSTANT(BREAK); + BIND_ENUM_CONSTANT(BREAK_RESET_COUNTER); BIND_ENUM_CONSTANT(BREAK_REMOVE_ATTRIBUTE_EFFECT); BIND_ENUM_CONSTANT(BREAK_REMOVE_ATTRIBUTE_EFFECT_OF_THIS_TYPE); BIND_ENUM_CONSTANT(BREAK_REMOVE_ANY_ATTRIBUTE_EFFECT); diff --git a/src/system/attribute/attribute_effect_condition.h b/src/system/attribute/attribute_effect_condition.h index 795a5e6..70a0d53 100644 --- a/src/system/attribute/attribute_effect_condition.h +++ b/src/system/attribute/attribute_effect_condition.h @@ -28,6 +28,7 @@ namespace ggs { NO_BREAK, BREAK, + BREAK_RESET_COUNTER, BREAK_REMOVE_ATTRIBUTE_EFFECT, BREAK_REMOVE_ATTRIBUTE_EFFECT_OF_THIS_TYPE, BREAK_REMOVE_ANY_ATTRIBUTE_EFFECT,