Browse Source

-Many fixes to VisualScript, fixed property names, etc.
-Added ability to set/get a field in GetSet, as well as assignment ops
-Added a Select node
-Fixed update bugs related to variable list and exported properties, closes #9458

Juan Linietsky 8 years ago
parent
commit
2a3e00c8c7
36 changed files with 1089 additions and 293 deletions
  1. 7 9
      core/variant_op.cpp
  2. 117 9
      drivers/gles3/rasterizer_canvas_gles3.cpp
  3. 2 0
      drivers/gles3/rasterizer_canvas_gles3.h
  4. 7 6
      editor/plugins/script_editor_plugin.cpp
  5. 3 0
      editor/plugins/script_editor_plugin.h
  6. 4 0
      editor/plugins/script_text_editor.cpp
  7. 1 1
      editor/plugins/script_text_editor.h
  8. 1 1
      editor/property_selector.cpp
  9. 1 0
      modules/visual_script/register_types.cpp
  10. 10 4
      modules/visual_script/visual_script.cpp
  11. 1 1
      modules/visual_script/visual_script.h
  12. 184 120
      modules/visual_script/visual_script_editor.cpp
  13. 21 3
      modules/visual_script/visual_script_editor.h
  14. 8 8
      modules/visual_script/visual_script_expression.cpp
  15. 19 4
      modules/visual_script/visual_script_flow_control.cpp
  16. 3 0
      modules/visual_script/visual_script_flow_control.h
  17. 275 54
      modules/visual_script/visual_script_func_nodes.cpp
  18. 30 0
      modules/visual_script/visual_script_func_nodes.h
  19. 151 44
      modules/visual_script/visual_script_nodes.cpp
  20. 33 0
      modules/visual_script/visual_script_nodes.h
  21. 7 7
      modules/visual_script/visual_script_yield_nodes.cpp
  22. 23 0
      scene/2d/canvas_item.cpp
  23. 2 0
      scene/2d/canvas_item.h
  24. 3 0
      scene/3d/mesh_instance.cpp
  25. 15 5
      scene/gui/graph_edit.cpp
  26. 1 1
      scene/gui/graph_edit.h
  27. 16 6
      scene/gui/split_container.cpp
  28. 17 7
      scene/resources/primitive_meshes.cpp
  29. 2 1
      scene/resources/primitive_meshes.h
  30. 38 0
      servers/visual/rasterizer.h
  31. 82 2
      servers/visual/visual_server_canvas.cpp
  32. 1 0
      servers/visual/visual_server_canvas.h
  33. 1 0
      servers/visual/visual_server_raster.h
  34. 1 0
      servers/visual/visual_server_viewport.cpp
  35. 1 0
      servers/visual/visual_server_wrap_mt.h
  36. 1 0
      servers/visual_server.h

+ 7 - 9
core/variant_op.cpp

@@ -1109,11 +1109,11 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid)
 
 				const String *str = reinterpret_cast<const String *>(p_index._data._mem);
 				Vector2 *v = reinterpret_cast<Vector2 *>(_data._mem);
-				if (*str == "x" || *str == "width") {
+				if (*str == "x") {
 					valid = true;
 					v->x = p_value;
 					return;
-				} else if (*str == "y" || *str == "height") {
+				} else if (*str == "y") {
 					valid = true;
 					v->y = p_value;
 					return;
@@ -1177,7 +1177,7 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid)
 					valid = true;
 					v->elements[1] = p_value;
 					return;
-				} else if (*str == "o") {
+				} else if (*str == "origin") {
 					valid = true;
 					v->elements[2] = p_value;
 					return;
@@ -1572,10 +1572,10 @@ Variant Variant::get(const Variant &p_index, bool *r_valid) const {
 
 				const String *str = reinterpret_cast<const String *>(p_index._data._mem);
 				const Vector2 *v = reinterpret_cast<const Vector2 *>(_data._mem);
-				if (*str == "x" || *str == "width") {
+				if (*str == "x") {
 					valid = true;
 					return v->x;
-				} else if (*str == "y" || *str == "height") {
+				} else if (*str == "y") {
 					valid = true;
 					return v->y;
 				}
@@ -1657,7 +1657,7 @@ Variant Variant::get(const Variant &p_index, bool *r_valid) const {
 				} else if (*str == "y") {
 					valid = true;
 					return v->elements[1];
-				} else if (*str == "o") {
+				} else if (*str == "origin") {
 					valid = true;
 					return v->elements[2];
 				}
@@ -2105,8 +2105,6 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
 
 			p_list->push_back(PropertyInfo(Variant::REAL, "x"));
 			p_list->push_back(PropertyInfo(Variant::REAL, "y"));
-			p_list->push_back(PropertyInfo(Variant::REAL, "width"));
-			p_list->push_back(PropertyInfo(Variant::REAL, "height"));
 
 		} break; // 5
 		case RECT2: {
@@ -2127,7 +2125,7 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const {
 
 			p_list->push_back(PropertyInfo(Variant::VECTOR2, "x"));
 			p_list->push_back(PropertyInfo(Variant::VECTOR2, "y"));
-			p_list->push_back(PropertyInfo(Variant::VECTOR2, "o"));
+			p_list->push_back(PropertyInfo(Variant::VECTOR2, "origin"));
 
 		} break;
 		case PLANE: {

+ 117 - 9
drivers/gles3/rasterizer_canvas_gles3.cpp

@@ -348,6 +348,53 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun
 	glBindVertexArray(0);
 }
 
+void RasterizerCanvasGLES3::_draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor) {
+
+	glBindVertexArray(data.polygon_buffer_pointer_array);
+	glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
+
+	uint32_t buffer_ofs = 0;
+
+	//vertex
+	glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_vertices);
+	glEnableVertexAttribArray(VS::ARRAY_VERTEX);
+	glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, false, sizeof(Vector2), ((uint8_t *)0) + buffer_ofs);
+	buffer_ofs += sizeof(Vector2) * p_vertex_count;
+	//color
+
+	if (p_singlecolor) {
+		glDisableVertexAttribArray(VS::ARRAY_COLOR);
+		Color m = *p_colors;
+		glVertexAttrib4f(VS::ARRAY_COLOR, m.r, m.g, m.b, m.a);
+	} else if (!p_colors) {
+		glDisableVertexAttribArray(VS::ARRAY_COLOR);
+		glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);
+	} else {
+
+		glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors);
+		glEnableVertexAttribArray(VS::ARRAY_COLOR);
+		glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, false, sizeof(Color), ((uint8_t *)0) + buffer_ofs);
+		buffer_ofs += sizeof(Color) * p_vertex_count;
+	}
+
+	if (p_uvs) {
+
+		glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs);
+		glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
+		glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, false, sizeof(Vector2), ((uint8_t *)0) + buffer_ofs);
+		buffer_ofs += sizeof(Vector2) * p_vertex_count;
+
+	} else {
+		glDisableVertexAttribArray(VS::ARRAY_TEX_UV);
+	}
+
+	glDrawArrays(p_primitive, 0, p_vertex_count);
+
+	storage->frame.canvas_draw_commands++;
+
+	glBindVertexArray(0);
+}
+
 void RasterizerCanvasGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs) {
 
 	static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_FAN };
@@ -425,22 +472,83 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
 
 				glVertexAttrib4f(VS::ARRAY_COLOR, line->color.r, line->color.g, line->color.b, line->color.a);
 
-				Vector2 verts[2] = {
-					Vector2(line->from.x, line->from.y),
-					Vector2(line->to.x, line->to.y)
-				};
+				if (line->width <= 1) {
+					Vector2 verts[2] = {
+						Vector2(line->from.x, line->from.y),
+						Vector2(line->to.x, line->to.y)
+					};
 
 #ifdef GLES_OVER_GL
-				if (line->antialiased)
-					glEnable(GL_LINE_SMOOTH);
+					if (line->antialiased)
+						glEnable(GL_LINE_SMOOTH);
 #endif
-				//glLineWidth(line->width);
-				_draw_gui_primitive(2, verts, NULL, NULL);
+					//glLineWidth(line->width);
+					_draw_gui_primitive(2, verts, NULL, NULL);
 
 #ifdef GLES_OVER_GL
-				if (line->antialiased)
+					if (line->antialiased)
+						glDisable(GL_LINE_SMOOTH);
+#endif
+				} else {
+					//thicker line
+
+					Vector2 t = (line->from - line->to).normalized().tangent() * line->width * 0.5;
+
+					Vector2 verts[4] = {
+						line->from - t,
+						line->from + t,
+						line->to + t,
+						line->to - t,
+					};
+
+					//glLineWidth(line->width);
+					_draw_gui_primitive(4, verts, NULL, NULL);
+#ifdef GLES_OVER_GL
+					if (line->antialiased) {
+						glEnable(GL_LINE_SMOOTH);
+						for (int i = 0; i < 4; i++) {
+							Vector2 vertsl[2] = {
+								verts[i],
+								verts[(i + 1) % 4],
+							};
+							_draw_gui_primitive(2, vertsl, NULL, NULL);
+						}
+						glDisable(GL_LINE_SMOOTH);
+					}
+#endif
+				}
+
+			} break;
+			case Item::Command::TYPE_POLYLINE: {
+
+				Item::CommandPolyLine *pline = static_cast<Item::CommandPolyLine *>(c);
+				_set_texture_rect_mode(false);
+
+				_bind_canvas_texture(RID(), RID());
+
+				if (pline->triangles.size()) {
+
+					_draw_generic(GL_TRIANGLE_STRIP, pline->triangles.size(), pline->triangles.ptr(), NULL, pline->triangle_colors.ptr(), pline->triangle_colors.size() == 1);
+#ifdef GLES_OVER_GL
+					glEnable(GL_LINE_SMOOTH);
+					if (pline->lines.size()) {
+						_draw_generic(GL_LINE_LOOP, pline->lines.size(), pline->lines.ptr(), NULL, pline->line_colors.ptr(), pline->line_colors.size() == 1);
+					}
 					glDisable(GL_LINE_SMOOTH);
 #endif
+				} else {
+
+#ifdef GLES_OVER_GL
+					if (pline->antialiased)
+						glEnable(GL_LINE_SMOOTH);
+#endif
+					_draw_generic(GL_LINE_STRIP, pline->lines.size(), pline->lines.ptr(), NULL, pline->line_colors.ptr(), pline->line_colors.size() == 1);
+
+#ifdef GLES_OVER_GL
+					if (pline->antialiased)
+						glDisable(GL_LINE_SMOOTH);
+#endif
+				}
 
 			} break;
 			case Item::Command::TYPE_RECT: {

+ 2 - 0
drivers/gles3/rasterizer_canvas_gles3.h

@@ -121,6 +121,8 @@ public:
 
 	_FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs);
 	_FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
+	_FORCE_INLINE_ void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
+
 	_FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip);
 	_FORCE_INLINE_ void _copy_texscreen(const Rect2 &p_rect);
 

+ 7 - 6
editor/plugins/script_editor_plugin.cpp

@@ -509,9 +509,8 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save) {
 		if (p_save) {
 			apply_scripts();
 		}
-		if (current->get_edit_menu()) {
-			memdelete(current->get_edit_menu());
-		}
+		current->clear_edit_menu();
+
 	} else {
 		EditorHelp *help = tab_container->get_child(selected)->cast_to<EditorHelp>();
 		_add_recent_script(help->get_class());
@@ -2180,13 +2179,15 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
 
 	script_list = memnew(ItemList);
 	list_split->add_child(script_list);
-	script_list->set_custom_minimum_size(Size2(0, 0));
+	script_list->set_custom_minimum_size(Size2(150 * EDSCALE, 100)); //need to give a bit of limit to avoid it from disappearing
+	script_list->set_v_size_flags(SIZE_EXPAND_FILL);
 	script_split->set_split_offset(140);
-	list_split->set_split_offset(500);
+	//list_split->set_split_offset(500);
 
 	members_overview = memnew(ItemList);
 	list_split->add_child(members_overview);
-	members_overview->set_custom_minimum_size(Size2(0, 0));
+	members_overview->set_custom_minimum_size(Size2(0, 100)); //need to give a bit of limit to avoid it from disappearing
+	members_overview->set_v_size_flags(SIZE_EXPAND_FILL);
 
 	tab_container = memnew(TabContainer);
 	tab_container->add_style_override("panel", p_editor->get_gui_base()->get_stylebox("ScriptPanel", "EditorStyles"));

+ 3 - 0
editor/plugins/script_editor_plugin.h

@@ -106,6 +106,7 @@ public:
 
 	virtual void set_tooltip_request_func(String p_method, Object *p_obj) = 0;
 	virtual Control *get_edit_menu() = 0;
+	virtual void clear_edit_menu() = 0;
 
 	ScriptEditorBase() {}
 };
@@ -363,6 +364,8 @@ public:
 
 	bool can_take_away_focus() const;
 
