2023-04-13 07:29:45 +00:00
|
|
|
/* behavior_tree_view.cpp */
|
|
|
|
|
|
|
|
#include "behavior_tree_view.h"
|
|
|
|
#include "behavior_tree_data.h"
|
|
|
|
#include "core/math/color.h"
|
2023-04-14 08:16:26 +00:00
|
|
|
#include "core/math/math_defs.h"
|
|
|
|
#include "core/object/callable_method_pointer.h"
|
|
|
|
#include "core/typedefs.h"
|
|
|
|
#include "editor/editor_scale.h"
|
2023-04-13 07:29:45 +00:00
|
|
|
#include "modules/limboai/bt/bt_task.h"
|
|
|
|
#include "modules/limboai/limbo_utility.h"
|
2023-04-14 08:16:26 +00:00
|
|
|
#include "scene/resources/style_box.h"
|
|
|
|
|
|
|
|
void BehaviorTreeView::_draw_running_status(Object *p_obj, Rect2 p_rect) {
|
|
|
|
p_rect = p_rect.grow_side(SIDE_LEFT, p_rect.get_position().x);
|
|
|
|
sbf_running.draw(tree->get_canvas_item(), p_rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BehaviorTreeView::_draw_success_status(Object *p_obj, Rect2 p_rect) {
|
|
|
|
p_rect = p_rect.grow_side(SIDE_LEFT, p_rect.get_position().x);
|
|
|
|
sbf_success.draw(tree->get_canvas_item(), p_rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BehaviorTreeView::_draw_failure_status(Object *p_obj, Rect2 p_rect) {
|
|
|
|
p_rect = p_rect.grow_side(SIDE_LEFT, p_rect.get_position().x);
|
|
|
|
sbf_failure.draw(tree->get_canvas_item(), p_rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BehaviorTreeView::_item_collapsed(Object *p_obj) {
|
|
|
|
TreeItem *item = Object::cast_to<TreeItem>(p_obj);
|
|
|
|
if (!item) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int id = item->get_metadata(0);
|
|
|
|
bool collapsed = item->is_collapsed();
|
|
|
|
if (!collapsed_ids.has(id) && collapsed) {
|
|
|
|
collapsed_ids.push_back(item->get_metadata(0));
|
|
|
|
} else if (collapsed_ids.has(id) && !collapsed) {
|
|
|
|
collapsed_ids.erase(id);
|
|
|
|
}
|
|
|
|
}
|
2023-04-13 07:29:45 +00:00
|
|
|
|
|
|
|
void BehaviorTreeView::update_tree(const BehaviorTreeData &p_data) {
|
2023-04-14 08:16:26 +00:00
|
|
|
// Remember selected.
|
|
|
|
int selected_id = -1;
|
|
|
|
if (tree->get_selected()) {
|
|
|
|
selected_id = tree->get_selected()->get_metadata(0);
|
|
|
|
}
|
|
|
|
|
2023-04-13 07:29:45 +00:00
|
|
|
tree->clear();
|
|
|
|
TreeItem *parent = nullptr;
|
|
|
|
List<Pair<TreeItem *, int>> parents;
|
|
|
|
for (const BehaviorTreeData::TaskData &task_data : p_data.tasks) {
|
|
|
|
// Figure out parent.
|
|
|
|
parent = nullptr;
|
|
|
|
if (parents.size()) {
|
|
|
|
Pair<TreeItem *, int> &p = parents[0];
|
|
|
|
parent = p.first;
|
|
|
|
if (!(--p.second)) {
|
|
|
|
// No children left, remove it.
|
|
|
|
parents.pop_front();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TreeItem *item = tree->create_item(parent);
|
2023-04-14 08:16:26 +00:00
|
|
|
// Do this first because it resets properties of the cell...
|
|
|
|
item->set_cell_mode(0, TreeItem::CELL_MODE_CUSTOM);
|
2023-04-14 11:18:23 +00:00
|
|
|
item->set_cell_mode(1, TreeItem::CELL_MODE_ICON);
|
2023-04-14 08:16:26 +00:00
|
|
|
|
|
|
|
item->set_metadata(0, task_data.id);
|
2023-04-13 07:29:45 +00:00
|
|
|
item->set_text(0, task_data.name);
|
|
|
|
item->set_icon(0, LimboUtility::get_singleton()->get_task_icon(task_data.type_name));
|
2023-04-14 11:42:17 +00:00
|
|
|
item->set_text(2, rtos(Math::snapped(task_data.elapsed_time, 0.01)).pad_decimals(2));
|
2023-04-13 07:29:45 +00:00
|
|
|
|
2023-04-14 08:16:26 +00:00
|
|
|
if (task_data.status == BTTask::SUCCESS) {
|
|
|
|
item->set_custom_draw(0, this, SNAME("_draw_success_status"));
|
2023-04-14 11:18:23 +00:00
|
|
|
item->set_icon(1, icon_success);
|
2023-04-14 08:16:26 +00:00
|
|
|
} else if (task_data.status == BTTask::FAILURE) {
|
|
|
|
item->set_custom_draw(0, this, SNAME("_draw_failure_status"));
|
2023-04-14 11:18:23 +00:00
|
|
|
item->set_icon(1, icon_failure);
|
2023-04-14 08:16:26 +00:00
|
|
|
} else if (task_data.status == BTTask::RUNNING) {
|
|
|
|
item->set_custom_draw(0, this, SNAME("_draw_running_status"));
|
2023-04-14 11:18:23 +00:00
|
|
|
item->set_icon(1, icon_running);
|
2023-04-13 07:29:45 +00:00
|
|
|
}
|
2023-04-14 08:16:26 +00:00
|
|
|
|
|
|
|
if (task_data.id == selected_id) {
|
|
|
|
tree->set_selected(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (collapsed_ids.has(task_data.id)) {
|
|
|
|
item->set_collapsed(true);
|
2023-04-13 07:29:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add in front of parents stack if it expects children.
|
|
|
|
if (task_data.num_children) {
|
|
|
|
parents.push_front(Pair<TreeItem *, int>(item, task_data.num_children));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BehaviorTreeView::clear() {
|
|
|
|
tree->clear();
|
2023-04-14 08:16:26 +00:00
|
|
|
collapsed_ids.clear();
|
|
|
|
}
|
|
|
|
|
2023-04-14 11:18:23 +00:00
|
|
|
void BehaviorTreeView::_notification(int p_notification) {
|
|
|
|
if (p_notification == NOTIFICATION_THEME_CHANGED) {
|
2023-04-14 12:03:06 +00:00
|
|
|
icon_running = get_theme_icon(SNAME("LimboExtraClock"), SNAME("EditorIcons"));
|
2023-04-14 11:18:23 +00:00
|
|
|
icon_success = get_theme_icon(SNAME("BTAlwaysSucceed"), SNAME("EditorIcons"));
|
|
|
|
icon_failure = get_theme_icon(SNAME("BTAlwaysFail"), SNAME("EditorIcons"));
|
2023-04-14 12:31:20 +00:00
|
|
|
|
|
|
|
Color running_border = Color::html("#fea900");
|
|
|
|
Color running_fill = Color(running_border, 0.1);
|
|
|
|
Color success_border = Color::html("#2fa139");
|
|
|
|
Color success_fill = Color(success_border, 0.1);
|
|
|
|
Color failure_border = Color::html("#cd3838");
|
|
|
|
Color failure_fill = Color(failure_border, 0.1);
|
|
|
|
|
|
|
|
sbf_running.set_border_color(running_border);
|
|
|
|
sbf_running.set_bg_color(running_fill);
|
|
|
|
sbf_running.set_border_width(SIDE_LEFT, 4.0);
|
|
|
|
sbf_running.set_border_width(SIDE_RIGHT, 4.0);
|
|
|
|
|
|
|
|
sbf_success.set_border_color(success_border);
|
|
|
|
sbf_success.set_bg_color(success_fill);
|
|
|
|
sbf_success.set_border_width(SIDE_LEFT, 4.0);
|
|
|
|
sbf_success.set_border_width(SIDE_RIGHT, 4.0);
|
|
|
|
|
|
|
|
sbf_failure.set_border_color(failure_border);
|
|
|
|
sbf_failure.set_bg_color(failure_fill);
|
|
|
|
sbf_failure.set_border_width(SIDE_LEFT, 4.0);
|
|
|
|
sbf_failure.set_border_width(SIDE_RIGHT, 4.0);
|
2023-04-14 11:18:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-14 08:16:26 +00:00
|
|
|
void BehaviorTreeView::_bind_methods() {
|
|
|
|
ClassDB::bind_method(D_METHOD("_draw_running_status"), &BehaviorTreeView::_draw_running_status);
|
|
|
|
ClassDB::bind_method(D_METHOD("_draw_success_status"), &BehaviorTreeView::_draw_success_status);
|
|
|
|
ClassDB::bind_method(D_METHOD("_draw_failure_status"), &BehaviorTreeView::_draw_failure_status);
|
|
|
|
ClassDB::bind_method(D_METHOD("_item_collapsed"), &BehaviorTreeView::_item_collapsed);
|
2023-04-13 07:29:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BehaviorTreeView::BehaviorTreeView() {
|
|
|
|
tree = memnew(Tree);
|
|
|
|
add_child(tree);
|
2023-04-14 11:18:23 +00:00
|
|
|
tree->set_columns(3);
|
2023-04-13 07:29:45 +00:00
|
|
|
tree->set_column_expand(0, true);
|
2023-04-14 08:16:26 +00:00
|
|
|
tree->set_column_expand(1, false);
|
2023-04-14 11:18:23 +00:00
|
|
|
tree->set_column_expand(2, false);
|
|
|
|
tree->set_column_custom_minimum_width(1, 18.0 * EDSCALE);
|
|
|
|
tree->set_column_custom_minimum_width(2, 40.0 * EDSCALE);
|
2023-04-13 07:29:45 +00:00
|
|
|
tree->set_anchor(SIDE_RIGHT, ANCHOR_END);
|
|
|
|
tree->set_anchor(SIDE_BOTTOM, ANCHOR_END);
|
2023-04-14 08:16:26 +00:00
|
|
|
|
|
|
|
tree->connect(SNAME("item_collapsed"), callable_mp(this, &BehaviorTreeView::_item_collapsed));
|
2023-04-13 07:29:45 +00:00
|
|
|
}
|