Add editor support for BTProbabilitySelector
This commit is contained in:
parent
32cbce6b80
commit
52a70fdee5
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include "modules/limboai/bt/tasks/bt_composite.h"
|
#include "modules/limboai/bt/tasks/bt_composite.h"
|
||||||
|
|
||||||
|
#include "core/core_string_names.h"
|
||||||
#include "core/typedefs.h"
|
#include "core/typedefs.h"
|
||||||
|
|
||||||
class BTProbabilitySelector : public BTComposite {
|
class BTProbabilitySelector : public BTComposite {
|
||||||
|
@ -29,7 +30,10 @@ private:
|
||||||
|
|
||||||
_FORCE_INLINE_ double _get_weight(int p_index) const { return get_child(p_index)->get_meta(SNAME("_weight_"), 1.0); }
|
_FORCE_INLINE_ double _get_weight(int p_index) const { return get_child(p_index)->get_meta(SNAME("_weight_"), 1.0); }
|
||||||
_FORCE_INLINE_ double _get_weight(Ref<BTTask> p_task) const { return p_task->get_meta(SNAME("_weight_"), 1.0); }
|
_FORCE_INLINE_ double _get_weight(Ref<BTTask> p_task) const { return p_task->get_meta(SNAME("_weight_"), 1.0); }
|
||||||
_FORCE_INLINE_ void _set_weight(int p_index, double p_weight) { get_child(p_index)->set_meta(SNAME("_weight_"), Variant(p_weight)); }
|
_FORCE_INLINE_ void _set_weight(int p_index, double p_weight) {
|
||||||
|
get_child(p_index)->set_meta(SNAME("_weight_"), Variant(p_weight));
|
||||||
|
get_child(p_index)->emit_signal(CoreStringNames::get_singleton()->changed);
|
||||||
|
}
|
||||||
_FORCE_INLINE_ double _get_total_weight() const {
|
_FORCE_INLINE_ double _get_total_weight() const {
|
||||||
double total = 0.0;
|
double total = 0.0;
|
||||||
for (int i = 0; i < get_child_count(); i++) {
|
for (int i = 0; i < get_child_count(); i++) {
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
|
|
||||||
#include "action_banner.h"
|
#include "action_banner.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_selector.h"
|
#include "modules/limboai/bt/tasks/composites/bt_selector.h"
|
||||||
#include "modules/limboai/editor/debugger/limbo_debugger_plugin.h"
|
#include "modules/limboai/editor/debugger/limbo_debugger_plugin.h"
|
||||||
#include "modules/limboai/util/limbo_utility.h"
|
#include "modules/limboai/util/limbo_utility.h"
|
||||||
|
|
||||||
#include "core/config/project_settings.h"
|
#include "core/config/project_settings.h"
|
||||||
|
#include "core/error/error_macros.h"
|
||||||
#include "editor/debugger/editor_debugger_node.h"
|
#include "editor/debugger/editor_debugger_node.h"
|
||||||
#include "editor/debugger/script_editor_debugger.h"
|
#include "editor/debugger/script_editor_debugger.h"
|
||||||
#include "editor/editor_file_system.h"
|
#include "editor/editor_file_system.h"
|
||||||
|
@ -267,10 +269,13 @@ void LimboAIEditor::_on_tree_rmb(const Vector2 &p_menu_pos) {
|
||||||
Ref<BTTask> task = task_tree->get_selected();
|
Ref<BTTask> task = task_tree->get_selected();
|
||||||
ERR_FAIL_COND_MSG(task.is_null(), "LimboAIEditor: get_selected() returned null");
|
ERR_FAIL_COND_MSG(task.is_null(), "LimboAIEditor: get_selected() returned null");
|
||||||
|
|
||||||
|
if (task_tree->selected_has_probability()) {
|
||||||
|
menu->add_item(TTR("Edit Probability"), ACTION_EDIT_PROBABILITY);
|
||||||
|
}
|
||||||
menu->add_icon_shortcut(theme_cache.rename_task_icon, ED_GET_SHORTCUT("limbo_ai/rename_task"), ACTION_RENAME);
|
menu->add_icon_shortcut(theme_cache.rename_task_icon, ED_GET_SHORTCUT("limbo_ai/rename_task"), ACTION_RENAME);
|
||||||
menu->add_icon_item(theme_cache.edit_script_icon, TTR("Edit Script"), ACTION_EDIT_SCRIPT);
|
menu->add_icon_item(theme_cache.edit_script_icon, TTR("Edit Script"), ACTION_EDIT_SCRIPT);
|
||||||
menu->add_icon_item(theme_cache.open_doc_icon, TTR("Open Documentation"), ACTION_OPEN_DOC);
|
menu->add_icon_item(theme_cache.open_doc_icon, TTR("Open Documentation"), ACTION_OPEN_DOC);
|
||||||
menu->set_item_disabled(ACTION_EDIT_SCRIPT, task->get_script().is_null());
|
menu->set_item_disabled(menu->get_item_index(ACTION_EDIT_SCRIPT), task->get_script().is_null());
|
||||||
|
|
||||||
menu->add_separator();
|
menu->add_separator();
|
||||||
menu->add_icon_shortcut(theme_cache.move_task_up_icon, ED_GET_SHORTCUT("limbo_ai/move_task_up"), ACTION_MOVE_UP);
|
menu->add_icon_shortcut(theme_cache.move_task_up_icon, ED_GET_SHORTCUT("limbo_ai/move_task_up"), ACTION_MOVE_UP);
|
||||||
|
@ -308,6 +313,15 @@ void LimboAIEditor::_action_selected(int p_id) {
|
||||||
rename_edit->select_all();
|
rename_edit->select_all();
|
||||||
rename_edit->grab_focus();
|
rename_edit->grab_focus();
|
||||||
} break;
|
} break;
|
||||||
|
case ACTION_EDIT_PROBABILITY: {
|
||||||
|
Rect2 rect = task_tree->get_selected_probability_rect();
|
||||||
|
ERR_FAIL_COND(rect == Rect2());
|
||||||
|
rect.position.y += rect.size.y;
|
||||||
|
rect.position += task_tree->get_rect().position;
|
||||||
|
rect = task_tree->get_screen_transform().xform(rect);
|
||||||
|
probability_edit->set_value_no_signal(task_tree->get_selected_probability_weight());
|
||||||
|
probability_popup->popup(rect);
|
||||||
|
} break;
|
||||||
case ACTION_EDIT_SCRIPT: {
|
case ACTION_EDIT_SCRIPT: {
|
||||||
ERR_FAIL_COND(task_tree->get_selected().is_null());
|
ERR_FAIL_COND(task_tree->get_selected().is_null());
|
||||||
EditorNode::get_singleton()->edit_resource(task_tree->get_selected()->get_script());
|
EditorNode::get_singleton()->edit_resource(task_tree->get_selected()->get_script());
|
||||||
|
@ -419,6 +433,14 @@ void LimboAIEditor::_action_selected(int p_id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LimboAIEditor::_on_probability_edited(double p_value) {
|
||||||
|
Ref<BTTask> selected = task_tree->get_selected();
|
||||||
|
ERR_FAIL_COND(selected == nullptr);
|
||||||
|
Ref<BTProbabilitySelector> probability_selector = selected->get_parent();
|
||||||
|
ERR_FAIL_COND(probability_selector.is_null());
|
||||||
|
probability_selector->set_weight(probability_selector->get_child_index(selected), p_value);
|
||||||
|
}
|
||||||
|
|
||||||
void LimboAIEditor::_misc_option_selected(int p_id) {
|
void LimboAIEditor::_misc_option_selected(int p_id) {
|
||||||
switch (p_id) {
|
switch (p_id) {
|
||||||
case MISC_OPEN_DEBUGGER: {
|
case MISC_OPEN_DEBUGGER: {
|
||||||
|
@ -491,10 +513,6 @@ void LimboAIEditor::_on_tree_task_selected(const Ref<BTTask> &p_task) {
|
||||||
EditorNode::get_singleton()->edit_resource(p_task);
|
EditorNode::get_singleton()->edit_resource(p_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LimboAIEditor::_on_tree_task_double_clicked() {
|
|
||||||
_action_selected(ACTION_RENAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LimboAIEditor::_on_visibility_changed() {
|
void LimboAIEditor::_on_visibility_changed() {
|
||||||
if (task_tree->is_visible_in_tree()) {
|
if (task_tree->is_visible_in_tree()) {
|
||||||
Ref<BTTask> sel = task_tree->get_selected();
|
Ref<BTTask> sel = task_tree->get_selected();
|
||||||
|
@ -925,7 +943,8 @@ LimboAIEditor::LimboAIEditor() {
|
||||||
task_tree->connect("rmb_pressed", callable_mp(this, &LimboAIEditor::_on_tree_rmb));
|
task_tree->connect("rmb_pressed", callable_mp(this, &LimboAIEditor::_on_tree_rmb));
|
||||||
task_tree->connect("task_selected", callable_mp(this, &LimboAIEditor::_on_tree_task_selected));
|
task_tree->connect("task_selected", callable_mp(this, &LimboAIEditor::_on_tree_task_selected));
|
||||||
task_tree->connect("task_dragged", callable_mp(this, &LimboAIEditor::_on_task_dragged));
|
task_tree->connect("task_dragged", callable_mp(this, &LimboAIEditor::_on_task_dragged));
|
||||||
task_tree->connect("task_double_clicked", callable_mp(this, &LimboAIEditor::_on_tree_task_double_clicked));
|
task_tree->connect("task_activated", callable_mp(this, &LimboAIEditor::_action_selected).bind(ACTION_RENAME));
|
||||||
|
task_tree->connect("probability_clicked", callable_mp(this, &LimboAIEditor::_action_selected).bind(ACTION_EDIT_PROBABILITY));
|
||||||
task_tree->connect("visibility_changed", callable_mp(this, &LimboAIEditor::_on_visibility_changed));
|
task_tree->connect("visibility_changed", callable_mp(this, &LimboAIEditor::_on_visibility_changed));
|
||||||
task_tree->connect("visibility_changed", callable_mp(this, &LimboAIEditor::_update_banners));
|
task_tree->connect("visibility_changed", callable_mp(this, &LimboAIEditor::_update_banners));
|
||||||
hsc->add_child(task_tree);
|
hsc->add_child(task_tree);
|
||||||
|
@ -957,6 +976,56 @@ LimboAIEditor::LimboAIEditor() {
|
||||||
add_child(menu);
|
add_child(menu);
|
||||||
menu->connect("id_pressed", callable_mp(this, &LimboAIEditor::_action_selected));
|
menu->connect("id_pressed", callable_mp(this, &LimboAIEditor::_action_selected));
|
||||||
|
|
||||||
|
probability_popup = memnew(PopupPanel);
|
||||||
|
{
|
||||||
|
VBoxContainer *vbc = memnew(VBoxContainer);
|
||||||
|
probability_popup->add_child(vbc);
|
||||||
|
|
||||||
|
// PanelContainer *mode_panel = memnew(PanelContainer);
|
||||||
|
// vbc->add_child(mode_panel);
|
||||||
|
|
||||||
|
// HBoxContainer *mode_hbox = memnew(HBoxContainer);
|
||||||
|
// mode_panel->add_child(mode_hbox);
|
||||||
|
|
||||||
|
// Ref<ButtonGroup> button_group;
|
||||||
|
// button_group.instantiate();
|
||||||
|
|
||||||
|
// Button *percent_button = memnew(Button);
|
||||||
|
// mode_hbox->add_child(percent_button);
|
||||||
|
// percent_button->set_flat(true);
|
||||||
|
// percent_button->set_toggle_mode(true);
|
||||||
|
// percent_button->set_button_group(button_group);
|
||||||
|
// percent_button->set_focus_mode(Control::FOCUS_NONE);
|
||||||
|
// percent_button->set_text(TTR("Percent"));
|
||||||
|
// percent_button->set_tooltip_text(TTR("Edit percent"));
|
||||||
|
// percent_button->set_pressed(true);
|
||||||
|
// // percent_button->connect(SNAME("pressed"), callable_mp())
|
||||||
|
|
||||||
|
// Button *weight_button = memnew(Button);
|
||||||
|
// mode_hbox->add_child(weight_button);
|
||||||
|
// weight_button->set_flat(true);
|
||||||
|
// weight_button->set_toggle_mode(true);
|
||||||
|
// weight_button->set_button_group(button_group);
|
||||||
|
// weight_button->set_focus_mode(Control::FOCUS_NONE);
|
||||||
|
// weight_button->set_text(TTR("Weight"));
|
||||||
|
// weight_button->set_tooltip_text(TTR("Edit weight"));
|
||||||
|
|
||||||
|
Label *probability_header = memnew(Label);
|
||||||
|
vbc->add_child(probability_header);
|
||||||
|
probability_header->set_text(TTR("Weight"));
|
||||||
|
probability_header->set_theme_type_variation("HeaderSmall");
|
||||||
|
|
||||||
|
probability_edit = memnew(EditorSpinSlider);
|
||||||
|
vbc->add_child(probability_edit);
|
||||||
|
probability_edit->set_min(0.0);
|
||||||
|
probability_edit->set_max(10.0);
|
||||||
|
probability_edit->set_step(0.01);
|
||||||
|
probability_edit->set_allow_greater(true);
|
||||||
|
probability_edit->set_custom_minimum_size(Size2(200.0 * EDSCALE, 0.0));
|
||||||
|
probability_edit->connect(SNAME("value_changed"), callable_mp(this, &LimboAIEditor::_on_probability_edited));
|
||||||
|
}
|
||||||
|
add_child(probability_popup);
|
||||||
|
|
||||||
rename_dialog = memnew(ConfirmationDialog);
|
rename_dialog = memnew(ConfirmationDialog);
|
||||||
{
|
{
|
||||||
VBoxContainer *vbc = memnew(VBoxContainer);
|
VBoxContainer *vbc = memnew(VBoxContainer);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "core/templates/hash_set.h"
|
#include "core/templates/hash_set.h"
|
||||||
#include "editor/editor_node.h"
|
#include "editor/editor_node.h"
|
||||||
#include "editor/editor_plugin.h"
|
#include "editor/editor_plugin.h"
|
||||||
|
#include "editor/gui/editor_spin_slider.h"
|
||||||
#include "scene/gui/box_container.h"
|
#include "scene/gui/box_container.h"
|
||||||
#include "scene/gui/control.h"
|
#include "scene/gui/control.h"
|
||||||
#include "scene/gui/file_dialog.h"
|
#include "scene/gui/file_dialog.h"
|
||||||
|
@ -41,6 +42,7 @@ class LimboAIEditor : public Control {
|
||||||
private:
|
private:
|
||||||
enum Action {
|
enum Action {
|
||||||
ACTION_RENAME,
|
ACTION_RENAME,
|
||||||
|
ACTION_EDIT_PROBABILITY,
|
||||||
ACTION_EDIT_SCRIPT,
|
ACTION_EDIT_SCRIPT,
|
||||||
ACTION_OPEN_DOC,
|
ACTION_OPEN_DOC,
|
||||||
ACTION_MOVE_UP,
|
ACTION_MOVE_UP,
|
||||||
|
@ -79,12 +81,16 @@ private:
|
||||||
VBoxContainer *banners;
|
VBoxContainer *banners;
|
||||||
Panel *usage_hint;
|
Panel *usage_hint;
|
||||||
PopupMenu *menu;
|
PopupMenu *menu;
|
||||||
|
HBoxContainer *fav_tasks_hbox;
|
||||||
|
TaskPalette *task_palette;
|
||||||
|
|
||||||
|
PopupPanel *probability_popup;
|
||||||
|
EditorSpinSlider *probability_edit;
|
||||||
|
|
||||||
FileDialog *save_dialog;
|
FileDialog *save_dialog;
|
||||||
FileDialog *load_dialog;
|
FileDialog *load_dialog;
|
||||||
Button *history_back;
|
Button *history_back;
|
||||||
Button *history_forward;
|
Button *history_forward;
|
||||||
TaskPalette *task_palette;
|
|
||||||
HBoxContainer *fav_tasks_hbox;
|
|
||||||
|
|
||||||
Button *new_btn;
|
Button *new_btn;
|
||||||
Button *load_btn;
|
Button *load_btn;
|
||||||
|
@ -124,8 +130,8 @@ private:
|
||||||
void _on_tree_rmb(const Vector2 &p_menu_pos);
|
void _on_tree_rmb(const Vector2 &p_menu_pos);
|
||||||
void _action_selected(int p_id);
|
void _action_selected(int p_id);
|
||||||
void _misc_option_selected(int p_id);
|
void _misc_option_selected(int p_id);
|
||||||
|
void _on_probability_edited(double p_value);
|
||||||
void _on_tree_task_selected(const Ref<BTTask> &p_task);
|
void _on_tree_task_selected(const Ref<BTTask> &p_task);
|
||||||
void _on_tree_task_double_clicked();
|
|
||||||
void _on_visibility_changed();
|
void _on_visibility_changed();
|
||||||
void _on_header_pressed();
|
void _on_header_pressed();
|
||||||
void _on_save_pressed();
|
void _on_save_pressed();
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "task_tree.h"
|
#include "task_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/util/limbo_utility.h"
|
#include "modules/limboai/util/limbo_utility.h"
|
||||||
|
|
||||||
#include "editor/editor_scale.h"
|
#include "editor/editor_scale.h"
|
||||||
|
@ -34,6 +35,15 @@ void TaskTree::_update_item(TreeItem *p_item) {
|
||||||
if (p_item == nullptr) {
|
if (p_item == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p_item->get_parent()) {
|
||||||
|
Ref<BTProbabilitySelector> sel = p_item->get_parent()->get_metadata(0);
|
||||||
|
if (sel.is_valid()) {
|
||||||
|
p_item->set_custom_draw(0, this, SNAME("_draw_probability"));
|
||||||
|
p_item->set_cell_mode(0, TreeItem::CELL_MODE_CUSTOM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ref<BTTask> task = p_item->get_metadata(0);
|
Ref<BTTask> task = p_item->get_metadata(0);
|
||||||
ERR_FAIL_COND_MSG(!task.is_valid(), "Invalid task reference in metadata.");
|
ERR_FAIL_COND_MSG(!task.is_valid(), "Invalid task reference in metadata.");
|
||||||
p_item->set_text(0, task->get_task_name());
|
p_item->set_text(0, task->get_task_name());
|
||||||
|
@ -72,8 +82,6 @@ void TaskTree::_update_item(TreeItem *p_item) {
|
||||||
if (!warning_text.is_empty()) {
|
if (!warning_text.is_empty()) {
|
||||||
p_item->add_button(0, theme_cache.task_warning_icon, 0, false, warning_text);
|
p_item->add_button(0, theme_cache.task_warning_icon, 0, false, warning_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Update probabilities.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskTree::_update_tree() {
|
void TaskTree::_update_tree() {
|
||||||
|
@ -116,8 +124,13 @@ TreeItem *TaskTree::_find_item(const Ref<BTTask> &p_task) const {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskTree::_on_item_mouse_selected(const Vector2 &p_pos, int p_button_index) {
|
void TaskTree::_on_item_mouse_selected(const Vector2 &p_pos, MouseButton p_button_index) {
|
||||||
if (p_button_index == 2) {
|
if (p_button_index == MouseButton::LEFT) {
|
||||||
|
Rect2 rect = get_selected_probability_rect();
|
||||||
|
if (rect != Rect2() && rect.has_point(p_pos)) {
|
||||||
|
emit_signal(SNAME("probability_clicked"));
|
||||||
|
}
|
||||||
|
} else if (p_button_index == MouseButton::RIGHT) {
|
||||||
emit_signal(SNAME("rmb_pressed"), get_screen_position() + p_pos);
|
emit_signal(SNAME("rmb_pressed"), get_screen_position() + p_pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,17 +139,17 @@ void TaskTree::_on_item_selected() {
|
||||||
Callable on_task_changed = callable_mp(this, &TaskTree::_on_task_changed);
|
Callable on_task_changed = callable_mp(this, &TaskTree::_on_task_changed);
|
||||||
if (last_selected.is_valid()) {
|
if (last_selected.is_valid()) {
|
||||||
update_task(last_selected);
|
update_task(last_selected);
|
||||||
if (last_selected->is_connected("changed", on_task_changed)) {
|
if (last_selected->is_connected(SNAME("changed"), on_task_changed)) {
|
||||||
last_selected->disconnect("changed", on_task_changed);
|
last_selected->disconnect(SNAME("changed"), on_task_changed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
last_selected = get_selected();
|
last_selected = get_selected();
|
||||||
last_selected->connect("changed", on_task_changed);
|
last_selected->connect(SNAME("changed"), on_task_changed);
|
||||||
emit_signal(SNAME("task_selected"), last_selected);
|
emit_signal(SNAME("task_selected"), last_selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskTree::_on_item_double_clicked() {
|
void TaskTree::_on_item_activated() {
|
||||||
emit_signal(SNAME("task_double_clicked"));
|
emit_signal(SNAME("task_activated"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskTree::_on_task_changed() {
|
void TaskTree::_on_task_changed() {
|
||||||
|
@ -153,6 +166,7 @@ void TaskTree::load_bt(const Ref<BehaviorTree> &p_behavior_tree) {
|
||||||
|
|
||||||
bt = p_behavior_tree;
|
bt = p_behavior_tree;
|
||||||
tree->clear();
|
tree->clear();
|
||||||
|
probability_rect_cache.clear();
|
||||||
if (bt->get_root_task().is_valid()) {
|
if (bt->get_root_task().is_valid()) {
|
||||||
_create_tree(bt->get_root_task(), nullptr);
|
_create_tree(bt->get_root_task(), nullptr);
|
||||||
}
|
}
|
||||||
|
@ -190,6 +204,37 @@ void TaskTree::deselect() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rect2 TaskTree::get_selected_probability_rect() const {
|
||||||
|
if (tree->get_selected() == nullptr) {
|
||||||
|
return Rect2();
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectID key = tree->get_selected()->get_instance_id();
|
||||||
|
if (unlikely(!probability_rect_cache.has(key))) {
|
||||||
|
return Rect2();
|
||||||
|
} else {
|
||||||
|
return probability_rect_cache[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double TaskTree::get_selected_probability_weight() const {
|
||||||
|
Ref<BTTask> selected = get_selected();
|
||||||
|
ERR_FAIL_COND_V(selected.is_null(), 0.0);
|
||||||
|
Ref<BTProbabilitySelector> probability_selector = selected->get_parent();
|
||||||
|
ERR_FAIL_COND_V(probability_selector.is_null(), 0.0);
|
||||||
|
return probability_selector->get_weight(probability_selector->get_child_index(selected));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TaskTree::selected_has_probability() const {
|
||||||
|
bool result = false;
|
||||||
|
Ref<BTTask> selected = get_selected();
|
||||||
|
if (selected.is_valid()) {
|
||||||
|
Ref<BTProbabilitySelector> probability_selector = selected->get_parent();
|
||||||
|
result = probability_selector.is_valid();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Variant TaskTree::_get_drag_data_fw(const Point2 &p_point) {
|
Variant TaskTree::_get_drag_data_fw(const Point2 &p_point) {
|
||||||
if (editable && tree->get_item_at_position(p_point)) {
|
if (editable && tree->get_item_at_position(p_point)) {
|
||||||
Dictionary drag_data;
|
Dictionary drag_data;
|
||||||
|
@ -241,16 +286,57 @@ void TaskTree::_drop_data_fw(const Point2 &p_point, const Variant &p_data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TaskTree::_draw_probability(Object *item_obj, Rect2 rect) {
|
||||||
|
TreeItem *item = Object::cast_to<TreeItem>(item_obj);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ref<BTProbabilitySelector> sel = item->get_parent()->get_metadata(0);
|
||||||
|
if (sel.is_null()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String text = rtos(Math::snapped(sel->get_probability(item->get_index()) * 100, 0.01)) + "%";
|
||||||
|
Size2 text_size = theme_cache.probability_font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.probability_font_size);
|
||||||
|
|
||||||
|
Rect2 prob_rect = rect;
|
||||||
|
prob_rect.position.x += theme_cache.name_font->get_string_size(item->get_text(0), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.name_font_size).x;
|
||||||
|
prob_rect.position.x += EDSCALE * 40.0;
|
||||||
|
prob_rect.size.x = text_size.x + EDSCALE * 12;
|
||||||
|
prob_rect.position.y += 4 * EDSCALE;
|
||||||
|
prob_rect.size.y -= 8 * EDSCALE;
|
||||||
|
probability_rect_cache[item->get_instance_id()] = prob_rect; // Cache rect for later click detection.
|
||||||
|
|
||||||
|
theme_cache.probability_bg->draw(tree->get_canvas_item(), prob_rect);
|
||||||
|
|
||||||
|
Point2 text_pos = prob_rect.position;
|
||||||
|
text_pos.y += text_size.y + (prob_rect.size.y - text_size.y) * 0.5;
|
||||||
|
text_pos.y -= theme_cache.probability_font->get_descent(theme_cache.probability_font_size);
|
||||||
|
text_pos.y = Math::floor(text_pos.y);
|
||||||
|
|
||||||
|
tree->draw_string(theme_cache.probability_font, text_pos, text, HORIZONTAL_ALIGNMENT_CENTER,
|
||||||
|
prob_rect.size.x, theme_cache.probability_font_size, theme_cache.probability_font_color);
|
||||||
|
}
|
||||||
|
|
||||||
void TaskTree::_update_theme_item_cache() {
|
void TaskTree::_update_theme_item_cache() {
|
||||||
Control::_update_theme_item_cache();
|
Control::_update_theme_item_cache();
|
||||||
|
|
||||||
theme_cache.comment_font = get_theme_font(SNAME("doc_italic"), SNAME("EditorFonts"));
|
theme_cache.name_font = get_theme_font(SNAME("font"));
|
||||||
theme_cache.custom_name_font = get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
|
theme_cache.custom_name_font = get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
|
||||||
// theme_cache.normal_name_font = Ref<Font>(nullptr);
|
theme_cache.comment_font = get_theme_font(SNAME("doc_italic"), SNAME("EditorFonts"));
|
||||||
|
theme_cache.probability_font = get_theme_font(SNAME("font"));
|
||||||
|
|
||||||
|
theme_cache.name_font_size = get_theme_font_size("font_size");
|
||||||
|
theme_cache.probability_font_size = Math::floor(get_theme_font_size("font_size") * 0.9);
|
||||||
|
|
||||||
theme_cache.task_warning_icon = get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"));
|
theme_cache.task_warning_icon = get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"));
|
||||||
|
|
||||||
theme_cache.comment_color = get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"));
|
theme_cache.comment_color = get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"));
|
||||||
|
theme_cache.probability_font_color = get_theme_color(SNAME("font_color"), SNAME("Editor"));
|
||||||
|
|
||||||
|
theme_cache.probability_bg.instantiate();
|
||||||
|
theme_cache.probability_bg->set_bg_color(get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.25));
|
||||||
|
theme_cache.probability_bg->set_corner_radius_all(12.0 * EDSCALE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskTree::_notification(int p_what) {
|
void TaskTree::_notification(int p_what) {
|
||||||
|
@ -272,10 +358,12 @@ void TaskTree::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &TaskTree::_get_drag_data_fw);
|
ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &TaskTree::_get_drag_data_fw);
|
||||||
ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TaskTree::_can_drop_data_fw);
|
ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TaskTree::_can_drop_data_fw);
|
||||||
ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TaskTree::_drop_data_fw);
|
ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TaskTree::_drop_data_fw);
|
||||||
|
ClassDB::bind_method(D_METHOD("_draw_probability"), &TaskTree::_draw_probability);
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("rmb_pressed"));
|
ADD_SIGNAL(MethodInfo("rmb_pressed"));
|
||||||
ADD_SIGNAL(MethodInfo("task_selected"));
|
ADD_SIGNAL(MethodInfo("task_selected"));
|
||||||
ADD_SIGNAL(MethodInfo("task_double_clicked"));
|
ADD_SIGNAL(MethodInfo("task_activated"));
|
||||||
|
ADD_SIGNAL(MethodInfo("probability_clicked"));
|
||||||
ADD_SIGNAL(MethodInfo("task_dragged",
|
ADD_SIGNAL(MethodInfo("task_dragged",
|
||||||
PropertyInfo(Variant::OBJECT, "p_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask"),
|
PropertyInfo(Variant::OBJECT, "p_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask"),
|
||||||
PropertyInfo(Variant::OBJECT, "p_to_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask"),
|
PropertyInfo(Variant::OBJECT, "p_to_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask"),
|
||||||
|
@ -296,7 +384,7 @@ TaskTree::TaskTree() {
|
||||||
tree->set_allow_rmb_select(true);
|
tree->set_allow_rmb_select(true);
|
||||||
tree->connect("item_mouse_selected", callable_mp(this, &TaskTree::_on_item_mouse_selected));
|
tree->connect("item_mouse_selected", callable_mp(this, &TaskTree::_on_item_mouse_selected));
|
||||||
tree->connect("item_selected", callable_mp(this, &TaskTree::_on_item_selected));
|
tree->connect("item_selected", callable_mp(this, &TaskTree::_on_item_selected));
|
||||||
tree->connect("item_activated", callable_mp(this, &TaskTree::_on_item_double_clicked));
|
tree->connect("item_activated", callable_mp(this, &TaskTree::_on_item_activated));
|
||||||
|
|
||||||
tree->set_drag_forwarding(callable_mp(this, &TaskTree::_get_drag_data_fw), callable_mp(this, &TaskTree::_can_drop_data_fw), callable_mp(this, &TaskTree::_drop_data_fw));
|
tree->set_drag_forwarding(callable_mp(this, &TaskTree::_get_drag_data_fw), callable_mp(this, &TaskTree::_can_drop_data_fw), callable_mp(this, &TaskTree::_drop_data_fw));
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include "scene/gui/control.h"
|
#include "scene/gui/control.h"
|
||||||
#include "scene/gui/tree.h"
|
#include "scene/gui/tree.h"
|
||||||
|
#include "scene/resources/style_box.h"
|
||||||
|
|
||||||
class TaskTree : public Control {
|
class TaskTree : public Control {
|
||||||
GDCLASS(TaskTree, Control);
|
GDCLASS(TaskTree, Control);
|
||||||
|
@ -22,15 +23,24 @@ private:
|
||||||
Ref<BehaviorTree> bt;
|
Ref<BehaviorTree> bt;
|
||||||
Ref<BTTask> last_selected;
|
Ref<BTTask> last_selected;
|
||||||
bool editable;
|
bool editable;
|
||||||
|
HashMap<ObjectID, Rect2> probability_rect_cache;
|
||||||
|
|
||||||
struct ThemeCache {
|
struct ThemeCache {
|
||||||
Ref<Font> comment_font;
|
Ref<Font> comment_font;
|
||||||
|
Ref<Font> name_font;
|
||||||
Ref<Font> custom_name_font;
|
Ref<Font> custom_name_font;
|
||||||
Ref<Font> normal_name_font;
|
Ref<Font> normal_name_font;
|
||||||
|
Ref<Font> probability_font;
|
||||||
|
|
||||||
|
double name_font_size = 18.0;
|
||||||
|
double probability_font_size = 16.0;
|
||||||
|
|
||||||
Ref<Texture2D> task_warning_icon;
|
Ref<Texture2D> task_warning_icon;
|
||||||
|
|
||||||
Color comment_color;
|
Color comment_color;
|
||||||
|
Color probability_font_color;
|
||||||
|
|
||||||
|
Ref<StyleBoxFlat> probability_bg;
|
||||||
} theme_cache;
|
} theme_cache;
|
||||||
|
|
||||||
TreeItem *_create_tree(const Ref<BTTask> &p_task, TreeItem *p_parent, int p_idx = -1);
|
TreeItem *_create_tree(const Ref<BTTask> &p_task, TreeItem *p_parent, int p_idx = -1);
|
||||||
|
@ -39,14 +49,16 @@ private:
|
||||||
TreeItem *_find_item(const Ref<BTTask> &p_task) const;
|
TreeItem *_find_item(const Ref<BTTask> &p_task) const;
|
||||||
|
|
||||||
void _on_item_selected();
|
void _on_item_selected();
|
||||||
void _on_item_double_clicked();
|
void _on_item_activated();
|
||||||
void _on_item_mouse_selected(const Vector2 &p_pos, int p_button_index);
|
void _on_item_mouse_selected(const Vector2 &p_pos, MouseButton p_button_index);
|
||||||
void _on_task_changed();
|
void _on_task_changed();
|
||||||
|
|
||||||
Variant _get_drag_data_fw(const Point2 &p_point);
|
Variant _get_drag_data_fw(const Point2 &p_point);
|
||||||
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data) const;
|
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data) const;
|
||||||
void _drop_data_fw(const Point2 &p_point, const Variant &p_data);
|
void _drop_data_fw(const Point2 &p_point, const Variant &p_data);
|
||||||
|
|
||||||
|
void _draw_probability(Object *item_obj, Rect2 rect);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void _update_theme_item_cache() override;
|
virtual void _update_theme_item_cache() override;
|
||||||
|
|
||||||
|
@ -62,6 +74,10 @@ public:
|
||||||
Ref<BTTask> get_selected() const;
|
Ref<BTTask> get_selected() const;
|
||||||
void deselect();
|
void deselect();
|
||||||
|
|
||||||
|
Rect2 get_selected_probability_rect() const;
|
||||||
|
double get_selected_probability_weight() const;
|
||||||
|
bool selected_has_probability() const;
|
||||||
|
|
||||||
virtual bool editor_can_reload_from_file() { return false; }
|
virtual bool editor_can_reload_from_file() { return false; }
|
||||||
|
|
||||||
TaskTree();
|
TaskTree();
|
||||||
|
|
Loading…
Reference in New Issue