+	VSplitContainer *get_left_list_split() { return list_split; }
+
 	ScriptEditorDebugger *get_debugger() { return debugger; }
 	void set_live_auto_reload_running_scripts(bool p_enabled);
 

+ 4 - 0
editor/plugins/script_text_editor.cpp

@@ -1093,6 +1093,10 @@ Control *ScriptTextEditor::get_edit_menu() {
 	return edit_hb;
 }
 
+void ScriptTextEditor::clear_edit_menu() {
+	memdelete(edit_hb);
+}
+
 void ScriptTextEditor::reload(bool p_soft) {
 
 	TextEdit *te = code_editor->get_text_edit();

+ 1 - 1
editor/plugins/script_text_editor.h

@@ -156,7 +156,7 @@ public:
 	virtual void set_debugger_active(bool p_active);
 
 	Control *get_edit_menu();
-
+	virtual void clear_edit_menu();
 	static void register_editor();
 
 	ScriptTextEditor();

+ 1 - 1
editor/property_selector.cpp

@@ -167,7 +167,7 @@ void PropertySelector::_update_search() {
 				continue;
 			}
 
-			if (!(E->get().usage & PROPERTY_USAGE_EDITOR))
+			if (!(E->get().usage & PROPERTY_USAGE_EDITOR) && !(E->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE))
 				continue;
 
 			if (search_box->get_text() != String() && E->get().name.find(search_box->get_text()) == -1)

+ 1 - 0
modules/visual_script/register_types.cpp

@@ -90,6 +90,7 @@ void register_visual_script_types() {
 	ClassDB::register_class<VisualScriptSequence>();
 	//ClassDB::register_class<VisualScriptInputFilter>();
 	ClassDB::register_class<VisualScriptSwitch>();
+	ClassDB::register_class<VisualScriptSelect>();
 
 	ClassDB::register_class<VisualScriptYield>();
 	ClassDB::register_class<VisualScriptYieldSignal>();

+ 10 - 4
modules/visual_script/visual_script.cpp

@@ -140,7 +140,7 @@ VisualScriptNode::TypeGuess VisualScriptNode::guess_output_type(TypeGuess *p_inp
 
 	tg.type = pinfo.type;
 	if (pinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
-		tg.GDCLASS = pinfo.hint_string;
+		tg.gdclass = pinfo.hint_string;
 	}
 
 	return tg;
@@ -660,6 +660,9 @@ void VisualScript::set_variable_export(const StringName &p_name, bool p_export)
 	ERR_FAIL_COND(!variables.has(p_name));
 
 	variables[p_name]._export = p_export;
+#ifdef TOOLS_ENABLED
+	_update_placeholders();
+#endif
 }
 
 bool VisualScript::get_variable_export(const StringName &p_name) const {
@@ -1067,9 +1070,11 @@ void VisualScript::get_script_property_list(List<PropertyInfo> *p_list) const {
 	get_variable_list(&vars);
 
 	for (List<StringName>::Element *E = vars.front(); E; E = E->next()) {
-		if (!variables[E->get()]._export)
-			continue;
-		p_list->push_back(variables[E->get()].info);
+		//if (!variables[E->get()]._export)
+		//	continue;
+		PropertyInfo pi = variables[E->get()].info;
+		pi.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
+		p_list->push_back(pi);
 	}
 }
 
@@ -1358,6 +1363,7 @@ void VisualScriptInstance::get_property_list(List<PropertyInfo> *p_properties) c
 			continue;
 		PropertyInfo p = E->get().info;
 		p.name = String(E->key());
+		p.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
 		p_properties->push_back(p);
 	}
 }

+ 1 - 1
modules/visual_script/visual_script.h

@@ -89,7 +89,7 @@ public:
 	struct TypeGuess {
 
 		Variant::Type type;
-		StringName GDCLASS;
+		StringName gdclass;
 		Ref<Script> script;
 
 		TypeGuess() {

+ 184 - 120
modules/visual_script/visual_script_editor.cpp

@@ -52,11 +52,13 @@ public:
 protected:
 	static void _bind_methods() {
 		ClassDB::bind_method("_sig_changed", &VisualScriptEditorSignalEdit::_sig_changed);
+		ADD_SIGNAL(MethodInfo("changed"));
 	}
 
 	void _sig_changed() {
 
 		_change_notify();
+		emit_signal("changed");
 	}
 
 	bool _set(const StringName &p_name, const Variant &p_value) {
@@ -191,15 +193,18 @@ protected:
 	static void _bind_methods() {
 		ClassDB::bind_method("_var_changed", &VisualScriptEditorVariableEdit::_var_changed);
 		ClassDB::bind_method("_var_value_changed", &VisualScriptEditorVariableEdit::_var_value_changed);
+		ADD_SIGNAL(MethodInfo("changed"));
 	}
 
 	void _var_changed() {
 
 		_change_notify();
+		emit_signal("changed");
 	}
 	void _var_value_changed() {
 
 		_change_notify("value"); //so the whole tree is not redrawn, makes editing smoother in general
+		emit_signal("changed");
 	}
 
 	bool _set(const StringName &p_name, const Variant &p_value) {
@@ -261,6 +266,7 @@ protected:
 
 		if (String(p_name) == "export") {
 			script->set_variable_export(var, p_value);
+			EditorNode::get_singleton()->get_property_editor()->update_tree();
 			return true;
 		}
 
@@ -699,7 +705,7 @@ void VisualScriptEditor::_update_members() {
 		ti->set_selectable(0, true);
 		ti->set_editable(0, true);
 		//ti->add_button(0,Control::get_icon("Edit","EditorIcons"),0); function arguments are in the node now
-		ti->add_button(0, Control::get_icon("Del", "EditorIcons"), 1);
+		//ti->add_button(0, Control::get_icon("Del", "EditorIcons"), 1);
 		ti->set_metadata(0, E->get());
 		if (E->get() == edited_func) {
 			ti->set_custom_bg_color(0, get_color("prop_category", "Editor"));
@@ -757,8 +763,8 @@ void VisualScriptEditor::_update_members() {
 
 		ti->set_selectable(0, true);
 		ti->set_editable(0, true);
-		ti->add_button(0, Control::get_icon("Edit", "EditorIcons"), 0);
-		ti->add_button(0, Control::get_icon("Del", "EditorIcons"), 1);
+		//ti->add_button(0, Control::get_icon("Edit", "EditorIcons"), 0);
+		//ti->add_button(0, Control::get_icon("Del", "EditorIcons"), 1);
 		ti->set_metadata(0, E->get());
 		if (selected == E->get())
 			ti->select(0);
@@ -777,8 +783,8 @@ void VisualScriptEditor::_update_members() {
 		ti->set_text(0, E->get());
 		ti->set_selectable(0, true);
 		ti->set_editable(0, true);
-		ti->add_button(0, Control::get_icon("Edit", "EditorIcons"), 0);
-		ti->add_button(0, Control::get_icon("Del", "EditorIcons"), 1);
+		//ti->add_button(0, Control::get_icon("Edit", "EditorIcons"), 0);
+		//ti->add_button(0, Control::get_icon("Del", "EditorIcons"), 1);
 		ti->set_metadata(0, E->get());
 		if (selected == E->get())
 			ti->select(0);
@@ -1068,105 +1074,6 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt
 			undo_redo->commit_action();
 			return; //or crash because it will become invalid
 		}
-
-	} else {
-
-		if (ti->get_parent() == root->get_children()) {
-			//edit/remove function
-			String name = ti->get_metadata(0);
-
-			if (p_button == 1) {
-				//delete the function
-				undo_redo->create_action(TTR("Remove Function"));
-				undo_redo->add_do_method(script.ptr(), "remove_function", name);
-				undo_redo->add_undo_method(script.ptr(), "add_function", name);
-				List<int> nodes;
-				script->get_node_list(name, &nodes);
-				for (List<int>::Element *E = nodes.front(); E; E = E->next()) {
-					undo_redo->add_undo_method(script.ptr(), "add_node", name, E->get(), script->get_node(name, E->get()), script->get_node_pos(name, E->get()));
-				}
-
-				List<VisualScript::SequenceConnection> seq_connections;
-
-				script->get_sequence_connection_list(name, &seq_connections);
-
-				for (List<VisualScript::SequenceConnection>::Element *E = seq_connections.front(); E; E = E->next()) {
-					undo_redo->add_undo_method(script.ptr(), "sequence_connect", name, E->get().from_node, E->get().from_output, E->get().to_node);
-				}
-
-				List<VisualScript::DataConnection> data_connections;
-
-				script->get_data_connection_list(name, &data_connections);
-
-				for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
-					undo_redo->add_undo_method(script.ptr(), "data_connect", name, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
-				}
-
-				/*
-				for(int i=0;i<script->function_get_argument_count(name);i++) {
-					undo_redo->add_undo_method(script.ptr(),"function_add_argument",name,script->function_get_argument_name(name,i),script->function_get_argument_type(name,i));
-				}
-				*/
-				undo_redo->add_do_method(this, "_update_members");
-				undo_redo->add_undo_method(this, "_update_members");
-				undo_redo->add_do_method(this, "_update_graph");
-				undo_redo->add_undo_method(this, "_update_graph");
-				undo_redo->commit_action();
-
-			} else if (p_button == 0) {
-			}
-			return; //or crash because it will become invalid
-		}
-
-		if (ti->get_parent() == root->get_children()->get_next()) {
-			//edit/remove variable
-
-			String name = ti->get_metadata(0);
-
-			if (p_button == 1) {
-
-				undo_redo->create_action(TTR("Remove Variable"));
-				undo_redo->add_do_method(script.ptr(), "remove_variable", name);
-				undo_redo->add_undo_method(script.ptr(), "add_variable", name, script->get_variable_default_value(name));
-				undo_redo->add_undo_method(script.ptr(), "set_variable_info", name, script->call("get_variable_info", name)); //return as dict
-				undo_redo->add_do_method(this, "_update_members");
-				undo_redo->add_undo_method(this, "_update_members");
-				undo_redo->commit_action();
-				return; //or crash because it will become invalid
-			} else if (p_button == 0) {
-
-				variable_editor->edit(name);
-				edit_variable_dialog->set_title(TTR("Editing Variable:") + " " + name);
-				edit_variable_dialog->popup_centered_minsize(Size2(400, 200) * EDSCALE);
-			}
-		}
-
-		if (ti->get_parent() == root->get_children()->get_next()->get_next()) {
-			//edit/remove variable
-			String name = ti->get_metadata(0);
-
-			if (p_button == 1) {
-
-				undo_redo->create_action(TTR("Remove Signal"));
-				undo_redo->add_do_method(script.ptr(), "remove_custom_signal", name);
-				undo_redo->add_undo_method(script.ptr(), "add_custom_signal", name);
-
-				for (int i = 0; i < script->custom_signal_get_argument_count(name); i++) {
-					undo_redo->add_undo_method(script.ptr(), "custom_signal_add_argument", name, script->custom_signal_get_argument_name(name, i), script->custom_signal_get_argument_type(name, i));
-				}
-
-				undo_redo->add_do_method(this, "_update_members");
-				undo_redo->add_undo_method(this, "_update_members");
-				undo_redo->commit_action();
-			} else if (p_button == 0) {
-
-				signal_editor->edit(name);
-				edit_signal_dialog->set_title(TTR("Editing Signal:") + " " + name);
-				edit_signal_dialog->popup_centered_minsize(Size2(400, 300) * EDSCALE);
-			}
-
-			return; //or crash because it will become invalid
-		}
 	}
 }
 
@@ -2269,6 +2176,11 @@ void VisualScriptEditor::_change_base_type() {
 	select_base_type->popup_create(true);
 }
 
+void VisualScriptEditor::clear_edit_menu() {
+	memdelete(edit_menu);
+	memdelete(left_vsplit);
+}
+
 void VisualScriptEditor::_change_base_type_callback() {
 
 	String bt = select_base_type->get_selected_type();
@@ -2556,7 +2468,7 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_node, i
 					if (obj) {
 
 						g.type = Variant::OBJECT;
-						g.GDCLASS = obj->get_class();
+						g.gdclass = obj->get_class();
 						g.script = obj->get_script();
 					}
 				}
@@ -2596,8 +2508,8 @@ void VisualScriptEditor::_port_action_menu(int p_option) {
 			if (tg.type == Variant::OBJECT) {
 				n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
 
-				if (tg.GDCLASS != StringName()) {
-					n->set_base_type(tg.GDCLASS);
+				if (tg.gdclass != StringName()) {
+					n->set_base_type(tg.gdclass);
 				} else {
 					n->set_base_type("Object");
 				}
@@ -2627,8 +2539,8 @@ void VisualScriptEditor::_port_action_menu(int p_option) {
 			if (tg.type == Variant::OBJECT) {
 				n->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
 
-				if (tg.GDCLASS != StringName()) {
-					n->set_base_type(tg.GDCLASS);
+				if (tg.gdclass != StringName()) {
+					n->set_base_type(tg.gdclass);
 				} else {
 					n->set_base_type("Object");
 				}
@@ -2657,8 +2569,8 @@ void VisualScriptEditor::_port_action_menu(int p_option) {
 			if (tg.type == Variant::OBJECT) {
 				n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
 
-				if (tg.GDCLASS != StringName()) {
-					n->set_base_type(tg.GDCLASS);
+				if (tg.gdclass != StringName()) {
+					n->set_base_type(tg.gdclass);
 				} else {
 					n->set_base_type("Object");
 				}
@@ -2834,12 +2746,17 @@ void VisualScriptEditor::_notification(int p_what) {
 
 	if (p_what == NOTIFICATION_READY) {
 		node_filter_icon->set_texture(Control::get_icon("Zoom", "EditorIcons"));
+		variable_editor->connect("changed", this, "_update_members");
+		signal_editor->connect("changed", this, "_update_members");
+	}
+	if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+		left_vsplit->set_visible(is_visible_in_tree());
 	}
 }
 
 void VisualScriptEditor::_graph_ofs_changed(const Vector2 &p_ofs) {
 
-	if (updating_graph)
+	if (updating_graph || !script.is_valid())
 		return;
 
 	updating_graph = true;
@@ -3053,6 +2970,142 @@ void VisualScriptEditor::_menu_option(int p_what) {
 	}
 }
 
+void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) {
+
+	TreeItem *ti = members->get_selected();
+	ERR_FAIL_COND(!ti);
+
+	member_popup->clear();
+	member_popup->set_position(members->get_global_position() + p_pos);
+	member_popup->set_size(Vector2());
+
+	TreeItem *root = members->get_root();
+
+	Ref<Texture> del_icon = Control::get_icon("Del", "EditorIcons");
+
+	Ref<Texture> edit_icon = Control::get_icon("Edit", "EditorIcons");
+
+	if (ti->get_parent() == root->get_children()) {
+
+		member_type = MEMBER_FUNCTION;
+		member_name = ti->get_text(0);
+		member_popup->add_icon_item(del_icon, TTR("Remove Function"), MEMBER_REMOVE);
+		member_popup->popup();
+		return;
+	}
+
+	if (ti->get_parent() == root->get_children()->get_next()) {
+
+		member_type = MEMBER_VARIABLE;
+		member_name = ti->get_text(0);
+		member_popup->add_icon_item(edit_icon, TTR("Edit Variable"), MEMBER_EDIT);
+		member_popup->add_separator();
+		member_popup->add_icon_item(del_icon, TTR("Remove Variable"), MEMBER_REMOVE);
+		member_popup->popup();
+		return;
+	}
+
+	if (ti->get_parent() == root->get_children()->get_next()->get_next()) {
+
+		member_type = MEMBER_SIGNAL;
+		member_name = ti->get_text(0);
+		member_popup->add_icon_item(edit_icon, TTR("Edit Signal"), MEMBER_EDIT);
+		member_popup->add_separator();
+		member_popup->add_icon_item(del_icon, TTR("Remove Signal"), MEMBER_REMOVE);
+		member_popup->popup();
+		return;
+	}
+}
+
+void VisualScriptEditor::_member_option(int p_option) {
+
+	switch (member_type) {
+		case MEMBER_FUNCTION: {
+
+			if (p_option == MEMBER_REMOVE) {
+				//delete the function
+				String name = member_name;
+
+				undo_redo->create_action(TTR("Remove Function"));
+				undo_redo->add_do_method(script.ptr(), "remove_function", name);
+				undo_redo->add_undo_method(script.ptr(), "add_function", name);
+				List<int> nodes;
+				script->get_node_list(name, &nodes);
+				for (List<int>::Element *E = nodes.front(); E; E = E->next()) {
+					undo_redo->add_undo_method(script.ptr(), "add_node", name, E->get(), script->get_node(name, E->get()), script->get_node_pos(name, E->get()));
+				}
+
+				List<VisualScript::SequenceConnection> seq_connections;
+
+				script->get_sequence_connection_list(name, &seq_connections);
+
+				for (List<VisualScript::SequenceConnection>::Element *E = seq_connections.front(); E; E = E->next()) {
+					undo_redo->add_undo_method(script.ptr(), "sequence_connect", name, E->get().from_node, E->get().from_output, E->get().to_node);
+				}
+
+				List<VisualScript::DataConnection> data_connections;
+
+				script->get_data_connection_list(name, &data_connections);
+
+				for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
+					undo_redo->add_undo_method(script.ptr(), "data_connect", name, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+				}
+
+				/*
+				for(int i=0;i<script->function_get_argument_count(name);i++) {
+					undo_redo->add_undo_method(script.ptr(),"function_add_argument",name,script->function_get_argument_name(name,i),script->function_get_argument_type(name,i));
+				}
+				*/
+				undo_redo->add_do_method(this, "_update_members");
+				undo_redo->add_undo_method(this, "_update_members");
+				undo_redo->add_do_method(this, "_update_graph");
+				undo_redo->add_undo_method(this, "_update_graph");
+				undo_redo->commit_action();
+			}
+		} break;
+		case MEMBER_VARIABLE: {
+
+			String name = member_name;
+
+			if (p_option == MEMBER_REMOVE) {
+				undo_redo->create_action(TTR("Remove Variable"));
+				undo_redo->add_do_method(script.ptr(), "remove_variable", name);
+				undo_redo->add_undo_method(script.ptr(), "add_variable", name, script->get_variable_default_value(name));
+				undo_redo->add_undo_method(script.ptr(), "set_variable_info", name, script->call("get_variable_info", name)); //return as dict
+				undo_redo->add_do_method(this, "_update_members");
+				undo_redo->add_undo_method(this, "_update_members");
+				undo_redo->commit_action();
+			} else if (p_option == MEMBER_EDIT) {
+				variable_editor->edit(name);
+				edit_variable_dialog->set_title(TTR("Editing Variable:") + " " + name);
+				edit_variable_dialog->popup_centered_minsize(Size2(400, 200) * EDSCALE);
+			}
+		} break;
+		case MEMBER_SIGNAL: {
+			String name = member_name;
+
+			if (p_option == MEMBER_REMOVE) {
+				undo_redo->create_action(TTR("Remove Signal"));
+				undo_redo->add_do_method(script.ptr(), "remove_custom_signal", name);
+				undo_redo->add_undo_method(script.ptr(), "add_custom_signal", name);
+
+				for (int i = 0; i < script->custom_signal_get_argument_count(name); i++) {
+					undo_redo->add_undo_method(script.ptr(), "custom_signal_add_argument", name, script->custom_signal_get_argument_name(name, i), script->custom_signal_get_argument_type(name, i));
+				}
+
+				undo_redo->add_do_method(this, "_update_members");
+				undo_redo->add_undo_method(this, "_update_members");
+				undo_redo->commit_action();
+			} else if (p_option == MEMBER_EDIT) {
+
+				signal_editor->edit(name);
+				edit_signal_dialog->set_title(TTR("Editing Signal:") + " " + name);
+				edit_signal_dialog->popup_centered_minsize(Size2(400, 300) * EDSCALE);
+			}
+		} break;
+	}
+}
+
 void VisualScriptEditor::_bind_methods() {
 
 	ClassDB::bind_method("_member_button", &VisualScriptEditor::_member_button);
@@ -3101,6 +3154,10 @@ void VisualScriptEditor::_bind_methods() {
 
 	ClassDB::bind_method("_selected_method", &VisualScriptEditor::_selected_method);
 	ClassDB::bind_method("_draw_color_over_button", &VisualScriptEditor::_draw_color_over_button);
+
+	ClassDB::bind_method("_member_rmb_selected", &VisualScriptEditor::_member_rmb_selected);
+
+	ClassDB::bind_method("_member_option", &VisualScriptEditor::_member_option);
 }
 
 VisualScriptEditor::VisualScriptEditor() {
@@ -3122,17 +3179,16 @@ VisualScriptEditor::VisualScriptEditor() {
 
 	edit_menu->get_popup()->connect("id_pressed", this, "_menu_option");
 
-	main_hsplit = memnew(HSplitContainer);
-	add_child(main_hsplit);
-	main_hsplit->set_area_as_parent_rect();
-
 	left_vsplit = memnew(VSplitContainer);
-	main_hsplit->add_child(left_vsplit);
+	ScriptEditor::get_singleton()->get_left_list_split()->call_deferred("add_child", left_vsplit); //add but wait until done settig up this
+	left_vsplit->set_v_size_flags(SIZE_EXPAND_FILL);
+	left_vsplit->set_stretch_ratio(2);
+	left_vsplit->hide();
 
 	VBoxContainer *left_vb = memnew(VBoxContainer);
 	left_vsplit->add_child(left_vb);
 	left_vb->set_v_size_flags(SIZE_EXPAND_FILL);
-	left_vb->set_custom_minimum_size(Size2(230, 1) * EDSCALE);
+	//left_vb->set_custom_minimum_size(Size2(230, 1) * EDSCALE);
 
 	base_type_select = memnew(Button);
 	left_vb->add_margin_child(TTR("Base Type:"), base_type_select);
@@ -3174,7 +3230,8 @@ VisualScriptEditor::VisualScriptEditor() {
 	nodes->set_drag_forwarding(this);
 
 	graph = memnew(GraphEdit);
-	main_hsplit->add_child(graph);
+	add_child(graph);
+	graph->set_area_as_parent_rect();
 	graph->set_h_size_flags(SIZE_EXPAND_FILL);
 	graph->connect("node_selected", this, "_node_selected");
 	graph->connect("_begin_node_move", this, "_begin_node_move");
@@ -3190,7 +3247,8 @@ VisualScriptEditor::VisualScriptEditor() {
 	select_func_text->set_align(Label::ALIGN_CENTER);
 	select_func_text->set_valign(Label::VALIGN_CENTER);
 	select_func_text->set_h_size_flags(SIZE_EXPAND_FILL);
-	main_hsplit->add_child(select_func_text);
+	add_child(select_func_text);
+	graph->set_area_as_parent_rect();
 
 	hint_text = memnew(Label);
 	hint_text->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, 100);
@@ -3280,6 +3338,12 @@ VisualScriptEditor::VisualScriptEditor() {
 	port_action_popup = memnew(PopupMenu);
 	add_child(port_action_popup);
 	port_action_popup->connect("id_pressed", this, "_port_action_menu");
+
+	member_popup = memnew(PopupMenu);
+	add_child(member_popup);
+	members->connect("item_rmb_selected", this, "_member_rmb_selected");
+	members->set_allow_rmb_select(true);
+	member_popup->connect("id_pressed", this, "_member_option");
 }
 
 VisualScriptEditor::~VisualScriptEditor() {

+ 21 - 3
modules/visual_script/visual_script_editor.h

@@ -72,15 +72,25 @@ class VisualScriptEditor : public ScriptEditorBase {
 		CREATE_RETURN,
 	};
 
+	enum MemberAction {
+		MEMBER_EDIT,
+		MEMBER_REMOVE
+
+	};
+
+	enum MemberType {
+		MEMBER_FUNCTION,
+		MEMBER_VARIABLE,
+		MEMBER_SIGNAL
+	};
+
+	VSplitContainer *left_vsplit;
 	MenuButton *edit_menu;
 
 	Ref<VisualScript> script;
 
 	Button *base_type_select;
 
-	HSplitContainer *main_hsplit;
-	VSplitContainer *left_vsplit;
-
 	GraphEdit *graph;
 
 	LineEdit *node_filter;
@@ -154,6 +164,10 @@ class VisualScriptEditor : public ScriptEditorBase {
 	static Clipboard *clipboard;
 
 	PopupMenu *port_action_popup;
+	PopupMenu *member_popup;
+
+	MemberType member_type;
+	String member_name;
 
 	PortAction port_action;
 	int port_action_node;
@@ -223,6 +237,9 @@ class VisualScriptEditor : public ScriptEditorBase {
 
 	VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &visited_nodes);
 
+	void _member_rmb_selected(const Vector2 &p_pos);
+	void _member_option(int p_option);
+
 protected:
 	void _notification(int p_what);
 	static void _bind_methods();
@@ -252,6 +269,7 @@ public:
 	virtual void set_debugger_active(bool p_active);
 	virtual void set_tooltip_request_func(String p_method, Object *p_obj);
 	virtual Control *get_edit_menu();
+	virtual void clear_edit_menu();
 	virtual bool can_lose_focus_on_node_selection() { return false; }
 
 	static void register_editor();

+ 8 - 8
modules/visual_script/visual_script_expression.cpp

@@ -68,12 +68,12 @@ bool VisualScriptExpression::_set(const StringName &p_name, const Variant &p_val
 		return true;
 	}
 
-	if (String(p_name).begins_with("input/")) {
+	if (String(p_name).begins_with("input_")) {
 
-		int idx = String(p_name).get_slice("/", 1).to_int();
+		int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int();
 		ERR_FAIL_INDEX_V(idx, inputs.size(), false);
 
-		String what = String(p_name).get_slice("/", 2);
+		String what = String(p_name).get_slice("/", 1);
 
 		if (what == "type") {
 
@@ -115,12 +115,12 @@ bool VisualScriptExpression::_get(const StringName &p_name, Variant &r_ret) cons
 		return true;
 	}
 
-	if (String(p_name).begins_with("input/")) {
+	if (String(p_name).begins_with("input_")) {
 
-		int idx = String(p_name).get_slice("/", 1).to_int();
+		int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int();
 		ERR_FAIL_INDEX_V(idx, inputs.size(), false);
 
-		String what = String(p_name).get_slice("/", 2);
+		String what = String(p_name).get_slice("/", 1);
 
 		if (what == "type") {
 
@@ -151,8 +151,8 @@ void VisualScriptExpression::_get_property_list(List<PropertyInfo> *p_list) cons
 
 	for (int i = 0; i < inputs.size(); i++) {
 
-		p_list->push_back(PropertyInfo(Variant::INT, "input/" + itos(i) + "/type", PROPERTY_HINT_ENUM, argt));
-		p_list->push_back(PropertyInfo(Variant::STRING, "input/" + itos(i) + "/name"));
+		p_list->push_back(PropertyInfo(Variant::INT, "input_" + itos(i) + "/type", PROPERTY_HINT_ENUM, argt));
+		p_list->push_back(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name"));
 	}
 }
 

+ 19 - 4
modules/visual_script/visual_script_flow_control.cpp

@@ -30,6 +30,7 @@
 #include "visual_script_flow_control.h"
 
 #include "global_config.h"
+#include "io/resource_loader.h"
 #include "os/keyboard.h"
 
 //////////////////////////////////////////
@@ -119,8 +120,8 @@ void VisualScriptReturn::_bind_methods() {
 		argt += "," + Variant::get_type_name(Variant::Type(i));
 	}
 
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "return_value/enabled"), "set_enable_return_value", "is_return_value_enabled");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "return_value/type", PROPERTY_HINT_ENUM, argt), "set_return_type", "get_return_type");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "return_enabled"), "set_enable_return_value", "is_return_value_enabled");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "return_type", PROPERTY_HINT_ENUM, argt), "set_return_type", "get_return_type");
 }
 
 class VisualScriptNodeInstanceReturn : public VisualScriptNodeInstance {
@@ -1725,6 +1726,20 @@ String VisualScriptTypeCast::get_base_script() const {
 	return script;
 }
 
+VisualScriptTypeCast::TypeGuess VisualScriptTypeCast::guess_output_type(TypeGuess *p_inputs, int p_output) const {
+
+	TypeGuess tg;
+	tg.type = Variant::OBJECT;
+	if (script != String()) {
+		tg.script = ResourceLoader::load(script);
+	}
+	//if (!tg.script.is_valid()) {
+	//	tg.gdclass = base_type;
+	//}
+
+	return tg;
+}
+
 class VisualScriptNodeInstanceTypeCast : public VisualScriptNodeInstance {
 public:
 	VisualScriptInstance *instance;
@@ -1815,8 +1830,8 @@ void VisualScriptTypeCast::_bind_methods() {
 		script_ext_hint += "*." + E->get();
 	}
 
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "function/base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "property/base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
 }
 
 VisualScriptTypeCast::VisualScriptTypeCast() {

+ 3 - 0
modules/visual_script/visual_script_flow_control.h

@@ -261,6 +261,7 @@ public:
 	VisualScriptInputFilter();
 };
 #endif
+
 class VisualScriptTypeCast : public VisualScriptNode {
 
 	GDCLASS(VisualScriptTypeCast, VisualScriptNode)
@@ -293,6 +294,8 @@ public:
 	void set_base_script(const String &p_path);
 	String get_base_script() const;
 
+	virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const;
+
 	virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
 
 	VisualScriptTypeCast();

+ 275 - 54
modules/visual_script/visual_script_func_nodes.cpp

@@ -546,25 +546,25 @@ Dictionary VisualScriptFunctionCall::_get_argument_cache() const {
 
 void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const {
 
-	if (property.name == "function/base_type") {
+	if (property.name == "base_type") {
 		if (call_mode != CALL_MODE_INSTANCE) {
 			property.usage = PROPERTY_USAGE_NOEDITOR;
 		}
 	}
 
-	if (property.name == "function/base_script") {
+	if (property.name == "base_script") {
 		if (call_mode != CALL_MODE_INSTANCE) {
 			property.usage = 0;
 		}
 	}
 
-	if (property.name == "function/basic_type") {
+	if (property.name == "basic_type") {
 		if (call_mode != CALL_MODE_BASIC_TYPE) {
 			property.usage = 0;
 		}
 	}
 
-	if (property.name == "function/singleton") {
+	if (property.name == "singleton") {
 		if (call_mode != CALL_MODE_SINGLETON) {
 			property.usage = 0;
 		} else {
@@ -581,7 +581,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
 		}
 	}
 
-	if (property.name == "function/node_path") {
+	if (property.name == "node_path") {
 		if (call_mode != CALL_MODE_NODE_PATH) {
 			property.usage = 0;
 		} else {
@@ -594,7 +594,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
 		}
 	}
 
-	if (property.name == "function/function") {
+	if (property.name == "function") {
 
 		if (call_mode == CALL_MODE_BASIC_TYPE) {
 
@@ -648,7 +648,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
 		}
 	}
 
-	if (property.name == "function/use_default_args") {
+	if (property.name == "use_default_args") {
 
 		property.hint = PROPERTY_HINT_RANGE;
 
@@ -673,7 +673,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
 		}
 	}
 
-	if (property.name == "rpc/call_mode") {
+	if (property.name == "rpc_call_mode") {
 		if (call_mode == CALL_MODE_BASIC_TYPE) {
 			property.usage = 0;
 		}
@@ -735,17 +735,17 @@ void VisualScriptFunctionCall::_bind_methods() {
 		script_ext_hint += "*." + E->get();
 	}
 
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "function/call_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type,Singleton"), "set_call_mode", "get_call_mode");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "function/base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "function/base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "function/singleton"), "set_singleton", "get_singleton");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "function/basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
-	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "function/node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
-	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "function/argument_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_argument_cache", "_get_argument_cache");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "function/function"), "set_function", "get_function"); //when set, if loaded properly, will override argument count.
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "function/use_default_args"), "set_use_default_args", "get_use_default_args");
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "function/validate"), "set_validate", "get_validate");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "rpc/call_mode", PROPERTY_HINT_ENUM, "Disabled,Reliable,Unreliable,ReliableToID,UnreliableToID"), "set_rpc_call_mode", "get_rpc_call_mode"); //when set, if loaded properly, will override argument count.
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "call_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type,Singleton"), "set_call_mode", "get_call_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "singleton"), "set_singleton", "get_singleton");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
+	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
+	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "argument_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_argument_cache", "_get_argument_cache");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "function"), "set_function", "get_function"); //when set, if loaded properly, will override argument count.
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "use_default_args"), "set_use_default_args", "get_use_default_args");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "validate"), "set_validate", "get_validate");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "rpc_call_mode", PROPERTY_HINT_ENUM, "Disabled,Reliable,Unreliable,ReliableToID,UnreliableToID"), "set_rpc_call_mode", "get_rpc_call_mode"); //when set, if loaded properly, will override argument count.
 
 	BIND_CONSTANT(CALL_MODE_SELF);
 	BIND_CONSTANT(CALL_MODE_NODE_PATH);
@@ -1020,6 +1020,18 @@ String VisualScriptPropertySet::get_output_sequence_port_text(int p_port) const
 	return String();
 }
 
+void VisualScriptPropertySet::_adjust_input_index(PropertyInfo &pinfo) const {
+
+	if (index != StringName()) {
+
+		Variant v;
+		Variant::CallError ce;
+		v = Variant::construct(pinfo.type, NULL, 0, ce);
+		Variant i = v.get(index);
+		pinfo.type = i.get_type();
+	}
+}
+
 PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const {
 
 	if (call_mode == CALL_MODE_INSTANCE || call_mode == CALL_MODE_BASIC_TYPE) {
@@ -1027,6 +1039,7 @@ PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const
 			PropertyInfo pi;
 			pi.type = (call_mode == CALL_MODE_INSTANCE ? Variant::OBJECT : basic_type);
 			pi.name = (call_mode == CALL_MODE_INSTANCE ? String("instance") : Variant::get_type_name(basic_type).to_lower());
+			_adjust_input_index(pi);
 			return pi;
 		} else {
 			p_idx--;
@@ -1035,6 +1048,7 @@ PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const
 
 	PropertyInfo pinfo = type_cache;
 	pinfo.name = "value";
+	_adjust_input_index(pinfo);
 	return pinfo;
 }
 
@@ -1051,13 +1065,16 @@ PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) cons
 String VisualScriptPropertySet::get_caption() const {
 
 	static const char *cname[4] = {
-		"SelfSet",
-		"NodeSet",
-		"InstanceSet",
-		"BasicSet"
+		"Self",
+		"Node",
+		"Instance",
+		"Basic"
 	};
 
-	return cname[call_mode];
+	static const char *opname[ASSIGN_OP_MAX] = {
+		"Set", "Add", "Sub", "Mul", "Div", "Mod", "ShiftLeft", "ShiftRight", "BitAnd", "BitOr", "BitXor"
+	};
+	return String(cname[call_mode]) + opname[assign_op];
 }
 
 String VisualScriptPropertySet::get_text() const {
@@ -1073,6 +1090,9 @@ String VisualScriptPropertySet::get_text() const {
 	else if (call_mode == CALL_MODE_INSTANCE)
 		prop = String(base_type) + ":" + property;
 
+	if (index != StringName()) {
+		prop += "." + String(index);
+	}
 	return prop;
 }
 
@@ -1236,6 +1256,7 @@ void VisualScriptPropertySet::set_property(const StringName &p_type) {
 		return;
 
 	property = p_type;
+	index = StringName();
 	_update_cache();
 	_change_notify();
 	ports_changed_notify();
@@ -1285,27 +1306,58 @@ Dictionary VisualScriptPropertySet::_get_type_cache() const {
 	return type_cache;
 }
 
+void VisualScriptPropertySet::set_index(const StringName &p_type) {
+
+	if (index == p_type)
+		return;
+	index = p_type;
+	_update_cache();
+	_change_notify();
+	ports_changed_notify();
+}
+
+StringName VisualScriptPropertySet::get_index() const {
+
+	return index;
+}
+
+void VisualScriptPropertySet::set_assign_op(AssignOp p_op) {
+
+	ERR_FAIL_INDEX(p_op, ASSIGN_OP_MAX);
+	if (assign_op == p_op)
+		return;
+
+	assign_op = p_op;
+	_update_cache();
+	_change_notify();
+	ports_changed_notify();
+}
+
+VisualScriptPropertySet::AssignOp VisualScriptPropertySet::get_assign_op() const {
+	return assign_op;
+}
+
 void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const {
 
-	if (property.name == "property/base_type") {
+	if (property.name == "base_type") {
 		if (call_mode != CALL_MODE_INSTANCE) {
 			property.usage = PROPERTY_USAGE_NOEDITOR;
 		}
 	}
 
-	if (property.name == "property/base_script") {
+	if (property.name == "base_script") {
 		if (call_mode != CALL_MODE_INSTANCE) {
 			property.usage = 0;
 		}
 	}
 
-	if (property.name == "property/basic_type") {
+	if (property.name == "basic_type") {
 		if (call_mode != CALL_MODE_BASIC_TYPE) {
 			property.usage = 0;
 		}
 	}
 
-	if (property.name == "property/node_path") {
+	if (property.name == "node_path") {
 		if (call_mode != CALL_MODE_NODE_PATH) {
 			property.usage = 0;
 		} else {
@@ -1318,7 +1370,7 @@ void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const {
 		}
 	}
 
-	if (property.name == "property/property") {
+	if (property.name == "property") {
 
 		if (call_mode == CALL_MODE_BASIC_TYPE) {
 
@@ -1360,6 +1412,24 @@ void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const {
 			}
 		}
 	}
+
+	if (property.name == "index") {
+
+		Variant::CallError ce;
+		Variant v = Variant::construct(type_cache.type, NULL, 0, ce);
+		List<PropertyInfo> plist;
+		v.get_property_list(&plist);
+		String options = "";
+		for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+			options += "," + E->get().name;
+		}
+
+		property.hint = PROPERTY_HINT_ENUM;
+		property.hint_string = options;
+		property.type = Variant::STRING;
+		if (options == "")
+			property.usage = 0; //hide if type has no usable index
+	}
 }
 
 void VisualScriptPropertySet::_bind_methods() {
@@ -1385,6 +1455,12 @@ void VisualScriptPropertySet::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &VisualScriptPropertySet::set_base_path);
 	ClassDB::bind_method(D_METHOD("get_base_path"), &VisualScriptPropertySet::get_base_path);
 
+	ClassDB::bind_method(D_METHOD("set_index", "index"), &VisualScriptPropertySet::set_index);
+	ClassDB::bind_method(D_METHOD("get_index"), &VisualScriptPropertySet::get_index);
+
+	ClassDB::bind_method(D_METHOD("set_assign_op", "assign_op"), &VisualScriptPropertySet::set_assign_op);
+	ClassDB::bind_method(D_METHOD("get_assign_op"), &VisualScriptPropertySet::get_assign_op);
+
 	String bt;
 	for (int i = 0; i < Variant::VARIANT_MAX; i++) {
 		if (i > 0)
@@ -1405,14 +1481,15 @@ void VisualScriptPropertySet::_bind_methods() {
 		script_ext_hint += "*." + E->get();
 	}
 
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "property/set_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type"), "set_call_mode", "get_call_mode");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "property/base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "property/base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "property/type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_type_cache", "_get_type_cache");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "property/basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
-	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "property/node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "property/property"), "set_property", "get_property");
-
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "set_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type"), "set_call_mode", "get_call_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_type_cache", "_get_type_cache");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
+	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "property"), "set_property", "get_property");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "index"), "set_index", "get_index");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "assign_op", PROPERTY_HINT_ENUM, "Assign,Add,Sub,Mul,Div,Mod,ShiftLeft,ShiftRight,BitAnd,BitOr,Bitxor"), "set_assign_op", "get_assign_op");
 	BIND_CONSTANT(CALL_MODE_SELF);
 	BIND_CONSTANT(CALL_MODE_NODE_PATH);
 	BIND_CONSTANT(CALL_MODE_INSTANCE);
