Add `LimboTaskDB` to handle task categories and scanning user tasks

This commit is contained in:
Serhii Snitsaruk 2023-08-25 17:32:46 +02:00
parent 26b9327090
commit 2ffcbf0565
43 changed files with 236 additions and 140 deletions

View File

@ -20,6 +20,7 @@
class BTAwaitAnimation : public BTAction { class BTAwaitAnimation : public BTAction {
GDCLASS(BTAwaitAnimation, BTAction); GDCLASS(BTAwaitAnimation, BTAction);
TASK_CATEGORY(Actions);
private: private:
Ref<BBNode> animation_player_param; Ref<BBNode> animation_player_param;

View File

@ -18,6 +18,7 @@
class BTCallMethod : public BTAction { class BTCallMethod : public BTAction {
GDCLASS(BTCallMethod, BTAction); GDCLASS(BTCallMethod, BTAction);
TASK_CATEGORY(Actions);
private: private:
StringName method_name; StringName method_name;

View File

@ -18,6 +18,7 @@
class BTConsolePrint : public BTAction { class BTConsolePrint : public BTAction {
GDCLASS(BTConsolePrint, BTAction); GDCLASS(BTConsolePrint, BTAction);
TASK_CATEGORY(Actions);
private: private:
String text; String text;

View File

@ -16,6 +16,7 @@
class BTFail : public BTAction { class BTFail : public BTAction {
GDCLASS(BTFail, BTAction); GDCLASS(BTFail, BTAction);
TASK_CATEGORY(Actions);
protected: protected:
virtual int _tick(double p_delta) override; virtual int _tick(double p_delta) override;

View File

@ -20,6 +20,7 @@
class BTPauseAnimation : public BTAction { class BTPauseAnimation : public BTAction {
GDCLASS(BTPauseAnimation, BTAction); GDCLASS(BTPauseAnimation, BTAction);
TASK_CATEGORY(Actions);
private: private:
Ref<BBNode> animation_player_param; Ref<BBNode> animation_player_param;

View File

@ -20,6 +20,7 @@
class BTPlayAnimation : public BTAction { class BTPlayAnimation : public BTAction {
GDCLASS(BTPlayAnimation, BTAction); GDCLASS(BTPlayAnimation, BTAction);
TASK_CATEGORY(Actions);
private: private:
Ref<BBNode> animation_player_param; Ref<BBNode> animation_player_param;

View File

@ -16,6 +16,7 @@
class BTRandomWait : public BTAction { class BTRandomWait : public BTAction {
GDCLASS(BTRandomWait, BTAction); GDCLASS(BTRandomWait, BTAction);
TASK_CATEGORY(Actions);
private: private:
double min_duration = 1.0; double min_duration = 1.0;

View File

@ -18,6 +18,7 @@
class BTSetAgentProperty : public BTAction { class BTSetAgentProperty : public BTAction {
GDCLASS(BTSetAgentProperty, BTAction); GDCLASS(BTSetAgentProperty, BTAction);
TASK_CATEGORY(Actions);
private: private:
StringName property; StringName property;

View File

@ -8,7 +8,6 @@
* https://opensource.org/licenses/MIT. * https://opensource.org/licenses/MIT.
* ============================================================================= * =============================================================================
*/ */
/* bt_set_var.h */
#ifndef BT_SET_VAR_H #ifndef BT_SET_VAR_H
#define BT_SET_VAR_H #define BT_SET_VAR_H
@ -21,6 +20,7 @@
class BTSetVar : public BTAction { class BTSetVar : public BTAction {
GDCLASS(BTSetVar, BTAction); GDCLASS(BTSetVar, BTAction);
TASK_CATEGORY(Actions);
private: private:
String variable; String variable;

View File

@ -20,6 +20,7 @@
class BTStopAnimation : public BTAction { class BTStopAnimation : public BTAction {
GDCLASS(BTStopAnimation, BTAction); GDCLASS(BTStopAnimation, BTAction);
TASK_CATEGORY(Actions);
private: private:
Ref<BBNode> animation_player_param; Ref<BBNode> animation_player_param;

View File

@ -16,6 +16,7 @@
class BTWait : public BTAction { class BTWait : public BTAction {
GDCLASS(BTWait, BTAction); GDCLASS(BTWait, BTAction);
TASK_CATEGORY(Actions);
private: private:
double duration = 1.0; double duration = 1.0;

View File

@ -16,6 +16,7 @@
class BTWaitTicks : public BTAction { class BTWaitTicks : public BTAction {
GDCLASS(BTWaitTicks, BTAction); GDCLASS(BTWaitTicks, BTAction);
TASK_CATEGORY(Actions);
private: private:
int num_ticks = 1; int num_ticks = 1;

View File

@ -8,7 +8,6 @@
* https://opensource.org/licenses/MIT. * https://opensource.org/licenses/MIT.
* ============================================================================= * =============================================================================
*/ */
/* bt_comment.h */
#ifndef BT_COMMENT_H #ifndef BT_COMMENT_H
#define BT_COMMENT_H #define BT_COMMENT_H
@ -18,8 +17,9 @@
class BTComment : public BTTask { class BTComment : public BTTask {
GDCLASS(BTComment, BTTask); GDCLASS(BTComment, BTTask);
private:
public: public:
static _FORCE_INLINE_ String get_task_category() { return LimboTaskDB::get_misc_category(); }
virtual Ref<BTTask> clone() const override; virtual Ref<BTTask> clone() const override;
virtual PackedStringArray get_configuration_warnings() const override; virtual PackedStringArray get_configuration_warnings() const override;
}; };

View File

@ -13,6 +13,7 @@
#define BTTASK_H #define BTTASK_H
#include "modules/limboai/blackboard/blackboard.h" #include "modules/limboai/blackboard/blackboard.h"
#include "modules/limboai/util/limbo_task_db.h"
#include "core/io/resource.h" #include "core/io/resource.h"
#include "core/object/object.h" #include "core/object/object.h"

View File

@ -16,6 +16,7 @@
class BTDynamicSelector : public BTComposite { class BTDynamicSelector : public BTComposite {
GDCLASS(BTDynamicSelector, BTComposite); GDCLASS(BTDynamicSelector, BTComposite);
TASK_CATEGORY(Composites);
private: private:
int last_running_idx = 0; int last_running_idx = 0;

View File

@ -16,6 +16,7 @@
class BTDynamicSequence : public BTComposite { class BTDynamicSequence : public BTComposite {
GDCLASS(BTDynamicSequence, BTComposite); GDCLASS(BTDynamicSequence, BTComposite);
TASK_CATEGORY(Composites);
private: private:
int last_running_idx = 0; int last_running_idx = 0;

View File

@ -16,6 +16,7 @@
class BTParallel : public BTComposite { class BTParallel : public BTComposite {
GDCLASS(BTParallel, BTComposite); GDCLASS(BTParallel, BTComposite);
TASK_CATEGORY(Composites);
private: private:
int num_successes_required = 1; int num_successes_required = 1;

View File

@ -18,6 +18,7 @@
class BTRandomSelector : public BTComposite { class BTRandomSelector : public BTComposite {
GDCLASS(BTRandomSelector, BTComposite); GDCLASS(BTRandomSelector, BTComposite);
TASK_CATEGORY(Composites);
private: private:
int last_running_idx = 0; int last_running_idx = 0;

View File

@ -18,6 +18,7 @@
class BTRandomSequence : public BTComposite { class BTRandomSequence : public BTComposite {
GDCLASS(BTRandomSequence, BTComposite); GDCLASS(BTRandomSequence, BTComposite);
TASK_CATEGORY(Composites);
private: private:
int last_running_idx = 0; int last_running_idx = 0;

View File

@ -16,6 +16,7 @@
class BTSelector : public BTComposite { class BTSelector : public BTComposite {
GDCLASS(BTSelector, BTComposite); GDCLASS(BTSelector, BTComposite);
TASK_CATEGORY(Composites);
private: private:
int last_running_idx = 0; int last_running_idx = 0;

View File

@ -16,6 +16,7 @@
class BTSequence : public BTComposite { class BTSequence : public BTComposite {
GDCLASS(BTSequence, BTComposite); GDCLASS(BTSequence, BTComposite);
TASK_CATEGORY(Composites);
private: private:
int last_running_idx = 0; int last_running_idx = 0;

View File

@ -21,6 +21,7 @@
class BTCheckAgentProperty : public BTCondition { class BTCheckAgentProperty : public BTCondition {
GDCLASS(BTCheckAgentProperty, BTCondition); GDCLASS(BTCheckAgentProperty, BTCondition);
TASK_CATEGORY(Conditions);
private: private:
StringName property; StringName property;

View File

@ -9,8 +9,6 @@
* ============================================================================= * =============================================================================
*/ */
/* bt_check_trigger.h */
#ifndef BT_CHECK_TRIGGER_H #ifndef BT_CHECK_TRIGGER_H
#define BT_CHECK_TRIGGER_H #define BT_CHECK_TRIGGER_H
@ -20,6 +18,7 @@
class BTCheckTrigger : public BTCondition { class BTCheckTrigger : public BTCondition {
GDCLASS(BTCheckTrigger, BTCondition); GDCLASS(BTCheckTrigger, BTCondition);
TASK_CATEGORY(Conditions);
private: private:
String variable; String variable;

View File

@ -19,6 +19,7 @@
class BTCheckVar : public BTCondition { class BTCheckVar : public BTCondition {
GDCLASS(BTCheckVar, BTCondition); GDCLASS(BTCheckVar, BTCondition);
TASK_CATEGORY(Conditions);
private: private:
String variable; String variable;

View File

@ -16,6 +16,7 @@
class BTAlwaysFail : public BTDecorator { class BTAlwaysFail : public BTDecorator {
GDCLASS(BTAlwaysFail, BTDecorator); GDCLASS(BTAlwaysFail, BTDecorator);
TASK_CATEGORY(Decorators);
protected: protected:
virtual int _tick(double p_delta) override; virtual int _tick(double p_delta) override;

View File

@ -16,6 +16,7 @@
class BTAlwaysSucceed : public BTDecorator { class BTAlwaysSucceed : public BTDecorator {
GDCLASS(BTAlwaysSucceed, BTDecorator); GDCLASS(BTAlwaysSucceed, BTDecorator);
TASK_CATEGORY(Decorators);
protected: protected:
virtual int _tick(double p_delta) override; virtual int _tick(double p_delta) override;

View File

@ -18,6 +18,7 @@
class BTCooldown : public BTDecorator { class BTCooldown : public BTDecorator {
GDCLASS(BTCooldown, BTDecorator); GDCLASS(BTCooldown, BTDecorator);
TASK_CATEGORY(Decorators);
private: private:
double duration = 10.0; double duration = 10.0;

View File

@ -16,6 +16,7 @@
class BTDelay : public BTDecorator { class BTDelay : public BTDecorator {
GDCLASS(BTDelay, BTDecorator); GDCLASS(BTDelay, BTDecorator);
TASK_CATEGORY(Decorators);
private: private:
double seconds = 1.0; double seconds = 1.0;

View File

@ -16,6 +16,7 @@
class BTForEach : public BTDecorator { class BTForEach : public BTDecorator {
GDCLASS(BTForEach, BTDecorator); GDCLASS(BTForEach, BTDecorator);
TASK_CATEGORY(Decorators);
private: private:
String array_var; String array_var;

View File

@ -16,6 +16,7 @@
class BTInvert : public BTDecorator { class BTInvert : public BTDecorator {
GDCLASS(BTInvert, BTDecorator); GDCLASS(BTInvert, BTDecorator);
TASK_CATEGORY(Decorators);
protected: protected:
virtual int _tick(double p_delta) override; virtual int _tick(double p_delta) override;

View File

@ -16,6 +16,7 @@
class BTNewScope : public BTDecorator { class BTNewScope : public BTDecorator {
GDCLASS(BTNewScope, BTDecorator); GDCLASS(BTNewScope, BTDecorator);
TASK_CATEGORY(Actions);
private: private:
Dictionary blackboard_data; Dictionary blackboard_data;

View File

@ -16,6 +16,7 @@
class BTProbability : public BTDecorator { class BTProbability : public BTDecorator {
GDCLASS(BTProbability, BTDecorator); GDCLASS(BTProbability, BTDecorator);
TASK_CATEGORY(Decorators);
private: private:
float run_chance = 0.5; float run_chance = 0.5;

View File

@ -16,6 +16,7 @@
class BTRepeat : public BTDecorator { class BTRepeat : public BTDecorator {
GDCLASS(BTRepeat, BTDecorator); GDCLASS(BTRepeat, BTDecorator);
TASK_CATEGORY(Decorators);
private: private:
bool forever = false; bool forever = false;

View File

@ -16,6 +16,7 @@
class BTRepeatUntilFailure : public BTDecorator { class BTRepeatUntilFailure : public BTDecorator {
GDCLASS(BTRepeatUntilFailure, BTDecorator); GDCLASS(BTRepeatUntilFailure, BTDecorator);
TASK_CATEGORY(Decorators);
protected: protected:
virtual int _tick(double p_delta) override; virtual int _tick(double p_delta) override;

View File

@ -16,6 +16,7 @@
class BTRepeatUntilSuccess : public BTDecorator { class BTRepeatUntilSuccess : public BTDecorator {
GDCLASS(BTRepeatUntilSuccess, BTDecorator); GDCLASS(BTRepeatUntilSuccess, BTDecorator);
TASK_CATEGORY(Decorators);
protected: protected:
virtual int _tick(double p_delta) override; virtual int _tick(double p_delta) override;

View File

@ -16,6 +16,7 @@
class BTRunLimit : public BTDecorator { class BTRunLimit : public BTDecorator {
GDCLASS(BTRunLimit, BTDecorator); GDCLASS(BTRunLimit, BTDecorator);
TASK_CATEGORY(Decorators);
private: private:
int run_limit = 1; int run_limit = 1;

View File

@ -18,6 +18,7 @@
class BTSubtree : public BTNewScope { class BTSubtree : public BTNewScope {
GDCLASS(BTSubtree, BTNewScope); GDCLASS(BTSubtree, BTNewScope);
TASK_CATEGORY(Actions);
private: private:
Ref<BehaviorTree> subtree; Ref<BehaviorTree> subtree;

View File

@ -16,6 +16,7 @@
class BTTimeLimit : public BTDecorator { class BTTimeLimit : public BTDecorator {
GDCLASS(BTTimeLimit, BTDecorator); GDCLASS(BTTimeLimit, BTDecorator);
TASK_CATEGORY(Decorators);
private: private:
double time_limit = 5.0; double time_limit = 5.0;

View File

@ -563,39 +563,12 @@ void TaskPanel::refresh() {
} }
} }
HashMap<String, List<String>> categorized_tasks; LimboTaskDB::scan_user_tasks();
List<String> categories = LimboTaskDB::get_categories();
categorized_tasks["Composites"] = List<String>();
_populate_core_tasks_from_class("BTComposite", &categorized_tasks["Composites"]);
categorized_tasks["Actions"] = List<String>();
_populate_core_tasks_from_class("BTAction", &categorized_tasks["Actions"]);
categorized_tasks["Decorators"] = List<String>();
_populate_core_tasks_from_class("BTDecorator", &categorized_tasks["Decorators"]);
categorized_tasks["Conditions"] = List<String>();
_populate_core_tasks_from_class("BTCondition", &categorized_tasks["Conditions"]);
categorized_tasks["Uncategorized"] = List<String>();
String dir1 = GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_1");
_populate_from_user_dir(dir1, &categorized_tasks);
String dir2 = GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_2");
_populate_from_user_dir(dir2, &categorized_tasks);
String dir3 = GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_3");
_populate_from_user_dir(dir3, &categorized_tasks);
List<String> categories;
for (KeyValue<String, List<String>> &K : categorized_tasks) {
K.value.sort();
categories.push_back(K.key);
}
categories.sort(); categories.sort();
for (String cat : categories) { for (String cat : categories) {
List<String> tasks = categorized_tasks.get(cat); List<String> tasks = LimboTaskDB::get_tasks_in_category(cat);
if (tasks.size() == 0) { if (tasks.size() == 0) {
continue; continue;
@ -642,70 +615,6 @@ void TaskPanel::refresh() {
} }
} }
void TaskPanel::_populate_core_tasks_from_class(const StringName &p_base_class, List<String> *p_task_classes) {
List<StringName> inheriters;
ClassDB::get_inheriters_from_class(p_base_class, &inheriters);
for (StringName cl : inheriters) {
p_task_classes->push_back(cl);
}
}
void TaskPanel::_populate_from_user_dir(String p_path, HashMap<String, List<String>> *p_categories) {
if (p_path.is_empty()) {
return;
}
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (dir->change_dir(p_path) == OK) {
dir->list_dir_begin();
String fn = dir->get_next();
while (!fn.is_empty()) {
if (dir->current_is_dir() && fn != "..") {
String full_path;
String category;
if (fn == ".") {
full_path = p_path;
category = "Uncategorized";
} else {
full_path = p_path.path_join(fn);
category = fn.capitalize();
}
if (!p_categories->has(category)) {
p_categories->insert(category, List<String>());
}
_populate_scripted_tasks_from_dir(full_path, &p_categories->get(category));
}
fn = dir->get_next();
}
dir->list_dir_end();
} else {
ERR_FAIL_MSG(vformat("Failed to list \"%s\" directory.", p_path));
}
}
void TaskPanel::_populate_scripted_tasks_from_dir(String p_path, List<String> *p_task_classes) {
if (p_path.is_empty()) {
return;
}
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (dir->change_dir(p_path) == OK) {
dir->list_dir_begin();
String fn = dir->get_next();
while (!fn.is_empty()) {
if (fn.ends_with(".gd")) {
String full_path = p_path.path_join(fn);
p_task_classes->push_back(full_path);
}
fn = dir->get_next();
}
dir->list_dir_end();
} else {
ERR_FAIL_MSG(vformat("Failed to list \"%s\" directory.", p_path));
}
}
void TaskPanel::_notification(int p_what) { void TaskPanel::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_EXIT_TREE: { case NOTIFICATION_EXIT_TREE: {

View File

@ -129,9 +129,6 @@ private:
String context_task; String context_task;
void _populate_core_tasks_from_class(const StringName &p_base_class, List<String> *p_task_classes);
void _populate_from_user_dir(String p_path, HashMap<String, List<String>> *p_categories);
void _populate_scripted_tasks_from_dir(String p_path, List<String> *p_task_classes);
void _menu_action_selected(int p_id); void _menu_action_selected(int p_id);
void _on_task_button_pressed(const String &p_task); void _on_task_button_pressed(const String &p_task);
void _on_task_button_rmb(const String &p_task); void _on_task_button_rmb(const String &p_task);

View File

@ -93,6 +93,7 @@
#include "hsm/limbo_hsm.h" #include "hsm/limbo_hsm.h"
#include "hsm/limbo_state.h" #include "hsm/limbo_state.h"
#include "util/limbo_string_names.h" #include "util/limbo_string_names.h"
#include "util/limbo_task_db.h"
#include "util/limbo_utility.h" #include "util/limbo_utility.h"
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
@ -121,51 +122,51 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(BTPlayer); GDREGISTER_CLASS(BTPlayer);
GDREGISTER_CLASS(BTState); GDREGISTER_CLASS(BTState);
GDREGISTER_CLASS(BTComment); LIMBO_REGISTER_TASK(BTComment);
GDREGISTER_CLASS(BTComposite); GDREGISTER_CLASS(BTComposite);
GDREGISTER_CLASS(BTSequence); LIMBO_REGISTER_TASK(BTSequence);
GDREGISTER_CLASS(BTSelector); LIMBO_REGISTER_TASK(BTSelector);
GDREGISTER_CLASS(BTParallel); LIMBO_REGISTER_TASK(BTParallel);
GDREGISTER_CLASS(BTDynamicSequence); LIMBO_REGISTER_TASK(BTDynamicSequence);
GDREGISTER_CLASS(BTDynamicSelector); LIMBO_REGISTER_TASK(BTDynamicSelector);
GDREGISTER_CLASS(BTRandomSequence); LIMBO_REGISTER_TASK(BTRandomSequence);
GDREGISTER_CLASS(BTRandomSelector); LIMBO_REGISTER_TASK(BTRandomSelector);
GDREGISTER_CLASS(BTDecorator); GDREGISTER_CLASS(BTDecorator);
GDREGISTER_CLASS(BTInvert); LIMBO_REGISTER_TASK(BTInvert);
GDREGISTER_CLASS(BTAlwaysFail); LIMBO_REGISTER_TASK(BTAlwaysFail);
GDREGISTER_CLASS(BTAlwaysSucceed); LIMBO_REGISTER_TASK(BTAlwaysSucceed);
GDREGISTER_CLASS(BTDelay); LIMBO_REGISTER_TASK(BTDelay);
GDREGISTER_CLASS(BTRepeat); LIMBO_REGISTER_TASK(BTRepeat);
GDREGISTER_CLASS(BTRepeatUntilFailure); LIMBO_REGISTER_TASK(BTRepeatUntilFailure);
GDREGISTER_CLASS(BTRepeatUntilSuccess); LIMBO_REGISTER_TASK(BTRepeatUntilSuccess);
GDREGISTER_CLASS(BTRunLimit); LIMBO_REGISTER_TASK(BTRunLimit);
GDREGISTER_CLASS(BTTimeLimit); LIMBO_REGISTER_TASK(BTTimeLimit);
GDREGISTER_CLASS(BTCooldown); LIMBO_REGISTER_TASK(BTCooldown);
GDREGISTER_CLASS(BTProbability); LIMBO_REGISTER_TASK(BTProbability);
GDREGISTER_CLASS(BTForEach); LIMBO_REGISTER_TASK(BTForEach);
GDREGISTER_CLASS(BTAction); GDREGISTER_CLASS(BTAction);
GDREGISTER_CLASS(BTAwaitAnimation); LIMBO_REGISTER_TASK(BTAwaitAnimation);
GDREGISTER_CLASS(BTCallMethod); LIMBO_REGISTER_TASK(BTCallMethod);
GDREGISTER_CLASS(BTConsolePrint); LIMBO_REGISTER_TASK(BTConsolePrint);
GDREGISTER_CLASS(BTFail); LIMBO_REGISTER_TASK(BTFail);
GDREGISTER_CLASS(BTNewScope); LIMBO_REGISTER_TASK(BTNewScope);
GDREGISTER_CLASS(BTPauseAnimation); LIMBO_REGISTER_TASK(BTPauseAnimation);
GDREGISTER_CLASS(BTPlayAnimation); LIMBO_REGISTER_TASK(BTPlayAnimation);
GDREGISTER_CLASS(BTRandomWait); LIMBO_REGISTER_TASK(BTRandomWait);
GDREGISTER_CLASS(BTSetAgentProperty); LIMBO_REGISTER_TASK(BTSetAgentProperty);
GDREGISTER_CLASS(BTSetVar); LIMBO_REGISTER_TASK(BTSetVar);
GDREGISTER_CLASS(BTStopAnimation); LIMBO_REGISTER_TASK(BTStopAnimation);
GDREGISTER_CLASS(BTSubtree); LIMBO_REGISTER_TASK(BTSubtree);
GDREGISTER_CLASS(BTWait); LIMBO_REGISTER_TASK(BTWait);
GDREGISTER_CLASS(BTWaitTicks); LIMBO_REGISTER_TASK(BTWaitTicks);
GDREGISTER_CLASS(BTCondition); GDREGISTER_CLASS(BTCondition);
GDREGISTER_CLASS(BTCheckAgentProperty); LIMBO_REGISTER_TASK(BTCheckAgentProperty);
GDREGISTER_CLASS(BTCheckTrigger); LIMBO_REGISTER_TASK(BTCheckTrigger);
GDREGISTER_CLASS(BTCheckVar); LIMBO_REGISTER_TASK(BTCheckVar);
GDREGISTER_ABSTRACT_CLASS(BBParam); GDREGISTER_ABSTRACT_CLASS(BBParam);
GDREGISTER_CLASS(BBInt); GDREGISTER_CLASS(BBInt);

98
util/limbo_task_db.cpp Normal file
View File

@ -0,0 +1,98 @@
/**
* limbo_task_db.cpp
* =============================================================================
* Copyright 2021-2023 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
#include "limbo_task_db.h"
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
HashMap<String, List<String>> LimboTaskDB::core_tasks;
HashMap<String, List<String>> LimboTaskDB::tasks_cache;
_FORCE_INLINE_ void _populate_scripted_tasks_from_dir(String p_path, List<String> *p_task_classes) {
if (p_path.is_empty()) {
return;
}
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (dir->change_dir(p_path) == OK) {
dir->list_dir_begin();
String fn = dir->get_next();
while (!fn.is_empty()) {
if (fn.ends_with(".gd")) {
String full_path = p_path.path_join(fn);
p_task_classes->push_back(full_path);
}
fn = dir->get_next();
}
dir->list_dir_end();
} else {
ERR_FAIL_MSG(vformat("Failed to list \"%s\" directory.", p_path));
}
}
_FORCE_INLINE_ void _populate_from_user_dir(String p_path, HashMap<String, List<String>> *p_categories) {
if (p_path.is_empty()) {
return;
}
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (dir->change_dir(p_path) == OK) {
dir->list_dir_begin();
String fn = dir->get_next();
while (!fn.is_empty()) {
if (dir->current_is_dir() && fn != "..") {
String full_path;
String category;
if (fn == ".") {
full_path = p_path;
category = LimboTaskDB::get_misc_category();
} else {
full_path = p_path.path_join(fn);
category = fn.capitalize();
}
if (!p_categories->has(category)) {
p_categories->insert(category, List<String>());
}
_populate_scripted_tasks_from_dir(full_path, &p_categories->get(category));
}
fn = dir->get_next();
}
dir->list_dir_end();
} else {
ERR_FAIL_MSG(vformat("Failed to list \"%s\" directory.", p_path));
}
}
void LimboTaskDB::scan_user_tasks() {
tasks_cache = HashMap<String, List<String>>(core_tasks);
if (!tasks_cache.has(LimboTaskDB::get_misc_category())) {
tasks_cache[LimboTaskDB::get_misc_category()] = List<String>();
}
for (int i = 1; i < 4; i++) {
String dir1 = GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_" + itos(i));
_populate_from_user_dir(dir1, &tasks_cache);
}
}
List<String> LimboTaskDB::get_categories() {
List<String> r_cat;
for (const KeyValue<String, List<String>> &E : tasks_cache) {
r_cat.push_back(E.key);
}
return r_cat;
}
List<String> LimboTaskDB::get_tasks_in_category(const String &p_category) {
return List<String>(tasks_cache[p_category]);
}

57
util/limbo_task_db.h Normal file
View File

@ -0,0 +1,57 @@
/**
* limbo_task_db.h
* =============================================================================
* Copyright 2021-2023 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
#ifndef LIMBO_TASK_DB_H
#define LIMBO_TASK_DB_H
#include "core/object/class_db.h"
#include "core/templates/hash_map.h"
#include "core/templates/list.h"
class LimboTaskDB {
private:
static HashMap<String, List<String>> core_tasks;
static HashMap<String, List<String>> tasks_cache;
public:
template <class T>
static void register_task() {
GDREGISTER_CLASS(T);
HashMap<String, List<String>>::Iterator E = core_tasks.find(T::get_task_category());
if (E) {
E->value.push_back(T::get_class_static());
} else {
List<String> tasks;
tasks.push_back(T::get_class_static());
core_tasks.insert(T::get_task_category(), tasks);
}
}
static void scan_user_tasks();
static _FORCE_INLINE_ String get_misc_category() { return "Misc"; }
static List<String> get_categories();
static List<String> get_tasks_in_category(const String &p_category);
};
#define LIMBO_REGISTER_TASK(m_class) \
if (m_class::_class_is_enabled) { \
::LimboTaskDB::register_task<m_class>(); \
}
#define TASK_CATEGORY(m_cat) \
public: \
static _FORCE_INLINE_ String get_task_category() { \
return String(#m_cat); \
} \
\
private:
#endif // LIMBO_TASK_DB_H