Browse Source

Merge pull request #33569 from akien-mga/3.1

Assorted cherry-picks from the master branch for Godot 3.1.2 [4th batch]
Rémi Verschelde 5 years ago
parent
commit
7f6e2c5037
100 changed files with 700 additions and 327 deletions
  1. 5 5
      COPYRIGHT.txt
  2. 2 0
      core/SCsub
  3. 6 0
      core/io/http_client.cpp
  4. 5 1
      core/io/marshalls.cpp
  5. 6 4
      core/io/multiplayer_api.cpp
  6. 4 2
      core/math/geometry.h
  7. 1 5
      core/math/vector3.h
  8. 7 5
      core/message_queue.cpp
  9. 5 7
      core/object.cpp
  10. 6 1
      core/object.h
  11. 1 0
      core/os/input.cpp
  12. 2 2
      core/os/input.h
  13. 11 2
      core/os/input_event.cpp
  14. 10 0
      core/os/os.cpp
  15. 3 2
      core/os/os.h
  16. 16 2
      core/translation.cpp
  17. 6 0
      doc/classes/EditorExportPlugin.xml
  18. 6 1
      doc/classes/ParticlesMaterial.xml
  19. 6 0
      doc/classes/TileMap.xml
  20. 2 2
      drivers/gles2/rasterizer_scene_gles2.cpp
  21. 1 1
      drivers/gles3/rasterizer_scene_gles3.cpp
  22. 8 0
      drivers/pulseaudio/audio_driver_pulseaudio.cpp
  23. 46 38
      editor/animation_track_editor.cpp
  24. 7 7
      editor/animation_track_editor.h
  25. 1 1
      editor/editor_autoload_settings.cpp
  26. 1 0
      editor/editor_data.cpp
  27. 1 0
      editor/editor_export.cpp
  28. 2 1
      editor/editor_help.cpp
  29. 50 26
      editor/editor_node.cpp
  30. 9 2
      editor/editor_node.h
  31. 11 1
      editor/editor_properties.cpp
  32. 15 0
      editor/editor_settings.cpp
  33. 30 1
      editor/filesystem_dock.cpp
  34. 1 0
      editor/filesystem_dock.h
  35. 1 2
      editor/find_in_files.cpp
  36. 56 0
      editor/icons/icon_gizmo_c_p_u_particles.svg
  37. 0 5
      editor/icons/icon_key_valid.svg
  38. 7 6
      editor/import/editor_scene_importer_gltf.cpp
  39. 1 1
      editor/import/resource_importer_scene.cpp
  40. 2 1
      editor/plugins/animation_player_editor_plugin.cpp
  41. 4 0
      editor/plugins/asset_library_editor_plugin.cpp
  42. 20 7
      editor/plugins/canvas_item_editor_plugin.cpp
  43. 12 2
      editor/plugins/script_editor_plugin.cpp
  44. 13 5
      editor/plugins/script_text_editor.cpp
  45. 1 1
      editor/plugins/script_text_editor.h
  46. 11 1
      editor/plugins/shader_editor_plugin.cpp
  47. 2 2
      editor/plugins/shader_editor_plugin.h
  48. 1 0
      editor/plugins/spatial_editor_plugin.cpp
  49. 1 1
      editor/plugins/text_editor.cpp
  50. 1 3
      editor/plugins/theme_editor_plugin.cpp
  51. 17 10
      editor/plugins/tile_map_editor_plugin.cpp
  52. 6 18
      editor/project_manager.cpp
  53. 1 1
      editor/project_manager.h
  54. 32 2
      editor/spatial_editor_gizmos.cpp
  55. 12 0
      editor/spatial_editor_gizmos.h
  56. 7 16
      main/input_default.cpp
  57. 2 2
      main/input_default.h
  58. 5 1
      main/main.cpp
  59. 1 0
      methods.py
  60. 7 2
      misc/dist/html/full-size.html
  61. 3 0
      modules/csg/csg_shape.cpp
  62. 1 1
      modules/gdnative/gdnative.cpp
  63. 4 0
      modules/gdnative/gdnative.h
  64. 1 1
      modules/gdnative/gdnative/variant.cpp
  65. 5 2
      modules/gdnative/nativescript/nativescript.cpp
  66. 6 0
      modules/gdscript/gdscript.cpp
  67. 1 0
      modules/gdscript/gdscript.h
  68. 35 27
      modules/gdscript/gdscript_function.cpp
  69. 23 3
      modules/gdscript/gdscript_parser.cpp
  70. 3 3
      modules/mono/config.py
  71. 7 2
      modules/mono/csharp_script.cpp
  72. 12 3
      modules/mono/csharp_script.h
  73. 1 1
      modules/mono/doc_classes/@C#.xml
  74. 1 1
      modules/mono/doc_classes/CSharpScript.xml
  75. 1 1
      modules/mono/doc_classes/GodotSharp.xml
  76. 1 1
      modules/mono/editor/csharp_project.cpp
  77. 8 7
      modules/mono/mono_gd/gd_mono.cpp
  78. 1 0
      modules/regex/SCsub
  79. 2 2
      modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
  80. 2 1
      modules/upnp/SCsub
  81. 2 13
      modules/visual_script/visual_script.cpp
  82. 0 1
      modules/visual_script/visual_script.h
  83. 1 1
      modules/visual_script/visual_script_editor.cpp
  84. 1 1
      platform/SCsub
  85. 1 1
      platform/android/build.gradle.template
  86. 11 15
      platform/android/java/THIRDPARTY.md
  87. 1 1
      platform/android/java/gradle/wrapper/gradle-wrapper.properties
  88. 2 1
      platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java
  89. 2 1
      platform/android/java/src/com/google/android/vending/licensing/LicenseChecker.java
  90. 0 8
      platform/android/os_android.cpp
  91. 0 3
      platform/android/os_android.h
  92. 4 0
      platform/haiku/os_haiku.cpp
  93. 1 0
      platform/haiku/os_haiku.h
  94. 0 6
      platform/iphone/os_iphone.cpp
  95. 0 3
      platform/iphone/os_iphone.h
  96. 4 0
      platform/iphone/view_controller.h
  97. 12 0
      platform/iphone/view_controller.mm
  98. 18 4
      platform/javascript/os_javascript.cpp
  99. 1 0
      platform/osx/os_osx.h
  100. 5 4
      platform/osx/os_osx.mm

+ 5 - 5
COPYRIGHT.txt

@@ -165,7 +165,7 @@ License: FTL
 
 
 Files: ./thirdparty/glad/
 Files: ./thirdparty/glad/
 Comment: glad
 Comment: glad
-Copyright: 2013-2018, David Herberth
+Copyright: 2013-2019, David Herberth
 License: Expat
 License: Expat
 
 
 Files: ./thirdparty/jpeg_compressor/
 Files: ./thirdparty/jpeg_compressor/
@@ -321,7 +321,7 @@ License: BSD-3-clause
 Files: ./thirdparty/misc/stb_truetype.h
 Files: ./thirdparty/misc/stb_truetype.h
  ./thirdparty/misc/stb_vorbis.c
  ./thirdparty/misc/stb_vorbis.c
 Comment: stb libraries
 Comment: stb libraries
-Copyright: 2007-2017, Sean Barrett
+Copyright: 2007-2019, Sean Barrett
 License: public-domain
 License: public-domain
 
 
 Files: ./thirdparty/misc/triangulator.cpp
 Files: ./thirdparty/misc/triangulator.cpp
@@ -350,8 +350,8 @@ License: BSD-3-clause
 
 
 Files: ./thirdparty/pcre2/
 Files: ./thirdparty/pcre2/
 Comment: PCRE2
 Comment: PCRE2
-Copyright: 1997-2018, University of Cambridge,
- 2009-2018, Zoltan Herczeg
+Copyright: 1997-2019, University of Cambridge,
+ 2009-2019, Zoltan Herczeg
 License: BSD-3-clause
 License: BSD-3-clause
 
 
 Files: ./thirdparty/pvrtccompressor/
 Files: ./thirdparty/pvrtccompressor/
@@ -371,7 +371,7 @@ License: Expat
 
 
 Files: ./thirdparty/tinyexr/
 Files: ./thirdparty/tinyexr/
 Comment: TinyEXR
 Comment: TinyEXR
-Copyright: 2014-2018, Syoyo Fujita
+Copyright: 2014-2019, Syoyo Fujita
   2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC
   2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC
 License: BSD-3-clause
 License: BSD-3-clause
 
 

+ 2 - 0
core/SCsub

@@ -121,6 +121,8 @@ if env['builtin_zstd']:
         "compress/zstd_ldm.c",
         "compress/zstd_ldm.c",
         "compress/zstd_opt.c",
         "compress/zstd_opt.c",
         "compress/zstdmt_compress.c",
         "compress/zstdmt_compress.c",
+        "compress/zstd_compress_literals.c",
+        "compress/zstd_compress_sequences.c",
         "decompress/huf_decompress.c",
         "decompress/huf_decompress.c",
         "decompress/zstd_ddict.c",
         "decompress/zstd_ddict.c",
         "decompress/zstd_decompress_block.c",
         "decompress/zstd_decompress_block.c",

+ 6 - 0
core/io/http_client.cpp

@@ -346,6 +346,12 @@ Error HTTPClient::poll() {
 						} else {
 						} else {
 							// We are already handshaking, which means we can use your already active SSL connection
 							// We are already handshaking, which means we can use your already active SSL connection
 							ssl = static_cast<Ref<StreamPeerSSL> >(connection);
 							ssl = static_cast<Ref<StreamPeerSSL> >(connection);
+							if (ssl.is_null()) {
+								close();
+								status = STATUS_SSL_HANDSHAKE_ERROR;
+								return ERR_CANT_CONNECT;
+							}
+
 							ssl->poll(); // Try to finish the handshake
 							ssl->poll(); // Try to finish the handshake
 						}
 						}
 
 

+ 5 - 1
core/io/marshalls.cpp

@@ -1230,11 +1230,15 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 				buf += 4;
 				buf += 4;
 				PoolVector<uint8_t>::Read r = data.read();
 				PoolVector<uint8_t>::Read r = data.read();
 				copymem(buf, &r[0], datalen * datasize);
 				copymem(buf, &r[0], datalen * datasize);
+				buf += datalen * datasize;
 			}
 			}
 
 
 			r_len += 4 + datalen * datasize;
 			r_len += 4 + datalen * datasize;
-			while (r_len % 4)
+			while (r_len % 4) {
 				r_len++;
 				r_len++;
+				if (buf)
+					*(buf++) = 0;
+			}
 
 
 		} break;
 		} break;
 		case Variant::POOL_INT_ARRAY: {
 		case Variant::POOL_INT_ARRAY: {

+ 6 - 4
core/io/multiplayer_api.cpp

@@ -283,8 +283,9 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
 		rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name);
 		rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name);
 	}
 	}
 
 
-	ERR_EXPLAIN("RPC '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
-	ERR_FAIL_COND(!_can_call_mode(p_node, rpc_mode, p_from));
+	bool can_call = _can_call_mode(p_node, rpc_mode, p_from);
+	ERR_EXPLAIN("RPC '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
+	ERR_FAIL_COND(!can_call);
 
 
 	int argc = p_packet[p_offset];
 	int argc = p_packet[p_offset];
 	Vector<Variant> args;
 	Vector<Variant> args;
@@ -332,8 +333,9 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p
 		rset_mode = p_node->get_script_instance()->get_rset_mode(p_name);
 		rset_mode = p_node->get_script_instance()->get_rset_mode(p_name);
 	}
 	}
 
 
-	ERR_EXPLAIN("RSET '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
-	ERR_FAIL_COND(!_can_call_mode(p_node, rset_mode, p_from));
+	bool can_call = _can_call_mode(p_node, rset_mode, p_from);
+	ERR_EXPLAIN("RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
+	ERR_FAIL_COND(!can_call);
 
 
 	Variant value;
 	Variant value;
 	Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed());
 	Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed());

+ 4 - 2
core/math/geometry.h

@@ -702,9 +702,11 @@ public:
 		/* if we can assume that the line segment starts outside the circle (e.g. for continuous time collision detection) then the following can be skipped and we can just return the equivalent of res1 */
 		/* if we can assume that the line segment starts outside the circle (e.g. for continuous time collision detection) then the following can be skipped and we can just return the equivalent of res1 */
 		sqrtterm = Math::sqrt(sqrtterm);
 		sqrtterm = Math::sqrt(sqrtterm);
 		real_t res1 = (-b - sqrtterm) / (2 * a);
 		real_t res1 = (-b - sqrtterm) / (2 * a);
-		//real_t res2 = ( -b + sqrtterm ) / (2 * a);
+		real_t res2 = (-b + sqrtterm) / (2 * a);
 
 
-		return (res1 >= 0 && res1 <= 1) ? res1 : -1;
+		if (res1 >= 0 && res1 <= 1) return res1;
+		if (res2 >= 0 && res2 <= 1) return res2;
+		return -1;
 	}
 	}
 
 
 	static inline Vector<Vector3> clip_polygon(const Vector<Vector3> &polygon, const Plane &p_plane) {
 	static inline Vector<Vector3> clip_polygon(const Vector<Vector3> &polygon, const Plane &p_plane) {

+ 1 - 5
core/math/vector3.h

@@ -218,12 +218,8 @@ Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const {
 }
 }
 
 
 Vector3 Vector3::slerp(const Vector3 &p_b, real_t p_t) const {
 Vector3 Vector3::slerp(const Vector3 &p_b, real_t p_t) const {
-#ifdef MATH_CHECKS
-	ERR_FAIL_COND_V(!is_normalized(), Vector3());
-#endif
-
 	real_t theta = angle_to(p_b);
 	real_t theta = angle_to(p_b);
-	return rotated(cross(p_b), theta * p_t);
+	return rotated(cross(p_b).normalized(), theta * p_t);
 }
 }
 
 
 real_t Vector3::distance_to(const Vector3 &p_b) const {
 real_t Vector3::distance_to(const Vector3 &p_b) const {

+ 7 - 5
core/message_queue.cpp

@@ -302,10 +302,6 @@ void MessageQueue::flush() {
 
 
 					_call_function(target, message->target, args, message->args, message->type & FLAG_SHOW_ERROR);
 					_call_function(target, message->target, args, message->args, message->type & FLAG_SHOW_ERROR);
 
 
-					for (int i = 0; i < message->args; i++) {
-						args[i].~Variant();
-					}
-
 				} break;
 				} break;
 				case TYPE_NOTIFICATION: {
 				case TYPE_NOTIFICATION: {
 
 
@@ -319,11 +315,17 @@ void MessageQueue::flush() {
 					// messages don't expect a return value
 					// messages don't expect a return value
 					target->set(message->target, *arg);
 					target->set(message->target, *arg);
 
 
-					arg->~Variant();
 				} break;
 				} break;
 			}
 			}
 		}
 		}
 
 
+		if ((message->type & FLAG_MASK) != TYPE_NOTIFICATION) {
+			Variant *args = (Variant *)(message + 1);
+			for (int i = 0; i < message->args; i++) {
+				args[i].~Variant();
+			}
+		}
+
 		message->~Message();
 		message->~Message();
 
 
 		_THREAD_SAFE_LOCK_
 		_THREAD_SAFE_LOCK_

+ 5 - 7
core/object.cpp

@@ -608,18 +608,16 @@ Variant Object::get_indexed(const Vector<StringName> &p_names, bool *r_valid) co
 	}
 	}
 	bool valid = false;
 	bool valid = false;
 
 
-	Variant current_value = get(p_names[0]);
+	Variant current_value = get(p_names[0], &valid);
 	for (int i = 1; i < p_names.size(); i++) {
 	for (int i = 1; i < p_names.size(); i++) {
 		current_value = current_value.get_named(p_names[i], &valid);
 		current_value = current_value.get_named(p_names[i], &valid);
 
 
-		if (!valid) {
-			if (r_valid)
-				*r_valid = false;
-			return Variant();
-		}
+		if (!valid)
+			break;
 	}
 	}
 	if (r_valid)
 	if (r_valid)
-		*r_valid = true;
+		*r_valid = valid;
+
 	return current_value;
 	return current_value;
 }
 }
 
 

+ 6 - 1
core/object.h

@@ -779,8 +779,13 @@ public:
 	static int get_object_count();
 	static int get_object_count();
 
 
 	_FORCE_INLINE_ static bool instance_validate(Object *p_ptr) {
 	_FORCE_INLINE_ static bool instance_validate(Object *p_ptr) {
+		rw_lock->read_lock();
 
 
-		return instance_checks.has(p_ptr);
+		bool exists = instance_checks.has(p_ptr);
+
+		rw_lock->read_unlock();
+
+		return exists;
 	}
 	}
 };
 };
 
 

+ 1 - 0
core/os/input.cpp

@@ -90,6 +90,7 @@ void Input::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("action_press", "action", "strength"), &Input::action_press, DEFVAL(1.f));
 	ClassDB::bind_method(D_METHOD("action_press", "action", "strength"), &Input::action_press, DEFVAL(1.f));
 	ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release);
 	ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release);
 	ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW));
 	ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW));
+	ClassDB::bind_method(D_METHOD("get_current_cursor_shape"), &Input::get_current_cursor_shape);
 	ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2()));
 	ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2()));
 	ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event);
 	ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event);
 	ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input);
 	ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input);

+ 2 - 2
core/os/input.h

@@ -122,10 +122,10 @@ public:
 	virtual bool is_emulating_touch_from_mouse() const = 0;
 	virtual bool is_emulating_touch_from_mouse() const = 0;
 	virtual bool is_emulating_mouse_from_touch() const = 0;
 	virtual bool is_emulating_mouse_from_touch() const = 0;
 
 
-	virtual CursorShape get_default_cursor_shape() = 0;
+	virtual CursorShape get_default_cursor_shape() const = 0;
 	virtual void set_default_cursor_shape(CursorShape p_shape) = 0;
 	virtual void set_default_cursor_shape(CursorShape p_shape) = 0;
+	virtual CursorShape get_current_cursor_shape() const = 0;
 	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) = 0;
 	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) = 0;
-	virtual void set_mouse_in_window(bool p_in_window) = 0;
 
 
 	virtual String get_joy_button_string(int p_button) = 0;
 	virtual String get_joy_button_string(int p_button) = 0;
 	virtual String get_joy_axis_string(int p_axis) = 0;
 	virtual String get_joy_axis_string(int p_axis) = 0;

+ 11 - 2
core/os/input_event.cpp

@@ -717,8 +717,17 @@ bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool *
 		bool pressed = same_direction ? Math::abs(jm->get_axis_value()) >= p_deadzone : false;
 		bool pressed = same_direction ? Math::abs(jm->get_axis_value()) >= p_deadzone : false;
 		if (p_pressed != NULL)
 		if (p_pressed != NULL)
 			*p_pressed = pressed;
 			*p_pressed = pressed;
-		if (p_strength != NULL)
-			*p_strength = pressed ? CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, Math::abs(jm->get_axis_value())), 0.0f, 1.0f) : 0.0f;
+		if (p_strength != NULL) {
+			if (pressed) {
+				if (p_deadzone == 1.0f) {
+					*p_strength = 1.0f;
+				} else {
+					*p_strength = CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, Math::abs(jm->get_axis_value())), 0.0f, 1.0f);
+				}
+			} else {
+				*p_strength = 0.0f;
+			}
+		}
 	}
 	}
 	return match;
 	return match;
 }
 }

+ 10 - 0
core/os/os.cpp

@@ -225,6 +225,16 @@ int OS::get_virtual_keyboard_height() const {
 	return 0;
 	return 0;
 }
 }
 
 
+void OS::set_cursor_shape(CursorShape p_shape) {
+}
+
+OS::CursorShape OS::get_cursor_shape() const {
+	return CURSOR_ARROW;
+}
+
+void OS::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+}
+
 void OS::print_all_resources(String p_to_file) {
 void OS::print_all_resources(String p_to_file) {
 
 
 	ERR_FAIL_COND(p_to_file != "" && _OSPRF);
 	ERR_FAIL_COND(p_to_file != "" && _OSPRF);

+ 3 - 2
core/os/os.h

@@ -378,8 +378,9 @@ public:
 	// returns height of the currently shown virtual keyboard (0 if keyboard is hidden)
 	// returns height of the currently shown virtual keyboard (0 if keyboard is hidden)
 	virtual int get_virtual_keyboard_height() const;
 	virtual int get_virtual_keyboard_height() const;
 
 
-	virtual void set_cursor_shape(CursorShape p_shape) = 0;
-	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) = 0;
+	virtual void set_cursor_shape(CursorShape p_shape);
+	virtual CursorShape get_cursor_shape() const;
+	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
 
 
 	virtual bool get_swap_ok_cancel() { return false; }
 	virtual bool get_swap_ok_cancel() { return false; }
 	virtual void dump_memory_to_file(const char *p_file);
 	virtual void dump_memory_to_file(const char *p_file);

+ 16 - 2
core/translation.cpp

@@ -179,6 +179,7 @@ static const char *locale_list[] = {
 	"ff_SN", //  Fulah (Senegal)
 	"ff_SN", //  Fulah (Senegal)
 	"fi", //  Finnish
 	"fi", //  Finnish
 	"fi_FI", //  Finnish (Finland)
 	"fi_FI", //  Finnish (Finland)
+	"fil", //  Filipino
 	"fil_PH", //  Filipino (Philippines)
 	"fil_PH", //  Filipino (Philippines)
 	"fo_FO", //  Faroese (Faroe Islands)
 	"fo_FO", //  Faroese (Faroe Islands)
 	"fr", //  French
 	"fr", //  French
@@ -227,6 +228,7 @@ static const char *locale_list[] = {
 	"ja", //  Japanese
 	"ja", //  Japanese
 	"ja_JP", //  Japanese (Japan)
 	"ja_JP", //  Japanese (Japan)
 	"kab_DZ", //  Kabyle (Algeria)
 	"kab_DZ", //  Kabyle (Algeria)
+	"ka", //  Georgian
 	"ka_GE", //  Georgian (Georgia)
 	"ka_GE", //  Georgian (Georgia)
 	"kk_KZ", //  Kazakh (Kazakhstan)
 	"kk_KZ", //  Kazakh (Kazakhstan)
 	"kl_GL", //  Kalaallisut (Greenland)
 	"kl_GL", //  Kalaallisut (Greenland)
@@ -257,10 +259,12 @@ static const char *locale_list[] = {
 	"mg_MG", //  Malagasy (Madagascar)
 	"mg_MG", //  Malagasy (Madagascar)
 	"mh_MH", //  Marshallese (Marshall Islands)
 	"mh_MH", //  Marshallese (Marshall Islands)
 	"mhr_RU", //  Eastern Mari (Russia)
 	"mhr_RU", //  Eastern Mari (Russia)
-	"mi_NZ", //  Maori (New Zealand)
+	"mi", //  Māori
+	"mi_NZ", //  Māori (New Zealand)
 	"miq_NI", //  Mískito (Nicaragua)
 	"miq_NI", //  Mískito (Nicaragua)
 	"mk", //  Macedonian
 	"mk", //  Macedonian
 	"mk_MK", //  Macedonian (Macedonia)
 	"mk_MK", //  Macedonian (Macedonia)
+	"ml", //  Malayalam
 	"ml_IN", //  Malayalam (India)
 	"ml_IN", //  Malayalam (India)
 	"mni_IN", //  Manipuri (India)
 	"mni_IN", //  Manipuri (India)
 	"mn_MN", //  Mongolian (Mongolia)
 	"mn_MN", //  Mongolian (Mongolia)
@@ -326,6 +330,7 @@ static const char *locale_list[] = {
 	"sgs_LT", //  Samogitian (Lithuania)
 	"sgs_LT", //  Samogitian (Lithuania)
 	"shs_CA", //  Shuswap (Canada)
 	"shs_CA", //  Shuswap (Canada)
 	"sid_ET", //  Sidamo (Ethiopia)
 	"sid_ET", //  Sidamo (Ethiopia)
+	"si", //  Sinhala
 	"si_LK", //  Sinhala (Sri Lanka)
 	"si_LK", //  Sinhala (Sri Lanka)
 	"sk", //  Slovak
 	"sk", //  Slovak
 	"sk_SK", //  Slovak (Slovakia)
 	"sk_SK", //  Slovak (Slovakia)
@@ -343,6 +348,7 @@ static const char *locale_list[] = {
 	"sq_MK", //  Albanian (Macedonia)
 	"sq_MK", //  Albanian (Macedonia)
 	"sr", //  Serbian
 	"sr", //  Serbian
 	"sr_Cyrl", //  Serbian (Cyrillic)
 	"sr_Cyrl", //  Serbian (Cyrillic)
+	"sr_Latn", //  Serbian (Latin)
 	"sr_ME", //  Serbian (Montenegro)
 	"sr_ME", //  Serbian (Montenegro)
 	"sr_RS", //  Serbian (Serbia)
 	"sr_RS", //  Serbian (Serbia)
 	"ss_ZA", //  Swati (South Africa)
 	"ss_ZA", //  Swati (South Africa)
@@ -357,6 +363,7 @@ static const char *locale_list[] = {
 	"ta_IN", //  Tamil (India)
 	"ta_IN", //  Tamil (India)
 	"ta_LK", //  Tamil (Sri Lanka)
 	"ta_LK", //  Tamil (Sri Lanka)
 	"tcy_IN", //  Tulu (India)
 	"tcy_IN", //  Tulu (India)
+	"te", //  Telugu
 	"te_IN", //  Telugu (India)
 	"te_IN", //  Telugu (India)
 	"tg_TJ", //  Tajik (Tajikistan)
 	"tg_TJ", //  Tajik (Tajikistan)
 	"the_NP", //  Chitwania Tharu (Nepal)
 	"the_NP", //  Chitwania Tharu (Nepal)
@@ -540,6 +547,7 @@ static const char *locale_names[] = {
 	"Fulah (Senegal)",
 	"Fulah (Senegal)",
 	"Finnish",
 	"Finnish",
 	"Finnish (Finland)",
 	"Finnish (Finland)",
+	"Filipino",
 	"Filipino (Philippines)",
 	"Filipino (Philippines)",
 	"Faroese (Faroe Islands)",
 	"Faroese (Faroe Islands)",
 	"French",
 	"French",
@@ -588,6 +596,7 @@ static const char *locale_names[] = {
 	"Japanese",
 	"Japanese",
 	"Japanese (Japan)",
 	"Japanese (Japan)",
 	"Kabyle (Algeria)",
 	"Kabyle (Algeria)",
+	"Georgian",
 	"Georgian (Georgia)",
 	"Georgian (Georgia)",
 	"Kazakh (Kazakhstan)",
 	"Kazakh (Kazakhstan)",
 	"Kalaallisut (Greenland)",
 	"Kalaallisut (Greenland)",
@@ -618,10 +627,12 @@ static const char *locale_names[] = {
 	"Malagasy (Madagascar)",
 	"Malagasy (Madagascar)",
 	"Marshallese (Marshall Islands)",
 	"Marshallese (Marshall Islands)",
 	"Eastern Mari (Russia)",
 	"Eastern Mari (Russia)",
-	"Maori (New Zealand)",
+	"Māori",
+	"Māori (New Zealand)",
 	"Mískito (Nicaragua)",
 	"Mískito (Nicaragua)",
 	"Macedonian",
 	"Macedonian",
 	"Macedonian (Macedonia)",
 	"Macedonian (Macedonia)",
+	"Malayalam",
 	"Malayalam (India)",
 	"Malayalam (India)",
 	"Manipuri (India)",
 	"Manipuri (India)",
 	"Mongolian (Mongolia)",
 	"Mongolian (Mongolia)",
@@ -687,6 +698,7 @@ static const char *locale_names[] = {
 	"Samogitian (Lithuania)",
 	"Samogitian (Lithuania)",
 	"Shuswap (Canada)",
 	"Shuswap (Canada)",
 	"Sidamo (Ethiopia)",
 	"Sidamo (Ethiopia)",
+	"Sinhala",
 	"Sinhala (Sri Lanka)",
 	"Sinhala (Sri Lanka)",
 	"Slovak",
 	"Slovak",
 	"Slovak (Slovakia)",
 	"Slovak (Slovakia)",
@@ -704,6 +716,7 @@ static const char *locale_names[] = {
 	"Albanian (Macedonia)",
 	"Albanian (Macedonia)",
 	"Serbian",
 	"Serbian",
 	"Serbian (Cyrillic)",
 	"Serbian (Cyrillic)",
+	"Serbian (Latin)",
 	"Serbian (Montenegro)",
 	"Serbian (Montenegro)",
 	"Serbian (Serbia)",
 	"Serbian (Serbia)",
 	"Swati (South Africa)",
 	"Swati (South Africa)",
@@ -718,6 +731,7 @@ static const char *locale_names[] = {
 	"Tamil (India)",
 	"Tamil (India)",
 	"Tamil (Sri Lanka)",
 	"Tamil (Sri Lanka)",
 	"Tulu (India)",
 	"Tulu (India)",
+	"Telugu",
 	"Telugu (India)",
 	"Telugu (India)",
 	"Tajik (Tajikistan)",
 	"Tajik (Tajikistan)",
 	"Chitwania Tharu (Nepal)",
 	"Chitwania Tharu (Nepal)",

+ 6 - 0
doc/classes/EditorExportPlugin.xml

@@ -21,6 +21,12 @@
 			<description>
 			<description>
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="_export_end" qualifiers="virtual">
+			<return type="void">
+			</return>
+			<description>
+			</description>
+		</method>
 		<method name="_export_file" qualifiers="virtual">
 		<method name="_export_file" qualifiers="virtual">
 			<return type="void">
 			<return type="void">
 			</return>
 			</return>

+ 6 - 1
doc/classes/ParticlesMaterial.xml

@@ -214,14 +214,19 @@
 			Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set animation offset properties.
 			Use with [method set_param], [method set_param_randomness], and [method set_param_texture] to set animation offset properties.
 		</constant>
 		</constant>
 		<constant name="PARAM_MAX" value="12" enum="Parameter">
 		<constant name="PARAM_MAX" value="12" enum="Parameter">
+			Represents the size of the [enum Parameter] enum.
 		</constant>
 		</constant>
 		<constant name="FLAG_ALIGN_Y_TO_VELOCITY" value="0" enum="Flags">
 		<constant name="FLAG_ALIGN_Y_TO_VELOCITY" value="0" enum="Flags">
 			Use with [method set_flag] to set [member flag_align_y].
 			Use with [method set_flag] to set [member flag_align_y].
 		</constant>
 		</constant>
 		<constant name="FLAG_ROTATE_Y" value="1" enum="Flags">
 		<constant name="FLAG_ROTATE_Y" value="1" enum="Flags">
-			Use with [method set_flag] to set [member flag_rotate_y]
+			Use with [method set_flag] to set [member flag_rotate_y].
+		</constant>
+		<constant name="FLAG_DISABLE_Z" value="2" enum="Flags">
+			Use with [method set_flag] to set [member flag_disable_z].
 		</constant>
 		</constant>
 		<constant name="FLAG_MAX" value="3" enum="Flags">
 		<constant name="FLAG_MAX" value="3" enum="Flags">
+			Represents the size of the [enum Flags] enum.
 		</constant>
 		</constant>
 		<constant name="EMISSION_SHAPE_POINT" value="0" enum="EmissionShape">
 		<constant name="EMISSION_SHAPE_POINT" value="0" enum="EmissionShape">
 			All particles will be emitted from a single point.
 			All particles will be emitted from a single point.

+ 6 - 0
doc/classes/TileMap.xml

@@ -321,6 +321,12 @@
 		<constant name="HALF_OFFSET_DISABLED" value="2" enum="HalfOffset">
 		<constant name="HALF_OFFSET_DISABLED" value="2" enum="HalfOffset">
 			Half offset disabled.
 			Half offset disabled.
 		</constant>
 		</constant>
+		<constant name="HALF_OFFSET_NEGATIVE_X" value="3" enum="HalfOffset">
+			Half offset on the X coordinate (negative).
+		</constant>
+		<constant name="HALF_OFFSET_NEGATIVE_Y" value="4" enum="HalfOffset">
+			Half offset on the Y coordinate (negative).
+		</constant>
 		<constant name="TILE_ORIGIN_TOP_LEFT" value="0" enum="TileOrigin">
 		<constant name="TILE_ORIGIN_TOP_LEFT" value="0" enum="TileOrigin">
 			Tile origin at its top-left corner.
 			Tile origin at its top-left corner.
 		</constant>
 		</constant>

+ 2 - 2
drivers/gles2/rasterizer_scene_gles2.cpp

@@ -1110,8 +1110,8 @@ void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::G
 
 
 				LightInstance *li = light_instance_owner.getornull(e->instance->light_instances[i]);
 				LightInstance *li = light_instance_owner.getornull(e->instance->light_instances[i]);
 
 
-				if (li->light_index >= render_light_instance_count) {
-					continue; // too many
+				if (li->light_index >= render_light_instance_count || render_light_instances[li->light_index] != li) {
+					continue; // too many or light_index did not correspond to the light instances to be rendered
 				}
 				}
 
 
 				if (copy) {
 				if (copy) {

+ 1 - 1
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -2354,7 +2354,7 @@ void RasterizerSceneGLES3::_add_geometry_with_material(RasterizerStorageGLES3::G
 
 
 	if (p_depth_pass) {
 	if (p_depth_pass) {
 
 
-		if (has_blend_alpha || p_material->shader->spatial.uses_depth_texture || (has_base_alpha && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES3::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) || p_material->shader->spatial.depth_draw_mode == RasterizerStorageGLES3::Shader::Spatial::DEPTH_DRAW_NEVER || p_material->shader->spatial.no_depth_test)
+		if (has_blend_alpha || p_material->shader->spatial.uses_depth_texture || (has_base_alpha && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES3::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) || p_material->shader->spatial.depth_draw_mode == RasterizerStorageGLES3::Shader::Spatial::DEPTH_DRAW_NEVER || p_material->shader->spatial.no_depth_test || p_instance->cast_shadows == VS::SHADOW_CASTING_SETTING_OFF)
 			return; //bye
 			return; //bye
 
 
 		if (!p_material->shader->spatial.uses_alpha_scissor && !p_material->shader->spatial.writes_modelview_or_projection && !p_material->shader->spatial.uses_vertex && !p_material->shader->spatial.uses_discard && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES3::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) {
 		if (!p_material->shader->spatial.uses_alpha_scissor && !p_material->shader->spatial.writes_modelview_or_projection && !p_material->shader->spatial.uses_vertex && !p_material->shader->spatial.uses_discard && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES3::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) {

+ 8 - 0
drivers/pulseaudio/audio_driver_pulseaudio.cpp

@@ -191,6 +191,14 @@ Error AudioDriverPulseAudio::init_device() {
 	spec.format = PA_SAMPLE_S16LE;
 	spec.format = PA_SAMPLE_S16LE;
 	spec.channels = pa_map.channels;
 	spec.channels = pa_map.channels;
 	spec.rate = mix_rate;
 	spec.rate = mix_rate;
+	pa_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+	pa_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+	pa_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+	pa_map.map[3] = PA_CHANNEL_POSITION_LFE;
+	pa_map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
+	pa_map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
+	pa_map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
+	pa_map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
 
 
 	pa_str = pa_stream_new(pa_ctx, "Sound", &spec, &pa_map);
 	pa_str = pa_stream_new(pa_ctx, "Sound", &spec, &pa_map);
 	if (pa_str == NULL) {
 	if (pa_str == NULL) {

+ 46 - 38
editor/animation_track_editor.cpp

@@ -544,7 +544,7 @@ public:
 
 
 		if (use_fps && animation->get_step() > 0) {
 		if (use_fps && animation->get_step() > 0) {
 			float max_frame = animation->get_length() / animation->get_step();
 			float max_frame = animation->get_length() / animation->get_step();
-			p_list->push_back(PropertyInfo(Variant::REAL, "frame", PROPERTY_HINT_RANGE, "0," + rtos(max_frame) + ",0.01"));
+			p_list->push_back(PropertyInfo(Variant::REAL, "frame", PROPERTY_HINT_RANGE, "0," + rtos(max_frame) + ",1"));
 		} else {
 		} else {
 			p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01"));
 			p_list->push_back(PropertyInfo(Variant::REAL, "time", PROPERTY_HINT_RANGE, "0," + rtos(animation->get_length()) + ",0.01"));
 		}
 		}
@@ -1205,7 +1205,9 @@ AnimationTimelineEdit::AnimationTimelineEdit() {
 ////////////////////////////////////
 ////////////////////////////////////
 
 
 void AnimationTrackEdit::_notification(int p_what) {
 void AnimationTrackEdit::_notification(int p_what) {
+
 	if (p_what == NOTIFICATION_DRAW) {
 	if (p_what == NOTIFICATION_DRAW) {
+
 		if (animation.is_null())
 		if (animation.is_null())
 			return;
 			return;
 		ERR_FAIL_INDEX(track, animation->get_track_count());
 		ERR_FAIL_INDEX(track, animation->get_track_count());
@@ -1241,20 +1243,15 @@ void AnimationTrackEdit::_notification(int p_what) {
 			int ofs = in_group ? check->get_width() : 0; //not the best reference for margin but..
 			int ofs = in_group ? check->get_width() : 0; //not the best reference for margin but..
 
 
 			check_rect = Rect2(Point2(ofs, int(get_size().height - check->get_height()) / 2), check->get_size());
 			check_rect = Rect2(Point2(ofs, int(get_size().height - check->get_height()) / 2), check->get_size());
-
 			draw_texture(check, check_rect.position);
 			draw_texture(check, check_rect.position);
-
 			ofs += check->get_width() + hsep;
 			ofs += check->get_width() + hsep;
 
 
 			Ref<Texture> type_icon = type_icons[animation->track_get_type(track)];
 			Ref<Texture> type_icon = type_icons[animation->track_get_type(track)];
-
 			draw_texture(type_icon, Point2(ofs, int(get_size().height - type_icon->get_height()) / 2));
 			draw_texture(type_icon, Point2(ofs, int(get_size().height - type_icon->get_height()) / 2));
 			ofs += type_icon->get_width() + hsep;
 			ofs += type_icon->get_width() + hsep;
 
 
 			NodePath path = animation->track_get_path(track);
 			NodePath path = animation->track_get_path(track);
-
 			Node *node = NULL;
 			Node *node = NULL;
-
 			if (root && root->has_node(path)) {
 			if (root && root->has_node(path)) {
 				node = root->get_node(path);
 				node = root->get_node(path);
 			}
 			}
@@ -1319,12 +1316,11 @@ void AnimationTrackEdit::_notification(int p_what) {
 			draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor, Math::round(EDSCALE));
 			draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor, Math::round(EDSCALE));
 		}
 		}
 
 
-		// KEYFAMES //
+		// KEYFRAMES //
 
 
 		draw_bg(limit, get_size().width - timeline->get_buttons_width());
 		draw_bg(limit, get_size().width - timeline->get_buttons_width());
 
 
 		{
 		{
-
 			float scale = timeline->get_zoom_scale();
 			float scale = timeline->get_zoom_scale();
 			int limit_end = get_size().width - timeline->get_buttons_width();
 			int limit_end = get_size().width - timeline->get_buttons_width();
 
 
@@ -1353,6 +1349,7 @@ void AnimationTrackEdit::_notification(int p_what) {
 		draw_fg(limit, get_size().width - timeline->get_buttons_width());
 		draw_fg(limit, get_size().width - timeline->get_buttons_width());
 
 
 		// BUTTONS //
 		// BUTTONS //
+
 		{
 		{
 
 
 			Ref<Texture> wrap_icon[2] = {
 			Ref<Texture> wrap_icon[2] = {
@@ -1577,7 +1574,18 @@ void AnimationTrackEdit::draw_key(int p_index, float p_pixels_sec, int p_x, bool
 	if (p_x < p_clip_left || p_x > p_clip_right)
 	if (p_x < p_clip_left || p_x > p_clip_right)
 		return;
 		return;
 
 
-	Vector2 ofs(p_x - type_icon->get_width() / 2, int(get_size().height - type_icon->get_height()) / 2);
+	Ref<Texture> icon_to_draw = p_selected ? selected_icon : type_icon;
+
+	// Override type icon for invalid value keys, unless selected.
+	if (!p_selected && animation->track_get_type(track) == Animation::TYPE_VALUE) {
+		const Variant &v = animation->track_get_key_value(track, p_index);
+		Variant::Type valid_type = Variant::NIL;
+		if (!_is_value_key_valid(v, valid_type)) {
+			icon_to_draw = get_icon("KeyInvalid", "EditorIcons");
+		}
+	}
+
+	Vector2 ofs(p_x - icon_to_draw->get_width() / 2, int(get_size().height - icon_to_draw->get_height()) / 2);
 
 
 	if (animation->track_get_type(track) == Animation::TYPE_METHOD) {
 	if (animation->track_get_type(track) == Animation::TYPE_METHOD) {
 		Ref<Font> font = get_font("font", "Label");
 		Ref<Font> font = get_font("font", "Label");
@@ -1601,16 +1609,13 @@ void AnimationTrackEdit::draw_key(int p_index, float p_pixels_sec, int p_x, bool
 		}
 		}
 		text += ")";
 		text += ")";
 
 
-		int limit = MAX(0, p_clip_right - p_x - type_icon->get_width());
+		int limit = MAX(0, p_clip_right - p_x - icon_to_draw->get_width());
 		if (limit > 0) {
 		if (limit > 0) {
-			draw_string(font, Vector2(p_x + type_icon->get_width(), int(get_size().height - font->get_height()) / 2 + font->get_ascent()), text, color, limit);
+			draw_string(font, Vector2(p_x + icon_to_draw->get_width(), int(get_size().height - font->get_height()) / 2 + font->get_ascent()), text, color, limit);
 		}
 		}
 	}
 	}
-	if (p_selected) {
-		draw_texture(selected_icon, ofs);
-	} else {
-		draw_texture(type_icon, ofs);
-	}
+
+	draw_texture(icon_to_draw, ofs);
 }
 }
 
 
 //helper
 //helper
@@ -1775,6 +1780,27 @@ void AnimationTrackEdit::_path_entered(const String &p_text) {
 	undo_redo->commit_action();
 	undo_redo->commit_action();
 }
 }
 
 
+bool AnimationTrackEdit::_is_value_key_valid(const Variant &p_key_value, Variant::Type &r_valid_type) const {
+
+	RES res;
+	Vector<StringName> leftover_path;
+	Node *node = root->get_node_and_resource(animation->track_get_path(track), res, leftover_path);
+
+	Object *obj = NULL;
+	if (res.is_valid()) {
+		obj = res.ptr();
+	} else if (node) {
+		obj = node;
+	}
+
+	bool prop_exists = false;
+	if (obj) {
+		r_valid_type = obj->get_static_property_type_indexed(leftover_path, &prop_exists);
+	}
+
+	return (!prop_exists || Variant::can_convert(p_key_value.get_type(), r_valid_type));
+}
+
 String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
 String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
 
 
 	if (check_rect.has_point(p_pos)) {
 	if (check_rect.has_point(p_pos)) {
@@ -1845,29 +1871,10 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
 				} break;
 				} break;
 				case Animation::TYPE_VALUE: {
 				case Animation::TYPE_VALUE: {
 
 
-					Variant v = animation->track_get_key_value(track, key_idx);
-					//text+="value: "+String(v)+"\n";
-
-					bool prop_exists = false;
-					Variant::Type valid_type = Variant::NIL;
-					Object *obj = NULL;
-
-					RES res;
-					Vector<StringName> leftover_path;
-					Node *node = root->get_node_and_resource(animation->track_get_path(track), res, leftover_path);
-
-					if (res.is_valid()) {
-						obj = res.ptr();
-					} else if (node) {
-						obj = node;
-					}
-
-					if (obj) {
-						valid_type = obj->get_static_property_type_indexed(leftover_path, &prop_exists);
-					}
-
+					const Variant &v = animation->track_get_key_value(track, key_idx);
 					text += "Type: " + Variant::get_type_name(v.get_type()) + "\n";
 					text += "Type: " + Variant::get_type_name(v.get_type()) + "\n";
-					if (prop_exists && !Variant::can_convert(v.get_type(), valid_type)) {
+					Variant::Type valid_type = Variant::NIL;
+					if (!_is_value_key_valid(v, valid_type)) {
 						text += "Value: " + String(v) + "  (Invalid, expected type: " + Variant::get_type_name(valid_type) + ")\n";
 						text += "Value: " + String(v) + "  (Invalid, expected type: " + Variant::get_type_name(valid_type) + ")\n";
 					} else {
 					} else {
 						text += "Value: " + String(v) + "\n";
 						text += "Value: " + String(v) + "\n";
@@ -4122,6 +4129,7 @@ void AnimationTrackEditor::_update_key_edit() {
 	key_edit = memnew(AnimationTrackKeyEdit);
 	key_edit = memnew(AnimationTrackKeyEdit);
 	key_edit->animation = animation;
 	key_edit->animation = animation;
 	key_edit->track = selection.front()->key().track;
 	key_edit->track = selection.front()->key().track;
+	key_edit->use_fps = timeline->is_using_fps();
 
 
 	float ofs = animation->track_get_key_time(key_edit->track, selection.front()->key().key);
 	float ofs = animation->track_get_key_time(key_edit->track, selection.front()->key().key);
 	key_edit->key_ofs = ofs;
 	key_edit->key_ofs = ofs;

+ 7 - 7
editor/animation_track_editor.h

@@ -31,6 +31,11 @@
 #ifndef ANIMATION_TRACK_EDITOR_H
 #ifndef ANIMATION_TRACK_EDITOR_H
 #define ANIMATION_TRACK_EDITOR_H
 #define ANIMATION_TRACK_EDITOR_H
 
 
+#include "editor/editor_data.h"
+#include "editor/editor_spin_slider.h"
+#include "editor/property_editor.h"
+#include "editor/property_selector.h"
+#include "scene/animation/animation_cache.h"
 #include "scene/gui/control.h"
 #include "scene/gui/control.h"
 #include "scene/gui/file_dialog.h"
 #include "scene/gui/file_dialog.h"
 #include "scene/gui/menu_button.h"
 #include "scene/gui/menu_button.h"
@@ -40,12 +45,6 @@
 #include "scene/gui/tab_container.h"
 #include "scene/gui/tab_container.h"
 #include "scene/gui/texture_rect.h"
 #include "scene/gui/texture_rect.h"
 #include "scene/gui/tool_button.h"
 #include "scene/gui/tool_button.h"
-
-#include "editor/property_selector.h"
-#include "editor_data.h"
-#include "editor_spin_slider.h"
-#include "property_editor.h"
-#include "scene/animation/animation_cache.h"
 #include "scene/resources/animation.h"
 #include "scene/resources/animation.h"
 #include "scene_tree_editor.h"
 #include "scene_tree_editor.h"
 
 
@@ -175,8 +174,9 @@ class AnimationTrackEdit : public Control {
 
 
 	void _path_entered(const String &p_text);
 	void _path_entered(const String &p_text);
 	void _play_position_draw();
 	void _play_position_draw();
-	mutable int dropping_at;
+	bool _is_value_key_valid(const Variant &p_key_value, Variant::Type &r_valid_type) const;
 
 
+	mutable int dropping_at;
 	float insert_at_pos;
 	float insert_at_pos;
 	bool moving_selection_attempt;
 	bool moving_selection_attempt;
 	int select_single_attempt;
 	int select_single_attempt;

+ 1 - 1
editor/editor_autoload_settings.cpp

@@ -772,7 +772,7 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
 			}
 			}
 		}
 		}
 
 
-		if (!info.is_singleton && !info.in_editor) {
+		if (!info.is_singleton && !info.in_editor && info.node != NULL) {
 			memdelete(info.node);
 			memdelete(info.node);
 			info.node = NULL;
 			info.node = NULL;
 		}
 		}

+ 1 - 0
editor/editor_data.cpp

@@ -560,6 +560,7 @@ void EditorData::move_edited_scene_index(int p_idx, int p_to_idx) {
 	ERR_FAIL_INDEX(p_to_idx, edited_scene.size());
 	ERR_FAIL_INDEX(p_to_idx, edited_scene.size());
 	SWAP(edited_scene.write[p_idx], edited_scene.write[p_to_idx]);
 	SWAP(edited_scene.write[p_idx], edited_scene.write[p_to_idx]);
 }
 }
+
 void EditorData::remove_scene(int p_idx) {
 void EditorData::remove_scene(int p_idx) {
 	ERR_FAIL_INDEX(p_idx, edited_scene.size());
 	ERR_FAIL_INDEX(p_idx, edited_scene.size());
 	if (edited_scene[p_idx].root) {
 	if (edited_scene[p_idx].root) {

+ 1 - 0
editor/editor_export.cpp

@@ -611,6 +611,7 @@ void EditorExportPlugin::_bind_methods() {
 
 
 	BIND_VMETHOD(MethodInfo("_export_file", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::POOL_STRING_ARRAY, "features")));
 	BIND_VMETHOD(MethodInfo("_export_file", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::POOL_STRING_ARRAY, "features")));
 	BIND_VMETHOD(MethodInfo("_export_begin", PropertyInfo(Variant::POOL_STRING_ARRAY, "features"), PropertyInfo(Variant::BOOL, "is_debug"), PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "flags")));
 	BIND_VMETHOD(MethodInfo("_export_begin", PropertyInfo(Variant::POOL_STRING_ARRAY, "features"), PropertyInfo(Variant::BOOL, "is_debug"), PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "flags")));
+	BIND_VMETHOD(MethodInfo("_export_end"));
 }
 }
 
 
 EditorExportPlugin::EditorExportPlugin() {
 EditorExportPlugin::EditorExportPlugin() {

+ 2 - 1
editor/editor_help.cpp

@@ -175,8 +175,9 @@ String EditorHelp::_fix_constant(const String &p_constant) const {
 	if (p_constant.strip_edges() == "2147483647") {
 	if (p_constant.strip_edges() == "2147483647") {
 		return "0x7FFFFFFF";
 		return "0x7FFFFFFF";
 	}
 	}
+
 	if (p_constant.strip_edges() == "1048575") {
 	if (p_constant.strip_edges() == "1048575") {
-		return "0xfffff";
+		return "0xFFFFF";
 	}
 	}
 
 
 	return p_constant;
 	return p_constant;

+ 50 - 26
editor/editor_node.cpp

@@ -504,6 +504,7 @@ void EditorNode::_fs_changed() {
 void EditorNode::_resources_reimported(const Vector<String> &p_resources) {
 void EditorNode::_resources_reimported(const Vector<String> &p_resources) {
 
 
 	List<String> scenes; //will load later
 	List<String> scenes; //will load later
+	int current_tab = scene_tabs->get_current_tab();
 
 
 	for (int i = 0; i < p_resources.size(); i++) {
 	for (int i = 0; i < p_resources.size(); i++) {
 		String file_type = ResourceLoader::get_resource_type(p_resources[i]);
 		String file_type = ResourceLoader::get_resource_type(p_resources[i]);
@@ -526,6 +527,8 @@ void EditorNode::_resources_reimported(const Vector<String> &p_resources) {
 	for (List<String>::Element *E = scenes.front(); E; E = E->next()) {
 	for (List<String>::Element *E = scenes.front(); E; E = E->next()) {
 		reload_scene(E->get());
 		reload_scene(E->get());
 	}
 	}
+
+	scene_tabs->set_current_tab(current_tab);
 }
 }
 
 
 void EditorNode::_sources_changed(bool p_exist) {
 void EditorNode::_sources_changed(bool p_exist) {
@@ -1189,6 +1192,17 @@ void EditorNode::save_all_scenes() {
 	_save_all_scenes();
 	_save_all_scenes();
 }
 }
 
 
+void EditorNode::save_scene_list(Vector<String> p_scene_filenames) {
+
+	for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
+		Node *scene = editor_data.get_edited_scene_root(i);
+
+		if (scene && (p_scene_filenames.find(scene->get_filename()) >= 0)) {
+			_save_scene(scene->get_filename(), i);
+		}
+	}
+}
+
 void EditorNode::restart_editor() {
 void EditorNode::restart_editor() {
 
 
 	exiting = true;
 	exiting = true;
@@ -2744,7 +2758,7 @@ bool EditorNode::is_addon_plugin_enabled(const String &p_addon) const {
 	return plugin_addons.has(p_addon);
 	return plugin_addons.has(p_addon);
 }
 }
 
 
-void EditorNode::_remove_edited_scene() {
+void EditorNode::_remove_edited_scene(bool p_change_tab) {
 	int new_index = editor_data.get_edited_scene();
 	int new_index = editor_data.get_edited_scene();
 	int old_index = new_index;
 	int old_index = new_index;
 
 
@@ -2760,18 +2774,19 @@ void EditorNode::_remove_edited_scene() {
 	if (editor_data.get_scene_path(old_index) != String()) {
 	if (editor_data.get_scene_path(old_index) != String()) {
 		ScriptEditor::get_singleton()->close_builtin_scripts_from_scene(editor_data.get_scene_path(old_index));
 		ScriptEditor::get_singleton()->close_builtin_scripts_from_scene(editor_data.get_scene_path(old_index));
 	}
 	}
-	_scene_tab_changed(new_index);
+
+	if (p_change_tab) _scene_tab_changed(new_index);
 	editor_data.remove_scene(old_index);
 	editor_data.remove_scene(old_index);
 	editor_data.get_undo_redo().clear_history(false);
 	editor_data.get_undo_redo().clear_history(false);
 	_update_title();
 	_update_title();
 	_update_scene_tabs();
 	_update_scene_tabs();
 }
 }
 
 
-void EditorNode::_remove_scene(int index) {
+void EditorNode::_remove_scene(int index, bool p_change_tab) {
 
 
 	if (editor_data.get_edited_scene() == index) {
 	if (editor_data.get_edited_scene() == index) {
 		//Scene to remove is current scene
 		//Scene to remove is current scene
-		_remove_edited_scene();
+		_remove_edited_scene(p_change_tab);
 	} else {
 	} else {
 		//Scene to remove is not active scene
 		//Scene to remove is not active scene
 		editor_data.remove_scene(index);
 		editor_data.remove_scene(index);
@@ -4023,6 +4038,14 @@ bool EditorNode::has_scenes_in_session() {
 	return !scenes.empty();
 	return !scenes.empty();
 }
 }
 
 
+int EditorNode::get_current_tab() {
+	return scene_tabs->get_current_tab();
+}
+
+void EditorNode::set_current_tab(int p_tab) {
+	scene_tabs->set_current_tab(p_tab);
+}
+
 void EditorNode::_update_layouts_menu() {
 void EditorNode::_update_layouts_menu() {
 
 
 	editor_layouts->clear();
 	editor_layouts->clear();
@@ -4612,8 +4635,7 @@ void EditorNode::reload_scene(const String &p_path) {
 
 
 	if (scene_idx == -1) {
 	if (scene_idx == -1) {
 		if (get_edited_scene()) {
 		if (get_edited_scene()) {
-			//scene is not open, so at it might be instanced, just refresh, set tab to itself and it will reload
-			set_current_scene(current_tab);
+			//scene is not open, so at it might be instanced. We'll refresh the whole scene later.
 			editor_data.get_undo_redo().clear_history();
 			editor_data.get_undo_redo().clear_history();
 		}
 		}
 		return;
 		return;
@@ -4623,17 +4645,19 @@ void EditorNode::reload_scene(const String &p_path) {
 		editor_data.apply_changes_in_editors();
 		editor_data.apply_changes_in_editors();
 		_set_scene_metadata(p_path);
 		_set_scene_metadata(p_path);
 	}
 	}
+
 	//remove scene
 	//remove scene
-	_remove_scene(scene_idx);
-	//reload scene
+	_remove_scene(scene_idx, false);
 
 
+	//reload scene
 	load_scene(p_path, true, false, true, true);
 	load_scene(p_path, true, false, true, true);
+
 	//adjust index so tab is back a the previous position
 	//adjust index so tab is back a the previous position
 	editor_data.move_edited_scene_to_index(scene_idx);
 	editor_data.move_edited_scene_to_index(scene_idx);
 	get_undo_redo()->clear_history();
 	get_undo_redo()->clear_history();
+
 	//recover the tab
 	//recover the tab
 	scene_tabs->set_current_tab(current_tab);
 	scene_tabs->set_current_tab(current_tab);
-	_scene_tab_changed(current_tab);
 }
 }
 
 
 int EditorNode::plugin_init_callback_count = 0;
 int EditorNode::plugin_init_callback_count = 0;
@@ -5440,7 +5464,7 @@ EditorNode::EditorNode() {
 	p->add_shortcut(ED_SHORTCUT("editor/undo", TTR("Undo"), KEY_MASK_CMD + KEY_Z), EDIT_UNDO, true);
 	p->add_shortcut(ED_SHORTCUT("editor/undo", TTR("Undo"), KEY_MASK_CMD + KEY_Z), EDIT_UNDO, true);
 	p->add_shortcut(ED_SHORTCUT("editor/redo", TTR("Redo"), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_Z), EDIT_REDO, true);
 	p->add_shortcut(ED_SHORTCUT("editor/redo", TTR("Redo"), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_Z), EDIT_REDO, true);
 	p->add_separator();
 	p->add_separator();
-	p->add_item(TTR("Revert Scene"), EDIT_REVERT);
+	p->add_shortcut(ED_SHORTCUT("editor/revert_scene", TTR("Revert Scene")), EDIT_REVERT);
 
 
 	recent_scenes = memnew(PopupMenu);
 	recent_scenes = memnew(PopupMenu);
 	recent_scenes->set_name("RecentScenes");
 	recent_scenes->set_name("RecentScenes");
@@ -5460,10 +5484,10 @@ EditorNode::EditorNode() {
 
 
 	p = project_menu->get_popup();
 	p = project_menu->get_popup();
 	p->set_hide_on_window_lose_focus(true);
 	p->set_hide_on_window_lose_focus(true);
-	p->add_item(TTR("Project Settings"), RUN_SETTINGS);
+	p->add_shortcut(ED_SHORTCUT("editor/project_settings", TTR("Project Settings")), RUN_SETTINGS);
 	p->add_separator();
 	p->add_separator();
 	p->connect("id_pressed", this, "_menu_option");
 	p->connect("id_pressed", this, "_menu_option");
-	p->add_item(TTR("Export"), FILE_EXPORT_PROJECT);
+	p->add_shortcut(ED_SHORTCUT("editor/export", TTR("Export")), FILE_EXPORT_PROJECT);
 
 
 	plugin_config_dialog = memnew(PluginConfigDialog);
 	plugin_config_dialog = memnew(PluginConfigDialog);
 	plugin_config_dialog->connect("plugin_ready", this, "_on_plugin_ready");
 	plugin_config_dialog->connect("plugin_ready", this, "_on_plugin_ready");
@@ -5474,10 +5498,10 @@ EditorNode::EditorNode() {
 	tool_menu->connect("index_pressed", this, "_tool_menu_option");
 	tool_menu->connect("index_pressed", this, "_tool_menu_option");
 	p->add_child(tool_menu);
 	p->add_child(tool_menu);
 	p->add_submenu_item(TTR("Tools"), "Tools");
 	p->add_submenu_item(TTR("Tools"), "Tools");
-	tool_menu->add_item(TTR("Orphan Resource Explorer"), TOOLS_ORPHAN_RESOURCES);
+	tool_menu->add_shortcut(ED_SHORTCUT("editor/orphan_resource_explorer", TTR("Orphan Resource Explorer")), TOOLS_ORPHAN_RESOURCES);
 	p->add_separator();
 	p->add_separator();
 
 
-	p->add_item(TTR("Open Project Data Folder"), RUN_PROJECT_DATA_FOLDER);
+	p->add_shortcut(ED_SHORTCUT("editor/open_project_data_folder", TTR("Open Project Data Folder")), RUN_PROJECT_DATA_FOLDER);
 	p->add_separator();
 	p->add_separator();
 
 
 #ifdef OSX_ENABLED
 #ifdef OSX_ENABLED
@@ -5501,21 +5525,21 @@ EditorNode::EditorNode() {
 	p = debug_menu->get_popup();
 	p = debug_menu->get_popup();
 	p->set_hide_on_window_lose_focus(true);
 	p->set_hide_on_window_lose_focus(true);
 	p->set_hide_on_item_selection(false);
 	p->set_hide_on_item_selection(false);
-	p->add_check_item(TTR("Deploy with Remote Debug"), RUN_DEPLOY_REMOTE_DEBUG);
+	p->add_check_shortcut(ED_SHORTCUT("editor/deploy_with_remote_debug", TTR("Deploy with Remote Debug")), RUN_DEPLOY_REMOTE_DEBUG);
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("When exporting or deploying, the resulting executable will attempt to connect to the IP of this computer in order to be debugged."));
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("When exporting or deploying, the resulting executable will attempt to connect to the IP of this computer in order to be debugged."));
-	p->add_check_item(TTR("Small Deploy with Network FS"), RUN_FILE_SERVER);
+	p->add_check_shortcut(ED_SHORTCUT("editor/small_deploy_with_network_fs", TTR("Small Deploy with Network FS")), RUN_FILE_SERVER);
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is enabled, export or deploy will produce a minimal executable.\nThe filesystem will be provided from the project by the editor over the network.\nOn Android, deploy will use the USB cable for faster performance. This option speeds up testing for games with a large footprint."));
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is enabled, export or deploy will produce a minimal executable.\nThe filesystem will be provided from the project by the editor over the network.\nOn Android, deploy will use the USB cable for faster performance. This option speeds up testing for games with a large footprint."));
 	p->add_separator();
 	p->add_separator();
-	p->add_check_item(TTR("Visible Collision Shapes"), RUN_DEBUG_COLLISONS);
+	p->add_check_shortcut(ED_SHORTCUT("editor/visible_collision_shapes", TTR("Visible Collision Shapes")), RUN_DEBUG_COLLISONS);
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("Collision shapes and raycast nodes (for 2D and 3D) will be visible on the running game if this option is turned on."));
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("Collision shapes and raycast nodes (for 2D and 3D) will be visible on the running game if this option is turned on."));
-	p->add_check_item(TTR("Visible Navigation"), RUN_DEBUG_NAVIGATION);
+	p->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION);
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("Navigation meshes and polygons will be visible on the running game if this option is turned on."));
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("Navigation meshes and polygons will be visible on the running game if this option is turned on."));
 	p->add_separator();
 	p->add_separator();
 	//those are now on by default, since they are harmless
 	//those are now on by default, since they are harmless
-	p->add_check_item(TTR("Sync Scene Changes"), RUN_LIVE_DEBUG);
+	p->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Sync Scene Changes")), RUN_LIVE_DEBUG);
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any changes made to the scene in the editor will be replicated in the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any changes made to the scene in the editor will be replicated in the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
 	p->set_item_checked(p->get_item_count() - 1, true);
 	p->set_item_checked(p->get_item_count() - 1, true);
-	p->add_check_item(TTR("Sync Script Changes"), RUN_RELOAD_SCRIPTS);
+	p->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Sync Script Changes")), RUN_RELOAD_SCRIPTS);
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any script that is saved will be reloaded on the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any script that is saved will be reloaded on the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
 	p->set_item_checked(p->get_item_count() - 1, true);
 	p->set_item_checked(p->get_item_count() - 1, true);
 	p->connect("id_pressed", this, "_menu_option");
 	p->connect("id_pressed", this, "_menu_option");
@@ -5531,7 +5555,7 @@ EditorNode::EditorNode() {
 
 
 	p = settings_menu->get_popup();
 	p = settings_menu->get_popup();
 	p->set_hide_on_window_lose_focus(true);
 	p->set_hide_on_window_lose_focus(true);
-	p->add_item(TTR("Editor Settings"), SETTINGS_PREFERENCES);
+	p->add_shortcut(ED_SHORTCUT("editor/editor_settings", TTR("Editor Settings")), SETTINGS_PREFERENCES);
 	p->add_separator();
 	p->add_separator();
 
 
 	editor_layouts = memnew(PopupMenu);
 	editor_layouts = memnew(PopupMenu);
@@ -5571,12 +5595,12 @@ EditorNode::EditorNode() {
 	p->connect("id_pressed", this, "_menu_option");
 	p->connect("id_pressed", this, "_menu_option");
 	p->add_icon_shortcut(gui_base->get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("editor/editor_help", TTR("Search"), KEY_F4), HELP_SEARCH);
 	p->add_icon_shortcut(gui_base->get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("editor/editor_help", TTR("Search"), KEY_F4), HELP_SEARCH);
 	p->add_separator();
 	p->add_separator();
-	p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Online Docs"), HELP_DOCS);
-	p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Q&A"), HELP_QA);
-	p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Issue Tracker"), HELP_ISSUES);
-	p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Community"), HELP_COMMUNITY);
+	p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/online_docs", TTR("Online Docs")), HELP_DOCS);
+	p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/q&a", TTR("Q&A")), HELP_QA);
+	p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/issue_tracker", TTR("Issue Tracker")), HELP_ISSUES);
+	p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/community", TTR("Community")), HELP_COMMUNITY);
 	p->add_separator();
 	p->add_separator();
-	p->add_icon_item(gui_base->get_icon("Godot", "EditorIcons"), TTR("About"), HELP_ABOUT);
+	p->add_icon_shortcut(gui_base->get_icon("Godot", "EditorIcons"), ED_SHORTCUT("editor/about", TTR("About")), HELP_ABOUT);
 
 
 	HBoxContainer *play_hb = memnew(HBoxContainer);
 	HBoxContainer *play_hb = memnew(HBoxContainer);
 	menu_hb->add_child(play_hb);
 	menu_hb->add_child(play_hb);

+ 9 - 2
editor/editor_node.h

@@ -499,8 +499,8 @@ private:
 	static void _editor_file_dialog_unregister(EditorFileDialog *p_dialog);
 	static void _editor_file_dialog_unregister(EditorFileDialog *p_dialog);
 
 
 	void _cleanup_scene();
 	void _cleanup_scene();
-	void _remove_edited_scene();
-	void _remove_scene(int index);
+	void _remove_edited_scene(bool p_change_tab = true);
+	void _remove_scene(int index, bool p_change_tab = true);
 	bool _find_and_save_resource(RES p_res, Map<RES, bool> &processed, int32_t flags);
 	bool _find_and_save_resource(RES p_res, Map<RES, bool> &processed, int32_t flags);
 	bool _find_and_save_edited_subresources(Object *obj, Map<RES, bool> &processed, int32_t flags);
 	bool _find_and_save_edited_subresources(Object *obj, Map<RES, bool> &processed, int32_t flags);
 	void _save_edited_subresources(Node *scene, Map<RES, bool> &processed, int32_t flags);
 	void _save_edited_subresources(Node *scene, Map<RES, bool> &processed, int32_t flags);
@@ -615,6 +615,12 @@ protected:
 	void _notification(int p_what);
 	void _notification(int p_what);
 	static void _bind_methods();
 	static void _bind_methods();
 
 
+protected:
+	friend class FileSystemDock;
+
+	int get_current_tab();
+	void set_current_tab(int p_tab);
+
 public:
 public:
 	bool call_build();
 	bool call_build();
 
 
@@ -786,6 +792,7 @@ public:
 	void remove_tool_menu_item(const String &p_name);
 	void remove_tool_menu_item(const String &p_name);
 
 
 	void save_all_scenes();
 	void save_all_scenes();
+	void save_scene_list(Vector<String> p_scene_filenames);
 	void restart_editor();
 	void restart_editor();
 
 
 	void dim_editor(bool p_dimming);
 	void dim_editor(bool p_dimming);

+ 11 - 1
editor/editor_properties.cpp

@@ -2289,6 +2289,16 @@ void EditorPropertyResource::_update_menu_items() {
 				E = E->next();
 				E = E->next();
 			}
 			}
 
 
+			List<StringName> global_classes;
+			ScriptServer::get_global_class_list(&global_classes);
+			E = global_classes.front();
+			while (E) {
+				if (EditorNode::get_editor_data().script_class_is_parent(E->get(), base_type)) {
+					valid_inheritors.insert(E->get());
+				}
+				E = E->next();
+			}
+
 			for (Set<String>::Element *F = valid_inheritors.front(); F; F = F->next()) {
 			for (Set<String>::Element *F = valid_inheritors.front(); F; F = F->next()) {
 				String t = F->get();
 				String t = F->get();
 
 
@@ -2305,7 +2315,7 @@ void EditorPropertyResource::_update_menu_items() {
 					}
 					}
 				}
 				}
 
 
-				if (!is_custom_resource && !ClassDB::can_instance(t))
+				if (!is_custom_resource && !(ScriptServer::is_global_class(t) || ClassDB::can_instance(t)))
 					continue;
 					continue;
 
 
 				inheritors_array.push_back(t);
 				inheritors_array.push_back(t);

+ 15 - 0
editor/editor_settings.cpp

@@ -268,6 +268,11 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 		String host_lang = OS::get_singleton()->get_locale();
 		String host_lang = OS::get_singleton()->get_locale();
 		host_lang = TranslationServer::standardize_locale(host_lang);
 		host_lang = TranslationServer::standardize_locale(host_lang);
 
 
+		// Some locales are not properly supported currently in Godot due to lack of font shaping
+		// (e.g. Arabic or Hindi), so even though we have work in progress translations for them,
+		// we skip them as they don't render properly. (GH-28577)
+		const Vector<String> locales_to_skip = String("ar,bn,fa,he,hi,ml,si,ta,te,ur").split(",");
+
 		String best;
 		String best;
 
 
 		EditorTranslationList *etl = _editor_translations;
 		EditorTranslationList *etl = _editor_translations;
@@ -275,6 +280,15 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 		while (etl->data) {
 		while (etl->data) {
 
 
 			const String &locale = etl->lang;
 			const String &locale = etl->lang;
+
+			// Skip locales which we can't render properly (see above comment).
+			// Test against language code without regional variants (e.g. ur_PK).
+			String lang_code = locale.get_slice("_", 0);
+			if (locales_to_skip.find(lang_code) != -1) {
+				etl++;
+				continue;
+			}
+
 			lang_hint += ",";
 			lang_hint += ",";
 			lang_hint += locale;
 			lang_hint += locale;
 
 
@@ -543,6 +557,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 	_initial_set("editors/2d/bone_outline_size", 2);
 	_initial_set("editors/2d/bone_outline_size", 2);
 	_initial_set("editors/2d/keep_margins_when_changing_anchors", false);
 	_initial_set("editors/2d/keep_margins_when_changing_anchors", false);
 	_initial_set("editors/2d/viewport_border_color", Color(0.4, 0.4, 1.0, 0.4));
 	_initial_set("editors/2d/viewport_border_color", Color(0.4, 0.4, 1.0, 0.4));
+	_initial_set("editors/2d/constrain_editor_view", true);
 	_initial_set("editors/2d/warped_mouse_panning", true);
 	_initial_set("editors/2d/warped_mouse_panning", true);
 	_initial_set("editors/2d/simple_spacebar_panning", false);
 	_initial_set("editors/2d/simple_spacebar_panning", false);
 	_initial_set("editors/2d/scroll_to_pan", false);
 	_initial_set("editors/2d/scroll_to_pan", false);

+ 30 - 1
editor/filesystem_dock.cpp

@@ -1167,6 +1167,21 @@ void FileSystemDock::_update_favorites_list_after_move(const Map<String, String>
 	EditorSettings::get_singleton()->set_favorites(new_favorites);
 	EditorSettings::get_singleton()->set_favorites(new_favorites);
 }
 }
 
 
+void FileSystemDock::_save_scenes_after_move(const Map<String, String> &p_renames) const {
+	Vector<String> remaps;
+	_find_remaps(EditorFileSystem::get_singleton()->get_filesystem(), p_renames, remaps);
+	Vector<String> new_filenames;
+
+	for (int i = 0; i < remaps.size(); ++i) {
+		String file = p_renames.has(remaps[i]) ? p_renames[remaps[i]] : remaps[i];
+		if (ResourceLoader::get_resource_type(file) == "PackedScene") {
+			new_filenames.push_back(file);
+		}
+	}
+
+	editor->save_scene_list(new_filenames);
+}
+
 void FileSystemDock::_make_dir_confirm() {
 void FileSystemDock::_make_dir_confirm() {
 	String dir_name = make_dir_dialog_text->get_text().strip_edges();
 	String dir_name = make_dir_dialog_text->get_text().strip_edges();
 
 
@@ -1241,14 +1256,21 @@ void FileSystemDock::_rename_operation_confirm() {
 	Map<String, String> file_renames;
 	Map<String, String> file_renames;
 	Map<String, String> folder_renames;
 	Map<String, String> folder_renames;
 	_try_move_item(to_rename, new_path, file_renames, folder_renames);
 	_try_move_item(to_rename, new_path, file_renames, folder_renames);
+
+	int current_tab = editor->get_current_tab();
+
 	_update_dependencies_after_move(file_renames);
 	_update_dependencies_after_move(file_renames);
 	_update_resource_paths_after_move(file_renames);
 	_update_resource_paths_after_move(file_renames);
 	_update_project_settings_after_move(file_renames);
 	_update_project_settings_after_move(file_renames);
 	_update_favorites_list_after_move(file_renames, folder_renames);
 	_update_favorites_list_after_move(file_renames, folder_renames);
 
 
-	//Rescan everything
+	editor->set_current_tab(current_tab);
+
 	print_verbose("FileSystem: calling rescan.");
 	print_verbose("FileSystem: calling rescan.");
 	_rescan();
 	_rescan();
+
+	print_verbose("FileSystem: saving moved scenes.");
+	_save_scenes_after_move(file_renames);
 }
 }
 
 
 void FileSystemDock::_duplicate_operation_confirm() {
 void FileSystemDock::_duplicate_operation_confirm() {
@@ -1334,13 +1356,20 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool overw
 	}
 	}
 
 
 	if (is_moved) {
 	if (is_moved) {
+		int current_tab = editor->get_current_tab();
+
 		_update_dependencies_after_move(file_renames);
 		_update_dependencies_after_move(file_renames);
 		_update_resource_paths_after_move(file_renames);
 		_update_resource_paths_after_move(file_renames);
 		_update_project_settings_after_move(file_renames);
 		_update_project_settings_after_move(file_renames);
 		_update_favorites_list_after_move(file_renames, folder_renames);
 		_update_favorites_list_after_move(file_renames, folder_renames);
 
 
+		editor->set_current_tab(current_tab);
+
 		print_verbose("FileSystem: calling rescan.");
 		print_verbose("FileSystem: calling rescan.");
 		_rescan();
 		_rescan();
+
+		print_verbose("FileSystem: saving moved scenes.");
+		_save_scenes_after_move(file_renames);
 	}
 	}
 }
 }
 
 

+ 1 - 0
editor/filesystem_dock.h

@@ -201,6 +201,7 @@ private:
 	void _try_duplicate_item(const FileOrFolder &p_item, const String &p_new_path) const;
 	void _try_duplicate_item(const FileOrFolder &p_item, const String &p_new_path) const;
 	void _update_dependencies_after_move(const Map<String, String> &p_renames) const;
 	void _update_dependencies_after_move(const Map<String, String> &p_renames) const;
 	void _update_resource_paths_after_move(const Map<String, String> &p_renames) const;
 	void _update_resource_paths_after_move(const Map<String, String> &p_renames) const;
+	void _save_scenes_after_move(const Map<String, String> &p_renames) const;
 	void _update_favorites_list_after_move(const Map<String, String> &p_files_renames, const Map<String, String> &p_folders_renames) const;
 	void _update_favorites_list_after_move(const Map<String, String> &p_files_renames, const Map<String, String> &p_folders_renames) const;
 	void _update_project_settings_after_move(const Map<String, String> &p_folders_renames) const;
 	void _update_project_settings_after_move(const Map<String, String> &p_folders_renames) const;
 
 

+ 1 - 2
editor/find_in_files.cpp

@@ -751,7 +751,6 @@ void FindInFilesPanel::_on_replace_text_changed(String text) {
 void FindInFilesPanel::_on_replace_all_clicked() {
 void FindInFilesPanel::_on_replace_all_clicked() {
 
 
 	String replace_text = get_replace_text();
 	String replace_text = get_replace_text();
-	ERR_FAIL_COND(replace_text.empty());
 
 
 	PoolStringArray modified_files;
 	PoolStringArray modified_files;
 
 
@@ -887,7 +886,7 @@ String FindInFilesPanel::get_replace_text() {
 void FindInFilesPanel::update_replace_buttons() {
 void FindInFilesPanel::update_replace_buttons() {
 
 
 	String text = get_replace_text();
 	String text = get_replace_text();
-	bool disabled = text.empty() || _finder->is_searching();
+	bool disabled = _finder->is_searching();
 
 
 	_replace_all_button->set_disabled(disabled);
 	_replace_all_button->set_disabled(disabled);
 }
 }

File diff suppressed because it is too large
+ 56 - 0
editor/icons/icon_gizmo_c_p_u_particles.svg


+ 0 - 5
editor/icons/icon_key_valid.svg

@@ -1,5 +0,0 @@
-<svg width="8" height="8" version="1.1" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1044.4)">
-<rect transform="rotate(-45)" x="-741.53" y="741.08" width="6.1027" height="6.1027" ry=".76286" fill="#fff"/>
-</g>
-</svg>

+ 7 - 6
editor/import/editor_scene_importer_gltf.cpp

@@ -2087,22 +2087,23 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye
 				animation->add_track(Animation::TYPE_VALUE);
 				animation->add_track(Animation::TYPE_VALUE);
 				animation->track_set_path(track_idx, node_path);
 				animation->track_set_path(track_idx, node_path);
 
 
-				if (track.weight_tracks[i].interpolation <= GLTFAnimation::INTERP_STEP) {
-					animation->track_set_interpolation_type(track_idx, track.weight_tracks[i].interpolation == GLTFAnimation::INTERP_STEP ? Animation::INTERPOLATION_NEAREST : Animation::INTERPOLATION_NEAREST);
+				// Only LINEAR and STEP (NEAREST) can be supported out of the box by Godot's Animation,
+				// the other modes have to be baked.
+				GLTFAnimation::Interpolation gltf_interp = track.weight_tracks[i].interpolation;
+				if (gltf_interp == GLTFAnimation::INTERP_LINEAR || gltf_interp == GLTFAnimation::INTERP_STEP) {
+					animation->track_set_interpolation_type(track_idx, gltf_interp == GLTFAnimation::INTERP_STEP ? Animation::INTERPOLATION_NEAREST : Animation::INTERPOLATION_LINEAR);
 					for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
 					for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
 						float t = track.weight_tracks[i].times[j];
 						float t = track.weight_tracks[i].times[j];
 						float w = track.weight_tracks[i].values[j];
 						float w = track.weight_tracks[i].values[j];
 						animation->track_insert_key(track_idx, t, w);
 						animation->track_insert_key(track_idx, t, w);
 					}
 					}
 				} else {
 				} else {
-					//must bake, apologies.
+					// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
 					float increment = 1.0 / float(bake_fps);
 					float increment = 1.0 / float(bake_fps);
 					float time = 0.0;
 					float time = 0.0;
-
 					bool last = false;
 					bool last = false;
 					while (true) {
 					while (true) {
-
-						_interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, track.weight_tracks[i].interpolation);
+						_interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp);
 						if (last) {
 						if (last) {
 							break;
 							break;
 						}
 						}

+ 1 - 1
editor/import/resource_importer_scene.cpp

@@ -1094,7 +1094,7 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "animation/filter_script", PROPERTY_HINT_MULTILINE_TEXT), ""));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "animation/filter_script", PROPERTY_HINT_MULTILINE_TEXT), ""));
-	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "animation/storage", PROPERTY_HINT_ENUM, "Built-In,Files"), animations_out ? true : false));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "animation/storage", PROPERTY_HINT_ENUM, "Built-In,Files", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), animations_out ? true : false));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/keep_custom_tracks"), animations_out ? true : false));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/keep_custom_tracks"), animations_out ? true : false));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/optimizer/max_linear_error"), 0.05));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/optimizer/max_linear_error"), 0.05));

+ 2 - 1
editor/plugins/animation_player_editor_plugin.cpp

@@ -1430,6 +1430,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
 		new_state["show_rulers"] = false;
 		new_state["show_rulers"] = false;
 		new_state["show_guides"] = false;
 		new_state["show_guides"] = false;
 		new_state["show_helpers"] = false;
 		new_state["show_helpers"] = false;
+		new_state["show_zoom_control"] = false;
 		// TODO: Save/restore only affected entries
 		// TODO: Save/restore only affected entries
 		CanvasItemEditor::get_singleton()->set_state(new_state);
 		CanvasItemEditor::get_singleton()->set_state(new_state);
 	}
 	}
@@ -1482,7 +1483,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
 		if (valid) {
 		if (valid) {
 			player->seek(pos, true);
 			player->seek(pos, true);
 			get_tree()->flush_transform_notifications(); // Needed for transforms of Spatials
 			get_tree()->flush_transform_notifications(); // Needed for transforms of Spatials
-			values_backup.update_skeletons(); // Needed for Skeletons
+			values_backup.update_skeletons(); // Needed for Skeletons (2D & 3D)
 
 
 			VS::get_singleton()->viewport_set_active(onion.captures[cidx], true);
 			VS::get_singleton()->viewport_set_active(onion.captures[cidx], true);
 			VS::get_singleton()->viewport_set_parent_viewport(root_vp, onion.captures[cidx]);
 			VS::get_singleton()->viewport_set_parent_viewport(root_vp, onion.captures[cidx]);

+ 4 - 0
editor/plugins/asset_library_editor_plugin.cpp

@@ -726,6 +726,7 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByt
 
 
 				image_data = cached_data;
 				image_data = cached_data;
 				file->close();
 				file->close();
+				memdelete(file);
 			}
 			}
 		}
 		}
 
 
@@ -800,6 +801,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
 					if (file) {
 					if (file) {
 						file->store_line(new_etag);
 						file->store_line(new_etag);
 						file->close();
 						file->close();
+						memdelete(file);
 					}
 					}
 
 
 					int len = p_data.size();
 					int len = p_data.size();
@@ -809,6 +811,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
 						file->store_32(len);
 						file->store_32(len);
 						file->store_buffer(r.ptr(), len);
 						file->store_buffer(r.ptr(), len);
 						file->close();
 						file->close();
+						memdelete(file);
 					}
 					}
 
 
 					break;
 					break;
@@ -848,6 +851,7 @@ void EditorAssetLibrary::_update_image_queue() {
 				if (file) {
 				if (file) {
 					headers.push_back("If-None-Match: " + file->get_line());
 					headers.push_back("If-None-Match: " + file->get_line());
 					file->close();
 					file->close();
+					memdelete(file);
 				}
 				}
 			}
 			}
 
 

+ 20 - 7
editor/plugins/canvas_item_editor_plugin.cpp

@@ -3547,18 +3547,19 @@ void CanvasItemEditor::_update_scrollbars() {
 	// Constraints the view offset and updates the scrollbars
 	// Constraints the view offset and updates the scrollbars
 	Point2 begin = canvas_item_rect.position;
 	Point2 begin = canvas_item_rect.position;
 	Point2 end = canvas_item_rect.position + canvas_item_rect.size - local_rect.size / zoom;
 	Point2 end = canvas_item_rect.position + canvas_item_rect.size - local_rect.size / zoom;
+	bool constrain_editor_view = bool(EditorSettings::get_singleton()->get("editors/2d/constrain_editor_view"));
 
 
 	if (canvas_item_rect.size.height <= (local_rect.size.y / zoom)) {
 	if (canvas_item_rect.size.height <= (local_rect.size.y / zoom)) {
-		if (ABS(begin.y - previous_update_view_offset.y) < ABS(begin.y - view_offset.y)) {
+		if (constrain_editor_view && ABS(begin.y - previous_update_view_offset.y) < ABS(begin.y - view_offset.y)) {
 			view_offset.y = previous_update_view_offset.y;
 			view_offset.y = previous_update_view_offset.y;
 		}
 		}
 
 
 		v_scroll->hide();
 		v_scroll->hide();
 	} else {
 	} else {
-		if (view_offset.y > end.y && view_offset.y > previous_update_view_offset.y) {
+		if (constrain_editor_view && view_offset.y > end.y && view_offset.y > previous_update_view_offset.y) {
 			view_offset.y = MAX(end.y, previous_update_view_offset.y);
 			view_offset.y = MAX(end.y, previous_update_view_offset.y);
 		}
 		}
-		if (view_offset.y < begin.y && view_offset.y < previous_update_view_offset.y) {
+		if (constrain_editor_view && view_offset.y < begin.y && view_offset.y < previous_update_view_offset.y) {
 			view_offset.y = MIN(begin.y, previous_update_view_offset.y);
 			view_offset.y = MIN(begin.y, previous_update_view_offset.y);
 		}
 		}
 
 
@@ -3569,16 +3570,16 @@ void CanvasItemEditor::_update_scrollbars() {
 	}
 	}
 
 
 	if (canvas_item_rect.size.width <= (local_rect.size.x / zoom)) {
 	if (canvas_item_rect.size.width <= (local_rect.size.x / zoom)) {
-		if (ABS(begin.x - previous_update_view_offset.x) < ABS(begin.x - view_offset.x)) {
+		if (constrain_editor_view && ABS(begin.x - previous_update_view_offset.x) < ABS(begin.x - view_offset.x)) {
 			view_offset.x = previous_update_view_offset.x;
 			view_offset.x = previous_update_view_offset.x;
 		}
 		}
 
 
 		h_scroll->hide();
 		h_scroll->hide();
 	} else {
 	} else {
-		if (view_offset.x > end.x && view_offset.x > previous_update_view_offset.x) {
+		if (constrain_editor_view && view_offset.x > end.x && view_offset.x > previous_update_view_offset.x) {
 			view_offset.x = MAX(end.x, previous_update_view_offset.x);
 			view_offset.x = MAX(end.x, previous_update_view_offset.x);
 		}
 		}
-		if (view_offset.x < begin.x && view_offset.x < previous_update_view_offset.x) {
+		if (constrain_editor_view && view_offset.x < begin.x && view_offset.x < previous_update_view_offset.x) {
 			view_offset.x = MIN(begin.x, previous_update_view_offset.x);
 			view_offset.x = MIN(begin.x, previous_update_view_offset.x);
 		}
 		}
 
 
@@ -3903,6 +3904,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
 			show_rulers = !show_rulers;
 			show_rulers = !show_rulers;
 			int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS);
 			int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS);
 			view_menu->get_popup()->set_item_checked(idx, show_rulers);
 			view_menu->get_popup()->set_item_checked(idx, show_rulers);
+			_update_scrollbars();
 			viewport->update();
 			viewport->update();
 		} break;
 		} break;
 		case SHOW_GUIDES: {
 		case SHOW_GUIDES: {
@@ -4371,6 +4373,7 @@ Dictionary CanvasItemEditor::get_state() const {
 	state["show_rulers"] = show_rulers;
 	state["show_rulers"] = show_rulers;
 	state["show_guides"] = show_guides;
 	state["show_guides"] = show_guides;
 	state["show_helpers"] = show_helpers;
 	state["show_helpers"] = show_helpers;
+	state["show_zoom_control"] = zoom_hb->is_visible();
 	state["show_edit_locks"] = show_edit_locks;
 	state["show_edit_locks"] = show_edit_locks;
 	state["snap_rotation"] = snap_rotation;
 	state["snap_rotation"] = snap_rotation;
 	state["snap_relative"] = snap_relative;
 	state["snap_relative"] = snap_relative;
@@ -4381,6 +4384,7 @@ Dictionary CanvasItemEditor::get_state() const {
 
 
 void CanvasItemEditor::set_state(const Dictionary &p_state) {
 void CanvasItemEditor::set_state(const Dictionary &p_state) {
 
 
+	bool update_scrollbars = false;
 	Dictionary state = p_state;
 	Dictionary state = p_state;
 	if (state.has("zoom")) {
 	if (state.has("zoom")) {
 		zoom = p_state["zoom"];
 		zoom = p_state["zoom"];
@@ -4389,7 +4393,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
 	if (state.has("ofs")) {
 	if (state.has("ofs")) {
 		view_offset = p_state["ofs"];
 		view_offset = p_state["ofs"];
 		previous_update_view_offset = view_offset;
 		previous_update_view_offset = view_offset;
-		_update_scrollbars();
+		update_scrollbars = true;
 	}
 	}
 
 
 	if (state.has("grid_offset")) {
 	if (state.has("grid_offset")) {
@@ -4477,6 +4481,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
 		show_rulers = state["show_rulers"];
 		show_rulers = state["show_rulers"];
 		int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS);
 		int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS);
 		view_menu->get_popup()->set_item_checked(idx, show_rulers);
 		view_menu->get_popup()->set_item_checked(idx, show_rulers);
+		update_scrollbars = true;
 	}
 	}
 
 
 	if (state.has("show_guides")) {
 	if (state.has("show_guides")) {
@@ -4497,6 +4502,11 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
 		view_menu->get_popup()->set_item_checked(idx, show_edit_locks);
 		view_menu->get_popup()->set_item_checked(idx, show_edit_locks);
 	}
 	}
 
 
+	if (state.has("show_zoom_control")) {
+		// This one is not user-controllable, but instrumentable
+		zoom_hb->set_visible(state["show_zoom_control"]);
+	}
+
 	if (state.has("snap_rotation")) {
 	if (state.has("snap_rotation")) {
 		snap_rotation = state["snap_rotation"];
 		snap_rotation = state["snap_rotation"];
 		int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION);
 		int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION);
@@ -4521,6 +4531,9 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
 		skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones);
 		skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones);
 	}
 	}
 
 
+	if (update_scrollbars) {
+		_update_scrollbars();
+	}
 	viewport->update();
 	viewport->update();
 }
 }
 
 

+ 12 - 2
editor/plugins/script_editor_plugin.cpp

@@ -1906,10 +1906,11 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
 		String flags = EditorSettings::get_singleton()->get("text_editor/external/exec_flags");
 		String flags = EditorSettings::get_singleton()->get("text_editor/external/exec_flags");
 
 
 		List<String> args;
 		List<String> args;
+		bool has_file_flag = false;
+		String script_path = ProjectSettings::get_singleton()->globalize_path(p_resource->get_path());
 
 
 		if (flags.size()) {
 		if (flags.size()) {
 			String project_path = ProjectSettings::get_singleton()->get_resource_path();
 			String project_path = ProjectSettings::get_singleton()->get_resource_path();
-			String script_path = ProjectSettings::get_singleton()->globalize_path(p_resource->get_path());
 
 
 			flags = flags.replacen("{line}", itos(p_line > 0 ? p_line : 0));
 			flags = flags.replacen("{line}", itos(p_line > 0 ? p_line : 0));
 			flags = flags.replacen("{col}", itos(p_col));
 			flags = flags.replacen("{col}", itos(p_col));
@@ -1931,6 +1932,9 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
 				} else if (flags[i] == '\0' || (!inside_quotes && flags[i] == ' ')) {
 				} else if (flags[i] == '\0' || (!inside_quotes && flags[i] == ' ')) {
 
 
 					String arg = flags.substr(from, num_chars);
 					String arg = flags.substr(from, num_chars);
+					if (arg.find("{file}") != -1) {
+						has_file_flag = true;
+					}
 
 
 					// do path replacement here, else there will be issues with spaces and quotes
 					// do path replacement here, else there will be issues with spaces and quotes
 					arg = arg.replacen("{project}", project_path);
 					arg = arg.replacen("{project}", project_path);
@@ -1945,6 +1949,11 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
 			}
 			}
 		}
 		}
 
 
+		// Default to passing script path if no {file} flag is specified.
+		if (!has_file_flag) {
+			args.push_back(script_path);
+		}
+
 		Error err = OS::get_singleton()->execute(path, args, false);
 		Error err = OS::get_singleton()->execute(path, args, false);
 		if (err == OK)
 		if (err == OK)
 			return false;
 			return false;
@@ -3331,7 +3340,8 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/open_scripts/list_script_names_as", PROPERTY_HINT_ENUM, "Name,Parent Directory And Name,Full Path"));
 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/open_scripts/list_script_names_as", PROPERTY_HINT_ENUM, "Name,Parent Directory And Name,Full Path"));
 	EDITOR_DEF("text_editor/open_scripts/list_script_names_as", 0);
 	EDITOR_DEF("text_editor/open_scripts/list_script_names_as", 0);
 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_path", PROPERTY_HINT_GLOBAL_FILE));
 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_path", PROPERTY_HINT_GLOBAL_FILE));
-	EDITOR_DEF("text_editor/external/exec_flags", "");
+	EDITOR_DEF("text_editor/external/exec_flags", "{file}");
+	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_flags", PROPERTY_HINT_PLACEHOLDER_TEXT, "Call flags with placeholders: {project}, {file}, {col}, {line}."));
 
 
 	ED_SHORTCUT("script_editor/open_recent", TTR("Open Recent"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T);
 	ED_SHORTCUT("script_editor/open_recent", TTR("Open Recent"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T);
 	ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Files"));
 	ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Files"));

+ 13 - 5
editor/plugins/script_text_editor.cpp

@@ -165,7 +165,7 @@ void ScriptTextEditor::_load_theme_settings() {
 	text_edit->add_color_override("safe_line_number_color", safe_line_number_color);
 	text_edit->add_color_override("safe_line_number_color", safe_line_number_color);
 	text_edit->add_color_override("caret_color", caret_color);
 	text_edit->add_color_override("caret_color", caret_color);
 	text_edit->add_color_override("caret_background_color", caret_background_color);
 	text_edit->add_color_override("caret_background_color", caret_background_color);
-	text_edit->add_color_override("font_selected_color", text_selected_color);
+	text_edit->add_color_override("font_color_selected", text_selected_color);
 	text_edit->add_color_override("selection_color", selection_color);
 	text_edit->add_color_override("selection_color", selection_color);
 	text_edit->add_color_override("brace_mismatch_color", brace_mismatch_color);
 	text_edit->add_color_override("brace_mismatch_color", brace_mismatch_color);
 	text_edit->add_color_override("current_line_color", current_line_color);
 	text_edit->add_color_override("current_line_color", current_line_color);
@@ -1329,7 +1329,9 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
 
 
 			if (has_color) {
 			if (has_color) {
 				String line = tx->get_line(row);
 				String line = tx->get_line(row);
-				color_line = row;
+				color_position.x = row;
+				color_position.y = col;
+
 				int begin = 0;
 				int begin = 0;
 				int end = 0;
 				int end = 0;
 				bool valid = false;
 				bool valid = false;
@@ -1369,10 +1371,15 @@ void ScriptTextEditor::_color_changed(const Color &p_color) {
 		new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ", " + rtos(p_color.a) + ")");
 		new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ", " + rtos(p_color.a) + ")");
 	}
 	}
 
 
-	String line = code_editor->get_text_edit()->get_line(color_line);
-	String new_line = line.replace(color_args, new_args);
+	String line = code_editor->get_text_edit()->get_line(color_position.x);
+	int color_args_pos = line.find(color_args, color_position.y);
+	String line_with_replaced_args = line;
+	line_with_replaced_args.erase(color_args_pos, color_args.length());
+	line_with_replaced_args = line_with_replaced_args.insert(color_args_pos, new_args);
+
 	color_args = new_args;
 	color_args = new_args;
-	code_editor->get_text_edit()->set_line(color_line, new_line);
+	code_editor->get_text_edit()->set_line(color_position.x, line_with_replaced_args);
+	code_editor->get_text_edit()->update();
 }
 }
 
 
 void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition) {
 void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition) {
@@ -1465,6 +1472,7 @@ ScriptTextEditor::ScriptTextEditor() {
 	color_panel = memnew(PopupPanel);
 	color_panel = memnew(PopupPanel);
 	add_child(color_panel);
 	add_child(color_panel);
 	color_picker = memnew(ColorPicker);
 	color_picker = memnew(ColorPicker);
+	color_picker->set_deferred_mode(true);
 	color_panel->add_child(color_picker);
 	color_panel->add_child(color_picker);
 	color_picker->connect("color_changed", this, "_color_changed");
 	color_picker->connect("color_changed", this, "_color_changed");
 
 

+ 1 - 1
editor/plugins/script_text_editor.h

@@ -59,7 +59,7 @@ class ScriptTextEditor : public ScriptEditorBase {
 
 
 	PopupPanel *color_panel;
 	PopupPanel *color_panel;
 	ColorPicker *color_picker;
 	ColorPicker *color_picker;
-	int color_line;
+	Vector2 color_position;
 	String color_args;
 	String color_args;
 
 
 	void _update_member_keywords();
 	void _update_member_keywords();

+ 11 - 1
editor/plugins/shader_editor_plugin.cpp

@@ -102,7 +102,7 @@ void ShaderTextEditor::_load_theme_settings() {
 	get_text_edit()->add_color_override("line_number_color", line_number_color);
 	get_text_edit()->add_color_override("line_number_color", line_number_color);
 	get_text_edit()->add_color_override("caret_color", caret_color);
 	get_text_edit()->add_color_override("caret_color", caret_color);
 	get_text_edit()->add_color_override("caret_background_color", caret_background_color);
 	get_text_edit()->add_color_override("caret_background_color", caret_background_color);
-	get_text_edit()->add_color_override("font_selected_color", text_selected_color);
+	get_text_edit()->add_color_override("font_color_selected", text_selected_color);
 	get_text_edit()->add_color_override("selection_color", selection_color);
 	get_text_edit()->add_color_override("selection_color", selection_color);
 	get_text_edit()->add_color_override("brace_mismatch_color", brace_mismatch_color);
 	get_text_edit()->add_color_override("brace_mismatch_color", brace_mismatch_color);
 	get_text_edit()->add_color_override("current_line_color", current_line_color);
 	get_text_edit()->add_color_override("current_line_color", current_line_color);
@@ -346,6 +346,9 @@ void ShaderEditor::_menu_option(int p_option) {
 
 
 			goto_line_dialog->popup_find_line(shader_editor->get_text_edit());
 			goto_line_dialog->popup_find_line(shader_editor->get_text_edit());
 		} break;
 		} break;
+		case HELP_DOCS: {
+			OS::get_singleton()->shell_open("https://docs.godotengine.org/en/stable/tutorials/shading/shading_reference/index.html");
+		} break;
 	}
 	}
 	if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) {
 	if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) {
 		shader_editor->get_text_edit()->call_deferred("grab_focus");
 		shader_editor->get_text_edit()->call_deferred("grab_focus");
@@ -577,10 +580,17 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) {
 	search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
 	search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
 	search_menu->get_popup()->connect("id_pressed", this, "_menu_option");
 	search_menu->get_popup()->connect("id_pressed", this, "_menu_option");
 
 
+	help_menu = memnew(MenuButton);
+	help_menu->set_text(TTR("Help"));
+	help_menu->set_switch_on_hover(true);
+	help_menu->get_popup()->add_icon_item(p_node->get_gui_base()->get_icon("Instance", "EditorIcons"), TTR("Online Docs"), HELP_DOCS);
+	help_menu->get_popup()->connect("id_pressed", this, "_menu_option");
+
 	add_child(main_container);
 	add_child(main_container);
 	main_container->add_child(hbc);
 	main_container->add_child(hbc);
 	hbc->add_child(search_menu);
 	hbc->add_child(search_menu);
 	hbc->add_child(edit_menu);
 	hbc->add_child(edit_menu);
+	hbc->add_child(help_menu);
 	hbc->add_style_override("panel", p_node->get_gui_base()->get_stylebox("ScriptEditorPanel", "EditorStyles"));
 	hbc->add_style_override("panel", p_node->get_gui_base()->get_stylebox("ScriptEditorPanel", "EditorStyles"));
 	main_container->add_child(shader_editor);
 	main_container->add_child(shader_editor);
 
 

+ 2 - 2
editor/plugins/shader_editor_plugin.h

@@ -88,12 +88,12 @@ class ShaderEditor : public PanelContainer {
 		SEARCH_FIND_PREV,
 		SEARCH_FIND_PREV,
 		SEARCH_REPLACE,
 		SEARCH_REPLACE,
 		SEARCH_GOTO_LINE,
 		SEARCH_GOTO_LINE,
-
+		HELP_DOCS,
 	};
 	};
 
 
 	MenuButton *edit_menu;
 	MenuButton *edit_menu;
 	MenuButton *search_menu;
 	MenuButton *search_menu;
-	MenuButton *settings_menu;
+	MenuButton *help_menu;
 	PopupMenu *context_menu;
 	PopupMenu *context_menu;
 	uint64_t idle;
 	uint64_t idle;
 
 

+ 1 - 0
editor/plugins/spatial_editor_plugin.cpp

@@ -5323,6 +5323,7 @@ void SpatialEditor::_register_all_gizmos() {
 	add_gizmo_plugin(Ref<VehicleWheelSpatialGizmoPlugin>(memnew(VehicleWheelSpatialGizmoPlugin)));
 	add_gizmo_plugin(Ref<VehicleWheelSpatialGizmoPlugin>(memnew(VehicleWheelSpatialGizmoPlugin)));
 	add_gizmo_plugin(Ref<VisibilityNotifierGizmoPlugin>(memnew(VisibilityNotifierGizmoPlugin)));
 	add_gizmo_plugin(Ref<VisibilityNotifierGizmoPlugin>(memnew(VisibilityNotifierGizmoPlugin)));
 	add_gizmo_plugin(Ref<ParticlesGizmoPlugin>(memnew(ParticlesGizmoPlugin)));
 	add_gizmo_plugin(Ref<ParticlesGizmoPlugin>(memnew(ParticlesGizmoPlugin)));
+	add_gizmo_plugin(Ref<CPUParticlesGizmoPlugin>(memnew(CPUParticlesGizmoPlugin)));
 	add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
 	add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
 	add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin)));
 	add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin)));
 	add_gizmo_plugin(Ref<BakedIndirectLightGizmoPlugin>(memnew(BakedIndirectLightGizmoPlugin)));
 	add_gizmo_plugin(Ref<BakedIndirectLightGizmoPlugin>(memnew(BakedIndirectLightGizmoPlugin)));

+ 1 - 1
editor/plugins/text_editor.cpp

@@ -115,7 +115,7 @@ void TextEditor::_load_theme_settings() {
 	text_edit->add_color_override("line_number_color", line_number_color);
 	text_edit->add_color_override("line_number_color", line_number_color);
 	text_edit->add_color_override("caret_color", caret_color);
 	text_edit->add_color_override("caret_color", caret_color);
 	text_edit->add_color_override("caret_background_color", caret_background_color);
 	text_edit->add_color_override("caret_background_color", caret_background_color);
-	text_edit->add_color_override("font_selected_color", text_selected_color);
+	text_edit->add_color_override("font_color_selected", text_selected_color);
 	text_edit->add_color_override("selection_color", selection_color);
 	text_edit->add_color_override("selection_color", selection_color);
 	text_edit->add_color_override("brace_mismatch_color", brace_mismatch_color);
 	text_edit->add_color_override("brace_mismatch_color", brace_mismatch_color);
 	text_edit->add_color_override("current_line_color", current_line_color);
 	text_edit->add_color_override("current_line_color", current_line_color);

+ 1 - 3
editor/plugins/theme_editor_plugin.cpp

@@ -878,11 +878,9 @@ ThemeEditor::ThemeEditor() {
 void ThemeEditorPlugin::edit(Object *p_node) {
 void ThemeEditorPlugin::edit(Object *p_node) {
 
 
 	if (Object::cast_to<Theme>(p_node)) {
 	if (Object::cast_to<Theme>(p_node)) {
-		theme_editor->show();
 		theme_editor->edit(Object::cast_to<Theme>(p_node));
 		theme_editor->edit(Object::cast_to<Theme>(p_node));
 	} else {
 	} else {
 		theme_editor->edit(Ref<Theme>());
 		theme_editor->edit(Ref<Theme>());
-		theme_editor->hide();
 	}
 	}
 }
 }
 
 
@@ -897,11 +895,11 @@ void ThemeEditorPlugin::make_visible(bool p_visible) {
 		theme_editor->set_process(true);
 		theme_editor->set_process(true);
 		button->show();
 		button->show();
 		editor->make_bottom_panel_item_visible(theme_editor);
 		editor->make_bottom_panel_item_visible(theme_editor);
-
 	} else {
 	} else {
 		theme_editor->set_process(false);
 		theme_editor->set_process(false);
 		if (theme_editor->is_visible_in_tree())
 		if (theme_editor->is_visible_in_tree())
 			editor->hide_bottom_panel();
 			editor->hide_bottom_panel();
+
 		button->hide();
 		button->hide();
 	}
 	}
 }
 }

+ 17 - 10
editor/plugins/tile_map_editor_plugin.cpp

@@ -363,6 +363,8 @@ void TileMapEditor::_update_palette() {
 
 
 	// Update the palette
 	// Update the palette
 	Vector<int> selected = get_selected_tiles();
 	Vector<int> selected = get_selected_tiles();
+	int selected_single = palette->get_current();
+	int selected_manual = manual_palette->get_current();
 	palette->clear();
 	palette->clear();
 	manual_palette->clear();
 	manual_palette->clear();
 	manual_palette->hide();
 	manual_palette->hide();
@@ -470,7 +472,7 @@ void TileMapEditor::_update_palette() {
 	if (selected.get(0) != TileMap::INVALID_CELL) {
 	if (selected.get(0) != TileMap::INVALID_CELL) {
 		set_selected_tiles(selected);
 		set_selected_tiles(selected);
 		sel_tile = selected.get(Math::rand() % selected.size());
 		sel_tile = selected.get(Math::rand() % selected.size());
-	} else {
+	} else if (palette->get_item_count() > 0) {
 		palette->select(0);
 		palette->select(0);
 	}
 	}
 
 
@@ -511,9 +513,10 @@ void TileMapEditor::_update_palette() {
 
 
 	if (manual_palette->get_item_count() > 0) {
 	if (manual_palette->get_item_count() > 0) {
 		// Only show the manual palette if at least tile exists in it
 		// Only show the manual palette if at least tile exists in it
-		int selected2 = manual_palette->get_current();
-		if (selected2 == -1) selected2 = 0;
-		manual_palette->set_current(selected2);
+		if (selected_manual == -1 || selected_single != palette->get_current())
+			selected_manual = 0;
+		if (selected_manual < manual_palette->get_item_count())
+			manual_palette->set_current(selected_manual);
 		manual_palette->show();
 		manual_palette->show();
 	}
 	}
 
 
@@ -1434,9 +1437,9 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
 		aabb.expand_to(node->world_to_map(xform_inv.xform(screen_size)));
 		aabb.expand_to(node->world_to_map(xform_inv.xform(screen_size)));
 		Rect2i si = aabb.grow(1.0);
 		Rect2i si = aabb.grow(1.0);
 
 
-		if (node->get_half_offset() != TileMap::HALF_OFFSET_X) {
+		if (node->get_half_offset() != TileMap::HALF_OFFSET_X && node->get_half_offset() != TileMap::HALF_OFFSET_NEGATIVE_X) {
 
 
-			int max_lines = 2000; //avoid crash if size too smal
+			int max_lines = 2000; //avoid crash if size too small
 
 
 			for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) {
 			for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) {
 
 
@@ -1450,7 +1453,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
 			}
 			}
 		} else {
 		} else {
 
 
-			int max_lines = 10000; //avoid crash if size too smal
+			int max_lines = 10000; //avoid crash if size too small
 
 
 			for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) {
 			for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) {
 
 
@@ -1458,7 +1461,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
 
 
 					Vector2 ofs;
 					Vector2 ofs;
 					if (ABS(j) & 1) {
 					if (ABS(j) & 1) {
-						ofs = cell_xf[0] * 0.5;
+						ofs = cell_xf[0] * (node->get_half_offset() == TileMap::HALF_OFFSET_X ? 0.5 : -0.5);
 					}
 					}
 
 
 					Vector2 from = xform.xform(node->map_to_world(Vector2(i, j), true) + ofs);
 					Vector2 from = xform.xform(node->map_to_world(Vector2(i, j), true) + ofs);
@@ -1477,7 +1480,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
 
 
 		int max_lines = 10000; //avoid crash if size too smal
 		int max_lines = 10000; //avoid crash if size too smal
 
 
-		if (node->get_half_offset() != TileMap::HALF_OFFSET_Y) {
+		if (node->get_half_offset() != TileMap::HALF_OFFSET_Y && node->get_half_offset() != TileMap::HALF_OFFSET_NEGATIVE_Y) {
 
 
 			for (int i = (si.position.y) - 1; i <= (si.position.y + si.size.y); i++) {
 			for (int i = (si.position.y) - 1; i <= (si.position.y + si.size.y); i++) {
 
 
@@ -1498,7 +1501,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
 
 
 					Vector2 ofs;
 					Vector2 ofs;
 					if (ABS(j) & 1) {
 					if (ABS(j) & 1) {
-						ofs = cell_xf[1] * 0.5;
+						ofs = cell_xf[1] * (node->get_half_offset() == TileMap::HALF_OFFSET_Y ? 0.5 : -0.5);
 					}
 					}
 
 
 					Vector2 from = xform.xform(node->map_to_world(Vector2(j, i), true) + ofs);
 					Vector2 from = xform.xform(node->map_to_world(Vector2(j, i), true) + ofs);
@@ -1539,8 +1542,12 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
 		for (int i = 0; i < 4; i++) {
 		for (int i = 0; i < 4; i++) {
 			if (node->get_half_offset() == TileMap::HALF_OFFSET_X && ABS(over_tile.y) & 1)
 			if (node->get_half_offset() == TileMap::HALF_OFFSET_X && ABS(over_tile.y) & 1)
 				endpoints[i] += cell_xf[0] * 0.5;
 				endpoints[i] += cell_xf[0] * 0.5;
+			if (node->get_half_offset() == TileMap::HALF_OFFSET_NEGATIVE_X && ABS(over_tile.y) & 1)
+				endpoints[i] += cell_xf[0] * -0.5;
 			if (node->get_half_offset() == TileMap::HALF_OFFSET_Y && ABS(over_tile.x) & 1)
 			if (node->get_half_offset() == TileMap::HALF_OFFSET_Y && ABS(over_tile.x) & 1)
 				endpoints[i] += cell_xf[1] * 0.5;
 				endpoints[i] += cell_xf[1] * 0.5;
+			if (node->get_half_offset() == TileMap::HALF_OFFSET_NEGATIVE_Y && ABS(over_tile.x) & 1)
+				endpoints[i] += cell_xf[1] * -0.5;
 			endpoints[i] = xform.xform(endpoints[i]);
 			endpoints[i] = xform.xform(endpoints[i]);
 		}
 		}
 		Color col;
 		Color col;

+ 6 - 18
editor/project_manager.cpp

@@ -1609,40 +1609,28 @@ void ProjectManager::_show_project(const String &p_path) {
 	OS::get_singleton()->shell_open(String("file://") + p_path);
 	OS::get_singleton()->shell_open(String("file://") + p_path);
 }
 }
 
 
-void ProjectManager::_scan_dir(DirAccess *da, float pos, float total, List<String> *r_projects) {
-
-	List<String> subdirs;
+void ProjectManager::_scan_dir(const String &path, List<String> *r_projects) {
+	DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+	da->change_dir(path);
 	da->list_dir_begin();
 	da->list_dir_begin();
 	String n = da->get_next();
 	String n = da->get_next();
 	while (n != String()) {
 	while (n != String()) {
 		if (da->current_is_dir() && !n.begins_with(".")) {
 		if (da->current_is_dir() && !n.begins_with(".")) {
-			subdirs.push_front(n);
+			_scan_dir(da->get_current_dir().plus_file(n), r_projects);
 		} else if (n == "project.godot") {
 		} else if (n == "project.godot") {
 			r_projects->push_back(da->get_current_dir());
 			r_projects->push_back(da->get_current_dir());
 		}
 		}
 		n = da->get_next();
 		n = da->get_next();
 	}
 	}
 	da->list_dir_end();
 	da->list_dir_end();
-	int m = 0;
-	for (List<String>::Element *E = subdirs.front(); E; E = E->next()) {
-
-		da->change_dir(E->get());
-
-		float slice = total / subdirs.size();
-		_scan_dir(da, pos + slice * m, slice, r_projects);
-		da->change_dir("..");
-		m++;
-	}
+	memdelete(da);
 }
 }
 
 
 void ProjectManager::_scan_begin(const String &p_base) {
 void ProjectManager::_scan_begin(const String &p_base) {
 
 
 	print_line("Scanning projects at: " + p_base);
 	print_line("Scanning projects at: " + p_base);
 	List<String> projects;
 	List<String> projects;
-	DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
-	da->change_dir(p_base);
-	_scan_dir(da, 0, 1, &projects);
-	memdelete(da);
+	_scan_dir(p_base, &projects);
 	print_line("Found " + itos(projects.size()) + " projects.");
 	print_line("Found " + itos(projects.size()) + " projects.");
 
 
 	for (List<String>::Element *E = projects.front(); E; E = E->next()) {
 	for (List<String>::Element *E = projects.front(); E; E = E->next()) {

+ 1 - 1
editor/project_manager.h

@@ -102,7 +102,7 @@ class ProjectManager : public Control {
 	void _on_project_created(const String &dir);
 	void _on_project_created(const String &dir);
 	void _on_projects_updated();
 	void _on_projects_updated();
 	void _update_scroll_position(const String &dir);
 	void _update_scroll_position(const String &dir);
-	void _scan_dir(DirAccess *da, float pos, float total, List<String> *r_projects);
+	void _scan_dir(const String &path, List<String> *r_projects);
 
 
 	void _install_project(const String &p_zip_path, const String &p_title);
 	void _install_project(const String &p_zip_path, const String &p_title);
 
 

+ 32 - 2
editor/spatial_editor_gizmos.cpp

@@ -36,6 +36,7 @@
 #include "scene/3d/baked_lightmap.h"
 #include "scene/3d/baked_lightmap.h"
 #include "scene/3d/collision_polygon.h"
 #include "scene/3d/collision_polygon.h"
 #include "scene/3d/collision_shape.h"
 #include "scene/3d/collision_shape.h"
+#include "scene/3d/cpu_particles.h"
 #include "scene/3d/gi_probe.h"
 #include "scene/3d/gi_probe.h"
 #include "scene/3d/light.h"
 #include "scene/3d/light.h"
 #include "scene/3d/listener.h"
 #include "scene/3d/listener.h"
@@ -571,9 +572,11 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point,
 
 
 		Transform orig_camera_transform = p_camera->get_camera_transform();
 		Transform orig_camera_transform = p_camera->get_camera_transform();
 
 
-		if (orig_camera_transform.origin.distance_squared_to(t.origin) > 0.01) {
+		if (orig_camera_transform.origin.distance_squared_to(t.origin) > 0.01 &&
+				ABS(orig_camera_transform.basis.get_axis(Vector3::AXIS_Z).dot(Vector3(0, 1, 0))) < 0.99) {
 			p_camera->look_at(t.origin, Vector3(0, 1, 0));
 			p_camera->look_at(t.origin, Vector3(0, 1, 0));
 		}
 		}
+
 		Vector3 c0 = t.xform(Vector3(selectable_icon_size, selectable_icon_size, 0) * scale);
 		Vector3 c0 = t.xform(Vector3(selectable_icon_size, selectable_icon_size, 0) * scale);
 		Vector3 c1 = t.xform(Vector3(-selectable_icon_size, -selectable_icon_size, 0) * scale);
 		Vector3 c1 = t.xform(Vector3(-selectable_icon_size, -selectable_icon_size, 0) * scale);
 
 
@@ -582,7 +585,7 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point,
 
 
 		p_camera->set_global_transform(orig_camera_transform);
 		p_camera->set_global_transform(orig_camera_transform);
 
 
-		Rect2 rect(p0, p1 - p0);
+		Rect2 rect(p0, (p1 - p0).abs());
 
 
 		rect.set_position(center - rect.get_size() / 2.0);
 		rect.set_position(center - rect.get_size() / 2.0);
 
 
@@ -2373,6 +2376,33 @@ void VisibilityNotifierGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
 
 
 ////
 ////
 
 
+CPUParticlesGizmoPlugin::CPUParticlesGizmoPlugin() {
+	create_icon_material("particles_icon", SpatialEditor::get_singleton()->get_icon("GizmoCPUParticles", "EditorIcons"));
+}
+
+bool CPUParticlesGizmoPlugin::has_gizmo(Spatial *p_spatial) {
+	return Object::cast_to<CPUParticles>(p_spatial) != NULL;
+}
+
+String CPUParticlesGizmoPlugin::get_name() const {
+	return "CPUParticles";
+}
+
+int CPUParticlesGizmoPlugin::get_priority() const {
+	return -1;
+}
+
+bool CPUParticlesGizmoPlugin::is_selectable_when_hidden() const {
+	return true;
+}
+
+void CPUParticlesGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
+	Ref<Material> icon = get_material("particles_icon", p_gizmo);
+	p_gizmo->add_unscaled_billboard(icon, 0.05);
+}
+
+////
+
 ParticlesGizmoPlugin::ParticlesGizmoPlugin() {
 ParticlesGizmoPlugin::ParticlesGizmoPlugin() {
 	Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4));
 	Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4));
 	create_material("particles_material", gizmo_color);
 	create_material("particles_material", gizmo_color);

+ 12 - 0
editor/spatial_editor_gizmos.h

@@ -249,6 +249,18 @@ public:
 	VisibilityNotifierGizmoPlugin();
 	VisibilityNotifierGizmoPlugin();
 };
 };
 
 
+class CPUParticlesGizmoPlugin : public EditorSpatialGizmoPlugin {
+	GDCLASS(CPUParticlesGizmoPlugin, EditorSpatialGizmoPlugin);
+
+public:
+	bool has_gizmo(Spatial *p_spatial);
+	String get_name() const;
+	int get_priority() const;
+	bool is_selectable_when_hidden() const;
+	void redraw(EditorSpatialGizmo *p_gizmo);
+	CPUParticlesGizmoPlugin();
+};
+
 class ParticlesGizmoPlugin : public EditorSpatialGizmoPlugin {
 class ParticlesGizmoPlugin : public EditorSpatialGizmoPlugin {
 
 
 	GDCLASS(ParticlesGizmoPlugin, EditorSpatialGizmoPlugin);
 	GDCLASS(ParticlesGizmoPlugin, EditorSpatialGizmoPlugin);

+ 7 - 16
main/input_default.cpp

@@ -627,7 +627,8 @@ bool InputDefault::is_emulating_mouse_from_touch() const {
 	return emulate_mouse_from_touch;
 	return emulate_mouse_from_touch;
 }
 }
 
 
-Input::CursorShape InputDefault::get_default_cursor_shape() {
+Input::CursorShape InputDefault::get_default_cursor_shape() const {
+
 	return default_shape;
 	return default_shape;
 }
 }
 
 
@@ -646,6 +647,11 @@ void InputDefault::set_default_cursor_shape(CursorShape p_shape) {
 	parse_input_event(mm);
 	parse_input_event(mm);
 }
 }
 
 
+Input::CursorShape InputDefault::get_current_cursor_shape() const {
+
+	return (Input::CursorShape)OS::get_singleton()->get_cursor_shape();
+}
+
 void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
 void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
 	if (Engine::get_singleton()->is_editor_hint())
 	if (Engine::get_singleton()->is_editor_hint())
 		return;
 		return;
@@ -653,21 +659,6 @@ void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_sh
 	OS::get_singleton()->set_custom_mouse_cursor(p_cursor, (OS::CursorShape)p_shape, p_hotspot);
 	OS::get_singleton()->set_custom_mouse_cursor(p_cursor, (OS::CursorShape)p_shape, p_hotspot);
 }
 }
 
 
-void InputDefault::set_mouse_in_window(bool p_in_window) {
-	/* no longer supported, leaving this for reference to anyone who might want to implement hardware cursors
-	if (custom_cursor.is_valid()) {
-
-		if (p_in_window) {
-			set_mouse_mode(MOUSE_MODE_HIDDEN);
-			VisualServer::get_singleton()->cursor_set_visible(true);
-		} else {
-			set_mouse_mode(MOUSE_MODE_VISIBLE);
-			VisualServer::get_singleton()->cursor_set_visible(false);
-		}
-	}
-	*/
-}
-
 void InputDefault::accumulate_input_event(const Ref<InputEvent> &p_event) {
 void InputDefault::accumulate_input_event(const Ref<InputEvent> &p_event) {
 	ERR_FAIL_COND(p_event.is_null());
 	ERR_FAIL_COND(p_event.is_null());
 
 

+ 2 - 2
main/input_default.h

@@ -243,10 +243,10 @@ public:
 	void set_emulate_mouse_from_touch(bool p_emulate);
 	void set_emulate_mouse_from_touch(bool p_emulate);
 	virtual bool is_emulating_mouse_from_touch() const;
 	virtual bool is_emulating_mouse_from_touch() const;
 
 
-	virtual CursorShape get_default_cursor_shape();
+	virtual CursorShape get_default_cursor_shape() const;
 	virtual void set_default_cursor_shape(CursorShape p_shape);
 	virtual void set_default_cursor_shape(CursorShape p_shape);
+	virtual CursorShape get_current_cursor_shape() const;
 	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
 	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
-	virtual void set_mouse_in_window(bool p_in_window);
 
 
 	void parse_mapping(String p_mapping);
 	void parse_mapping(String p_mapping);
 	void joy_button(int p_device, int p_button, bool p_pressed);
 	void joy_button(int p_device, int p_button, bool p_pressed);

+ 5 - 1
main/main.cpp

@@ -204,7 +204,8 @@ void finalize_physics() {
 
 
 void Main::print_help(const char *p_binary) {
 void Main::print_help(const char *p_binary) {
 
 
-	print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - https://godotengine.org");
+	print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE));
+	OS::get_singleton()->print("Free and open source software under the terms of the MIT license.\n");
 	OS::get_singleton()->print("(c) 2007-2019 Juan Linietsky, Ariel Manzur.\n");
 	OS::get_singleton()->print("(c) 2007-2019 Juan Linietsky, Ariel Manzur.\n");
 	OS::get_singleton()->print("(c) 2014-2019 Godot Engine contributors.\n");
 	OS::get_singleton()->print("(c) 2014-2019 Godot Engine contributors.\n");
 	OS::get_singleton()->print("\n");
 	OS::get_singleton()->print("\n");
@@ -1087,6 +1088,9 @@ error:
 
 
 Error Main::setup2(Thread::ID p_main_tid_override) {
 Error Main::setup2(Thread::ID p_main_tid_override) {
 
 
+	// Print engine name and version
+	print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE));
+
 	if (p_main_tid_override) {
 	if (p_main_tid_override) {
 		Thread::_main_thread_id = p_main_tid_override;
 		Thread::_main_thread_id = p_main_tid_override;
 	}
 	}

+ 1 - 0
methods.py

@@ -61,6 +61,7 @@ def update_version(module_version_string=""):
     f.write("#define VERSION_BUILD \"" + str(build_name) + "\"\n")
     f.write("#define VERSION_BUILD \"" + str(build_name) + "\"\n")
     f.write("#define VERSION_MODULE_CONFIG \"" + str(version.module_config) + module_version_string + "\"\n")
     f.write("#define VERSION_MODULE_CONFIG \"" + str(version.module_config) + module_version_string + "\"\n")
     f.write("#define VERSION_YEAR " + str(version.year) + "\n")
     f.write("#define VERSION_YEAR " + str(version.year) + "\n")
+    f.write("#define VERSION_WEBSITE \"" + str(version.website) + "\"\n")
     f.close()
     f.close()
 
 
     # NOTE: It is safe to generate this file here, since this is still executed serially
     # NOTE: It is safe to generate this file here, since this is still executed serially

+ 7 - 2
misc/dist/html/full-size.html

@@ -162,8 +162,13 @@ $GODOT_HEAD_INCLUDE
 			requestAnimationFrame(animate);
 			requestAnimationFrame(animate);
 
 
 			function adjustCanvasDimensions() {
 			function adjustCanvasDimensions() {
-				canvas.width = innerWidth;
-				canvas.height = innerHeight;
+				var scale = window.devicePixelRatio || 1;
+				var width = window.innerWidth;
+				var height = window.innerHeight;
+				canvas.width = width * scale;
+				canvas.height = height * scale;
+				canvas.style.width = width + "px";
+				canvas.style.height = height + "px";
 			}
 			}
 			animationCallbacks.push(adjustCanvasDimensions);
 			animationCallbacks.push(adjustCanvasDimensions);
 			adjustCanvasDimensions();
 			adjustCanvasDimensions();

+ 3 - 0
modules/csg/csg_shape.cpp

@@ -2060,6 +2060,9 @@ CSGBrush *CSGPolygon::_build_brush() {
 				for (int i = 0; i <= splits; i++) {
 				for (int i = 0; i <= splits; i++) {
 
 
 					float ofs = i * path_interval;
 					float ofs = i * path_interval;
+					if (ofs > bl) {
+						ofs = bl;
+					}
 					if (i == splits && path_joined) {
 					if (i == splits && path_joined) {
 						ofs = 0.0;
 						ofs = 0.0;
 					}
 					}

+ 1 - 1
modules/gdnative/gdnative.cpp

@@ -243,7 +243,7 @@ void GDNativeLibrary::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix);
 	ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix);
 	ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable);
 	ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable);
 
 
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file");
+	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile", 0), "set_config_file", "get_config_file");
 
 
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");

+ 4 - 0
modules/gdnative/gdnative.h

@@ -99,16 +99,20 @@ public:
 	}
 	}
 
 
 	_FORCE_INLINE_ void set_load_once(bool p_load_once) {
 	_FORCE_INLINE_ void set_load_once(bool p_load_once) {
+		config_file->set_value("general", "load_once", p_load_once);
 		load_once = p_load_once;
 		load_once = p_load_once;
 	}
 	}
 	_FORCE_INLINE_ void set_singleton(bool p_singleton) {
 	_FORCE_INLINE_ void set_singleton(bool p_singleton) {
+		config_file->set_value("general", "singleton", p_singleton);
 		singleton = p_singleton;
 		singleton = p_singleton;
 	}
 	}
 	_FORCE_INLINE_ void set_symbol_prefix(String p_symbol_prefix) {
 	_FORCE_INLINE_ void set_symbol_prefix(String p_symbol_prefix) {
+		config_file->set_value("general", "symbol_prefix", p_symbol_prefix);
 		symbol_prefix = p_symbol_prefix;
 		symbol_prefix = p_symbol_prefix;
 	}
 	}
 
 
 	_FORCE_INLINE_ void set_reloadable(bool p_reloadable) {
 	_FORCE_INLINE_ void set_reloadable(bool p_reloadable) {
+		config_file->set_value("general", "reloadable", p_reloadable);
 		reloadable = p_reloadable;
 		reloadable = p_reloadable;
 	}
 	}
 
 

+ 1 - 1
modules/gdnative/gdnative/variant.cpp

@@ -518,7 +518,7 @@ void GDAPI godot_variant_evaluate(godot_variant_operator p_op, const godot_varia
 	const Variant *a = (const Variant *)p_a;
 	const Variant *a = (const Variant *)p_a;
 	const Variant *b = (const Variant *)p_b;
 	const Variant *b = (const Variant *)p_b;
 	Variant *ret = (Variant *)r_ret;
 	Variant *ret = (Variant *)r_ret;
-	Variant::evaluate(op, a, b, *ret, *r_valid);
+	Variant::evaluate(op, *a, *b, *ret, *r_valid);
 }
 }
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus

+ 5 - 2
modules/gdnative/nativescript/nativescript.cpp

@@ -1309,7 +1309,7 @@ void NativeScriptLanguage::unregister_binding_functions(int p_idx) {
 	for (Set<Vector<void *> *>::Element *E = binding_instances.front(); E; E = E->next()) {
 	for (Set<Vector<void *> *>::Element *E = binding_instances.front(); E; E = E->next()) {
 		Vector<void *> &binding_data = *E->get();
 		Vector<void *> &binding_data = *E->get();
 
 
-		if (binding_data[p_idx] && binding_functions[p_idx].second.free_instance_binding_data)
+		if (p_idx < binding_data.size() && binding_data[p_idx] && binding_functions[p_idx].second.free_instance_binding_data)
 			binding_functions[p_idx].second.free_instance_binding_data(binding_functions[p_idx].second.data, binding_data[p_idx]);
 			binding_functions[p_idx].second.free_instance_binding_data(binding_functions[p_idx].second.data, binding_data[p_idx]);
 	}
 	}
 
 
@@ -1345,7 +1345,7 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec
 
 
 	if (!(*binding_data)[p_idx]) {
 	if (!(*binding_data)[p_idx]) {
 
 
-		const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name());
+		const void *global_type_tag = get_global_type_tag(p_idx, p_object->get_class_name());
 
 
 		// no binding data yet, soooooo alloc new one \o/
 		// no binding data yet, soooooo alloc new one \o/
 		(*binding_data).write[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object);
 		(*binding_data).write[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object);
@@ -1454,6 +1454,9 @@ const void *NativeScriptLanguage::get_global_type_tag(int p_idx, StringName p_cl
 
 
 	const HashMap<StringName, const void *> &tags = global_type_tags[p_idx];
 	const HashMap<StringName, const void *> &tags = global_type_tags[p_idx];
 
 
+	if (!tags.has(p_class_name))
+		return NULL;
+
 	const void *tag = tags.get(p_class_name);
 	const void *tag = tags.get(p_class_name);
 
 
 	return tag;
 	return tag;

+ 6 - 0
modules/gdscript/gdscript.cpp

@@ -1945,6 +1945,10 @@ String GDScriptWarning::get_message() const {
 			CHECK_SYMBOLS(1);
 			CHECK_SYMBOLS(1);
 			return "The local variable '" + symbols[0] + "' is declared but never used in the block.";
 			return "The local variable '" + symbols[0] + "' is declared but never used in the block.";
 		} break;
 		} break;
+		case SHADOWED_VARIABLE: {
+			CHECK_SYMBOLS(2);
+			return "The local variable '" + symbols[0] + "' is shadowing an already defined variable at line " + symbols[1] + ".";
+		} break;
 		case UNUSED_CLASS_VARIABLE: {
 		case UNUSED_CLASS_VARIABLE: {
 			CHECK_SYMBOLS(1);
 			CHECK_SYMBOLS(1);
 			return "The class variable '" + symbols[0] + "' is declared but never used in the script.";
 			return "The class variable '" + symbols[0] + "' is declared but never used in the script.";
@@ -2048,6 +2052,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
 		"UNASSIGNED_VARIABLE",
 		"UNASSIGNED_VARIABLE",
 		"UNASSIGNED_VARIABLE_OP_ASSIGN",
 		"UNASSIGNED_VARIABLE_OP_ASSIGN",
 		"UNUSED_VARIABLE",
 		"UNUSED_VARIABLE",
+		"SHADOWED_VARIABLE",
 		"UNUSED_CLASS_VARIABLE",
 		"UNUSED_CLASS_VARIABLE",
 		"UNUSED_ARGUMENT",
 		"UNUSED_ARGUMENT",
 		"UNREACHABLE_CODE",
 		"UNREACHABLE_CODE",
@@ -2129,6 +2134,7 @@ GDScriptLanguage::GDScriptLanguage() {
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 	GLOBAL_DEF("debug/gdscript/warnings/enable", true);
 	GLOBAL_DEF("debug/gdscript/warnings/enable", true);
 	GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false);
 	GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false);
+	GLOBAL_DEF("debug/gdscript/warnings/exclude_addons", true);
 	GLOBAL_DEF("debug/gdscript/completion/autocomplete_setters_and_getters", false);
 	GLOBAL_DEF("debug/gdscript/completion/autocomplete_setters_and_getters", false);
 	for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
 	for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
 		String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
 		String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();

+ 1 - 0
modules/gdscript/gdscript.h

@@ -273,6 +273,7 @@ struct GDScriptWarning {
 		UNASSIGNED_VARIABLE, // Variable used but never assigned
 		UNASSIGNED_VARIABLE, // Variable used but never assigned
 		UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc)
 		UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc)
 		UNUSED_VARIABLE, // Local variable is declared but never used
 		UNUSED_VARIABLE, // Local variable is declared but never used
+		SHADOWED_VARIABLE, // Variable name shadowed by other variable
 		UNUSED_CLASS_VARIABLE, // Class variable is declared but never used in the file
 		UNUSED_CLASS_VARIABLE, // Class variable is declared but never used in the file
 		UNUSED_ARGUMENT, // Function argument is never used
 		UNUSED_ARGUMENT, // Function argument is never used
 		UNREACHABLE_CODE, // Code after a return statement
 		UNREACHABLE_CODE, // Code after a return statement

+ 35 - 27
modules/gdscript/gdscript_function.cpp

@@ -133,35 +133,13 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta
 	return NULL;
 	return NULL;
 }
 }
 
 
-String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const String &p_where, const Variant **argptrs) const {
-
-	String err_text;
-
-	if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) {
-		int errorarg = p_err.argument;
-		err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(p_err.expected) + ".";
-	} else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
-		err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
-	} else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
-		err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
-	} else if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_METHOD) {
-		err_text = "Invalid call. Nonexistent " + p_where + ".";
-	} else if (p_err.error == Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
-		err_text = "Attempt to call " + p_where + " on a null instance.";
-	} else {
-		err_text = "Bug, call error: #" + itos(p_err.error);
-	}
-
-	return err_text;
-}
-
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
-static String _get_var_type(const Variant *p_type) {
+static String _get_var_type(const Variant *p_var) {
 
 
 	String basestr;
 	String basestr;
 
 
-	if (p_type->get_type() == Variant::OBJECT) {
-		Object *bobj = *p_type;
+	if (p_var->get_type() == Variant::OBJECT) {
+		Object *bobj = *p_var;
 		if (!bobj) {
 		if (!bobj) {
 			basestr = "null instance";
 			basestr = "null instance";
 		} else {
 		} else {
@@ -176,12 +154,42 @@ static String _get_var_type(const Variant *p_type) {
 		}
 		}
 
 
 	} else {
 	} else {
-		basestr = Variant::get_type_name(p_type->get_type());
+		basestr = Variant::get_type_name(p_var->get_type());
 	}
 	}
 
 
 	return basestr;
 	return basestr;
 }
 }
-#endif
+#endif // DEBUG_ENABLED
+
+String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const String &p_where, const Variant **argptrs) const {
+
+	String err_text;
+
+	if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+		int errorarg = p_err.argument;
+		// Handle the Object to Object case separately as we don't have further class details.
+#ifdef DEBUG_ENABLED
+		if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
+			err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class.";
+		} else
+#endif // DEBUG_ENABLED
+		{
+			err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(p_err.expected) + ".";
+		}
+	} else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
+		err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+	} else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
+		err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+	} else if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_METHOD) {
+		err_text = "Invalid call. Nonexistent " + p_where + ".";
+	} else if (p_err.error == Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
+		err_text = "Attempt to call " + p_where + " on a null instance.";
+	} else {
+		err_text = "Bug, call error: #" + itos(p_err.error);
+	}
+
+	return err_text;
+}
 
 
 #if defined(__GNUC__)
 #if defined(__GNUC__)
 #define OPCODES_TABLE                         \
 #define OPCODES_TABLE                         \

+ 23 - 3
modules/gdscript/gdscript_parser.cpp

@@ -5238,6 +5238,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
 			if (base_script.is_valid()) {
 			if (base_script.is_valid()) {
 
 
 				String ident = base;
 				String ident = base;
+				Ref<GDScript> find_subclass = base_script;
 
 
 				for (int i = extend_iter; i < p_class->extends_class.size(); i++) {
 				for (int i = extend_iter; i < p_class->extends_class.size(); i++) {
 
 
@@ -5247,7 +5248,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
 
 
 					if (base_script->get_subclasses().has(subclass)) {
 					if (base_script->get_subclasses().has(subclass)) {
 
 
-						base_script = base_script->get_subclasses()[subclass];
+						find_subclass = base_script->get_subclasses()[subclass];
 					} else if (base_script->get_constants().has(subclass)) {
 					} else if (base_script->get_constants().has(subclass)) {
 
 
 						Ref<GDScript> new_base_class = base_script->get_constants()[subclass];
 						Ref<GDScript> new_base_class = base_script->get_constants()[subclass];
@@ -5255,7 +5256,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
 							_set_error("Constant is not a class: " + ident, p_class->line);
 							_set_error("Constant is not a class: " + ident, p_class->line);
 							return;
 							return;
 						}
 						}
-						base_script = new_base_class;
+						find_subclass = new_base_class;
 					} else {
 					} else {
 
 
 						_set_error("Could not find subclass: " + ident, p_class->line);
 						_set_error("Could not find subclass: " + ident, p_class->line);
@@ -5263,7 +5264,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
 					}
 					}
 				}
 				}
 
 
-				script = base_script;
+				script = find_subclass;
 
 
 			} else if (!base_class) {
 			} else if (!base_class) {
 
 
@@ -7661,6 +7662,11 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
 		if (p_function->arguments_usage[i] == 0 && !p_function->arguments[i].operator String().begins_with("_")) {
 		if (p_function->arguments_usage[i] == 0 && !p_function->arguments[i].operator String().begins_with("_")) {
 			_add_warning(GDScriptWarning::UNUSED_ARGUMENT, p_function->line, p_function->name, p_function->arguments[i].operator String());
 			_add_warning(GDScriptWarning::UNUSED_ARGUMENT, p_function->line, p_function->name, p_function->arguments[i].operator String());
 		}
 		}
+		for (int j = 0; j < current_class->variables.size(); j++) {
+			if (current_class->variables[j].identifier == p_function->arguments[i]) {
+				_add_warning(GDScriptWarning::SHADOWED_VARIABLE, p_function->line, p_function->arguments[i], itos(current_class->variables[j].line));
+			}
+		}
 #endif // DEBUG_ENABLED
 #endif // DEBUG_ENABLED
 	}
 	}
 
 
@@ -7734,6 +7740,17 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
 		p_function->return_type.has_type = false;
 		p_function->return_type.has_type = false;
 		p_function->return_type.may_yield = true;
 		p_function->return_type.may_yield = true;
 	}
 	}
