summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SConstruct2
-rw-r--r--core/io/marshalls.cpp20
-rw-r--r--core/io/resource_format_binary.cpp6
-rw-r--r--core/node_path.cpp76
-rw-r--r--core/node_path.h13
-rw-r--r--core/object.cpp123
-rw-r--r--core/object.h5
-rw-r--r--core/os/os.cpp2
-rw-r--r--core/variant_call.cpp4
-rw-r--r--editor/animation_editor.cpp40
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp6
-rw-r--r--editor/scene_tree_dock.cpp2
-rw-r--r--editor/script_editor_debugger.cpp9
-rw-r--r--modules/gdnative/gdnative.cpp6
-rw-r--r--modules/gdnative/gdnative/node_path.cpp4
-rw-r--r--modules/gdnative/gdnative_api.json2
-rw-r--r--modules/gdnative/include/gdnative/node_path.h2
-rw-r--r--platform/android/export/export.cpp140
-rw-r--r--scene/animation/animation_cache.cpp95
-rw-r--r--scene/animation/animation_cache.h2
-rw-r--r--scene/animation/animation_player.cpp30
-rw-r--r--scene/animation/animation_player.h2
-rw-r--r--scene/animation/animation_tree_player.cpp20
-rw-r--r--scene/animation/animation_tree_player.h6
-rw-r--r--scene/animation/tween.cpp128
-rw-r--r--scene/animation/tween.h27
-rwxr-xr-xscene/main/node.cpp37
-rw-r--r--scene/main/node.h2
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 {