summaryrefslogtreecommitdiff
path: root/scene/main/node.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/main/node.cpp')
-rw-r--r--[-rwxr-xr-x]scene/main/node.cpp161
1 files changed, 117 insertions, 44 deletions
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index e6e11de177..cae368aeca 100755..100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1198,7 +1198,7 @@ void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) {
unique = false;
} else {
//check if exists
- Node **childs = data.children.ptr();
+ Node **childs = data.children.ptrw();
int cc = data.children.size();
for (int i = 0; i < cc; i++) {
@@ -2067,7 +2067,7 @@ int Node::get_position_in_parent() const {
return data.pos;
}
-Node *Node::_duplicate(int p_flags) const {
+Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const {
Node *node = NULL;
@@ -2084,7 +2084,12 @@ Node *Node::_duplicate(int p_flags) const {
Ref<PackedScene> res = ResourceLoader::load(get_filename());
ERR_FAIL_COND_V(res.is_null(), NULL);
- node = res->instance();
+ PackedScene::GenEditState ges = PackedScene::GEN_EDIT_STATE_DISABLED;
+#ifdef TOOLS_ENABLED
+ if (p_flags & DUPLICATE_FROM_EDITOR)
+ ges = PackedScene::GEN_EDIT_STATE_INSTANCE;
+#endif
+ node = res->instance(ges);
ERR_FAIL_COND_V(!node, NULL);
instanced = true;
@@ -2103,53 +2108,84 @@ Node *Node::_duplicate(int p_flags) const {
node->set_filename(get_filename());
}
- List<PropertyInfo> plist;
+ StringName script_property_name = CoreStringNames::get_singleton()->_script;
- get_property_list(&plist);
+ List<const Node *> node_tree;
+ node_tree.push_front(this);
- StringName script_property_name = CoreStringNames::get_singleton()->_script;
+ if (instanced) {
+ // Since nodes in the instanced hierarchy won't be duplicated explicitly, we need to make an inventory
+ // of all the nodes in the tree of the instanced scene in order to transfer the values of the properties
+
+ for (List<const Node *>::Element *N = node_tree.front(); N; N = N->next()) {
+ for (int i = 0; i < N->get()->get_child_count(); ++i) {
- if (p_flags & DUPLICATE_SCRIPTS) {
- bool is_valid = false;
- Variant script = get(script_property_name, &is_valid);
- if (is_valid) {
- node->set(script_property_name, script);
+ // Skip nodes not really belonging to the instanced hierarchy; they'll be processed normally later
+ if (N->get()->get_child(i)->data.owner != this)
+ continue;
+
+ node_tree.push_back(N->get()->get_child(i));
+ }
}
}
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+ for (List<const Node *>::Element *N = node_tree.front(); N; N = N->next()) {
- if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
- continue;
- String name = E->get().name;
- if (name == script_property_name)
- continue;
+ Node *current_node = node->get_node(get_path_to(N->get()));
+ ERR_CONTINUE(!current_node);
- Variant value = get(name);
- // Duplicate dictionaries and arrays, mainly needed for __meta__
- if (value.get_type() == Variant::DICTIONARY) {
- value = Dictionary(value).copy();
- } else if (value.get_type() == Variant::ARRAY) {
- value = Array(value).duplicate();
+ if (p_flags & DUPLICATE_SCRIPTS) {
+ bool is_valid = false;
+ Variant script = N->get()->get(script_property_name, &is_valid);
+ if (is_valid) {
+ current_node->set(script_property_name, script);
+ }
}
- node->set(name, value);
+ List<PropertyInfo> plist;
+ N->get()->get_property_list(&plist);
+
+ for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+
+ if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
+ continue;
+ String name = E->get().name;
+ if (name == script_property_name)
+ continue;
+
+ Variant value = N->get()->get(name);
+ // Duplicate dictionaries and arrays, mainly needed for __meta__
+ if (value.get_type() == Variant::DICTIONARY) {
+ value = Dictionary(value).copy();
+ } else if (value.get_type() == Variant::ARRAY) {
+ value = Array(value).duplicate();
+ }
+
+ current_node->set(name, value);
+ }
}
node->set_name(get_name());
+#ifdef TOOLS_ENABLED
+ if ((p_flags & DUPLICATE_FROM_EDITOR) && r_duplimap)
+ r_duplimap->insert(this, node);
+#endif
+
if (p_flags & DUPLICATE_GROUPS) {
List<GroupInfo> gi;
get_groups(&gi);
for (List<GroupInfo>::Element *E = gi.front(); E; E = E->next()) {
+#ifdef TOOLS_ENABLED
+ if ((p_flags & DUPLICATE_FROM_EDITOR) && !E->get().persistent)
+ continue;
+#endif
+
node->add_to_group(E->get().name, E->get().persistent);
}
}
- if (p_flags & DUPLICATE_SIGNALS)
- _duplicate_signals(this, node);
-
for (int i = 0; i < get_child_count(); i++) {
if (get_child(i)->data.parent_owned)
@@ -2157,7 +2193,7 @@ Node *Node::_duplicate(int p_flags) const {
if (instanced && get_child(i)->data.owner == this)
continue; //part of instance
- Node *dup = get_child(i)->duplicate(p_flags);
+ Node *dup = get_child(i)->_duplicate(p_flags, r_duplimap);
if (!dup) {
memdelete(node);
@@ -2181,6 +2217,20 @@ Node *Node::duplicate(int p_flags) const {
return dupe;
}
+#ifdef TOOLS_ENABLED
+Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const {
+
+ Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR, &r_duplimap);
+
+ // Duplication of signals must happen after all the node descendants have been copied,
+ // because re-targeting of connections from some descendant to another is not possible
+ // if the emitter node comes later in tree order than the receiver
+ _duplicate_signals(this, dupe);
+
+ return dupe;
+}
+#endif
+
void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p_reown_map) const {
if (get_owner() != get_parent()->get_owner())
@@ -2251,6 +2301,9 @@ void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p
}
}
+// Duplication of signals must happen after all the node descendants have been copied,
+// because re-targeting of connections from some descendant to another is not possible
+// if the emitter node comes later in tree order than the receiver
void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const {
if (this != p_original && (get_owner() != p_original && get_owner() != p_original->get_owner()))
@@ -2273,8 +2326,14 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const {
NodePath ptarget = p_original->get_path_to(target);
Node *copytarget = p_copy->get_node(ptarget);
+ // Cannot find a path to the duplicate target, so it seems it's not part
+ // of the duplicated and not yet parented hierarchy, so at least try to connect
+ // to the same target as the original
+ if (!copytarget)
+ copytarget = target;
+
if (copy && copytarget) {
- copy->connect(E->get().signal, copytarget, E->get().method, E->get().binds, CONNECT_PERSIST);
+ copy->connect(E->get().signal, copytarget, E->get().method, E->get().binds, E->get().flags);
}
}
}
@@ -2319,6 +2378,9 @@ Node *Node::duplicate_and_reown(const Map<Node *, Node *> &p_reown_map) const {
get_child(i)->_duplicate_and_reown(node, p_reown_map);
}
+ // Duplication of signals must happen after all the node descendants have been copied,
+ // because re-targeting of connections from some descendant to another is not possible
+ // if the emitter node comes later in tree order than the receiver
_duplicate_signals(this, node);
return node;
}
@@ -2422,7 +2484,9 @@ void Node::_replace_connections_target(Node *p_new_target) {
if (c.flags & CONNECT_PERSIST) {
c.source->disconnect(c.signal, this, c.method);
- ERR_CONTINUE(!p_new_target->has_method(c.method));
+ bool valid = p_new_target->has_method(c.method) || p_new_target->get_script().is_null() || Ref<Script>(p_new_target->get_script())->has_method(c.method);
+ ERR_EXPLAIN("Attempt to connect signal \'" + c.source->get_class() + "." + c.signal + "\' to nonexistent method \'" + c.target->get_class() + "." + c.method + "\'");
+ ERR_CONTINUE(!valid);
c.source->connect(c.signal, p_new_target, c.method, c.binds, c.flags);
}
}
@@ -2466,24 +2530,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)
@@ -2496,21 +2555,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));
}
}