@@ -1426,11 +1503,72 @@ public:
 
 	VisualScriptPropertySet *node;
 	VisualScriptInstance *instance;
+	VisualScriptPropertySet::AssignOp assign_op;
+	StringName index;
+	bool needs_get;
 
 	//virtual int get_working_memory_size() const { return 0; }
 	//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
 	//virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
 
+	_FORCE_INLINE_ void _process_get(Variant &source, const Variant &p_argument, bool &valid) {
+
+		if (index != StringName() && assign_op == VisualScriptPropertySet::ASSIGN_OP_NONE) {
+			source.set_named(index, p_argument, &valid);
+		} else {
+
+			Variant value;
+			if (index != StringName()) {
+				value = source.get_named(index, &valid);
+			} else {
+				value = source;
+			}
+
+			switch (assign_op) {
+				case VisualScriptPropertySet::ASSIGN_OP_NONE: {
+					//should never get here
+				} break;
+				case VisualScriptPropertySet::ASSIGN_OP_ADD: {
+					value = Variant::evaluate(Variant::OP_ADD, value, p_argument);
+				} break;
+				case VisualScriptPropertySet::ASSIGN_OP_SUB: {
+					value = Variant::evaluate(Variant::OP_SUBSTRACT, value, p_argument);
+				} break;
+				case VisualScriptPropertySet::ASSIGN_OP_MUL: {
+					value = Variant::evaluate(Variant::OP_MULTIPLY, value, p_argument);
+				} break;
+				case VisualScriptPropertySet::ASSIGN_OP_DIV: {
+					value = Variant::evaluate(Variant::OP_DIVIDE, value, p_argument);
+				} break;
+				case VisualScriptPropertySet::ASSIGN_OP_MOD: {
+					value = Variant::evaluate(Variant::OP_MODULE, value, p_argument);
+				} break;
+				case VisualScriptPropertySet::ASSIGN_OP_SHIFT_LEFT: {
+					value = Variant::evaluate(Variant::OP_SHIFT_LEFT, value, p_argument);
+				} break;
+				case VisualScriptPropertySet::ASSIGN_OP_SHIFT_RIGHT: {
+					value = Variant::evaluate(Variant::OP_SHIFT_RIGHT, value, p_argument);
+				} break;
+				case VisualScriptPropertySet::ASSIGN_OP_BIT_AND: {
+					value = Variant::evaluate(Variant::OP_BIT_AND, value, p_argument);
+				} break;
+				case VisualScriptPropertySet::ASSIGN_OP_BIT_OR: {
+					value = Variant::evaluate(Variant::OP_BIT_OR, value, p_argument);
+				} break;
+				case VisualScriptPropertySet::ASSIGN_OP_BIT_XOR: {
+					value = Variant::evaluate(Variant::OP_BIT_XOR, value, p_argument);
+				} break;
+				default: {}
+			}
+
+			if (index != StringName()) {
+				source.set_named(index, value, &valid);
+			} else {
+				source = value;
+			}
+		}
+	}
+
 	virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Variant::CallError &r_error, String &r_error_str) {
 
 		switch (call_mode) {
@@ -1441,7 +1579,13 @@ public:
 
 				bool valid;
 
-				object->set(property, *p_inputs[0], &valid);
+				if (needs_get) {
+					Variant value = object->get(property, &valid);
+					_process_get(value, *p_inputs[0], valid);
+					object->set(property, value, &valid);
+				} else {
+					object->set(property, *p_inputs[0], &valid);
+				}
 
 				if (!valid) {
 					r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
@@ -1466,7 +1610,14 @@ public:
 
 				bool valid;
 
-				another->set(property, *p_inputs[0], &valid);
+				if (needs_get) {
+
+					Variant value = another->get(property, &valid);
+					_process_get(value, *p_inputs[0], valid);
+					another->set(property, value, &valid);
+				} else {
+					another->set(property, *p_inputs[0], &valid);
+				}
 
 				if (!valid) {
 					r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
@@ -1481,7 +1632,14 @@ public:
 
 				bool valid;
 
-				v.set(property, *p_inputs[1], &valid);
+				if (needs_get) {
+					Variant value = v.get_named(property, &valid);
+					_process_get(value, *p_inputs[1], valid);
+					v.set_named(property, value, &valid);
+
+				} else {
+					v.set_named(property, *p_inputs[1], &valid);
+				}
 
 				if (!valid) {
 					r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
@@ -1504,6 +1662,9 @@ VisualScriptNodeInstance *VisualScriptPropertySet::instance(VisualScriptInstance
 	instance->property = property;
 	instance->call_mode = call_mode;
 	instance->node_path = base_path;
+	instance->assign_op = assign_op;
+	instance->index = index;
+	instance->needs_get = index != StringName() || assign_op != ASSIGN_OP_NONE;
 	return instance;
 }
 
@@ -1517,6 +1678,7 @@ VisualScriptPropertySet::TypeGuess VisualScriptPropertySet::guess_output_type(Ty
 }
 VisualScriptPropertySet::VisualScriptPropertySet() {
 
+	assign_op = ASSIGN_OP_NONE;
 	call_mode = CALL_MODE_SELF;
 	base_type = "Object";
 	basic_type = Variant::NIL;
@@ -1641,6 +1803,15 @@ PropertyInfo VisualScriptPropertyGet::get_input_value_port_info(int p_idx) const
 
 PropertyInfo VisualScriptPropertyGet::get_output_value_port_info(int p_idx) const {
 
+	if (index != StringName()) {
+
+		Variant v;
+		Variant::CallError ce;
+		v = Variant::construct(type_cache, NULL, 0, ce);
+		Variant i = v.get(index);
+		return PropertyInfo(i.get_type(), "value." + String(index));
+	}
+
 	return PropertyInfo(type_cache, "value");
 }
 
@@ -1867,27 +2038,42 @@ Variant::Type VisualScriptPropertyGet::_get_type_cache() const {
 	return type_cache;
 }
 
+void VisualScriptPropertyGet::set_index(const StringName &p_type) {
+
+	if (index == p_type)
+		return;
+	index = p_type;
+	_update_cache();
+	_change_notify();
+	ports_changed_notify();
+}
+
+StringName VisualScriptPropertyGet::get_index() const {
+
+	return index;
+}
+
 void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const {
 
-	if (property.name == "property/base_type") {
+	if (property.name == "base_type") {
 		if (call_mode != CALL_MODE_INSTANCE) {
 			property.usage = PROPERTY_USAGE_NOEDITOR;
 		}
 	}
 
-	if (property.name == "property/base_script") {
+	if (property.name == "base_script") {
 		if (call_mode != CALL_MODE_INSTANCE) {
 			property.usage = 0;
 		}
 	}
 
-	if (property.name == "property/basic_type") {
+	if (property.name == "basic_type") {
 		if (call_mode != CALL_MODE_BASIC_TYPE) {
 			property.usage = 0;
 		}
 	}
 
-	if (property.name == "property/node_path") {
+	if (property.name == "node_path") {
 		if (call_mode != CALL_MODE_NODE_PATH) {
 			property.usage = 0;
 		} else {
@@ -1900,7 +2086,7 @@ void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const {
 		}
 	}
 
-	if (property.name == "property/property") {
+	if (property.name == "property") {
 
 		if (call_mode == CALL_MODE_BASIC_TYPE) {
 
@@ -1941,6 +2127,24 @@ void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const {
 			}
 		}
 	}
+
+	if (property.name == "index") {
+
+		Variant::CallError ce;
+		Variant v = Variant::construct(type_cache, NULL, 0, ce);
+		List<PropertyInfo> plist;
+		v.get_property_list(&plist);
+		String options = "";
+		for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+			options += "," + E->get().name;
+		}
+
+		property.hint = PROPERTY_HINT_ENUM;
+		property.hint_string = options;
+		property.type = Variant::STRING;
+		if (options == "")
+			property.usage = 0; //hide if type has no usable index
+	}
 }
 
 void VisualScriptPropertyGet::_bind_methods() {
@@ -1966,6 +2170,9 @@ void VisualScriptPropertyGet::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &VisualScriptPropertyGet::set_base_path);
 	ClassDB::bind_method(D_METHOD("get_base_path"), &VisualScriptPropertyGet::get_base_path);
 
+	ClassDB::bind_method(D_METHOD("set_index", "index"), &VisualScriptPropertyGet::set_index);
+	ClassDB::bind_method(D_METHOD("get_index"), &VisualScriptPropertyGet::get_index);
+
 	String bt;
 	for (int i = 0; i < Variant::VARIANT_MAX; i++) {
 		if (i > 0)
@@ -1986,13 +2193,14 @@ void VisualScriptPropertyGet::_bind_methods() {
 		script_ext_hint += "." + E->get();
 	}
 
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "property/set_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type"), "set_call_mode", "get_call_mode");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "property/base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "property/base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "property/type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_type_cache", "_get_type_cache");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "property/basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
-	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "property/node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "property/property"), "set_property", "get_property");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "set_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type"), "set_call_mode", "get_call_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_type_cache", "_get_type_cache");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
+	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "property"), "set_property", "get_property");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "index", PROPERTY_HINT_ENUM), "set_index", "get_index");
 
 	BIND_CONSTANT(CALL_MODE_SELF);
 	BIND_CONSTANT(CALL_MODE_NODE_PATH);
@@ -2004,6 +2212,7 @@ public:
 	VisualScriptPropertyGet::CallMode call_mode;
 	NodePath node_path;
 	StringName property;
+	StringName index;
 
 	VisualScriptPropertyGet *node;
 	VisualScriptInstance *instance;
@@ -2020,6 +2229,10 @@ public:
 
 				*p_outputs[0] = object->get(property, &valid);
 
+				if (index != StringName()) {
+					*p_outputs[0] = p_outputs[0]->get_named(index);
+				}
+
 				if (!valid) {
 					r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
 					r_error_str = RTR("Invalid index property name.");
@@ -2046,6 +2259,10 @@ public:
 
 				*p_outputs[0] = another->get(property, &valid);
 
+				if (index != StringName()) {
+					*p_outputs[0] = p_outputs[0]->get_named(index);
+				}
+
 				if (!valid) {
 					r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
 					r_error_str = vformat(RTR("Invalid index property name '%s' in node %s."), String(property), another->get_name());
@@ -2059,6 +2276,9 @@ public:
 				Variant v = *p_inputs[0];
 
 				*p_outputs[0] = v.get(property, &valid);
+				if (index != StringName()) {
+					*p_outputs[0] = p_outputs[0]->get_named(index);
+				}
 
 				if (!valid) {
 					r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
@@ -2079,6 +2299,7 @@ VisualScriptNodeInstance *VisualScriptPropertyGet::instance(VisualScriptInstance
 	instance->property = property;
 	instance->call_mode = call_mode;
 	instance->node_path = base_path;
+	instance->index = index;
 
 	return instance;
 }
@@ -2182,7 +2403,7 @@ StringName VisualScriptEmitSignal::get_signal() const {
 
 void VisualScriptEmitSignal::_validate_property(PropertyInfo &property) const {
 
-	if (property.name == "signal/signal") {
+	if (property.name == "signal") {
 		property.hint = PROPERTY_HINT_ENUM;
 
 		List<StringName> sigs;
@@ -2210,7 +2431,7 @@ void VisualScriptEmitSignal::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_signal", "name"), &VisualScriptEmitSignal::set_signal);
 	ClassDB::bind_method(D_METHOD("get_signal"), &VisualScriptEmitSignal::get_signal);
 
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "signal/signal"), "set_signal", "get_signal");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "signal"), "set_signal", "get_signal");
 }
 
 class VisualScriptNodeInstanceEmitSignal : public VisualScriptNodeInstance {

+ 30 - 0
modules/visual_script/visual_script_func_nodes.h

@@ -146,6 +146,21 @@ public:
 
 	};
 
+	enum AssignOp {
+		ASSIGN_OP_NONE,
+		ASSIGN_OP_ADD,
+		ASSIGN_OP_SUB,
+		ASSIGN_OP_MUL,
+		ASSIGN_OP_DIV,
+		ASSIGN_OP_MOD,
+		ASSIGN_OP_SHIFT_LEFT,
+		ASSIGN_OP_SHIFT_RIGHT,
+		ASSIGN_OP_BIT_AND,
+		ASSIGN_OP_BIT_OR,
+		ASSIGN_OP_BIT_XOR,
+		ASSIGN_OP_MAX
+	};
+
 private:
 	PropertyInfo type_cache;
 
@@ -155,6 +170,8 @@ private:
 	String base_script;
 	NodePath base_path;
 	StringName property;
+	StringName index;
+	AssignOp assign_op;
 
 	Node *_get_base_node() const;
 	StringName _get_base_type() const;
@@ -166,6 +183,8 @@ private:
 	void _set_type_cache(const Dictionary &p_type);
 	Dictionary _get_type_cache() const;
 
+	void _adjust_input_index(PropertyInfo &pinfo) const;
+
 protected:
 	virtual void _validate_property(PropertyInfo &property) const;
 
@@ -205,6 +224,12 @@ public:
 	void set_call_mode(CallMode p_mode);
 	CallMode get_call_mode() const;
 
+	void set_index(const StringName &p_type);
+	StringName get_index() const;
+
+	void set_assign_op(AssignOp p_op);
+	AssignOp get_assign_op() const;
+
 	virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
 	virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const;
 
@@ -212,6 +237,7 @@ public:
 };
 
 VARIANT_ENUM_CAST(VisualScriptPropertySet::CallMode);
+VARIANT_ENUM_CAST(VisualScriptPropertySet::AssignOp);
 
 class VisualScriptPropertyGet : public VisualScriptNode {
 
@@ -234,6 +260,7 @@ private:
 	String base_script;
 	NodePath base_path;
 	StringName property;
+	StringName index;
 
 	void _update_base_type();
 	Node *_get_base_node() const;
@@ -283,6 +310,9 @@ public:
 	void set_call_mode(CallMode p_mode);
 	CallMode get_call_mode() const;
 
+	void set_index(const StringName &p_type);
+	StringName get_index() const;
+
 	virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
 
 	VisualScriptPropertyGet();

+ 151 - 44
modules/visual_script/visual_script_nodes.cpp

@@ -59,10 +59,10 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value
 		_change_notify();
 		return true;
 	}
-	if (String(p_name).begins_with("argument/")) {
-		int idx = String(p_name).get_slice("/", 1).to_int() - 1;
+	if (String(p_name).begins_with("argument_")) {
+		int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1;
 		ERR_FAIL_INDEX_V(idx, arguments.size(), false);
-		String what = String(p_name).get_slice("/", 2);
+		String what = String(p_name).get_slice("/", 1);
 		if (what == "type") {
 
 			Variant::Type new_type = Variant::Type(int(p_value));
@@ -104,10 +104,10 @@ bool VisualScriptFunction::_get(const StringName &p_name, Variant &r_ret) const
 		r_ret = arguments.size();
 		return true;
 	}
-	if (String(p_name).begins_with("argument/")) {
-		int idx = String(p_name).get_slice("/", 1).to_int() - 1;
+	if (String(p_name).begins_with("argument_")) {
+		int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1;
 		ERR_FAIL_INDEX_V(idx, arguments.size(), false);
-		String what = String(p_name).get_slice("/", 2);
+		String what = String(p_name).get_slice("/", 1);
 		if (what == "type") {
 			r_ret = arguments[idx].type;
 			return true;
@@ -144,8 +144,8 @@ void VisualScriptFunction::_get_property_list(List<PropertyInfo> *p_list) const
 	}
 
 	for (int i = 0; i < arguments.size(); i++) {
-		p_list->push_back(PropertyInfo(Variant::INT, "argument/" + itos(i + 1) + "/type", PROPERTY_HINT_ENUM, argt));
-		p_list->push_back(PropertyInfo(Variant::STRING, "argument/" + itos(i + 1) + "/name"));
+		p_list->push_back(PropertyInfo(Variant::INT, "argument_" + itos(i + 1) + "/type", PROPERTY_HINT_ENUM, argt));
+		p_list->push_back(PropertyInfo(Variant::STRING, "argument_" + itos(i + 1) + "/name"));
 	}
 	if (!stack_less) {
 		p_list->push_back(PropertyInfo(Variant::INT, "stack/size", PROPERTY_HINT_RANGE, "1,100000"));
@@ -559,8 +559,8 @@ void VisualScriptOperator::_bind_methods() {
 		argt += "," + Variant::get_type_name(Variant::Type(i));
 	}
 
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "operator_value/type", PROPERTY_HINT_ENUM, types), "set_operator", "get_operator");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "typed_value/typed", PROPERTY_HINT_ENUM, argt), "set_typed", "get_typed");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, types), "set_operator", "get_operator");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_typed", "get_typed");
 }
 
 class VisualScriptNodeInstanceOperator : public VisualScriptNodeInstance {
@@ -620,6 +620,113 @@ static Ref<VisualScriptNode> create_op_node(const String &p_name) {
 	return node;
 }
 
+//////////////////////////////////////////
+////////////////OPERATOR//////////////////
+//////////////////////////////////////////
+
+int VisualScriptSelect::get_output_sequence_port_count() const {
+
+	return 0;
+}
+
+bool VisualScriptSelect::has_input_sequence_port() const {
+
+	return false;
+}
+
+int VisualScriptSelect::get_input_value_port_count() const {
+
+	return 3;
+}
+int VisualScriptSelect::get_output_value_port_count() const {
+
+	return 1;
+}
+
+String VisualScriptSelect::get_output_sequence_port_text(int p_port) const {
+
+	return String();
+}
+
+PropertyInfo VisualScriptSelect::get_input_value_port_info(int p_idx) const {
+
+	if (p_idx == 0) {
+		return PropertyInfo(Variant::BOOL, "cond");
+	} else if (p_idx == 1) {
+		return PropertyInfo(typed, "a");
+	} else {
+		return PropertyInfo(typed, "b");
+	}
+}
+PropertyInfo VisualScriptSelect::get_output_value_port_info(int p_idx) const {
+
+	return PropertyInfo(typed, "out");
+}
+
+String VisualScriptSelect::get_caption() const {
+
+	return "Select";
+}
+
+String VisualScriptSelect::get_text() const {
+
+	return "a if cond, else b";
+}
+
+void VisualScriptSelect::set_typed(Variant::Type p_op) {
+
+	if (typed == p_op)
+		return;
+
+	typed = p_op;
+	ports_changed_notify();
+}
+
+Variant::Type VisualScriptSelect::get_typed() const {
+
+	return typed;
+}
+
+void VisualScriptSelect::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("set_typed", "type"), &VisualScriptSelect::set_typed);
+	ClassDB::bind_method(D_METHOD("get_typed"), &VisualScriptSelect::get_typed);
+
+	String argt = "Any";
+	for (int i = 1; i < Variant::VARIANT_MAX; i++) {
+		argt += "," + Variant::get_type_name(Variant::Type(i));
+	}
+
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_typed", "get_typed");
+}
+
+class VisualScriptNodeInstanceSelect : public VisualScriptNodeInstance {
+public:
+	//virtual int get_working_memory_size() const { return 0; }
+
+	virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Variant::CallError &r_error, String &r_error_str) {
+
+		bool cond = *p_inputs[0];
+		if (cond)
+			*p_outputs[0] = *p_inputs[1];
+		else
+			*p_outputs[0] = *p_inputs[2];
+
+		return 0;
+	}
+};
+
+VisualScriptNodeInstance *VisualScriptSelect::instance(VisualScriptInstance *p_instance) {
+
+	VisualScriptNodeInstanceSelect *instance = memnew(VisualScriptNodeInstanceSelect);
+	return instance;
+}
+
+VisualScriptSelect::VisualScriptSelect() {
+
+	typed = Variant::NIL;
+}
+
 //////////////////////////////////////////
 ////////////////VARIABLE GET//////////////////
 //////////////////////////////////////////
@@ -691,7 +798,7 @@ StringName VisualScriptVariableGet::get_variable() const {
 
 void VisualScriptVariableGet::_validate_property(PropertyInfo &property) const {
 
-	if (property.name == "variable/name" && get_visual_script().is_valid()) {
+	if (property.name == "var_name" && get_visual_script().is_valid()) {
 		Ref<VisualScript> vs = get_visual_script();
 		List<StringName> vars;
 		vs->get_variable_list(&vars);
@@ -714,7 +821,7 @@ void VisualScriptVariableGet::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_variable", "name"), &VisualScriptVariableGet::set_variable);
 	ClassDB::bind_method(D_METHOD("get_variable"), &VisualScriptVariableGet::get_variable);
 
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "variable/name"), "set_variable", "get_variable");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "var_name"), "set_variable", "get_variable");
 }
 
 class VisualScriptNodeInstanceVariableGet : public VisualScriptNodeInstance {
@@ -816,7 +923,7 @@ StringName VisualScriptVariableSet::get_variable() const {
 
 void VisualScriptVariableSet::_validate_property(PropertyInfo &property) const {
 
-	if (property.name == "variable/name" && get_visual_script().is_valid()) {
+	if (property.name == "var_name" && get_visual_script().is_valid()) {
 		Ref<VisualScript> vs = get_visual_script();
 		List<StringName> vars;
 		vs->get_variable_list(&vars);
@@ -839,7 +946,7 @@ void VisualScriptVariableSet::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_variable", "name"), &VisualScriptVariableSet::set_variable);
 	ClassDB::bind_method(D_METHOD("get_variable"), &VisualScriptVariableSet::get_variable);
 
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "variable/name"), "set_variable", "get_variable");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "var_name"), "set_variable", "get_variable");
 }
 
 class VisualScriptNodeInstanceVariableSet : public VisualScriptNodeInstance {
@@ -956,7 +1063,7 @@ Variant VisualScriptConstant::get_constant_value() const {
 
 void VisualScriptConstant::_validate_property(PropertyInfo &property) const {
 
-	if (property.name == "constant/value") {
+	if (property.name == "value") {
 		property.type = type;
 		if (type == Variant::NIL)
 			property.usage = 0; //do not save if nil
@@ -976,8 +1083,8 @@ void VisualScriptConstant::_bind_methods() {
 		argt += "," + Variant::get_type_name(Variant::Type(i));
 	}
 
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "constant/type", PROPERTY_HINT_ENUM, argt), "set_constant_type", "get_constant_type");
-	ADD_PROPERTY(PropertyInfo(Variant::NIL, "constant/value"), "set_constant_value", "get_constant_value");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_constant_type", "get_constant_type");
+	ADD_PROPERTY(PropertyInfo(Variant::NIL, "value"), "set_constant_value", "get_constant_value");
 }
 
 class VisualScriptNodeInstanceConstant : public VisualScriptNodeInstance {
@@ -1842,7 +1949,7 @@ VisualScriptEngineSingleton::TypeGuess VisualScriptEngineSingleton::guess_output
 	TypeGuess tg;
 	tg.type = Variant::OBJECT;
 	if (obj) {
-		tg.GDCLASS = obj->get_class();
+		tg.gdclass = obj->get_class();
 		tg.script = obj->get_script();
 	}
 
@@ -2002,7 +2109,7 @@ VisualScriptSceneNode::TypeGuess VisualScriptSceneNode::guess_output_type(TypeGu
 
 	VisualScriptSceneNode::TypeGuess tg;
 	tg.type = Variant::OBJECT;
-	tg.GDCLASS = "Node";
+	tg.gdclass = "Node";
 
 #ifdef TOOLS_ENABLED
 	Ref<Script> script = get_visual_script();
@@ -2031,7 +2138,7 @@ VisualScriptSceneNode::TypeGuess VisualScriptSceneNode::guess_output_type(TypeGu
 	Node *another = script_node->get_node(path);
 
 	if (another) {
-		tg.GDCLASS = another->get_class();
+		tg.gdclass = another->get_class();
 		tg.script = another->get_script();
 	}
 #endif
@@ -2173,7 +2280,7 @@ VisualScriptSceneTree::TypeGuess VisualScriptSceneTree::guess_output_type(TypeGu
 
 	TypeGuess tg;
 	tg.type = Variant::OBJECT;
-	tg.GDCLASS = "SceneTree";
+	tg.gdclass = "SceneTree";
 	return tg;
 }
 
@@ -2353,13 +2460,13 @@ VisualScriptSelf::TypeGuess VisualScriptSelf::guess_output_type(TypeGuess *p_inp
 
 	VisualScriptSceneNode::TypeGuess tg;
 	tg.type = Variant::OBJECT;
-	tg.GDCLASS = "Object";
+	tg.gdclass = "Object";
 
 	Ref<Script> script = get_visual_script();
 	if (!script.is_valid())
 		return tg;
 
-	tg.GDCLASS = script->get_instance_base_type();
+	tg.gdclass = script->get_instance_base_type();
 	tg.script = script;
 
 	return tg;
@@ -3088,8 +3195,8 @@ void VisualScriptLocalVar::_bind_methods() {
 		argt += "," + Variant::get_type_name(Variant::Type(i));
 	}
 
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "variable/name"), "set_var_name", "get_var_name");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "variable/type", PROPERTY_HINT_ENUM, argt), "set_var_type", "get_var_type");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "var_name"), "set_var_name", "get_var_name");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_var_type", "get_var_type");
 }
 
 VisualScriptLocalVar::VisualScriptLocalVar() {
@@ -3210,8 +3317,8 @@ void VisualScriptLocalVarSet::_bind_methods() {
 		argt += "," + Variant::get_type_name(Variant::Type(i));
 	}
 
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "variable/name"), "set_var_name", "get_var_name");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "variable/type", PROPERTY_HINT_ENUM, argt), "set_var_type", "get_var_type");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "var_name"), "set_var_name", "get_var_name");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_var_type", "get_var_type");
 }
 
 VisualScriptLocalVarSet::VisualScriptLocalVarSet() {
@@ -3253,32 +3360,33 @@ PropertyInfo VisualScriptInputAction::get_input_value_port_info(int p_idx) const
 }
 PropertyInfo VisualScriptInputAction::get_output_value_port_info(int p_idx) const {
 
-	return PropertyInfo(Variant::BOOL, "pressed");
-}
-
-String VisualScriptInputAction::get_caption() const {
-
-	return "Action";
-}
-
-String VisualScriptInputAction::get_text() const {
-
+	String mstr;
 	switch (mode) {
 		case MODE_PRESSED: {
-			return name;
+			mstr = "pressed";
 		} break;
 		case MODE_RELEASED: {
-			return "not " + name;
+			mstr = "not pressed";
 		} break;
 		case MODE_JUST_PRESSED: {
-			return String(name) + " " + TTR("just pressed");
+			mstr = "just pressed";
 		} break;
 		case MODE_JUST_RELEASED: {
-			return String(name) + " " + TTR("just released");
+			mstr = "just released";
 		} break;
 	}
 
-	return String();
+	return PropertyInfo(Variant::BOOL, mstr);
+}
+
+String VisualScriptInputAction::get_caption() const {
+
+	return "Action";
+}
+
+String VisualScriptInputAction::get_text() const {
+
+	return name;
 }
 
 String VisualScriptInputAction::get_category() const {
@@ -3319,8 +3427,6 @@ public:
 	StringName action;
 	VisualScriptInputAction::Mode mode;
 
-	virtual int get_working_memory_size() const { return 1; }
-
 	virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Variant::CallError &r_error, String &r_error_str) {
 
 		switch (mode) {
@@ -3628,6 +3734,7 @@ void register_visual_script_nodes() {
 	VisualScriptLanguage::singleton->add_register_func("operators/logic/xor", create_op_node<Variant::OP_XOR>);
 	VisualScriptLanguage::singleton->add_register_func("operators/logic/not", create_op_node<Variant::OP_NOT>);
 	VisualScriptLanguage::singleton->add_register_func("operators/logic/in", create_op_node<Variant::OP_IN>);
+	VisualScriptLanguage::singleton->add_register_func("operators/logic/select", create_node_generic<VisualScriptSelect>);
 
 	VisualScriptLanguage::singleton->add_register_func("functions/deconstruct", create_node_generic<VisualScriptDeconstruct>);
 

+ 33 - 0
modules/visual_script/visual_script_nodes.h

@@ -127,6 +127,39 @@ public:
 	VisualScriptOperator();
 };
 
+class VisualScriptSelect : public VisualScriptNode {
+
+	GDCLASS(VisualScriptSelect, VisualScriptNode)
+
+	Variant::Type typed;
+
+protected:
+	static void _bind_methods();
+
+public:
+	virtual int get_output_sequence_port_count() const;
+	virtual bool has_input_sequence_port() const;
+
+	virtual String get_output_sequence_port_text(int p_port) const;
+
+	virtual int get_input_value_port_count() const;
+	virtual int get_output_value_port_count() const;
+
+	virtual PropertyInfo get_input_value_port_info(int p_idx) const;
+	virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+
+	virtual String get_caption() const;
+	virtual String get_text() const;
+	virtual String get_category() const { return "operators"; }
+
+	void set_typed(Variant::Type p_op);
+	Variant::Type get_typed() const;
+
+	virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+
+	VisualScriptSelect();
+};
+
 class VisualScriptVariableGet : public VisualScriptNode {
 
 	GDCLASS(VisualScriptVariableGet, VisualScriptNode)

+ 7 - 7
modules/visual_script/visual_script_yield_nodes.cpp

@@ -419,13 +419,13 @@ VisualScriptYieldSignal::CallMode VisualScriptYieldSignal::get_call_mode() const
 
 void VisualScriptYieldSignal::_validate_property(PropertyInfo &property) const {
 
-	if (property.name == "signal/base_type") {
+	if (property.name == "base_type") {
 		if (call_mode != CALL_MODE_INSTANCE) {
 			property.usage = PROPERTY_USAGE_NOEDITOR;
 		}
 	}
 
-	if (property.name == "signal/node_path") {
+	if (property.name == "node_path") {
 		if (call_mode != CALL_MODE_NODE_PATH) {
 			property.usage = 0;
 		} else {
@@ -438,7 +438,7 @@ void VisualScriptYieldSignal::_validate_property(PropertyInfo &property) const {
 		}
 	}
 
-	if (property.name == "signal/signal") {
+	if (property.name == "signal") {
 		property.hint = PROPERTY_HINT_ENUM;
 
 		List<MethodInfo> methods;
@@ -488,10 +488,10 @@ void VisualScriptYieldSignal::_bind_methods() {
 		bt += Variant::get_type_name(Variant::Type(i));
 	}
 
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "signal/call_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance"), "set_call_mode", "get_call_mode");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "signal/base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
-	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "signal/node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
-	ADD_PROPERTY(PropertyInfo(Variant::STRING, "signal/signal"), "set_signal", "get_signal");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "call_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance"), "set_call_mode", "get_call_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
+	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
+	ADD_PROPERTY(PropertyInfo(Variant::STRING, "signal"), "set_signal", "get_signal");
 
 	BIND_CONSTANT(CALL_MODE_SELF);
 	BIND_CONSTANT(CALL_MODE_NODE_PATH);

+ 23 - 0
scene/2d/canvas_item.cpp

@@ -609,6 +609,27 @@ void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color
 	VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width, p_antialiased);
 }
 
+void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width, bool p_antialiased) {
+
+	if (!drawing) {
+		ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+		ERR_FAIL();
+	}
+
+	Vector<Color> colors;
+	colors.push_back(p_color);
+	VisualServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, colors, p_width, p_antialiased);
+}
+
+void CanvasItem::draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width, bool p_antialiased) {
+
+	if (!drawing) {
+		ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+		ERR_FAIL();
+	}
+
+	VisualServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width, p_antialiased);
+}
 void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled) {
 
 	if (!drawing) {
@@ -955,6 +976,8 @@ void CanvasItem::_bind_methods() {
 	//ClassDB::bind_method(D_METHOD("get_transform"),&CanvasItem::get_transform);
 
 	ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(1.0), DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0), DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled"), &CanvasItem::draw_rect, DEFVAL(true));
 	ClassDB::bind_method(D_METHOD("draw_circle", "pos", "radius", "color"), &CanvasItem::draw_circle);
 	ClassDB::bind_method(D_METHOD("draw_texture", "texture:Texture", "pos", "modulate", "normal_map:Texture"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Variant()));

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

@@ -242,6 +242,8 @@ public:
 	/* DRAWING API */
 
 	void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
+	void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
+	void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false);
 	void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true);
 	void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color);
 	void draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1), const Ref<Texture> &p_normal_map = Ref<Texture>());

