Compare commits

..

14 Commits

Author SHA1 Message Date
Wilson E. Alvarez dd150f46d7
Fix unhandled PROPERTY_HINT_NO_NODEPATH warning
Due to upstream change:

	6f7525c396
2025-01-02 07:13:32 -05:00
Wilson E. Alvarez 9525eff8e0
Override renamed EditorPlugin::get_name() method
Due to upstream change:

	0ab3dc273e
2025-01-02 07:13:32 -05:00
Wilson E. Alvarez 903fc2279c
Refactor EditorHelpBit usage
Due to upstream change:

	4e19ab8afe
2025-01-02 07:13:32 -05:00
Wilson E. Alvarez 8aff1dbedf
Fix unhandled PROPERTY_HINT_ONESHOT warning
Due to upstream change:

	761a20f7a7
2025-01-02 07:13:32 -05:00
Wilson E. Alvarez b7cb41bf03
Fix forbidden comparisons between Ref and nullptr.
Necessary when compiling with strict_checks=yes.

Due to upstream change:

	df29cc696f
2025-01-02 07:13:32 -05:00
Wilson E. Alvarez ce4db8c086
Fix internal Button set_icon calls to set_button_icon
Due to upstream change:

    562c666e3d
2025-01-02 07:13:30 -05:00
Wilson E. Alvarez 10930ec63d
Fix unhandled PROPERTY_HINT_TOOL_BUTTON warning
Due to upstream change:

	85dfd89653
2025-01-02 07:11:22 -05:00
Wilson E. Alvarez 8d1d90ea48
Fix unhandled PROPERTY_HINT_DICTIONARY_TYPE warning
Due to upstream change:

	9853a69144
2025-01-02 07:11:22 -05:00
Wilson E. Alvarez 66c36afae7
Update EditorMainScreen calls after its extraction
Due to upstream change:

	5e1c9d68aa
2025-01-02 07:11:22 -05:00
Dave Palais b144ebc7bc
Do not show warning for NIL type BlackboardPlan variables on BBParam. (#267)
Enable variable mapping for NIL type public BlackboardPlan variables.
2025-01-01 18:14:39 +01:00
Serhii Snitsaruk 672f92c87b
Update doc 2024-12-30 01:24:52 +01:00
Serhii Snitsaruk e2e42b90a8 Fix documentation links in the editor 2024-12-29 16:03:21 -08:00
Serhii Snitsaruk a952009293
Add `Blackboard.print_state()` (#264)
Method that prints values of all variables in each scope.
2024-12-29 23:45:59 +01:00
Pheubel 0982804a86
Remove arbitrary limit on user task directories (#263)
Resolves GH-256
2024-12-28 19:03:51 +01:00
12 changed files with 68 additions and 22 deletions

View File

@ -10,6 +10,7 @@
*/
#include "blackboard.h"
#include "../util/limbo_compat.h"
#ifdef LIMBOAI_MODULE
#include "core/variant/variant.h"
@ -75,6 +76,26 @@ TypedArray<StringName> Blackboard::list_vars() const {
return var_names;
}
void Blackboard::print_state() const {
Ref<Blackboard> bb{ this };
int scope_idx = 0;
while (bb.is_valid()) {
int i = 0;
String line = "Scope " + itos(scope_idx) + ": { ";
for (const KeyValue<StringName, BBVariable> &kv : bb->data) {
if (i > 0) {
line += ", ";
}
line += String(kv.key) + ": " + String(kv.value.get_value());
i++;
}
line += " }";
PRINT_LINE(line);
bb = bb->get_parent();
scope_idx++;
}
}
Dictionary Blackboard::get_vars_as_dict() const {
Dictionary dict;
for (const KeyValue<StringName, BBVariable> &kv : data) {
@ -136,6 +157,7 @@ void Blackboard::_bind_methods() {
ClassDB::bind_method(D_METHOD("erase_var", "var_name"), &Blackboard::erase_var);
ClassDB::bind_method(D_METHOD("clear"), &Blackboard::clear);
ClassDB::bind_method(D_METHOD("list_vars"), &Blackboard::list_vars);
ClassDB::bind_method(D_METHOD("print_state"), &Blackboard::print_state);
ClassDB::bind_method(D_METHOD("get_vars_as_dict"), &Blackboard::get_vars_as_dict);
ClassDB::bind_method(D_METHOD("populate_from_dict", "dictionary"), &Blackboard::populate_from_dict);
ClassDB::bind_method(D_METHOD("top"), &Blackboard::top);

View File

@ -57,6 +57,7 @@ public:
void erase_var(const StringName &p_name);
void clear() { data.clear(); }
TypedArray<StringName> list_vars() const;
void print_state() const;
Dictionary get_vars_as_dict() const;
void populate_from_dict(const Dictionary &p_dictionary);

View File

@ -200,7 +200,7 @@ void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {
#ifdef TOOLS_ENABLED
// * Editor
if (!_is_var_hidden(var_name, var)) {
if (!_is_var_nil(var) || !_is_var_private(var_name, var)) {
if (has_mapping(var_name) || has_property_binding(var_name)) {
p_list->push_back(PropertyInfo(Variant::STRING, var_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
} else {
@ -226,7 +226,7 @@ void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {
if (is_mapping_enabled()) {
p_list->push_back(PropertyInfo(Variant::NIL, "Mapping", PROPERTY_HINT_NONE, "mapping/", PROPERTY_USAGE_GROUP));
for (const Pair<StringName, BBVariable> &p : var_list) {
if (_is_var_hidden(p.first, p.second)) {
if (_is_var_private(p.first, p.second)) {
continue;
}
if (unlikely(has_property_binding(p.first))) {
@ -242,7 +242,7 @@ void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {
// * Binding
p_list->push_back(PropertyInfo(Variant::NIL, "Binding", PROPERTY_HINT_NONE, "binding/", PROPERTY_USAGE_GROUP));
for (const Pair<StringName, BBVariable> &p : var_list) {
if (_is_var_hidden(p.first, p.second)) {
if (_is_var_nil(p.second) || _is_var_private(p.first, p.second)) {
continue;
}
if (unlikely(has_mapping(p.first))) {

View File

@ -51,7 +51,8 @@ private:
// If true, NodePath variables will be prefetched, so that the vars will contain node pointers instead (upon BB creation/population).
bool prefetch_nodepath_vars = true;
_FORCE_INLINE_ bool _is_var_hidden(const String &p_name, const BBVariable &p_var) const { return p_var.get_type() == Variant::NIL || (is_derived() && p_name.begins_with("_")); }
_FORCE_INLINE_ bool _is_var_nil(const BBVariable &p_var) const { return p_var.get_type() == Variant::NIL; }
_FORCE_INLINE_ bool _is_var_private(const String &p_name, const BBVariable &p_var) const { return is_derived() && p_name.begins_with("_"); }
protected:
static void _bind_methods();

View File

@ -54,6 +54,8 @@ Methods
+---------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |void| | :ref:`populate_from_dict<class_Blackboard_method_populate_from_dict>`\ (\ dictionary\: ``Dictionary``\ ) |
+---------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |void| | :ref:`print_state<class_Blackboard_method_print_state>`\ (\ ) |const| |
+---------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |void| | :ref:`set_parent<class_Blackboard_method_set_parent>`\ (\ blackboard\: :ref:`Blackboard<class_Blackboard>`\ ) |
+---------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |void| | :ref:`set_var<class_Blackboard_method_set_var>`\ (\ var_name\: ``StringName``, value\: ``Variant``\ ) |
@ -194,6 +196,18 @@ Fills the Blackboard with multiple variables from a dictionary. The dictionary k
----
.. _class_Blackboard_method_print_state:
.. rst-class:: classref-method
|void| **print_state**\ (\ ) |const| :ref:`🔗<class_Blackboard_method_print_state>`
Prints the values of all variables in each scope.
.. rst-class:: classref-item-separator
----
.. _class_Blackboard_method_set_parent:
.. rst-class:: classref-method

View File

@ -86,6 +86,12 @@
Fills the Blackboard with multiple variables from a dictionary. The dictionary keys must be variable names and the dictionary values must be variable values. Keys must be StringName or String.
</description>
</method>
<method name="print_state" qualifiers="const">
<return type="void" />
<description>
Prints the values of all variables in each scope.
</description>
</method>
<method name="set_parent">
<return type="void" />
<param index="0" name="blackboard" type="Blackboard" />

View File

@ -89,7 +89,7 @@ void EditorPropertyVariableName::_update_status() {
status_btn->set_button_icon(theme_cache.var_empty_icon);
status_btn->set_tooltip_text(TTR("Variable name not specified.\nClick to open the blackboard plan."));
} else if (plan->has_var(var_name)) {
if (expected_type == Variant::NIL || plan->get_var(var_name).get_type() == expected_type) {
if (expected_type == Variant::NIL || plan->get_var(var_name).get_type() == Variant::NIL || plan->get_var(var_name).get_type() == expected_type) {
status_btn->set_button_icon(theme_cache.var_exists_icon);
status_btn->set_tooltip_text(TTR("This variable is present in the blackboard plan.\nClick to open the blackboard plan."));
} else {

View File

@ -399,8 +399,7 @@ void LimboAIEditor::_set_as_dirty(const Ref<BehaviorTree> &p_bt, bool p_dirty) {
}
}
void LimboAIEditor::_create_user_task_dir() {
String task_dir = GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_1");
void LimboAIEditor::_create_user_task_dir(String task_dir) {
ERR_FAIL_COND_MSG(DirAccess::dir_exists_absolute(task_dir), "LimboAIEditor: Directory already exists: " + task_dir);
Error err = DirAccess::make_dir_recursive_absolute(task_dir);
@ -1045,7 +1044,10 @@ void LimboAIEditor::_on_filesystem_changed() {
}
void LimboAIEditor::_on_new_script_pressed() {
SCRIPT_EDITOR()->open_script_create_dialog("BTAction", String(GLOBAL_GET("limbo_ai/behavior_tree/user_task_dir_1")).path_join("new_task"));
PackedStringArray user_task_directories = GLOBAL_GET("limbo_ai/behavior_tree/user_task_dirs");
ERR_FAIL_INDEX_MSG(0, user_task_directories.size(), "LimboAI: No user task directory set");
String default_task_dir = user_task_directories[0];
SCRIPT_EDITOR()->open_script_create_dialog("BTAction", default_task_dir.path_join("new_task"));
}
void LimboAIEditor::_task_type_selected(const String &p_class_or_path) {
@ -1415,12 +1417,12 @@ void LimboAIEditor::_update_banners() {
}
}
for (String dir_setting : { "limbo_ai/behavior_tree/user_task_dir_1", "limbo_ai/behavior_tree/user_task_dir_2", "limbo_ai/behavior_tree/user_task_dir_3" }) {
String task_dir = GLOBAL_GET(dir_setting);
PackedStringArray user_task_directories = GLOBAL_GET("limbo_ai/behavior_tree/user_task_dirs");
for (const String &task_dir : user_task_directories) {
if (!task_dir.is_empty() && !DirAccess::dir_exists_absolute(task_dir)) {
ActionBanner *banner = memnew(ActionBanner);
banner->set_text(vformat(TTR("Task folder not found: %s"), task_dir));
banner->add_action(TTR("Create"), callable_mp(this, &LimboAIEditor::_create_user_task_dir), true);
banner->add_action(TTR("Create"), callable_mp(this, &LimboAIEditor::_create_user_task_dir).bind(task_dir), true);
banner->add_action(TTR("Edit Path..."), callable_mp(this, &LimboAIEditor::_edit_project_settings));
banner->add_spacer();
banner->add_action(TTR("Help..."), callable_mp(LimboUtility::get_singleton(), &LimboUtility::open_doc_custom_tasks));
@ -1893,9 +1895,9 @@ LimboAIEditor::LimboAIEditor() {
BASE_CONTROL()->add_child(disk_changed);
GLOBAL_DEF(PropertyInfo(Variant::STRING, "limbo_ai/behavior_tree/behavior_tree_default_dir", PROPERTY_HINT_DIR), "res://ai/trees");
GLOBAL_DEF(PropertyInfo(Variant::STRING, "limbo_ai/behavior_tree/user_task_dir_1", PROPERTY_HINT_DIR), "res://ai/tasks");
GLOBAL_DEF(PropertyInfo(Variant::STRING, "limbo_ai/behavior_tree/user_task_dir_2", PROPERTY_HINT_DIR), "");
GLOBAL_DEF(PropertyInfo(Variant::STRING, "limbo_ai/behavior_tree/user_task_dir_3", PROPERTY_HINT_DIR), "");
PackedStringArray user_task_dir_default;
user_task_dir_default.append("res://ai/tasks");
GLOBAL_DEF(PropertyInfo(Variant::PACKED_STRING_ARRAY, "limbo_ai/behavior_tree/user_task_dirs", PROPERTY_HINT_TYPE_STRING, vformat("%s/%s:", Variant::STRING, PROPERTY_HINT_DIR)), user_task_dir_default);
String bt_default_dir = GLOBAL_GET("limbo_ai/behavior_tree/behavior_tree_default_dir");
save_dialog->set_current_dir(bt_default_dir);

View File

@ -213,7 +213,7 @@ private:
void _update_task_tree(const Ref<BehaviorTree> &p_bt, const Ref<BTTask> &p_specific_task = nullptr);
void _disable_editing();
void _set_as_dirty(const Ref<BehaviorTree> &p_bt, bool p_dirty);
void _create_user_task_dir();
void _create_user_task_dir(String task_dir);
void _remove_task_from_favorite(const String &p_task);
void _save_and_restart();
void _extract_subtree(const String &p_path);

View File

@ -4,7 +4,7 @@ major = 1
minor = 3
patch = 0
status = "dev"
doc_branch = "master"
doc_branch = "latest"
godot_cpp_ref = "godot-4.3-stable"
# Code that generates version header

View File

@ -95,9 +95,9 @@ void LimboTaskDB::scan_user_tasks() {
tasks_cache[LimboTaskDB::get_misc_category()] = List<String>();
}
for (int i = 1; i < 4; i++) {
String dir1 = ProjectSettings::get_singleton()->get_setting_with_override("limbo_ai/behavior_tree/user_task_dir_" + itos(i));
_populate_from_user_dir(dir1, &tasks_cache);
PackedStringArray user_task_directories = GLOBAL_GET("limbo_ai/behavior_tree/user_task_dirs");
for (const String &user_task_dir : user_task_directories) {
_populate_from_user_dir(user_task_dir, &tasks_cache);
}
for (KeyValue<String, List<String>> &E : tasks_cache) {

View File

@ -587,7 +587,7 @@ inline void _open_online_doc_page(const String &p_page) {
}
void LimboUtility::open_doc_introduction() {
_open_online_doc_page("getting-started/introduction.html");
_open_online_doc_page("behavior-trees/introduction.html");
}
void LimboUtility::open_doc_online() {
@ -595,11 +595,11 @@ void LimboUtility::open_doc_online() {
}
void LimboUtility::open_doc_gdextension_limitations() {
_open_online_doc_page("getting-started/gdextension.html#limitations-of-the-gdextension-version");
_open_online_doc_page("getting-started/getting-limboai.html#get-gdextension-version");
}
void LimboUtility::open_doc_custom_tasks() {
_open_online_doc_page("getting-started/custom-tasks.html");
_open_online_doc_page("behavior-trees/custom-tasks.html");
}
void LimboUtility::open_doc_class(const String &p_class_name) {