Add Editor functionality (rudimentary)
* Adding fav tasks * Saving/loading * Popup menu with a number of functions * Header Also: * BTTask: Fix broken get_icon() * BTTask: Initialize parent during set_children()
This commit is contained in:
parent
d128bdf8f6
commit
4c580c6b1d
1
SCsub
1
SCsub
|
@ -8,3 +8,4 @@ env.add_source_files(env.modules_sources, "bt/composites/*.cpp")
|
|||
env.add_source_files(env.modules_sources, "bt/actions/*.cpp")
|
||||
env.add_source_files(env.modules_sources, "bt/decorators/*.cpp")
|
||||
env.add_source_files(env.modules_sources, "bt/conditions/*.cpp")
|
||||
env.add_source_files(env.modules_sources, "editor/*.cpp")
|
||||
|
|
|
@ -42,7 +42,8 @@ void BTTask::_set_children(Array p_children) {
|
|||
children.resize(num_children);
|
||||
for (int i = 0; i < num_children; i++) {
|
||||
Variant task_var = p_children[i];
|
||||
const Ref<BTTask> task_ref = task_var;
|
||||
Ref<BTTask> task_ref = task_var;
|
||||
task_ref->parent = this;
|
||||
children.set(i, task_var);
|
||||
}
|
||||
}
|
||||
|
@ -202,7 +203,7 @@ String BTTask::get_configuration_warning() const {
|
|||
}
|
||||
|
||||
Ref<Texture> BTTask::get_icon() const {
|
||||
return EditorNode::get_singleton()->get_class_icon(_class_name, "Object");
|
||||
return EditorNode::get_singleton()->get_class_icon(get_class(), "Object");
|
||||
}
|
||||
|
||||
void BTTask::print_tree(int p_initial_tabs) const {
|
||||
|
|
|
@ -0,0 +1,473 @@
|
|||
/* limbo_ai_editor_plugin.cpp */
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
#include "limbo_ai_editor_plugin.h"
|
||||
|
||||
#include "../bt/composites/bt_parallel.h"
|
||||
#include "../bt/composites/bt_selector.h"
|
||||
#include "../bt/composites/bt_sequence.h"
|
||||
#include "core/class_db.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/math/math_defs.h"
|
||||
#include "core/object.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/print_string.h"
|
||||
#include "core/variant.h"
|
||||
#include "core/vector.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_plugin.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/file_dialog.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/tree.h"
|
||||
#include <cstddef>
|
||||
|
||||
TreeItem *TaskTree::_create_tree(const Ref<BTTask> &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<BTTask> 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);
|
||||
|
||||
// TODO: Update configuration warning.
|
||||
|
||||
// TODO: Update probabilities.
|
||||
}
|
||||
|
||||
void TaskTree::_update_tree() {
|
||||
Ref<BTTask> 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<BTTask> &p_task) const {
|
||||
if (p_task.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
TreeItem *item = tree->get_root();
|
||||
List<TreeItem *> 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<BehaviorTree> &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<BTTask> &p_task) {
|
||||
ERR_FAIL_COND(p_task.is_null());
|
||||
TreeItem *item = _find_item(p_task);
|
||||
if (item) {
|
||||
_update_item(item);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<BTTask> 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 LimboAIEditor::_add_task(const Ref<BTTask> &p_prototype) {
|
||||
ERR_FAIL_COND(p_prototype.is_null());
|
||||
Ref<BTTask> parent = task_tree->get_selected();
|
||||
if (parent.is_null()) {
|
||||
parent = task_tree->get_bt()->get_root_task();
|
||||
}
|
||||
if (parent.is_null()) {
|
||||
task_tree->get_bt()->set_root_task(p_prototype->clone());
|
||||
} else {
|
||||
parent->add_child(p_prototype->clone());
|
||||
}
|
||||
task_tree->update_tree();
|
||||
}
|
||||
|
||||
void LimboAIEditor::_update_header() {
|
||||
String text = task_tree->get_bt()->get_path();
|
||||
if (text.empty()) {
|
||||
text = TTR("New Behavior Tree");
|
||||
}
|
||||
header->set_text(text);
|
||||
header->set_icon(editor->get_object_icon(task_tree->get_bt().ptr(), "BehaviorTree"));
|
||||
}
|
||||
|
||||
void LimboAIEditor::_new_bt() {
|
||||
BehaviorTree *bt = memnew(BehaviorTree);
|
||||
bt->set_root_task(memnew(BTSelector));
|
||||
task_tree->load_bt(bt);
|
||||
_update_header();
|
||||
}
|
||||
|
||||
void LimboAIEditor::_save_bt(String p_path) {
|
||||
ERR_FAIL_COND_MSG(p_path.empty(), "Empty p_path");
|
||||
ERR_FAIL_COND_MSG(task_tree->get_bt().is_null(), "Behavior Tree is null.");
|
||||
task_tree->get_bt()->set_path(p_path, true);
|
||||
ResourceSaver::save(p_path, task_tree->get_bt(), ResourceSaver::FLAG_CHANGE_PATH);
|
||||
_update_header();
|
||||
}
|
||||
|
||||
void LimboAIEditor::_load_bt(String p_path) {
|
||||
ERR_FAIL_COND_MSG(p_path.empty(), "Empty p_path");
|
||||
task_tree->load_bt(ResourceLoader::load(p_path, "BehaviorTree"));
|
||||
_update_header();
|
||||
}
|
||||
|
||||
void LimboAIEditor::_on_tree_rmb(const Vector2 &p_menu_pos) {
|
||||
menu->set_size(Size2(1, 1));
|
||||
menu->set_position(p_menu_pos);
|
||||
|
||||
menu->clear();
|
||||
menu->add_icon_item(get_icon("Remove", "EditorIcons"), TTR("Remove"), ACTION_REMOVE);
|
||||
menu->add_separator();
|
||||
menu->add_icon_item(get_icon("MoveUp", "EditorIcons"), TTR("Move Up"), ACTION_MOVE_UP);
|
||||
menu->add_icon_item(get_icon("MoveDown", "EditorIcons"), TTR("Move Down"), ACTION_MOVE_DOWN);
|
||||
menu->add_icon_item(get_icon("Duplicate", "EditorIcons"), TTR("Duplicate"), ACTION_DUPLICATE);
|
||||
menu->add_icon_item(get_icon("NewRoot", "EditorIcons"), TTR("Make Root"), ACTION_MAKE_ROOT);
|
||||
|
||||
menu->popup();
|
||||
}
|
||||
|
||||
void LimboAIEditor::_on_action_selected(int p_id) {
|
||||
switch (p_id) {
|
||||
case ACTION_REMOVE: {
|
||||
Ref<BTTask> sel = task_tree->get_selected();
|
||||
if (sel.is_valid()) {
|
||||
if (sel->get_parent().is_null()) {
|
||||
task_tree->get_bt()->set_root_task(nullptr);
|
||||
} else {
|
||||
sel->get_parent()->remove_child(sel);
|
||||
}
|
||||
task_tree->update_tree();
|
||||
editor->edit_node(nullptr);
|
||||
}
|
||||
} break;
|
||||
case ACTION_MOVE_UP: {
|
||||
Ref<BTTask> sel = task_tree->get_selected();
|
||||
if (sel.is_valid() && sel->get_parent().is_valid()) {
|
||||
Ref<BTTask> parent = sel->get_parent();
|
||||
int idx = parent->get_child_index(sel);
|
||||
if (idx > 0 && idx < parent->get_child_count()) {
|
||||
parent->remove_child(sel);
|
||||
parent->add_child_at_index(sel, idx - 1);
|
||||
task_tree->update_tree();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case ACTION_MOVE_DOWN: {
|
||||
Ref<BTTask> sel = task_tree->get_selected();
|
||||
if (sel.is_valid() && sel->get_parent().is_valid()) {
|
||||
Ref<BTTask> parent = sel->get_parent();
|
||||
int idx = parent->get_child_index(sel);
|
||||
if (idx >= 0 && idx < (parent->get_child_count() - 1)) {
|
||||
parent->remove_child(sel);
|
||||
parent->add_child_at_index(sel, idx + 1);
|
||||
task_tree->update_tree();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case ACTION_DUPLICATE: {
|
||||
Ref<BTTask> sel = task_tree->get_selected();
|
||||
if (sel.is_valid()) {
|
||||
Ref<BTTask> parent = sel->get_parent();
|
||||
if (parent.is_null()) {
|
||||
parent = sel;
|
||||
}
|
||||
parent->add_child(sel->clone());
|
||||
task_tree->update_tree();
|
||||
}
|
||||
} break;
|
||||
case ACTION_MAKE_ROOT: {
|
||||
Ref<BTTask> sel = task_tree->get_selected();
|
||||
if (sel.is_valid() && task_tree->get_bt()->get_root_task() != sel) {
|
||||
Ref<BTTask> parent = sel->get_parent();
|
||||
ERR_FAIL_COND(parent.is_null());
|
||||
parent->remove_child(sel);
|
||||
Ref<BTTask> old_root = task_tree->get_bt()->get_root_task();
|
||||
task_tree->get_bt()->set_root_task(sel);
|
||||
sel->add_child(old_root);
|
||||
task_tree->update_tree();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void LimboAIEditor::_on_task_selected(const Ref<BTTask> &p_task) const {
|
||||
editor->edit_resource(p_task);
|
||||
}
|
||||
|
||||
void LimboAIEditor::_on_visibility_changed() const {
|
||||
if (is_visible()) {
|
||||
Ref<BTTask> sel = task_tree->get_selected();
|
||||
if (sel.is_valid()) {
|
||||
editor->edit_resource(sel);
|
||||
} else {
|
||||
editor->edit_resource(task_tree->get_bt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LimboAIEditor::_on_header_pressed() const {
|
||||
task_tree->deselect();
|
||||
editor->edit_resource(task_tree->get_bt());
|
||||
}
|
||||
|
||||
void LimboAIEditor::_on_save_pressed() {
|
||||
String path = task_tree->get_bt()->get_path();
|
||||
if (path.empty()) {
|
||||
save_dialog->popup_centered_ratio();
|
||||
} else {
|
||||
_save_bt(path);
|
||||
}
|
||||
}
|
||||
|
||||
void LimboAIEditor::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_add_task", "p_task"), &LimboAIEditor::_add_task);
|
||||
ClassDB::bind_method(D_METHOD("_on_tree_rmb"), &LimboAIEditor::_on_tree_rmb);
|
||||
ClassDB::bind_method(D_METHOD("_on_action_selected", "p_id"), &LimboAIEditor::_on_action_selected);
|
||||
ClassDB::bind_method(D_METHOD("_on_task_selected", "p_task"), &LimboAIEditor::_on_task_selected);
|
||||
ClassDB::bind_method(D_METHOD("_on_visibility_changed"), &LimboAIEditor::_on_visibility_changed);
|
||||
ClassDB::bind_method(D_METHOD("_on_header_pressed"), &LimboAIEditor::_on_header_pressed);
|
||||
ClassDB::bind_method(D_METHOD("_on_save_pressed"), &LimboAIEditor::_on_save_pressed);
|
||||
ClassDB::bind_method(D_METHOD("_new_bt"), &LimboAIEditor::_new_bt);
|
||||
ClassDB::bind_method(D_METHOD("_save_bt", "p_path"), &LimboAIEditor::_save_bt);
|
||||
ClassDB::bind_method(D_METHOD("_load_bt", "p_path"), &LimboAIEditor::_load_bt);
|
||||
}
|
||||
|
||||
LimboAIEditor::LimboAIEditor(EditorNode *p_editor) {
|
||||
editor = p_editor;
|
||||
|
||||
save_dialog = memnew(FileDialog);
|
||||
add_child(save_dialog);
|
||||
save_dialog->set_mode(FileDialog::MODE_SAVE_FILE);
|
||||
save_dialog->set_title("Save Behavior Tree");
|
||||
save_dialog->add_filter("*.tres");
|
||||
save_dialog->connect("file_selected", this, "_save_bt");
|
||||
save_dialog->hide();
|
||||
|
||||
load_dialog = memnew(FileDialog);
|
||||
add_child(load_dialog);
|
||||
load_dialog->set_mode(FileDialog::MODE_OPEN_FILE);
|
||||
load_dialog->set_title("Load Behavior Tree");
|
||||
load_dialog->add_filter("*.tres");
|
||||
load_dialog->connect("file_selected", this, "_load_bt");
|
||||
load_dialog->hide();
|
||||
|
||||
VBoxContainer *vbox = memnew(VBoxContainer);
|
||||
vbox->set_anchor(MARGIN_RIGHT, ANCHOR_END);
|
||||
vbox->set_anchor(MARGIN_BOTTOM, ANCHOR_END);
|
||||
add_child(vbox);
|
||||
|
||||
HBoxContainer *panel = memnew(HBoxContainer);
|
||||
vbox->add_child(panel);
|
||||
|
||||
Button *selector_btn = memnew(Button);
|
||||
selector_btn->set_text(TTR("Selector"));
|
||||
selector_btn->set_tooltip(TTR("Add Selector task."));
|
||||
selector_btn->set_icon(editor->get_class_icon("BTSelector"));
|
||||
selector_btn->set_flat(true);
|
||||
selector_btn->set_focus_mode(Control::FOCUS_NONE);
|
||||
selector_btn->connect("pressed", this, "_add_task", varray(Ref<BTTask>(memnew(BTSelector))));
|
||||
panel->add_child(selector_btn);
|
||||
|
||||
Button *sequence_btn = memnew(Button);
|
||||
sequence_btn->set_text(TTR("Sequence"));
|
||||
sequence_btn->set_tooltip(TTR("Add Sequence task."));
|
||||
sequence_btn->set_icon(editor->get_class_icon("BTSequence"));
|
||||
sequence_btn->set_flat(true);
|
||||
sequence_btn->set_focus_mode(Control::FOCUS_NONE);
|
||||
sequence_btn->connect("pressed", this, "_add_task", varray(Ref<BTTask>(memnew(BTSequence))));
|
||||
panel->add_child(sequence_btn);
|
||||
|
||||
Button *parallel_btn = memnew(Button);
|
||||
parallel_btn->set_text(TTR("Parallel"));
|
||||
parallel_btn->set_tooltip(TTR("Add Parallel task."));
|
||||
parallel_btn->set_icon(editor->get_class_icon("BTParallel"));
|
||||
parallel_btn->set_flat(true);
|
||||
parallel_btn->set_focus_mode(Control::FOCUS_NONE);
|
||||
parallel_btn->connect("pressed", this, "_add_task", varray(Ref<BTTask>(memnew(BTParallel))));
|
||||
panel->add_child(parallel_btn);
|
||||
|
||||
panel->add_child(memnew(VSeparator));
|
||||
|
||||
Button *new_btn = memnew(Button);
|
||||
panel->add_child(new_btn);
|
||||
new_btn->set_text(TTR("New"));
|
||||
new_btn->set_tooltip(TTR("Create new behavior tree."));
|
||||
new_btn->set_icon(editor->get_gui_base()->get_icon("New", "EditorIcons"));
|
||||
new_btn->set_flat(true);
|
||||
new_btn->set_focus_mode(Control::FOCUS_NONE);
|
||||
new_btn->connect("pressed", this, "_new_bt");
|
||||
|
||||
Button *load_btn = memnew(Button);
|
||||
panel->add_child(load_btn);
|
||||
load_btn->set_text(TTR("Load"));
|
||||
load_btn->set_tooltip(TTR("Load behavior tree."));
|
||||
load_btn->set_icon(editor->get_gui_base()->get_icon("Load", "EditorIcons"));
|
||||
load_btn->set_flat(true);
|
||||
load_btn->set_focus_mode(Control::FOCUS_NONE);
|
||||
load_btn->connect("pressed", load_dialog, "popup_centered_ratio");
|
||||
|
||||
Button *save_btn = memnew(Button);
|
||||
panel->add_child(save_btn);
|
||||
save_btn->set_text(TTR("Save"));
|
||||
save_btn->set_tooltip(TTR("Save current behavior tree."));
|
||||
save_btn->set_icon(editor->get_gui_base()->get_icon("Save", "EditorIcons"));
|
||||
save_btn->set_flat(true);
|
||||
save_btn->set_focus_mode(Control::FOCUS_NONE);
|
||||
save_btn->connect("pressed", this, "_on_save_pressed");
|
||||
|
||||
panel->add_child(memnew(VSeparator));
|
||||
|
||||
header = memnew(Button);
|
||||
vbox->add_child(header);
|
||||
header->set_text_align(Button::ALIGN_LEFT);
|
||||
header->add_constant_override("hseparation", 8);
|
||||
header->connect("pressed", this, "_on_header_pressed");
|
||||
|
||||
task_tree = memnew(TaskTree);
|
||||
task_tree->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
task_tree->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
vbox->add_child(task_tree);
|
||||
task_tree->connect("rmb_pressed", this, "_on_tree_rmb");
|
||||
task_tree->connect("task_selected", this, "_on_task_selected");
|
||||
|
||||
menu = memnew(PopupMenu);
|
||||
add_child(menu);
|
||||
menu->connect("id_pressed", this, "_on_action_selected");
|
||||
menu->set_hide_on_window_lose_focus(true);
|
||||
|
||||
BehaviorTree *bt = memnew(BehaviorTree);
|
||||
BTSelector *seq = memnew(BTSelector);
|
||||
bt->set_root_task(seq);
|
||||
|
||||
task_tree->load_bt(bt);
|
||||
_update_header();
|
||||
|
||||
task_tree->connect("visibility_changed", this, "_on_visibility_changed");
|
||||
}
|
||||
|
||||
LimboAIEditor::~LimboAIEditor() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const Ref<Texture> LimboAIEditorPlugin::get_icon() const {
|
||||
// TODO:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void LimboAIEditorPlugin::_notification(int p_notification) {
|
||||
// print_line(vformat("NOTIFICATION: %d", p_notification));
|
||||
}
|
||||
|
||||
void LimboAIEditorPlugin::make_visible(bool p_visible) {
|
||||
limbo_ai_editor->set_visible(p_visible);
|
||||
}
|
||||
|
||||
LimboAIEditorPlugin::LimboAIEditorPlugin(EditorNode *p_editor) {
|
||||
editor = p_editor;
|
||||
limbo_ai_editor = memnew(LimboAIEditor(p_editor));
|
||||
limbo_ai_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
editor->get_viewport()->add_child(limbo_ai_editor);
|
||||
limbo_ai_editor->hide();
|
||||
}
|
||||
|
||||
LimboAIEditorPlugin::~LimboAIEditorPlugin() {
|
||||
}
|
||||
|
||||
#endif // TOOLS_ENABLED
|
|
@ -0,0 +1,108 @@
|
|||
/* limbo_ai_editor_plugin.h */
|
||||
|
||||
#ifndef LIMBO_AI_EDITOR_PLUGIN_H
|
||||
#define LIMBO_AI_EDITOR_PLUGIN_H
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
#include "../bt/behavior_tree.h"
|
||||
#include "core/object.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_plugin.h"
|
||||
#include "scene/gui/file_dialog.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class TaskTree : public Control {
|
||||
GDCLASS(TaskTree, Control);
|
||||
|
||||
private:
|
||||
Tree *tree;
|
||||
Ref<BehaviorTree> bt;
|
||||
Ref<BTTask> last_selected;
|
||||
|
||||
TreeItem *_create_tree(const Ref<BTTask> &p_task, TreeItem *p_parent, int p_idx = -1);
|
||||
void _update_item(TreeItem *p_item);
|
||||
void _update_tree();
|
||||
TreeItem *_find_item(const Ref<BTTask> &p_task) const;
|
||||
|
||||
void _on_item_selected();
|
||||
void _on_item_rmb_selected(const Vector2 &p_pos);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void load_bt(const Ref<BehaviorTree> &p_behavior_tree);
|
||||
Ref<BehaviorTree> get_bt() const { return bt; }
|
||||
void update_tree() { _update_tree(); }
|
||||
void update_task(const Ref<BTTask> &p_task);
|
||||
Ref<BTTask> get_selected() const;
|
||||
void deselect();
|
||||
|
||||
TaskTree();
|
||||
~TaskTree();
|
||||
};
|
||||
|
||||
class LimboAIEditor : public Control {
|
||||
GDCLASS(LimboAIEditor, Control);
|
||||
|
||||
private:
|
||||
enum Action {
|
||||
ACTION_REMOVE,
|
||||
ACTION_MOVE_UP,
|
||||
ACTION_MOVE_DOWN,
|
||||
ACTION_DUPLICATE,
|
||||
ACTION_MAKE_ROOT,
|
||||
};
|
||||
|
||||
EditorNode *editor;
|
||||
Button *header;
|
||||
TaskTree *task_tree;
|
||||
PopupMenu *menu;
|
||||
FileDialog *save_dialog;
|
||||
FileDialog *load_dialog;
|
||||
|
||||
void _add_task(const Ref<BTTask> &p_prototype);
|
||||
void _update_header();
|
||||
void _new_bt();
|
||||
void _save_bt(String p_path);
|
||||
void _load_bt(String p_path);
|
||||
|
||||
void _on_tree_rmb(const Vector2 &p_menu_pos);
|
||||
void _on_action_selected(int p_id);
|
||||
void _on_task_selected(const Ref<BTTask> &p_task) const;
|
||||
void _on_visibility_changed() const;
|
||||
void _on_header_pressed() const;
|
||||
void _on_save_pressed();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
LimboAIEditor(EditorNode *p_editor);
|
||||
~LimboAIEditor();
|
||||
};
|
||||
|
||||
class LimboAIEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(LimboAIEditorPlugin, EditorPlugin);
|
||||
|
||||
private:
|
||||
EditorNode *editor;
|
||||
LimboAIEditor *limbo_ai_editor;
|
||||
|
||||
protected:
|
||||
void _notification(int p_notification);
|
||||
|
||||
public:
|
||||
virtual String get_name() const { return "LimboAI"; }
|
||||
virtual const Ref<Texture> get_icon() const;
|
||||
bool has_main_screen() const { return true; }
|
||||
virtual void make_visible(bool p_visible);
|
||||
|
||||
LimboAIEditorPlugin(EditorNode *p_editor);
|
||||
~LimboAIEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#endif // LIMBO_AI_EDITOR_PLUGIN_H
|
|
@ -36,6 +36,10 @@
|
|||
#include "limbo_string_names.h"
|
||||
#include "limbo_utility.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/limbo_ai_editor_plugin.h"
|
||||
#endif
|
||||
|
||||
void register_limboai_types() {
|
||||
ClassDB::register_class<BTTask>();
|
||||
ClassDB::register_class<BehaviorTree>();
|
||||
|
@ -72,6 +76,10 @@ void register_limboai_types() {
|
|||
|
||||
ClassDB::register_class<BTCondition>();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorPlugins::add_by_type<LimboAIEditorPlugin>();
|
||||
#endif
|
||||
|
||||
LimboStringNames::create();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue