Merge pull request #106 from limbonaut/mapping

Mapping variables
This commit is contained in:
Serhii Snitsaruk 2024-05-18 13:47:42 +02:00 committed by GitHub
commit ff61d55c44
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 464 additions and 95 deletions

View File

@ -11,6 +11,8 @@
#include "blackboard_plan.h" #include "blackboard_plan.h"
#include "../util/limbo_utility.h"
bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) { bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
String name_str = p_name; String name_str = p_name;
@ -25,6 +27,28 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
return true; return true;
} }
// * Mapping
if (name_str.begins_with("mapping/")) {
StringName mapped_var_name = name_str.get_slicec('/', 1);
StringName value = p_value;
bool properties_changed = false;
if (value == StringName()) {
if (parent_scope_mapping.has(mapped_var_name)) {
properties_changed = true;
parent_scope_mapping.erase(mapped_var_name);
}
} else {
if (!parent_scope_mapping.has(mapped_var_name)) {
properties_changed = true;
}
parent_scope_mapping[mapped_var_name] = value;
}
if (properties_changed) {
notify_property_list_changed();
}
return true;
}
// * Storage // * Storage
if (name_str.begins_with("var/")) { if (name_str.begins_with("var/")) {
StringName var_name = name_str.get_slicec('/', 1); StringName var_name = name_str.get_slicec('/', 1);
@ -56,7 +80,23 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const {
// * Editor // * Editor
if (var_map.has(p_name)) { if (var_map.has(p_name)) {
if (has_mapping(p_name)) {
r_ret = "Mapped to " + LimboUtility::get_singleton()->decorate_var(parent_scope_mapping[p_name]);
} else {
r_ret = var_map[p_name].get_value(); r_ret = var_map[p_name].get_value();
}
return true;
}
// * Mapping
if (name_str.begins_with("mapping/")) {
StringName mapped_var_name = name_str.get_slicec('/', 1);
ERR_FAIL_COND_V(mapped_var_name == StringName(), false);
if (parent_scope_mapping.has(mapped_var_name)) {
r_ret = parent_scope_mapping[mapped_var_name];
} else {
r_ret = StringName();
}
return true; return true;
} }
@ -64,7 +104,6 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const {
if (!name_str.begins_with("var/")) { if (!name_str.begins_with("var/")) {
return false; return false;
} }
StringName var_name = name_str.get_slicec('/', 1); StringName var_name = name_str.get_slicec('/', 1);
String what = name_str.get_slicec('/', 2); String what = name_str.get_slicec('/', 2);
ERR_FAIL_COND_V(!var_map.has(var_name), false); ERR_FAIL_COND_V(!var_map.has(var_name), false);
@ -90,29 +129,49 @@ void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {
// * Editor // * Editor
if (var.get_type() != Variant::NIL && (!is_derived() || !var_name.begins_with("_"))) { if (var.get_type() != Variant::NIL && (!is_derived() || !var_name.begins_with("_"))) {
if (has_mapping(var_name)) {
p_list->push_back(PropertyInfo(Variant::STRING, var_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
} else {
p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR)); p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR));
} }
}
// * Storage
if (is_derived() && (!var.is_value_changed() || var.get_value() == base->var_map[var_name].get_value())) { if (is_derived() && (!var.is_value_changed() || var.get_value() == base->var_map[var_name].get_value())) {
// Don't store variable if it's not modified in a derived plan. // Don't store variable if it's not modified in a derived plan.
// Variable is considered modified when it's marked as changed and its value is different from the base plan. // Variable is considered modified when it's marked as changed and its value is different from the base plan.
continue; continue;
} }
// * 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::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(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(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::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)); p_list->push_back(PropertyInfo(Variant::STRING, "var/" + var_name + "/hint_string", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
} }
// * Mapping
if (is_mapping_enabled()) {
p_list->push_back(PropertyInfo(Variant::NIL, "Mapping", PROPERTY_HINT_NONE, "mapping/", PROPERTY_USAGE_GROUP));
for (const Pair<StringName, BBVariable> &p : var_list) {
// Serialize only non-empty mappings.
PropertyUsageFlags usage = has_mapping(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR;
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "mapping/" + p.first, PROPERTY_HINT_NONE, "", usage));
}
}
} }
bool BlackboardPlan::_property_can_revert(const StringName &p_name) const { bool BlackboardPlan::_property_can_revert(const StringName &p_name) const {
if (String(p_name).begins_with("mapping/")) {
return true;
}
return base.is_valid() && base->var_map.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 { bool BlackboardPlan::_property_get_revert(const StringName &p_name, Variant &r_property) const {
if (String(p_name).begins_with("mapping/")) {
r_property = StringName();
return true;
}
if (base->var_map.has(p_name)) { if (base->var_map.has(p_name)) {
r_property = base->var_map[p_name].get_value(); r_property = base->var_map[p_name].get_value();
return true; return true;
@ -131,6 +190,15 @@ void BlackboardPlan::set_base_plan(const Ref<BlackboardPlan> &p_base) {
notify_property_list_changed(); notify_property_list_changed();
} }
void BlackboardPlan::set_parent_scope_plan_provider(const Callable &p_parent_scope_plan_provider) {
parent_scope_plan_provider = p_parent_scope_plan_provider;
notify_property_list_changed();
}
bool BlackboardPlan::has_mapping(const StringName &p_name) const {
return is_mapping_enabled() && parent_scope_mapping.has(p_name) && parent_scope_mapping[p_name] != StringName();
}
void BlackboardPlan::set_prefetch_nodepath_vars(bool p_enable) { void BlackboardPlan::set_prefetch_nodepath_vars(bool p_enable) {
prefetch_nodepath_vars = p_enable; prefetch_nodepath_vars = p_enable;
emit_changed(); emit_changed();
@ -214,6 +282,11 @@ void BlackboardPlan::rename_var(const StringName &p_name, const StringName &p_ne
var_map.erase(p_name); var_map.erase(p_name);
var_map.insert(p_new_name, var); var_map.insert(p_new_name, var);
if (parent_scope_mapping.has(p_name)) {
parent_scope_mapping[p_new_name] = parent_scope_mapping[p_name];
parent_scope_mapping.erase(p_name);
}
notify_property_list_changed(); notify_property_list_changed();
emit_changed(); emit_changed();
} }
@ -335,12 +408,11 @@ inline void bb_add_var_dup_with_prefetch(const Ref<Blackboard> &p_blackboard, co
} }
} }
Ref<Blackboard> BlackboardPlan::create_blackboard(Node *p_node) { Ref<Blackboard> BlackboardPlan::create_blackboard(Node *p_node, const Ref<Blackboard> &p_parent_scope) {
ERR_FAIL_COND_V(p_node == nullptr && prefetch_nodepath_vars, memnew(Blackboard)); ERR_FAIL_COND_V(p_node == nullptr && prefetch_nodepath_vars, memnew(Blackboard));
Ref<Blackboard> bb = memnew(Blackboard); Ref<Blackboard> bb = memnew(Blackboard);
for (const Pair<StringName, BBVariable> &p : var_list) { bb->set_parent(p_parent_scope);
bb_add_var_dup_with_prefetch(bb, p.first, p.second, prefetch_nodepath_vars, p_node); populate_blackboard(bb, true, p_node);
}
return bb; return bb;
} }
@ -351,6 +423,13 @@ void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bo
continue; continue;
} }
bb_add_var_dup_with_prefetch(p_blackboard, p.first, p.second, prefetch_nodepath_vars, p_node); bb_add_var_dup_with_prefetch(p_blackboard, p.first, p.second, prefetch_nodepath_vars, p_node);
if (parent_scope_mapping.has(p.first)) {
StringName target_var = parent_scope_mapping[p.first];
if (target_var != StringName()) {
ERR_CONTINUE_MSG(p_blackboard->get_parent() == nullptr, vformat("BlackboardPlan: Cannot link variable $%s to parent scope because the parent scope is not set.", p.first));
p_blackboard->link_var(p.first, p_blackboard->get_parent(), target_var);
}
}
} }
} }
@ -362,7 +441,9 @@ void BlackboardPlan::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_base_plan"), &BlackboardPlan::get_base_plan); ClassDB::bind_method(D_METHOD("get_base_plan"), &BlackboardPlan::get_base_plan);
ClassDB::bind_method(D_METHOD("is_derived"), &BlackboardPlan::is_derived); ClassDB::bind_method(D_METHOD("is_derived"), &BlackboardPlan::is_derived);
ClassDB::bind_method(D_METHOD("sync_with_base_plan"), &BlackboardPlan::sync_with_base_plan); ClassDB::bind_method(D_METHOD("sync_with_base_plan"), &BlackboardPlan::sync_with_base_plan);
ClassDB::bind_method(D_METHOD("create_blackboard", "node"), &BlackboardPlan::create_blackboard); ClassDB::bind_method(D_METHOD("set_parent_scope_plan_provider", "callable"), &BlackboardPlan::set_parent_scope_plan_provider);
ClassDB::bind_method(D_METHOD("get_parent_scope_plan_provider"), &BlackboardPlan::get_parent_scope_plan_provider);
ClassDB::bind_method(D_METHOD("create_blackboard", "node", "parent_scope"), &BlackboardPlan::create_blackboard, DEFVAL(Ref<Blackboard>()));
ClassDB::bind_method(D_METHOD("populate_blackboard", "blackboard", "overwrite", "node"), &BlackboardPlan::populate_blackboard); ClassDB::bind_method(D_METHOD("populate_blackboard", "blackboard", "overwrite", "node"), &BlackboardPlan::populate_blackboard);
// To avoid cluttering the member namespace, we do not export unnecessary properties in this class. // To avoid cluttering the member namespace, we do not export unnecessary properties in this class.

