Compare commits

...

12 Commits

Author SHA1 Message Date
Wilson E. Alvarez c868bda033
Merge 224ceda390 into ae61d551c0 2024-11-05 16:06:50 -05:00
Serhii Snitsaruk ae61d551c0
Doc: Clarify when `LimboState::_exit` is called 2024-11-05 19:41:16 +01:00
Serhii Snitsaruk a14cc4cc68
GHA: Use 4.3-stable in "All builds" 2024-11-05 19:16:41 +01:00
Serhii Snitsaruk d304f957ef
Merge pull request #244 from limbonaut/save-builtin-bt-on-ctrls
Improve workflow with built-in BT resources
2024-11-05 01:40:51 -08:00
Serhii Snitsaruk bf85350260
Open the owner scene when switching to a built-in BT 2024-11-04 19:45:54 +01:00
Serhii Snitsaruk 03476721d9
Change shortcut for "Jump to Owner"
Ctrl+J is no longer available by default
2024-11-04 15:52:42 +01:00
Serhii Snitsaruk bebd6a15eb
Save edited built-in BT resource on `Ctrl+S` 2024-11-04 14:52:35 +01:00
Wilson E. Alvarez 224ceda390
Fix forbidden comparisons between Ref and nullptr.
Necessary when compiling with strict_checks=yes.

Due to upstream change:

	df29cc696f
2024-11-03 00:06:40 -04:00
Wilson E. Alvarez 085cde72af
Fix internal Button set_icon calls to set_button_icon
Due to upstream change:

    562c666e3d
2024-11-02 21:36:55 -04:00
Wilson E. Alvarez b322802a06
Fix unhandled tool button property hint
Due to upstream change:

	85dfd89653
2024-11-02 04:49:00 -04:00
Wilson E. Alvarez bba1f9c028
Fix unhandled dictionary property hint
Due to upstream change:

	9853a69144
2024-11-02 04:49:00 -04:00
Wilson E. Alvarez 29077d8713
Update EditorMainScreen calls after its extraction
Due to upstream change:

	5e1c9d68aa
2024-11-02 04:49:00 -04:00
27 changed files with 123 additions and 96 deletions

View File

@ -5,7 +5,7 @@ on:
godot-ref:
description: A tag, branch or commit hash in the Godot repository.
type: string
default: 4.3
default: 4.3-stable
limboai-ref:
description: A tag, branch or commit hash in the LimboAI repository.
type: string

View File

