Node *SceneState::instance(bool p_gen_edit_state) const { // nodes where instancing failed (because something is missing) List<Node*> stray_instances; #define NODE_FROM_ID(p_name,p_id)\ Node *p_name;\ if (p_id&FLAG_ID_IS_PATH) {\ NodePath np=node_paths[p_id&FLAG_MASK];\ p_name=ret_nodes[0]->_get_node(np);\ } else {\ ERR_FAIL_INDEX_V(p_id&FLAG_MASK,nc,NULL);\ p_name=ret_nodes[p_id&FLAG_MASK];\ } int nc = nodes.size(); ERR_FAIL_COND_V(nc==0,NULL); const StringName*snames=NULL; int sname_count=names.size(); if (sname_count) snames=&names[0]; const Variant*props=NULL; int prop_count=variants.size(); if (prop_count) props=&variants[0]; //Vector<Variant> properties; const NodeData *nd = &nodes[0]; Node **ret_nodes=(Node**)alloca( sizeof(Node*)*nc ); bool gen_node_path_cache=p_gen_edit_state && node_path_cache.empty(); for(int i=0;i<nc;i++) { const NodeData &n=nd[i]; Node *parent=NULL; if (i>0) { NODE_FROM_ID(nparent,n.parent); #ifdef DEBUG_ENABLED if (!nparent && n.parent&FLAG_ID_IS_PATH) { WARN_PRINT(String("Parent path '"+String(node_paths[n.parent&FLAG_MASK])+"' for node '"+String(snames[n.name])+"' has vanished when instancing: '"+get_path()+"'.").ascii().get_data()); } #endif parent=nparent; } Node *node=NULL; if (i==0 && base_scene_idx>=0) { //scene inheritance on root node //print_line("scene inherit"); Ref<PackedScene> sdata = props[ base_scene_idx ]; ERR_FAIL_COND_V( !sdata.is_valid(), NULL); node = sdata->instance(p_gen_edit_state); ERR_FAIL_COND_V(!node,NULL); if (p_gen_edit_state) { node->set_scene_inherited_state(sdata->get_state()); } } else if (n.instance>=0) { //instance a scene into this node //print_line("instance"); if (n.instance&FLAG_INSTANCE_IS_PLACEHOLDER) { String path = props[n.instance&FLAG_MASK]; if (disable_placeholders) { Ref<PackedScene> sdata = ResourceLoader::load(path,"PackedScene"); ERR_FAIL_COND_V( !sdata.is_valid(), NULL); node = sdata->instance(p_gen_edit_state); ERR_FAIL_COND_V(!node,NULL); } else { InstancePlaceholder *ip = memnew( InstancePlaceholder ); ip->set_instance_path(path); node=ip; } node->set_scene_instance_load_placeholder(true); } else { Ref<PackedScene> sdata = props[ n.instance&FLAG_MASK ]; ERR_FAIL_COND_V( !sdata.is_valid(), NULL); node = sdata->instance(p_gen_edit_state); ERR_FAIL_COND_V(!node,NULL); } } else if (n.type==TYPE_INSTANCED) { //print_line("instanced"); //get the node from somewhere, it likely already exists from another instance if (parent) { node=parent->_get_child_by_name(snames[n.name]); #ifdef DEBUG_ENABLED if (!node) { WARN_PRINT(String("Node '"+String(ret_nodes[0]->get_path_to(parent))+"/"+String(snames[n.name])+"' was modified from inside a instance, but it has vanished.").ascii().get_data()); } #endif } } else if (ObjectTypeDB::is_type_enabled(snames[n.type])) { //print_line("created"); //node belongs to this scene and must be created Object * obj = ObjectTypeDB::instance(snames[ n.type ]); if (!obj || !obj->cast_to<Node>()) { if (obj) { memdelete(obj); obj=NULL; } WARN_PRINT(String("Warning node of type "+snames[n.type].operator String()+" does not exist.").ascii().get_data()); if (n.parent>=0 && n.parent<nc && ret_nodes[n.parent]) { if (ret_nodes[n.parent]->cast_to<Spatial>()) { obj = memnew( Spatial ); } else if (ret_nodes[n.parent]->cast_to<Control>()) { obj = memnew( Control ); } else if (ret_nodes[n.parent]->cast_to<Node2D>()) { obj = memnew( Node2D ); } } if (!obj) { obj = memnew( Node ); } } node = obj->cast_to<Node>(); } if (node) { // may not have found the node (part of instanced scene and removed) // if found all is good, otherwise ignore //properties int nprop_count=n.properties.size(); if (nprop_count) { const NodeData::Property* nprops=&n.properties[0]; for(int j=0;j<nprop_count;j++) { bool valid; ERR_FAIL_INDEX_V( nprops[j].name, sname_count, NULL ); ERR_FAIL_INDEX_V( nprops[j].value, prop_count, NULL ); if (snames[ nprops[j].name ]==CoreStringNames::get_singleton()->_script) { //work around to avoid old script variables from disappearing, should be the proper fix to: //https://github.com/godotengine/godot/issues/2958 //store old state List<Pair<StringName,Variant> > old_state; if (node->get_script_instance()) { node->get_script_instance()->get_property_state(old_state); } node->set(snames[ nprops[j].name ],props[ nprops[j].value ],&valid); //restore old state for new script, if exists for (List<Pair<StringName,Variant> >::Element *E=old_state.front();E;E=E->next()) { node->set(E->get().first,E->get().second); } } else { node->set(snames[ nprops[j].name ],props[ nprops[j].value ],&valid); } } } //name //groups for(int j=0;j<n.groups.size();j++) { ERR_FAIL_INDEX_V( n.groups[j], sname_count, NULL ); node->add_to_group( snames[ n.groups[j] ], true ); } if (n.instance>=0 || n.type!=TYPE_INSTANCED || i==0) { //if node was not part of instance, must set it's name, parenthood and ownership if (i>0) { if (parent) { parent->_add_child_nocheck(node,snames[n.name]); } else { //it may be possible that an instanced scene has changed //and the node has nowhere to go anymore stray_instances.push_back(node); //can't be added, go to stray list } } else { node->_set_name_nocheck( snames[ n.name ] ); } } if (n.owner>=0) { NODE_FROM_ID(owner,n.owner); if (owner) node->_set_owner_nocheck(owner); } } ret_nodes[i]=node; if (node && gen_node_path_cache && ret_nodes[0]) { NodePath n = ret_nodes[0]->get_path_to(node); node_path_cache[n]=i; } } //do connections int cc = connections.size(); const ConnectionData *cdata = connections.ptr(); for(int i=0;i<cc;i++) { const ConnectionData &c=cdata[i]; //ERR_FAIL_INDEX_V( c.from, nc, NULL ); //ERR_FAIL_INDEX_V( c.to, nc, NULL ); NODE_FROM_ID(cfrom,c.from); NODE_FROM_ID(cto,c.to); if (!cfrom || !cto) continue; Vector<Variant> binds; if (c.binds.size()) { binds.resize(c.binds.size()); for(int j=0;j<c.binds.size();j++) binds[j]=props[ c.binds[j] ]; } cfrom->connect( snames[ c.signal], cto, snames[ c.method], binds,CONNECT_PERSIST|c.flags ); } //Node *s = ret_nodes[0]; //remove nodes that could not be added, likely as a result that while(stray_instances.size()) { memdelete(stray_instances.front()->get()); stray_instances.pop_front();; } for(int i=0;i<editable_instances.size();i++) { Node *ei = ret_nodes[0]->_get_node(editable_instances[i]); if (ei) { ret_nodes[0]->set_editable_instance(ei,true); } } return ret_nodes[0]; }
Node *Node::duplicate(bool p_use_instancing) const { Node *node=NULL; bool instanced=false; if (cast_to<InstancePlaceholder>()) { const InstancePlaceholder *ip = cast_to<const InstancePlaceholder>(); InstancePlaceholder *nip = memnew( InstancePlaceholder ); nip->set_instance_path( ip->get_instance_path() ); node=nip; } else if (p_use_instancing && get_filename()!=String()) { Ref<PackedScene> res = ResourceLoader::load(get_filename()); ERR_FAIL_COND_V(res.is_null(),NULL); node=res->instance(); ERR_FAIL_COND_V(!node,NULL); instanced=true; } else { Object *obj = ObjectTypeDB::instance(get_type()); ERR_FAIL_COND_V(!obj,NULL); node = obj->cast_to<Node>(); if (!node) memdelete(obj); ERR_FAIL_COND_V(!node,NULL); } if (get_filename()!="") { //an instance node->set_filename(get_filename()); } List<PropertyInfo> plist; 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; node->set( name, get(name) ); } node->set_name(get_name()); List<GroupInfo> gi; get_groups(&gi); for (List<GroupInfo>::Element *E=gi.front();E;E=E->next()) { node->add_to_group(E->get().name, E->get().persistent); } _duplicate_signals(this, node); for(int i=0;i<get_child_count();i++) { if (get_child(i)->data.parent_owned) continue; if (instanced && get_child(i)->data.owner==this) continue; //part of instance Node *dup = get_child(i)->duplicate(p_use_instancing); if (!dup) { memdelete(node); return NULL; } node->add_child(dup); } return node; }