diff --git a/blackboard/bb_variable.cpp b/blackboard/bb_variable.cpp
index 12ad83b..0dfb0e7 100644
--- a/blackboard/bb_variable.cpp
+++ b/blackboard/bb_variable.cpp
@@ -20,19 +20,35 @@ void BBVariable::unref() {
data = nullptr;
}
-// void BBVariable::init_ref() {
-// if (data) {
-// unref();
-// }
-// data = memnew(Data);
-// data->refcount.init();
-// }
-
void BBVariable::set_value(const Variant &p_value) {
- data->value = p_value;
+ data->value = p_value; // Setting value even when bound as a fallback in case the binding fails.
+
+ if (is_bound()) {
+ Object *obj = ObjectDB::get_instance(ObjectID(data->bound_object));
+ ERR_FAIL_COND_MSG(!obj, "Blackboard: Failed to get bound object.");
+#ifdef LIMBOAI_MODULE
+ bool r_valid;
+ obj->set(data->bound_property, p_value, &r_valid);
+ ERR_FAIL_COND_MSG(!r_valid, vformat("Blackboard: Failed to set bound property `%s` on %s", data->bound_property, obj));
+#elif LIMBOAI_GDEXTENSION
+ obj->set(data->bound_property, p_value);
+#endif
+ }
}
Variant BBVariable::get_value() const {
+ if (is_bound()) {
+ Object *obj = ObjectDB::get_instance(ObjectID(data->bound_object));
+ ERR_FAIL_COND_V_MSG(!obj, data->value, "Blackboard: Failed to get bound object.");
+#ifdef LIMBOAI_MODULE
+ bool r_valid;
+ Variant ret = obj->get(data->bound_property, &r_valid);
+ ERR_FAIL_COND_V_MSG(!r_valid, data->value, vformat("Blackboard: Failed to get bound property `%s` on %s", data->bound_property, obj));
+#elif LIMBOAI_GDEXTENSION
+ Variant ret = obj->get(data->bound_property);
+#endif
+ return ret;
+ }
return data->value;
}
@@ -89,6 +105,19 @@ void BBVariable::copy_prop_info(const BBVariable &p_other) {
data->hint_string = p_other.data->hint_string;
}
+void BBVariable::bind(Object *p_object, const StringName &p_property) {
+ ERR_FAIL_NULL_MSG(p_object, "Blackboard: Binding failed - object is null.");
+ ERR_FAIL_COND_MSG(p_property == StringName(), "Blackboard: Binding failed - property name is empty.");
+ ERR_FAIL_COND_MSG(!OBJECT_HAS_PROPERTY(p_object, p_property), vformat("Blackboard: Binding failed - %s has no property `%s`.", p_object, p_property));
+ data->bound_object = p_object->get_instance_id();
+ data->bound_property = p_property;
+}
+
+void BBVariable::unbind() {
+ data->bound_object = 0;
+ data->bound_property = StringName();
+}
+
bool BBVariable::operator==(const BBVariable &p_var) const {
if (data == p_var.data) {
return true;
diff --git a/blackboard/bb_variable.h b/blackboard/bb_variable.h
index cccf2b8..aca84fd 100644
--- a/blackboard/bb_variable.h
+++ b/blackboard/bb_variable.h
@@ -14,14 +14,10 @@
#ifdef LIMBOAI_MODULE
#include "core/object/object.h"
-#include "core/templates/safe_refcount.h"
-#include "core/variant/variant.h"
#endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION
-#include "godot_cpp/core/defs.hpp"
-#include "godot_cpp/templates/safe_refcount.hpp"
-#include "godot_cpp/variant/variant.hpp"
+#include "godot_cpp/core/object.hpp"
using namespace godot;
#endif // LIMBOAI_GDEXTENSION
@@ -33,14 +29,14 @@ private:
Variant::Type type = Variant::NIL;
PropertyHint hint = PropertyHint::PROPERTY_HINT_NONE;
String hint_string;
- // bool bound = false;
- // uint64_t bound_object = 0;
- // StringName bound_property;
+
+ NodePath binding_path;
+ uint64_t bound_object = 0;
+ StringName bound_property;
};
Data *data = nullptr;
void unref();
- // void init_ref();
public:
void set_value(const Variant &p_value);
@@ -60,10 +56,15 @@ public:
bool is_same_prop_info(const BBVariable &p_other) const;
void copy_prop_info(const BBVariable &p_other);
- // bool is_bound() { return bound; }
+ // * Editor binding methods
+ String get_binding_path() const { return data->binding_path; }
+ void set_binding_path(const String &p_binding_path) { data->binding_path = p_binding_path; }
+ bool has_binding() { return data->binding_path.is_empty(); }
- // void bind(Node *p_root, NodePath p_path);
- // void unbind();
+ // * Runtime binding methods
+ bool is_bound() const { return data->bound_object != 0; }
+ void bind(Object *p_object, const StringName &p_property);
+ void unbind();
bool operator==(const BBVariable &p_var) const;
bool operator!=(const BBVariable &p_var) const;
diff --git a/blackboard/blackboard.cpp b/blackboard/blackboard.cpp
index 62584e0..6e3f8c8 100644
--- a/blackboard/blackboard.cpp
+++ b/blackboard/blackboard.cpp
@@ -12,7 +12,6 @@
#include "blackboard.h"
#ifdef LIMBOAI_MODULE
-#include "core/error/error_macros.h"
#include "core/variant/variant.h"
#include "scene/main/node.h"
#endif // LIMBOAI_MODULE
@@ -62,6 +61,16 @@ void Blackboard::erase_var(const String &p_name) {
data.erase(p_name);
}
+void Blackboard::bind_var_to_property(const String &p_name, Object *p_object, const StringName &p_property) {
+ ERR_FAIL_COND_MSG(!data.has(p_name), "Blackboard: Binding failed - can't bind variable that doesn't exist.");
+ data[p_name].bind(p_object, p_property);
+}
+
+void Blackboard::unbind_var(const String &p_name) {
+ ERR_FAIL_COND_MSG(data.has(p_name), "Blackboard: Can't unbind variable that doesn't exist.");
+ data[p_name].unbind();
+}
+
void Blackboard::add_var(const String &p_name, const BBVariable &p_var) {
ERR_FAIL_COND(data.has(p_name));
data.insert(p_name, p_var);
@@ -89,4 +98,6 @@ void Blackboard::_bind_methods() {
ClassDB::bind_method(D_METHOD("erase_var", "p_name"), &Blackboard::erase_var);
ClassDB::bind_method(D_METHOD("prefetch_nodepath_vars", "p_node"), &Blackboard::prefetch_nodepath_vars);
ClassDB::bind_method(D_METHOD("top"), &Blackboard::top);
+ ClassDB::bind_method(D_METHOD("bind_var_to_property", "p_name", "p_object", "p_property"), &Blackboard::bind_var_to_property);
+ ClassDB::bind_method(D_METHOD("unbind_var", "p_name"), &Blackboard::unbind_var);
}
diff --git a/blackboard/blackboard.h b/blackboard/blackboard.h
index 42c04c7..eba492b 100644
--- a/blackboard/blackboard.h
+++ b/blackboard/blackboard.h
@@ -51,6 +51,9 @@ public:
bool has_var(const String &p_name) const;
void erase_var(const String &p_name);
+ void bind_var_to_property(const String &p_name, Object *p_object, const StringName &p_property);
+ void unbind_var(const String &p_name);
+
void add_var(const String &p_name, const BBVariable &p_var);
void prefetch_nodepath_vars(Node *p_node);
diff --git a/doc_classes/Blackboard.xml b/doc_classes/Blackboard.xml
index 91f4492..adb20f6 100644
--- a/doc_classes/Blackboard.xml
+++ b/doc_classes/Blackboard.xml
@@ -11,6 +11,15 @@
+
+
+
+
+
+
+ Establish a binding between a variable and the object's property specified by [param p_property] and [param p_object]. Changes to the variable update the property, and vice versa.
+
+
@@ -67,5 +76,12 @@
Returns the topmost [Blackboard] in the scope chain.
+
+
+
+
+ Remove binding from a variable.
+
+
diff --git a/util/limbo_compat.h b/util/limbo_compat.h
index fdfce9a..690682b 100644
--- a/util/limbo_compat.h
+++ b/util/limbo_compat.h
@@ -54,6 +54,11 @@
#define GET_SCRIPT(m_obj) (m_obj->get_script_instance() ? m_obj->get_script_instance()->get_script() : nullptr)
#define ADD_STYLEBOX_OVERRIDE(m_control, m_name, m_stylebox) (m_control->add_theme_style_override(m_name, m_stylebox))
+_FORCE_INLINE_ bool OBJECT_HAS_PROPERTY(Object *p_obj, const StringName &p_prop) {
+ bool r_valid;
+ return Variant(p_obj).has_key(p_prop, r_valid);
+}
+
#define VARIANT_EVALUATE(m_op, m_lvalue, m_rvalue, r_ret) r_ret = Variant::evaluate(m_op, m_lvalue, m_rvalue)
// * Virtual calls
@@ -133,6 +138,10 @@ using namespace godot;
#define GET_SCRIPT(m_obj) (m_obj->get_script())
#define ADD_STYLEBOX_OVERRIDE(m_control, m_name, m_stylebox) (m_control->add_theme_stylebox_override(m_name, m_stylebox))
+_FORCE_INLINE_ bool OBJECT_HAS_PROPERTY(Object *p_obj, const StringName &p_prop) {
+ return Variant(p_obj).has_key(p_prop);
+}
+
#define VARIANT_EVALUATE(m_op, m_lvalue, m_rvalue, r_ret) \
{ \
bool r_valid; \