From bd7936e3573647865b7ce31f9793776219370677 Mon Sep 17 00:00:00 2001 From: Sara Date: Wed, 18 Mar 2026 15:06:31 +0100 Subject: [PATCH 1/3] feat: BehaviourInvertResult --- decorator_nodes.cpp | 40 ++++++++++++++++++++++++++++++++++++++-- decorator_nodes.h | 13 ++++++++++++- register_types.cpp | 1 + 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/decorator_nodes.cpp b/decorator_nodes.cpp index 00bb62d881..b46b41b1af 100644 --- a/decorator_nodes.cpp +++ b/decorator_nodes.cpp @@ -2,8 +2,6 @@ #include "behaviour_nodes/behaviour_node.h" #include "core/variant/variant.h" -void BehaviourAlwaysSuccess::_bind_methods() {} - PackedStringArray BehaviourAlwaysSuccess::get_configuration_warnings() const { PackedStringArray warnings{ super_type::get_configuration_warnings() }; if (get_child_behaviours().size() != 1) { @@ -28,3 +26,41 @@ BehaviourNode *BehaviourAlwaysSuccess::get_next() { ? get_child_behaviours().get(0) : cast_to(get_parent()); } + +PackedStringArray BehaviourInvertResult::get_configuration_warnings() const { + PackedStringArray warnings{ super_type::get_configuration_warnings() }; + if (get_child_behaviours().size() != 1) { + warnings.push_back("BehaviourInvertResult should have exactly one child behaviour"); + } + return warnings; +} + +void BehaviourInvertResult::enter() { + set_status(Running); +} + +void BehaviourInvertResult::execute() { + if (get_child_behaviours().is_empty()) { + set_status(Fail); + ERR_FAIL_EDMSG("BehaviourInvertResult executed with no children"); + } else if (get_status() == Running) { + Status child_status{ get_child_behaviours().get(0)->get_status() }; + switch (child_status) { + case Fail: + set_status(Success); + return; + case Success: + set_status(Fail); + return; + case Running: + set_status(Running); + return; + } + } +} + +BehaviourNode *BehaviourInvertResult::get_next() { + return get_status() == Running + ? get_child_behaviours().get(0) + : cast_to(get_parent()); +} diff --git a/decorator_nodes.h b/decorator_nodes.h index fc95249a8d..96ac7f7ec7 100644 --- a/decorator_nodes.h +++ b/decorator_nodes.h @@ -6,10 +6,21 @@ class BehaviourAlwaysSuccess : public BehaviourComposite { GDCLASS(BehaviourAlwaysSuccess, BehaviourComposite); - static void _bind_methods(); + static void _bind_methods() {} public: PackedStringArray get_configuration_warnings() const override; void execute() override; BehaviourNode *get_next() override; }; + +class BehaviourInvertResult : public BehaviourComposite { + GDCLASS(BehaviourInvertResult, BehaviourComposite); + static void _bind_methods() {} + +public: + PackedStringArray get_configuration_warnings() const override; + void enter() override; + void execute() override; + BehaviourNode *get_next() override; +}; diff --git a/register_types.cpp b/register_types.cpp index ffb0aea7e9..cea03f315f 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -18,6 +18,7 @@ void initialize_behaviour_nodes_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); + ClassDB::register_class(); } void uninitialize_behaviour_nodes_module(ModuleInitializationLevel p_level) { From fef1e3f8bde2e751d3e0c684c286d7c372bddd1c Mon Sep 17 00:00:00 2001 From: Sara Date: Wed, 18 Mar 2026 15:06:58 +0100 Subject: [PATCH 2/3] fix: RepeatUntilFail checks child status after init --- control_nodes.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/control_nodes.cpp b/control_nodes.cpp index a8eefbd7ea..15864a27e9 100644 --- a/control_nodes.cpp +++ b/control_nodes.cpp @@ -107,10 +107,12 @@ void BehaviourRepeatUntilFail::execute() { if (get_child_behaviours().is_empty()) { set_status(Fail); ERR_FAIL_EDMSG("BehaviourRepeatUntilFail execution with no child"); - } else { + } else if (get_status() == Running) { set_status(get_child_behaviours().get(0)->get_status() == Fail ? Success : Running); + } else { + set_status(Running); } } From 427c0a7ec8511d3dbf1e0bfa3667413d7b8f568d Mon Sep 17 00:00:00 2001 From: Sara Date: Wed, 18 Mar 2026 15:07:29 +0100 Subject: [PATCH 3/3] fix: behaviourtree ends frame when root is hit --- behaviour_node.h | 6 ++++-- behaviour_tree.cpp | 8 +++++--- behaviour_tree.h | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/behaviour_node.h b/behaviour_node.h index cf865e4b74..715db02584 100644 --- a/behaviour_node.h +++ b/behaviour_node.h @@ -14,15 +14,17 @@ protected: void _notification(int what); public: - virtual void enter() {} virtual void execute() {} + virtual void enter() { + set_status(Fail); + } virtual void exit() {} virtual BehaviourNode *get_next() { return this; } private: class BehaviourTree *behaviour_tree{ nullptr }; BehaviourNode *parent{ nullptr }; - Status status{}; + Status status{ Fail }; bool leaf{ false }; public: diff --git a/behaviour_tree.cpp b/behaviour_tree.cpp index 2aed80a598..7268f749cf 100644 --- a/behaviour_tree.cpp +++ b/behaviour_tree.cpp @@ -1,6 +1,7 @@ #include "behaviour_tree.h" #include "behaviour_nodes/behaviour_composite.h" #include "behaviour_nodes/behaviour_node.h" +#include "behaviour_nodes/control_nodes.h" #include "core/config/engine.h" #include "core/variant/typed_array.h" @@ -21,11 +22,12 @@ void BehaviourTree::_notification(int what) { return; case NOTIFICATION_READY: for (Variant var : get_children()) { - if ((this->current = cast_to(var))) { + if ((this->root = cast_to(var))) { + this->current = this->root; break; } } - ERR_FAIL_COND_EDMSG(this->current == nullptr, "No valid BehaviourNode in BehaviourTree"); + ERR_FAIL_COND_EDMSG(this->root == nullptr, "No valid BehaviourNode in BehaviourTree"); set_process(true); return; case NOTIFICATION_PROCESS: @@ -47,6 +49,6 @@ bool BehaviourTree::execute_next() { next->enter(); } this->current = next; - return cast_to(this->current) || this->current->get_status() == BehaviourNode::Fail; + return this->current != this->root && (cast_to(this->current) || this->current->get_status() != BehaviourNode::Running); } } diff --git a/behaviour_tree.h b/behaviour_tree.h index f9593c15c4..66d39ede2e 100644 --- a/behaviour_tree.h +++ b/behaviour_tree.h @@ -15,4 +15,5 @@ public: private: class BehaviourNode *current{ nullptr }; + class BehaviourNode *root{ nullptr }; };