+
+#ifdef DEBUG_ENABLED
+	for (Map<StringName, LocalVarNode *>::Element *E = p_function->body->variables.front(); E; E = E->next()) {
+		LocalVarNode *lv = E->get();
+		for (int i = 0; i < current_class->variables.size(); i++) {
+			if (current_class->variables[i].identifier == lv->name) {
+				_add_warning(GDScriptWarning::SHADOWED_VARIABLE, lv->line, lv->name, itos(current_class->variables[i].line));
+			}
+		}
+	}
+#endif // DEBUG_ENABLED
 }
 }
 
 
 void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) {
 void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) {
@@ -8163,6 +8180,9 @@ void GDScriptParser::_add_warning(int p_code, int p_line, const String &p_symbol
 }
 }
 
 
 void GDScriptParser::_add_warning(int p_code, int p_line, const Vector<String> &p_symbols) {
 void GDScriptParser::_add_warning(int p_code, int p_line, const Vector<String> &p_symbols) {
+	if (GLOBAL_GET("debug/gdscript/warnings/exclude_addons").booleanize() && base_path.begins_with("res://addons/")) {
+		return;
+	}
 	if (tokenizer->is_ignoring_warnings() || !GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) {
 	if (tokenizer->is_ignoring_warnings() || !GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) {
 		return;
 		return;
 	}
 	}

+ 3 - 3
modules/mono/config.py

@@ -120,8 +120,8 @@ def configure(env):
             else:
             else:
                 env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix))
                 env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix))
 
 