View File

@ -36,6 +36,13 @@ private:
// and only the values can be different in those variables. // and only the values can be different in those variables.
Ref<BlackboardPlan> base; Ref<BlackboardPlan> base;
// Mapping between variables in this plan and their parent scope names.
// Used for linking variables to their parent scope counterparts upon Blackboard creation/population.
HashMap<StringName, StringName> parent_scope_mapping;
// Fetcher function for the parent scope plan. Funtion should return a Ref<BlackboardPlan>.
// Used in the inspector. When set, mapping feature becomes available.
Callable parent_scope_plan_provider;
// If true, NodePath variables will be prefetched, so that the vars will contain node pointers instead (upon BB creation/population). // If true, NodePath variables will be prefetched, so that the vars will contain node pointers instead (upon BB creation/population).
bool prefetch_nodepath_vars = true; bool prefetch_nodepath_vars = true;
@ -52,6 +59,12 @@ public:
void set_base_plan(const Ref<BlackboardPlan> &p_base); void set_base_plan(const Ref<BlackboardPlan> &p_base);
Ref<BlackboardPlan> get_base_plan() const { return base; } Ref<BlackboardPlan> get_base_plan() const { return base; }
void set_parent_scope_plan_provider(const Callable &p_parent_scope_plan_provider);
Callable get_parent_scope_plan_provider() const { return parent_scope_plan_provider; }
bool is_mapping_enabled() const { return parent_scope_plan_provider.is_valid() && (parent_scope_plan_provider.call() != Ref<BlackboardPlan>()); }
bool has_mapping(const StringName &p_name) const;
void set_prefetch_nodepath_vars(bool p_enable); void set_prefetch_nodepath_vars(bool p_enable);
bool is_prefetching_nodepath_vars() const; bool is_prefetching_nodepath_vars() const;
@ -72,7 +85,7 @@ public:
void sync_with_base_plan(); void sync_with_base_plan();
_FORCE_INLINE_ bool is_derived() const { return base.is_valid(); } _FORCE_INLINE_ bool is_derived() const { return base.is_valid(); }
Ref<Blackboard> create_blackboard(Node *p_agent); Ref<Blackboard> create_blackboard(Node *p_agent, const Ref<Blackboard> &p_parent_scope = Ref<Blackboard>());
void populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_node); void populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_node);
BlackboardPlan(); BlackboardPlan();

View File

@ -52,7 +52,13 @@ void BehaviorTree::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
} }
void BehaviorTree::set_root_task(const Ref<BTTask> &p_value) { void BehaviorTree::set_root_task(const Ref<BTTask> &p_value) {
#ifdef TOOLS_ENABLED
_unset_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
root_task = p_value; root_task = p_value;
#ifdef TOOLS_ENABLED
_set_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
emit_changed(); emit_changed();
} }
@ -85,6 +91,22 @@ void BehaviorTree::_plan_changed() {
emit_changed(); emit_changed();
} }
#ifdef TOOLS_ENABLED
void BehaviorTree::_set_editor_behavior_tree_hint() {
if (root_task.is_valid()) {
root_task->data.behavior_tree_id = this->get_instance_id();
}
}
void BehaviorTree::_unset_editor_behavior_tree_hint() {
if (root_task.is_valid()) {
root_task->data.behavior_tree_id = ObjectID();
}
}
#endif // TOOLS_ENABLED
void BehaviorTree::_bind_methods() { void BehaviorTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_description", "description"), &BehaviorTree::set_description); ClassDB::bind_method(D_METHOD("set_description", "description"), &BehaviorTree::set_description);
ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description); ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description);

View File

@ -34,6 +34,11 @@ private:
void _plan_changed(); void _plan_changed();
#ifdef TOOLS_ENABLED
void _set_editor_behavior_tree_hint();
void _unset_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
protected: protected:
static void _bind_methods(); static void _bind_methods();

View File

