Compare commits

...

10 Commits

Author SHA1 Message Date
Serhii Snitsaruk 8a23496265
Bump version to 1.1.1 2024-08-06 21:41:09 +02:00
Serhii Snitsaruk 820663099d Merge branch “cherrypicks-4.2” 2024-08-06 21:36:10 +02:00
Serhii Snitsaruk ad821c4514 Fix C# exports print errors due to missing ClassDB binding 2024-08-06 19:58:05 +02:00
Serhii Snitsaruk 5a58eef4f1 Fix error if `changed` signal is already connected to a BBParam in a bunch of tasks 2024-08-06 19:20:25 +02:00
Serhii Snitsaruk 997f9572e2 Fix process_input is not enabled in active substate 2024-08-06 19:18:52 +02:00
Serhii Snitsaruk 01ae6a46de Fix BTPlayer.restart() crash 2024-08-06 19:17:41 +02:00
yds b5602169d4 Fix new task script path don't update after changing the setting 2024-08-06 19:16:48 +02:00
Serhii Snitsaruk d999e33efa Defer banner actions to not crash upon restart 2024-08-06 19:15:10 +02:00
Serhii Snitsaruk 9da78e52e7 Fix BlackboardPlan arrays shouldn't be shared between instanced agents
Variable values are deep copied now using Variant.duplicate(true). Note that currently it doesn't duplicate objects.
2024-08-06 19:14:42 +02:00
Serhii Snitsaruk e6d1e0d076 Fix connected signal error when loading BT for the second time 2024-08-06 19:12:54 +02:00
21 changed files with 70 additions and 42 deletions

View File

@ -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;

View File

@ -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; }

View File

@ -391,7 +391,7 @@ void BlackboardPlan::sync_with_base_plan() {
inline void bb_add_var_dup_with_prefetch(const Ref<Blackboard> &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<Blackboard> &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));
}
}

View File

@ -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);
}

View File

@ -24,8 +24,9 @@ void BTCheckVar::set_check_type(LimboUtility::CheckType p_check_type) {
void BTCheckVar::set_value(const Ref<BBVariant> &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));
}
}

View File

@ -48,8 +48,9 @@ void BTSetVar::set_variable(const StringName &p_variable) {
void BTSetVar::set_value(const Ref<BBVariant> &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));
}
}

View File

@ -377,16 +377,21 @@ void BTTask::print_tree(int p_initial_tabs) {
}
}
#ifdef TOOLS_ENABLED
Ref<BehaviorTree> 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<BehaviorTree>(ObjectDB::get_instance(task->data.behavior_tree_id));
#else
ERR_PRINT("BTTask::editor_get_behavior_tree: Not available in release builds.");
return Ref<BehaviorTree>();
#endif
}
#ifdef TOOLS_ENABLED
void BTTask::editor_set_behavior_tree(const Ref<BehaviorTree> &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);

View File

@ -167,8 +167,8 @@ public:
void print_tree(int p_initial_tabs = 0);
#ifdef TOOLS_ENABLED
Ref<BehaviorTree> editor_get_behavior_tree();
#ifdef TOOLS_ENABLED
void editor_set_behavior_tree(const Ref<BehaviorTree> &p_bt);
#endif

View File

@ -16,8 +16,9 @@
void BTAwaitAnimation::set_animation_player(Ref<BBNode> 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));
}
}

View File

@ -24,8 +24,9 @@ void BTCheckAgentProperty::set_check_type(LimboUtility::CheckType p_check_type)
void BTCheckAgentProperty::set_value(Ref<BBVariant> 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));
}
}

View File

@ -16,8 +16,9 @@
void BTPauseAnimation::set_animation_player(Ref<BBNode> 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));
}
}

View File

@ -16,8 +16,9 @@
void BTPlayAnimation::set_animation_player(Ref<BBNode> 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));
}
}

View File

@ -19,8 +19,9 @@ void BTSetAgentProperty::set_property(StringName p_prop) {
void BTSetAgentProperty::set_value(Ref<BBVariant> 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));
}
}

View File

@ -16,8 +16,9 @@
void BTStopAnimation::set_animation_player(Ref<BBNode> 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));
}
}

View File

@ -28,8 +28,9 @@ void BTCallMethod::set_method(const StringName &p_method_name) {
void BTCallMethod::set_node_param(const Ref<BBNode> &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));
}
}

View File

@ -29,8 +29,9 @@ void BTEvaluateExpression::set_expression_string(const String &p_expression_stri
void BTEvaluateExpression::set_node_param(Ref<BBNode> 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));
}
}

View File

@ -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);
}

View File

@ -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<BehaviorTree> p_behavior_tree, bool p_force_refresh) {
@ -234,14 +236,9 @@ void LimboAIEditor::edit_bt(Ref<BehaviorTree> 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<BehaviorTree> 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));

View File

@ -227,6 +227,7 @@ private:
void _on_history_forward();
void _on_task_dragged(Ref<BTTask> p_task, Ref<BTTask> 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();

View File

@ -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);
}

View File

@ -2,7 +2,7 @@
major = 1
minor = 1
patch = 0
patch = 1
status = ""
doc_branch = "v1.1.0"