-                env.Append(LIBS='psapi')
-                env.Append(LIBS='version')
+                env.Append(LIBS=['psapi'])
+                env.Append(LIBS=['version'])
         else:
         else:
             mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
             mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
 
 
@@ -131,7 +131,7 @@ def configure(env):
             if env.msvc:
             if env.msvc:
                 env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
                 env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
             else:
             else:
-                env.Append(LIBS=mono_lib_name)
+                env.Append(LIBS=[mono_lib_name])
 
 
             mono_bin_path = os.path.join(mono_root, 'bin')
             mono_bin_path = os.path.join(mono_root, 'bin')
 
 

+ 7 - 2
modules/mono/csharp_script.cpp

@@ -919,7 +919,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
 }
 }
 #endif
 #endif
 
 
-void CSharpLanguage::project_assembly_loaded() {
+void CSharpLanguage::_load_scripts_metadata() {
 
 
 	scripts_metadata.clear();
 	scripts_metadata.clear();
 
 
@@ -953,6 +953,7 @@ void CSharpLanguage::project_assembly_loaded() {
 		}
 		}
 
 
 		scripts_metadata = old_dict_var.operator Dictionary();
 		scripts_metadata = old_dict_var.operator Dictionary();
+		scripts_metadata_invalidated = false;
 
 
 		print_verbose("Successfully loaded scripts metadata");
 		print_verbose("Successfully loaded scripts metadata");
 	} else {
 	} else {
@@ -1024,11 +1025,13 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) {
 	}
 	}
 }
 }
 
 