@ -32,10 +32,10 @@ void BTState::set_behavior_tree(const Ref<BehaviorTree> &p_tree) {
p_tree->connect(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan)); p_tree->connect(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan));
} }
behavior_tree = p_tree; behavior_tree = p_tree;
_update_blackboard_plan();
} else { } else {
behavior_tree = p_tree; behavior_tree = p_tree;
} }
_update_blackboard_plan();
} }
void BTState::_update_blackboard_plan() { void BTState::_update_blackboard_plan() {

View File

@ -14,6 +14,7 @@
#include "../../blackboard/blackboard.h" #include "../../blackboard/blackboard.h"
#include "../../util/limbo_string_names.h" #include "../../util/limbo_string_names.h"
#include "../../util/limbo_utility.h" #include "../../util/limbo_utility.h"
#include "../behavior_tree.h"
#include "bt_comment.h" #include "bt_comment.h"
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
@ -376,6 +377,22 @@ void BTTask::print_tree(int p_initial_tabs) {
} }
} }
#ifdef TOOLS_ENABLED
Ref<BehaviorTree> BTTask::editor_get_behavior_tree() {
BTTask *task = this;
while (task->data.behavior_tree_id.is_null() && task->get_parent().is_valid()) {
task = task->data.parent;
}
return Object::cast_to<BehaviorTree>(ObjectDB::get_instance(task->data.behavior_tree_id));
}
void BTTask::editor_set_behavior_tree(const Ref<BehaviorTree> &p_bt) {
data.behavior_tree_id = p_bt->get_instance_id();
}
#endif // TOOLS_ENABLED
void BTTask::_bind_methods() { void BTTask::_bind_methods() {
// Public Methods. // Public Methods.
ClassDB::bind_method(D_METHOD("is_root"), &BTTask::is_root); ClassDB::bind_method(D_METHOD("is_root"), &BTTask::is_root);
@ -397,6 +414,9 @@ void BTTask::_bind_methods() {
ClassDB::bind_method(D_METHOD("print_tree", "initial_tabs"), &BTTask::print_tree, Variant(0)); ClassDB::bind_method(D_METHOD("print_tree", "initial_tabs"), &BTTask::print_tree, Variant(0));
ClassDB::bind_method(D_METHOD("get_task_name"), &BTTask::get_task_name); ClassDB::bind_method(D_METHOD("get_task_name"), &BTTask::get_task_name);
ClassDB::bind_method(D_METHOD("abort"), &BTTask::abort); ClassDB::bind_method(D_METHOD("abort"), &BTTask::abort);
#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("editor_get_behavior_tree"), &BTTask::editor_get_behavior_tree);
#endif // TOOLS_ENABLED
// Properties, setters and getters. // Properties, setters and getters.
ClassDB::bind_method(D_METHOD("get_agent"), &BTTask::get_agent); ClassDB::bind_method(D_METHOD("get_agent"), &BTTask::get_agent);

View File

@ -42,6 +42,8 @@
using namespace godot; using namespace godot;
#endif // LIMBOAI_GDEXTENSION #endif // LIMBOAI_GDEXTENSION
class BehaviorTree;
/** /**
* Base class for BTTask. * Base class for BTTask.
* Note: In order to properly return Status in the _tick virtual method (GDVIRTUAL1R...) * Note: In order to properly return Status in the _tick virtual method (GDVIRTUAL1R...)
@ -82,6 +84,9 @@ private:
Status status = FRESH; Status status = FRESH;
double elapsed = 0.0; double elapsed = 0.0;
bool display_collapsed = false; bool display_collapsed = false;
#ifdef TOOLS_ENABLED
ObjectID behavior_tree_id;
#endif
} data; } data;
Array _get_children() const; Array _get_children() const;
@ -162,6 +167,11 @@ public:
void print_tree(int p_initial_tabs = 0); void print_tree(int p_initial_tabs = 0);
#ifdef TOOLS_ENABLED
Ref<BehaviorTree> editor_get_behavior_tree();
void editor_set_behavior_tree(const Ref<BehaviorTree> &p_bt);
#endif
BTTask(); BTTask();
~BTTask(); ~BTTask();
}; };

View File

@ -11,28 +11,45 @@
#include "bt_new_scope.h" #include "bt_new_scope.h"
#include "../../behavior_tree.h"
void BTNewScope::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) { void BTNewScope::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
blackboard_plan = p_plan; blackboard_plan = p_plan;
if (blackboard_plan.is_null()) { if (blackboard_plan.is_null()) {
blackboard_plan.instantiate(); blackboard_plan.instantiate();
} }
_update_blackboard_plan(); _update_blackboard_plan();
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
callable_mp(this, &BTNewScope::_set_parent_scope_plan_from_bt).call_deferred();
}
#endif // TOOLS_ENABLED
emit_changed(); emit_changed();
} }
#ifdef TOOLS_ENABLED
void BTNewScope::_set_parent_scope_plan_from_bt() {
ERR_FAIL_NULL(get_blackboard_plan());
Ref<BehaviorTree> bt = get_root()->editor_get_behavior_tree();
ERR_FAIL_NULL(bt);
get_blackboard_plan()->set_parent_scope_plan_provider(callable_mp(bt.ptr(), &BehaviorTree::get_blackboard_plan));
}
#endif // TOOLS_ENABLED
void BTNewScope::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) { void BTNewScope::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
ERR_FAIL_COND(p_agent == nullptr); ERR_FAIL_COND(p_agent == nullptr);
ERR_FAIL_COND(p_blackboard == nullptr); ERR_FAIL_COND(p_blackboard == nullptr);
Ref<Blackboard> bb; Ref<Blackboard> bb;
if (blackboard_plan.is_valid()) { if (blackboard_plan.is_valid()) {
bb = blackboard_plan->create_blackboard(p_agent); bb = blackboard_plan->create_blackboard(p_agent, p_blackboard);
} else { } else {
bb = Ref<Blackboard>(memnew(Blackboard)); bb = Ref<Blackboard>(memnew(Blackboard));
}
bb->set_parent(p_blackboard); bb->set_parent(p_blackboard);
}
BTDecorator::initialize(p_agent, bb, p_scene_root); BTDecorator::initialize(p_agent, bb, p_scene_root);
} }

View File

@ -23,6 +23,10 @@ class BTNewScope : public BTDecorator {
private: private:
Ref<BlackboardPlan> blackboard_plan; Ref<BlackboardPlan> blackboard_plan;
#ifdef TOOLS_ENABLED
void _set_parent_scope_plan_from_bt();
#endif // TOOLS_ENABLED
protected: protected:
static void _bind_methods(); static void _bind_methods();

View File

@ -35,16 +35,20 @@ Methods
:widths: auto :widths: auto
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Blackboard<class_Blackboard>` | :ref:`create_blackboard<class_BlackboardPlan_method_create_blackboard>` **(** Node node **)** | | :ref:`Blackboard<class_Blackboard>` | :ref:`create_blackboard<class_BlackboardPlan_method_create_blackboard>` **(** Node node, :ref:`Blackboard<class_Blackboard>` parent_scope=null **)** |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BlackboardPlan<class_BlackboardPlan>` | :ref:`get_base_plan<class_BlackboardPlan_method_get_base_plan>` **(** **)** |const| | | :ref:`BlackboardPlan<class_BlackboardPlan>` | :ref:`get_base_plan<class_BlackboardPlan_method_get_base_plan>` **(** **)** |const| |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Callable | :ref:`get_parent_scope_plan_provider<class_BlackboardPlan_method_get_parent_scope_plan_provider>` **(** **)** |const| |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`is_derived<class_BlackboardPlan_method_is_derived>` **(** **)** |const| | | bool | :ref:`is_derived<class_BlackboardPlan_method_is_derived>` **(** **)** |const| |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`populate_blackboard<class_BlackboardPlan_method_populate_blackboard>` **(** :ref:`Blackboard<class_Blackboard>` blackboard, bool overwrite, Node node **)** | | void | :ref:`populate_blackboard<class_BlackboardPlan_method_populate_blackboard>` **(** :ref:`Blackboard<class_Blackboard>` blackboard, bool overwrite, Node node **)** |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_base_plan<class_BlackboardPlan_method_set_base_plan>` **(** :ref:`BlackboardPlan<class_BlackboardPlan>` blackboard_plan **)** | | void | :ref:`set_base_plan<class_BlackboardPlan_method_set_base_plan>` **(** :ref:`BlackboardPlan<class_BlackboardPlan>` blackboard_plan **)** |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`set_parent_scope_plan_provider<class_BlackboardPlan_method_set_parent_scope_plan_provider>` **(** Callable callable **)** |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`sync_with_base_plan<class_BlackboardPlan_method_sync_with_base_plan>` **(** **)** | | void | :ref:`sync_with_base_plan<class_BlackboardPlan_method_sync_with_base_plan>` **(** **)** |
+---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +---------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
@ -83,7 +87,7 @@ Method Descriptions
.. rst-class:: classref-method .. rst-class:: classref-method
:ref:`Blackboard<class_Blackboard>` **create_blackboard** **(** Node node **)** :ref:`Blackboard<class_Blackboard>` **create_blackboard** **(** Node node, :ref:`Blackboard<class_Blackboard>` parent_scope=null **)**
Constructs a new instance of a :ref:`Blackboard<class_Blackboard>` using this plan. If ``NodePath`` prefetching is enabled, ``node`` will be used to retrieve node instances for ``NodePath`` variables and substitute their values. Constructs a new instance of a :ref:`Blackboard<class_Blackboard>` using this plan. If ``NodePath`` prefetching is enabled, ``node`` will be used to retrieve node instances for ``NodePath`` variables and substitute their values.
@ -103,6 +107,18 @@ Returns the base plan. See :ref:`is_derived<class_BlackboardPlan_method_is_deriv
---- ----
.. _class_BlackboardPlan_method_get_parent_scope_plan_provider:
.. rst-class:: classref-method
Callable **get_parent_scope_plan_provider** **(** **)** |const|
Returns the parent scope plan provider - a callable that returns a **BlackboardPlan**.
.. rst-class:: classref-item-separator
----
.. _class_BlackboardPlan_method_is_derived: .. _class_BlackboardPlan_method_is_derived:
.. rst-class:: classref-method .. rst-class:: classref-method
@ -141,6 +157,18 @@ Use with caution, as it will remove variables not present in the base plan. Only
---- ----
.. _class_BlackboardPlan_method_set_parent_scope_plan_provider:
.. rst-class:: classref-method
void **set_parent_scope_plan_provider** **(** Callable callable **)**
Sets the parent scope plan provider - a callable that returns a **BlackboardPlan**. Used to provide hints in the inspector. When set, mapping feature becomes available.
.. rst-class:: classref-item-separator
----
.. _class_BlackboardPlan_method_sync_with_base_plan: .. _class_BlackboardPlan_method_sync_with_base_plan:
.. rst-class:: classref-method .. rst-class:: classref-method

View File

@ -59,59 +59,61 @@ Methods
.. table:: .. table::
:widths: auto :widths: auto
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_enter<class_BTTask_private_method__enter>` **(** **)** |virtual| | | void | :ref:`_enter<class_BTTask_private_method__enter>` **(** **)** |virtual| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_exit<class_BTTask_private_method__exit>` **(** **)** |virtual| | | void | :ref:`_exit<class_BTTask_private_method__exit>` **(** **)** |virtual| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| String | :ref:`_generate_name<class_BTTask_private_method__generate_name>` **(** **)** |virtual| |const| | | String | :ref:`_generate_name<class_BTTask_private_method__generate_name>` **(** **)** |virtual| |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| PackedStringArray | :ref:`_get_configuration_warnings<class_BTTask_private_method__get_configuration_warnings>` **(** **)** |virtual| |const| | | PackedStringArray | :ref:`_get_configuration_warnings<class_BTTask_private_method__get_configuration_warnings>` **(** **)** |virtual| |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`_setup<class_BTTask_private_method__setup>` **(** **)** |virtual| | | void | :ref:`_setup<class_BTTask_private_method__setup>` **(** **)** |virtual| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Status<enum_BT_Status>` | :ref:`_tick<class_BTTask_private_method__tick>` **(** float delta **)** |virtual| | | :ref:`Status<enum_BT_Status>` | :ref:`_tick<class_BTTask_private_method__tick>` **(** float delta **)** |virtual| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`abort<class_BTTask_method_abort>` **(** **)** | | void | :ref:`abort<class_BTTask_method_abort>` **(** **)** |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`add_child<class_BTTask_method_add_child>` **(** :ref:`BTTask<class_BTTask>` task **)** | | void | :ref:`add_child<class_BTTask_method_add_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`add_child_at_index<class_BTTask_method_add_child_at_index>` **(** :ref:`BTTask<class_BTTask>` task, int idx **)** | | void | :ref:`add_child_at_index<class_BTTask_method_add_child_at_index>` **(** :ref:`BTTask<class_BTTask>` task, int idx **)** |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`clone<class_BTTask_method_clone>` **(** **)** |const| | | :ref:`BTTask<class_BTTask>` | :ref:`clone<class_BTTask_method_clone>` **(** **)** |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BehaviorTree<class_BehaviorTree>` | :ref:`editor_get_behavior_tree<class_BTTask_method_editor_get_behavior_tree>` **(** **)** |
+-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`Status<enum_BT_Status>` | :ref:`execute<class_BTTask_method_execute>` **(** float delta **)** | | :ref:`Status<enum_BT_Status>` | :ref:`execute<class_BTTask_method_execute>` **(** float delta **)** |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_child<class_BTTask_method_get_child>` **(** int idx **)** |const| | | :ref:`BTTask<class_BTTask>` | :ref:`get_child<class_BTTask_method_get_child>` **(** int idx **)** |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_child_count<class_BTTask_method_get_child_count>` **(** **)** |const| | | int | :ref:`get_child_count<class_BTTask_method_get_child_count>` **(** **)** |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_child_count_excluding_comments<class_BTTask_method_get_child_count_excluding_comments>` **(** **)** |const| | | int | :ref:`get_child_count_excluding_comments<class_BTTask_method_get_child_count_excluding_comments>` **(** **)** |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| int | :ref:`get_index<class_BTTask_method_get_index>` **(** **)** |const| | | int | :ref:`get_index<class_BTTask_method_get_index>` **(** **)** |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_parent<class_BTTask_method_get_parent>` **(** **)** |const| | | :ref:`BTTask<class_BTTask>` | :ref:`get_parent<class_BTTask_method_get_parent>` **(** **)** |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`get_root<class_BTTask_method_get_root>` **(** **)** |const| | | :ref:`BTTask<class_BTTask>` | :ref:`get_root<class_BTTask_method_get_root>` **(** **)** |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| String | :ref:`get_task_name<class_BTTask_method_get_task_name>` **(** **)** | | String | :ref:`get_task_name<class_BTTask_method_get_task_name>` **(** **)** |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`has_child<class_BTTask_method_has_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |const| | | bool | :ref:`has_child<class_BTTask_method_has_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`initialize<class_BTTask_method_initialize>` **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard, Node scene_root **)** | | void | :ref:`initialize<class_BTTask_method_initialize>` **(** Node agent, :ref:`Blackboard<class_Blackboard>` blackboard, Node scene_root **)** |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`is_descendant_of<class_BTTask_method_is_descendant_of>` **(** :ref:`BTTask<class_BTTask>` task **)** |const| | | bool | :ref:`is_descendant_of<class_BTTask_method_is_descendant_of>` **(** :ref:`BTTask<class_BTTask>` task **)** |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| bool | :ref:`is_root<class_BTTask_method_is_root>` **(** **)** |const| | | bool | :ref:`is_root<class_BTTask_method_is_root>` **(** **)** |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| :ref:`BTTask<class_BTTask>` | :ref:`next_sibling<class_BTTask_method_next_sibling>` **(** **)** |const| | | :ref:`BTTask<class_BTTask>` | :ref:`next_sibling<class_BTTask_method_next_sibling>` **(** **)** |const| |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`print_tree<class_BTTask_method_print_tree>` **(** int initial_tabs=0 **)** | | void | :ref:`print_tree<class_BTTask_method_print_tree>` **(** int initial_tabs=0 **)** |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`remove_child<class_BTTask_method_remove_child>` **(** :ref:`BTTask<class_BTTask>` task **)** | | void | :ref:`remove_child<class_BTTask_method_remove_child>` **(** :ref:`BTTask<class_BTTask>` task **)** |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| void | :ref:`remove_child_at_index<class_BTTask_method_remove_child_at_index>` **(** int idx **)** | | void | :ref:`remove_child_at_index<class_BTTask_method_remove_child_at_index>` **(** int idx **)** |
+-------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+ +-----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------+
.. rst-class:: classref-section-separator .. rst-class:: classref-section-separator
@ -366,6 +368,18 @@ Duplicates the task and its children, copying the exported members. Sub-resource
---- ----
.. _class_BTTask_method_editor_get_behavior_tree:
.. rst-class:: classref-method
:ref:`BehaviorTree<class_BehaviorTree>` **editor_get_behavior_tree** **(** **)**
Returns the behavior tree that owns this task. This is only available in the editor.
.. rst-class:: classref-item-separator
----
.. _class_BTTask_method_execute: .. _class_BTTask_method_execute:
.. rst-class:: classref-method .. rst-class:: classref-method

View File

@ -79,6 +79,12 @@
Duplicates the task and its children, copying the exported members. Sub-resources are shared for efficiency, except for [BBParam] subtypes, which are always copied. Used by the editor to instantiate [BehaviorTree] and copy-paste tasks. Duplicates the task and its children, copying the exported members. Sub-resources are shared for efficiency, except for [BBParam] subtypes, which are always copied. Used by the editor to instantiate [BehaviorTree] and copy-paste tasks.
</description> </description>
</method> </method>
<method name="editor_get_behavior_tree">
<return type="BehaviorTree" />
<description>
Returns the behavior tree that owns this task. This is only available in the editor.
</description>
</method>
<method name="execute"> <method name="execute">
<return type="int" enum="BT.Status" /> <return type="int" enum="BT.Status" />
<param index="0" name="delta" type="float" /> <param index="0" name="delta" type="float" />

View File

@ -11,6 +11,7 @@
<method name="create_blackboard"> <method name="create_blackboard">
<return type="Blackboard" /> <return type="Blackboard" />
<param index="0" name="node" type="Node" /> <param index="0" name="node" type="Node" />
<param index="1" name="parent_scope" type="Blackboard" default="null" />
<description> <description>
Constructs a new instance of a [Blackboard] using this plan. If [NodePath] prefetching is enabled, [param node] will be used to retrieve node instances for [NodePath] variables and substitute their values. Constructs a new instance of a [Blackboard] using this plan. If [NodePath] prefetching is enabled, [param node] will be used to retrieve node instances for [NodePath] variables and substitute their values.
</description> </description>
@ -21,6 +22,12 @@
Returns the base plan. See [method is_derived]. Returns the base plan. See [method is_derived].
</description> </description>
</method> </method>
<method name="get_parent_scope_plan_provider" qualifiers="const">
<return type="Callable" />
<description>
Returns the parent scope plan provider - a callable that returns a [BlackboardPlan].
</description>
</method>
<method name="is_derived" qualifiers="const"> <method name="is_derived" qualifiers="const">
<return type="bool" /> <return type="bool" />
<description> <description>
@ -44,6 +51,13 @@
Use with caution, as it will remove variables not present in the base plan. Only use this for custom tooling. Use with caution, as it will remove variables not present in the base plan. Only use this for custom tooling.
</description> </description>
</method> </method>
<method name="set_parent_scope_plan_provider">
<return type="void" />
<param index="0" name="callable" type="Callable" />
<description>
Sets the parent scope plan provider - a callable that returns a [BlackboardPlan]. Used to provide hints in the inspector. When set, mapping feature becomes available.
</description>
</method>
<method name="sync_with_base_plan"> <method name="sync_with_base_plan">
<return type="void" /> <return type="void" />
<description> <description>

View File

@ -48,14 +48,18 @@ void BlackboardPlanEditor::_add_var() {
ERR_FAIL_NULL(plan); ERR_FAIL_NULL(plan);
int suffix = 1; int suffix = 1;
StringName var_name = default_var_name; StringName var_name = default_var_name == StringName() ? "var" : default_var_name;
while (plan->has_var(var_name)) { while (plan->has_var(var_name)) {
suffix += 1; suffix += 1;
var_name = String(default_var_name) + itos(suffix); var_name = String(default_var_name) + itos(suffix);
} }
BBVariable var(Variant::Type::FLOAT); BBVariable var(default_type, default_hint, default_hint_string);
if (default_value.get_type() == default_type) {
var.set_value(default_value);
}
plan->add_var(var_name, var); plan->add_var(var_name, var);
reset_defaults();
_refresh(); _refresh();
} }
@ -127,10 +131,19 @@ void BlackboardPlanEditor::edit_plan(const Ref<BlackboardPlan> &p_plan) {
_refresh(); _refresh();
} }
void BlackboardPlanEditor::set_next_var_name(const StringName &p_name) { void BlackboardPlanEditor::set_defaults(const StringName &p_var_name, Variant::Type p_type, PropertyHint p_hint, String p_hint_string, Variant p_value) {
if (String(p_name).is_valid_identifier()) { default_var_name = p_var_name;
default_var_name = p_name; default_type = p_type;
default_hint = p_hint;
default_hint_string = p_hint_string;
default_value = p_value;
} }
void BlackboardPlanEditor::reset_defaults() {
default_var_name = "var";
default_type = Variant::FLOAT;
default_hint = PROPERTY_HINT_NONE;
default_hint_string = "";
} }
void BlackboardPlanEditor::_show_button_popup(Button *p_button, PopupMenu *p_popup, int p_index) { void BlackboardPlanEditor::_show_button_popup(Button *p_button, PopupMenu *p_popup, int p_index) {
@ -233,7 +246,7 @@ void BlackboardPlanEditor::_drag_button_gui_input(const Ref<InputEvent> &p_event
void BlackboardPlanEditor::_visibility_changed() { void BlackboardPlanEditor::_visibility_changed() {
if (!is_visible() && plan.is_valid()) { if (!is_visible() && plan.is_valid()) {
plan->notify_property_list_changed(); plan->notify_property_list_changed();
default_var_name = "var"; reset_defaults();
} }
} }
@ -367,7 +380,7 @@ void BlackboardPlanEditor::_notification(int p_what) {
} }
BlackboardPlanEditor::BlackboardPlanEditor() { BlackboardPlanEditor::BlackboardPlanEditor() {
default_var_name = "var"; reset_defaults();
set_title(TTR("Manage Blackboard Plan")); set_title(TTR("Manage Blackboard Plan"));

View File

@ -59,6 +59,10 @@ private:
Ref<BlackboardPlan> plan; Ref<BlackboardPlan> plan;
StringName default_var_name; StringName default_var_name;
Variant::Type default_type = Variant::NIL;
PropertyHint default_hint = PROPERTY_HINT_NONE;
String default_hint_string;
Variant default_value;
VBoxContainer *rows_vbox; VBoxContainer *rows_vbox;
Button *add_var_tool; Button *add_var_tool;
@ -99,7 +103,8 @@ public:
_FORCE_INLINE_ static BlackboardPlanEditor *get_singleton() { return singleton; } _FORCE_INLINE_ static BlackboardPlanEditor *get_singleton() { return singleton; }
void edit_plan(const Ref<BlackboardPlan> &p_plan); void edit_plan(const Ref<BlackboardPlan> &p_plan);
void set_next_var_name(const StringName &p_name); void set_defaults(const StringName &p_name, Variant::Type p_type = Variant::FLOAT, PropertyHint p_hint = PROPERTY_HINT_NONE, String p_hint_string = "", Variant p_value = Variant());
void reset_defaults();
BlackboardPlanEditor(); BlackboardPlanEditor();
}; };

View File

@ -300,7 +300,7 @@ void EditorPropertyBBParam::update_property() {
void EditorPropertyBBParam::setup(PropertyHint p_hint, const String &p_hint_text, const Ref<BlackboardPlan> &p_plan) { void EditorPropertyBBParam::setup(PropertyHint p_hint, const String &p_hint_text, const Ref<BlackboardPlan> &p_plan) {
param_type = p_hint_text; param_type = p_hint_text;
property_hint = p_hint; property_hint = p_hint;
variable_editor->setup(p_plan); variable_editor->setup(p_plan, false);
variable_editor->set_name_split_ratio(0.0); variable_editor->set_name_split_ratio(0.0);
} }

View File

@ -31,6 +31,8 @@
#include <godot_cpp/classes/h_box_container.hpp> #include <godot_cpp/classes/h_box_container.hpp>
#endif // LIMBOAI_GDEXTENSION #endif // LIMBOAI_GDEXTENSION
int EditorPropertyVariableName::last_caret_column = 0;
//***** EditorPropertyVariableName //***** EditorPropertyVariableName
void EditorPropertyVariableName::_show_variables_popup() { void EditorPropertyVariableName::_show_variables_popup() {
@ -55,14 +57,26 @@ void EditorPropertyVariableName::_show_variables_popup() {
} }
void EditorPropertyVariableName::_name_changed(const String &p_new_name) { void EditorPropertyVariableName::_name_changed(const String &p_new_name) {
if (updating) {
return;
}
emit_changed(get_edited_property(), p_new_name); emit_changed(get_edited_property(), p_new_name);
last_caret_column = name_edit->get_caret_column();
_update_status(); _update_status();
} }
void EditorPropertyVariableName::_name_submitted() {
_name_changed(name_edit->get_text());
if (name_edit->has_focus()) {
name_edit->release_focus();
}
}
void EditorPropertyVariableName::_variable_selected(int p_id) { void EditorPropertyVariableName::_variable_selected(int p_id) {
String var_name = plan->get_var_by_index(p_id).first; String var_name = plan->get_var_by_index(p_id).first;
name_edit->set_text(var_name); name_edit->set_text(var_name);
_name_changed(var_name); _name_submitted();
} }
void EditorPropertyVariableName::_update_status() { void EditorPropertyVariableName::_update_status() {
@ -71,9 +85,21 @@ void EditorPropertyVariableName::_update_status() {
if (plan.is_null()) { if (plan.is_null()) {
return; return;
} }
if (plan->has_var(name_edit->get_text())) { String var_name = name_edit->get_text();
if (var_name.is_empty() && allow_empty) {
BUTTON_SET_ICON(status_btn, theme_cache.var_empty_icon);
status_btn->set_tooltip_text(TTR("Variable name not specified.\nClick to open the blackboard plan."));
} else if (plan->has_var(var_name)) {
if (expected_type == Variant::NIL || plan->get_var(var_name).get_type() == expected_type) {
BUTTON_SET_ICON(status_btn, theme_cache.var_exists_icon); BUTTON_SET_ICON(status_btn, theme_cache.var_exists_icon);
status_btn->set_tooltip_text(TTR("This variable is present in the blackboard plan.\nClick to open the blackboard plan.")); status_btn->set_tooltip_text(TTR("This variable is present in the blackboard plan.\nClick to open the blackboard plan."));
} else {
BUTTON_SET_ICON(status_btn, theme_cache.var_error_icon);
status_btn->set_tooltip_text(TTR(vformat(
"The %s variable in the blackboard plan is not of the same type as this variable (expected %s).\nClick to open the blackboard plan and fix the variable type.",
LimboUtility::get_singleton()->decorate_var(var_name),
Variant::get_type_name(expected_type))));
}
} else if (name_edit->get_text().begins_with("_")) { } else if (name_edit->get_text().begins_with("_")) {
BUTTON_SET_ICON(status_btn, theme_cache.var_private_icon); BUTTON_SET_ICON(status_btn, theme_cache.var_private_icon);
status_btn->set_tooltip_text(TTR("This variable is private and is not included in the blackboard plan.\nClick to open the blackboard plan.")); status_btn->set_tooltip_text(TTR("This variable is private and is not included in the blackboard plan.\nClick to open the blackboard plan."));
@ -86,7 +112,9 @@ void EditorPropertyVariableName::_update_status() {
void EditorPropertyVariableName::_status_pressed() { void EditorPropertyVariableName::_status_pressed() {
ERR_FAIL_NULL(plan); ERR_FAIL_NULL(plan);
if (!plan->has_var(name_edit->get_text())) { if (!plan->has_var(name_edit->get_text())) {
BlackboardPlanEditor::get_singleton()->set_next_var_name(name_edit->get_text()); BlackboardPlanEditor::get_singleton()->set_defaults(name_edit->get_text(),
expected_type == Variant::NIL ? Variant::FLOAT : expected_type,
default_hint, default_hint_string, default_value);
} }
BlackboardPlanEditor::get_singleton()->edit_plan(plan); BlackboardPlanEditor::get_singleton()->edit_plan(plan);
BlackboardPlanEditor::get_singleton()->popup_centered(); BlackboardPlanEditor::get_singleton()->popup_centered();
@ -110,17 +138,27 @@ void EditorPropertyVariableName::update_property() {
void EditorPropertyVariableName::_update_property() { void EditorPropertyVariableName::_update_property() {
#endif // LIMBOAI_GDEXTENSION #endif // LIMBOAI_GDEXTENSION
String s = get_edited_object()->get(get_edited_property()); String s = get_edited_object()->get(get_edited_property());
updating = true;
if (name_edit->get_text() != s) { if (name_edit->get_text() != s) {
int caret = name_edit->get_caret_column(); int caret = name_edit->get_caret_column();
if (caret == 0) {
caret = last_caret_column;
}
name_edit->set_text(s); name_edit->set_text(s);
name_edit->set_caret_column(caret); name_edit->set_caret_column(caret);
} }
name_edit->set_editable(!is_read_only()); name_edit->set_editable(!is_read_only());
_update_status(); _update_status();
updating = false;
} }
void EditorPropertyVariableName::setup(const Ref<BlackboardPlan> &p_plan) { void EditorPropertyVariableName::setup(const Ref<BlackboardPlan> &p_plan, bool p_allow_empty, Variant::Type p_type, PropertyHint p_hint, String p_hint_string, Variant p_default_value) {
plan = p_plan; plan = p_plan;
allow_empty = p_allow_empty;
expected_type = p_type;
default_hint = p_hint;
default_hint_string = p_hint_string;
default_value = p_default_value;
_update_status(); _update_status();
} }
@ -128,6 +166,8 @@ void EditorPropertyVariableName::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_READY: { case NOTIFICATION_READY: {
name_edit->connect(LW_NAME(text_changed), callable_mp(this, &EditorPropertyVariableName::_name_changed)); name_edit->connect(LW_NAME(text_changed), callable_mp(this, &EditorPropertyVariableName::_name_changed));
name_edit->connect(LW_NAME(text_submitted), callable_mp(this, &EditorPropertyVariableName::_name_submitted).unbind(1));
name_edit->connect(LW_NAME(focus_exited), callable_mp(this, &EditorPropertyVariableName::_name_submitted));
variables_popup->connect(LW_NAME(id_pressed), callable_mp(this, &EditorPropertyVariableName::_variable_selected)); variables_popup->connect(LW_NAME(id_pressed), callable_mp(this, &EditorPropertyVariableName::_variable_selected));
drop_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorPropertyVariableName::_show_variables_popup)); drop_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorPropertyVariableName::_show_variables_popup));
status_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorPropertyVariableName::_status_pressed)); status_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorPropertyVariableName::_status_pressed));
@ -148,6 +188,8 @@ void EditorPropertyVariableName::_notification(int p_what) {
theme_cache.var_exists_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarExists)); theme_cache.var_exists_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarExists));
theme_cache.var_not_found_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarNotFound)); theme_cache.var_not_found_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarNotFound));
theme_cache.var_private_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarPrivate)); theme_cache.var_private_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarPrivate));
theme_cache.var_empty_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarEmpty));
theme_cache.var_error_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarError));
} break; } break;
} }
} }
@ -192,6 +234,10 @@ bool EditorInspectorPluginVariableName::_can_handle(Object *p_object) const {
if (param.is_valid()) { if (param.is_valid()) {
return true; return true;
} }
Ref<BlackboardPlan> plan = Object::cast_to<BlackboardPlan>(p_object);
if (plan.is_valid()) {
return true;
}
return false; return false;
} }
@ -200,13 +246,41 @@ bool EditorInspectorPluginVariableName::parse_property(Object *p_object, const V
#elif LIMBOAI_GDEXTENSION #elif LIMBOAI_GDEXTENSION
bool EditorInspectorPluginVariableName::_parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) { bool EditorInspectorPluginVariableName::_parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
#endif #endif
if (!(p_type == Variant::Type::STRING_NAME || p_type == Variant::Type::STRING) || !(p_path.ends_with("_var") || p_path.ends_with("variable"))) { bool is_mapping = p_path.begins_with("mapping/");
if (!(p_type == Variant::Type::STRING_NAME || p_type == Variant::Type::STRING) || !(is_mapping || p_path.ends_with("_var") || p_path.ends_with("variable"))) {
return false; return false;
} }
Ref<BlackboardPlan> plan;
Variant::Type expected_type = Variant::NIL;
PropertyHint default_hint = PROPERTY_HINT_NONE;
String default_hint_string;
Variant default_value;
if (is_mapping) {
plan.reference_ptr(Object::cast_to<BlackboardPlan>(p_object));
ERR_FAIL_NULL_V(plan, false);
String var_name = p_path.trim_prefix("mapping/");
if (plan->has_var(var_name)) {
BBVariable variable = plan->get_var(var_name);
expected_type = variable.get_type();
default_hint = variable.get_hint();
default_hint_string = variable.get_hint_string();
default_value = variable.get_value();
}
if (plan->get_parent_scope_plan_provider().is_valid()) {
Ref<BlackboardPlan> parent_plan = plan->get_parent_scope_plan_provider().call();
if (parent_plan.is_valid()) {
plan = parent_plan;
}
}
ERR_FAIL_NULL_V(plan, false);
} else {
plan = editor_plan_provider.call();
}
EditorPropertyVariableName *ed = memnew(EditorPropertyVariableName); EditorPropertyVariableName *ed = memnew(EditorPropertyVariableName);
ed->setup(plan_getter.call()); ed->setup(plan, is_mapping, expected_type, default_hint, default_hint_string, default_value);
add_property_editor(p_path, ed); add_property_editor(p_path, ed, expected_type);
return true; return true;
} }

View File

@ -31,17 +31,30 @@ using namespace godot;
class EditorPropertyVariableName : public EditorProperty { class EditorPropertyVariableName : public EditorProperty {
GDCLASS(EditorPropertyVariableName, EditorProperty); GDCLASS(EditorPropertyVariableName, EditorProperty);
private:
static int last_caret_column;
private: private:
struct ThemeCache { struct ThemeCache {
Ref<Texture2D> var_add_icon;
Ref<Texture2D> var_empty_icon;
Ref<Texture2D> var_error_icon;
Ref<Texture2D> var_exists_icon; Ref<Texture2D> var_exists_icon;
Ref<Texture2D> var_not_found_icon; Ref<Texture2D> var_not_found_icon;
Ref<Texture2D> var_add_icon;
Ref<Texture2D> var_private_icon; Ref<Texture2D> var_private_icon;
}; };
ThemeCache theme_cache; ThemeCache theme_cache;
Ref<BlackboardPlan> plan; Ref<BlackboardPlan> plan;
bool allow_empty = false;
Variant::Type expected_type = Variant::NIL;
PropertyHint default_hint = PROPERTY_HINT_NONE;
String default_hint_string;
Variant default_value;
bool updating = false;
LineEdit *name_edit; LineEdit *name_edit;
Button *drop_btn; Button *drop_btn;
Button *status_btn; Button *status_btn;
@ -49,6 +62,7 @@ private:
void _show_variables_popup(); void _show_variables_popup();
void _name_changed(const String &p_new_name); void _name_changed(const String &p_new_name);
void _name_submitted();
void _variable_selected(int p_id); void _variable_selected(int p_id);
void _update_status(); void _update_status();
@ -68,7 +82,7 @@ public:
virtual void _update_property() override; virtual void _update_property() override;
#endif #endif
void setup(const Ref<BlackboardPlan> &p_plan); void setup(const Ref<BlackboardPlan> &p_plan, bool p_allow_empty, Variant::Type p_type = Variant::NIL, PropertyHint p_hint = PROPERTY_HINT_NONE, String p_hint_string = "", Variant p_default_value = Variant());
EditorPropertyVariableName(); EditorPropertyVariableName();
}; };
@ -76,7 +90,7 @@ class EditorInspectorPluginVariableName : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginVariableName, EditorInspectorPlugin); GDCLASS(EditorInspectorPluginVariableName, EditorInspectorPlugin);
private: private:
Callable plan_getter; Callable editor_plan_provider;
protected: protected:
static void _bind_methods() {} static void _bind_methods() {}
@ -90,7 +104,7 @@ public:
virtual bool _parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override; virtual bool _parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
#endif #endif
void set_plan_getter(const Callable &p_getter) { plan_getter = p_getter; } void set_editor_plan_provider(const Callable &p_getter) { editor_plan_provider = p_getter; }
EditorInspectorPluginVariableName() = default; EditorInspectorPluginVariableName() = default;
}; };

View File

@ -1486,7 +1486,7 @@ void LimboAIEditorPlugin::_notification(int p_notification) {
add_debugger_plugin(memnew(LimboDebuggerPlugin)); add_debugger_plugin(memnew(LimboDebuggerPlugin));
add_inspector_plugin(memnew(EditorInspectorPluginBBPlan)); add_inspector_plugin(memnew(EditorInspectorPluginBBPlan));
EditorInspectorPluginVariableName *var_plugin = memnew(EditorInspectorPluginVariableName); EditorInspectorPluginVariableName *var_plugin = memnew(EditorInspectorPluginVariableName);
var_plugin->set_plan_getter(Callable(limbo_ai_editor, "get_edited_blackboard_plan")); var_plugin->set_editor_plan_provider(Callable(limbo_ai_editor, "get_edited_blackboard_plan"));
add_inspector_plugin(var_plugin); add_inspector_plugin(var_plugin);
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
// ! Only used in the module version. // ! Only used in the module version.

View File

@ -22,12 +22,31 @@
void LimboState::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) { void LimboState::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
blackboard_plan = p_plan; blackboard_plan = p_plan;
if (Engine::get_singleton()->is_editor_hint() && blackboard_plan.is_valid()) {
blackboard_plan->set_parent_scope_plan_provider(callable_mp(this, &LimboState::_get_parent_scope_plan));
}
_update_blackboard_plan(); _update_blackboard_plan();
} }
void LimboState::_update_blackboard_plan() { void LimboState::_update_blackboard_plan() {
} }
Ref<BlackboardPlan> LimboState::_get_parent_scope_plan() const {
BlackboardPlan *parent_plan = nullptr;
const LimboState *state = this;
while (state->get_parent() && IS_CLASS(state->get_parent(), LimboState)) {
state = Object::cast_to<LimboState>(state->get_parent());
ERR_FAIL_NULL_V(state, parent_plan);
if (state->blackboard_plan.is_valid()) {
parent_plan = state->blackboard_plan.ptr();
break;
}
}
return parent_plan;
}
LimboState *LimboState::get_root() const { LimboState *LimboState::get_root() const {
const Node *state = this; const Node *state = this;
while (state->get_parent() && IS_CLASS(state->get_parent(), LimboState)) { while (state->get_parent() && IS_CLASS(state->get_parent(), LimboState)) {

View File

@ -38,6 +38,8 @@ private:
HashMap<StringName, Callable> handlers; HashMap<StringName, Callable> handlers;
Callable guard_callable; Callable guard_callable;
Ref<BlackboardPlan> _get_parent_scope_plan() const;
protected: protected:
friend LimboHSM; friend LimboHSM;

1
icons/LimboVarEmpty.svg Normal file
View File

@ -0,0 +1 @@
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path clip-rule="evenodd" d="m0 0h16v16h-16z"/></clipPath><path d="m0 0h16v16h-16z" fill="none"/><g clip-path="url(#a)"><circle cx="8" cy="8" fill="#e0e0e0" r="8"/></g></svg>

After

Width:  |  Height:  |  Size: 380 B

1
icons/LimboVarError.svg Normal file
View File

@ -0,0 +1 @@
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path clip-rule="evenodd" d="m0 0h16v16h-16z"/></clipPath><path d="m0 0h16v16h-16z" fill="none"/><g clip-path="url(#a)"><path d="m8 0c4.415 0 8 3.585 8 8s-3.585 8-8 8-8-3.585-8-8 3.585-8 8-8zm2.357 8 2.357-2.357-2.357-2.357-2.357 2.357-2.357-2.357-2.357 2.357 2.357 2.357-2.357 2.357 2.357 2.357 2.357-2.357 2.357 2.357 2.357-2.357z" fill="#fc7f7f"/></g><path d="m22.481 21.885 2.357-2.357-2.357-2.357-2.357 2.357-2.357-2.357-2.357 2.357 2.357 2.357-2.357 2.357 2.357 2.357 2.357-2.357 2.357 2.357 2.357-2.357z" fill="#8eef97" fill-rule="nonzero"/></svg>

After

Width:  |  Height:  |  Size: 760 B

View File

@ -85,6 +85,7 @@ LimboStringNames::LimboStringNames() {
exited = SN("exited"); exited = SN("exited");
favorite_tasks_changed = SN("favorite_tasks_changed"); favorite_tasks_changed = SN("favorite_tasks_changed");
Favorites = SN("Favorites"); Favorites = SN("Favorites");
focus_exited = SN("focus_exited");
font = SN("font"); font = SN("font");
font_color = SN("font_color"); font_color = SN("font_color");
font_size = SN("font_size"); font_size = SN("font_size");
@ -105,6 +106,8 @@ LimboStringNames::LimboStringNames() {
LimboPercent = SN("LimboPercent"); LimboPercent = SN("LimboPercent");
LimboSelectAll = SN("LimboSelectAll"); LimboSelectAll = SN("LimboSelectAll");
LimboVarAdd = SN("LimboVarAdd"); LimboVarAdd = SN("LimboVarAdd");
LimboVarEmpty = SN("LimboVarEmpty");
LimboVarError = SN("LimboVarError");
LimboVarExists = SN("LimboVarExists"); LimboVarExists = SN("LimboVarExists");
LimboVarNotFound = SN("LimboVarNotFound"); LimboVarNotFound = SN("LimboVarNotFound");
LimboVarPrivate = SN("LimboVarPrivate"); LimboVarPrivate = SN("LimboVarPrivate");

View File

@ -101,6 +101,7 @@ public:
StringName exited; StringName exited;
StringName favorite_tasks_changed; StringName favorite_tasks_changed;
StringName Favorites; StringName Favorites;
StringName focus_exited;
StringName font_color; StringName font_color;
StringName font_size; StringName font_size;
StringName font; StringName font;
@ -122,6 +123,8 @@ public:
StringName LimboPercent; StringName LimboPercent;
StringName LimboSelectAll; StringName LimboSelectAll;
StringName LimboVarAdd; StringName LimboVarAdd;
StringName LimboVarEmpty;
StringName LimboVarError;
StringName LimboVarExists; StringName LimboVarExists;
StringName LimboVarNotFound; StringName LimboVarNotFound;
StringName LimboVarPrivate; StringName LimboVarPrivate;