Compare commits
7 Commits
Author | SHA1 | Date |
---|---|---|
|
d3d32953f7 | |
|
a5c318bbf2 | |
|
576692294b | |
|
65a1a3d46b | |
|
5ac6cda415 | |
|
fd1ac1cbb8 | |
|
644c589705 |
|
@ -11,15 +11,6 @@
|
||||||
|
|
||||||
#include "bb_node.h"
|
#include "bb_node.h"
|
||||||
|
|
||||||
#ifdef LIMBOAI_MODULE
|
|
||||||
#include "core/error/error_macros.h"
|
|
||||||
#include "scene/main/node.h"
|
|
||||||
#endif // LIMBOAI_MODULE
|
|
||||||
|
|
||||||
#ifdef LIMBOAI_GDEXTENSION
|
|
||||||
#include <godot_cpp/classes/node.hpp>
|
|
||||||
#endif // LIMBOAI_GDEXTENSION
|
|
||||||
|
|
||||||
Variant BBNode::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
|
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_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_NULL_V_MSG(p_blackboard, Variant(), "BBNode: get_value() failed - blackboard is null.");
|
||||||
|
@ -33,13 +24,10 @@ Variant BBNode::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboar
|
||||||
|
|
||||||
if (val.get_type() == Variant::NODE_PATH) {
|
if (val.get_type() == Variant::NODE_PATH) {
|
||||||
return p_scene_root->get_node_or_null(val);
|
return p_scene_root->get_node_or_null(val);
|
||||||
|
} else if (val.get_type() == Variant::OBJECT || val.get_type() == Variant::NIL) {
|
||||||
|
return val;
|
||||||
} else {
|
} else {
|
||||||
Object *obj = val;
|
WARN_PRINT("BBNode: Unexpected variant type: " + Variant::get_type_name(val.get_type()) + ". Returning default value.");
|
||||||
if (unlikely(obj == nullptr && val.get_type() != Variant::NIL)) {
|
|
||||||
WARN_PRINT("BBNode: Unexpected variant type of a blackboard variable.");
|
|
||||||
return p_default;
|
return p_default;
|
||||||
} else {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,33 +387,16 @@ void BlackboardPlan::sync_with_base_plan() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a variable duplicate to the blackboard, optionally with NodePath prefetch.
|
Ref<Blackboard> BlackboardPlan::create_blackboard(Node *p_prefetch_root, const Ref<Blackboard> &p_parent_scope, Node *p_prefetch_root_for_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) {
|
ERR_FAIL_COND_V(p_prefetch_root == nullptr && prefetch_nodepath_vars, memnew(Blackboard));
|
||||||
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(true);
|
|
||||||
if (n != nullptr) {
|
|
||||||
var.set_value(n);
|
|
||||||
} else {
|
|
||||||
ERR_PRINT(vformat("BlackboardPlan: Prefetch failed for variable $%s with value: %s", p_name, p_var.get_value()));
|
|
||||||
var.set_value(Variant());
|
|
||||||
}
|
|
||||||
p_blackboard->assign_var(p_name, var);
|
|
||||||
} else {
|
|
||||||
p_blackboard->assign_var(p_name, p_var.duplicate(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<Blackboard> BlackboardPlan::create_blackboard(Node *p_node, const Ref<Blackboard> &p_parent_scope) {
|
|
||||||
ERR_FAIL_COND_V(p_node == nullptr && prefetch_nodepath_vars, memnew(Blackboard));
|
|
||||||
Ref<Blackboard> bb = memnew(Blackboard);
|
Ref<Blackboard> bb = memnew(Blackboard);
|
||||||
bb->set_parent(p_parent_scope);
|
bb->set_parent(p_parent_scope);
|
||||||
populate_blackboard(bb, true, p_node);
|
populate_blackboard(bb, true, p_prefetch_root, p_prefetch_root_for_base_plan);
|
||||||
return bb;
|
return bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_node) {
|
void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_prefetch_root, Node *p_prefetch_root_for_base_plan) {
|
||||||
ERR_FAIL_COND(p_node == nullptr && prefetch_nodepath_vars);
|
ERR_FAIL_COND(p_prefetch_root == nullptr && prefetch_nodepath_vars);
|
||||||
ERR_FAIL_COND(p_blackboard.is_null());
|
ERR_FAIL_COND(p_blackboard.is_null());
|
||||||
for (const Pair<StringName, BBVariable> &p : var_list) {
|
for (const Pair<StringName, BBVariable> &p : var_list) {
|
||||||
if (p_blackboard->has_local_var(p.first) && !overwrite) {
|
if (p_blackboard->has_local_var(p.first) && !overwrite) {
|
||||||
|
@ -429,7 +412,21 @@ void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bo
|
||||||
}
|
}
|
||||||
bool has_mapping = parent_scope_mapping.has(p.first);
|
bool has_mapping = parent_scope_mapping.has(p.first);
|
||||||
bool do_prefetch = !has_mapping && prefetch_nodepath_vars;
|
bool do_prefetch = !has_mapping && prefetch_nodepath_vars;
|
||||||
bb_add_var_dup_with_prefetch(p_blackboard, p.first, p.second, do_prefetch, p_node);
|
|
||||||
|
// Add a variable duplicate to the blackboard, optionally with NodePath prefetch.
|
||||||
|
BBVariable var = p.second.duplicate(true);
|
||||||
|
if (unlikely(do_prefetch && p.second.get_type() == Variant::NODE_PATH)) {
|
||||||
|
Node *prefetch_root = !p_prefetch_root_for_base_plan || !is_derived() || is_derived_var_changed(p.first) ? p_prefetch_root : p_prefetch_root_for_base_plan;
|
||||||
|
Node *n = prefetch_root->get_node_or_null(p.second.get_value());
|
||||||
|
if (n != nullptr) {
|
||||||
|
var.set_value(n);
|
||||||
|
} else {
|
||||||
|
ERR_PRINT(vformat("BlackboardPlan: Prefetch failed for variable $%s with value: %s", p.first, p.second.get_value()));
|
||||||
|
var.set_value(Variant());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p_blackboard->assign_var(p.first, var);
|
||||||
|
|
||||||
if (has_mapping) {
|
if (has_mapping) {
|
||||||
StringName target_var = parent_scope_mapping[p.first];
|
StringName target_var = parent_scope_mapping[p.first];
|
||||||
if (target_var != StringName()) {
|
if (target_var != StringName()) {
|
||||||
|
@ -450,8 +447,8 @@ void BlackboardPlan::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("sync_with_base_plan"), &BlackboardPlan::sync_with_base_plan);
|
ClassDB::bind_method(D_METHOD("sync_with_base_plan"), &BlackboardPlan::sync_with_base_plan);
|
||||||
ClassDB::bind_method(D_METHOD("set_parent_scope_plan_provider", "callable"), &BlackboardPlan::set_parent_scope_plan_provider);
|
ClassDB::bind_method(D_METHOD("set_parent_scope_plan_provider", "callable"), &BlackboardPlan::set_parent_scope_plan_provider);
|
||||||
ClassDB::bind_method(D_METHOD("get_parent_scope_plan_provider"), &BlackboardPlan::get_parent_scope_plan_provider);
|
ClassDB::bind_method(D_METHOD("get_parent_scope_plan_provider"), &BlackboardPlan::get_parent_scope_plan_provider);
|
||||||
ClassDB::bind_method(D_METHOD("create_blackboard", "node", "parent_scope"), &BlackboardPlan::create_blackboard, DEFVAL(Ref<Blackboard>()));
|
ClassDB::bind_method(D_METHOD("create_blackboard", "prefetch_root", "parent_scope", "prefetch_root_for_base_plan"), &BlackboardPlan::create_blackboard, DEFVAL(Ref<Blackboard>()), DEFVAL(Variant()));
|
||||||
ClassDB::bind_method(D_METHOD("populate_blackboard", "blackboard", "overwrite", "node"), &BlackboardPlan::populate_blackboard);
|
ClassDB::bind_method(D_METHOD("populate_blackboard", "blackboard", "overwrite", "prefetch_root", "prefetch_root_for_base_plan"), &BlackboardPlan::populate_blackboard, DEFVAL(Variant()));
|
||||||
|
|
||||||
// To avoid cluttering the member namespace, we do not export unnecessary properties in this class.
|
// To avoid cluttering the member namespace, we do not export unnecessary properties in this class.
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prefetch_nodepath_vars", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_prefetch_nodepath_vars", "is_prefetching_nodepath_vars");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prefetch_nodepath_vars", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_prefetch_nodepath_vars", "is_prefetching_nodepath_vars");
|
||||||
|
|
|
@ -88,9 +88,10 @@ public:
|
||||||
|
|
||||||
void sync_with_base_plan();
|
void sync_with_base_plan();
|
||||||
_FORCE_INLINE_ bool is_derived() const { return base.is_valid(); }
|
_FORCE_INLINE_ bool is_derived() const { return base.is_valid(); }
|
||||||
|
_FORCE_INLINE_ bool is_derived_var_changed(const StringName &p_name) const { return base.is_valid() && var_map.has(p_name) && var_map[p_name].is_value_changed(); }
|
||||||
|
|
||||||
Ref<Blackboard> create_blackboard(Node *p_agent, const Ref<Blackboard> &p_parent_scope = Ref<Blackboard>());
|
Ref<Blackboard> create_blackboard(Node *p_prefetch_root, const Ref<Blackboard> &p_parent_scope = Ref<Blackboard>(), Node *p_prefetch_root_for_base_plan = nullptr);
|
||||||
void populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_node);
|
void populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_prefetch_root, Node *p_prefetch_root_for_base_plan = nullptr);
|
||||||
|
|
||||||
BlackboardPlan();
|
BlackboardPlan();
|
||||||
};
|
};
|
||||||
|
|
|
@ -48,7 +48,7 @@ void BTPlayer::_load_tree() {
|
||||||
ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Initialization failed - behavior tree has no valid root task.");
|
ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Initialization failed - behavior tree has no valid root task.");
|
||||||
Node *agent = GET_NODE(this, agent_node);
|
Node *agent = GET_NODE(this, agent_node);
|
||||||
ERR_FAIL_NULL_MSG(agent, vformat("BTPlayer: Initialization failed - can't get agent with path '%s'.", agent_node));
|
ERR_FAIL_NULL_MSG(agent, vformat("BTPlayer: Initialization failed - can't get agent with path '%s'.", agent_node));
|
||||||
Node *scene_root = scene_root_hint ? scene_root_hint : get_owner();
|
Node *scene_root = _get_scene_root();
|
||||||
ERR_FAIL_COND_MSG(scene_root == nullptr,
|
ERR_FAIL_COND_MSG(scene_root == nullptr,
|
||||||
"BTPlayer: Initialization failed - unable to establish scene root. This is likely due to BTPlayer not being owned by a scene node. Check BTPlayer.set_scene_root_hint().");
|
"BTPlayer: Initialization failed - unable to establish scene root. This is likely due to BTPlayer not being owned by a scene node. Check BTPlayer.set_scene_root_hint().");
|
||||||
bt_instance = behavior_tree->instantiate(agent, blackboard, this, scene_root);
|
bt_instance = behavior_tree->instantiate(agent, blackboard, this, scene_root);
|
||||||
|
@ -184,7 +184,7 @@ void BTPlayer::_notification(int p_notification) {
|
||||||
}
|
}
|
||||||
if (blackboard_plan.is_valid()) {
|
if (blackboard_plan.is_valid()) {
|
||||||
// Don't overwrite existing blackboard values as they may be initialized from code.
|
// Don't overwrite existing blackboard values as they may be initialized from code.
|
||||||
blackboard_plan->populate_blackboard(blackboard, false, this);
|
blackboard_plan->populate_blackboard(blackboard, false, this, _get_scene_root());
|
||||||
}
|
}
|
||||||
if (behavior_tree.is_valid()) {
|
if (behavior_tree.is_valid()) {
|
||||||
_load_tree();
|
_load_tree();
|
||||||
|
|
|
@ -50,6 +50,7 @@ private:
|
||||||
|
|
||||||
void _load_tree();
|
void _load_tree();
|
||||||
void _update_blackboard_plan();
|
void _update_blackboard_plan();
|
||||||
|
_FORCE_INLINE_ Node *_get_scene_root() const { return scene_root_hint ? scene_root_hint : get_owner(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
|
@ -65,10 +65,14 @@ void BTState::_update_blackboard_plan() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node *BTState::_get_prefetch_root_for_base_plan() {
|
||||||
|
return _get_scene_root();
|
||||||
|
}
|
||||||
|
|
||||||
void BTState::_setup() {
|
void BTState::_setup() {
|
||||||
LimboState::_setup();
|
LimboState::_setup();
|
||||||
ERR_FAIL_COND_MSG(behavior_tree.is_null(), "BTState: BehaviorTree is not assigned.");
|
ERR_FAIL_COND_MSG(behavior_tree.is_null(), "BTState: BehaviorTree is not assigned.");
|
||||||
Node *scene_root = scene_root_hint ? scene_root_hint : get_owner();
|
Node *scene_root = _get_scene_root();
|
||||||
ERR_FAIL_NULL_MSG(scene_root, "BTState: Initialization failed - unable to establish scene root. This is likely due to BTState not being owned by a scene node. Check BTState.set_scene_root_hint().");
|
ERR_FAIL_NULL_MSG(scene_root, "BTState: Initialization failed - unable to establish scene root. This is likely due to BTState not being owned by a scene node. Check BTState.set_scene_root_hint().");
|
||||||
bt_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), this, scene_root);
|
bt_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), this, scene_root);
|
||||||
ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTState: Initialization failed - failed to instantiate behavior tree.");
|
ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTState: Initialization failed - failed to instantiate behavior tree.");
|
||||||
|
|
|
@ -28,6 +28,8 @@ private:
|
||||||
Node *scene_root_hint = nullptr;
|
Node *scene_root_hint = nullptr;
|
||||||
bool monitor_performance = false;
|
bool monitor_performance = false;
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Node *_get_scene_root() const { return scene_root_hint ? scene_root_hint : get_owner(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
|
@ -35,6 +37,7 @@ protected:
|
||||||
|
|
||||||
virtual bool _should_use_new_scope() const override { return true; }
|
virtual bool _should_use_new_scope() const override { return true; }
|
||||||
virtual void _update_blackboard_plan() override;
|
virtual void _update_blackboard_plan() override;
|
||||||
|
virtual Node *_get_prefetch_root_for_base_plan() override;
|
||||||
|
|
||||||
virtual void _setup() override;
|
virtual void _setup() override;
|
||||||
virtual void _exit() override;
|
virtual void _exit() override;
|
||||||
|
|
|
@ -43,12 +43,15 @@ void BTForEach::_enter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
BT::Status BTForEach::_tick(double p_delta) {
|
BT::Status BTForEach::_tick(double p_delta) {
|
||||||
ERR_FAIL_COND_V_MSG(get_child_count() == 0, FAILURE, "ForEach decorator has no child.");
|
ERR_FAIL_COND_V_MSG(get_child_count() == 0, FAILURE, "BTForEach: Decorator has no child.");
|
||||||
ERR_FAIL_COND_V_MSG(save_var == StringName(), FAILURE, "ForEach save variable is not set.");
|
ERR_FAIL_COND_V_MSG(save_var == StringName(), FAILURE, "BTForEach: Save variable is not set.");
|
||||||
ERR_FAIL_COND_V_MSG(array_var == StringName(), FAILURE, "ForEach array variable is not set.");
|
ERR_FAIL_COND_V_MSG(array_var == StringName(), FAILURE, "BTForEach: Array variable is not set.");
|
||||||
|
|
||||||
Array arr = get_blackboard()->get_var(array_var, Variant());
|
Array arr = get_blackboard()->get_var(array_var, Variant());
|
||||||
if (arr.size() == 0) {
|
if (current_idx >= arr.size()) {
|
||||||
|
if (current_idx != 0) {
|
||||||
|
WARN_PRINT("BTForEach: Array size changed during iteration.");
|
||||||
|
}
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
Variant elem = arr[current_idx];
|
Variant elem = arr[current_idx];
|
||||||
|
|
|
@ -46,7 +46,9 @@ String BTSetAgentProperty::_generate_name() {
|
||||||
return "SetAgentProperty ???";
|
return "SetAgentProperty ???";
|
||||||
}
|
}
|
||||||
|
|
||||||
return vformat("Set agent.%s = %s", property,
|
return vformat("Set agent.%s %s= %s",
|
||||||
|
property,
|
||||||
|
LimboUtility::get_singleton()->get_operation_string(operation),
|
||||||
value.is_valid() ? Variant(value) : Variant("???"));
|
value.is_valid() ? Variant(value) : Variant("???"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
<member name="blackboard_plan" type="BlackboardPlan" setter="set_blackboard_plan" getter="get_blackboard_plan">
|
<member name="blackboard_plan" type="BlackboardPlan" setter="set_blackboard_plan" getter="get_blackboard_plan">
|
||||||
Stores and manages variables that will be used in constructing new [Blackboard] instances.
|
Stores and manages variables that will be used in constructing new [Blackboard] instances.
|
||||||
</member>
|
</member>
|
||||||
<member name="monitor_performance" type="bool" setter="_set_monitor_performance" getter="_get_monitor_performance" default="false">
|
<member name="monitor_performance" type="bool" setter="set_monitor_performance" getter="get_monitor_performance" default="false">
|
||||||
If [code]true[/code], adds a performance monitor to "Debugger->Monitors" for each instance of this [BTPlayer] node.
|
If [code]true[/code], adds a performance monitor to "Debugger->Monitors" for each instance of this [BTPlayer] node.
|
||||||
</member>
|
</member>
|
||||||
<member name="update_mode" type="int" setter="set_update_mode" getter="get_update_mode" enum="BTPlayer.UpdateMode" default="1">
|
<member name="update_mode" type="int" setter="set_update_mode" getter="get_update_mode" enum="BTPlayer.UpdateMode" default="1">
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
<member name="failure_event" type="StringName" setter="set_failure_event" getter="get_failure_event" default="&"failure"">
|
<member name="failure_event" type="StringName" setter="set_failure_event" getter="get_failure_event" default="&"failure"">
|
||||||
HSM event that will be dispatched when the behavior tree results in [code]FAILURE[/code]. See [method LimboState.dispatch].
|
HSM event that will be dispatched when the behavior tree results in [code]FAILURE[/code]. See [method LimboState.dispatch].
|
||||||
</member>
|
</member>
|
||||||
<member name="monitor_performance" type="bool" setter="_set_monitor_performance" getter="_get_monitor_performance" default="false">
|
<member name="monitor_performance" type="bool" setter="set_monitor_performance" getter="get_monitor_performance" default="false">
|
||||||
If [code]true[/code], adds a performance monitor to "Debugger->Monitors" for each instance of this [BTState] node.
|
If [code]true[/code], adds a performance monitor to "Debugger->Monitors" for each instance of this [BTState] node.
|
||||||
</member>
|
</member>
|
||||||
<member name="success_event" type="StringName" setter="set_success_event" getter="get_success_event" default="&"success"">
|
<member name="success_event" type="StringName" setter="set_success_event" getter="get_success_event" default="&"success"">
|
||||||
|
|
|
@ -10,10 +10,11 @@
|
||||||
<methods>
|
<methods>
|
||||||
<method name="create_blackboard">
|
<method name="create_blackboard">
|
||||||
<return type="Blackboard" />
|
<return type="Blackboard" />
|
||||||
<param index="0" name="node" type="Node" />
|
<param index="0" name="prefetch_root" type="Node" />
|
||||||
<param index="1" name="parent_scope" type="Blackboard" default="null" />
|
<param index="1" name="parent_scope" type="Blackboard" default="null" />
|
||||||
|
<param index="2" name="prefetch_root_for_base_plan" type="Node" default="null" />
|
||||||
<description>
|
<description>
|
||||||
Constructs a new instance of a [Blackboard] using this plan. If [NodePath] prefetching is enabled, [param node] will be used to retrieve node instances for [NodePath] variables and substitute their values.
|
Constructs a new instance of a [Blackboard] using this plan. If [NodePath] prefetching is enabled, [param prefetch_root] will be used to retrieve node instances for [NodePath] variables and substitute their values.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="get_base_plan" qualifiers="const">
|
<method name="get_base_plan" qualifiers="const">
|
||||||
|
@ -38,9 +39,10 @@
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<param index="0" name="blackboard" type="Blackboard" />
|
<param index="0" name="blackboard" type="Blackboard" />
|
||||||
<param index="1" name="overwrite" type="bool" />
|
<param index="1" name="overwrite" type="bool" />
|
||||||
<param index="2" name="node" type="Node" />
|
<param index="2" name="prefetch_root" type="Node" />
|
||||||
|
<param index="3" name="prefetch_root_for_base_plan" type="Node" default="null" />
|
||||||
<description>
|
<description>
|
||||||
Populates [param blackboard] with the variables from this plan. If [param overwrite] is [code]true[/code], existing variables with the same names will be overwritten. If [NodePath] prefetching is enabled, [param node] will be used to retrieve node instances for [NodePath] variables and substitute their values.
|
Populates [param blackboard] with the variables from this plan. If [param overwrite] is [code]true[/code], existing variables with the same names will be overwritten. If [NodePath] prefetching is enabled, [param prefetch_root] will be used to retrieve node instances for [NodePath] variables and substitute their values.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="set_base_plan">
|
<method name="set_base_plan">
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<description>
|
<description>
|
||||||
Registers a [param handler] to be called when [param event] is dispatched. The handler function should have the following signature:
|
Registers a [param handler] to be called when [param event] is dispatched. The handler function should have the following signature:
|
||||||
[codeblock]
|
[codeblock]
|
||||||
func my_event_handler(cargo=null) -> bool:
|
func my_event_handler(cargo=null) -> bool:
|
||||||
[/codeblock]
|
[/codeblock]
|
||||||
If the handler returns [code]true[/code], the event will be consumed. Cargo is an optional parameter that can be passed to the handler. See also [method dispatch].
|
If the handler returns [code]true[/code], the event will be consumed. Cargo is an optional parameter that can be passed to the handler. See also [method dispatch].
|
||||||
</description>
|
</description>
|
||||||
|
|
|
@ -33,6 +33,10 @@ void LimboState::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
|
||||||
void LimboState::_update_blackboard_plan() {
|
void LimboState::_update_blackboard_plan() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node *LimboState::_get_prefetch_root_for_base_plan() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
Ref<BlackboardPlan> LimboState::_get_parent_scope_plan() const {
|
Ref<BlackboardPlan> LimboState::_get_parent_scope_plan() const {
|
||||||
BlackboardPlan *parent_plan = nullptr;
|
BlackboardPlan *parent_plan = nullptr;
|
||||||
const LimboState *state = this;
|
const LimboState *state = this;
|
||||||
|
@ -96,7 +100,7 @@ void LimboState::_initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard)
|
||||||
}
|
}
|
||||||
if (blackboard_plan.is_valid() && !blackboard_plan->is_empty()) {
|
if (blackboard_plan.is_valid() && !blackboard_plan->is_empty()) {
|
||||||
// Don't overwrite existing blackboard values as they may be initialized from code.
|
// Don't overwrite existing blackboard values as they may be initialized from code.
|
||||||
blackboard_plan->populate_blackboard(blackboard, false, this);
|
blackboard_plan->populate_blackboard(blackboard, false, this, _get_prefetch_root_for_base_plan());
|
||||||
}
|
}
|
||||||
|
|
||||||
_setup();
|
_setup();
|
||||||
|
|
|
@ -54,6 +54,7 @@ protected:
|
||||||
|
|
||||||
virtual bool _should_use_new_scope() const { return blackboard_plan.is_valid() || is_root(); }
|
virtual bool _should_use_new_scope() const { return blackboard_plan.is_valid() || is_root(); }
|
||||||
virtual void _update_blackboard_plan();
|
virtual void _update_blackboard_plan();
|
||||||
|
virtual Node *_get_prefetch_root_for_base_plan();
|
||||||
|
|
||||||
virtual void _setup();
|
virtual void _setup();
|
||||||
virtual void _enter();
|
virtual void _enter();
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
major = 1
|
major = 1
|
||||||
minor = 2
|
minor = 2
|
||||||
patch = 1
|
patch = 2
|
||||||
status = ""
|
status = ""
|
||||||
doc_branch = "v1.2.0"
|
doc_branch = "v1.2.2"
|
||||||
godot_cpp_ref = "godot-4.3-stable"
|
godot_cpp_ref = "godot-4.3-stable"
|
||||||
|
|
||||||
# Code that generates version header
|
# Code that generates version header
|
||||||
|
|
|
@ -96,6 +96,22 @@ TEST_CASE("[Modules][LimboAI] BTForEach") {
|
||||||
CHECK_ENTRIES_TICKS_EXITS(task, 3, 6, 3);
|
CHECK_ENTRIES_TICKS_EXITS(task, 3, 6, 3);
|
||||||
CHECK(blackboard->get_var("element", "wetgoop") == "mushroom");
|
CHECK(blackboard->get_var("element", "wetgoop") == "mushroom");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SUBCASE("Shouldn't crash if elements are removed during iteration") {
|
||||||
|
CHECK(fe->execute(0.01666) == BTTask::RUNNING);
|
||||||
|
CHECK(task->get_status() == BTTask::SUCCESS);
|
||||||
|
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1);
|
||||||
|
CHECK(blackboard->get_var("element", "wetgoop") == "apple");
|
||||||
|
|
||||||
|
arr.clear();
|
||||||
|
|
||||||
|
ERR_PRINT_OFF;
|
||||||
|
CHECK(fe->execute(0.01666) == BTTask::SUCCESS); // Returns SUCCESS and prints a warning without executing child task.
|
||||||
|
ERR_PRINT_ON;
|
||||||
|
CHECK(task->get_status() == BTTask::SUCCESS); // Same status.
|
||||||
|
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1); // Task is not re-executed as there is not enough elements to continue iteration.
|
||||||
|
CHECK(blackboard->get_var("element", "wetgoop") == "apple"); // Not changed.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} //namespace TestForEach
|
} //namespace TestForEach
|
||||||
|
|
Loading…
Reference in New Issue