-void CSharpLanguage::_uninitialize_script_bindings() {
+void CSharpLanguage::_on_scripts_domain_unloaded() {
 	for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
 	for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
 		CSharpScriptBinding &script_binding = E->value();
 		CSharpScriptBinding &script_binding = E->value();
 		script_binding.inited = false;
 		script_binding.inited = false;
 	}
 	}
+
+	scripts_metadata_invalidated = true;
 }
 }
 
 
 void CSharpLanguage::set_language_index(int p_idx) {
 void CSharpLanguage::set_language_index(int p_idx) {
@@ -1086,6 +1089,8 @@ CSharpLanguage::CSharpLanguage() {
 #endif
 #endif
 
 
 	lang_idx = -1;
 	lang_idx = -1;
+
+	scripts_metadata_invalidated = true;
 }
 }
 
 
 CSharpLanguage::~CSharpLanguage() {
 CSharpLanguage::~CSharpLanguage() {

+ 12 - 3
modules/mono/csharp_script.h

@@ -309,14 +309,17 @@ class CSharpLanguage : public ScriptLanguage {
 	int lang_idx;
 	int lang_idx;
 
 
 	Dictionary scripts_metadata;
 	Dictionary scripts_metadata;
+	bool scripts_metadata_invalidated;
 
 
 	// For debug_break and debug_break_parse
 	// For debug_break and debug_break_parse
 	int _debug_parse_err_line;
 	int _debug_parse_err_line;
 	String _debug_parse_err_file;
 	String _debug_parse_err_file;
 	String _debug_error;
 	String _debug_error;
 
 
+	void _load_scripts_metadata();
+
 	friend class GDMono;
 	friend class GDMono;
-	void _uninitialize_script_bindings();
+	void _on_scripts_domain_unloaded();
 
 
 public:
 public:
 	StringNameCache string_names;
 	StringNameCache string_names;
@@ -341,9 +344,15 @@ public:
 	void reload_assemblies(bool p_soft_reload);
 	void reload_assemblies(bool p_soft_reload);
 #endif
 #endif
 
 
-	void project_assembly_loaded();
+	_FORCE_INLINE_ Dictionary get_scripts_metadata_or_nothing() {
+		return scripts_metadata_invalidated ? Dictionary() : scripts_metadata;
+	}
 
 
-	_FORCE_INLINE_ const Dictionary &get_scripts_metadata() { return scripts_metadata; }
+	_FORCE_INLINE_ const Dictionary &get_scripts_metadata() {
+		if (scripts_metadata_invalidated)
+			_load_scripts_metadata();
+		return scripts_metadata;
+	}
 
 
 	virtual String get_name() const;
 	virtual String get_name() const;
 
 

+ 1 - 1
modules/mono/doc_classes/@C#.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <?xml version="1.0" encoding="UTF-8" ?>
-<class name="@C#" category="Core" version="3.1.1">
+<class name="@C#" category="Core" version="3.1.2">
 	<brief_description>
 	<brief_description>
 	</brief_description>
 	</brief_description>
 	<description>
 	<description>

+ 1 - 1
modules/mono/doc_classes/CSharpScript.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <?xml version="1.0" encoding="UTF-8" ?>
-<class name="CSharpScript" inherits="Script" category="Core" version="3.1.1">
+<class name="CSharpScript" inherits="Script" category="Core" version="3.1.2">
 	<brief_description>
 	<brief_description>
 	</brief_description>
 	</brief_description>
 	<description>
 	<description>

+ 1 - 1
modules/mono/doc_classes/GodotSharp.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <?xml version="1.0" encoding="UTF-8" ?>
-<class name="GodotSharp" inherits="Object" category="Core" version="3.1.1">
+<class name="GodotSharp" inherits="Object" category="Core" version="3.1.2">
 	<brief_description>
 	<brief_description>
 	</brief_description>
 	</brief_description>
 	<description>
 	<description>

+ 1 - 1
modules/mono/editor/csharp_project.cpp

@@ -158,7 +158,7 @@ Error generate_scripts_metadata(const String &p_project_path, const String &p_ou
 	PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret);
 	PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret);
 	PoolStringArray::Read r = project_files.read();
 	PoolStringArray::Read r = project_files.read();
 
 
-	Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata();
+	Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata_or_nothing();
 	Dictionary new_dict;
 	Dictionary new_dict;
 
 
 	for (int i = 0; i < project_files.size(); i++) {
 	for (int i = 0; i < project_files.size(); i++) {

+ 8 - 7
modules/mono/mono_gd/gd_mono.cpp

@@ -121,25 +121,28 @@ void gdmono_debug_init() {
 
 
 	mono_debug_init(MONO_DEBUG_FORMAT_MONO);
 	mono_debug_init(MONO_DEBUG_FORMAT_MONO);
 
 
+	CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
+
+#ifdef TOOLS_ENABLED
 	int da_port = GLOBAL_DEF("mono/debugger_agent/port", 23685);
 	int da_port = GLOBAL_DEF("mono/debugger_agent/port", 23685);
 	bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
 	bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
 	int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
 	int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
 
 
-#ifdef TOOLS_ENABLED
 	if (Engine::get_singleton()->is_editor_hint() ||
 	if (Engine::get_singleton()->is_editor_hint() ||
 			ProjectSettings::get_singleton()->get_resource_path().empty() ||
 			ProjectSettings::get_singleton()->get_resource_path().empty() ||
 			Main::is_project_manager()) {
 			Main::is_project_manager()) {
 		return;
 		return;
 	}
 	}
-#endif
-
-	CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
 
 
 	if (da_args.length() == 0) {
 	if (da_args.length() == 0) {
 		da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
 		da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
 						 ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
 						 ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
 						  .utf8();
 						  .utf8();
 	}
 	}
+#else
+	if (da_args.length() == 0)
+		return; // Exported games don't use the project settings to setup the debugger agent
+#endif
 
 
 	// --debugger-agent=help
 	// --debugger-agent=help
 	const char *options[] = {
 	const char *options[] = {
@@ -665,8 +668,6 @@ bool GDMono::_load_project_assembly() {
 
 
 	if (success) {
 	if (success) {
 		mono_assembly_set_main(project_assembly->get_assembly());
 		mono_assembly_set_main(project_assembly->get_assembly());
-
-		CSharpLanguage::get_singleton()->project_assembly_loaded();
 	} else {
 	} else {
 		if (OS::get_singleton()->is_stdout_verbose())
 		if (OS::get_singleton()->is_stdout_verbose())
 			print_error("Mono: Failed to load project assembly");
 			print_error("Mono: Failed to load project assembly");
@@ -873,7 +874,7 @@ Error GDMono::reload_scripts_domain() {
 		}
 		}
 	}
 	}
 
 
-	CSharpLanguage::get_singleton()->_uninitialize_script_bindings();
+	CSharpLanguage::get_singleton()->_on_scripts_domain_unloaded();
 
 
 	Error err = _load_scripts_domain();
 	Error err = _load_scripts_domain();
 	if (err != OK) {
 	if (err != OK) {

+ 1 - 0
modules/regex/SCsub

@@ -33,6 +33,7 @@ if env['builtin_pcre2']:
         "pcre2_newline.c",
         "pcre2_newline.c",
         "pcre2_ord2utf.c",
         "pcre2_ord2utf.c",
         "pcre2_pattern_info.c",
         "pcre2_pattern_info.c",
+        "pcre2_script_run.c",
         "pcre2_serialize.c",
         "pcre2_serialize.c",
         "pcre2_string_utils.c",
         "pcre2_string_utils.c",
         "pcre2_study.c",
         "pcre2_study.c",

+ 2 - 2
modules/stb_vorbis/audio_stream_ogg_vorbis.cpp

@@ -266,8 +266,8 @@ void AudioStreamOGGVorbis::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset);
 	ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset);
 
 
 	ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
 	ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop", "has_loop");
-	ADD_PROPERTY(PropertyInfo(Variant::REAL, "loop_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop_offset", "get_loop_offset");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "loop_offset"), "set_loop_offset", "get_loop_offset");
 }
 }
 
 
 AudioStreamOGGVorbis::AudioStreamOGGVorbis() {
 AudioStreamOGGVorbis::AudioStreamOGGVorbis() {

+ 2 - 1
modules/upnp/SCsub

@@ -23,10 +23,11 @@ if env['builtin_miniupnpc']:
         "portlistingparse.c",
         "portlistingparse.c",
         "upnpreplyparse.c",
         "upnpreplyparse.c",
     ]
     ]
-    thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+    thirdparty_sources = [thirdparty_dir + "miniupnpc/" + file for file in thirdparty_sources]
 
 
     env_upnp.Append(CPPPATH=[thirdparty_dir])
     env_upnp.Append(CPPPATH=[thirdparty_dir])
     env_upnp.Append(CPPFLAGS=["-DMINIUPNP_STATICLIB"])
     env_upnp.Append(CPPFLAGS=["-DMINIUPNP_STATICLIB"])
+    env_upnp.Append(CPPFLAGS=["-DMINIUPNPC_SET_SOCKET_TIMEOUT"])
 
 
     env_thirdparty = env_upnp.Clone()
     env_thirdparty = env_upnp.Clone()
     env_thirdparty.disable_warnings()
     env_thirdparty.disable_warnings()

+ 2 - 13
modules/visual_script/visual_script.cpp

@@ -45,15 +45,7 @@ bool VisualScriptNode::is_breakpoint() const {
 	return breakpoint;
 	return breakpoint;
 }
 }
 
 
-void VisualScriptNode::_notification(int p_what) {
-
-	if (p_what == NOTIFICATION_POSTINITIALIZE) {
-		validate_input_default_values();
-	}
-}
-
 void VisualScriptNode::ports_changed_notify() {
 void VisualScriptNode::ports_changed_notify() {
-	validate_input_default_values();
 	emit_signal("ports_changed");
 	emit_signal("ports_changed");
 }
 }
 
 
@@ -272,11 +264,7 @@ void VisualScript::_node_ports_changed(int p_id) {
 	Function &func = functions[function];
 	Function &func = functions[function];
 	Ref<VisualScriptNode> vsn = func.nodes[p_id].node;
 	Ref<VisualScriptNode> vsn = func.nodes[p_id].node;
 
 
-	if (OS::get_singleton()->get_main_loop() &&
-			Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop()) &&
-			Engine::get_singleton()->is_editor_hint()) {
-		vsn->validate_input_default_values(); //force validate default values when editing on editor
-	}
+	vsn->validate_input_default_values();
 
 
 	//must revalidate all the functions
 	//must revalidate all the functions
 
 
@@ -352,6 +340,7 @@ void VisualScript::add_node(const StringName &p_func, int p_id, const Ref<Visual
 	Ref<VisualScriptNode> vsn = p_node;
 	Ref<VisualScriptNode> vsn = p_node;
 	vsn->connect("ports_changed", this, "_node_ports_changed", varray(p_id));
 	vsn->connect("ports_changed", this, "_node_ports_changed", varray(p_id));
 	vsn->scripts_used.insert(this);
 	vsn->scripts_used.insert(this);
+	vsn->validate_input_default_values(); // Validate when fully loaded
 
 
 	func.nodes[p_id] = nd;
 	func.nodes[p_id] = nd;
 }
 }

+ 0 - 1
modules/visual_script/visual_script.h

@@ -54,7 +54,6 @@ class VisualScriptNode : public Resource {
 	void validate_input_default_values();
 	void validate_input_default_values();
 
 
 protected:
 protected:
-	void _notification(int p_what);
 	void ports_changed_notify();
 	void ports_changed_notify();
 	static void _bind_methods();
 	static void _bind_methods();
 
 

+ 1 - 1
modules/visual_script/visual_script_editor.cpp

@@ -2040,7 +2040,7 @@ void VisualScriptEditor::set_edit_state(const Variant &p_state) {
 
 
 	Dictionary d = p_state;
 	Dictionary d = p_state;
 	if (d.has("function")) {
 	if (d.has("function")) {
-		edited_func = p_state;
+		edited_func = d["function"];
 		selected = edited_func;
 		selected = edited_func;
 	}
 	}
 
 

+ 1 - 1
platform/SCsub

@@ -28,4 +28,4 @@ with open_utf8('register_platform_apis.gen.cpp', 'w') as f:
 platform_sources.append('register_platform_apis.gen.cpp')
 platform_sources.append('register_platform_apis.gen.cpp')
 
 
 lib = env.add_library('platform', platform_sources)
 lib = env.add_library('platform', platform_sources)
-env.Prepend(LIBS=lib)
+env.Prepend(LIBS=[lib])

+ 1 - 1
platform/android/build.gradle.template

@@ -5,7 +5,7 @@ buildscript {
 		$$GRADLE_REPOSITORY_URLS$$
 		$$GRADLE_REPOSITORY_URLS$$
 	}
 	}
 	dependencies {
 	dependencies {
-		classpath 'com.android.tools.build:gradle:3.2.1'
+		classpath 'com.android.tools.build:gradle:3.4.2'
 		$$GRADLE_CLASSPATH$$
 		$$GRADLE_CLASSPATH$$
 	}
 	}
 }
 }

+ 11 - 15
platform/android/java/README.md → platform/android/java/THIRDPARTY.md

@@ -1,5 +1,7 @@
-# Third party libraries
+# Third-party libraries
 
 
+This file list third-party libraries used in the Android source folder,
+with their provenance and, when relevant, modifications made to those files.
 
 
 ## Google's vending library
 ## Google's vending library
 
 
@@ -7,12 +9,13 @@
 - Version: git (eb57657, 2018) with modifications
 - Version: git (eb57657, 2018) with modifications
 - License: Apache 2.0
 - License: Apache 2.0
 
 
-Overwrite all files under `com/google/android/vending`
+Overwrite all files under `com/google/android/vending`.
 
 
-### Modify some files to avoid compile error and lint warning
+Modify those files to avoid compile error and lint warning:
 
 
-#### com/google/android/vending/licensing/util/Base64.java
-```
+- `com/google/android/vending/licensing/util/Base64.java`
+
+```diff
 @@ -338,7 +338,8 @@ public class Base64 {
 @@ -338,7 +338,8 @@ public class Base64 {
                         e += 4;
                         e += 4;
                 }
                 }
@@ -24,8 +27,9 @@ Overwrite all files under `com/google/android/vending`
         }
         }
 ```
 ```
 
 
