diff options
28 files changed, 521 insertions, 290 deletions
diff --git a/SConstruct b/SConstruct index 856994ef15..61edb1233b 100644 --- a/SConstruct +++ b/SConstruct @@ -326,6 +326,8 @@ if selected_platform in platform_list: env.Append(CCFLAGS=['/W2'] + disable_nonessential_warnings) else: # 'no' env.Append(CCFLAGS=['/w']) + # Set exception handling model to avoid warnings caused by Windows system headers. + env.Append(CCFLAGS=['/EHsc']) else: # Rest of the world if (env["warnings"] == 'extra'): env.Append(CCFLAGS=['-Wall', '-Wextra']) diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 1d9d2dd959..37320d7a77 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -324,7 +324,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int ERR_FAIL_COND_V(len < 12, ERR_INVALID_DATA); Vector<StringName> names; Vector<StringName> subnames; - StringName prop; uint32_t namecount = strlen &= 0x7FFFFFFF; uint32_t subnamecount = decode_uint32(buf + 4); @@ -333,9 +332,10 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int len -= 12; buf += 12; + if (flags & 2) // Obsolete format with property seperate from subpath + subnamecount++; + uint32_t total = namecount + subnamecount; - if (flags & 2) - total++; if (r_len) (*r_len) += 12; @@ -359,10 +359,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (i < namecount) names.push_back(str); - else if (i < namecount + subnamecount) - subnames.push_back(str); else - prop = str; + subnames.push_back(str); buf += strlen + pad; len -= strlen + pad; @@ -371,7 +369,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int (*r_len) += 4 + strlen + pad; } - r_variant = NodePath(names, subnames, flags & 1, prop); + r_variant = NodePath(names, subnames, flags & 1); } else { //old format, just a string @@ -919,8 +917,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo uint32_t flags = 0; if (np.is_absolute()) flags |= 1; - if (np.get_property() != StringName()) - flags |= 2; encode_uint32(flags, buf + 8); @@ -930,8 +926,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 12; int total = np.get_name_count() + np.get_subname_count(); - if (np.get_property() != StringName()) - total++; for (int i = 0; i < total; i++) { @@ -939,10 +933,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo if (i < np.get_name_count()) str = np.get_name(i); - else if (i < np.get_name_count() + np.get_subname_count()) - str = np.get_subname(i - np.get_subname_count()); else - str = np.get_property(); + str = np.get_subname(i - np.get_name_count()); CharString utf8 = str.utf8(); diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 8dc396c362..aa51b00028 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -267,7 +267,6 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { Vector<StringName> names; Vector<StringName> subnames; - StringName property; bool absolute; int name_count = f->get_16(); @@ -279,9 +278,8 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { names.push_back(_get_string()); for (uint32_t i = 0; i < subname_count; i++) subnames.push_back(_get_string()); - property = _get_string(); - NodePath np = NodePath(names, subnames, absolute, property); + NodePath np = NodePath(names, subnames, absolute); r_v = np; @@ -1454,7 +1452,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(const Variant &p_property, f->store_32(get_string_index(np.get_name(i))); for (int i = 0; i < np.get_subname_count(); i++) f->store_32(get_string_index(np.get_subname(i))); - f->store_32(get_string_index(np.get_property())); } break; case Variant::_RID: { @@ -1685,7 +1682,6 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant get_string_index(np.get_name(i)); for (int i = 0; i < np.get_subname_count(); i++) get_string_index(np.get_subname(i)); - get_string_index(np.get_property()); } break; default: {} diff --git a/core/node_path.cpp b/core/node_path.cpp index 15f950f605..a01bb420a5 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -48,8 +48,6 @@ uint32_t NodePath::hash() const { h = h ^ ssn[i].hash(); } - h = h ^ data->property.hash(); - return h; } @@ -81,13 +79,6 @@ StringName NodePath::get_name(int p_idx) const { return data->path[p_idx]; } -StringName NodePath::get_property() const { - - if (!data) - return StringName(); - return data->property; -} - int NodePath::get_subname_count() const { if (!data) @@ -128,9 +119,6 @@ bool NodePath::operator==(const NodePath &p_path) const { if (data->subpath.size() != p_path.data->subpath.size()) return false; - if (data->property != p_path.data->property) - return false; - for (int i = 0; i < data->path.size(); i++) { if (data->path[i] != p_path.data->path[i]) @@ -184,8 +172,6 @@ NodePath::operator String() const { ret += ":" + data->subpath[i].operator String(); } - if (data->property.operator String() != "") - ret += ":" + String(data->property); return ret; } @@ -205,6 +191,7 @@ Vector<StringName> NodePath::get_names() const { return data->path; return Vector<StringName>(); } + Vector<StringName> NodePath::get_subnames() const { if (data) @@ -212,6 +199,21 @@ Vector<StringName> NodePath::get_subnames() const { return Vector<StringName>(); } +StringName NodePath::get_concatenated_subnames() const { + ERR_FAIL_COND_V(!data, StringName()); + + if (!data->concatenated_subpath) { + int spc = data->subpath.size(); + String concatenated; + const StringName *ssn = data->subpath.ptr(); + for (int i = 0; i < spc; i++) { + concatenated += i == 0 ? ssn[i].operator String() : "." + ssn[i]; + } + data->concatenated_subpath = concatenated; + } + return data->concatenated_subpath; +} + NodePath NodePath::rel_path_to(const NodePath &p_np) const { ERR_FAIL_COND_V(!is_absolute(), NodePath()); @@ -250,10 +252,23 @@ NodePath NodePath::rel_path_to(const NodePath &p_np) const { if (relpath.size() == 0) relpath.push_back("."); - return NodePath(relpath, p_np.get_subnames(), false, p_np.get_property()); + return NodePath(relpath, p_np.get_subnames(), false); +} + +NodePath NodePath::get_as_property_path() const { + + if (data->has_slashes || !data->path.size()) { + return NodePath(Vector<StringName>(), data->subpath, false); + } else { + ERR_FAIL_COND_V(data->path.size() != 1, NodePath()); + + Vector<StringName> new_path = data->subpath; + new_path.insert(0, data->path[0]); + return NodePath(Vector<StringName>(), new_path, false); + } } -NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute, const String &p_property) { +NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute) { data = NULL; @@ -264,14 +279,14 @@ NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute, const Stri data->refcount.init(); data->absolute = p_absolute; data->path = p_path; - data->property = p_property; + data->has_slashes = true; } -NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute, const String &p_property) { +NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute) { data = NULL; - if (p_path.size() == 0) + if (p_path.size() == 0 && p_subpath.size() == 0) return; data = memnew(Data); @@ -279,7 +294,7 @@ NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p data->absolute = p_absolute; data->path = p_path; data->subpath = p_subpath; - data->property = p_property; + data->has_slashes = true; } void NodePath::simplify() { @@ -325,6 +340,7 @@ NodePath::NodePath(const String &p_path) { int absolute = (path[0] == '/') ? 1 : 0; bool last_is_slash = true; + bool has_slashes = false; int slices = 0; int subpath_pos = path.find(":"); @@ -337,16 +353,13 @@ NodePath::NodePath(const String &p_path) { if (path[i] == ':' || path[i] == 0) { String str = path.substr(from, i - from); - if (path[i] == ':') { - if (str == "") { - ERR_EXPLAIN("Invalid NodePath: " + p_path); - ERR_FAIL(); - } - subpath.push_back(str); - } else { - //property can be empty - property = str; + if (str == "") { + if (path[i] == 0) continue; // Allow end-of-path : + + ERR_EXPLAIN("Invalid NodePath: " + p_path); + ERR_FAIL(); } + subpath.push_back(str); from = i + 1; } @@ -360,6 +373,7 @@ NodePath::NodePath(const String &p_path) { if (path[i] == '/') { last_is_slash = true; + has_slashes = true; } else { if (last_is_slash) @@ -369,13 +383,13 @@ NodePath::NodePath(const String &p_path) { } } - if (slices == 0 && !absolute && !property) + if (slices == 0 && !absolute && !subpath.size()) return; data = memnew(Data); data->refcount.init(); data->absolute = absolute ? true : false; - data->property = property; + data->has_slashes = has_slashes; data->subpath = subpath; if (slices == 0) diff --git a/core/node_path.h b/core/node_path.h index eb5b9eb6cf..063c4f62db 100644 --- a/core/node_path.h +++ b/core/node_path.h @@ -41,10 +41,11 @@ class NodePath { struct Data { SafeRefCount refcount; - StringName property; Vector<StringName> path; Vector<StringName> subpath; + StringName concatenated_subpath; bool absolute; + bool has_slashes; }; Data *data; @@ -53,7 +54,7 @@ class NodePath { public: _FORCE_INLINE_ StringName get_sname() const { - if (data && data->path.size() == 1 && data->subpath.empty() && !data->property) { + if (data && data->path.size() == 1 && data->subpath.empty()) { return data->path[0]; } else { return operator String(); @@ -67,13 +68,13 @@ public: StringName get_subname(int p_idx) const; Vector<StringName> get_names() const; Vector<StringName> get_subnames() const; + StringName get_concatenated_subnames() const; NodePath rel_path_to(const NodePath &p_np) const; + NodePath get_as_property_path() const; void prepend_period(); - StringName get_property() const; - NodePath get_parent() const; uint32_t hash() const; @@ -88,8 +89,8 @@ public: void simplify(); NodePath simplified() const; - NodePath(const Vector<StringName> &p_path, bool p_absolute, const String &p_property = ""); - NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute, const String &p_property = ""); + NodePath(const Vector<StringName> &p_path, bool p_absolute); + NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute); NodePath(const NodePath &p_path); NodePath(const String &p_path); NodePath(); diff --git a/core/object.cpp b/core/object.cpp index 823cbe14d4..631676d827 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -517,6 +517,80 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { } } +void Object::set_indexed(const Vector<StringName> &p_names, const Variant &p_value, bool *r_valid) { + if (p_names.empty()) { + if (r_valid) + *r_valid = false; + return; + } + if (p_names.size() == 1) { + set(p_names[0], p_value, r_valid); + return; + } + + bool valid = false; + if (!r_valid) r_valid = &valid; + + List<Variant> value_stack; + + value_stack.push_back(get(p_names[0], r_valid)); + + if (!*r_valid) { + value_stack.clear(); + return; + } + + for (int i = 1; i < p_names.size() - 1; i++) { + value_stack.push_back(value_stack.back()->get().get_named(p_names[i], r_valid)); + + if (!*r_valid) { + value_stack.clear(); + return; + } + } + + value_stack.push_back(p_value); // p_names[p_names.size() - 1] + + for (int i = p_names.size() - 1; i > 0; i--) { + + value_stack.back()->prev()->get().set_named(p_names[i], value_stack.back()->get(), r_valid); + value_stack.pop_back(); + + if (!*r_valid) { + value_stack.clear(); + return; + } + } + + set(p_names[0], value_stack.back()->get(), r_valid); + value_stack.pop_back(); + + ERR_FAIL_COND(!value_stack.empty()); +} + +Variant Object::get_indexed(const Vector<StringName> &p_names, bool *r_valid) const { + if (p_names.empty()) { + if (r_valid) + *r_valid = false; + return Variant(); + } + bool valid = false; + + Variant current_value = get(p_names[0]); + for (int i = 1; i < p_names.size(); i++) { + current_value = current_value.get_named(p_names[i], &valid); + + if (!valid) { + if (r_valid) + *r_valid = false; + return Variant(); + } + } + if (r_valid) + *r_valid = true; + return current_value; +} + void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) const { if (script_instance && p_reversed) { @@ -1416,6 +1490,16 @@ Variant Object::_get_bind(const String &p_name) const { return get(p_name); } +void Object::_set_indexed_bind(const NodePath &p_name, const Variant &p_value) { + + set_indexed(p_name.get_as_property_path().get_subnames(), p_value); +} + +Variant Object::_get_indexed_bind(const NodePath &p_name) const { + + return get_indexed(p_name.get_as_property_path().get_subnames()); +} + void Object::initialize_class() { static bool initialized = false; @@ -1513,6 +1597,8 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("is_class", "type"), &Object::is_class); ClassDB::bind_method(D_METHOD("set", "property", "value"), &Object::_set_bind); ClassDB::bind_method(D_METHOD("get", "property"), &Object::_get_bind); + ClassDB::bind_method(D_METHOD("set_indexed", "property", "value"), &Object::_set_indexed_bind); + ClassDB::bind_method(D_METHOD("get_indexed", "property"), &Object::_get_indexed_bind); ClassDB::bind_method(D_METHOD("get_property_list"), &Object::_get_property_list_bind); ClassDB::bind_method(D_METHOD("get_method_list"), &Object::_get_method_list_bind); ClassDB::bind_method(D_METHOD("notification", "what", "reversed"), &Object::notification, DEFVAL(false)); @@ -1661,6 +1747,43 @@ Variant::Type Object::get_static_property_type(const StringName &p_property, boo return Variant::NIL; } +Variant::Type Object::get_static_property_type_indexed(const Vector<StringName> &p_path, bool *r_valid) const { + + bool valid = false; + Variant::Type t = get_static_property_type(p_path[0], &valid); + if (!valid) { + if (r_valid) + *r_valid = false; + + return Variant::NIL; + } + + Variant::CallError ce; + Variant check = Variant::construct(t, NULL, 0, ce); + + for (int i = 1; i < p_path.size(); i++) { + if (check.get_type() == Variant::OBJECT || check.get_type() == Variant::DICTIONARY || check.get_type() == Variant::ARRAY) { + // We cannot be sure about the type of properties this types can have + if (r_valid) + *r_valid = false; + return Variant::NIL; + } + + check = check.get_named(p_path[i], &valid); + + if (!valid) { + if (r_valid) + *r_valid = false; + return Variant::NIL; + } + } + + if (r_valid) + *r_valid = true; + + return check.get_type(); +} + bool Object::is_queued_for_deletion() const { return _is_queued_for_deletion; } diff --git a/core/object.h b/core/object.h index 7af2c78fc3..3ac699f978 100644 --- a/core/object.h +++ b/core/object.h @@ -477,6 +477,8 @@ private: Array _get_incoming_connections() const; void _set_bind(const String &p_set, const Variant &p_value); Variant _get_bind(const String &p_name) const; + void _set_indexed_bind(const NodePath &p_name, const Variant &p_value); + Variant _get_indexed_bind(const NodePath &p_name) const; void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS]; @@ -627,6 +629,8 @@ public: void set(const StringName &p_name, const Variant &p_value, bool *r_valid = NULL); Variant get(const StringName &p_name, bool *r_valid = NULL) const; + void set_indexed(const Vector<StringName> &p_names, const Variant &p_value, bool *r_valid = NULL); + Variant get_indexed(const Vector<StringName> &p_names, bool *r_valid = NULL) const; void get_property_list(List<PropertyInfo> *p_list, bool p_reversed = false) const; @@ -687,6 +691,7 @@ public: bool is_blocking_signals() const; Variant::Type get_static_property_type(const StringName &p_property, bool *r_valid = NULL) const; + Variant::Type get_static_property_type_indexed(const Vector<StringName> &p_path, bool *r_valid = NULL) const; virtual void get_translatable_strings(List<String> *p_strings) const; diff --git a/core/os/os.cpp b/core/os/os.cpp index 0e7e26df73..84937c0e59 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -554,7 +554,7 @@ bool OS::has_feature(const String &p_feature) { if (sizeof(void *) == 4 && p_feature == "32") { return true; } -#if defined(__x86_64) || defined(__x86_64__) +#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) if (p_feature == "x86_64") { return true; } diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 12750c9001..78047b8dad 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -446,7 +446,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(NodePath, get_name); VCALL_LOCALMEM0R(NodePath, get_subname_count); VCALL_LOCALMEM1R(NodePath, get_subname); - VCALL_LOCALMEM0R(NodePath, get_property); + VCALL_LOCALMEM0R(NodePath, get_concatenated_subnames); VCALL_LOCALMEM0R(NodePath, is_empty); VCALL_LOCALMEM0R(Dictionary, size); @@ -1587,7 +1587,7 @@ void register_variant_methods() { ADDFUNC1R(NODE_PATH, STRING, NodePath, get_name, INT, "idx", varray()); ADDFUNC0R(NODE_PATH, INT, NodePath, get_subname_count, varray()); ADDFUNC1R(NODE_PATH, STRING, NodePath, get_subname, INT, "idx", varray()); - ADDFUNC0R(NODE_PATH, STRING, NodePath, get_property, varray()); + ADDFUNC0R(NODE_PATH, STRING, NodePath, get_concatenated_subnames, varray()); ADDFUNC0R(NODE_PATH, BOOL, NodePath, is_empty, varray()); ADDFUNC0R(DICTIONARY, INT, Dictionary, size, varray()); diff --git a/editor/animation_editor.cpp b/editor/animation_editor.cpp index ae304ed0bc..1d70f8ba55 100644 --- a/editor/animation_editor.cpp +++ b/editor/animation_editor.cpp @@ -966,7 +966,9 @@ void AnimationKeyEditor::_cleanup_animation(Ref<Animation> p_animation) { Object *obj = NULL; RES res; - Node *node = root->get_node_and_resource(p_animation->track_get_path(i), res); + Vector<StringName> leftover_path; + + Node *node = root->get_node_and_resource(p_animation->track_get_path(i), res, leftover_path); if (res.is_valid()) { obj = res.ptr(); @@ -975,7 +977,7 @@ void AnimationKeyEditor::_cleanup_animation(Ref<Animation> p_animation) { } if (obj && p_animation->track_get_type(i) == Animation::TYPE_VALUE) { - valid_type = obj->get_static_property_type(p_animation->track_get_path(i).get_property(), &prop_exists); + valid_type = obj->get_static_property_type_indexed(leftover_path, &prop_exists); } if (!obj && cleanup_tracks->is_pressed()) { @@ -1315,7 +1317,9 @@ void AnimationKeyEditor::_track_editor_draw() { Object *obj = NULL; RES res; - Node *node = root ? root->get_node_and_resource(animation->track_get_path(idx), res) : (Node *)NULL; + Vector<StringName> leftover_path; + + Node *node = root ? root->get_node_and_resource(animation->track_get_path(idx), res, leftover_path) : (Node *)NULL; if (res.is_valid()) { obj = res.ptr(); @@ -1324,7 +1328,8 @@ void AnimationKeyEditor::_track_editor_draw() { } if (obj && animation->track_get_type(idx) == Animation::TYPE_VALUE) { - valid_type = obj->get_static_property_type(animation->track_get_path(idx).get_property(), &prop_exists); + // While leftover_path might be still empty, we wouldn't be able to get here anyway + valid_type = obj->get_static_property_type_indexed(leftover_path, &prop_exists); } if (/*mouse_over.over!=MouseOver::OVER_NONE &&*/ idx == mouse_over.track) { @@ -1648,26 +1653,34 @@ PropertyInfo AnimationKeyEditor::_find_hint_for_track(int p_idx, NodePath &r_bas return PropertyInfo(); RES res; - Node *node = root->get_node_and_resource(path, res); + Vector<StringName> leftover_path; + Node *node = root->get_node_and_resource(path, res, leftover_path, true); if (node) { r_base_path = node->get_path(); } - String property = path.get_property(); - if (property == "") + if (leftover_path.empty()) return PropertyInfo(); - List<PropertyInfo> pinfo; + Variant property_info_base; if (res.is_valid()) - res->get_property_list(&pinfo); + property_info_base = res; else if (node) - node->get_property_list(&pinfo); + property_info_base = node; + + for (int i = 0; i < leftover_path.size() - 1; i++) { + property_info_base = property_info_base.get_named(leftover_path[i]); + } + + List<PropertyInfo> pinfo; + property_info_base.get_property_list(&pinfo); for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - if (E->get().name == property) + if (E->get().name == leftover_path[leftover_path.size() - 1]) { return E->get(); + } } return PropertyInfo(); @@ -2779,7 +2792,8 @@ void AnimationKeyEditor::_track_editor_gui_input(const Ref<InputEvent> &p_input) Object *obj = NULL; RES res; - Node *node = root->get_node_and_resource(animation->track_get_path(idx), res); + Vector<StringName> leftover_path; + Node *node = root->get_node_and_resource(animation->track_get_path(idx), res, leftover_path); if (res.is_valid()) { obj = res.ptr(); @@ -2788,7 +2802,7 @@ void AnimationKeyEditor::_track_editor_gui_input(const Ref<InputEvent> &p_input) } if (obj) { - valid_type = obj->get_static_property_type(animation->track_get_path(idx).get_property(), &prop_exists); + valid_type = obj->get_static_property_type_indexed(leftover_path, &prop_exists); } text += "type: " + Variant::get_type_name(v.get_type()) + "\n"; diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 22d23e1c72..8fe6538653 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -1196,14 +1196,14 @@ void AnimationTreeEditor::_edit_filters() { if (base) { NodePath np = E->get(); - if (np.get_property() != StringName()) { + if (np.get_subname_count() == 1) { Node *n = base->get_node(np); Skeleton *s = Object::cast_to<Skeleton>(n); if (s) { String skelbase = E->get().substr(0, E->get().find(":")); - int bidx = s->find_bone(np.get_property()); + int bidx = s->find_bone(np.get_subname(0)); if (bidx != -1) { int bparent = s->get_bone_parent(bidx); @@ -1213,7 +1213,7 @@ void AnimationTreeEditor::_edit_filters() { String bpn = skelbase + ":" + s->get_bone_name(bparent); if (pm.has(bpn)) { parent = pm[bpn]; - descr = np.get_property(); + descr = np.get_subname(0); } } else { diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 1d2647badc..a97a5630e6 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -986,7 +986,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP //will be renamed NodePath rel_path = new_root_path.rel_path_to(E->get().second); - NodePath new_path = NodePath(rel_path.get_names(), track_np.get_subnames(), false, track_np.get_property()); + NodePath new_path = NodePath(rel_path.get_names(), track_np.get_subnames(), false); if (new_path == track_np) continue; //bleh editor_data->get_undo_redo().add_do_method(anim.ptr(), "track_set_path", i, new_path); diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 2a3e47b2a6..ef082f29c3 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -1127,6 +1127,15 @@ void ScriptEditorDebugger::_notification(int p_what) { tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles")); tabs->set_margin(MARGIN_LEFT, -EditorNode::get_singleton()->get_gui_base()->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_LEFT)); tabs->set_margin(MARGIN_RIGHT, EditorNode::get_singleton()->get_gui_base()->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles")->get_margin(MARGIN_RIGHT)); + + bool enable_rl = EditorSettings::get_singleton()->get("docks/scene_tree/draw_relationship_lines"); + Color rl_color = EditorSettings::get_singleton()->get("docks/scene_tree/relationship_line_color"); + + if (enable_rl) { + inspect_scene_tree->add_constant_override("draw_relationship_lines", 1); + inspect_scene_tree->add_color_override("relationship_line_color", rl_color); + } else + inspect_scene_tree->add_constant_override("draw_relationship_lines", 0); } break; } } diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index de118043ca..0132ef3c5d 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -123,7 +123,13 @@ bool GDNative::initialize() { return false; } #ifdef IPHONE_ENABLED + // on iOS we use static linking String path = ""; +#elif defined(ANDROID_ENABLED) + // On Android dynamic libraries are located separately from resource assets, + // we should pass library name to dlopen(). The library name is flattened + // during export. + String path = lib_path.get_file(); #else String path = ProjectSettings::get_singleton()->globalize_path(lib_path); #endif diff --git a/modules/gdnative/gdnative/node_path.cpp b/modules/gdnative/gdnative/node_path.cpp index 2bd278e050..8dfe151f91 100644 --- a/modules/gdnative/gdnative/node_path.cpp +++ b/modules/gdnative/gdnative/node_path.cpp @@ -91,10 +91,10 @@ godot_string GDAPI godot_node_path_get_subname(const godot_node_path *p_self, co return dest; } -godot_string GDAPI godot_node_path_get_property(const godot_node_path *p_self) { +godot_string GDAPI godot_node_path_get_concatenated_subnames(const godot_node_path *p_self) { godot_string dest; const NodePath *self = (const NodePath *)p_self; - memnew_placement(&dest, String(self->get_property())); + memnew_placement(&dest, String(self->get_concatenated_subnames())); return dest; } diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 0438a196cf..488ed93206 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -2918,7 +2918,7 @@ ] }, { - "name": "godot_node_path_get_property", + "name": "godot_node_path_get_concatenated_subnames", "return_type": "godot_string", "arguments": [ ["const godot_node_path *", "p_self"] diff --git a/modules/gdnative/include/gdnative/node_path.h b/modules/gdnative/include/gdnative/node_path.h index 42446175d8..b5a59fd325 100644 --- a/modules/gdnative/include/gdnative/node_path.h +++ b/modules/gdnative/include/gdnative/node_path.h @@ -73,7 +73,7 @@ godot_int GDAPI godot_node_path_get_subname_count(const godot_node_path *p_self) godot_string GDAPI godot_node_path_get_subname(const godot_node_path *p_self, const godot_int p_idx); -godot_string GDAPI godot_node_path_get_property(const godot_node_path *p_self); +godot_string GDAPI godot_node_path_get_concatenated_subnames(const godot_node_path *p_self); godot_bool GDAPI godot_node_path_is_empty(const godot_node_path *p_self); diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index e1ff12c5ac..8776e6081e 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -467,52 +467,72 @@ class EditorExportAndroid : public EditorExportPlatform { return zipfi; } - static Set<String> get_abis() { - Set<String> abis; - abis.insert("armeabi"); - abis.insert("armeabi-v7a"); - abis.insert("arm64-v8a"); - abis.insert("x86"); - abis.insert("x86_64"); - abis.insert("mips"); - abis.insert("mips64"); + static Vector<String> get_abis() { + // mips and armv6 are dead (especially for games), so not including them + Vector<String> abis; + abis.push_back("armeabi-v7a"); + abis.push_back("arm64-v8a"); + abis.push_back("x86"); + abis.push_back("x86_64"); return abis; } - static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { - APKExportData *ed = (APKExportData *)p_userdata; - String dst_path = p_path; - static Set<String> android_abis = get_abis(); - - if (dst_path.ends_with(".so")) { - String abi = dst_path.get_base_dir().get_file().strip_edges(); // parent dir name - if (android_abis.has(abi)) { - dst_path = "lib/" + abi + "/" + dst_path.get_file(); - } else { - String err = "Dynamic libraries must be located in the folder named after Android ABI they were compiled for. " + - p_path + " does not follow this convention."; - ERR_PRINT(err.utf8().get_data()); - return ERR_FILE_BAD_PATH; - } - } else { - dst_path = dst_path.replace_first("res://", "assets/"); - } - + static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) { zip_fileinfo zipfi = get_zip_fileinfo(); - zipOpenNewFileInZip(ed->apk, - dst_path.utf8().get_data(), + p_path.utf8().get_data(), &zipfi, NULL, 0, NULL, 0, NULL, - _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0, + compression_method, Z_DEFAULT_COMPRESSION); zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size()); zipCloseFileInZip(ed->apk); + + return OK; + } + + static Error save_apk_so(void *p_userdata, const SharedObject &p_so) { + if (!p_so.path.get_file().begins_with("lib")) { + String err = "Android .so file names must start with \"lib\", but got: " + p_so.path; + ERR_PRINT(err.utf8().get_data()); + return FAILED; + } + APKExportData *ed = (APKExportData *)p_userdata; + Vector<String> abis = get_abis(); + bool exported = false; + for (int i = 0; i < p_so.tags.size(); ++i) { + // shared objects can be fat (compatible with multiple ABIs) + int start_pos = 0; + int abi_index = abis.find(p_so.tags[i]); + if (abi_index != -1) { + exported = true; + start_pos = abi_index + 1; + String abi = abis[abi_index]; + String dst_path = "lib/" + abi + "/" + p_so.path.get_file(); + Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path); + Error store_err = store_in_apk(ed, dst_path, array); + ERR_FAIL_COND_V(store_err, store_err); + } + } + if (!exported) { + String abis_string = String(" ").join(abis); + String err = "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + abis_string; + ERR_PRINT(err.utf8().get_data()); + return FAILED; + } + return OK; + } + + static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { + APKExportData *ed = (APKExportData *)p_userdata; + String dst_path = p_path.replace_first("res://", "assets/"); + + store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0); ed->ep->step("File: " + p_path, 3 + p_file * 100 / p_total); return OK; } @@ -935,6 +955,18 @@ class EditorExportAndroid : public EditorExportPlatform { //printf("end\n"); } + static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset) { + Vector<String> abis = get_abis(); + Vector<String> enabled_abis; + for (int i = 0; i < abis.size(); ++i) { + bool is_enabled = p_preset->get("architectures/" + abis[i]); + if (is_enabled) { + enabled_abis.push_back(abis[i]); + } + } + return enabled_abis; + } + public: enum { MAX_USER_PERMISSIONS = 20 @@ -951,6 +983,11 @@ public: r_features->push_back("etc"); else*/ r_features->push_back("etc2"); + + Vector<String> abis = get_enabled_abis(p_preset); + for (int i = 0; i < abis.size(); ++i) { + r_features->push_back(abis[i]); + } } virtual void get_export_options(List<ExportOption> *r_options) { @@ -967,8 +1004,6 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/icon", PROPERTY_HINT_FILE, "png"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architecture/arm"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architecture/x86"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "screen/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true)); @@ -982,6 +1017,13 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), "")); + Vector<String> abis = get_abis(); + for (int i = 0; i < abis.size(); ++i) { + String abi = abis[i]; + bool is_default = (abi == "armeabi-v7a"); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default)); + } + const char **perms = android_perms; while (*perms) { @@ -1295,10 +1337,6 @@ public: String unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk"); zipFile unaligned_apk = zipOpen2(unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2); - bool export_x86 = p_preset->get("architecture/x86"); - bool export_arm = p_preset->get("architecture/arm"); - bool export_arm64 = p_preset->get("architecture/arm64"); - bool use_32_fb = p_preset->get("graphics/32_bits_framebuffer"); bool immersive = p_preset->get("screen/immersive_mode"); @@ -1318,6 +1356,8 @@ public: String release_username = p_preset->get("keystore/release_user"); String release_password = p_preset->get("keystore/release_password"); + Vector<String> enabled_abis = get_enabled_abis(p_preset); + while (ret == UNZ_OK) { //get filename @@ -1381,25 +1421,25 @@ public: } } - if (file == "lib/x86/*.so" && !export_x86) { - skip = true; - } - - if (file.match("lib/armeabi*/*.so") && !export_arm) { - skip = true; - } - - if (file.match("lib/arm64*/*.so") && !export_arm64) { - skip = true; + if (file.ends_with(".so")) { + bool enabled = false; + for (int i = 0; i < enabled_abis.size(); ++i) { + if (file.begins_with("lib/" + enabled_abis[i] + "/")) { + enabled = true; + break; + } + } + if (!enabled) { + skip = true; + } } if (file.begins_with("META-INF") && _signed) { skip = true; } - print_line("ADDING: " + file); - if (!skip) { + print_line("ADDING: " + file); // Respect decision on compression made by AAPT for the export template const bool uncompressed = info.compression_method == 0; @@ -1473,7 +1513,7 @@ public: ed.ep = &ep; ed.apk = unaligned_apk; - err = export_project_files(p_preset, save_apk_file, &ed); + err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so); } } diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp index b35b2568d1..fbe2593362 100644 --- a/scene/animation/animation_cache.cpp +++ b/scene/animation/animation_cache.cpp @@ -87,95 +87,90 @@ void AnimationCache::_update_cache() { Ref<Resource> res; - if (np.get_subname_count()) { - - if (animation->track_get_type(i) == Animation::TYPE_TRANSFORM) { + if (animation->track_get_type(i) == Animation::TYPE_TRANSFORM) { + if (np.get_subname_count() > 1) { path_cache.push_back(Path()); ERR_EXPLAIN("Transform tracks can't have a subpath: " + np); ERR_CONTINUE(animation->track_get_type(i) == Animation::TYPE_TRANSFORM); } - RES res; - - for (int j = 0; j < np.get_subname_count(); j++) { - res = j == 0 ? node->get(np.get_subname(j)) : res->get(np.get_subname(j)); - if (res.is_null()) - break; - } + Spatial *sp = Object::cast_to<Spatial>(node); - if (res.is_null()) { + if (!sp) { path_cache.push_back(Path()); - ERR_EXPLAIN("Invalid Track SubPath in Animation: " + np); - ERR_CONTINUE(res.is_null()); + ERR_EXPLAIN("Transform track not of type Spatial: " + np); + ERR_CONTINUE(!sp); } - path.resource = res; - path.object = res.ptr(); - - } else { - - if (animation->track_get_type(i) == Animation::TYPE_TRANSFORM) { - StringName property = np.get_property(); + if (np.get_subname_count() == 1) { + StringName property = np.get_subname(0); String ps = property; - Spatial *sp = Object::cast_to<Spatial>(node); + Skeleton *sk = Object::cast_to<Skeleton>(node); + if (!sk) { - if (!sp) { + path_cache.push_back(Path()); + ERR_EXPLAIN("Property defined in Transform track, but not a Skeleton!: " + np); + ERR_CONTINUE(!sk); + } + int idx = sk->find_bone(ps); + if (idx == -1) { path_cache.push_back(Path()); - ERR_EXPLAIN("Transform track not of type Spatial: " + np); - ERR_CONTINUE(!sp); + ERR_EXPLAIN("Property defined in Transform track, but not a Skeleton Bone!: " + np); + ERR_CONTINUE(idx == -1); } - if (ps != "") { + path.bone_idx = idx; + path.skeleton = sk; + } - Skeleton *sk = Object::cast_to<Skeleton>(node); - if (!sk) { + path.spatial = sp; - path_cache.push_back(Path()); - ERR_EXPLAIN("Property defined in Transform track, but not a Skeleton!: " + np); - ERR_CONTINUE(!sk); - } + } else { + if (np.get_subname_count() > 0) { - int idx = sk->find_bone(ps); - if (idx == -1) { + RES res; + Vector<StringName> leftover_subpath; - path_cache.push_back(Path()); - ERR_EXPLAIN("Property defined in Transform track, but not a Skeleton Bone!: " + np); - ERR_CONTINUE(idx == -1); - } + // We don't want to cache the last resource unless it is a method call + bool is_method = animation->track_get_type(i) == Animation::TYPE_METHOD; + root->get_node_and_resource(np, res, leftover_subpath, is_method); - path.bone_idx = idx; - path.skeleton = sk; + if (res.is_valid()) { + path.resource = res; + } else { + path.node = node; } + path.object = res.is_valid() ? res.ptr() : (Object *)node; + path.subpath = leftover_subpath; - path.spatial = sp; - } + } else { - path.node = node; - path.object = node; + path.node = node; + path.object = node; + path.subpath = np.get_subnames(); + } } if (animation->track_get_type(i) == Animation::TYPE_VALUE) { - if (np.get_property().operator String() == "") { + if (np.get_subname_count() == 0) { path_cache.push_back(Path()); ERR_EXPLAIN("Value Track lacks property: " + np); - ERR_CONTINUE(np.get_property().operator String() == ""); + ERR_CONTINUE(np.get_subname_count() == 0); } - path.property = np.get_property(); - } else if (animation->track_get_type(i) == Animation::TYPE_METHOD) { - if (np.get_property().operator String() != "") { + if (path.subpath.size() != 0) { // Trying to call a method of a non-resource path_cache.push_back(Path()); ERR_EXPLAIN("Method Track has property: " + np); - ERR_CONTINUE(np.get_property().operator String() != ""); + ERR_CONTINUE(path.subpath.size() != 0); } } @@ -226,7 +221,7 @@ void AnimationCache::set_track_value(int p_idx, const Variant &p_value) { return; ERR_FAIL_COND(!p.object); - p.object->set(p.property, p_value); + p.object->set_indexed(p.subpath, p_value); } void AnimationCache::call_track(int p_idx, const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { diff --git a/scene/animation/animation_cache.h b/scene/animation/animation_cache.h index e593668df6..481de59730 100644 --- a/scene/animation/animation_cache.h +++ b/scene/animation/animation_cache.h @@ -46,7 +46,7 @@ class AnimationCache : public Object { Spatial *spatial; int bone_idx; - StringName property; + Vector<StringName> subpath; bool valid; Path() { object = NULL; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 6be3ff88d9..010f5a586f 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -242,7 +242,8 @@ void AnimationPlayer::_generate_node_caches(AnimationData *p_anim) { p_anim->node_cache[i] = NULL; RES resource; - Node *child = parent->get_node_and_resource(a->track_get_path(i), resource); + Vector<StringName> leftover_path; + Node *child = parent->get_node_and_resource(a->track_get_path(i), resource, leftover_path); if (!child) { ERR_EXPLAIN("On Animation: '" + p_anim->name + "', couldn't resolve track: '" + String(a->track_get_path(i)) + "'"); } @@ -250,9 +251,9 @@ void AnimationPlayer::_generate_node_caches(AnimationData *p_anim) { uint32_t id = resource.is_valid() ? resource->get_instance_id() : child->get_instance_id(); int bone_idx = -1; - if (a->track_get_path(i).get_property() && Object::cast_to<Skeleton>(child)) { + if (a->track_get_path(i).get_subname_count() == 1 && Object::cast_to<Skeleton>(child)) { - bone_idx = Object::cast_to<Skeleton>(child)->find_bone(a->track_get_path(i).get_property()); + bone_idx = Object::cast_to<Skeleton>(child)->find_bone(a->track_get_path(i).get_subname(0)); if (bone_idx == -1) { continue; @@ -289,8 +290,8 @@ void AnimationPlayer::_generate_node_caches(AnimationData *p_anim) { p_anim->node_cache[i]->skeleton = Object::cast_to<Skeleton>(child); if (p_anim->node_cache[i]->skeleton) { - StringName bone_name = a->track_get_path(i).get_property(); - if (bone_name.operator String() != "") { + if (a->track_get_path(i).get_subname_count() == 1) { + StringName bone_name = a->track_get_path(i).get_subname(0); p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name); if (p_anim->node_cache[i]->bone_idx < 0) { @@ -311,24 +312,23 @@ void AnimationPlayer::_generate_node_caches(AnimationData *p_anim) { if (a->track_get_type(i) == Animation::TYPE_VALUE) { - StringName property = a->track_get_path(i).get_property(); - if (!p_anim->node_cache[i]->property_anim.has(property)) { + if (!p_anim->node_cache[i]->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) { TrackNodeCache::PropertyAnim pa; - pa.prop = property; + pa.subpath = leftover_path; pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; pa.special = SP_NONE; pa.owner = p_anim->node_cache[i]; if (false && p_anim->node_cache[i]->node_2d) { - if (pa.prop == SceneStringNames::get_singleton()->transform_pos) + if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_pos) pa.special = SP_NODE2D_POS; - else if (pa.prop == SceneStringNames::get_singleton()->transform_rot) + else if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_rot) pa.special = SP_NODE2D_ROT; - else if (pa.prop == SceneStringNames::get_singleton()->transform_scale) + else if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_scale) pa.special = SP_NODE2D_SCALE; } - p_anim->node_cache[i]->property_anim[property] = pa; + p_anim->node_cache[i]->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa; } } } @@ -396,7 +396,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float //StringName property=a->track_get_path(i).get_property(); - Map<StringName, TrackNodeCache::PropertyAnim>::Element *E = nc->property_anim.find(a->track_get_path(i).get_property()); + Map<StringName, TrackNodeCache::PropertyAnim>::Element *E = nc->property_anim.find(a->track_get_path(i).get_concatenated_subnames()); ERR_CONTINUE(!E); //should it continue, or create a new one? TrackNodeCache::PropertyAnim *pa = &E->get(); @@ -434,7 +434,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float case SP_NONE: { bool valid; - pa->object->set(pa->prop, value, &valid); //you are not speshul + pa->object->set_indexed(pa->subpath, value, &valid); //you are not speshul #ifdef DEBUG_ENABLED if (!valid) { ERR_PRINTS("Failed setting track value '" + String(pa->owner->path) + "'. Check if property exists or the type of key is valid. Animation '" + a->get_name() + "' at node '" + get_path() + "'."); @@ -622,7 +622,7 @@ void AnimationPlayer::_animation_update_transforms() { case SP_NONE: { bool valid; - pa->object->set(pa->prop, pa->value_accum, &valid); //you are not speshul + pa->object->set_indexed(pa->subpath, pa->value_accum, &valid); //you are not speshul #ifdef DEBUG_ENABLED if (!valid) { ERR_PRINTS("Failed setting key at time " + rtos(playback.current.pos) + " in Animation '" + get_current_animation() + "' at Node '" + get_path() + "', Track '" + String(pa->owner->path) + "'. Check if property exists or the type of key is right for the property"); diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index 83da3b2e5c..e4e021c7fe 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -83,7 +83,7 @@ private: TrackNodeCache *owner; SpecialProperty special; //small optimization - StringName prop; + Vector<StringName> subpath; Object *object; Variant value_accum; uint64_t accum_pass; diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index ad5329c94b..23eccec82f 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -811,7 +811,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) { t.scale.y = 0; t.scale.z = 0; - t.value = t.object->get(t.property); + t.value = t.object->get_indexed(t.subpath); t.value.zero(); t.skip = false; @@ -890,8 +890,8 @@ void AnimationTreePlayer::_process_animation(float p_delta) { if (t.skip || !t.object) continue; - if (t.property) { // value track - t.object->set(t.property, t.value); + if (t.subpath.size()) { // value track + t.object->set_indexed(t.subpath, t.value); continue; } @@ -1475,7 +1475,8 @@ AnimationTreePlayer::Track *AnimationTreePlayer::_find_track(const NodePath &p_p ERR_FAIL_COND_V(!parent, NULL); RES resource; - Node *child = parent->get_node_and_resource(p_path, resource); + Vector<StringName> leftover_path; + Node *child = parent->get_node_and_resource(p_path, resource, leftover_path); if (!child) { String err = "Animation track references unknown Node: '" + String(p_path) + "'."; WARN_PRINT(err.ascii().get_data()); @@ -1483,21 +1484,18 @@ AnimationTreePlayer::Track *AnimationTreePlayer::_find_track(const NodePath &p_p } ObjectID id = child->get_instance_id(); - StringName property; int bone_idx = -1; - if (p_path.get_property()) { + if (p_path.get_subname_count()) { if (Object::cast_to<Skeleton>(child)) - bone_idx = Object::cast_to<Skeleton>(child)->find_bone(p_path.get_property()); - if (bone_idx == -1) - property = p_path.get_property(); + bone_idx = Object::cast_to<Skeleton>(child)->find_bone(p_path.get_subname(0)); } TrackKey key; key.id = id; key.bone_idx = bone_idx; - key.property = property; + key.subpath_concatenated = p_path.get_concatenated_subnames(); if (!track_map.has(key)) { @@ -1507,7 +1505,7 @@ AnimationTreePlayer::Track *AnimationTreePlayer::_find_track(const NodePath &p_p tr.skeleton = Object::cast_to<Skeleton>(child); tr.spatial = Object::cast_to<Spatial>(child); tr.bone_idx = bone_idx; - tr.property = property; + if (bone_idx == -1) tr.subpath = leftover_path; track_map[key] = tr; } diff --git a/scene/animation/animation_tree_player.h b/scene/animation/animation_tree_player.h index 3e2bb88198..c49b0c4d1b 100644 --- a/scene/animation/animation_tree_player.h +++ b/scene/animation/animation_tree_player.h @@ -78,14 +78,14 @@ private: struct TrackKey { uint32_t id; - StringName property; + StringName subpath_concatenated; int bone_idx; inline bool operator<(const TrackKey &p_right) const { if (id == p_right.id) { if (bone_idx == p_right.bone_idx) { - return property < p_right.property; + return subpath_concatenated < p_right.subpath_concatenated; } else return bone_idx < p_right.bone_idx; } else @@ -99,7 +99,7 @@ private: Spatial *spatial; Skeleton *skeleton; int bone_idx; - StringName property; + Vector<StringName> subpath; Vector3 loc; Quat rot; diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 40d06dc756..151632a0cb 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -264,12 +264,12 @@ Variant &Tween::_get_initial_val(InterpolateData &p_data) { if (p_data.type == TARGETING_PROPERTY) { bool valid = false; - initial_val = object->get(p_data.target_key, &valid); + initial_val = object->get_indexed(p_data.target_key, &valid); ERR_FAIL_COND_V(!valid, p_data.initial_val); } else { Variant::CallError error; - initial_val = object->call(p_data.target_key, NULL, 0, error); + initial_val = object->call(p_data.target_key[0], NULL, 0, error); ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, p_data.initial_val); } return initial_val; @@ -296,12 +296,12 @@ Variant &Tween::_get_delta_val(InterpolateData &p_data) { if (p_data.type == FOLLOW_PROPERTY) { bool valid = false; - final_val = target->get(p_data.target_key, &valid); + final_val = target->get_indexed(p_data.target_key, &valid); ERR_FAIL_COND_V(!valid, p_data.initial_val); } else { Variant::CallError error; - final_val = target->call(p_data.target_key, NULL, 0, error); + final_val = target->call(p_data.target_key[0], NULL, 0, error); ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, p_data.initial_val); } @@ -462,6 +462,9 @@ Variant Tween::_run_equation(InterpolateData &p_data) { result = r; } break; + default: { + result = initial_val; + } break; }; #undef APPLY_EQUATION @@ -479,7 +482,7 @@ bool Tween::_apply_tween_value(InterpolateData &p_data, Variant &value) { case FOLLOW_PROPERTY: case TARGETING_PROPERTY: { bool valid = false; - object->set(p_data.key, value, &valid); + object->set_indexed(p_data.key, value, &valid); return valid; } @@ -489,9 +492,9 @@ bool Tween::_apply_tween_value(InterpolateData &p_data, Variant &value) { Variant::CallError error; if (value.get_type() != Variant::NIL) { Variant *arg[1] = { &value }; - object->call(p_data.key, (const Variant **)arg, 1, error); + object->call(p_data.key[0], (const Variant **)arg, 1, error); } else { - object->call(p_data.key, NULL, 0, error); + object->call(p_data.key[0], NULL, 0, error); } if (error.error == Variant::CallError::CALL_OK) @@ -548,7 +551,7 @@ void Tween::_tween_process(float p_delta) { continue; else if (prev_delaying) { - emit_signal("tween_started", object, data.key); + emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false)); _apply_tween_value(data, data.initial_val); } @@ -562,7 +565,7 @@ void Tween::_tween_process(float p_delta) { case INTER_PROPERTY: case INTER_METHOD: { Variant result = _run_equation(data); - emit_signal("tween_step", object, data.key, data.elapsed, result); + emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); _apply_tween_value(data, result); if (data.finish) _apply_tween_value(data, data.final_val); @@ -574,22 +577,22 @@ void Tween::_tween_process(float p_delta) { switch (data.args) { case 0: - object->call_deferred(data.key); + object->call_deferred(data.key[0]); break; case 1: - object->call_deferred(data.key, data.arg[0]); + object->call_deferred(data.key[0], data.arg[0]); break; case 2: - object->call_deferred(data.key, data.arg[0], data.arg[1]); + object->call_deferred(data.key[0], data.arg[0], data.arg[1]); break; case 3: - object->call_deferred(data.key, data.arg[0], data.arg[1], data.arg[2]); + object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2]); break; case 4: - object->call_deferred(data.key, data.arg[0], data.arg[1], data.arg[2], data.arg[3]); + object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2], data.arg[3]); break; case 5: - object->call_deferred(data.key, data.arg[0], data.arg[1], data.arg[2], data.arg[3], data.arg[4]); + object->call_deferred(data.key[0], data.arg[0], data.arg[1], data.arg[2], data.arg[3], data.arg[4]); break; } } else { @@ -601,17 +604,18 @@ void Tween::_tween_process(float p_delta) { &data.arg[3], &data.arg[4], }; - object->call(data.key, (const Variant **)arg, data.args, error); + object->call(data.key[0], (const Variant **)arg, data.args, error); } } break; + default: {} } if (data.finish) { - emit_signal("tween_completed", object, data.key); + emit_signal("tween_completed", object, NodePath(Vector<StringName>(), data.key, false)); // not repeat mode, remove completed action if (!repeat) - call_deferred("_remove", object, data.key, true); + call_deferred("_remove", object, NodePath(Vector<StringName>(), data.key, false), true); } } pending_update--; @@ -690,7 +694,7 @@ bool Tween::start() { return true; } -bool Tween::reset(Object *p_object, String p_key) { +bool Tween::reset(Object *p_object, StringName p_key) { pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -700,7 +704,7 @@ bool Tween::reset(Object *p_object, String p_key) { if (object == NULL) continue; - if (object == p_object && (data.key == p_key || p_key == "")) { + if (object == p_object && (data.concatenated_key == p_key || p_key == "")) { data.elapsed = 0; data.finish = false; @@ -727,7 +731,7 @@ bool Tween::reset_all() { return true; } -bool Tween::stop(Object *p_object, String p_key) { +bool Tween::stop(Object *p_object, StringName p_key) { pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -736,7 +740,7 @@ bool Tween::stop(Object *p_object, String p_key) { Object *object = ObjectDB::get_instance(data.id); if (object == NULL) continue; - if (object == p_object && (data.key == p_key || p_key == "")) + if (object == p_object && (data.concatenated_key == p_key || p_key == "")) data.active = false; } pending_update--; @@ -758,7 +762,7 @@ bool Tween::stop_all() { return true; } -bool Tween::resume(Object *p_object, String p_key) { +bool Tween::resume(Object *p_object, StringName p_key) { set_active(true); _set_process(true); @@ -770,7 +774,7 @@ bool Tween::resume(Object *p_object, String p_key) { Object *object = ObjectDB::get_instance(data.id); if (object == NULL) continue; - if (object == p_object && (data.key == p_key || p_key == "")) + if (object == p_object && (data.concatenated_key == p_key || p_key == "")) data.active = true; } pending_update--; @@ -792,12 +796,12 @@ bool Tween::resume_all() { return true; } -bool Tween::remove(Object *p_object, String p_key) { +bool Tween::remove(Object *p_object, StringName p_key) { _remove(p_object, p_key, false); return true; } -void Tween::_remove(Object *p_object, String p_key, bool first_only) { +void Tween::_remove(Object *p_object, StringName p_key, bool first_only) { if (pending_update != 0) { call_deferred("_remove", p_object, p_key, first_only); @@ -810,7 +814,7 @@ void Tween::_remove(Object *p_object, String p_key, bool first_only) { Object *object = ObjectDB::get_instance(data.id); if (object == NULL) continue; - if (object == p_object && (data.key == p_key || p_key == "")) { + if (object == p_object && (data.concatenated_key == p_key || p_key == "")) { for_removal.push_back(E); if (first_only) { break; @@ -850,8 +854,9 @@ bool Tween::seek(real_t p_time) { data.finish = true; data.elapsed = (data.delay + data.duration); - } else + } else { data.finish = false; + } switch (data.type) { case INTER_PROPERTY: @@ -993,11 +998,15 @@ bool Tween::_calc_delta_val(const Variant &p_initial_val, const Variant &p_final return true; } -bool Tween::interpolate_property(Object *p_object, String p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { +bool Tween::interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { if (pending_update != 0) { _add_pending_command("interpolate_property", p_object, p_property, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay); return true; } + p_property = p_property.get_as_property_path(); + + if (p_initial_val.get_type() == Variant::NIL) p_initial_val = p_object->get_indexed(p_property.get_subnames()); + // convert INT to REAL is better for interpolaters if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t(); if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t(); @@ -1011,7 +1020,7 @@ bool Tween::interpolate_property(Object *p_object, String p_property, Variant p_ ERR_FAIL_COND_V(p_delay < 0, false); bool prop_valid = false; - p_object->get(p_property, &prop_valid); + p_object->get_indexed(p_property.get_subnames(), &prop_valid); ERR_FAIL_COND_V(!prop_valid, false); InterpolateData data; @@ -1021,7 +1030,8 @@ bool Tween::interpolate_property(Object *p_object, String p_property, Variant p_ data.elapsed = 0; data.id = p_object->get_instance_id(); - data.key = p_property; + data.key = p_property.get_subnames(); + data.concatenated_key = p_property.get_concatenated_subnames(); data.initial_val = p_initial_val; data.final_val = p_final_val; data.duration = p_duration; @@ -1036,7 +1046,7 @@ bool Tween::interpolate_property(Object *p_object, String p_property, Variant p_ return true; } -bool Tween::interpolate_method(Object *p_object, String p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { +bool Tween::interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { if (pending_update != 0) { _add_pending_command("interpolate_method", p_object, p_method, p_initial_val, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay); return true; @@ -1063,7 +1073,8 @@ bool Tween::interpolate_method(Object *p_object, String p_method, Variant p_init data.elapsed = 0; data.id = p_object->get_instance_id(); - data.key = p_method; + data.key.push_back(p_method); + data.concatenated_key = p_method; data.initial_val = p_initial_val; data.final_val = p_final_val; data.duration = p_duration; @@ -1100,7 +1111,8 @@ bool Tween::interpolate_callback(Object *p_object, real_t p_duration, String p_c data.elapsed = 0; data.id = p_object->get_instance_id(); - data.key = p_callback; + data.key.push_back(p_callback); + data.concatenated_key = p_callback; data.duration = p_duration; data.delay = 0; @@ -1152,7 +1164,8 @@ bool Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, S data.elapsed = 0; data.id = p_object->get_instance_id(); - data.key = p_callback; + data.key.push_back(p_callback); + data.concatenated_key = p_callback; data.duration = p_duration; data.delay = 0; @@ -1183,11 +1196,16 @@ bool Tween::interpolate_deferred_callback(Object *p_object, real_t p_duration, S return true; } -bool Tween::follow_property(Object *p_object, String p_property, Variant p_initial_val, Object *p_target, String p_target_property, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { +bool Tween::follow_property(Object *p_object, NodePath p_property, Variant p_initial_val, Object *p_target, NodePath p_target_property, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { if (pending_update != 0) { _add_pending_command("follow_property", p_object, p_property, p_initial_val, p_target, p_target_property, p_duration, p_trans_type, p_ease_type, p_delay); return true; } + p_property = p_property.get_as_property_path(); + p_target_property = p_target_property.get_as_property_path(); + + if (p_initial_val.get_type() == Variant::NIL) p_initial_val = p_object->get_indexed(p_property.get_subnames()); + // convert INT to REAL is better for interpolaters if (p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t(); @@ -1201,11 +1219,11 @@ bool Tween::follow_property(Object *p_object, String p_property, Variant p_initi ERR_FAIL_COND_V(p_delay < 0, false); bool prop_valid = false; - p_object->get(p_property, &prop_valid); + p_object->get_indexed(p_property.get_subnames(), &prop_valid); ERR_FAIL_COND_V(!prop_valid, false); bool target_prop_valid = false; - Variant target_val = p_target->get(p_target_property, &target_prop_valid); + Variant target_val = p_target->get_indexed(p_target_property.get_subnames(), &target_prop_valid); ERR_FAIL_COND_V(!target_prop_valid, false); // convert INT to REAL is better for interpolaters @@ -1219,10 +1237,11 @@ bool Tween::follow_property(Object *p_object, String p_property, Variant p_initi data.elapsed = 0; data.id = p_object->get_instance_id(); - data.key = p_property; + data.key = p_property.get_subnames(); + data.concatenated_key = p_property.get_concatenated_subnames(); data.initial_val = p_initial_val; data.target_id = p_target->get_instance_id(); - data.target_key = p_target_property; + data.target_key = p_target_property.get_subnames(); data.duration = p_duration; data.trans_type = p_trans_type; data.ease_type = p_ease_type; @@ -1232,7 +1251,7 @@ bool Tween::follow_property(Object *p_object, String p_property, Variant p_initi return true; } -bool Tween::follow_method(Object *p_object, String p_method, Variant p_initial_val, Object *p_target, String p_target_method, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { +bool Tween::follow_method(Object *p_object, StringName p_method, Variant p_initial_val, Object *p_target, StringName p_target_method, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { if (pending_update != 0) { _add_pending_command("follow_method", p_object, p_method, p_initial_val, p_target, p_target_method, p_duration, p_trans_type, p_ease_type, p_delay); return true; @@ -1269,10 +1288,11 @@ bool Tween::follow_method(Object *p_object, String p_method, Variant p_initial_v data.elapsed = 0; data.id = p_object->get_instance_id(); - data.key = p_method; + data.key.push_back(p_method); + data.concatenated_key = p_method; data.initial_val = p_initial_val; data.target_id = p_target->get_instance_id(); - data.target_key = p_target_method; + data.target_key.push_back(p_target_method); data.duration = p_duration; data.trans_type = p_trans_type; data.ease_type = p_ease_type; @@ -1282,11 +1302,15 @@ bool Tween::follow_method(Object *p_object, String p_method, Variant p_initial_v return true; } -bool Tween::targeting_property(Object *p_object, String p_property, Object *p_initial, String p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { +bool Tween::targeting_property(Object *p_object, NodePath p_property, Object *p_initial, NodePath p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { + if (pending_update != 0) { _add_pending_command("targeting_property", p_object, p_property, p_initial, p_initial_property, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay); return true; } + p_property = p_property.get_as_property_path(); + p_initial_property = p_initial_property.get_as_property_path(); + // convert INT to REAL is better for interpolaters if (p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t(); @@ -1300,11 +1324,11 @@ bool Tween::targeting_property(Object *p_object, String p_property, Object *p_in ERR_FAIL_COND_V(p_delay < 0, false); bool prop_valid = false; - p_object->get(p_property, &prop_valid); + p_object->get_indexed(p_property.get_subnames(), &prop_valid); ERR_FAIL_COND_V(!prop_valid, false); bool initial_prop_valid = false; - Variant initial_val = p_initial->get(p_initial_property, &initial_prop_valid); + Variant initial_val = p_initial->get_indexed(p_initial_property.get_subnames(), &initial_prop_valid); ERR_FAIL_COND_V(!initial_prop_valid, false); // convert INT to REAL is better for interpolaters @@ -1318,9 +1342,10 @@ bool Tween::targeting_property(Object *p_object, String p_property, Object *p_in data.elapsed = 0; data.id = p_object->get_instance_id(); - data.key = p_property; + data.key = p_property.get_subnames(); + data.concatenated_key = p_property.get_concatenated_subnames(); data.target_id = p_initial->get_instance_id(); - data.target_key = p_initial_property; + data.target_key = p_initial_property.get_subnames(); data.initial_val = initial_val; data.final_val = p_final_val; data.duration = p_duration; @@ -1335,7 +1360,7 @@ bool Tween::targeting_property(Object *p_object, String p_property, Object *p_in return true; } -bool Tween::targeting_method(Object *p_object, String p_method, Object *p_initial, String p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { +bool Tween::targeting_method(Object *p_object, StringName p_method, Object *p_initial, StringName p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay) { if (pending_update != 0) { _add_pending_command("targeting_method", p_object, p_method, p_initial, p_initial_method, p_final_val, p_duration, p_trans_type, p_ease_type, p_delay); return true; @@ -1372,9 +1397,10 @@ bool Tween::targeting_method(Object *p_object, String p_method, Object *p_initia data.elapsed = 0; data.id = p_object->get_instance_id(); - data.key = p_method; + data.key.push_back(p_method); + data.concatenated_key = p_method; data.target_id = p_initial->get_instance_id(); - data.target_key = p_initial_method; + data.target_key.push_back(p_initial_method); data.initial_val = initial_val; data.final_val = p_final_val; data.duration = p_duration; diff --git a/scene/animation/tween.h b/scene/animation/tween.h index fac1d346b4..44710b25f9 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -86,12 +86,13 @@ private: bool call_deferred; real_t elapsed; ObjectID id; - StringName key; + Vector<StringName> key; + StringName concatenated_key; Variant initial_val; Variant delta_val; Variant final_val; ObjectID target_id; - StringName target_key; + Vector<StringName> target_key; real_t duration; TransitionType trans_type; EaseType ease_type; @@ -132,7 +133,7 @@ private: void _tween_process(float p_delta); void _set_process(bool p_process, bool p_force = false); - void _remove(Object *p_object, String p_key, bool first_only); + void _remove(Object *p_object, StringName p_key, bool first_only); protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -156,34 +157,34 @@ public: float get_speed_scale() const; bool start(); - bool reset(Object *p_object, String p_key); + bool reset(Object *p_object, StringName p_key); bool reset_all(); - bool stop(Object *p_object, String p_key); + bool stop(Object *p_object, StringName p_key); bool stop_all(); - bool resume(Object *p_object, String p_key); + bool resume(Object *p_object, StringName p_key); bool resume_all(); - bool remove(Object *p_object, String p_key); + bool remove(Object *p_object, StringName p_key); bool remove_all(); bool seek(real_t p_time); real_t tell() const; real_t get_runtime() const; - bool interpolate_property(Object *p_object, String p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); + bool interpolate_property(Object *p_object, NodePath p_property, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); - bool interpolate_method(Object *p_object, String p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); + bool interpolate_method(Object *p_object, StringName p_method, Variant p_initial_val, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); bool interpolate_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE); bool interpolate_deferred_callback(Object *p_object, real_t p_duration, String p_callback, VARIANT_ARG_DECLARE); - bool follow_property(Object *p_object, String p_property, Variant p_initial_val, Object *p_target, String p_target_property, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); + bool follow_property(Object *p_object, NodePath p_property, Variant p_initial_val, Object *p_target, NodePath p_target_property, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); - bool follow_method(Object *p_object, String p_method, Variant p_initial_val, Object *p_target, String p_target_method, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); + bool follow_method(Object *p_object, StringName p_method, Variant p_initial_val, Object *p_target, StringName p_target_method, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); - bool targeting_property(Object *p_object, String p_property, Object *p_initial, String p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); + bool targeting_property(Object *p_object, NodePath p_property, Object *p_initial, NodePath p_initial_property, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); - bool targeting_method(Object *p_object, String p_method, Object *p_initial, String p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); + bool targeting_method(Object *p_object, StringName p_method, Object *p_initial, StringName p_initial_method, Variant p_final_val, real_t p_duration, TransitionType p_trans_type, EaseType p_ease_type, real_t p_delay = 0); Tween(); ~Tween(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 01e11962ff..30b831adfc 100755 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2502,24 +2502,19 @@ bool Node::has_node_and_resource(const NodePath &p_path) const { return false; Node *node = get_node(p_path); - if (p_path.get_subname_count()) { + bool result = false; - RES r; - for (int j = 0; j < p_path.get_subname_count(); j++) { - r = j == 0 ? node->get(p_path.get_subname(j)) : r->get(p_path.get_subname(j)); - if (r.is_null()) - return false; - } - } + node->get_indexed(p_path.get_subnames(), &result); - return true; + return result; } Array Node::_get_node_and_resource(const NodePath &p_path) { Node *node; RES res; - node = get_node_and_resource(p_path, res); + Vector<StringName> leftover_path; + node = get_node_and_resource(p_path, res, leftover_path); Array result; if (node) @@ -2532,21 +2527,35 @@ Array Node::_get_node_and_resource(const NodePath &p_path) { else result.push_back(Variant()); + result.push_back(NodePath(Vector<StringName>(), leftover_path, false)); + return result; } -Node *Node::get_node_and_resource(const NodePath &p_path, RES &r_res) const { +Node *Node::get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property) const { Node *node = get_node(p_path); r_res = RES(); + r_leftover_subpath = Vector<StringName>(); if (!node) return NULL; if (p_path.get_subname_count()) { - for (int j = 0; j < p_path.get_subname_count(); j++) { - r_res = j == 0 ? node->get(p_path.get_subname(j)) : r_res->get(p_path.get_subname(j)); - ERR_FAIL_COND_V(r_res.is_null(), node); + int j = 0; + // If not p_last_is_property, we shouldn't consider the last one as part of the resource + for (; j < p_path.get_subname_count() - p_last_is_property; j++) { + RES new_res = j == 0 ? node->get(p_path.get_subname(j)) : r_res->get(p_path.get_subname(j)); + + if (new_res.is_null()) { + break; + } + + r_res = new_res; + } + for (; j < p_path.get_subname_count(); j++) { + // Put the rest of the subpath in the leftover path + r_leftover_subpath.push_back(p_path.get_subname(j)); } } diff --git a/scene/main/node.h b/scene/main/node.h index bd0b18c87a..2b71b71c8d 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -245,7 +245,7 @@ public: Node *get_node(const NodePath &p_path) const; Node *find_node(const String &p_mask, bool p_recursive = true, bool p_owned = true) const; bool has_node_and_resource(const NodePath &p_path) const; - Node *get_node_and_resource(const NodePath &p_path, RES &r_res) const; + Node *get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property = true) const; Node *get_parent() const; _FORCE_INLINE_ SceneTree *get_tree() const { |