From 605444ee0f30943a67e5cda0ebd552a99fec1fe3 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 10 Aug 2023 13:04:53 +0200 Subject: [PATCH] Add BTCheckAgentProperty condition --- bt/conditions/bt_check_agent_property.cpp | 84 +++++++++++++++++++++++ bt/conditions/bt_check_agent_property.h | 50 ++++++++++++++ config.py | 1 + register_types.cpp | 4 +- util/limbo_utility.cpp | 59 ++++++++++++++++ util/limbo_utility.h | 16 +++++ 6 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 bt/conditions/bt_check_agent_property.cpp create mode 100644 bt/conditions/bt_check_agent_property.h diff --git a/bt/conditions/bt_check_agent_property.cpp b/bt/conditions/bt_check_agent_property.cpp new file mode 100644 index 0000000..fc4a07a --- /dev/null +++ b/bt/conditions/bt_check_agent_property.cpp @@ -0,0 +1,84 @@ +/** + * bt_check_agent_property.cpp + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#include "bt_check_agent_property.h" + +#include "modules/limboai/util/limbo_utility.h" + +#include "core/variant/callable.h" + +void BTCheckAgentProperty::set_property_name(StringName p_prop) { + property_name = p_prop; + emit_changed(); +} + +void BTCheckAgentProperty::set_check_type(LimboUtility::CheckType p_check_type) { + check_type = p_check_type; + emit_changed(); +} + +void BTCheckAgentProperty::set_value(Ref p_value) { + value = p_value; + emit_changed(); + if (Engine::get_singleton()->is_editor_hint() && value.is_valid()) { + value->connect(SNAME("changed"), Callable(this, SNAME("emit_changed"))); + } +} + +String BTCheckAgentProperty::get_configuration_warning() const { + String warning = BTCondition::get_configuration_warning(); + if (!warning.is_empty()) { + warning += "\n"; + } + if (property_name == StringName()) { + warning += "`property_name` should be assigned.\n"; + } + if (!value.is_valid()) { + warning += "`value` should be assigned.\n"; + } + return warning; +} + +String BTCheckAgentProperty::_generate_name() const { + if (property_name == StringName()) { + return "CheckAgentProperty ???"; + } + + return vformat("Check if: agent.%s %s %s", property_name, + LimboUtility::get_singleton()->get_check_operator_string(check_type), + value.is_valid() ? Variant(value) : Variant("???")); +} + +int BTCheckAgentProperty::_tick(double p_delta) { + ERR_FAIL_COND_V_MSG(property_name == StringName(), FAILURE, "BTCheckAgentProperty: `property_name` is not set."); + ERR_FAIL_COND_V_MSG(!value.is_valid(), FAILURE, "BTCheckAgentProperty: `value` is not set."); + + bool r_valid; + Variant left_value = get_agent()->get(property_name, &r_valid); + ERR_FAIL_COND_V_MSG(r_valid == false, FAILURE, vformat("BTCheckAgentProperty: Agent has no property named \"%s\"", property_name)); + + Variant right_value = value->get_value(get_agent(), get_blackboard()); + + return LimboUtility::get_singleton()->perform_check(check_type, left_value, right_value) ? SUCCESS : FAILURE; +} + +void BTCheckAgentProperty::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_property_name", "p_property_name"), &BTCheckAgentProperty::set_property_name); + ClassDB::bind_method(D_METHOD("get_property_name"), &BTCheckAgentProperty::get_property_name); + ClassDB::bind_method(D_METHOD("set_check_type", "p_check_type"), &BTCheckAgentProperty::set_check_type); + ClassDB::bind_method(D_METHOD("get_check_type"), &BTCheckAgentProperty::get_check_type); + ClassDB::bind_method(D_METHOD("set_value", "p_value"), &BTCheckAgentProperty::set_value); + ClassDB::bind_method(D_METHOD("get_value"), &BTCheckAgentProperty::get_value); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "property_name"), "set_property_name", "get_property_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "check_type", PROPERTY_HINT_ENUM, "Equal,Less Than,Less Than Or Equal,Greater Than,Greater Than Or Equal,Not Equal"), "set_check_type", "get_check_type"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "value", PROPERTY_HINT_RESOURCE_TYPE, "BBVariant"), "set_value", "get_value"); +} diff --git a/bt/conditions/bt_check_agent_property.h b/bt/conditions/bt_check_agent_property.h new file mode 100644 index 0000000..10a9fc2 --- /dev/null +++ b/bt/conditions/bt_check_agent_property.h @@ -0,0 +1,50 @@ +/** + * bt_check_agent_property.h + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#ifndef BT_CHECK_AGENT_PROPERTY +#define BT_CHECK_AGENT_PROPERTY + +#include "bt_condition.h" + +#include "modules/limboai/blackboard/bb_param/bb_variant.h" +#include "modules/limboai/util/limbo_utility.h" + +#include "core/object/object.h" +#include "core/string/string_name.h" + +class BTCheckAgentProperty : public BTCondition { + GDCLASS(BTCheckAgentProperty, BTCondition); + +private: + StringName property_name; + LimboUtility::CheckType check_type = LimboUtility::CheckType::CHECK_EQUAL; + Ref value; + +protected: + static void _bind_methods(); + + virtual String _generate_name() const override; + virtual int _tick(double p_delta) override; + +public: + virtual String get_configuration_warning() const override; + + void set_property_name(StringName p_prop); + StringName get_property_name() const { return property_name; } + + void set_check_type(LimboUtility::CheckType p_check_type); + LimboUtility::CheckType get_check_type() const { return check_type; } + + void set_value(Ref p_value); + Ref get_value() const { return value; } +}; + +#endif // BT_CHECK_AGENT_PROPERTY \ No newline at end of file diff --git a/config.py b/config.py index 7a8a185..41cb6df 100644 --- a/config.py +++ b/config.py @@ -60,6 +60,7 @@ def get_doc_classes(): "BTAction", "BTAlwaysFail", "BTAlwaysSucceed", + "BTCheckAgentProperty", "BTCheckTrigger", "BTCheckVar", "BTComposite", diff --git a/register_types.cpp b/register_types.cpp index fd48a19..67400ba 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -63,6 +63,7 @@ #include "bt/composites/bt_random_sequence.h" #include "bt/composites/bt_selector.h" #include "bt/composites/bt_sequence.h" +#include "bt/conditions/bt_check_agent_property.h" #include "bt/conditions/bt_check_trigger.h" #include "bt/conditions/bt_check_var.h" #include "bt/conditions/bt_condition.h" @@ -102,6 +103,7 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) { LimboDebugger::initialize(); + GDREGISTER_CLASS(LimboUtility); GDREGISTER_CLASS(Blackboard); GDREGISTER_CLASS(LimboState); @@ -146,6 +148,7 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(BTWaitTicks); GDREGISTER_CLASS(BTCondition); + GDREGISTER_CLASS(BTCheckAgentProperty); GDREGISTER_CLASS(BTCheckTrigger); GDREGISTER_CLASS(BTCheckVar); @@ -184,7 +187,6 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(BBVariant); _limbo_utility = memnew(LimboUtility); - GDREGISTER_CLASS(LimboUtility); Engine::get_singleton()->add_singleton(Engine::Singleton("LimboUtility", LimboUtility::get_singleton())); LimboStringNames::create(); diff --git a/util/limbo_utility.cpp b/util/limbo_utility.cpp index 79358e7..2594b77 100644 --- a/util/limbo_utility.cpp +++ b/util/limbo_utility.cpp @@ -80,10 +80,69 @@ Ref LimboUtility::get_task_icon(String p_class_or_script_path) const return nullptr; } +String LimboUtility::get_check_operator_string(CheckType p_check_type) { + switch (p_check_type) { + case LimboUtility::CheckType::CHECK_EQUAL: { + return "=="; + } break; + case LimboUtility::CheckType::CHECK_LESS_THAN: { + return "<"; + } break; + case LimboUtility::CheckType::CHECK_LESS_THAN_OR_EQUAL: { + return "<="; + } break; + case LimboUtility::CheckType::CHECK_GREATER_THAN: { + return ">"; + } break; + case LimboUtility::CheckType::CHECK_GREATER_THAN_OR_EQUAL: { + return ">="; + } break; + case LimboUtility::CheckType::CHECK_NOT_EQUAL: { + return "!="; + } break; + default: { + return "?"; + } break; + } +} + +bool LimboUtility::perform_check(CheckType p_check_type, const Variant &left_value, const Variant &right_value) { + switch (p_check_type) { + case LimboUtility::CheckType::CHECK_EQUAL: { + return Variant::evaluate(Variant::OP_EQUAL, left_value, right_value); + } break; + case LimboUtility::CheckType::CHECK_LESS_THAN: { + return Variant::evaluate(Variant::OP_LESS, left_value, right_value); + } break; + case LimboUtility::CheckType::CHECK_LESS_THAN_OR_EQUAL: { + return Variant::evaluate(Variant::OP_LESS_EQUAL, left_value, right_value); + } break; + case LimboUtility::CheckType::CHECK_GREATER_THAN: { + return Variant::evaluate(Variant::OP_GREATER, left_value, right_value); + } break; + case LimboUtility::CheckType::CHECK_GREATER_THAN_OR_EQUAL: { + return Variant::evaluate(Variant::OP_GREATER_EQUAL, left_value, right_value); + } break; + case LimboUtility::CheckType::CHECK_NOT_EQUAL: { + return Variant::evaluate(Variant::OP_NOT_EQUAL, left_value, right_value); + } break; + default: { + return false; + } break; + } +} + void LimboUtility::_bind_methods() { ClassDB::bind_method(D_METHOD("decorate_var", "p_variable"), &LimboUtility::decorate_var); ClassDB::bind_method(D_METHOD("get_status_name", "p_status"), &LimboUtility::get_status_name); ClassDB::bind_method(D_METHOD("get_task_icon", "p_class_or_script_path"), &LimboUtility::get_task_icon); + + BIND_ENUM_CONSTANT(CHECK_EQUAL); + BIND_ENUM_CONSTANT(CHECK_LESS_THAN); + BIND_ENUM_CONSTANT(CHECK_LESS_THAN_OR_EQUAL); + BIND_ENUM_CONSTANT(CHECK_GREATER_THAN); + BIND_ENUM_CONSTANT(CHECK_GREATER_THAN_OR_EQUAL); + BIND_ENUM_CONSTANT(CHECK_NOT_EQUAL); } LimboUtility::LimboUtility() { diff --git a/util/limbo_utility.h b/util/limbo_utility.h index 5c3e54a..7d410d6 100644 --- a/util/limbo_utility.h +++ b/util/limbo_utility.h @@ -14,11 +14,22 @@ #include "core/object/class_db.h" #include "core/object/object.h" +#include "core/variant/variant.h" #include "scene/resources/texture.h" class LimboUtility : public Object { GDCLASS(LimboUtility, Object); +public: + enum CheckType : unsigned int { + CHECK_EQUAL, + CHECK_LESS_THAN, + CHECK_LESS_THAN_OR_EQUAL, + CHECK_GREATER_THAN, + CHECK_GREATER_THAN_OR_EQUAL, + CHECK_NOT_EQUAL + }; + protected: static LimboUtility *singleton; static void _bind_methods(); @@ -30,8 +41,13 @@ public: String get_status_name(int p_status) const; Ref get_task_icon(String p_class_or_script_path) const; + String get_check_operator_string(CheckType p_check_type); + bool perform_check(CheckType p_check_type, const Variant &left_value, const Variant &right_value); + LimboUtility(); ~LimboUtility(); }; +VARIANT_ENUM_CAST(LimboUtility::CheckType); + #endif // LIMBO_UTILITY_H \ No newline at end of file