-#### com/google/android/vending/licensing/LicenseChecker.java
-```
+- `com/google/android/vending/licensing/LicenseChecker.java`
+
+```diff
 @@ -29,8 +29,8 @@ import android.os.RemoteException;
 @@ -29,8 +29,8 @@ import android.os.RemoteException;
  import android.provider.Settings.Secure;
  import android.provider.Settings.Secure;
  import android.util.Log;
  import android.util.Log;
@@ -37,11 +41,3 @@ Overwrite all files under `com/google/android/vending`
  import com.google.android.vending.licensing.util.Base64;
  import com.google.android.vending.licensing.util.Base64;
  import com.google.android.vending.licensing.util.Base64DecoderException;
  import com.google.android.vending.licensing.util.Base64DecoderException;
 ```
 ```
-```
-@@ -287,13 +287,15 @@ public class LicenseChecker implements ServiceConnection {
-     if (logResponse) {
--        String android_id = Secure.getString(mContext.getContentResolver(),
--                            Secure.ANDROID_ID);
-+        String android_id = Secure.ANDROID_ID;
-         Date date = new Date();
-```

+ 1 - 1
platform/android/java/gradle/wrapper/gradle-wrapper.properties

@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
 zipStorePath=wrapper/dists

+ 2 - 1
platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java