@ -13,7 +13,7 @@
Variant BBNode::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
ERR_FAIL_NULL_V_MSG(p_scene_root, Variant(), "BBNode: get_value() failed - scene_root is null.");
ERR_FAIL_NULL_V_MSG(p_blackboard, Variant(), "BBNode: get_value() failed - blackboard is null.");
ERR_FAIL_COND_V_MSG(p_blackboard.is_null(), Variant(), "BBNode: get_value() failed - blackboard is null.");
Variant val;
if (get_value_source() == SAVED_VALUE) {

View File

@ -430,7 +430,7 @@ void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bo
if (has_mapping) {
StringName target_var = parent_scope_mapping[p.first];
if (target_var != StringName()) {
ERR_CONTINUE_MSG(p_blackboard->get_parent() == nullptr, vformat("BlackboardPlan: Cannot link variable %s to parent scope because the parent scope is not set.", LimboUtility::get_singleton()->decorate_var(p.first)));
ERR_CONTINUE_MSG(p_blackboard->get_parent().is_null(), vformat("BlackboardPlan: Cannot link variable %s to parent scope because the parent scope is not set.", LimboUtility::get_singleton()->decorate_var(p.first)));
p_blackboard->link_var(p.first, p_blackboard->get_parent(), target_var);
}
}

View File

@ -78,10 +78,10 @@ void BehaviorTree::copy_other(const Ref<BehaviorTree> &p_other) {
}
Ref<BTInstance> BehaviorTree::instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_instance_owner, Node *p_custom_scene_root) const {
ERR_FAIL_COND_V_MSG(root_task == nullptr, nullptr, "BehaviorTree: Instantiation failed - BT has no valid root task.");
ERR_FAIL_COND_V_MSG(root_task.is_null(), nullptr, "BehaviorTree: Instantiation failed - BT has no valid root task.");
ERR_FAIL_NULL_V_MSG(p_agent, nullptr, "BehaviorTree: Instantiation failed - agent can't be null.");
ERR_FAIL_NULL_V_MSG(p_instance_owner, nullptr, "BehaviorTree: Instantiation failed -- instance owner can't be null.");
ERR_FAIL_NULL_V_MSG(p_blackboard, nullptr, "BehaviorTree: Instantiation failed - blackboard can't be null.");
ERR_FAIL_COND_V_MSG(p_blackboard.is_null(), nullptr, "BehaviorTree: Instantiation failed - blackboard can't be null.");
Node *scene_root = p_custom_scene_root ? p_custom_scene_root : p_instance_owner->get_owner();
ERR_FAIL_NULL_V_MSG(scene_root, nullptr, "BehaviorTree: Instantiation failed - unable to establish scene root. This is likely due to the instance owner not being owned by a scene node and custom_scene_root being null.");
Ref<BTTask> root_copy = root_task->clone();

View File

@ -25,7 +25,7 @@
#endif
Ref<BTInstance> BTInstance::create(Ref<BTTask> p_root_task, String p_source_bt_path, Node *p_owner_node) {
ERR_FAIL_NULL_V(p_root_task, nullptr);
ERR_FAIL_COND_V(p_root_task.is_null(), nullptr);
ERR_FAIL_NULL_V(p_owner_node, nullptr);
Ref<BTInstance> inst;
inst.instantiate();
@ -102,7 +102,7 @@ double BTInstance::_get_mean_update_time_msec_and_reset() {
void BTInstance::_add_custom_monitor() {
ERR_FAIL_NULL(get_owner_node());
ERR_FAIL_NULL(root_task);
ERR_FAIL_COND(root_task.is_null());
ERR_FAIL_NULL(root_task->get_agent());
if (monitor_id == StringName()) {

View File

@ -98,7 +98,7 @@ void BTState::_update(double p_delta) {
// Bail out if a transition happened in the meantime.
return;
}
ERR_FAIL_NULL(bt_instance);
ERR_FAIL_COND(bt_instance.is_null());
BT::Status status = bt_instance->update(p_delta);
if (status == BTTask::SUCCESS) {
get_root()->dispatch(success_event, Variant());

View File

@ -30,7 +30,7 @@ PackedStringArray BTComment::get_configuration_warnings() {
if (get_child_count_excluding_comments() > 0) {
warnings.append("Can only have other comment tasks as children.");
}
if (get_parent() == nullptr) {
if (get_parent().is_null()) {
warnings.append("Can't be the root task.");
}
return warnings;

View File

@ -163,7 +163,7 @@ void BTTask::set_custom_name(const String &p_name) {
void BTTask::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
ERR_FAIL_NULL(p_agent);
ERR_FAIL_NULL(p_blackboard);
ERR_FAIL_COND(p_blackboard.is_null());
ERR_FAIL_NULL(p_scene_root);
data.agent = p_agent;
data.blackboard = p_blackboard;

View File

@ -80,7 +80,7 @@ void BTCooldown::_chill() {
timer->set_time_left(duration);
} else {
timer = SCENE_TREE()->create_timer(duration, process_pause);
ERR_FAIL_NULL(timer);
ERR_FAIL_COND(timer.is_null());
timer->connect(LW_NAME(timeout), callable_mp(this, &BTCooldown::_on_timeout), CONNECT_ONE_SHOT);
}
}

View File

@ -32,16 +32,16 @@ void BTNewScope::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
#ifdef TOOLS_ENABLED
void BTNewScope::_set_parent_scope_plan_from_bt() {
ERR_FAIL_NULL(get_blackboard_plan());
ERR_FAIL_COND(get_blackboard_plan().is_null());
Ref<BehaviorTree> bt = get_root()->editor_get_behavior_tree();
ERR_FAIL_NULL(bt);
ERR_FAIL_COND(bt.is_null());
get_blackboard_plan()->set_parent_scope_plan_provider(callable_mp(bt.ptr(), &BehaviorTree::get_blackboard_plan));
}
#endif // TOOLS_ENABLED
void BTNewScope::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
ERR_FAIL_COND(p_agent == nullptr);
ERR_FAIL_COND(p_blackboard == nullptr);
ERR_FAIL_COND(p_blackboard.is_null());
Ref<Blackboard> bb;
if (blackboard_plan.is_valid()) {

View File

@ -234,7 +234,7 @@ Called when the state is entered.
|void| **_exit**\ (\ ) |virtual| :ref:`🔗<class_LimboState_private_method__exit>`
Called when the state is exited.
Called when the state is exited. This happens on a transition to another state, and when the state machine is removed from the scene tree (e.g., when the node is freed with :ref:`Node.queue_free<class_Node_method_queue_free>` or the scene changes). Due to implementation details, :ref:`_exit<class_LimboState_private_method__exit>` will not be called on :ref:`Object.free<class_Object_method_free>`!
.. rst-class:: classref-item-separator

View File

@ -20,7 +20,7 @@
<method name="_exit" qualifiers="virtual">
<return type="void" />
<description>
Called when the state is exited.
Called when the state is exited. This happens on a transition to another state, and when the state machine is removed from the scene tree (e.g., when the node is freed with [method Node.queue_free] or the scene changes). Due to implementation details, [method _exit] will not be called on [method Object.free]!
</description>
</method>
<method name="_setup" qualifiers="virtual">

View File

@ -46,7 +46,7 @@ LineEdit *BlackboardPlanEditor::_get_name_edit(int p_row_index) const {
}
void BlackboardPlanEditor::_add_var() {
ERR_FAIL_NULL(plan);
ERR_FAIL_COND(plan.is_null());
int suffix = 1;
StringName var_name = default_var_name == StringName() ? "var" : default_var_name;
@ -65,14 +65,14 @@ void BlackboardPlanEditor::_add_var() {
}
void BlackboardPlanEditor::_trash_var(int p_index) {
ERR_FAIL_NULL(plan);
ERR_FAIL_COND(plan.is_null());
StringName var_name = plan->get_var_by_index(p_index).first;
plan->remove_var(var_name);
_refresh();
}
void BlackboardPlanEditor::_rename_var(const StringName &p_new_name, int p_index) {
ERR_FAIL_NULL(plan);
ERR_FAIL_COND(plan.is_null());
LineEdit *name_edit = _get_name_edit(p_index);
ERR_FAIL_NULL(name_edit);
@ -96,7 +96,7 @@ void BlackboardPlanEditor::_rename_var(const StringName &p_new_name, int p_index
}
void BlackboardPlanEditor::_change_var_type(Variant::Type p_new_type, int p_index) {
ERR_FAIL_NULL(plan);
ERR_FAIL_COND(plan.is_null());
BBVariable var = plan->get_var_by_index(p_index).second;
if (var.get_type() == p_new_type) {
@ -115,14 +115,14 @@ void BlackboardPlanEditor::_change_var_type(Variant::Type p_new_type, int p_inde
}
void BlackboardPlanEditor::_change_var_hint(PropertyHint p_new_hint, int p_index) {
ERR_FAIL_NULL(plan);
ERR_FAIL_COND(plan.is_null());
plan->get_var_by_index(p_index).second.set_hint(p_new_hint);
plan->notify_property_list_changed();
_refresh();
}
void BlackboardPlanEditor::_change_var_hint_string(const String &p_new_hint_string, int p_index) {
ERR_FAIL_NULL(plan);
ERR_FAIL_COND(plan.is_null());
plan->get_var_by_index(p_index).second.set_hint_string(p_new_hint_string);
plan->notify_property_list_changed();
}
@ -278,7 +278,7 @@ void BlackboardPlanEditor::_refresh() {
Button *drag_button = memnew(Button);
props_hbox->add_child(drag_button);
drag_button->set_custom_minimum_size(Size2(28.0, 28.0) * EDSCALE);
BUTTON_SET_ICON(drag_button, theme_cache.grab_icon);
drag_button->set_button_icon(theme_cache.grab_icon);
drag_button->connect(LW_NAME(gui_input), callable_mp(this, &BlackboardPlanEditor::_drag_button_gui_input));
drag_button->connect(LW_NAME(button_down), callable_mp(this, &BlackboardPlanEditor::_drag_button_down).bind(row_panel));
drag_button->connect(LW_NAME(button_up), callable_mp(this, &BlackboardPlanEditor::_drag_button_up));
@ -297,7 +297,7 @@ void BlackboardPlanEditor::_refresh() {
type_choice->set_custom_minimum_size(Size2(170, 0.0) * EDSCALE);
type_choice->set_text(Variant::get_type_name(var.get_type()));
type_choice->set_tooltip_text(Variant::get_type_name(var.get_type()));
BUTTON_SET_ICON(type_choice, get_theme_icon(Variant::get_type_name(var.get_type()), LW_NAME(EditorIcons)));
type_choice->set_button_icon(get_theme_icon(Variant::get_type_name(var.get_type()), LW_NAME(EditorIcons)));
type_choice->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
type_choice->set_flat(true);
type_choice->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
@ -326,7 +326,7 @@ void BlackboardPlanEditor::_refresh() {
Button *trash_button = memnew(Button);
props_hbox->add_child(trash_button);
trash_button->set_custom_minimum_size(Size2(24.0, 0.0) * EDSCALE);
BUTTON_SET_ICON(trash_button, theme_cache.trash_icon);
trash_button->set_button_icon(theme_cache.trash_icon);
trash_button->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_trash_var).bind(i));
}
}
@ -337,7 +337,7 @@ void BlackboardPlanEditor::_notification(int p_what) {
theme_cache.trash_icon = get_theme_icon(LW_NAME(Remove), LW_NAME(EditorIcons));
theme_cache.grab_icon = get_theme_icon(LW_NAME(TripleBar), LW_NAME(EditorIcons));
BUTTON_SET_ICON(add_var_tool, get_theme_icon(LW_NAME(Add), LW_NAME(EditorIcons)));
add_var_tool->set_button_icon(get_theme_icon(LW_NAME(Add), LW_NAME(EditorIcons)));
type_menu->clear();
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
@ -472,14 +472,14 @@ BlackboardPlanEditor::BlackboardPlanEditor() {
// ***** EditorInspectorPluginBBPlan *****
void EditorInspectorPluginBBPlan::_edit_plan(const Ref<BlackboardPlan> &p_plan) {
ERR_FAIL_NULL(p_plan);
ERR_FAIL_COND(p_plan.is_null());
plan_editor->edit_plan(p_plan);
plan_editor->popup_centered();
}
void EditorInspectorPluginBBPlan::_open_base_plan(const Ref<BlackboardPlan> &p_plan) {
ERR_FAIL_NULL(p_plan);
ERR_FAIL_NULL(p_plan->get_base_plan());
ERR_FAIL_COND(p_plan.is_null());
ERR_FAIL_COND(p_plan->get_base_plan().is_null());
EditorInterface::get_singleton()->call_deferred("edit_resource", p_plan->get_base_plan());
}
@ -501,7 +501,7 @@ void EditorInspectorPluginBBPlan::parse_begin(Object *p_object) {
void EditorInspectorPluginBBPlan::_parse_begin(Object *p_object) {
#endif
Ref<BlackboardPlan> plan = Object::cast_to<BlackboardPlan>(p_object);
ERR_FAIL_NULL(plan);
ERR_FAIL_COND(plan.is_null());
PanelContainer *panel = memnew(PanelContainer);
ADD_STYLEBOX_OVERRIDE(panel, LW_NAME(panel), toolbar_style);
@ -522,7 +522,7 @@ void EditorInspectorPluginBBPlan::_parse_begin(Object *p_object) {
goto_btn->set_text(TTR("Edit Base"));
goto_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
goto_btn->set_custom_minimum_size(Size2(150.0, 0.0) * EDSCALE);
BUTTON_SET_ICON(goto_btn, EditorInterface::get_singleton()->get_editor_theme()->get_icon(LW_NAME(Edit), LW_NAME(EditorIcons)));
goto_btn->set_button_icon(EditorInterface::get_singleton()->get_editor_theme()->get_icon(LW_NAME(Edit), LW_NAME(EditorIcons)));
goto_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_open_base_plan).bind(plan));
} else {
Button *edit_btn = memnew(Button);
@ -530,7 +530,7 @@ void EditorInspectorPluginBBPlan::_parse_begin(Object *p_object) {
edit_btn->set_text(TTR("Manage..."));
edit_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
edit_btn->set_custom_minimum_size(Size2(150.0, 0.0) * EDSCALE);
BUTTON_SET_ICON(edit_btn, EditorInterface::get_singleton()->get_editor_theme()->get_icon(LW_NAME(EditAddRemove), LW_NAME(EditorIcons)));
edit_btn->set_button_icon(EditorInterface::get_singleton()->get_editor_theme()->get_icon(LW_NAME(EditAddRemove), LW_NAME(EditorIcons)));
edit_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_edit_plan).bind(plan));
}

View File

@ -209,7 +209,7 @@ void LimboDebuggerTab::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED: {
alert_icon->set_texture(get_theme_icon(LW_NAME(StatusWarning), LW_NAME(EditorIcons)));
BUTTON_SET_ICON(resource_header, LimboUtility::get_singleton()->get_task_icon("BehaviorTree"));
resource_header->set_button_icon(LimboUtility::get_singleton()->get_task_icon("BehaviorTree"));
} break;
}
}

View File

@ -287,14 +287,14 @@ void EditorPropertyBBParam::update_property() {
variable_editor->update_property();
variable_editor->show();
bottom_container->hide();
type_choice->set_icon(get_editor_theme_icon(SNAME("LimboExtraVariable")));
type_choice->set_button_icon(get_editor_theme_icon(SNAME("LimboExtraVariable")));
} else {
_create_value_editor(param->get_type());
variable_editor->hide();
value_editor->show();
value_editor->set_object_and_property(param.ptr(), SNAME("saved_value"));
value_editor->update_property();
type_choice->set_icon(get_editor_theme_icon(Variant::get_type_name(param->get_type())));
type_choice->set_button_icon(get_editor_theme_icon(Variant::get_type_name(param->get_type())));
}
}
@ -316,7 +316,7 @@ void EditorPropertyBBParam::_notification(int p_what) {
{
String type = Variant::get_type_name(_get_edited_param()->get_type());
type_choice->set_icon(get_editor_theme_icon(type));
type_choice->set_button_icon(get_editor_theme_icon(type));
}
// Initialize type choice.

View File

@ -36,7 +36,7 @@ int EditorPropertyVariableName::last_caret_column = 0;
//***** EditorPropertyVariableName
void EditorPropertyVariableName::_show_variables_popup() {
ERR_FAIL_NULL(plan);
ERR_FAIL_COND(plan.is_null());
variables_popup->clear();
variables_popup->reset_size();
@ -86,30 +86,30 @@ void EditorPropertyVariableName::_update_status() {
}
String var_name = name_edit->get_text();
if (var_name.is_empty() && allow_empty) {
BUTTON_SET_ICON(status_btn, theme_cache.var_empty_icon);
status_btn->set_button_icon(theme_cache.var_empty_icon);
status_btn->set_tooltip_text(TTR("Variable name not specified.\nClick to open the blackboard plan."));
} else if (plan->has_var(var_name)) {
if (expected_type == Variant::NIL || plan->get_var(var_name).get_type() == expected_type) {
BUTTON_SET_ICON(status_btn, theme_cache.var_exists_icon);
status_btn->set_button_icon(theme_cache.var_exists_icon);
status_btn->set_tooltip_text(TTR("This variable is present in the blackboard plan.\nClick to open the blackboard plan."));
} else {
BUTTON_SET_ICON(status_btn, theme_cache.var_error_icon);
status_btn->set_button_icon(theme_cache.var_error_icon);
status_btn->set_tooltip_text(TTR(vformat(
"The %s variable in the blackboard plan should be of type %s.\nClick to open the blackboard plan.",
LimboUtility::get_singleton()->decorate_var(var_name),
Variant::get_type_name(expected_type))));
}
} else if (name_edit->get_text().begins_with("_")) {
BUTTON_SET_ICON(status_btn, theme_cache.var_private_icon);
status_btn->set_button_icon(theme_cache.var_private_icon);
status_btn->set_tooltip_text(TTR("This variable is private and is not included in the blackboard plan.\nClick to open the blackboard plan."));
} else {
BUTTON_SET_ICON(status_btn, theme_cache.var_not_found_icon);
status_btn->set_button_icon(theme_cache.var_not_found_icon);
status_btn->set_tooltip_text(TTR("No matching variable found in the blackboard plan!\nClick to open the blackboard plan."));
}
}
void EditorPropertyVariableName::_status_pressed() {
ERR_FAIL_NULL(plan);
ERR_FAIL_COND(plan.is_null());
if (!plan->has_var(name_edit->get_text())) {
BlackboardPlanEditor::get_singleton()->set_defaults(name_edit->get_text(),
expected_type == Variant::NIL ? Variant::FLOAT : expected_type,
@ -120,14 +120,14 @@ void EditorPropertyVariableName::_status_pressed() {
}
void EditorPropertyVariableName::_status_mouse_entered() {
ERR_FAIL_NULL(plan);
ERR_FAIL_COND(plan.is_null());
if (!plan->has_var(name_edit->get_text())) {
BUTTON_SET_ICON(status_btn, theme_cache.var_add_icon);
status_btn->set_button_icon(theme_cache.var_add_icon);
}
}
void EditorPropertyVariableName::_status_mouse_exited() {
ERR_FAIL_NULL(plan);
ERR_FAIL_COND(plan.is_null());
_update_status();
}
@ -182,7 +182,7 @@ void EditorPropertyVariableName::_notification(int p_what) {
}
} break;
case NOTIFICATION_THEME_CHANGED: {
BUTTON_SET_ICON(drop_btn, get_theme_icon(LW_NAME(GuiOptionArrow), LW_NAME(EditorIcons)));
drop_btn->set_button_icon(get_theme_icon(LW_NAME(GuiOptionArrow), LW_NAME(EditorIcons)));
theme_cache.var_add_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarAdd));
theme_cache.var_exists_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarExists));
theme_cache.var_not_found_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarNotFound));
@ -257,7 +257,7 @@ bool EditorInspectorPluginVariableName::_parse_property(Object *p_object, const
Variant default_value;
if (is_mapping) {
plan.reference_ptr(Object::cast_to<BlackboardPlan>(p_object));
ERR_FAIL_NULL_V(plan, false);
ERR_FAIL_COND_V(plan.is_null(), false);
String var_name = p_path.trim_prefix("mapping/");
if (plan->has_var(var_name)) {
BBVariable variable = plan->get_var(var_name);
@ -272,7 +272,7 @@ bool EditorInspectorPluginVariableName::_parse_property(Object *p_object, const
plan = parent_plan;
}
}
ERR_FAIL_NULL_V(plan, false);
ERR_FAIL_COND_V(plan.is_null(), false);
} else {
plan = editor_plan_provider.call();
}

View File

@ -66,6 +66,7 @@
#include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/classes/resource_loader.hpp>
#include <godot_cpp/classes/resource_saver.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/classes/script.hpp>
#include <godot_cpp/classes/script_editor.hpp>
#include <godot_cpp/classes/script_editor_base.hpp>
@ -73,6 +74,21 @@
#include <godot_cpp/core/error_macros.hpp>
#endif // LIMBOAI_GDEXTENSION
namespace {
// If built-in resource - switch to the owner scene (open it if not already).
inline void _switch_to_owner_scene_if_builtin(const Ref<BehaviorTree> &p_behavior_tree) {
if (p_behavior_tree.is_valid() && p_behavior_tree->get_path().contains("::")) {
String current_scene = SCENE_TREE()->get_edited_scene_root()->get_scene_file_path();
String scene_path = p_behavior_tree->get_path().get_slice("::", 0);
if (current_scene != scene_path) {
EditorInterface::get_singleton()->open_scene_from_path(scene_path);
}
}
}
} // unnamed namespace
//**** LimboAIEditor
_FORCE_INLINE_ String _get_script_template_path() {
@ -183,7 +199,7 @@ void LimboAIEditor::_remove_task(const Ref<BTTask> &p_task) {
ERR_FAIL_COND(p_task.is_null());
ERR_FAIL_COND(task_tree->get_bt().is_null());
EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Remove BT Task"));
if (p_task->get_parent() == nullptr) {
if (p_task->get_parent().is_null()) {
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_undo_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), task_tree->get_bt()->get_root_task());
@ -291,6 +307,8 @@ void LimboAIEditor::_disable_editing() {
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");
_switch_to_owner_scene_if_builtin(p_behavior_tree);
if (!p_force_refresh && task_tree->get_bt() == p_behavior_tree) {
return;
}
@ -476,6 +494,14 @@ void LimboAIEditor::_process_shortcut_input(const Ref<InputEvent> &p_event) {
} else if (LW_IS_SHORTCUT("limbo_ai/close_tab", p_event)) {
_tab_menu_option_selected(TAB_CLOSE);
handled = true;
} else if (LW_IS_SHORTCUT("limbo_ai/editor_save_scene", p_event)) {
// This intercepts the editor save action, but does not set the event as handled because we don't know the user's intention.
// We just want to save the currently edited BT as well, which may cause a loop with built-in resource if done from "_save_external_data".
// Workaround for: https://github.com/limbonaut/limboai/issues/240#issuecomment-2453087424
if (task_tree->get_bt().is_valid() && RESOURCE_IS_BUILT_IN(task_tree->get_bt())) {
_on_save_pressed();
}
handled = false; // intentionally not set as handled
}
}
@ -719,7 +745,7 @@ 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);
ERR_FAIL_COND(selected.is_null());
Ref<BTProbabilitySelector> probability_selector = selected->get_parent();
ERR_FAIL_COND(probability_selector.is_null());
if (percent_mode->is_pressed()) {
@ -874,6 +900,7 @@ void LimboAIEditor::_on_tree_task_activated() {
void LimboAIEditor::_on_visibility_changed() {
if (task_tree->is_visible_in_tree()) {
_switch_to_owner_scene_if_builtin(task_tree->get_bt());
Ref<BTTask> sel = task_tree->get_selected();
if (sel.is_valid()) {
EDIT_RESOURCE(sel);
@ -891,16 +918,6 @@ void LimboAIEditor::_on_visibility_changed() {
}
}
void LimboAIEditor::_on_header_pressed() {
task_tree->clear_selection();
#ifdef LIMBOAI_MODULE
if (task_tree->get_bt().is_valid()) {
task_tree->get_bt()->editor_set_section_unfold("blackboard_plan", true);
}
#endif // LIMBOAI_MODULE
EDIT_RESOURCE(task_tree->get_bt());
}
void LimboAIEditor::_on_save_pressed() {
if (task_tree->get_bt().is_null()) {
return;
@ -1223,7 +1240,7 @@ void LimboAIEditor::_tab_menu_option_selected(int p_id) {
} break;
case TAB_JUMP_TO_OWNER: {
Ref<BehaviorTree> bt = history[idx_history];
ERR_FAIL_NULL(bt);
ERR_FAIL_COND(bt.is_null());
String bt_path = bt->get_path();
if (!bt_path.is_empty()) {
owner_picker->pick_and_open_owner_of_resource(bt_path);
@ -1341,7 +1358,7 @@ void LimboAIEditor::_update_favorite_tasks() {
}
btn->set_text(task_name);
btn->set_meta(LW_NAME(task_meta), task_meta);
BUTTON_SET_ICON(btn, LimboUtility::get_singleton()->get_task_icon(task_meta));
btn->set_button_icon(LimboUtility::get_singleton()->get_task_icon(task_meta));
btn->set_tooltip_text(vformat(TTR("Add %s task."), task_name));
btn->set_flat(true);
btn->add_theme_constant_override(LW_NAME(icon_max_width), 16 * EDSCALE); // Force user icons to be of the proper size.
@ -1512,11 +1529,11 @@ void LimboAIEditor::_notification(int p_what) {
ADD_STYLEBOX_OVERRIDE(tab_bar_panel, "panel", get_theme_stylebox("tabbar_background", "TabContainer"));
BUTTON_SET_ICON(new_btn, get_theme_icon(LW_NAME(New), LW_NAME(EditorIcons)));
BUTTON_SET_ICON(load_btn, get_theme_icon(LW_NAME(Load), LW_NAME(EditorIcons)));
BUTTON_SET_ICON(save_btn, get_theme_icon(LW_NAME(Save), LW_NAME(EditorIcons)));
BUTTON_SET_ICON(new_script_btn, get_theme_icon(LW_NAME(ScriptCreate), LW_NAME(EditorIcons)));
BUTTON_SET_ICON(misc_btn, get_theme_icon(LW_NAME(Tools), LW_NAME(EditorIcons)));
new_btn->set_button_icon(get_theme_icon(LW_NAME(New), LW_NAME(EditorIcons)));
load_btn->set_button_icon(get_theme_icon(LW_NAME(Load), LW_NAME(EditorIcons)));
save_btn->set_button_icon(get_theme_icon(LW_NAME(Save), LW_NAME(EditorIcons)));
new_script_btn->set_button_icon(get_theme_icon(LW_NAME(ScriptCreate), LW_NAME(EditorIcons)));
misc_btn->set_button_icon(get_theme_icon(LW_NAME(Tools), LW_NAME(EditorIcons)));
_update_favorite_tasks();
} break;
@ -1580,11 +1597,14 @@ LimboAIEditor::LimboAIEditor() {
LW_SHORTCUT("limbo_ai/save_behavior_tree", TTR("Save Behavior Tree"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY_MASK(ALT) | LW_KEY(S)));
LW_SHORTCUT("limbo_ai/load_behavior_tree", TTR("Load Behavior Tree"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY_MASK(ALT) | LW_KEY(L)));
LW_SHORTCUT("limbo_ai/open_debugger", TTR("Open Debugger"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY_MASK(ALT) | LW_KEY(D)));
LW_SHORTCUT("limbo_ai/jump_to_owner", TTR("Jump to Owner"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(J)));
LW_SHORTCUT("limbo_ai/jump_to_owner", TTR("Jump to Owner"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(G)));
LW_SHORTCUT("limbo_ai/close_tab", TTR("Close Tab"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(W)));
LW_SHORTCUT("limbo_ai/find_task", TTR("Find Task"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(F)));
LW_SHORTCUT("limbo_ai/hide_tree_search", TTR("Close Search"), (Key)(LW_KEY(ESCAPE)));
// Intercept editor save scene action.
LW_SHORTCUT("limbo_ai/editor_save_scene", TTR("Save Scene"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(S)));
set_process_shortcut_input(true);
save_dialog = memnew(FileDialog);
@ -1948,8 +1968,9 @@ void LimboAIEditorPlugin::edit(Object *p_object) {
#elif LIMBOAI_GDEXTENSION
void LimboAIEditorPlugin::_edit(Object *p_object) {
#endif
if (Object::cast_to<BehaviorTree>(p_object)) {
limbo_ai_editor->edit_bt(Object::cast_to<BehaviorTree>(p_object));
Ref<BehaviorTree> bt = Object::cast_to<BehaviorTree>(p_object);
if (bt.is_valid()) {
limbo_ai_editor->edit_bt(bt);
}
}

View File

@ -26,6 +26,7 @@
#include "core/object/class_db.h"
#include "core/object/object.h"
#include "core/templates/hash_set.h"
#include "editor/editor_main_screen.h"
#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_spin_slider.h"
@ -42,6 +43,7 @@
#include "scene/gui/popup.h"
#include "scene/gui/popup_menu.h"
#include "scene/gui/split_container.h"
#include "scene/gui/tab_bar.h"
#include "scene/gui/tree.h"
#include "scene/resources/texture.h"
#endif // LIMBOAI_MODULE
@ -241,7 +243,6 @@ private:
void _on_tree_task_selected(const Ref<BTTask> &p_task);
void _on_tree_task_activated();
void _on_visibility_changed();
void _on_header_pressed();
void _on_save_pressed();
void _on_history_back();
void _on_history_forward();

View File

@ -44,7 +44,7 @@ private:
_FORCE_INLINE_ void _set_mode_by_index(int p_index) {
current_mode_index = p_index;
BUTTON_SET_ICON(this, modes[current_mode_index].icon);
this->set_button_icon(modes[current_mode_index].icon);
if (!modes[current_mode_index].tooltip.is_empty()) {
set_tooltip_text(modes[current_mode_index].tooltip);
}

View File

@ -184,7 +184,7 @@ void TaskPaletteSection::set_filter(String p_filter_text) {
void TaskPaletteSection::add_task_button(const String &p_name, const Ref<Texture> &icon, const String &p_meta) {
TaskButton *btn = memnew(TaskButton);
btn->set_text(p_name);
BUTTON_SET_ICON(btn, icon);
btn->set_button_icon(icon);
btn->set_tooltip_text("dummy_text"); // Force tooltip to be shown.
btn->set_task_meta(p_meta);
btn->add_theme_constant_override(LW_NAME(icon_max_width), 16 * EDSCALE); // Force user icons to be of the proper size.
@ -195,7 +195,7 @@ void TaskPaletteSection::add_task_button(const String &p_name, const Ref<Texture
void TaskPaletteSection::set_collapsed(bool p_collapsed) {
tasks_container->set_visible(!p_collapsed);
BUTTON_SET_ICON(section_header, (p_collapsed ? theme_cache.arrow_right_icon : theme_cache.arrow_down_icon));
section_header->set_button_icon((p_collapsed ? theme_cache.arrow_right_icon : theme_cache.arrow_down_icon));
}
bool TaskPaletteSection::is_collapsed() const {
@ -214,7 +214,7 @@ void TaskPaletteSection::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED: {
_do_update_theme_item_cache();
BUTTON_SET_ICON(section_header, (is_collapsed() ? theme_cache.arrow_right_icon : theme_cache.arrow_down_icon));
section_header->set_button_icon((is_collapsed() ? theme_cache.arrow_right_icon : theme_cache.arrow_down_icon));
section_header->add_theme_font_override(LW_NAME(font), get_theme_font(LW_NAME(bold), LW_NAME(EditorFonts)));
} break;
}
@ -600,13 +600,13 @@ void TaskPalette::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
_do_update_theme_item_cache();
BUTTON_SET_ICON(tool_filters, get_theme_icon(LW_NAME(AnimationFilter), LW_NAME(EditorIcons)));
BUTTON_SET_ICON(tool_refresh, get_theme_icon(LW_NAME(Reload), LW_NAME(EditorIcons)));
tool_filters->set_button_icon(get_theme_icon(LW_NAME(AnimationFilter), LW_NAME(EditorIcons)));
tool_refresh->set_button_icon(get_theme_icon(LW_NAME(Reload), LW_NAME(EditorIcons)));
filter_edit->set_right_icon(get_theme_icon(LW_NAME(Search), LW_NAME(EditorIcons)));
BUTTON_SET_ICON(select_all, LimboUtility::get_singleton()->get_task_icon("LimboSelectAll"));
BUTTON_SET_ICON(deselect_all, LimboUtility::get_singleton()->get_task_icon("LimboDeselectAll"));
select_all->set_button_icon(LimboUtility::get_singleton()->get_task_icon("LimboSelectAll"));
deselect_all->set_button_icon(LimboUtility::get_singleton()->get_task_icon("LimboDeselectAll"));
category_choice->queue_redraw();

View File

@ -192,7 +192,7 @@ void TaskTree::_on_item_collapsed(Object *p_obj) {
}
Ref<BTTask> task = item->get_metadata(0);
ERR_FAIL_NULL(task);
ERR_FAIL_COND(task.is_null());
task->set_display_collapsed(item->is_collapsed());
}
@ -574,7 +574,7 @@ void TaskTree::_bind_methods() {
// TreeSearch API
void TaskTree::tree_search_show_and_focus() {
ERR_FAIL_NULL(tree_search);
ERR_FAIL_COND(tree_search.is_null());
tree_search_panel->set_visible(true);
tree_search_panel->focus_editor();
}
@ -587,7 +587,7 @@ TreeSearch::SearchInfo TaskTree::tree_search_get_search_info() const {
}
void TaskTree::tree_search_set_search_info(const TreeSearch::SearchInfo &p_search_info) {
ERR_FAIL_NULL(tree_search);
ERR_FAIL_COND(tree_search.is_null());
tree_search_panel->set_search_info(p_search_info);
}

View File

@ -95,7 +95,7 @@ protected:
public:
void load_bt(const Ref<BehaviorTree> &p_behavior_tree);
void unload();
Ref<BehaviorTree> get_bt() const { return bt; }
_FORCE_INLINE_ Ref<BehaviorTree> get_bt() const { return bt; }
void update_tree() { _update_tree(); }
void update_task(const Ref<BTTask> &p_task);
void add_selection(const Ref<BTTask> &p_task);

View File

@ -158,7 +158,7 @@ void TreeSearch::_draw_highlight_item(TreeItem *p_tree_item, const Rect2 p_rect,
if (font.is_null()) {
font = p_tree_item->get_tree()->get_theme_font(LW_NAME(font));
}
ERR_FAIL_NULL(font);
ERR_FAIL_COND(font.is_null());
float font_size = p_tree_item->get_custom_font_size(0);
if (font_size == -1) {
font_size = p_tree_item->get_tree()->get_theme_font_size(LW_NAME(font));
@ -176,7 +176,7 @@ void TreeSearch::_draw_highlight_item(TreeItem *p_tree_item, const Rect2 p_rect,
// Stylebox
Ref<StyleBox> stylebox = p_tree_item->get_tree()->get_theme_stylebox(LW_NAME(Focus));
ERR_FAIL_NULL(stylebox);
ERR_FAIL_COND(stylebox.is_null());
// Extract separation
float h_sep = p_tree_item->get_tree()->get_theme_constant(LW_NAME(h_separation));
@ -559,9 +559,9 @@ void TreeSearchPanel::_notification(int p_what) {
break;
}
case NOTIFICATION_THEME_CHANGED: {
BUTTON_SET_ICON(close_button, get_theme_icon(LW_NAME(Close), LW_NAME(EditorIcons)));
BUTTON_SET_ICON(find_prev_button, get_theme_icon("MoveUp", LW_NAME(EditorIcons)));
BUTTON_SET_ICON(find_next_button, get_theme_icon("MoveDown", LW_NAME(EditorIcons)));
close_button->set_button_icon(get_theme_icon(LW_NAME(Close), LW_NAME(EditorIcons)));
find_prev_button->set_button_icon(get_theme_icon("MoveUp", LW_NAME(EditorIcons)));
find_next_button->set_button_icon(get_theme_icon("MoveDown", LW_NAME(EditorIcons)));
label_filter->set_text(TTR("Filter"));
break;
}

View File

@ -78,7 +78,7 @@ TEST_CASE("[Modules][LimboAI] BTTask") {
SUBCASE("Test next_sibling()") {
CHECK(child1->next_sibling() == child2);
CHECK(child2->next_sibling() == child3);
CHECK(child3->next_sibling() == nullptr);
CHECK(child3->next_sibling().is_null());
}
SUBCASE("Test remove_child()") {
task->remove_child(child2);
@ -153,7 +153,7 @@ TEST_CASE("[Modules][LimboAI] BTTask") {
CHECK(child3->get_root() == task);
}
SUBCASE("Test get_parent()") {
CHECK(task->get_parent() == nullptr);
CHECK(task->get_parent().is_null());
CHECK(child1->get_parent() == task);
CHECK(child2->get_parent() == task);
CHECK(child2->get_parent() == task);

View File

@ -16,6 +16,7 @@
#ifdef TOOLS_ENABLED
#include "core/io/resource.h"
#include "core/variant/variant.h"
#include "editor/editor_main_screen.h"
#include "editor/editor_node.h"
#include "editor/plugins/script_editor_plugin.h"
#endif // TOOLS_ENABLED
@ -213,7 +214,7 @@ Variant VARIANT_DEFAULT(Variant::Type p_type) {
void SHOW_BUILTIN_DOC(const String &p_topic) {
#ifdef LIMBOAI_MODULE
ScriptEditor::get_singleton()->goto_help(p_topic);
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
#elif LIMBOAI_GDEXTENSION
TypedArray<ScriptEditorBase> open_editors = EditorInterface::get_singleton()->get_script_editor()->get_open_script_editors();
ERR_FAIL_COND_MSG(open_editors.size() == 0, "Can't open help page. Need at least one script open in the script editor.");

View File

@ -28,7 +28,7 @@
#define EDITOR_FILE_SYSTEM() (EditorFileSystem::get_singleton())
#define EDITOR_SETTINGS() (EditorSettings::get_singleton())
#define BASE_CONTROL() (EditorNode::get_singleton()->get_gui_base())
#define MAIN_SCREEN_CONTROL() (EditorNode::get_singleton()->get_main_screen_control())
#define MAIN_SCREEN_CONTROL() (EditorNode::get_singleton()->get_editor_main_screen())
#define SCENE_TREE() (SceneTree::get_singleton())
#define IS_DEBUGGER_ACTIVE() (EngineDebugger::is_active())
#define FS_DOCK_SELECT_FILE(m_path) FileSystemDock::get_singleton()->select_file(m_path)
@ -37,7 +37,6 @@
#define IS_CLASS(m_obj, m_class) (m_obj->is_class_ptr(m_class::get_class_ptr_static()))
#define RAND_RANGE(m_from, m_to) (Math::random(m_from, m_to))
#define RANDF() (Math::randf())
#define BUTTON_SET_ICON(m_btn, m_icon) m_btn->set_icon(m_icon)
#define RESOURCE_LOAD(m_path, m_hint) ResourceLoader::load(m_path, m_hint)
#define RESOURCE_LOAD_NO_CACHE(m_path, m_hint) ResourceLoader::load(m_path, m_hint, ResourceFormatLoader::CACHE_MODE_IGNORE)
#define RESOURCE_SAVE(m_res, m_path, m_flags) ResourceSaver::save(m_res, m_path, m_flags)
@ -47,7 +46,7 @@
#define GET_PROJECT_SETTINGS_DIR() EditorPaths::get_singleton()->get_project_settings_dir()
#define EDIT_RESOURCE(m_res) EditorNode::get_singleton()->edit_resource(m_res)
#define INSPECTOR_GET_EDITED_OBJECT() (InspectorDock::get_inspector_singleton()->get_edited_object())
#define SET_MAIN_SCREEN_EDITOR(m_name) (EditorNode::get_singleton()->select_editor_by_name(m_name))
#define SET_MAIN_SCREEN_EDITOR(m_name) (EditorNode::get_singleton()->get_editor_main_screen()->select_by_name(m_name))
#define FILE_EXISTS(m_path) FileAccess::exists(m_path)
#define DIR_ACCESS_CREATE() DirAccess::create(DirAccess::ACCESS_RESOURCES)
#define PERFORMANCE_ADD_CUSTOM_MONITOR(m_id, m_callable) (Performance::get_singleton()->add_custom_monitor(m_id, m_callable, Variant()))
@ -96,7 +95,6 @@ using namespace godot;
#define IS_CLASS(m_obj, m_class) (m_obj->is_class(#m_class))
#define RAND_RANGE(m_from, m_to) (UtilityFunctions::randf_range(m_from, m_to))
#define RANDF() (UtilityFunctions::randf())
#define BUTTON_SET_ICON(m_btn, m_icon) m_btn->set_button_icon(m_icon)
#define RESOURCE_LOAD(m_path, m_hint) ResourceLoader::get_singleton()->load(m_path, m_hint)
#define RESOURCE_LOAD_NO_CACHE(m_path, m_hint) ResourceLoader::get_singleton()->load(m_path, m_hint, ResourceLoader::CACHE_MODE_IGNORE)
#define RESOURCE_SAVE(m_res, m_path, m_flags) ResourceSaver::get_singleton()->save(m_res, m_path, m_flags)

View File

@ -395,6 +395,12 @@ String LimboUtility::get_property_hint_text(PropertyHint p_hint) const {
case PROPERTY_HINT_ARRAY_TYPE: {
return "ARRAY_TYPE";
}
case PROPERTY_HINT_DICTIONARY_TYPE: {
return "DICTIONARY_TYPE";
}
case PROPERTY_HINT_TOOL_BUTTON: {
return "TOOL_BUTTON";
}
case PROPERTY_HINT_LOCALE_ID: {
return "LOCALE_ID";
}