+ 3 - 0
scene/3d/mesh_instance.cpp

@@ -281,6 +281,9 @@ void MeshInstance::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_skeleton_path", "skeleton_path:NodePath"), &MeshInstance::set_skeleton_path);
 	ClassDB::bind_method(D_METHOD("get_skeleton_path:NodePath"), &MeshInstance::get_skeleton_path);
 
+	ClassDB::bind_method(D_METHOD("set_surface_material", "surface", "material:Material"), &MeshInstance::set_surface_material);
+	ClassDB::bind_method(D_METHOD("get_surface_material:Material", "surface"), &MeshInstance::get_surface_material);
+
 	ClassDB::bind_method(D_METHOD("create_trimesh_collision"), &MeshInstance::create_trimesh_collision);
 	ClassDB::set_method_flags("MeshInstance", "create_trimesh_collision", METHOD_FLAGS_DEFAULT);
 	ClassDB::bind_method(D_METHOD("create_convex_collision"), &MeshInstance::create_convex_collision);

+ 15 - 5
scene/gui/graph_edit.cpp

@@ -568,7 +568,7 @@ static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, Vector2 start, Vector2 co
 	return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
 }
 
-void GraphEdit::_bake_segment2d(CanvasItem *p_where, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_min_depth, int p_max_depth, float p_tol, const Color &p_color, const Color &p_to_color, int &lines) const {
+void GraphEdit::_bake_segment2d(Vector<Vector2> &points, Vector<Color> &colors, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_min_depth, int p_max_depth, float p_tol, const Color &p_color, const Color &p_to_color, int &lines) const {
 
 	float mp = p_begin + (p_end - p_begin) * 0.5;
 	Vector2 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b);
@@ -581,11 +581,12 @@ void GraphEdit::_bake_segment2d(CanvasItem *p_where, float p_begin, float p_end,
 
 	if (p_depth >= p_min_depth && (dp < p_tol || p_depth >= p_max_depth)) {
 
-		p_where->draw_line(beg, end, p_color.linear_interpolate(p_to_color, mp), 2, true);
+		points.push_back((beg + end) * 0.5);
+		colors.push_back(p_color.linear_interpolate(p_to_color, mp));
 		lines++;
 	} else {
-		_bake_segment2d(p_where, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_min_depth, p_max_depth, p_tol, p_color, p_to_color, lines);
-		_bake_segment2d(p_where, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_min_depth, p_max_depth, p_tol, p_color, p_to_color, lines);
+		_bake_segment2d(points, colors, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_min_depth, p_max_depth, p_tol, p_color, p_to_color, lines);
+		_bake_segment2d(points, colors, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_min_depth, p_max_depth, p_tol, p_color, p_to_color, lines);
 	}
 }
 
@@ -609,7 +610,16 @@ void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const
 	Vector2 c2 = Vector2(-cp_offset * zoom, 0);
 
 	int lines = 0;
-	_bake_segment2d(p_where, 0, 1, p_from, c1, p_to, c2, 0, 3, 9, 8, p_color, p_to_color, lines);
+
+	Vector<Point2> points;
+	Vector<Color> colors;
+	points.push_back(p_from);
+	colors.push_back(p_color);
+	_bake_segment2d(points, colors, 0, 1, p_from, c1, p_to, c2, 0, 3, 9, 8, p_color, p_to_color, lines);
+	points.push_back(p_to);
+	colors.push_back(p_to_color);
+
+	p_where->draw_polyline_colors(points, colors, 2, true);
 
 #else
 

+ 1 - 1
scene/gui/graph_edit.h

@@ -110,7 +110,7 @@ private:
 	bool awaiting_scroll_offset_update;
 	List<Connection> connections;
 
-	void _bake_segment2d(CanvasItem *p_where, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_min_depth, int p_max_depth, float p_tol, const Color &p_color, const Color &p_to_color, int &lines) const;
+	void _bake_segment2d(Vector<Vector2> &points, Vector<Color> &colors, float p_begin, float p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_min_depth, int p_max_depth, float p_tol, const Color &p_color, const Color &p_to_color, int &lines) const;
 
 	void _draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color);
 