@@ -746,7 +746,8 @@ public abstract class DownloaderService extends CustomIntentService implements I
 		public void run() {
 		public void run() {
 			setServiceRunning(true);
 			setServiceRunning(true);
 			mNotification.onDownloadStateChanged(IDownloaderClient.STATE_FETCHING_URL);
 			mNotification.onDownloadStateChanged(IDownloaderClient.STATE_FETCHING_URL);
-			String deviceId = Secure.ANDROID_ID;
+			String deviceId = Secure.getString(mContext.getContentResolver(),
+					Secure.ANDROID_ID);
 
 
 			final APKExpansionPolicy aep = new APKExpansionPolicy(mContext,
 			final APKExpansionPolicy aep = new APKExpansionPolicy(mContext,
 					new AESObfuscator(getSALT(), mContext.getPackageName(), deviceId));
 					new AESObfuscator(getSALT(), mContext.getPackageName(), deviceId));

+ 2 - 1
platform/android/java/src/com/google/android/vending/licensing/LicenseChecker.java

@@ -287,7 +287,8 @@ public class LicenseChecker implements ServiceConnection {
 						}
 						}
 
 
 						if (logResponse) {
 						if (logResponse) {
-							String android_id = Secure.ANDROID_ID;
+							String android_id = Secure.getString(mContext.getContentResolver(),
+									Secure.ANDROID_ID);
 							Date date = new Date();
 							Date date = new Date();
 							Log.d(TAG, "Server Failure: " + stringError);
 							Log.d(TAG, "Server Failure: " + stringError);
 							Log.d(TAG, "Android ID: " + android_id);
 							Log.d(TAG, "Android ID: " + android_id);

+ 0 - 8
platform/android/os_android.cpp

@@ -287,14 +287,6 @@ bool OS_Android::can_draw() const {
 	return true; //always?
 	return true; //always?
 }
 }
 
 
-void OS_Android::set_cursor_shape(CursorShape p_shape) {
-
-	//android really really really has no mouse.. how amazing..
-}
-
-void OS_Android::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
-}
-
 void OS_Android::main_loop_begin() {
 void OS_Android::main_loop_begin() {
 
 
 	if (main_loop)
 	if (main_loop)

+ 0 - 3
platform/android/os_android.h

@@ -187,9 +187,6 @@ public:
 
 
 	virtual bool can_draw() const;
 	virtual bool can_draw() const;
 
 
-	virtual void set_cursor_shape(CursorShape p_shape);
-	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
-
 	void main_loop_begin();
 	void main_loop_begin();
 	bool main_loop_iterate();
 	bool main_loop_iterate();
 	void main_loop_request_go_back();
 	void main_loop_request_go_back();

+ 4 - 0
platform/haiku/os_haiku.cpp

@@ -203,6 +203,10 @@ void OS_Haiku::set_cursor_shape(CursorShape p_shape) {
 	//ERR_PRINT("set_cursor_shape() NOT IMPLEMENTED");
 	//ERR_PRINT("set_cursor_shape() NOT IMPLEMENTED");
 }
 }
 
 
+OS::CursorShape OS_Haiku::get_cursor_shape() const {
+	// TODO: implement get_cursor_shape
+}
+
 void OS_Haiku::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
 void OS_Haiku::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
 	// TODO
 	// TODO
 }
 }

+ 1 - 0
platform/haiku/os_haiku.h

@@ -86,6 +86,7 @@ public:
 	virtual Point2 get_mouse_position() const;
 	virtual Point2 get_mouse_position() const;
 	virtual int get_mouse_button_state() const;
 	virtual int get_mouse_button_state() const;
 	virtual void set_cursor_shape(CursorShape p_shape);
 	virtual void set_cursor_shape(CursorShape p_shape);
+	virtual CursorShape get_cursor_shape() const;
 	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
 	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
 
 
 	virtual int get_screen_count() const;
 	virtual int get_screen_count() const;

+ 0 - 6
platform/iphone/os_iphone.cpp

@@ -491,17 +491,11 @@ void OSIPhone::set_keep_screen_on(bool p_enabled) {
 	_set_keep_screen_on(p_enabled);
 	_set_keep_screen_on(p_enabled);
 };
 };
 
 
-void OSIPhone::set_cursor_shape(CursorShape p_shape){
-
-};
-
 String OSIPhone::get_user_data_dir() const {
 String OSIPhone::get_user_data_dir() const {
 
 
 	return data_dir;
 	return data_dir;
 };
 };
 
 
-void OSIPhone::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot){};
-
 String OSIPhone::get_name() {
 String OSIPhone::get_name() {
 
 
 	return "iOS";
 	return "iOS";

+ 0 - 3
platform/iphone/os_iphone.h

@@ -167,9 +167,6 @@ public:
 	virtual void hide_virtual_keyboard();
 	virtual void hide_virtual_keyboard();
 	virtual int get_virtual_keyboard_height() const;
 	virtual int get_virtual_keyboard_height() const;
 
 
-	virtual void set_cursor_shape(CursorShape p_shape);
-	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
-
 	virtual Size2 get_window_size() const;
 	virtual Size2 get_window_size() const;
 	virtual Rect2 get_window_safe_area() const;
 	virtual Rect2 get_window_safe_area() const;
 
 

+ 4 - 0
platform/iphone/view_controller.h

@@ -39,6 +39,10 @@
 
 
 - (void)didReceiveMemoryWarning;
 - (void)didReceiveMemoryWarning;
 
 
+- (void)viewDidLoad;
+
+- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures;
+
 - (BOOL)prefersStatusBarHidden;
 - (BOOL)prefersStatusBarHidden;
 
 
 @end
 @end

+ 12 - 0
platform/iphone/view_controller.mm

@@ -83,6 +83,18 @@ int add_cmdline(int p_argc, char **p_args) {
 	printf("*********** did receive memory warning!\n");
 	printf("*********** did receive memory warning!\n");
 };
 };
 
 
+- (void)viewDidLoad {
+	[super viewDidLoad];
+
+	if (@available(iOS 11.0, *)) {
+		[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
+	}
+}
+
+- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
+	return UIRectEdgeAll;
+}
+
 - (BOOL)shouldAutorotate {
 - (BOOL)shouldAutorotate {
 	switch (OS::get_singleton()->get_screen_orientation()) {
 	switch (OS::get_singleton()->get_screen_orientation()) {
 		case OS::SCREEN_SENSOR:
 		case OS::SCREEN_SENSOR:

+ 18 - 4
platform/javascript/os_javascript.cpp

@@ -70,6 +70,20 @@ static bool is_canvas_focused() {
 	/* clang-format on */
 	/* clang-format on */
 }
 }
 
 
+static Point2 correct_canvas_position(int x, int y) {
+	int canvas_width;
+	int canvas_height;
+	emscripten_get_canvas_element_size(NULL, &canvas_width, &canvas_height);
+
+	double element_width;
+	double element_height;
+	emscripten_get_element_css_size(NULL, &element_width, &element_height);
+
+	x = (int)(canvas_width / element_width * x);
+	y = (int)(canvas_height / element_height * y);
+	return Point2(x, y);
+}
+
 static bool cursor_inside_canvas = true;
 static bool cursor_inside_canvas = true;
 
 
 EM_BOOL OS_JavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
 EM_BOOL OS_JavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
@@ -285,7 +299,7 @@ EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenM
 	Ref<InputEventMouseButton> ev;
 	Ref<InputEventMouseButton> ev;
 	ev.instance();
 	ev.instance();
 	ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN);
 	ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN);
-	ev->set_position(Point2(p_event->canvasX, p_event->canvasY));
+	ev->set_position(correct_canvas_position(p_event->canvasX, p_event->canvasY));
 	ev->set_global_position(ev->get_position());
 	ev->set_global_position(ev->get_position());
 	dom2godot_mod(p_event, ev);
 	dom2godot_mod(p_event, ev);
 	switch (p_event->button) {
 	switch (p_event->button) {
@@ -349,7 +363,7 @@ EM_BOOL OS_JavaScript::mousemove_callback(int p_event_type, const EmscriptenMous
 	OS_JavaScript *os = get_singleton();
 	OS_JavaScript *os = get_singleton();
 
 
 	int input_mask = os->input->get_mouse_button_mask();
 	int input_mask = os->input->get_mouse_button_mask();
-	Point2 pos = Point2(p_event->canvasX, p_event->canvasY);
+	Point2 pos = correct_canvas_position(p_event->canvasX, p_event->canvasY);
 	// For motion outside the canvas, only read mouse movement if dragging
 	// For motion outside the canvas, only read mouse movement if dragging
 	// started inside the canvas; imitating desktop app behaviour.
 	// started inside the canvas; imitating desktop app behaviour.
 	if (!cursor_inside_canvas && !input_mask)
 	if (!cursor_inside_canvas && !input_mask)
@@ -666,7 +680,7 @@ EM_BOOL OS_JavaScript::touch_press_callback(int p_event_type, const EmscriptenTo
 		if (!touch.isChanged)
 		if (!touch.isChanged)
 			continue;
 			continue;
 		ev->set_index(touch.identifier);
 		ev->set_index(touch.identifier);
-		ev->set_position(Point2(touch.canvasX, touch.canvasY));
+		ev->set_position(correct_canvas_position(touch.canvasX, touch.canvasY));
 		os->touches[i] = ev->get_position();
 		os->touches[i] = ev->get_position();
 		ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART);
 		ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART);
 
 
@@ -691,7 +705,7 @@ EM_BOOL OS_JavaScript::touchmove_callback(int p_event_type, const EmscriptenTouc
 		if (!touch.isChanged)
 		if (!touch.isChanged)
 			continue;
 			continue;
 		ev->set_index(touch.identifier);
 		ev->set_index(touch.identifier);
-		ev->set_position(Point2(touch.canvasX, touch.canvasY));
+		ev->set_position(correct_canvas_position(touch.canvasX, touch.canvasY));
 		Point2 &prev = os->touches[i];
 		Point2 &prev = os->touches[i];
 		ev->set_relative(ev->get_position() - prev);
 		ev->set_relative(ev->get_position() - prev);
 		prev = ev->get_position();
 		prev = ev->get_position();

+ 1 - 0
platform/osx/os_osx.h

@@ -172,6 +172,7 @@ public:
 	virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
 	virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
 
 
 	virtual void set_cursor_shape(CursorShape p_shape);
 	virtual void set_cursor_shape(CursorShape p_shape);
+	virtual CursorShape get_cursor_shape() const;
 	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
 	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
 
 
 	virtual void set_mouse_show(bool p_show);
 	virtual void set_mouse_show(bool p_show);

+ 5 - 4
platform/osx/os_osx.mm

@@ -714,8 +714,6 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
 
 
 	if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
 	if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
 		OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
 		OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
-	if (OS_OSX::singleton->input)
-		OS_OSX::singleton->input->set_mouse_in_window(false);
 }
 }
 
 
 - (void)mouseEntered:(NSEvent *)event {
 - (void)mouseEntered:(NSEvent *)event {
@@ -723,8 +721,6 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
 		return;
 		return;
 	if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
 	if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
 		OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
 		OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
-	if (OS_OSX::singleton->input)
-		OS_OSX::singleton->input->set_mouse_in_window(true);
 
 
 	OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape;
 	OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape;
 	OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
 	OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
@@ -1700,6 +1696,11 @@ void OS_OSX::set_cursor_shape(CursorShape p_shape) {
 	cursor_shape = p_shape;
 	cursor_shape = p_shape;
 }
 }
 
 
+OS::CursorShape OS_OSX::get_cursor_shape() const {
+
+	return cursor_shape;
+}
+
 void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
 void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
 	if (p_cursor.is_valid()) {
 	if (p_cursor.is_valid()) {
 		Ref<Texture> texture = p_cursor;
 		Ref<Texture> texture = p_cursor;

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