Compare commits
25 Commits
224ceda390
...
a00eb6df69
Author | SHA1 | Date |
---|---|---|
Wilson E. Alvarez | a00eb6df69 | |
Wilson E. Alvarez | 70994acd15 | |
Wilson E. Alvarez | 9a869115b4 | |
Wilson E. Alvarez | 364620fee0 | |
Wilson E. Alvarez | 63544281e5 | |
Serhii Snitsaruk | ba90deaa6a | |
Serhii Snitsaruk | 08d8fcdf92 | |
Serhii Snitsaruk | 51878e4b6e | |
Serhii Snitsaruk | 008702b1e4 | |
Serhii Snitsaruk | 6e8c22d598 | |
Serhii Snitsaruk | 0b3b11a383 | |
Serhii Snitsaruk | 4cac6276aa | |
Serhii Snitsaruk | ea671dd54b | |
Serhii Snitsaruk | bc7f677810 | |
Serhii Snitsaruk | 057d4e669c | |
Serhii Snitsaruk | f600d633a3 | |
Serhii Snitsaruk | ae61d551c0 | |
Serhii Snitsaruk | a14cc4cc68 | |
Serhii Snitsaruk | d304f957ef | |
Serhii Snitsaruk | bf85350260 | |
Serhii Snitsaruk | 03476721d9 | |
Serhii Snitsaruk | bebd6a15eb | |
Serhii Snitsaruk | 4c9028fc66 | |
Serhii Snitsaruk | e21156df35 | |
Serhii Snitsaruk | 591a1fe672 |
|
@ -5,7 +5,7 @@ on:
|
|||
godot-ref:
|
||||
description: A tag, branch or commit hash in the Godot repository.
|
||||
type: string
|
||||
default: 4.3
|
||||
default: 4.3-stable
|
||||
limboai-ref:
|
||||
description: A tag, branch or commit hash in the LimboAI repository.
|
||||
type: string
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
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_blackboard, Variant(), "BBNode: get_value() failed - blackboard is null.");
|
||||
ERR_FAIL_COND_V_MSG(p_blackboard.is_null(), Variant(), "BBNode: get_value() failed - blackboard is null.");
|
||||
|
||||
Variant val;
|
||||
if (get_value_source() == SAVED_VALUE) {
|
||||
|
|
|
@ -13,9 +13,20 @@
|
|||
|
||||
#include "../util/limbo_utility.h"
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/editor_interface.h"
|
||||
#elif LIMBOAI_GDEXTENSION
|
||||
#include <godot_cpp/classes/editor_inspector.hpp>
|
||||
#include <godot_cpp/classes/editor_interface.hpp>
|
||||
#include <godot_cpp/classes/engine.hpp>
|
||||
#include <godot_cpp/classes/scene_tree.hpp>
|
||||
#endif
|
||||
|
||||
bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
|
||||
String name_str = p_name;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// * Editor
|
||||
if (var_map.has(p_name)) {
|
||||
BBVariable &var = var_map[p_name];
|
||||
|
@ -26,29 +37,51 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
// * Mapping
|
||||
if (name_str.begins_with("mapping/")) {
|
||||
StringName mapped_var_name = name_str.get_slicec('/', 1);
|
||||
StringName value = p_value;
|
||||
bool properties_changed = false;
|
||||
bool prop_list_changed = false;
|
||||
if (value == StringName()) {
|
||||
if (parent_scope_mapping.has(mapped_var_name)) {
|
||||
properties_changed = true;
|
||||
prop_list_changed = true;
|
||||
parent_scope_mapping.erase(mapped_var_name);
|
||||
}
|
||||
} else {
|
||||
if (!parent_scope_mapping.has(mapped_var_name)) {
|
||||
properties_changed = true;
|
||||
prop_list_changed = true;
|
||||
}
|
||||
parent_scope_mapping[mapped_var_name] = value;
|
||||
}
|
||||
if (properties_changed) {
|
||||
if (prop_list_changed) {
|
||||
notify_property_list_changed();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// * Binding
|
||||
if (name_str.begins_with("binding/")) {
|
||||
StringName bound_var = name_str.get_slicec('/', 1);
|
||||
NodePath value = p_value;
|
||||
bool prop_list_changed = false;
|
||||
if (value.is_empty()) {
|
||||
if (property_bindings.has(bound_var)) {
|
||||
prop_list_changed = true;
|
||||
property_bindings.erase(bound_var);
|
||||
}
|
||||
} else {
|
||||
if (!property_bindings.has(bound_var)) {
|
||||
prop_list_changed = true;
|
||||
}
|
||||
property_bindings[bound_var] = value;
|
||||
}
|
||||
if (prop_list_changed) {
|
||||
notify_property_list_changed();
|
||||
}
|
||||
}
|
||||
|
||||
// * Storage
|
||||
if (name_str.begins_with("var/")) {
|
||||
StringName var_name = name_str.get_slicec('/', 1);
|
||||
|
@ -66,6 +99,8 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
|
|||
var_map[var_name].set_hint((PropertyHint)(int)p_value);
|
||||
} else if (what == "hint_string") {
|
||||
var_map[var_name].set_hint_string(p_value);
|
||||
} else if (what == "property_binding") {
|
||||
property_bindings[var_name] = NodePath(p_value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -78,28 +113,64 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
|
|||
bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
String name_str = p_name;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// * Editor
|
||||
if (var_map.has(p_name)) {
|
||||
if (has_mapping(p_name)) {
|
||||
r_ret = "Mapped to " + LimboUtility::get_singleton()->decorate_var(parent_scope_mapping[p_name]);
|
||||
} else if (has_property_binding(p_name)) {
|
||||
const NodePath &binding = property_bindings[p_name];
|
||||
|
||||
Node *edited_node = Object::cast_to<Node>(EditorInterface::get_singleton()->get_inspector()->get_edited_object());
|
||||
if (!edited_node) {
|
||||
edited_node = SCENE_TREE()->get_edited_scene_root();
|
||||
}
|
||||
Node *bound_node = edited_node ? edited_node->get_node_or_null(binding) : nullptr;
|
||||
|
||||
String shortened_path;
|
||||
if (bound_node) {
|
||||
shortened_path = (String)bound_node->get_name() +
|
||||
":" + (String)binding.get_concatenated_subnames();
|
||||
} else {
|
||||
shortened_path = (String)binding.get_name(binding.get_name_count() - 1) +
|
||||
":" + (String)binding.get_concatenated_subnames();
|
||||
}
|
||||
r_ret = String::utf8("🔗 ") + shortened_path;
|
||||
} else {
|
||||
r_ret = var_map[p_name].get_value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
// * Mapping
|
||||
if (name_str.begins_with("mapping/")) {
|
||||
StringName mapped_var_name = name_str.get_slicec('/', 1);
|
||||
ERR_FAIL_COND_V(mapped_var_name == StringName(), false);
|
||||
if (parent_scope_mapping.has(mapped_var_name)) {
|
||||
if (has_mapping(mapped_var_name)) {
|
||||
r_ret = parent_scope_mapping[mapped_var_name];
|
||||
} else if (has_property_binding(mapped_var_name)) {
|
||||
r_ret = RTR("Already bound to property.");
|
||||
} else {
|
||||
r_ret = StringName();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// * Binding
|
||||
if (name_str.begins_with("binding/")) {
|
||||
StringName bound_var = name_str.get_slicec('/', 1);
|
||||
ERR_FAIL_COND_V(bound_var == StringName(), false);
|
||||
if (has_property_binding(bound_var)) {
|
||||
r_ret = property_bindings[bound_var];
|
||||
} else if (has_mapping(bound_var)) {
|
||||
r_ret = RTR("Already mapped to variable.");
|
||||
} else {
|
||||
r_ret = NodePath();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// * Storage
|
||||
if (!name_str.begins_with("var/")) {
|
||||
return false;
|
||||
|
@ -127,14 +198,16 @@ void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {
|
|||
String var_name = p.first;
|
||||
BBVariable var = p.second;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// * Editor
|
||||
if (var.get_type() != Variant::NIL && (!is_derived() || !var_name.begins_with("_"))) {
|
||||
if (has_mapping(var_name)) {
|
||||
if (!_is_var_hidden(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 {
|
||||
p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR));
|
||||
}
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
// * Storage
|
||||
if (is_derived() && (!var.is_value_changed() || var.get_value() == base->var_map[var_name].get_value())) {
|
||||
|
@ -153,9 +226,32 @@ 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) {
|
||||
// Serialize only non-empty mappings.
|
||||
PropertyUsageFlags usage = has_mapping(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR;
|
||||
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "mapping/" + p.first, PROPERTY_HINT_NONE, "", usage));
|
||||
if (_is_var_hidden(p.first, p.second)) {
|
||||
continue;
|
||||
}
|
||||
if (unlikely(has_property_binding(p.first))) {
|
||||
p_list->push_back(PropertyInfo(Variant::STRING, "mapping/" + p.first, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
|
||||
} else {
|
||||
// Serialize only non-empty mappings.
|
||||
PropertyUsageFlags usage = has_mapping(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR;
|
||||
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "mapping/" + p.first, PROPERTY_HINT_NONE, "", usage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// * 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)) {
|
||||
continue;
|
||||
}
|
||||
if (unlikely(has_mapping(p.first))) {
|
||||
p_list->push_back(PropertyInfo(Variant::STRING, "binding/" + p.first, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
|
||||
} else {
|
||||
PropertyUsageFlags usage = has_property_binding(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR;
|
||||
// PROPERTY_HINT_LINK is used to signal that NodePath should point to a property.
|
||||
// Our inspector plugin will know how to handle it.
|
||||
p_list->push_back(PropertyInfo(Variant::NODE_PATH, "binding/" + p.first, PROPERTY_HINT_LINK, itos(p.second.get_type()), usage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,6 +295,11 @@ bool BlackboardPlan::has_mapping(const StringName &p_name) const {
|
|||
return is_mapping_enabled() && parent_scope_mapping.has(p_name) && parent_scope_mapping[p_name] != StringName();
|
||||
}
|
||||
|
||||
void BlackboardPlan::set_property_binding(const StringName &p_name, const NodePath &p_path) {
|
||||
property_bindings[p_name] = p_path;
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
void BlackboardPlan::set_prefetch_nodepath_vars(bool p_enable) {
|
||||
prefetch_nodepath_vars = p_enable;
|
||||
emit_changed();
|
||||
|
@ -410,8 +511,9 @@ void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bo
|
|||
#endif
|
||||
continue;
|
||||
}
|
||||
bool is_bound = has_property_binding(p.first) || (is_derived() && get_base_plan()->has_property_binding(p.first));
|
||||
bool has_mapping = parent_scope_mapping.has(p.first);
|
||||
bool do_prefetch = !has_mapping && prefetch_nodepath_vars;
|
||||
bool do_prefetch = !is_bound && !has_mapping && prefetch_nodepath_vars;
|
||||
|
||||
// Add a variable duplicate to the blackboard, optionally with NodePath prefetch.
|
||||
BBVariable var = p.second.duplicate(true);
|
||||
|
@ -430,9 +532,27 @@ void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bo
|
|||
if (has_mapping) {
|
||||
StringName target_var = parent_scope_mapping[p.first];
|
||||
if (target_var != StringName()) {
|
||||
ERR_CONTINUE_MSG(p_blackboard->get_parent() == nullptr, vformat("BlackboardPlan: Cannot link variable %s to parent scope because the parent scope is not set.", LimboUtility::get_singleton()->decorate_var(p.first)));
|
||||
ERR_CONTINUE_MSG(p_blackboard->get_parent().is_null(), vformat("BlackboardPlan: Cannot link variable %s to parent scope because the parent scope is not set.", LimboUtility::get_singleton()->decorate_var(p.first)));
|
||||
p_blackboard->link_var(p.first, p_blackboard->get_parent(), target_var);
|
||||
}
|
||||
} else if (is_bound) {
|
||||
// Bind variable to a property of a scene node.
|
||||
NodePath binding_path;
|
||||
Node *binding_root;
|
||||
if (has_property_binding(p.first)) {
|
||||
binding_path = property_bindings[p.first];
|
||||
binding_root = p_prefetch_root;
|
||||
} else {
|
||||
binding_path = get_base_plan()->property_bindings[p.first];
|
||||
binding_root = p_prefetch_root_for_base_plan;
|
||||
}
|
||||
ERR_CONTINUE_MSG(binding_path.get_subname_count() != 1, vformat("BlackboardPlan: Can't bind variable %s using property path that contains multiple sub-names: %s", LimboUtility::get_singleton()->decorate_var(p.first), binding_path));
|
||||
NodePath node_path{ binding_path.get_concatenated_names() };
|
||||
StringName prop_name = binding_path.get_subname(0);
|
||||
// TODO: Implement binding for base plan as well.
|
||||
Node *n = binding_root->get_node_or_null(node_path);
|
||||
ERR_CONTINUE_MSG(n == nullptr, vformat("BlackboardPlan: Binding failed for variable %s using property path: %s", LimboUtility::get_singleton()->decorate_var(p.first), binding_path));
|
||||
var.bind(n, prop_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,18 +34,25 @@ private:
|
|||
// When base is not null, the plan is considered to be derived from the base plan.
|
||||
// A derived plan can only have variables that exist in the base plan,
|
||||
// and only the values can be different in those variables.
|
||||
// The derived plan is synced with the base plan to maintain consistency.
|
||||
Ref<BlackboardPlan> base;
|
||||
|
||||
// Mapping between variables in this plan and their parent scope names.
|
||||
// Used for linking variables to their parent scope counterparts upon Blackboard creation/population.
|
||||
HashMap<StringName, StringName> parent_scope_mapping;
|
||||
// Fetcher function for the parent scope plan. Funtion should return a Ref<BlackboardPlan>.
|
||||
// Used in the inspector. When set, mapping feature becomes available.
|
||||
// Fetcher function for the parent scope plan. Function should return a Ref<BlackboardPlan>.
|
||||
// Used in the inspector: enables mapping feature when set.
|
||||
Callable parent_scope_plan_provider;
|
||||
|
||||
// Bindings to properties in the scene to which this plan belongs.
|
||||
HashMap<StringName, NodePath> property_bindings;
|
||||
bool property_binding_enabled = false;
|
||||
|
||||
// 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("_")); }
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
|
@ -69,6 +76,10 @@ public:
|
|||
bool is_mapping_enabled() const { return parent_scope_plan_provider.is_valid() && (parent_scope_plan_provider.call() != Ref<BlackboardPlan>()); }
|
||||
bool has_mapping(const StringName &p_name) const;
|
||||
|
||||
bool has_property_binding(const StringName &p_name) const { return property_bindings.has(p_name); }
|
||||
void set_property_binding(const StringName &p_name, const NodePath &p_path);
|
||||
NodePath get_property_binding(const StringName &p_name) const { return property_bindings.has(p_name) ? property_bindings[p_name] : NodePath(); }
|
||||
|
||||
void set_prefetch_nodepath_vars(bool p_enable);
|
||||
bool is_prefetching_nodepath_vars() const;
|
||||
|
||||
|
|
|
@ -78,10 +78,10 @@ void BehaviorTree::copy_other(const Ref<BehaviorTree> &p_other) {
|
|||
}
|
||||
|
||||
Ref<BTInstance> BehaviorTree::instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_instance_owner, Node *p_custom_scene_root) const {
|
||||
ERR_FAIL_COND_V_MSG(root_task == nullptr, nullptr, "BehaviorTree: Instantiation failed - BT has no valid root task.");
|
||||
ERR_FAIL_COND_V_MSG(root_task.is_null(), nullptr, "BehaviorTree: Instantiation failed - BT has no valid root task.");
|
||||
ERR_FAIL_NULL_V_MSG(p_agent, nullptr, "BehaviorTree: Instantiation failed - agent can't be null.");
|
||||
ERR_FAIL_NULL_V_MSG(p_instance_owner, nullptr, "BehaviorTree: Instantiation failed -- instance owner can't be null.");
|
||||
ERR_FAIL_NULL_V_MSG(p_blackboard, nullptr, "BehaviorTree: Instantiation failed - blackboard can't be null.");
|
||||
ERR_FAIL_COND_V_MSG(p_blackboard.is_null(), nullptr, "BehaviorTree: Instantiation failed - blackboard can't be null.");
|
||||
Node *scene_root = p_custom_scene_root ? p_custom_scene_root : p_instance_owner->get_owner();
|
||||
ERR_FAIL_NULL_V_MSG(scene_root, nullptr, "BehaviorTree: Instantiation failed - unable to establish scene root. This is likely due to the instance owner not being owned by a scene node and custom_scene_root being null.");
|
||||
Ref<BTTask> root_copy = root_task->clone();
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#endif
|
||||
|
||||
Ref<BTInstance> BTInstance::create(Ref<BTTask> p_root_task, String p_source_bt_path, Node *p_owner_node) {
|
||||
ERR_FAIL_NULL_V(p_root_task, nullptr);
|
||||
ERR_FAIL_COND_V(p_root_task.is_null(), nullptr);
|
||||
ERR_FAIL_NULL_V(p_owner_node, nullptr);
|
||||
Ref<BTInstance> inst;
|
||||
inst.instantiate();
|
||||
|
@ -102,7 +102,7 @@ double BTInstance::_get_mean_update_time_msec_and_reset() {
|
|||
|
||||
void BTInstance::_add_custom_monitor() {
|
||||
ERR_FAIL_NULL(get_owner_node());
|
||||
ERR_FAIL_NULL(root_task);
|
||||
ERR_FAIL_COND(root_task.is_null());
|
||||
ERR_FAIL_NULL(root_task->get_agent());
|
||||
|
||||
if (monitor_id == StringName()) {
|
||||
|
|
|
@ -98,7 +98,7 @@ void BTState::_update(double p_delta) {
|
|||
// Bail out if a transition happened in the meantime.
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_NULL(bt_instance);
|
||||
ERR_FAIL_COND(bt_instance.is_null());
|
||||
BT::Status status = bt_instance->update(p_delta);
|
||||
if (status == BTTask::SUCCESS) {
|
||||
get_root()->dispatch(success_event, Variant());
|
||||
|
|
|
@ -30,7 +30,7 @@ PackedStringArray BTComment::get_configuration_warnings() {
|
|||
if (get_child_count_excluding_comments() > 0) {
|
||||
warnings.append("Can only have other comment tasks as children.");
|
||||
}
|
||||
if (get_parent() == nullptr) {
|
||||
if (get_parent().is_null()) {
|
||||
warnings.append("Can't be the root task.");
|
||||
}
|
||||
return warnings;
|
||||
|
|
|
@ -163,7 +163,7 @@ void BTTask::set_custom_name(const String &p_name) {
|
|||
|
||||
void BTTask::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
|
||||
ERR_FAIL_NULL(p_agent);
|
||||
ERR_FAIL_NULL(p_blackboard);
|
||||
ERR_FAIL_COND(p_blackboard.is_null());
|
||||
ERR_FAIL_NULL(p_scene_root);
|
||||
data.agent = p_agent;
|
||||
data.blackboard = p_blackboard;
|
||||
|
|
|
@ -80,7 +80,7 @@ void BTCooldown::_chill() {
|
|||
timer->set_time_left(duration);
|
||||
} else {
|
||||
timer = SCENE_TREE()->create_timer(duration, process_pause);
|
||||
ERR_FAIL_NULL(timer);
|
||||
ERR_FAIL_COND(timer.is_null());
|
||||
timer->connect(LW_NAME(timeout), callable_mp(this, &BTCooldown::_on_timeout), CONNECT_ONE_SHOT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,16 +32,16 @@ void BTNewScope::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void BTNewScope::_set_parent_scope_plan_from_bt() {
|
||||
ERR_FAIL_NULL(get_blackboard_plan());
|
||||
ERR_FAIL_COND(get_blackboard_plan().is_null());
|
||||
Ref<BehaviorTree> bt = get_root()->editor_get_behavior_tree();
|
||||
ERR_FAIL_NULL(bt);
|
||||
ERR_FAIL_COND(bt.is_null());
|
||||
get_blackboard_plan()->set_parent_scope_plan_provider(callable_mp(bt.ptr(), &BehaviorTree::get_blackboard_plan));
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
void BTNewScope::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
|
||||
ERR_FAIL_COND(p_agent == nullptr);
|
||||
ERR_FAIL_COND(p_blackboard == nullptr);
|
||||
ERR_FAIL_COND(p_blackboard.is_null());
|
||||
|
||||
Ref<Blackboard> bb;
|
||||
if (blackboard_plan.is_valid()) {
|
||||
|
|
|
@ -234,7 +234,7 @@ Called when the state is entered.
|
|||
|
||||
|void| **_exit**\ (\ ) |virtual| :ref:`🔗<class_LimboState_private_method__exit>`
|
||||
|
||||
Called when the state is exited.
|
||||
Called when the state is exited. This happens on a transition to another state, and when the state machine is removed from the scene tree (e.g., when the node is freed with :ref:`Node.queue_free<class_Node_method_queue_free>` or the scene changes). Due to implementation details, :ref:`_exit<class_LimboState_private_method__exit>` will not be called on :ref:`Object.free<class_Object_method_free>`!
|
||||
|
||||
.. rst-class:: classref-item-separator
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<method name="_exit" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Called when the state is exited.
|
||||
Called when the state is exited. This happens on a transition to another state, and when the state machine is removed from the scene tree (e.g., when the node is freed with [method Node.queue_free] or the scene changes). Due to implementation details, [method _exit] will not be called on [method Object.free]!
|
||||
</description>
|
||||
</method>
|
||||
<method name="_setup" qualifiers="virtual">
|
||||
|
|
|
@ -46,7 +46,7 @@ LineEdit *BlackboardPlanEditor::_get_name_edit(int p_row_index) const {
|
|||
}
|
||||
|
||||
void BlackboardPlanEditor::_add_var() {
|
||||
ERR_FAIL_NULL(plan);
|
||||
ERR_FAIL_COND(plan.is_null());
|
||||
|
||||
int suffix = 1;
|
||||
StringName var_name = default_var_name == StringName() ? "var" : default_var_name;
|
||||
|
@ -65,14 +65,14 @@ void BlackboardPlanEditor::_add_var() {
|
|||
}
|
||||
|
||||
void BlackboardPlanEditor::_trash_var(int p_index) {
|
||||
ERR_FAIL_NULL(plan);
|
||||
ERR_FAIL_COND(plan.is_null());
|
||||
StringName var_name = plan->get_var_by_index(p_index).first;
|
||||
plan->remove_var(var_name);
|
||||
_refresh();
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_rename_var(const StringName &p_new_name, int p_index) {
|
||||
ERR_FAIL_NULL(plan);
|
||||
ERR_FAIL_COND(plan.is_null());
|
||||
|
||||
LineEdit *name_edit = _get_name_edit(p_index);
|
||||
ERR_FAIL_NULL(name_edit);
|
||||
|
@ -96,7 +96,7 @@ void BlackboardPlanEditor::_rename_var(const StringName &p_new_name, int p_index
|
|||
}
|
||||
|
||||
void BlackboardPlanEditor::_change_var_type(Variant::Type p_new_type, int p_index) {
|
||||
ERR_FAIL_NULL(plan);
|
||||
ERR_FAIL_COND(plan.is_null());
|
||||
|
||||
BBVariable var = plan->get_var_by_index(p_index).second;
|
||||
if (var.get_type() == p_new_type) {
|
||||
|
@ -115,14 +115,14 @@ void BlackboardPlanEditor::_change_var_type(Variant::Type p_new_type, int p_inde
|
|||
}
|
||||
|
||||
void BlackboardPlanEditor::_change_var_hint(PropertyHint p_new_hint, int p_index) {
|
||||
ERR_FAIL_NULL(plan);
|
||||
ERR_FAIL_COND(plan.is_null());
|
||||
plan->get_var_by_index(p_index).second.set_hint(p_new_hint);
|
||||
plan->notify_property_list_changed();
|
||||
_refresh();
|
||||
}
|
||||
|
||||
void BlackboardPlanEditor::_change_var_hint_string(const String &p_new_hint_string, int p_index) {
|
||||
ERR_FAIL_NULL(plan);
|
||||
ERR_FAIL_COND(plan.is_null());
|
||||
plan->get_var_by_index(p_index).second.set_hint_string(p_new_hint_string);
|
||||
plan->notify_property_list_changed();
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ void BlackboardPlanEditor::_refresh() {
|
|||
Button *drag_button = memnew(Button);
|
||||
props_hbox->add_child(drag_button);
|
||||
drag_button->set_custom_minimum_size(Size2(28.0, 28.0) * EDSCALE);
|
||||
BUTTON_SET_ICON(drag_button, theme_cache.grab_icon);
|
||||
drag_button->set_button_icon(theme_cache.grab_icon);
|
||||
drag_button->connect(LW_NAME(gui_input), callable_mp(this, &BlackboardPlanEditor::_drag_button_gui_input));
|
||||
drag_button->connect(LW_NAME(button_down), callable_mp(this, &BlackboardPlanEditor::_drag_button_down).bind(row_panel));
|
||||
drag_button->connect(LW_NAME(button_up), callable_mp(this, &BlackboardPlanEditor::_drag_button_up));
|
||||
|
@ -297,7 +297,7 @@ void BlackboardPlanEditor::_refresh() {
|
|||
type_choice->set_custom_minimum_size(Size2(170, 0.0) * EDSCALE);
|
||||
type_choice->set_text(Variant::get_type_name(var.get_type()));
|
||||
type_choice->set_tooltip_text(Variant::get_type_name(var.get_type()));
|
||||
BUTTON_SET_ICON(type_choice, get_theme_icon(Variant::get_type_name(var.get_type()), LW_NAME(EditorIcons)));
|
||||
type_choice->set_button_icon(get_theme_icon(Variant::get_type_name(var.get_type()), LW_NAME(EditorIcons)));
|
||||
type_choice->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
|
||||
type_choice->set_flat(true);
|
||||
type_choice->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
|
||||
|
@ -326,7 +326,7 @@ void BlackboardPlanEditor::_refresh() {
|
|||
Button *trash_button = memnew(Button);
|
||||
props_hbox->add_child(trash_button);
|
||||
trash_button->set_custom_minimum_size(Size2(24.0, 0.0) * EDSCALE);
|
||||
BUTTON_SET_ICON(trash_button, theme_cache.trash_icon);
|
||||
trash_button->set_button_icon(theme_cache.trash_icon);
|
||||
trash_button->connect(LW_NAME(pressed), callable_mp(this, &BlackboardPlanEditor::_trash_var).bind(i));
|
||||
}
|
||||
}
|
||||
|
@ -337,7 +337,7 @@ void BlackboardPlanEditor::_notification(int p_what) {
|
|||
theme_cache.trash_icon = get_theme_icon(LW_NAME(Remove), LW_NAME(EditorIcons));
|
||||
theme_cache.grab_icon = get_theme_icon(LW_NAME(TripleBar), LW_NAME(EditorIcons));
|
||||
|
||||
BUTTON_SET_ICON(add_var_tool, get_theme_icon(LW_NAME(Add), LW_NAME(EditorIcons)));
|
||||
add_var_tool->set_button_icon(get_theme_icon(LW_NAME(Add), LW_NAME(EditorIcons)));
|
||||
|
||||
type_menu->clear();
|
||||
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
||||
|
@ -472,14 +472,14 @@ BlackboardPlanEditor::BlackboardPlanEditor() {
|
|||
// ***** EditorInspectorPluginBBPlan *****
|
||||
|
||||
void EditorInspectorPluginBBPlan::_edit_plan(const Ref<BlackboardPlan> &p_plan) {
|
||||
ERR_FAIL_NULL(p_plan);
|
||||
ERR_FAIL_COND(p_plan.is_null());
|
||||
plan_editor->edit_plan(p_plan);
|
||||
plan_editor->popup_centered();
|
||||
}
|
||||
|
||||
void EditorInspectorPluginBBPlan::_open_base_plan(const Ref<BlackboardPlan> &p_plan) {
|
||||
ERR_FAIL_NULL(p_plan);
|
||||
ERR_FAIL_NULL(p_plan->get_base_plan());
|
||||
ERR_FAIL_COND(p_plan.is_null());
|
||||
ERR_FAIL_COND(p_plan->get_base_plan().is_null());
|
||||
EditorInterface::get_singleton()->call_deferred("edit_resource", p_plan->get_base_plan());
|
||||
}
|
||||
|
||||
|
@ -501,7 +501,7 @@ void EditorInspectorPluginBBPlan::parse_begin(Object *p_object) {
|
|||
void EditorInspectorPluginBBPlan::_parse_begin(Object *p_object) {
|
||||
#endif
|
||||
Ref<BlackboardPlan> plan = Object::cast_to<BlackboardPlan>(p_object);
|
||||
ERR_FAIL_NULL(plan);
|
||||
ERR_FAIL_COND(plan.is_null());
|
||||
|
||||
PanelContainer *panel = memnew(PanelContainer);
|
||||
ADD_STYLEBOX_OVERRIDE(panel, LW_NAME(panel), toolbar_style);
|
||||
|
@ -522,7 +522,7 @@ void EditorInspectorPluginBBPlan::_parse_begin(Object *p_object) {
|
|||
goto_btn->set_text(TTR("Edit Base"));
|
||||
goto_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
goto_btn->set_custom_minimum_size(Size2(150.0, 0.0) * EDSCALE);
|
||||
BUTTON_SET_ICON(goto_btn, EditorInterface::get_singleton()->get_editor_theme()->get_icon(LW_NAME(Edit), LW_NAME(EditorIcons)));
|
||||
goto_btn->set_button_icon(EditorInterface::get_singleton()->get_editor_theme()->get_icon(LW_NAME(Edit), LW_NAME(EditorIcons)));
|
||||
goto_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_open_base_plan).bind(plan));
|
||||
} else {
|
||||
Button *edit_btn = memnew(Button);
|
||||
|
@ -530,7 +530,7 @@ void EditorInspectorPluginBBPlan::_parse_begin(Object *p_object) {
|
|||
edit_btn->set_text(TTR("Manage..."));
|
||||
edit_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
edit_btn->set_custom_minimum_size(Size2(150.0, 0.0) * EDSCALE);
|
||||
BUTTON_SET_ICON(edit_btn, EditorInterface::get_singleton()->get_editor_theme()->get_icon(LW_NAME(EditAddRemove), LW_NAME(EditorIcons)));
|
||||
edit_btn->set_button_icon(EditorInterface::get_singleton()->get_editor_theme()->get_icon(LW_NAME(EditAddRemove), LW_NAME(EditorIcons)));
|
||||
edit_btn->connect(LW_NAME(pressed), callable_mp(this, &EditorInspectorPluginBBPlan::_edit_plan).bind(plan));
|
||||
}
|
||||
|
||||
|
|
|
@ -209,7 +209,7 @@ void LimboDebuggerTab::_notification(int p_what) {
|
|||
} break;
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
alert_icon->set_texture(get_theme_icon(LW_NAME(StatusWarning), LW_NAME(EditorIcons)));
|
||||
BUTTON_SET_ICON(resource_header, LimboUtility::get_singleton()->get_task_icon("BehaviorTree"));
|
||||
resource_header->set_button_icon(LimboUtility::get_singleton()->get_task_icon("BehaviorTree"));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -287,14 +287,14 @@ void EditorPropertyBBParam::update_property() {
|
|||
variable_editor->update_property();
|
||||
variable_editor->show();
|
||||
bottom_container->hide();
|
||||
type_choice->set_icon(get_editor_theme_icon(SNAME("LimboExtraVariable")));
|
||||
type_choice->set_button_icon(get_editor_theme_icon(SNAME("LimboExtraVariable")));
|
||||
} else {
|
||||
_create_value_editor(param->get_type());
|
||||
variable_editor->hide();
|
||||
value_editor->show();
|
||||
value_editor->set_object_and_property(param.ptr(), SNAME("saved_value"));
|
||||
value_editor->update_property();
|
||||
type_choice->set_icon(get_editor_theme_icon(Variant::get_type_name(param->get_type())));
|
||||
type_choice->set_button_icon(get_editor_theme_icon(Variant::get_type_name(param->get_type())));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,7 +316,7 @@ void EditorPropertyBBParam::_notification(int p_what) {
|
|||
|
||||
{
|
||||
String type = Variant::get_type_name(_get_edited_param()->get_type());
|
||||
type_choice->set_icon(get_editor_theme_icon(type));
|
||||
type_choice->set_button_icon(get_editor_theme_icon(type));
|
||||
}
|
||||
|
||||
// Initialize type choice.
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
/**
|
||||
* editor_property_path.cpp
|
||||
* =============================================================================
|
||||
* Copyright 2021-2024 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 "editor_property_property_path.h"
|
||||
|
||||
#include "../util/limbo_compat.h"
|
||||
#include "../util/limbo_string_names.h"
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
#include "editor/editor_data.h"
|
||||
#include "editor/editor_interface.h"
|
||||
#include "servers/display_server.h"
|
||||
#endif
|
||||
|
||||
#ifdef LIMBOAI_GDEXTENSION
|
||||
#include <godot_cpp/classes/display_server.hpp>
|
||||
#include <godot_cpp/classes/editor_inspector.hpp>
|
||||
#include <godot_cpp/classes/editor_interface.hpp>
|
||||
#include <godot_cpp/classes/editor_selection.hpp>
|
||||
#include <godot_cpp/classes/h_box_container.hpp>
|
||||
#include <godot_cpp/classes/popup_menu.hpp>
|
||||
#include <godot_cpp/classes/scene_tree.hpp>
|
||||
#endif // LIMBOAI_MODULE
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
namespace {
|
||||
|
||||
Node *_get_base_node(Object *p_edited_object, SceneTree *p_scene_tree) {
|
||||
Node *base_node = Object::cast_to<Node>(p_edited_object);
|
||||
if (!base_node) {
|
||||
base_node = Object::cast_to<Node>(EditorInterface::get_singleton()->get_inspector()->get_edited_object());
|
||||
}
|
||||
if (!base_node) {
|
||||
base_node = p_scene_tree->get_edited_scene_root();
|
||||
}
|
||||
return base_node;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
Node *EditorPropertyPropertyPath::_get_selected_node() {
|
||||
ERR_FAIL_NULL_V(get_edited_object(), nullptr);
|
||||
|
||||
NodePath path = get_edited_object()->get(get_edited_property());
|
||||
if (path.is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node *base_node = _get_base_node(get_edited_object(), get_tree());
|
||||
ERR_FAIL_NULL_V(base_node, nullptr);
|
||||
Node *selected_node = base_node->get_node_or_null(path);
|
||||
return selected_node;
|
||||
}
|
||||
|
||||
void EditorPropertyPropertyPath::_action_selected(int p_idx) {
|
||||
switch (p_idx) {
|
||||
case ACTION_CLEAR: {
|
||||
emit_changed(get_edited_property(), NodePath());
|
||||
} break;
|
||||
case ACTION_COPY: {
|
||||
DisplayServer::get_singleton()->clipboard_set(get_edited_object()->get(get_edited_property()));
|
||||
} break;
|
||||
case ACTION_EDIT: {
|
||||
assign_button->hide();
|
||||
action_menu->hide();
|
||||
path_edit->show();
|
||||
path_edit->set_text(get_edited_object()->get(get_edited_property()));
|
||||
path_edit->grab_focus();
|
||||
} break;
|
||||
case ACTION_SELECT: {
|
||||
Node *selected_node = _get_selected_node();
|
||||
if (selected_node) {
|
||||
EditorInterface::get_singleton()->get_selection()->clear();
|
||||
EditorInterface::get_singleton()->get_selection()->add_node(selected_node);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyPropertyPath::_accept_text() {
|
||||
path_edit->hide();
|
||||
assign_button->show();
|
||||
action_menu->show();
|
||||
emit_changed(get_edited_property(), path_edit->get_text());
|
||||
}
|
||||
|
||||
void EditorPropertyPropertyPath::_property_selected(const NodePath &p_property_path, const NodePath &p_node_path) {
|
||||
if (p_property_path.is_empty()) {
|
||||
return;
|
||||
}
|
||||
Node *base_node = _get_base_node(get_edited_object(), get_tree());
|
||||
ERR_FAIL_NULL(base_node);
|
||||
Node *selected_node = get_tree()->get_edited_scene_root()->get_node_or_null(p_node_path);
|
||||
ERR_FAIL_NULL(selected_node);
|
||||
NodePath path = String(base_node->get_path_to(selected_node)) + String(p_property_path);
|
||||
|
||||
emit_changed(get_edited_property(), path);
|
||||
update_property();
|
||||
}
|
||||
|
||||
void EditorPropertyPropertyPath::_node_selected(const NodePath &p_path) {
|
||||
if (p_path.is_empty()) {
|
||||
return;
|
||||
}
|
||||
Node *selected_node = get_tree()->get_edited_scene_root()->get_node_or_null(p_path);
|
||||
ERR_FAIL_NULL(selected_node);
|
||||
EditorInterface::get_singleton()->popup_property_selector(
|
||||
selected_node,
|
||||
callable_mp(this, &EditorPropertyPropertyPath::_property_selected).bind(p_path),
|
||||
valid_types);
|
||||
}
|
||||
|
||||
void EditorPropertyPropertyPath::_choose_property() {
|
||||
EditorInterface::get_singleton()->popup_node_selector(callable_mp(this, &EditorPropertyPropertyPath::_node_selected));
|
||||
}
|
||||
|
||||
void EditorPropertyPropertyPath::_set_read_only(bool p_read_only) {
|
||||
assign_button->set_disabled(p_read_only);
|
||||
action_menu->set_disabled(p_read_only);
|
||||
}
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
void EditorPropertyPropertyPath::update_property() {
|
||||
#elif LIMBOAI_GDEXTENSION
|
||||
void EditorPropertyPropertyPath::_update_property() {
|
||||
#endif
|
||||
NodePath path = get_edited_object()->get(get_edited_property());
|
||||
if (path.is_empty()) {
|
||||
assign_button->set_text(TTR("Bind..."));
|
||||
} else {
|
||||
Node *base_node = _get_base_node(get_edited_object(), get_tree());
|
||||
ERR_FAIL_NULL(base_node);
|
||||
Node *selected_node = base_node->get_node_or_null(path);
|
||||
String text;
|
||||
if (selected_node) {
|
||||
text = (String)selected_node->get_name() +
|
||||
":" + (String)path.get_concatenated_subnames();
|
||||
} else {
|
||||
text = (String)path;
|
||||
}
|
||||
assign_button->set_text(text);
|
||||
assign_button->set_tooltip_text(path);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyPropertyPath::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
action_menu->set_button_icon(get_theme_icon(LW_NAME(GuiTabMenuHl), LW_NAME(EditorIcons)));
|
||||
action_menu->get_popup()->set_item_icon(ACTION_CLEAR, get_theme_icon(LW_NAME(Clear), LW_NAME(EditorIcons)));
|
||||
action_menu->get_popup()->set_item_icon(ACTION_COPY, get_theme_icon(LW_NAME(ActionCopy), LW_NAME(EditorIcons)));
|
||||
action_menu->get_popup()->set_item_icon(ACTION_EDIT, get_theme_icon(LW_NAME(Edit), LW_NAME(EditorIcons)));
|
||||
action_menu->get_popup()->set_item_icon(ACTION_SELECT, get_theme_icon(LW_NAME(ExternalLink), LW_NAME(EditorIcons)));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyPropertyPath::setup(const PackedInt32Array &p_valid_types) {
|
||||
valid_types = p_valid_types;
|
||||
}
|
||||
|
||||
EditorPropertyPropertyPath::EditorPropertyPropertyPath() {
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
add_child(hb);
|
||||
hb->add_theme_constant_override(LW_NAME(separation), 0);
|
||||
|
||||
assign_button = memnew(Button);
|
||||
hb->add_child(assign_button);
|
||||
assign_button->set_flat(true);
|
||||
assign_button->set_text(TTR("Bind..."));
|
||||
assign_button->set_clip_text(true);
|
||||
assign_button->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
assign_button->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
assign_button->connect(LW_NAME(pressed), callable_mp(this, &EditorPropertyPropertyPath::_choose_property));
|
||||
|
||||
path_edit = memnew(LineEdit);
|
||||
hb->add_child(path_edit);
|
||||
path_edit->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
path_edit->connect(LW_NAME(focus_exited), callable_mp(this, &EditorPropertyPropertyPath::_accept_text));
|
||||
path_edit->connect(LW_NAME(text_submitted), callable_mp(this, &EditorPropertyPropertyPath::_accept_text).unbind(1));
|
||||
path_edit->hide();
|
||||
|
||||
action_menu = memnew(MenuButton);
|
||||
action_menu->get_popup()->add_item(TTR("Clear"), ACTION_CLEAR);
|
||||
action_menu->get_popup()->add_item(TTR("Copy as Text"), ACTION_COPY);
|
||||
action_menu->get_popup()->add_item(TTR("Edit"), ACTION_EDIT);
|
||||
action_menu->get_popup()->add_item(TTR("Show Node in Tree"), ACTION_SELECT);
|
||||
action_menu->get_popup()->connect(LW_NAME(id_pressed), callable_mp(this, &EditorPropertyPropertyPath::_action_selected));
|
||||
hb->add_child(action_menu);
|
||||
}
|
||||
|
||||
//***** EditorInspectorPluginPropertyPath
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
bool EditorInspectorPluginPropertyPath::can_handle(Object *p_object) {
|
||||
#elif LIMBOAI_GDEXTENSION
|
||||
bool EditorInspectorPluginPropertyPath::_can_handle(Object *p_object) const {
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
bool EditorInspectorPluginPropertyPath::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
|
||||
#elif LIMBOAI_GDEXTENSION
|
||||
bool EditorInspectorPluginPropertyPath::_parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
|
||||
#endif
|
||||
if (p_type != Variant::NODE_PATH || p_hint != PROPERTY_HINT_LINK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorPropertyPropertyPath *ed = memnew(EditorPropertyPropertyPath);
|
||||
|
||||
// Convert the hint text to an array of valid types.
|
||||
PackedInt32Array valid_types;
|
||||
PackedStringArray type_specifiers = p_hint_text.split(",");
|
||||
for (const String &t : type_specifiers) {
|
||||
if (t.is_valid_int()) {
|
||||
valid_types.append(t.to_int());
|
||||
}
|
||||
}
|
||||
ed->setup(valid_types);
|
||||
|
||||
add_property_editor(p_path, ed);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // TOOLS_ENABLED
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* editor_property_path.h
|
||||
* =============================================================================
|
||||
* Copyright 2021-2024 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 EDITOR_PROPERTY_PROPERTY_PATH
|
||||
#define EDITOR_PROPERTY_PROPERTY_PATH
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#endif // LIMBOAI_MODULE
|
||||
|
||||
#ifdef LIMBOAI_GDEXTENSION
|
||||
#include <godot_cpp/classes/button.hpp>
|
||||
#include <godot_cpp/classes/editor_inspector_plugin.hpp>
|
||||
#include <godot_cpp/classes/editor_property.hpp>
|
||||
#include <godot_cpp/classes/line_edit.hpp>
|
||||
#include <godot_cpp/classes/menu_button.hpp>
|
||||
using namespace godot;
|
||||
#endif // LIMBOAI_GDEXTENSION
|
||||
|
||||
// Specialized property editor for NodePath properties that represent a path to a specific property instead of just a node.
|
||||
// Handles NodePath properties that have PROPERTY_HINT_LINK.
|
||||
// Hint string can list the valid Variant types as comma-separated integers.
|
||||
class EditorPropertyPropertyPath : public EditorProperty {
|
||||
GDCLASS(EditorPropertyPropertyPath, EditorProperty);
|
||||
|
||||
private:
|
||||
enum Action {
|
||||
ACTION_CLEAR,
|
||||
ACTION_COPY,
|
||||
ACTION_EDIT,
|
||||
ACTION_SELECT,
|
||||
};
|
||||
|
||||
Button *assign_button;
|
||||
MenuButton *action_menu;
|
||||
LineEdit *path_edit;
|
||||
|
||||
PackedInt32Array valid_types;
|
||||
|
||||
Node *_get_selected_node();
|
||||
void _action_selected(int p_idx);
|
||||
void _accept_text();
|
||||
void _property_selected(const NodePath &p_property_path, const NodePath &p_node_path);
|
||||
void _node_selected(const NodePath &p_path);
|
||||
void _choose_property();
|
||||
|
||||
protected:
|
||||
static void _bind_methods() {}
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
// Note: Needs to be public in GDExtension.
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
virtual void update_property() override;
|
||||
#elif LIMBOAI_GDEXTENSION
|
||||
virtual void _update_property() override;
|
||||
#endif
|
||||
|
||||
void setup(const PackedInt32Array &p_valid_types);
|
||||
EditorPropertyPropertyPath();
|
||||
};
|
||||
|
||||
class EditorInspectorPluginPropertyPath : public EditorInspectorPlugin {
|
||||
GDCLASS(EditorInspectorPluginPropertyPath, EditorInspectorPlugin);
|
||||
|
||||
private:
|
||||
protected:
|
||||
static void _bind_methods() {}
|
||||
|
||||
public:
|
||||
#ifdef LIMBOAI_MODULE
|
||||
virtual bool can_handle(Object *p_object) override;
|
||||
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
|
||||
#elif LIMBOAI_GDEXTENSION
|
||||
virtual bool _can_handle(Object *p_object) const override;
|
||||
virtual bool _parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
|
||||
#endif
|
||||
|
||||
EditorInspectorPluginPropertyPath() = default;
|
||||
};
|
||||
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#endif // EDITOR_PROPERTY_PROPERTY_PATH
|
|
@ -36,7 +36,7 @@ int EditorPropertyVariableName::last_caret_column = 0;
|
|||
//***** EditorPropertyVariableName
|
||||
|
||||
void EditorPropertyVariableName::_show_variables_popup() {
|
||||
ERR_FAIL_NULL(plan);
|
||||
ERR_FAIL_COND(plan.is_null());
|
||||
|
||||
variables_popup->clear();
|
||||
variables_popup->reset_size();
|
||||
|
@ -86,30 +86,30 @@ void EditorPropertyVariableName::_update_status() {
|
|||
}
|
||||
String var_name = name_edit->get_text();
|
||||
if (var_name.is_empty() && allow_empty) {
|
||||
BUTTON_SET_ICON(status_btn, theme_cache.var_empty_icon);
|
||||
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) {
|
||||
BUTTON_SET_ICON(status_btn, theme_cache.var_exists_icon);
|
||||
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 {
|
||||
BUTTON_SET_ICON(status_btn, theme_cache.var_error_icon);
|
||||
status_btn->set_button_icon(theme_cache.var_error_icon);
|
||||
status_btn->set_tooltip_text(TTR(vformat(
|
||||
"The %s variable in the blackboard plan should be of type %s.\nClick to open the blackboard plan.",
|
||||
LimboUtility::get_singleton()->decorate_var(var_name),
|
||||
Variant::get_type_name(expected_type))));
|
||||
}
|
||||
} else if (name_edit->get_text().begins_with("_")) {
|
||||
BUTTON_SET_ICON(status_btn, theme_cache.var_private_icon);
|
||||
status_btn->set_button_icon(theme_cache.var_private_icon);
|
||||
status_btn->set_tooltip_text(TTR("This variable is private and is not included in the blackboard plan.\nClick to open the blackboard plan."));
|
||||
} else {
|
||||
BUTTON_SET_ICON(status_btn, theme_cache.var_not_found_icon);
|
||||
status_btn->set_button_icon(theme_cache.var_not_found_icon);
|
||||
status_btn->set_tooltip_text(TTR("No matching variable found in the blackboard plan!\nClick to open the blackboard plan."));
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyVariableName::_status_pressed() {
|
||||
ERR_FAIL_NULL(plan);
|
||||
ERR_FAIL_COND(plan.is_null());
|
||||
if (!plan->has_var(name_edit->get_text())) {
|
||||
BlackboardPlanEditor::get_singleton()->set_defaults(name_edit->get_text(),
|
||||
expected_type == Variant::NIL ? Variant::FLOAT : expected_type,
|
||||
|
@ -120,14 +120,14 @@ void EditorPropertyVariableName::_status_pressed() {
|
|||
}
|
||||
|
||||
void EditorPropertyVariableName::_status_mouse_entered() {
|
||||
ERR_FAIL_NULL(plan);
|
||||
ERR_FAIL_COND(plan.is_null());
|
||||
if (!plan->has_var(name_edit->get_text())) {
|
||||
BUTTON_SET_ICON(status_btn, theme_cache.var_add_icon);
|
||||
status_btn->set_button_icon(theme_cache.var_add_icon);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyVariableName::_status_mouse_exited() {
|
||||
ERR_FAIL_NULL(plan);
|
||||
ERR_FAIL_COND(plan.is_null());
|
||||
_update_status();
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ void EditorPropertyVariableName::_notification(int p_what) {
|
|||
}
|
||||
} break;
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
BUTTON_SET_ICON(drop_btn, get_theme_icon(LW_NAME(GuiOptionArrow), LW_NAME(EditorIcons)));
|
||||
drop_btn->set_button_icon(get_theme_icon(LW_NAME(GuiOptionArrow), LW_NAME(EditorIcons)));
|
||||
theme_cache.var_add_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarAdd));
|
||||
theme_cache.var_exists_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarExists));
|
||||
theme_cache.var_not_found_icon = LimboUtility::get_singleton()->get_task_icon(LW_NAME(LimboVarNotFound));
|
||||
|
@ -245,6 +245,11 @@ bool EditorInspectorPluginVariableName::parse_property(Object *p_object, const V
|
|||
#elif LIMBOAI_GDEXTENSION
|
||||
bool EditorInspectorPluginVariableName::_parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
|
||||
#endif
|
||||
if (p_usage & PROPERTY_USAGE_READ_ONLY) {
|
||||
// Don't handle read-only properties using this plugin.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_mapping = p_path.begins_with("mapping/");
|
||||
if (!(p_type == Variant::Type::STRING_NAME || p_type == Variant::Type::STRING) || !(is_mapping || p_path.ends_with("_var") || p_path.ends_with("variable"))) {
|
||||
return false;
|
||||
|
@ -257,7 +262,7 @@ bool EditorInspectorPluginVariableName::_parse_property(Object *p_object, const
|
|||
Variant default_value;
|
||||
if (is_mapping) {
|
||||
plan.reference_ptr(Object::cast_to<BlackboardPlan>(p_object));
|
||||
ERR_FAIL_NULL_V(plan, false);
|
||||
ERR_FAIL_COND_V(plan.is_null(), false);
|
||||
String var_name = p_path.trim_prefix("mapping/");
|
||||
if (plan->has_var(var_name)) {
|
||||
BBVariable variable = plan->get_var(var_name);
|
||||
|
@ -272,7 +277,7 @@ bool EditorInspectorPluginVariableName::_parse_property(Object *p_object, const
|
|||
plan = parent_plan;
|
||||
}
|
||||
}
|
||||
ERR_FAIL_NULL_V(plan, false);
|
||||
ERR_FAIL_COND_V(plan.is_null(), false);
|
||||
} else {
|
||||
plan = editor_plan_provider.call();
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "blackboard_plan_editor.h"
|
||||
#include "debugger/limbo_debugger_plugin.h"
|
||||
#include "editor_property_bb_param.h"
|
||||
#include "editor_property_property_path.h"
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
#include "core/config/project_settings.h"
|
||||
|
@ -66,6 +67,7 @@
|
|||
#include <godot_cpp/classes/ref_counted.hpp>
|
||||
#include <godot_cpp/classes/resource_loader.hpp>
|
||||
#include <godot_cpp/classes/resource_saver.hpp>
|
||||
#include <godot_cpp/classes/scene_tree.hpp>
|
||||
#include <godot_cpp/classes/script.hpp>
|
||||
#include <godot_cpp/classes/script_editor.hpp>
|
||||
#include <godot_cpp/classes/script_editor_base.hpp>
|
||||
|
@ -73,6 +75,21 @@
|
|||
#include <godot_cpp/core/error_macros.hpp>
|
||||
#endif // LIMBOAI_GDEXTENSION
|
||||
|
||||
namespace {
|
||||
|
||||
// If built-in resource - switch to the owner scene (open it if not already).
|
||||
inline void _switch_to_owner_scene_if_builtin(const Ref<BehaviorTree> &p_behavior_tree) {
|
||||
if (p_behavior_tree.is_valid() && p_behavior_tree->get_path().contains("::")) {
|
||||
String current_scene = SCENE_TREE()->get_edited_scene_root()->get_scene_file_path();
|
||||
String scene_path = p_behavior_tree->get_path().get_slice("::", 0);
|
||||
if (current_scene != scene_path) {
|
||||
EditorInterface::get_singleton()->open_scene_from_path(scene_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
//**** LimboAIEditor
|
||||
|
||||
_FORCE_INLINE_ String _get_script_template_path() {
|
||||
|
@ -97,7 +114,7 @@ void LimboAIEditor::_commit_action_with_update(EditorUndoRedoManager *p_undo_red
|
|||
p_undo_redo->add_do_method(this, LW_NAME(_update_task_tree), task_tree->get_bt());
|
||||
p_undo_redo->add_undo_method(this, LW_NAME(_update_task_tree), task_tree->get_bt());
|
||||
p_undo_redo->commit_action();
|
||||
_mark_as_dirty(true);
|
||||
_set_as_dirty(task_tree->get_bt(), true);
|
||||
}
|
||||
|
||||
void LimboAIEditor::_add_task(const Ref<BTTask> &p_task, bool p_as_sibling) {
|
||||
|
@ -183,7 +200,7 @@ void LimboAIEditor::_remove_task(const Ref<BTTask> &p_task) {
|
|||
ERR_FAIL_COND(p_task.is_null());
|
||||
ERR_FAIL_COND(task_tree->get_bt().is_null());
|
||||
EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Remove BT Task"));
|
||||
if (p_task->get_parent() == nullptr) {
|
||||
if (p_task->get_parent().is_null()) {
|
||||
ERR_FAIL_COND(task_tree->get_bt()->get_root_task() != p_task);
|
||||
undo_redo->add_do_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), Variant());
|
||||
undo_redo->add_undo_method(task_tree->get_bt().ptr(), LW_NAME(set_root_task), task_tree->get_bt()->get_root_task());
|
||||
|
@ -201,20 +218,58 @@ void LimboAIEditor::_new_bt() {
|
|||
EDIT_RESOURCE(bt);
|
||||
}
|
||||
|
||||
void LimboAIEditor::_save_bt(String p_path) {
|
||||
ERR_FAIL_COND_MSG(p_path.is_empty(), "Empty p_path");
|
||||
ERR_FAIL_COND_MSG(task_tree->get_bt().is_null(), "Behavior Tree is null.");
|
||||
void LimboAIEditor::_save_bt(const Ref<BehaviorTree> &p_bt, const String &p_path) {
|
||||
ERR_FAIL_COND(p_path.is_empty());
|
||||
ERR_FAIL_COND(!p_path.begins_with("res://"));
|
||||
ERR_FAIL_COND(p_bt.is_null());
|
||||
|
||||
if (p_bt->get_path() != p_path) {
|
||||
#ifdef LIMBOAI_MODULE
|
||||
task_tree->get_bt()->set_path(p_path, true);
|
||||
task_tree->get_bt()->set_path(p_path, true);
|
||||
#elif LIMBOAI_GDEXTENSION
|
||||
task_tree->get_bt()->take_over_path(p_path);
|
||||
task_tree->get_bt()->take_over_path(p_path);
|
||||
#endif
|
||||
RESOURCE_SAVE(task_tree->get_bt(), p_path, ResourceSaver::FLAG_CHANGE_PATH);
|
||||
}
|
||||
|
||||
// This is a workaround, because EditorNode::save_resource() function is not accessible in GDExtension.
|
||||
if (RESOURCE_IS_BUILT_IN(p_bt)) {
|
||||
// If built-in resource - save the containing resource instead.
|
||||
String file_path = p_path.get_slice("::", 0);
|
||||
ERR_FAIL_COND_MSG(!RESOURCE_EXISTS(file_path, "Resource"), "LimboAI: SAVE FAILED - resource file doesn't exist: " + file_path);
|
||||
if (RESOURCE_IS_SCENE_FILE(file_path)) {
|
||||
// Packed scene - save the scene instead.
|
||||
if (EditorInterface::get_singleton()->get_open_scenes().has(file_path)) {
|
||||
// If scene is open, switch to it first, and then ask to save.
|
||||
// This is needed because saving the currently edited scene can have complications.
|
||||
EditorInterface::get_singleton()->open_scene_from_path(file_path);
|
||||
EditorInterface::get_singleton()->save_scene();
|
||||
} else {
|
||||
// If scene is not currently open in the editor, load and resave it.
|
||||
Ref<Resource> scene = RESOURCE_LOAD(file_path, "PackedScene");
|
||||
RESOURCE_SAVE(scene, file_path, ResourceSaver::FLAG_NONE);
|
||||
}
|
||||
} else {
|
||||
// Not a packed scene - save the containing resource to file.
|
||||
Ref<Resource> res = RESOURCE_LOAD(file_path, "Resource");
|
||||
RESOURCE_SAVE(res, file_path, ResourceSaver::FLAG_NONE);
|
||||
}
|
||||
} else {
|
||||
// If external resource - save to file.
|
||||
RESOURCE_SAVE(p_bt, p_path, ResourceSaver::FLAG_CHANGE_PATH);
|
||||
}
|
||||
|
||||
_set_as_dirty(p_bt, false);
|
||||
_update_tabs();
|
||||
_mark_as_dirty(false);
|
||||
}
|
||||
|
||||
void LimboAIEditor::_load_bt(String p_path) {
|
||||
void LimboAIEditor::_save_current_bt(const String &p_path) {
|
||||
ERR_FAIL_COND_MSG(p_path.is_empty(), "LimboAI: SAVE FAILED - p_path is empty");
|
||||
ERR_FAIL_COND_MSG(task_tree->get_bt().is_null(), "LimboAI: SAVE FAILED - bt is null");
|
||||
|
||||
_save_bt(task_tree->get_bt(), p_path);
|
||||
}
|
||||
|
||||
void LimboAIEditor::_load_bt(const String &p_path) {
|
||||
ERR_FAIL_COND_MSG(p_path.is_empty(), "Empty p_path");
|
||||
Ref<BehaviorTree> bt = RESOURCE_LOAD(p_path, "BehaviorTree");
|
||||
ERR_FAIL_COND(!bt.is_valid());
|
||||
|
@ -253,6 +308,8 @@ void LimboAIEditor::_disable_editing() {
|
|||
void LimboAIEditor::edit_bt(const Ref<BehaviorTree> &p_behavior_tree, bool p_force_refresh) {
|
||||
ERR_FAIL_COND_MSG(p_behavior_tree.is_null(), "p_behavior_tree is null");
|
||||
|
||||
_switch_to_owner_scene_if_builtin(p_behavior_tree);
|
||||
|
||||
if (!p_force_refresh && task_tree->get_bt() == p_behavior_tree) {
|
||||
return;
|
||||
}
|
||||
|
@ -268,8 +325,8 @@ void LimboAIEditor::edit_bt(const Ref<BehaviorTree> &p_behavior_tree, bool p_for
|
|||
|
||||
task_tree->load_bt(p_behavior_tree);
|
||||
|
||||
if (task_tree->get_bt().is_valid() && !task_tree->get_bt()->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty))) {
|
||||
task_tree->get_bt()->connect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty).bind(true));
|
||||
if (task_tree->get_bt().is_valid() && !task_tree->get_bt()->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_set_as_dirty))) {
|
||||
task_tree->get_bt()->connect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_set_as_dirty).bind(task_tree->get_bt(), true));
|
||||
}
|
||||
|
||||
int idx = history.find(p_behavior_tree);
|
||||
|
@ -334,12 +391,11 @@ void LimboAIEditor::get_window_layout(const Ref<ConfigFile> &p_configuration) {
|
|||
p_configuration->set_value("LimboAI", "bteditor_hsplit", split_offset);
|
||||
}
|
||||
|
||||
void LimboAIEditor::_mark_as_dirty(bool p_dirty) {
|
||||
Ref<BehaviorTree> bt = task_tree->get_bt();
|
||||
if (p_dirty && !dirty.has(bt)) {
|
||||
dirty.insert(bt);
|
||||
} else if (p_dirty == false && dirty.has(bt)) {
|
||||
dirty.erase(bt);
|
||||
void LimboAIEditor::_set_as_dirty(const Ref<BehaviorTree> &p_bt, bool p_dirty) {
|
||||
if (p_dirty && !dirty.has(p_bt)) {
|
||||
dirty.insert(p_bt);
|
||||
} else if (p_dirty == false && dirty.has(p_bt)) {
|
||||
dirty.erase(p_bt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -439,6 +495,14 @@ void LimboAIEditor::_process_shortcut_input(const Ref<InputEvent> &p_event) {
|
|||
} else if (LW_IS_SHORTCUT("limbo_ai/close_tab", p_event)) {
|
||||
_tab_menu_option_selected(TAB_CLOSE);
|
||||
handled = true;
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/editor_save_scene", p_event)) {
|
||||
// This intercepts the editor save action, but does not set the event as handled because we don't know the user's intention.
|
||||
// We just want to save the currently edited BT as well, which may cause a loop with built-in resource if done from "_save_external_data".
|
||||
// Workaround for: https://github.com/limbonaut/limboai/issues/240#issuecomment-2453087424
|
||||
if (task_tree->get_bt().is_valid() && RESOURCE_IS_BUILT_IN(task_tree->get_bt())) {
|
||||
_on_save_pressed();
|
||||
}
|
||||
handled = false; // intentionally not set as handled
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -682,7 +746,7 @@ void LimboAIEditor::_action_selected(int p_id) {
|
|||
|
||||
void LimboAIEditor::_on_probability_edited(double p_value) {
|
||||
Ref<BTTask> selected = task_tree->get_selected();
|
||||
ERR_FAIL_COND(selected == nullptr);
|
||||
ERR_FAIL_COND(selected.is_null());
|
||||
Ref<BTProbabilitySelector> probability_selector = selected->get_parent();
|
||||
ERR_FAIL_COND(probability_selector.is_null());
|
||||
if (percent_mode->is_pressed()) {
|
||||
|
@ -837,6 +901,7 @@ void LimboAIEditor::_on_tree_task_activated() {
|
|||
|
||||
void LimboAIEditor::_on_visibility_changed() {
|
||||
if (task_tree->is_visible_in_tree()) {
|
||||
_switch_to_owner_scene_if_builtin(task_tree->get_bt());
|
||||
Ref<BTTask> sel = task_tree->get_selected();
|
||||
if (sel.is_valid()) {
|
||||
EDIT_RESOURCE(sel);
|
||||
|
@ -854,16 +919,6 @@ void LimboAIEditor::_on_visibility_changed() {
|
|||
}
|
||||
}
|
||||
|
||||
void LimboAIEditor::_on_header_pressed() {
|
||||
task_tree->clear_selection();
|
||||
#ifdef LIMBOAI_MODULE
|
||||
if (task_tree->get_bt().is_valid()) {
|
||||
task_tree->get_bt()->editor_set_section_unfold("blackboard_plan", true);
|
||||
}
|
||||
#endif // LIMBOAI_MODULE
|
||||
EDIT_RESOURCE(task_tree->get_bt());
|
||||
}
|
||||
|
||||
void LimboAIEditor::_on_save_pressed() {
|
||||
if (task_tree->get_bt().is_null()) {
|
||||
return;
|
||||
|
@ -872,7 +927,7 @@ void LimboAIEditor::_on_save_pressed() {
|
|||
if (path.is_empty()) {
|
||||
save_dialog->popup_centered_ratio();
|
||||
} else {
|
||||
_save_bt(path);
|
||||
_save_current_bt(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1060,8 +1115,8 @@ void LimboAIEditor::_tab_clicked(int p_tab) {
|
|||
void LimboAIEditor::_tab_closed(int p_tab) {
|
||||
ERR_FAIL_INDEX(p_tab, history.size());
|
||||
Ref<BehaviorTree> history_bt = history[p_tab];
|
||||
if (history_bt.is_valid() && history_bt->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty))) {
|
||||
history_bt->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty));
|
||||
if (history_bt.is_valid() && history_bt->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_set_as_dirty))) {
|
||||
history_bt->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_set_as_dirty));
|
||||
}
|
||||
if (tab_search_context.has(history_bt)) {
|
||||
tab_search_context.erase(history_bt);
|
||||
|
@ -1186,7 +1241,7 @@ void LimboAIEditor::_tab_menu_option_selected(int p_id) {
|
|||
} break;
|
||||
case TAB_JUMP_TO_OWNER: {
|
||||
Ref<BehaviorTree> bt = history[idx_history];
|
||||
ERR_FAIL_NULL(bt);
|
||||
ERR_FAIL_COND(bt.is_null());
|
||||
String bt_path = bt->get_path();
|
||||
if (!bt_path.is_empty()) {
|
||||
owner_picker->pick_and_open_owner_of_resource(bt_path);
|
||||
|
@ -1241,10 +1296,13 @@ void LimboAIEditor::_reload_modified() {
|
|||
|
||||
void LimboAIEditor::_resave_modified(String _str) {
|
||||
for (const String &res_path : disk_changed_files) {
|
||||
Ref<BehaviorTree> res = RESOURCE_LOAD(res_path, "BehaviorTree");
|
||||
if (res.is_valid()) {
|
||||
ERR_FAIL_COND(!res->is_class("BehaviorTree"));
|
||||
RESOURCE_SAVE(res, res->get_path(), 0);
|
||||
Ref<BehaviorTree> bt = RESOURCE_LOAD(res_path, "BehaviorTree");
|
||||
if (bt.is_valid()) {
|
||||
ERR_FAIL_COND(!bt->is_class("BehaviorTree"));
|
||||
if (RESOURCE_IS_EXTERNAL(bt)) {
|
||||
// Only resave external - scene files are handled by the editor.
|
||||
_save_bt(bt, bt->get_path());
|
||||
}
|
||||
}
|
||||
}
|
||||
task_tree->update_tree();
|
||||
|
@ -1269,14 +1327,13 @@ void LimboAIEditor::_rename_task_confirmed() {
|
|||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void LimboAIEditor::apply_changes() {
|
||||
void LimboAIEditor::save_all(bool p_external_only) {
|
||||
for (int i = 0; i < history.size(); i++) {
|
||||
Ref<BehaviorTree> bt = history.get(i);
|
||||
String path = bt->get_path();
|
||||
if (RESOURCE_EXISTS(path, "BehaviorTree")) {
|
||||
RESOURCE_SAVE(bt, path, 0);
|
||||
if (RESOURCE_EXISTS(path, "BehaviorTree") && (!p_external_only || RESOURCE_PATH_IS_EXTERNAL(path))) {
|
||||
_save_bt(bt, path);
|
||||
}
|
||||
dirty.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1302,7 +1359,7 @@ void LimboAIEditor::_update_favorite_tasks() {
|
|||
}
|
||||
btn->set_text(task_name);
|
||||
btn->set_meta(LW_NAME(task_meta), task_meta);
|
||||
BUTTON_SET_ICON(btn, LimboUtility::get_singleton()->get_task_icon(task_meta));
|
||||
btn->set_button_icon(LimboUtility::get_singleton()->get_task_icon(task_meta));
|
||||
btn->set_tooltip_text(vformat(TTR("Add %s task."), task_name));
|
||||
btn->set_flat(true);
|
||||
btn->add_theme_constant_override(LW_NAME(icon_max_width), 16 * EDSCALE); // Force user icons to be of the proper size.
|
||||
|
@ -1423,14 +1480,14 @@ void LimboAIEditor::_notification(int p_what) {
|
|||
case NOTIFICATION_EXIT_TREE: {
|
||||
task_tree->unload();
|
||||
for (int i = 0; i < history.size(); i++) {
|
||||
if (history[i]->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty))) {
|
||||
history[i]->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty));
|
||||
if (history[i]->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_set_as_dirty))) {
|
||||
history[i]->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_set_as_dirty));
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_READY: {
|
||||
// **** Signals
|
||||
save_dialog->connect("file_selected", callable_mp(this, &LimboAIEditor::_save_bt));
|
||||
save_dialog->connect("file_selected", callable_mp(this, &LimboAIEditor::_save_current_bt));
|
||||
load_dialog->connect("file_selected", callable_mp(this, &LimboAIEditor::_load_bt));
|
||||
extract_dialog->connect("file_selected", callable_mp(this, &LimboAIEditor::_extract_subtree));
|
||||
new_btn->connect(LW_NAME(pressed), callable_mp(this, &LimboAIEditor::_new_bt));
|
||||
|
@ -1473,11 +1530,11 @@ void LimboAIEditor::_notification(int p_what) {
|
|||
|
||||
ADD_STYLEBOX_OVERRIDE(tab_bar_panel, "panel", get_theme_stylebox("tabbar_background", "TabContainer"));
|
||||
|
||||
BUTTON_SET_ICON(new_btn, get_theme_icon(LW_NAME(New), LW_NAME(EditorIcons)));
|
||||
BUTTON_SET_ICON(load_btn, get_theme_icon(LW_NAME(Load), LW_NAME(EditorIcons)));
|
||||
BUTTON_SET_ICON(save_btn, get_theme_icon(LW_NAME(Save), LW_NAME(EditorIcons)));
|
||||
BUTTON_SET_ICON(new_script_btn, get_theme_icon(LW_NAME(ScriptCreate), LW_NAME(EditorIcons)));
|
||||
BUTTON_SET_ICON(misc_btn, get_theme_icon(LW_NAME(Tools), LW_NAME(EditorIcons)));
|
||||
new_btn->set_button_icon(get_theme_icon(LW_NAME(New), LW_NAME(EditorIcons)));
|
||||
load_btn->set_button_icon(get_theme_icon(LW_NAME(Load), LW_NAME(EditorIcons)));
|
||||
save_btn->set_button_icon(get_theme_icon(LW_NAME(Save), LW_NAME(EditorIcons)));
|
||||
new_script_btn->set_button_icon(get_theme_icon(LW_NAME(ScriptCreate), LW_NAME(EditorIcons)));
|
||||
misc_btn->set_button_icon(get_theme_icon(LW_NAME(Tools), LW_NAME(EditorIcons)));
|
||||
|
||||
_update_favorite_tasks();
|
||||
} break;
|
||||
|
@ -1494,7 +1551,7 @@ void LimboAIEditor::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("_remove_task", "task"), &LimboAIEditor::_remove_task);
|
||||
ClassDB::bind_method(D_METHOD("_add_task_with_prototype", "prototype_task"), &LimboAIEditor::_add_task_with_prototype);
|
||||
ClassDB::bind_method(D_METHOD("_new_bt"), &LimboAIEditor::_new_bt);
|
||||
ClassDB::bind_method(D_METHOD("_save_bt", "path"), &LimboAIEditor::_save_bt);
|
||||
ClassDB::bind_method(D_METHOD("_save_bt", "path"), &LimboAIEditor::_save_current_bt);
|
||||
ClassDB::bind_method(D_METHOD("_load_bt", "path"), &LimboAIEditor::_load_bt);
|
||||
ClassDB::bind_method(D_METHOD("_update_task_tree", "bt", "specific_task"), &LimboAIEditor::_update_task_tree, DEFVAL(Variant()));
|
||||
ClassDB::bind_method(D_METHOD("edit_bt", "behavior_tree", "force_refresh"), &LimboAIEditor::edit_bt, Variant(false));
|
||||
|
@ -1541,11 +1598,14 @@ LimboAIEditor::LimboAIEditor() {
|
|||
LW_SHORTCUT("limbo_ai/save_behavior_tree", TTR("Save Behavior Tree"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY_MASK(ALT) | LW_KEY(S)));
|
||||
LW_SHORTCUT("limbo_ai/load_behavior_tree", TTR("Load Behavior Tree"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY_MASK(ALT) | LW_KEY(L)));
|
||||
LW_SHORTCUT("limbo_ai/open_debugger", TTR("Open Debugger"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY_MASK(ALT) | LW_KEY(D)));
|
||||
LW_SHORTCUT("limbo_ai/jump_to_owner", TTR("Jump to Owner"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(J)));
|
||||
LW_SHORTCUT("limbo_ai/jump_to_owner", TTR("Jump to Owner"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(G)));
|
||||
LW_SHORTCUT("limbo_ai/close_tab", TTR("Close Tab"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(W)));
|
||||
LW_SHORTCUT("limbo_ai/find_task", TTR("Find Task"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(F)));
|
||||
LW_SHORTCUT("limbo_ai/hide_tree_search", TTR("Close Search"), (Key)(LW_KEY(ESCAPE)));
|
||||
|
||||
// Intercept editor save scene action.
|
||||
LW_SHORTCUT("limbo_ai/editor_save_scene", TTR("Save Scene"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(S)));
|
||||
|
||||
set_process_shortcut_input(true);
|
||||
|
||||
save_dialog = memnew(FileDialog);
|
||||
|
@ -1851,14 +1911,6 @@ LimboAIEditor::~LimboAIEditor() {
|
|||
|
||||
//**** LimboAIEditorPlugin
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
void LimboAIEditorPlugin::apply_changes() {
|
||||
#elif LIMBOAI_GDEXTENSION
|
||||
void LimboAIEditorPlugin::_apply_changes() {
|
||||
#endif
|
||||
limbo_ai_editor->apply_changes();
|
||||
}
|
||||
|
||||
void LimboAIEditorPlugin::_bind_methods() {
|
||||
}
|
||||
|
||||
|
@ -1867,9 +1919,13 @@ void LimboAIEditorPlugin::_notification(int p_notification) {
|
|||
case NOTIFICATION_READY: {
|
||||
add_debugger_plugin(memnew(LimboDebuggerPlugin));
|
||||
add_inspector_plugin(memnew(EditorInspectorPluginBBPlan));
|
||||
|
||||
EditorInspectorPluginVariableName *var_plugin = memnew(EditorInspectorPluginVariableName);
|
||||
var_plugin->set_editor_plan_provider(Callable(limbo_ai_editor, "get_edited_blackboard_plan"));
|
||||
add_inspector_plugin(var_plugin);
|
||||
|
||||
EditorInspectorPluginPropertyPath *path_plugin = memnew(EditorInspectorPluginPropertyPath);
|
||||
add_inspector_plugin(path_plugin);
|
||||
#ifdef LIMBOAI_MODULE
|
||||
// ! Only used in the module version.
|
||||
EditorInspectorPluginBBParam *param_plugin = memnew(EditorInspectorPluginBBParam);
|
||||
|
@ -1917,8 +1973,9 @@ void LimboAIEditorPlugin::edit(Object *p_object) {
|
|||
#elif LIMBOAI_GDEXTENSION
|
||||
void LimboAIEditorPlugin::_edit(Object *p_object) {
|
||||
#endif
|
||||
if (Object::cast_to<BehaviorTree>(p_object)) {
|
||||
limbo_ai_editor->edit_bt(Object::cast_to<BehaviorTree>(p_object));
|
||||
Ref<BehaviorTree> bt = Object::cast_to<BehaviorTree>(p_object);
|
||||
if (bt.is_valid()) {
|
||||
limbo_ai_editor->edit_bt(bt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1933,6 +1990,14 @@ bool LimboAIEditorPlugin::_handles(Object *p_object) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifdef LIMBOAI_MODULE
|
||||
void LimboAIEditorPlugin::save_external_data() {
|
||||
#elif LIMBOAI_GDEXTENSION
|
||||
void LimboAIEditorPlugin::_save_external_data() {
|
||||
#endif
|
||||
limbo_ai_editor->save_all(true);
|
||||
}
|
||||
|
||||
#ifdef LIMBOAI_GDEXTENSION
|
||||
Ref<Texture2D> LimboAIEditorPlugin::_get_plugin_icon() const {
|
||||
return LimboUtility::get_singleton()->get_task_icon("LimboAI");
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "core/object/class_db.h"
|
||||
#include "core/object/object.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
#include "editor/editor_main_screen.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/gui/editor_spin_slider.h"
|
||||
|
@ -42,6 +43,7 @@
|
|||
#include "scene/gui/popup.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/tab_bar.h"
|
||||
#include "scene/gui/tree.h"
|
||||
#include "scene/resources/texture.h"
|
||||
#endif // LIMBOAI_MODULE
|
||||
|
@ -205,11 +207,12 @@ private:
|
|||
void _update_misc_menu();
|
||||
void _update_banners();
|
||||
void _new_bt();
|
||||
void _save_bt(String p_path);
|
||||
void _load_bt(String p_path);
|
||||
void _save_bt(const Ref<BehaviorTree> &p_bt, const String &p_path);
|
||||
void _save_current_bt(const String &p_path);
|
||||
void _load_bt(const String &p_path);
|
||||
void _update_task_tree(const Ref<BehaviorTree> &p_bt, const Ref<BTTask> &p_specific_task = nullptr);
|
||||
void _disable_editing();
|
||||
void _mark_as_dirty(bool p_dirty);
|
||||
void _set_as_dirty(const Ref<BehaviorTree> &p_bt, bool p_dirty);
|
||||
void _create_user_task_dir();
|
||||
void _remove_task_from_favorite(const String &p_task);
|
||||
void _save_and_restart();
|
||||
|
@ -240,7 +243,6 @@ private:
|
|||
void _on_tree_task_selected(const Ref<BTTask> &p_task);
|
||||
void _on_tree_task_activated();
|
||||
void _on_visibility_changed();
|
||||
void _on_header_pressed();
|
||||
void _on_save_pressed();
|
||||
void _on_history_back();
|
||||
void _on_history_forward();
|
||||
|
@ -271,7 +273,7 @@ public:
|
|||
void set_window_layout(const Ref<ConfigFile> &p_configuration);
|
||||
void get_window_layout(const Ref<ConfigFile> &p_configuration);
|
||||
|
||||
void apply_changes();
|
||||
void save_all(bool p_external_only = false);
|
||||
|
||||
#ifdef LIMBOAI_GDEXTENSION
|
||||
virtual void _shortcut_input(const Ref<InputEvent> &p_event) override { _process_shortcut_input(p_event); }
|
||||
|
@ -297,23 +299,23 @@ public:
|
|||
|
||||
virtual String get_name() const override { return "LimboAI"; }
|
||||
virtual void make_visible(bool p_visible) override;
|
||||
virtual void apply_changes() override;
|
||||
virtual void edit(Object *p_object) override;
|
||||
virtual bool handles(Object *p_object) const override;
|
||||
virtual void set_window_layout(Ref<ConfigFile> p_configuration) override;
|
||||
virtual void get_window_layout(Ref<ConfigFile> p_configuration) override;
|
||||
virtual void save_external_data() override;
|
||||
|
||||
#elif LIMBOAI_GDEXTENSION
|
||||
bool _has_main_screen() const override { return true; }
|
||||
|
||||
virtual String _get_plugin_name() const override { return "LimboAI"; }
|
||||
virtual void _make_visible(bool p_visible) override;
|
||||
virtual void _apply_changes() override;
|
||||
virtual void _edit(Object *p_object) override;
|
||||
virtual bool _handles(Object *p_object) const override;
|
||||
virtual Ref<Texture2D> _get_plugin_icon() const override;
|
||||
virtual void _set_window_layout(const Ref<ConfigFile> &p_configuration) override;
|
||||
virtual void _get_window_layout(const Ref<ConfigFile> &p_configuration) override;
|
||||
virtual void _save_external_data() override;
|
||||
#endif // LIMBOAI_MODULE & LIMBOAI_GDEXTENSION
|
||||
|
||||
LimboAIEditorPlugin();
|
||||
|
|
|
@ -44,7 +44,7 @@ private:
|
|||
|
||||
_FORCE_INLINE_ void _set_mode_by_index(int p_index) {
|
||||
current_mode_index = p_index;
|
||||
BUTTON_SET_ICON(this, modes[current_mode_index].icon);
|
||||
this->set_button_icon(modes[current_mode_index].icon);
|
||||
if (!modes[current_mode_index].tooltip.is_empty()) {
|
||||
set_tooltip_text(modes[current_mode_index].tooltip);
|
||||
}
|
||||
|
|
|
@ -184,7 +184,7 @@ void TaskPaletteSection::set_filter(String p_filter_text) {
|
|||
void TaskPaletteSection::add_task_button(const String &p_name, const Ref<Texture> &icon, const String &p_meta) {
|
||||
TaskButton *btn = memnew(TaskButton);
|
||||
btn->set_text(p_name);
|
||||
BUTTON_SET_ICON(btn, icon);
|
||||
btn->set_button_icon(icon);
|
||||
btn->set_tooltip_text("dummy_text"); // Force tooltip to be shown.
|
||||
btn->set_task_meta(p_meta);
|
||||
btn->add_theme_constant_override(LW_NAME(icon_max_width), 16 * EDSCALE); // Force user icons to be of the proper size.
|
||||
|
@ -195,7 +195,7 @@ void TaskPaletteSection::add_task_button(const String &p_name, const Ref<Texture
|
|||
|
||||
void TaskPaletteSection::set_collapsed(bool p_collapsed) {
|
||||
tasks_container->set_visible(!p_collapsed);
|
||||
BUTTON_SET_ICON(section_header, (p_collapsed ? theme_cache.arrow_right_icon : theme_cache.arrow_down_icon));
|
||||
section_header->set_button_icon((p_collapsed ? theme_cache.arrow_right_icon : theme_cache.arrow_down_icon));
|
||||
}
|
||||
|
||||
bool TaskPaletteSection::is_collapsed() const {
|
||||
|
@ -214,7 +214,7 @@ void TaskPaletteSection::_notification(int p_what) {
|
|||
} break;
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
_do_update_theme_item_cache();
|
||||
BUTTON_SET_ICON(section_header, (is_collapsed() ? theme_cache.arrow_right_icon : theme_cache.arrow_down_icon));
|
||||
section_header->set_button_icon((is_collapsed() ? theme_cache.arrow_right_icon : theme_cache.arrow_down_icon));
|
||||
section_header->add_theme_font_override(LW_NAME(font), get_theme_font(LW_NAME(bold), LW_NAME(EditorFonts)));
|
||||
} break;
|
||||
}
|
||||
|
@ -600,13 +600,13 @@ void TaskPalette::_notification(int p_what) {
|
|||
case NOTIFICATION_THEME_CHANGED: {
|
||||
_do_update_theme_item_cache();
|
||||
|
||||
BUTTON_SET_ICON(tool_filters, get_theme_icon(LW_NAME(AnimationFilter), LW_NAME(EditorIcons)));
|
||||
BUTTON_SET_ICON(tool_refresh, get_theme_icon(LW_NAME(Reload), LW_NAME(EditorIcons)));
|
||||
tool_filters->set_button_icon(get_theme_icon(LW_NAME(AnimationFilter), LW_NAME(EditorIcons)));
|
||||
tool_refresh->set_button_icon(get_theme_icon(LW_NAME(Reload), LW_NAME(EditorIcons)));
|
||||
|
||||
filter_edit->set_right_icon(get_theme_icon(LW_NAME(Search), LW_NAME(EditorIcons)));
|
||||
|
||||
BUTTON_SET_ICON(select_all, LimboUtility::get_singleton()->get_task_icon("LimboSelectAll"));
|
||||
BUTTON_SET_ICON(deselect_all, LimboUtility::get_singleton()->get_task_icon("LimboDeselectAll"));
|
||||
select_all->set_button_icon(LimboUtility::get_singleton()->get_task_icon("LimboSelectAll"));
|
||||
deselect_all->set_button_icon(LimboUtility::get_singleton()->get_task_icon("LimboDeselectAll"));
|
||||
|
||||
category_choice->queue_redraw();
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ void TaskTree::_on_item_collapsed(Object *p_obj) {
|
|||
}
|
||||
|
||||
Ref<BTTask> task = item->get_metadata(0);
|
||||
ERR_FAIL_NULL(task);
|
||||
ERR_FAIL_COND(task.is_null());
|
||||
task->set_display_collapsed(item->is_collapsed());
|
||||
}
|
||||
|
||||
|
@ -574,7 +574,7 @@ void TaskTree::_bind_methods() {
|
|||
|
||||
// TreeSearch API
|
||||
void TaskTree::tree_search_show_and_focus() {
|
||||
ERR_FAIL_NULL(tree_search);
|
||||
ERR_FAIL_COND(tree_search.is_null());
|
||||
tree_search_panel->set_visible(true);
|
||||
tree_search_panel->focus_editor();
|
||||
}
|
||||
|
@ -587,7 +587,7 @@ TreeSearch::SearchInfo TaskTree::tree_search_get_search_info() const {
|
|||
}
|
||||
|
||||
void TaskTree::tree_search_set_search_info(const TreeSearch::SearchInfo &p_search_info) {
|
||||
ERR_FAIL_NULL(tree_search);
|
||||
ERR_FAIL_COND(tree_search.is_null());
|
||||
tree_search_panel->set_search_info(p_search_info);
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ protected:
|
|||
public:
|
||||
void load_bt(const Ref<BehaviorTree> &p_behavior_tree);
|
||||
void unload();
|
||||
Ref<BehaviorTree> get_bt() const { return bt; }
|
||||
_FORCE_INLINE_ Ref<BehaviorTree> get_bt() const { return bt; }
|
||||
void update_tree() { _update_tree(); }
|
||||
void update_task(const Ref<BTTask> &p_task);
|
||||
void add_selection(const Ref<BTTask> &p_task);
|
||||
|
|
|
@ -158,7 +158,7 @@ void TreeSearch::_draw_highlight_item(TreeItem *p_tree_item, const Rect2 p_rect,
|
|||
if (font.is_null()) {
|
||||
font = p_tree_item->get_tree()->get_theme_font(LW_NAME(font));
|
||||
}
|
||||
ERR_FAIL_NULL(font);
|
||||
ERR_FAIL_COND(font.is_null());
|
||||
float font_size = p_tree_item->get_custom_font_size(0);
|
||||
if (font_size == -1) {
|
||||
font_size = p_tree_item->get_tree()->get_theme_font_size(LW_NAME(font));
|
||||
|
@ -176,7 +176,7 @@ void TreeSearch::_draw_highlight_item(TreeItem *p_tree_item, const Rect2 p_rect,
|
|||
|
||||
// Stylebox
|
||||
Ref<StyleBox> stylebox = p_tree_item->get_tree()->get_theme_stylebox(LW_NAME(Focus));
|
||||
ERR_FAIL_NULL(stylebox);
|
||||
ERR_FAIL_COND(stylebox.is_null());
|
||||
|
||||
// Extract separation
|
||||
float h_sep = p_tree_item->get_tree()->get_theme_constant(LW_NAME(h_separation));
|
||||
|
@ -559,9 +559,9 @@ void TreeSearchPanel::_notification(int p_what) {
|
|||
break;
|
||||
}
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
BUTTON_SET_ICON(close_button, get_theme_icon(LW_NAME(Close), LW_NAME(EditorIcons)));
|
||||
BUTTON_SET_ICON(find_prev_button, get_theme_icon("MoveUp", LW_NAME(EditorIcons)));
|
||||
BUTTON_SET_ICON(find_next_button, get_theme_icon("MoveDown", LW_NAME(EditorIcons)));
|
||||
close_button->set_button_icon(get_theme_icon(LW_NAME(Close), LW_NAME(EditorIcons)));
|
||||
find_prev_button->set_button_icon(get_theme_icon("MoveUp", LW_NAME(EditorIcons)));
|
||||
find_next_button->set_button_icon(get_theme_icon("MoveDown", LW_NAME(EditorIcons)));
|
||||
label_filter->set_text(TTR("Filter"));
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
#endif // LIMBOAI_MODULE
|
||||
|
||||
#ifdef LIMBOAI_GDEXTENSION
|
||||
#include "editor/editor_property_property_path.h"
|
||||
#include <godot_cpp/classes/engine.hpp>
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/core/memory.hpp>
|
||||
|
@ -250,24 +251,26 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) {
|
|||
#ifdef TOOLS_ENABLED
|
||||
if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
|
||||
#ifdef LIMBOAI_GDEXTENSION
|
||||
GDREGISTER_CLASS(TaskTree);
|
||||
GDREGISTER_CLASS(TaskButton);
|
||||
GDREGISTER_CLASS(TaskPaletteSection);
|
||||
GDREGISTER_CLASS(TaskPalette);
|
||||
GDREGISTER_CLASS(ActionBanner);
|
||||
GDREGISTER_CLASS(ModeSwitchButton);
|
||||
GDREGISTER_CLASS(CompatShortcutBin);
|
||||
GDREGISTER_CLASS(CompatScreenSelect);
|
||||
GDREGISTER_CLASS(CompatWindowWrapper);
|
||||
GDREGISTER_CLASS(LimboDebuggerTab);
|
||||
GDREGISTER_CLASS(LimboDebuggerPlugin);
|
||||
GDREGISTER_CLASS(BlackboardPlanEditor);
|
||||
GDREGISTER_CLASS(EditorInspectorPluginBBPlan);
|
||||
GDREGISTER_CLASS(EditorPropertyVariableName);
|
||||
GDREGISTER_CLASS(EditorInspectorPluginVariableName);
|
||||
GDREGISTER_CLASS(OwnerPicker);
|
||||
GDREGISTER_CLASS(LimboAIEditor);
|
||||
GDREGISTER_CLASS(LimboAIEditorPlugin);
|
||||
GDREGISTER_INTERNAL_CLASS(TaskTree);
|
||||
GDREGISTER_INTERNAL_CLASS(TaskButton);
|
||||
GDREGISTER_INTERNAL_CLASS(TaskPaletteSection);
|
||||
GDREGISTER_INTERNAL_CLASS(TaskPalette);
|
||||
GDREGISTER_INTERNAL_CLASS(ActionBanner);
|
||||
GDREGISTER_INTERNAL_CLASS(ModeSwitchButton);
|
||||
GDREGISTER_INTERNAL_CLASS(CompatShortcutBin);
|
||||
GDREGISTER_INTERNAL_CLASS(CompatScreenSelect);
|
||||
GDREGISTER_INTERNAL_CLASS(CompatWindowWrapper);
|
||||
GDREGISTER_INTERNAL_CLASS(LimboDebuggerTab);
|
||||
GDREGISTER_INTERNAL_CLASS(LimboDebuggerPlugin);
|
||||
GDREGISTER_INTERNAL_CLASS(BlackboardPlanEditor);
|
||||
GDREGISTER_INTERNAL_CLASS(EditorInspectorPluginBBPlan);
|
||||
GDREGISTER_INTERNAL_CLASS(EditorInspectorPluginPropertyPath);
|
||||
GDREGISTER_INTERNAL_CLASS(EditorPropertyPropertyPath);
|
||||
GDREGISTER_INTERNAL_CLASS(EditorPropertyVariableName);
|
||||
GDREGISTER_INTERNAL_CLASS(EditorInspectorPluginVariableName);
|
||||
GDREGISTER_INTERNAL_CLASS(OwnerPicker);
|
||||
GDREGISTER_INTERNAL_CLASS(LimboAIEditor);
|
||||
GDREGISTER_INTERNAL_CLASS(LimboAIEditorPlugin);
|
||||
GDREGISTER_INTERNAL_CLASS(TreeSearchPanel);
|
||||
GDREGISTER_INTERNAL_CLASS(TreeSearch);
|
||||
#endif // LIMBOAI_GDEXTENSION
|
||||
|
|
|
@ -78,7 +78,7 @@ TEST_CASE("[Modules][LimboAI] BTTask") {
|
|||
SUBCASE("Test next_sibling()") {
|
||||
CHECK(child1->next_sibling() == child2);
|
||||
CHECK(child2->next_sibling() == child3);
|
||||
CHECK(child3->next_sibling() == nullptr);
|
||||
CHECK(child3->next_sibling().is_null());
|
||||
}
|
||||
SUBCASE("Test remove_child()") {
|
||||
task->remove_child(child2);
|
||||
|
@ -153,7 +153,7 @@ TEST_CASE("[Modules][LimboAI] BTTask") {
|
|||
CHECK(child3->get_root() == task);
|
||||
}
|
||||
SUBCASE("Test get_parent()") {
|
||||
CHECK(task->get_parent() == nullptr);
|
||||
CHECK(task->get_parent().is_null());
|
||||
CHECK(child1->get_parent() == task);
|
||||
CHECK(child2->get_parent() == task);
|
||||
CHECK(child2->get_parent() == task);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#ifdef TOOLS_ENABLED
|
||||
#include "core/io/resource.h"
|
||||
#include "core/variant/variant.h"
|
||||
#include "editor/editor_main_screen.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#endif // TOOLS_ENABLED
|
||||
|
@ -213,7 +214,7 @@ Variant VARIANT_DEFAULT(Variant::Type p_type) {
|
|||
void SHOW_BUILTIN_DOC(const String &p_topic) {
|
||||
#ifdef LIMBOAI_MODULE
|
||||
ScriptEditor::get_singleton()->goto_help(p_topic);
|
||||
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
|
||||
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
|
||||
#elif LIMBOAI_GDEXTENSION
|
||||
TypedArray<ScriptEditorBase> open_editors = EditorInterface::get_singleton()->get_script_editor()->get_open_script_editors();
|
||||
ERR_FAIL_COND_MSG(open_editors.size() == 0, "Can't open help page. Need at least one script open in the script editor.");
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#define EDITOR_FILE_SYSTEM() (EditorFileSystem::get_singleton())
|
||||
#define EDITOR_SETTINGS() (EditorSettings::get_singleton())
|
||||
#define BASE_CONTROL() (EditorNode::get_singleton()->get_gui_base())
|
||||
#define MAIN_SCREEN_CONTROL() (EditorNode::get_singleton()->get_main_screen_control())
|
||||
#define MAIN_SCREEN_CONTROL() (EditorNode::get_singleton()->get_editor_main_screen())
|
||||
#define SCENE_TREE() (SceneTree::get_singleton())
|
||||
#define IS_DEBUGGER_ACTIVE() (EngineDebugger::is_active())
|
||||
#define FS_DOCK_SELECT_FILE(m_path) FileSystemDock::get_singleton()->select_file(m_path)
|
||||
|
@ -37,7 +37,6 @@
|
|||
#define IS_CLASS(m_obj, m_class) (m_obj->is_class_ptr(m_class::get_class_ptr_static()))
|
||||
#define RAND_RANGE(m_from, m_to) (Math::random(m_from, m_to))
|
||||
#define RANDF() (Math::randf())
|
||||
#define BUTTON_SET_ICON(m_btn, m_icon) m_btn->set_icon(m_icon)
|
||||
#define RESOURCE_LOAD(m_path, m_hint) ResourceLoader::load(m_path, m_hint)
|
||||
#define RESOURCE_LOAD_NO_CACHE(m_path, m_hint) ResourceLoader::load(m_path, m_hint, ResourceFormatLoader::CACHE_MODE_IGNORE)
|
||||
#define RESOURCE_SAVE(m_res, m_path, m_flags) ResourceSaver::save(m_res, m_path, m_flags)
|
||||
|
@ -47,7 +46,7 @@
|
|||
#define GET_PROJECT_SETTINGS_DIR() EditorPaths::get_singleton()->get_project_settings_dir()
|
||||
#define EDIT_RESOURCE(m_res) EditorNode::get_singleton()->edit_resource(m_res)
|
||||
#define INSPECTOR_GET_EDITED_OBJECT() (InspectorDock::get_inspector_singleton()->get_edited_object())
|
||||
#define SET_MAIN_SCREEN_EDITOR(m_name) (EditorNode::get_singleton()->select_editor_by_name(m_name))
|
||||
#define SET_MAIN_SCREEN_EDITOR(m_name) (EditorNode::get_singleton()->get_editor_main_screen()->select_by_name(m_name))
|
||||
#define FILE_EXISTS(m_path) FileAccess::exists(m_path)
|
||||
#define DIR_ACCESS_CREATE() DirAccess::create(DirAccess::ACCESS_RESOURCES)
|
||||
#define PERFORMANCE_ADD_CUSTOM_MONITOR(m_id, m_callable) (Performance::get_singleton()->add_custom_monitor(m_id, m_callable, Variant()))
|
||||
|
@ -96,7 +95,6 @@ using namespace godot;
|
|||
#define IS_CLASS(m_obj, m_class) (m_obj->is_class(#m_class))
|
||||
#define RAND_RANGE(m_from, m_to) (UtilityFunctions::randf_range(m_from, m_to))
|
||||
#define RANDF() (UtilityFunctions::randf())
|
||||
#define BUTTON_SET_ICON(m_btn, m_icon) m_btn->set_button_icon(m_icon)
|
||||
#define RESOURCE_LOAD(m_path, m_hint) ResourceLoader::get_singleton()->load(m_path, m_hint)
|
||||
#define RESOURCE_LOAD_NO_CACHE(m_path, m_hint) ResourceLoader::get_singleton()->load(m_path, m_hint, ResourceLoader::CACHE_MODE_IGNORE)
|
||||
#define RESOURCE_SAVE(m_res, m_path, m_flags) ResourceSaver::get_singleton()->save(m_res, m_path, m_flags)
|
||||
|
@ -152,6 +150,7 @@ Variant _GLOBAL_DEF(const PropertyInfo &p_info, const Variant &p_default, bool p
|
|||
#define EDSCALE (EditorInterface::get_singleton()->get_editor_scale())
|
||||
|
||||
String TTR(const String &p_text, const String &p_context = "");
|
||||
#define RTR(m_text) TTR(m_text)
|
||||
|
||||
#endif // ! LIMBOAI_GDEXTENSION
|
||||
|
||||
|
@ -175,7 +174,9 @@ Variant VARIANT_DEFAULT(Variant::Type p_type);
|
|||
#define IS_RESOURCE_FILE(m_path) (m_path.begins_with("res://") && m_path.find("::") == -1)
|
||||
#define RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type)
|
||||
#define RESOURCE_IS_BUILT_IN(m_res) (m_res->get_path().is_empty() || m_res->get_path().contains("::"))
|
||||
#define RESOURCE_IS_EXTERNAL(m_res) (!RESOURCE_IS_BUILT_IN(m_res))
|
||||
#define RESOURCE_PATH_IS_BUILT_IN(m_path) (m_path.is_empty() || m_path.contains("::"))
|
||||
#define RESOURCE_PATH_IS_EXTERNAL(m_path) (!RESOURCE_PATH_IS_BUILT_IN(m_path))
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ LimboStringNames::LimboStringNames() {
|
|||
button_up = SN("button_up");
|
||||
call_deferred = SN("call_deferred");
|
||||
changed = SN("changed");
|
||||
Clear = SN("Clear");
|
||||
Close = SN("Close");
|
||||
dark_color_2 = SN("dark_color_2");
|
||||
Debug = SN("Debug");
|
||||
|
@ -67,6 +68,7 @@ LimboStringNames::LimboStringNames() {
|
|||
EVENT_FINISHED = SN("finished");
|
||||
EVENT_SUCCESS = SN("success");
|
||||
exited = SN("exited");
|
||||
ExternalLink = SN("ExternalLink");
|
||||
favorite_tasks_changed = SN("favorite_tasks_changed");
|
||||
Favorites = SN("Favorites");
|
||||
FlatButton = SN("FlatButton");
|
||||
|
@ -78,6 +80,7 @@ LimboStringNames::LimboStringNames() {
|
|||
freed = SN("freed");
|
||||
gui_input = SN("gui_input");
|
||||
GuiOptionArrow = SN("GuiOptionArrow");
|
||||
GuiTabMenuHl = SN("GuiTabMenuHl");
|
||||
GuiTreeArrowDown = SN("GuiTreeArrowDown");
|
||||
GuiTreeArrowRight = SN("GuiTreeArrowRight");
|
||||
HeaderSmall = SN("HeaderSmall");
|
||||
|
|
|
@ -63,6 +63,7 @@ public:
|
|||
StringName button_up;
|
||||
StringName call_deferred;
|
||||
StringName changed;
|
||||
StringName Clear;
|
||||
StringName Close;
|
||||
StringName dark_color_2;
|
||||
StringName Debug;
|
||||
|
@ -83,6 +84,7 @@ public:
|
|||
StringName EVENT_FINISHED;
|
||||
StringName EVENT_SUCCESS;
|
||||
StringName exited;
|
||||
StringName ExternalLink;
|
||||
StringName favorite_tasks_changed;
|
||||
StringName Favorites;
|
||||
StringName FlatButton;
|
||||
|
@ -94,6 +96,7 @@ public:
|
|||
StringName freed;
|
||||
StringName gui_input;
|
||||
StringName GuiOptionArrow;
|
||||
StringName GuiTabMenuHl;
|
||||
StringName GuiTreeArrowDown;
|
||||
StringName GuiTreeArrowRight;
|
||||
StringName HeaderSmall;
|
||||
|
|
|
@ -395,6 +395,12 @@ String LimboUtility::get_property_hint_text(PropertyHint p_hint) const {
|
|||
case PROPERTY_HINT_ARRAY_TYPE: {
|
||||
return "ARRAY_TYPE";
|
||||
}
|
||||
case PROPERTY_HINT_DICTIONARY_TYPE: {
|
||||
return "DICTIONARY_TYPE";
|
||||
}
|
||||
case PROPERTY_HINT_TOOL_BUTTON: {
|
||||
return "TOOL_BUTTON";
|
||||
}
|
||||
case PROPERTY_HINT_LOCALE_ID: {
|
||||
return "LOCALE_ID";
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue