From f600d633a3df19756b9709fdfb32f3a95edde0dc Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 10 Nov 2024 19:08:56 +0100 Subject: [PATCH 01/10] Store property bindings in BlackboardPlan --- blackboard/blackboard_plan.cpp | 63 ++++++++++++++++++++++++++++------ blackboard/blackboard_plan.h | 13 +++++-- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index c4bee42..0cdd385 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -31,24 +31,45 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) { if (name_str.begins_with("mapping/")) { StringName mapped_var_name = name_str.get_slicec('/', 1); StringName value = p_value; - bool properties_changed = false; + bool prop_list_changed = false; if (value == StringName()) { if (parent_scope_mapping.has(mapped_var_name)) { - properties_changed = true; + prop_list_changed = true; parent_scope_mapping.erase(mapped_var_name); } } else { if (!parent_scope_mapping.has(mapped_var_name)) { - properties_changed = true; + prop_list_changed = true; } parent_scope_mapping[mapped_var_name] = value; } - if (properties_changed) { + if (prop_list_changed) { notify_property_list_changed(); } return true; } + // * Binding + if (name_str.begins_with("binding/")) { + StringName bound_var = name_str.get_slicec('/', 1); + NodePath value = p_value; + bool prop_list_changed = false; + if (value.is_empty()) { + if (property_bindings.has(bound_var)) { + prop_list_changed = true; + property_bindings.erase(bound_var); + } + } else { + if (!property_bindings.has(bound_var)) { + prop_list_changed = true; + } + property_bindings[bound_var] = value; + } + if (prop_list_changed) { + notify_property_list_changed(); + } + } + // * Storage if (name_str.begins_with("var/")) { StringName var_name = name_str.get_slicec('/', 1); @@ -66,6 +87,8 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) { var_map[var_name].set_hint((PropertyHint)(int)p_value); } else if (what == "hint_string") { var_map[var_name].set_hint_string(p_value); + } else if (what == "property_binding") { + property_bindings[var_name] = NodePath(p_value); } else { return false; } @@ -82,6 +105,8 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { 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 if (has_property_binding(p_name)) { + r_ret = "Bound to " + property_bindings[p_name]; } else { r_ret = var_map[p_name].get_value(); } @@ -92,11 +117,15 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { 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(); - } + r_ret = parent_scope_mapping.has(mapped_var_name) ? parent_scope_mapping[mapped_var_name] : StringName(); + return true; + } + + // * Binding + if (name_str.begins_with("binding/")) { + StringName bound_var = name_str.get_slicec('/', 1); + ERR_FAIL_COND_V(bound_var == StringName(), false); + r_ret = property_bindings.has(bound_var) ? property_bindings[bound_var] : NodePath(); return true; } @@ -129,7 +158,7 @@ void BlackboardPlan::_get_property_list(List *p_list) const { // * Editor if (var.get_type() != Variant::NIL && (!is_derived() || !var_name.begins_with("_"))) { - if (has_mapping(var_name)) { + if (has_mapping(var_name) || has_property_binding(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)); @@ -158,6 +187,15 @@ void BlackboardPlan::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(Variant::STRING_NAME, "mapping/" + p.first, PROPERTY_HINT_NONE, "", usage)); } } + + // * Binding + p_list->push_back(PropertyInfo(Variant::NIL, "Binding", PROPERTY_HINT_NONE, "binding/", PROPERTY_USAGE_GROUP)); + for (const Pair &p : var_list) { + PropertyUsageFlags usage = has_property_binding(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR; + // PROPERTY_HINT_LINK is used to signal that NodePath should point to a property. + // Our inspector plugin will know how to handle it. + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "binding/" + p.first, PROPERTY_HINT_LINK, "", usage)); + } } bool BlackboardPlan::_property_can_revert(const StringName &p_name) const { @@ -199,6 +237,11 @@ 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_property_binding(const StringName &p_name, const NodePath &p_path) { + property_bindings[p_name] = p_path; + emit_changed(); +} + void BlackboardPlan::set_prefetch_nodepath_vars(bool p_enable) { prefetch_nodepath_vars = p_enable; emit_changed(); diff --git a/blackboard/blackboard_plan.h b/blackboard/blackboard_plan.h index 804f325..be22018 100644 --- a/blackboard/blackboard_plan.h +++ b/blackboard/blackboard_plan.h @@ -34,15 +34,20 @@ private: // When base is not null, the plan is considered to be derived from the base plan. // A derived plan can only have variables that exist in the base plan, // and only the values can be different in those variables. + // The derived plan is synced with the base plan to maintain consistency. Ref 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 parent_scope_mapping; - // Fetcher function for the parent scope plan. Funtion should return a Ref. - // Used in the inspector. When set, mapping feature becomes available. + // Fetcher function for the parent scope plan. Function should return a Ref. + // Used in the inspector: enables mapping feature when set. Callable parent_scope_plan_provider; + // Bindings to properties in the scene to which this plan belongs. + HashMap property_bindings; + bool property_binding_enabled = false; + // 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; @@ -69,6 +74,10 @@ public: bool is_mapping_enabled() const { return parent_scope_plan_provider.is_valid() && (parent_scope_plan_provider.call() != Ref()); } bool has_mapping(const StringName &p_name) const; + bool has_property_binding(const StringName &p_name) const { return property_bindings.has(p_name); } + void set_property_binding(const StringName &p_name, const NodePath &p_path); + NodePath get_property_binding(const StringName &p_name) const { return property_bindings.has(p_name) ? property_bindings[p_name] : NodePath(); } + void set_prefetch_nodepath_vars(bool p_enable); bool is_prefetching_nodepath_vars() const; From 057d4e669cb702ed0ca675c8e214408a68b52a47 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sun, 10 Nov 2024 19:35:49 +0100 Subject: [PATCH 02/10] Bind variables to scene node properties upon runtime Blackboard creation --- blackboard/blackboard_plan.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index 0cdd385..839a172 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -453,8 +453,9 @@ void BlackboardPlan::populate_blackboard(const Ref &p_blackboard, bo #endif continue; } + bool is_bound = property_bindings.has(p.first); bool has_mapping = parent_scope_mapping.has(p.first); - bool do_prefetch = !has_mapping && prefetch_nodepath_vars; + bool do_prefetch = !is_bound && !has_mapping && prefetch_nodepath_vars; // Add a variable duplicate to the blackboard, optionally with NodePath prefetch. BBVariable var = p.second.duplicate(true); @@ -476,6 +477,17 @@ void BlackboardPlan::populate_blackboard(const Ref &p_blackboard, bo ERR_CONTINUE_MSG(p_blackboard->get_parent() == nullptr, vformat("BlackboardPlan: Cannot link variable %s to parent scope because the parent scope is not set.", LimboUtility::get_singleton()->decorate_var(p.first))); p_blackboard->link_var(p.first, p_blackboard->get_parent(), target_var); } + } else if (is_bound) { + // Bind variable to a property of a scene node. + NodePath binding_path = property_bindings[p.first]; + ERR_CONTINUE_MSG(binding_path.get_subname_count() != 1, vformat("BlackboardPlan: Can't bind variable %s using property path that contains multiple sub-names: %s", LimboUtility::get_singleton()->decorate_var(p.first), binding_path)); + NodePath node_path{ binding_path.get_concatenated_names() }; + StringName prop_name = binding_path.get_subname(0); + // TODO: Implement binding for base plan as well. + Node *binding_root = p_prefetch_root; + Node *n = binding_root->get_node_or_null(node_path); + ERR_CONTINUE_MSG(n == nullptr, vformat("BlackboardPlan: Binding failed for variable %s using property path: %s", LimboUtility::get_singleton()->decorate_var(p.first), binding_path)); + var.bind(n, prop_name); } } } From bc7f67781072a9835c251de26bd32e495fd442d0 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 11 Nov 2024 08:24:54 +0100 Subject: [PATCH 03/10] Specialized inspector property editor for NodePath --- blackboard/blackboard_plan.cpp | 7 +- editor/editor_property_property_path.cpp | 227 +++++++++++++++++++++++ editor/editor_property_property_path.h | 99 ++++++++++ editor/limbo_ai_editor_plugin.cpp | 5 + register_types.cpp | 2 + util/limbo_string_names.cpp | 3 + util/limbo_string_names.h | 3 + 7 files changed, 344 insertions(+), 2 deletions(-) create mode 100644 editor/editor_property_property_path.cpp create mode 100644 editor/editor_property_property_path.h diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index 839a172..2a9723c 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -106,7 +106,10 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { if (has_mapping(p_name)) { r_ret = "Mapped to " + LimboUtility::get_singleton()->decorate_var(parent_scope_mapping[p_name]); } else if (has_property_binding(p_name)) { - r_ret = "Bound to " + property_bindings[p_name]; + const NodePath &binding = property_bindings[p_name]; + String path_str = (String)binding.get_name(binding.get_name_count() - 1) + + ":" + (String)binding.get_concatenated_subnames(); + r_ret = "Bound to " + path_str; } else { r_ret = var_map[p_name].get_value(); } @@ -194,7 +197,7 @@ void BlackboardPlan::_get_property_list(List *p_list) const { PropertyUsageFlags usage = has_property_binding(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR; // PROPERTY_HINT_LINK is used to signal that NodePath should point to a property. // Our inspector plugin will know how to handle it. - p_list->push_back(PropertyInfo(Variant::NODE_PATH, "binding/" + p.first, PROPERTY_HINT_LINK, "", usage)); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "binding/" + p.first, PROPERTY_HINT_LINK, itos(p.second.get_type()), usage)); } } diff --git a/editor/editor_property_property_path.cpp b/editor/editor_property_property_path.cpp new file mode 100644 index 0000000..e2e2239 --- /dev/null +++ b/editor/editor_property_property_path.cpp @@ -0,0 +1,227 @@ +/** + * editor_property_path.cpp + * ============================================================================= + * Copyright 2021-2024 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#include "editor_property_property_path.h" + +#include "../util/limbo_compat.h" +#include "../util/limbo_string_names.h" +#include "core/variant/variant.h" + +#ifdef LIMBOAI_MODULE +#include "editor/editor_data.h" +#include "editor/editor_interface.h" +#include "servers/display_server.h" +#endif + +#ifdef LIMBOAI_GDEXTENSION +#include +#include +#include +#include +#include +#include +#include +#endif // LIMBOAI_MODULE + +#ifdef TOOLS_ENABLED + +namespace { + +Node *_get_base_node(Object *p_edited_object, SceneTree *p_scene_tree) { + Node *base_node = Object::cast_to(p_edited_object); + if (!base_node) { + base_node = Object::cast_to(EditorInterface::get_singleton()->get_inspector()->get_edited_object()); + } + if (!base_node) { + base_node = p_scene_tree->get_edited_scene_root(); + } + return base_node; +} + +} // unnamed namespace + +Node *EditorPropertyPropertyPath::_get_selected_node() { + NodePath path = get_edited_object()->get(get_edited_property()); + if (path.is_empty()) { + return nullptr; + } + + Node *base_node = _get_base_node(get_edited_object(), get_tree()); + Node *selected_node = base_node->get_node_or_null(path); + return selected_node; +} + +void EditorPropertyPropertyPath::_action_selected(int p_idx) { + switch (p_idx) { + case ACTION_CLEAR: { + emit_changed(get_edited_property(), NodePath()); + } break; + case ACTION_COPY: { + DisplayServer::get_singleton()->clipboard_set(get_edited_object()->get(get_edited_property())); + } break; + case ACTION_EDIT: { + assign_button->hide(); + action_menu->hide(); + path_edit->show(); + path_edit->set_text(get_edited_object()->get(get_edited_property())); + path_edit->grab_focus(); + } break; + case ACTION_SELECT: { + Node *selected_node = _get_selected_node(); + if (selected_node) { + EditorInterface::get_singleton()->get_selection()->clear(); + EditorInterface::get_singleton()->get_selection()->add_node(selected_node); + } + } break; + } +} + +void EditorPropertyPropertyPath::_accept_text() { + path_edit->hide(); + assign_button->show(); + action_menu->show(); + emit_changed(get_edited_property(), path_edit->get_text()); +} + +void EditorPropertyPropertyPath::_property_selected(const NodePath &p_property_path, const NodePath &p_node_path) { + if (p_property_path.is_empty()) { + return; + } + Node *base_node = _get_base_node(get_edited_object(), get_tree()); + ERR_FAIL_NULL(base_node); + Node *chosen_node = get_tree()->get_edited_scene_root()->get_node_or_null(p_node_path); + ERR_FAIL_NULL(chosen_node); + NodePath path = String(base_node->get_path_to(chosen_node)) + String(p_property_path); + + emit_changed(get_edited_property(), path); + update_property(); +} + +void EditorPropertyPropertyPath::_node_selected(const NodePath &p_path) { + if (p_path.is_empty()) { + return; + } + Node *selected_node = get_tree()->get_edited_scene_root()->get_node_or_null(p_path); + ERR_FAIL_NULL(selected_node); + EditorInterface::get_singleton()->popup_property_selector( + selected_node, + callable_mp(this, &EditorPropertyPropertyPath::_property_selected).bind(p_path), + valid_types); +} + +void EditorPropertyPropertyPath::_choose_property() { + EditorInterface::get_singleton()->popup_node_selector(callable_mp(this, &EditorPropertyPropertyPath::_node_selected)); +} + +void EditorPropertyPropertyPath::_set_read_only(bool p_read_only) { + assign_button->set_disabled(p_read_only); + action_menu->set_disabled(p_read_only); +} + +#ifdef LIMBOAI_MODULE +void EditorPropertyPropertyPath::update_property() { +#elif LIMBOAI_GDEXTENSION +void EditorPropertyPropertyPath::_update_property() { +#endif + NodePath path = get_edited_object()->get(get_edited_property()); + if (path.is_empty()) { + assign_button->set_text(TTR("Bind...")); + } else { + String text = (String)path.get_name(path.get_name_count() - 1) + + ":" + (String)path.get_concatenated_subnames(); + assign_button->set_text(text); + assign_button->set_tooltip_text(path); + } +} + +void EditorPropertyPropertyPath::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + BUTTON_SET_ICON(action_menu, get_theme_icon(LW_NAME(GuiTabMenuHl), LW_NAME(EditorIcons))); + action_menu->get_popup()->set_item_icon(ACTION_CLEAR, get_theme_icon(LW_NAME(Clear), LW_NAME(EditorIcons))); + action_menu->get_popup()->set_item_icon(ACTION_COPY, get_theme_icon(LW_NAME(ActionCopy), LW_NAME(EditorIcons))); + action_menu->get_popup()->set_item_icon(ACTION_EDIT, get_theme_icon(LW_NAME(Edit), LW_NAME(EditorIcons))); + action_menu->get_popup()->set_item_icon(ACTION_SELECT, get_theme_icon(LW_NAME(ExternalLink), LW_NAME(EditorIcons))); + } break; + } +} + +void EditorPropertyPropertyPath::setup(const PackedInt32Array &p_valid_types) { + valid_types = p_valid_types; +} + +EditorPropertyPropertyPath::EditorPropertyPropertyPath() { + HBoxContainer *hb = memnew(HBoxContainer); + add_child(hb); + hb->add_theme_constant_override(LW_NAME(separation), 0); + + assign_button = memnew(Button); + hb->add_child(assign_button); + assign_button->set_flat(true); + assign_button->set_text(TTR("Bind...")); + assign_button->set_clip_text(true); + assign_button->set_h_size_flags(SIZE_EXPAND_FILL); + assign_button->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); + assign_button->connect(LW_NAME(pressed), callable_mp(this, &EditorPropertyPropertyPath::_choose_property)); + + path_edit = memnew(LineEdit); + hb->add_child(path_edit); + path_edit->set_h_size_flags(SIZE_EXPAND_FILL); + path_edit->connect(LW_NAME(focus_exited), callable_mp(this, &EditorPropertyPropertyPath::_accept_text)); + path_edit->connect(LW_NAME(text_submitted), callable_mp(this, &EditorPropertyPropertyPath::_accept_text).unbind(1)); + path_edit->hide(); + + action_menu = memnew(MenuButton); + action_menu->get_popup()->add_item(TTR("Clear"), ACTION_CLEAR); + action_menu->get_popup()->add_item(TTR("Copy as Text"), ACTION_COPY); + action_menu->get_popup()->add_item(TTR("Edit"), ACTION_EDIT); + action_menu->get_popup()->add_item(TTR("Show Node in Tree"), ACTION_SELECT); + action_menu->get_popup()->connect(LW_NAME(id_pressed), callable_mp(this, &EditorPropertyPropertyPath::_action_selected)); + hb->add_child(action_menu); +} + +//***** EditorInspectorPluginPropertyPath + +#ifdef LIMBOAI_MODULE +bool EditorInspectorPluginPropertyPath::can_handle(Object *p_object) { +#elif LIMBOAI_GDEXTENSION +bool EditorInspectorPluginPropertyPath::_can_handle(Object *p_object) const { +#endif + return true; +} + +#ifdef LIMBOAI_MODULE +bool EditorInspectorPluginPropertyPath::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 p_usage, const bool p_wide) { +#elif LIMBOAI_GDEXTENSION +bool EditorInspectorPluginPropertyPath::_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 p_usage, const bool p_wide) { +#endif + if (p_type != Variant::NODE_PATH || p_hint != PROPERTY_HINT_LINK) { + return false; + } + + EditorPropertyPropertyPath *ed = memnew(EditorPropertyPropertyPath); + + PackedInt32Array valid_types; + Vector type_specifiers = p_hint_text.split(","); + for (const String &t : type_specifiers) { + if (t.is_numeric()) { + valid_types.append(t.to_int()); + } + } + ed->setup(valid_types); + + add_property_editor(p_path, ed); + + return true; +} + +#endif // TOOLS_ENABLED diff --git a/editor/editor_property_property_path.h b/editor/editor_property_property_path.h new file mode 100644 index 0000000..2172511 --- /dev/null +++ b/editor/editor_property_property_path.h @@ -0,0 +1,99 @@ +/** + * editor_property_path.h + * ============================================================================= + * Copyright 2021-2024 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#ifndef EDITOR_PROPERTY_PROPERTY_PATH +#define EDITOR_PROPERTY_PROPERTY_PATH + +#include "core/variant/variant.h" +#ifdef TOOLS_ENABLED + +#ifdef LIMBOAI_MODULE +#include "editor/editor_inspector.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/menu_button.h" +#endif // LIMBOAI_MODULE + +#ifdef LIMBOAI_GDEXTENSION +#include +#include +#include +#include +#include +using namespace godot; +#endif // LIMBOAI_GDEXTENSION + +// Specialized property editor for NodePath properties that represent a path to a specific property instead of just a node. +// Handles NodePath properties that have PROPERTY_HINT_LINK. +// Hint string can list the valid Variant types as comma-separated integers. +class EditorPropertyPropertyPath : public EditorProperty { + GDCLASS(EditorPropertyPropertyPath, EditorProperty); + +private: + enum Action { + ACTION_CLEAR, + ACTION_COPY, + ACTION_EDIT, + ACTION_SELECT, + }; + + Button *assign_button; + MenuButton *action_menu; + LineEdit *path_edit; + + PackedInt32Array valid_types; + + Node *_get_selected_node(); + void _action_selected(int p_idx); + void _accept_text(); + void _property_selected(const NodePath &p_property_path, const NodePath &p_node_path); + void _node_selected(const NodePath &p_path); + void _choose_property(); + +protected: + static void _bind_methods() {} + void _notification(int p_what); + +public: + // Note: Needs to be public in GDExtension. + virtual void _set_read_only(bool p_read_only) override; + +#ifdef LIMBOAI_MODULE + virtual void update_property() override; +#elif LIMBOAI_GDEXTENSION + virtual void _update_property() override; +#endif + + void setup(const PackedInt32Array &p_valid_types); + EditorPropertyPropertyPath(); +}; + +class EditorInspectorPluginPropertyPath : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginPropertyPath, EditorInspectorPlugin); + +private: +protected: + static void _bind_methods() {} + +public: +#ifdef LIMBOAI_MODULE + virtual bool can_handle(Object *p_object) 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 p_usage, const bool p_wide = false) override; +#elif LIMBOAI_GDEXTENSION + virtual bool _can_handle(Object *p_object) const 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 p_usage, const bool p_wide = false) override; +#endif + + EditorInspectorPluginPropertyPath() = default; +}; + +#endif // TOOLS_ENABLED + +#endif // EDITOR_PROPERTY_PROPERTY_PATH diff --git a/editor/limbo_ai_editor_plugin.cpp b/editor/limbo_ai_editor_plugin.cpp index d6d59d6..e50b06e 100644 --- a/editor/limbo_ai_editor_plugin.cpp +++ b/editor/limbo_ai_editor_plugin.cpp @@ -25,6 +25,7 @@ #include "blackboard_plan_editor.h" #include "debugger/limbo_debugger_plugin.h" #include "editor_property_bb_param.h" +#include "editor_property_property_path.h" #ifdef LIMBOAI_MODULE #include "core/config/project_settings.h" @@ -1918,9 +1919,13 @@ void LimboAIEditorPlugin::_notification(int p_notification) { case NOTIFICATION_READY: { add_debugger_plugin(memnew(LimboDebuggerPlugin)); add_inspector_plugin(memnew(EditorInspectorPluginBBPlan)); + EditorInspectorPluginVariableName *var_plugin = memnew(EditorInspectorPluginVariableName); var_plugin->set_editor_plan_provider(Callable(limbo_ai_editor, "get_edited_blackboard_plan")); add_inspector_plugin(var_plugin); + + EditorInspectorPluginPropertyPath *path_plugin = memnew(EditorInspectorPluginPropertyPath); + add_inspector_plugin(path_plugin); #ifdef LIMBOAI_MODULE // ! Only used in the module version. EditorInspectorPluginBBParam *param_plugin = memnew(EditorInspectorPluginBBParam); diff --git a/register_types.cpp b/register_types.cpp index 2bcce87..451e072 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -120,6 +120,7 @@ #endif // LIMBOAI_MODULE #ifdef LIMBOAI_GDEXTENSION +#include "editor/editor_property_property_path.h" #include #include #include @@ -263,6 +264,7 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(LimboDebuggerPlugin); GDREGISTER_CLASS(BlackboardPlanEditor); GDREGISTER_CLASS(EditorInspectorPluginBBPlan); + GDREGISTER_CLASS(EditorPropertyPropertyPath); GDREGISTER_CLASS(EditorPropertyVariableName); GDREGISTER_CLASS(EditorInspectorPluginVariableName); GDREGISTER_CLASS(OwnerPicker); diff --git a/util/limbo_string_names.cpp b/util/limbo_string_names.cpp index 22f099a..793a7da 100644 --- a/util/limbo_string_names.cpp +++ b/util/limbo_string_names.cpp @@ -47,6 +47,7 @@ LimboStringNames::LimboStringNames() { button_up = SN("button_up"); call_deferred = SN("call_deferred"); changed = SN("changed"); + Clear = SN("Clear"); Close = SN("Close"); dark_color_2 = SN("dark_color_2"); Debug = SN("Debug"); @@ -67,6 +68,7 @@ LimboStringNames::LimboStringNames() { EVENT_FINISHED = SN("finished"); EVENT_SUCCESS = SN("success"); exited = SN("exited"); + ExternalLink = SN("ExternalLink"); favorite_tasks_changed = SN("favorite_tasks_changed"); Favorites = SN("Favorites"); FlatButton = SN("FlatButton"); @@ -78,6 +80,7 @@ LimboStringNames::LimboStringNames() { freed = SN("freed"); gui_input = SN("gui_input"); GuiOptionArrow = SN("GuiOptionArrow"); + GuiTabMenuHl = SN("GuiTabMenuHl"); GuiTreeArrowDown = SN("GuiTreeArrowDown"); GuiTreeArrowRight = SN("GuiTreeArrowRight"); HeaderSmall = SN("HeaderSmall"); diff --git a/util/limbo_string_names.h b/util/limbo_string_names.h index f6eca76..3b538cd 100644 --- a/util/limbo_string_names.h +++ b/util/limbo_string_names.h @@ -63,6 +63,7 @@ public: StringName button_up; StringName call_deferred; StringName changed; + StringName Clear; StringName Close; StringName dark_color_2; StringName Debug; @@ -83,6 +84,7 @@ public: StringName EVENT_FINISHED; StringName EVENT_SUCCESS; StringName exited; + StringName ExternalLink; StringName favorite_tasks_changed; StringName Favorites; StringName FlatButton; @@ -94,6 +96,7 @@ public: StringName freed; StringName gui_input; StringName GuiOptionArrow; + StringName GuiTabMenuHl; StringName GuiTreeArrowDown; StringName GuiTreeArrowRight; StringName HeaderSmall; From ea671dd54b821eaf01096565b47fbf1aa16ba7dc Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 11 Nov 2024 09:49:01 +0100 Subject: [PATCH 04/10] Improve binding presentation within inspector, fix compilation issues --- blackboard/blackboard_plan.cpp | 29 +++++++++++++++++++++--- editor/editor_property_property_path.cpp | 17 ++++++++++---- editor/editor_property_property_path.h | 1 - 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index 2a9723c..e617160 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -13,6 +13,16 @@ #include "../util/limbo_utility.h" +#ifdef LIMBOAI_MODULE +#include "editor/editor_inspector.h" +#include "editor/editor_interface.h" +#elif LIMBOAI_GDEXTENSION +#include +#include +#include +#include +#endif + bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) { String name_str = p_name; @@ -107,9 +117,22 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { r_ret = "Mapped to " + LimboUtility::get_singleton()->decorate_var(parent_scope_mapping[p_name]); } else if (has_property_binding(p_name)) { const NodePath &binding = property_bindings[p_name]; - String path_str = (String)binding.get_name(binding.get_name_count() - 1) + - ":" + (String)binding.get_concatenated_subnames(); - r_ret = "Bound to " + path_str; + + Node *edited_node = Object::cast_to(EditorInterface::get_singleton()->get_inspector()->get_edited_object()); + if (!edited_node) { + edited_node = SCENE_TREE()->get_edited_scene_root(); + } + Node *bound_node = edited_node->get_node_or_null(binding); + + String shortened_path; + if (bound_node) { + shortened_path = (String)bound_node->get_name() + + ":" + (String)binding.get_concatenated_subnames(); + } else { + shortened_path = (String)binding.get_name(binding.get_name_count() - 1) + + ":" + (String)binding.get_concatenated_subnames(); + } + r_ret = String::utf8("🔗 ") + shortened_path; } else { r_ret = var_map[p_name].get_value(); } diff --git a/editor/editor_property_property_path.cpp b/editor/editor_property_property_path.cpp index e2e2239..cc31f55 100644 --- a/editor/editor_property_property_path.cpp +++ b/editor/editor_property_property_path.cpp @@ -13,7 +13,6 @@ #include "../util/limbo_compat.h" #include "../util/limbo_string_names.h" -#include "core/variant/variant.h" #ifdef LIMBOAI_MODULE #include "editor/editor_data.h" @@ -135,8 +134,16 @@ void EditorPropertyPropertyPath::_update_property() { if (path.is_empty()) { assign_button->set_text(TTR("Bind...")); } else { - String text = (String)path.get_name(path.get_name_count() - 1) + - ":" + (String)path.get_concatenated_subnames(); + Node *base_node = _get_base_node(get_edited_object(), get_tree()); + ERR_FAIL_NULL(base_node); + Node *selected_node = base_node->get_node_or_null(path); + String text; + if (selected_node) { + text = (String)selected_node->get_name() + + ":" + (String)path.get_concatenated_subnames(); + } else { + text = (String)path; + } assign_button->set_text(text); assign_button->set_tooltip_text(path); } @@ -211,9 +218,9 @@ bool EditorInspectorPluginPropertyPath::_parse_property(Object *p_object, const EditorPropertyPropertyPath *ed = memnew(EditorPropertyPropertyPath); PackedInt32Array valid_types; - Vector type_specifiers = p_hint_text.split(","); + PackedStringArray type_specifiers = p_hint_text.split(","); for (const String &t : type_specifiers) { - if (t.is_numeric()) { + if (t.is_valid_int()) { valid_types.append(t.to_int()); } } diff --git a/editor/editor_property_property_path.h b/editor/editor_property_property_path.h index 2172511..a05ebef 100644 --- a/editor/editor_property_property_path.h +++ b/editor/editor_property_property_path.h @@ -12,7 +12,6 @@ #ifndef EDITOR_PROPERTY_PROPERTY_PATH #define EDITOR_PROPERTY_PROPERTY_PATH -#include "core/variant/variant.h" #ifdef TOOLS_ENABLED #ifdef LIMBOAI_MODULE From 4cac6276aa54bf1f2cdd507ebbfbbe0d2fad373b Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 11 Nov 2024 10:01:20 +0100 Subject: [PATCH 05/10] Fix GDExtension issues --- register_types.cpp | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/register_types.cpp b/register_types.cpp index 451e072..51b900f 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -251,25 +251,26 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { #ifdef TOOLS_ENABLED if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { #ifdef LIMBOAI_GDEXTENSION - GDREGISTER_CLASS(TaskTree); - GDREGISTER_CLASS(TaskButton); - GDREGISTER_CLASS(TaskPaletteSection); - GDREGISTER_CLASS(TaskPalette); - GDREGISTER_CLASS(ActionBanner); - GDREGISTER_CLASS(ModeSwitchButton); - GDREGISTER_CLASS(CompatShortcutBin); - GDREGISTER_CLASS(CompatScreenSelect); - GDREGISTER_CLASS(CompatWindowWrapper); - GDREGISTER_CLASS(LimboDebuggerTab); - GDREGISTER_CLASS(LimboDebuggerPlugin); - GDREGISTER_CLASS(BlackboardPlanEditor); - GDREGISTER_CLASS(EditorInspectorPluginBBPlan); - GDREGISTER_CLASS(EditorPropertyPropertyPath); - GDREGISTER_CLASS(EditorPropertyVariableName); - GDREGISTER_CLASS(EditorInspectorPluginVariableName); - GDREGISTER_CLASS(OwnerPicker); - GDREGISTER_CLASS(LimboAIEditor); - GDREGISTER_CLASS(LimboAIEditorPlugin); + GDREGISTER_INTERNAL_CLASS(TaskTree); + GDREGISTER_INTERNAL_CLASS(TaskButton); + GDREGISTER_INTERNAL_CLASS(TaskPaletteSection); + GDREGISTER_INTERNAL_CLASS(TaskPalette); + GDREGISTER_INTERNAL_CLASS(ActionBanner); + GDREGISTER_INTERNAL_CLASS(ModeSwitchButton); + GDREGISTER_INTERNAL_CLASS(CompatShortcutBin); + GDREGISTER_INTERNAL_CLASS(CompatScreenSelect); + GDREGISTER_INTERNAL_CLASS(CompatWindowWrapper); + GDREGISTER_INTERNAL_CLASS(LimboDebuggerTab); + GDREGISTER_INTERNAL_CLASS(LimboDebuggerPlugin); + GDREGISTER_INTERNAL_CLASS(BlackboardPlanEditor); + GDREGISTER_INTERNAL_CLASS(EditorInspectorPluginBBPlan); + GDREGISTER_INTERNAL_CLASS(EditorInspectorPluginPropertyPath); + GDREGISTER_INTERNAL_CLASS(EditorPropertyPropertyPath); + GDREGISTER_INTERNAL_CLASS(EditorPropertyVariableName); + GDREGISTER_INTERNAL_CLASS(EditorInspectorPluginVariableName); + GDREGISTER_INTERNAL_CLASS(OwnerPicker); + GDREGISTER_INTERNAL_CLASS(LimboAIEditor); + GDREGISTER_INTERNAL_CLASS(LimboAIEditorPlugin); GDREGISTER_INTERNAL_CLASS(TreeSearchPanel); GDREGISTER_INTERNAL_CLASS(TreeSearch); #endif // LIMBOAI_GDEXTENSION From 0b3b11a38385da4f391ad0bb4f01800902b481d4 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 11 Nov 2024 10:10:20 +0100 Subject: [PATCH 06/10] Enable binding in BehaviorTree-owned blackboard plan --- blackboard/blackboard_plan.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index e617160..bc054d4 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -479,7 +479,7 @@ void BlackboardPlan::populate_blackboard(const Ref &p_blackboard, bo #endif continue; } - bool is_bound = property_bindings.has(p.first); + bool is_bound = has_property_binding(p.first) || (is_derived() && get_base_plan()->has_property_binding(p.first)); bool has_mapping = parent_scope_mapping.has(p.first); bool do_prefetch = !is_bound && !has_mapping && prefetch_nodepath_vars; @@ -505,12 +505,19 @@ void BlackboardPlan::populate_blackboard(const Ref &p_blackboard, bo } } else if (is_bound) { // Bind variable to a property of a scene node. - NodePath binding_path = property_bindings[p.first]; + NodePath binding_path; + Node *binding_root; + if (has_property_binding(p.first)) { + binding_path = property_bindings[p.first]; + binding_root = p_prefetch_root; + } else { + binding_path = get_base_plan()->property_bindings[p.first]; + binding_root = p_prefetch_root_for_base_plan; + } ERR_CONTINUE_MSG(binding_path.get_subname_count() != 1, vformat("BlackboardPlan: Can't bind variable %s using property path that contains multiple sub-names: %s", LimboUtility::get_singleton()->decorate_var(p.first), binding_path)); NodePath node_path{ binding_path.get_concatenated_names() }; StringName prop_name = binding_path.get_subname(0); // TODO: Implement binding for base plan as well. - Node *binding_root = p_prefetch_root; Node *n = binding_root->get_node_or_null(node_path); ERR_CONTINUE_MSG(n == nullptr, vformat("BlackboardPlan: Binding failed for variable %s using property path: %s", LimboUtility::get_singleton()->decorate_var(p.first), binding_path)); var.bind(n, prop_name); From 6e8c22d5987622b932d8722dba3463e02207ed1c Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 11 Nov 2024 10:23:08 +0100 Subject: [PATCH 07/10] Clean up & strengthen code --- blackboard/blackboard_plan.cpp | 2 +- editor/editor_property_property_path.cpp | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index bc054d4..333fa53 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -122,7 +122,7 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { if (!edited_node) { edited_node = SCENE_TREE()->get_edited_scene_root(); } - Node *bound_node = edited_node->get_node_or_null(binding); + Node *bound_node = edited_node ? edited_node->get_node_or_null(binding) : nullptr; String shortened_path; if (bound_node) { diff --git a/editor/editor_property_property_path.cpp b/editor/editor_property_property_path.cpp index cc31f55..6d6de88 100644 --- a/editor/editor_property_property_path.cpp +++ b/editor/editor_property_property_path.cpp @@ -48,12 +48,15 @@ Node *_get_base_node(Object *p_edited_object, SceneTree *p_scene_tree) { } // unnamed namespace Node *EditorPropertyPropertyPath::_get_selected_node() { + ERR_FAIL_NULL_V(get_edited_object(), nullptr); + NodePath path = get_edited_object()->get(get_edited_property()); if (path.is_empty()) { return nullptr; } Node *base_node = _get_base_node(get_edited_object(), get_tree()); + ERR_FAIL_NULL_V(base_node, nullptr); Node *selected_node = base_node->get_node_or_null(path); return selected_node; } @@ -96,9 +99,9 @@ void EditorPropertyPropertyPath::_property_selected(const NodePath &p_property_p } Node *base_node = _get_base_node(get_edited_object(), get_tree()); ERR_FAIL_NULL(base_node); - Node *chosen_node = get_tree()->get_edited_scene_root()->get_node_or_null(p_node_path); - ERR_FAIL_NULL(chosen_node); - NodePath path = String(base_node->get_path_to(chosen_node)) + String(p_property_path); + Node *selected_node = get_tree()->get_edited_scene_root()->get_node_or_null(p_node_path); + ERR_FAIL_NULL(selected_node); + NodePath path = String(base_node->get_path_to(selected_node)) + String(p_property_path); emit_changed(get_edited_property(), path); update_property(); @@ -217,6 +220,7 @@ bool EditorInspectorPluginPropertyPath::_parse_property(Object *p_object, const EditorPropertyPropertyPath *ed = memnew(EditorPropertyPropertyPath); + // Convert the hint text to an array of valid types. PackedInt32Array valid_types; PackedStringArray type_specifiers = p_hint_text.split(","); for (const String &t : type_specifiers) { From 008702b1e42606a0b3e9d56e64e618f5f6635b84 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Mon, 11 Nov 2024 11:43:26 +0100 Subject: [PATCH 08/10] Fix template compilation --- blackboard/blackboard_plan.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index 333fa53..7714ab0 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -26,6 +26,7 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) { String name_str = p_name; +#ifdef TOOLS_ENABLED // * Editor if (var_map.has(p_name)) { BBVariable &var = var_map[p_name]; @@ -36,6 +37,7 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) { } return true; } +#endif // TOOLS_ENABLED // * Mapping if (name_str.begins_with("mapping/")) { @@ -111,6 +113,7 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) { bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { String name_str = p_name; +#ifdef TOOLS_ENABLED // * Editor if (var_map.has(p_name)) { if (has_mapping(p_name)) { @@ -138,6 +141,7 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { } return true; } +#endif // TOOLS_ENABLED // * Mapping if (name_str.begins_with("mapping/")) { @@ -182,6 +186,7 @@ void BlackboardPlan::_get_property_list(List *p_list) const { String var_name = p.first; BBVariable var = p.second; +#ifdef TOOLS_ENABLED // * Editor if (var.get_type() != Variant::NIL && (!is_derived() || !var_name.begins_with("_"))) { if (has_mapping(var_name) || has_property_binding(var_name)) { @@ -190,6 +195,7 @@ void BlackboardPlan::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR)); } } +#endif // TOOLS_ENABLED // * Storage if (is_derived() && (!var.is_value_changed() || var.get_value() == base->var_map[var_name].get_value())) { From 51878e4b6efa1e935b58011777d66ba151c39843 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 15 Nov 2024 18:00:55 +0100 Subject: [PATCH 09/10] Limit a variable to either binding or mapping, but not both at once --- blackboard/blackboard_plan.cpp | 38 ++++++++++++++++++------ editor/editor_property_variable_name.cpp | 5 ++++ util/limbo_compat.h | 1 + 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index 7714ab0..e951f01 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -147,7 +147,13 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { if (name_str.begins_with("mapping/")) { StringName mapped_var_name = name_str.get_slicec('/', 1); ERR_FAIL_COND_V(mapped_var_name == StringName(), false); - r_ret = parent_scope_mapping.has(mapped_var_name) ? parent_scope_mapping[mapped_var_name] : StringName(); + if (has_mapping(mapped_var_name)) { + r_ret = parent_scope_mapping[mapped_var_name]; + } else if (has_property_binding(mapped_var_name)) { + r_ret = RTR("Already bound to property."); + } else { + r_ret = StringName(); + } return true; } @@ -155,7 +161,13 @@ bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const { if (name_str.begins_with("binding/")) { StringName bound_var = name_str.get_slicec('/', 1); ERR_FAIL_COND_V(bound_var == StringName(), false); - r_ret = property_bindings.has(bound_var) ? property_bindings[bound_var] : NodePath(); + if (has_property_binding(bound_var)) { + r_ret = property_bindings[bound_var]; + } else if (has_mapping(bound_var)) { + r_ret = RTR("Already mapped to variable."); + } else { + r_ret = NodePath(); + } return true; } @@ -214,19 +226,27 @@ void BlackboardPlan::_get_property_list(List *p_list) const { if (is_mapping_enabled()) { p_list->push_back(PropertyInfo(Variant::NIL, "Mapping", PROPERTY_HINT_NONE, "mapping/", PROPERTY_USAGE_GROUP)); for (const Pair &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)); + if (unlikely(has_property_binding(p.first))) { + p_list->push_back(PropertyInfo(Variant::STRING, "mapping/" + p.first, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)); + } else { + // 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)); + } } } // * Binding p_list->push_back(PropertyInfo(Variant::NIL, "Binding", PROPERTY_HINT_NONE, "binding/", PROPERTY_USAGE_GROUP)); for (const Pair &p : var_list) { - PropertyUsageFlags usage = has_property_binding(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR; - // PROPERTY_HINT_LINK is used to signal that NodePath should point to a property. - // Our inspector plugin will know how to handle it. - p_list->push_back(PropertyInfo(Variant::NODE_PATH, "binding/" + p.first, PROPERTY_HINT_LINK, itos(p.second.get_type()), usage)); + if (unlikely(has_mapping(p.first))) { + p_list->push_back(PropertyInfo(Variant::STRING, "binding/" + p.first, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)); + } else { + PropertyUsageFlags usage = has_property_binding(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR; + // PROPERTY_HINT_LINK is used to signal that NodePath should point to a property. + // Our inspector plugin will know how to handle it. + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "binding/" + p.first, PROPERTY_HINT_LINK, itos(p.second.get_type()), usage)); + } } } diff --git a/editor/editor_property_variable_name.cpp b/editor/editor_property_variable_name.cpp index 44c11f8..4232591 100644 --- a/editor/editor_property_variable_name.cpp +++ b/editor/editor_property_variable_name.cpp @@ -245,6 +245,11 @@ bool EditorInspectorPluginVariableName::parse_property(Object *p_object, const V #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 p_usage, const bool p_wide) { #endif + if (p_usage & PROPERTY_USAGE_READ_ONLY) { + // Don't handle read-only properties using this plugin. + return false; + } + 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; diff --git a/util/limbo_compat.h b/util/limbo_compat.h index 5740477..ef9da4a 100644 --- a/util/limbo_compat.h +++ b/util/limbo_compat.h @@ -152,6 +152,7 @@ Variant _GLOBAL_DEF(const PropertyInfo &p_info, const Variant &p_default, bool p #define EDSCALE (EditorInterface::get_singleton()->get_editor_scale()) String TTR(const String &p_text, const String &p_context = ""); +#define RTR(m_text) TTR(m_text) #endif // ! LIMBOAI_GDEXTENSION From 08d8fcdf921e328320db02e70e1fb651b367b13d Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 15 Nov 2024 18:50:42 +0100 Subject: [PATCH 10/10] Don't show binding/mapping for hidden variables --- blackboard/blackboard_plan.cpp | 8 +++++++- blackboard/blackboard_plan.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index e951f01..567e1d9 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -200,7 +200,7 @@ void BlackboardPlan::_get_property_list(List *p_list) const { #ifdef TOOLS_ENABLED // * Editor - if (var.get_type() != Variant::NIL && (!is_derived() || !var_name.begins_with("_"))) { + if (!_is_var_hidden(var_name, var)) { if (has_mapping(var_name) || has_property_binding(var_name)) { p_list->push_back(PropertyInfo(Variant::STRING, var_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)); } else { @@ -226,6 +226,9 @@ void BlackboardPlan::_get_property_list(List *p_list) const { if (is_mapping_enabled()) { p_list->push_back(PropertyInfo(Variant::NIL, "Mapping", PROPERTY_HINT_NONE, "mapping/", PROPERTY_USAGE_GROUP)); for (const Pair &p : var_list) { + if (_is_var_hidden(p.first, p.second)) { + continue; + } if (unlikely(has_property_binding(p.first))) { p_list->push_back(PropertyInfo(Variant::STRING, "mapping/" + p.first, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)); } else { @@ -239,6 +242,9 @@ void BlackboardPlan::_get_property_list(List *p_list) const { // * Binding p_list->push_back(PropertyInfo(Variant::NIL, "Binding", PROPERTY_HINT_NONE, "binding/", PROPERTY_USAGE_GROUP)); for (const Pair &p : var_list) { + if (_is_var_hidden(p.first, p.second)) { + continue; + } if (unlikely(has_mapping(p.first))) { p_list->push_back(PropertyInfo(Variant::STRING, "binding/" + p.first, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY)); } else { diff --git a/blackboard/blackboard_plan.h b/blackboard/blackboard_plan.h index be22018..7626542 100644 --- a/blackboard/blackboard_plan.h +++ b/blackboard/blackboard_plan.h @@ -51,6 +51,8 @@ private: // 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; + _FORCE_INLINE_ bool _is_var_hidden(const String &p_name, const BBVariable &p_var) const { return p_var.get_type() == Variant::NIL || (is_derived() && p_name.begins_with("_")); } + protected: static void _bind_methods();