/* limbo_ai_editor_plugin.cpp */ #ifdef TOOLS_ENABLED #include "limbo_ai_editor_plugin.h" #include "core/class_db.h" #include "core/error_list.h" #include "core/error_macros.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/list.h" #include "core/math/math_defs.h" #include "core/object.h" #include "core/os/dir_access.h" #include "core/os/memory.h" #include "core/print_string.h" #include "core/project_settings.h" #include "core/script_language.h" #include "core/string_name.h" #include "core/typedefs.h" #include "core/ustring.h" #include "core/variant.h" #include "core/vector.h" #include "editor/editor_node.h" #include "editor/editor_plugin.h" #include "modules/limboai/bt/actions/bt_action.h" #include "modules/limboai/bt/behavior_tree.h" #include "modules/limboai/bt/bt_task.h" #include "modules/limboai/bt/composites/bt_parallel.h" #include "modules/limboai/bt/composites/bt_selector.h" #include "modules/limboai/bt/composites/bt_sequence.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/file_dialog.h" #include "scene/gui/flow_container.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" #include "scene/gui/popup_menu.h" #include "scene/gui/scroll_container.h" #include "scene/gui/separator.h" #include "scene/gui/split_container.h" #include "scene/gui/tree.h" #include TreeItem *TaskTree::_create_tree(const Ref &p_task, TreeItem *p_parent, int p_idx) { ERR_FAIL_COND_V(p_task.is_null(), nullptr); TreeItem *item = tree->create_item(p_parent, p_idx); item->set_metadata(0, p_task); // p_task->connect("changed"...) for (int i = 0; i < p_task->get_child_count(); i++) { _create_tree(p_task->get_child(i), item); } _update_item(item); return item; } void TaskTree::_update_item(TreeItem *p_item) { ERR_FAIL_COND_MSG(p_item == nullptr, "Argument \"p_item\" is null."); Ref task = p_item->get_metadata(0); ERR_FAIL_COND_MSG(!task.is_valid(), "Invalid task reference in metadata."); p_item->set_text(0, task->get_task_name()); p_item->set_icon(0, task->get_icon()); p_item->set_editable(0, false); for (int i = 0; i < p_item->get_button_count(0); i++) { p_item->erase_button(0, i); } String warning = task->get_configuration_warning(); if (!warning.empty()) { p_item->add_button(0, get_icon("NodeWarning", "EditorIcons"), 0, false, warning); } // TODO: Update probabilities. } void TaskTree::_update_tree() { Ref sel; if (tree->get_selected()) { sel = tree->get_selected()->get_metadata(0); } tree->clear(); if (bt->get_root_task().is_valid()) { _create_tree(bt->get_root_task(), nullptr); } TreeItem *item = _find_item(sel); if (item) { item->select(0); } } TreeItem *TaskTree::_find_item(const Ref &p_task) const { if (p_task.is_null()) { return nullptr; } TreeItem *item = tree->get_root(); List stack; while (item && item->get_metadata(0) != p_task) { if (item->get_children()) { stack.push_back(item->get_children()); } item = item->get_next(); if (item == nullptr && !stack.empty()) { item = stack.front()->get(); stack.pop_front(); } } return item; } void TaskTree::_on_item_rmb_selected(const Vector2 &p_pos) { emit_signal("rmb_pressed", tree->get_global_transform().xform(p_pos)); } void TaskTree::_on_item_selected() { if (last_selected.is_valid()) { update_task(last_selected); } last_selected = get_selected(); emit_signal("task_selected", last_selected); } void TaskTree::load_bt(const Ref &p_behavior_tree) { ERR_FAIL_COND_MSG(p_behavior_tree.is_null(), "Tried to load a null tree."); bt = p_behavior_tree; tree->clear(); if (bt->get_root_task().is_valid()) { _create_tree(bt->get_root_task(), nullptr); } } void TaskTree::update_task(const Ref &p_task) { ERR_FAIL_COND(p_task.is_null()); TreeItem *item = _find_item(p_task); if (item) { _update_item(item); } } Ref TaskTree::get_selected() const { if (tree->get_selected()) { return tree->get_selected()->get_metadata(0); } return nullptr; } void TaskTree::deselect() { TreeItem *sel = tree->get_selected(); if (sel) { sel->deselect(0); } } void TaskTree::_bind_methods() { ClassDB::bind_method(D_METHOD("_on_item_rmb_selected"), &TaskTree::_on_item_rmb_selected); ClassDB::bind_method(D_METHOD("_on_item_selected"), &TaskTree::_on_item_selected); ClassDB::bind_method(D_METHOD("load_bt", "p_behavior_tree"), &TaskTree::load_bt); ClassDB::bind_method(D_METHOD("get_bt"), &TaskTree::get_bt); ClassDB::bind_method(D_METHOD("update_tree"), &TaskTree::update_tree); ClassDB::bind_method(D_METHOD("update_task", "p_task"), &TaskTree::update_task); ClassDB::bind_method(D_METHOD("get_selected"), &TaskTree::get_selected); ClassDB::bind_method(D_METHOD("deselect"), &TaskTree::deselect); ADD_SIGNAL(MethodInfo("rmb_pressed")); ADD_SIGNAL(MethodInfo("task_selected")); } TaskTree::TaskTree() { tree = memnew(Tree); add_child(tree); tree->set_columns(2); tree->set_column_expand(0, true); tree->set_column_expand(1, false); tree->set_column_min_width(1, 64); tree->set_anchor(MARGIN_RIGHT, ANCHOR_END); tree->set_anchor(MARGIN_BOTTOM, ANCHOR_END); tree->set_allow_rmb_select(true); tree->connect("item_rmb_selected", this, "_on_item_rmb_selected"); tree->connect("item_selected", this, "_on_item_selected"); } TaskTree::~TaskTree() { } //////////////////////////////////////////////////////////////////////////////// void TaskSection::_on_task_button_pressed(const StringName &p_task) { emit_signal("task_button_pressed", p_task); } void TaskSection::_on_header_pressed() { tasks_container->set_visible(!tasks_container->is_visible()); section_header->set_icon(tasks_container->is_visible() ? get_icon("GuiTreeArrowDown", "EditorIcons") : get_icon("GuiTreeArrowRight", "EditorIcons")); } void TaskSection::set_filter(String p_filter_text) { int num_hidden = 0; if (p_filter_text.empty()) { for (int i = 0; i < tasks_container->get_child_count(); i++) { Object::cast_to