Browse Source

Merge branch 'master' of https://github.com/okamstudio/godot

Conflicts:
	demos/2d/polygon_path_finder_demo/.fscache
Carl Olsson 10 years ago
parent
commit
573ae5dd92

+ 3 - 0
.gitignore

@@ -19,6 +19,9 @@ tools/editor/register_exporters.cpp
 tools/editor/doc_data_compressed.h
 tools/editor/doc_data_compressed.h
 tools/editor/editor_icons.cpp
 tools/editor/editor_icons.cpp
 -fpic
 -fpic
+.fscache
+make.bat
+log.txt
 
 
 # Android specific
 # Android specific
 platform/android/java/local.properties
 platform/android/java/local.properties

+ 339 - 1
bin/tests/test_string.cpp

@@ -487,7 +487,7 @@ struct test_27_data {
 
 
 bool test_27() {
 bool test_27() {
 
 
-	OS::get_singleton()->print("\n\nTest 26: begins_with\n");
+	OS::get_singleton()->print("\n\nTest 27: begins_with\n");
 	test_27_data tc[] = {
 	test_27_data tc[] = {
 		{"res://foobar", "res://", true},
 		{"res://foobar", "res://", true},
 		{"res", "res://", false},
 		{"res", "res://", false},
@@ -504,11 +504,348 @@ bool test_27() {
 		}
 		}
 		if (!state) {
 		if (!state) {
 			OS::get_singleton()->print("\n\t Failure on:\n\t\tstring: ", tc[i].data, "\n\t\tbegin: ", tc[i].begin, "\n\t\texpected: ", tc[i].expected ? "true" : "false", "\n");
 			OS::get_singleton()->print("\n\t Failure on:\n\t\tstring: ", tc[i].data, "\n\t\tbegin: ", tc[i].begin, "\n\t\texpected: ", tc[i].expected ? "true" : "false", "\n");
+			break;
 		}
 		}
 	};
 	};
 	return state;
 	return state;
 };
 };
 
 
