2022-08-28 10:54:34 +00:00
|
|
|
/* bt_task.cpp */
|
|
|
|
|
|
|
|
#include "bt_task.h"
|
|
|
|
|
|
|
|
#include "core/class_db.h"
|
|
|
|
#include "core/object.h"
|
|
|
|
#include "core/script_language.h"
|
|
|
|
#include "core/variant.h"
|
|
|
|
#include "editor/editor_node.h"
|
2022-08-31 15:05:25 +00:00
|
|
|
#include "modules/limboai/limbo_string_names.h"
|
|
|
|
#include "modules/limboai/limbo_utility.h"
|
2022-08-28 10:54:34 +00:00
|
|
|
|
|
|
|
String BTTask::_generate_name() const {
|
|
|
|
if (get_script_instance()) {
|
|
|
|
if (get_script_instance()->has_method(LimboStringNames::get_singleton()->_generate_name)) {
|
|
|
|
return get_script_instance()->call(LimboStringNames::get_singleton()->_generate_name);
|
|
|
|
}
|
|
|
|
String name = get_script_instance()->get_script()->get_path();
|
|
|
|
if (!name.empty()) {
|
|
|
|
// Generate name based on script file
|
|
|
|
name = name.get_basename().get_file();
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
}
|
2022-08-28 20:58:30 +00:00
|
|
|
return get_class().trim_prefix("BT");
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Array BTTask::_get_children() const {
|
|
|
|
Array arr;
|
|
|
|
int num_children = get_child_count();
|
|
|
|
arr.resize(num_children);
|
|
|
|
for (int i = 0; i < num_children; i++) {
|
|
|
|
arr[i] = get_child(i).ptr();
|
|
|
|
}
|
|
|
|
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BTTask::_set_children(Array p_children) {
|
2022-08-31 15:05:25 +00:00
|
|
|
children.clear();
|
2022-08-28 10:54:34 +00:00
|
|
|
const int num_children = p_children.size();
|
2022-08-31 15:05:25 +00:00
|
|
|
children.resize(num_children);
|
2022-08-28 10:54:34 +00:00
|
|
|
for (int i = 0; i < num_children; i++) {
|
|
|
|
Variant task_var = p_children[i];
|
2022-09-01 22:20:37 +00:00
|
|
|
Ref<BTTask> task_ref = task_var;
|
|
|
|
task_ref->parent = this;
|
2022-08-31 15:05:25 +00:00
|
|
|
children.set(i, task_var);
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
String BTTask::get_task_name() const {
|
2022-08-31 15:05:25 +00:00
|
|
|
if (custom_name.empty()) {
|
2022-08-28 10:54:34 +00:00
|
|
|
return _generate_name();
|
|
|
|
}
|
2022-08-31 15:05:25 +00:00
|
|
|
return custom_name;
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ref<BTTask> BTTask::get_root() const {
|
|
|
|
const BTTask *task = this;
|
|
|
|
while (!task->is_root()) {
|
2022-08-31 15:05:25 +00:00
|
|
|
task = task->parent;
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
return Ref<BTTask>(task);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BTTask::set_custom_name(const String &p_name) {
|
2022-08-31 15:05:25 +00:00
|
|
|
if (custom_name != p_name) {
|
|
|
|
custom_name = p_name;
|
2022-08-28 10:54:34 +00:00
|
|
|
emit_changed();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void BTTask::initialize(Object *p_agent, Dictionary p_blackboard) {
|
2022-08-31 15:05:25 +00:00
|
|
|
agent = p_agent;
|
|
|
|
blackboard = p_blackboard;
|
|
|
|
for (int i = 0; i < children.size(); i++) {
|
2022-08-28 10:54:34 +00:00
|
|
|
get_child(i)->initialize(p_agent, p_blackboard);
|
|
|
|
}
|
|
|
|
if (get_script_instance() &&
|
|
|
|
get_script_instance()->has_method(LimboStringNames::get_singleton()->_setup)) {
|
|
|
|
get_script_instance()->call(LimboStringNames::get_singleton()->_setup);
|
|
|
|
} else {
|
|
|
|
_setup();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ref<BTTask> BTTask::clone() const {
|
|
|
|
Ref<BTTask> inst = duplicate(true);
|
2022-08-31 15:05:25 +00:00
|
|
|
inst->parent = nullptr;
|
2022-08-30 16:48:49 +00:00
|
|
|
CRASH_COND(inst->get_parent().is_valid());
|
2022-08-31 15:05:25 +00:00
|
|
|
for (int i = 0; i < children.size(); i++) {
|
2022-08-28 10:54:34 +00:00
|
|
|
Ref<BTTask> c = get_child(i)->clone();
|
2022-08-31 15:05:25 +00:00
|
|
|
c->parent = inst.ptr();
|
|
|
|
inst->children.set(i, c);
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
|
|
|
|
int BTTask::execute(float p_delta) {
|
2022-08-31 15:05:25 +00:00
|
|
|
if (status != RUNNING) {
|
2022-08-28 10:54:34 +00:00
|
|
|
if (get_script_instance() &&
|
|
|
|
// get_script_instance()->get_script()->is_valid() &&
|
|
|
|
get_script_instance()->has_method(LimboStringNames::get_singleton()->_enter)) {
|
|
|
|
get_script_instance()->call(LimboStringNames::get_singleton()->_enter);
|
|
|
|
} else {
|
|
|
|
_enter();
|
|
|
|
}
|
|
|
|
}
|
2022-08-31 15:05:25 +00:00
|
|
|
|
2022-08-28 10:54:34 +00:00
|
|
|
if (get_script_instance() &&
|
|
|
|
// get_script_instance()->get_script()->is_valid() &&
|
|
|
|
get_script_instance()->has_method(LimboStringNames::get_singleton()->_tick)) {
|
2022-08-31 15:05:25 +00:00
|
|
|
status = get_script_instance()->call(LimboStringNames::get_singleton()->_tick, Variant(p_delta));
|
2022-08-28 10:54:34 +00:00
|
|
|
} else {
|
2022-08-31 15:05:25 +00:00
|
|
|
status = _tick(p_delta);
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
2022-08-31 15:05:25 +00:00
|
|
|
if (status != RUNNING) {
|
2022-08-28 10:54:34 +00:00
|
|
|
if (get_script_instance() &&
|
|
|
|
// get_script_instance()->get_script()->is_valid() &&
|
|
|
|
get_script_instance()->has_method(LimboStringNames::get_singleton()->_exit)) {
|
|
|
|
get_script_instance()->call(LimboStringNames::get_singleton()->_exit);
|
|
|
|
} else {
|
|
|
|
_exit();
|
|
|
|
}
|
|
|
|
}
|
2022-08-31 15:05:25 +00:00
|
|
|
return status;
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BTTask::cancel() {
|
2022-08-31 15:05:25 +00:00
|
|
|
for (int i = 0; i < children.size(); i++) {
|
2022-08-28 10:54:34 +00:00
|
|
|
get_child(i)->cancel();
|
|
|
|
}
|
2022-08-31 15:05:25 +00:00
|
|
|
if (status == RUNNING) {
|
2022-08-28 10:54:34 +00:00
|
|
|
if (get_script_instance() &&
|
|
|
|
// get_script_instance()->get_script()->is_valid() &&
|
|
|
|
get_script_instance()->has_method(LimboStringNames::get_singleton()->_exit)) {
|
|
|
|
get_script_instance()->call(LimboStringNames::get_singleton()->_exit);
|
|
|
|
} else {
|
|
|
|
_exit();
|
|
|
|
}
|
|
|
|
}
|
2022-08-31 15:05:25 +00:00
|
|
|
status = FRESH;
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ref<BTTask> BTTask::get_child(int p_idx) const {
|
2022-08-31 15:05:25 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_idx, children.size(), nullptr);
|
|
|
|
return children.get(p_idx);
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int BTTask::get_child_count() const {
|
2022-08-31 15:05:25 +00:00
|
|
|
return children.size();
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BTTask::add_child(Ref<BTTask> p_child) {
|
2022-08-30 16:48:49 +00:00
|
|
|
ERR_FAIL_COND_MSG(p_child->get_parent().is_valid(), "p_child already has a parent!");
|
2022-08-31 15:05:25 +00:00
|
|
|
p_child->parent = this;
|
|
|
|
children.push_back(p_child);
|
2022-08-28 10:54:34 +00:00
|
|
|
emit_changed();
|
|
|
|
}
|
|
|
|
|
|
|
|
void BTTask::add_child_at_index(Ref<BTTask> p_child, int p_idx) {
|
2022-08-30 16:48:49 +00:00
|
|
|
ERR_FAIL_COND_MSG(p_child->get_parent().is_valid(), "p_child already has a parent!");
|
2022-08-31 15:05:25 +00:00
|
|
|
if (p_idx < 0 || p_idx > children.size()) {
|
|
|
|
p_idx = children.size();
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
2022-08-31 15:05:25 +00:00
|
|
|
children.insert(p_idx, p_child);
|
|
|
|
p_child->parent = this;
|
2022-08-28 10:54:34 +00:00
|
|
|
emit_changed();
|
|
|
|
}
|
|
|
|
|
|
|
|
void BTTask::remove_child(Ref<BTTask> p_child) {
|
2022-08-31 15:05:25 +00:00
|
|
|
int idx = children.find(p_child);
|
2022-08-28 10:54:34 +00:00
|
|
|
if (idx == -1) {
|
|
|
|
ERR_FAIL_MSG("p_child not found!");
|
|
|
|
} else {
|
2022-08-31 15:05:25 +00:00
|
|
|
children.remove(idx);
|
|
|
|
p_child->parent = nullptr;
|
2022-08-28 10:54:34 +00:00
|
|
|
emit_changed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BTTask::has_child(const Ref<BTTask> &p_child) const {
|
2022-08-31 15:05:25 +00:00
|
|
|
return children.find(p_child) != -1;
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int BTTask::get_child_index(const Ref<BTTask> &p_child) const {
|
2022-08-31 15:05:25 +00:00
|
|
|
return children.find(p_child);
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ref<BTTask> BTTask::next_sibling() const {
|
2022-08-31 15:05:25 +00:00
|
|
|
if (parent != nullptr) {
|
|
|
|
int idx = parent->get_child_index(Ref<BTTask>(this));
|
|
|
|
if (idx != -1 && parent->get_child_count() > (idx + 1)) {
|
|
|
|
return parent->get_child(idx + 1);
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return Ref<BTTask>();
|
|
|
|
}
|
|
|
|
|
|
|
|
String BTTask::get_configuration_warning() const {
|
|
|
|
return String();
|
|
|
|
}
|
|
|
|
|
|
|
|
Ref<Texture> BTTask::get_icon() const {
|
2022-09-01 22:20:37 +00:00
|
|
|
return EditorNode::get_singleton()->get_class_icon(get_class(), "Object");
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BTTask::print_tree(int p_initial_tabs) const {
|
|
|
|
String tabs = "--";
|
|
|
|
for (int i = 0; i < p_initial_tabs; i++) {
|
|
|
|
tabs += "--";
|
|
|
|
}
|
|
|
|
print_line(vformat("%s Name: %s Instance: %s", tabs, get_task_name(), Ref<BTTask>(this)));
|
|
|
|
for (int i = 0; i < get_child_count(); i++) {
|
|
|
|
get_child(i)->print_tree(p_initial_tabs + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BTTask::_bind_methods() {
|
|
|
|
// Properties.
|
|
|
|
ClassDB::bind_method(D_METHOD("get_custom_name"), &BTTask::get_custom_name);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_custom_name", "p_name"), &BTTask::set_custom_name);
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_name"), "set_custom_name", "get_custom_name");
|
|
|
|
ClassDB::bind_method(D_METHOD("get_agent"), &BTTask::get_agent);
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "agent", PROPERTY_HINT_NONE, "", 0, "Object"), "", "get_agent");
|
|
|
|
ClassDB::bind_method(D_METHOD("get_blackboard"), &BTTask::get_blackboard);
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "blackboard", PROPERTY_HINT_NONE, "", 0, "Dictionary"), "", "get_blackboard");
|
|
|
|
ClassDB::bind_method(D_METHOD("get_parent"), &BTTask::get_parent);
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_NONE, "", 0, "BTTask"), "", "get_parent");
|
|
|
|
ClassDB::bind_method(D_METHOD("get_children"), &BTTask::_get_children);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_children", "p_children"), &BTTask::_set_children);
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "children", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_children", "get_children");
|
|
|
|
ClassDB::bind_method(D_METHOD("get_status"), &BTTask::get_status);
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "status"), "", "get_status");
|
|
|
|
|
|
|
|
// Virtual methods.
|
|
|
|
ClassDB::bind_method(D_METHOD("_setup"), &BTTask::_setup);
|
|
|
|
BIND_VMETHOD(MethodInfo("_setup"));
|
|
|
|
ClassDB::bind_method(D_METHOD("_enter"), &BTTask::_enter);
|
|
|
|
BIND_VMETHOD(MethodInfo("_enter"))
|
|
|
|
ClassDB::bind_method(D_METHOD("_exit"), &BTTask::_exit);
|
|
|
|
BIND_VMETHOD(MethodInfo("_exit"));
|
|
|
|
ClassDB::bind_method(D_METHOD("_tick", "p_delta"), &BTTask::_tick);
|
|
|
|
BIND_VMETHOD(MethodInfo(Variant::INT, "_tick", PropertyInfo(Variant::REAL, "p_delta")));
|
|
|
|
ClassDB::bind_method(D_METHOD("_generate_name"), &BTTask::_generate_name);
|
|
|
|
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::STRING, ""), "_generate_name"));
|
|
|
|
ClassDB::bind_method(D_METHOD("_get_configuration_warning"), &BTTask::get_configuration_warning);
|
|
|
|
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::STRING, ""), "_get_configuration_warning"));
|
|
|
|
ClassDB::bind_method(D_METHOD("_get_icon"), &BTTask::get_icon);
|
|
|
|
BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "_get_icon"));
|
|
|
|
|
|
|
|
// Public Methods.
|
|
|
|
ClassDB::bind_method(D_METHOD("is_root"), &BTTask::is_root);
|
2022-08-28 11:07:26 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_root"), &BTTask::get_root);
|
2022-08-28 10:54:34 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("initialize", "p_agent", "p_blackboard"), &BTTask::initialize);
|
|
|
|
ClassDB::bind_method(D_METHOD("clone"), &BTTask::clone);
|
|
|
|
ClassDB::bind_method(D_METHOD("execute", "p_delta"), &BTTask::execute);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_child", "p_idx"), &BTTask::get_child);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_child_count"), &BTTask::get_child_count);
|
|
|
|
ClassDB::bind_method(D_METHOD("add_child", "p_child"), &BTTask::add_child);
|
|
|
|
ClassDB::bind_method(D_METHOD("add_child_at_index", "p_child", "p_idx"), &BTTask::add_child_at_index);
|
|
|
|
ClassDB::bind_method(D_METHOD("remove_child", "p_child"), &BTTask::remove_child);
|
|
|
|
ClassDB::bind_method(D_METHOD("has_child", "p_child"), &BTTask::has_child);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_child_index", "p_child"), &BTTask::get_child_index);
|
|
|
|
ClassDB::bind_method(D_METHOD("next_sibling"), &BTTask::next_sibling);
|
|
|
|
ClassDB::bind_method(D_METHOD("print_tree", "p_initial_tabs"), &BTTask::print_tree, Variant(0));
|
|
|
|
ClassDB::bind_method(D_METHOD("get_task_name"), &BTTask::get_task_name);
|
|
|
|
|
|
|
|
// Enums.
|
|
|
|
ClassDB::bind_integer_constant(get_class_static(), "TaskStatus", "FRESH", FRESH);
|
|
|
|
ClassDB::bind_integer_constant(get_class_static(), "TaskStatus", "RUNNING", RUNNING);
|
|
|
|
ClassDB::bind_integer_constant(get_class_static(), "TaskStatus", "FAILURE", FAILURE);
|
|
|
|
ClassDB::bind_integer_constant(get_class_static(), "TaskStatus", "SUCCESS", SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
BTTask::BTTask() {
|
2022-08-31 15:05:25 +00:00
|
|
|
custom_name = String();
|
|
|
|
agent = nullptr;
|
|
|
|
parent = nullptr;
|
|
|
|
blackboard = Dictionary();
|
|
|
|
children = Vector<Ref<BTTask>>();
|
|
|
|
status = FRESH;
|
2022-08-30 16:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BTTask::~BTTask() {
|
|
|
|
for (int i = 0; i < get_child_count(); i++) {
|
|
|
|
ERR_FAIL_COND(!get_child(i).is_valid());
|
2022-08-31 15:05:25 +00:00
|
|
|
get_child(i)->parent = nullptr;
|
2022-08-30 16:48:49 +00:00
|
|
|
get_child(i).unref();
|
|
|
|
}
|
2022-08-28 10:54:34 +00:00
|
|
|
}
|