Compare commits

...

5 Commits

Author SHA1 Message Date
Serhii Snitsaruk 338c9c8ccb
Bump version to 1.2.0 2024-08-22 00:50:16 +02:00
Serhii Snitsaruk a92fb9da8e
Merge pull request #201 from limbonaut/fix-undo-redo
Fix editor undo polluting scene history if `BehaviorTree` is not saved to a file
2024-08-21 20:57:51 +02:00
Serhii Snitsaruk 9da16a1e0f
Clean up unused string names 2024-08-21 20:04:35 +02:00
Serhii Snitsaruk 472f360cf9
Switch to contextual behavior tree upon undo action 2024-08-21 17:30:30 +02:00
Serhii Snitsaruk c94ec7613d
Add hack to associate undo-redo actions with global history 2024-08-21 16:40:02 +02:00
10 changed files with 86 additions and 132 deletions

View File

@ -43,7 +43,7 @@ BT::Status BTInstance::update(double p_delta) {
#endif #endif
last_status = root_task->execute(p_delta); last_status = root_task->execute(p_delta);
emit_signal(LimboStringNames::get_singleton()->updated, last_status); emit_signal(LW_NAME(updated), last_status);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
double end = Time::get_singleton()->get_ticks_usec(); double end = Time::get_singleton()->get_ticks_usec();

View File

@ -125,6 +125,7 @@ String BTTask::get_task_name() {
Ref<Script> task_script = get_script(); Ref<Script> task_script = get_script();
if (task_script.is_valid()) { if (task_script.is_valid()) {
// ! CURSED: Currently, has_method() doesn't return true for ClassDB-registered native virtual methods. This may break in the future.
bool has_generate_method = has_method(LW_NAME(_generate_name)); bool has_generate_method = has_method(LW_NAME(_generate_name));
ERR_FAIL_COND_V_MSG(has_generate_method && !task_script->is_tool(), _generate_name(), vformat("BTTask: @tool annotation is required if _generate_name is defined: %s", task_script->get_path())); ERR_FAIL_COND_V_MSG(has_generate_method && !task_script->is_tool(), _generate_name(), vformat("BTTask: @tool annotation is required if _generate_name is defined: %s", task_script->get_path()));
if (task_script->is_tool() && has_generate_method) { if (task_script->is_tool() && has_generate_method) {

View File

@ -37,7 +37,6 @@
#include "editor/editor_interface.h" #include "editor/editor_interface.h"
#include "editor/editor_paths.h" #include "editor/editor_paths.h"
#include "editor/editor_settings.h" #include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/filesystem_dock.h" #include "editor/filesystem_dock.h"
#include "editor/gui/editor_bottom_panel.h" #include "editor/gui/editor_bottom_panel.h"
#include "editor/inspector_dock.h" #include "editor/inspector_dock.h"
@ -58,7 +57,6 @@
#include <godot_cpp/classes/editor_interface.hpp> #include <godot_cpp/classes/editor_interface.hpp>
#include <godot_cpp/classes/editor_paths.hpp> #include <godot_cpp/classes/editor_paths.hpp>
#include <godot_cpp/classes/editor_settings.hpp> #include <godot_cpp/classes/editor_settings.hpp>
#include <godot_cpp/classes/editor_undo_redo_manager.hpp>
#include <godot_cpp/classes/file_access.hpp> #include <godot_cpp/classes/file_access.hpp>
#include <godot_cpp/classes/file_system_dock.hpp> #include <godot_cpp/classes/file_system_dock.hpp>
#include <godot_cpp/classes/input.hpp> #include <godot_cpp/classes/input.hpp>
@ -82,14 +80,33 @@ _FORCE_INLINE_ String _get_script_template_path() {
return templates_search_path.path_join("BTTask").path_join("custom_task.gd"); return templates_search_path.path_join("BTTask").path_join("custom_task.gd");
} }
EditorUndoRedoManager *LimboAIEditor::_new_undo_redo_action(const String &p_name, UndoRedo::MergeMode p_mode) {
#ifdef LIMBOAI_MODULE
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
#elif LIMBOAI_GDEXTENSION
EditorUndoRedoManager *undo_redo = plugin->get_undo_redo();
#endif
// ! HACK: Force global history to be used for resources without a set path.
undo_redo->create_action(p_name, p_mode, dummy_history_context);
undo_redo->force_fixed_history();
return undo_redo;
}
void LimboAIEditor::_commit_action_with_update(EditorUndoRedoManager *p_undo_redo) {
ERR_FAIL_NULL(p_undo_redo);
p_undo_redo->add_do_method(this, LW_NAME(_update_task_tree), task_tree->get_bt());
p_undo_redo->add_undo_method(this, LW_NAME(_update_task_tree), task_tree->get_bt());
p_undo_redo->commit_action();
_mark_as_dirty(true);
}
void LimboAIEditor::_add_task(const Ref<BTTask> &p_task, bool p_as_sibling) { void LimboAIEditor::_add_task(const Ref<BTTask> &p_task, bool p_as_sibling) {
if (task_tree->get_bt().is_null()) { if (task_tree->get_bt().is_null()) {
return; return;
} }
ERR_FAIL_COND(p_task.is_null()); ERR_FAIL_COND(p_task.is_null());
EditorUndoRedoManager *undo_redo = GET_UNDO_REDO(); EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Add BT Task"));
undo_redo->create_action(TTR("Add BT Task"));
int insert_idx = -1; int insert_idx = -1;
Ref<BTTask> selected = task_tree->get_selected(); Ref<BTTask> selected = task_tree->get_selected();
@ -112,11 +129,7 @@ void LimboAIEditor::_add_task(const Ref<BTTask> &p_task, bool p_as_sibling) {
undo_redo->add_do_method(parent.ptr(), LW_NAME(add_child_at_index), p_task, insert_idx); undo_redo->add_do_method(parent.ptr(), LW_NAME(add_child_at_index), p_task, insert_idx);
undo_redo->add_undo_method(parent.ptr(), LW_NAME(remove_child), p_task); undo_redo->add_undo_method(parent.ptr(), LW_NAME(remove_child), p_task);
} }
undo_redo->add_do_method(task_tree, LW_NAME(update_tree)); _commit_action_with_update(undo_redo);
undo_redo->add_undo_method(task_tree, LW_NAME(update_tree));
undo_redo->commit_action();
_mark_as_dirty(true);
} }
void LimboAIEditor::_add_task_with_prototype(const Ref<BTTask> &p_prototype) { void LimboAIEditor::_add_task_with_prototype(const Ref<BTTask> &p_prototype) {
@ -169,8 +182,7 @@ void LimboAIEditor::_add_task_by_class_or_path(const String &p_class_or_path) {
void LimboAIEditor::_remove_task(const Ref<BTTask> &p_task) { void LimboAIEditor::_remove_task(const Ref<BTTask> &p_task) {
ERR_FAIL_COND(p_task.is_null()); ERR_FAIL_COND(p_task.is_null());
ERR_FAIL_COND(task_tree->get_bt().is_null()); ERR_FAIL_COND(task_tree->get_bt().is_null());
EditorUndoRedoManager *undo_redo = GET_UNDO_REDO(); EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Remove BT Task"));
undo_redo->create_action(TTR("Remove BT Task"));
if (p_task->get_parent() == nullptr) { if (p_task->get_parent() == nullptr) {
ERR_FAIL_COND(task_tree->get_bt()->get_root_task() != p_task); ERR_FAIL_COND(task_tree->get_bt()->get_root_task() != p_task);
undo_redo->add_do_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), Variant()); undo_redo->add_do_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), Variant());
@ -179,9 +191,7 @@ void LimboAIEditor::_remove_task(const Ref<BTTask> &p_task) {
undo_redo->add_do_method(p_task->get_parent().ptr(), LW_NAME(remove_child), p_task); undo_redo->add_do_method(p_task->get_parent().ptr(), LW_NAME(remove_child), p_task);
undo_redo->add_undo_method(p_task->get_parent().ptr(), LW_NAME(add_child_at_index), p_task, p_task->get_index()); undo_redo->add_undo_method(p_task->get_parent().ptr(), LW_NAME(add_child_at_index), p_task, p_task->get_index());
} }
undo_redo->add_do_method(task_tree, LW_NAME(update_tree)); _commit_action_with_update(undo_redo);
undo_redo->add_undo_method(task_tree, LW_NAME(update_tree));
undo_redo->commit_action();
} }
void LimboAIEditor::_new_bt() { void LimboAIEditor::_new_bt() {
@ -219,6 +229,20 @@ void LimboAIEditor::_load_bt(String p_path) {
EDIT_RESOURCE(bt); EDIT_RESOURCE(bt);
} }
void LimboAIEditor::_update_task_tree(const Ref<BehaviorTree> &p_bt, const Ref<BTTask> &p_specific_task) {
ERR_FAIL_COND(p_bt.is_null());
if (task_tree->get_bt() == p_bt) {
if (p_specific_task.is_null()) {
task_tree->update_tree();
} else {
task_tree->update_task(p_specific_task);
}
} else {
// The given BT is not being displayed - open it.
edit_bt(p_bt);
}
}
void LimboAIEditor::_disable_editing() { void LimboAIEditor::_disable_editing() {
task_tree->unload(); task_tree->unload();
task_palette->hide(); task_palette->hide();
@ -226,7 +250,7 @@ void LimboAIEditor::_disable_editing() {
usage_hint->show(); usage_hint->show();
} }
void LimboAIEditor::edit_bt(Ref<BehaviorTree> p_behavior_tree, bool p_force_refresh) { void LimboAIEditor::edit_bt(const Ref<BehaviorTree> &p_behavior_tree, bool p_force_refresh) {
ERR_FAIL_COND_MSG(p_behavior_tree.is_null(), "p_behavior_tree is null"); ERR_FAIL_COND_MSG(p_behavior_tree.is_null(), "p_behavior_tree is null");
if (!p_force_refresh && task_tree->get_bt() == p_behavior_tree) { if (!p_force_refresh && task_tree->get_bt() == p_behavior_tree) {
@ -327,8 +351,7 @@ void LimboAIEditor::_extract_subtree(const String &p_path) {
Ref<BTTask> selected = task_tree->get_selected(); Ref<BTTask> selected = task_tree->get_selected();
ERR_FAIL_COND(selected.is_null()); ERR_FAIL_COND(selected.is_null());
EditorUndoRedoManager *undo_redo = GET_UNDO_REDO(); EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Extract Subtree"));
undo_redo->create_action(TTR("Extract Subtree"));
Ref<BehaviorTree> bt = memnew(BehaviorTree); Ref<BehaviorTree> bt = memnew(BehaviorTree);
bt->set_root_task(selected->clone()); bt->set_root_task(selected->clone());
@ -348,12 +371,8 @@ void LimboAIEditor::_extract_subtree(const String &p_path) {
undo_redo->add_undo_method(selected->get_parent().ptr(), LW_NAME(remove_child), subtree); undo_redo->add_undo_method(selected->get_parent().ptr(), LW_NAME(remove_child), subtree);
undo_redo->add_undo_method(selected->get_parent().ptr(), LW_NAME(add_child_at_index), selected, idx); undo_redo->add_undo_method(selected->get_parent().ptr(), LW_NAME(add_child_at_index), selected, idx);
} }
undo_redo->add_do_method(task_tree, LW_NAME(update_tree)); _commit_action_with_update(undo_redo);
undo_redo->add_undo_method(task_tree, LW_NAME(update_tree));
undo_redo->commit_action();
EDIT_RESOURCE(task_tree->get_selected()); EDIT_RESOURCE(task_tree->get_selected());
_mark_as_dirty(true);
} }
void LimboAIEditor::_process_shortcut_input(const Ref<InputEvent> &p_event) { void LimboAIEditor::_process_shortcut_input(const Ref<InputEvent> &p_event) {
@ -459,7 +478,6 @@ void LimboAIEditor::_on_tree_rmb(const Vector2 &p_menu_pos) {
} }
void LimboAIEditor::_action_selected(int p_id) { void LimboAIEditor::_action_selected(int p_id) {
EditorUndoRedoManager *undo_redo = GET_UNDO_REDO();
switch (p_id) { switch (p_id) {
case ACTION_RENAME: { case ACTION_RENAME: {
if (!task_tree->get_selected().is_valid()) { if (!task_tree->get_selected().is_valid()) {
@ -537,15 +555,12 @@ void LimboAIEditor::_action_selected(int p_id) {
Ref<BTTask> parent = sel->get_parent(); Ref<BTTask> parent = sel->get_parent();
int idx = sel->get_index(); int idx = sel->get_index();
if (idx > 0 && idx < parent->get_child_count()) { if (idx > 0 && idx < parent->get_child_count()) {
undo_redo->create_action(TTR("Move BT Task")); EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Move BT Task"));
undo_redo->add_do_method(parent.ptr(), LW_NAME(remove_child), sel); undo_redo->add_do_method(parent.ptr(), LW_NAME(remove_child), sel);
undo_redo->add_do_method(parent.ptr(), LW_NAME(add_child_at_index), sel, idx - 1); undo_redo->add_do_method(parent.ptr(), LW_NAME(add_child_at_index), sel, idx - 1);
undo_redo->add_undo_method(parent.ptr(), LW_NAME(remove_child), sel); undo_redo->add_undo_method(parent.ptr(), LW_NAME(remove_child), sel);
undo_redo->add_undo_method(parent.ptr(), LW_NAME(add_child_at_index), sel, idx); undo_redo->add_undo_method(parent.ptr(), LW_NAME(add_child_at_index), sel, idx);
undo_redo->add_do_method(task_tree, LW_NAME(update_tree)); _commit_action_with_update(undo_redo);
undo_redo->add_undo_method(task_tree, LW_NAME(update_tree));
undo_redo->commit_action();
_mark_as_dirty(true);
} }
} }
} break; } break;
@ -555,22 +570,19 @@ void LimboAIEditor::_action_selected(int p_id) {
Ref<BTTask> parent = sel->get_parent(); Ref<BTTask> parent = sel->get_parent();
int idx = sel->get_index(); int idx = sel->get_index();
if (idx >= 0 && idx < (parent->get_child_count() - 1)) { if (idx >= 0 && idx < (parent->get_child_count() - 1)) {
undo_redo->create_action(TTR("Move BT Task")); EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Move BT Task"));
undo_redo->add_do_method(parent.ptr(), LW_NAME(remove_child), sel); undo_redo->add_do_method(parent.ptr(), LW_NAME(remove_child), sel);
undo_redo->add_do_method(parent.ptr(), LW_NAME(add_child_at_index), sel, idx + 1); undo_redo->add_do_method(parent.ptr(), LW_NAME(add_child_at_index), sel, idx + 1);
undo_redo->add_undo_method(parent.ptr(), LW_NAME(remove_child), sel); undo_redo->add_undo_method(parent.ptr(), LW_NAME(remove_child), sel);
undo_redo->add_undo_method(parent.ptr(), LW_NAME(add_child_at_index), sel, idx); undo_redo->add_undo_method(parent.ptr(), LW_NAME(add_child_at_index), sel, idx);
undo_redo->add_do_method(task_tree, LW_NAME(update_tree)); _commit_action_with_update(undo_redo);
undo_redo->add_undo_method(task_tree, LW_NAME(update_tree));
undo_redo->commit_action();
_mark_as_dirty(true);
} }
} }
} break; } break;
case ACTION_DUPLICATE: { case ACTION_DUPLICATE: {
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("Duplicate BT Task")); EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Duplicate BT Task"));
Ref<BTTask> parent = sel->get_parent(); Ref<BTTask> parent = sel->get_parent();
if (parent.is_null()) { if (parent.is_null()) {
parent = sel; parent = sel;
@ -578,10 +590,7 @@ void LimboAIEditor::_action_selected(int p_id) {
const Ref<BTTask> &sel_dup = sel->clone(); const Ref<BTTask> &sel_dup = sel->clone();
undo_redo->add_do_method(parent.ptr(), LW_NAME(add_child_at_index), sel_dup, sel->get_index() + 1); undo_redo->add_do_method(parent.ptr(), LW_NAME(add_child_at_index), sel_dup, sel->get_index() + 1);
undo_redo->add_undo_method(parent.ptr(), LW_NAME(remove_child), sel_dup); undo_redo->add_undo_method(parent.ptr(), LW_NAME(remove_child), sel_dup);
undo_redo->add_do_method(task_tree, LW_NAME(update_tree)); _commit_action_with_update(undo_redo);
undo_redo->add_undo_method(task_tree, LW_NAME(update_tree));
undo_redo->commit_action();
_mark_as_dirty(true);
} }
} break; } break;
case ACTION_MAKE_ROOT: { case ACTION_MAKE_ROOT: {
@ -589,7 +598,7 @@ void LimboAIEditor::_action_selected(int p_id) {
if (sel.is_valid() && task_tree->get_bt()->get_root_task() != sel) { if (sel.is_valid() && task_tree->get_bt()->get_root_task() != sel) {
Ref<BTTask> parent = sel->get_parent(); Ref<BTTask> parent = sel->get_parent();
ERR_FAIL_COND(parent.is_null()); ERR_FAIL_COND(parent.is_null());
undo_redo->create_action(TTR("Make Root")); EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Make Root"));
undo_redo->add_do_method(parent.ptr(), LW_NAME(remove_child), sel); undo_redo->add_do_method(parent.ptr(), LW_NAME(remove_child), sel);
Ref<BTTask> old_root = task_tree->get_bt()->get_root_task(); Ref<BTTask> old_root = task_tree->get_bt()->get_root_task();
undo_redo->add_do_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), sel); undo_redo->add_do_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), sel);
@ -597,10 +606,7 @@ void LimboAIEditor::_action_selected(int p_id) {
undo_redo->add_undo_method(sel.ptr(), LW_NAME(remove_child), old_root); undo_redo->add_undo_method(sel.ptr(), LW_NAME(remove_child), old_root);
undo_redo->add_undo_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), old_root); undo_redo->add_undo_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), old_root);
undo_redo->add_undo_method(parent.ptr(), LW_NAME(add_child_at_index), sel, sel->get_index()); undo_redo->add_undo_method(parent.ptr(), LW_NAME(add_child_at_index), sel, sel->get_index());
undo_redo->add_do_method(task_tree, LW_NAME(update_tree)); _commit_action_with_update(undo_redo);
undo_redo->add_undo_method(task_tree, LW_NAME(update_tree));
undo_redo->commit_action();
_mark_as_dirty(true);
} }
} break; } break;
case ACTION_EXTRACT_SUBTREE: { case ACTION_EXTRACT_SUBTREE: {
@ -616,8 +622,7 @@ void LimboAIEditor::_action_selected(int p_id) {
if (p_id == ACTION_CUT) { if (p_id == ACTION_CUT) {
clipboard_task = sel->clone(); clipboard_task = sel->clone();
} }
EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Remove BT Task"));
undo_redo->create_action(TTR("Remove BT Task"));
if (sel->is_root()) { if (sel->is_root()) {
undo_redo->add_do_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), Variant()); undo_redo->add_do_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), Variant());
undo_redo->add_undo_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), task_tree->get_bt()->get_root_task()); undo_redo->add_undo_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), task_tree->get_bt()->get_root_task());
@ -625,11 +630,8 @@ void LimboAIEditor::_action_selected(int p_id) {
undo_redo->add_do_method(sel->get_parent().ptr(), LW_NAME(remove_child), sel); undo_redo->add_do_method(sel->get_parent().ptr(), LW_NAME(remove_child), sel);
undo_redo->add_undo_method(sel->get_parent().ptr(), LW_NAME(add_child_at_index), sel, sel->get_index()); undo_redo->add_undo_method(sel->get_parent().ptr(), LW_NAME(add_child_at_index), sel, sel->get_index());
} }
undo_redo->add_do_method(task_tree, LW_NAME(update_tree)); _commit_action_with_update(undo_redo);
undo_redo->add_undo_method(task_tree, LW_NAME(update_tree));
undo_redo->commit_action();
EDIT_RESOURCE(task_tree->get_selected()); EDIT_RESOURCE(task_tree->get_selected());
_mark_as_dirty(true);
} }
} break; } break;
} }
@ -848,8 +850,7 @@ void LimboAIEditor::_on_task_dragged(Ref<BTTask> p_task, Ref<BTTask> p_to_task,
return; return;
} }
EditorUndoRedoManager *undo_redo = GET_UNDO_REDO(); EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Drag BT Task"));
undo_redo->create_action(TTR("Drag BT Task"));
undo_redo->add_do_method(p_task->get_parent().ptr(), LW_NAME(remove_child), p_task); undo_redo->add_do_method(p_task->get_parent().ptr(), LW_NAME(remove_child), p_task);
if (p_type == 0) { if (p_type == 0) {
@ -871,11 +872,7 @@ void LimboAIEditor::_on_task_dragged(Ref<BTTask> p_task, Ref<BTTask> p_to_task,
undo_redo->add_undo_method(p_task->get_parent().ptr(), "add_child_at_index", p_task, p_task->get_index()); undo_redo->add_undo_method(p_task->get_parent().ptr(), "add_child_at_index", p_task, p_task->get_index());
undo_redo->add_do_method(task_tree, LW_NAME(update_tree)); _commit_action_with_update(undo_redo);
undo_redo->add_undo_method(task_tree, LW_NAME(update_tree));
undo_redo->commit_action();
_mark_as_dirty(true);
} }
void LimboAIEditor::_on_resources_reload(const PackedStringArray &p_resources) { void LimboAIEditor::_on_resources_reload(const PackedStringArray &p_resources) {
@ -943,14 +940,10 @@ void LimboAIEditor::_task_type_selected(const String &p_class_or_path) {
Ref<BTTask> new_task = _create_task_by_class_or_path(p_class_or_path); Ref<BTTask> new_task = _create_task_by_class_or_path(p_class_or_path);
ERR_FAIL_COND_MSG(new_task.is_null(), "LimboAI: Unable to construct task."); ERR_FAIL_COND_MSG(new_task.is_null(), "LimboAI: Unable to construct task.");
EditorUndoRedoManager *undo_redo = GET_UNDO_REDO(); EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Change BT task type"));
undo_redo->create_action(TTR("Change BT task type"));
undo_redo->add_do_method(this, LW_NAME(_replace_task), selected_task, new_task); undo_redo->add_do_method(this, LW_NAME(_replace_task), selected_task, new_task);
undo_redo->add_undo_method(this, LW_NAME(_replace_task), new_task, selected_task); undo_redo->add_undo_method(this, LW_NAME(_replace_task), new_task, selected_task);
undo_redo->add_do_method(task_tree, LW_NAME(update_tree)); _commit_action_with_update(undo_redo);
undo_redo->add_undo_method(task_tree, LW_NAME(update_tree));
undo_redo->commit_action();
_mark_as_dirty(true);
} }
void LimboAIEditor::_copy_version_info() { void LimboAIEditor::_copy_version_info() {
@ -1198,12 +1191,11 @@ void LimboAIEditor::_rename_task_confirmed() {
ERR_FAIL_COND(!task_tree->get_selected().is_valid()); ERR_FAIL_COND(!task_tree->get_selected().is_valid());
rename_dialog->hide(); rename_dialog->hide();
EditorUndoRedoManager *undo_redo = GET_UNDO_REDO(); EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Set Custom Name"));
undo_redo->create_action(TTR("Set Custom Name"));
undo_redo->add_do_method(task_tree->get_selected().ptr(), LW_NAME(set_custom_name), rename_edit->get_text()); undo_redo->add_do_method(task_tree->get_selected().ptr(), LW_NAME(set_custom_name), rename_edit->get_text());
undo_redo->add_undo_method(task_tree->get_selected().ptr(), LW_NAME(set_custom_name), task_tree->get_selected()->get_custom_name()); undo_redo->add_undo_method(task_tree->get_selected().ptr(), LW_NAME(set_custom_name), task_tree->get_selected()->get_custom_name());
undo_redo->add_do_method(task_tree, LW_NAME(update_task), task_tree->get_selected()); undo_redo->add_do_method(this, LW_NAME(_update_task_tree), task_tree->get_bt(), task_tree->get_selected());
undo_redo->add_undo_method(task_tree, LW_NAME(update_task), task_tree->get_selected()); undo_redo->add_undo_method(this, LW_NAME(_update_task_tree), task_tree->get_bt(), task_tree->get_selected());
undo_redo->commit_action(); undo_redo->commit_action();
} }
@ -1227,7 +1219,7 @@ void LimboAIEditor::_update_favorite_tasks() {
String task_meta = favorite_tasks[i]; String task_meta = favorite_tasks[i];
if (task_meta.is_empty() || (!FILE_EXISTS(task_meta) && !ClassDB::class_exists(task_meta))) { if (task_meta.is_empty() || (!FILE_EXISTS(task_meta) && !ClassDB::class_exists(task_meta))) {
call_deferred(LW_NAME(_update_banners)); callable_mp(this, &LimboAIEditor::_update_banners).call_deferred();
continue; continue;
} }
@ -1451,6 +1443,7 @@ void LimboAIEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_new_bt"), &LimboAIEditor::_new_bt); ClassDB::bind_method(D_METHOD("_new_bt"), &LimboAIEditor::_new_bt);
ClassDB::bind_method(D_METHOD("_save_bt", "path"), &LimboAIEditor::_save_bt); ClassDB::bind_method(D_METHOD("_save_bt", "path"), &LimboAIEditor::_save_bt);
ClassDB::bind_method(D_METHOD("_load_bt", "path"), &LimboAIEditor::_load_bt); ClassDB::bind_method(D_METHOD("_load_bt", "path"), &LimboAIEditor::_load_bt);
ClassDB::bind_method(D_METHOD("_update_task_tree", "bt", "specific_task"), &LimboAIEditor::_update_task_tree, DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("edit_bt", "behavior_tree", "force_refresh"), &LimboAIEditor::edit_bt, Variant(false)); ClassDB::bind_method(D_METHOD("edit_bt", "behavior_tree", "force_refresh"), &LimboAIEditor::edit_bt, Variant(false));
ClassDB::bind_method(D_METHOD("_reload_modified"), &LimboAIEditor::_reload_modified); ClassDB::bind_method(D_METHOD("_reload_modified"), &LimboAIEditor::_reload_modified);
ClassDB::bind_method(D_METHOD("_resave_modified"), &LimboAIEditor::_resave_modified); ClassDB::bind_method(D_METHOD("_resave_modified"), &LimboAIEditor::_resave_modified);
@ -1462,6 +1455,7 @@ void LimboAIEditor::_bind_methods() {
LimboAIEditor::LimboAIEditor() { LimboAIEditor::LimboAIEditor() {
plugin = nullptr; plugin = nullptr;
idx_history = 0; idx_history = 0;
dummy_history_context = memnew(Object);
EDITOR_DEF("limbo_ai/editor/prefer_online_documentation", false); EDITOR_DEF("limbo_ai/editor/prefer_online_documentation", false);
@ -1795,6 +1789,7 @@ LimboAIEditor::LimboAIEditor() {
} }
LimboAIEditor::~LimboAIEditor() { LimboAIEditor::~LimboAIEditor() {
memdelete(dummy_history_context);
} }
//**** LimboAIEditor ^ //**** LimboAIEditor ^

View File

@ -26,6 +26,7 @@
#include "core/object/object.h" #include "core/object/object.h"
#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_undo_redo_manager.h"
#include "editor/gui/editor_spin_slider.h" #include "editor/gui/editor_spin_slider.h"
#include "editor/plugins/editor_plugin.h" #include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h" #include "scene/gui/box_container.h"
@ -42,9 +43,6 @@
#include "scene/gui/split_container.h" #include "scene/gui/split_container.h"
#include "scene/gui/tree.h" #include "scene/gui/tree.h"
#include "scene/resources/texture.h" #include "scene/resources/texture.h"
#define GET_UNDO_REDO() EditorUndoRedoManager::get_singleton()
#endif // LIMBOAI_MODULE #endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION #ifdef LIMBOAI_GDEXTENSION
@ -52,6 +50,7 @@
#include <godot_cpp/classes/control.hpp> #include <godot_cpp/classes/control.hpp>
#include <godot_cpp/classes/editor_plugin.hpp> #include <godot_cpp/classes/editor_plugin.hpp>
#include <godot_cpp/classes/editor_spin_slider.hpp> #include <godot_cpp/classes/editor_spin_slider.hpp>
#include <godot_cpp/classes/editor_undo_redo_manager.hpp>
#include <godot_cpp/classes/file_dialog.hpp> #include <godot_cpp/classes/file_dialog.hpp>
#include <godot_cpp/classes/h_box_container.hpp> #include <godot_cpp/classes/h_box_container.hpp>
#include <godot_cpp/classes/h_split_container.hpp> #include <godot_cpp/classes/h_split_container.hpp>
@ -67,8 +66,6 @@
using namespace godot; using namespace godot;
#define GET_UNDO_REDO() plugin->get_undo_redo()
#endif // LIMBOAI_GDEXTENSION #endif // LIMBOAI_GDEXTENSION
class LimboAIEditor : public Control { class LimboAIEditor : public Control {
@ -189,6 +186,11 @@ private:
AcceptDialog *info_dialog; AcceptDialog *info_dialog;
// ! HACK: Force global history to be used for resources without a set path.
Object *dummy_history_context = nullptr;
EditorUndoRedoManager *_new_undo_redo_action(const String &p_name = "", UndoRedo::MergeMode p_mode = UndoRedo::MERGE_DISABLE);
void _commit_action_with_update(EditorUndoRedoManager *p_undo_redo);
void _add_task(const Ref<BTTask> &p_task, bool p_as_sibling); void _add_task(const Ref<BTTask> &p_task, bool p_as_sibling);
void _add_task_with_prototype(const Ref<BTTask> &p_prototype); void _add_task_with_prototype(const Ref<BTTask> &p_prototype);
Ref<BTTask> _create_task_by_class_or_path(const String &p_class_or_path) const; Ref<BTTask> _create_task_by_class_or_path(const String &p_class_or_path) const;
@ -200,6 +202,7 @@ private:
void _new_bt(); void _new_bt();
void _save_bt(String p_path); void _save_bt(String p_path);
void _load_bt(String p_path); void _load_bt(String p_path);
void _update_task_tree(const Ref<BehaviorTree> &p_bt, const Ref<BTTask> &p_specific_task = nullptr);
void _disable_editing(); void _disable_editing();
void _mark_as_dirty(bool p_dirty); void _mark_as_dirty(bool p_dirty);
void _create_user_task_dir(); void _create_user_task_dir();
@ -258,7 +261,7 @@ protected:
public: public:
void set_plugin(EditorPlugin *p_plugin) { plugin = p_plugin; }; void set_plugin(EditorPlugin *p_plugin) { plugin = p_plugin; };
void edit_bt(Ref<BehaviorTree> p_behavior_tree, bool p_force_refresh = false); void edit_bt(const Ref<BehaviorTree> &p_behavior_tree, bool p_force_refresh = false);
Ref<BlackboardPlan> get_edited_blackboard_plan(); Ref<BlackboardPlan> get_edited_blackboard_plan();
void apply_changes(); void apply_changes();

View File

@ -420,7 +420,7 @@ void TaskPalette::_category_item_toggled(bool p_pressed, const String &p_categor
} }
void TaskPalette::_filter_data_changed() { void TaskPalette::_filter_data_changed() {
call_deferred(LW_NAME(refresh)); callable_mp(this, &TaskPalette::refresh).call_deferred();
_update_filter_button(); _update_filter_button();
} }

View File

@ -60,7 +60,7 @@ void LimboHSM::change_active_state(LimboState *p_state) {
active_state->_enter(); active_state->_enter();
active_state->set_process_input(true); active_state->set_process_input(true);
emit_signal(LimboStringNames::get_singleton()->active_state_changed, active_state, previous_active); emit_signal(LW_NAME(active_state_changed), active_state, previous_active);
} }
void LimboHSM::_enter() { void LimboHSM::_enter() {

View File

@ -63,7 +63,7 @@ LimboState *LimboState::named(const String &p_name) {
void LimboState::_enter() { void LimboState::_enter() {
active = true; active = true;
GDVIRTUAL_CALL(_enter); GDVIRTUAL_CALL(_enter);
emit_signal(LimboStringNames::get_singleton()->entered); emit_signal(LW_NAME(entered));
} }
void LimboState::_exit() { void LimboState::_exit() {
@ -71,18 +71,18 @@ void LimboState::_exit() {
return; return;
} }
GDVIRTUAL_CALL(_exit); GDVIRTUAL_CALL(_exit);
emit_signal(LimboStringNames::get_singleton()->exited); emit_signal(LW_NAME(exited));
active = false; active = false;
} }
void LimboState::_update(double p_delta) { void LimboState::_update(double p_delta) {
GDVIRTUAL_CALL(_update, p_delta); GDVIRTUAL_CALL(_update, p_delta);
emit_signal(LimboStringNames::get_singleton()->updated, p_delta); emit_signal(LW_NAME(updated), p_delta);
} }
void LimboState::_setup() { void LimboState::_setup() {
GDVIRTUAL_CALL(_setup); GDVIRTUAL_CALL(_setup);
emit_signal(LimboStringNames::get_singleton()->setup); emit_signal(LW_NAME(setup));
} }
void LimboState::_initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) { void LimboState::_initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) {
@ -154,19 +154,19 @@ bool LimboState::dispatch(const StringName &p_event, const Variant &p_cargo) {
LimboState *LimboState::call_on_enter(const Callable &p_callable) { LimboState *LimboState::call_on_enter(const Callable &p_callable) {
ERR_FAIL_COND_V(!p_callable.is_valid(), this); ERR_FAIL_COND_V(!p_callable.is_valid(), this);
connect(LimboStringNames::get_singleton()->entered, p_callable); connect(LW_NAME(entered), p_callable);
return this; return this;
} }
LimboState *LimboState::call_on_exit(const Callable &p_callable) { LimboState *LimboState::call_on_exit(const Callable &p_callable) {
ERR_FAIL_COND_V(!p_callable.is_valid(), this); ERR_FAIL_COND_V(!p_callable.is_valid(), this);
connect(LimboStringNames::get_singleton()->exited, p_callable); connect(LW_NAME(exited), p_callable);
return this; return this;
} }
LimboState *LimboState::call_on_update(const Callable &p_callable) { LimboState *LimboState::call_on_update(const Callable &p_callable) {
ERR_FAIL_COND_V(!p_callable.is_valid(), this); ERR_FAIL_COND_V(!p_callable.is_valid(), this);
connect(LimboStringNames::get_singleton()->updated, p_callable); connect(LW_NAME(updated), p_callable);
return this; return this;
} }

View File

@ -3,8 +3,8 @@
major = 1 major = 1
minor = 2 minor = 2
patch = 0 patch = 0
status = "rc" status = ""
doc_branch = "latest" doc_branch = "v1.2.0"
godot_cpp_ref = "godot-4.3-stable" godot_cpp_ref = "godot-4.3-stable"
# Code that generates version header # Code that generates version header

View File

@ -27,15 +27,9 @@
LimboStringNames *LimboStringNames::singleton = nullptr; LimboStringNames *LimboStringNames::singleton = nullptr;
LimboStringNames::LimboStringNames() { LimboStringNames::LimboStringNames() {
_enter = SN("_enter");
_exit = SN("_exit");
_generate_name = SN("_generate_name"); _generate_name = SN("_generate_name");
_get_configuration_warnings = SN("_get_configuration_warnings");
_replace_task = SN("_replace_task"); _replace_task = SN("_replace_task");
_setup = SN("_setup"); _update_task_tree = SN("_update_task_tree");
_tick = SN("_tick");
_update = SN("_update");
_update_banners = SN("_update_banners");
_weight_ = SN("_weight_"); _weight_ = SN("_weight_");
accent_color = SN("accent_color"); accent_color = SN("accent_color");
ActionCopy = SN("ActionCopy"); ActionCopy = SN("ActionCopy");
@ -46,19 +40,12 @@ LimboStringNames::LimboStringNames() {
add_child = SN("add_child"); add_child = SN("add_child");
add_child_at_index = SN("add_child_at_index"); add_child_at_index = SN("add_child_at_index");
AnimationFilter = SN("AnimationFilter"); AnimationFilter = SN("AnimationFilter");
Back = SN("Back");
behavior_tree = SN("behavior_tree");
behavior_tree_finished = SN("behavior_tree_finished"); behavior_tree_finished = SN("behavior_tree_finished");
BehaviorTree = SN("BehaviorTree");
bold = SN("bold"); bold = SN("bold");
BTAlwaysFail = SN("BTAlwaysFail");
BTAlwaysSucceed = SN("BTAlwaysSucceed");
button_down = SN("button_down"); button_down = SN("button_down");
button_up = SN("button_up"); button_up = SN("button_up");
call_deferred = SN("call_deferred"); call_deferred = SN("call_deferred");
changed = SN("changed"); changed = SN("changed");
connect = SN("connect");
dark_color_1 = SN("dark_color_1");
dark_color_2 = SN("dark_color_2"); dark_color_2 = SN("dark_color_2");
Debug = SN("Debug"); Debug = SN("Debug");
disabled_font_color = SN("disabled_font_color"); disabled_font_color = SN("disabled_font_color");
@ -71,7 +58,6 @@ LimboStringNames::LimboStringNames() {
EditorFonts = SN("EditorFonts"); EditorFonts = SN("EditorFonts");
EditorIcons = SN("EditorIcons"); EditorIcons = SN("EditorIcons");
EditorStyles = SN("EditorStyles"); EditorStyles = SN("EditorStyles");
emit_changed = SN("emit_changed");
entered = SN("entered"); entered = SN("entered");
error_value = SN("error_value"); error_value = SN("error_value");
EVENT_FAILURE = SN("failure"); EVENT_FAILURE = SN("failure");
@ -84,7 +70,6 @@ LimboStringNames::LimboStringNames() {
font = SN("font"); font = SN("font");
font_color = SN("font_color"); font_color = SN("font_color");
font_size = SN("font_size"); font_size = SN("font_size");
Forward = SN("Forward");
freed = SN("freed"); freed = SN("freed");
gui_input = SN("gui_input"); gui_input = SN("gui_input");
GuiOptionArrow = SN("GuiOptionArrow"); GuiOptionArrow = SN("GuiOptionArrow");
@ -97,10 +82,6 @@ LimboStringNames::LimboStringNames() {
Info = SN("Info"); Info = SN("Info");
item_collapsed = SN("item_collapsed"); item_collapsed = SN("item_collapsed");
item_selected = SN("item_selected"); item_selected = SN("item_selected");
LimboDeselectAll = SN("LimboDeselectAll");
LimboExtraClock = SN("LimboExtraClock");
LimboPercent = SN("LimboPercent");
LimboSelectAll = SN("LimboSelectAll");
LimboVarAdd = SN("LimboVarAdd"); LimboVarAdd = SN("LimboVarAdd");
LimboVarEmpty = SN("LimboVarEmpty"); LimboVarEmpty = SN("LimboVarEmpty");
LimboVarError = SN("LimboVarError"); LimboVarError = SN("LimboVarError");
@ -125,7 +106,6 @@ LimboStringNames::LimboStringNames() {
popup_hide = SN("popup_hide"); popup_hide = SN("popup_hide");
pressed = SN("pressed"); pressed = SN("pressed");
probability_clicked = SN("probability_clicked"); probability_clicked = SN("probability_clicked");
refresh = SN("refresh");
Reload = SN("Reload"); Reload = SN("Reload");
Remove = SN("Remove"); Remove = SN("Remove");
remove_child = SN("remove_child"); remove_child = SN("remove_child");
@ -158,8 +138,6 @@ LimboStringNames::LimboStringNames() {
Tree = SN("Tree"); Tree = SN("Tree");
TripleBar = SN("TripleBar"); TripleBar = SN("TripleBar");
update_mode = SN("update_mode"); update_mode = SN("update_mode");
update_task = SN("update_task");
update_tree = SN("update_tree");
updated = SN("updated"); updated = SN("updated");
visibility_changed = SN("visibility_changed"); visibility_changed = SN("visibility_changed");
window_visibility_changed = SN("window_visibility_changed"); window_visibility_changed = SN("window_visibility_changed");

View File

@ -43,15 +43,9 @@ class LimboStringNames {
public: public:
_FORCE_INLINE_ static LimboStringNames *get_singleton() { return singleton; } _FORCE_INLINE_ static LimboStringNames *get_singleton() { return singleton; }
StringName _enter;
StringName _exit;
StringName _generate_name; StringName _generate_name;
StringName _get_configuration_warnings;
StringName _replace_task; StringName _replace_task;
StringName _setup; StringName _update_task_tree;
StringName _tick;
StringName _update_banners;
StringName _update;
StringName _weight_; StringName _weight_;
StringName accent_color; StringName accent_color;
StringName ActionCopy; StringName ActionCopy;
@ -62,19 +56,12 @@ public:
StringName add_child; StringName add_child;
StringName Add; StringName Add;
StringName AnimationFilter; StringName AnimationFilter;
StringName Back;
StringName behavior_tree_finished; StringName behavior_tree_finished;
StringName behavior_tree;
StringName BehaviorTree;
StringName bold; StringName bold;
StringName BTAlwaysFail;
StringName BTAlwaysSucceed;
StringName button_down; StringName button_down;
StringName button_up; StringName button_up;
StringName call_deferred; StringName call_deferred;
StringName changed; StringName changed;
StringName connect;
StringName dark_color_1;
StringName dark_color_2; StringName dark_color_2;
StringName Debug; StringName Debug;
StringName disabled_font_color; StringName disabled_font_color;
@ -87,7 +74,6 @@ public:
StringName EditorFonts; StringName EditorFonts;
StringName EditorIcons; StringName EditorIcons;
StringName EditorStyles; StringName EditorStyles;
StringName emit_changed;
StringName entered; StringName entered;
StringName error_value; StringName error_value;
StringName EVENT_FAILURE; StringName EVENT_FAILURE;
@ -100,7 +86,6 @@ public:
StringName font_color; StringName font_color;
StringName font_size; StringName font_size;
StringName font; StringName font;
StringName Forward;
StringName freed; StringName freed;
StringName gui_input; StringName gui_input;
StringName GuiOptionArrow; StringName GuiOptionArrow;
@ -113,11 +98,6 @@ public:
StringName Info; StringName Info;
StringName item_collapsed; StringName item_collapsed;
StringName item_selected; StringName item_selected;
StringName LimboDeselectAll;
StringName LimboExtraClock;
StringName LimboExtractSubtree;
StringName LimboPercent;
StringName LimboSelectAll;
StringName LimboVarAdd; StringName LimboVarAdd;
StringName LimboVarEmpty; StringName LimboVarEmpty;
StringName LimboVarError; StringName LimboVarError;
@ -142,7 +122,6 @@ public:
StringName popup_hide; StringName popup_hide;
StringName pressed; StringName pressed;
StringName probability_clicked; StringName probability_clicked;
StringName refresh;
StringName Reload; StringName Reload;
StringName remove_child; StringName remove_child;
StringName Remove; StringName Remove;
@ -175,8 +154,6 @@ public:
StringName Tree; StringName Tree;
StringName TripleBar; StringName TripleBar;
StringName update_mode; StringName update_mode;
StringName update_task;
StringName update_tree;
StringName updated; StringName updated;
StringName visibility_changed; StringName visibility_changed;
StringName window_visibility_changed; StringName window_visibility_changed;