+
+bool test_28() {
+
+	OS::get_singleton()->print("\n\nTest 28: sprintf\n");
+
+	bool success, state = true;
+	char output_format[] = "\tTest:\t%ls => %ls (%s)\n";
+	String format, output;
+	Array args;
+	
+	// %%
+	format = "fish %% frog";
+	args.clear();
+	output = format.sprintf(args);
+	success = (output == String("fish % frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	//////// INTS
+
+	// Int
+	format = "fish %d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish 5 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int left padded with zeroes.
+	format = "fish %05d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish 00005 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int left padded with spaces.
+	format = "fish %5d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish     5 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int right padded with spaces.
+	format = "fish %-5d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish 5     frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int with sign (positive).
+	format = "fish %+d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish +5 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Negative int.
+	format = "fish %d frog";
+	args.clear();
+	args.push_back(-5);
+	output = format.sprintf(args);
+	success = (output == String("fish -5 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Hex (lower)
+	format = "fish %x frog";
+	args.clear();
+	args.push_back(45);
+	output = format.sprintf(args);
+	success = (output == String("fish 2d frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Hex (upper)
+	format = "fish %X frog";
+	args.clear();
+	args.push_back(45);
+	output = format.sprintf(args);
+	success = (output == String("fish 2D frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Octal
+	format = "fish %o frog";
+	args.clear();
+	args.push_back(99);
+	output = format.sprintf(args);
+	success = (output == String("fish 143 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	////// REALS
+
+	// Real
+	format = "fish %f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 99.990000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real left-padded
+	format = "fish %11f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish   99.990000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real right-padded
+	format = "fish %-11f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 99.990000   frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real given int.
+	format = "fish %f frog";
+	args.clear();
+	args.push_back(99);
+	output = format.sprintf(args);
+	success = (output == String("fish 99.000000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real with sign (positive).
+	format = "fish %+f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish +99.990000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real with 1 decimals.
+	format = "fish %.1f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 100.0 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real with 12 decimals.
+	format = "fish %.12f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 99.990000000000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real with no decimals.
+	format = "fish %.f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 100 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	/////// Strings.
+
+	// String
+	format = "fish %s frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == String("fish cheese frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// String left-padded
+	format = "fish %10s frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == String("fish     cheese frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// String right-padded
+	format = "fish %-10s frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == String("fish cheese     frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	///// Characters
+
+	// Character as string.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back("A");
+	output = format.sprintf(args);
+	success = (output == String("fish A frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Character as int.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back(65);
+	output = format.sprintf(args);
+	success = (output == String("fish A frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	///// Dynamic width
+
+	// String dynamic width
+	format = "fish %*s frog";
+	args.clear();
+	args.push_back(10);
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == String("fish     cheese frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int dynamic width
+	format = "fish %*d frog";
+	args.clear();
+	args.push_back(10);
+	args.push_back(99);
+	output = format.sprintf(args);
+	success = (output == String("fish         99 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Float dynamic width
+	format = "fish %*.*f frog";
+	args.clear();
+	args.push_back(10);
+	args.push_back(3);
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish     99.990 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	///// Errors
+
+	// More formats than arguments.
+	format = "fish %s %s frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// More arguments than formats.
+	format = "fish %s frog";
+	args.clear();
+	args.push_back("hello");
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Incomplete format.
+	format = "fish %10";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Bad character in format string
+	format = "fish %&f frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Too many decimals.
+	format = "fish %2.2.2f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// * not a number
+	format = "fish %*f frog";
+	args.clear();
+	args.push_back("cheese");
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Character too long.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back("sc");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Character bad type.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back(Array());
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	return state;
+}
+
 typedef bool (*TestFunc)(void);
 typedef bool (*TestFunc)(void);
 
 
 TestFunc test_funcs[] = {
 TestFunc test_funcs[] = {
@@ -540,6 +877,7 @@ TestFunc test_funcs[] = {
 	test_25,
 	test_25,
 	test_26,
 	test_26,
 	test_27,
 	test_27,
+	test_28,
 	0
 	0
 	
 	
 };
 };

+ 34 - 9
core/io/http_client.cpp

@@ -273,7 +273,7 @@ Error HTTPClient::poll(){
 			while(true) {
 			while(true) {
 				uint8_t byte;
 				uint8_t byte;
 				int rec=0;
 				int rec=0;
-				Error err = connection->get_partial_data(&byte,1,rec);
+				Error err = _get_http_data(&byte,1,rec);
 				if (err!=OK) {
 				if (err!=OK) {
 					close();
 					close();
 					status=STATUS_CONNECTION_ERROR;
 					status=STATUS_CONNECTION_ERROR;
@@ -417,7 +417,7 @@ ByteArray HTTPClient::read_response_body_chunk() {
 				//reading len
 				//reading len
 				uint8_t b;
 				uint8_t b;
 				int rec=0;
 				int rec=0;
-				err = connection->get_partial_data(&b,1,rec);
+				err = _get_http_data(&b,1,rec);
 
 
 				if (rec==0)
 				if (rec==0)
 					break;
 					break;
@@ -471,7 +471,7 @@ ByteArray HTTPClient::read_response_body_chunk() {
 			} else {
 			} else {
 
 
 				int rec=0;
 				int rec=0;
-				err = connection->get_partial_data(&chunk[chunk.size()-chunk_left],chunk_left,rec);
+				err = _get_http_data(&chunk[chunk.size()-chunk_left],chunk_left,rec);
 				if (rec==0) {
 				if (rec==0) {
 					break;
 					break;
 				}
 				}
@@ -502,18 +502,23 @@ ByteArray HTTPClient::read_response_body_chunk() {
 		}
 		}
 
 
 	} else {
 	} else {
+
+		int to_read = MIN(body_left,read_chunk_size);
 		ByteArray ret;
 		ByteArray ret;
-		ret.resize(MAX(body_left,tmp_read.size()));
+		ret.resize(to_read);
 		ByteArray::Write w = ret.write();
 		ByteArray::Write w = ret.write();
 		int _offset = 0;
 		int _offset = 0;
-		while (body_left > 0) {
-			ByteArray::Write r = tmp_read.write();
+		while (to_read > 0) {
 			int rec=0;
 			int rec=0;
-			err = connection->get_partial_data(r.ptr(),MIN(body_left,tmp_read.size()),rec);
+			err = _get_http_data(w.ptr()+_offset,to_read,rec);
 			if (rec>0) {
 			if (rec>0) {
-				copymem(w.ptr()+_offset,r.ptr(),rec);
 				body_left-=rec;
 				body_left-=rec;
+				to_read-=rec;
 				_offset += rec;
 				_offset += rec;
+			} else {
+				if (to_read>0) //ended up reading less
+					ret.resize(_offset);
+				break;
 			}
 			}
 		}
 		}
 		if (body_left==0) {
 		if (body_left==0) {
@@ -557,6 +562,20 @@ bool HTTPClient::is_blocking_mode_enabled() const{
 	return blocking;
 	return blocking;
 }
 }
 
 
+Error HTTPClient::_get_http_data(uint8_t* p_buffer, int p_bytes,int &r_received) {
+
+	if (blocking) {
+
+		Error err = connection->get_data(p_buffer,p_bytes);
+		if (err==OK)
+			r_received=p_bytes;
+		else
+			r_received=0;
+		return err;
+	} else {
+		return connection->get_partial_data(p_buffer,p_bytes,r_received);
+	}
+}
 
 
 void HTTPClient::_bind_methods() {
 void HTTPClient::_bind_methods() {
 
 
@@ -574,6 +593,7 @@ void HTTPClient::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("get_response_headers_as_dictionary"),&HTTPClient::_get_response_headers_as_dictionary);
 	ObjectTypeDB::bind_method(_MD("get_response_headers_as_dictionary"),&HTTPClient::_get_response_headers_as_dictionary);
 	ObjectTypeDB::bind_method(_MD("get_response_body_length"),&HTTPClient::get_response_body_length);
 	ObjectTypeDB::bind_method(_MD("get_response_body_length"),&HTTPClient::get_response_body_length);
 	ObjectTypeDB::bind_method(_MD("read_response_body_chunk"),&HTTPClient::read_response_body_chunk);
 	ObjectTypeDB::bind_method(_MD("read_response_body_chunk"),&HTTPClient::read_response_body_chunk);
+	ObjectTypeDB::bind_method(_MD("set_read_chunk_size","bytes"),&HTTPClient::set_read_chunk_size);
 
 
 	ObjectTypeDB::bind_method(_MD("set_blocking_mode","enabled"),&HTTPClient::set_blocking_mode);
 	ObjectTypeDB::bind_method(_MD("set_blocking_mode","enabled"),&HTTPClient::set_blocking_mode);
 	ObjectTypeDB::bind_method(_MD("is_blocking_mode_enabled"),&HTTPClient::is_blocking_mode_enabled);
 	ObjectTypeDB::bind_method(_MD("is_blocking_mode_enabled"),&HTTPClient::is_blocking_mode_enabled);
@@ -664,6 +684,11 @@ void HTTPClient::_bind_methods() {
 
 
 }
 }
 
 
+void HTTPClient::set_read_chunk_size(int p_size) {
+	ERR_FAIL_COND(p_size<256 || p_size>(1<<24));
+	read_chunk_size=p_size;
+}
+
 HTTPClient::HTTPClient(){
 HTTPClient::HTTPClient(){
 
 
 	tcp_connection = StreamPeerTCP::create_ref();
 	tcp_connection = StreamPeerTCP::create_ref();
@@ -677,7 +702,7 @@ HTTPClient::HTTPClient(){
 	response_num=0;
 	response_num=0;
 	ssl=false;
 	ssl=false;
 	blocking=false;
 	blocking=false;
-	tmp_read.resize(4096);
+	read_chunk_size=4096;
 }
 }
 
 
 HTTPClient::~HTTPClient(){
 HTTPClient::~HTTPClient(){

+ 5 - 1
core/io/http_client.h

@@ -157,7 +157,10 @@ private:
 	static void _bind_methods();
 	static void _bind_methods();
 	StringArray _get_response_headers();
 	StringArray _get_response_headers();
 	Dictionary _get_response_headers_as_dictionary();
 	Dictionary _get_response_headers_as_dictionary();
-	ByteArray tmp_read;
+	int read_chunk_size;
+
+	Error _get_http_data(uint8_t* p_buffer, int p_bytes,int &r_received);
+
 public:
 public:
 
 
 
 
@@ -185,6 +188,7 @@ public:
 	void set_blocking_mode(bool p_enable); //useful mostly if running in a thread
 	void set_blocking_mode(bool p_enable); //useful mostly if running in a thread
 	bool is_blocking_mode_enabled() const;
 	bool is_blocking_mode_enabled() const;
 
 
+	void set_read_chunk_size(int p_size);
 
 
 	Error poll();
 	Error poll();
 
 

+ 1 - 1
core/resource.cpp

@@ -130,7 +130,7 @@ void ResourceImportMetadata::_bind_methods() {
 
 
 	ObjectTypeDB::bind_method(_MD("set_editor","name"),&ResourceImportMetadata::set_editor);
 	ObjectTypeDB::bind_method(_MD("set_editor","name"),&ResourceImportMetadata::set_editor);
 	ObjectTypeDB::bind_method(_MD("get_editor"),&ResourceImportMetadata::get_editor);
 	ObjectTypeDB::bind_method(_MD("get_editor"),&ResourceImportMetadata::get_editor);
-	ObjectTypeDB::bind_method(_MD("add_source","path","md5"),&ResourceImportMetadata::add_source);
+	ObjectTypeDB::bind_method(_MD("add_source","path","md5"),&ResourceImportMetadata::add_source, "");
 	ObjectTypeDB::bind_method(_MD("get_source_path","idx"),&ResourceImportMetadata::get_source_path);
 	ObjectTypeDB::bind_method(_MD("get_source_path","idx"),&ResourceImportMetadata::get_source_path);
 	ObjectTypeDB::bind_method(_MD("get_source_md5","idx"),&ResourceImportMetadata::get_source_md5);
 	ObjectTypeDB::bind_method(_MD("get_source_md5","idx"),&ResourceImportMetadata::get_source_md5);
 	ObjectTypeDB::bind_method(_MD("remove_source","idx"),&ResourceImportMetadata::remove_source);
 	ObjectTypeDB::bind_method(_MD("remove_source","idx"),&ResourceImportMetadata::remove_source);

+ 292 - 4
core/ustring.cpp

@@ -34,6 +34,7 @@
 #include "io/md5.h"
 #include "io/md5.h"
 #include "ucaps.h"
 #include "ucaps.h"
 #include "color.h"
 #include "color.h"
+#include "variant.h"
 #define MAX_DIGITS 6
 #define MAX_DIGITS 6
 #define UPPERCASE(m_c) (((m_c)>='a' && (m_c)<='z')?((m_c)-('a'-'A')):(m_c))
 #define UPPERCASE(m_c) (((m_c)>='a' && (m_c)<='z')?((m_c)-('a'-'A')):(m_c))
 #define LOWERCASE(m_c) (((m_c)>='A' && (m_c)<='Z')?((m_c)+('a'-'A')):(m_c))
 #define LOWERCASE(m_c) (((m_c)>='A' && (m_c)<='Z')?((m_c)+('a'-'A')):(m_c))
@@ -981,7 +982,7 @@ String String::num(double p_num,int p_decimals) {
 
 
 }
 }
 
 
-String String::num_int64(int64_t p_num) {
+String String::num_int64(int64_t p_num, int base, bool capitalize_hex) {
 
 
 	bool sign=p_num<0;
 	bool sign=p_num<0;
 	int64_t num=ABS(p_num);
 	int64_t num=ABS(p_num);
@@ -990,7 +991,7 @@ String String::num_int64(int64_t p_num) {
 
 
 	int chars=0;
 	int chars=0;
 	do {
 	do {
-		n/=10;
+		n/=base;
 		chars++;
 		chars++;
 	} while(n);
 	} while(n);
 
 
@@ -1002,8 +1003,15 @@ String String::num_int64(int64_t p_num) {
 	c[chars]=0;
 	c[chars]=0;
 	n=num;
 	n=num;
 	do {
 	do {
-		c[--chars]='0'+(n%10);
-		n/=10;
+		int mod = n%base;
+		if (mod >= 10) {
+			char a = (capitalize_hex ? 'A' : 'a');
+			c[--chars]=a+(mod - 10);
+		} else {
+			c[--chars]='0'+mod;
+		}
+
+		n/=base;
 	} while(n);
 	} while(n);
 
 
 	if (sign)
 	if (sign)
@@ -3518,4 +3526,284 @@ String rtoss(double p_val) {
 	return String::num_scientific(p_val);
 	return String::num_scientific(p_val);
 }
 }
 
 
+// Right-pad with a character.
+String String::rpad(int min_length, const String& character) const {
+	String s = *this;
+	int padding = min_length - s.length();
+	if (padding > 0) {
+		for (int i = 0; i < padding; i++) s = s + character;
+	}
+
+	return s;
+}
+// Left-pad with a character.
+String String::lpad(int min_length, const String& character) const {
+	String s = *this;
+	int padding = min_length - s.length();
+	if (padding > 0) {
+		for (int i = 0; i < padding; i++) s = character + s;
+	}
+
+	return s;
+}
+
+// sprintf is implemented in GDScript via:
+//   "fish %s pie" % "frog"
+//   "fish %s %d pie" % ["frog", 12]
+String String::sprintf(const Array& values) const {
+
+	String formatted;
+	CharType* self = (CharType*)c_str();
+	int num_items = values.size();
+	bool in_format = false;
+	int value_index = 0;
+	int min_chars;
+	int min_decimals;
+	bool in_decimals;
+	bool pad_with_zeroes;
+	bool left_justified;
+	bool show_sign;
+
+
+	for (; *self; self++) {
+		const CharType c = *self;
+
+		if (in_format) { // We have % - lets see what else we get.
+			switch (c) {
+				case '%': { // Replace %% with %
+					formatted += chr(c);
+					in_format = false;
+					break;
+				}
+				case 'd': // Integer (signed)
+				case 'o': // Octal
+				case 'x': // Hexadecimal (lowercase)
+				case 'X': { // Hexadecimal (uppercase)
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					if (!values[value_index].is_num()) {
+						ERR_EXPLAIN("a number is required");
+						ERR_FAIL_V("");
+					}
+					
+					int64_t value = values[value_index];
+					int base;
+					bool capitalize = false;
+					switch (c) {
+						case 'd': base = 10; break;
+						case 'o': base = 8; break;
+						case 'x': base = 16; break;
+						case 'X': base = 16; capitalize = true; break;
+					}
+					// Get basic number.
+					String str = String::num_int64(value, base, capitalize);
+
+					// Sign.
+					if (show_sign && value >= 0) {
+						str = str.insert(0, "+");
+					}
+
+					// Padding.
+					String pad_char = pad_with_zeroes ? String("0") : String(" ");
+					if (left_justified) {
+						str = str.rpad(min_chars, pad_char);
+					} else {
+						str = str.lpad(min_chars, pad_char);
+					}
+
+					formatted += str;
+					++value_index;
+					in_format = false;
+
+					break;
+				}
+				case 'f': { // Float
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					if (!values[value_index].is_num()) {
+						ERR_EXPLAIN("a number is required");
+						ERR_FAIL_V("");
+					}
+
+					double value = values[value_index];
+					String str = String::num(value, min_decimals);
+
+					// Pad decimals out.
+					str = str.pad_decimals(min_decimals);
+
+					// Show sign
+					if (show_sign && value >= 0) {
+						str = str.insert(0, "+");
+					}
+
+					// Padding
+					if (left_justified) {
+						str = str.rpad(min_chars);
+					} else {
+						str = str.lpad(min_chars);
+					}
+
+					formatted += str;
+					++value_index;
+					in_format = false;
+					
+					break;
+				}
+				case 's': { // String
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					String str = values[value_index];
+					// Padding.
+					if (left_justified) {
+						str = str.rpad(min_chars);
+					} else {
+						str = str.lpad(min_chars);
+					}
+
+					formatted += str;
+					++value_index;
+					in_format = false;
+					break;
+				}
+				case 'c': {
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					// Convert to character.
+					String str;
+					if (values[value_index].is_num()) {
+						int value = values[value_index];
+						if (value < 0) {
+							ERR_EXPLAIN("unsigned byte integer is lower than maximum")
+							ERR_FAIL_V("");
+						} else if (value > 255) {
+							ERR_EXPLAIN("unsigned byte integer is greater than maximum")
+							ERR_FAIL_V("");
+						}
+						str = chr(values[value_index]);
+					} else if (values[value_index].get_type() == Variant::STRING) {
+						str = values[value_index];
+						if (str.length() != 1) {
+							ERR_EXPLAIN("%c requires number or single-character string");
+							ERR_FAIL_V("");
+						}
+					} else {
+						ERR_EXPLAIN("%c requires number or single-character string");
+						ERR_FAIL_V("");
+					}
+
+					// Padding.
+					if (left_justified) {
+						str = str.rpad(min_chars);
+					} else {
+						str = str.lpad(min_chars);
+					}
 
 
+					formatted += str;
+					++value_index;
+					in_format = false;
+					break;
+				}
+				case '-': { // Left justify
+					left_justified = true;
+					break;
+				}
+				case '+': { // Show + if positive.
+					show_sign = true;
+					break;
+				}
+				case '0': case '1': case '2': case '3': case '4':
+				case '5': case '6': case '7': case '8': case '9': {
+					int n = c - '0';
+					if (in_decimals) {
+						min_decimals *= 10;
+						min_decimals += n;
+					} else {
+						if (c == '0' && min_chars == 0) {
+							pad_with_zeroes = true;
+						} else {
+							min_chars *= 10;
+							min_chars += n;
+						}
+					}
+					break;
+				}
+				case '.': { // Float separtor.
+					if (in_decimals) {
+						ERR_EXPLAIN("too many decimal points in format");
+						ERR_FAIL_V("");
+					}
+					in_decimals = true;
+					min_decimals = 0; // We want to add the value manually.
+					break;
+				}
+
+				case '*': { // Dyanmic width, based on value.
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					if (!values[value_index].is_num()) {
+						ERR_EXPLAIN("* wants number");
+						ERR_FAIL_V("");
+					}
+
+					int size = values[value_index];
+
+					if (in_decimals) {
+						min_decimals = size;
+					} else {
+						min_chars = size;
+					}
+
+					++value_index;
+					break;
+				}
+
+				default: {
+					ERR_EXPLAIN("unsupported format character");
+  					ERR_FAIL_V("");
+  				}
+			}
+		} else { // Not in format string.
+			switch (c) {
+				case '%':
+					in_format = true;
+					// Back to defaults:
+					min_chars = 0;
+					min_decimals = 6;
+					pad_with_zeroes = false;
+					left_justified = false;
+					show_sign = false;
+					in_decimals = false;
+					break;
+				default:
+					formatted += chr(c);
+			}
+		}
+	}
+
+	if (in_format) {
+		ERR_EXPLAIN("incomplete format");
+  		ERR_FAIL_V("");
+	}
+
+	if (value_index != values.size()) {
+		ERR_EXPLAIN("not all arguments converted during string formatting");
+  		ERR_FAIL_V("");
+	}
+
+	return formatted;
+}

+ 6 - 2
core/ustring.h

@@ -31,6 +31,7 @@
 
 
 #include "typedefs.h"
 #include "typedefs.h"
 #include "vector.h"
 #include "vector.h"
+#include "array.h"
 
 
 /**
 /**
 	@author red <red@killy>
 	@author red <red@killy>
@@ -127,10 +128,13 @@ public:
 	String insert(int p_at_pos,String p_string) const;
 	String insert(int p_at_pos,String p_string) const;
 	String pad_decimals(int p_digits) const;
 	String pad_decimals(int p_digits) const;
 	String pad_zeros(int p_digits) const;
 	String pad_zeros(int p_digits) const;
+	String lpad(int min_length,const String& character=" ") const;
+	String rpad(int min_length,const String& character=" ") const;
+	String sprintf(const Array& values) const;
 	static String num(double p_num,int p_decimals=-1);
 	static String num(double p_num,int p_decimals=-1);
 	static String num_scientific(double p_num);
 	static String num_scientific(double p_num);
 	static String num_real(double p_num);
 	static String num_real(double p_num);
-	static String num_int64(int64_t p_num);
+	static String num_int64(int64_t p_num,int base=10,bool capitalize_hex=false);
 	static String chr(CharType p_char);
 	static String chr(CharType p_char);
 	static String md5(const uint8_t *p_md5);
 	static String md5(const uint8_t *p_md5);
 	bool is_numeric() const;
 	bool is_numeric() const;
@@ -203,7 +207,7 @@ public:
 	String xml_unescape() const;
 	String xml_unescape() const;
 	String c_escape() const;
 	String c_escape() const;
 	String c_unescape() const;
 	String c_unescape() const;
-
+	
 	String percent_encode() const;
 	String percent_encode() const;
 	String percent_decode() const;
 	String percent_decode() const;
 
 

+ 14 - 0
core/variant_op.cpp

@@ -736,6 +736,20 @@ void Variant::evaluate(const Operator& p_op, const Variant& p_a, const Variant&
 				}
 				}
 #endif
 #endif
 				_RETURN( p_a._data._int % p_b._data._int );
 				_RETURN( p_a._data._int % p_b._data._int );
+				
+			} else if (p_a.type==STRING) {
+				const String *str=reinterpret_cast<const String*>(p_a._data._mem);
+
+				if (p_b.type==ARRAY) {
+					// e.g. "frog %s %d" % ["fish", 12]
+					const Array *arr=reinterpret_cast<const Array*>(p_b._data._mem);
+					_RETURN(str->sprintf(*arr));
+				} else {
+					// e.g. "frog %d" % 12
+					Array arr;
+					arr.push_back(p_b);
+					_RETURN(str->sprintf(arr));
+				}
 			}
 			}
 
 
 			r_valid=false;
 			r_valid=false;

+ 0 - 33
demos/2d/hexamap/.fscache

@@ -1,33 +0,0 @@
-::res://::1412302385
-WWT-01.png::ImageTexture::1412126473::
-WWT-02.png::ImageTexture::1412126474::
-WWT-03.png::ImageTexture::1412126474::
-WWT-04.png::ImageTexture::1412126474::
-WWT-05.png::ImageTexture::1412126474::
-WWT-06.png::ImageTexture::1412126474::
-WWT-07.png::ImageTexture::1412126474::
-WWT-08.png::ImageTexture::1412126474::
-WWT-09.png::ImageTexture::1412126474::
-WWT-10.png::ImageTexture::1412126474::
-WWT-11.png::ImageTexture::1412126475::
-WWT-12.png::ImageTexture::1412126475::
-WWT-13.png::ImageTexture::1412126475::
-WWT-14.png::ImageTexture::1412126475::
-WWT-15.png::ImageTexture::1412126475::
-WWT-16.png::ImageTexture::1412126475::
-WWT-17.png::ImageTexture::1412126475::
-WWT-18.png::ImageTexture::1412126475::
-WWT-19.png::ImageTexture::1412126476::
-WWT-20.png::ImageTexture::1412126476::
-WWT-21.png::ImageTexture::1412126476::
-WWT-22.png::ImageTexture::1412126476::
-WWT-23.png::ImageTexture::1412126476::
-WWT-24.png::ImageTexture::1412126476::
-WWT-25.png::ImageTexture::1412126476::
-WWT-26.png::ImageTexture::1412126476::
-map.scn::PackedScene::1412127344::
-tiles.scn::PackedScene::1412126994::
-tileset.res::TileSet::1412127001::
-troll.gd::GDScript::1412302377::
-troll.png::ImageTexture::1412302385::
-troll.scn::PackedScene::1412302380::

+ 0 - 4
demos/2d/polygon_path_finder_demo/.fscache

@@ -1,4 +0,0 @@
-::res://::1421669411
-icon.png::ImageTexture::1421499066::
-new_scene_poly_with_holes.scn::PackedScene::1421499066::
-polygonpathfinder.gd::GDScript::1421669411::

File diff suppressed because it is too large
+ 354 - 13
demos/3d/platformer/stage.xml


+ 34 - 8
drivers/gles2/rasterizer_gles2.cpp

@@ -4038,8 +4038,16 @@ void RasterizerGLES2::render_target_set_size(RID p_render_target,int p_width,int
 	glGenTextures(1, &rt->color);
 	glGenTextures(1, &rt->color);
 	glBindTexture(GL_TEXTURE_2D, rt->color);
 	glBindTexture(GL_TEXTURE_2D, rt->color);
 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  rt->width, rt->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  rt->width, rt->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+	if (rt->texture_ptr->flags&VS::TEXTURE_FLAG_FILTER) {
+
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	} else {
+
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	}
 	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
 	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
@@ -5494,13 +5502,15 @@ Error RasterizerGLES2::_setup_geometry(const Geometry *p_geometry, const Materia
 				base = surf->array_local;
 				base = surf->array_local;
 				glBindBuffer(GL_ARRAY_BUFFER, 0);
 				glBindBuffer(GL_ARRAY_BUFFER, 0);
 				bool can_copy_to_local=surf->local_stride * surf->array_len <= skinned_buffer_size;
 				bool can_copy_to_local=surf->local_stride * surf->array_len <= skinned_buffer_size;
+				if (p_morphs && surf->stride * surf->array_len > skinned_buffer_size)
+					can_copy_to_local=false;
+
+
 				if (!can_copy_to_local)
 				if (!can_copy_to_local)
 					skeleton_valid=false;
 					skeleton_valid=false;
 
 
-
 				/* compute morphs */
 				/* compute morphs */
 
 
-
 				if (p_morphs && surf->morph_target_count && can_copy_to_local) {
 				if (p_morphs && surf->morph_target_count && can_copy_to_local) {
 
 
 
 
@@ -9584,9 +9594,6 @@ void RasterizerGLES2::init() {
 	//glClearDepth(1.0);
 	//glClearDepth(1.0);
 	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 
 
-	skinned_buffer_size = GLOBAL_DEF("rasterizer/skinned_buffer_size",DEFAULT_SKINNED_BUFFER_SIZE);
-	skinned_buffer = memnew_arr( uint8_t, skinned_buffer_size );
-
 	glGenTextures(1, &white_tex);
 	glGenTextures(1, &white_tex);
 	unsigned char whitetexdata[8*8*3];
 	unsigned char whitetexdata[8*8*3];
 	for(int i=0;i<8*8*3;i++) {
 	for(int i=0;i<8*8*3;i++) {
@@ -9762,7 +9769,6 @@ void RasterizerGLES2::init() {
 void RasterizerGLES2::finish() {
 void RasterizerGLES2::finish() {
 
 
 
 
-	memdelete_arr(skinned_buffer);
 }
 }
 
 
 int RasterizerGLES2::get_render_info(VS::RenderInfo p_info) {
 int RasterizerGLES2::get_render_info(VS::RenderInfo p_info) {
@@ -10042,10 +10048,29 @@ RasterizerGLES2* RasterizerGLES2::get_singleton() {
 	return _singleton;
 	return _singleton;
 };
 };
 
 
+int RasterizerGLES2::RenderList::max_elements=RenderList::DEFAULT_MAX_ELEMENTS;
+
 RasterizerGLES2::RasterizerGLES2(bool p_compress_arrays,bool p_keep_ram_copy,bool p_default_fragment_lighting,bool p_use_reload_hooks) {
 RasterizerGLES2::RasterizerGLES2(bool p_compress_arrays,bool p_keep_ram_copy,bool p_default_fragment_lighting,bool p_use_reload_hooks) {
 
 
 	_singleton = this;
 	_singleton = this;
 
 
+	RenderList::max_elements=GLOBAL_DEF("rasterizer/max_render_elements",(int)RenderList::DEFAULT_MAX_ELEMENTS);
+	if (RenderList::max_elements>64000)
+		RenderList::max_elements=64000;
+	if (RenderList::max_elements<1024)
+		RenderList::max_elements=1024;
+
+	opaque_render_list.init();
+	alpha_render_list.init();
+
+	skinned_buffer_size = GLOBAL_DEF("rasterizer/skeleton_buffer_size_kb",DEFAULT_SKINNED_BUFFER_SIZE);
+	if (skinned_buffer_size<256)
+		skinned_buffer_size=256;
+	if (skinned_buffer_size>16384)
+		skinned_buffer_size=16384;
+	skinned_buffer_size*=1024;
+	skinned_buffer = memnew_arr( uint8_t, skinned_buffer_size );
+
 	keep_copies=p_keep_ram_copy;
 	keep_copies=p_keep_ram_copy;
 	use_reload_hooks=p_use_reload_hooks;
 	use_reload_hooks=p_use_reload_hooks;
 	pack_arrays=p_compress_arrays;
 	pack_arrays=p_compress_arrays;
@@ -10089,6 +10114,7 @@ RasterizerGLES2::RasterizerGLES2(bool p_compress_arrays,bool p_keep_ram_copy,boo
 
 
 RasterizerGLES2::~RasterizerGLES2() {
 RasterizerGLES2::~RasterizerGLES2() {
 
 
+	memdelete_arr(skinned_buffer);
 };
 };
 
 
 
 

+ 21 - 7
drivers/gles2/rasterizer_gles2.h

@@ -65,7 +65,7 @@ class RasterizerGLES2 : public Rasterizer {
 
 
 		MAX_SCENE_LIGHTS=2048,
 		MAX_SCENE_LIGHTS=2048,
 		LIGHT_SPOT_BIT=0x80,
 		LIGHT_SPOT_BIT=0x80,
-		DEFAULT_SKINNED_BUFFER_SIZE = 2048 * 1024, // 10k vertices
+		DEFAULT_SKINNED_BUFFER_SIZE = 2048, // 10k vertices
 		MAX_HW_LIGHTS = 1,
 		MAX_HW_LIGHTS = 1,
 	};
 	};
 
 
@@ -827,15 +827,18 @@ class RasterizerGLES2 : public Rasterizer {
 	GLuint gui_quad_buffer;
 	GLuint gui_quad_buffer;
 
 
 
 
+
 	struct RenderList {
 	struct RenderList {
 
 
 		enum {
 		enum {
-			MAX_ELEMENTS=4096,
+			DEFAULT_MAX_ELEMENTS=4096,
 			MAX_LIGHTS=4,
 			MAX_LIGHTS=4,
 			SORT_FLAG_SKELETON=1,
 			SORT_FLAG_SKELETON=1,
 			SORT_FLAG_INSTANCING=2,
 			SORT_FLAG_INSTANCING=2,
 		};
 		};
 
 
+		static int max_elements;
+
 		struct Element {
 		struct Element {
 
 
 
 
@@ -868,8 +871,8 @@ class RasterizerGLES2 : public Rasterizer {
 		};
 		};
 
 
 
 
-		Element _elements[MAX_ELEMENTS];
-		Element *elements[MAX_ELEMENTS];
+		Element *_elements;
+		Element **elements;
 		int element_count;
 		int element_count;
 
 
 		void clear() {
 		void clear() {
@@ -1004,17 +1007,28 @@ class RasterizerGLES2 : public Rasterizer {
 		}
 		}
 		_FORCE_INLINE_ Element* add_element() {
 		_FORCE_INLINE_ Element* add_element() {
 
 
-			if (element_count>MAX_ELEMENTS)
+			if (element_count>=max_elements)
 				return NULL;
 				return NULL;
 			elements[element_count]=&_elements[element_count];
 			elements[element_count]=&_elements[element_count];
 			return elements[element_count++];
 			return elements[element_count++];
 		}
 		}
 
 
-		RenderList() {
+		void init() {
 
 
 			element_count = 0;
 			element_count = 0;
-			for (int i=0;i<MAX_ELEMENTS;i++)
+			elements=memnew_arr(Element*,max_elements);
+			_elements=memnew_arr(Element,max_elements);
+			for (int i=0;i<max_elements;i++)
 				elements[i]=&_elements[i]; // assign elements
 				elements[i]=&_elements[i]; // assign elements
+
+		}
+
+		RenderList() {
+
+		}
+		~RenderList() {
+			memdelete_arr(elements);
+			memdelete_arr(_elements);
 		}
 		}
 	};
 	};
 
 

+ 6 - 1
drivers/unix/ip_unix.cpp

@@ -50,11 +50,16 @@
  #ifdef ANDROID_ENABLED
  #ifdef ANDROID_ENABLED
   #include "platform/android/ifaddrs_android.h"
   #include "platform/android/ifaddrs_android.h"
  #else
  #else
+  #ifdef __FreeBSD__
+   #include <sys/types.h>
+  #endif
   #include <ifaddrs.h>
   #include <ifaddrs.h>
  #endif
  #endif
  #include <arpa/inet.h>
  #include <arpa/inet.h>
  #include <sys/socket.h>
  #include <sys/socket.h>
-
+ #ifdef __FreeBSD__
+  #include <netinet/in.h>
+ #endif
 #endif
 #endif
 
 
 IP_Address IP_Unix::_resolve_hostname(const String& p_hostname) {
 IP_Address IP_Unix::_resolve_hostname(const String& p_hostname) {

+ 19 - 1
drivers/unix/os_unix.cpp

@@ -44,7 +44,9 @@
 #include "stream_peer_tcp_posix.h"
 #include "stream_peer_tcp_posix.h"
 #include "packet_peer_udp_posix.h"
 #include "packet_peer_udp_posix.h"
 
 
-
+#ifdef __FreeBSD__
+#include <sys/param.h>
+#endif
 #include <stdarg.h>
 #include <stdarg.h>
 #include <sys/time.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <sys/wait.h>
@@ -305,7 +307,17 @@ Error OS_Unix::execute(const String& p_path, const List<String>& p_arguments,boo
 			args.push_back((char*)cs[i].get_data());// shitty C cast
 			args.push_back((char*)cs[i].get_data());// shitty C cast
 		args.push_back(0);
 		args.push_back(0);
 
 
+#ifdef __FreeBSD__
+		if(p_path.find("/")) {
+			// exec name contains path so use it
+			execv(p_path.utf8().get_data(),&args[0]);
+		}else{
+			// use program name and search through PATH to find it
+			execvp(getprogname(),&args[0]);
+		}
+#else
 		execv(p_path.utf8().get_data(),&args[0]);
 		execv(p_path.utf8().get_data(),&args[0]);
+#endif
 		// still alive? something failed..
 		// still alive? something failed..
 		fprintf(stderr,"**ERROR** OS_Unix::execute - Could not create child process while executing: %s\n",p_path.utf8().get_data());
 		fprintf(stderr,"**ERROR** OS_Unix::execute - Could not create child process while executing: %s\n",p_path.utf8().get_data());
 		abort();
 		abort();
@@ -421,6 +433,12 @@ String OS_Unix::get_executable_path() const {
 		return OS::get_executable_path();
 		return OS::get_executable_path();
 	}
 	}
 	return b;
 	return b;
+#elif defined(__FreeBSD__)
+	char resolved_path[MAXPATHLEN];
+
+	realpath(OS::get_executable_path().utf8().get_data(), resolved_path);
+
+	return String(resolved_path);
 #else
 #else
 	ERR_PRINT("Warning, don't know how to obtain executable path on this OS! Please override this function properly.");
 	ERR_PRINT("Warning, don't know how to obtain executable path on this OS! Please override this function properly.");
 	return OS::get_executable_path();
 	return OS::get_executable_path();

+ 1 - 1
modules/gdscript/gd_editor.cpp

@@ -51,7 +51,7 @@ String GDScriptLanguage::get_template(const String& p_class_name, const String&
 	"# var a=2\n"+
 	"# var a=2\n"+
 	"# var b=\"textvar\"\n\n"+
 	"# var b=\"textvar\"\n\n"+
 	"func _ready():\n"+
 	"func _ready():\n"+
-	"\t# Initalization here\n"+
+	"\t# Initialization here\n"+
 	"\tpass\n"+
 	"\tpass\n"+
 	"\n"+
 	"\n"+
 	"\n";
 	"\n";

+ 12 - 7
platform/iphone/app_delegate.mm

@@ -84,13 +84,11 @@ static int frame_count = 0;
 	switch (frame_count) {
 	switch (frame_count) {
 
 
 	case 0: {
 	case 0: {
-
-		int backingWidth;
-		int backingHeight;
-		glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
-		glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
-
-		iphone_main(backingWidth, backingHeight, gargc, gargv);
+        int backingWidth;
+        int backingHeight;
+        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
+        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+		
 
 
 		OS::VideoMode vm;
 		OS::VideoMode vm;
 		vm.fullscreen = true;
 		vm.fullscreen = true;
@@ -198,6 +196,13 @@ static int frame_count = 0;
 	//glView.autoresizesSubviews = YES;
 	//glView.autoresizesSubviews = YES;
 	//[glView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleWidth];
 	//[glView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleWidth];
 
 
+    int backingWidth;
+    int backingHeight;
+    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
+    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+    
+    iphone_main(backingWidth, backingHeight, gargc, gargv);
+    
 	view_controller = [[ViewController alloc] init];
 	view_controller = [[ViewController alloc] init];
 	view_controller.view = glView;
 	view_controller.view = glView;
 	window.rootViewController = view_controller;
 	window.rootViewController = view_controller;

+ 1 - 1
platform/isim/detect.py

@@ -22,7 +22,7 @@ def get_opts():
 	return [
 	return [
 		('ISIMPLATFORM', 'name of the iphone platform', 'iPhoneSimulator'),
 		('ISIMPLATFORM', 'name of the iphone platform', 'iPhoneSimulator'),
 		('ISIMPATH', 'the path to iphone toolchain', '/Applications/Xcode.app/Contents/Developer/Platforms/${ISIMPLATFORM}.platform'),
 		('ISIMPATH', 'the path to iphone toolchain', '/Applications/Xcode.app/Contents/Developer/Platforms/${ISIMPLATFORM}.platform'),
-		('ISIMSDK', 'path to the iphone SDK', '$ISIMPATH/Developer/SDKs/${ISIMPLATFORM}7.1.sdk'),
+		('ISIMSDK', 'path to the iphone SDK', '$ISIMPATH/Developer/SDKs/${ISIMPLATFORM}.sdk'),
 		('game_center', 'Support for game center', 'yes'),
 		('game_center', 'Support for game center', 'yes'),
 		('store_kit', 'Support for in-app store', 'yes'),
 		('store_kit', 'Support for in-app store', 'yes'),
 		('ios_gles22_override', 'Force GLES2.0 on iOS', 'yes'),
 		('ios_gles22_override', 'Force GLES2.0 on iOS', 'yes'),

+ 4 - 3
platform/windows/os_windows.cpp

@@ -593,10 +593,11 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM	wParam,	LPARAM	lParam) {
 
 
 			ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
 			ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
 
 
+			// Make sure we don't include modifiers for the modifier key itself.
 			KeyEvent ke;
 			KeyEvent ke;
-			ke.mod_state.shift=shift_mem;
-			ke.mod_state.alt=alt_mem;
-			ke.mod_state.control=control_mem;
+			ke.mod_state.shift= (wParam != VK_SHIFT) ? shift_mem : false;
+			ke.mod_state.alt= (! (wParam == VK_MENU && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN))) ? alt_mem : false;
+			ke.mod_state.control= (wParam != VK_CONTROL) ? control_mem : false;
 			ke.mod_state.meta=meta_mem;
 			ke.mod_state.meta=meta_mem;
 			ke.uMsg=uMsg;
 			ke.uMsg=uMsg;
 
 

+ 11 - 17
platform/x11/detect.py

@@ -70,24 +70,23 @@ def configure(env):
 		else:
 		else:
 			env["bits"]="32"
 			env["bits"]="32"
 
 
-
 	env.Append(CPPPATH=['#platform/x11'])
 	env.Append(CPPPATH=['#platform/x11'])
 	if (env["use_llvm"]=="yes"):
 	if (env["use_llvm"]=="yes"):
-		env["CC"]="clang"
-		env["CXX"]="clang++"
-		env["LD"]="clang++"
-		if (env["use_sanitizer"]=="yes"):
-			env.Append(CXXFLAGS=['-fsanitize=address','-fno-omit-frame-pointer'])
-			env.Append(LINKFLAGS=['-fsanitize=address'])
-			env.extra_suffix=".llvms"
-		else:
-			env.extra_suffix=".llvm"
+		if 'clang++' not in env['CXX']:
+			env["CC"]="clang"
+			env["CXX"]="clang++"
+			env["LD"]="clang++"
+		env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND'])
+		env.extra_suffix=".llvm"
+
 		if (env["colored"]=="yes"):
 		if (env["colored"]=="yes"):
 			if sys.stdout.isatty():
 			if sys.stdout.isatty():
 				env.Append(CXXFLAGS=["-fcolor-diagnostics"])
 				env.Append(CXXFLAGS=["-fcolor-diagnostics"])
 
 
-
-
+	if (env["use_sanitizer"]=="yes"):
+		env.Append(CXXFLAGS=['-fsanitize=address','-fno-omit-frame-pointer'])
+		env.Append(LINKFLAGS=['-fsanitize=address'])
+		env.extra_suffix+="s"
 
 
 	#if (env["tools"]=="no"):
 	#if (env["tools"]=="no"):
 	#	#no tools suffix
 	#	#no tools suffix
@@ -141,11 +140,6 @@ def configure(env):
 		env.Append(LINKFLAGS=['-m64','-L/usr/lib/i686-linux-gnu'])
 		env.Append(LINKFLAGS=['-m64','-L/usr/lib/i686-linux-gnu'])
 
 
 
 
-	if (env["CXX"]=="clang++"):
-		env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND'])
-		env["CC"]="clang"
-		env["LD"]="clang++"
-
 	import methods
 	import methods
 
 
 	env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } )
 	env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } )

+ 22 - 1
scene/2d/canvas_item.cpp

@@ -810,6 +810,23 @@ void CanvasItem::_shader_changed() {
 }
 }
 #endif
 #endif
 
 
+void CanvasItem::get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const {
+
+	if (p_idx==0 && shader.is_valid() && (p_function.operator String()=="get_shader_param" || p_function.operator String()=="set_shader_param")) {
+
+		List<PropertyInfo> pl;
+		shader->get_param_list(&pl);
+		for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
+			r_options->push_back("\""+E->get().name.replace_first("shader_param/","")+"\"");
+		}
+
+		return;
+	}
+
+	Node::get_argument_options(p_function,p_idx,r_options);
+}
+
+
 void CanvasItem::_bind_methods() {
 void CanvasItem::_bind_methods() {
 
 
 	ObjectTypeDB::bind_method(_MD("_sort_children"),&CanvasItem::_sort_children);
 	ObjectTypeDB::bind_method(_MD("_sort_children"),&CanvasItem::_sort_children);
@@ -845,7 +862,7 @@ void CanvasItem::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("set_self_opacity","self_opacity"),&CanvasItem::set_self_opacity);
 	ObjectTypeDB::bind_method(_MD("set_self_opacity","self_opacity"),&CanvasItem::set_self_opacity);
 	ObjectTypeDB::bind_method(_MD("get_self_opacity"),&CanvasItem::get_self_opacity);
 	ObjectTypeDB::bind_method(_MD("get_self_opacity"),&CanvasItem::get_self_opacity);
 
 
-	ObjectTypeDB::bind_method(_MD("set_draw_behind_parent","enabe"),&CanvasItem::set_draw_behind_parent);
+	ObjectTypeDB::bind_method(_MD("set_draw_behind_parent","enable"),&CanvasItem::set_draw_behind_parent);
 	ObjectTypeDB::bind_method(_MD("is_draw_behind_parent_enabled"),&CanvasItem::is_draw_behind_parent_enabled);
 	ObjectTypeDB::bind_method(_MD("is_draw_behind_parent_enabled"),&CanvasItem::is_draw_behind_parent_enabled);
 
 
 	ObjectTypeDB::bind_method(_MD("_set_on_top","on_top"),&CanvasItem::_set_on_top);
 	ObjectTypeDB::bind_method(_MD("_set_on_top","on_top"),&CanvasItem::_set_on_top);
@@ -882,6 +899,10 @@ void CanvasItem::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("set_use_parent_shader","enable"),&CanvasItem::set_use_parent_shader);
 	ObjectTypeDB::bind_method(_MD("set_use_parent_shader","enable"),&CanvasItem::set_use_parent_shader);
 	ObjectTypeDB::bind_method(_MD("get_use_parent_shader"),&CanvasItem::get_use_parent_shader);
 	ObjectTypeDB::bind_method(_MD("get_use_parent_shader"),&CanvasItem::get_use_parent_shader);
 
 
+	ObjectTypeDB::bind_method(_MD("set_shader_param","param","value"),&CanvasItem::set_shader_param);
+	ObjectTypeDB::bind_method(_MD("get_shader_param","param"),&CanvasItem::get_shader_param);
+
+
 	BIND_VMETHOD(MethodInfo("_draw"));
 	BIND_VMETHOD(MethodInfo("_draw"));
 
 
 	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"visibility/visible"), _SCS("_set_visible_"),_SCS("_is_visible_") );
 	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"visibility/visible"), _SCS("_set_visible_"),_SCS("_is_visible_") );

+ 2 - 0
scene/2d/canvas_item.h

@@ -221,6 +221,8 @@ public:
 	void set_shader_param(const StringName& p_param,const Variant& p_value);
 	void set_shader_param(const StringName& p_param,const Variant& p_value);
 	Variant get_shader_param(const StringName& p_param) const;
 	Variant get_shader_param(const StringName& p_param) const;
 
 
+	void get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const;
+
 	CanvasItem();
 	CanvasItem();
 	~CanvasItem();
 	~CanvasItem();
 };
 };

+ 1 - 1
scene/3d/camera.cpp

@@ -604,7 +604,7 @@ Vector3 Camera::project_position(const Point2& p_point) const {
 
 
 	Vector2 point;
 	Vector2 point;
 	point.x = (p_point.x/viewport_size.x) * 2.0 - 1.0;
 	point.x = (p_point.x/viewport_size.x) * 2.0 - 1.0;
-	point.y = (p_point.y/viewport_size.y) * 2.0 - 1.0;
+	point.y = (1.0-(p_point.y/viewport_size.y)) * 2.0 - 1.0;
 	point*=vp_size;
 	point*=vp_size;
 
 
 	Vector3 p(point.x,point.y,-near);
 	Vector3 p(point.x,point.y,-near);

+ 1 - 1
scene/resources/material.cpp

@@ -582,7 +582,7 @@ void ShaderMaterial::get_argument_options(const StringName& p_function,int p_idx
 			List<PropertyInfo> pl;
 			List<PropertyInfo> pl;
 			shader->get_param_list(&pl);
 			shader->get_param_list(&pl);
 			for (List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
 			for (List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
-				r_options->push_back("\""+E->get().name.replace("shader_param/","")+"\"");
+				r_options->push_back("\""+E->get().name.replace_first("shader_param/","")+"\"");
 			}
 			}
 		}
 		}
 	}
 	}

+ 17 - 9
scene/resources/polygon_path_finder.cpp

@@ -525,24 +525,32 @@ bool PolygonPathFinder::is_point_inside(const Vector2& p_point) const {
 
 
 Vector2 PolygonPathFinder::get_closest_point(const Vector2& p_point) const {
 Vector2 PolygonPathFinder::get_closest_point(const Vector2& p_point) const {
 
 
-	int closest_idx=-1;
 	float closest_dist=1e20;
 	float closest_dist=1e20;
-	for(int i=0;i<points.size()-2;i++) {
+	Vector2 closest_point;
+
+	for (Set<Edge>::Element *E=edges.front();E;E=E->next()) {
+
+		const Edge& e=E->get();
+		Vector2 seg[2]={
+			points[e.points[0]].pos,
+			points[e.points[1]].pos
+		};
+
+
+		Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point,seg);
+		float d = p_point.distance_squared_to(closest);
 
 
-		float d = p_point.distance_squared_to(points[i].pos);
 		if (d<closest_dist) {
 		if (d<closest_dist) {
 			closest_dist=d;
 			closest_dist=d;
-			closest_idx=i;
+			closest_point=closest;
 		}
 		}
-
 	}
 	}
+	
+	ERR_FAIL_COND_V(closest_dist==1e20,Vector2());
 
 
-	ERR_FAIL_COND_V(closest_idx==-1,Vector2());
-
-	return points[closest_idx].pos;
+	return closest_point;
 }
 }
 
 
-
 Vector<Vector2> PolygonPathFinder::get_intersections(const Vector2& p_from, const Vector2& p_to) const {
 Vector<Vector2> PolygonPathFinder::get_intersections(const Vector2& p_from, const Vector2& p_to) const {
 
 
 	Vector<Vector2> inters;
 	Vector<Vector2> inters;

+ 6 - 0
scene/resources/style_box.cpp

@@ -85,6 +85,12 @@ void StyleBox::_bind_methods() {
 
 
 	ObjectTypeDB::bind_method(_MD("draw"),&StyleBox::draw);
 	ObjectTypeDB::bind_method(_MD("draw"),&StyleBox::draw);
 
 
+	ADD_PROPERTYI( PropertyInfo( Variant::REAL, "content_margin/left", PROPERTY_HINT_RANGE,"-1,2048,1" ), _SCS("set_default_margin"),_SCS("get_default_margin"), MARGIN_LEFT );
+	ADD_PROPERTYI( PropertyInfo( Variant::REAL, "content_margin/right", PROPERTY_HINT_RANGE,"-1,2048,1" ), _SCS("set_default_margin"),_SCS("get_default_margin"), MARGIN_RIGHT );
+	ADD_PROPERTYI( PropertyInfo( Variant::REAL, "content_margin/top", PROPERTY_HINT_RANGE,"-1,2048,1" ), _SCS("set_default_margin"),_SCS("get_default_margin"), MARGIN_TOP);
+	ADD_PROPERTYI( PropertyInfo( Variant::REAL, "content_margin/bottom", PROPERTY_HINT_RANGE,"-1,2048,1" ), _SCS("set_default_margin"),_SCS("get_default_margin"), MARGIN_BOTTOM );
+
+
 }
 }
 
 
 StyleBox::StyleBox() {
 StyleBox::StyleBox() {

+ 18 - 18
scene/resources/texture.cpp

@@ -936,21 +936,21 @@ float CubeMap::get_lossy_storage_quality() const {
 
 
 bool CubeMap::_set(const StringName& p_name, const Variant& p_value) {
 bool CubeMap::_set(const StringName& p_name, const Variant& p_value) {
 
 
-	if (p_name=="side/left")
+	if (p_name=="side/left") {
 		set_side(SIDE_LEFT,p_value);
 		set_side(SIDE_LEFT,p_value);
-	if (p_name=="side/right")
+	} else if (p_name=="side/right") {
 		set_side(SIDE_RIGHT,p_value);
 		set_side(SIDE_RIGHT,p_value);
-	if (p_name=="side/bottom")
+	} else if (p_name=="side/bottom") {
 		set_side(SIDE_BOTTOM,p_value);
 		set_side(SIDE_BOTTOM,p_value);
-	if (p_name=="side/top")
+	} else if (p_name=="side/top") {
 		set_side(SIDE_TOP,p_value);
 		set_side(SIDE_TOP,p_value);
-	if (p_name=="side/front")
+	} else if (p_name=="side/front") {
 		set_side(SIDE_FRONT,p_value);
 		set_side(SIDE_FRONT,p_value);
-	if (p_name=="side/back")
+	} else if (p_name=="side/back") {
 		set_side(SIDE_BACK,p_value);
 		set_side(SIDE_BACK,p_value);
-	else if (p_name=="flags")
+	} else if (p_name=="flags") {
 		set_flags(p_value);
 		set_flags(p_value);
-	else if (p_name=="storage") {
+	} else if (p_name=="storage") {
 		storage=Storage(p_value.operator int());
 		storage=Storage(p_value.operator int());
 	} else if (p_name=="lossy_quality") {
 	} else if (p_name=="lossy_quality") {
 		lossy_storage_quality=p_value;
 		lossy_storage_quality=p_value;
@@ -963,25 +963,25 @@ bool CubeMap::_set(const StringName& p_name, const Variant& p_value) {
 
 
 bool CubeMap::_get(const StringName& p_name,Variant &r_ret) const {
 bool CubeMap::_get(const StringName& p_name,Variant &r_ret) const {
 
 
-	if (p_name=="side/left")
+	if (p_name=="side/left") {
 		r_ret=get_side(SIDE_LEFT);
 		r_ret=get_side(SIDE_LEFT);
-	if (p_name=="side/right")
+	} else if (p_name=="side/right") {
 		r_ret=get_side(SIDE_RIGHT);
 		r_ret=get_side(SIDE_RIGHT);
-	if (p_name=="side/bottom")
+	} else if (p_name=="side/bottom") {
 		r_ret=get_side(SIDE_BOTTOM);
 		r_ret=get_side(SIDE_BOTTOM);
-	if (p_name=="side/top")
+	} else if (p_name=="side/top") {
 		r_ret=get_side(SIDE_TOP);
 		r_ret=get_side(SIDE_TOP);
-	if (p_name=="side/front")
+	} else if (p_name=="side/front") {
 		r_ret=get_side(SIDE_FRONT);
 		r_ret=get_side(SIDE_FRONT);
-	if (p_name=="side/back")
+	} else if (p_name=="side/back") {
 		r_ret=get_side(SIDE_BACK);
 		r_ret=get_side(SIDE_BACK);
-	else if (p_name=="flags")
+	} else if (p_name=="flags") {
 		r_ret= flags;
 		r_ret= flags;
-	else if (p_name=="storage")
+	} else if (p_name=="storage") {
 		r_ret= storage;
 		r_ret= storage;
-	else if (p_name=="lossy_quality")
+	} else if (p_name=="lossy_quality") {
 		r_ret= lossy_storage_quality;
 		r_ret= lossy_storage_quality;
-	else
+	} else
 		return false;
 		return false;
 
 
 	return true;
 	return true;

+ 26 - 0
tools/editor/editor_import_export.cpp

@@ -1141,10 +1141,36 @@ EditorImportExport* EditorImportExport::singleton=NULL;
 
 
 void EditorImportExport::add_import_plugin(const Ref<EditorImportPlugin>& p_plugin) {
 void EditorImportExport::add_import_plugin(const Ref<EditorImportPlugin>& p_plugin) {
 
 
+	// Need to make sure the name is unique if we are going to lookup by it
+	ERR_FAIL_COND(by_idx.has(p_plugin->get_name()));
+
 	by_idx[ p_plugin->get_name() ]=plugins.size();
 	by_idx[ p_plugin->get_name() ]=plugins.size();
 	plugins.push_back(p_plugin);
 	plugins.push_back(p_plugin);
 }
 }
 
 
+void EditorImportExport::remove_import_plugin(const Ref<EditorImportPlugin>& p_plugin) {
+
+	String plugin_name = p_plugin->get_name();
+
+	// Keep the indices the same
+	// Find the index of the target plugin
+	ERR_FAIL_COND(!by_idx.has(plugin_name));
+	int idx = by_idx[plugin_name];
+	int last_idx = plugins.size() - 1;
+
+	// Swap the last plugin and the target one
+	SWAP(plugins[idx], plugins[last_idx]);
+
+	// Update the index of the old last one
+	by_idx[plugins[idx]->get_name()] = idx;
+
+	// Remove the target plugin's by_idx entry
+	by_idx.erase(plugin_name);
+
+	// Erase the plugin
+	plugins.remove(last_idx);
+}
+
 int EditorImportExport::get_import_plugin_count() const{
 int EditorImportExport::get_import_plugin_count() const{
 
 
 	return plugins.size();
 	return plugins.size();

+ 1 - 0
tools/editor/editor_import_export.h

@@ -270,6 +270,7 @@ public:
 	static EditorImportExport* get_singleton() { return singleton; }
 	static EditorImportExport* get_singleton() { return singleton; }
 
 
 	void add_import_plugin(const Ref<EditorImportPlugin>& p_plugin);
 	void add_import_plugin(const Ref<EditorImportPlugin>& p_plugin);
+	void remove_import_plugin(const Ref<EditorImportPlugin>& p_plugin);
 	int get_import_plugin_count() const;
 	int get_import_plugin_count() const;
 	Ref<EditorImportPlugin> get_import_plugin(int p_idx) const;
 	Ref<EditorImportPlugin> get_import_plugin(int p_idx) const;
 	Ref<EditorImportPlugin> get_import_plugin_by_name(const String& p_string) const;
 	Ref<EditorImportPlugin> get_import_plugin_by_name(const String& p_string) const;

+ 52 - 11
tools/editor/editor_node.cpp

@@ -336,6 +336,19 @@ void EditorNode::_vp_resized() {
 
 
 }
 }
 
 
+void EditorNode::_rebuild_import_menu()
+{
+	PopupMenu* p = import_menu->get_popup();
+	p->clear();
+	p->add_item("Sub-Scene", FILE_IMPORT_SUBSCENE);
+	p->add_separator();
+	for (int i = 0; i < editor_import_export->get_import_plugin_count(); i++) {
+		p->add_item(editor_import_export->get_import_plugin(i)->get_visible_name(), IMPORT_PLUGIN_BASE + i);
+	}
+	p->add_separator();
+	p->add_item("Re-Import..", SETTINGS_IMPORT);
+}
+
 void EditorNode::_node_renamed() {
 void EditorNode::_node_renamed() {
 
 
 	if (property_editor)
 	if (property_editor)
@@ -1967,6 +1980,25 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
 				log->add_message("REDO: "+action);
 				log->add_message("REDO: "+action);
 
 
 		} break;
 		} break;
+
+		case EDIT_REVERT: {
+
+			Node *scene = get_edited_scene();
+
+			if (!scene)
+				break;
+			
+			if (unsaved_cache && !p_confirmed) {
+				confirmation->get_ok()->set_text("Revert");
+				confirmation->set_text("This action cannot be undone. Revert anyway?");
+				confirmation->popup_centered(Size2(300,70));
+				break;
+			}
+
+			Error err = load_scene(scene->get_filename());
+
+		} break;
+
 #if 0
 #if 0
 		case NODE_EXTERNAL_INSTANCE: {
 		case NODE_EXTERNAL_INSTANCE: {
 
 
@@ -2388,6 +2420,19 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor) {
 }
 }
 
 
 
 
+void EditorNode::add_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import) {
+
+	editor_import_export->add_import_plugin(p_editor_import);
+	_rebuild_import_menu();
+}
+
+void EditorNode::remove_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import) {
+
+	editor_import_export->remove_import_plugin(p_editor_import);
+	_rebuild_import_menu();
+}
+
+
 void EditorNode::set_edited_scene(Node *p_scene) {
 void EditorNode::set_edited_scene(Node *p_scene) {
 
 
 	if (edited_scene) {
 	if (edited_scene) {
@@ -3152,6 +3197,9 @@ void EditorNode::_bind_methods() {
 	ObjectTypeDB::bind_method("_sources_changed",&EditorNode::_sources_changed);
 	ObjectTypeDB::bind_method("_sources_changed",&EditorNode::_sources_changed);
 	ObjectTypeDB::bind_method("_fs_changed",&EditorNode::_fs_changed);
 	ObjectTypeDB::bind_method("_fs_changed",&EditorNode::_fs_changed);
 
 
+	ObjectTypeDB::bind_method(_MD("add_editor_import_plugin", "plugin"), &EditorNode::add_editor_import_plugin);
+	ObjectTypeDB::bind_method(_MD("remove_editor_import_plugin", "plugin"), &EditorNode::remove_editor_import_plugin);
+	ObjectTypeDB::bind_method(_MD("get_gui_base"), &EditorNode::get_gui_base);
 
 
 	ADD_SIGNAL( MethodInfo("play_pressed") );
 	ADD_SIGNAL( MethodInfo("play_pressed") );
 	ADD_SIGNAL( MethodInfo("pause_pressed") );
 	ADD_SIGNAL( MethodInfo("pause_pressed") );
@@ -3469,6 +3517,8 @@ EditorNode::EditorNode() {
 	p->add_separator();
 	p->add_separator();
 	p->add_item("Project Settings",RUN_SETTINGS);
 	p->add_item("Project Settings",RUN_SETTINGS);
 	p->add_separator();
 	p->add_separator();
+	p->add_item("Revert Scene",EDIT_REVERT);
+	p->add_separator();
 	p->add_item("Quit to Project List",RUN_PROJECT_MANAGER,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_Q);
 	p->add_item("Quit to Project List",RUN_PROJECT_MANAGER,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_Q);
 	p->add_item("Quit",FILE_QUIT,KEY_MASK_CMD+KEY_Q);
 	p->add_item("Quit",FILE_QUIT,KEY_MASK_CMD+KEY_Q);
 
 
@@ -3513,8 +3563,6 @@ EditorNode::EditorNode() {
 	left_menu_hb->add_child( import_menu );
 	left_menu_hb->add_child( import_menu );
 
 
 	p=import_menu->get_popup();
 	p=import_menu->get_popup();
-	p->add_item("Sub-Scene",FILE_IMPORT_SUBSCENE);
-	p->add_separator();
 	p->connect("item_pressed",this,"_menu_option");
 	p->connect("item_pressed",this,"_menu_option");
 
 
 	export_button = memnew( ToolButton );
 	export_button = memnew( ToolButton );
@@ -3552,7 +3600,7 @@ EditorNode::EditorNode() {
 	play_button->set_icon(gui_base->get_icon("MainPlay","EditorIcons"));
 	play_button->set_icon(gui_base->get_icon("MainPlay","EditorIcons"));
 	play_button->set_focus_mode(Control::FOCUS_NONE);
 	play_button->set_focus_mode(Control::FOCUS_NONE);
 	play_button->connect("pressed", this,"_menu_option",make_binds(RUN_PLAY));
 	play_button->connect("pressed", this,"_menu_option",make_binds(RUN_PLAY));
-	play_button->set_tooltip("Start the scene (F5).");
+	play_button->set_tooltip("Play the project (F5).");
 
 
 
 
 
 
@@ -4023,11 +4071,6 @@ EditorNode::EditorNode() {
 	editor_import_export->add_import_plugin( Ref<EditorSampleImportPlugin>( memnew(EditorSampleImportPlugin(this))));
 	editor_import_export->add_import_plugin( Ref<EditorSampleImportPlugin>( memnew(EditorSampleImportPlugin(this))));
 	editor_import_export->add_import_plugin( Ref<EditorTranslationImportPlugin>( memnew(EditorTranslationImportPlugin(this))));
 	editor_import_export->add_import_plugin( Ref<EditorTranslationImportPlugin>( memnew(EditorTranslationImportPlugin(this))));
 
 
-
-	for(int i=0;i<editor_import_export->get_import_plugin_count();i++) {
-		    import_menu->get_popup()->add_item(editor_import_export->get_import_plugin(i)->get_visible_name(),IMPORT_PLUGIN_BASE+i);
-	}
-
 	editor_import_export->add_export_plugin( Ref<EditorTextureExportPlugin>( memnew(EditorTextureExportPlugin)));
 	editor_import_export->add_export_plugin( Ref<EditorTextureExportPlugin>( memnew(EditorTextureExportPlugin)));
 
 
 	add_editor_plugin( memnew( CanvasItemEditorPlugin(this) ) );
 	add_editor_plugin( memnew( CanvasItemEditorPlugin(this) ) );
@@ -4072,9 +4115,7 @@ EditorNode::EditorNode() {
 	circle_step_frame=OS::get_singleton()->get_frames_drawn();;
 	circle_step_frame=OS::get_singleton()->get_frames_drawn();;
 	circle_step=0;
 	circle_step=0;
 
 
-
-	import_menu->get_popup()->add_separator();
-	import_menu->get_popup()->add_item("Re-Import..",SETTINGS_IMPORT);
+	_rebuild_import_menu();
 
 
 	editor_plugin_screen=NULL;
 	editor_plugin_screen=NULL;
 	editor_plugin_over=NULL;
 	editor_plugin_over=NULL;

+ 6 - 0
tools/editor/editor_node.h

@@ -127,6 +127,7 @@ class EditorNode : public Node {
 		FILE_EXTERNAL_OPEN_SCENE,
 		FILE_EXTERNAL_OPEN_SCENE,
 		EDIT_UNDO,
 		EDIT_UNDO,
 		EDIT_REDO,
 		EDIT_REDO,
+		EDIT_REVERT,
 		RESOURCE_NEW,
 		RESOURCE_NEW,
 		RESOURCE_LOAD,
 		RESOURCE_LOAD,
 		RESOURCE_SAVE,
 		RESOURCE_SAVE,
@@ -339,6 +340,8 @@ class EditorNode : public Node {
 	void _show_messages();
 	void _show_messages();
 	void _vp_resized();
 	void _vp_resized();
 
 
+	void _rebuild_import_menu();
+
 	void _save_scene(String p_file);
 	void _save_scene(String p_file);
 
 
 
 
@@ -420,6 +423,9 @@ public:
 	static void add_editor_plugin(EditorPlugin *p_editor);
 	static void add_editor_plugin(EditorPlugin *p_editor);
 	static void remove_editor_plugin(EditorPlugin *p_editor);
 	static void remove_editor_plugin(EditorPlugin *p_editor);
 
 
+	void add_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import);
+	void remove_editor_import_plugin(const Ref<EditorImportPlugin>& p_editor_import);
+
 
 
 	void edit_node(Node *p_node);
 	void edit_node(Node *p_node);
 	void edit_resource(const Ref<Resource>& p_resource);
 	void edit_resource(const Ref<Resource>& p_resource);

+ 5 - 1
tools/editor/plugins/script_editor_plugin.cpp

@@ -631,7 +631,10 @@ bool ScriptEditor::_test_script_times_on_disk() {
 
 
 
 
 	if (!all_ok)
 	if (!all_ok)
-		disk_changed->call_deferred("popup_centered_ratio",0.5);
+		if (bool(EDITOR_DEF("text_editor/auto_reload_changed_scripts",false)))
+			script_editor->_reload_scripts();
+		else
+			disk_changed->call_deferred("popup_centered_ratio",0.5);
 
 
 	return all_ok;
 	return all_ok;
 }
 }
@@ -1806,6 +1809,7 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
 
 
 	script_editor->hide();
 	script_editor->hide();
 
 
+	EDITOR_DEF("text_editor/auto_reload_changed_scripts",false);
 	EDITOR_DEF("external_editor/use_external_editor",false);
 	EDITOR_DEF("external_editor/use_external_editor",false);
 	EDITOR_DEF("external_editor/exec_path","");
 	EDITOR_DEF("external_editor/exec_path","");
 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"external_editor/exec_path",PROPERTY_HINT_GLOBAL_FILE));
 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,"external_editor/exec_path",PROPERTY_HINT_GLOBAL_FILE));

+ 32 - 1
tools/editor/scene_tree_dock.cpp

@@ -79,10 +79,22 @@ Node* SceneTreeDock::instance(const String& p_file) {
 		//accept->get_cancel()->hide();
 		//accept->get_cancel()->hide();
 		accept->get_ok()->set_text("Ugh");
 		accept->get_ok()->set_text("Ugh");
 		accept->set_text(String("Error loading scene from ")+p_file);
 		accept->set_text(String("Error loading scene from ")+p_file);
-		accept->popup_centered(Size2(300,70));;
+		accept->popup_centered(Size2(300,70));
 		return NULL;
 		return NULL;
 	}
 	}
 
 
+	// If the scene hasn't been saved yet a cyclical dependency cannot exist.
+	if (edited_scene->get_filename()!="") {
+
+		if (_cyclical_dependency_exists(edited_scene->get_filename(), instanced_scene)) {
+
+			accept->get_ok()->set_text("Ok");
+			accept->set_text(String("Cannot instance the scene '")+p_file+String("' because the current scene exists within one of its' nodes."));
+			accept->popup_centered(Size2(300,90));
+			return NULL;
+		}
+	}
+
 	instanced_scene->generate_instance_state();
 	instanced_scene->generate_instance_state();
 	instanced_scene->set_filename( Globals::get_singleton()->localize_path(p_file) );
 	instanced_scene->set_filename( Globals::get_singleton()->localize_path(p_file) );
 
 
@@ -100,6 +112,25 @@ Node* SceneTreeDock::instance(const String& p_file) {
 
 
 }
 }
 
 
+bool SceneTreeDock::_cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node) {
+	int childCount = p_desired_node->get_child_count();
+
+	if (p_desired_node->get_filename()==p_target_scene_path) {
+		return true;
+	}
+
+	for (int i=0;i<childCount;i++) {
+		Node* child=p_desired_node->get_child(i);
+
+		if(_cyclical_dependency_exists(p_target_scene_path,child)) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+
 static String _get_name_num_separator() {
 static String _get_name_num_separator() {
 	switch(EditorSettings::get_singleton()->get("scenetree_editor/duplicate_node_name_num_separator").operator int()) {
 	switch(EditorSettings::get_singleton()->get("scenetree_editor/duplicate_node_name_num_separator").operator int()) {
 		case 0: return "";
 		case 0: return "";

+ 1 - 0
tools/editor/scene_tree_dock.h

@@ -102,6 +102,7 @@ class SceneTreeDock : public VBoxContainer {
 	void _load_request(const String& p_path);
 	void _load_request(const String& p_path);
 	void _script_open_request(const Ref<Script>& p_script);
 	void _script_open_request(const Ref<Script>& p_script);
 
 
+	bool _cyclical_dependency_exists(const String& p_target_scene_path, Node* p_desired_node);
 
 
 	void _node_selected();
 	void _node_selected();
 	void _node_renamed();
 	void _node_renamed();

+ 2 - 2
tools/export/blender25/io_scene_dae/export_dae.py

@@ -212,8 +212,8 @@ class DaeExporter:
 		imgid = self.new_id("image")
 		imgid = self.new_id("image")
 		
 		
 		if (not os.path.isfile(imgpath)):
 		if (not os.path.isfile(imgpath)):
-			if img_tmp_path.endswith((".bmp",".rgb",".png",".jpeg",".jpg",".jp2",".tga",".cin",".dpx",".exr",".hdr",".tif")):
-				imgpath="images/"+os.path.basename(img_tmp_path)
+			if imgpath.endswith((".bmp",".rgb",".png",".jpeg",".jpg",".jp2",".tga",".cin",".dpx",".exr",".hdr",".tif")):
+				imgpath="images/"+os.path.basename(imgpath)
 			else:
 			else:
 				imgpath="images/"+image.name+".png"
 				imgpath="images/"+image.name+".png"
 		
 		

Some files were not shown because too many files changed in this diff