Compare commits

..

23 Commits

Author SHA1 Message Date
Serhii Snitsaruk 2f326e5b6f
Merge pull request #110 from limbonaut/type-validation
Editor: Check if variable is of correct type for BBParam subtypes
2024-05-18 15:33:39 +02:00
Serhii Snitsaruk 064d00fdfa
Editor: Check if variable is of correct type for BBParam subtypes 2024-05-18 14:12:00 +02:00
Serhii Snitsaruk ff61d55c44
Merge pull request #106 from limbonaut/mapping
Mapping variables
2024-05-18 13:47:42 +02:00
Serhii Snitsaruk 7ab7a9d098
BlackboardPlan: Improve inspector update while manually typing in mappings
Fixes property glitches observed while a map variable is typed in.
2024-05-17 21:54:50 +02:00
Serhii Snitsaruk b54f3696ff
Fix crash upon adding variable in the plan editor with empty default name
Also default to float when expected type is not specified.
2024-05-17 11:08:58 +02:00
Serhii Snitsaruk dc77ecd2b2
Fix `parent_scope` argument in `create_blackboard` should have a default value 2024-05-17 10:36:25 +02:00
Serhii Snitsaruk 026272f7f7
Fix BBParam subtypes incorrectly display type error in inspector 2024-05-17 10:27:22 +02:00
Serhii Snitsaruk d08018b7b1
BlackboardPlan: Update mapping after variable renamed
Currently, only for owned variables, not parent's.
2024-05-17 10:00:12 +02:00
Serhii Snitsaruk d920060dee
Update class documentation 2024-05-15 20:58:38 +02:00
Serhii Snitsaruk 2718271bb1
Clean up & renames 2024-05-15 20:43:24 +02:00
Serhii Snitsaruk 3cf90f9387
BlackboardPlan: Don't show mapping in root plans 2024-05-15 13:13:27 +02:00
Serhii Snitsaruk c6b1a40627
BlackboardPlan: Update inspector upon mapping editing finished 2024-05-15 12:52:50 +02:00
Serhii Snitsaruk d36f8f1122
Fix variables missing from BTState blackboard
Non-overridden variables could be missing at runtime in the BTState blackboard due to a bug this commit fixes.
2024-05-15 11:43:31 +02:00
Serhii Snitsaruk a572613001
BlackboardPlan: Utilize mapping in LimboHSM
Also changes how parent plan providing is implemented (used for editor hints).
2024-05-15 11:38:53 +02:00
Serhii Snitsaruk c30c5a4d7a
BlackboardPlan: Auto-fill type info when adding a missing variable for a mapping 2024-05-14 22:55:25 +02:00
Serhii Snitsaruk ef1c1e5192
Fix circular ref & non-tools compilation errors 2024-05-14 22:03:29 +02:00
Serhii Snitsaruk 3b12288ae0
BlackboardPlan: Serialize only non-empty mapping values 2024-05-14 20:25:18 +02:00
Serhii Snitsaruk a1cdff2e2e
Fix issues with mapping in BTSubtree 2024-05-14 19:47:05 +02:00
Serhii Snitsaruk e43bc25d82
BlackboardPlan: Provide editor hints for mappings 2024-05-14 13:29:04 +02:00
Ola S. 549d73b8fc
Add icons for empty & error variable statuses 2024-05-14 13:25:24 +02:00
Serhii Snitsaruk 0d1e846d93
BlackboardPlan: Use mapping with BTSubtree 2024-05-14 11:39:32 +02:00
Serhii Snitsaruk 2d493a76bd
BlackboardPlan: Improve mapping and serialize 2024-05-14 09:29:56 +02:00
Serhii Snitsaruk bdfe5f52c2
BlackboardPlan: Implement rudimentary mapping 2024-05-13 23:21:55 +02:00
29 changed files with 471 additions and 100 deletions

View File

@ -17,10 +17,8 @@
class BBNode : public BBParam { class BBNode : public BBParam {
GDCLASS(BBNode, BBParam); GDCLASS(BBNode, BBParam);
protected:
virtual Variant::Type get_type() const override { return Variant::NODE_PATH; }
public: public:
virtual Variant::Type get_type() const override { return Variant::NODE_PATH; }
virtual Variant get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant()) override; virtual Variant get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant()) override;
}; };

View File

@ -49,8 +49,6 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const; void _get_property_list(List<PropertyInfo> *p_list) const;
public: public:
virtual Variant::Type get_type() const { return Variant::NIL; }
void set_value_source(ValueSource p_value); void set_value_source(ValueSource p_value);
ValueSource get_value_source() const { return value_source; } ValueSource get_value_source() const { return value_source; }
@ -66,6 +64,8 @@ public:
virtual String _to_string(); virtual String _to_string();
#endif #endif
virtual Variant::Type get_type() const { return Variant::NIL; }
virtual Variant::Type get_variable_expected_type() const { return get_type(); }
virtual Variant get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant()); virtual Variant get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant());
BBParam(); BBParam();

View File

@ -27,6 +27,8 @@ public:
virtual Variant::Type get_type() const override; virtual Variant::Type get_type() const override;
void set_type(Variant::Type p_type); void set_type(Variant::Type p_type);
virtual Variant::Type get_variable_expected_type() const override { return Variant::NIL; }
BBVariant(const Variant &p_value); BBVariant(const Variant &p_value);
BBVariant(); BBVariant();
}; };

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

@ -283,6 +283,7 @@ void EditorPropertyBBParam::update_property() {
if (param->get_value_source() == BBParam::BLACKBOARD_VAR) { if (param->get_value_source() == BBParam::BLACKBOARD_VAR) {
_remove_value_editor(); _remove_value_editor();
variable_editor->set_object_and_property(param.ptr(), SNAME("variable")); variable_editor->set_object_and_property(param.ptr(), SNAME("variable"));
variable_editor->setup(plan, false, param->get_variable_expected_type());
variable_editor->update_property(); variable_editor->update_property();
variable_editor->show(); variable_editor->show();
bottom_container->hide(); bottom_container->hide();
@ -300,7 +301,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); plan = p_plan;
variable_editor->set_name_split_ratio(0.0); variable_editor->set_name_split_ratio(0.0);
} }

View File

@ -36,6 +36,7 @@ private:
bool initialized = false; bool initialized = false;
Ref<BlackboardPlan> plan;
StringName param_type; StringName param_type;
PropertyHint property_hint = PROPERTY_HINT_NONE; PropertyHint property_hint = PROPERTY_HINT_NONE;

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 should be of type %s.\nClick to open the blackboard plan.",
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;