diff --git a/blackboard/bb_variable.cpp b/blackboard/bb_variable.cpp index 9ab99ad..fac7d08 100644 --- a/blackboard/bb_variable.cpp +++ b/blackboard/bb_variable.cpp @@ -78,12 +78,16 @@ String BBVariable::get_hint_string() const { return data->hint_string; } -BBVariable BBVariable::duplicate() const { +BBVariable BBVariable::duplicate(bool p_deep) const { BBVariable var; var.data->hint = data->hint; var.data->hint_string = data->hint_string; var.data->type = data->type; - var.data->value = data->value; + if (p_deep) { + var.data->value = data->value.duplicate(p_deep); + } else { + var.data->value = data->value; + } var.data->binding_path = data->binding_path; var.data->bound_object = data->bound_object; var.data->bound_property = data->bound_property; diff --git a/blackboard/bb_variable.h b/blackboard/bb_variable.h index 02cdd30..3eab7ec 100644 --- a/blackboard/bb_variable.h +++ b/blackboard/bb_variable.h @@ -54,7 +54,7 @@ public: void set_hint_string(const String &p_hint_string); String get_hint_string() const; - BBVariable duplicate() const; + BBVariable duplicate(bool p_deep = false) const; _FORCE_INLINE_ bool is_value_changed() const { return data->value_changed; } _FORCE_INLINE_ void reset_value_changed() { data->value_changed = false; } diff --git a/blackboard/blackboard_plan.cpp b/blackboard/blackboard_plan.cpp index 25ddf9e..727e4c3 100644 --- a/blackboard/blackboard_plan.cpp +++ b/blackboard/blackboard_plan.cpp @@ -391,7 +391,7 @@ void BlackboardPlan::sync_with_base_plan() { inline void bb_add_var_dup_with_prefetch(const Ref &p_blackboard, const StringName &p_name, const BBVariable &p_var, bool p_prefetch, Node *p_node) { if (unlikely(p_prefetch && p_var.get_type() == Variant::NODE_PATH)) { Node *n = p_node->get_node_or_null(p_var.get_value()); - BBVariable var = p_var.duplicate(); + BBVariable var = p_var.duplicate(true); if (n != nullptr) { var.set_value(n); } else { @@ -404,7 +404,7 @@ inline void bb_add_var_dup_with_prefetch(const Ref &p_blackboard, co } p_blackboard->assign_var(p_name, var); } else { - p_blackboard->assign_var(p_name, p_var.duplicate()); + p_blackboard->assign_var(p_name, p_var.duplicate(true)); } } diff --git a/bt/bt_player.cpp b/bt/bt_player.cpp index 838baf7..2c66cd7 100644 --- a/bt/bt_player.cpp +++ b/bt/bt_player.cpp @@ -144,6 +144,7 @@ void BTPlayer::update(double p_delta) { } void BTPlayer::restart() { + ERR_FAIL_COND_MSG(tree_instance.is_null(), "BTPlayer: Restart failed - no valid tree instance. Make sure the BTPlayer has a valid behavior tree with a valid root task."); tree_instance->abort(); set_active(true); } diff --git a/bt/tasks/blackboard/bt_check_var.cpp b/bt/tasks/blackboard/bt_check_var.cpp index 14962dc..98f092e 100644 --- a/bt/tasks/blackboard/bt_check_var.cpp +++ b/bt/tasks/blackboard/bt_check_var.cpp @@ -24,8 +24,9 @@ void BTCheckVar::set_check_type(LimboUtility::CheckType p_check_type) { void BTCheckVar::set_value(const Ref &p_value) { value = p_value; emit_changed(); - if (Engine::get_singleton()->is_editor_hint() && value.is_valid()) { - value->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed))); + if (Engine::get_singleton()->is_editor_hint() && value.is_valid() && + !value->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { + value->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed)); } } diff --git a/bt/tasks/blackboard/bt_set_var.cpp b/bt/tasks/blackboard/bt_set_var.cpp index 4b44d1d..9b6bc77 100644 --- a/bt/tasks/blackboard/bt_set_var.cpp +++ b/bt/tasks/blackboard/bt_set_var.cpp @@ -48,8 +48,9 @@ void BTSetVar::set_variable(const StringName &p_variable) { void BTSetVar::set_value(const Ref &p_value) { value = p_value; emit_changed(); - if (Engine::get_singleton()->is_editor_hint() && value.is_valid()) { - value->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed))); + if (Engine::get_singleton()->is_editor_hint() && value.is_valid() && + !value->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { + value->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed)); } } diff --git a/bt/tasks/bt_task.cpp b/bt/tasks/bt_task.cpp index 818999b..b4526ab 100644 --- a/bt/tasks/bt_task.cpp +++ b/bt/tasks/bt_task.cpp @@ -377,16 +377,21 @@ void BTTask::print_tree(int p_initial_tabs) { } } -#ifdef TOOLS_ENABLED - Ref BTTask::editor_get_behavior_tree() { +#ifdef TOOLS_ENABLED BTTask *task = this; while (task->data.behavior_tree_id.is_null() && task->get_parent().is_valid()) { task = task->data.parent; } return Object::cast_to(ObjectDB::get_instance(task->data.behavior_tree_id)); +#else + ERR_PRINT("BTTask::editor_get_behavior_tree: Not available in release builds."); + return Ref(); +#endif } +#ifdef TOOLS_ENABLED + void BTTask::editor_set_behavior_tree(const Ref &p_bt) { data.behavior_tree_id = p_bt->get_instance_id(); } @@ -414,9 +419,7 @@ void BTTask::_bind_methods() { ClassDB::bind_method(D_METHOD("print_tree", "initial_tabs"), &BTTask::print_tree, Variant(0)); ClassDB::bind_method(D_METHOD("get_task_name"), &BTTask::get_task_name); ClassDB::bind_method(D_METHOD("abort"), &BTTask::abort); -#ifdef TOOLS_ENABLED ClassDB::bind_method(D_METHOD("editor_get_behavior_tree"), &BTTask::editor_get_behavior_tree); -#endif // TOOLS_ENABLED // Properties, setters and getters. ClassDB::bind_method(D_METHOD("get_agent"), &BTTask::get_agent); diff --git a/bt/tasks/bt_task.h b/bt/tasks/bt_task.h index b571c27..37e9449 100644 --- a/bt/tasks/bt_task.h +++ b/bt/tasks/bt_task.h @@ -167,8 +167,8 @@ public: void print_tree(int p_initial_tabs = 0); -#ifdef TOOLS_ENABLED Ref editor_get_behavior_tree(); +#ifdef TOOLS_ENABLED void editor_set_behavior_tree(const Ref &p_bt); #endif diff --git a/bt/tasks/scene/bt_await_animation.cpp b/bt/tasks/scene/bt_await_animation.cpp index a511cdf..a2fe9cb 100644 --- a/bt/tasks/scene/bt_await_animation.cpp +++ b/bt/tasks/scene/bt_await_animation.cpp @@ -16,8 +16,9 @@ void BTAwaitAnimation::set_animation_player(Ref p_animation_player) { animation_player_param = p_animation_player; emit_changed(); - if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid()) { - animation_player_param->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed))); + if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid() && + !animation_player_param->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { + animation_player_param->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed)); } } diff --git a/bt/tasks/scene/bt_check_agent_property.cpp b/bt/tasks/scene/bt_check_agent_property.cpp index 7ac0c4d..8d64d6d 100644 --- a/bt/tasks/scene/bt_check_agent_property.cpp +++ b/bt/tasks/scene/bt_check_agent_property.cpp @@ -24,8 +24,9 @@ void BTCheckAgentProperty::set_check_type(LimboUtility::CheckType p_check_type) void BTCheckAgentProperty::set_value(Ref p_value) { value = p_value; emit_changed(); - if (Engine::get_singleton()->is_editor_hint() && value.is_valid()) { - value->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed))); + if (Engine::get_singleton()->is_editor_hint() && value.is_valid() && + !value->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { + value->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed)); } } diff --git a/bt/tasks/scene/bt_pause_animation.cpp b/bt/tasks/scene/bt_pause_animation.cpp index 4f1dd3c..71f88f1 100644 --- a/bt/tasks/scene/bt_pause_animation.cpp +++ b/bt/tasks/scene/bt_pause_animation.cpp @@ -16,8 +16,9 @@ void BTPauseAnimation::set_animation_player(Ref p_animation_player) { animation_player_param = p_animation_player; emit_changed(); - if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid()) { - animation_player_param->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed))); + if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid() && + !animation_player_param->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { + animation_player_param->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed)); } } diff --git a/bt/tasks/scene/bt_play_animation.cpp b/bt/tasks/scene/bt_play_animation.cpp index f373e5f..c021401 100644 --- a/bt/tasks/scene/bt_play_animation.cpp +++ b/bt/tasks/scene/bt_play_animation.cpp @@ -16,8 +16,9 @@ void BTPlayAnimation::set_animation_player(Ref p_animation_player) { animation_player_param = p_animation_player; emit_changed(); - if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid()) { - animation_player_param->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed))); + if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid() && + !animation_player_param->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { + animation_player_param->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed)); } } diff --git a/bt/tasks/scene/bt_set_agent_property.cpp b/bt/tasks/scene/bt_set_agent_property.cpp index 600e138..a182391 100644 --- a/bt/tasks/scene/bt_set_agent_property.cpp +++ b/bt/tasks/scene/bt_set_agent_property.cpp @@ -19,8 +19,9 @@ void BTSetAgentProperty::set_property(StringName p_prop) { void BTSetAgentProperty::set_value(Ref p_value) { value = p_value; emit_changed(); - if (Engine::get_singleton()->is_editor_hint() && value.is_valid()) { - value->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed))); + if (Engine::get_singleton()->is_editor_hint() && value.is_valid() && + !value->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { + value->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed)); } } diff --git a/bt/tasks/scene/bt_stop_animation.cpp b/bt/tasks/scene/bt_stop_animation.cpp index 0bc247b..d64e079 100644 --- a/bt/tasks/scene/bt_stop_animation.cpp +++ b/bt/tasks/scene/bt_stop_animation.cpp @@ -16,8 +16,9 @@ void BTStopAnimation::set_animation_player(Ref p_animation_player) { animation_player_param = p_animation_player; emit_changed(); - if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid()) { - animation_player_param->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed))); + if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid() && + !animation_player_param->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { + animation_player_param->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed)); } } diff --git a/bt/tasks/utility/bt_call_method.cpp b/bt/tasks/utility/bt_call_method.cpp index f559d2e..0b46804 100644 --- a/bt/tasks/utility/bt_call_method.cpp +++ b/bt/tasks/utility/bt_call_method.cpp @@ -28,8 +28,9 @@ void BTCallMethod::set_method(const StringName &p_method_name) { void BTCallMethod::set_node_param(const Ref &p_object) { node_param = p_object; emit_changed(); - if (Engine::get_singleton()->is_editor_hint() && node_param.is_valid()) { - node_param->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed))); + if (Engine::get_singleton()->is_editor_hint() && node_param.is_valid() && + !node_param->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { + node_param->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed)); } } diff --git a/bt/tasks/utility/bt_evaluate_expression.cpp b/bt/tasks/utility/bt_evaluate_expression.cpp index b6ab59b..33e453e 100644 --- a/bt/tasks/utility/bt_evaluate_expression.cpp +++ b/bt/tasks/utility/bt_evaluate_expression.cpp @@ -29,8 +29,9 @@ void BTEvaluateExpression::set_expression_string(const String &p_expression_stri void BTEvaluateExpression::set_node_param(Ref p_object) { node_param = p_object; emit_changed(); - if (Engine::get_singleton()->is_editor_hint() && node_param.is_valid()) { - node_param->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed))); + if (Engine::get_singleton()->is_editor_hint() && node_param.is_valid() && + !node_param->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { + node_param->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed)); } } diff --git a/editor/action_banner.cpp b/editor/action_banner.cpp index b3764a1..199400e 100644 --- a/editor/action_banner.cpp +++ b/editor/action_banner.cpp @@ -41,7 +41,7 @@ void ActionBanner::close() { void ActionBanner::add_action(const String &p_name, const Callable &p_action, bool p_auto_close) { Button *action_btn = memnew(Button); action_btn->set_text(p_name); - action_btn->connect(LW_NAME(pressed), callable_mp(this, &ActionBanner::_execute_action).bind(p_action, p_auto_close)); + action_btn->connect(LW_NAME(pressed), callable_mp(this, &ActionBanner::_execute_action).bind(p_action, p_auto_close), CONNECT_DEFERRED); hbox->add_child(action_btn); } diff --git a/editor/limbo_ai_editor_plugin.cpp b/editor/limbo_ai_editor_plugin.cpp index 467af76..50a271b 100644 --- a/editor/limbo_ai_editor_plugin.cpp +++ b/editor/limbo_ai_editor_plugin.cpp @@ -220,6 +220,8 @@ void LimboAIEditor::_load_bt(String p_path) { void LimboAIEditor::_disable_editing() { task_tree->unload(); task_palette->hide(); + task_tree->hide(); + usage_hint->show(); } void LimboAIEditor::edit_bt(Ref p_behavior_tree, bool p_force_refresh) { @@ -234,14 +236,9 @@ void LimboAIEditor::edit_bt(Ref p_behavior_tree, bool p_force_refr p_behavior_tree->notify_property_list_changed(); #endif // LIMBOAI_MODULE - if (task_tree->get_bt().is_valid() && - task_tree->get_bt()->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty).bind(true))) { - task_tree->get_bt()->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty).bind(true)); - } - task_tree->load_bt(p_behavior_tree); - if (task_tree->get_bt().is_valid()) { + if (task_tree->get_bt().is_valid() && !task_tree->get_bt()->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty))) { task_tree->get_bt()->connect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty).bind(true)); } @@ -888,6 +885,10 @@ void LimboAIEditor::_on_resources_reload(const PackedStringArray &p_resources) { #endif } +void LimboAIEditor::_on_new_script_pressed() { + SCRIPT_EDITOR()->open_script_create_dialog("BTAction", String(GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_1")).path_join("new_task")); +} + void LimboAIEditor::_task_type_selected(const String &p_class_or_path) { change_type_popup->hide(); @@ -958,6 +959,10 @@ void LimboAIEditor::_tab_clicked(int p_tab) { void LimboAIEditor::_tab_closed(int p_tab) { ERR_FAIL_INDEX(p_tab, history.size()); + Ref history_bt = history[p_tab]; + if (history_bt.is_valid() && history_bt->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty))) { + history_bt->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty)); + } history.remove_at(p_tab); idx_history = MIN(idx_history, history.size() - 1); if (idx_history < 0) { @@ -1296,9 +1301,11 @@ void LimboAIEditor::_notification(int p_what) { cf->set_value("bt_editor", "bteditor_hsplit", hsc->get_split_offset()); cf->save(conf_path); - if (task_tree->get_bt().is_valid() && - task_tree->get_bt()->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty).bind(true))) { - task_tree->get_bt()->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty).bind(true)); + task_tree->unload(); + for (int i = 0; i < history.size(); i++) { + if (history[i]->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty))) { + history[i]->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty)); + } } } break; case NOTIFICATION_READY: { @@ -1329,7 +1336,7 @@ void LimboAIEditor::_notification(int p_what) { disk_changed->connect("confirmed", callable_mp(this, &LimboAIEditor::_reload_modified)); disk_changed->connect("custom_action", callable_mp(this, &LimboAIEditor::_resave_modified)); rename_dialog->connect("confirmed", callable_mp(this, &LimboAIEditor::_rename_task_confirmed)); - new_script_btn->connect(LW_NAME(pressed), callable_mp(SCRIPT_EDITOR(), &ScriptEditor::open_script_create_dialog).bind("BTAction", String(GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_1")).path_join("new_task"))); + new_script_btn->connect(LW_NAME(pressed), callable_mp(this, &LimboAIEditor::_on_new_script_pressed)); tab_bar->connect("tab_clicked", callable_mp(this, &LimboAIEditor::_tab_clicked)); tab_bar->connect("active_tab_rearranged", callable_mp(this, &LimboAIEditor::_move_active_tab)); tab_bar->connect("tab_close_pressed", callable_mp(this, &LimboAIEditor::_tab_closed)); diff --git a/editor/limbo_ai_editor_plugin.h b/editor/limbo_ai_editor_plugin.h index 03e74ec..2e350bf 100644 --- a/editor/limbo_ai_editor_plugin.h +++ b/editor/limbo_ai_editor_plugin.h @@ -227,6 +227,7 @@ private: void _on_history_forward(); void _on_task_dragged(Ref p_task, Ref p_to_task, int p_type); void _on_resources_reload(const PackedStringArray &p_resources); + void _on_new_script_pressed(); void _task_type_selected(const String &p_class_or_path); void _copy_version_info(); diff --git a/hsm/limbo_hsm.cpp b/hsm/limbo_hsm.cpp index 71c81aa..6288cfe 100644 --- a/hsm/limbo_hsm.cpp +++ b/hsm/limbo_hsm.cpp @@ -51,11 +51,13 @@ void LimboHSM::_change_state(LimboState *p_state) { if (active_state) { active_state->_exit(); + active_state->set_process_input(false); previous_active = active_state; } active_state = p_state; active_state->_enter(); + active_state->set_process_input(true); emit_signal(LimboStringNames::get_singleton()->active_state_changed, active_state, previous_active); }