+ 16 - 6
scene/gui/split_container.cpp

@@ -131,7 +131,12 @@ void SplitContainer::_resort() {
 
 		if (ratiomode) {
 
-			middle_sep = ms_first[axis] + available / 2;
+			int first_ratio = first->get_stretch_ratio();
+			int second_ratio = second->get_stretch_ratio();
+
+			float ratio = float(first_ratio) / (first_ratio + second_ratio);
+
+			middle_sep = ms_first[axis] + available * ratio;
 
 		} else if (expand_first_mode) {
 
@@ -144,12 +149,17 @@ void SplitContainer::_resort() {
 
 	} else if (ratiomode) {
 
-		if (expand_ofs < -(available / 2))
-			expand_ofs = -(available / 2);
-		else if (expand_ofs > (available / 2))
-			expand_ofs = (available / 2);
+		int first_ratio = first->get_stretch_ratio();
+		int second_ratio = second->get_stretch_ratio();
+
+		float ratio = float(first_ratio) / (first_ratio + second_ratio);
+
+		if (expand_ofs < -(available * ratio))
+			expand_ofs = -(available * ratio);
+		else if (expand_ofs > (available * (1.0 - ratio)))
+			expand_ofs = (available * (1.0 - ratio));
 
-		middle_sep = ms_first[axis] + available / 2 + expand_ofs;
+		middle_sep = ms_first[axis] + available * ratio + expand_ofs;
 
 	} else if (expand_first_mode) {
 

+ 17 - 7
scene/resources/primitive_meshes.cpp

@@ -53,7 +53,15 @@ void PrimitiveMesh::_update() {
 	emit_changed();
 }
 
-void PrimitiveMesh::_queue_update() {
+void PrimitiveMesh::_queue_update(bool p_first_mesh) {
+
+	if (first_mesh && p_first_mesh) {
+		first_mesh = false;
+		cache_is_dirty = true;
+		_update();
+		return;
+	}
+
 	if (!cache_is_dirty) {
 		cache_is_dirty = true;
 		call_deferred("_update");
@@ -145,6 +153,7 @@ PrimitiveMesh::PrimitiveMesh() {
 
 	// make sure we do an update after we've finished constructing our object
 	cache_is_dirty = false;
+	first_mesh = true;
 	_queue_update();
 }
 
@@ -350,7 +359,7 @@ int CapsuleMesh::get_radial_segments() const {
 
 void CapsuleMesh::set_rings(const int p_rings) {
 	rings = p_rings > 1 ? p_rings : 1;
-	_queue_update();
+	_queue_update(true); //last property set, force update mesh
 }
 
 int CapsuleMesh::get_rings() const {
@@ -608,7 +617,7 @@ int CubeMesh::get_subdivide_height() const {
 
 void CubeMesh::set_subdivide_depth(const int p_subdivide) {
 	subdivide_d = p_subdivide > 0 ? p_subdivide : 0;
-	_queue_update();
+	_queue_update(true); //last property set, force update mesh
 }
 
 int CubeMesh::get_subdivide_depth() const {
@@ -825,7 +834,7 @@ int CylinderMesh::get_radial_segments() const {
 
 void CylinderMesh::set_rings(const int p_rings) {
 	rings = p_rings > 0 ? p_rings : 0;
-	_queue_update();
+	_queue_update(true); //last property set, force update mesh
 }
 
 int CylinderMesh::get_rings() const {
@@ -942,7 +951,7 @@ int PlaneMesh::get_subdivide_width() const {
 
 void PlaneMesh::set_subdivide_depth(const int p_subdivide) {
 	subdivide_d = p_subdivide > 0 ? p_subdivide : 0;
-	_queue_update();
+	_queue_update(true); //last property set, force update mesh
 }
 
 int PlaneMesh::get_subdivide_depth() const {
@@ -1232,7 +1241,7 @@ int PrismMesh::get_subdivide_height() const {
 
 void PrismMesh::set_subdivide_depth(const int p_divisions) {
 	subdivide_d = p_divisions > 0 ? p_divisions : 0;
-	_queue_update();
+	_queue_update(true); //last property set, force update mesh
 }
 
 int PrismMesh::get_subdivide_depth() const {
@@ -1301,6 +1310,7 @@ void QuadMesh::_bind_methods() {
 
 QuadMesh::QuadMesh() {
 	primitive_type = PRIMITIVE_TRIANGLE_FAN;
+	_queue_update(true);
 }
 
 /**
@@ -1437,7 +1447,7 @@ int SphereMesh::get_rings() const {
 
 void SphereMesh::set_is_hemisphere(const bool p_is_hemisphere) {
 	is_hemisphere = p_is_hemisphere;
-	_queue_update();
+	_queue_update(true); //last property set, force update mesh
 }
 
 bool SphereMesh::get_is_hemisphere() const {

+ 2 - 1
scene/resources/primitive_meshes.h

@@ -51,6 +51,7 @@ private:
 
 	Ref<Material> material;
 
+	bool first_mesh;
 	bool cache_is_dirty;
 	void _update();
 
@@ -60,7 +61,7 @@ protected:
 	static void _bind_methods();
 
 	virtual void _create_mesh_array(Array &p_arr) = 0;
-	void _queue_update();
+	void _queue_update(bool p_first_mesh = false); //pretty bad hack to have the mesh built firt time parameters are set without delay
 
 	void set_aabb(Rect3 p_aabb);
 

+ 38 - 0
servers/visual/rasterizer.h

@@ -612,6 +612,7 @@ public:
 			enum Type {
 
 				TYPE_LINE,
+				TYPE_POLYLINE,
 				TYPE_RECT,
 				TYPE_NINEPATCH,
 				TYPE_PRIMITIVE,
@@ -636,6 +637,18 @@ public:
 			bool antialiased;
 			CommandLine() { type = TYPE_LINE; }
 		};
+		struct CommandPolyLine : public Command {
+
+			bool antialiased;
+			Vector<Point2> triangles;
+			Vector<Color> triangle_colors;
+			Vector<Point2> lines;
+			Vector<Color> line_colors;
+			CommandPolyLine() {
+				type = TYPE_POLYLINE;
+				antialiased = false;
+			}
+		};
 
 		struct CommandRect : public Command {
 
@@ -815,6 +828,31 @@ public:
 						r.position = line->from;
 						r.expand_to(line->to);
 					} break;
+					case Item::Command::TYPE_POLYLINE: {
+
+						const Item::CommandPolyLine *pline = static_cast<const Item::CommandPolyLine *>(c);
+						if (pline->triangles.size()) {
+							for (int j = 0; j < pline->triangles.size(); j++) {
+
+								if (j == 0) {
+									r.position = pline->triangles[j];
+								} else {
+									r.expand_to(pline->triangles[j]);
+								}
+							}
+						} else {
+
+							for (int j = 0; j < pline->lines.size(); j++) {
+
+								if (j == 0) {
+									r.position = pline->lines[j];
+								} else {
+									r.expand_to(pline->lines[j]);
+								}
+							}
+						}
+
+					} break;
 					case Item::Command::TYPE_RECT: {
 
 						const Item::CommandRect *crect = static_cast<const Item::CommandRect *>(c);

+ 82 - 2
servers/visual/visual_server_canvas.cpp

@@ -392,14 +392,14 @@ void VisualServerCanvas::canvas_item_set_draw_behind_parent(RID p_item, bool p_e
 	canvas_item->behind = p_enable;
 }
 
-void VisualServerCanvas::canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width, bool p_antialiased) {
+void VisualServerCanvas::canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_colors, float p_width, bool p_antialiased) {
 
 	Item *canvas_item = canvas_item_owner.getornull(p_item);
 	ERR_FAIL_COND(!canvas_item);
 
 	Item::CommandLine *line = memnew(Item::CommandLine);
 	ERR_FAIL_COND(!line);
-	line->color = p_color;
+	line->color = p_colors;
 	line->from = p_from;
 	line->to = p_to;
 	line->width = p_width;
@@ -409,6 +409,86 @@ void VisualServerCanvas::canvas_item_add_line(RID p_item, const Point2 &p_from,
 	canvas_item->commands.push_back(line);
 }
 
+void VisualServerCanvas::canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width, bool p_antialiased) {
+
+	ERR_FAIL_COND(p_points.size() < 2);
+	Item *canvas_item = canvas_item_owner.getornull(p_item);
+	ERR_FAIL_COND(!canvas_item);
+
+	Item::CommandPolyLine *pline = memnew(Item::CommandPolyLine);
+	ERR_FAIL_COND(!pline);
+
+	pline->antialiased = p_antialiased;
+
+	if (p_width <= 1) {
+		pline->lines = p_points;
+		pline->line_colors = p_colors;
+		if (pline->line_colors.size() == 0) {
+			pline->line_colors.push_back(Color(1, 1, 1, 1));
+		} else if (pline->line_colors.size() > 1 && pline->line_colors.size() != pline->lines.size()) {
+			pline->line_colors.resize(1);
+		}
+	} else {
+		//make a trianglestrip for drawing the line...
+		Vector2 prev_t;
+		pline->triangles.resize(p_points.size() * 2);
+		if (p_antialiased) {
+			pline->lines.resize(p_points.size() * 2);
+		}
+
+		if (p_colors.size() == 0) {
+			pline->triangle_colors.push_back(Color(1, 1, 1, 1));
+			if (p_antialiased) {
+				pline->line_colors.push_back(Color(1, 1, 1, 1));
+			}
+		}
+		if (p_colors.size() == 1) {
+			pline->triangle_colors = p_colors;
+			pline->line_colors = p_colors;
+		} else {
+			pline->triangle_colors.resize(pline->triangles.size());
+			pline->line_colors.resize(pline->lines.size());
+		}
+
+		for (int i = 0; i < p_points.size(); i++) {
+
+			Vector2 t;
+			if (i == p_points.size() - 1) {
+				t = prev_t;
+			} else {
+				t = (p_points[i + 1] - p_points[i]).normalized().tangent();
+				if (i == 0) {
+					prev_t = t;
+				}
+			}
+
+			Vector2 tangent = ((t + prev_t).normalized()) * p_width * 0.5;
+
+			if (p_antialiased) {
+				pline->lines[i] = p_points[i] + tangent;
+				pline->lines[p_points.size() * 2 - i - 1] = p_points[i] - tangent;
+				if (pline->line_colors.size() > 1) {
+					pline->line_colors[i] = p_colors[i];
+					pline->line_colors[p_points.size() * 2 - i - 1] = p_colors[i];
+				}
+			}
+
+			pline->triangles[i * 2 + 0] = p_points[i] + tangent;
+			pline->triangles[i * 2 + 1] = p_points[i] - tangent;
+
+			if (pline->triangle_colors.size() > 1) {
+
+				pline->triangle_colors[i * 2 + 0] = p_colors[i];
+				pline->triangle_colors[i * 2 + 1] = p_colors[i];
+			}
+
+			prev_t = t;
+		}
+	}
+	canvas_item->rect_dirty = true;
+	canvas_item->commands.push_back(pline);
+}
+
 void VisualServerCanvas::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color) {
 
 	Item *canvas_item = canvas_item_owner.getornull(p_item);

+ 1 - 0
servers/visual/visual_server_canvas.h

@@ -171,6 +171,7 @@ public:
 	void canvas_item_set_draw_behind_parent(RID p_item, bool p_enable);
 
 	void canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
+	void canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_line, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false);
 	void canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color);
 	void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color);
 	void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, RID p_normal_map = RID());

+ 1 - 0
servers/visual/visual_server_raster.h

@@ -1042,6 +1042,7 @@ public:
 	BIND2(canvas_item_set_draw_behind_parent, RID, bool)
 
 	BIND6(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float, bool)
+	BIND5(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool)
 	BIND3(canvas_item_add_rect, RID, const Rect2 &, const Color &)
 	BIND4(canvas_item_add_circle, RID, const Point2 &, float, const Color &)
 	BIND7(canvas_item_add_texture_rect, RID, const Rect2 &, RID, bool, const Color &, bool, RID)

+ 1 - 0
servers/visual/visual_server_viewport.cpp

@@ -279,6 +279,7 @@ void VisualServerViewport::draw_viewports() {
 		ERR_CONTINUE(!vp->render_target.is_valid());
 
 		bool visible = vp->viewport_to_screen_rect != Rect2() || vp->update_mode == VS::VIEWPORT_UPDATE_ALWAYS || vp->update_mode == VS::VIEWPORT_UPDATE_ONCE || (vp->update_mode == VS::VIEWPORT_UPDATE_WHEN_VISIBLE && VSG::storage->render_target_was_used(vp->render_target));
+		visible = visible && vp->size.x > 0 && vp->size.y > 0;
 
 		if (!visible)
 			continue;

+ 1 - 0
servers/visual/visual_server_wrap_mt.h

@@ -469,6 +469,7 @@ public:
 	FUNC2(canvas_item_set_draw_behind_parent, RID, bool)
 
 	FUNC6(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float, bool)
+	FUNC5(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool)
 	FUNC3(canvas_item_add_rect, RID, const Rect2 &, const Color &)
 	FUNC4(canvas_item_add_circle, RID, const Point2 &, float, const Color &)
 	FUNC7(canvas_item_add_texture_rect, RID, const Rect2 &, RID, bool, const Color &, bool, RID)

+ 1 - 0
servers/visual_server.h

@@ -787,6 +787,7 @@ public:
 	};
 
 	virtual void canvas_item_add_line(RID p_item, const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false) = 0;
+	virtual void canvas_item_add_polyline(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false) = 0;
 	virtual void canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color) = 0;
 	virtual void canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color) = 0;
 	virtual void canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, RID p_normal_map = RID()) = 0;