From 6ef0dd942e207fc0e6bcbbd05039e0f865bfde23 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 23 Jan 2024 10:33:57 +0100 Subject: [PATCH 01/15] Add initial implementation of BBVariable and BlackboardSource classes --- blackboard/bb_variable.cpp | 146 +++++++++++++++++++++++++++++++ blackboard/bb_variable.h | 67 ++++++++++++++ blackboard/blackboard_source.cpp | 70 +++++++++++++++ blackboard/blackboard_source.h | 42 +++++++++ 4 files changed, 325 insertions(+) create mode 100644 blackboard/bb_variable.cpp create mode 100644 blackboard/bb_variable.h create mode 100644 blackboard/blackboard_source.cpp create mode 100644 blackboard/blackboard_source.h diff --git a/blackboard/bb_variable.cpp b/blackboard/bb_variable.cpp new file mode 100644 index 00000000..fda7c802 --- /dev/null +++ b/blackboard/bb_variable.cpp @@ -0,0 +1,146 @@ +/** + * bb_variable.cpp + * ============================================================================= + * Copyright 2021-2024 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 "bb_variable.h" + +void BBVariable::unref() { + if (data && data->refcount.unref()) { + memdelete(data); + } + 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; +} + +Variant BBVariable::get_value() const { + return data->value; +} + +void BBVariable::set_type(Variant::Type p_type) { + data->type = p_type; +} + +Variant::Type BBVariable::get_type() const { + return data->type; +} + +void BBVariable::set_hint(PropertyHint p_hint) { + data->hint = p_hint; +} + +PropertyHint BBVariable::get_hint() const { + return data->hint; +} + +void BBVariable::set_hint_string(const String &p_hint_string) { + data->hint_string = p_hint_string; +} + +String BBVariable::get_hint_string() const { + return data->hint_string; +} + +BBVariable BBVariable::duplicate() const { + BBVariable var; + var.data->hint = data->hint; + var.data->hint_string = data->hint_string; + var.data->type = data->type; + var.data->value = data->value; + return var; +} + +bool BBVariable::is_same_prop_info(const BBVariable &p_other) const { + if (data->type != p_other.data->type) { + return false; + } + if (data->hint != p_other.data->hint) { + return false; + } + if (data->hint_string != p_other.data->hint_string) { + return false; + } + return true; +} + +void BBVariable::copy_prop_info(const BBVariable &p_other) { + data->type = p_other.data->type; + data->hint = p_other.data->hint; + data->hint_string = p_other.data->hint_string; +} + +bool BBVariable::operator==(const BBVariable &p_var) const { + if (data == p_var.data) { + return true; + } + + if (!data || !p_var.data) { + return false; + } + + if (data->type != p_var.data->type) { + return false; + } + + if (data->hint != p_var.data->hint) { + return false; + } + + if (data->value != p_var.data->value) { + return false; + } + + if (data->hint_string != p_var.data->hint_string) { + return false; + } + + return true; +} + +bool BBVariable::operator!=(const BBVariable &p_var) const { + return !(*this == p_var); +} + +void BBVariable::operator=(const BBVariable &p_var) { + if (this == &p_var) { + return; + } + + unref(); + + if (p_var.data && p_var.data->refcount.ref()) { + data = p_var.data; + } +} + +BBVariable::BBVariable(const BBVariable &p_var) { + if (p_var.data && p_var.data->refcount.ref()) { + data = p_var.data; + } +} + +BBVariable::BBVariable() { + data = memnew(Data); + data->refcount.init(); +} + +BBVariable::~BBVariable() { + unref(); +} diff --git a/blackboard/bb_variable.h b/blackboard/bb_variable.h new file mode 100644 index 00000000..d5b2fc3b --- /dev/null +++ b/blackboard/bb_variable.h @@ -0,0 +1,67 @@ +/** + * bb_variable.h + * ============================================================================= + * Copyright 2021-2024 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 BB_VARIABLE_H +#define BB_VARIABLE_H + +#include "core/object/object.h" +#include "core/templates/safe_refcount.h" + +class BBVariable { +private: + struct Data { + SafeRefCount refcount; + Variant value; + 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; + }; + + Data *data = nullptr; + void unref(); + // void init_ref(); + +public: + void set_value(const Variant &p_value); + Variant get_value() const; + + void set_type(Variant::Type p_type); + Variant::Type get_type() const; + + void set_hint(PropertyHint p_hint); + PropertyHint get_hint() const; + + void set_hint_string(const String &p_hint_string); + String get_hint_string() const; + + BBVariable duplicate() const; + + bool is_same_prop_info(const BBVariable &p_other) const; + void copy_prop_info(const BBVariable &p_other); + + // bool is_bound() { return bound; } + + // void bind(Node *p_root, NodePath p_path); + // void unbind(); + + bool operator==(const BBVariable &p_var) const; + bool operator!=(const BBVariable &p_var) const; + void operator=(const BBVariable &p_var); + + BBVariable(const BBVariable &p_var); + BBVariable(); + ~BBVariable(); +}; + +#endif // BB_VARIABLE_H diff --git a/blackboard/blackboard_source.cpp b/blackboard/blackboard_source.cpp new file mode 100644 index 00000000..9b26fef0 --- /dev/null +++ b/blackboard/blackboard_source.cpp @@ -0,0 +1,70 @@ +/** + * blackboard_source.cpp + * ============================================================================= + * Copyright 2021-2024 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 "blackboard_source.h" + +void BlackboardSource::set_value(const String &p_name, const Variant &p_value) { + ERR_FAIL_COND(!vars.has(p_name)); + vars.get(p_name).set_value(p_value); +} + +Variant BlackboardSource::get_value(const String &p_name) const { + ERR_FAIL_COND_V(!vars.has(p_name), Variant()); + return vars.get(p_name).get_value(); +} + +void BlackboardSource::add_var(const String &p_name, const BBVariable &p_var) { + ERR_FAIL_COND(vars.has(p_name)); + ERR_FAIL_COND(base.is_valid()); + vars.insert(p_name, p_var); +} + +void BlackboardSource::remove_var(const String &p_name) { + ERR_FAIL_COND(!vars.has(p_name)); + ERR_FAIL_COND(base.is_valid()); + vars.erase(p_name); +} + +BBVariable BlackboardSource::get_var(const String &p_name) { + ERR_FAIL_COND_V(!vars.has(p_name), BBVariable()); + return vars.get(p_name); +} + +PackedStringArray BlackboardSource::list_vars() const { + PackedStringArray ret; + for (const KeyValue &kv : vars) { + ret.append(kv.key); + } + return ret; +} + +void BlackboardSource::sync_base() { + for (const KeyValue &kv : base->vars) { + if (!vars.has(kv.key)) { + vars.insert(kv.key, kv.value.duplicate()); + continue; + } + + BBVariable var = vars.get(kv.key); + if (!var.is_same_prop_info(kv.value)) { + var.copy_prop_info(kv.value); + } + if (var.get_value().get_type() != kv.value.get_type()) { + var.set_value(kv.value.get_value()); + } + } +} + +Ref BlackboardSource::instantiate() { + Ref bb = memnew(Blackboard); + // TODO: fill bb + return bb; +} diff --git a/blackboard/blackboard_source.h b/blackboard/blackboard_source.h new file mode 100644 index 00000000..81dcdd5f --- /dev/null +++ b/blackboard/blackboard_source.h @@ -0,0 +1,42 @@ +/** + * blackboard_source.h + * ============================================================================= + * Copyright 2021-2024 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 BLACKBOARD_SOURCE_H +#define BLACKBOARD_SOURCE_H + +#include "core/io/resource.h" + +#include "bb_variable.h" +#include "blackboard.h" + +class BlackboardSource : public Resource { + GDCLASS(BlackboardSource, Resource); + +private: + HashMap vars; + Ref base; + // HashMap overrides; + +public: + void set_value(const String &p_name, const Variant &p_value); + Variant get_value(const String &p_name) const; + void add_var(const String &p_name, const BBVariable &p_var); + void remove_var(const String &p_name); + BBVariable get_var(const String &p_name); + PackedStringArray list_vars() const; + + void sync_base(); + Ref instantiate(); + + BlackboardSource() = default; +}; + +#endif // BLACKBOARD_SOURCE_H From a247d0d67bf76b6a27d3daad878db0a8f247dfcd Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 23 Jan 2024 12:05:54 +0100 Subject: [PATCH 02/15] Replace node blackboard data with BlackboardSource --- blackboard/blackboard.cpp | 54 ++++++++++++++++------------ blackboard/blackboard.h | 23 +++++++----- blackboard/blackboard_source.cpp | 49 ++++++++++++++++--------- blackboard/blackboard_source.h | 6 ++-- bt/bt_player.cpp | 9 +++-- bt/bt_player.h | 8 +++-- bt/tasks/decorators/bt_new_scope.cpp | 14 +++++--- bt/tasks/decorators/bt_new_scope.h | 8 +++-- hsm/limbo_state.cpp | 9 ++--- hsm/limbo_state.h | 8 +++-- 10 files changed, 117 insertions(+), 71 deletions(-) diff --git a/blackboard/blackboard.cpp b/blackboard/blackboard.cpp index cfd69731..d0769b7d 100644 --- a/blackboard/blackboard.cpp +++ b/blackboard/blackboard.cpp @@ -34,51 +34,61 @@ Ref Blackboard::top() const { return bb; } -Variant Blackboard::get_var(const Variant &p_key, const Variant &p_default) const { - if (data.has(p_key)) { - return data.get(p_key, Variant()); +Variant Blackboard::get_var(const String &p_name, const Variant &p_default) const { + if (data.has(p_name)) { + return data.get(p_name).get_value(); } else if (parent.is_valid()) { - return parent->get_var(p_key, p_default); + return parent->get_var(p_name, p_default); } else { return p_default; } } -void Blackboard::set_var(const Variant &p_key, const Variant &p_value) { - data[p_key] = p_value; +void Blackboard::set_var(const String &p_name, const Variant &p_value) { + // TODO: Check if p_value can be converted into required type! + if (data.has(p_name)) { + data[p_name].set_value(p_value); + } else { + BBVariable var; + var.set_value(p_value); + data.insert(p_name, var); + } +} + +bool Blackboard::has_var(const String &p_name) const { + return data.has(p_name) || (parent.is_valid() && parent->has_var(p_name)); } -bool Blackboard::has_var(const Variant &p_key) const { - return data.has(p_key) || (parent.is_valid() && parent->has_var(p_key)); +void Blackboard::erase_var(const String &p_name) { + data.erase(p_name); } -void Blackboard::erase_var(const Variant &p_key) { - data.erase(p_key); +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); } void Blackboard::prefetch_nodepath_vars(Node *p_node) { ERR_FAIL_COND(p_node == nullptr); - Array keys = data.keys(); - Array values = data.values(); - for (int i = 0; i < keys.size(); i++) { - if (values[i].get_type() == Variant::NODE_PATH) { - Node *fetched_node = p_node->get_node_or_null(values[i]); + for (KeyValue &kv : data) { + if (kv.value.get_value().get_type() == Variant::NODE_PATH) { + Node *fetched_node = p_node->get_node_or_null(kv.value.get_value()); if (fetched_node != nullptr) { - data[keys[i]] = fetched_node; + kv.value.set_value(fetched_node); } } } } void Blackboard::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_data"), &Blackboard::get_data); - ClassDB::bind_method(D_METHOD("set_data", "p_data"), &Blackboard::set_data); - ClassDB::bind_method(D_METHOD("get_var", "p_key", "p_default"), &Blackboard::get_var, Variant()); - ClassDB::bind_method(D_METHOD("set_var", "p_key", "p_value"), &Blackboard::set_var); - ClassDB::bind_method(D_METHOD("has_var", "p_key"), &Blackboard::has_var); + ClassDB::bind_method(D_METHOD("get_var", "p_name", "p_default"), &Blackboard::get_var, Variant()); + ClassDB::bind_method(D_METHOD("set_var", "p_name", "p_value"), &Blackboard::set_var); + ClassDB::bind_method(D_METHOD("has_var", "p_name"), &Blackboard::has_var); ClassDB::bind_method(D_METHOD("set_parent_scope", "p_blackboard"), &Blackboard::set_parent_scope); ClassDB::bind_method(D_METHOD("get_parent_scope"), &Blackboard::get_parent_scope); - ClassDB::bind_method(D_METHOD("erase_var", "p_key"), &Blackboard::erase_var); + 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("get_data"), &Blackboard::get_data); + // ClassDB::bind_method(D_METHOD("set_data", "p_data"), &Blackboard::set_data); } diff --git a/blackboard/blackboard.h b/blackboard/blackboard.h index 893bd892..062d8b20 100644 --- a/blackboard/blackboard.h +++ b/blackboard/blackboard.h @@ -12,6 +12,8 @@ #ifndef BLACKBOARD_H #define BLACKBOARD_H +#include "bb_variable.h" + #ifdef LIMBOAI_MODULE #include "core/object/object.h" #include "core/object/ref_counted.h" @@ -33,27 +35,30 @@ class Blackboard : public RefCounted { GDCLASS(Blackboard, RefCounted); private: - Dictionary data; + HashMap data; Ref parent; protected: static void _bind_methods(); public: - void set_data(const Dictionary &p_value) { data = p_value; } - Dictionary get_data() const { return data; } - void set_parent_scope(const Ref &p_blackboard) { parent = p_blackboard; } Ref get_parent_scope() const { return parent; } Ref top() const; - Variant get_var(const Variant &p_key, const Variant &p_default) const; - void set_var(const Variant &p_key, const Variant &p_value); - bool has_var(const Variant &p_key) const; - void erase_var(const Variant &p_key); + Variant get_var(const String &p_name, const Variant &p_default) const; + void set_var(const String &p_name, const Variant &p_value); + bool has_var(const String &p_name) const; + void erase_var(const String &p_name); + + void add_var(const String &p_name, const BBVariable &p_var); void prefetch_nodepath_vars(Node *p_node); + + // TODO: Rework serialization API. + // void set_data(const Dictionary &p_value); + // Dictionary get_data() const; }; -#endif // BLACKBOARD_H \ No newline at end of file +#endif // BLACKBOARD_H diff --git a/blackboard/blackboard_source.cpp b/blackboard/blackboard_source.cpp index 9b26fef0..08cfb962 100644 --- a/blackboard/blackboard_source.cpp +++ b/blackboard/blackboard_source.cpp @@ -12,48 +12,48 @@ #include "blackboard_source.h" void BlackboardSource::set_value(const String &p_name, const Variant &p_value) { - ERR_FAIL_COND(!vars.has(p_name)); - vars.get(p_name).set_value(p_value); + ERR_FAIL_COND(!data.has(p_name)); + data.get(p_name).set_value(p_value); } Variant BlackboardSource::get_value(const String &p_name) const { - ERR_FAIL_COND_V(!vars.has(p_name), Variant()); - return vars.get(p_name).get_value(); + ERR_FAIL_COND_V(!data.has(p_name), Variant()); + return data.get(p_name).get_value(); } void BlackboardSource::add_var(const String &p_name, const BBVariable &p_var) { - ERR_FAIL_COND(vars.has(p_name)); + ERR_FAIL_COND(data.has(p_name)); ERR_FAIL_COND(base.is_valid()); - vars.insert(p_name, p_var); + data.insert(p_name, p_var); } void BlackboardSource::remove_var(const String &p_name) { - ERR_FAIL_COND(!vars.has(p_name)); + ERR_FAIL_COND(!data.has(p_name)); ERR_FAIL_COND(base.is_valid()); - vars.erase(p_name); + data.erase(p_name); } BBVariable BlackboardSource::get_var(const String &p_name) { - ERR_FAIL_COND_V(!vars.has(p_name), BBVariable()); - return vars.get(p_name); + ERR_FAIL_COND_V(!data.has(p_name), BBVariable()); + return data.get(p_name); } PackedStringArray BlackboardSource::list_vars() const { PackedStringArray ret; - for (const KeyValue &kv : vars) { + for (const KeyValue &kv : data) { ret.append(kv.key); } return ret; } void BlackboardSource::sync_base() { - for (const KeyValue &kv : base->vars) { - if (!vars.has(kv.key)) { - vars.insert(kv.key, kv.value.duplicate()); + for (const KeyValue &kv : base->data) { + if (!data.has(kv.key)) { + data.insert(kv.key, kv.value.duplicate()); continue; } - BBVariable var = vars.get(kv.key); + BBVariable var = data.get(kv.key); if (!var.is_same_prop_info(kv.value)) { var.copy_prop_info(kv.value); } @@ -63,8 +63,23 @@ void BlackboardSource::sync_base() { } } -Ref BlackboardSource::instantiate() { +Ref BlackboardSource::create_blackboard() { Ref bb = memnew(Blackboard); - // TODO: fill bb + for (const KeyValue &kv : data) { + bb->add_var(kv.key, kv.value.duplicate()); + } return bb; } + +void BlackboardSource::populate_blackboard(const Ref &p_blackboard, bool overwrite) { + for (const KeyValue &kv : data) { + if (p_blackboard->has_var(kv.key)) { + if (overwrite) { + p_blackboard->erase_var(kv.key); + } else { + continue; + } + } + p_blackboard->add_var(kv.key, kv.value.duplicate()); + } +} diff --git a/blackboard/blackboard_source.h b/blackboard/blackboard_source.h index 81dcdd5f..38b4b5c8 100644 --- a/blackboard/blackboard_source.h +++ b/blackboard/blackboard_source.h @@ -21,7 +21,7 @@ class BlackboardSource : public Resource { GDCLASS(BlackboardSource, Resource); private: - HashMap vars; + HashMap data; Ref base; // HashMap overrides; @@ -32,9 +32,11 @@ class BlackboardSource : public Resource { void remove_var(const String &p_name); BBVariable get_var(const String &p_name); PackedStringArray list_vars() const; + bool is_empty() const { return data.is_empty(); } void sync_base(); - Ref instantiate(); + Ref create_blackboard(); + void populate_blackboard(const Ref &p_blackboard, bool overwrite); BlackboardSource() = default; }; diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 25d80048..b5e6d4e4 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -160,6 +160,9 @@ void BTPlayer::_notification(int p_notification) { } break; case NOTIFICATION_READY: { if (!Engine::get_singleton()->is_editor_hint()) { + if (blackboard_source.is_valid()) { + blackboard = blackboard_source->create_blackboard(); + } if (behavior_tree.is_valid()) { _load_tree(); } @@ -196,8 +199,8 @@ void BTPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_prefetch_nodepath_vars", "p_value"), &BTPlayer::set_prefetch_nodepath_vars); ClassDB::bind_method(D_METHOD("get_prefetch_nodepath_vars"), &BTPlayer::get_prefetch_nodepath_vars); - ClassDB::bind_method(D_METHOD("_set_blackboard_data", "p_blackboard"), &BTPlayer::_set_blackboard_data); - ClassDB::bind_method(D_METHOD("_get_blackboard_data"), &BTPlayer::_get_blackboard_data); + ClassDB::bind_method(D_METHOD("set_blackboard_source", "p_blackboard"), &BTPlayer::set_blackboard_source); + ClassDB::bind_method(D_METHOD("get_blackboard_source"), &BTPlayer::get_blackboard_source); ClassDB::bind_method(D_METHOD("update", "p_delta"), &BTPlayer::update); ClassDB::bind_method(D_METHOD("restart"), &BTPlayer::restart); @@ -207,7 +210,7 @@ void BTPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,Manual"), "set_update_mode", "get_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "get_active"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_NONE, "Blackboard", 0), "set_blackboard", "get_blackboard"); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_blackboard_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "_set_blackboard_data", "_get_blackboard_data"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT), "set_blackboard_source", "get_blackboard_source"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prefetch_nodepath_vars"), "set_prefetch_nodepath_vars", "get_prefetch_nodepath_vars"); BIND_ENUM_CONSTANT(IDLE); diff --git a/bt/bt_player.h b/bt/bt_player.h index 175fafc2..70430d96 100644 --- a/bt/bt_player.h +++ b/bt/bt_player.h @@ -13,6 +13,7 @@ #define BT_PLAYER_H #include "../blackboard/blackboard.h" +#include "../blackboard/blackboard_source.h" #include "behavior_tree.h" #include "tasks/bt_task.h" @@ -36,6 +37,7 @@ class BTPlayer : public Node { private: Ref behavior_tree; + Ref blackboard_source; UpdateMode update_mode = UpdateMode::PHYSICS; bool active = true; Ref blackboard; @@ -49,15 +51,15 @@ class BTPlayer : public Node { protected: static void _bind_methods(); - void _set_blackboard_data(Dictionary p_value) { blackboard->set_data(p_value.duplicate()); } - Dictionary _get_blackboard_data() const { return blackboard->get_data(); } - void _notification(int p_notification); public: void set_behavior_tree(const Ref &p_tree); Ref get_behavior_tree() const { return behavior_tree; }; + void set_blackboard_source(const Ref &p_source) { blackboard_source = p_source; } + Ref get_blackboard_source() const { return blackboard_source; } + void set_update_mode(UpdateMode p_mode); UpdateMode get_update_mode() const { return update_mode; } diff --git a/bt/tasks/decorators/bt_new_scope.cpp b/bt/tasks/decorators/bt_new_scope.cpp index 7bca1b30..faafd9f7 100644 --- a/bt/tasks/decorators/bt_new_scope.cpp +++ b/bt/tasks/decorators/bt_new_scope.cpp @@ -15,9 +15,13 @@ void BTNewScope::initialize(Node *p_agent, const Ref &p_blackboard) ERR_FAIL_COND(p_agent == nullptr); ERR_FAIL_COND(p_blackboard == nullptr); - Ref bb = memnew(Blackboard); + Ref bb; + if (blackboard_source.is_valid()) { + bb = blackboard_source->create_blackboard(); + } else { + bb = Ref(memnew(Blackboard)); + } - bb->set_data(blackboard_data.duplicate()); bb->set_parent_scope(p_blackboard); BTDecorator::initialize(p_agent, bb); @@ -29,8 +33,8 @@ BT::Status BTNewScope::_tick(double p_delta) { } void BTNewScope::_bind_methods() { - ClassDB::bind_method(D_METHOD("_set_blackboard_data", "p_data"), &BTNewScope::_set_blackboard_data); - ClassDB::bind_method(D_METHOD("_get_blackboard_data"), &BTNewScope::_get_blackboard_data); + ClassDB::bind_method(D_METHOD("set_blackboard_source", "p_source"), &BTNewScope::set_blackboard_source); + ClassDB::bind_method(D_METHOD("get_blackboard_source"), &BTNewScope::get_blackboard_source); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_blackboard_data"), "_set_blackboard_data", "_get_blackboard_data"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT), "set_blackboard_source", "get_blackboard_source"); } diff --git a/bt/tasks/decorators/bt_new_scope.h b/bt/tasks/decorators/bt_new_scope.h index 919ac898..84109907 100644 --- a/bt/tasks/decorators/bt_new_scope.h +++ b/bt/tasks/decorators/bt_new_scope.h @@ -14,18 +14,20 @@ #include "../bt_decorator.h" +#include "../../../blackboard/blackboard_source.h" + class BTNewScope : public BTDecorator { GDCLASS(BTNewScope, BTDecorator); TASK_CATEGORY(Decorators); private: - Dictionary blackboard_data; + Ref blackboard_source; protected: static void _bind_methods(); - void _set_blackboard_data(const Dictionary &p_value) { blackboard_data = p_value; } - Dictionary _get_blackboard_data() const { return blackboard_data; } + void set_blackboard_source(const Ref &p_source) { blackboard_source = p_source; } + Ref get_blackboard_source() const { return blackboard_source; } virtual Status _tick(double p_delta) override; diff --git a/hsm/limbo_state.cpp b/hsm/limbo_state.cpp index 4950e21e..d7517892 100644 --- a/hsm/limbo_state.cpp +++ b/hsm/limbo_state.cpp @@ -69,7 +69,8 @@ void LimboState::_initialize(Node *p_agent, const Ref &p_blackboard) agent = p_agent; if (!p_blackboard.is_null()) { - if (!blackboard->get_data().is_empty()) { + if (blackboard_source.is_valid() && !blackboard_source->is_empty()) { + blackboard = blackboard_source->create_blackboard(); blackboard->set_parent_scope(p_blackboard); } else { blackboard = p_blackboard; @@ -179,8 +180,8 @@ void LimboState::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_guard"), &LimboState::clear_guard); ClassDB::bind_method(D_METHOD("get_blackboard"), &LimboState::get_blackboard); - ClassDB::bind_method(D_METHOD("_set_blackboard_data", "p_blackboard"), &LimboState::_set_blackboard_data); - ClassDB::bind_method(D_METHOD("_get_blackboard_data"), &LimboState::_get_blackboard_data); + ClassDB::bind_method(D_METHOD("set_blackboard_source", "p_source"), &LimboState::set_blackboard_source); + ClassDB::bind_method(D_METHOD("get_blackboard_source"), &LimboState::get_blackboard_source); #ifdef LIMBOAI_MODULE GDVIRTUAL_BIND(_setup); @@ -194,7 +195,7 @@ void LimboState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "EVENT_FINISHED", PROPERTY_HINT_NONE, "", 0), "", "event_finished"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "agent", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_agent", "get_agent"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_RESOURCE_TYPE, "Blackboard", 0), "", "get_blackboard"); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_blackboard_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "_set_blackboard_data", "_get_blackboard_data"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_blackboard_source", "get_blackboard_source"); ADD_SIGNAL(MethodInfo("setup")); ADD_SIGNAL(MethodInfo("entered")); diff --git a/hsm/limbo_state.h b/hsm/limbo_state.h index 7a908d60..f9d84cda 100644 --- a/hsm/limbo_state.h +++ b/hsm/limbo_state.h @@ -13,6 +13,7 @@ #define LIMBO_STATE_H #include "../blackboard/blackboard.h" +#include "../blackboard/blackboard_source.h" #include "../util/limbo_string_names.h" @@ -37,6 +38,7 @@ class LimboState : public Node { GDCLASS(LimboState, Node); private: + Ref blackboard_source; Node *agent; Ref blackboard; HashMap handlers; @@ -51,9 +53,6 @@ class LimboState : public Node { void _notification(int p_what); - void _set_blackboard_data(Dictionary p_value) { blackboard->set_data(p_value.duplicate()); } - Dictionary _get_blackboard_data() const { return blackboard->get_data(); } - virtual void _initialize(Node *p_agent, const Ref &p_blackboard); virtual void _setup(); @@ -71,6 +70,9 @@ class LimboState : public Node { void add_event_handler(const String &p_event, const Callable &p_handler); public: + void set_blackboard_source(const Ref p_source) { blackboard_source = p_source; } + Ref get_blackboard_source() const { return blackboard_source; } + Ref get_blackboard() const { return blackboard; } Node *get_agent() const { return agent; } From 8c67886b52d6c90a5320332730c5835c14c55ffe Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 23 Jan 2024 15:31:56 +0100 Subject: [PATCH 03/15] Parsing BlackboardSource properties --- blackboard/bb_variable.cpp | 9 ++- blackboard/bb_variable.h | 3 +- blackboard/blackboard_source.cpp | 94 +++++++++++++++++++++++ blackboard/blackboard_source.h | 10 ++- bt/behavior_tree.cpp | 7 ++ bt/behavior_tree.h | 11 ++- bt/bt_player.cpp | 22 ++++++ bt/bt_player.h | 3 +- bt/bt_state.cpp | 13 ++++ bt/bt_state.h | 2 + register_types.cpp | 2 + util/limbo_compat.cpp | 123 +++++++++++++++++++++++++++++++ util/limbo_compat.h | 2 + 13 files changed, 296 insertions(+), 5 deletions(-) diff --git a/blackboard/bb_variable.cpp b/blackboard/bb_variable.cpp index fda7c802..12ad83bc 100644 --- a/blackboard/bb_variable.cpp +++ b/blackboard/bb_variable.cpp @@ -11,6 +11,8 @@ #include "bb_variable.h" +#include "../util/limbo_compat.h" + void BBVariable::unref() { if (data && data->refcount.unref()) { memdelete(data); @@ -36,6 +38,7 @@ Variant BBVariable::get_value() const { void BBVariable::set_type(Variant::Type p_type) { data->type = p_type; + data->value = VARIANT_DEFAULT(p_type); } Variant::Type BBVariable::get_type() const { @@ -136,9 +139,13 @@ BBVariable::BBVariable(const BBVariable &p_var) { } } -BBVariable::BBVariable() { +BBVariable::BBVariable(Variant::Type p_type, PropertyHint p_hint, const String &p_hint_string) { data = memnew(Data); data->refcount.init(); + + set_type(p_type); + data->hint = p_hint; + data->hint_string = p_hint_string; } BBVariable::~BBVariable() { diff --git a/blackboard/bb_variable.h b/blackboard/bb_variable.h index d5b2fc3b..d6d2371a 100644 --- a/blackboard/bb_variable.h +++ b/blackboard/bb_variable.h @@ -14,6 +14,7 @@ #include "core/object/object.h" #include "core/templates/safe_refcount.h" +#include "core/variant/variant.h" class BBVariable { private: @@ -60,7 +61,7 @@ class BBVariable { void operator=(const BBVariable &p_var); BBVariable(const BBVariable &p_var); - BBVariable(); + BBVariable(Variant::Type p_type = Variant::Type::NIL, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = ""); ~BBVariable(); }; diff --git a/blackboard/blackboard_source.cpp b/blackboard/blackboard_source.cpp index 08cfb962..9d1b718a 100644 --- a/blackboard/blackboard_source.cpp +++ b/blackboard/blackboard_source.cpp @@ -11,6 +11,93 @@ #include "blackboard_source.h" +bool BlackboardSource::_set(const StringName &p_name, const Variant &p_value) { + String prop_name = p_name; + + // * Editor + if (data.has(prop_name)) { + data[prop_name].set_value(p_value); + return true; + } + + // * Storage + if (prop_name.begins_with("var/")) { + String var_name = prop_name.get_slicec('/', 1); + String what = prop_name.get_slicec('/', 2); + if (!data.has(var_name) && what == "name") { + data.insert(var_name, BBVariable()); + } + if (what == "name") { + // We don't store variable name with the variable. + } else if (what == "type") { + data[var_name].set_type((Variant::Type)(int)p_value); + } else if (what == "value") { + data[var_name].set_value(p_value); + } else if (what == "hint") { + data[var_name].set_hint((PropertyHint)(int)p_value); + } else if (what == "hint_string") { + data[var_name].set_hint_string(p_value); + } else { + return false; + } + return true; + } + + return false; +} + +bool BlackboardSource::_get(const StringName &p_name, Variant &r_ret) const { + String prop_name = p_name; + + // * Editor + if (data.has(prop_name)) { + r_ret = data[prop_name].get_value(); + return true; + } + + // * Storage + if (!prop_name.begins_with("var/")) { + return false; + } + + String var_name = prop_name.get_slicec('/', 1); + String what = prop_name.get_slicec('/', 2); + ERR_FAIL_COND_V(!data.has(var_name), false); + if (what == "type") { + r_ret = data[var_name].get_type(); + } else if (what == "value") { + r_ret = data[var_name].get_value(); + } else if (what == "hint") { + r_ret = data[var_name].get_hint(); + } else if (what == "hint_string") { + r_ret = data[var_name].get_hint_string(); + } + return true; +} + +void BlackboardSource::_get_property_list(List *p_list) const { + for (const KeyValue &kv : data) { + String var_name = kv.key; + BBVariable var = kv.value; + + // * Editor + p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR)); + + // * Storage + p_list->push_back(PropertyInfo(Variant::STRING, "var/" + var_name + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::INT, "var/" + var_name + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(var.get_type(), "var/" + var_name + "/value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::INT, "var/" + var_name + "/hint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::STRING, "var/" + var_name + "/hint_string", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + } +} + +void BlackboardSource::set_base_source(const Ref &p_base) { + base = p_base; + sync_base(); + emit_changed(); +} + void BlackboardSource::set_value(const String &p_name, const Variant &p_value) { ERR_FAIL_COND(!data.has(p_name)); data.get(p_name).set_value(p_value); @@ -83,3 +170,10 @@ void BlackboardSource::populate_blackboard(const Ref &p_blackboard, p_blackboard->add_var(kv.key, kv.value.duplicate()); } } + +BlackboardSource::BlackboardSource() { + // TODO: REMOVE ALL BELOW + data.insert("speed", BBVariable(Variant::Type::FLOAT, PropertyHint::PROPERTY_HINT_NONE, "")); + data.insert("limit_speed", BBVariable(Variant::Type::BOOL, PropertyHint::PROPERTY_HINT_NONE, "")); + data.insert("about", BBVariable(Variant::Type::STRING, PropertyHint::PROPERTY_HINT_MULTILINE_TEXT, "")); +} diff --git a/blackboard/blackboard_source.h b/blackboard/blackboard_source.h index 38b4b5c8..56eaf9c6 100644 --- a/blackboard/blackboard_source.h +++ b/blackboard/blackboard_source.h @@ -25,7 +25,15 @@ class BlackboardSource : public Resource { Ref base; // HashMap overrides; +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List *p_list) const; + public: + void set_base_source(const Ref &p_base); + Ref get_base_source() const { return base; } + void set_value(const String &p_name, const Variant &p_value); Variant get_value(const String &p_name) const; void add_var(const String &p_name, const BBVariable &p_var); @@ -38,7 +46,7 @@ class BlackboardSource : public Resource { Ref create_blackboard(); void populate_blackboard(const Ref &p_blackboard, bool overwrite); - BlackboardSource() = default; + BlackboardSource(); }; #endif // BLACKBOARD_SOURCE_H diff --git a/bt/behavior_tree.cpp b/bt/behavior_tree.cpp index b6fc0d93..5508b6d2 100644 --- a/bt/behavior_tree.cpp +++ b/bt/behavior_tree.cpp @@ -47,6 +47,8 @@ Ref BehaviorTree::instantiate(Node *p_agent, const Ref &p_bl void BehaviorTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_description", "p_value"), &BehaviorTree::set_description); ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description); + ClassDB::bind_method(D_METHOD("set_blackboard_source", "p_source"), &BehaviorTree::set_blackboard_source); + ClassDB::bind_method(D_METHOD("get_blackboard_source"), &BehaviorTree::get_blackboard_source); ClassDB::bind_method(D_METHOD("set_root_task", "p_value"), &BehaviorTree::set_root_task); ClassDB::bind_method(D_METHOD("get_root_task"), &BehaviorTree::get_root_task); ClassDB::bind_method(D_METHOD("clone"), &BehaviorTree::clone); @@ -55,4 +57,9 @@ void BehaviorTree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_root_task", "get_root_task"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT), "set_blackboard_source", "get_blackboard_source"); +} + +BehaviorTree::BehaviorTree() { + blackboard_source = Ref(memnew(BlackboardSource)); } diff --git a/bt/behavior_tree.h b/bt/behavior_tree.h index ffd6f53a..3687524e 100644 --- a/bt/behavior_tree.h +++ b/bt/behavior_tree.h @@ -12,11 +12,11 @@ #ifndef BEHAVIOR_TREE_H #define BEHAVIOR_TREE_H +#include "../blackboard/blackboard_source.h" #include "tasks/bt_task.h" #ifdef LIMBOAI_MODULE #include "core/io/resource.h" -#include "modules/limboai/blackboard/blackboard.h" #endif // LIMBOAI_MODULE #ifdef LIMBOAI_GDEXTENSION @@ -29,6 +29,7 @@ class BehaviorTree : public Resource { private: String description; + Ref blackboard_source; Ref root_task; protected: @@ -39,6 +40,12 @@ class BehaviorTree : public Resource { virtual bool editor_can_reload_from_file() override { return false; } #endif + void set_blackboard_source(const Ref &p_source) { + blackboard_source = p_source; + emit_changed(); + } + Ref get_blackboard_source() const { return blackboard_source; } + void set_description(String p_value) { description = p_value; emit_changed(); @@ -54,6 +61,8 @@ class BehaviorTree : public Resource { Ref clone() const; void copy_other(const Ref &p_other); Ref instantiate(Node *p_agent, const Ref &p_blackboard) const; + + BehaviorTree(); }; #endif // BEHAVIOR_TREE_H diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index b5e6d4e4..93587913 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -63,11 +63,33 @@ void BTPlayer::_load_tree() { #endif } +void BTPlayer::_update_blackboard_source() { + if (behavior_tree.is_valid() && behavior_tree->get_blackboard_source().is_valid()) { + if (blackboard_source.is_null()) { + blackboard_source = Ref(memnew(BlackboardSource)); + } + if (blackboard_source == behavior_tree->get_blackboard_source()) { + blackboard_source->sync_base(); + } else { + blackboard_source->set_base_source(behavior_tree->get_blackboard_source()); + } + } +} + void BTPlayer::set_behavior_tree(const Ref &p_tree) { behavior_tree = p_tree; if (Engine::get_singleton()->is_editor_hint() == false && get_owner()) { _load_tree(); } + _update_blackboard_source(); +} + +void BTPlayer::set_blackboard_source(const Ref &p_source) { + blackboard_source = p_source; + if (blackboard_source.is_null()) { + blackboard_source = Ref(memnew(BlackboardSource)); + } + _update_blackboard_source(); } void BTPlayer::set_update_mode(UpdateMode p_mode) { diff --git a/bt/bt_player.h b/bt/bt_player.h index 70430d96..17238d81 100644 --- a/bt/bt_player.h +++ b/bt/bt_player.h @@ -47,6 +47,7 @@ class BTPlayer : public Node { Ref tree_instance; void _load_tree(); + void _update_blackboard_source(); protected: static void _bind_methods(); @@ -57,7 +58,7 @@ class BTPlayer : public Node { void set_behavior_tree(const Ref &p_tree); Ref get_behavior_tree() const { return behavior_tree; }; - void set_blackboard_source(const Ref &p_source) { blackboard_source = p_source; } + void set_blackboard_source(const Ref &p_source); Ref get_blackboard_source() const { return blackboard_source; } void set_update_mode(UpdateMode p_mode); diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index 2a181043..f71abb1a 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -26,6 +26,19 @@ #include #endif // LIMBOAI_GDEXTENSION +void BTState::_update_blackboard_source() { + if (behavior_tree.is_valid() && behavior_tree->get_blackboard_source().is_valid()) { + if (get_blackboard_source().is_null()) { + set_blackboard_source(Ref(memnew(BlackboardSource))); + } + if (get_blackboard_source() == behavior_tree->get_blackboard_source()) { + get_blackboard_source()->sync_base(); + } else { + get_blackboard_source()->set_base_source(behavior_tree->get_blackboard_source()); + } + } +} + void BTState::_setup() { ERR_FAIL_COND_MSG(behavior_tree.is_null(), "BTState: BehaviorTree is not assigned."); tree_instance = behavior_tree->instantiate(get_agent(), get_blackboard()); diff --git a/bt/bt_state.h b/bt/bt_state.h index b3baa74f..d492db2f 100644 --- a/bt/bt_state.h +++ b/bt/bt_state.h @@ -26,6 +26,8 @@ class BTState : public LimboState { String success_event; String failure_event; + void _update_blackboard_source(); + protected: static void _bind_methods(); diff --git a/register_types.cpp b/register_types.cpp index 4d6edf9c..181c8b96 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -47,6 +47,7 @@ #include "blackboard/bb_param/bb_vector4.h" #include "blackboard/bb_param/bb_vector4i.h" #include "blackboard/blackboard.h" +#include "blackboard/blackboard_source.h" #include "bt/behavior_tree.h" #include "bt/bt_player.h" #include "bt/bt_state.h" @@ -133,6 +134,7 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(LimboUtility); GDREGISTER_CLASS(Blackboard); + GDREGISTER_CLASS(BlackboardSource); GDREGISTER_CLASS(LimboState); GDREGISTER_CLASS(LimboHSM); diff --git a/util/limbo_compat.cpp b/util/limbo_compat.cpp index a02f34a8..1e04f86c 100644 --- a/util/limbo_compat.cpp +++ b/util/limbo_compat.cpp @@ -15,6 +15,7 @@ #ifdef TOOLS_ENABLED #include "core/io/resource.h" +#include "core/variant/variant.h" #include "editor/editor_node.h" #include "editor/plugins/script_editor_plugin.h" #endif // TOOLS_ENABLED @@ -85,6 +86,128 @@ Variant _GLOBAL_DEF(const PropertyInfo &p_info, const Variant &p_default, bool p // **** Shared +Variant VARIANT_DEFAULT(Variant::Type p_type) { + switch (p_type) { + case Variant::Type::NIL: { + return Variant(); + } break; + case Variant::Type::BOOL: { + return Variant(false); + } break; + case Variant::Type::INT: { + return Variant(0); + } break; + case Variant::Type::FLOAT: { + return Variant(0.0); + } break; + case Variant::Type::STRING: { + return Variant(""); + } break; + case Variant::Type::VECTOR2: { + return Variant(Vector2()); + } break; + case Variant::Type::VECTOR2I: { + return Variant(Vector2i()); + } break; + case Variant::Type::RECT2: { + return Variant(Rect2()); + } break; + case Variant::Type::RECT2I: { + return Variant(Rect2i()); + } break; + case Variant::Type::VECTOR3: { + return Variant(Vector3()); + } break; + case Variant::Type::VECTOR3I: { + return Variant(Vector3i()); + } break; + case Variant::Type::TRANSFORM2D: { + return Variant(Transform2D()); + } break; + case Variant::Type::VECTOR4: { + return Variant(Vector4()); + } break; + case Variant::Type::VECTOR4I: { + return Variant(Vector4i()); + } break; + case Variant::Type::PLANE: { + return Variant(Plane()); + } break; + case Variant::Type::QUATERNION: { + return Variant(Quaternion()); + } break; + case Variant::Type::AABB: { + return Variant(AABB()); + } break; + case Variant::Type::BASIS: { + return Variant(Basis()); + } break; + case Variant::Type::TRANSFORM3D: { + return Variant(Transform3D()); + } break; + case Variant::Type::PROJECTION: { + return Variant(Projection()); + } break; + case Variant::Type::COLOR: { + return Variant(Color()); + } break; + case Variant::Type::STRING_NAME: { + return Variant(StringName()); + } break; + case Variant::Type::NODE_PATH: { + return Variant(NodePath()); + } break; + case Variant::Type::RID: { + return Variant(RID()); + } break; + case Variant::Type::OBJECT: { + return Variant(); + } break; + case Variant::Type::CALLABLE: { + return Variant(); + } break; + case Variant::Type::SIGNAL: { + return Variant(); + } break; + case Variant::Type::DICTIONARY: { + return Variant(Dictionary()); + } break; + case Variant::Type::ARRAY: { + return Variant(Array()); + } break; + case Variant::Type::PACKED_BYTE_ARRAY: { + return Variant(PackedByteArray()); + } break; + case Variant::Type::PACKED_INT32_ARRAY: { + return Variant(PackedInt32Array()); + } break; + case Variant::Type::PACKED_INT64_ARRAY: { + return Variant(PackedInt64Array()); + } break; + case Variant::Type::PACKED_FLOAT32_ARRAY: { + return Variant(PackedFloat32Array()); + } break; + case Variant::Type::PACKED_FLOAT64_ARRAY: { + return Variant(PackedFloat64Array()); + } break; + case Variant::Type::PACKED_STRING_ARRAY: { + return Variant(PackedStringArray()); + } break; + case Variant::Type::PACKED_VECTOR2_ARRAY: { + return Variant(PackedVector2Array()); + } break; + case Variant::Type::PACKED_VECTOR3_ARRAY: { + return Variant(PackedVector3Array()); + } break; + case Variant::Type::PACKED_COLOR_ARRAY: { + return Variant(PackedColorArray()); + } break; + default: { + return Variant(); + } + } +} + #ifdef TOOLS_ENABLED void SHOW_DOC(const String &p_topic) { diff --git a/util/limbo_compat.h b/util/limbo_compat.h index a382f015..762aeaf4 100644 --- a/util/limbo_compat.h +++ b/util/limbo_compat.h @@ -218,6 +218,8 @@ inline void VARIANT_DELETE_IF_OBJECT(Variant m_variant) { } } +Variant VARIANT_DEFAULT(Variant::Type p_type); + #define PROJECT_CONFIG_FILE() GET_PROJECT_SETTINGS_DIR().path_join("limbo_ai.cfg") #define IS_RESOURCE_FILE(m_path) (m_path.begins_with("res://") && m_path.find("::") == -1) #define RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type) From dc40109ade366ee8b446ee189a1335591de45426 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 23 Jan 2024 15:56:30 +0100 Subject: [PATCH 04/15] Fix default object value issues --- bt/behavior_tree.cpp | 11 +++++++++-- bt/behavior_tree.h | 5 +---- bt/bt_player.cpp | 6 +----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bt/behavior_tree.cpp b/bt/behavior_tree.cpp index 5508b6d2..f06b3154 100644 --- a/bt/behavior_tree.cpp +++ b/bt/behavior_tree.cpp @@ -22,6 +22,14 @@ #include "godot_cpp/core/error_macros.hpp" #endif // ! LIMBOAI_GDEXTENSION +void BehaviorTree::set_blackboard_source(const Ref &p_source) { + blackboard_source = p_source; + if (blackboard_source.is_null()) { + blackboard_source = Ref(memnew(BlackboardSource)); + } + emit_changed(); +} + Ref BehaviorTree::clone() const { Ref copy = duplicate(false); copy->set_path(""); @@ -57,9 +65,8 @@ void BehaviorTree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_root_task", "get_root_task"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT), "set_blackboard_source", "get_blackboard_source"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_source", "get_blackboard_source"); } BehaviorTree::BehaviorTree() { - blackboard_source = Ref(memnew(BlackboardSource)); } diff --git a/bt/behavior_tree.h b/bt/behavior_tree.h index 3687524e..6b5305fc 100644 --- a/bt/behavior_tree.h +++ b/bt/behavior_tree.h @@ -40,10 +40,7 @@ class BehaviorTree : public Resource { virtual bool editor_can_reload_from_file() override { return false; } #endif - void set_blackboard_source(const Ref &p_source) { - blackboard_source = p_source; - emit_changed(); - } + void set_blackboard_source(const Ref &p_source); Ref get_blackboard_source() const { return blackboard_source; } void set_description(String p_value) { diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 93587913..36879160 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -86,9 +86,6 @@ void BTPlayer::set_behavior_tree(const Ref &p_tree) { void BTPlayer::set_blackboard_source(const Ref &p_source) { blackboard_source = p_source; - if (blackboard_source.is_null()) { - blackboard_source = Ref(memnew(BlackboardSource)); - } _update_blackboard_source(); } @@ -232,7 +229,7 @@ void BTPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,Manual"), "set_update_mode", "get_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "get_active"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_NONE, "Blackboard", 0), "set_blackboard", "get_blackboard"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT), "set_blackboard_source", "get_blackboard_source"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_source", "get_blackboard_source"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prefetch_nodepath_vars"), "set_prefetch_nodepath_vars", "get_prefetch_nodepath_vars"); BIND_ENUM_CONSTANT(IDLE); @@ -250,7 +247,6 @@ void BTPlayer::_bind_methods() { } BTPlayer::BTPlayer() { - blackboard = Ref(memnew(Blackboard)); } BTPlayer::~BTPlayer() { From f84127657b1cacef1ec3286c97124461f7e0c8d8 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 23 Jan 2024 16:22:10 +0100 Subject: [PATCH 05/15] Fix crash while initializing blackboard in the BTPlayer node --- blackboard/blackboard.cpp | 9 +++++---- bt/bt_player.cpp | 6 +++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/blackboard/blackboard.cpp b/blackboard/blackboard.cpp index d0769b7d..68727a70 100644 --- a/blackboard/blackboard.cpp +++ b/blackboard/blackboard.cpp @@ -70,11 +70,12 @@ void Blackboard::add_var(const String &p_name, const BBVariable &p_var) { void Blackboard::prefetch_nodepath_vars(Node *p_node) { ERR_FAIL_COND(p_node == nullptr); - for (KeyValue &kv : data) { - if (kv.value.get_value().get_type() == Variant::NODE_PATH) { - Node *fetched_node = p_node->get_node_or_null(kv.value.get_value()); + for (const KeyValue &kv : data) { + BBVariable var = kv.value; + if (var.get_value().get_type() == Variant::NODE_PATH) { + Node *fetched_node = p_node->get_node_or_null(var.get_value()); if (fetched_node != nullptr) { - kv.value.set_value(fetched_node); + var.set_value(fetched_node); } } } diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 36879160..6ddf2708 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -179,8 +179,11 @@ void BTPlayer::_notification(int p_notification) { } break; case NOTIFICATION_READY: { if (!Engine::get_singleton()->is_editor_hint()) { + if (blackboard.is_null()) { + blackboard = Ref(memnew(Blackboard)); + } if (blackboard_source.is_valid()) { - blackboard = blackboard_source->create_blackboard(); + blackboard_source->populate_blackboard(blackboard, false); } if (behavior_tree.is_valid()) { _load_tree(); @@ -247,6 +250,7 @@ void BTPlayer::_bind_methods() { } BTPlayer::BTPlayer() { + blackboard = Ref(memnew(Blackboard)); } BTPlayer::~BTPlayer() { From b04f7530e48b2f0dde33fd573cfe66d1dbb2d5a0 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 23 Jan 2024 17:54:20 +0100 Subject: [PATCH 06/15] Add a mechanism for overriding and resetting variables in a derived blackboard source --- blackboard/blackboard_source.cpp | 19 +++++++++++++++++-- blackboard/blackboard_source.h | 10 ++++++++-- bt/bt_player.cpp | 2 +- bt/bt_state.cpp | 2 +- bt/tasks/decorators/bt_new_scope.cpp | 2 +- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/blackboard/blackboard_source.cpp b/blackboard/blackboard_source.cpp index 9d1b718a..e9a33e7e 100644 --- a/blackboard/blackboard_source.cpp +++ b/blackboard/blackboard_source.cpp @@ -92,9 +92,21 @@ void BlackboardSource::_get_property_list(List *p_list) const { } } +bool BlackboardSource::_property_can_revert(const StringName &p_name) const { + return base.is_valid() && base->data.has(p_name); +} + +bool BlackboardSource::_property_get_revert(const StringName &p_name, Variant &r_property) const { + if (base->data.has(p_name)) { + r_property = base->data[p_name].get_value(); + return true; + } + return false; +} + void BlackboardSource::set_base_source(const Ref &p_base) { base = p_base; - sync_base(); + sync_with_base_source(); emit_changed(); } @@ -133,7 +145,10 @@ PackedStringArray BlackboardSource::list_vars() const { return ret; } -void BlackboardSource::sync_base() { +void BlackboardSource::sync_with_base_source() { + if (base.is_null()) { + return; + } for (const KeyValue &kv : base->data) { if (!data.has(kv.key)) { data.insert(kv.key, kv.value.duplicate()); diff --git a/blackboard/blackboard_source.h b/blackboard/blackboard_source.h index 56eaf9c6..5f771f4f 100644 --- a/blackboard/blackboard_source.h +++ b/blackboard/blackboard_source.h @@ -22,13 +22,17 @@ class BlackboardSource : public Resource { private: HashMap data; + + // When base is not null, the source is considered to be derived from the base source. + // A derived source can only have variables that exist in the base source. Ref base; - // HashMap overrides; protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List *p_list) const; + bool _property_can_revert(const StringName &p_name) const; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; public: void set_base_source(const Ref &p_base); @@ -42,7 +46,9 @@ class BlackboardSource : public Resource { PackedStringArray list_vars() const; bool is_empty() const { return data.is_empty(); } - void sync_base(); + void sync_with_base_source(); + bool is_derived() { return base.is_valid(); } + Ref create_blackboard(); void populate_blackboard(const Ref &p_blackboard, bool overwrite); diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 6ddf2708..236ed634 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -69,7 +69,7 @@ void BTPlayer::_update_blackboard_source() { blackboard_source = Ref(memnew(BlackboardSource)); } if (blackboard_source == behavior_tree->get_blackboard_source()) { - blackboard_source->sync_base(); + blackboard_source->sync_with_base_source(); } else { blackboard_source->set_base_source(behavior_tree->get_blackboard_source()); } diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index f71abb1a..fc66c34a 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -32,7 +32,7 @@ void BTState::_update_blackboard_source() { set_blackboard_source(Ref(memnew(BlackboardSource))); } if (get_blackboard_source() == behavior_tree->get_blackboard_source()) { - get_blackboard_source()->sync_base(); + get_blackboard_source()->sync_with_base_source(); } else { get_blackboard_source()->set_base_source(behavior_tree->get_blackboard_source()); } diff --git a/bt/tasks/decorators/bt_new_scope.cpp b/bt/tasks/decorators/bt_new_scope.cpp index faafd9f7..b501fc5b 100644 --- a/bt/tasks/decorators/bt_new_scope.cpp +++ b/bt/tasks/decorators/bt_new_scope.cpp @@ -36,5 +36,5 @@ void BTNewScope::_bind_methods() { ClassDB::bind_method(D_METHOD("set_blackboard_source", "p_source"), &BTNewScope::set_blackboard_source); ClassDB::bind_method(D_METHOD("get_blackboard_source"), &BTNewScope::get_blackboard_source); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT), "set_blackboard_source", "get_blackboard_source"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_source", "get_blackboard_source"); } From 20a995d1d22335e316f62b907f4065b70aff2cbc Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 23 Jan 2024 20:02:23 +0100 Subject: [PATCH 07/15] Rename BlackboardSource => BlackboardPlan Because it's a better name ;) --- ...ckboard_source.cpp => blackboard_plan.cpp} | 56 +++++++++++-------- ...{blackboard_source.h => blackboard_plan.h} | 26 ++++----- bt/behavior_tree.cpp | 14 ++--- bt/behavior_tree.h | 8 +-- bt/bt_player.cpp | 35 ++++++------ bt/bt_player.h | 10 ++-- bt/bt_state.cpp | 14 ++--- bt/bt_state.h | 2 +- bt/tasks/decorators/bt_new_scope.cpp | 10 ++-- bt/tasks/decorators/bt_new_scope.h | 8 +-- hsm/limbo_state.cpp | 10 ++-- hsm/limbo_state.h | 8 +-- register_types.cpp | 4 +- 13 files changed, 109 insertions(+), 96 deletions(-) rename blackboard/{blackboard_source.cpp => blackboard_plan.cpp} (73%) rename blackboard/{blackboard_source.h => blackboard_plan.h} (70%) diff --git a/blackboard/blackboard_source.cpp b/blackboard/blackboard_plan.cpp similarity index 73% rename from blackboard/blackboard_source.cpp rename to blackboard/blackboard_plan.cpp index e9a33e7e..b7b2508f 100644 --- a/blackboard/blackboard_source.cpp +++ b/blackboard/blackboard_plan.cpp @@ -1,5 +1,5 @@ /** - * blackboard_source.cpp + * blackboard_plan.cpp * ============================================================================= * Copyright 2021-2024 Serhii Snitsaruk * @@ -9,9 +9,9 @@ * ============================================================================= */ -#include "blackboard_source.h" +#include "blackboard_plan.h" -bool BlackboardSource::_set(const StringName &p_name, const Variant &p_value) { +bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) { String prop_name = p_name; // * Editor @@ -46,7 +46,7 @@ bool BlackboardSource::_set(const StringName &p_name, const Variant &p_value) { return false; } -bool BlackboardSource::_get(const StringName &p_name, Variant &r_ret) const { +bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { String prop_name = p_name; // * Editor @@ -75,7 +75,7 @@ bool BlackboardSource::_get(const StringName &p_name, Variant &r_ret) const { return true; } -void BlackboardSource::_get_property_list(List *p_list) const { +void BlackboardPlan::_get_property_list(List *p_list) const { for (const KeyValue &kv : data) { String var_name = kv.key; BBVariable var = kv.value; @@ -92,11 +92,11 @@ void BlackboardSource::_get_property_list(List *p_list) const { } } -bool BlackboardSource::_property_can_revert(const StringName &p_name) const { +bool BlackboardPlan::_property_can_revert(const StringName &p_name) const { return base.is_valid() && base->data.has(p_name); } -bool BlackboardSource::_property_get_revert(const StringName &p_name, Variant &r_property) const { +bool BlackboardPlan::_property_get_revert(const StringName &p_name, Variant &r_property) const { if (base->data.has(p_name)) { r_property = base->data[p_name].get_value(); return true; @@ -104,40 +104,40 @@ bool BlackboardSource::_property_get_revert(const StringName &p_name, Variant &r return false; } -void BlackboardSource::set_base_source(const Ref &p_base) { +void BlackboardPlan::set_base_plan(const Ref &p_base) { base = p_base; - sync_with_base_source(); + sync_with_base_plan(); emit_changed(); } -void BlackboardSource::set_value(const String &p_name, const Variant &p_value) { +void BlackboardPlan::set_value(const String &p_name, const Variant &p_value) { ERR_FAIL_COND(!data.has(p_name)); data.get(p_name).set_value(p_value); } -Variant BlackboardSource::get_value(const String &p_name) const { +Variant BlackboardPlan::get_value(const String &p_name) const { ERR_FAIL_COND_V(!data.has(p_name), Variant()); return data.get(p_name).get_value(); } -void BlackboardSource::add_var(const String &p_name, const BBVariable &p_var) { +void BlackboardPlan::add_var(const String &p_name, const BBVariable &p_var) { ERR_FAIL_COND(data.has(p_name)); ERR_FAIL_COND(base.is_valid()); data.insert(p_name, p_var); } -void BlackboardSource::remove_var(const String &p_name) { +void BlackboardPlan::remove_var(const String &p_name) { ERR_FAIL_COND(!data.has(p_name)); ERR_FAIL_COND(base.is_valid()); data.erase(p_name); } -BBVariable BlackboardSource::get_var(const String &p_name) { +BBVariable BlackboardPlan::get_var(const String &p_name) { ERR_FAIL_COND_V(!data.has(p_name), BBVariable()); return data.get(p_name); } -PackedStringArray BlackboardSource::list_vars() const { +PackedStringArray BlackboardPlan::list_vars() const { PackedStringArray ret; for (const KeyValue &kv : data) { ret.append(kv.key); @@ -145,10 +145,12 @@ PackedStringArray BlackboardSource::list_vars() const { return ret; } -void BlackboardSource::sync_with_base_source() { +void BlackboardPlan::sync_with_base_plan() { if (base.is_null()) { return; } + + // Sync variables with the base plan. for (const KeyValue &kv : base->data) { if (!data.has(kv.key)) { data.insert(kv.key, kv.value.duplicate()); @@ -163,9 +165,16 @@ void BlackboardSource::sync_with_base_source() { var.set_value(kv.value.get_value()); } } + + // Erase variables that do not exist in the base plan. + for (const KeyValue &kv : data) { + if (!base->data.has(kv.key)) { + data.erase(kv.key); + } + } } -Ref BlackboardSource::create_blackboard() { +Ref BlackboardPlan::create_blackboard() { Ref bb = memnew(Blackboard); for (const KeyValue &kv : data) { bb->add_var(kv.key, kv.value.duplicate()); @@ -173,7 +182,7 @@ Ref BlackboardSource::create_blackboard() { return bb; } -void BlackboardSource::populate_blackboard(const Ref &p_blackboard, bool overwrite) { +void BlackboardPlan::populate_blackboard(const Ref &p_blackboard, bool overwrite) { for (const KeyValue &kv : data) { if (p_blackboard->has_var(kv.key)) { if (overwrite) { @@ -186,9 +195,12 @@ void BlackboardSource::populate_blackboard(const Ref &p_blackboard, } } -BlackboardSource::BlackboardSource() { - // TODO: REMOVE ALL BELOW - data.insert("speed", BBVariable(Variant::Type::FLOAT, PropertyHint::PROPERTY_HINT_NONE, "")); - data.insert("limit_speed", BBVariable(Variant::Type::BOOL, PropertyHint::PROPERTY_HINT_NONE, "")); +BlackboardPlan::BlackboardPlan() { + // TODO: REMOVE THE TEST DATA BELOW. + data.insert("speed", BBVariable(Variant::Type::FLOAT)); + data["speed"].set_value(200.0); + data.insert("limit_speed", BBVariable(Variant::Type::BOOL)); + data["limit_speed"].set_value(500.0); data.insert("about", BBVariable(Variant::Type::STRING, PropertyHint::PROPERTY_HINT_MULTILINE_TEXT, "")); + data["about"].set_value("Hello, World!"); } diff --git a/blackboard/blackboard_source.h b/blackboard/blackboard_plan.h similarity index 70% rename from blackboard/blackboard_source.h rename to blackboard/blackboard_plan.h index 5f771f4f..6c2a6b7a 100644 --- a/blackboard/blackboard_source.h +++ b/blackboard/blackboard_plan.h @@ -1,5 +1,5 @@ /** - * blackboard_source.h + * blackboard_plan.h * ============================================================================= * Copyright 2021-2024 Serhii Snitsaruk * @@ -9,23 +9,23 @@ * ============================================================================= */ -#ifndef BLACKBOARD_SOURCE_H -#define BLACKBOARD_SOURCE_H +#ifndef BLACKBOARD_PLAN_H +#define BLACKBOARD_PLAN_H #include "core/io/resource.h" #include "bb_variable.h" #include "blackboard.h" -class BlackboardSource : public Resource { - GDCLASS(BlackboardSource, Resource); +class BlackboardPlan : public Resource { + GDCLASS(BlackboardPlan, Resource); private: HashMap data; - // When base is not null, the source is considered to be derived from the base source. - // A derived source can only have variables that exist in the base source. - Ref base; + // When base is not null, the plan is considered to be derived from the base plan. + // A derived plan can only have variables that exist in the base plan. + Ref base; protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -35,8 +35,8 @@ class BlackboardSource : public Resource { bool _property_get_revert(const StringName &p_name, Variant &r_property) const; public: - void set_base_source(const Ref &p_base); - Ref get_base_source() const { return base; } + void set_base_plan(const Ref &p_base); + Ref get_base_plan() const { return base; } void set_value(const String &p_name, const Variant &p_value); Variant get_value(const String &p_name) const; @@ -46,13 +46,13 @@ class BlackboardSource : public Resource { PackedStringArray list_vars() const; bool is_empty() const { return data.is_empty(); } - void sync_with_base_source(); + void sync_with_base_plan(); bool is_derived() { return base.is_valid(); } Ref create_blackboard(); void populate_blackboard(const Ref &p_blackboard, bool overwrite); - BlackboardSource(); + BlackboardPlan(); }; -#endif // BLACKBOARD_SOURCE_H +#endif // BLACKBOARD_PLAN_H diff --git a/bt/behavior_tree.cpp b/bt/behavior_tree.cpp index f06b3154..3c425d7d 100644 --- a/bt/behavior_tree.cpp +++ b/bt/behavior_tree.cpp @@ -22,10 +22,10 @@ #include "godot_cpp/core/error_macros.hpp" #endif // ! LIMBOAI_GDEXTENSION -void BehaviorTree::set_blackboard_source(const Ref &p_source) { - blackboard_source = p_source; - if (blackboard_source.is_null()) { - blackboard_source = Ref(memnew(BlackboardSource)); +void BehaviorTree::set_blackboard_plan(const Ref &p_plan) { + blackboard_plan = p_plan; + if (blackboard_plan.is_null()) { + blackboard_plan = Ref(memnew(BlackboardPlan)); } emit_changed(); } @@ -55,8 +55,8 @@ Ref BehaviorTree::instantiate(Node *p_agent, const Ref &p_bl void BehaviorTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_description", "p_value"), &BehaviorTree::set_description); ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description); - ClassDB::bind_method(D_METHOD("set_blackboard_source", "p_source"), &BehaviorTree::set_blackboard_source); - ClassDB::bind_method(D_METHOD("get_blackboard_source"), &BehaviorTree::get_blackboard_source); + ClassDB::bind_method(D_METHOD("set_blackboard_plan", "p_plan"), &BehaviorTree::set_blackboard_plan); + ClassDB::bind_method(D_METHOD("get_blackboard_plan"), &BehaviorTree::get_blackboard_plan); ClassDB::bind_method(D_METHOD("set_root_task", "p_value"), &BehaviorTree::set_root_task); ClassDB::bind_method(D_METHOD("get_root_task"), &BehaviorTree::get_root_task); ClassDB::bind_method(D_METHOD("clone"), &BehaviorTree::clone); @@ -65,7 +65,7 @@ void BehaviorTree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_root_task", "get_root_task"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_source", "get_blackboard_source"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_plan", "get_blackboard_plan"); } BehaviorTree::BehaviorTree() { diff --git a/bt/behavior_tree.h b/bt/behavior_tree.h index 6b5305fc..a92fa563 100644 --- a/bt/behavior_tree.h +++ b/bt/behavior_tree.h @@ -12,7 +12,7 @@ #ifndef BEHAVIOR_TREE_H #define BEHAVIOR_TREE_H -#include "../blackboard/blackboard_source.h" +#include "../blackboard/blackboard_plan.h" #include "tasks/bt_task.h" #ifdef LIMBOAI_MODULE @@ -29,7 +29,7 @@ class BehaviorTree : public Resource { private: String description; - Ref blackboard_source; + Ref blackboard_plan; Ref root_task; protected: @@ -40,8 +40,8 @@ class BehaviorTree : public Resource { virtual bool editor_can_reload_from_file() override { return false; } #endif - void set_blackboard_source(const Ref &p_source); - Ref get_blackboard_source() const { return blackboard_source; } + void set_blackboard_plan(const Ref &p_plan); + Ref get_blackboard_plan() const { return blackboard_plan; } void set_description(String p_value) { description = p_value; diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 236ed634..b5fc8251 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -63,15 +63,16 @@ void BTPlayer::_load_tree() { #endif } -void BTPlayer::_update_blackboard_source() { - if (behavior_tree.is_valid() && behavior_tree->get_blackboard_source().is_valid()) { - if (blackboard_source.is_null()) { - blackboard_source = Ref(memnew(BlackboardSource)); - } - if (blackboard_source == behavior_tree->get_blackboard_source()) { - blackboard_source->sync_with_base_source(); +void BTPlayer::_update_blackboard_plan() { + if (blackboard_plan.is_null()) { + blackboard_plan = Ref(memnew(BlackboardPlan)); + } + + if (behavior_tree.is_valid()) { + if (blackboard_plan == behavior_tree->get_blackboard_plan()) { + blackboard_plan->sync_with_base_plan(); } else { - blackboard_source->set_base_source(behavior_tree->get_blackboard_source()); + blackboard_plan->set_base_plan(behavior_tree->get_blackboard_plan()); } } } @@ -81,12 +82,12 @@ void BTPlayer::set_behavior_tree(const Ref &p_tree) { if (Engine::get_singleton()->is_editor_hint() == false && get_owner()) { _load_tree(); } - _update_blackboard_source(); + _update_blackboard_plan(); } -void BTPlayer::set_blackboard_source(const Ref &p_source) { - blackboard_source = p_source; - _update_blackboard_source(); +void BTPlayer::set_blackboard_plan(const Ref &p_plan) { + blackboard_plan = p_plan; + _update_blackboard_plan(); } void BTPlayer::set_update_mode(UpdateMode p_mode) { @@ -182,8 +183,8 @@ void BTPlayer::_notification(int p_notification) { if (blackboard.is_null()) { blackboard = Ref(memnew(Blackboard)); } - if (blackboard_source.is_valid()) { - blackboard_source->populate_blackboard(blackboard, false); + if (blackboard_plan.is_valid()) { + blackboard_plan->populate_blackboard(blackboard, false); } if (behavior_tree.is_valid()) { _load_tree(); @@ -221,8 +222,8 @@ void BTPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_prefetch_nodepath_vars", "p_value"), &BTPlayer::set_prefetch_nodepath_vars); ClassDB::bind_method(D_METHOD("get_prefetch_nodepath_vars"), &BTPlayer::get_prefetch_nodepath_vars); - ClassDB::bind_method(D_METHOD("set_blackboard_source", "p_blackboard"), &BTPlayer::set_blackboard_source); - ClassDB::bind_method(D_METHOD("get_blackboard_source"), &BTPlayer::get_blackboard_source); + ClassDB::bind_method(D_METHOD("set_blackboard_plan", "p_plan"), &BTPlayer::set_blackboard_plan); + ClassDB::bind_method(D_METHOD("get_blackboard_plan"), &BTPlayer::get_blackboard_plan); ClassDB::bind_method(D_METHOD("update", "p_delta"), &BTPlayer::update); ClassDB::bind_method(D_METHOD("restart"), &BTPlayer::restart); @@ -232,7 +233,7 @@ void BTPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,Manual"), "set_update_mode", "get_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "get_active"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_NONE, "Blackboard", 0), "set_blackboard", "get_blackboard"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_source", "get_blackboard_source"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_plan", "get_blackboard_plan"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prefetch_nodepath_vars"), "set_prefetch_nodepath_vars", "get_prefetch_nodepath_vars"); BIND_ENUM_CONSTANT(IDLE); diff --git a/bt/bt_player.h b/bt/bt_player.h index 17238d81..703596e9 100644 --- a/bt/bt_player.h +++ b/bt/bt_player.h @@ -13,7 +13,7 @@ #define BT_PLAYER_H #include "../blackboard/blackboard.h" -#include "../blackboard/blackboard_source.h" +#include "../blackboard/blackboard_plan.h" #include "behavior_tree.h" #include "tasks/bt_task.h" @@ -37,7 +37,7 @@ class BTPlayer : public Node { private: Ref behavior_tree; - Ref blackboard_source; + Ref blackboard_plan; UpdateMode update_mode = UpdateMode::PHYSICS; bool active = true; Ref blackboard; @@ -47,7 +47,7 @@ class BTPlayer : public Node { Ref tree_instance; void _load_tree(); - void _update_blackboard_source(); + void _update_blackboard_plan(); protected: static void _bind_methods(); @@ -58,8 +58,8 @@ class BTPlayer : public Node { void set_behavior_tree(const Ref &p_tree); Ref get_behavior_tree() const { return behavior_tree; }; - void set_blackboard_source(const Ref &p_source); - Ref get_blackboard_source() const { return blackboard_source; } + void set_blackboard_plan(const Ref &p_plan); + Ref get_blackboard_plan() const { return blackboard_plan; } void set_update_mode(UpdateMode p_mode); UpdateMode get_update_mode() const { return update_mode; } diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index fc66c34a..d6406f6a 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -26,15 +26,15 @@ #include #endif // LIMBOAI_GDEXTENSION -void BTState::_update_blackboard_source() { - if (behavior_tree.is_valid() && behavior_tree->get_blackboard_source().is_valid()) { - if (get_blackboard_source().is_null()) { - set_blackboard_source(Ref(memnew(BlackboardSource))); +void BTState::_update_blackboard_plan() { + if (behavior_tree.is_valid() && behavior_tree->get_blackboard_plan().is_valid()) { + if (get_blackboard_plan().is_null()) { + set_blackboard_plan(Ref(memnew(BlackboardPlan))); } - if (get_blackboard_source() == behavior_tree->get_blackboard_source()) { - get_blackboard_source()->sync_with_base_source(); + if (get_blackboard_plan() == behavior_tree->get_blackboard_plan()) { + get_blackboard_plan()->sync_with_base_plan(); } else { - get_blackboard_source()->set_base_source(behavior_tree->get_blackboard_source()); + get_blackboard_plan()->set_base_plan(behavior_tree->get_blackboard_plan()); } } } diff --git a/bt/bt_state.h b/bt/bt_state.h index d492db2f..b8c531d8 100644 --- a/bt/bt_state.h +++ b/bt/bt_state.h @@ -26,7 +26,7 @@ class BTState : public LimboState { String success_event; String failure_event; - void _update_blackboard_source(); + void _update_blackboard_plan(); protected: static void _bind_methods(); diff --git a/bt/tasks/decorators/bt_new_scope.cpp b/bt/tasks/decorators/bt_new_scope.cpp index b501fc5b..24e38d85 100644 --- a/bt/tasks/decorators/bt_new_scope.cpp +++ b/bt/tasks/decorators/bt_new_scope.cpp @@ -16,8 +16,8 @@ void BTNewScope::initialize(Node *p_agent, const Ref &p_blackboard) ERR_FAIL_COND(p_blackboard == nullptr); Ref bb; - if (blackboard_source.is_valid()) { - bb = blackboard_source->create_blackboard(); + if (blackboard_plan.is_valid()) { + bb = blackboard_plan->create_blackboard(); } else { bb = Ref(memnew(Blackboard)); } @@ -33,8 +33,8 @@ BT::Status BTNewScope::_tick(double p_delta) { } void BTNewScope::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_blackboard_source", "p_source"), &BTNewScope::set_blackboard_source); - ClassDB::bind_method(D_METHOD("get_blackboard_source"), &BTNewScope::get_blackboard_source); + ClassDB::bind_method(D_METHOD("set_blackboard_plan", "p_plan"), &BTNewScope::set_blackboard_plan); + ClassDB::bind_method(D_METHOD("get_blackboard_plan"), &BTNewScope::get_blackboard_plan); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_source", "get_blackboard_source"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_plan", "get_blackboard_plan"); } diff --git a/bt/tasks/decorators/bt_new_scope.h b/bt/tasks/decorators/bt_new_scope.h index 84109907..c5644a19 100644 --- a/bt/tasks/decorators/bt_new_scope.h +++ b/bt/tasks/decorators/bt_new_scope.h @@ -14,20 +14,20 @@ #include "../bt_decorator.h" -#include "../../../blackboard/blackboard_source.h" +#include "../../../blackboard/blackboard_plan.h" class BTNewScope : public BTDecorator { GDCLASS(BTNewScope, BTDecorator); TASK_CATEGORY(Decorators); private: - Ref blackboard_source; + Ref blackboard_plan; protected: static void _bind_methods(); - void set_blackboard_source(const Ref &p_source) { blackboard_source = p_source; } - Ref get_blackboard_source() const { return blackboard_source; } + void set_blackboard_plan(const Ref &p_plan) { blackboard_plan = p_plan; } + Ref get_blackboard_plan() const { return blackboard_plan; } virtual Status _tick(double p_delta) override; diff --git a/hsm/limbo_state.cpp b/hsm/limbo_state.cpp index d7517892..7539ec38 100644 --- a/hsm/limbo_state.cpp +++ b/hsm/limbo_state.cpp @@ -69,8 +69,8 @@ void LimboState::_initialize(Node *p_agent, const Ref &p_blackboard) agent = p_agent; if (!p_blackboard.is_null()) { - if (blackboard_source.is_valid() && !blackboard_source->is_empty()) { - blackboard = blackboard_source->create_blackboard(); + if (blackboard_plan.is_valid() && !blackboard_plan->is_empty()) { + blackboard = blackboard_plan->create_blackboard(); blackboard->set_parent_scope(p_blackboard); } else { blackboard = p_blackboard; @@ -180,8 +180,8 @@ void LimboState::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_guard"), &LimboState::clear_guard); ClassDB::bind_method(D_METHOD("get_blackboard"), &LimboState::get_blackboard); - ClassDB::bind_method(D_METHOD("set_blackboard_source", "p_source"), &LimboState::set_blackboard_source); - ClassDB::bind_method(D_METHOD("get_blackboard_source"), &LimboState::get_blackboard_source); + ClassDB::bind_method(D_METHOD("set_blackboard_plan", "p_plan"), &LimboState::set_blackboard_plan); + ClassDB::bind_method(D_METHOD("get_blackboard_plan"), &LimboState::get_blackboard_plan); #ifdef LIMBOAI_MODULE GDVIRTUAL_BIND(_setup); @@ -195,7 +195,7 @@ void LimboState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "EVENT_FINISHED", PROPERTY_HINT_NONE, "", 0), "", "event_finished"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "agent", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_agent", "get_agent"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_RESOURCE_TYPE, "Blackboard", 0), "", "get_blackboard"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_source", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardSource", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_blackboard_source", "get_blackboard_source"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_blackboard_plan", "get_blackboard_plan"); ADD_SIGNAL(MethodInfo("setup")); ADD_SIGNAL(MethodInfo("entered")); diff --git a/hsm/limbo_state.h b/hsm/limbo_state.h index f9d84cda..5f337a12 100644 --- a/hsm/limbo_state.h +++ b/hsm/limbo_state.h @@ -13,7 +13,7 @@ #define LIMBO_STATE_H #include "../blackboard/blackboard.h" -#include "../blackboard/blackboard_source.h" +#include "../blackboard/blackboard_plan.h" #include "../util/limbo_string_names.h" @@ -38,7 +38,7 @@ class LimboState : public Node { GDCLASS(LimboState, Node); private: - Ref blackboard_source; + Ref blackboard_plan; Node *agent; Ref blackboard; HashMap handlers; @@ -70,8 +70,8 @@ class LimboState : public Node { void add_event_handler(const String &p_event, const Callable &p_handler); public: - void set_blackboard_source(const Ref p_source) { blackboard_source = p_source; } - Ref get_blackboard_source() const { return blackboard_source; } + void set_blackboard_plan(const Ref p_plan) { blackboard_plan = p_plan; } + Ref get_blackboard_plan() const { return blackboard_plan; } Ref get_blackboard() const { return blackboard; } diff --git a/register_types.cpp b/register_types.cpp index 181c8b96..ad8d0a0e 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -47,7 +47,7 @@ #include "blackboard/bb_param/bb_vector4.h" #include "blackboard/bb_param/bb_vector4i.h" #include "blackboard/blackboard.h" -#include "blackboard/blackboard_source.h" +#include "blackboard/blackboard_plan.h" #include "bt/behavior_tree.h" #include "bt/bt_player.h" #include "bt/bt_state.h" @@ -134,7 +134,7 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(LimboUtility); GDREGISTER_CLASS(Blackboard); - GDREGISTER_CLASS(BlackboardSource); + GDREGISTER_CLASS(BlackboardPlan); GDREGISTER_CLASS(LimboState); GDREGISTER_CLASS(LimboHSM); From 15e0323919cbd7bf3f1fdf7eeef6f20420807ebf Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Wed, 24 Jan 2024 23:11:09 +0100 Subject: [PATCH 08/15] Implement BlackboardPlan editor --- blackboard/blackboard_plan.cpp | 77 +++++- blackboard/blackboard_plan.h | 13 +- bt/bt_player.cpp | 16 +- editor/blackboard_plan_editor.cpp | 411 ++++++++++++++++++++++++++++++ editor/blackboard_plan_editor.h | 95 +++++++ editor/limbo_ai_editor_plugin.cpp | 6 +- util/limbo_string_names.cpp | 11 + util/limbo_string_names.h | 11 + util/limbo_utility.cpp | 123 +++++++++ util/limbo_utility.h | 2 + 10 files changed, 745 insertions(+), 20 deletions(-) create mode 100644 editor/blackboard_plan_editor.cpp create mode 100644 editor/blackboard_plan_editor.h diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index b7b2508f..a02869c0 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -108,11 +108,13 @@ void BlackboardPlan::set_base_plan(const Ref &p_base) { base = p_base; sync_with_base_plan(); emit_changed(); + notify_property_list_changed(); } void BlackboardPlan::set_value(const String &p_name, const Variant &p_value) { ERR_FAIL_COND(!data.has(p_name)); data.get(p_name).set_value(p_value); + emit_changed(); } Variant BlackboardPlan::get_value(const String &p_name) const { @@ -124,12 +126,16 @@ void BlackboardPlan::add_var(const String &p_name, const BBVariable &p_var) { ERR_FAIL_COND(data.has(p_name)); ERR_FAIL_COND(base.is_valid()); data.insert(p_name, p_var); + notify_property_list_changed(); + emit_changed(); } void BlackboardPlan::remove_var(const String &p_name) { ERR_FAIL_COND(!data.has(p_name)); ERR_FAIL_COND(base.is_valid()); data.erase(p_name); + notify_property_list_changed(); + emit_changed(); } BBVariable BlackboardPlan::get_var(const String &p_name) { @@ -137,6 +143,22 @@ BBVariable BlackboardPlan::get_var(const String &p_name) { return data.get(p_name); } +Pair BlackboardPlan::get_var_by_index(int p_index) { + Pair ret; + ERR_FAIL_INDEX_V(p_index, (int)data.size(), ret); + + int i = 0; + for (const KeyValue &kv : data) { + if (i == p_index) { + ret.first = kv.key; + ret.second = kv.value; + } + i += 1; + } + + return ret; +} + PackedStringArray BlackboardPlan::list_vars() const { PackedStringArray ret; for (const KeyValue &kv : data) { @@ -145,24 +167,66 @@ PackedStringArray BlackboardPlan::list_vars() const { return ret; } +String BlackboardPlan::get_var_name(const BBVariable &p_var) const { + for (const KeyValue &kv : data) { + if (kv.value == p_var) { + return kv.key; + } + } + return String(); +} + +void BlackboardPlan::rename_var(const String &p_name, const String &p_new_name) { + ERR_FAIL_COND(p_new_name.is_empty()); + ERR_FAIL_COND(data.has(p_new_name)); + ERR_FAIL_COND(!data.has(p_name)); + + data.replace_key(p_name, p_new_name); + notify_property_list_changed(); + emit_changed(); +} + +void BlackboardPlan::swap_vars(int p_idx_a, int p_idx_b) { + ERR_FAIL_INDEX(p_idx_a, (int)data.size()); + ERR_FAIL_INDEX(p_idx_b, (int)data.size()); + + Pair a = get_var_by_index(p_idx_a); + Pair b = get_var_by_index(p_idx_b); + + data.replace_key(a.first, "__tmp__"); + data.replace_key(b.first, a.first); + data.replace_key("__tmp__", b.first); + + data[b.first] = b.second; + data[a.first] = a.second; + + notify_property_list_changed(); + emit_changed(); +} + void BlackboardPlan::sync_with_base_plan() { if (base.is_null()) { return; } + bool changed = false; + // Sync variables with the base plan. for (const KeyValue &kv : base->data) { if (!data.has(kv.key)) { data.insert(kv.key, kv.value.duplicate()); + changed = true; continue; } BBVariable var = data.get(kv.key); if (!var.is_same_prop_info(kv.value)) { var.copy_prop_info(kv.value); + changed = true; } if (var.get_value().get_type() != kv.value.get_type()) { var.set_value(kv.value.get_value()); + changed = true; } } @@ -170,8 +234,14 @@ void BlackboardPlan::sync_with_base_plan() { for (const KeyValue &kv : data) { if (!base->data.has(kv.key)) { data.erase(kv.key); + changed = true; } } + + if (changed) { + notify_property_list_changed(); + emit_changed(); + } } Ref BlackboardPlan::create_blackboard() { @@ -196,11 +266,4 @@ void BlackboardPlan::populate_blackboard(const Ref &p_blackboard, bo } BlackboardPlan::BlackboardPlan() { - // TODO: REMOVE THE TEST DATA BELOW. - data.insert("speed", BBVariable(Variant::Type::FLOAT)); - data["speed"].set_value(200.0); - data.insert("limit_speed", BBVariable(Variant::Type::BOOL)); - data["limit_speed"].set_value(500.0); - data.insert("about", BBVariable(Variant::Type::STRING, PropertyHint::PROPERTY_HINT_MULTILINE_TEXT, "")); - data["about"].set_value("Hello, World!"); } diff --git a/blackboard/blackboard_plan.h b/blackboard/blackboard_plan.h index 6c2a6b7a..ed6c55e2 100644 --- a/blackboard/blackboard_plan.h +++ b/blackboard/blackboard_plan.h @@ -24,7 +24,8 @@ class BlackboardPlan : public Resource { HashMap data; // When base is not null, the plan is considered to be derived from the base plan. - // A derived plan can only have variables that exist in the base plan. + // A derived plan can only have variables that exist in the base plan, + // and only the values can be different in those variables. Ref base; protected: @@ -40,11 +41,19 @@ class BlackboardPlan : public Resource { void set_value(const String &p_name, const Variant &p_value); Variant get_value(const String &p_name) const; + void add_var(const String &p_name, const BBVariable &p_var); void remove_var(const String &p_name); BBVariable get_var(const String &p_name); - PackedStringArray list_vars() const; + Pair get_var_by_index(int p_index); + bool has_var(const String &p_name) { return data.has(p_name); } bool is_empty() const { return data.is_empty(); } + int get_var_count() const { return data.size(); } + + PackedStringArray list_vars() const; + String get_var_name(const BBVariable &p_var) const; + void rename_var(const String &p_name, const String &p_new_name); + void swap_vars(int idx_a, int idx_b); void sync_with_base_plan(); bool is_derived() { return base.is_valid(); } diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index b5fc8251..9c8724bd 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -67,20 +67,18 @@ void BTPlayer::_update_blackboard_plan() { if (blackboard_plan.is_null()) { blackboard_plan = Ref(memnew(BlackboardPlan)); } - - if (behavior_tree.is_valid()) { - if (blackboard_plan == behavior_tree->get_blackboard_plan()) { - blackboard_plan->sync_with_base_plan(); - } else { - blackboard_plan->set_base_plan(behavior_tree->get_blackboard_plan()); - } - } + blackboard_plan->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr); } void BTPlayer::set_behavior_tree(const Ref &p_tree) { + if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) { + behavior_tree->disconnect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); + } behavior_tree = p_tree; - if (Engine::get_singleton()->is_editor_hint() == false && get_owner()) { + if (!Engine::get_singleton()->is_editor_hint() && get_owner()) { _load_tree(); + } else if (behavior_tree.is_valid()) { + behavior_tree->connect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); } _update_blackboard_plan(); } diff --git a/editor/blackboard_plan_editor.cpp b/editor/blackboard_plan_editor.cpp new file mode 100644 index 00000000..94a96ca0 --- /dev/null +++ b/editor/blackboard_plan_editor.cpp @@ -0,0 +1,411 @@ +/** + * blackboard_plan_editor.cpp + * ============================================================================= + * Copyright 2021-2024 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 "blackboard_plan_editor.h" + +#include "../util/limbo_string_names.h" +#include "../util/limbo_utility.h" + +#ifdef LIMBOAI_MODULE +#include "editor/editor_interface.h" +#include "editor/editor_scale.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/panel_container.h" +#include "scene/resources/style_box_flat.h" +#endif // LIMBOAI_MODULE + +void BlackboardPlanEditor::_add_var() { + ERR_FAIL_NULL(plan); + + int suffix = 1; + String name = "var" + itos(suffix); + while (plan->has_var(name)) { + suffix += 1; + name = "var" + itos(suffix); + } + + BBVariable var(Variant::Type::FLOAT); + plan->add_var(name, var); + _refresh(); +} + +void BlackboardPlanEditor::_trash_var(int p_index) { + ERR_FAIL_NULL(plan); + String var_name = plan->get_var_by_index(p_index).first; + plan->remove_var(var_name); + _refresh(); +} + +void BlackboardPlanEditor::_rename_var(const String &p_new_name, int p_index) { + ERR_FAIL_NULL(plan); + plan->rename_var(plan->get_var_by_index(p_index).first, p_new_name); +} + +void BlackboardPlanEditor::_change_var_type(Variant::Type p_new_type, int p_index) { + ERR_FAIL_NULL(plan); + plan->get_var_by_index(p_index).second.set_type(p_new_type); + plan->notify_property_list_changed(); + _refresh(); +} + +void BlackboardPlanEditor::_change_var_hint(PropertyHint p_new_hint, int p_index) { + ERR_FAIL_NULL(plan); + plan->get_var_by_index(p_index).second.set_hint(p_new_hint); + plan->notify_property_list_changed(); + _refresh(); +} + +void BlackboardPlanEditor::_change_var_hint_string(const String &p_new_hint_string, int p_index) { + ERR_FAIL_NULL(plan); + plan->get_var_by_index(p_index).second.set_hint_string(p_new_hint_string); + plan->notify_property_list_changed(); +} + +void BlackboardPlanEditor::edit_plan(const Ref &p_plan) { + plan = p_plan; + _refresh(); +} + +void BlackboardPlanEditor::_show_button_popup(Button *p_button, PopupMenu *p_popup, int p_index) { + ERR_FAIL_NULL(p_button); + ERR_FAIL_NULL(p_popup); + + Rect2 rect = p_button->get_screen_rect(); + rect.position.y += rect.size.height; + rect.size.height = 0; + p_popup->set_size(rect.size); + p_popup->set_position(rect.position); + + last_index = p_index; + p_popup->popup(); +} + +void BlackboardPlanEditor::_type_chosen(int id) { + _change_var_type(Variant::Type(id), last_index); +} + +void BlackboardPlanEditor::_hint_chosen(int id) { + _change_var_hint(PropertyHint(id), last_index); +} + +void BlackboardPlanEditor::_drag_button_down(Control *p_row) { + drag_index = p_row->get_index(); + drag_mouse_y_delta = 0.0; + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED); +} + +void BlackboardPlanEditor::_drag_button_up() { + drag_index = -1; + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + _refresh(); +} + +void BlackboardPlanEditor::_drag_button_gui_input(const Ref &p_event) { + if (drag_index < 0) { + return; + } + + Ref mm = p_event; + if (mm.is_null()) { + return; + } + + drag_mouse_y_delta += mm->get_relative().y; + + if ((drag_index == 0 && drag_mouse_y_delta < 0.0) || (drag_index == (plan->get_var_count() - 1) && drag_mouse_y_delta > 0.0)) { + drag_mouse_y_delta = 0.0; + return; + } + + float required_distance = 20.0f * EDSCALE; + if (ABS(drag_mouse_y_delta) > required_distance) { + int drag_dir = drag_mouse_y_delta > 0.0f ? 1 : -1; + drag_mouse_y_delta -= required_distance * drag_dir; + + plan->swap_vars(drag_index, drag_index + drag_dir); + + Control *row = Object::cast_to(rows_vbox->get_child(drag_index)); + Control *other_row = Object::cast_to(rows_vbox->get_child(drag_index + drag_dir)); + ERR_FAIL_NULL(row); + ERR_FAIL_NULL(other_row); + rows_vbox->move_child(row, drag_index + drag_dir); + row->add_theme_style_override(LW_NAME(panel), row->get_index() % 2 ? theme_cache.odd_style : theme_cache.even_style); + other_row->add_theme_style_override(LW_NAME(panel), other_row->get_index() % 2 ? theme_cache.odd_style : theme_cache.even_style); + + drag_index += drag_dir; + } +} + +void BlackboardPlanEditor::_visibility_changed() { + if (!is_visible() && plan.is_valid()) { + plan->notify_property_list_changed(); + } +} + +void BlackboardPlanEditor::_refresh() { + for (int i = 0; i < rows_vbox->get_child_count(); i++) { + Control *child = Object::cast_to(rows_vbox->get_child(i)); + ERR_FAIL_NULL(child); + child->hide(); + child->queue_free(); + } + + // TODO: Name validation + + PackedStringArray names = plan->list_vars(); + int idx = 0; + for (const String &var_name : names) { + BBVariable var = plan->get_var(var_name); + + PanelContainer *row_panel = memnew(PanelContainer); + rows_vbox->add_child(row_panel); + row_panel->add_theme_style_override(LW_NAME(panel), idx % 2 ? theme_cache.odd_style : theme_cache.even_style); + row_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL); + + HBoxContainer *props_hbox = memnew(HBoxContainer); + row_panel->add_child(props_hbox); + props_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL); + + Button *drag_button = memnew(Button); + props_hbox->add_child(drag_button); + drag_button->set_custom_minimum_size(Size2(28.0, 28.0) * EDSCALE); + drag_button->set_icon(theme_cache.grab_icon); + drag_button->connect(LW_NAME(gui_input), callable_mp(this, &BlackboardPlanEditor::_drag_button_gui_input)); + drag_button->connect(LW_NAME(button_down), callable_mp(this, &BlackboardPlanEditor::_drag_button_down).bind(row_panel)); + drag_button->connect(LW_NAME(button_up), callable_mp(this, &BlackboardPlanEditor::_drag_button_up)); + + LineEdit *name_edit = memnew(LineEdit); + props_hbox->add_child(name_edit); + name_edit->set_text(var_name); + name_edit->set_placeholder(TTR("Variable name")); + name_edit->set_flat(true); + name_edit->set_custom_minimum_size(Size2(300.0, 0.0) * EDSCALE); + name_edit->connect(LW_NAME(text_changed), callable_mp(this, &BlackboardPlanEditor::_rename_var).bind(idx)); + name_edit->connect(LW_NAME(text_submitted), callable_mp(this, &BlackboardPlanEditor::_refresh).unbind(1)); + + Button *type_choice = memnew(Button); + props_hbox->add_child(type_choice); + type_choice->set_custom_minimum_size(Size2(170, 0.0) * EDSCALE); + type_choice->set_text(Variant::get_type_name(var.get_type())); + type_choice->set_tooltip_text(Variant::get_type_name(var.get_type())); + type_choice->set_icon(get_theme_icon(Variant::get_type_name(var.get_type()), LW_NAME(EditorIcons))); + type_choice->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); + type_choice->set_flat(true); + type_choice->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); + type_choice->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_show_button_popup).bind(type_choice, type_menu, idx)); + + Button *hint_choice = memnew(Button); + props_hbox->add_child(hint_choice); + hint_choice->set_custom_minimum_size(Size2(150.0, 0.0) * EDSCALE); + hint_choice->set_text(LimboUtility::get_singleton()->get_property_hint_text(var.get_hint())); + hint_choice->set_tooltip_text(LimboUtility::get_singleton()->get_property_hint_text(var.get_hint())); + hint_choice->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); + hint_choice->set_flat(true); + hint_choice->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); + hint_choice->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_show_button_popup).bind(hint_choice, hint_menu, idx)); + + LineEdit *hint_string_edit = memnew(LineEdit); + props_hbox->add_child(hint_string_edit); + hint_string_edit->set_custom_minimum_size(Size2(300.0, 0.0) * EDSCALE); + hint_string_edit->set_text(var.get_hint_string()); + hint_string_edit->set_placeholder(TTR("Hint string")); + hint_string_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hint_string_edit->set_flat(true); + hint_string_edit->connect(LW_NAME(text_changed), callable_mp(this, &BlackboardPlanEditor::_change_var_hint_string).bind(idx)); + hint_string_edit->connect(LW_NAME(text_submitted), callable_mp(this, &BlackboardPlanEditor::_refresh).unbind(1)); + + Button *trash_button = memnew(Button); + props_hbox->add_child(trash_button); + trash_button->set_custom_minimum_size(Size2(24.0, 0.0) * EDSCALE); + trash_button->set_icon(theme_cache.trash_icon); + trash_button->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_trash_var).bind(idx)); + + idx += 1; + } +} + +void BlackboardPlanEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + theme_cache.trash_icon = get_theme_icon(LW_NAME(Remove), LW_NAME(EditorIcons)); + theme_cache.grab_icon = get_theme_icon(LW_NAME(TripleBar), LW_NAME(EditorIcons)); + + add_var_tool->set_icon(get_theme_icon(LW_NAME(Add), LW_NAME(EditorIcons))); + + type_menu->clear(); + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (i == Variant::RID || i == Variant::CALLABLE || i == Variant::SIGNAL) { + continue; + } + String type = Variant::get_type_name(Variant::Type(i)); + type_menu->add_icon_item(get_theme_icon(type, LW_NAME(EditorIcons)), type, i); + } + + scroll_container->add_theme_style_override(LW_NAME(panel), get_theme_stylebox(LW_NAME(panel), LW_NAME(Tree))); + + Color bg_color = get_theme_color(LW_NAME(dark_color_2), LW_NAME(Editor)); + theme_cache.odd_style->set_bg_color(bg_color.darkened(-0.05)); + theme_cache.even_style->set_bg_color(bg_color.darkened(0.05)); + theme_cache.header_style->set_bg_color(bg_color.darkened(-0.2)); + + header_row->add_theme_style_override(LW_NAME(panel), theme_cache.header_style); + } break; + case NOTIFICATION_READY: { + add_var_tool->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_add_var)); + connect(LW_NAME(visibility_changed), callable_mp(this, &BlackboardPlanEditor::_visibility_changed)); + } break; + } +} + +BlackboardPlanEditor::BlackboardPlanEditor() { + set_title(TTR("Edit Blackboard Plan")); + + VBoxContainer *vbox = memnew(VBoxContainer); + vbox->add_theme_constant_override(LW_NAME(separation), 8 * EDSCALE); + add_child(vbox); + + HBoxContainer *toolbar = memnew(HBoxContainer); + vbox->add_child(toolbar); + + add_var_tool = memnew(Button); + toolbar->add_child(add_var_tool); + add_var_tool->set_focus_mode(Control::FOCUS_NONE); + add_var_tool->set_text(TTR("Add variable")); + + { + // * Header + header_row = memnew(PanelContainer); + vbox->add_child(header_row); + header_row->set_h_size_flags(Control::SIZE_EXPAND_FILL); + + HBoxContainer *labels_hbox = memnew(HBoxContainer); + header_row->add_child(labels_hbox); + labels_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL); + + Control *offset = memnew(Control); + labels_hbox->add_child(offset); + offset->set_custom_minimum_size(Size2(2.0, 0.0) * EDSCALE); + + Label *drag_header = memnew(Label); + labels_hbox->add_child(drag_header); + drag_header->set_custom_minimum_size(Size2(28.0, 28.0) * EDSCALE); + + Label *name_header = memnew(Label); + labels_hbox->add_child(name_header); + name_header->set_text(TTR("Name")); + name_header->set_custom_minimum_size(Size2(300.0, 0.0) * EDSCALE); + name_header->set_theme_type_variation(LW_NAME(HeaderSmall)); + + Label *type_header = memnew(Label); + labels_hbox->add_child(type_header); + type_header->set_text(TTR("Type")); + type_header->set_custom_minimum_size(Size2(170, 0.0) * EDSCALE); + type_header->set_theme_type_variation(LW_NAME(HeaderSmall)); + + Label *hint_header = memnew(Label); + labels_hbox->add_child(hint_header); + hint_header->set_text(TTR("Hint")); + hint_header->set_custom_minimum_size(Size2(150.0, 0.0) * EDSCALE); + hint_header->set_theme_type_variation(LW_NAME(HeaderSmall)); + + Label *hint_string_header = memnew(Label); + labels_hbox->add_child(hint_string_header); + hint_string_header->set_text(TTR("Hint string")); + hint_string_header->set_custom_minimum_size(Size2(300.0, 0.0) * EDSCALE); + hint_string_header->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hint_string_header->set_theme_type_variation(LW_NAME(HeaderSmall)); + } + + scroll_container = memnew(ScrollContainer); + vbox->add_child(scroll_container); + scroll_container->set_v_size_flags(Control::SIZE_EXPAND_FILL); + scroll_container->set_h_size_flags(Control::SIZE_EXPAND_FILL); + scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); + scroll_container->set_custom_minimum_size(Size2(0.0, 600.0) * EDSCALE); + + rows_vbox = memnew(VBoxContainer); + scroll_container->add_child(rows_vbox); + rows_vbox->set_h_size_flags(Control::SIZE_EXPAND_FILL); + rows_vbox->add_theme_constant_override(LW_NAME(separation), 0); + + type_menu = memnew(PopupMenu); + add_child(type_menu); + type_menu->connect(LW_NAME(id_pressed), callable_mp(this, &BlackboardPlanEditor::_type_chosen)); + + hint_menu = memnew(PopupMenu); + add_child(hint_menu); + hint_menu->connect(LW_NAME(id_pressed), callable_mp(this, &BlackboardPlanEditor::_hint_chosen)); + for (int i = 0; i < PropertyHint::PROPERTY_HINT_MAX; i++) { + hint_menu->add_item(LimboUtility::get_singleton()->get_property_hint_text(PropertyHint(i)), i); + } + + theme_cache.odd_style.instantiate(); + theme_cache.even_style.instantiate(); + theme_cache.header_style.instantiate(); +} + +// ***** + +void EditorInspectorPluginBBPlan::_edit_plan(const Ref &p_plan) { + ERR_FAIL_NULL(p_plan); + plan_editor->edit_plan(p_plan); + plan_editor->popup_centered(); +} + +void EditorInspectorPluginBBPlan::_open_base_plan(const Ref &p_plan) { + ERR_FAIL_NULL(p_plan); + ERR_FAIL_NULL(p_plan->get_base_plan()); + EditorInterface::get_singleton()->call_deferred("edit_resource", p_plan->get_base_plan()); +} + +bool EditorInspectorPluginBBPlan::can_handle(Object *p_object) { + Ref plan = Object::cast_to(p_object); + if (plan.is_valid()) { + plan->sync_with_base_plan(); + } + return plan.is_valid(); +} + +void EditorInspectorPluginBBPlan::parse_begin(Object *p_object) { + Ref plan = Object::cast_to(p_object); + ERR_FAIL_NULL(plan); + + MarginContainer *margin_container = memnew(MarginContainer); + margin_container->set_theme_type_variation("MarginContainer4px"); + + VBoxContainer *toolbar = memnew(VBoxContainer); + margin_container->add_child(toolbar); + toolbar->set_h_size_flags(Control::SIZE_SHRINK_CENTER); + + if (plan->is_derived()) { + Button *goto_btn = memnew(Button); + toolbar->add_child(goto_btn); + goto_btn->set_text(TTR("Open Base Plan")); + goto_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER); + goto_btn->set_custom_minimum_size(Size2(200.0, 0.0) * EDSCALE); + goto_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_open_base_plan).bind(plan)); + } else { + Button *edit_btn = memnew(Button); + toolbar->add_child(edit_btn); + edit_btn->set_text(TTR("Edit...")); + edit_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER); + edit_btn->set_custom_minimum_size(Size2(200.0, 0.0) * EDSCALE); + edit_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_edit_plan).bind(plan)); + } + + add_custom_control(margin_container); +} + +EditorInspectorPluginBBPlan::EditorInspectorPluginBBPlan() { + plan_editor = memnew(BlackboardPlanEditor); + EditorInterface::get_singleton()->get_base_control()->add_child(plan_editor); + plan_editor->hide(); +} diff --git a/editor/blackboard_plan_editor.h b/editor/blackboard_plan_editor.h new file mode 100644 index 00000000..2c6eaaa8 --- /dev/null +++ b/editor/blackboard_plan_editor.h @@ -0,0 +1,95 @@ +/** + * blackboard_plan_editor.h + * ============================================================================= + * Copyright 2021-2024 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 BLACKBOARD_PLAN_EDITOR_H +#define BLACKBOARD_PLAN_EDITOR_H + +#include "../blackboard/blackboard_plan.h" + +#ifdef LIMBOAI_MODULE +#include "editor/editor_inspector.h" +#include "scene/gui/dialogs.h" +#endif // LIMBOAI_MODULE + +// ***** + +class BlackboardPlanEditor : public AcceptDialog { + GDCLASS(BlackboardPlanEditor, AcceptDialog); + +private: + struct ThemeCache { + Ref trash_icon; + Ref grab_icon; + Ref odd_style; + Ref even_style; + Ref header_style; + } theme_cache; + + int last_index = 0; + + int drag_mouse_y_delta = 0; + int drag_index = 0; + + Ref plan; + + VBoxContainer *rows_vbox; + Button *add_var_tool; + PanelContainer *header_row; + ScrollContainer *scroll_container; + PopupMenu *type_menu; + PopupMenu *hint_menu; + + void _add_var(); + void _trash_var(int p_index); + void _rename_var(const String &p_new_name, int p_index); + void _change_var_type(Variant::Type p_new_type, int p_index); + void _change_var_hint(PropertyHint p_new_hint, int p_index); + void _change_var_hint_string(const String &p_new_hint_string, int p_index); + + void _show_button_popup(Button *p_button, PopupMenu *p_popup, int p_index); + void _type_chosen(int id); + void _hint_chosen(int id); + + void _drag_button_down(Control *p_row); + void _drag_button_up(); + void _drag_button_gui_input(const Ref &p_event); + + void _refresh(); + void _visibility_changed(); + +protected: + void _notification(int p_what); + +public: + void edit_plan(const Ref &p_plan); + + BlackboardPlanEditor(); +}; + +// ***** + +class EditorInspectorPluginBBPlan : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginBBPlan, EditorInspectorPlugin); + +private: + BlackboardPlanEditor *plan_editor; + + void _edit_plan(const Ref &p_plan); + void _open_base_plan(const Ref &p_plan); + +public: + virtual bool can_handle(Object *p_object) override; + virtual void parse_begin(Object *p_object) override; + + EditorInspectorPluginBBPlan(); +}; + +#endif // BLACKBOARD_PLAN_EDITOR_H diff --git a/editor/limbo_ai_editor_plugin.cpp b/editor/limbo_ai_editor_plugin.cpp index 08e30ac1..6477c295 100644 --- a/editor/limbo_ai_editor_plugin.cpp +++ b/editor/limbo_ai_editor_plugin.cpp @@ -18,11 +18,12 @@ #include "../bt/tasks/composites/bt_probability_selector.h" #include "../bt/tasks/composites/bt_selector.h" #include "../bt/tasks/decorators/bt_subtree.h" -#include "../editor/debugger/limbo_debugger_plugin.h" -#include "../editor/editor_property_bb_param.h" #include "../util/limbo_compat.h" #include "../util/limbo_utility.h" #include "action_banner.h" +#include "blackboard_plan_editor.h" +#include "debugger/limbo_debugger_plugin.h" +#include "editor_property_bb_param.h" #ifdef LIMBOAI_MODULE #include "core/config/project_settings.h" @@ -1447,6 +1448,7 @@ LimboAIEditorPlugin::LimboAIEditorPlugin() { limbo_ai_editor->hide(); limbo_ai_editor->set_plugin(this); + add_inspector_plugin(memnew(EditorInspectorPluginBBPlan)); #ifdef LIMBOAI_MODULE // ! Only used in the module version. add_inspector_plugin(memnew(EditorInspectorPluginBBParam)); diff --git a/util/limbo_string_names.cpp b/util/limbo_string_names.cpp index ee045bd9..ab920075 100644 --- a/util/limbo_string_names.cpp +++ b/util/limbo_string_names.cpp @@ -42,6 +42,7 @@ LimboStringNames::LimboStringNames() { _update_banners = SN("_update_banners"); _weight_ = SN("_weight_"); accent_color = SN("accent_color"); + Add = SN("Add"); add_child = SN("add_child"); add_child_at_index = SN("add_child_at_index"); AnimationFilter = SN("AnimationFilter"); @@ -52,8 +53,12 @@ LimboStringNames::LimboStringNames() { bold = SN("bold"); BTAlwaysFail = SN("BTAlwaysFail"); BTAlwaysSucceed = SN("BTAlwaysSucceed"); + button_down = SN("button_down"); + button_up = SN("button_up"); changed = SN("changed"); connect = SN("connect"); + dark_color_1 = SN("dark_color_1"); + dark_color_2 = SN("dark_color_2"); Debug = SN("Debug"); disabled_font_color = SN("disabled_font_color"); doc_italic = SN("doc_italic"); @@ -76,6 +81,7 @@ LimboStringNames::LimboStringNames() { gui_input = SN("gui_input"); GuiTreeArrowDown = SN("GuiTreeArrowDown"); GuiTreeArrowRight = SN("GuiTreeArrowRight"); + HeaderSmall = SN("HeaderSmall"); Help = SN("Help"); icon_max_width = SN("icon_max_width"); id_pressed = SN("id_pressed"); @@ -97,6 +103,7 @@ LimboStringNames::LimboStringNames() { NodeWarning = SN("NodeWarning"); NonFavorite = SN("NonFavorite"); normal = SN("normal"); + panel = SN("panel"); popup_hide = SN("popup_hide"); pressed = SN("pressed"); probability_clicked = SN("probability_clicked"); @@ -111,6 +118,7 @@ LimboStringNames::LimboStringNames() { Script = SN("Script"); ScriptCreate = SN("ScriptCreate"); Search = SN("Search"); + separation = SN("separation"); set_custom_name = SN("set_custom_name"); set_root_task = SN("set_root_task"); setup = SN("setup"); @@ -125,9 +133,12 @@ LimboStringNames::LimboStringNames() { task_meta = SN("task_meta"); task_selected = SN("task_selected"); text_changed = SN("text_changed"); + text_submitted = SN("text_submitted"); timeout = SN("timeout"); toggled = SN("toggled"); Tools = SN("Tools"); + Tree = SN("Tree"); + TripleBar = SN("TripleBar"); update_task = SN("update_task"); update_tree = SN("update_tree"); updated = SN("updated"); diff --git a/util/limbo_string_names.h b/util/limbo_string_names.h index d1bd93f8..85d7cf1b 100644 --- a/util/limbo_string_names.h +++ b/util/limbo_string_names.h @@ -58,6 +58,7 @@ class LimboStringNames { StringName accent_color; StringName add_child_at_index; StringName add_child; + StringName Add; StringName AnimationFilter; StringName Back; StringName behavior_tree_finished; @@ -66,8 +67,12 @@ class LimboStringNames { StringName bold; StringName BTAlwaysFail; StringName BTAlwaysSucceed; + StringName button_down; + StringName button_up; StringName changed; StringName connect; + StringName dark_color_1; + StringName dark_color_2; StringName Debug; StringName disabled_font_color; StringName doc_italic; @@ -90,6 +95,7 @@ class LimboStringNames { StringName gui_input; StringName GuiTreeArrowDown; StringName GuiTreeArrowRight; + StringName HeaderSmall; StringName Help; StringName icon_max_width; StringName id_pressed; @@ -112,6 +118,7 @@ class LimboStringNames { StringName NodeWarning; StringName NonFavorite; StringName normal; + StringName panel; StringName popup_hide; StringName pressed; StringName probability_clicked; @@ -126,6 +133,7 @@ class LimboStringNames { StringName Script; StringName ScriptCreate; StringName Search; + StringName separation; StringName set_custom_name; StringName set_root_task; StringName setup; @@ -140,9 +148,12 @@ class LimboStringNames { StringName task_meta; StringName task_selected; StringName text_changed; + StringName text_submitted; StringName timeout; StringName toggled; StringName Tools; + StringName Tree; + StringName TripleBar; StringName update_task; StringName update_tree; StringName updated; diff --git a/util/limbo_utility.cpp b/util/limbo_utility.cpp index 087dcd05..ed2f64fb 100644 --- a/util/limbo_utility.cpp +++ b/util/limbo_utility.cpp @@ -288,6 +288,129 @@ Variant LimboUtility::perform_operation(Operation p_operation, const Variant &le return ret; } +String LimboUtility::get_property_hint_text(PropertyHint p_hint) const { + switch (p_hint) { + case PROPERTY_HINT_NONE: { + return "NONE"; + } + case PROPERTY_HINT_RANGE: { + return "RANGE"; + } + case PROPERTY_HINT_ENUM: { + return "ENUM"; + } + case PROPERTY_HINT_ENUM_SUGGESTION: { + return "SUGGESTION"; + } + case PROPERTY_HINT_EXP_EASING: { + return "EXP_EASING"; + } + case PROPERTY_HINT_LINK: { + return "LINK"; + } + case PROPERTY_HINT_FLAGS: { + return "FLAGS"; + } + case PROPERTY_HINT_LAYERS_2D_RENDER: { + return "LAYERS_2D_RENDER"; + } + case PROPERTY_HINT_LAYERS_2D_PHYSICS: { + return "LAYERS_2D_PHYSICS"; + } + case PROPERTY_HINT_LAYERS_2D_NAVIGATION: { + return "LAYERS_2D_NAVIGATION"; + } + case PROPERTY_HINT_LAYERS_3D_RENDER: { + return "LAYERS_3D_RENDER"; + } + case PROPERTY_HINT_LAYERS_3D_PHYSICS: { + return "LAYERS_3D_PHYSICS"; + } + case PROPERTY_HINT_LAYERS_3D_NAVIGATION: { + return "LAYERS_3D_NAVIGATION"; + } + case PROPERTY_HINT_FILE: { + return "FILE"; + } + case PROPERTY_HINT_DIR: { + return "DIR"; + } + case PROPERTY_HINT_GLOBAL_FILE: { + return "GLOBAL_FILE"; + } + case PROPERTY_HINT_GLOBAL_DIR: { + return "GLOBAL_DIR"; + } + case PROPERTY_HINT_RESOURCE_TYPE: { + return "RESOURCE_TYPE"; + } + case PROPERTY_HINT_MULTILINE_TEXT: { + return "MULTILINE_TEXT"; + } + case PROPERTY_HINT_EXPRESSION: { + return "EXPRESSION"; + } + case PROPERTY_HINT_PLACEHOLDER_TEXT: { + return "PLACEHOLDER_TEXT"; + } + case PROPERTY_HINT_COLOR_NO_ALPHA: { + return "COLOR_NO_ALPHA"; + } + case PROPERTY_HINT_OBJECT_ID: { + return "OBJECT_ID"; + } + case PROPERTY_HINT_TYPE_STRING: { + return "TYPE_STRING"; + } + case PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE: { + return "NODE_PATH_TO_EDITED_NODE"; + } + case PROPERTY_HINT_OBJECT_TOO_BIG: { + return "OBJECT_TOO_BIG"; + } + case PROPERTY_HINT_NODE_PATH_VALID_TYPES: { + return "NODE_PATH_VALID_TYPES"; + } + case PROPERTY_HINT_SAVE_FILE: { + return "SAVE_FILE"; + } + case PROPERTY_HINT_GLOBAL_SAVE_FILE: { + return "GLOBAL_SAVE_FILE"; + } + case PROPERTY_HINT_INT_IS_OBJECTID: { + return "INT_IS_OBJECTID"; + } + case PROPERTY_HINT_INT_IS_POINTER: { + return "INT_IS_POINTER"; + } + case PROPERTY_HINT_ARRAY_TYPE: { + return "ARRAY_TYPE"; + } + case PROPERTY_HINT_LOCALE_ID: { + return "LOCALE_ID"; + } + case PROPERTY_HINT_LOCALIZABLE_STRING: { + return "LOCALIZABLE_STRING"; + } + case PROPERTY_HINT_NODE_TYPE: { + return "NODE_TYPE"; + } + case PROPERTY_HINT_HIDE_QUATERNION_EDIT: { + return "HIDE_QUATERNION_EDIT"; + } + case PROPERTY_HINT_PASSWORD: { + return "PASSWORD"; + } + case PROPERTY_HINT_LAYERS_AVOIDANCE: { + return "LAYERS_AVOIDANCE"; + } + case PROPERTY_HINT_MAX: { + return "MAX"; + } + } + return ""; +} + #ifdef TOOLS_ENABLED Ref LimboUtility::add_shortcut(const String &p_path, const String &p_name, Key p_keycode) { diff --git a/util/limbo_utility.h b/util/limbo_utility.h index bcb5a1d8..39045a40 100644 --- a/util/limbo_utility.h +++ b/util/limbo_utility.h @@ -88,6 +88,8 @@ class LimboUtility : public Object { String get_operation_string(Operation p_operation) const; Variant perform_operation(Operation p_operation, const Variant &left_value, const Variant &right_value); + String get_property_hint_text(PropertyHint p_hint) const; + #ifdef TOOLS_ENABLED Ref add_shortcut(const String &p_path, const String &p_name, Key p_keycode = LW_KEY(NONE)); bool is_shortcut(const String &p_path, const Ref &p_event) const; From 06052332b2b72c0f127feb3c07fa180b5d0c4673 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 25 Jan 2024 00:23:34 +0100 Subject: [PATCH 09/15] BlackboardPlanEditor: Fixes and clean up --- blackboard/blackboard_plan.cpp | 2 -- bt/bt_player.cpp | 36 +++++++++++++------ bt/bt_state.cpp | 34 ++++++++++++------ bt/bt_state.h | 5 +-- demo/demo/ai/trees/waypoints.tres | 10 +++++- .../examples/waypoints/patrolling_agent.tscn | 13 ++++--- editor/blackboard_plan_editor.cpp | 20 ++++++++--- editor/blackboard_plan_editor.h | 3 +- 8 files changed, 86 insertions(+), 37 deletions(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index a02869c0..c88e81ee 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -107,8 +107,6 @@ bool BlackboardPlan::_property_get_revert(const StringName &p_name, Variant &r_p void BlackboardPlan::set_base_plan(const Ref &p_base) { base = p_base; sync_with_base_plan(); - emit_changed(); - notify_property_list_changed(); } void BlackboardPlan::set_value(const String &p_name, const Variant &p_value) { diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 9c8724bd..32548ddb 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -71,16 +71,21 @@ void BTPlayer::_update_blackboard_plan() { } void BTPlayer::set_behavior_tree(const Ref &p_tree) { - if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) { - behavior_tree->disconnect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); - } - behavior_tree = p_tree; - if (!Engine::get_singleton()->is_editor_hint() && get_owner()) { - _load_tree(); - } else if (behavior_tree.is_valid()) { - behavior_tree->connect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); + if (Engine::get_singleton()->is_editor_hint()) { + if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) { + behavior_tree->disconnect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); + } + if (p_tree.is_valid()) { + p_tree->connect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); + } + behavior_tree = p_tree; + _update_blackboard_plan(); + } else { + behavior_tree = p_tree; + if (get_owner()) { + _load_tree(); + } } - _update_blackboard_plan(); } void BTPlayer::set_blackboard_plan(const Ref &p_plan) { @@ -193,18 +198,27 @@ void BTPlayer::_notification(int p_notification) { #endif } } break; -#ifdef DEBUG_ENABLED case NOTIFICATION_ENTER_TREE: { +#ifdef DEBUG_ENABLED if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) { LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path()); } +#endif // DEBUG_ENABLED } break; case NOTIFICATION_EXIT_TREE: { +#ifdef DEBUG_ENABLED if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) { LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path()); } - } break; #endif // DEBUG_ENABLED + + if (Engine::get_singleton()->is_editor_hint()) { + if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) { + behavior_tree->disconnect(LW_NAME(changed), callable_mp(this, &BTPlayer::_update_blackboard_plan)); + } + } + + } break; } } diff --git a/bt/bt_state.cpp b/bt/bt_state.cpp index d6406f6a..75b2787d 100644 --- a/bt/bt_state.cpp +++ b/bt/bt_state.cpp @@ -26,17 +26,23 @@ #include #endif // LIMBOAI_GDEXTENSION -void BTState::_update_blackboard_plan() { - if (behavior_tree.is_valid() && behavior_tree->get_blackboard_plan().is_valid()) { - if (get_blackboard_plan().is_null()) { - set_blackboard_plan(Ref(memnew(BlackboardPlan))); +void BTState::set_behavior_tree(const Ref &p_tree) { + if (Engine::get_singleton()->is_editor_hint()) { + if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(changed), callable_mp(this, &BTState::_update_blackboard_plan))) { + behavior_tree->disconnect(LW_NAME(changed), callable_mp(this, &BTState::_update_blackboard_plan)); } - if (get_blackboard_plan() == behavior_tree->get_blackboard_plan()) { - get_blackboard_plan()->sync_with_base_plan(); - } else { - get_blackboard_plan()->set_base_plan(behavior_tree->get_blackboard_plan()); + if (p_tree.is_valid()) { + p_tree->connect(LW_NAME(changed), callable_mp(this, &BTState::_update_blackboard_plan)); } } + behavior_tree = p_tree; +} + +void BTState::_update_blackboard_plan() { + if (get_blackboard_plan().is_null()) { + set_blackboard_plan(Ref(memnew(BlackboardPlan))); + } + get_blackboard_plan()->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr); } void BTState::_setup() { @@ -66,22 +72,30 @@ void BTState::_update(double p_delta) { } } -#ifdef DEBUG_ENABLED void BTState::_notification(int p_notification) { switch (p_notification) { +#ifdef DEBUG_ENABLED case NOTIFICATION_ENTER_TREE: { if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) { LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path()); } } break; +#endif // DEBUG_ENABLED case NOTIFICATION_EXIT_TREE: { +#ifdef DEBUG_ENABLED if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) { LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path()); } +#endif // DEBUG_ENABLED + + if (Engine::get_singleton()->is_editor_hint()) { + if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(changed), callable_mp(this, &BTState::_update_blackboard_plan))) { + behavior_tree->disconnect(LW_NAME(changed), callable_mp(this, &BTState::_update_blackboard_plan)); + } + } } break; } } -#endif // DEBUG_ENABLED void BTState::_bind_methods() { ClassDB::bind_method(D_METHOD("set_behavior_tree", "p_value"), &BTState::set_behavior_tree); diff --git a/bt/bt_state.h b/bt/bt_state.h index b8c531d8..3ebd9baf 100644 --- a/bt/bt_state.h +++ b/bt/bt_state.h @@ -36,7 +36,7 @@ class BTState : public LimboState { virtual void _update(double p_delta) override; public: - void set_behavior_tree(const Ref &p_value) { behavior_tree = p_value; } + void set_behavior_tree(const Ref &p_value); Ref get_behavior_tree() const { return behavior_tree; } void set_success_event(String p_success_event) { success_event = p_success_event; } @@ -47,11 +47,8 @@ class BTState : public LimboState { BTState(); -#ifdef DEBUG_ENABLED protected: void _notification(int p_notification); - -#endif }; #endif // BT_STATE_H diff --git a/demo/demo/ai/trees/waypoints.tres b/demo/demo/ai/trees/waypoints.tres index f94049a9..5254c91d 100644 --- a/demo/demo/ai/trees/waypoints.tres +++ b/demo/demo/ai/trees/waypoints.tres @@ -1,7 +1,14 @@ -[gd_resource type="BehaviorTree" load_steps=10 format=3 uid="uid://cjkqi41oagagd"] +[gd_resource type="BehaviorTree" load_steps=11 format=3 uid="uid://cjkqi41oagagd"] [ext_resource type="Script" path="res://demo/ai/tasks/arrive_pos.gd" id="1_rhs33"] +[sub_resource type="BlackboardPlan" id="BlackboardPlan_2hcqi"] +var/speed/name = null +var/speed/type = 3 +var/speed/value = 200.0 +var/speed/hint = 1 +var/speed/hint_string = "10,1000,10" + [sub_resource type="BTAction" id="BTAction_3xal7"] script = ExtResource("1_rhs33") target_position_var = "wp" @@ -37,3 +44,4 @@ children = [SubResource("BTCooldown_gen0l")] [resource] root_task = SubResource("BTSelector_5dclr") +blackboard_plan = SubResource("BlackboardPlan_2hcqi") diff --git a/demo/demo/examples/waypoints/patrolling_agent.tscn b/demo/demo/examples/waypoints/patrolling_agent.tscn index e058444c..4b1c6091 100644 --- a/demo/demo/examples/waypoints/patrolling_agent.tscn +++ b/demo/demo/examples/waypoints/patrolling_agent.tscn @@ -1,9 +1,16 @@ -[gd_scene load_steps=7 format=3 uid="uid://c26b8c8dndtop"] +[gd_scene load_steps=8 format=3 uid="uid://c26b8c8dndtop"] [ext_resource type="Script" path="res://demo/examples/waypoints/patrolling_agent.gd" id="1_5wwhb"] [ext_resource type="BehaviorTree" uid="uid://cjkqi41oagagd" path="res://demo/ai/trees/waypoints.tres" id="2_66y4v"] [ext_resource type="Texture2D" uid="uid://d0mht3ntak7e5" path="res://demo/godot.png" id="3_64ge2"] +[sub_resource type="BlackboardPlan" id="BlackboardPlan_b86q8"] +var/speed/name = null +var/speed/type = 3 +var/speed/value = 200.0 +var/speed/hint = 1 +var/speed/hint_string = "10,1000,10" + [sub_resource type="Animation" id="Animation_5id00"] length = 0.001 tracks/0/type = "value" @@ -46,9 +53,7 @@ script = ExtResource("1_5wwhb") [node name="BTPlayer" type="BTPlayer" parent="."] behavior_tree = ExtResource("2_66y4v") -_blackboard_data = { -"speed": 200.0 -} +blackboard_plan = SubResource("BlackboardPlan_b86q8") [node name="Sprite2D" type="Sprite2D" parent="."] texture = ExtResource("3_64ge2") diff --git a/editor/blackboard_plan_editor.cpp b/editor/blackboard_plan_editor.cpp index 94a96ca0..504fb55d 100644 --- a/editor/blackboard_plan_editor.cpp +++ b/editor/blackboard_plan_editor.cpp @@ -266,7 +266,7 @@ void BlackboardPlanEditor::_notification(int p_what) { } BlackboardPlanEditor::BlackboardPlanEditor() { - set_title(TTR("Edit Blackboard Plan")); + set_title(TTR("Manage Blackboard Plan")); VBoxContainer *vbox = memnew(VBoxContainer); vbox->add_theme_constant_override(LW_NAME(separation), 8 * EDSCALE); @@ -378,8 +378,14 @@ void EditorInspectorPluginBBPlan::parse_begin(Object *p_object) { Ref plan = Object::cast_to(p_object); ERR_FAIL_NULL(plan); + PanelContainer *panel = memnew(PanelContainer); + panel->add_theme_style_override(LW_NAME(panel), toolbar_style); + MarginContainer *margin_container = memnew(MarginContainer); + panel->add_child(margin_container); margin_container->set_theme_type_variation("MarginContainer4px"); + margin_container->set_v_size_flags(Control::SIZE_EXPAND_FILL); + margin_container->set_h_size_flags(Control::SIZE_EXPAND_FILL); VBoxContainer *toolbar = memnew(VBoxContainer); margin_container->add_child(toolbar); @@ -388,24 +394,30 @@ void EditorInspectorPluginBBPlan::parse_begin(Object *p_object) { if (plan->is_derived()) { Button *goto_btn = memnew(Button); toolbar->add_child(goto_btn); - goto_btn->set_text(TTR("Open Base Plan")); + goto_btn->set_text(TTR("Edit Base")); goto_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER); goto_btn->set_custom_minimum_size(Size2(200.0, 0.0) * EDSCALE); goto_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_open_base_plan).bind(plan)); } else { Button *edit_btn = memnew(Button); toolbar->add_child(edit_btn); - edit_btn->set_text(TTR("Edit...")); + edit_btn->set_text(TTR("Manage...")); edit_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER); edit_btn->set_custom_minimum_size(Size2(200.0, 0.0) * EDSCALE); edit_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_edit_plan).bind(plan)); } - add_custom_control(margin_container); + add_custom_control(panel); } EditorInspectorPluginBBPlan::EditorInspectorPluginBBPlan() { plan_editor = memnew(BlackboardPlanEditor); EditorInterface::get_singleton()->get_base_control()->add_child(plan_editor); plan_editor->hide(); + + toolbar_style = Ref(memnew(StyleBoxFlat)); + Color bg = EditorInterface::get_singleton()->get_editor_theme()->get_color(LW_NAME(accent_color), LW_NAME(Editor)); + bg = bg.darkened(-0.1); + bg.a *= 0.2; + toolbar_style->set_bg_color(bg); } diff --git a/editor/blackboard_plan_editor.h b/editor/blackboard_plan_editor.h index 2c6eaaa8..8812ab27 100644 --- a/editor/blackboard_plan_editor.h +++ b/editor/blackboard_plan_editor.h @@ -36,7 +36,7 @@ class BlackboardPlanEditor : public AcceptDialog { int last_index = 0; int drag_mouse_y_delta = 0; - int drag_index = 0; + int drag_index = -1; Ref plan; @@ -81,6 +81,7 @@ class EditorInspectorPluginBBPlan : public EditorInspectorPlugin { private: BlackboardPlanEditor *plan_editor; + Ref toolbar_style; void _edit_plan(const Ref &p_plan); void _open_base_plan(const Ref &p_plan); From fb1ab16682a044159270511bd5eb319b225c2c8f Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 25 Jan 2024 11:51:35 +0100 Subject: [PATCH 10/15] In a derived BB plan, hide variables with names that begin with "_" --- blackboard/blackboard_plan.cpp | 4 +++- blackboard/blackboard_plan.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index c88e81ee..261b60c8 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -81,7 +81,9 @@ void BlackboardPlan::_get_property_list(List *p_list) const { BBVariable var = kv.value; // * Editor - p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR)); + if (!is_derived() || !var_name.begins_with("_")) { + p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR)); + } // * Storage p_list->push_back(PropertyInfo(Variant::STRING, "var/" + var_name + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); diff --git a/blackboard/blackboard_plan.h b/blackboard/blackboard_plan.h index ed6c55e2..ecd9376d 100644 --- a/blackboard/blackboard_plan.h +++ b/blackboard/blackboard_plan.h @@ -56,7 +56,7 @@ class BlackboardPlan : public Resource { void swap_vars(int idx_a, int idx_b); void sync_with_base_plan(); - bool is_derived() { return base.is_valid(); } + bool is_derived() const { return base.is_valid(); } Ref create_blackboard(); void populate_blackboard(const Ref &p_blackboard, bool overwrite); From 8155c2764a52885fc89d70cb226899d525ce2926 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 25 Jan 2024 14:27:14 +0100 Subject: [PATCH 11/15] Refactoring and clean up --- blackboard/blackboard.cpp | 14 ++++++-------- blackboard/blackboard.h | 8 +++----- blackboard/blackboard_plan.cpp | 16 ++++------------ blackboard/blackboard_plan.h | 3 --- bt/behavior_tree.cpp | 12 +++++++++++- bt/behavior_tree.h | 14 ++++---------- bt/tasks/decorators/bt_new_scope.cpp | 2 +- demo/demo/ai/trees/waypoints.tres | 4 ++-- .../demo/examples/waypoints/example_waypoints.gd | 2 -- .../examples/waypoints/patrolling_agent.tscn | 4 ++-- editor/blackboard_plan_editor.h | 1 - hsm/limbo_hsm.cpp | 2 +- hsm/limbo_state.cpp | 2 +- tests/test_new_scope.h | 2 +- 14 files changed, 36 insertions(+), 50 deletions(-) diff --git a/blackboard/blackboard.cpp b/blackboard/blackboard.cpp index 68727a70..d0561526 100644 --- a/blackboard/blackboard.cpp +++ b/blackboard/blackboard.cpp @@ -28,8 +28,8 @@ using namespace godot; Ref Blackboard::top() const { Ref bb(this); - while (bb->get_parent_scope().is_valid()) { - bb = bb->get_parent_scope(); + while (bb->get_parent().is_valid()) { + bb = bb->get_parent(); } return bb; } @@ -45,11 +45,11 @@ Variant Blackboard::get_var(const String &p_name, const Variant &p_default) cons } void Blackboard::set_var(const String &p_name, const Variant &p_value) { - // TODO: Check if p_value can be converted into required type! if (data.has(p_name)) { + // Not checking type - allowing duck-typing. data[p_name].set_value(p_value); } else { - BBVariable var; + BBVariable var(p_value.get_type()); var.set_value(p_value); data.insert(p_name, var); } @@ -85,11 +85,9 @@ void Blackboard::_bind_methods() { ClassDB::bind_method(D_METHOD("get_var", "p_name", "p_default"), &Blackboard::get_var, Variant()); ClassDB::bind_method(D_METHOD("set_var", "p_name", "p_value"), &Blackboard::set_var); ClassDB::bind_method(D_METHOD("has_var", "p_name"), &Blackboard::has_var); - ClassDB::bind_method(D_METHOD("set_parent_scope", "p_blackboard"), &Blackboard::set_parent_scope); - ClassDB::bind_method(D_METHOD("get_parent_scope"), &Blackboard::get_parent_scope); + ClassDB::bind_method(D_METHOD("set_parent_scope", "p_blackboard"), &Blackboard::set_parent); + ClassDB::bind_method(D_METHOD("get_parent_scope"), &Blackboard::get_parent); 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("get_data"), &Blackboard::get_data); - // ClassDB::bind_method(D_METHOD("set_data", "p_data"), &Blackboard::set_data); } diff --git a/blackboard/blackboard.h b/blackboard/blackboard.h index 062d8b20..1b79f824 100644 --- a/blackboard/blackboard.h +++ b/blackboard/blackboard.h @@ -42,8 +42,8 @@ class Blackboard : public RefCounted { static void _bind_methods(); public: - void set_parent_scope(const Ref &p_blackboard) { parent = p_blackboard; } - Ref get_parent_scope() const { return parent; } + void set_parent(const Ref &p_blackboard) { parent = p_blackboard; } + Ref get_parent() const { return parent; } Ref top() const; @@ -56,9 +56,7 @@ class Blackboard : public RefCounted { void prefetch_nodepath_vars(Node *p_node); - // TODO: Rework serialization API. - // void set_data(const Dictionary &p_value); - // Dictionary get_data() const; + // TODO: Add serialization API. }; #endif // BLACKBOARD_H diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index 261b60c8..f359b5ed 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -63,7 +63,10 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { String var_name = prop_name.get_slicec('/', 1); String what = prop_name.get_slicec('/', 2); ERR_FAIL_COND_V(!data.has(var_name), false); - if (what == "type") { + + if (what == "name") { + r_ret = var_name; + } else if (what == "type") { r_ret = data[var_name].get_type(); } else if (what == "value") { r_ret = data[var_name].get_value(); @@ -111,17 +114,6 @@ void BlackboardPlan::set_base_plan(const Ref &p_base) { sync_with_base_plan(); } -void BlackboardPlan::set_value(const String &p_name, const Variant &p_value) { - ERR_FAIL_COND(!data.has(p_name)); - data.get(p_name).set_value(p_value); - emit_changed(); -} - -Variant BlackboardPlan::get_value(const String &p_name) const { - ERR_FAIL_COND_V(!data.has(p_name), Variant()); - return data.get(p_name).get_value(); -} - void BlackboardPlan::add_var(const String &p_name, const BBVariable &p_var) { ERR_FAIL_COND(data.has(p_name)); ERR_FAIL_COND(base.is_valid()); diff --git a/blackboard/blackboard_plan.h b/blackboard/blackboard_plan.h index ecd9376d..869e851c 100644 --- a/blackboard/blackboard_plan.h +++ b/blackboard/blackboard_plan.h @@ -39,9 +39,6 @@ class BlackboardPlan : public Resource { void set_base_plan(const Ref &p_base); Ref get_base_plan() const { return base; } - void set_value(const String &p_name, const Variant &p_value); - Variant get_value(const String &p_name) const; - void add_var(const String &p_name, const BBVariable &p_var); void remove_var(const String &p_name); BBVariable get_var(const String &p_name); diff --git a/bt/behavior_tree.cpp b/bt/behavior_tree.cpp index 3c425d7d..53d24426 100644 --- a/bt/behavior_tree.cpp +++ b/bt/behavior_tree.cpp @@ -22,6 +22,11 @@ #include "godot_cpp/core/error_macros.hpp" #endif // ! LIMBOAI_GDEXTENSION +void BehaviorTree::set_description(const String &p_value) { + description = p_value; + emit_changed(); +} + void BehaviorTree::set_blackboard_plan(const Ref &p_plan) { blackboard_plan = p_plan; if (blackboard_plan.is_null()) { @@ -30,6 +35,11 @@ void BehaviorTree::set_blackboard_plan(const Ref &p_plan) { emit_changed(); } +void BehaviorTree::set_root_task(const Ref &p_value) { + root_task = p_value; + emit_changed(); +} + Ref BehaviorTree::clone() const { Ref copy = duplicate(false); copy->set_path(""); @@ -64,8 +74,8 @@ void BehaviorTree::_bind_methods() { ClassDB::bind_method(D_METHOD("instantiate", "p_agent", "p_blackboard"), &BehaviorTree::instantiate); ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_root_task", "get_root_task"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_plan", "get_blackboard_plan"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_root_task", "get_root_task"); } BehaviorTree::BehaviorTree() { diff --git a/bt/behavior_tree.h b/bt/behavior_tree.h index a92fa563..c04cd112 100644 --- a/bt/behavior_tree.h +++ b/bt/behavior_tree.h @@ -40,19 +40,13 @@ class BehaviorTree : public Resource { virtual bool editor_can_reload_from_file() override { return false; } #endif + void set_description(const String &p_value); + String get_description() const { return description; } + void set_blackboard_plan(const Ref &p_plan); Ref get_blackboard_plan() const { return blackboard_plan; } - void set_description(String p_value) { - description = p_value; - emit_changed(); - } - String get_description() const { return description; } - - void set_root_task(const Ref &p_value) { - root_task = p_value; - emit_changed(); - } + void set_root_task(const Ref &p_value); Ref get_root_task() const { return root_task; } Ref clone() const; diff --git a/bt/tasks/decorators/bt_new_scope.cpp b/bt/tasks/decorators/bt_new_scope.cpp index 24e38d85..dd462f18 100644 --- a/bt/tasks/decorators/bt_new_scope.cpp +++ b/bt/tasks/decorators/bt_new_scope.cpp @@ -22,7 +22,7 @@ void BTNewScope::initialize(Node *p_agent, const Ref &p_blackboard) bb = Ref(memnew(Blackboard)); } - bb->set_parent_scope(p_blackboard); + bb->set_parent(p_blackboard); BTDecorator::initialize(p_agent, bb); } diff --git a/demo/demo/ai/trees/waypoints.tres b/demo/demo/ai/trees/waypoints.tres index 5254c91d..e48e4852 100644 --- a/demo/demo/ai/trees/waypoints.tres +++ b/demo/demo/ai/trees/waypoints.tres @@ -3,7 +3,7 @@ [ext_resource type="Script" path="res://demo/ai/tasks/arrive_pos.gd" id="1_rhs33"] [sub_resource type="BlackboardPlan" id="BlackboardPlan_2hcqi"] -var/speed/name = null +var/speed/name = "speed" var/speed/type = 3 var/speed/value = 200.0 var/speed/hint = 1 @@ -43,5 +43,5 @@ duration = 3.0 children = [SubResource("BTCooldown_gen0l")] [resource] -root_task = SubResource("BTSelector_5dclr") blackboard_plan = SubResource("BlackboardPlan_2hcqi") +root_task = SubResource("BTSelector_5dclr") diff --git a/demo/demo/examples/waypoints/example_waypoints.gd b/demo/demo/examples/waypoints/example_waypoints.gd index 19d153da..c9ddbe84 100644 --- a/demo/demo/examples/waypoints/example_waypoints.gd +++ b/demo/demo/examples/waypoints/example_waypoints.gd @@ -24,5 +24,3 @@ func _ready() -> void: waypoints.reverse() for wp in waypoints: agent2.add_waypoint(wp.global_position) - - diff --git a/demo/demo/examples/waypoints/patrolling_agent.tscn b/demo/demo/examples/waypoints/patrolling_agent.tscn index 4b1c6091..a91dc6bb 100644 --- a/demo/demo/examples/waypoints/patrolling_agent.tscn +++ b/demo/demo/examples/waypoints/patrolling_agent.tscn @@ -5,9 +5,9 @@ [ext_resource type="Texture2D" uid="uid://d0mht3ntak7e5" path="res://demo/godot.png" id="3_64ge2"] [sub_resource type="BlackboardPlan" id="BlackboardPlan_b86q8"] -var/speed/name = null +var/speed/name = "speed" var/speed/type = 3 -var/speed/value = 200.0 +var/speed/value = 300.0 var/speed/hint = 1 var/speed/hint_string = "10,1000,10" diff --git a/editor/blackboard_plan_editor.h b/editor/blackboard_plan_editor.h index 8812ab27..a69c9af8 100644 --- a/editor/blackboard_plan_editor.h +++ b/editor/blackboard_plan_editor.h @@ -34,7 +34,6 @@ class BlackboardPlanEditor : public AcceptDialog { } theme_cache; int last_index = 0; - int drag_mouse_y_delta = 0; int drag_index = -1; diff --git a/hsm/limbo_hsm.cpp b/hsm/limbo_hsm.cpp index 1eb8616a..8834f527 100644 --- a/hsm/limbo_hsm.cpp +++ b/hsm/limbo_hsm.cpp @@ -190,7 +190,7 @@ bool LimboHSM::dispatch(const String &p_event, const Variant &p_cargo) { void LimboHSM::initialize(Node *p_agent, const Ref &p_parent_scope) { ERR_FAIL_COND(p_agent == nullptr); if (!p_parent_scope.is_null()) { - blackboard->set_parent_scope(p_parent_scope); + blackboard->set_parent(p_parent_scope); } _initialize(p_agent, nullptr); diff --git a/hsm/limbo_state.cpp b/hsm/limbo_state.cpp index 7539ec38..d0e7166a 100644 --- a/hsm/limbo_state.cpp +++ b/hsm/limbo_state.cpp @@ -71,7 +71,7 @@ void LimboState::_initialize(Node *p_agent, const Ref &p_blackboard) if (!p_blackboard.is_null()) { if (blackboard_plan.is_valid() && !blackboard_plan->is_empty()) { blackboard = blackboard_plan->create_blackboard(); - blackboard->set_parent_scope(p_blackboard); + blackboard->set_parent(p_blackboard); } else { blackboard = p_blackboard; } diff --git a/tests/test_new_scope.h b/tests/test_new_scope.h index a16a45b2..421ffbde 100644 --- a/tests/test_new_scope.h +++ b/tests/test_new_scope.h @@ -50,7 +50,7 @@ TEST_CASE("[Modules][LimboAI] BTNewScope") { CHECK(ns->get_blackboard() != parent->get_blackboard()); CHECK(ns->get_blackboard() == child->get_blackboard()); CHECK(parent->get_blackboard() == parent_bb); - CHECK(ns->get_blackboard()->get_parent_scope() == parent_bb); + CHECK(ns->get_blackboard()->get_parent() == parent_bb); ns->get_blackboard()->set_var("fruit", "pear"); // * override "fruit" From a251d92aa375dc1cdb794b3f4e5663fc8e8f518f Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 25 Jan 2024 14:56:59 +0100 Subject: [PATCH 12/15] Update class documentation --- config.py | 1 + doc/source/classes/class_bbfloat32array.rst | 23 +++++++++++ doc/source/classes/class_bbfloat64array.rst | 23 +++++++++++ doc/source/classes/class_bbint32array.rst | 23 +++++++++++ doc/source/classes/class_bbint64array.rst | 23 +++++++++++ doc/source/classes/class_bbparam.rst | 2 +- doc/source/classes/class_bbprojection.rst | 23 +++++++++++ doc/source/classes/class_behaviortree.rst | 25 ++++++++++-- doc/source/classes/class_blackboard.rst | 44 ++++----------------- doc/source/classes/class_blackboardplan.rst | 23 +++++++++++ doc/source/classes/class_btnewscope.rst | 17 +++++--- doc/source/classes/class_btplayer.rst | 19 +++++++++ doc/source/classes/class_limbostate.rst | 33 ++++++++++++---- doc_classes/BTNewScope.xml | 4 +- doc_classes/BTPlayer.xml | 3 ++ doc_classes/BehaviorTree.xml | 3 ++ doc_classes/Blackboard.xml | 21 ++-------- doc_classes/BlackboardPlan.xml | 10 +++++ doc_classes/LimboState.xml | 3 ++ hsm/limbo_state.cpp | 2 +- 20 files changed, 252 insertions(+), 73 deletions(-) create mode 100644 doc/source/classes/class_bbfloat32array.rst create mode 100644 doc/source/classes/class_bbfloat64array.rst create mode 100644 doc/source/classes/class_bbint32array.rst create mode 100644 doc/source/classes/class_bbint64array.rst create mode 100644 doc/source/classes/class_bbprojection.rst create mode 100644 doc/source/classes/class_blackboardplan.rst create mode 100644 doc_classes/BlackboardPlan.xml diff --git a/config.py b/config.py index e5a8134b..458072f2 100644 --- a/config.py +++ b/config.py @@ -60,6 +60,7 @@ def get_doc_classes(): "BehaviorTree", "BehaviorTreeView", "Blackboard", + "BlackboardPlan", "BT", "BTAction", "BTAlwaysFail", diff --git a/doc/source/classes/class_bbfloat32array.rst b/doc/source/classes/class_bbfloat32array.rst new file mode 100644 index 00000000..8add4a9b --- /dev/null +++ b/doc/source/classes/class_bbfloat32array.rst @@ -0,0 +1,23 @@ +:github_url: hide + +.. DO NOT EDIT THIS FILE!!! +.. Generated automatically from Godot engine sources. +.. Generator: https://github.com/godotengine/godot/tree/4.2/doc/tools/make_rst.py. +.. XML source: https://github.com/godotengine/godot/tree/4.2/modules/limboai/doc_classes/BBFloat32Array.xml. + +.. _class_BBFloat32Array: + +BBFloat32Array +============== + +**Inherits:** :ref:`BBParam` + +PackedFloat32Array-type parameter for :ref:`BehaviorTree` tasks. See :ref:`BBParam`. + +.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)` +.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)` +.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)` +.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)` +.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)` +.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)` +.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)` diff --git a/doc/source/classes/class_bbfloat64array.rst b/doc/source/classes/class_bbfloat64array.rst new file mode 100644 index 00000000..3db474ff --- /dev/null +++ b/doc/source/classes/class_bbfloat64array.rst @@ -0,0 +1,23 @@ +:github_url: hide + +.. DO NOT EDIT THIS FILE!!! +.. Generated automatically from Godot engine sources. +.. Generator: https://github.com/godotengine/godot/tree/4.2/doc/tools/make_rst.py. +.. XML source: https://github.com/godotengine/godot/tree/4.2/modules/limboai/doc_classes/BBFloat64Array.xml. + +.. _class_BBFloat64Array: + +BBFloat64Array +============== + +**Inherits:** :ref:`BBParam` + +PackedFloat64Array-type parameter for :ref:`BehaviorTree` tasks. See :ref:`BBParam`. + +.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)` +.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)` +.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)` +.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)` +.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)` +.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)` +.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)` diff --git a/doc/source/classes/class_bbint32array.rst b/doc/source/classes/class_bbint32array.rst new file mode 100644 index 00000000..d4fcaf78 --- /dev/null +++ b/doc/source/classes/class_bbint32array.rst @@ -0,0 +1,23 @@ +:github_url: hide + +.. DO NOT EDIT THIS FILE!!! +.. Generated automatically from Godot engine sources. +.. Generator: https://github.com/godotengine/godot/tree/4.2/doc/tools/make_rst.py. +.. XML source: https://github.com/godotengine/godot/tree/4.2/modules/limboai/doc_classes/BBInt32Array.xml. + +.. _class_BBInt32Array: + +BBInt32Array +============ + +**Inherits:** :ref:`BBParam` + +PackedInt32Array-type parameter for :ref:`BehaviorTree` tasks. See :ref:`BBParam`. + +.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)` +.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)` +.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)` +.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)` +.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)` +.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)` +.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)` diff --git a/doc/source/classes/class_bbint64array.rst b/doc/source/classes/class_bbint64array.rst new file mode 100644 index 00000000..51ebf2ab --- /dev/null +++ b/doc/source/classes/class_bbint64array.rst @@ -0,0 +1,23 @@ +:github_url: hide + +.. DO NOT EDIT THIS FILE!!! +.. Generated automatically from Godot engine sources. +.. Generator: https://github.com/godotengine/godot/tree/4.2/doc/tools/make_rst.py. +.. XML source: https://github.com/godotengine/godot/tree/4.2/modules/limboai/doc_classes/BBInt64Array.xml. + +.. _class_BBInt64Array: + +BBInt64Array +============ + +**Inherits:** :ref:`BBParam` + +PackedInt64Array-type parameter for :ref:`BehaviorTree` tasks. See :ref:`BBParam`. + +.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)` +.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)` +.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)` +.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)` +.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)` +.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)` +.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)` diff --git a/doc/source/classes/class_bbparam.rst b/doc/source/classes/class_bbparam.rst index 32c39341..04ba624f 100644 --- a/doc/source/classes/class_bbparam.rst +++ b/doc/source/classes/class_bbparam.rst @@ -12,7 +12,7 @@ BBParam **Inherits:** -**Inherited By:** :ref:`BBAabb`, :ref:`BBArray`, :ref:`BBBasis`, :ref:`BBBool`, :ref:`BBByteArray`, :ref:`BBColor`, :ref:`BBColorArray`, :ref:`BBDictionary`, :ref:`BBFloat`, :ref:`BBFloatArray`, :ref:`BBInt`, :ref:`BBIntArray`, :ref:`BBNode`, :ref:`BBPlane`, :ref:`BBQuaternion`, :ref:`BBRect2`, :ref:`BBRect2i`, :ref:`BBString`, :ref:`BBStringArray`, :ref:`BBStringName`, :ref:`BBTransform2D`, :ref:`BBTransform3D`, :ref:`BBVariant`, :ref:`BBVector2`, :ref:`BBVector2Array`, :ref:`BBVector2i`, :ref:`BBVector3`, :ref:`BBVector3Array`, :ref:`BBVector3i`, :ref:`BBVector4`, :ref:`BBVector4i` +**Inherited By:** :ref:`BBAabb`, :ref:`BBArray`, :ref:`BBBasis`, :ref:`BBBool`, :ref:`BBByteArray`, :ref:`BBColor`, :ref:`BBColorArray`, :ref:`BBDictionary`, :ref:`BBFloat`, :ref:`BBFloat32Array`, :ref:`BBFloat64Array`, :ref:`BBInt`, :ref:`BBInt32Array`, :ref:`BBInt64Array`, :ref:`BBNode`, :ref:`BBPlane`, :ref:`BBProjection`, :ref:`BBQuaternion`, :ref:`BBRect2`, :ref:`BBRect2i`, :ref:`BBString`, :ref:`BBStringArray`, :ref:`BBStringName`, :ref:`BBTransform2D`, :ref:`BBTransform3D`, :ref:`BBVariant`, :ref:`BBVector2`, :ref:`BBVector2Array`, :ref:`BBVector2i`, :ref:`BBVector3`, :ref:`BBVector3Array`, :ref:`BBVector3i`, :ref:`BBVector4`, :ref:`BBVector4i` A base class for LimboAI typed parameters. diff --git a/doc/source/classes/class_bbprojection.rst b/doc/source/classes/class_bbprojection.rst new file mode 100644 index 00000000..7a091499 --- /dev/null +++ b/doc/source/classes/class_bbprojection.rst @@ -0,0 +1,23 @@ +:github_url: hide + +.. DO NOT EDIT THIS FILE!!! +.. Generated automatically from Godot engine sources. +.. Generator: https://github.com/godotengine/godot/tree/4.2/doc/tools/make_rst.py. +.. XML source: https://github.com/godotengine/godot/tree/4.2/modules/limboai/doc_classes/BBProjection.xml. + +.. _class_BBProjection: + +BBProjection +============ + +**Inherits:** :ref:`BBParam` + +Projection-type parameter for :ref:`BehaviorTree` tasks. See :ref:`BBParam`. + +.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)` +.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)` +.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)` +.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)` +.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)` +.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)` +.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)` diff --git a/doc/source/classes/class_behaviortree.rst b/doc/source/classes/class_behaviortree.rst index e6fc70f2..a1ba8e97 100644 --- a/doc/source/classes/class_behaviortree.rst +++ b/doc/source/classes/class_behaviortree.rst @@ -41,9 +41,11 @@ Properties .. table:: :widths: auto - +--------+-------------------------------------------------------------+--------+ - | String | :ref:`description` | ``""`` | - +--------+-------------------------------------------------------------+--------+ + +---------------------------------------------+---------------------------------------------------------------------+--------+ + | :ref:`BlackboardPlan` | :ref:`blackboard_plan` | | + +---------------------------------------------+---------------------------------------------------------------------+--------+ + | String | :ref:`description` | ``""`` | + +---------------------------------------------+---------------------------------------------------------------------+--------+ .. rst-class:: classref-reftable-group @@ -74,6 +76,23 @@ Methods Property Descriptions --------------------- +.. _class_BehaviorTree_property_blackboard_plan: + +.. rst-class:: classref-property + +:ref:`BlackboardPlan` **blackboard_plan** + +.. rst-class:: classref-property-setget + +- void **set_blackboard_plan** **(** :ref:`BlackboardPlan` value **)** +- :ref:`BlackboardPlan` **get_blackboard_plan** **(** **)** + +Stores and manages variables that will be used in constructing new :ref:`Blackboard` instances. + +.. rst-class:: classref-item-separator + +---- + .. _class_BehaviorTree_property_description: .. rst-class:: classref-property diff --git a/doc/source/classes/class_blackboard.rst b/doc/source/classes/class_blackboard.rst index 7e4672df..c21130be 100644 --- a/doc/source/classes/class_blackboard.rst +++ b/doc/source/classes/class_blackboard.rst @@ -34,23 +34,19 @@ Methods :widths: auto +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`erase_var` **(** Variant p_key **)** | - +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+ - | Dictionary | :ref:`get_data` **(** **)** |const| | + | void | :ref:`erase_var` **(** String p_name **)** | +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+ | :ref:`Blackboard` | :ref:`get_parent_scope` **(** **)** |const| | +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+ - | Variant | :ref:`get_var` **(** Variant p_key, Variant p_default=null **)** |const| | + | Variant | :ref:`get_var` **(** String p_name, Variant p_default=null **)** |const| | +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+ - | bool | :ref:`has_var` **(** Variant p_key **)** |const| | + | bool | :ref:`has_var` **(** String p_name **)** |const| | +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+ | void | :ref:`prefetch_nodepath_vars` **(** Node p_node **)** | +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`set_data` **(** Dictionary p_data **)** | - +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+ | void | :ref:`set_parent_scope` **(** :ref:`Blackboard` p_blackboard **)** | +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+ - | void | :ref:`set_var` **(** Variant p_key, Variant p_value **)** | + | void | :ref:`set_var` **(** String p_name, Variant p_value **)** | +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+ | :ref:`Blackboard` | :ref:`top` **(** **)** |const| | +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------+ @@ -68,7 +64,7 @@ Method Descriptions .. rst-class:: classref-method -void **erase_var** **(** Variant p_key **)** +void **erase_var** **(** String p_name **)** Removes a variable by its name. @@ -76,18 +72,6 @@ Removes a variable by its name. ---- -.. _class_Blackboard_method_get_data: - -.. rst-class:: classref-method - -Dictionary **get_data** **(** **)** |const| - -Returns Blackboard data as a ``Dictionary``. - -.. rst-class:: classref-item-separator - ----- - .. _class_Blackboard_method_get_parent_scope: .. rst-class:: classref-method @@ -104,7 +88,7 @@ Returns a Blackboard that serves as the parent scope for this instance. .. rst-class:: classref-method -Variant **get_var** **(** Variant p_key, Variant p_default=null **)** |const| +Variant **get_var** **(** String p_name, Variant p_default=null **)** |const| Returns variable value. @@ -116,7 +100,7 @@ Returns variable value. .. rst-class:: classref-method -bool **has_var** **(** Variant p_key **)** |const| +bool **has_var** **(** String p_name **)** |const| Returns ``true`` if the Blackboard contains the ``p_key`` variable, including the parent scopes. @@ -136,18 +120,6 @@ If ``true``, any ``NodePath`` variables in the **Blackboard** are replaced with ---- -.. _class_Blackboard_method_set_data: - -.. rst-class:: classref-method - -void **set_data** **(** Dictionary p_data **)** - -Overwrites Blackboard data, replacing any previously stored variables within current scope. Use with caution. - -.. rst-class:: classref-item-separator - ----- - .. _class_Blackboard_method_set_parent_scope: .. rst-class:: classref-method @@ -164,7 +136,7 @@ Assigns the parent scope. If a value isn't in the current Blackboard scope, it w .. rst-class:: classref-method -void **set_var** **(** Variant p_key, Variant p_value **)** +void **set_var** **(** String p_name, Variant p_value **)** Assigns a value to a Blackboard variable. diff --git a/doc/source/classes/class_blackboardplan.rst b/doc/source/classes/class_blackboardplan.rst new file mode 100644 index 00000000..251fab33 --- /dev/null +++ b/doc/source/classes/class_blackboardplan.rst @@ -0,0 +1,23 @@ +:github_url: hide + +.. DO NOT EDIT THIS FILE!!! +.. Generated automatically from Godot engine sources. +.. Generator: https://github.com/godotengine/godot/tree/4.2/doc/tools/make_rst.py. +.. XML source: https://github.com/godotengine/godot/tree/4.2/modules/limboai/doc_classes/BlackboardPlan.xml. + +.. _class_BlackboardPlan: + +BlackboardPlan +============== + +**Inherits:** + +Stores and manages variables that will be used in constructing new :ref:`Blackboard` instances. + +.. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)` +.. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)` +.. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)` +.. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)` +.. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)` +.. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)` +.. |bitfield| replace:: :abbr:`BitField (This value is an integer composed as a bitmask of the following flags.)` diff --git a/doc/source/classes/class_btnewscope.rst b/doc/source/classes/class_btnewscope.rst index 8fe6a9ac..a1f84f67 100644 --- a/doc/source/classes/class_btnewscope.rst +++ b/doc/source/classes/class_btnewscope.rst @@ -33,9 +33,9 @@ Properties .. table:: :widths: auto - +------------+---------------------------------------------------------------------+--------+ - | Dictionary | :ref:`_blackboard_data` | ``{}`` | - +------------+---------------------------------------------------------------------+--------+ + +---------------------------------------------+-------------------------------------------------------------------+ + | :ref:`BlackboardPlan` | :ref:`blackboard_plan` | + +---------------------------------------------+-------------------------------------------------------------------+ .. rst-class:: classref-section-separator @@ -46,13 +46,18 @@ Properties Property Descriptions --------------------- -.. _class_BTNewScope_property__blackboard_data: +.. _class_BTNewScope_property_blackboard_plan: .. rst-class:: classref-property -Dictionary **_blackboard_data** = ``{}`` +:ref:`BlackboardPlan` **blackboard_plan** -Data that is used to populate a new scope of the :ref:`Blackboard`. +.. rst-class:: classref-property-setget + +- void **set_blackboard_plan** **(** :ref:`BlackboardPlan` value **)** +- :ref:`BlackboardPlan` **get_blackboard_plan** **(** **)** + +Stores and manages variables that will be used in constructing new :ref:`Blackboard` instances. .. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)` .. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)` diff --git a/doc/source/classes/class_btplayer.rst b/doc/source/classes/class_btplayer.rst index a8eea4c9..4bb23ea0 100644 --- a/doc/source/classes/class_btplayer.rst +++ b/doc/source/classes/class_btplayer.rst @@ -38,6 +38,8 @@ Properties +---------------------------------------------+-------------------------------------------------------------------------------+-----------+ | :ref:`Blackboard` | :ref:`blackboard` | | +---------------------------------------------+-------------------------------------------------------------------------------+-----------+ + | :ref:`BlackboardPlan` | :ref:`blackboard_plan` | | + +---------------------------------------------+-------------------------------------------------------------------------------+-----------+ | bool | :ref:`monitor_performance` | ``false`` | +---------------------------------------------+-------------------------------------------------------------------------------+-----------+ | bool | :ref:`prefetch_nodepath_vars` | ``true`` | @@ -193,6 +195,23 @@ Holds data shared by the behavior tree tasks. See :ref:`Blackboard` **blackboard_plan** + +.. rst-class:: classref-property-setget + +- void **set_blackboard_plan** **(** :ref:`BlackboardPlan` value **)** +- :ref:`BlackboardPlan` **get_blackboard_plan** **(** **)** + +Stores and manages variables that will be used in constructing new :ref:`Blackboard` instances. + +.. rst-class:: classref-item-separator + +---- + .. _class_BTPlayer_property_monitor_performance: .. rst-class:: classref-property diff --git a/doc/source/classes/class_limbostate.rst b/doc/source/classes/class_limbostate.rst index d2899899..4f10e24f 100644 --- a/doc/source/classes/class_limbostate.rst +++ b/doc/source/classes/class_limbostate.rst @@ -35,13 +35,15 @@ Properties .. table:: :widths: auto - +-------------------------------------+-----------------------------------------------------------------+ - | String | :ref:`EVENT_FINISHED` | - +-------------------------------------+-----------------------------------------------------------------+ - | Node | :ref:`agent` | - +-------------------------------------+-----------------------------------------------------------------+ - | :ref:`Blackboard` | :ref:`blackboard` | - +-------------------------------------+-----------------------------------------------------------------+ + +---------------------------------------------+-------------------------------------------------------------------+ + | String | :ref:`EVENT_FINISHED` | + +---------------------------------------------+-------------------------------------------------------------------+ + | Node | :ref:`agent` | + +---------------------------------------------+-------------------------------------------------------------------+ + | :ref:`Blackboard` | :ref:`blackboard` | + +---------------------------------------------+-------------------------------------------------------------------+ + | :ref:`BlackboardPlan` | :ref:`blackboard_plan` | + +---------------------------------------------+-------------------------------------------------------------------+ .. rst-class:: classref-reftable-group @@ -188,6 +190,23 @@ An agent associated with the state, assigned during initialization. A key/value data store shared by states within the state machine to which this state belongs. +.. rst-class:: classref-item-separator + +---- + +.. _class_LimboState_property_blackboard_plan: + +.. rst-class:: classref-property + +:ref:`BlackboardPlan` **blackboard_plan** + +.. rst-class:: classref-property-setget + +- void **set_blackboard_plan** **(** :ref:`BlackboardPlan` value **)** +- :ref:`BlackboardPlan` **get_blackboard_plan** **(** **)** + +Stores and manages variables that will be used in constructing new :ref:`Blackboard` instances. + .. rst-class:: classref-section-separator ---- diff --git a/doc_classes/BTNewScope.xml b/doc_classes/BTNewScope.xml index 252fa481..ddb1965e 100644 --- a/doc_classes/BTNewScope.xml +++ b/doc_classes/BTNewScope.xml @@ -10,8 +10,8 @@ - - Data that is used to populate a new scope of the [Blackboard]. + + Stores and manages variables that will be used in constructing new [Blackboard] instances. diff --git a/doc_classes/BTPlayer.xml b/doc_classes/BTPlayer.xml index 29cbdbff..a2844686 100644 --- a/doc_classes/BTPlayer.xml +++ b/doc_classes/BTPlayer.xml @@ -40,6 +40,9 @@ Holds data shared by the behavior tree tasks. See [Blackboard]. + + Stores and manages variables that will be used in constructing new [Blackboard] instances. + If [code]true[/code], adds a performance monitor to "Debugger->Monitors" for each instance of this [BTPlayer] node. diff --git a/doc_classes/BehaviorTree.xml b/doc_classes/BehaviorTree.xml index 958c4c54..6f1efb37 100644 --- a/doc_classes/BehaviorTree.xml +++ b/doc_classes/BehaviorTree.xml @@ -51,6 +51,9 @@ + + Stores and manages variables that will be used in constructing new [Blackboard] instances. + User-provided description of the BehaviorTree. diff --git a/doc_classes/Blackboard.xml b/doc_classes/Blackboard.xml index 6fbfbbfc..91f4492b 100644 --- a/doc_classes/Blackboard.xml +++ b/doc_classes/Blackboard.xml @@ -13,17 +13,11 @@ - + Removes a variable by its name. - - - - Returns Blackboard data as a [Dictionary]. - - @@ -32,7 +26,7 @@ - + Returns variable value. @@ -40,7 +34,7 @@ - + Returns [code]true[/code] if the Blackboard contains the [param p_key] variable, including the parent scopes. @@ -52,13 +46,6 @@ If [code]true[/code], any [NodePath] variables in the [Blackboard] are replaced with [Node] references when the tree is instantiated. References are retrieved by calling [method Node.get_node] on the agent instance. - - - - - Overwrites Blackboard data, replacing any previously stored variables within current scope. Use with caution. - - @@ -68,7 +55,7 @@ - + Assigns a value to a Blackboard variable. diff --git a/doc_classes/BlackboardPlan.xml b/doc_classes/BlackboardPlan.xml new file mode 100644 index 00000000..6a7a48c5 --- /dev/null +++ b/doc_classes/BlackboardPlan.xml @@ -0,0 +1,10 @@ + + + + Stores and manages variables that will be used in constructing new [Blackboard] instances. + + + + + + diff --git a/doc_classes/LimboState.xml b/doc_classes/LimboState.xml index f3a3c790..508bf41c 100644 --- a/doc_classes/LimboState.xml +++ b/doc_classes/LimboState.xml @@ -117,6 +117,9 @@ A key/value data store shared by states within the state machine to which this state belongs. + + Stores and manages variables that will be used in constructing new [Blackboard] instances. + diff --git a/hsm/limbo_state.cpp b/hsm/limbo_state.cpp index d0e7166a..bf75f49c 100644 --- a/hsm/limbo_state.cpp +++ b/hsm/limbo_state.cpp @@ -195,7 +195,7 @@ void LimboState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "EVENT_FINISHED", PROPERTY_HINT_NONE, "", 0), "", "event_finished"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "agent", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_agent", "get_agent"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_RESOURCE_TYPE, "Blackboard", 0), "", "get_blackboard"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_blackboard_plan", "get_blackboard_plan"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT), "set_blackboard_plan", "get_blackboard_plan"); ADD_SIGNAL(MethodInfo("setup")); ADD_SIGNAL(MethodInfo("entered")); From 5fee89b00b64bd8b826b943d307e8db5f4445937 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 25 Jan 2024 17:59:38 +0100 Subject: [PATCH 13/15] Make blackboard improvements compatible with GDExtension --- blackboard/bb_variable.h | 9 ++ blackboard/blackboard.cpp | 1 - blackboard/blackboard.h | 3 +- blackboard/blackboard_plan.cpp | 147 +++++++++++++++--------------- blackboard/blackboard_plan.h | 22 +++-- editor/blackboard_plan_editor.cpp | 51 ++++++++--- editor/blackboard_plan_editor.h | 21 +++++ editor/limbo_ai_editor_plugin.cpp | 11 +-- register_types.cpp | 3 + util/limbo_compat.h | 2 + 10 files changed, 165 insertions(+), 105 deletions(-) diff --git a/blackboard/bb_variable.h b/blackboard/bb_variable.h index d6d2371a..cccf2b85 100644 --- a/blackboard/bb_variable.h +++ b/blackboard/bb_variable.h @@ -12,9 +12,18 @@ #ifndef BB_VARIABLE_H #define BB_VARIABLE_H +#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" +using namespace godot; +#endif // LIMBOAI_GDEXTENSION class BBVariable { private: diff --git a/blackboard/blackboard.cpp b/blackboard/blackboard.cpp index d0561526..62584e03 100644 --- a/blackboard/blackboard.cpp +++ b/blackboard/blackboard.cpp @@ -22,7 +22,6 @@ #include #include #include -#include using namespace godot; #endif diff --git a/blackboard/blackboard.h b/blackboard/blackboard.h index 1b79f824..42c04c7e 100644 --- a/blackboard/blackboard.h +++ b/blackboard/blackboard.h @@ -17,7 +17,6 @@ #ifdef LIMBOAI_MODULE #include "core/object/object.h" #include "core/object/ref_counted.h" -#include "core/variant/dictionary.h" #include "core/variant/variant.h" #include "scene/main/node.h" #endif // LIMBOAI_MODULE @@ -27,7 +26,7 @@ #include #include #include -#include +#include using namespace godot; #endif // LIMBOAI_GDEXTENSION diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index f359b5ed..f1a07a5c 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -15,8 +15,8 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) { String prop_name = p_name; // * Editor - if (data.has(prop_name)) { - data[prop_name].set_value(p_value); + if (var_map.has(prop_name)) { + var_map[prop_name].set_value(p_value); return true; } @@ -24,19 +24,19 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) { if (prop_name.begins_with("var/")) { String var_name = prop_name.get_slicec('/', 1); String what = prop_name.get_slicec('/', 2); - if (!data.has(var_name) && what == "name") { - data.insert(var_name, BBVariable()); + if (!var_map.has(var_name) && what == "name") { + add_var(var_name, BBVariable()); } if (what == "name") { // We don't store variable name with the variable. } else if (what == "type") { - data[var_name].set_type((Variant::Type)(int)p_value); + var_map[var_name].set_type((Variant::Type)(int)p_value); } else if (what == "value") { - data[var_name].set_value(p_value); + var_map[var_name].set_value(p_value); } else if (what == "hint") { - data[var_name].set_hint((PropertyHint)(int)p_value); + var_map[var_name].set_hint((PropertyHint)(int)p_value); } else if (what == "hint_string") { - data[var_name].set_hint_string(p_value); + var_map[var_name].set_hint_string(p_value); } else { return false; } @@ -50,8 +50,8 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { String prop_name = p_name; // * Editor - if (data.has(prop_name)) { - r_ret = data[prop_name].get_value(); + if (var_map.has(prop_name)) { + r_ret = var_map[prop_name].get_value(); return true; } @@ -62,26 +62,26 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { String var_name = prop_name.get_slicec('/', 1); String what = prop_name.get_slicec('/', 2); - ERR_FAIL_COND_V(!data.has(var_name), false); + ERR_FAIL_COND_V(!var_map.has(var_name), false); if (what == "name") { r_ret = var_name; } else if (what == "type") { - r_ret = data[var_name].get_type(); + r_ret = var_map[var_name].get_type(); } else if (what == "value") { - r_ret = data[var_name].get_value(); + r_ret = var_map[var_name].get_value(); } else if (what == "hint") { - r_ret = data[var_name].get_hint(); + r_ret = var_map[var_name].get_hint(); } else if (what == "hint_string") { - r_ret = data[var_name].get_hint_string(); + r_ret = var_map[var_name].get_hint_string(); } return true; } void BlackboardPlan::_get_property_list(List *p_list) const { - for (const KeyValue &kv : data) { - String var_name = kv.key; - BBVariable var = kv.value; + for (const Pair &p : var_list) { + String var_name = p.first; + BBVariable var = p.second; // * Editor if (!is_derived() || !var_name.begins_with("_")) { @@ -98,12 +98,12 @@ void BlackboardPlan::_get_property_list(List *p_list) const { } bool BlackboardPlan::_property_can_revert(const StringName &p_name) const { - return base.is_valid() && base->data.has(p_name); + return base.is_valid() && base->var_map.has(p_name); } bool BlackboardPlan::_property_get_revert(const StringName &p_name, Variant &r_property) const { - if (base->data.has(p_name)) { - r_property = base->data[p_name].get_value(); + if (base->var_map.has(p_name)) { + r_property = base->var_map[p_name].get_value(); return true; } return false; @@ -115,54 +115,44 @@ void BlackboardPlan::set_base_plan(const Ref &p_base) { } void BlackboardPlan::add_var(const String &p_name, const BBVariable &p_var) { - ERR_FAIL_COND(data.has(p_name)); - ERR_FAIL_COND(base.is_valid()); - data.insert(p_name, p_var); + ERR_FAIL_COND(var_map.has(p_name)); + var_map.insert(p_name, p_var); + var_list.push_back(Pair(p_name, p_var)); notify_property_list_changed(); emit_changed(); } void BlackboardPlan::remove_var(const String &p_name) { - ERR_FAIL_COND(!data.has(p_name)); - ERR_FAIL_COND(base.is_valid()); - data.erase(p_name); + ERR_FAIL_COND(!var_map.has(p_name)); + var_list.erase(Pair(p_name, var_map[p_name])); + var_map.erase(p_name); notify_property_list_changed(); emit_changed(); } BBVariable BlackboardPlan::get_var(const String &p_name) { - ERR_FAIL_COND_V(!data.has(p_name), BBVariable()); - return data.get(p_name); + ERR_FAIL_COND_V(!var_map.has(p_name), BBVariable()); + return var_map.get(p_name); } Pair BlackboardPlan::get_var_by_index(int p_index) { Pair ret; - ERR_FAIL_INDEX_V(p_index, (int)data.size(), ret); - - int i = 0; - for (const KeyValue &kv : data) { - if (i == p_index) { - ret.first = kv.key; - ret.second = kv.value; - } - i += 1; - } - - return ret; + ERR_FAIL_INDEX_V(p_index, (int)var_map.size(), ret); + return var_list[p_index]; } PackedStringArray BlackboardPlan::list_vars() const { PackedStringArray ret; - for (const KeyValue &kv : data) { - ret.append(kv.key); + for (const Pair &p : var_list) { + ret.append(p.first); } return ret; } String BlackboardPlan::get_var_name(const BBVariable &p_var) const { - for (const KeyValue &kv : data) { - if (kv.value == p_var) { - return kv.key; + for (const Pair &p : var_list) { + if (p.second == p_var) { + return p.first; } } return String(); @@ -170,27 +160,29 @@ String BlackboardPlan::get_var_name(const BBVariable &p_var) const { void BlackboardPlan::rename_var(const String &p_name, const String &p_new_name) { ERR_FAIL_COND(p_new_name.is_empty()); - ERR_FAIL_COND(data.has(p_new_name)); - ERR_FAIL_COND(!data.has(p_name)); + ERR_FAIL_COND(var_map.has(p_new_name)); + ERR_FAIL_COND(!var_map.has(p_name)); + + BBVariable var = var_map[p_name]; + Pair new_entry(p_new_name, var); + Pair old_entry(p_name, var); + var_list.find(old_entry)->set(new_entry); + + var_map.erase(p_name); + var_map.insert(p_new_name, var); - data.replace_key(p_name, p_new_name); notify_property_list_changed(); emit_changed(); } void BlackboardPlan::swap_vars(int p_idx_a, int p_idx_b) { - ERR_FAIL_INDEX(p_idx_a, (int)data.size()); - ERR_FAIL_INDEX(p_idx_b, (int)data.size()); + ERR_FAIL_INDEX(p_idx_a, (int)var_map.size()); + ERR_FAIL_INDEX(p_idx_b, (int)var_map.size()); - Pair a = get_var_by_index(p_idx_a); - Pair b = get_var_by_index(p_idx_b); + Pair a = var_list[p_idx_a]; + Pair b = var_list[p_idx_b]; - data.replace_key(a.first, "__tmp__"); - data.replace_key(b.first, a.first); - data.replace_key("__tmp__", b.first); - - data[b.first] = b.second; - data[a.first] = a.second; + var_list.swap(var_list.find(a), var_list.find(b)); notify_property_list_changed(); emit_changed(); @@ -204,28 +196,31 @@ void BlackboardPlan::sync_with_base_plan() { bool changed = false; // Sync variables with the base plan. - for (const KeyValue &kv : base->data) { - if (!data.has(kv.key)) { - data.insert(kv.key, kv.value.duplicate()); + for (const Pair &p : base->var_list) { + const String &base_name = p.first; + const BBVariable &base_var = p.second; + + if (!var_map.has(base_name)) { + add_var(base_name, base_var.duplicate()); changed = true; continue; } - BBVariable var = data.get(kv.key); - if (!var.is_same_prop_info(kv.value)) { - var.copy_prop_info(kv.value); + BBVariable var = var_map[base_name]; + if (!var.is_same_prop_info(base_var)) { + var.copy_prop_info(base_var); changed = true; } - if (var.get_value().get_type() != kv.value.get_type()) { - var.set_value(kv.value.get_value()); + if (var.get_value().get_type() != base_var.get_type()) { + var.set_value(base_var.get_value()); changed = true; } } // Erase variables that do not exist in the base plan. - for (const KeyValue &kv : data) { - if (!base->data.has(kv.key)) { - data.erase(kv.key); + for (const Pair &p : var_list) { + if (!base->has_var(p.first)) { + remove_var(p.first); changed = true; } } @@ -238,22 +233,22 @@ void BlackboardPlan::sync_with_base_plan() { Ref BlackboardPlan::create_blackboard() { Ref bb = memnew(Blackboard); - for (const KeyValue &kv : data) { - bb->add_var(kv.key, kv.value.duplicate()); + for (const Pair &p : var_list) { + bb->add_var(p.first, p.second.duplicate()); } return bb; } void BlackboardPlan::populate_blackboard(const Ref &p_blackboard, bool overwrite) { - for (const KeyValue &kv : data) { - if (p_blackboard->has_var(kv.key)) { + for (const Pair &p : var_list) { + if (p_blackboard->has_var(p.first)) { if (overwrite) { - p_blackboard->erase_var(kv.key); + p_blackboard->erase_var(p.first); } else { continue; } } - p_blackboard->add_var(kv.key, kv.value.duplicate()); + p_blackboard->add_var(p.first, p.second.duplicate()); } } diff --git a/blackboard/blackboard_plan.h b/blackboard/blackboard_plan.h index 869e851c..c4de76a8 100644 --- a/blackboard/blackboard_plan.h +++ b/blackboard/blackboard_plan.h @@ -12,16 +12,24 @@ #ifndef BLACKBOARD_PLAN_H #define BLACKBOARD_PLAN_H -#include "core/io/resource.h" - #include "bb_variable.h" #include "blackboard.h" +#ifdef LIMBOAI_MODULE +#include "core/io/resource.h" +#endif // LIMBOAI_MODULE + +#ifdef LIMBOAI_GDEXTENSION +#include +using namespace godot; +#endif // LIMBOAI_GDEXTENSION + class BlackboardPlan : public Resource { GDCLASS(BlackboardPlan, Resource); private: - HashMap data; + List> var_list; + HashMap var_map; // When base is not null, the plan is considered to be derived from the base plan. // A derived plan can only have variables that exist in the base plan, @@ -29,6 +37,8 @@ class BlackboardPlan : public Resource { Ref base; protected: + static void _bind_methods() {} + bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List *p_list) const; @@ -43,9 +53,9 @@ class BlackboardPlan : public Resource { void remove_var(const String &p_name); BBVariable get_var(const String &p_name); Pair get_var_by_index(int p_index); - bool has_var(const String &p_name) { return data.has(p_name); } - bool is_empty() const { return data.is_empty(); } - int get_var_count() const { return data.size(); } + bool has_var(const String &p_name) { return var_map.has(p_name); } + bool is_empty() const { return var_map.is_empty(); } + int get_var_count() const { return var_map.size(); } PackedStringArray list_vars() const; String get_var_name(const BBVariable &p_var) const; diff --git a/editor/blackboard_plan_editor.cpp b/editor/blackboard_plan_editor.cpp index 504fb55d..8201d992 100644 --- a/editor/blackboard_plan_editor.cpp +++ b/editor/blackboard_plan_editor.cpp @@ -11,6 +11,7 @@ #include "blackboard_plan_editor.h" +#include "../util/limbo_compat.h" #include "../util/limbo_string_names.h" #include "../util/limbo_utility.h" @@ -22,6 +23,19 @@ #include "scene/resources/style_box_flat.h" #endif // LIMBOAI_MODULE +#ifdef LIMBOAI_GDEXTENSION +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace godot; +#endif // LIMBOAI_GDEXTENSION + void BlackboardPlanEditor::_add_var() { ERR_FAIL_NULL(plan); @@ -78,7 +92,8 @@ void BlackboardPlanEditor::_show_button_popup(Button *p_button, PopupMenu *p_pop ERR_FAIL_NULL(p_button); ERR_FAIL_NULL(p_popup); - Rect2 rect = p_button->get_screen_rect(); + Transform2D xform = p_button->get_screen_transform(); + Rect2 rect(xform.get_origin(), xform.get_scale() * p_button->get_size()); rect.position.y += rect.size.height; rect.size.height = 0; p_popup->set_size(rect.size); @@ -137,8 +152,8 @@ void BlackboardPlanEditor::_drag_button_gui_input(const Ref &p_event ERR_FAIL_NULL(row); ERR_FAIL_NULL(other_row); rows_vbox->move_child(row, drag_index + drag_dir); - row->add_theme_style_override(LW_NAME(panel), row->get_index() % 2 ? theme_cache.odd_style : theme_cache.even_style); - other_row->add_theme_style_override(LW_NAME(panel), other_row->get_index() % 2 ? theme_cache.odd_style : theme_cache.even_style); + ADD_STYLEBOX_OVERRIDE(row, LW_NAME(panel), row->get_index() % 2 ? theme_cache.odd_style : theme_cache.even_style); + ADD_STYLEBOX_OVERRIDE(other_row, LW_NAME(panel), other_row->get_index() % 2 ? theme_cache.odd_style : theme_cache.even_style); drag_index += drag_dir; } @@ -167,7 +182,7 @@ void BlackboardPlanEditor::_refresh() { PanelContainer *row_panel = memnew(PanelContainer); rows_vbox->add_child(row_panel); - row_panel->add_theme_style_override(LW_NAME(panel), idx % 2 ? theme_cache.odd_style : theme_cache.even_style); + ADD_STYLEBOX_OVERRIDE(row_panel, LW_NAME(panel), idx % 2 ? theme_cache.odd_style : theme_cache.even_style); row_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL); HBoxContainer *props_hbox = memnew(HBoxContainer); @@ -177,7 +192,7 @@ void BlackboardPlanEditor::_refresh() { Button *drag_button = memnew(Button); props_hbox->add_child(drag_button); drag_button->set_custom_minimum_size(Size2(28.0, 28.0) * EDSCALE); - drag_button->set_icon(theme_cache.grab_icon); + BUTTON_SET_ICON(drag_button, theme_cache.grab_icon); drag_button->connect(LW_NAME(gui_input), callable_mp(this, &BlackboardPlanEditor::_drag_button_gui_input)); drag_button->connect(LW_NAME(button_down), callable_mp(this, &BlackboardPlanEditor::_drag_button_down).bind(row_panel)); drag_button->connect(LW_NAME(button_up), callable_mp(this, &BlackboardPlanEditor::_drag_button_up)); @@ -196,7 +211,7 @@ void BlackboardPlanEditor::_refresh() { type_choice->set_custom_minimum_size(Size2(170, 0.0) * EDSCALE); type_choice->set_text(Variant::get_type_name(var.get_type())); type_choice->set_tooltip_text(Variant::get_type_name(var.get_type())); - type_choice->set_icon(get_theme_icon(Variant::get_type_name(var.get_type()), LW_NAME(EditorIcons))); + BUTTON_SET_ICON(type_choice, get_theme_icon(Variant::get_type_name(var.get_type()), LW_NAME(EditorIcons))); type_choice->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); type_choice->set_flat(true); type_choice->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); @@ -225,7 +240,7 @@ void BlackboardPlanEditor::_refresh() { Button *trash_button = memnew(Button); props_hbox->add_child(trash_button); trash_button->set_custom_minimum_size(Size2(24.0, 0.0) * EDSCALE); - trash_button->set_icon(theme_cache.trash_icon); + BUTTON_SET_ICON(trash_button, theme_cache.trash_icon); trash_button->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_trash_var).bind(idx)); idx += 1; @@ -238,7 +253,7 @@ void BlackboardPlanEditor::_notification(int p_what) { theme_cache.trash_icon = get_theme_icon(LW_NAME(Remove), LW_NAME(EditorIcons)); theme_cache.grab_icon = get_theme_icon(LW_NAME(TripleBar), LW_NAME(EditorIcons)); - add_var_tool->set_icon(get_theme_icon(LW_NAME(Add), LW_NAME(EditorIcons))); + BUTTON_SET_ICON(add_var_tool, get_theme_icon(LW_NAME(Add), LW_NAME(EditorIcons))); type_menu->clear(); for (int i = 0; i < Variant::VARIANT_MAX; i++) { @@ -249,18 +264,20 @@ void BlackboardPlanEditor::_notification(int p_what) { type_menu->add_icon_item(get_theme_icon(type, LW_NAME(EditorIcons)), type, i); } - scroll_container->add_theme_style_override(LW_NAME(panel), get_theme_stylebox(LW_NAME(panel), LW_NAME(Tree))); + ADD_STYLEBOX_OVERRIDE(scroll_container, LW_NAME(panel), get_theme_stylebox(LW_NAME(panel), LW_NAME(Tree))); Color bg_color = get_theme_color(LW_NAME(dark_color_2), LW_NAME(Editor)); theme_cache.odd_style->set_bg_color(bg_color.darkened(-0.05)); theme_cache.even_style->set_bg_color(bg_color.darkened(0.05)); theme_cache.header_style->set_bg_color(bg_color.darkened(-0.2)); - header_row->add_theme_style_override(LW_NAME(panel), theme_cache.header_style); + ADD_STYLEBOX_OVERRIDE(header_row, LW_NAME(panel), theme_cache.header_style); } break; case NOTIFICATION_READY: { add_var_tool->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_add_var)); connect(LW_NAME(visibility_changed), callable_mp(this, &BlackboardPlanEditor::_visibility_changed)); + type_menu->connect(LW_NAME(id_pressed), callable_mp(this, &BlackboardPlanEditor::_type_chosen)); + hint_menu->connect(LW_NAME(id_pressed), callable_mp(this, &BlackboardPlanEditor::_hint_chosen)); } break; } } @@ -338,11 +355,9 @@ BlackboardPlanEditor::BlackboardPlanEditor() { type_menu = memnew(PopupMenu); add_child(type_menu); - type_menu->connect(LW_NAME(id_pressed), callable_mp(this, &BlackboardPlanEditor::_type_chosen)); hint_menu = memnew(PopupMenu); add_child(hint_menu); - hint_menu->connect(LW_NAME(id_pressed), callable_mp(this, &BlackboardPlanEditor::_hint_chosen)); for (int i = 0; i < PropertyHint::PROPERTY_HINT_MAX; i++) { hint_menu->add_item(LimboUtility::get_singleton()->get_property_hint_text(PropertyHint(i)), i); } @@ -352,7 +367,7 @@ BlackboardPlanEditor::BlackboardPlanEditor() { theme_cache.header_style.instantiate(); } -// ***** +// ***** EditorInspectorPluginBBPlan ***** void EditorInspectorPluginBBPlan::_edit_plan(const Ref &p_plan) { ERR_FAIL_NULL(p_plan); @@ -366,7 +381,11 @@ void EditorInspectorPluginBBPlan::_open_base_plan(const Ref &p_p EditorInterface::get_singleton()->call_deferred("edit_resource", p_plan->get_base_plan()); } +#ifdef LIMBOAI_MODULE bool EditorInspectorPluginBBPlan::can_handle(Object *p_object) { +#elif LIMBOAI_GDEXTENSION +bool EditorInspectorPluginBBPlan::_can_handle(Object *p_object) const { +#endif Ref plan = Object::cast_to(p_object); if (plan.is_valid()) { plan->sync_with_base_plan(); @@ -374,12 +393,16 @@ bool EditorInspectorPluginBBPlan::can_handle(Object *p_object) { return plan.is_valid(); } +#ifdef LIMBOAI_MODULE void EditorInspectorPluginBBPlan::parse_begin(Object *p_object) { +#elif LIMBOAI_GDEXTENSION +void EditorInspectorPluginBBPlan::_parse_begin(Object *p_object) { +#endif Ref plan = Object::cast_to(p_object); ERR_FAIL_NULL(plan); PanelContainer *panel = memnew(PanelContainer); - panel->add_theme_style_override(LW_NAME(panel), toolbar_style); + ADD_STYLEBOX_OVERRIDE(panel, LW_NAME(panel), toolbar_style); MarginContainer *margin_container = memnew(MarginContainer); panel->add_child(margin_container); diff --git a/editor/blackboard_plan_editor.h b/editor/blackboard_plan_editor.h index a69c9af8..ae81f2fe 100644 --- a/editor/blackboard_plan_editor.h +++ b/editor/blackboard_plan_editor.h @@ -19,6 +19,17 @@ #include "scene/gui/dialogs.h" #endif // LIMBOAI_MODULE +#ifdef LIMBOAI_GDEXTENSION +#include +#include +#include +#include +#include +#include +#include +using namespace godot; +#endif // LIMBOAI_GDEXTENSION + // ***** class BlackboardPlanEditor : public AcceptDialog { @@ -65,6 +76,8 @@ class BlackboardPlanEditor : public AcceptDialog { void _visibility_changed(); protected: + static void _bind_methods() {} + void _notification(int p_what); public: @@ -85,9 +98,17 @@ class EditorInspectorPluginBBPlan : public EditorInspectorPlugin { void _edit_plan(const Ref &p_plan); void _open_base_plan(const Ref &p_plan); +protected: + static void _bind_methods() {} + public: +#ifdef LIMBOAI_MODULE virtual bool can_handle(Object *p_object) override; virtual void parse_begin(Object *p_object) override; +#elif LIMBOAI_GDEXTENSION + virtual bool _can_handle(Object *p_object) const override; + virtual void _parse_begin(Object *p_object) override; +#endif EditorInspectorPluginBBPlan(); }; diff --git a/editor/limbo_ai_editor_plugin.cpp b/editor/limbo_ai_editor_plugin.cpp index 6477c295..fa361201 100644 --- a/editor/limbo_ai_editor_plugin.cpp +++ b/editor/limbo_ai_editor_plugin.cpp @@ -1394,6 +1394,11 @@ void LimboAIEditorPlugin::_notification(int p_notification) { switch (p_notification) { case NOTIFICATION_READY: { add_debugger_plugin(memnew(LimboDebuggerPlugin)); + add_inspector_plugin(memnew(EditorInspectorPluginBBPlan)); +#ifdef LIMBOAI_MODULE + // ! Only used in the module version. + add_inspector_plugin(memnew(EditorInspectorPluginBBParam)); +#endif // LIMBOAI_MODULE } break; case NOTIFICATION_ENTER_TREE: { // Add BehaviorTree to the list of resources that should open in a new inspector. @@ -1447,12 +1452,6 @@ LimboAIEditorPlugin::LimboAIEditorPlugin() { MAIN_SCREEN_CONTROL()->add_child(limbo_ai_editor); limbo_ai_editor->hide(); limbo_ai_editor->set_plugin(this); - - add_inspector_plugin(memnew(EditorInspectorPluginBBPlan)); -#ifdef LIMBOAI_MODULE - // ! Only used in the module version. - add_inspector_plugin(memnew(EditorInspectorPluginBBParam)); -#endif // LIMBOAI_MODULE } LimboAIEditorPlugin::~LimboAIEditorPlugin() { diff --git a/register_types.cpp b/register_types.cpp index ad8d0a0e..be7be652 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -95,6 +95,7 @@ #include "bt/tasks/utility/bt_wait.h" #include "bt/tasks/utility/bt_wait_ticks.h" #include "editor/action_banner.h" +#include "editor/blackboard_plan_editor.h" #include "editor/debugger/behavior_tree_data.h" #include "editor/debugger/limbo_debugger.h" #include "editor/debugger/limbo_debugger_plugin.h" @@ -253,6 +254,8 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(BehaviorTreeView); GDREGISTER_CLASS(LimboDebuggerTab); GDREGISTER_CLASS(LimboDebuggerPlugin); + GDREGISTER_CLASS(BlackboardPlanEditor); + GDREGISTER_CLASS(EditorInspectorPluginBBPlan); GDREGISTER_CLASS(LimboAIEditor); GDREGISTER_CLASS(LimboAIEditorPlugin); #endif // LIMBOAI_GDEXTENSION diff --git a/util/limbo_compat.h b/util/limbo_compat.h index 762aeaf4..fdfce9a1 100644 --- a/util/limbo_compat.h +++ b/util/limbo_compat.h @@ -52,6 +52,7 @@ #define DIR_ACCESS_CREATE() DirAccess::create(DirAccess::ACCESS_RESOURCES) #define PERFORMANCE_ADD_CUSTOM_MONITOR(m_id, m_callable) (Performance::get_singleton()->add_custom_monitor(m_id, m_callable, Variant())) #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)) #define VARIANT_EVALUATE(m_op, m_lvalue, m_rvalue, r_ret) r_ret = Variant::evaluate(m_op, m_lvalue, m_rvalue) @@ -130,6 +131,7 @@ using namespace godot; #define DIR_ACCESS_CREATE() DirAccess::open("res://") #define PERFORMANCE_ADD_CUSTOM_MONITOR(m_id, m_callable) (Performance::get_singleton()->add_custom_monitor(m_id, m_callable)) #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)) #define VARIANT_EVALUATE(m_op, m_lvalue, m_rvalue, r_ret) \ { \ From a22860b244d16ee9d4cdf6b35382051f4c9db61b Mon Sep 17 00:00:00 2001 From: "Ola S." Date: Thu, 25 Jan 2024 21:08:03 +0100 Subject: [PATCH 14/15] Add BlackboardPlan icon --- icons/BlackboardPlan.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 icons/BlackboardPlan.svg diff --git a/icons/BlackboardPlan.svg b/icons/BlackboardPlan.svg new file mode 100644 index 00000000..958fba3b --- /dev/null +++ b/icons/BlackboardPlan.svg @@ -0,0 +1 @@ + \ No newline at end of file From f912f0acc4af4f516e17bf6beaebc1110d099f19 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Thu, 25 Jan 2024 22:01:14 +0100 Subject: [PATCH 15/15] Usability improvements --- blackboard/blackboard_plan.cpp | 25 +++++++++++++++++++------ blackboard/blackboard_plan.h | 2 +- editor/blackboard_plan_editor.cpp | 17 ++++++++++------- editor/blackboard_plan_editor.h | 1 + editor/limbo_ai_editor_plugin.cpp | 11 ++++++++++- util/limbo_string_names.cpp | 2 ++ util/limbo_string_names.h | 2 ++ 7 files changed, 45 insertions(+), 15 deletions(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index f1a07a5c..a52cb6cd 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -175,14 +175,27 @@ void BlackboardPlan::rename_var(const String &p_name, const String &p_new_name) emit_changed(); } -void BlackboardPlan::swap_vars(int p_idx_a, int p_idx_b) { - ERR_FAIL_INDEX(p_idx_a, (int)var_map.size()); - ERR_FAIL_INDEX(p_idx_b, (int)var_map.size()); +void BlackboardPlan::move_var(int p_index, int p_new_index) { + ERR_FAIL_INDEX(p_index, (int)var_map.size()); + ERR_FAIL_INDEX(p_new_index, (int)var_map.size()); - Pair a = var_list[p_idx_a]; - Pair b = var_list[p_idx_b]; + if (p_index == p_new_index) { + return; + } + + List>::Element *E = var_list.front(); + for (int i = 0; i < p_index; i++) { + E = E->next(); + } + List>::Element *E2 = var_list.front(); + for (int i = 0; i < p_new_index; i++) { + E2 = E2->next(); + } - var_list.swap(var_list.find(a), var_list.find(b)); + var_list.move_before(E, E2); + if (p_new_index > p_index) { + var_list.move_before(E2, E); + } notify_property_list_changed(); emit_changed(); diff --git a/blackboard/blackboard_plan.h b/blackboard/blackboard_plan.h index c4de76a8..e5a315af 100644 --- a/blackboard/blackboard_plan.h +++ b/blackboard/blackboard_plan.h @@ -60,7 +60,7 @@ class BlackboardPlan : public Resource { PackedStringArray list_vars() const; String get_var_name(const BBVariable &p_var) const; void rename_var(const String &p_name, const String &p_new_name); - void swap_vars(int idx_a, int idx_b); + void move_var(int p_index, int p_new_index); void sync_with_base_plan(); bool is_derived() const { return base.is_valid(); } diff --git a/editor/blackboard_plan_editor.cpp b/editor/blackboard_plan_editor.cpp index 8201d992..bde3d017 100644 --- a/editor/blackboard_plan_editor.cpp +++ b/editor/blackboard_plan_editor.cpp @@ -113,13 +113,16 @@ void BlackboardPlanEditor::_hint_chosen(int id) { void BlackboardPlanEditor::_drag_button_down(Control *p_row) { drag_index = p_row->get_index(); + drag_start = drag_index; drag_mouse_y_delta = 0.0; Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED); } void BlackboardPlanEditor::_drag_button_up() { - drag_index = -1; Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + plan->move_var(drag_start, drag_index); + drag_index = -1; + drag_start = -1; _refresh(); } @@ -140,13 +143,11 @@ void BlackboardPlanEditor::_drag_button_gui_input(const Ref &p_event return; } - float required_distance = 20.0f * EDSCALE; + float required_distance = 30.0f * EDSCALE; if (ABS(drag_mouse_y_delta) > required_distance) { int drag_dir = drag_mouse_y_delta > 0.0f ? 1 : -1; drag_mouse_y_delta -= required_distance * drag_dir; - plan->swap_vars(drag_index, drag_index + drag_dir); - Control *row = Object::cast_to(rows_vbox->get_child(drag_index)); Control *other_row = Object::cast_to(rows_vbox->get_child(drag_index + drag_dir)); ERR_FAIL_NULL(row); @@ -412,21 +413,23 @@ void EditorInspectorPluginBBPlan::_parse_begin(Object *p_object) { VBoxContainer *toolbar = memnew(VBoxContainer); margin_container->add_child(toolbar); - toolbar->set_h_size_flags(Control::SIZE_SHRINK_CENTER); + toolbar->set_h_size_flags(Control::SIZE_EXPAND_FILL); if (plan->is_derived()) { Button *goto_btn = memnew(Button); toolbar->add_child(goto_btn); goto_btn->set_text(TTR("Edit Base")); goto_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER); - goto_btn->set_custom_minimum_size(Size2(200.0, 0.0) * EDSCALE); + goto_btn->set_custom_minimum_size(Size2(150.0, 0.0) * EDSCALE); + BUTTON_SET_ICON(goto_btn, EditorInterface::get_singleton()->get_editor_theme()->get_icon(LW_NAME(Edit), LW_NAME(EditorIcons))); goto_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_open_base_plan).bind(plan)); } else { Button *edit_btn = memnew(Button); toolbar->add_child(edit_btn); edit_btn->set_text(TTR("Manage...")); edit_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER); - edit_btn->set_custom_minimum_size(Size2(200.0, 0.0) * EDSCALE); + edit_btn->set_custom_minimum_size(Size2(150.0, 0.0) * EDSCALE); + BUTTON_SET_ICON(edit_btn, EditorInterface::get_singleton()->get_editor_theme()->get_icon(LW_NAME(EditAddRemove), LW_NAME(EditorIcons))); edit_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_edit_plan).bind(plan)); } diff --git a/editor/blackboard_plan_editor.h b/editor/blackboard_plan_editor.h index ae81f2fe..0cceb3a9 100644 --- a/editor/blackboard_plan_editor.h +++ b/editor/blackboard_plan_editor.h @@ -46,6 +46,7 @@ class BlackboardPlanEditor : public AcceptDialog { int last_index = 0; int drag_mouse_y_delta = 0; + int drag_start = -1; int drag_index = -1; Ref plan; diff --git a/editor/limbo_ai_editor_plugin.cpp b/editor/limbo_ai_editor_plugin.cpp index fa361201..788f1fd1 100644 --- a/editor/limbo_ai_editor_plugin.cpp +++ b/editor/limbo_ai_editor_plugin.cpp @@ -184,8 +184,9 @@ void LimboAIEditor::_update_history_buttons() { } void LimboAIEditor::_new_bt() { - BehaviorTree *bt = memnew(BehaviorTree); + Ref bt = memnew(BehaviorTree); bt->set_root_task(memnew(BTSelector)); + bt->set_blackboard_plan(memnew(BlackboardPlan)); EDIT_RESOURCE(bt); } @@ -222,6 +223,11 @@ void LimboAIEditor::edit_bt(Ref p_behavior_tree, bool p_force_refr return; } +#ifdef LIMBOAI_MODULE + p_behavior_tree->editor_set_section_unfold("blackboard_plan", true); + p_behavior_tree->notify_property_list_changed(); +#endif // LIMBOAI_MODULE + task_tree->load_bt(p_behavior_tree); int idx = history.find(p_behavior_tree); @@ -698,6 +704,9 @@ void LimboAIEditor::_on_visibility_changed() { void LimboAIEditor::_on_header_pressed() { _update_header(); task_tree->deselect(); +#ifdef LIMBOAI_MODULE + task_tree->get_bt()->editor_set_section_unfold("blackboard_plan", true); +#endif // LIMBOAI_MODULE EDIT_RESOURCE(task_tree->get_bt()); } diff --git a/util/limbo_string_names.cpp b/util/limbo_string_names.cpp index ab920075..25b55d1d 100644 --- a/util/limbo_string_names.cpp +++ b/util/limbo_string_names.cpp @@ -64,6 +64,8 @@ LimboStringNames::LimboStringNames() { doc_italic = SN("doc_italic"); draw = SN("draw"); Duplicate = SN("Duplicate"); + Edit = SN("Edit"); + EditAddRemove = SN("EditAddRemove"); Editor = SN("Editor"); EditorFonts = SN("EditorFonts"); EditorIcons = SN("EditorIcons"); diff --git a/util/limbo_string_names.h b/util/limbo_string_names.h index 85d7cf1b..bccbafda 100644 --- a/util/limbo_string_names.h +++ b/util/limbo_string_names.h @@ -78,6 +78,8 @@ class LimboStringNames { StringName doc_italic; StringName draw; StringName Duplicate; + StringName Edit; + StringName EditAddRemove; StringName Editor; StringName EditorFonts; StringName EditorIcons;