diff --git a/bt/actions/bt_call_method.cpp b/bt/actions/bt_call_method.cpp new file mode 100644 index 0000000..40d70fe --- /dev/null +++ b/bt/actions/bt_call_method.cpp @@ -0,0 +1,95 @@ +/** + * bt_call_method.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_call_method.h" + +//**** Setters / Getters + +void BTCallMethod::set_method_name(StringName p_method_name) { + method_name = p_method_name; + emit_changed(); +} + +void BTCallMethod::set_node_param(Ref p_object) { + node_param = p_object; + emit_changed(); + if (Engine::get_singleton()->is_editor_hint() && node_param.is_valid()) { + node_param->connect(SNAME("changed"), Callable(this, SNAME("emit_changed"))); + } +} + +void BTCallMethod::set_args(Array p_args) { + args = p_args; + emit_changed(); +} + +//**** Task Implementation + +String BTCallMethod::get_configuration_warning() const { + String warnings = BTAction::get_configuration_warning(); + if (method_name == StringName()) { + warnings += "Method Name is not set.\n"; + } + if (node_param.is_null()) { + warnings += "Node parameter is not set.\n"; + } else if (node_param->get_value_source() == BBParam::SAVED_VALUE && node_param->get_saved_value().is_zero()) { + warnings += "Path to node is not set.\n"; + } else if (node_param->get_value_source() == BBParam::BLACKBOARD_VAR && node_param->get_variable() == StringName()) { + warnings += "Node blackboard variable is not set.\n"; + } + return warnings; +} + +String BTCallMethod::_generate_name() const { + return vformat("CallMethod %s(%s) node: %s", + (method_name != StringName() ? method_name : "???"), + (args.size() > 0 ? Variant(args).get_construct_string().trim_prefix("[").trim_suffix("]") : ""), + (node_param.is_valid() && !node_param->to_string().is_empty() ? node_param->to_string() : "???")); +} + +int BTCallMethod::_tick(double p_delta) { + ERR_FAIL_COND_V_MSG(method_name == StringName(), FAILURE, "BTCallMethod: Method Name is not set."); + ERR_FAIL_COND_V_MSG(node_param.is_null(), FAILURE, "BTCallMethod: Node parameter is not set."); + Object *obj = node_param->get_value(get_agent(), get_blackboard()); + ERR_FAIL_COND_V_MSG(obj == nullptr, FAILURE, "BTCallMethod: Failed to get node: " + node_param->to_string()); + + const Variant **argptrs = nullptr; + + if (args.size() > 0) { + argptrs = (const Variant **)alloca(sizeof(Variant *) * args.size()); + for (int i = 0; i < args.size(); i++) { + argptrs[i] = &args[i]; + } + } + + Callable::CallError ce; + obj->callp(method_name, argptrs, args.size(), ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_FAIL_V_MSG(FAILURE, "BTCallMethod: Error calling method: " + Variant::get_call_error_text(obj, method_name, argptrs, args.size(), ce) + "."); + } + + return SUCCESS; +} + +//**** Godot + +void BTCallMethod::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_method_name", "p_method_name"), &BTCallMethod::set_method_name); + ClassDB::bind_method(D_METHOD("get_method_name"), &BTCallMethod::get_method_name); + ClassDB::bind_method(D_METHOD("set_node_param", "p_param"), &BTCallMethod::set_node_param); + ClassDB::bind_method(D_METHOD("get_node_param"), &BTCallMethod::get_node_param); + ClassDB::bind_method(D_METHOD("set_args", "p_args"), &BTCallMethod::set_args); + ClassDB::bind_method(D_METHOD("get_args"), &BTCallMethod::get_args); + + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "method_name"), "set_method_name", "get_method_name"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "BBNode"), "set_node_param", "get_node_param"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "args"), "set_args", "get_args"); +} diff --git a/bt/actions/bt_call_method.h b/bt/actions/bt_call_method.h new file mode 100644 index 0000000..e777d14 --- /dev/null +++ b/bt/actions/bt_call_method.h @@ -0,0 +1,46 @@ +/** + * bt_call_method.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_CALL_METHOD_H +#define BT_CALL_METHOD_H + +#include "bt_action.h" + +#include "modules/limboai/blackboard/bb_param/bb_node.h" + +class BTCallMethod : public BTAction { + GDCLASS(BTCallMethod, BTAction); + +private: + StringName method_name; + Ref node_param; + Array args; + +protected: + static void _bind_methods(); + + virtual String _generate_name() const override; + virtual int _tick(double p_delta) override; + +public: + void set_method_name(StringName p_method_name); + StringName get_method_name() const { return method_name; } + + void set_node_param(Ref p_object); + Ref get_node_param() const { return node_param; } + + void set_args(Array p_args); + Array get_args() const { return args; } + + virtual String get_configuration_warning() const override; +}; + +#endif // BT_CALL_METHOD \ No newline at end of file diff --git a/config.py b/config.py index bee70be..ef9e5dd 100644 --- a/config.py +++ b/config.py @@ -61,6 +61,7 @@ def get_doc_classes(): "BTAlwaysFail", "BTAlwaysSucceed", "BTAwaitAnimation", + "BTCallMethod", "BTCheckAgentProperty", "BTCheckTrigger", "BTCheckVar", diff --git a/register_types.cpp b/register_types.cpp index ce49ceb..2e99327 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -46,6 +46,7 @@ #include "blackboard/blackboard.h" #include "bt/actions/bt_action.h" #include "bt/actions/bt_await_animation.h" +#include "bt/actions/bt_call_method.h" #include "bt/actions/bt_console_print.h" #include "bt/actions/bt_fail.h" #include "bt/actions/bt_pause_animation.h" @@ -144,6 +145,7 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(BTAction); GDREGISTER_CLASS(BTAwaitAnimation); + GDREGISTER_CLASS(BTCallMethod); GDREGISTER_CLASS(BTConsolePrint); GDREGISTER_CLASS(BTFail); GDREGISTER_CLASS(BTNewScope);