summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/path_db.cpp31
-rw-r--r--core/path_db.h5
-rw-r--r--core/resource.h2
-rw-r--r--demos/2d/platformer/coin.gd8
-rw-r--r--modules/gdscript/gd_parser.cpp46
-rw-r--r--platform/osx/os_osx.h2
-rw-r--r--platform/osx/os_osx.mm5
-rw-r--r--scene/main/instance_placeholder.cpp74
-rw-r--r--scene/main/instance_placeholder.h37
-rw-r--r--scene/main/node.cpp76
-rw-r--r--scene/main/node.h37
-rw-r--r--scene/register_scene_types.cpp13
-rw-r--r--scene/resources/packed_scene.cpp1144
-rw-r--r--scene/resources/packed_scene.h112
-rw-r--r--scene/resources/scene_format_text.cpp792
-rw-r--r--scene/resources/scene_format_text.h46
-rw-r--r--tools/editor/editor_node.cpp19
-rw-r--r--tools/editor/editor_node.h3
-rw-r--r--tools/editor/property_editor.cpp151
-rw-r--r--tools/editor/property_editor.h7
-rw-r--r--tools/editor/reparent_dialog.cpp1
-rw-r--r--tools/editor/scene_tree_dock.cpp51
-rw-r--r--tools/editor/scene_tree_editor.cpp123
-rw-r--r--tools/editor/scene_tree_editor.h8
24 files changed, 2539 insertions, 254 deletions
diff --git a/core/path_db.cpp b/core/path_db.cpp
index d3dc3aceb8..c6ea25d966 100644
--- a/core/path_db.cpp
+++ b/core/path_db.cpp
@@ -286,6 +286,37 @@ NodePath::NodePath(const Vector<StringName>& p_path,const Vector<StringName>& p_
data->property=p_property;
}
+
+void NodePath::simplify() {
+
+ if (!data)
+ return;
+ for(int i=0;i<data->path.size();i++) {
+ if (data->path.size()==1)
+ break;
+ if (data->path[i].operator String()==".") {
+ data->path.remove(i);
+ i--;
+ } else if (data->path[i].operator String()==".." && i>0 && data->path[i-1].operator String()!="." && data->path[i-1].operator String()!="..") {
+ //remove both
+ data->path.remove(i-1);
+ data->path.remove(i-1);
+ i-=2;
+ if (data->path.size()==0) {
+ data->path.push_back(".");
+ break;
+ }
+ }
+ }
+}
+
+NodePath NodePath::simplified() const {
+
+ NodePath np=*this;
+ np.simplify();
+ return np;
+}
+
NodePath::NodePath(const String& p_path) {
data=NULL;
diff --git a/core/path_db.h b/core/path_db.h
index b4f13d50be..de84216006 100644
--- a/core/path_db.h
+++ b/core/path_db.h
@@ -84,7 +84,10 @@ public:
bool operator==(const NodePath& p_path) const;
bool operator!=(const NodePath& p_path) const;
void operator=(const NodePath& p_path);
-
+
+ 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 NodePath& p_path);
diff --git a/core/resource.h b/core/resource.h
index 9d9c445e1d..3596abe673 100644
--- a/core/resource.h
+++ b/core/resource.h
@@ -130,7 +130,7 @@ public:
void set_name(const String& p_name);
String get_name() const;
- void set_path(const String& p_path,bool p_take_over=false);
+ virtual void set_path(const String& p_path,bool p_take_over=false);
String get_path() const;
void set_subindex(int p_sub_index);
diff --git a/demos/2d/platformer/coin.gd b/demos/2d/platformer/coin.gd
index 3e31a395ae..983cd46d88 100644
--- a/demos/2d/platformer/coin.gd
+++ b/demos/2d/platformer/coin.gd
@@ -18,3 +18,11 @@ func _ready():
# Initalization here
pass
+
+
+func _on_coin_area_enter( area ):
+ pass # replace with function body
+
+
+func _on_coin_area_enter_shape( area_id, area, area_shape, area_shape ):
+ pass # replace with function body
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 9c39051b7f..313fb57d0e 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -2421,6 +2421,16 @@ void GDParser::_parse_class(ClassNode *p_class) {
}; //fallthrough to use the same
case Variant::REAL: {
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="EASE") {
+ current_export.hint=PROPERTY_HINT_EXP_EASING;
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in hint.");
+ return;
+ }
+ break;
+ }
+
float sign=1.0;
if (tokenizer->get_token()==GDTokenizer::TK_OP_SUB) {
@@ -2571,6 +2581,17 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
break;
}
+
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="MULTILINE") {
+
+ current_export.hint=PROPERTY_HINT_MULTILINE_TEXT;
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in hint.");
+ return;
+ }
+ break;
+ }
} break;
case Variant::COLOR: {
@@ -2607,23 +2628,16 @@ void GDParser::_parse_class(ClassNode *p_class) {
} else if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
String identifier = tokenizer->get_token_identifier();
- if (identifier == "flag") {
- current_export.type=Variant::INT;
- current_export.hint=PROPERTY_HINT_ALL_FLAGS;
- }else if (identifier == "multiline"){
- current_export.type=Variant::STRING;
- current_export.hint=PROPERTY_HINT_MULTILINE_TEXT;
- } else {
- if (!ObjectTypeDB::is_type(identifier,"Resource")) {
-
- current_export=PropertyInfo();
- _set_error("Export hint not a type or resource.");
- }
-
- current_export.type=Variant::OBJECT;
- current_export.hint=PROPERTY_HINT_RESOURCE_TYPE;
- current_export.hint_string=identifier;
+ if (!ObjectTypeDB::is_type(identifier,"Resource")) {
+
+ current_export=PropertyInfo();
+ _set_error("Export hint not a type or resource.");
}
+
+ current_export.type=Variant::OBJECT;
+ current_export.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ current_export.hint_string=identifier;
+
tokenizer->advance();
}
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 49fa6c0862..e8277688ac 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -157,6 +157,8 @@ public:
Error shell_open(String p_uri);
void push_input(const InputEvent& p_event);
+ String get_locale() const;
+
virtual void set_video_mode(const VideoMode& p_video_mode,int p_screen=0);
virtual VideoMode get_video_mode(int p_screen=0) const;
virtual void get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen=0) const;
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index b0ce37cecf..4990d04ab6 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -1254,6 +1254,11 @@ Error OS_OSX::shell_open(String p_uri) {
return OK;
}
+String OS_OSX::get_locale() const {
+ NSString* preferredLang = [[NSLocale preferredLanguages] objectAtIndex:0];
+ return [preferredLang UTF8String];
+}
+
void OS_OSX::swap_buffers() {
[context flushBuffer];
diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp
new file mode 100644
index 0000000000..370eb1e74a
--- /dev/null
+++ b/scene/main/instance_placeholder.cpp
@@ -0,0 +1,74 @@
+#include "instance_placeholder.h"
+
+#include "scene/resources/packed_scene.h"
+#include "io/resource_loader.h"
+
+bool InstancePlaceholder::_set(const StringName& p_name, const Variant& p_value) {
+
+ PropSet ps;
+ ps.name=p_name;
+ ps.value=p_value;
+ stored_values.push_back(ps);
+ return true;
+}
+
+bool InstancePlaceholder::_get(const StringName& p_name,Variant &r_ret) const{
+
+ return false;
+}
+void InstancePlaceholder::_get_property_list( List<PropertyInfo> *p_list) const{
+
+
+}
+
+
+void InstancePlaceholder::set_path(const String& p_name) {
+
+ path=p_name;
+}
+
+String InstancePlaceholder::get_path() const {
+
+ return path;
+}
+void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_scene){
+
+ ERR_FAIL_COND(!is_inside_tree());
+
+ Node *base = get_parent();
+ if (!base)
+ return;
+
+ Ref<PackedScene> ps;
+ if (p_custom_scene.is_valid())
+ ps = p_custom_scene;
+ else
+ ps = ResourceLoader::load(path,"PackedScene");
+
+ if (!ps.is_valid())
+ return;
+ Node *scene = ps->instance();
+ scene->set_name(get_name());
+ int pos = get_position_in_parent();
+
+ for(List<PropSet>::Element *E=stored_values.front();E;E=E->next()) {
+ scene->set(E->get().name,E->get().value);
+ }
+
+ queue_delete();
+
+ base->remove_child(this);
+ base->add_child(scene);
+ base->move_child(scene,pos);
+
+}
+
+void InstancePlaceholder::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("replace_by_instance","custom_scene:PackedScene"),&InstancePlaceholder::replace_by_instance,DEFVAL(Variant()));
+}
+
+InstancePlaceholder::InstancePlaceholder() {
+
+
+}
diff --git a/scene/main/instance_placeholder.h b/scene/main/instance_placeholder.h
new file mode 100644
index 0000000000..e9e76e7a2d
--- /dev/null
+++ b/scene/main/instance_placeholder.h
@@ -0,0 +1,37 @@
+#ifndef INSTANCE_PLACEHOLDER_H
+#define INSTANCE_PLACEHOLDER_H
+
+#include "scene/main/node.h"
+
+class PackedScene;
+
+class InstancePlaceholder : public Node {
+
+ OBJ_TYPE(InstancePlaceholder,Node);
+
+ String path;
+ struct PropSet {
+ StringName name;
+ Variant value;
+ };
+
+ List<PropSet> stored_values;
+
+protected:
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+ static void _bind_methods();
+
+public:
+
+ void set_path(const String& p_name);
+ String get_path() const;
+
+ void replace_by_instance(const Ref<PackedScene>& p_custom_scene=Ref<PackedScene>());
+
+ InstancePlaceholder();
+};
+
+#endif // INSTANCE_PLACEHOLDER_H
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 8336ce35f6..dd4587c19f 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -841,6 +841,20 @@ Node *Node::get_child(int p_index) const {
return data.children[p_index];
}
+
+Node *Node::_get_child_by_name(const StringName& p_name) const {
+
+ int cc=data.children.size();
+ Node* const* cd=data.children.ptr();
+
+ for(int i=0;i<cc;i++){
+ if (cd[i]->data.name==p_name)
+ return cd[i];
+ }
+
+ return NULL;
+}
+
Node *Node::_get_node(const NodePath& p_path) const {
ERR_FAIL_COND_V( !data.inside_tree && p_path.is_absolute(), NULL );
@@ -906,8 +920,10 @@ Node *Node::_get_node(const NodePath& p_path) const {
Node *Node::get_node(const NodePath& p_path) const {
Node *node = _get_node(p_path);
- ERR_EXPLAIN("Node not found: "+p_path);
- ERR_FAIL_COND_V(!node,NULL);
+ if (!node) {
+ ERR_EXPLAIN("Node not found: "+p_path);
+ ERR_FAIL_COND_V(!node,NULL);
+ }
return node;
}
@@ -1036,6 +1052,7 @@ void Node::get_owned_by(Node *p_by,List<Node*> *p_owned) {
void Node::_set_owner_nocheck(Node* p_owner) {
+ ERR_FAIL_COND(data.owner);
data.owner=p_owner;
data.owner->data.owned.push_back( this );
data.OW = data.owner->data.owned.back();
@@ -1332,7 +1349,29 @@ String Node::get_filename() const {
return data.filename;
}
+void Node::set_editable_instance(Node* p_node,bool p_editable) {
+
+ ERR_FAIL_NULL(p_node);
+ ERR_FAIL_COND(!is_a_parent_of(p_node));
+ NodePath p = get_path_to(p_node);
+ if (!p_editable)
+ data.editable_instances.erase(p);
+ else
+ data.editable_instances[p]=true;
+
+}
+
+bool Node::is_editable_instance(Node *p_node) const {
+
+ if (!p_node)
+ return false; //easier, null is never editable :)
+ ERR_FAIL_COND_V(!is_a_parent_of(p_node),false);
+ NodePath p = get_path_to(p_node);
+ return data.editable_instances.has(p);
+}
+
+#if 0
void Node::generate_instance_state() {
@@ -1383,13 +1422,36 @@ Dictionary Node::get_instance_state() const {
return data.instance_state;
}
-Vector<StringName> Node::get_instance_groups() const {
+#endif
+
+void Node::set_scene_instance_state(const Ref<SceneState>& p_state) {
+
+ data.instance_state=p_state;
+}
+
+Ref<SceneState> Node::get_scene_instance_state() const{
+
+ return data.instance_state;
+}
+
+void Node::set_scene_inherited_state(const Ref<SceneState>& p_state) {
+
+ data.inherited_state=p_state;
+}
+
+Ref<SceneState> Node::get_scene_inherited_state() const{
- return data.instance_groups;
+ return data.inherited_state;
}
-Vector<Node::Connection> Node::get_instance_connections() const{
- return data.instance_connections;
+void Node::set_scene_instance_load_placeholder(bool p_enable) {
+
+ data.use_placeholder=p_enable;
+}
+
+bool Node::get_scene_instance_load_placeholder() const{
+
+ return data.use_placeholder;
}
int Node::get_position_in_parent() const {
@@ -2049,6 +2111,7 @@ Node::Node() {
data.parent_owned=false;
data.in_constructor=true;
data.viewport=NULL;
+ data.use_placeholder=false;
}
Node::~Node() {
@@ -2065,3 +2128,4 @@ Node::~Node() {
}
+////////////////////////////////
diff --git a/scene/main/node.h b/scene/main/node.h
index a6d5bfbd9f..87fa4dd6ca 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -38,6 +38,7 @@
class Viewport;
+class SceneState;
class Node : public Object {
OBJ_TYPE( Node, Object );
@@ -69,9 +70,10 @@ private:
struct Data {
String filename;
- Dictionary instance_state;
- Vector<StringName> instance_groups;
- Vector<Connection> instance_connections;
+ Ref<SceneState> instance_state;
+ Ref<SceneState> inherited_state;
+
+ HashMap<NodePath,int> editable_instances;
Node *parent;
Node *owner;
@@ -96,6 +98,7 @@ private:
PauseMode pause_mode;
Node *pause_owner;
// variables used to properly sort the node when processing, ignored otherwise
+ //should move all the stuff below to bits
bool fixed_process;
bool idle_process;
@@ -105,6 +108,9 @@ private:
bool parent_owned;
bool in_constructor;
+ bool use_placeholder;
+
+
} data;
@@ -112,6 +118,7 @@ private:
virtual bool _use_builtin_script() const { return true; }
Node *_get_node(const NodePath& p_path) const;
+ Node *_get_child_by_name(const StringName& p_name) const;
@@ -151,7 +158,7 @@ protected:
static void _bind_methods();
-friend class PackedScene;
+friend class SceneState;
void _add_child_nocheck(Node* p_child,const StringName& p_name);
void _set_owner_nocheck(Node* p_owner);
@@ -208,7 +215,7 @@ public:
struct GroupInfo {
- String name;
+ StringName name;
bool persistent;
};
@@ -229,7 +236,11 @@ public:
void set_filename(const String& p_filename);
String get_filename() const;
-
+
+ void set_editable_instance(Node* p_node,bool p_editable);
+ bool is_editable_instance(Node* p_node) const;
+
+
/* NOTIFICATIONS */
void propagate_notification(int p_notification);
@@ -261,10 +272,14 @@ public:
//Node *clone_tree() const;
// used by editors, to save what has changed only
- void generate_instance_state();
- Dictionary get_instance_state() const;
- Vector<StringName> get_instance_groups() const;
- Vector<Connection> get_instance_connections() const;
+ void set_scene_instance_state(const Ref<SceneState>& p_state);
+ Ref<SceneState> get_scene_instance_state() const;
+
+ void set_scene_inherited_state(const Ref<SceneState>& p_state);
+ Ref<SceneState> get_scene_inherited_state() const;
+
+ void set_scene_instance_load_placeholder(bool p_enable);
+ bool get_scene_instance_load_placeholder() const;
static Vector<Variant> make_binds(VARIANT_ARG_LIST);
@@ -307,6 +322,4 @@ public:
typedef Set<Node*,Node::Comparator> NodeSet;
-
-
#endif
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 851de4a89f..fe6b192d78 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -36,6 +36,7 @@
#include "resources/default_theme/default_theme.h"
#include "object_type_db.h"
#include "scene/main/canvas_layer.h"
+#include "scene/main/instance_placeholder.h"
#include "scene/main/viewport.h"
#include "scene/gui/control.h"
#include "scene/gui/texture_progress.h"
@@ -216,7 +217,7 @@
#include "scene/3d/collision_polygon.h"
#endif
-
+#include "scene/resources/scene_format_text.h"
static ResourceFormatLoaderImage *resource_loader_image=NULL;
static ResourceFormatLoaderWAV *resource_loader_wav=NULL;
@@ -229,6 +230,8 @@ static ResourceFormatLoaderBitMap *resource_loader_bitmap=NULL;
static ResourceFormatLoaderTheme *resource_loader_theme=NULL;
static ResourceFormatLoaderShader *resource_loader_shader=NULL;
+static ResourceFormatSaverText *resource_saver_text=NULL;
+
//static SceneStringNames *string_names;
void register_scene_types() {
@@ -267,6 +270,7 @@ void register_scene_types() {
ObjectTypeDB::register_type<Object>();
ObjectTypeDB::register_type<Node>();
+ ObjectTypeDB::register_virtual_type<InstancePlaceholder>();
ObjectTypeDB::register_type<Viewport>();
ObjectTypeDB::register_virtual_type<RenderTargetTexture>();
@@ -612,6 +616,9 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
+ resource_saver_text = memnew( ResourceFormatSaverText );
+ ResourceSaver::add_resource_format_saver(resource_saver_text);
+
}
void unregister_scene_types() {
@@ -629,5 +636,9 @@ void unregister_scene_types() {
memdelete( resource_loader_theme );
memdelete( resource_loader_shader );
+
+ if (resource_saver_text) {
+ memdelete(resource_saver_text);
+ }
SceneStringNames::free();
}
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index b6082c3a76..a9dc0e8bfb 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -32,13 +32,30 @@
#include "scene/3d/spatial.h"
#include "scene/gui/control.h"
#include "scene/2d/node_2d.h"
+#include "scene/main/instance_placeholder.h"
-bool PackedScene::can_instance() const {
+#define PACK_VERSION 2
+
+bool SceneState::can_instance() const {
return nodes.size()>0;
}
-Node *PackedScene::instance(bool p_gen_edit_state) const {
+
+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);
@@ -59,29 +76,80 @@ Node *PackedScene::instance(bool p_gen_edit_state) const {
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];
- if (!ObjectTypeDB::is_type_enabled(snames[n.type])) {
- ret_nodes[i]=NULL;
- continue;
+ 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 (n.instance>=0) {
- //instance existing
- Ref<PackedScene> sdata = props[ n.instance ];
+ 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();
+ node = sdata->instance(p_gen_edit_state);
ERR_FAIL_COND_V(!node,NULL);
- if (p_gen_edit_state)
- node->generate_instance_state();
+ if (p_gen_edit_state) {
+ node->set_scene_inherited_state(sdata->get_state());
+ }
- } else {
- //create anew
+ } 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_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) {
@@ -109,49 +177,68 @@ Node *PackedScene::instance(bool p_gen_edit_state) const {
}
- //properties
- int nprop_count=n.properties.size();
- if (nprop_count) {
+ 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];
+ const NodeData::Property* nprops=&n.properties[0];
- for(int j=0;j<nprop_count;j++) {
+ 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 );
+ bool valid;
+ ERR_FAIL_INDEX_V( nprops[j].name, sname_count, NULL );
+ ERR_FAIL_INDEX_V( nprops[j].value, prop_count, NULL );
- node->set(snames[ nprops[j].name ],props[ nprops[j].value ],&valid);
+ node->set(snames[ nprops[j].name ],props[ nprops[j].value ],&valid);
+ }
}
- }
- //name
+ //name
- //groups
- for(int j=0;j<n.groups.size();j++) {
+ //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 );
- }
+ 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) {
+ //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 ] );
+ }
- ret_nodes[i]=node;
+ }
- if (i>0) {
- ERR_FAIL_INDEX_V(n.parent,i,NULL);
- ERR_FAIL_COND_V(!ret_nodes[n.parent],NULL);
- ret_nodes[n.parent]->_add_child_nocheck(node,snames[n.name]);
- } 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);
+ }
- if (n.owner>=0) {
- ERR_FAIL_INDEX_V(n.owner,i,NULL);
- node->_set_owner_nocheck(ret_nodes[n.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;
+ }
}
@@ -163,8 +250,14 @@ Node *PackedScene::instance(bool p_gen_edit_state) const {
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 );
+ //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()) {
@@ -173,17 +266,25 @@ Node *PackedScene::instance(bool p_gen_edit_state) const {
binds[j]=props[ c.binds[j] ];
}
- if (!ret_nodes[c.from] || !ret_nodes[c.to])
- continue;
- ret_nodes[c.from]->connect( snames[ c.signal], ret_nodes[ c.to ], snames[ c.method], binds,CONNECT_PERSIST|c.flags );
+
+ cfrom->connect( snames[ c.signal], cto, snames[ c.method], binds,CONNECT_PERSIST|c.flags );
}
- Node *s = ret_nodes[0];
+ //Node *s = ret_nodes[0];
- if (get_path()!="" && get_path().find("::")==-1)
- s->set_filename(get_path());
+ //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);
+ }
+ }
- s->notification(Node::NOTIFICATION_INSTANCED);
return ret_nodes[0];
}
@@ -209,44 +310,157 @@ static int _vm_get_variant(const Variant& p_variant, HashMap<Variant,int,Variant
return idx;
}
-Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map) {
+Error SceneState::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,Map<Node*,int> &nodepath_map) {
+
- if (p_node!=p_owner && (p_node->get_owner()!=p_owner))
- return OK; //nothing to do with this node, may either belong to another scene or be onowned
+ // this function handles all the work related to properly packing scenes, be it
+ // instanced or inherited.
+ // given the complexity of this process, an attempt will be made to properly
+ // document it. if you fail to understand something, please ask!
+
+ //discard nodes that do not belong to be processed
+ if (p_node!=p_owner && p_node->get_owner()!=p_owner && !p_owner->is_editable_instance(p_node->get_owner()))
+ return OK;
+
+ // save the child instanced scenes that are chosen as editable, so they can be restored
+ // upon load back
+ if (p_node!=p_owner && p_node->get_filename()!=String() && p_owner->is_editable_instance(p_node))
+ editable_instances.push_back(p_owner->get_path_to(p_node));
NodeData nd;
nd.name=_nm_get_string(p_node->get_name(),name_map);
- nd.type=_nm_get_string(p_node->get_type(),name_map);
- nd.parent=p_parent_idx;
+ nd.instance=-1; //not instanced by default
+
+ // if this node is part of an instanced scene or sub-instanced scene
+ // we need to get the corresponding instance states.
+ // with the instance states, we can query for identical properties/groups
+ // and only save what has changed
+
+ List<PackState> pack_state_stack;
+
+ bool instanced_by_owner=true;
+
+ {
+ Node *n=p_node;
+
+ while(n) {
+
+ if (n==p_owner) {
+
+ Ref<SceneState> state = n->get_scene_inherited_state();
+ if (state.is_valid()) {
+ int node = state->find_node_by_path(n->get_path_to(p_node));
+ if (node>=0) {
+ //this one has state for this node, save
+ PackState ps;
+ ps.node=node;
+ ps.state=state;
+ pack_state_stack.push_front(ps);
+ instanced_by_owner=false;
+ }
+ }
+
+ if (p_node->get_filename()!=String() && p_node->get_owner()==p_owner && instanced_by_owner) {
+
+ if (p_node->get_scene_instance_load_placeholder()) {
+ //it's a placeholder, use the placeholder path
+ nd.instance=_vm_get_variant(p_node->get_filename(),variant_map);
+ nd.instance|=FLAG_INSTANCE_IS_PLACEHOLDER;
+ } else {
+ //must instance ourselves
+ Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename());
+ if (!instance.is_valid()) {
+ return ERR_CANT_OPEN;
+ }
+ nd.instance=_vm_get_variant(instance,variant_map);
+ }
+ }
+ n=NULL;
+ } else {
+ if (n->get_filename()!=String()) {
+ //is an instance
+ Ref<SceneState> state = n->get_scene_instance_state();
+ if (state.is_valid()) {
+ int node = state->find_node_by_path(n->get_path_to(p_node));
+ if (node>=0) {
+ //this one has state for this node, save
+ PackState ps;
+ ps.node=node;
+ ps.state=state;
+ pack_state_stack.push_back(ps);
+ }
+ }
+
+ }
+ n=n->get_owner();
+ }
+ }
+ }
+
+#if 0
+
+ Ref<SceneState> base_scene = p_node->get_scene_inherited_state(); //for inheritance
+ Ref<SceneState> instance_state;
+ int instance_state_node=-1;
+
+ if (base_scene.is_valid() && (p_node==p_owner || p_node->get_owner()==p_owner)) {
+ //scene inheritance in use, see if this node is actually inherited
+ NodePath path = p_owner->get_path_to(p_node);
+ instance_state_node = base_scene->find_node_by_path(path);
+ if (instance_state_node>=0) {
+ instance_state=base_scene;
+ }
+ }
- Dictionary instance_state;
- Set<StringName> instance_groups;
+ // check that this is a directly instanced scene from the scene being packed, if so
+ // this information must be saved. Of course, if using scene instancing and this node
+ // does belong to base scene, ignore.
+ if (instance_state.is_null() && p_node!=p_owner && p_node->get_owner()==p_owner && p_node->get_filename()!="") {
- if (p_node!=p_owner && p_node->get_filename()!="") {
- //instanced
+ //instanced, only direct sub-scnes are supported of course
Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename());
if (!instance.is_valid()) {
return ERR_CANT_OPEN;
}
nd.instance=_vm_get_variant(instance,variant_map);
- instance_state = p_node->get_instance_state();
- Vector<StringName> ig = p_node->get_instance_groups();
- for(int i=0;i<ig.size();i++)
- instance_groups.insert(ig[i]);
+
} else {
nd.instance=-1;
}
+ // finally, if this does not belong to scene inheritance, check
+ // if it belongs to scene instancing
+
+ if (instance_state.is_null() && p_node!=p_owner) {
+ //if not affected by scene inheritance, this may be
+ if (p_node->get_owner()==p_owner && p_node->get_filename()!=String()) {
+ instance_state=p_node->get_scene_instance_state();
+ if (instance_state.is_valid()) {
+ instance_state_node=instance_state->find_node_by_path(p_node->get_path_to(p_node));
+ }
- //instance state makes sure that only changes to instance are saved
+ } else if (p_node->get_owner()!=p_owner && p_owner->is_editable_instance(p_node->get_owner())) {
+ instance_state=p_node->get_owner()->get_scene_instance_state();
+ if (instance_state.is_valid()) {
+ instance_state_node=instance_state->find_node_by_path(p_node->get_owner()->get_path_to(p_node));
+ }
+ }
+ }
+#endif
+ int subscene_prop_search_from=0;
+
+ // all setup, we then proceed to check all properties for the node
+ // and save the ones that are worth saving
List<PropertyInfo> plist;
p_node->get_property_list(&plist);
+
+
for (List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) {
@@ -257,34 +471,63 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<
String name = E->get().name;
Variant value = p_node->get( E->get().name );
- if (nd.instance<0 && ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one())) {
- continue;
- }
- print_line("PASSED!");
- print_line("at: "+String(p_node->get_name())+"::"+name+": - nz: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO)+" no: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONONE));
- print_line("value: "+String(value)+" is zero: "+itos(value.is_zero())+" is one" +itos(value.is_one()));
+ bool isdefault = ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one());
+
+// if (nd.instance<0 && ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one())) {
+// continue;
+// }
- if (nd.instance>=0) {
- //only save changed properties in instance
- /*
- // this was commented because it would not save properties created from within script
- // done with _get_property_list, that are not in the original node.
- // if some property is not saved, check again
- if (!instance_state.has(name)) {
- print_line("skip not in instance");
+
+ //print_line("PASSED!");
+ //print_line("at: "+String(p_node->get_name())+"::"+name+": - nz: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO)+" no: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONONE));
+ //print_line("value: "+String(value)+" is zero: "+itos(value.is_zero())+" is one" +itos(value.is_one()));
+
+ if (pack_state_stack.size()) {
+ // we are on part of an instanced subscene
+ // or part of instanced scene.
+ // only save what has been changed
+ // only save changed properties in instance
+
+ if (E->get().usage & PROPERTY_USAGE_NO_INSTANCE_STATE || E->get().name=="__meta__") {
+ //property has requested that no instance state is saved, sorry
+ //also, meta won't be overriden or saved
continue;
- }*/
+ }
+
+ bool exists=false;
+ Variant original;
+
+ for (List<PackState>::Element *F=pack_state_stack.back();F;F=F->prev()) {
+ //check all levels of pack to see if the property exists somewhere
+ const PackState &ps=F->get();
- if (E->get().usage & PROPERTY_USAGE_NO_INSTANCE_STATE) {
+ original = ps.state->get_property_value(ps.node,E->get().name,exists);
+ if (exists) {
+ break;
+ }
+ }
+
+
+ if (exists && bool(Variant::evaluate(Variant::OP_EQUAL,value,original))) {
+ //exists and did not change
continue;
}
- if (instance_state.has(name) && instance_state[name]==value) {
+ if (!exists && isdefault) {
+ //does not exist in original node, but it's the default value
+ //so safe to skip too.
continue;
}
+
+ } else {
+
+ if (isdefault) {
+ //it's the default value, no point in saving it
+ continue;
+ }
}
NodeData::Property prop;
@@ -295,6 +538,9 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<
}
+ // save the groups this node is into
+ // discard groups that come from the original scene
+
List<Node::GroupInfo> groups;
p_node->get_groups(&groups);
for(List<Node::GroupInfo>::Element *E=groups.front();E;E=E->next()) {
@@ -302,27 +548,123 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<
if (!gi.persistent)
continue;
- if (nd.instance>=0 && instance_groups.has(gi.name))
- continue; //group was instanced, don't add here
+// if (instance_state_node>=0 && instance_state->is_node_in_group(instance_state_node,gi.name))
+// continue; //group was instanced, don't add here
+
+ bool skip=false;
+ for (List<PackState>::Element *F=pack_state_stack.front();F;F=F->next()) {
+ //check all levels of pack to see if the group was added somewhere
+ const PackState &ps=F->get();
+ if (ps.state->is_node_in_group(ps.node,gi.name)) {
+ skip=true;
+ break;
+ }
+ }
+
+ if (skip)
+ continue;
nd.groups.push_back(_nm_get_string(gi.name,name_map));
}
- if (node_map.has(p_node->get_owner()))
- nd.owner=node_map[p_node->get_owner()];
- else
+
+ // save the right owner
+ // for the saved scene root this is -1
+ // for nodes of the saved scene this is 0
+ // for nodes of instanced scenes this is >0
+
+ if (p_node==p_owner) {
+ //saved scene root
+ nd.owner=-1;
+ } else if (p_node->get_owner()==p_owner) {
+ //part of saved scene
+ nd.owner=0;
+ } else {
+
nd.owner=-1;
+#if 0
+ // this is pointless, if this was instanced by something else,
+ // the owner will already be set.
+
+ if (node_map.has(p_node->get_owner())) {
+ //maybe an existing saved node
+ nd.owner=node_map[p_node->get_owner()];
+ } else {
+ //not saved, use nodepath map
+ int sidx;
+ if (nodepath_map.has(p_node->get_owner())) {
+ sidx=nodepath_map[p_node->get_owner()];
+ } else {
+ sidx=nodepath_map.size();
+ nodepath_map[p_node->get_owner()]=sidx;
+ }
+
+ nd.owner=FLAG_ID_IS_PATH|sidx;
+
+ }
+#endif
+
+
+ }
+
+ // Save the right type. If this node was created by an instance
+ // then flag that the node should not be created but reused
+ if (pack_state_stack.empty()) {
+ //this node is not part of an instancing process, so save the type
+ nd.type=_nm_get_string(p_node->get_type(),name_map);
+ } else {
+ // this node is part of an instanced process, so do not save the type.
+ // instead, save that it was instanced
+ nd.type=TYPE_INSTANCED;
+ }
+
+
+ // determine whether to save this node or not
+ // if this node is part of an instanced sub-scene, we can skip storing it if basically
+ // no properties changed and no groups were added to it.
+ // below condition is true for all nodes of the scene being saved, and ones in subscenes
+ // that hold changes
+
+ bool save_node = nd.properties.size() || nd.groups.size(); // some local properties or groups exist
+ save_node = save_node || p_node==p_owner; // owner is always saved
+ save_node = save_node || (p_node->get_owner()==p_owner && instanced_by_owner); //part of scene and not instanced
+
int idx = nodes.size();
- node_map[p_node]=idx;
- nodes.push_back(nd);
+ int parent_node=NO_PARENT_SAVED;
+
+ if (save_node) {
+ //don't save the node if nothing and subscene
+
+ node_map[p_node]=idx;
+
+ //ok validate parent node
+ if (p_parent_idx==NO_PARENT_SAVED) {
+
+ int sidx;
+ if (nodepath_map.has(p_node->get_parent())) {
+ sidx=nodepath_map[p_node->get_parent()];
+ } else {
+ sidx=nodepath_map.size();
+ nodepath_map[p_node->get_parent()]=sidx;
+ }
+
+ nd.parent=FLAG_ID_IS_PATH|sidx;
+ } else {
+ nd.parent=p_parent_idx;
+ }
+
+ parent_node=idx;
+ nodes.push_back(nd);
+
+ }
for(int i=0;i<p_node->get_child_count();i++) {
Node *c=p_node->get_child(i);
- Error err = _parse_node(p_owner,c,idx,name_map,variant_map,node_map);
+ Error err = _parse_node(p_owner,c,parent_node,name_map,variant_map,node_map,nodepath_map);
if (err)
return err;
}
@@ -331,53 +673,135 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<
}
-Error PackedScene::_parse_connections(Node *p_owner,Node *p_node, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map) {
-
- if (p_node!=p_owner && (p_node->get_owner()!=p_owner))
- return OK; //nothing to do with this node, may either belong to another scene or be onowned
-
- List<MethodInfo> signals;
- p_node->get_signal_list(&signals);
+Error SceneState::_parse_connections(Node *p_owner,Node *p_node, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,Map<Node*,int> &nodepath_map) {
- ERR_FAIL_COND_V( !node_map.has(p_node), ERR_BUG);
- NodeData &nd = nodes[node_map[p_node]];
- Set<Connection> instance_connections;
+ if (p_node!=p_owner && p_node->get_owner() && p_node->get_owner()!=p_owner && !p_owner->is_editable_instance(p_node->get_owner()))
+ return OK;
- if (nd.instance>=0) {
- Vector<Connection> iconns = p_node->get_instance_connections();
- for(int i=0;i<iconns.size();i++) {
+ List<MethodInfo> _signals;
+ p_node->get_signal_list(&_signals);
- instance_connections.insert(iconns[i]);
- }
- }
+ //ERR_FAIL_COND_V( !node_map.has(p_node), ERR_BUG);
+ //NodeData &nd = nodes[node_map[p_node]];
- for(List<MethodInfo>::Element *E=signals.front();E;E=E->next()) {
+ for(List<MethodInfo>::Element *E=_signals.front();E;E=E->next()) {
List<Node::Connection> conns;
p_node->get_signal_connection_list(E->get().name,&conns);
for(List<Node::Connection>::Element *F=conns.front();F;F=F->next()) {
const Node::Connection &c = F->get();
- if (!(c.flags&CONNECT_PERSIST))
+
+ if (!(c.flags&CONNECT_PERSIST)) //only persistent connections get saved
continue;
- if (nd.instance>=0 && instance_connections.has(c))
- continue; //came from instance, don't save!
+ // only connections that originate or end into main saved scene are saved
+ // everything else is discarded
Node *n=c.target->cast_to<Node>();
- if (!n)
+ if (!n) {
continue;
+ }
+
+ //source node is outside saved scene?
+ bool src_is_out = p_node!=p_owner && (p_node->get_filename()!=String() || p_node->get_owner()!=p_owner);
+ //target node is outside saved scene?
+ bool dst_is_out = n!=p_owner && (n->get_filename()!=String() || n->get_owner()!=p_owner);
- if (!node_map.has(n)) {
- WARN_PRINT("Connection to node outside scene??")
+ //if both are out, ignore connection
+ if (src_is_out && dst_is_out) {
continue;
}
+
+ {
+ Node *nl=p_node;
+
+ bool exists=false;
+
+ while(nl) {
+
+ if (nl==p_owner) {
+
+ Ref<SceneState> state = nl->get_scene_inherited_state();
+ if (state.is_valid()) {
+ int from_node = state->find_node_by_path(nl->get_path_to(p_node));
+ int to_node = state->find_node_by_path(nl->get_path_to(n));
+
+ if (from_node>=0 && to_node>=0) {
+ //this one has state for this node, save
+ if (state->is_connection(from_node,c.signal,to_node,c.method)) {
+ exists=true;
+ break;
+ }
+ }
+ }
+
+ nl=NULL;
+ } else {
+ if (nl->get_filename()!=String()) {
+ //is an instance
+ Ref<SceneState> state = nl->get_scene_instance_state();
+ if (state.is_valid()) {
+ int from_node = state->find_node_by_path(nl->get_path_to(p_node));
+ int to_node = state->find_node_by_path(nl->get_path_to(n));
+
+ if (from_node>=0 && to_node>=0) {
+ //this one has state for this node, save
+ if (state->is_connection(from_node,c.signal,to_node,c.method)) {
+ exists=true;
+ break;
+ }
+ }
+ }
+
+ }
+ nl=nl->get_owner();
+ }
+ }
+
+ if (exists) {
+ continue;
+ }
+
+ }
+
+
+ int src_id;
+
+ if (node_map.has(p_node)) {
+ src_id=node_map[p_node];
+ } else {
+ if (nodepath_map.has(p_node)) {
+ src_id=FLAG_ID_IS_PATH|nodepath_map[p_node];
+ } else {
+ int sidx=nodepath_map.size();
+ nodepath_map[p_node]=sidx;
+ src_id=FLAG_ID_IS_PATH|sidx;
+ }
+ }
+
+
+
+ int target_id;
+
+ if (node_map.has(n)) {
+ target_id=node_map[n];
+ } else {
+ if (nodepath_map.has(n)) {
+ target_id=FLAG_ID_IS_PATH|nodepath_map[n];
+ } else {
+ int sidx=nodepath_map.size();
+ nodepath_map[n]=sidx;
+ target_id=FLAG_ID_IS_PATH|sidx;
+ }
+ }
+
ConnectionData cd;
- cd.from=node_map[p_node];
- cd.to=node_map[n];
+ cd.from=src_id;
+ cd.to=target_id;
cd.method=_nm_get_string(c.method,name_map);
cd.signal=_nm_get_string(c.signal,name_map);
cd.flags=c.flags;
@@ -392,7 +816,7 @@ Error PackedScene::_parse_connections(Node *p_owner,Node *p_node, Map<StringName
for(int i=0;i<p_node->get_child_count();i++) {
Node *c=p_node->get_child(i);
- Error err = _parse_connections(p_owner,c,name_map,variant_map,node_map);
+ Error err = _parse_connections(p_owner,c,name_map,variant_map,node_map,nodepath_map);
if (err)
return err;
}
@@ -401,7 +825,7 @@ Error PackedScene::_parse_connections(Node *p_owner,Node *p_node, Map<StringName
}
-Error PackedScene::pack(Node *p_scene) {
+Error SceneState::pack(Node *p_scene) {
ERR_FAIL_NULL_V( p_scene, ERR_INVALID_PARAMETER );
@@ -412,14 +836,27 @@ Error PackedScene::pack(Node *p_scene) {
Map<StringName,int> name_map;
HashMap<Variant,int,VariantHasher> variant_map;
Map<Node*,int> node_map;
+ Map<Node*,int> nodepath_map;
+
+ //if using scene inheritance, pack the scene it inherits from
+ if (scene->get_scene_inherited_state().is_valid()) {
+ String path = scene->get_scene_inherited_state()->get_path();
+ Ref<PackedScene> instance = ResourceLoader::load(path);
+ if (instance.is_valid()) {
+
+ base_scene_idx=_vm_get_variant(instance,variant_map);
+ }
+ }
+ //instanced, only direct sub-scnes are supported of course
+
- Error err = _parse_node(scene,scene,-1,name_map,variant_map,node_map);
+ Error err = _parse_node(scene,scene,-1,name_map,variant_map,node_map,nodepath_map);
if (err) {
clear();
ERR_FAIL_V(err);
}
- err = _parse_connections(scene,scene,name_map,variant_map,node_map);
+ err = _parse_connections(scene,scene,name_map,variant_map,node_map,nodepath_map);
if (err) {
clear();
ERR_FAIL_V(err);
@@ -440,19 +877,177 @@ Error PackedScene::pack(Node *p_scene) {
variants[idx]=*K;
}
+ node_paths.resize(nodepath_map.size());
+ for(Map<Node*,int>::Element *E=nodepath_map.front();E;E=E->next()) {
+
+ node_paths[E->get()]=scene->get_path_to(E->key());
+ }
+
+
return OK;
}
-void PackedScene::clear() {
+void SceneState::set_path(const String &p_path) {
+
+ path=p_path;
+}
+
+String SceneState::get_path() const{
+
+ return path;
+}
+
+void SceneState::clear() {
names.clear();
variants.clear();
nodes.clear();
connections.clear();
+ node_path_cache.clear();
+ node_paths.clear();
+ editable_instances.clear();
+ base_scene_idx=-1;
}
-void PackedScene::_set_bundled_scene(const Dictionary& d) {
+Ref<SceneState> SceneState::_get_base_scene_state() const {
+
+ if (base_scene_idx>=0) {
+
+ Ref<PackedScene> ps = variants[base_scene_idx];
+ if (ps.is_valid()) {
+ return ps->get_state();
+ }
+ }
+
+ return Ref<SceneState>();
+}
+
+int SceneState::find_node_by_path(const NodePath& p_node) const {
+
+ if (!node_path_cache.has(p_node)) {
+ if (_get_base_scene_state().is_valid()) {
+ int idx = _get_base_scene_state()->find_node_by_path(p_node);
+ if (idx>=0) {
+ if (!base_scene_node_remap.has(idx)) {
+ int ridx = nodes.size() + base_scene_node_remap.size();
+ base_scene_node_remap[ridx]=idx;
+ }
+
+ return base_scene_node_remap[idx];
+ }
+ }
+ return -1;
+ }
+
+ int nid = node_path_cache[p_node];
+
+ if (_get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) {
+ //for nodes that _do_ exist in current scene, still try to look for
+ //the node in the instanced scene, as a property may be missing
+ //from the local one
+ int idx = _get_base_scene_state()->find_node_by_path(p_node);
+ base_scene_node_remap[nid]=idx;
+
+ }
+
+ return nid;
+}
+Variant SceneState::get_property_value(int p_node, const StringName& p_property, bool &found) const {
+
+ found=false;
+
+ ERR_FAIL_COND_V(p_node<0,Variant());
+
+ if (p_node<nodes.size()) {
+ //find in built-in nodes
+ int pc = nodes[p_node].properties.size();
+ const StringName* namep = names.ptr();
+
+ const NodeData::Property *p=nodes[p_node].properties.ptr();
+ for(int i=0;i<pc;i++) {
+ if (p_property==namep[p[i].name]) {
+ found=true;
+ return variants[p[i].value];
+ }
+ }
+ }
+
+ //property not found, try on instance
+
+ if (base_scene_node_remap.has(p_node)) {
+ return _get_base_scene_state()->get_property_value(base_scene_node_remap[p_node],p_property,found);
+ }
+
+ return Variant();
+}
+
+bool SceneState::is_node_in_group(int p_node,const StringName& p_group) const {
+
+ ERR_FAIL_COND_V(p_node<0,false);
+
+ if (p_node<nodes.size()) {
+ const StringName* namep = names.ptr();
+ for(int i=0;i<nodes[p_node].groups.size();i++) {
+ if (namep[nodes[p_node].groups[i]]==p_group)
+ return true;
+ }
+ }
+
+ if (base_scene_node_remap.has(p_node)) {
+ return _get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node],p_group);
+ }
+
+ return false;
+}
+
+bool SceneState::disable_placeholders=false;
+
+void SceneState::set_disable_placeholders(bool p_disable) {
+
+ disable_placeholders=p_disable;
+}
+
+bool SceneState::is_connection(int p_node,const StringName& p_signal,int p_to_node,const StringName& p_to_method) const {
+
+ ERR_FAIL_COND_V(p_node<0,false);
+ ERR_FAIL_COND_V(p_to_node<0,false);
+
+ if (p_node<nodes.size() && p_to_node<nodes.size()) {
+
+ int signal_idx=-1;
+ int method_idx=-1;
+ for(int i=0;i<names.size();i++) {
+ if (names[i]==p_signal) {
+ signal_idx=i;
+ } else if (names[i]==p_to_method) {
+ method_idx=i;
+ }
+ }
+
+ if (signal_idx>=0 && method_idx>=0) {
+ //signal and method strings are stored..
+
+ for(int i=0;i<connections.size();i++) {
+
+ if (connections[i].from==p_node && connections[i].to==p_to_node && connections[i].signal==signal_idx && connections[i].method==method_idx) {
+
+ return true;
+ }
+ }
+ }
+ }
+
+ if (base_scene_node_remap.has(p_node) && base_scene_node_remap.has(p_to_node)) {
+ return _get_base_scene_state()->is_connection(base_scene_node_remap[p_node],p_signal,base_scene_node_remap[p_to_node],p_to_method);
+ }
+
+ return false;
+
+}
+
+
+void SceneState::set_bundled_scene(const Dictionary& d) {
ERR_FAIL_COND( !d.has("names"));
@@ -463,6 +1058,15 @@ void PackedScene::_set_bundled_scene(const Dictionary& d) {
ERR_FAIL_COND( !d.has("conns"));
// ERR_FAIL_COND( !d.has("path"));
+ int version=1;
+ if (d.has("version"))
+ version=d["version"];
+
+ if (version>PACK_VERSION) {
+ ERR_EXPLAIN("Save format version too new!");
+ ERR_FAIL();
+ }
+
DVector<String> snames = d["names"];
if (snames.size()) {
@@ -540,11 +1144,34 @@ void PackedScene::_set_bundled_scene(const Dictionary& d) {
}
+ Array np;
+ if (d.has("node_paths")) {
+ np=d["node_paths"];
+ }
+ node_paths.resize(np.size());
+ for(int i=0;i<np.size();i++) {
+ node_paths[i]=np[i];
+ }
+
+ Array ei;
+ if (d.has("editable_instances")) {
+ ei=d["editable_instances"];
+ }
+
+ if (d.has("base_scene")) {
+ base_scene_idx=d["base_scene"];
+ }
+
+ editable_instances.resize(ei.size());
+ for(int i=0;i<editable_instances.size();i++) {
+ editable_instances[i]=ei[i];
+ }
+
// path=d["path"];
}
-Dictionary PackedScene::_get_bundled_scene() const {
+Dictionary SceneState::get_bundled_scene() const {
DVector<String> rnames;
rnames.resize(names.size());
@@ -605,7 +1232,25 @@ Dictionary PackedScene::_get_bundled_scene() const {
}
d["conns"]=rconns;
- d["version"]=1;
+
+ Array rnode_paths;
+ rnode_paths.resize(node_paths.size());
+ for(int i=0;i<node_paths.size();i++) {
+ rnode_paths[i]=node_paths[i];
+ }
+ d["node_paths"]=rnode_paths;
+
+ Array reditable_instances;
+ reditable_instances.resize(editable_instances.size());
+ for(int i=0;i<editable_instances.size();i++) {
+ reditable_instances[i]=editable_instances[i];
+ }
+ d["editable_instances"]=reditable_instances;
+ if (base_scene_idx>=0) {
+ d["base_scene"]=base_scene_idx;
+ }
+
+ d["version"]=PACK_VERSION;
// d["path"]=path;
@@ -614,10 +1259,264 @@ Dictionary PackedScene::_get_bundled_scene() const {
}
+int SceneState::get_node_count() const {
+
+ return nodes.size();
+}
+
+StringName SceneState::get_node_type(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),StringName());
+ if (nodes[p_idx].type==TYPE_INSTANCED)
+ return StringName();
+ return names[nodes[p_idx].type];
+}
+
+StringName SceneState::get_node_name(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),StringName());
+ return names[nodes[p_idx].name];
+}
+
+Ref<PackedScene> SceneState::get_node_instance(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),Ref<PackedScene>());
+ if (nodes[p_idx].instance>=0) {
+ return variants[nodes[p_idx].instance];
+ } else if (nodes[p_idx].parent<=0 || nodes[p_idx].parent==NO_PARENT_SAVED) {
+
+ if (base_scene_idx>=0) {
+ return variants[base_scene_idx];
+ }
+ }
+
+ return Ref<PackedScene>();
+
+
+}
+Vector<StringName> SceneState::get_node_groups(int p_idx) const{
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),Vector<StringName>());
+ Vector<StringName> groups;
+ for(int i=0;i<nodes[p_idx].groups.size();i++) {
+ groups.push_back(names[nodes[p_idx].groups[i]]);
+ }
+ return groups;
+}
+
+
+NodePath SceneState::get_node_path(int p_idx,bool p_for_parent) const {
+
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),NodePath());
+
+ if (nodes[p_idx].parent<0 || nodes[p_idx].parent==NO_PARENT_SAVED) {
+ if (p_for_parent) {
+ return NodePath();
+ } else {
+ return NodePath(".");
+ }
+ }
+
+ Vector<StringName> sub_path;
+ NodePath base_path;
+ int nidx=p_idx;
+ while(true) {
+ if (nodes[nidx].parent==NO_PARENT_SAVED || nodes[nidx].parent<0) {
+
+ sub_path.insert(0,".");
+ break;
+ }
+
+ if (!p_for_parent || p_idx!=nidx) {
+ sub_path.insert(0,names[nodes[nidx].name]);
+ }
+
+ if (nodes[nidx].parent&FLAG_ID_IS_PATH) {
+ base_path=node_paths[nodes[nidx].parent&FLAG_MASK];
+ break;
+ } else {
+ nidx=nodes[nidx].parent&FLAG_MASK;
+ }
+ }
+
+ for(int i=0;i<base_path.get_name_count();i++) {
+ StringName sn = base_path.get_name(i);
+ sub_path.insert(0,base_path.get_name(i));
+ }
+
+ if (sub_path.empty()) {
+ return NodePath(".");
+ }
+
+ return NodePath(sub_path,false);
+
+}
+
+int SceneState::get_node_property_count(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),-1);
+ return nodes[p_idx].properties.size();
+
+}
+StringName SceneState::get_node_property_name(int p_idx,int p_prop) const{
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),StringName());
+ ERR_FAIL_INDEX_V(p_prop,nodes[p_idx].properties.size(),StringName());
+ return names[nodes[p_idx].properties[p_prop].name];
+
+}
+Variant SceneState::get_node_property_value(int p_idx,int p_prop) const{
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),Variant());
+ ERR_FAIL_INDEX_V(p_prop,nodes[p_idx].properties.size(),Variant());
+
+ return variants[nodes[p_idx].properties[p_prop].value];
+}
+
+
+NodePath SceneState::get_node_owner_path(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),NodePath());
+ if (nodes[p_idx].owner<0 || nodes[p_idx].owner==NO_PARENT_SAVED)
+ return NodePath(); //root likely
+ if (nodes[p_idx].owner&FLAG_ID_IS_PATH) {
+ return node_paths[nodes[p_idx].owner&FLAG_MASK];
+ } else {
+ return get_node_path(nodes[p_idx].owner&FLAG_MASK);
+ }
+}
+
+int SceneState::get_connection_count() const {
+
+ return connections.size();
+}
+NodePath SceneState::get_connection_source(int p_idx) const{
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),NodePath());
+ if (connections[p_idx].from&FLAG_ID_IS_PATH) {
+ return node_paths[connections[p_idx].from&FLAG_MASK];
+ } else {
+ return get_node_path(connections[p_idx].from&FLAG_MASK);
+ }
+
+}
+
+StringName SceneState::get_connection_signal(int p_idx) const{
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),StringName());
+ return names[connections[p_idx].signal];
+
+}
+NodePath SceneState::get_connection_target(int p_idx) const{
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),NodePath());
+ if (connections[p_idx].to&FLAG_ID_IS_PATH) {
+ return node_paths[connections[p_idx].to&FLAG_MASK];
+ } else {
+ return get_node_path(connections[p_idx].to&FLAG_MASK);
+ }
+
+}
+StringName SceneState::get_connection_method(int p_idx) const{
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),StringName());
+ return names[connections[p_idx].method];
+
+}
+int SceneState::get_connection_flags(int p_idx) const{
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),-1);
+ return connections[p_idx].flags;
+}
+
+Array SceneState::get_connection_binds(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),-1);
+ Array binds;
+ for(int i=0;i<connections[p_idx].binds.size();i++) {
+ binds.push_back(variants[connections[p_idx].binds[i]]);
+ }
+ return binds;
+}
+
+Vector<NodePath> SceneState::get_editable_instances() const {
+ return editable_instances;
+}
+
+
+SceneState::SceneState() {
+
+ base_scene_idx=-1;
+}
+
+
+////////////////
+
+
+
+void PackedScene::_set_bundled_scene(const Dictionary& d) {
+
+ state->set_bundled_scene(d);
+}
+
+Dictionary PackedScene::_get_bundled_scene() const {
+
+ return state->get_bundled_scene();
+}
+
+
+Error PackedScene::pack(Node *p_scene) {
+
+ return state->pack(p_scene);
+}
+
+void PackedScene::clear() {
+
+ state->clear();
+}
+
+bool PackedScene::can_instance() const {
+
+ return state->can_instance();
+}
+
+Node *PackedScene::instance(bool p_gen_edit_state) const {
+
+#ifndef TOOLS_ENABLED
+ if (p_gen_edit_state) {
+ ERR_EXPLAIN("Edit state is only for editors, does not work without tools compiled");
+ ERR_FAIL_COND_V(p_gen_edit_state,NULL);
+ }
+#endif
+
+ Node *s = state->instance(p_gen_edit_state);
+ if (!s)
+ return NULL;
+
+ if (p_gen_edit_state) {
+ s->set_scene_instance_state(state);
+ }
+
+ if (get_path()!="" && get_path().find("::")==-1)
+ s->set_filename(get_path());
+
+
+ s->notification(Node::NOTIFICATION_INSTANCED);
+
+ return s;
+}
+
+Ref<SceneState> PackedScene::get_state() {
+
+ return state;
+}
+
+void PackedScene::set_path(const String& p_path,bool p_take_over) {
+
+ state->set_path(p_path);
+ Resource::set_path(p_path,p_take_over);
+}
+
void PackedScene::_bind_methods() {
ObjectTypeDB::bind_method(_MD("pack","path:Node"),&PackedScene::pack);
- ObjectTypeDB::bind_method(_MD("instance:Node"),&PackedScene::instance,DEFVAL(false));
+ ObjectTypeDB::bind_method(_MD("instance:Node","gen_edit_state"),&PackedScene::instance,DEFVAL(false));
ObjectTypeDB::bind_method(_MD("can_instance"),&PackedScene::can_instance);
ObjectTypeDB::bind_method(_MD("_set_bundled_scene"),&PackedScene::_set_bundled_scene);
ObjectTypeDB::bind_method(_MD("_get_bundled_scene"),&PackedScene::_get_bundled_scene);
@@ -628,5 +1527,6 @@ void PackedScene::_bind_methods() {
PackedScene::PackedScene() {
+ state = Ref<SceneState>( memnew( SceneState ));
}
diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h
index 0546addd3e..3956d2abe4 100644
--- a/scene/resources/packed_scene.h
+++ b/scene/resources/packed_scene.h
@@ -32,26 +32,27 @@
#include "resource.h"
#include "scene/main/node.h"
-//changes:
-//1-make the InstanceState a reference inside the resource that can be shared
-//2-make the instance "editable" with a flag, save here and load here, no need for property
-//3-properly save modifications in sub-scene
-//4-add scene inheritance
-//5-chain of instance states in editor? (to check what was modified)
-//6-saving will be hell
+class SceneState : public Reference {
-class PackedScene : public Resource {
-
- OBJ_TYPE( PackedScene, Resource );
- RES_BASE_EXTENSION("scn");
+ OBJ_TYPE( SceneState, Reference );
Vector<StringName> names;
Vector<Variant> variants;
+ Vector<NodePath> node_paths;
+ Vector<NodePath> editable_instances;
+ mutable HashMap<NodePath,int> node_path_cache;
+ mutable Map<int,int> base_scene_node_remap;
- //missing - instances
- //missing groups
- //missing - owner
- //missing - override names and values
+ int base_scene_idx;
+
+ enum {
+ FLAG_ID_IS_PATH=(1<<30),
+ FLAG_INSTANCE_IS_PLACEHOLDER=(1<<30),
+ FLAG_MASK=(1<<24)-1,
+ NO_PARENT_SAVED=0x7FFFFFFF,
+ TYPE_INSTANCED=0x7FFFFFFF,
+
+ };
struct NodeData {
@@ -68,9 +69,15 @@ class PackedScene : public Resource {
};
Vector<Property> properties;
- Vector<int> groups;
+ Vector<int> groups;
+
};
+ struct PackState {
+ Ref<SceneState> state;
+ int node;
+ PackState() { node=-1; }
+ };
Vector<NodeData> nodes;
@@ -86,16 +93,78 @@ class PackedScene : public Resource {
Vector<ConnectionData> connections;
- Error _parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map);
- Error _parse_connections(Node *p_owner,Node *p_node, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map);
+ Error _parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,Map<Node*,int> &nodepath_map);
+ Error _parse_connections(Node *p_owner,Node *p_node, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,Map<Node*,int> &nodepath_map);
+
+ String path;
+
+ _FORCE_INLINE_ Ref<SceneState> _get_base_scene_state() const;
+
+ static bool disable_placeholders;
+public:
+
+ static void set_disable_placeholders(bool p_disable);
+
+ int find_node_by_path(const NodePath& p_node) const;
+ Variant get_property_value(int p_node,const StringName& p_property,bool &found) const;
+ bool is_node_in_group(int p_node,const StringName& p_group) const;
+ bool is_connection(int p_node,const StringName& p_signal,int p_to_node,const StringName& p_to_method) const;
+
+
+ void set_bundled_scene(const Dictionary& p_dictionary);
+ Dictionary get_bundled_scene() const;
+
+ Error pack(Node *p_scene);
+
+ void set_path(const String &p_path);
+ String get_path() const;
+
+ void clear();
+
+ bool can_instance() const;
+ Node *instance(bool p_gen_edit_state=false) const;
+
+
+ //build-unbuild API
+
+ int get_node_count() const;
+ StringName get_node_type(int p_idx) const;
+ StringName get_node_name(int p_idx) const;
+ NodePath get_node_path(int p_idx,bool p_for_parent=false) const;
+ NodePath get_node_owner_path(int p_idx) const;
+ Ref<PackedScene> get_node_instance(int p_idx) const;
+ Vector<StringName> get_node_groups(int p_idx) const;
+
+ int get_node_property_count(int p_idx) const;
+ StringName get_node_property_name(int p_idx,int p_prop) const;
+ Variant get_node_property_value(int p_idx,int p_prop) const;
+
+ int get_connection_count() const;
+ NodePath get_connection_source(int p_idx) const;
+ StringName get_connection_signal(int p_idx) const;
+ NodePath get_connection_target(int p_idx) const;
+ StringName get_connection_method(int p_idx) const;
+ int get_connection_flags(int p_idx) const;
+ Array get_connection_binds(int p_idx) const;
+
+ Vector<NodePath> get_editable_instances() const;
+
+ SceneState();
+};
+
+class PackedScene : public Resource {
+
+ OBJ_TYPE(PackedScene, Resource );
+ RES_BASE_EXTENSION("scn");
+
+ Ref<SceneState> state;
void _set_bundled_scene(const Dictionary& p_scene);
Dictionary _get_bundled_scene() const;
protected:
-
static void _bind_methods();
public:
@@ -107,7 +176,12 @@ public:
bool can_instance() const;
Node *instance(bool p_gen_edit_state=false) const;
+ virtual void set_path(const String& p_path,bool p_take_over=false);
+
+ Ref<SceneState> get_state();
+
PackedScene();
+
};
#endif // SCENE_PRELOADER_H
diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp
new file mode 100644
index 0000000000..8403c06ad1
--- /dev/null
+++ b/scene/resources/scene_format_text.cpp
@@ -0,0 +1,792 @@
+#include "scene_format_text.h"
+
+#include "globals.h"
+#include "version.h"
+#include "os/dir_access.h"
+
+#define FORMAT_VERSION 1
+
+void ResourceFormatSaverTextInstance::write_property(const String& p_name,const Variant& p_property,bool *r_ok) {
+
+ if (r_ok)
+ *r_ok=false;
+
+ if (p_name!=String()) {
+ f->store_string(p_name+" = ");
+ }
+
+ switch( p_property.get_type() ) {
+
+ case Variant::NIL: {
+ f->store_string("null");
+ } break;
+ case Variant::BOOL: {
+
+ f->store_string(p_property.operator bool() ? "true":"false" );
+ } break;
+ case Variant::INT: {
+
+ f->store_string( itos(p_property.operator int()) );
+ } break;
+ case Variant::REAL: {
+
+ f->store_string( rtoss(p_property.operator real_t()) );
+ } break;
+ case Variant::STRING: {
+
+ String str=p_property;
+
+ str="\""+str.c_escape()+"\"";
+ f->store_string( str );
+ } break;
+ case Variant::VECTOR2: {
+
+ Vector2 v = p_property;
+ f->store_string("Vector2( "+rtoss(v.x) +", "+rtoss(v.y)+" )" );
+ } break;
+ case Variant::RECT2: {
+
+ Rect2 aabb = p_property;
+ f->store_string("Rect2( "+rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y)+" )" );
+
+ } break;
+ case Variant::VECTOR3: {
+
+ Vector3 v = p_property;
+ f->store_string("Vector3( "+rtoss(v.x) +", "+rtoss(v.y)+", "+rtoss(v.z)+" )");
+ } break;
+ case Variant::PLANE: {
+
+ Plane p = p_property;
+ f->store_string("Plane( "+rtoss(p.normal.x) +", "+rtoss(p.normal.y)+", "+rtoss(p.normal.z)+", "+rtoss(p.d)+" )" );
+
+ } break;
+ case Variant::_AABB: {
+
+ AABB aabb = p_property;
+ f->store_string("AABB( "+rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.pos.z) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y) +", "+rtoss(aabb.size.z)+" )" );
+
+ } break;
+ case Variant::QUAT: {
+
+ Quat quat = p_property;
+ f->store_string("Quat( "+rtoss(quat.x)+", "+rtoss(quat.y)+", "+rtoss(quat.z)+", "+rtoss(quat.w)+" )");
+
+ } break;
+ case Variant::MATRIX32: {
+
+ String s="Matrix32( ";
+ Matrix32 m3 = p_property;
+ for (int i=0;i<3;i++) {
+ for (int j=0;j<2;j++) {
+
+ if (i!=0 || j!=0)
+ s+=", ";
+ s+=rtoss( m3.elements[i][j] );
+ }
+ }
+
+ f->store_string(s+" )");
+
+ } break;
+ case Variant::MATRIX3: {
+
+ String s="Matrix3( ";
+ Matrix3 m3 = p_property;
+ for (int i=0;i<3;i++) {
+ for (int j=0;j<3;j++) {
+
+ if (i!=0 || j!=0)
+ s+=", ";
+ s+=rtoss( m3.elements[i][j] );
+ }
+ }
+
+ f->store_string(s+" )");
+
+ } break;
+ case Variant::TRANSFORM: {
+
+ String s="Transform( ";
+ Transform t = p_property;
+ Matrix3 &m3 = t.basis;
+ for (int i=0;i<3;i++) {
+ for (int j=0;j<3;j++) {
+
+ if (i!=0 || j!=0)
+ s+=", ";
+ s+=rtoss( m3.elements[i][j] );
+ }
+ }
+
+ s=s+", "+rtoss(t.origin.x) +", "+rtoss(t.origin.y)+", "+rtoss(t.origin.z);
+
+ f->store_string(s+" )");
+ } break;
+
+ // misc types
+ case Variant::COLOR: {
+
+ Color c = p_property;
+ f->store_string("Color( "+rtoss(c.r) +", "+rtoss(c.g)+", "+rtoss(c.b)+", "+rtoss(c.a)+" )");
+
+ } break;
+ case Variant::IMAGE: {
+
+
+ Image img=p_property;
+
+ if (img.empty()) {
+ f->store_string("RawImage()");
+ break;
+ }
+
+ String imgstr="RawImage( ";
+ imgstr+=itos(img.get_width());
+ imgstr+=", "+itos(img.get_height());
+ imgstr+=", "+itos(img.get_mipmaps());
+ imgstr+=", ";
+
+ switch(img.get_format()) {
+
+ case Image::FORMAT_GRAYSCALE: imgstr+="GRAYSCALE"; break;
+ case Image::FORMAT_INTENSITY: imgstr+="INTENSITY"; break;
+ case Image::FORMAT_GRAYSCALE_ALPHA: imgstr+="GRAYSCALE_ALPHA"; break;
+ case Image::FORMAT_RGB: imgstr+="RGB"; break;
+ case Image::FORMAT_RGBA: imgstr+="RGBA"; break;
+ case Image::FORMAT_INDEXED : imgstr+="INDEXED"; break;
+ case Image::FORMAT_INDEXED_ALPHA: imgstr+="INDEXED_ALPHA"; break;
+ case Image::FORMAT_BC1: imgstr+="BC1"; break;
+ case Image::FORMAT_BC2: imgstr+="BC2"; break;
+ case Image::FORMAT_BC3: imgstr+="BC3"; break;
+ case Image::FORMAT_BC4: imgstr+="BC4"; break;
+ case Image::FORMAT_BC5: imgstr+="BC5"; break;
+ case Image::FORMAT_PVRTC2: imgstr+="PVRTC2"; break;
+ case Image::FORMAT_PVRTC2_ALPHA: imgstr+="PVRTC2_ALPHA"; break;
+ case Image::FORMAT_PVRTC4: imgstr+="PVRTC4"; break;
+ case Image::FORMAT_PVRTC4_ALPHA: imgstr+="PVRTC4_ALPHA"; break;
+ case Image::FORMAT_ETC: imgstr+="ETC"; break;
+ case Image::FORMAT_ATC: imgstr+="ATC"; break;
+ case Image::FORMAT_ATC_ALPHA_EXPLICIT: imgstr+="ATC_ALPHA_EXPLICIT"; break;
+ case Image::FORMAT_ATC_ALPHA_INTERPOLATED: imgstr+="ATC_ALPHA_INTERPOLATED"; break;
+ case Image::FORMAT_CUSTOM: imgstr+="CUSTOM"; break;
+ default: {}
+ }
+
+
+ String s;
+
+ DVector<uint8_t> data = img.get_data();
+ int len = data.size();
+ DVector<uint8_t>::Read r = data.read();
+ const uint8_t *ptr=r.ptr();;
+ for (int i=0;i<len;i++) {
+
+ uint8_t byte = ptr[i];
+ const char hex[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+ char str[3]={ hex[byte>>4], hex[byte&0xF], 0};
+ s+=str;
+ }
+
+ imgstr+=", ";
+ f->store_string(imgstr);
+ f->store_string(s);
+ f->store_string(" )");
+ } break;
+ case Variant::NODE_PATH: {
+
+ String str=p_property;
+
+ str="NodePath(\""+str.c_escape()+"\")";
+ f->store_string(str);
+
+ } break;
+
+ case Variant::OBJECT: {
+
+ RES res = p_property;
+ if (res.is_null()) {
+ f->store_string("null");
+ if (r_ok)
+ *r_ok=true;
+
+ break; // don't save it
+ }
+
+ if (external_resources.has(res)) {
+
+ f->store_string("ExtResource( "+itos(external_resources[res]+1)+" )");
+ } else {
+
+ if (internal_resources.has(res)) {
+ f->store_string("SubResource( "+itos(internal_resources[res])+" )");
+ } else if (res->get_path().length() && res->get_path().find("::")==-1) {
+
+ //external resource
+ String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path();
+ f->store_string("Resource( \""+path+"\" )");
+ } else {
+ f->store_string("null");
+ ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?");
+ ERR_BREAK(true);
+ //internal resource
+ }
+ }
+
+ } break;
+ case Variant::INPUT_EVENT: {
+
+ f->store_string("InputEvent()"); //will be added later
+ } break;
+ case Variant::DICTIONARY: {
+
+ Dictionary dict = p_property;
+
+ List<Variant> keys;
+ dict.get_key_list(&keys);
+ keys.sort();
+
+ f->store_string("{ ");
+ for(List<Variant>::Element *E=keys.front();E;E=E->next()) {
+
+ //if (!_check_type(dict[E->get()]))
+ // continue;
+ bool ok;
+ write_property("",E->get(),&ok);
+ ERR_CONTINUE(!ok);
+
+ f->store_string(":");
+ write_property("",dict[E->get()],&ok);
+ if (!ok)
+ write_property("",Variant()); //at least make the file consistent..
+ if (E->next())
+ f->store_string(", ");
+ }
+
+
+ f->store_string(" }");
+
+
+ } break;
+ case Variant::ARRAY: {
+
+ f->store_string("[ ");
+ Array array = p_property;
+ int len=array.size();
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ write_property("",array[i]);
+
+
+ }
+ f->store_string(" ]");
+
+ } break;
+
+ case Variant::RAW_ARRAY: {
+
+ f->store_string("RawArray( ");
+ String s;
+ DVector<uint8_t> data = p_property;
+ int len = data.size();
+ DVector<uint8_t>::Read r = data.read();
+ const uint8_t *ptr=r.ptr();;
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ uint8_t byte = ptr[i];
+ const char hex[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+ char str[3]={ hex[byte>>4], hex[byte&0xF], 0};
+ f->store_string(str);
+
+ }
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::INT_ARRAY: {
+
+ f->store_string("IntArray( ");
+ DVector<int> data = p_property;
+ int len = data.size();
+ DVector<int>::Read r = data.read();
+ const int *ptr=r.ptr();;
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+
+ f->store_string(itos(ptr[i]));
+ }
+
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::REAL_ARRAY: {
+
+ f->store_string("FloatArray( ");
+ DVector<real_t> data = p_property;
+ int len = data.size();
+ DVector<real_t>::Read r = data.read();
+ const real_t *ptr=r.ptr();;
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ f->store_string(rtoss(ptr[i]));
+ }
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::STRING_ARRAY: {
+
+ f->store_string("StringArray( ");
+ DVector<String> data = p_property;
+ int len = data.size();
+ DVector<String>::Read r = data.read();
+ const String *ptr=r.ptr();;
+ String s;
+ //write_string("\n");
+
+
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ String str=ptr[i];
+ f->store_string(""+str.c_escape()+"\"");
+ }
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::VECTOR2_ARRAY: {
+
+ f->store_string("Vector2Array( ");
+ DVector<Vector2> data = p_property;
+ int len = data.size();
+ DVector<Vector2>::Read r = data.read();
+ const Vector2 *ptr=r.ptr();;
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ f->store_string(rtoss(ptr[i].x)+", "+rtoss(ptr[i].y) );
+ }
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::VECTOR3_ARRAY: {
+
+ f->store_string("Vector3Array( ");
+ DVector<Vector3> data = p_property;
+ int len = data.size();
+ DVector<Vector3>::Read r = data.read();
+ const Vector3 *ptr=r.ptr();;
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ f->store_string(rtoss(ptr[i].x)+", "+rtoss(ptr[i].y)+", "+rtoss(ptr[i].z) );
+ }
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::COLOR_ARRAY: {
+
+ f->store_string("ColorArray( ");
+
+ DVector<Color> data = p_property;
+ int len = data.size();
+ DVector<Color>::Read r = data.read();
+ const Color *ptr=r.ptr();;
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+
+ f->store_string(rtoss(ptr[i].r)+", "+rtoss(ptr[i].g)+", "+rtoss(ptr[i].b)+", "+rtoss(ptr[i].a) );
+
+ }
+ f->store_string(" )");
+
+ } break;
+ default: {}
+
+ }
+
+ if (r_ok)
+ *r_ok=true;
+
+}
+
+
+void ResourceFormatSaverTextInstance::_find_resources(const Variant& p_variant,bool p_main) {
+
+
+ switch(p_variant.get_type()) {
+ case Variant::OBJECT: {
+
+
+ RES res = p_variant.operator RefPtr();
+
+ if (res.is_null() || external_resources.has(res))
+ return;
+
+ if (!p_main && (!bundle_resources ) && res->get_path().length() && res->get_path().find("::") == -1 ) {
+ int index = external_resources.size();
+ external_resources[res]=index;
+ return;
+ }
+
+ if (resource_set.has(res))
+ return;
+
+ List<PropertyInfo> property_list;
+
+ res->get_property_list( &property_list );
+ property_list.sort();
+
+ List<PropertyInfo>::Element *I=property_list.front();
+
+ while(I) {
+
+ PropertyInfo pi=I->get();
+
+ if (pi.usage&PROPERTY_USAGE_STORAGE || (bundle_resources && pi.usage&PROPERTY_USAGE_BUNDLE)) {
+
+ Variant v=res->get(I->get().name);
+ _find_resources(v);
+ }
+
+ I=I->next();
+ }
+
+ resource_set.insert( res ); //saved after, so the childs it needs are available when loaded
+ saved_resources.push_back(res);
+
+ } break;
+ case Variant::ARRAY: {
+
+ Array varray=p_variant;
+ int len=varray.size();
+ for(int i=0;i<len;i++) {
+
+ Variant v=varray.get(i);
+ _find_resources(v);
+ }
+
+ } break;
+ case Variant::DICTIONARY: {
+
+ Dictionary d=p_variant;
+ List<Variant> keys;
+ d.get_key_list(&keys);
+ for(List<Variant>::Element *E=keys.front();E;E=E->next()) {
+
+ Variant v = d[E->get()];
+ _find_resources(v);
+ }
+ } break;
+ default: {}
+ }
+
+}
+
+
+
+Error ResourceFormatSaverTextInstance::save(const String &p_path,const RES& p_resource,uint32_t p_flags) {
+
+ if (p_path.ends_with(".tscn")) {
+ packed_scene=p_resource;
+ }
+
+ Error err;
+ f = FileAccess::open(p_path, FileAccess::WRITE,&err);
+ ERR_FAIL_COND_V( err, ERR_CANT_OPEN );
+ FileAccessRef _fref(f);
+
+ local_path = Globals::get_singleton()->localize_path(p_path);
+
+ relative_paths=p_flags&ResourceSaver::FLAG_RELATIVE_PATHS;
+ skip_editor=p_flags&ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES;
+ bundle_resources=p_flags&ResourceSaver::FLAG_BUNDLE_RESOURCES;
+ takeover_paths=p_flags&ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
+ if (!p_path.begins_with("res://")) {
+ takeover_paths=false;
+ }
+
+ // save resources
+ _find_resources(p_resource,true);
+
+ if (packed_scene.is_valid()) {
+ //add instances to external resources if saving a packed scene
+ for(int i=0;i<packed_scene->get_state()->get_node_count();i++) {
+ Ref<PackedScene> instance=packed_scene->get_state()->get_node_instance(i);
+ if (instance.is_valid() && !external_resources.has(instance)) {
+ int index = external_resources.size();
+ external_resources[instance]=index;
+ }
+ }
+ }
+
+
+ ERR_FAIL_COND_V(err!=OK,err);
+
+ {
+ String title=packed_scene.is_valid()?"[gd_scene ":"[gd_resource ";
+ if (packed_scene.is_null())
+ title+="type=\""+p_resource->get_type()+"\" ";
+ int load_steps=saved_resources.size()+external_resources.size();
+ //if (packed_scene.is_valid()) {
+ // load_steps+=packed_scene->get_node_count();
+ //}
+ //no, better to not use load steps from nodes, no point to that
+
+ if (load_steps>1) {
+ title+="load_steps="+itos(load_steps)+" ";
+ }
+ title+="format="+itos(FORMAT_VERSION)+"";
+ //title+="engine_version=\""+itos(VERSION_MAJOR)+"."+itos(VERSION_MINOR)+"\"";
+
+ f->store_string(title);
+ f->store_line("]\n"); //one empty line
+ }
+
+
+ for(Map<RES,int>::Element *E=external_resources.front();E;E=E->next()) {
+
+ String p = E->key()->get_path();
+
+ f->store_string("[ext_resource path=\""+p+"\" type=\""+E->key()->get_save_type()+"\" id="+itos(E->get()+1)+"]\n"); //bundled
+ }
+
+ if (external_resources.size())
+ f->store_line(String()); //separate
+
+ Set<int> used_indices;
+
+ for(List<RES>::Element *E=saved_resources.front();E;E=E->next()) {
+
+ RES res = E->get();
+ if (E->next() && (res->get_path()=="" || res->get_path().find("::") != -1 )) {
+
+ if (res->get_subindex()!=0) {
+ if (used_indices.has(res->get_subindex())) {
+ res->set_subindex(0); //repeated
+ } else {
+ used_indices.insert(res->get_subindex());
+ }
+ }
+ }
+ }
+
+ for(List<RES>::Element *E=saved_resources.front();E;E=E->next()) {
+
+ RES res = E->get();
+ ERR_CONTINUE(!resource_set.has(res));
+ bool main = (E->next()==NULL);
+
+ if (main && packed_scene.is_valid())
+ break; //save as a scene
+
+ if (main) {
+ f->store_line("[resource]\n");
+ } else {
+ String line="[sub_resource ";
+ if (res->get_subindex()==0) {
+ int new_subindex=1;
+ if (used_indices.size()) {
+ new_subindex=used_indices.back()->get()+1;
+ }
+
+ res->set_subindex(new_subindex);
+ used_indices.insert(new_subindex);
+ }
+
+ int idx = res->get_subindex();
+ line+="type=\""+res->get_type()+"\" id="+itos(idx);
+ f->store_line(line+"]\n");
+ if (takeover_paths) {
+ res->set_path(p_path+"::"+itos(idx),true);
+ }
+
+ internal_resources[res]=idx;
+
+ }
+
+
+ List<PropertyInfo> property_list;
+ res->get_property_list(&property_list);
+// property_list.sort();
+ for(List<PropertyInfo>::Element *PE = property_list.front();PE;PE=PE->next()) {
+
+
+ if (skip_editor && PE->get().name.begins_with("__editor"))
+ continue;
+
+ if (PE->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && PE->get().usage&PROPERTY_USAGE_BUNDLE)) {
+
+ String name = PE->get().name;
+ Variant value = res->get(name);
+
+
+ if ((PE->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO && value.is_zero())||(PE->get().usage&PROPERTY_USAGE_STORE_IF_NONONE && value.is_one()) )
+ continue;
+
+ if (PE->get().type==Variant::OBJECT && value.is_zero())
+ continue;
+
+ write_property(name,value);
+ f->store_string("\n");
+ }
+
+
+ }
+
+ f->store_string("\n");
+
+ }
+
+ if (packed_scene.is_valid()) {
+ //if this is a scene, save nodes and connections!
+ Ref<SceneState> state = packed_scene->get_state();
+ for(int i=0;i<state->get_node_count();i++) {
+
+ StringName type = state->get_node_type(i);
+ StringName name = state->get_node_name(i);
+ NodePath path = state->get_node_path(i,true);
+ NodePath owner = state->get_node_owner_path(i);
+ Ref<PackedScene> instance = state->get_node_instance(i);
+ Vector<StringName> groups = state->get_node_groups(i);
+
+ String header="[node";
+ header+=" name=\""+String(name)+"\"";
+ if (type!=StringName()) {
+ header+=" type=\""+String(type)+"\"";
+ }
+ if (path!=NodePath()) {
+ header+=" parent=\""+String(path.simplified())+"\"";
+ }
+ if (owner!=NodePath() && owner!=NodePath(".")) {
+ header+=" owner=\""+String(owner.simplified())+"\"";
+ }
+
+ if (groups.size()) {
+ String sgroups=" groups=[ ";
+ for(int j=0;j<groups.size();j++) {
+ if (j>0)
+ sgroups+=", ";
+ sgroups+="\""+groups[i].operator String().c_escape()+"\"";
+ }
+ sgroups+=" ]";
+ header+=sgroups;
+ }
+
+ f->store_string(header);
+
+ if (instance.is_valid()) {
+ f->store_string(" instance=");
+ write_property("",instance);
+ }
+
+ f->store_line("]\n");
+
+ for(int j=0;j<state->get_node_property_count(i);j++) {
+
+ write_property(state->get_node_property_name(i,j),state->get_node_property_value(i,j));
+ f->store_line(String());
+
+ }
+
+ if (state->get_node_property_count(i)) {
+ //add space
+ f->store_line(String());
+ }
+
+ }
+
+ for(int i=0;i<state->get_connection_count();i++) {
+
+ String connstr="[connection";
+ connstr+=" signal=\""+String(state->get_connection_signal(i))+"\"";
+ connstr+=" from=\""+String(state->get_connection_source(i).simplified())+"\"";
+ connstr+=" to=\""+String(state->get_connection_target(i).simplified())+"\"";
+ connstr+=" method=\""+String(state->get_connection_method(i))+"\"";
+ int flags = state->get_connection_flags(i);
+ if (flags!=Object::CONNECT_PERSIST) {
+ connstr+=" flags="+itos(flags);
+ }
+
+ Array binds=state->get_connection_binds(i);
+ f->store_string(connstr);
+ if (binds.size()) {
+ f->store_string(" binds=");
+ write_property("",binds);
+ }
+
+ f->store_line("]\n");
+ }
+
+ f->store_line(String());
+
+ Vector<NodePath> editable_instances = state->get_editable_instances();
+ for(int i=0;i<editable_instances.size();i++) {
+ f->store_line("[editable path=\""+editable_instances[i].operator String()+"\"]");
+ }
+ }
+
+ if (f->get_error()!=OK && f->get_error()!=ERR_FILE_EOF) {
+ f->close();
+ return ERR_CANT_CREATE;
+ }
+
+ f->close();
+ //memdelete(f);
+
+ return OK;
+}
+
+
+
+Error ResourceFormatSaverText::save(const String &p_path,const RES& p_resource,uint32_t p_flags) {
+
+ if (p_path.ends_with(".sct") && p_resource->get_type()!="PackedScene") {
+ return ERR_FILE_UNRECOGNIZED;
+ }
+
+ ResourceFormatSaverTextInstance saver;
+ return saver.save(p_path,p_resource,p_flags);
+
+}
+
+bool ResourceFormatSaverText::recognize(const RES& p_resource) const {
+
+
+ return true; // all recognized!
+}
+void ResourceFormatSaverText::get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const {
+
+ p_extensions->push_back("tres"); //text resource
+ if (p_resource->get_type()=="PackedScene")
+ p_extensions->push_back("tscn"); //text scene
+
+}
+
+ResourceFormatSaverText* ResourceFormatSaverText::singleton=NULL;
+ResourceFormatSaverText::ResourceFormatSaverText() {
+ singleton=this;
+}
diff --git a/scene/resources/scene_format_text.h b/scene/resources/scene_format_text.h
new file mode 100644
index 0000000000..576a78d183
--- /dev/null
+++ b/scene/resources/scene_format_text.h
@@ -0,0 +1,46 @@
+#ifndef SCENE_FORMAT_TEXT_H
+#define SCENE_FORMAT_TEXT_H
+
+#include "io/resource_loader.h"
+#include "io/resource_saver.h"
+#include "os/file_access.h"
+#include "scene/resources/packed_scene.h"
+
+class ResourceFormatSaverTextInstance {
+
+ String local_path;
+
+ Ref<PackedScene> packed_scene;
+
+ bool takeover_paths;
+ bool relative_paths;
+ bool bundle_resources;
+ bool skip_editor;
+ FileAccess *f;
+ Set<RES> resource_set;
+ List<RES> saved_resources;
+ Map<RES,int> external_resources;
+ Map<RES,int> internal_resources;
+
+ void _find_resources(const Variant& p_variant,bool p_main=false);
+ void write_property(const String& p_name,const Variant& p_property,bool *r_ok=NULL);
+
+public:
+
+ Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0);
+
+
+};
+
+class ResourceFormatSaverText : public ResourceFormatSaver {
+public:
+ static ResourceFormatSaverText* singleton;
+ virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0);
+ virtual bool recognize(const RES& p_resource) const;
+ virtual void get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const;
+
+ ResourceFormatSaverText();
+};
+
+
+#endif // SCENE_FORMAT_TEXT_H
diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp
index 4f5755bd3d..9137503e1b 100644
--- a/tools/editor/editor_node.cpp
+++ b/tools/editor/editor_node.cpp
@@ -1108,6 +1108,11 @@ void EditorNode::_dialog_action(String p_file) {
push_item(res.operator->() );
} break;
+ case FILE_NEW_INHERITED_SCENE: {
+
+
+ load_scene(p_file,false,true);
+ } break;
case FILE_OPEN_SCENE: {
@@ -1931,6 +1936,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
} break;
+ case FILE_NEW_INHERITED_SCENE:
case FILE_OPEN_SCENE: {
@@ -1951,7 +1957,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
if (scene) {
file->set_current_path(scene->get_filename());
};
- file->set_title("Open Scene");
+ file->set_title(p_option==FILE_OPEN_SCENE?"Open Scene":"Open Base Scene");
file->popup_centered_ratio();
} break;
@@ -3312,7 +3318,7 @@ void EditorNode::fix_dependencies(const String& p_for_file) {
dependency_fixer->edit(p_for_file);
}
-Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps) {
+Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps,bool p_set_inherited) {
if (!is_inside_tree()) {
defer_load_scene = p_scene;
@@ -3442,6 +3448,13 @@ Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps) {
}
*/
+ if (p_set_inherited) {
+ Ref<SceneState> state = sdata->get_state();
+ state->set_path(lpath);
+ new_scene->set_scene_inherited_state(state);
+ }
+
+
set_edited_scene(new_scene);
_get_scene_metadata();
/*
@@ -4404,6 +4417,7 @@ void EditorNode::_scene_tab_changed(int p_tab) {
EditorNode::EditorNode() {
EditorHelp::generate_doc(); //before any editor classes are crated
+ SceneState::set_disable_placeholders(true);
InputDefault *id = Input::get_singleton()->cast_to<InputDefault>();
@@ -4794,6 +4808,7 @@ EditorNode::EditorNode() {
file_menu->set_tooltip("Operations with scene files.");
p=file_menu->get_popup();
p->add_item("New Scene",FILE_NEW_SCENE);
+ p->add_item("New Inherited Scene..",FILE_NEW_INHERITED_SCENE);
p->add_item("Open Scene..",FILE_OPEN_SCENE,KEY_MASK_CMD+KEY_O);
p->add_separator();
p->add_item("Save Scene",FILE_SAVE_SCENE,KEY_MASK_CMD+KEY_S);
diff --git a/tools/editor/editor_node.h b/tools/editor/editor_node.h
index 53e16d9c58..d52e08191d 100644
--- a/tools/editor/editor_node.h
+++ b/tools/editor/editor_node.h
@@ -107,6 +107,7 @@ class EditorNode : public Node {
enum MenuOptions {
FILE_NEW_SCENE,
+ FILE_NEW_INHERITED_SCENE,
FILE_OPEN_SCENE,
FILE_SAVE_SCENE,
FILE_SAVE_AS_SCENE,
@@ -565,7 +566,7 @@ public:
void fix_dependencies(const String& p_for_file);
void clear_scene() { _cleanup_scene(); }
- Error load_scene(const String& p_scene,bool p_ignore_broken_deps=false);
+ Error load_scene(const String& p_scene, bool p_ignore_broken_deps=false, bool p_set_inherited=false);
Error load_resource(const String& p_scene);
bool is_scene_open(const String& p_path);
diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp
index b513e32c13..1a07f30e4e 100644
--- a/tools/editor/property_editor.cpp
+++ b/tools/editor/property_editor.cpp
@@ -42,6 +42,8 @@
#include "multi_node_edit.h"
#include "array_property_edit.h"
#include "editor_help.h"
+#include "scene/resources/packed_scene.h"
+
void CustomPropertyEditor::_notification(int p_what) {
@@ -1655,25 +1657,101 @@ CustomPropertyEditor::CustomPropertyEditor() {
menu->connect("item_pressed",this,"_menu_option");
}
+bool PropertyEditor::_might_be_in_instance() {
-
-Node *PropertyEditor::get_instanced_node() {
-
- //this sucks badly
if (!obj)
return NULL;
Node *node = obj->cast_to<Node>();
+
+ Node* edited_scene =EditorNode::get_singleton()->get_edited_scene();
+
+ bool might_be=false;
+
+ while(node) {
+
+ if (node->get_scene_instance_state().is_valid()) {
+ might_be=true;
+ break;
+ }
+ if (node==edited_scene) {
+ if (node->get_scene_inherited_state().is_valid()) {
+ might_be=true;
+ break;
+ }
+ might_be=false;
+ break;
+ }
+ node=node->get_owner();
+ }
+
+ return might_be;
+
+}
+
+bool PropertyEditor::_get_instanced_node_original_property(const StringName& p_prop,Variant& value) {
+
+ Node *node = obj->cast_to<Node>();
+
if (!node)
- return NULL;
+ return false;
- if (node->get_filename()=="")
- return NULL;
+ Node *orig=node;
+
+ Node* edited_scene =EditorNode::get_singleton()->get_edited_scene();
+
+ bool found=false;
- if (!node->get_owner())
- return NULL; //scene root i guess
+// print_line("for prop - "+String(p_prop));
- return node;
+ while(node) {
+
+ Ref<SceneState> ss;
+
+ if (node==edited_scene) {
+ ss=node->get_scene_inherited_state();
+ } else {
+ ss=node->get_scene_instance_state();
+ }
+// print_line("at - "+String(edited_scene->get_path_to(node)));
+
+ if (ss.is_valid()) {
+ NodePath np = node->get_path_to(orig);
+ int node_idx = ss->find_node_by_path(np);
+// print_line("\t valid, nodeidx "+itos(node_idx));
+ if (node_idx>=0) {
+ bool lfound=false;
+ Variant lvar;
+ lvar=ss->get_property_value(node_idx,p_prop,lfound);
+ if (lfound) {
+ found=true;
+ value=lvar;
+// print_line("\t found value "+String(value));
+ }
+ }
+ }
+ if (node==edited_scene) {
+ //just in case
+ break;
+ }
+ node=node->get_owner();
+
+ }
+
+ return found;
+}
+
+bool PropertyEditor::_is_property_different(const Variant& p_current, const Variant& p_orig,int p_usage) {
+
+ if (p_orig.get_type()==Variant::NIL) {
+ //special cases
+ if (p_current.is_zero() && p_usage&PROPERTY_USAGE_STORE_IF_NONZERO)
+ return false;
+ if (p_current.is_one() && p_usage&PROPERTY_USAGE_STORE_IF_NONONE)
+ return false;
+ }
+
+ return bool(Variant::evaluate(Variant::OP_NOT_EQUAL,p_current,p_orig));
}
TreeItem *PropertyEditor::find_item(TreeItem *p_item,const String& p_name) {
@@ -1910,6 +1988,8 @@ void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String& p
}
+
+
void PropertyEditor::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
@@ -1950,12 +2030,16 @@ void PropertyEditor::_notification(int p_what) {
if (!item)
continue;
- if (get_instanced_node()) {
+ if (_might_be_in_instance()) {
+
+
+ Variant vorig;
+ Dictionary d=item->get_metadata(0);
+ int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0;
- Dictionary d = get_instanced_node()->get_instance_state();
- if (d.has(*k)) {
+
+ if (_get_instanced_node_original_property(*k,vorig) || usage) {
Variant v = obj->get(*k);
- Variant vorig = d[*k];
int found=-1;
for(int i=0;i<item->get_button_count(1);i++) {
@@ -1966,7 +2050,7 @@ void PropertyEditor::_notification(int p_what) {
}
}
- bool changed = ! (v==vorig);
+ bool changed = _is_property_different(v,vorig,usage);
if ((found!=-1)!=changed) {
@@ -2049,12 +2133,14 @@ void PropertyEditor::_refresh_item(TreeItem *p_item) {
if (name!=String()) {
- if (get_instanced_node()) {
+ if (_might_be_in_instance()) {
+
+ Variant vorig;
+ Dictionary d=p_item->get_metadata(0);
+ int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0;
- Dictionary d = get_instanced_node()->get_instance_state();
- if (d.has(name)) {
+ if (_get_instanced_node_original_property(name,vorig) || usage) {
Variant v = obj->get(name);
- Variant vorig = d[name];
int found=-1;
for(int i=0;i<p_item->get_button_count(1);i++) {
@@ -2065,7 +2151,7 @@ void PropertyEditor::_refresh_item(TreeItem *p_item) {
}
}
- bool changed = ! (v==vorig);
+ bool changed = _is_property_different(v,vorig,usage);
if ((found!=-1)!=changed) {
@@ -2326,7 +2412,8 @@ void PropertyEditor::update_tree() {
d["type"]=(int)p.type;
d["hint"]=(int)p.hint;
d["hint_text"]=p.hint_string;
-
+ d["usage"]=(int)p.usage;
+
item->set_metadata( 0, d );
item->set_metadata( 1, p.name );
@@ -2777,14 +2864,17 @@ void PropertyEditor::update_tree() {
}
}
- if (get_instanced_node()) {
+ if (_might_be_in_instance()) {
- Dictionary d = get_instanced_node()->get_instance_state();
- if (d.has(p.name)) {
+ Variant vorig;
+ Dictionary d=item->get_metadata(0);
+ int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0;
+ if (_get_instanced_node_original_property(p.name,vorig) || usage) {
Variant v = obj->get(p.name);
- Variant vorig = d[p.name];
- if (! (v==vorig)) {
+
+ if (_is_property_different(v,vorig,usage)) {
+ //print_line("FOR "+String(p.name)+" RELOAD WITH: "+String(v)+"("+Variant::get_type_name(v.get_type())+")=="+String(vorig)+"("+Variant::get_type_name(vorig.get_type())+")");
item->add_button(1,get_icon("Reload","EditorIcons"),3);
}
}
@@ -3081,17 +3171,18 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) {
call_deferred("_set_range_def",ti,prop,ti->get_range(p_column)+1.0);
} else if (p_button==3) {
- if (!get_instanced_node())
+ if (!_might_be_in_instance())
return;
if (!d.has("name"))
return;
String prop=d["name"];
- Dictionary d2 = get_instanced_node()->get_instance_state();
- if (d2.has(prop)) {
+ Variant vorig;
+
+ if (_get_instanced_node_original_property(prop,vorig)) {
- _edit_set(prop,d2[prop]);
+ _edit_set(prop,vorig);
}
} else {
diff --git a/tools/editor/property_editor.h b/tools/editor/property_editor.h
index 36ecc794ed..9686bf0bd7 100644
--- a/tools/editor/property_editor.h
+++ b/tools/editor/property_editor.h
@@ -121,6 +121,7 @@ class CustomPropertyEditor : public Popup {
void show_value_editors(int p_amount);
void config_value_editors(int p_amount, int p_columns,int p_label_w,const List<String>& p_strings);
void config_action_buttons(const List<String>& p_strings);
+
protected:
void _notification(int p_what);
@@ -184,13 +185,17 @@ class PropertyEditor : public Control {
virtual void _changed_callback(Object *p_changed,const char * p_what);
virtual void _changed_callbacks(Object *p_changed,const String& p_callback);
+
void _edit_button(Object *p_item, int p_column, int p_button);
void _node_removed(Node *p_node);
void _edit_set(const String& p_name, const Variant& p_value);
void _draw_flags(Object *ti,const Rect2& p_rect);
- Node *get_instanced_node();
+ bool _might_be_in_instance();
+ bool _get_instanced_node_original_property(const StringName& p_prop,Variant& value);
+ bool _is_property_different(const Variant& p_current, const Variant& p_orig,int p_usage=0);
+
void _refresh_item(TreeItem *p_item);
void _set_range_def(Object *p_item, String prop, float p_frame);
diff --git a/tools/editor/reparent_dialog.cpp b/tools/editor/reparent_dialog.cpp
index 6d0c5b867e..78ba47d54b 100644
--- a/tools/editor/reparent_dialog.cpp
+++ b/tools/editor/reparent_dialog.cpp
@@ -103,6 +103,7 @@ ReparentDialog::ReparentDialog() {
add_child(node_only);
node_only->hide();
+ tree->set_show_enabled_subscene(true);
//vbc->add_margin_child("Options:",node_only);;
diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp
index 276f2dea33..920ab599e9 100644
--- a/tools/editor/scene_tree_dock.cpp
+++ b/tools/editor/scene_tree_dock.cpp
@@ -36,8 +36,8 @@
#include "script_editor_debugger.h"
#include "tools/editor/plugins/script_editor_plugin.h"
#include "multi_node_edit.h"
-void SceneTreeDock::_unhandled_key_input(InputEvent p_event) {
+void SceneTreeDock::_unhandled_key_input(InputEvent p_event) {
uint32_t sc = p_event.key.get_scancode_with_modifiers();
if (!p_event.key.pressed || p_event.key.echo)
@@ -71,7 +71,7 @@ Node* SceneTreeDock::instance(const String& p_file) {
Node*instanced_scene=NULL;
Ref<PackedScene> sdata = ResourceLoader::load(p_file);
if (sdata.is_valid())
- instanced_scene=sdata->instance();
+ instanced_scene=sdata->instance(true);
if (!instanced_scene) {
@@ -96,7 +96,7 @@ Node* SceneTreeDock::instance(const String& p_file) {
}
}
- instanced_scene->generate_instance_state();
+ //instanced_scene->generate_instance_state();
instanced_scene->set_filename( Globals::get_singleton()->localize_path(p_file) );
editor_data->get_undo_redo().create_action("Instance Scene");
@@ -158,8 +158,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
case TOOL_NEW: {
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
create_dialog->popup_centered_ratio();
} break;
case TOOL_INSTANCE: {
@@ -176,8 +176,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
List<String> extensions;
@@ -202,8 +202,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!current)
break;
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
connect_dialog->popup_centered_ratio();
connect_dialog->set_node(current);
@@ -213,8 +213,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
Node *current = scene_tree->get_selected();
if (!current)
break;
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
groups_editor->set_current(current);
groups_editor->popup_centered_ratio();
} break;
@@ -224,8 +224,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!selected)
break;
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
Ref<Script> existing = selected->get_script();
if (existing.is_valid())
@@ -573,9 +573,9 @@ Node *SceneTreeDock::_duplicate(Node *p_node, Map<Node*,Node*> &duplimap) {
Ref<PackedScene> sd = ResourceLoader::load( p_node->get_filename() );
ERR_FAIL_COND_V(!sd.is_valid(),NULL);
- node = sd->instance();
+ node = sd->instance(true);
ERR_FAIL_COND_V(!node,NULL);
- node->generate_instance_state();
+ //node->generate_instance_state();
} else {
Object *obj = ObjectTypeDB::instance(p_node->get_type());
ERR_FAIL_COND_V(!obj,NULL);
@@ -874,6 +874,16 @@ bool SceneTreeDock::_validate_no_foreign() {
return false;
}
+
+ if (edited_scene->get_scene_instance_state().is_valid() && edited_scene->get_scene_instance_state()->find_node_by_path(edited_scene->get_path_to(E->get()))>=0) {
+
+ accept->get_ok()->set_text("Makes Sense!");
+ accept->set_text("Can't operate on nodes the current scene inherits from!");
+ accept->popup_centered_minsize();
+ return false;
+
+ }
+
}
return true;
@@ -1078,14 +1088,15 @@ void SceneTreeDock::_delete_confirm() {
void SceneTreeDock::_update_tool_buttons() {
Node *sel = scene_tree->get_selected();
- bool disable = !sel || (sel!=edited_scene && sel->get_owner()!=edited_scene);
+ bool disable = !sel || (sel!=edited_scene && sel->get_owner()!=edited_scene) || (edited_scene->get_scene_instance_state().is_valid() && edited_scene->get_scene_instance_state()->find_node_by_path(edited_scene->get_path_to(sel))>=0);
bool disable_root = disable || sel->get_parent()==scene_root;
+ bool disable_edit = !sel;
- tool_buttons[TOOL_INSTANCE]->set_disabled(disable);
+ tool_buttons[TOOL_INSTANCE]->set_disabled(disable_edit);
tool_buttons[TOOL_REPLACE]->set_disabled(disable);
- tool_buttons[TOOL_CONNECT]->set_disabled(disable);
- tool_buttons[TOOL_GROUP]->set_disabled(disable);
- tool_buttons[TOOL_SCRIPT]->set_disabled(disable);
+ tool_buttons[TOOL_CONNECT]->set_disabled(disable_edit);
+ tool_buttons[TOOL_GROUP]->set_disabled(disable_edit);
+ tool_buttons[TOOL_SCRIPT]->set_disabled(disable_edit);
tool_buttons[TOOL_MOVE_UP]->set_disabled(disable_root);
tool_buttons[TOOL_MOVE_DOWN]->set_disabled(disable_root);
tool_buttons[TOOL_DUPLICATE]->set_disabled(disable_root);
diff --git a/tools/editor/scene_tree_editor.cpp b/tools/editor/scene_tree_editor.cpp
index fd841028a2..b746af7f0e 100644
--- a/tools/editor/scene_tree_editor.cpp
+++ b/tools/editor/scene_tree_editor.cpp
@@ -34,6 +34,8 @@
#include "scene/main/viewport.h"
#include "tools/editor/plugins/canvas_item_editor_plugin.h"
+#include "scene/resources/packed_scene.h"
+
Node *SceneTreeEditor::get_scene_node() {
ERR_FAIL_COND_V(!is_inside_tree(),NULL);
@@ -57,22 +59,58 @@ void SceneTreeEditor::_subscene_option(int p_idx) {
switch(p_idx) {
- case SCENE_MENU_SHOW_CHILDREN: {
+ case SCENE_MENU_EDITABLE_CHILDREN: {
- if (node->has_meta("__editor_show_subtree")) {
- instance_menu->set_item_checked(0,true);
- node->set_meta("__editor_show_subtree",Variant());
- _update_tree();
- } else {
- node->set_meta("__editor_show_subtree",true);
- _update_tree();
+ bool editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(node);
+ editable = !editable;
+
+ //node->set_instance_children_editable(editable);
+ EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node,editable);
+ instance_menu->set_item_checked(0,editable);
+ if (editable) {
+ node->set_scene_instance_load_placeholder(false);
+ instance_menu->set_item_checked(1,false);
+ }
+
+ _update_tree();
+
+ } break;
+ case SCENE_MENU_USE_PLACEHOLDER: {
+
+ bool placeholder = node->get_scene_instance_load_placeholder();
+ placeholder = !placeholder;
+
+ //node->set_instance_children_editable(editable);
+ if (placeholder) {
+ EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node,false);
}
+ node->set_scene_instance_load_placeholder(placeholder);
+ instance_menu->set_item_checked(0,false);
+ instance_menu->set_item_checked(1,placeholder);
+
+ _update_tree();
} break;
case SCENE_MENU_OPEN: {
emit_signal("open",node->get_filename());
} break;
+ case SCENE_MENU_CLEAR_INHERITANCE: {
+ clear_inherit_confirm->popup_centered_minsize();
+ } break;
+ case SCENE_MENU_OPEN_INHERITED: {
+ if (node && node->get_scene_inherited_state().is_valid()) {
+ emit_signal("open",node->get_scene_inherited_state()->get_path());
+ }
+ } break;
+ case SCENE_MENU_CLEAR_INHERITANCE_CONFIRM: {
+ if (node && node->get_scene_inherited_state().is_valid()) {
+ node->set_scene_inherited_state(Ref<SceneState>());
+ update_tree();
+ EditorNode::get_singleton()->get_property_editor()->update_tree();
+ }
+
+ } break;
}
@@ -94,15 +132,33 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id)
Rect2 item_rect = tree->get_item_rect(item,0);
item_rect.pos.y-=tree->get_scroll().y;
item_rect.pos+=tree->get_global_pos();
- instance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y));
- instance_menu->set_size(Vector2(item_rect.size.x,0));
- if (n->has_meta("__editor_show_subtree"))
- instance_menu->set_item_checked(0,true);
- else
- instance_menu->set_item_checked(0,false);
- instance_menu->popup();
- instance_node=n->get_instance_ID();
+ if (n==get_scene_node()) {
+ inheritance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y));
+ inheritance_menu->set_size(Vector2(item_rect.size.x,0));
+ inheritance_menu->popup();
+ instance_node=n->get_instance_ID();
+
+ } else {
+ instance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y));
+ instance_menu->set_size(Vector2(item_rect.size.x,0));
+ if (EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(n))
+ instance_menu->set_item_checked(0,true);
+ else
+ instance_menu->set_item_checked(0,false);
+
+ if (n->get_owner()==get_scene_node()) {
+ instance_menu->set_item_checked(1,n->get_scene_instance_load_placeholder());
+ instance_menu->set_item_disabled(1,false);
+ } else {
+
+ instance_menu->set_item_checked(1,false);
+ instance_menu->set_item_disabled(1,true);
+ }
+
+ instance_menu->popup();
+ instance_node=n->get_instance_ID();
+ }
//emit_signal("open",n->get_filename());
} else if (p_id==BUTTON_SCRIPT) {
RefPtr script=n->get_script();
@@ -168,15 +224,17 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
bool part_of_subscene=false;
- if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node()) {
+ if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node()) {
- if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && p_node->get_owner()->get_owner()==get_scene_node() && p_node->get_owner()->has_meta("__editor_show_subtree")) {
+ if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && (get_scene_node()->is_editable_instance(p_node->get_owner()))) {
part_of_subscene=true;
//allow
} else {
return;
}
+ } else {
+ part_of_subscene = get_scene_node()->get_scene_inherited_state().is_valid() && get_scene_node()->get_scene_inherited_state()->find_node_by_path(get_scene_node()->get_path_to(p_node))>=0;
}
TreeItem *item = tree->create_item(p_parent);
@@ -199,6 +257,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
icon=get_icon( (has_icon(p_node->get_type(),"EditorIcons")?p_node->get_type():String("Object")),"EditorIcons");
item->set_icon(0, icon );
item->set_metadata( 0,p_node->get_path() );
+
if (part_of_subscene) {
//item->set_selectable(0,marked_selectable);
@@ -221,7 +280,10 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
}
}
- if (p_node!=get_scene_node() && p_node->get_filename()!="" && can_open_instance) {
+ if (p_node==get_scene_node() && p_node->get_scene_inherited_state().is_valid()) {
+ item->add_button(0,get_icon("InstanceOptions","EditorIcons"),BUTTON_SUBSCENE);
+ item->set_tooltip(0,"Inherits: "+p_node->get_scene_inherited_state()->get_path()+"\nType: "+p_node->get_type());
+ } else if (p_node!=get_scene_node() && p_node->get_filename()!="" && can_open_instance) {
item->add_button(0,get_icon("InstanceOptions","EditorIcons"),BUTTON_SUBSCENE);
item->set_tooltip(0,"Instance: "+p_node->get_filename()+"\nType: "+p_node->get_type());
@@ -487,8 +549,11 @@ void SceneTreeEditor::_notification(int p_what) {
get_tree()->connect("tree_changed",this,"_tree_changed");
get_tree()->connect("node_removed",this,"_node_removed");
- instance_menu->set_item_icon(2,get_icon("Load","EditorIcons"));
+ instance_menu->set_item_icon(3,get_icon("Load","EditorIcons"));
tree->connect("item_collapsed",this,"_cell_collapsed");
+ inheritance_menu->set_item_icon(2,get_icon("Load","EditorIcons"));
+ clear_inherit_confirm->connect("confirmed",this,"_subscene_option",varray(SCENE_MENU_CLEAR_INHERITANCE_CONFIRM));
+
// get_scene()->connect("tree_changed",this,"_tree_changed",Vector<Variant>(),CONNECT_DEFERRED);
// get_scene()->connect("node_removed",this,"_node_removed",Vector<Variant>(),CONNECT_DEFERRED);
@@ -499,6 +564,7 @@ void SceneTreeEditor::_notification(int p_what) {
get_tree()->disconnect("tree_changed",this,"_tree_changed");
get_tree()->disconnect("node_removed",this,"_node_removed");
tree->disconnect("item_collapsed",this,"_cell_collapsed");
+ clear_inherit_confirm->disconnect("confirmed",this,"_subscene_option");
_update_tree();
}
@@ -788,12 +854,27 @@ SceneTreeEditor::SceneTreeEditor(bool p_label,bool p_can_rename, bool p_can_open
blocked=0;
instance_menu = memnew( PopupMenu );
- instance_menu->add_check_item("Show Children",SCENE_MENU_SHOW_CHILDREN);
+ instance_menu->add_check_item("Editable Children",SCENE_MENU_EDITABLE_CHILDREN);
+ instance_menu->add_check_item("Load As Placeholder",SCENE_MENU_USE_PLACEHOLDER);
instance_menu->add_separator();
instance_menu->add_item("Open in Editor",SCENE_MENU_OPEN);
instance_menu->connect("item_pressed",this,"_subscene_option");
add_child(instance_menu);
+ inheritance_menu = memnew( PopupMenu );
+ inheritance_menu->add_item("Clear Inheritance",SCENE_MENU_CLEAR_INHERITANCE);
+ inheritance_menu->add_separator();
+ inheritance_menu->add_item("Open in Editor",SCENE_MENU_OPEN_INHERITED);
+ inheritance_menu->connect("item_pressed",this,"_subscene_option");
+
+ add_child(inheritance_menu);
+
+ clear_inherit_confirm = memnew( ConfirmationDialog );
+ clear_inherit_confirm->set_text("Clear Inheritance? (No Undo!)");
+ clear_inherit_confirm->get_ok()->set_text("Clear!");
+ add_child(clear_inherit_confirm);
+
+
}
diff --git a/tools/editor/scene_tree_editor.h b/tools/editor/scene_tree_editor.h
index b05a52a2da..50cca4e24b 100644
--- a/tools/editor/scene_tree_editor.h
+++ b/tools/editor/scene_tree_editor.h
@@ -52,16 +52,22 @@ class SceneTreeEditor : public Control {
};
enum {
- SCENE_MENU_SHOW_CHILDREN,
+ SCENE_MENU_EDITABLE_CHILDREN,
+ SCENE_MENU_USE_PLACEHOLDER,
SCENE_MENU_OPEN,
+ SCENE_MENU_CLEAR_INHERITANCE,
+ SCENE_MENU_OPEN_INHERITED,
+ SCENE_MENU_CLEAR_INHERITANCE_CONFIRM,
};
Tree *tree;
Node *selected;
PopupMenu *instance_menu;
+ PopupMenu *inheritance_menu;
ObjectID instance_node;
AcceptDialog *error;
+ ConfirmationDialog *clear_inherit_confirm;
int blocked;