From 2ffcbf0565ce19a2bb7f1224f5fe6c55ac751f34 Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 25 Aug 2023 17:32:46 +0200 Subject: [PATCH 01/11] Add `LimboTaskDB` to handle task categories and scanning user tasks --- bt/tasks/actions/bt_await_animation.h | 1 + bt/tasks/actions/bt_call_method.h | 1 + bt/tasks/actions/bt_console_print.h | 1 + bt/tasks/actions/bt_fail.h | 1 + bt/tasks/actions/bt_pause_animation.h | 1 + bt/tasks/actions/bt_play_animation.h | 1 + bt/tasks/actions/bt_random_wait.h | 1 + bt/tasks/actions/bt_set_agent_property.h | 1 + bt/tasks/actions/bt_set_var.h | 2 +- bt/tasks/actions/bt_stop_animation.h | 1 + bt/tasks/actions/bt_wait.h | 1 + bt/tasks/actions/bt_wait_ticks.h | 1 + bt/tasks/bt_comment.h | 4 +- bt/tasks/bt_task.h | 1 + bt/tasks/composites/bt_dynamic_selector.h | 1 + bt/tasks/composites/bt_dynamic_sequence.h | 1 + bt/tasks/composites/bt_parallel.h | 1 + bt/tasks/composites/bt_random_selector.h | 1 + bt/tasks/composites/bt_random_sequence.h | 1 + bt/tasks/composites/bt_selector.h | 1 + bt/tasks/composites/bt_sequence.h | 1 + bt/tasks/conditions/bt_check_agent_property.h | 1 + bt/tasks/conditions/bt_check_trigger.h | 3 +- bt/tasks/conditions/bt_check_var.h | 1 + bt/tasks/decorators/bt_always_fail.h | 1 + bt/tasks/decorators/bt_always_succeed.h | 1 + bt/tasks/decorators/bt_cooldown.h | 1 + bt/tasks/decorators/bt_delay.h | 1 + bt/tasks/decorators/bt_for_each.h | 1 + bt/tasks/decorators/bt_invert.h | 1 + bt/tasks/decorators/bt_new_scope.h | 1 + bt/tasks/decorators/bt_probability.h | 1 + bt/tasks/decorators/bt_repeat.h | 1 + bt/tasks/decorators/bt_repeat_until_failure.h | 1 + bt/tasks/decorators/bt_repeat_until_success.h | 1 + bt/tasks/decorators/bt_run_limit.h | 1 + bt/tasks/decorators/bt_subtree.h | 1 + bt/tasks/decorators/bt_time_limit.h | 1 + editor/limbo_ai_editor_plugin.cpp | 99 +------------------ editor/limbo_ai_editor_plugin.h | 3 - register_types.cpp | 75 +++++++------- util/limbo_task_db.cpp | 98 ++++++++++++++++++ util/limbo_task_db.h | 57 +++++++++++ 43 files changed, 236 insertions(+), 140 deletions(-) create mode 100644 util/limbo_task_db.cpp create mode 100644 util/limbo_task_db.h diff --git a/bt/tasks/actions/bt_await_animation.h b/bt/tasks/actions/bt_await_animation.h index 794be74..5780efc 100644 --- a/bt/tasks/actions/bt_await_animation.h +++ b/bt/tasks/actions/bt_await_animation.h @@ -20,6 +20,7 @@ class BTAwaitAnimation : public BTAction { GDCLASS(BTAwaitAnimation, BTAction); + TASK_CATEGORY(Actions); private: Ref animation_player_param; diff --git a/bt/tasks/actions/bt_call_method.h b/bt/tasks/actions/bt_call_method.h index 2761583..c06a4c9 100644 --- a/bt/tasks/actions/bt_call_method.h +++ b/bt/tasks/actions/bt_call_method.h @@ -18,6 +18,7 @@ class BTCallMethod : public BTAction { GDCLASS(BTCallMethod, BTAction); + TASK_CATEGORY(Actions); private: StringName method_name; diff --git a/bt/tasks/actions/bt_console_print.h b/bt/tasks/actions/bt_console_print.h index bdf0e9c..2472bd0 100644 --- a/bt/tasks/actions/bt_console_print.h +++ b/bt/tasks/actions/bt_console_print.h @@ -18,6 +18,7 @@ class BTConsolePrint : public BTAction { GDCLASS(BTConsolePrint, BTAction); + TASK_CATEGORY(Actions); private: String text; diff --git a/bt/tasks/actions/bt_fail.h b/bt/tasks/actions/bt_fail.h index cf64e7d..0def5fe 100644 --- a/bt/tasks/actions/bt_fail.h +++ b/bt/tasks/actions/bt_fail.h @@ -16,6 +16,7 @@ class BTFail : public BTAction { GDCLASS(BTFail, BTAction); + TASK_CATEGORY(Actions); protected: virtual int _tick(double p_delta) override; diff --git a/bt/tasks/actions/bt_pause_animation.h b/bt/tasks/actions/bt_pause_animation.h index bc319a1..626cb5f 100644 --- a/bt/tasks/actions/bt_pause_animation.h +++ b/bt/tasks/actions/bt_pause_animation.h @@ -20,6 +20,7 @@ class BTPauseAnimation : public BTAction { GDCLASS(BTPauseAnimation, BTAction); + TASK_CATEGORY(Actions); private: Ref animation_player_param; diff --git a/bt/tasks/actions/bt_play_animation.h b/bt/tasks/actions/bt_play_animation.h index 5b85820..ce5bca9 100644 --- a/bt/tasks/actions/bt_play_animation.h +++ b/bt/tasks/actions/bt_play_animation.h @@ -20,6 +20,7 @@ class BTPlayAnimation : public BTAction { GDCLASS(BTPlayAnimation, BTAction); + TASK_CATEGORY(Actions); private: Ref animation_player_param; diff --git a/bt/tasks/actions/bt_random_wait.h b/bt/tasks/actions/bt_random_wait.h index ce65c90..2915f89 100644 --- a/bt/tasks/actions/bt_random_wait.h +++ b/bt/tasks/actions/bt_random_wait.h @@ -16,6 +16,7 @@ class BTRandomWait : public BTAction { GDCLASS(BTRandomWait, BTAction); + TASK_CATEGORY(Actions); private: double min_duration = 1.0; diff --git a/bt/tasks/actions/bt_set_agent_property.h b/bt/tasks/actions/bt_set_agent_property.h index c218547..7d2dd9b 100644 --- a/bt/tasks/actions/bt_set_agent_property.h +++ b/bt/tasks/actions/bt_set_agent_property.h @@ -18,6 +18,7 @@ class BTSetAgentProperty : public BTAction { GDCLASS(BTSetAgentProperty, BTAction); + TASK_CATEGORY(Actions); private: StringName property; diff --git a/bt/tasks/actions/bt_set_var.h b/bt/tasks/actions/bt_set_var.h index 7fb2b51..cf9a26c 100644 --- a/bt/tasks/actions/bt_set_var.h +++ b/bt/tasks/actions/bt_set_var.h @@ -8,7 +8,6 @@ * https://opensource.org/licenses/MIT. * ============================================================================= */ -/* bt_set_var.h */ #ifndef BT_SET_VAR_H #define BT_SET_VAR_H @@ -21,6 +20,7 @@ class BTSetVar : public BTAction { GDCLASS(BTSetVar, BTAction); + TASK_CATEGORY(Actions); private: String variable; diff --git a/bt/tasks/actions/bt_stop_animation.h b/bt/tasks/actions/bt_stop_animation.h index dbcd951..bd38bd0 100644 --- a/bt/tasks/actions/bt_stop_animation.h +++ b/bt/tasks/actions/bt_stop_animation.h @@ -20,6 +20,7 @@ class BTStopAnimation : public BTAction { GDCLASS(BTStopAnimation, BTAction); + TASK_CATEGORY(Actions); private: Ref animation_player_param; diff --git a/bt/tasks/actions/bt_wait.h b/bt/tasks/actions/bt_wait.h index 3043449..faed149 100644 --- a/bt/tasks/actions/bt_wait.h +++ b/bt/tasks/actions/bt_wait.h @@ -16,6 +16,7 @@ class BTWait : public BTAction { GDCLASS(BTWait, BTAction); + TASK_CATEGORY(Actions); private: double duration = 1.0; diff --git a/bt/tasks/actions/bt_wait_ticks.h b/bt/tasks/actions/bt_wait_ticks.h index 8271bd4..013d95e 100644 --- a/bt/tasks/actions/bt_wait_ticks.h +++ b/bt/tasks/actions/bt_wait_ticks.h @@ -16,6 +16,7 @@ class BTWaitTicks : public BTAction { GDCLASS(BTWaitTicks, BTAction); + TASK_CATEGORY(Actions); private: int num_ticks = 1; diff --git a/bt/tasks/bt_comment.h b/bt/tasks/bt_comment.h index 442db08..25b5033 100644 --- a/bt/tasks/bt_comment.h +++ b/bt/tasks/bt_comment.h @@ -8,7 +8,6 @@ * https://opensource.org/licenses/MIT. * ============================================================================= */ -/* bt_comment.h */ #ifndef BT_COMMENT_H #define BT_COMMENT_H @@ -18,8 +17,9 @@ class BTComment : public BTTask { GDCLASS(BTComment, BTTask); -private: public: + static _FORCE_INLINE_ String get_task_category() { return LimboTaskDB::get_misc_category(); } + virtual Ref clone() const override; virtual PackedStringArray get_configuration_warnings() const override; }; diff --git a/bt/tasks/bt_task.h b/bt/tasks/bt_task.h index 80e28c1..6728b64 100644 --- a/bt/tasks/bt_task.h +++ b/bt/tasks/bt_task.h @@ -13,6 +13,7 @@ #define BTTASK_H #include "modules/limboai/blackboard/blackboard.h" +#include "modules/limboai/util/limbo_task_db.h" #include "core/io/resource.h" #include "core/object/object.h" diff --git a/bt/tasks/composites/bt_dynamic_selector.h b/bt/tasks/composites/bt_dynamic_selector.h index b5d0631..e1b7246 100644 --- a/bt/tasks/composites/bt_dynamic_selector.h +++ b/bt/tasks/composites/bt_dynamic_selector.h @@ -16,6 +16,7 @@ class BTDynamicSelector : public BTComposite { GDCLASS(BTDynamicSelector, BTComposite); + TASK_CATEGORY(Composites); private: int last_running_idx = 0; diff --git a/bt/tasks/composites/bt_dynamic_sequence.h b/bt/tasks/composites/bt_dynamic_sequence.h index 3f5813a..d331eca 100644 --- a/bt/tasks/composites/bt_dynamic_sequence.h +++ b/bt/tasks/composites/bt_dynamic_sequence.h @@ -16,6 +16,7 @@ class BTDynamicSequence : public BTComposite { GDCLASS(BTDynamicSequence, BTComposite); + TASK_CATEGORY(Composites); private: int last_running_idx = 0; diff --git a/bt/tasks/composites/bt_parallel.h b/bt/tasks/composites/bt_parallel.h index 93cf0b5..40f18e7 100644 --- a/bt/tasks/composites/bt_parallel.h +++ b/bt/tasks/composites/bt_parallel.h @@ -16,6 +16,7 @@ class BTParallel : public BTComposite { GDCLASS(BTParallel, BTComposite); + TASK_CATEGORY(Composites); private: int num_successes_required = 1; diff --git a/bt/tasks/composites/bt_random_selector.h b/bt/tasks/composites/bt_random_selector.h index 1f10527..710b5e3 100644 --- a/bt/tasks/composites/bt_random_selector.h +++ b/bt/tasks/composites/bt_random_selector.h @@ -18,6 +18,7 @@ class BTRandomSelector : public BTComposite { GDCLASS(BTRandomSelector, BTComposite); + TASK_CATEGORY(Composites); private: int last_running_idx = 0; diff --git a/bt/tasks/composites/bt_random_sequence.h b/bt/tasks/composites/bt_random_sequence.h index c2ed551..866cb78 100644 --- a/bt/tasks/composites/bt_random_sequence.h +++ b/bt/tasks/composites/bt_random_sequence.h @@ -18,6 +18,7 @@ class BTRandomSequence : public BTComposite { GDCLASS(BTRandomSequence, BTComposite); + TASK_CATEGORY(Composites); private: int last_running_idx = 0; diff --git a/bt/tasks/composites/bt_selector.h b/bt/tasks/composites/bt_selector.h index c60b045..eb3be08 100644 --- a/bt/tasks/composites/bt_selector.h +++ b/bt/tasks/composites/bt_selector.h @@ -16,6 +16,7 @@ class BTSelector : public BTComposite { GDCLASS(BTSelector, BTComposite); + TASK_CATEGORY(Composites); private: int last_running_idx = 0; diff --git a/bt/tasks/composites/bt_sequence.h b/bt/tasks/composites/bt_sequence.h index b82174f..45b61d6 100644 --- a/bt/tasks/composites/bt_sequence.h +++ b/bt/tasks/composites/bt_sequence.h @@ -16,6 +16,7 @@ class BTSequence : public BTComposite { GDCLASS(BTSequence, BTComposite); + TASK_CATEGORY(Composites); private: int last_running_idx = 0; diff --git a/bt/tasks/conditions/bt_check_agent_property.h b/bt/tasks/conditions/bt_check_agent_property.h index 113b01f..d466e65 100644 --- a/bt/tasks/conditions/bt_check_agent_property.h +++ b/bt/tasks/conditions/bt_check_agent_property.h @@ -21,6 +21,7 @@ class BTCheckAgentProperty : public BTCondition { GDCLASS(BTCheckAgentProperty, BTCondition); + TASK_CATEGORY(Conditions); private: StringName property; diff --git a/bt/tasks/conditions/bt_check_trigger.h b/bt/tasks/conditions/bt_check_trigger.h index f7d899d..37bc50b 100644 --- a/bt/tasks/conditions/bt_check_trigger.h +++ b/bt/tasks/conditions/bt_check_trigger.h @@ -9,8 +9,6 @@ * ============================================================================= */ -/* bt_check_trigger.h */ - #ifndef BT_CHECK_TRIGGER_H #define BT_CHECK_TRIGGER_H @@ -20,6 +18,7 @@ class BTCheckTrigger : public BTCondition { GDCLASS(BTCheckTrigger, BTCondition); + TASK_CATEGORY(Conditions); private: String variable; diff --git a/bt/tasks/conditions/bt_check_var.h b/bt/tasks/conditions/bt_check_var.h index 2ff9815..e25723b 100644 --- a/bt/tasks/conditions/bt_check_var.h +++ b/bt/tasks/conditions/bt_check_var.h @@ -19,6 +19,7 @@ class BTCheckVar : public BTCondition { GDCLASS(BTCheckVar, BTCondition); + TASK_CATEGORY(Conditions); private: String variable; diff --git a/bt/tasks/decorators/bt_always_fail.h b/bt/tasks/decorators/bt_always_fail.h index d5e322f..eed4cf5 100644 --- a/bt/tasks/decorators/bt_always_fail.h +++ b/bt/tasks/decorators/bt_always_fail.h @@ -16,6 +16,7 @@ class BTAlwaysFail : public BTDecorator { GDCLASS(BTAlwaysFail, BTDecorator); + TASK_CATEGORY(Decorators); protected: virtual int _tick(double p_delta) override; diff --git a/bt/tasks/decorators/bt_always_succeed.h b/bt/tasks/decorators/bt_always_succeed.h index 411c595..d8700f2 100644 --- a/bt/tasks/decorators/bt_always_succeed.h +++ b/bt/tasks/decorators/bt_always_succeed.h @@ -16,6 +16,7 @@ class BTAlwaysSucceed : public BTDecorator { GDCLASS(BTAlwaysSucceed, BTDecorator); + TASK_CATEGORY(Decorators); protected: virtual int _tick(double p_delta) override; diff --git a/bt/tasks/decorators/bt_cooldown.h b/bt/tasks/decorators/bt_cooldown.h index a2b0759..8ab5979 100644 --- a/bt/tasks/decorators/bt_cooldown.h +++ b/bt/tasks/decorators/bt_cooldown.h @@ -18,6 +18,7 @@ class BTCooldown : public BTDecorator { GDCLASS(BTCooldown, BTDecorator); + TASK_CATEGORY(Decorators); private: double duration = 10.0; diff --git a/bt/tasks/decorators/bt_delay.h b/bt/tasks/decorators/bt_delay.h index 8093658..ee3eda2 100644 --- a/bt/tasks/decorators/bt_delay.h +++ b/bt/tasks/decorators/bt_delay.h @@ -16,6 +16,7 @@ class BTDelay : public BTDecorator { GDCLASS(BTDelay, BTDecorator); + TASK_CATEGORY(Decorators); private: double seconds = 1.0; diff --git a/bt/tasks/decorators/bt_for_each.h b/bt/tasks/decorators/bt_for_each.h index 41479b6..527093f 100644 --- a/bt/tasks/decorators/bt_for_each.h +++ b/bt/tasks/decorators/bt_for_each.h @@ -16,6 +16,7 @@ class BTForEach : public BTDecorator { GDCLASS(BTForEach, BTDecorator); + TASK_CATEGORY(Decorators); private: String array_var; diff --git a/bt/tasks/decorators/bt_invert.h b/bt/tasks/decorators/bt_invert.h index b224cd0..cc3f707 100644 --- a/bt/tasks/decorators/bt_invert.h +++ b/bt/tasks/decorators/bt_invert.h @@ -16,6 +16,7 @@ class BTInvert : public BTDecorator { GDCLASS(BTInvert, BTDecorator); + TASK_CATEGORY(Decorators); protected: virtual int _tick(double p_delta) override; diff --git a/bt/tasks/decorators/bt_new_scope.h b/bt/tasks/decorators/bt_new_scope.h index 99ac94c..80ba395 100644 --- a/bt/tasks/decorators/bt_new_scope.h +++ b/bt/tasks/decorators/bt_new_scope.h @@ -16,6 +16,7 @@ class BTNewScope : public BTDecorator { GDCLASS(BTNewScope, BTDecorator); + TASK_CATEGORY(Actions); private: Dictionary blackboard_data; diff --git a/bt/tasks/decorators/bt_probability.h b/bt/tasks/decorators/bt_probability.h index 10e0309..0c5689e 100644 --- a/bt/tasks/decorators/bt_probability.h +++ b/bt/tasks/decorators/bt_probability.h @@ -16,6 +16,7 @@ class BTProbability : public BTDecorator { GDCLASS(BTProbability, BTDecorator); + TASK_CATEGORY(Decorators); private: float run_chance = 0.5; diff --git a/bt/tasks/decorators/bt_repeat.h b/bt/tasks/decorators/bt_repeat.h index 448701d..b56c14c 100644 --- a/bt/tasks/decorators/bt_repeat.h +++ b/bt/tasks/decorators/bt_repeat.h @@ -16,6 +16,7 @@ class BTRepeat : public BTDecorator { GDCLASS(BTRepeat, BTDecorator); + TASK_CATEGORY(Decorators); private: bool forever = false; diff --git a/bt/tasks/decorators/bt_repeat_until_failure.h b/bt/tasks/decorators/bt_repeat_until_failure.h index 5d7d4f4..0518927 100644 --- a/bt/tasks/decorators/bt_repeat_until_failure.h +++ b/bt/tasks/decorators/bt_repeat_until_failure.h @@ -16,6 +16,7 @@ class BTRepeatUntilFailure : public BTDecorator { GDCLASS(BTRepeatUntilFailure, BTDecorator); + TASK_CATEGORY(Decorators); protected: virtual int _tick(double p_delta) override; diff --git a/bt/tasks/decorators/bt_repeat_until_success.h b/bt/tasks/decorators/bt_repeat_until_success.h index db65e85..d26af61 100644 --- a/bt/tasks/decorators/bt_repeat_until_success.h +++ b/bt/tasks/decorators/bt_repeat_until_success.h @@ -16,6 +16,7 @@ class BTRepeatUntilSuccess : public BTDecorator { GDCLASS(BTRepeatUntilSuccess, BTDecorator); + TASK_CATEGORY(Decorators); protected: virtual int _tick(double p_delta) override; diff --git a/bt/tasks/decorators/bt_run_limit.h b/bt/tasks/decorators/bt_run_limit.h index ec694c1..26d7fa7 100644 --- a/bt/tasks/decorators/bt_run_limit.h +++ b/bt/tasks/decorators/bt_run_limit.h @@ -16,6 +16,7 @@ class BTRunLimit : public BTDecorator { GDCLASS(BTRunLimit, BTDecorator); + TASK_CATEGORY(Decorators); private: int run_limit = 1; diff --git a/bt/tasks/decorators/bt_subtree.h b/bt/tasks/decorators/bt_subtree.h index edddc7e..9a7fe26 100644 --- a/bt/tasks/decorators/bt_subtree.h +++ b/bt/tasks/decorators/bt_subtree.h @@ -18,6 +18,7 @@ class BTSubtree : public BTNewScope { GDCLASS(BTSubtree, BTNewScope); + TASK_CATEGORY(Actions); private: Ref subtree; diff --git a/bt/tasks/decorators/bt_time_limit.h b/bt/tasks/decorators/bt_time_limit.h index d0207ed..aa960bf 100644 --- a/bt/tasks/decorators/bt_time_limit.h +++ b/bt/tasks/decorators/bt_time_limit.h @@ -16,6 +16,7 @@ class BTTimeLimit : public BTDecorator { GDCLASS(BTTimeLimit, BTDecorator); + TASK_CATEGORY(Decorators); private: double time_limit = 5.0; diff --git a/editor/limbo_ai_editor_plugin.cpp b/editor/limbo_ai_editor_plugin.cpp index 39f9275..accccdb 100644 --- a/editor/limbo_ai_editor_plugin.cpp +++ b/editor/limbo_ai_editor_plugin.cpp @@ -563,39 +563,12 @@ void TaskPanel::refresh() { } } - HashMap> categorized_tasks; - - categorized_tasks["Composites"] = List(); - _populate_core_tasks_from_class("BTComposite", &categorized_tasks["Composites"]); - - categorized_tasks["Actions"] = List(); - _populate_core_tasks_from_class("BTAction", &categorized_tasks["Actions"]); - - categorized_tasks["Decorators"] = List(); - _populate_core_tasks_from_class("BTDecorator", &categorized_tasks["Decorators"]); - - categorized_tasks["Conditions"] = List(); - _populate_core_tasks_from_class("BTCondition", &categorized_tasks["Conditions"]); - - categorized_tasks["Uncategorized"] = List(); - - 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 categories; - for (KeyValue> &K : categorized_tasks) { - K.value.sort(); - categories.push_back(K.key); - } + LimboTaskDB::scan_user_tasks(); + List categories = LimboTaskDB::get_categories(); categories.sort(); + for (String cat : categories) { - List tasks = categorized_tasks.get(cat); + List tasks = LimboTaskDB::get_tasks_in_category(cat); if (tasks.size() == 0) { continue; @@ -642,70 +615,6 @@ void TaskPanel::refresh() { } } -void TaskPanel::_populate_core_tasks_from_class(const StringName &p_base_class, List *p_task_classes) { - List 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> *p_categories) { - if (p_path.is_empty()) { - return; - } - Ref 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()); - } - - _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 *p_task_classes) { - if (p_path.is_empty()) { - return; - } - Ref 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) { switch (p_what) { case NOTIFICATION_EXIT_TREE: { diff --git a/editor/limbo_ai_editor_plugin.h b/editor/limbo_ai_editor_plugin.h index c5ed8b2..8c23427 100644 --- a/editor/limbo_ai_editor_plugin.h +++ b/editor/limbo_ai_editor_plugin.h @@ -129,9 +129,6 @@ private: String context_task; - void _populate_core_tasks_from_class(const StringName &p_base_class, List *p_task_classes); - void _populate_from_user_dir(String p_path, HashMap> *p_categories); - void _populate_scripted_tasks_from_dir(String p_path, List *p_task_classes); void _menu_action_selected(int p_id); void _on_task_button_pressed(const String &p_task); void _on_task_button_rmb(const String &p_task); diff --git a/register_types.cpp b/register_types.cpp index 6502698..0b9c234 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -93,6 +93,7 @@ #include "hsm/limbo_hsm.h" #include "hsm/limbo_state.h" #include "util/limbo_string_names.h" +#include "util/limbo_task_db.h" #include "util/limbo_utility.h" #ifdef TOOLS_ENABLED @@ -121,51 +122,51 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(BTPlayer); GDREGISTER_CLASS(BTState); - GDREGISTER_CLASS(BTComment); + LIMBO_REGISTER_TASK(BTComment); GDREGISTER_CLASS(BTComposite); - GDREGISTER_CLASS(BTSequence); - GDREGISTER_CLASS(BTSelector); - GDREGISTER_CLASS(BTParallel); - GDREGISTER_CLASS(BTDynamicSequence); - GDREGISTER_CLASS(BTDynamicSelector); - GDREGISTER_CLASS(BTRandomSequence); - GDREGISTER_CLASS(BTRandomSelector); + LIMBO_REGISTER_TASK(BTSequence); + LIMBO_REGISTER_TASK(BTSelector); + LIMBO_REGISTER_TASK(BTParallel); + LIMBO_REGISTER_TASK(BTDynamicSequence); + LIMBO_REGISTER_TASK(BTDynamicSelector); + LIMBO_REGISTER_TASK(BTRandomSequence); + LIMBO_REGISTER_TASK(BTRandomSelector); GDREGISTER_CLASS(BTDecorator); - GDREGISTER_CLASS(BTInvert); - GDREGISTER_CLASS(BTAlwaysFail); - GDREGISTER_CLASS(BTAlwaysSucceed); - GDREGISTER_CLASS(BTDelay); - GDREGISTER_CLASS(BTRepeat); - GDREGISTER_CLASS(BTRepeatUntilFailure); - GDREGISTER_CLASS(BTRepeatUntilSuccess); - GDREGISTER_CLASS(BTRunLimit); - GDREGISTER_CLASS(BTTimeLimit); - GDREGISTER_CLASS(BTCooldown); - GDREGISTER_CLASS(BTProbability); - GDREGISTER_CLASS(BTForEach); + LIMBO_REGISTER_TASK(BTInvert); + LIMBO_REGISTER_TASK(BTAlwaysFail); + LIMBO_REGISTER_TASK(BTAlwaysSucceed); + LIMBO_REGISTER_TASK(BTDelay); + LIMBO_REGISTER_TASK(BTRepeat); + LIMBO_REGISTER_TASK(BTRepeatUntilFailure); + LIMBO_REGISTER_TASK(BTRepeatUntilSuccess); + LIMBO_REGISTER_TASK(BTRunLimit); + LIMBO_REGISTER_TASK(BTTimeLimit); + LIMBO_REGISTER_TASK(BTCooldown); + LIMBO_REGISTER_TASK(BTProbability); + LIMBO_REGISTER_TASK(BTForEach); GDREGISTER_CLASS(BTAction); - GDREGISTER_CLASS(BTAwaitAnimation); - GDREGISTER_CLASS(BTCallMethod); - GDREGISTER_CLASS(BTConsolePrint); - GDREGISTER_CLASS(BTFail); - GDREGISTER_CLASS(BTNewScope); - GDREGISTER_CLASS(BTPauseAnimation); - GDREGISTER_CLASS(BTPlayAnimation); - GDREGISTER_CLASS(BTRandomWait); - GDREGISTER_CLASS(BTSetAgentProperty); - GDREGISTER_CLASS(BTSetVar); - GDREGISTER_CLASS(BTStopAnimation); - GDREGISTER_CLASS(BTSubtree); - GDREGISTER_CLASS(BTWait); - GDREGISTER_CLASS(BTWaitTicks); + LIMBO_REGISTER_TASK(BTAwaitAnimation); + LIMBO_REGISTER_TASK(BTCallMethod); + LIMBO_REGISTER_TASK(BTConsolePrint); + LIMBO_REGISTER_TASK(BTFail); + LIMBO_REGISTER_TASK(BTNewScope); + LIMBO_REGISTER_TASK(BTPauseAnimation); + LIMBO_REGISTER_TASK(BTPlayAnimation); + LIMBO_REGISTER_TASK(BTRandomWait); + LIMBO_REGISTER_TASK(BTSetAgentProperty); + LIMBO_REGISTER_TASK(BTSetVar); + LIMBO_REGISTER_TASK(BTStopAnimation); + LIMBO_REGISTER_TASK(BTSubtree); + LIMBO_REGISTER_TASK(BTWait); + LIMBO_REGISTER_TASK(BTWaitTicks); GDREGISTER_CLASS(BTCondition); - GDREGISTER_CLASS(BTCheckAgentProperty); - GDREGISTER_CLASS(BTCheckTrigger); - GDREGISTER_CLASS(BTCheckVar); + LIMBO_REGISTER_TASK(BTCheckAgentProperty); + LIMBO_REGISTER_TASK(BTCheckTrigger); + LIMBO_REGISTER_TASK(BTCheckVar); GDREGISTER_ABSTRACT_CLASS(BBParam); GDREGISTER_CLASS(BBInt); diff --git a/util/limbo_task_db.cpp b/util/limbo_task_db.cpp new file mode 100644 index 0000000..94a2899 --- /dev/null +++ b/util/limbo_task_db.cpp @@ -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> LimboTaskDB::core_tasks; +HashMap> LimboTaskDB::tasks_cache; + +_FORCE_INLINE_ void _populate_scripted_tasks_from_dir(String p_path, List *p_task_classes) { + if (p_path.is_empty()) { + return; + } + Ref 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> *p_categories) { + if (p_path.is_empty()) { + return; + } + Ref 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()); + } + + _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>(core_tasks); + + if (!tasks_cache.has(LimboTaskDB::get_misc_category())) { + tasks_cache[LimboTaskDB::get_misc_category()] = List(); + } + + 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 LimboTaskDB::get_categories() { + List r_cat; + for (const KeyValue> &E : tasks_cache) { + r_cat.push_back(E.key); + } + return r_cat; +} + +List LimboTaskDB::get_tasks_in_category(const String &p_category) { + return List(tasks_cache[p_category]); +} diff --git a/util/limbo_task_db.h b/util/limbo_task_db.h new file mode 100644 index 0000000..2849475 --- /dev/null +++ b/util/limbo_task_db.h @@ -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> core_tasks; + static HashMap> tasks_cache; + +public: + template + static void register_task() { + GDREGISTER_CLASS(T); + HashMap>::Iterator E = core_tasks.find(T::get_task_category()); + if (E) { + E->value.push_back(T::get_class_static()); + } else { + List 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 get_categories(); + static List get_tasks_in_category(const String &p_category); +}; + +#define LIMBO_REGISTER_TASK(m_class) \ + if (m_class::_class_is_enabled) { \ + ::LimboTaskDB::register_task(); \ + } + +#define TASK_CATEGORY(m_cat) \ +public: \ + static _FORCE_INLINE_ String get_task_category() { \ + return String(#m_cat); \ + } \ + \ +private: + +#endif // LIMBO_TASK_DB_H \ No newline at end of file From 04cb2b15608e558c25b1c6057990c148ad8079df Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Fri, 25 Aug 2023 18:08:18 +0200 Subject: [PATCH 02/11] Sort tasks within task categories --- util/limbo_task_db.cpp | 4 ++++ util/limbo_task_db.h | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/util/limbo_task_db.cpp b/util/limbo_task_db.cpp index 94a2899..6fc4c08 100644 --- a/util/limbo_task_db.cpp +++ b/util/limbo_task_db.cpp @@ -83,6 +83,10 @@ void LimboTaskDB::scan_user_tasks() { String dir1 = GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_" + itos(i)); _populate_from_user_dir(dir1, &tasks_cache); } + + for (KeyValue> &E : tasks_cache) { + E.value.sort_custom(); + } } List LimboTaskDB::get_categories() { diff --git a/util/limbo_task_db.h b/util/limbo_task_db.h index 2849475..6ac587a 100644 --- a/util/limbo_task_db.h +++ b/util/limbo_task_db.h @@ -21,6 +21,12 @@ private: static HashMap> core_tasks; static HashMap> tasks_cache; + struct ComparatorByTaskName { + bool operator()(const String &p_left, const String &p_right) const { + return get_task_name(p_left) < get_task_name(p_right); + } + }; + public: template static void register_task() { @@ -39,6 +45,13 @@ public: static _FORCE_INLINE_ String get_misc_category() { return "Misc"; } static List get_categories(); static List get_tasks_in_category(const String &p_category); + static _FORCE_INLINE_ String get_task_name(String p_class_or_script_path) { + if (p_class_or_script_path.begins_with("res:")) { + return p_class_or_script_path.get_file().get_basename().trim_prefix("BT").to_pascal_case(); + } else { + return p_class_or_script_path.trim_prefix("BT"); + } + } }; #define LIMBO_REGISTER_TASK(m_class) \ From cff4626b2ddb97f5ea77a1a9a01b52fa1039957a Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Sat, 26 Aug 2023 10:08:01 +0200 Subject: [PATCH 03/11] Extract TaskPanel code => TaskPalette --- editor/limbo_ai_editor_plugin.cpp | 348 +---------------------------- editor/limbo_ai_editor_plugin.h | 73 +----- editor/task_palette.cpp | 354 ++++++++++++++++++++++++++++++ editor/task_palette.h | 91 ++++++++ 4 files changed, 454 insertions(+), 412 deletions(-) create mode 100644 editor/task_palette.cpp create mode 100644 editor/task_palette.h diff --git a/editor/limbo_ai_editor_plugin.cpp b/editor/limbo_ai_editor_plugin.cpp index accccdb..c77a0c5 100644 --- a/editor/limbo_ai_editor_plugin.cpp +++ b/editor/limbo_ai_editor_plugin.cpp @@ -357,340 +357,6 @@ TaskTree::~TaskTree() { //**** TaskTree ^ -//**** TaskButton - -Control *TaskButton::make_custom_tooltip(const String &p_text) const { - EditorHelpBit *help_bit = memnew(EditorHelpBit); - help_bit->get_rich_text()->set_custom_minimum_size(Size2(360 * EDSCALE, 1)); - - String help_text; - if (!p_text.is_empty()) { - help_text = p_text; - } else { - help_text = "[i]" + TTR("No description.") + "[/i]"; - } - - help_bit->set_text(help_text); - - return help_bit; -} - -//**** TaskButton ^ - -//**** TaskSection - -void TaskSection::_on_task_button_pressed(const String &p_task) { - emit_signal(SNAME("task_button_pressed"), p_task); -} - -void TaskSection::_on_task_button_gui_input(const Ref &p_event, const String &p_task) { - if (!p_event->is_pressed()) { - return; - } - - Ref mb = p_event; - if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT) { - emit_signal(SNAME("task_button_rmb"), p_task); - } -} - -void TaskSection::_on_header_pressed() { - set_collapsed(!is_collapsed()); -} - -void TaskSection::set_filter(String p_filter_text) { - int num_hidden = 0; - if (p_filter_text.is_empty()) { - for (int i = 0; i < tasks_container->get_child_count(); i++) { - Object::cast_to