Editor: Add "Extract Subtree" action

Supports undo-redo, but it doesn't remove the newly created file automatically.
This commit is contained in:
Serhii Snitsaruk 2023-12-17 15:24:38 +01:00
parent a2a2ba1f7b
commit 35f1dabe93
3 changed files with 64 additions and 6 deletions

View File

@ -14,9 +14,11 @@
#include "limbo_ai_editor_plugin.h" #include "limbo_ai_editor_plugin.h"
#include "action_banner.h" #include "action_banner.h"
#include "modules/limboai/bt/behavior_tree.h"
#include "modules/limboai/bt/tasks/bt_comment.h" #include "modules/limboai/bt/tasks/bt_comment.h"
#include "modules/limboai/bt/tasks/composites/bt_probability_selector.h" #include "modules/limboai/bt/tasks/composites/bt_probability_selector.h"
#include "modules/limboai/bt/tasks/composites/bt_selector.h" #include "modules/limboai/bt/tasks/composites/bt_selector.h"
#include "modules/limboai/bt/tasks/decorators/bt_subtree.h"
#include "modules/limboai/editor/debugger/limbo_debugger_plugin.h" #include "modules/limboai/editor/debugger/limbo_debugger_plugin.h"
#include "modules/limboai/editor/editor_property_bb_param.h" #include "modules/limboai/editor/editor_property_bb_param.h"
#include "modules/limboai/util/limbo_utility.h" #include "modules/limboai/util/limbo_utility.h"
@ -244,6 +246,39 @@ void LimboAIEditor::_remove_task_from_favorite(const String &p_task) {
ProjectSettings::get_singleton()->save(); ProjectSettings::get_singleton()->save();
} }
void LimboAIEditor::_extract_subtree(const String &p_path) {
Ref<BTTask> selected = task_tree->get_selected();
ERR_FAIL_COND(selected.is_null());
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Extract Subtree"));
Ref<BehaviorTree> bt = memnew(BehaviorTree);
bt->set_root_task(selected->clone());
bt->set_path(p_path);
ResourceSaver::save(bt, p_path, ResourceSaver::FLAG_CHANGE_PATH);
Ref<BTSubtree> subtree = memnew(BTSubtree);
subtree->set_subtree(bt);
if (selected->is_root()) {
undo_redo->add_do_method(task_tree->get_bt().ptr(), SNAME("set_root_task"), subtree);
undo_redo->add_undo_method(task_tree->get_bt().ptr(), SNAME("set_root_task"), selected);
} else {
int idx = selected->get_index();
undo_redo->add_do_method(selected->get_parent().ptr(), SNAME("remove_child"), selected);
undo_redo->add_do_method(selected->get_parent().ptr(), SNAME("add_child_at_index"), subtree, idx);
undo_redo->add_undo_method(selected->get_parent().ptr(), SNAME("remove_child"), subtree);
undo_redo->add_undo_method(selected->get_parent().ptr(), SNAME("add_child_at_index"), selected, idx);
}
undo_redo->add_do_method(task_tree, SNAME("update_tree"));
undo_redo->add_undo_method(task_tree, SNAME("update_tree"));
undo_redo->commit_action();
EditorNode::get_singleton()->edit_resource(task_tree->get_selected());
_mark_as_dirty(true);
}
void LimboAIEditor::shortcut_input(const Ref<InputEvent> &p_event) { void LimboAIEditor::shortcut_input(const Ref<InputEvent> &p_event) {
if (!p_event->is_pressed()) { if (!p_event->is_pressed()) {
return; return;
@ -305,6 +340,7 @@ void LimboAIEditor::_on_tree_rmb(const Vector2 &p_menu_pos) {
menu->add_icon_shortcut(theme_cache.move_task_down_icon, ED_GET_SHORTCUT("limbo_ai/move_task_down"), ACTION_MOVE_DOWN); menu->add_icon_shortcut(theme_cache.move_task_down_icon, ED_GET_SHORTCUT("limbo_ai/move_task_down"), ACTION_MOVE_DOWN);
menu->add_icon_shortcut(theme_cache.duplicate_task_icon, ED_GET_SHORTCUT("limbo_ai/duplicate_task"), ACTION_DUPLICATE); menu->add_icon_shortcut(theme_cache.duplicate_task_icon, ED_GET_SHORTCUT("limbo_ai/duplicate_task"), ACTION_DUPLICATE);
menu->add_icon_item(theme_cache.make_root_icon, TTR("Make Root"), ACTION_MAKE_ROOT); menu->add_icon_item(theme_cache.make_root_icon, TTR("Make Root"), ACTION_MAKE_ROOT);
menu->add_icon_item(theme_cache.extract_subtree_icon, TTR("Extract Subtree"), ACTION_EXTRACT_SUBTREE);
menu->add_separator(); menu->add_separator();
menu->add_icon_shortcut(theme_cache.remove_task_icon, ED_GET_SHORTCUT("limbo_ai/remove_task"), ACTION_REMOVE); menu->add_icon_shortcut(theme_cache.remove_task_icon, ED_GET_SHORTCUT("limbo_ai/remove_task"), ACTION_REMOVE);
@ -441,11 +477,17 @@ void LimboAIEditor::_action_selected(int p_id) {
_mark_as_dirty(true); _mark_as_dirty(true);
} }
} break; } break;
case ACTION_EXTRACT_SUBTREE: {
Ref<BTTask> sel = task_tree->get_selected();
if (sel.is_valid() && !sel->is_class_ptr(BTSubtree::get_class_ptr_static())) {
extract_dialog->popup_centered_ratio();
}
} break;
case ACTION_REMOVE: { case ACTION_REMOVE: {
Ref<BTTask> sel = task_tree->get_selected(); Ref<BTTask> sel = task_tree->get_selected();
if (sel.is_valid()) { if (sel.is_valid()) {
undo_redo->create_action(TTR("Remove BT Task")); undo_redo->create_action(TTR("Remove BT Task"));
if (sel->get_parent().is_null()) { if (sel->is_root()) {
undo_redo->add_do_method(task_tree->get_bt().ptr(), SNAME("set_root_task"), Variant()); undo_redo->add_do_method(task_tree->get_bt().ptr(), SNAME("set_root_task"), Variant());
undo_redo->add_undo_method(task_tree->get_bt().ptr(), SNAME("set_root_task"), task_tree->get_bt()->get_root_task()); undo_redo->add_undo_method(task_tree->get_bt().ptr(), SNAME("set_root_task"), task_tree->get_bt()->get_root_task());
} else { } else {
@ -899,6 +941,7 @@ void LimboAIEditor::_update_theme_item_cache() {
theme_cache.remove_task_icon = get_editor_theme_icon(SNAME("Remove")); theme_cache.remove_task_icon = get_editor_theme_icon(SNAME("Remove"));
theme_cache.rename_task_icon = get_editor_theme_icon(SNAME("Rename")); theme_cache.rename_task_icon = get_editor_theme_icon(SNAME("Rename"));
theme_cache.change_type_icon = get_editor_theme_icon(SNAME("Reload")); theme_cache.change_type_icon = get_editor_theme_icon(SNAME("Reload"));
theme_cache.extract_subtree_icon = get_editor_theme_icon(SNAME("LimboExtractSubtree"));
} }
void LimboAIEditor::_notification(int p_what) { void LimboAIEditor::_notification(int p_what) {
@ -966,7 +1009,7 @@ LimboAIEditor::LimboAIEditor() {
save_dialog = memnew(FileDialog); save_dialog = memnew(FileDialog);
save_dialog->set_file_mode(FileDialog::FILE_MODE_SAVE_FILE); save_dialog->set_file_mode(FileDialog::FILE_MODE_SAVE_FILE);
save_dialog->set_title("Save Behavior Tree"); save_dialog->set_title(TTR("Save Behavior Tree"));
save_dialog->add_filter("*.tres"); save_dialog->add_filter("*.tres");
save_dialog->connect("file_selected", callable_mp(this, &LimboAIEditor::_save_bt)); save_dialog->connect("file_selected", callable_mp(this, &LimboAIEditor::_save_bt));
save_dialog->hide(); save_dialog->hide();
@ -974,12 +1017,20 @@ LimboAIEditor::LimboAIEditor() {
load_dialog = memnew(FileDialog); load_dialog = memnew(FileDialog);
load_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE); load_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE);
load_dialog->set_title("Load Behavior Tree"); load_dialog->set_title(TTR("Load Behavior Tree"));
load_dialog->add_filter("*.tres"); load_dialog->add_filter("*.tres");
load_dialog->connect("file_selected", callable_mp(this, &LimboAIEditor::_load_bt)); load_dialog->connect("file_selected", callable_mp(this, &LimboAIEditor::_load_bt));
load_dialog->hide(); load_dialog->hide();
add_child(load_dialog); add_child(load_dialog);
extract_dialog = memnew(FileDialog);
extract_dialog->set_file_mode(FileDialog::FILE_MODE_SAVE_FILE);
extract_dialog->set_title(TTR("Save Extracted Tree"));
extract_dialog->add_filter("*.tres");
extract_dialog->connect("file_selected", callable_mp(this, &LimboAIEditor::_extract_subtree));
extract_dialog->hide();
add_child(extract_dialog);
vbox = memnew(VBoxContainer); vbox = memnew(VBoxContainer);
vbox->set_anchor(SIDE_RIGHT, ANCHOR_END); vbox->set_anchor(SIDE_RIGHT, ANCHOR_END);
vbox->set_anchor(SIDE_BOTTOM, ANCHOR_END); vbox->set_anchor(SIDE_BOTTOM, ANCHOR_END);
@ -1216,8 +1267,10 @@ LimboAIEditor::LimboAIEditor() {
GLOBAL_DEF(PropertyInfo(Variant::STRING, "limbo_ai/behavior_tree/user_task_dir_2", PROPERTY_HINT_DIR), ""); GLOBAL_DEF(PropertyInfo(Variant::STRING, "limbo_ai/behavior_tree/user_task_dir_2", PROPERTY_HINT_DIR), "");
GLOBAL_DEF(PropertyInfo(Variant::STRING, "limbo_ai/behavior_tree/user_task_dir_3", PROPERTY_HINT_DIR), ""); GLOBAL_DEF(PropertyInfo(Variant::STRING, "limbo_ai/behavior_tree/user_task_dir_3", PROPERTY_HINT_DIR), "");
save_dialog->set_current_dir(GLOBAL_GET("limbo_ai/behavior_tree/behavior_tree_default_dir")); String bt_default_dir = GLOBAL_GET("limbo_ai/behavior_tree/behavior_tree_default_dir");
load_dialog->set_current_dir(GLOBAL_GET("limbo_ai/behavior_tree/behavior_tree_default_dir")); save_dialog->set_current_dir(bt_default_dir);
load_dialog->set_current_dir(bt_default_dir);
extract_dialog->set_current_dir(bt_default_dir);
new_script_btn->connect("pressed", callable_mp(ScriptEditor::get_singleton(), &ScriptEditor::open_script_create_dialog).bind("BTAction", String(GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_1")).path_join("new_task"))); new_script_btn->connect("pressed", callable_mp(ScriptEditor::get_singleton(), &ScriptEditor::open_script_create_dialog).bind("BTAction", String(GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_1")).path_join("new_task")));
EditorFileSystem::get_singleton()->connect("resources_reload", callable_mp(this, &LimboAIEditor::_on_resources_reload)); EditorFileSystem::get_singleton()->connect("resources_reload", callable_mp(this, &LimboAIEditor::_on_resources_reload));

View File

@ -42,15 +42,16 @@ class LimboAIEditor : public Control {
private: private:
enum Action { enum Action {
ACTION_EDIT_PROBABILITY,
ACTION_RENAME, ACTION_RENAME,
ACTION_CHANGE_TYPE, ACTION_CHANGE_TYPE,
ACTION_EDIT_PROBABILITY,
ACTION_EDIT_SCRIPT, ACTION_EDIT_SCRIPT,
ACTION_OPEN_DOC, ACTION_OPEN_DOC,
ACTION_MOVE_UP, ACTION_MOVE_UP,
ACTION_MOVE_DOWN, ACTION_MOVE_DOWN,
ACTION_DUPLICATE, ACTION_DUPLICATE,
ACTION_MAKE_ROOT, ACTION_MAKE_ROOT,
ACTION_EXTRACT_SUBTREE,
ACTION_REMOVE, ACTION_REMOVE,
}; };
@ -73,6 +74,7 @@ private:
Ref<Texture2D> remove_task_icon; Ref<Texture2D> remove_task_icon;
Ref<Texture2D> rename_task_icon; Ref<Texture2D> rename_task_icon;
Ref<Texture2D> change_type_icon; Ref<Texture2D> change_type_icon;
Ref<Texture2D> extract_subtree_icon;
} theme_cache; } theme_cache;
Vector<Ref<BehaviorTree>> history; Vector<Ref<BehaviorTree>> history;
@ -99,6 +101,7 @@ private:
FileDialog *save_dialog; FileDialog *save_dialog;
FileDialog *load_dialog; FileDialog *load_dialog;
FileDialog *extract_dialog;
Button *history_back; Button *history_back;
Button *history_forward; Button *history_forward;
@ -132,6 +135,7 @@ private:
void _create_user_task_dir(); void _create_user_task_dir();
void _edit_project_settings(); void _edit_project_settings();
void _remove_task_from_favorite(const String &p_task); void _remove_task_from_favorite(const String &p_task);
void _extract_subtree(const String &p_path);
void _reload_modified(); void _reload_modified();
void _resave_modified(String _str = ""); void _resave_modified(String _str = "");

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 35 KiB