diff options
Diffstat (limited to 'modules')
63 files changed, 3308 insertions, 1043 deletions
diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp index e5439fd132..f2f51d9dd3 100644 --- a/modules/assimp/editor_scene_importer_assimp.cpp +++ b/modules/assimp/editor_scene_importer_assimp.cpp @@ -529,7 +529,7 @@ void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_anim } // -// Mesh Generation from indicies ? why do we need so much mesh code +// Mesh Generation from indices ? why do we need so much mesh code // [debt needs looked into] Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices( ImportState &state, @@ -541,6 +541,25 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices( mesh.instance(); bool has_uvs = false; + Map<String, uint32_t> morph_mesh_string_lookup; + + for (int i = 0; i < p_surface_indices.size(); i++) { + const unsigned int mesh_idx = p_surface_indices[0]; + const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx]; + for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) { + + String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName); + if (!morph_mesh_string_lookup.has(ai_anim_mesh_name)) { + morph_mesh_string_lookup.insert(ai_anim_mesh_name, j); + mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); + if (ai_anim_mesh_name.empty()) { + ai_anim_mesh_name = String("morph_") + itos(j); + } + mesh->add_blend_shape(ai_anim_mesh_name); + } + } + } + // // Process Vertex Weights // @@ -680,6 +699,25 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices( mat->set_cull_mode(SpatialMaterial::CULL_BACK); // Now process materials + aiTextureType base_color = aiTextureType_BASE_COLOR; + { + String filename, path; + AssimpImageData image_data; + + if (AssimpUtils::GetAssimpTexture(state, ai_material, base_color, filename, path, image_data)) { + AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); + + // anything transparent must be culled + if (image_data.raw_image->detect_alpha() != Image::ALPHA_NONE) { + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS); + mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode + } + + mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, image_data.texture); + } + } + aiTextureType tex_diffuse = aiTextureType_DIFFUSE; { String filename, path; @@ -731,6 +769,60 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices( } } + aiTextureType tex_normal_camera = aiTextureType_NORMAL_CAMERA; + { + String filename, path; + Ref<ImageTexture> texture; + AssimpImageData image_data; + + // Process texture normal map + if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_normal_camera, filename, path, image_data)) { + AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture); + } + } + + aiTextureType tex_emission_color = aiTextureType_EMISSION_COLOR; + { + String filename, path; + Ref<ImageTexture> texture; + AssimpImageData image_data; + + // Process texture normal map + if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_emission_color, filename, path, image_data)) { + AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); + mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true); + mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture); + } + } + + aiTextureType tex_metalness = aiTextureType_METALNESS; + { + String filename, path; + Ref<ImageTexture> texture; + AssimpImageData image_data; + + // Process texture normal map + if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_metalness, filename, path, image_data)) { + AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); + mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, image_data.texture); + } + } + + aiTextureType tex_roughness = aiTextureType_DIFFUSE_ROUGHNESS; + { + String filename, path; + Ref<ImageTexture> texture; + AssimpImageData image_data; + + // Process texture normal map + if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_roughness, filename, path, image_data)) { + AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); + mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, image_data.texture); + } + } + aiTextureType tex_emissive = aiTextureType_EMISSIVE; { String filename = ""; @@ -772,16 +864,17 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices( } } - aiTextureType tex_roughness = aiTextureType_SHININESS; + aiTextureType tex_ao_map = aiTextureType_AMBIENT_OCCLUSION; { String filename, path; Ref<ImageTexture> texture; AssimpImageData image_data; // Process texture normal map - if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_roughness, filename, path, image_data)) { + if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_ao_map, filename, path, image_data)) { AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); - mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, image_data.texture); + mat->set_feature(SpatialMaterial::FEATURE_AMBIENT_OCCLUSION, true); + mat->set_texture(SpatialMaterial::TEXTURE_AMBIENT_OCCLUSION, image_data.texture); } } @@ -789,16 +882,15 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices( Array morphs; morphs.resize(ai_mesh->mNumAnimMeshes); Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES; - Map<uint32_t, String> morph_mesh_idx_names; + for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) { String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName); - mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED); + if (ai_anim_mesh_name.empty()) { ai_anim_mesh_name = String("morph_") + itos(j); } - mesh->add_blend_shape(ai_anim_mesh_name); - morph_mesh_idx_names.insert(j, ai_anim_mesh_name); + Array array_copy; array_copy.resize(VisualServer::ARRAY_MAX); @@ -1188,7 +1280,6 @@ void EditorSceneImporterAssimp::create_bone(ImportState &state, RecursiveState & // this transform is a bone recursive_state.skeleton->add_bone(recursive_state.node_name); - ERR_FAIL_COND(recursive_state.skeleton == NULL); // serious bug we must now exit. //ERR_FAIL_COND(recursive_state.skeleton->get_name() == ""); print_verbose("Bone added to lookup: " + AssimpUtils::get_assimp_string(recursive_state.bone->mName)); print_verbose("Skeleton attached to: " + recursive_state.skeleton->get_name()); diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index 5a32fa1c2c..8708430257 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -63,139 +63,137 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, ERR_FAIL_V(ERR_UNAVAILABLE); } - if (err == OK) { - // Image data (might be indexed) - PoolVector<uint8_t> data; - int data_len = 0; + // Image data (might be indexed) + PoolVector<uint8_t> data; + int data_len = 0; - if (bits_per_pixel <= 8) { // indexed - data_len = width * height; - } else { // color - data_len = width * height * 4; - } - ERR_FAIL_COND_V(data_len == 0, ERR_BUG); - err = data.resize(data_len); - - PoolVector<uint8_t>::Write data_w = data.write(); - uint8_t *write_buffer = data_w.ptr(); - - const uint32_t width_bytes = width * bits_per_pixel / 8; - const uint32_t line_width = (width_bytes + 3) & ~3; - - // The actual data traversal is determined by - // the data width in case of 8/4/1 bit images - const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes; - const uint8_t *line = p_buffer + (line_width * (height - 1)); - - for (unsigned int i = 0; i < height; i++) { - const uint8_t *line_ptr = line; - - for (unsigned int j = 0; j < w; j++) { - switch (bits_per_pixel) { - case 1: { - uint8_t color_index = *line_ptr; - - write_buffer[index + 0] = (color_index >> 7) & 1; - write_buffer[index + 1] = (color_index >> 6) & 1; - write_buffer[index + 2] = (color_index >> 5) & 1; - write_buffer[index + 3] = (color_index >> 4) & 1; - write_buffer[index + 4] = (color_index >> 3) & 1; - write_buffer[index + 5] = (color_index >> 2) & 1; - write_buffer[index + 6] = (color_index >> 1) & 1; - write_buffer[index + 7] = (color_index >> 0) & 1; - - index += 8; - line_ptr += 1; - } break; - case 4: { - uint8_t color_index = *line_ptr; - - write_buffer[index + 0] = (color_index >> 4) & 0x0f; - write_buffer[index + 1] = color_index & 0x0f; - - index += 2; - line_ptr += 1; - } break; - case 8: { - uint8_t color_index = *line_ptr; - - write_buffer[index] = color_index; - - index += 1; - line_ptr += 1; - } break; - case 24: { - uint32_t color = *((uint32_t *)line_ptr); - - write_buffer[index + 2] = color & 0xff; - write_buffer[index + 1] = (color >> 8) & 0xff; - write_buffer[index + 0] = (color >> 16) & 0xff; - write_buffer[index + 3] = 0xff; - - index += 4; - line_ptr += 3; - } break; - case 32: { - uint32_t color = *((uint32_t *)line_ptr); - - write_buffer[index + 2] = color & 0xff; - write_buffer[index + 1] = (color >> 8) & 0xff; - write_buffer[index + 0] = (color >> 16) & 0xff; - write_buffer[index + 3] = color >> 24; - - index += 4; - line_ptr += 4; - } break; - } + if (bits_per_pixel <= 8) { // indexed + data_len = width * height; + } else { // color + data_len = width * height * 4; + } + ERR_FAIL_COND_V(data_len == 0, ERR_BUG); + err = data.resize(data_len); + + PoolVector<uint8_t>::Write data_w = data.write(); + uint8_t *write_buffer = data_w.ptr(); + + const uint32_t width_bytes = width * bits_per_pixel / 8; + const uint32_t line_width = (width_bytes + 3) & ~3; + + // The actual data traversal is determined by + // the data width in case of 8/4/1 bit images + const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes; + const uint8_t *line = p_buffer + (line_width * (height - 1)); + + for (uint64_t i = 0; i < height; i++) { + const uint8_t *line_ptr = line; + + for (unsigned int j = 0; j < w; j++) { + switch (bits_per_pixel) { + case 1: { + uint8_t color_index = *line_ptr; + + write_buffer[index + 0] = (color_index >> 7) & 1; + write_buffer[index + 1] = (color_index >> 6) & 1; + write_buffer[index + 2] = (color_index >> 5) & 1; + write_buffer[index + 3] = (color_index >> 4) & 1; + write_buffer[index + 4] = (color_index >> 3) & 1; + write_buffer[index + 5] = (color_index >> 2) & 1; + write_buffer[index + 6] = (color_index >> 1) & 1; + write_buffer[index + 7] = (color_index >> 0) & 1; + + index += 8; + line_ptr += 1; + } break; + case 4: { + uint8_t color_index = *line_ptr; + + write_buffer[index + 0] = (color_index >> 4) & 0x0f; + write_buffer[index + 1] = color_index & 0x0f; + + index += 2; + line_ptr += 1; + } break; + case 8: { + uint8_t color_index = *line_ptr; + + write_buffer[index] = color_index; + + index += 1; + line_ptr += 1; + } break; + case 24: { + uint32_t color = *((uint32_t *)line_ptr); + + write_buffer[index + 2] = color & 0xff; + write_buffer[index + 1] = (color >> 8) & 0xff; + write_buffer[index + 0] = (color >> 16) & 0xff; + write_buffer[index + 3] = 0xff; + + index += 4; + line_ptr += 3; + } break; + case 32: { + uint32_t color = *((uint32_t *)line_ptr); + + write_buffer[index + 2] = color & 0xff; + write_buffer[index + 1] = (color >> 8) & 0xff; + write_buffer[index + 0] = (color >> 16) & 0xff; + write_buffer[index + 3] = color >> 24; + + index += 4; + line_ptr += 4; + } break; } - line -= line_width; } + line -= line_width; + } - if (p_color_buffer == NULL || color_table_size == 0) { // regular pixels + if (p_color_buffer == NULL || color_table_size == 0) { // regular pixels - p_image->create(width, height, 0, Image::FORMAT_RGBA8, data); + p_image->create(width, height, 0, Image::FORMAT_RGBA8, data); - } else { // data is in indexed format, extend it + } else { // data is in indexed format, extend it - // Palette data - PoolVector<uint8_t> palette_data; - palette_data.resize(color_table_size * 4); + // Palette data + PoolVector<uint8_t> palette_data; + palette_data.resize(color_table_size * 4); - PoolVector<uint8_t>::Write palette_data_w = palette_data.write(); - uint8_t *pal = palette_data_w.ptr(); + PoolVector<uint8_t>::Write palette_data_w = palette_data.write(); + uint8_t *pal = palette_data_w.ptr(); - const uint8_t *cb = p_color_buffer; + const uint8_t *cb = p_color_buffer; - for (unsigned int i = 0; i < color_table_size; ++i) { - uint32_t color = *((uint32_t *)cb); + for (unsigned int i = 0; i < color_table_size; ++i) { + uint32_t color = *((uint32_t *)cb); - pal[i * 4 + 0] = (color >> 16) & 0xff; - pal[i * 4 + 1] = (color >> 8) & 0xff; - pal[i * 4 + 2] = (color)&0xff; - pal[i * 4 + 3] = 0xff; + pal[i * 4 + 0] = (color >> 16) & 0xff; + pal[i * 4 + 1] = (color >> 8) & 0xff; + pal[i * 4 + 2] = (color)&0xff; + pal[i * 4 + 3] = 0xff; - cb += 4; - } - // Extend palette to image - PoolVector<uint8_t> extended_data; - extended_data.resize(data.size() * 4); + cb += 4; + } + // Extend palette to image + PoolVector<uint8_t> extended_data; + extended_data.resize(data.size() * 4); - PoolVector<uint8_t>::Write ex_w = extended_data.write(); - uint8_t *dest = ex_w.ptr(); + PoolVector<uint8_t>::Write ex_w = extended_data.write(); + uint8_t *dest = ex_w.ptr(); - const int num_pixels = width * height; + const int num_pixels = width * height; - for (int i = 0; i < num_pixels; i++) { - dest[0] = pal[write_buffer[i] * 4 + 0]; - dest[1] = pal[write_buffer[i] * 4 + 1]; - dest[2] = pal[write_buffer[i] * 4 + 2]; - dest[3] = pal[write_buffer[i] * 4 + 3]; + for (int i = 0; i < num_pixels; i++) { + dest[0] = pal[write_buffer[i] * 4 + 0]; + dest[1] = pal[write_buffer[i] * 4 + 1]; + dest[2] = pal[write_buffer[i] * 4 + 2]; + dest[3] = pal[write_buffer[i] * 4 + 3]; - dest += 4; - } - p_image->create(width, height, 0, Image::FORMAT_RGBA8, extended_data); + dest += 4; } + p_image->create(width, height, 0, Image::FORMAT_RGBA8, extended_data); } } return err; diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index f1b3fa2ac6..5a76f32977 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -114,7 +114,7 @@ void CSGBrush::_regen_face_aabbs() { faces.write[i].aabb.position = faces[i].vertices[0]; faces.write[i].aabb.expand_to(faces[i].vertices[1]); faces.write[i].aabb.expand_to(faces[i].vertices[2]); - faces.write[i].aabb.grow_by(faces[i].aabb.get_longest_axis_size() * 0.001); //make it a tad bigger to avoid num precision erros + faces.write[i].aabb.grow_by(faces[i].aabb.get_longest_axis_size() * 0.001); //make it a tad bigger to avoid num precision errors } } diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp index e6bfa5525d..0d26943af6 100644 --- a/modules/csg/csg_gizmos.cpp +++ b/modules/csg/csg_gizmos.cpp @@ -377,7 +377,7 @@ void CSGShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { break; } - p_gizmo->add_mesh(mesh, false, RID(), solid_material); + p_gizmo->add_mesh(mesh, false, Ref<SkinReference>(), solid_material); } if (Object::cast_to<CSGSphere>(cs)) { diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 23725c4960..9409b33f24 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -282,7 +282,7 @@ void CSGShape::_update_shape() { root_mesh.unref(); //byebye root mesh CSGBrush *n = _get_brush(); - ERR_FAIL_COND(!n); + ERR_FAIL_COND_MSG(!n, "Cannot get CSGBrush."); OAHashMap<Vector3, Vector3> vec_map; @@ -1067,6 +1067,7 @@ void CSGSphere::set_radius(const float p_radius) { radius = p_radius; _make_dirty(); update_gizmo(); + _change_notify("radius"); } float CSGSphere::get_radius() const { @@ -1251,6 +1252,7 @@ void CSGBox::set_width(const float p_width) { width = p_width; _make_dirty(); update_gizmo(); + _change_notify("width"); } float CSGBox::get_width() const { @@ -1261,6 +1263,7 @@ void CSGBox::set_height(const float p_height) { height = p_height; _make_dirty(); update_gizmo(); + _change_notify("height"); } float CSGBox::get_height() const { @@ -1271,6 +1274,7 @@ void CSGBox::set_depth(const float p_depth) { depth = p_depth; _make_dirty(); update_gizmo(); + _change_notify("depth"); } float CSGBox::get_depth() const { @@ -1465,6 +1469,7 @@ void CSGCylinder::set_radius(const float p_radius) { radius = p_radius; _make_dirty(); update_gizmo(); + _change_notify("radius"); } float CSGCylinder::get_radius() const { @@ -1475,6 +1480,7 @@ void CSGCylinder::set_height(const float p_height) { height = p_height; _make_dirty(); update_gizmo(); + _change_notify("height"); } float CSGCylinder::get_height() const { @@ -1690,6 +1696,7 @@ void CSGTorus::set_inner_radius(const float p_inner_radius) { inner_radius = p_inner_radius; _make_dirty(); update_gizmo(); + _change_notify("inner_radius"); } float CSGTorus::get_inner_radius() const { @@ -1700,6 +1707,7 @@ void CSGTorus::set_outer_radius(const float p_outer_radius) { outer_radius = p_outer_radius; _make_dirty(); update_gizmo(); + _change_notify("outer_radius"); } float CSGTorus::get_outer_radius() const { @@ -2407,7 +2415,7 @@ NodePath CSGPolygon::get_path_node() const { } void CSGPolygon::set_path_interval(float p_interval) { - ERR_FAIL_COND(p_interval < 0.001); + ERR_FAIL_COND_MSG(p_interval < 0.001, "Path interval cannot be smaller than 0.001."); path_interval = p_interval; _make_dirty(); update_gizmo(); diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub index 142af0c800..746b23ca28 100644 --- a/modules/cvtt/SCsub +++ b/modules/cvtt/SCsub @@ -6,19 +6,18 @@ Import('env_modules') env_cvtt = env_modules.Clone() # Thirdparty source files -if env['builtin_squish']: - thirdparty_dir = "#thirdparty/cvtt/" - thirdparty_sources = [ - "ConvectionKernels.cpp" - ] +thirdparty_dir = "#thirdparty/cvtt/" +thirdparty_sources = [ + "ConvectionKernels.cpp" +] - thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_cvtt.Prepend(CPPPATH=[thirdparty_dir]) +env_cvtt.Prepend(CPPPATH=[thirdparty_dir]) - env_thirdparty = env_cvtt.Clone() - env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +env_thirdparty = env_cvtt.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) # Godot source files env_cvtt.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index 0220dcae4a..16a0f9d6ee 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -108,7 +108,7 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, if (r_error) *r_error = ERR_FILE_CORRUPT; - ERR_FAIL_COND_V_MSG(err != OK, RES(), "Unable to open DDS texture file: " + p_path + "."); + ERR_FAIL_COND_V_MSG(err != OK, RES(), "Unable to open DDS texture file '" + p_path + "'."); uint32_t magic = f->get_32(); uint32_t hsize = f->get_32(); @@ -127,7 +127,7 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, if (magic != DDS_MAGIC || hsize != 124 || !(flags & DDSD_PIXELFORMAT) || !(flags & DDSD_CAPS)) { - ERR_FAIL_V_MSG(RES(), "Invalid or unsupported DDS texture file: " + p_path + "."); + ERR_FAIL_V_MSG(RES(), "Invalid or unsupported DDS texture file '" + p_path + "'."); } /* uint32_t format_size = */ f->get_32(); @@ -216,7 +216,7 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, } else { printf("unrecognized fourcc %x format_flags: %x - rgbbits %i - red_mask %x green mask %x blue mask %x alpha mask %x\n", format_fourcc, format_flags, format_rgb_bits, format_red_mask, format_green_mask, format_blue_mask, format_alpha_mask); - ERR_FAIL_V_MSG(RES(), "Unrecognized or unsupported color layout in DDS: " + p_path + "."); + ERR_FAIL_V_MSG(RES(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'."); } if (!(flags & DDSD_MIPMAPCOUNT)) diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index 84ed5fd9ee..4c10588aa6 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -115,9 +115,11 @@ <member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode" default="0"> The compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all. </member> + <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" /> <member name="transfer_channel" type="int" setter="set_transfer_channel" getter="get_transfer_channel" default="-1"> Set the default channel to be used to transfer data. By default, this value is [code]-1[/code] which means that ENet will only use 2 channels, one for reliable and one for unreliable packets. Channel [code]0[/code] is reserved, and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data. </member> + <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="NetworkedMultiplayerPeer.TransferMode" default="2" /> </members> <constants> <constant name="COMPRESS_NONE" value="0" enum="CompressionMode"> diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 44f69ca261..a787cd3b80 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -543,7 +543,7 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer if (target_peer != 0) { E = peer_map.find(ABS(target_peer)); - ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, "Invalid target peer: " + itos(target_peer) + "."); + ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, "Invalid target peer '" + itos(target_peer) + "'."); } ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 8, packet_flags); diff --git a/modules/etc/texture_loader_pkm.cpp b/modules/etc/texture_loader_pkm.cpp index dd61d816d4..4d8af6883f 100644 --- a/modules/etc/texture_loader_pkm.cpp +++ b/modules/etc/texture_loader_pkm.cpp @@ -56,14 +56,14 @@ RES ResourceFormatPKM::load(const String &p_path, const String &p_original_path, if (r_error) *r_error = ERR_FILE_CORRUPT; - ERR_FAIL_COND_V_MSG(err != OK, RES(), "Unable to open PKM texture file: " + p_path + "."); + ERR_FAIL_COND_V_MSG(err != OK, RES(), "Unable to open PKM texture file '" + p_path + "'."); // big endian f->set_endian_swap(true); ETC1Header h; f->get_buffer((uint8_t *)&h.tag, sizeof(h.tag)); - ERR_FAIL_COND_V_MSG(strncmp(h.tag, "PKM 10", sizeof(h.tag)), RES(), "Invalid or unsupported PKM texture file: " + p_path + "."); + ERR_FAIL_COND_V_MSG(strncmp(h.tag, "PKM 10", sizeof(h.tag)), RES(), "Invalid or unsupported PKM texture file '" + p_path + "'."); h.format = f->get_16(); h.texWidth = f->get_16(); diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp index 1ef8e9f900..e97a75cca8 100644 --- a/modules/gdnative/gdnative/array.cpp +++ b/modules/gdnative/gdnative/array.cpp @@ -327,6 +327,15 @@ godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_b return res; } +godot_array GDAPI godot_array_slice(const godot_array *p_self, const godot_int p_begin, const godot_int p_end, const godot_int p_step, const godot_bool p_deep) { + const Array *self = (const Array *)p_self; + godot_array res; + Array *val = (Array *)&res; + memnew_placement(val, Array); + *val = self->slice(p_begin, p_end, p_step, p_deep); + return res; +} + godot_variant GDAPI godot_array_max(const godot_array *p_self) { const Array *self = (const Array *)p_self; godot_variant v; diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 03258584ce..55ba4ecc1e 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -80,6 +80,17 @@ ["const godot_vector2 *", "p_self"], ["const godot_vector2 *", "p_to"] ] + }, + { + "name": "godot_array_slice", + "return_type": "godot_array", + "arguments": [ + ["const godot_array *", "p_self"], + ["const godot_int", "p_begin"], + ["const godot_int", "p_end"], + ["const godot_int", "p_step"], + ["const godot_bool", "p_deep"] + ] } ] }, diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h index 10ef8a73d2..2e3ce58033 100644 --- a/modules/gdnative/include/gdnative/array.h +++ b/modules/gdnative/include/gdnative/array.h @@ -132,6 +132,8 @@ void GDAPI godot_array_destroy(godot_array *p_self); godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep); +godot_array GDAPI godot_array_slice(const godot_array *p_self, const godot_int p_begin, const godot_int p_end, const godot_int p_delta, const godot_bool p_deep); + godot_variant GDAPI godot_array_max(const godot_array *p_self); godot_variant GDAPI godot_array_min(const godot_array *p_self); diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index 7f52f5736c..8a05b6cfa3 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -64,9 +64,9 @@ typedef enum { GODOT_PROPERTY_HINT_LAYERS_3D_RENDER, GODOT_PROPERTY_HINT_LAYERS_3D_PHYSICS, GODOT_PROPERTY_HINT_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc," - GODOT_PROPERTY_HINT_DIR, ///< a directort path must be passed + GODOT_PROPERTY_HINT_DIR, ///< a directory path must be passed GODOT_PROPERTY_HINT_GLOBAL_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc," - GODOT_PROPERTY_HINT_GLOBAL_DIR, ///< a directort path must be passed + GODOT_PROPERTY_HINT_GLOBAL_DIR, ///< a directory path must be passed GODOT_PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type GODOT_PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines GODOT_PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp index 9de073fc8e..22898a73ce 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.cpp +++ b/modules/gdnative/pluginscript/pluginscript_language.cpp @@ -200,7 +200,7 @@ void PluginScriptLanguage::get_recognized_extensions(List<String> *p_extensions) } void PluginScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const { - // TODO: provid this statically in `godot_pluginscript_language_desc` ? + // TODO: provide this statically in `godot_pluginscript_language_desc` ? if (_desc.get_public_functions) { Array functions; _desc.get_public_functions(_data, (godot_array *)&functions); @@ -212,7 +212,7 @@ void PluginScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) c } void PluginScriptLanguage::get_public_constants(List<Pair<String, Variant> > *p_constants) const { - // TODO: provid this statically in `godot_pluginscript_language_desc` ? + // TODO: provide this statically in `godot_pluginscript_language_desc` ? if (_desc.get_public_constants) { Dictionary constants; _desc.get_public_constants(_data, (godot_dictionary *)&constants); diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp index 94d38e1be1..f7c961d38b 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.cpp +++ b/modules/gdnative/pluginscript/pluginscript_script.cpp @@ -401,9 +401,7 @@ Error PluginScript::load_source_code(const String &p_path) { PoolVector<uint8_t> sourcef; Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - if (err) { - ERR_FAIL_COND_V(err, err); - } + ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + p_path + "'."); int len = f->get_len(); sourcef.resize(len + 1); diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp index be131c5402..f3c34fd5e0 100644 --- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp +++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp @@ -149,7 +149,7 @@ void VideoStreamPlaybackGDNative::update(float p_delta) { if (mix_callback) { if (pcm_write_idx >= 0) { // Previous remains - int mixed = mix_callback(mix_udata, pcm, samples_decoded); + int mixed = mix_callback(mix_udata, pcm + pcm_write_idx * num_channels, samples_decoded); if (mixed == samples_decoded) { pcm_write_idx = -1; } else { @@ -168,8 +168,12 @@ void VideoStreamPlaybackGDNative::update(float p_delta) { } } - while (interface->get_playback_position(data_struct) < time && playing) { + if (seek_backward) { + update_texture(); + seek_backward = false; + } + while (interface->get_playback_position(data_struct) < time && playing) { update_texture(); } } @@ -197,6 +201,7 @@ VideoStreamPlaybackGDNative::VideoStreamPlaybackGDNative() : mix_callback(NULL), num_channels(-1), time(0), + seek_backward(false), mix_rate(0), delay_compensation(0), pcm(NULL), @@ -261,6 +266,13 @@ void VideoStreamPlaybackGDNative::stop() { void VideoStreamPlaybackGDNative::seek(float p_time) { ERR_FAIL_COND(interface == NULL); interface->seek(data_struct, p_time); + if (p_time < time) + seek_backward = true; + time = p_time; + // reset audio buffers + memset(pcm, 0, num_channels * AUX_BUFFER_SIZE * sizeof(float)); + pcm_write_idx = -1; + samples_decoded = 0; } void VideoStreamPlaybackGDNative::set_paused(bool p_paused) { diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.h b/modules/gdnative/videodecoder/video_stream_gdnative.h index b9f1c8e4da..9aed1fd2a0 100644 --- a/modules/gdnative/videodecoder/video_stream_gdnative.h +++ b/modules/gdnative/videodecoder/video_stream_gdnative.h @@ -121,6 +121,7 @@ class VideoStreamPlaybackGDNative : public VideoStreamPlayback { int num_channels; float time; + bool seek_backward; int mix_rate; double delay_compensation; diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 4efa90fd86..788db7fb86 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -694,6 +694,14 @@ [/codeblock] </description> </method> + <method name="ord"> + <return type="int"> + </return> + <argument index="0" name="char" type="String"> + </argument> + <description> + </description> + </method> <method name="parse_json"> <return type="Variant"> </return> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 5dab063061..db7f8d22e6 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -99,7 +99,7 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco #endif instance->owner->set_script_instance(instance); - /* STEP 2, INITIALIZE AND CONSRTUCT */ + /* STEP 2, INITIALIZE AND CONSTRUCT */ #ifndef NO_THREADS GDScriptLanguage::singleton->lock->lock(); @@ -2177,11 +2177,11 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori script->set_script_path(p_original_path); // script needs this. script->set_path(p_original_path); Error err = script->load_byte_code(p_path); - ERR_FAIL_COND_V(err != OK, RES()); + ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load byte code from file '" + p_path + "'."); } else { Error err = script->load_source_code(p_path); - ERR_FAIL_COND_V(err != OK, RES()); + ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load source code from file '" + p_path + "'."); script->set_script_path(p_original_path); // script needs this. script->set_path(p_original_path); @@ -2217,7 +2217,7 @@ String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) con void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { FileAccessRef file = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND(!file); + ERR_FAIL_COND_MSG(!file, "Cannot open file '" + p_path + "'."); String source = file->get_as_utf8_string(); if (source.empty()) { @@ -2244,10 +2244,7 @@ Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resou Error err; FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); - if (err) { - - ERR_FAIL_COND_V(err, err); - } + ERR_FAIL_COND_V_MSG(err, err, "Cannot save GDScript file '" + p_path + "'."); file->store_string(source); if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 7166189416..dea2225e91 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1520,8 +1520,16 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo if (ret2 < 0) return ERR_PARSE_ERROR; + int message_ret = 0; + if (as->message) { + message_ret = _parse_expression(codegen, as->message, p_stack_level + 1, false); + if (message_ret < 0) + return ERR_PARSE_ERROR; + } + codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSERT); codegen.opcodes.push_back(ret2); + codegen.opcodes.push_back(message_ret); #endif } break; case GDScriptParser::Node::TYPE_BREAKPOINT: { @@ -2064,7 +2072,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa } instance->owner->set_script_instance(instance); - /* STEP 2, INITIALIZE AND CONSRTUCT */ + /* STEP 2, INITIALIZE AND CONSTRUCT */ Variant::CallError ce; p_script->initializer->call(instance, NULL, 0, ce); diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 68f2a9473e..bdeea9cef3 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -1475,20 +1475,25 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_ASSERT) { - CHECK_SPACE(2); + CHECK_SPACE(3); #ifdef DEBUG_ENABLED GET_VARIANT_PTR(test, 1); + GET_VARIANT_PTR(message, 2); bool result = test->booleanize(); if (!result) { - - err_text = "Assertion failed."; + const String &message_str = *message; + if (message_str.empty()) { + err_text = "Assertion failed."; + } else { + err_text = "Assertion failed: " + message_str; + } OPCODE_BREAK; } #endif - ip += 2; + ip += 3; } DISPATCH_OPCODE; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index e96bf0238a..21434cd150 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1207,7 +1207,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (_get_completable_identifier(COMPLETION_INDEX, identifier)) { if (identifier == StringName()) { - identifier = "@temp"; //so it parses allright + identifier = "@temp"; //so it parses alright } completion_node = op; @@ -1399,9 +1399,6 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s unary = true; break; case OperatorNode::OP_NEG: - priority = 1; - unary = true; - break; case OperatorNode::OP_POS: priority = 1; unary = true; @@ -2846,15 +2843,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { assigned = subexpr; } else { - ConstantNode *c = alloc_node<ConstantNode>(); - if (lv->datatype.has_type && lv->datatype.kind == DataType::BUILTIN) { - Variant::CallError err; - c->value = Variant::construct(lv->datatype.builtin_type, NULL, 0, err); - } else { - c->value = Variant(); - } - c->line = var_line; - assigned = c; + assigned = _get_default_value_for_type(lv->datatype, var_line); } lv->assign = assigned; //must be added later, to avoid self-referencing. @@ -3280,15 +3269,36 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { case GDScriptTokenizer::TK_PR_ASSERT: { tokenizer->advance(); - Node *condition = _parse_and_reduce_expression(p_block, p_static); - if (!condition) { - if (_recover_from_completion()) { - break; - } + + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after assert"); + return; + } + + tokenizer->advance(); + + Vector<Node *> args; + const bool result = _parse_arguments(p_block, args, p_static); + if (!result) { + return; + } + + if (args.empty() || args.size() > 2) { + _set_error("Wrong number of arguments, expected 1 or 2"); return; } + AssertNode *an = alloc_node<AssertNode>(); - an->condition = condition; + an->condition = _reduce_expression(args[0], p_static); + + if (args.size() == 2) { + an->message = _reduce_expression(args[1], p_static); + } else { + ConstantNode *message_node = alloc_node<ConstantNode>(); + message_node->value = String(); + an->message = message_node; + } + p_block->statements.push_back(an); if (!_end_statement()) { @@ -3533,6 +3543,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } + if (p_class->classname_used && ProjectSettings::get_singleton()->has_setting("autoload/" + p_class->name)) { + const String autoload_path = ProjectSettings::get_singleton()->get_setting("autoload/" + p_class->name); + if (autoload_path.begins_with("*")) { + // It's a singleton, and not just a regular AutoLoad script. + _set_error("The class \"" + p_class->name + "\" conflicts with the AutoLoad singleton of the same name, and is therefore redundant. Remove the class_name declaration to fix this error."); + } + return; + } + tokenizer->advance(2); if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { @@ -4839,35 +4858,29 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - Variant::Type initial_type = member.data_type.has_type ? member.data_type.builtin_type : member._export.type; + Node *expr; - if (initial_type != Variant::NIL && initial_type != Variant::OBJECT) { - IdentifierNode *id = alloc_node<IdentifierNode>(); - id->name = member.identifier; - - Node *expr; + if (member.data_type.has_type) { + expr = _get_default_value_for_type(member.data_type); + } else { + DataType exported_type; + exported_type.has_type = true; + exported_type.kind = DataType::BUILTIN; + exported_type.builtin_type = member._export.type; + expr = _get_default_value_for_type(exported_type); + } - // Make sure arrays and dictionaries are not shared - if (initial_type == Variant::ARRAY) { - expr = alloc_node<ArrayNode>(); - } else if (initial_type == Variant::DICTIONARY) { - expr = alloc_node<DictionaryNode>(); - } else { - ConstantNode *cn = alloc_node<ConstantNode>(); - Variant::CallError ce2; - cn->value = Variant::construct(initial_type, NULL, 0, ce2); - expr = cn; - } + IdentifierNode *id = alloc_node<IdentifierNode>(); + id->name = member.identifier; - OperatorNode *op = alloc_node<OperatorNode>(); - op->op = OperatorNode::OP_INIT_ASSIGN; - op->arguments.push_back(id); - op->arguments.push_back(expr); + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = OperatorNode::OP_INIT_ASSIGN; + op->arguments.push_back(id); + op->arguments.push_back(expr); - p_class->initializer->statements.push_back(op); + p_class->initializer->statements.push_back(op); - member.initial_assignment = op; - } + member.initial_assignment = op; } if (autoexport && member.data_type.has_type) { @@ -5727,28 +5740,35 @@ GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source, } } - // Still look for class constants in parent script + // Still look for class constants in parent scripts if (!found && (base_type.kind == DataType::GDSCRIPT || base_type.kind == DataType::SCRIPT)) { Ref<Script> scr = base_type.script_type; ERR_FAIL_COND_V(scr.is_null(), result); - Map<StringName, Variant> constants; - scr->get_constants(&constants); + while (scr.is_valid()) { + Map<StringName, Variant> constants; + scr->get_constants(&constants); - if (constants.has(id)) { - Ref<GDScript> gds = constants[id]; + if (constants.has(id)) { + Ref<GDScript> gds = constants[id]; - if (gds.is_valid()) { - result.kind = DataType::GDSCRIPT; - result.script_type = gds; - found = true; - } else { - Ref<Script> scr2 = constants[id]; - if (scr2.is_valid()) { - result.kind = DataType::SCRIPT; - result.script_type = scr2; + if (gds.is_valid()) { + result.kind = DataType::GDSCRIPT; + result.script_type = gds; found = true; + } else { + Ref<Script> scr2 = constants[id]; + if (scr2.is_valid()) { + result.kind = DataType::SCRIPT; + result.script_type = scr2; + found = true; + } } } + if (found) { + break; + } else { + scr = scr->get_base_script(); + } } } @@ -6140,6 +6160,31 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data return false; } +GDScriptParser::Node *GDScriptParser::_get_default_value_for_type(const DataType &p_type, int p_line) { + Node *result; + + if (p_type.has_type && p_type.kind == DataType::BUILTIN && p_type.builtin_type != Variant::NIL && p_type.builtin_type != Variant::OBJECT) { + if (p_type.builtin_type == Variant::ARRAY) { + result = alloc_node<ArrayNode>(); + } else if (p_type.builtin_type == Variant::DICTIONARY) { + result = alloc_node<DictionaryNode>(); + } else { + ConstantNode *c = alloc_node<ConstantNode>(); + Variant::CallError err; + c->value = Variant::construct(p_type.builtin_type, NULL, 0, err); + result = c; + } + } else { + ConstantNode *c = alloc_node<ConstantNode>(); + c->value = Variant(); + result = c; + } + + result->line = p_line; + + return result; +} + GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { #ifdef DEBUG_ENABLED if (p_node->get_datatype().has_type && p_node->type != Node::TYPE_ARRAY && p_node->type != Node::TYPE_DICTIONARY) { @@ -6655,7 +6700,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } } - p_node->set_datatype(_resolve_type(node_type, p_node->line)); + node_type = _resolve_type(node_type, p_node->line); + p_node->set_datatype(node_type); return node_type; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 72aa819a8c..04ce9cf4c6 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -481,7 +481,12 @@ public: struct AssertNode : public Node { Node *condition; - AssertNode() { type = TYPE_ASSERT; } + Node *message; + AssertNode() : + condition(0), + message(0) { + type = TYPE_ASSERT; + } }; struct BreakpointNode : public Node { @@ -610,6 +615,7 @@ private: bool _get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const; bool _get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type) const; bool _is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion = false) const; + Node *_get_default_value_for_type(const DataType &p_type, int p_line = -1); DataType _reduce_node_type(Node *p_node); DataType _reduce_function_call_type(const OperatorNode *p_call); diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 64b354bdb8..8b20b0ff48 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -1425,7 +1425,7 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) int len; // Objects cannot be constant, never encode objects Error err = encode_variant(E->get(), NULL, len, false); - ERR_FAIL_COND_V(err != OK, Vector<uint8_t>()); + ERR_FAIL_COND_V_MSG(err != OK, Vector<uint8_t>(), "Error when trying to encode Variant."); int pos = buf.size(); buf.resize(pos + len); encode_variant(E->get(), &buf.write[pos], len, false); diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 45f9ec9c6a..6db8cb2c88 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -189,6 +189,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p lsp::DocumentSymbol symbol; const GDScriptParser::ClassNode::Constant &c = E->value(); const GDScriptParser::ConstantNode *node = dynamic_cast<const GDScriptParser::ConstantNode *>(c.expression); + ERR_FAIL_COND(!node); symbol.name = E->key(); symbol.kind = lsp::SymbolKind::Constant; symbol.deprecated = false; @@ -344,15 +345,13 @@ String ExtendGDScriptParser::marked_documentation(const String &p_bbcode) { if (block_start != -1) { code_block_indent = block_start; in_code_block = true; - line = "'''gdscript"; - line = "\n"; + line = "'''gdscript\n"; } else if (in_code_block) { line = "\t" + line.substr(code_block_indent, line.length()); } if (in_code_block && line.find("[/codeblock]") != -1) { - line = "'''\n"; - line = "\n"; + line = "'''\n\n"; in_code_block = false; } @@ -674,6 +673,7 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode const GDScriptParser::ClassNode::Constant &c = E->value(); const GDScriptParser::ConstantNode *node = dynamic_cast<const GDScriptParser::ConstantNode *>(c.expression); + ERR_FAIL_COND_V(!node, class_api); Dictionary api; api["name"] = E->key(); diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index afe461b68e..ce3de9bc3b 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -100,9 +100,10 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { String root_uri = p_params["rootUri"]; String root = p_params["rootPath"]; - bool is_same_workspace = root == workspace->root; + bool is_same_workspace; +#ifndef WINDOWS_ENABLED is_same_workspace = root.to_lower() == workspace->root.to_lower(); -#ifdef WINDOWS_ENABLED +#else is_same_workspace = root.replace("\\", "/").to_lower() == workspace->root.to_lower(); #endif @@ -142,6 +143,7 @@ void GDScriptLanguageProtocol::poll() { Error GDScriptLanguageProtocol::start(int p_port) { if (server == NULL) { server = dynamic_cast<WebSocketServer *>(ClassDB::instance("WebSocketServer")); + ERR_FAIL_COND_V(!server, FAILED); server->set_buffers(8192, 1024, 8192, 1024); // 8mb should be way more than enough server->connect("data_received", this, "on_data_received"); server->connect("client_connected", this, "on_client_connected"); diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 9bea4557ac..62c212a2bd 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -64,7 +64,7 @@ void GDScriptLanguageServer::thread_main(void *p_userdata) { void GDScriptLanguageServer::start() { int port = (int)_EDITOR_GET("network/language_server/remote_port"); if (protocol.start(port) == OK) { - EditorNode::get_log()->add_message("** GDScript Language Server Started **"); + EditorNode::get_log()->add_message("--- GDScript language server started ---", EditorLog::MSG_TYPE_EDITOR); ERR_FAIL_COND(thread != NULL || thread_exit); thread_exit = false; thread = Thread::create(GDScriptLanguageServer::thread_main, this); @@ -78,7 +78,7 @@ void GDScriptLanguageServer::stop() { memdelete(thread); thread = NULL; protocol.stop(); - EditorNode::get_log()->add_message("** GDScript Language Server Stopped **"); + EditorNode::get_log()->add_message("--- GDScript language server stopped ---", EditorLog::MSG_TYPE_EDITOR); } void register_lsp_types() { diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index f211fae526..7c58c7aa15 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -380,8 +380,8 @@ GDScriptTextDocument::~GDScriptTextDocument() { memdelete(file_checker); } -void GDScriptTextDocument::sync_script_content(const String &p_uri, const String &p_content) { - String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_uri); +void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) { + String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path); GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content); } diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 07b4f7f596..c97524a54d 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -385,8 +385,8 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo if (!p.intersects_segment(from, from + normal * settings_pick_distance->get_value(), &inters)) return false; - //make sure the intersection is inside the frustum planes, to avoid - //painting on invisible regions + // Make sure the intersection is inside the frustum planes, to avoid + // Painting on invisible regions. for (int i = 0; i < planes.size(); i++) { Plane fp = local_xform.xform(planes[i]); @@ -397,8 +397,6 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo int cell[3]; float cell_size[3] = { node->get_cell_size().x, node->get_cell_size().y, node->get_cell_size().z }; - last_mouseover = Vector3(-1, -1, -1); - for (int i = 0; i < 3; i++) { if (i == edit_axis) @@ -407,19 +405,11 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo cell[i] = inters[i] / node->get_cell_size()[i]; if (inters[i] < 0) - cell[i] -= 1; //compensate negative + cell[i] -= 1; // Compensate negative. grid_ofs[i] = cell[i] * cell_size[i]; } - - /*if (cell[i]<0 || cell[i]>=grid_size[i]) { - - cursor_visible=false; - _update_cursor_transform(); - return false; - }*/ } - last_mouseover = Vector3(cell[0], cell[1], cell[2]); VS::get_singleton()->instance_set_transform(grid_instance[edit_axis], node->get_global_transform() * edit_grid_xform); if (cursor_instance.is_valid()) { @@ -656,7 +646,7 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu if (mb->is_pressed()) floor->set_value(floor->get_value() + mb->get_factor()); - return true; //eaten + return true; // Eaten. } else if (mb->get_button_index() == BUTTON_WHEEL_DOWN && (mb->get_command() || mb->get_shift())) { if (mb->is_pressed()) floor->set_value(floor->get_value() - mb->get_factor()); @@ -702,9 +692,7 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu return do_input_action(p_camera, Point2(mb->get_position().x, mb->get_position().y), true); } else { - if ( - (mb->get_button_index() == BUTTON_RIGHT && input_action == INPUT_ERASE) || - (mb->get_button_index() == BUTTON_LEFT && input_action == INPUT_PAINT)) { + if ((mb->get_button_index() == BUTTON_RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == BUTTON_LEFT && input_action == INPUT_PAINT)) { if (set_items.size()) { undo_redo->create_action(TTR("GridMap Paint")); @@ -884,9 +872,15 @@ void GridMapEditor::update_palette() { if (mesh_library.is_null()) { last_mesh_library = NULL; + search_box->set_text(""); + search_box->set_editable(false); + info_message->show(); return; } + search_box->set_editable(true); + info_message->hide(); + Vector<int> ids; ids = mesh_library->get_item_list(); @@ -927,7 +921,7 @@ void GridMapEditor::update_palette() { item++; } - if (selected != -1) { + if (selected != -1 && mesh_library_palette->get_item_count() > 0) { mesh_library_palette->select(selected); } @@ -939,7 +933,6 @@ void GridMapEditor::edit(GridMap *p_gridmap) { node = p_gridmap; VS *vs = VS::get_singleton(); - last_mouseover = Vector3(-1, -1, -1); input_action = INPUT_NONE; selection.active = false; _update_selection_transform(); @@ -975,7 +968,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) { { - //update grids + // Update grids. indicator_mat.instance(); indicator_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); indicator_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); @@ -1046,9 +1039,7 @@ void GridMapEditor::_update_clip() { void GridMapEditor::update_grid() { - grid_xform.origin.x -= 1; //force update in hackish way.. what do i care - - //VS *vs = VS::get_singleton(); + grid_xform.origin.x -= 1; // Force update in hackish way. grid_ofs[edit_axis] = edit_floor[edit_axis] * node->get_cell_size()[edit_axis]; @@ -1069,6 +1060,7 @@ void GridMapEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { + get_tree()->connect("node_removed", this, "_node_removed"); mesh_library_palette->connect("item_selected", this, "_item_selected_cbk"); for (int i = 0; i < 3; i++) { @@ -1085,6 +1077,7 @@ void GridMapEditor::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { + get_tree()->disconnect("node_removed", this, "_node_removed"); _clear_clipboard_data(); for (int i = 0; i < 3; i++) { @@ -1132,7 +1125,6 @@ void GridMapEditor::_notification(int p_what) { SpatialEditorPlugin *sep = Object::cast_to<SpatialEditorPlugin>(editor->get_editor_plugin_screen()); if (sep) sep->snap_cursor_to_plane(p); - //editor->get_editor_plugin_screen()->call("snap_cursor_to_plane",p); } } break; @@ -1198,6 +1190,7 @@ void GridMapEditor::_bind_methods() { ClassDB::bind_method("_floor_changed", &GridMapEditor::_floor_changed); ClassDB::bind_method("_floor_mouse_exited", &GridMapEditor::_floor_mouse_exited); ClassDB::bind_method("_set_selection", &GridMapEditor::_set_selection); + ClassDB::bind_method("_node_removed", &GridMapEditor::_node_removed); ClassDB::bind_method(D_METHOD("_set_display_mode", "mode"), &GridMapEditor::_set_display_mode); } @@ -1296,6 +1289,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { search_box = memnew(LineEdit); search_box->set_h_size_flags(SIZE_EXPAND_FILL); + search_box->set_placeholder(TTR("Filter meshes")); hb->add_child(search_box); search_box->connect("text_changed", this, "_text_changed"); search_box->connect("gui_input", this, "_sbox_input"); @@ -1331,6 +1325,14 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { add_child(mesh_library_palette); mesh_library_palette->set_v_size_flags(SIZE_EXPAND_FILL); + info_message = memnew(Label); + info_message->set_text(TTR("Give a MeshLibrary resource to this GridMap to use its meshes.")); + info_message->set_valign(Label::VALIGN_CENTER); + info_message->set_align(Label::ALIGN_CENTER); + info_message->set_autowrap(true); + info_message->set_anchors_and_margins_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE); + mesh_library_palette->add_child(info_message); + edit_axis = Vector3::AXIS_Y; edit_floor[0] = -1; edit_floor[1] = -1; @@ -1340,13 +1342,12 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { selected_palette = -1; lock_view = false; cursor_rot = 0; - last_mouseover = Vector3(-1, -1, -1); selection_mesh = VisualServer::get_singleton()->mesh_create(); paste_mesh = VisualServer::get_singleton()->mesh_create(); { - //selection mesh create + // Selection mesh create. PoolVector<Vector3> lines; PoolVector<Vector3> triangles; @@ -1424,7 +1425,6 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { inner_mat.instance(); inner_mat->set_albedo(Color(0.7, 0.7, 1.0, 0.2)); - //inner_mat->set_flag(SpatialMaterial::FLAG_ONTOP, true); inner_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); inner_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); @@ -1444,7 +1444,6 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { selection_floor_mat->set_on_top_of_alpha(); selection_floor_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); selection_floor_mat->set_line_width(3.0); - //selection_floor_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); d[VS::ARRAY_VERTEX] = lines; VisualServer::get_singleton()->mesh_add_surface_from_arrays(selection_mesh, VS::PRIMITIVE_LINES, d); diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h index d174ac1035..48a07e9c7f 100644 --- a/modules/gridmap/grid_map_editor_plugin.h +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -156,7 +156,6 @@ class GridMapEditor : public VBoxContainer { Transform cursor_transform; Vector3 cursor_origin; - Vector3 last_mouseover; int display_mode; int selected_palette; @@ -197,12 +196,16 @@ class GridMapEditor : public VBoxContainer { RID instance; }; + ItemList *mesh_library_palette; + Label *info_message; + + EditorNode *editor; + void update_grid(); void _configure(); void _menu_option(int); void update_palette(); void _set_display_mode(int p_mode); - ItemList *mesh_library_palette; void _item_selected_cbk(int idx); void _update_cursor_transform(); void _update_cursor_instance(); @@ -227,7 +230,6 @@ class GridMapEditor : public VBoxContainer { void _delete_selection(); void _fill_selection(); - EditorNode *editor; bool do_input_action(Camera *p_camera, const Point2 &p_point, bool p_click); friend class GridMapEditorPlugin; diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp index 1e02084ae2..ca656b4b9b 100644 --- a/modules/mbedtls/crypto_mbedtls.cpp +++ b/modules/mbedtls/crypto_mbedtls.cpp @@ -55,7 +55,7 @@ Error CryptoKeyMbedTLS::load(String p_path) { PoolByteArray out; FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V(!f, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(!f, ERR_INVALID_PARAMETER, "Cannot open CryptoKeyMbedTLS file '" + p_path + "'."); int flen = f->get_len(); out.resize(flen + 1); @@ -69,14 +69,14 @@ Error CryptoKeyMbedTLS::load(String p_path) { int ret = mbedtls_pk_parse_key(&pkey, out.read().ptr(), out.size(), NULL, 0); // We MUST zeroize the memory for safety! mbedtls_platform_zeroize(out.write().ptr(), out.size()); - ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing private key: " + itos(ret)); + ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing private key '" + itos(ret) + "'."); return OK; } Error CryptoKeyMbedTLS::save(String p_path) { FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V(!f, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(!f, ERR_INVALID_PARAMETER, "Cannot save CryptoKeyMbedTLS file '" + p_path + "'."); unsigned char w[16000]; memset(w, 0, sizeof(w)); @@ -85,7 +85,7 @@ Error CryptoKeyMbedTLS::save(String p_path) { if (ret != 0) { memdelete(f); memset(w, 0, sizeof(w)); // Zeroize anything we might have written. - ERR_FAIL_V_MSG(FAILED, "Error writing key: " + itos(ret)); + ERR_FAIL_V_MSG(FAILED, "Error writing key '" + itos(ret) + "'."); } size_t len = strlen((char *)w); @@ -104,7 +104,7 @@ Error X509CertificateMbedTLS::load(String p_path) { PoolByteArray out; FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V(!f, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(!f, ERR_INVALID_PARAMETER, "Cannot open X509CertificateMbedTLS file '" + p_path + "'."); int flen = f->get_len(); out.resize(flen + 1); @@ -131,7 +131,7 @@ Error X509CertificateMbedTLS::load_from_memory(const uint8_t *p_buffer, int p_le Error X509CertificateMbedTLS::save(String p_path) { FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V(!f, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(!f, ERR_INVALID_PARAMETER, "Cannot save X509CertificateMbedTLS file '" + p_path + "'."); mbedtls_x509_crt *crt = &cert; while (crt) { @@ -140,7 +140,7 @@ Error X509CertificateMbedTLS::save(String p_path) { int ret = mbedtls_pem_write_buffer(PEM_BEGIN_CRT, PEM_END_CRT, cert.raw.p, cert.raw.len, w, sizeof(w), &wrote); if (ret != 0 || wrote == 0) { memdelete(f); - ERR_FAIL_V_MSG(FAILED, "Error writing certificate: " + itos(ret)); + ERR_FAIL_V_MSG(FAILED, "Error writing certificate '" + itos(ret) + "'."); } f->store_buffer(w, wrote - 1); // don't write the string terminator diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index f751719531..4c1ebd8d74 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -18,7 +18,7 @@ android_arch_dirs = { def get_android_out_dir(env): - return os.path.join(Dir('#platform/android/java/libs').abspath, + return os.path.join(Dir('#platform/android/java/lib/libs').abspath, 'release' if env['target'] == 'release' else 'debug', android_arch_dirs[env['android_arch']]) diff --git a/modules/mono/class_db_api_json.cpp b/modules/mono/class_db_api_json.cpp index 7580911a0a..bbc779601e 100644 --- a/modules/mono/class_db_api_json.cpp +++ b/modules/mono/class_db_api_json.cpp @@ -236,7 +236,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) { } FileAccessRef f = FileAccess::open(p_output_file, FileAccess::WRITE); - ERR_FAIL_COND(!f); + ERR_FAIL_COND_MSG(!f, "Cannot open file '" + p_output_file + "'."); f->store_string(JSON::print(classes_dict, /*indent: */ "\t")); f->close(); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index e14e919f92..83be10dee3 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -1195,7 +1195,7 @@ void CSharpLanguage::release_script_gchandle(MonoObject *p_expected_obj, Ref<Mon CSharpLanguage::CSharpLanguage() { - ERR_FAIL_COND(singleton); + ERR_FAIL_COND_MSG(singleton, "C# singleton already exist."); singleton = this; finalizing = false; @@ -3242,7 +3242,7 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p #if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED) Error err = script->load_source_code(p_path); - ERR_FAIL_COND_V(err != OK, RES()); + ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load C# script file '" + p_path + "'."); #endif script->set_path(p_original_path); @@ -3325,7 +3325,7 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r Error err; FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V(err, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save C# script file '" + p_path + "'."); file->store_string(source); diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs index aefc51545e..4f93ef8530 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs @@ -16,7 +16,7 @@ namespace GodotTools { private void AddFile(string srcPath, string dstPath, bool remap = false) { - AddFile(dstPath, File.ReadAllBytes(srcPath), remap); + AddFile(dstPath.Replace("\\", "/"), File.ReadAllBytes(srcPath), remap); } public override void _ExportFile(string path, string type, string[] features) diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 1888bb3cb9..28f098d323 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -863,12 +863,14 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir, Vector<String> &r_compile_items) { + ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); if (!DirAccess::exists(p_proj_dir)) { Error err = da->make_dir_recursive(p_proj_dir); - ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Cannot create directory '" + p_proj_dir + "'."); } da->change_dir(p_proj_dir); @@ -984,6 +986,8 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir, Vect Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir, Vector<String> &r_compile_items) { + ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); @@ -1064,6 +1068,8 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir, Ve Error BindingsGenerator::generate_cs_api(const String &p_output_dir) { + ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED); + String output_dir = path::abspath(path::realpath(p_output_dir)); DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); @@ -1703,6 +1709,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf Error BindingsGenerator::generate_glue(const String &p_output_dir) { + ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED); + bool dir_exists = DirAccess::exists(p_output_dir); ERR_FAIL_COND_V_MSG(!dir_exists, ERR_FILE_BAD_PATH, "The output directory does not exist."); @@ -1785,6 +1793,9 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { output.append("uint32_t get_bindings_version() { return "); output.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n"); + output.append("uint32_t get_cs_glue_version() { return "); + output.append(String::num_uint64(CS_GLUE_VERSION) + "; }\n"); + output.append("\nvoid register_generated_icalls() " OPEN_BLOCK); output.append("\tgodot_register_glue_header_icalls();\n"); @@ -2148,7 +2159,7 @@ StringName BindingsGenerator::_get_float_type_name_from_meta(GodotTypeInfo::Meta } } -void BindingsGenerator::_populate_object_type_interfaces() { +bool BindingsGenerator::_populate_object_type_interfaces() { obj_types.clear(); @@ -2226,7 +2237,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { bool valid = false; iprop.index = ClassDB::get_property_index(type_cname, iprop.cname, &valid); - ERR_FAIL_COND(!valid); + ERR_FAIL_COND_V(!valid, false); iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname)); @@ -2290,7 +2301,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { imethod.is_vararg = m && m->is_vararg(); if (!m && !imethod.is_virtual) { - ERR_FAIL_COND_MSG(!virtual_method_list.find(method_info), + ERR_FAIL_COND_V_MSG(!virtual_method_list.find(method_info), false, "Missing MethodBind for non-virtual method: '" + itype.name + "." + imethod.name + "'."); // A virtual method without the virtual flag. This is a special case. @@ -2307,9 +2318,9 @@ void BindingsGenerator::_populate_object_type_interfaces() { // which could actually will return something different. // Let's put this to notify us if that ever happens. if (itype.cname != name_cache.type_Object || imethod.name != "free") { - ERR_PRINTS("Notification: New unexpected virtual non-overridable method found." - " We only expected Object.free, but found '" + - itype.name + "." + imethod.name + "'."); + WARN_PRINTS("Notification: New unexpected virtual non-overridable method found." + " We only expected Object.free, but found '" + + itype.name + "." + imethod.name + "'."); } } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { imethod.return_type.cname = return_info.class_name; @@ -2321,7 +2332,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { ERR_PRINTS("Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." " Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'."); /* clang-format on */ - ERR_FAIL(); + ERR_FAIL_V(false); } } else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { imethod.return_type.cname = return_info.hint_string; @@ -2342,8 +2353,10 @@ void BindingsGenerator::_populate_object_type_interfaces() { for (int i = 0; i < argc; i++) { PropertyInfo arginfo = method_info.arguments[i]; + String orig_arg_name = arginfo.name; + ArgumentInterface iarg; - iarg.name = arginfo.name; + iarg.name = orig_arg_name; if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { iarg.type.cname = arginfo.class_name; @@ -2367,7 +2380,9 @@ void BindingsGenerator::_populate_object_type_interfaces() { iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); if (m && m->has_default_argument(i)) { - _default_argument_from_variant(m->get_default_argument(i), iarg); + bool defval_ok = _arg_default_value_from_variant(m->get_default_argument(i), iarg); + ERR_FAIL_COND_V_MSG(!defval_ok, false, + "Cannot determine default value for argument '" + orig_arg_name + "' of method '" + itype.name + "." + imethod.name + "'."); } imethod.add_argument(iarg); @@ -2447,7 +2462,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { const StringName &constant_cname = E->get(); String constant_name = constant_cname.operator String(); int *value = class_info->constant_map.getptr(constant_cname); - ERR_FAIL_NULL(value); + ERR_FAIL_NULL_V(value, false); constants.erase(constant_name); ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value); @@ -2483,7 +2498,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { for (const List<String>::Element *E = constants.front(); E; E = E->next()) { const String &constant_name = E->get(); int *value = class_info->constant_map.getptr(StringName(E->get())); - ERR_FAIL_NULL(value); + ERR_FAIL_NULL_V(value, false); ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value); @@ -2504,9 +2519,11 @@ void BindingsGenerator::_populate_object_type_interfaces() { class_list.pop_front(); } + + return true; } -void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg) { +bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, ArgumentInterface &r_iarg) { r_iarg.default_argument = p_val; @@ -2552,16 +2569,24 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; break; case Variant::OBJECT: - if (p_val.is_zero()) { - r_iarg.default_argument = "null"; - break; - } - FALLTHROUGH; + ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false, + "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value."); + + r_iarg.default_argument = "null"; + break; case Variant::DICTIONARY: - case Variant::_RID: r_iarg.default_argument = "new %s()"; r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF; break; + case Variant::_RID: + ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_RID, false, + "Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_RID) + "'."); + + ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false, + "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value."); + + r_iarg.default_argument = "null"; + break; case Variant::ARRAY: case Variant::POOL_BYTE_ARRAY: case Variant::POOL_INT_ARRAY: @@ -2585,6 +2610,8 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null") r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF; + + return true; } void BindingsGenerator::_populate_builtin_type_interfaces() { @@ -2970,13 +2997,17 @@ void BindingsGenerator::_log(const char *p_format, ...) { void BindingsGenerator::_initialize() { + initialized = false; + EditorHelp::generate_doc(); enum_types.clear(); _initialize_blacklisted_methods(); - _populate_object_type_interfaces(); + bool obj_type_ok = _populate_object_type_interfaces(); + ERR_FAIL_COND_MSG(!obj_type_ok, "Failed to generate object type interfaces"); + _populate_builtin_type_interfaces(); _populate_global_constants(); @@ -2988,6 +3019,8 @@ void BindingsGenerator::_initialize() { for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) _generate_method_icalls(E.get()); + + initialized = true; } void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) { @@ -3048,6 +3081,11 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) BindingsGenerator bindings_generator; bindings_generator.set_log_print_enabled(true); + if (!bindings_generator.initialized) { + ERR_PRINTS("Failed to initialize the bindings generator"); + ::exit(0); + } + if (glue_dir_path.length()) { if (bindings_generator.generate_glue(glue_dir_path) != OK) ERR_PRINTS(generate_all_glue_option + ": Failed to generate the C++ glue."); diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 6f0c297575..26718f1d11 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -472,6 +472,7 @@ class BindingsGenerator { }; bool log_print_enabled; + bool initialized; OrderedHashMap<StringName, TypeInterface> obj_types; @@ -502,6 +503,7 @@ class BindingsGenerator { StringName type_VarArg; StringName type_Object; StringName type_Reference; + StringName type_RID; StringName type_String; StringName type_at_GlobalScope; StringName enum_Error; @@ -525,6 +527,7 @@ class BindingsGenerator { type_VarArg = StaticCString::create("VarArg"); type_Object = StaticCString::create("Object"); type_Reference = StaticCString::create("Reference"); + type_RID = StaticCString::create("RID"); type_String = StaticCString::create("String"); type_at_GlobalScope = StaticCString::create("@GlobalScope"); enum_Error = StaticCString::create("Error"); @@ -590,9 +593,9 @@ class BindingsGenerator { StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta); StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta); - void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg); + bool _arg_default_value_from_variant(const Variant &p_val, ArgumentInterface &r_iarg); - void _populate_object_type_interfaces(); + bool _populate_object_type_interfaces(); void _populate_builtin_type_interfaces(); void _populate_global_constants(); @@ -621,12 +624,15 @@ public: _FORCE_INLINE_ bool is_log_print_enabled() { return log_print_enabled; } _FORCE_INLINE_ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; } + _FORCE_INLINE_ bool is_initialized() { return initialized; } + static uint32_t get_version(); static void handle_cmdline_args(const List<String> &p_cmdline_args); BindingsGenerator() : - log_print_enabled(true) { + log_print_enabled(true), + initialized(false) { _initialize(); } }; diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index cd1ca2a2c7..5a84d9e3b8 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -429,6 +429,7 @@ void register_editor_internal_calls() { mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", (void *)godot_icall_Internal_ScriptEditorEdit); mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", (void *)godot_icall_Internal_EditorNodeShowScriptScreen); mono_add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", (void *)godot_icall_Internal_GetScriptsMetadataOrNothing); + mono_add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", (void *)godot_icall_Internal_MonoWindowsInstallRoot); mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorRunPlay", (void *)godot_icall_Internal_EditorRunPlay); mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorRunStop", (void *)godot_icall_Internal_EditorRunStop); mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebugger_ReloadScripts", (void *)godot_icall_Internal_ScriptEditorDebugger_ReloadScripts); diff --git a/modules/mono/glue/Managed/Files/Rect2.cs b/modules/mono/glue/Managed/Files/Rect2.cs index f3dc9d8490..99542d0c0a 100644 --- a/modules/mono/glue/Managed/Files/Rect2.cs +++ b/modules/mono/glue/Managed/Files/Rect2.cs @@ -157,13 +157,13 @@ namespace Godot public bool Intersects(Rect2 b) { - if (_position.x > b._position.x + b._size.x) + if (_position.x >= b._position.x + b._size.x) return false; - if (_position.x + _size.x < b._position.x) + if (_position.x + _size.x <= b._position.x) return false; - if (_position.y > b._position.y + b._size.y) + if (_position.y >= b._position.y + b._size.y) return false; - if (_position.y + _size.y < b._position.y) + if (_position.y + _size.y <= b._position.y) return false; return true; diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index cd111abd4d..b2e1deca01 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -44,7 +44,6 @@ #include "core/project_settings.h" #include "../csharp_script.h" -#include "../glue/cs_glue_version.gen.h" #include "../godotsharp_dirs.h" #include "../utils/path_utils.h" #include "gd_mono_class.h" @@ -120,26 +119,29 @@ void gdmono_debug_init() { mono_debug_init(MONO_DEBUG_FORMAT_MONO); + CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8(); + +#ifdef TOOLS_ENABLED int da_port = GLOBAL_DEF("mono/debugger_agent/port", 23685); bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false); int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000); - CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8(); - -#ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() || ProjectSettings::get_singleton()->get_resource_path().empty() || Main::is_project_manager()) { if (da_args.size() == 0) return; } -#endif if (da_args.length() == 0) { da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) + ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n")) .utf8(); } +#else + if (da_args.length() == 0) + return; // Exported games don't use the project settings to setup the debugger agent +#endif // --debugger-agent=help const char *options[] = { @@ -395,23 +397,24 @@ uint64_t get_core_api_hash(); uint64_t get_editor_api_hash(); #endif uint32_t get_bindings_version(); +uint32_t get_cs_glue_version(); void register_generated_icalls(); #else uint64_t get_core_api_hash() { - CRASH_NOW(); GD_UNREACHABLE(); } #ifdef TOOLS_ENABLED uint64_t get_editor_api_hash() { - CRASH_NOW(); GD_UNREACHABLE(); } #endif uint32_t get_bindings_version() { - CRASH_NOW(); + GD_UNREACHABLE(); +} +uint32_t get_cs_glue_version() { GD_UNREACHABLE(); } @@ -687,7 +690,7 @@ bool GDMono::_load_core_api_assembly() { APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE); core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - CS_GLUE_VERSION != api_assembly_ver.cs_glue_version; + GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; if (!core_api_assembly_out_of_sync) { GDMonoUtils::update_godot_api_cache(); @@ -722,7 +725,7 @@ bool GDMono::_load_editor_api_assembly() { APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR); editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - CS_GLUE_VERSION != api_assembly_ver.cs_glue_version; + GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; } else { editor_api_assembly_out_of_sync = false; } @@ -761,7 +764,9 @@ bool GDMono::_try_load_api_assemblies() { void GDMono::_load_api_assemblies() { - if (!_try_load_api_assemblies()) { + bool api_assemblies_loaded = _try_load_api_assemblies(); + + if (!api_assemblies_loaded) { #ifdef TOOLS_ENABLED // The API assemblies are out of sync. Fine, try one more time, but this time // update them from the prebuilt assemblies directory before trying to load them. @@ -782,28 +787,30 @@ void GDMono::_load_api_assemblies() { CRASH_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain."); // 4. Try loading the updated assemblies - if (!_try_load_api_assemblies()) { - // welp... too bad - - if (_are_api_assemblies_out_of_sync()) { - if (core_api_assembly_out_of_sync) { - ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync."); - } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { - ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed."); - } - - if (editor_api_assembly_out_of_sync) { - ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync."); - } - - CRASH_NOW(); - } else { - CRASH_NOW_MSG("Failed to load one of the API assemblies."); + api_assemblies_loaded = _try_load_api_assemblies(); +#endif + } + + if (!api_assemblies_loaded) { + // welp... too bad + + if (_are_api_assemblies_out_of_sync()) { + if (core_api_assembly_out_of_sync) { + ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync."); + } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { + ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed."); + } + +#ifdef TOOLS_ENABLED + if (editor_api_assembly_out_of_sync) { + ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync."); } - } -#else - CRASH_NOW_MSG("Failed to load one of the API assemblies."); #endif + + CRASH_NOW(); + } else { + CRASH_NOW_MSG("Failed to load one of the API assemblies."); + } } } diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index 5a0d728953..7b3421fdb3 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -104,7 +104,7 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { ERR_FAIL_COND(!da); Error err = da->change_dir(p_logs_dir); - ERR_FAIL_COND(err != OK); + ERR_FAIL_COND_MSG(err != OK, "Cannot change directory to '" + p_logs_dir + "'."); ERR_FAIL_COND(da->list_dir_begin() != OK); @@ -159,7 +159,7 @@ void GDMonoLog::initialize() { log_file = FileAccess::open(log_file_path, FileAccess::WRITE); if (!log_file) { - ERR_PRINT("Mono: Cannot create log file."); + ERR_PRINTS("Mono: Cannot create log file at: " + log_file_path); } } diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index ae5a2cde81..e9efc7626d 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -165,7 +165,7 @@ Error read_all_file_utf8(const String &p_path, String &r_content) { PoolVector<uint8_t> sourcef; Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V(err != OK, err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'."); int len = f->get_len(); sourcef.resize(len + 1); @@ -208,14 +208,18 @@ String str_format(const char *p_format, ...) { #endif #if defined(MINGW_ENABLED) || defined(_MSC_VER) && _MSC_VER < 1900 -#define vsnprintf(m_buffer, m_count, m_format, m_argptr) vsnprintf_s(m_buffer, m_count, _TRUNCATE, m_format, m_argptr) +#define gd_vsnprintf(m_buffer, m_count, m_format, m_args_copy) vsnprintf_s(m_buffer, m_count, _TRUNCATE, m_format, m_args_copy) +#define gd_vscprintf(m_format, m_args_copy) _vscprintf(m_format, m_args_copy) +#else +#define gd_vsnprintf(m_buffer, m_count, m_format, m_args_copy) vsnprintf(m_buffer, m_count, m_format, m_args_copy) +#define gd_vscprintf(m_format, m_args_copy) vsnprintf(NULL, 0, p_format, m_args_copy) #endif String str_format(const char *p_format, va_list p_list) { va_list list; va_copy(list, p_list); - int len = vsnprintf(NULL, 0, p_format, list); + int len = gd_vscprintf(p_format, list); va_end(list); len += 1; // for the trailing '/0' @@ -223,7 +227,7 @@ String str_format(const char *p_format, va_list p_list) { char *buffer(memnew_arr(char, len)); va_copy(list, p_list); - vsnprintf(buffer, len, p_format, list); + gd_vsnprintf(buffer, len, p_format, list); va_end(list); String res(buffer); diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml index 4b59a380f5..07d5eb27d6 100644 --- a/modules/opensimplex/doc_classes/NoiseTexture.xml +++ b/modules/opensimplex/doc_classes/NoiseTexture.xml @@ -17,6 +17,7 @@ </member> <member name="bump_strength" type="float" setter="set_bump_strength" getter="get_bump_strength" default="8.0"> </member> + <member name="flags" type="int" setter="set_flags" getter="get_flags" override="true" default="7" /> <member name="height" type="int" setter="set_height" getter="get_height" default="512"> Height of the generated texture. </member> diff --git a/modules/opus/audio_stream_opus.cpp b/modules/opus/audio_stream_opus.cpp index d3e8d3c9bb..43b0aecbf1 100644 --- a/modules/opus/audio_stream_opus.cpp +++ b/modules/opus/audio_stream_opus.cpp @@ -119,9 +119,7 @@ Error AudioStreamPlaybackOpus::_load_stream() { Error err; f = FileAccess::open(file, FileAccess::READ, &err); - if (err) { - ERR_FAIL_COND_V(err, err); - } + ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + file + "'."); int _err = 0; @@ -185,9 +183,7 @@ Error AudioStreamPlaybackOpus::set_file(const String &p_file) { Error err; f = FileAccess::open(file, FileAccess::READ, &err); - if (err) { - ERR_FAIL_COND_V(err, err); - } + ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + file + "'."); int _err; diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp index e10f29e310..977ff064bc 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp @@ -83,7 +83,7 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ); - ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); + ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file '" + p_source_file + "'."); size_t len = f->get_len(); diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 6a1b463305..28a8b77283 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -175,7 +175,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { memdelete(file); } file = FileAccess::open(p_file, FileAccess::READ); - ERR_FAIL_COND(!file); + ERR_FAIL_COND_MSG(!file, "Cannot open file '" + p_file + "'."); #ifdef THEORA_USE_THREAD_STREAMING thread_exit = false; diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 9e3670ec35..b5b452ee47 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -217,7 +217,9 @@ </constant> <constant name="MATH_LERP_ANGLE" value="66" enum="BuiltinFunc"> </constant> - <constant name="FUNC_MAX" value="67" enum="BuiltinFunc"> + <constant name="TEXT_ORD" value="67" enum="BuiltinFunc"> + </constant> + <constant name="FUNC_MAX" value="68" enum="BuiltinFunc"> Represents the size of the [enum BuiltinFunc] enum. </constant> </constants> diff --git a/modules/visual_script/doc_classes/VisualScriptComposeArray.xml b/modules/visual_script/doc_classes/VisualScriptComposeArray.xml new file mode 100644 index 0000000000..92efbc51d1 --- /dev/null +++ b/modules/visual_script/doc_classes/VisualScriptComposeArray.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualScriptComposeArray" inherits="VisualScriptLists" category="Core" version="3.2"> + <brief_description> + A Visual Script Node used to create array from a list of items. + </brief_description> + <description> + A Visual Script Node used to compose array from the list of elements provided with custom in-graph UI hard coded in the VisualScript Editor. + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/visual_script/doc_classes/VisualScriptLists.xml b/modules/visual_script/doc_classes/VisualScriptLists.xml new file mode 100644 index 0000000000..eac0b218af --- /dev/null +++ b/modules/visual_script/doc_classes/VisualScriptLists.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualScriptLists" inherits="VisualScriptNode" category="Core" version="3.2"> + <brief_description> + A Visual Script virtual class for in-graph editable nodes. + </brief_description> + <description> + A Visual Script virtual class that defines the shape and the default behaviour of the nodes that have to be in-graph editable nodes. + </description> + <tutorials> + </tutorials> + <methods> + <method name="add_input_data_port"> + <return type="void"> + </return> + <argument index="0" name="p_type" type="int" enum="Variant.Type"> + </argument> + <argument index="1" name="p_name" type="String"> + </argument> + <argument index="2" name="p_index" type="int"> + </argument> + <description> + </description> + </method> + <method name="add_output_data_port"> + <return type="void"> + </return> + <argument index="0" name="p_type" type="int" enum="Variant.Type"> + </argument> + <argument index="1" name="p_name" type="String"> + </argument> + <argument index="2" name="p_index" type="int"> + </argument> + <description> + </description> + </method> + <method name="remove_input_data_port"> + <return type="void"> + </return> + <argument index="0" name="p_index" type="int"> + </argument> + <description> + </description> + </method> + <method name="remove_output_data_port"> + <return type="void"> + </return> + <argument index="0" name="p_index" type="int"> + </argument> + <description> + </description> + </method> + <method name="set_input_data_port_name"> + <return type="void"> + </return> + <argument index="0" name="p_index" type="int"> + </argument> + <argument index="1" name="p_name" type="String"> + </argument> + <description> + </description> + </method> + <method name="set_input_data_port_type"> + <return type="void"> + </return> + <argument index="0" name="p_index" type="int"> + </argument> + <argument index="1" name="p_type" type="int" enum="Variant.Type"> + </argument> + <description> + </description> + </method> + <method name="set_output_data_port_name"> + <return type="void"> + </return> + <argument index="0" name="p_index" type="int"> + </argument> + <argument index="1" name="p_name" type="String"> + </argument> + <description> + </description> + </method> + <method name="set_output_data_port_type"> + <return type="void"> + </return> + <argument index="0" name="p_index" type="int"> + </argument> + <argument index="1" name="p_type" type="int" enum="Variant.Type"> + </argument> + <description> + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp index 24b96223d7..49272345fe 100644 --- a/modules/visual_script/register_types.cpp +++ b/modules/visual_script/register_types.cpp @@ -56,6 +56,8 @@ void register_visual_script_types() { ClassDB::register_virtual_class<VisualScriptNode>(); ClassDB::register_class<VisualScriptFunctionState>(); ClassDB::register_class<VisualScriptFunction>(); + ClassDB::register_virtual_class<VisualScriptLists>(); + ClassDB::register_class<VisualScriptComposeArray>(); ClassDB::register_class<VisualScriptOperator>(); ClassDB::register_class<VisualScriptVariableSet>(); ClassDB::register_class<VisualScriptVariableGet>(); diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 70389b6729..0cacd0f0b5 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -578,6 +578,10 @@ void VisualScript::get_data_connection_list(const StringName &p_func, List<DataC } } +void VisualScript::set_tool_enabled(bool p_enabled) { + is_tool_script = p_enabled; +} + void VisualScript::add_variable(const StringName &p_name, const Variant &p_default_value, bool p_export) { ERR_FAIL_COND(instances.size()); @@ -894,7 +898,7 @@ ScriptInstance *VisualScript::instance_create(Object *p_this) { #ifdef TOOLS_ENABLED - if (!ScriptServer::is_scripting_enabled()) { + if (!ScriptServer::is_scripting_enabled() && !is_tool_script) { PlaceHolderScriptInstance *sins = memnew(PlaceHolderScriptInstance(VisualScriptLanguage::singleton, Ref<Script>((Script *)this), p_this)); placeholders.insert(sins); @@ -958,7 +962,7 @@ Error VisualScript::reload(bool p_keep_state) { bool VisualScript::is_tool() const { - return false; + return is_tool_script; } bool VisualScript::is_valid() const { @@ -1010,17 +1014,16 @@ void VisualScript::get_script_method_list(List<MethodInfo> *p_list) const { Ref<VisualScriptFunction> func = E->get().nodes[E->get().function_id].node; if (func.is_valid()) { - for (int i = 0; i < func->get_argument_count(); i++) { PropertyInfo arg; arg.name = func->get_argument_name(i); arg.type = func->get_argument_type(i); mi.arguments.push_back(arg); } + + p_list->push_back(mi); } } - - p_list->push_back(mi); } } @@ -1133,6 +1136,9 @@ void VisualScript::_set_data(const Dictionary &p_data) { Array funcs = d["functions"]; functions.clear(); + Vector2 last_pos = Vector2(-100 * funcs.size(), -100 * funcs.size()); // this is the center of the last fn box + Vector2 last_size = Vector2(0.0, 0.0); + for (int i = 0; i < funcs.size(); i++) { Dictionary func = funcs[i]; @@ -1145,11 +1151,42 @@ void VisualScript::_set_data(const Dictionary &p_data) { Array nodes = func["nodes"]; - for (int j = 0; j < nodes.size(); j += 3) { + if (!d.has("vs_unify") && nodes.size() > 0) { + Vector2 top_left = nodes[1]; + Vector2 bottom_right = nodes[1]; - add_node(name, nodes[j], nodes[j + 2], nodes[j + 1]); - } + for (int j = 0; j < nodes.size(); j += 3) { + Point2 pos = nodes[j + 1]; + if (pos.y > top_left.y) { + top_left.y = pos.y; + } + if (pos.y < bottom_right.y) { + bottom_right.y = pos.y; + } + if (pos.x > bottom_right.x) { + bottom_right.x = pos.x; + } + if (pos.x < top_left.x) { + top_left.x = pos.x; + } + } + + Vector2 size = Vector2(bottom_right.x - top_left.x, top_left.y - bottom_right.y); + + Vector2 offset = last_pos + (last_size / 2.0) + (size / 2.0); // dunno I might just keep it in one axis but diagonal feels better.... + + last_pos = offset; + last_size = size; + + for (int j = 0; j < nodes.size(); j += 3) { + add_node(name, nodes[j], nodes[j + 2], offset + nodes[j + 1]); // also add an additional buffer if you want to + } + } else { + for (int j = 0; j < nodes.size(); j += 3) { + add_node(name, nodes[j], nodes[j + 2], nodes[j + 1]); + } + } Array sequence_connections = func["sequence_connections"]; for (int j = 0; j < sequence_connections.size(); j += 3) { @@ -1164,6 +1201,11 @@ void VisualScript::_set_data(const Dictionary &p_data) { data_connect(name, data_connections[j + 0], data_connections[j + 1], data_connections[j + 2], data_connections[j + 3]); } } + + if (d.has("is_tool_script")) + is_tool_script = d["is_tool_script"]; + else + is_tool_script = false; } Dictionary VisualScript::_get_data() const { @@ -1245,6 +1287,8 @@ Dictionary VisualScript::_get_data() const { } d["functions"] = funcs; + d["is_tool_script"] = is_tool_script; + d["vs_unify"] = true; return d; } @@ -1319,6 +1363,10 @@ VisualScript::VisualScript() { base_type = "Object"; } +StringName VisualScript::get_default_func() const { + return StringName("f_312843592"); +} + Set<int> VisualScript::get_output_sequence_ports_connected(const String &edited_func, int from_node) { List<VisualScript::SequenceConnection> *sc = memnew(List<VisualScript::SequenceConnection>); get_sequence_connection_list(edited_func, sc); @@ -1392,6 +1440,10 @@ void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const { for (const Map<StringName, VisualScript::Function>::Element *E = script->functions.front(); E; E = E->next()) { + if (E->key() == script->get_default_func()) { + continue; + } + MethodInfo mi; mi.name = E->key(); if (E->get().function_id >= 0 && E->get().nodes.has(E->get().function_id)) { @@ -1410,8 +1462,6 @@ void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const { if (!vsf->is_sequenced()) { //assumed constant if not sequenced mi.flags |= METHOD_FLAG_CONST; } - - //vsf->Get_ for now at least it does not return.. } } @@ -1420,6 +1470,9 @@ void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const { } bool VisualScriptInstance::has_method(const StringName &p_method) const { + if (p_method == script->get_default_func()) + return false; + return script->functions.has(p_method); } @@ -1991,6 +2044,9 @@ Ref<Script> VisualScriptInstance::get_script() const { MultiplayerAPI::RPCMode VisualScriptInstance::get_rpc_mode(const StringName &p_method) const { + if (p_method == script->get_default_func()) + return MultiplayerAPI::RPC_MODE_DISABLED; + const Map<StringName, VisualScript::Function>::Element *E = script->functions.find(p_method); if (!E) { return MultiplayerAPI::RPC_MODE_DISABLED; @@ -2039,11 +2095,14 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o for (const Map<StringName, VisualScript::Variable>::Element *E = script->variables.front(); E; E = E->next()) { variables[E->key()] = E->get().default_value; - //no hacer que todo exporte, solo las que queres! } for (const Map<StringName, VisualScript::Function>::Element *E = script->functions.front(); E; E = E->next()) { + if (E->key() == script->get_default_func()) { + continue; + } + Function function; function.node = E->get().function_id; function.max_stack = 0; @@ -2080,6 +2139,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o for (const Map<int, VisualScript::Function::NodeData>::Element *F = E->get().nodes.front(); F; F = F->next()) { Ref<VisualScriptNode> node = F->get().node; + VisualScriptNodeInstance *instance = node->instance(this); //create instance ERR_FAIL_COND(!instance); diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index 098c28370d..a035f6d42d 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -239,6 +239,7 @@ private: PropertyInfo info; Variant default_value; bool _export; + // add getter & setter options here }; Map<StringName, Function> functions; @@ -247,6 +248,8 @@ private: Map<Object *, VisualScriptInstance *> instances; + bool is_tool_script; + #ifdef TOOLS_ENABLED Set<PlaceHolderScriptInstance *> placeholders; //void _update_placeholder(PlaceHolderScriptInstance *p_placeholder); @@ -265,6 +268,8 @@ protected: static void _bind_methods(); public: + // TODO: Remove it in future when breaking changes are acceptable + StringName get_default_func() const; void add_function(const StringName &p_name); bool has_function(const StringName &p_name) const; void remove_function(const StringName &p_name); @@ -273,6 +278,7 @@ public: Vector2 get_function_scroll(const StringName &p_name) const; void get_function_list(List<StringName> *r_functions) const; int get_function_node_id(const StringName &p_name) const; + void set_tool_enabled(bool p_enabled); void add_node(const StringName &p_func, int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos = Point2()); void remove_node(const StringName &p_func, int p_id); diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 8faa342bbe..29b42b1673 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -421,31 +421,42 @@ void VisualScriptEditor::_update_graph_connections() { graph->clear_connections(); - List<VisualScript::SequenceConnection> sequence_conns; - script->get_sequence_connection_list(edited_func, &sequence_conns); - - for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { + List<StringName> funcs; + script->get_function_list(&funcs); - graph->connect_node(itos(E->get().from_node), E->get().from_output, itos(E->get().to_node), 0); + if (funcs.size() <= 0) { + updating_graph = false; + return; } - List<VisualScript::DataConnection> data_conns; - script->get_data_connection_list(edited_func, &data_conns); - - for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { + for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { - VisualScript::DataConnection dc = E->get(); + List<VisualScript::SequenceConnection> sequence_conns; + script->get_sequence_connection_list(F->get(), &sequence_conns); - Ref<VisualScriptNode> from_node = script->get_node(edited_func, E->get().from_node); - Ref<VisualScriptNode> to_node = script->get_node(edited_func, E->get().to_node); + for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { - if (to_node->has_input_sequence_port()) { - dc.to_port++; + graph->connect_node(itos(E->get().from_node), E->get().from_output, itos(E->get().to_node), 0); } - dc.from_port += from_node->get_output_sequence_port_count(); + List<VisualScript::DataConnection> data_conns; + script->get_data_connection_list(F->get(), &data_conns); - graph->connect_node(itos(E->get().from_node), dc.from_port, itos(E->get().to_node), dc.to_port); + for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { + + VisualScript::DataConnection dc = E->get(); + + Ref<VisualScriptNode> from_node = script->get_node(F->get(), E->get().from_node); + Ref<VisualScriptNode> to_node = script->get_node(F->get(), E->get().to_node); + + if (to_node->has_input_sequence_port()) { + dc.to_port++; + } + + dc.from_port += from_node->get_output_sequence_port_count(); + + graph->connect_node(itos(E->get().from_node), dc.from_port, itos(E->get().to_node), dc.to_port); + } } } @@ -474,7 +485,10 @@ void VisualScriptEditor::_update_graph(int p_only_id) { } } - if (!script->has_function(edited_func)) { + List<StringName> funcs; + script->get_function_list(&funcs); + + if (funcs.size() <= 0) { graph->hide(); select_func_text->show(); updating_graph = false; @@ -516,254 +530,390 @@ void VisualScriptEditor::_update_graph(int p_only_id) { Ref<Texture> seq_port = Control::get_icon("VisualShaderPort", "EditorIcons"); - List<int> ids; - script->get_node_list(edited_func, &ids); - StringName editor_icons = "EditorIcons"; + for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { // loop through all the functions - for (List<int>::Element *E = ids.front(); E; E = E->next()) { + List<int> ids; + script->get_node_list(F->get(), &ids); + StringName editor_icons = "EditorIcons"; - if (p_only_id >= 0 && p_only_id != E->get()) - continue; + for (List<int>::Element *E = ids.front(); E; E = E->next()) { - Ref<VisualScriptNode> node = script->get_node(edited_func, E->get()); - Vector2 pos = script->get_node_position(edited_func, E->get()); + if (p_only_id >= 0 && p_only_id != E->get()) + continue; - GraphNode *gnode = memnew(GraphNode); - gnode->set_title(node->get_caption()); - gnode->set_offset(pos * EDSCALE); - if (error_line == E->get()) { - gnode->set_overlay(GraphNode::OVERLAY_POSITION); - } else if (node->is_breakpoint()) { - gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT); - } + Ref<VisualScriptNode> node = script->get_node(F->get(), E->get()); + Vector2 pos = script->get_node_position(F->get(), E->get()); - gnode->set_meta("__vnode", node); - gnode->set_name(itos(E->get())); - gnode->connect("dragged", this, "_node_moved", varray(E->get())); - gnode->connect("close_request", this, "_remove_node", varray(E->get()), CONNECT_DEFERRED); + GraphNode *gnode = memnew(GraphNode); + gnode->set_title(node->get_caption()); + gnode->set_offset(pos * EDSCALE); + if (error_line == E->get()) { + gnode->set_overlay(GraphNode::OVERLAY_POSITION); + } else if (node->is_breakpoint()) { + gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT); + } - if (E->get() != script->get_function_node_id(edited_func)) { - //function can't be erased - gnode->set_show_close_button(true); - } + gnode->set_meta("__vnode", node); + gnode->set_name(itos(E->get())); + gnode->connect("dragged", this, "_node_moved", varray(E->get())); + gnode->connect("close_request", this, "_remove_node", varray(E->get()), CONNECT_DEFERRED); - bool has_gnode_text = false; + if (E->get() != script->get_function_node_id(F->get())) { + //function can't be erased + gnode->set_show_close_button(true); + } - if (Object::cast_to<VisualScriptExpression>(node.ptr())) { - has_gnode_text = true; - LineEdit *line_edit = memnew(LineEdit); - line_edit->set_text(node->get_text()); - line_edit->set_expand_to_text_length(true); - line_edit->add_font_override("font", get_font("source", "EditorFonts")); - gnode->add_child(line_edit); - line_edit->connect("text_changed", this, "_expression_text_changed", varray(E->get())); - } else { - String text = node->get_text(); - if (!text.empty()) { + bool has_gnode_text = false; + + Ref<VisualScriptLists> nd_list = node; + bool is_vslist = nd_list.is_valid(); + if (is_vslist) { + HBoxContainer *hbnc = memnew(HBoxContainer); + if (nd_list->is_input_port_editable()) { + has_gnode_text = true; + Button *btn = memnew(Button); + btn->set_text("Add Input Port"); + hbnc->add_child(btn); + btn->connect("pressed", this, "_add_input_port", varray(E->get())); + } + if (nd_list->is_output_port_editable()) { + if (nd_list->is_input_port_editable()) + hbnc->add_spacer(); + has_gnode_text = true; + Button *btn = memnew(Button); + btn->set_text("Add Output Port"); + hbnc->add_child(btn); + btn->connect("pressed", this, "_add_output_port", varray(E->get())); + } + gnode->add_child(hbnc); + } else if (Object::cast_to<VisualScriptExpression>(node.ptr())) { has_gnode_text = true; - Label *label = memnew(Label); - label->set_text(text); - gnode->add_child(label); + LineEdit *line_edit = memnew(LineEdit); + line_edit->set_text(node->get_text()); + line_edit->set_expand_to_text_length(true); + line_edit->add_font_override("font", get_font("source", "EditorFonts")); + gnode->add_child(line_edit); + line_edit->connect("text_changed", this, "_expression_text_changed", varray(E->get())); + } else { + String text = node->get_text(); + if (!text.empty()) { + has_gnode_text = true; + Label *label = memnew(Label); + label->set_text(text); + gnode->add_child(label); + } } - } - - if (Object::cast_to<VisualScriptComment>(node.ptr())) { - Ref<VisualScriptComment> vsc = node; - gnode->set_comment(true); - gnode->set_resizable(true); - gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE); - gnode->connect("resize_request", this, "_comment_node_resized", varray(E->get())); - } - if (node_styles.has(node->get_category())) { - Ref<StyleBoxFlat> sbf = node_styles[node->get_category()]; - if (gnode->is_comment()) - sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox("comment", "GraphNode"); - - Color c = sbf->get_border_color(); - c.a = 1; - if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { - Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); - mono_color.a = 0.85; - c = mono_color; + if (Object::cast_to<VisualScriptComment>(node.ptr())) { + Ref<VisualScriptComment> vsc = node; + gnode->set_comment(true); + gnode->set_resizable(true); + gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE); + gnode->connect("resize_request", this, "_comment_node_resized", varray(E->get())); } - gnode->add_color_override("title_color", c); - c.a = 0.7; - gnode->add_color_override("close_color", c); - gnode->add_color_override("resizer_color", c); - gnode->add_style_override("frame", sbf); - } - - const Color mono_color = get_color("mono_color", "Editor"); - - int slot_idx = 0; - - bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String(); - if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) { - // IF has_gnode_text is true BUT we have no sequence ports to draw (in here), - // we still draw the disabled default ones to shift up the slots by one, - // so the slots DON'T start with the content text. - - // IF has_gnode_text is false, but we DO want to draw default sequence ports, - // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly. - if (!has_gnode_text) { - Label *dummy = memnew(Label); - dummy->set_text(" "); - gnode->add_child(dummy); + if (node_styles.has(node->get_category())) { + Ref<StyleBoxFlat> sbf = node_styles[node->get_category()]; + if (gnode->is_comment()) + sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox("comment", "GraphNode"); + + Color c = sbf->get_border_color(); + c.a = 1; + if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { + Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); + mono_color.a = 0.85; + c = mono_color; + } + gnode->add_color_override("title_color", c); + c.a = 0.7; + gnode->add_color_override("close_color", c); + gnode->add_color_override("resizer_color", c); + gnode->add_style_override("frame", sbf); } - gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port); - slot_idx++; - } - int mixed_seq_ports = 0; + const Color mono_color = get_color("mono_color", "Editor"); - if (!single_seq_output) { + int slot_idx = 0; - if (node->has_mixed_input_and_sequence_ports()) { - mixed_seq_ports = node->get_output_sequence_port_count(); - } else { - for (int i = 0; i < node->get_output_sequence_port_count(); i++) { + bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String(); + if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) { + // IF has_gnode_text is true BUT we have no sequence ports to draw (in here), + // we still draw the disabled default ones to shift up the slots by one, + // so the slots DON'T start with the content text. - Label *text2 = memnew(Label); - text2->set_text(node->get_output_sequence_port_text(i)); - text2->set_align(Label::ALIGN_RIGHT); - gnode->add_child(text2); - gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port); - slot_idx++; + // IF has_gnode_text is false, but we DO want to draw default sequence ports, + // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly. + if (!has_gnode_text) { + Label *dummy = memnew(Label); + dummy->set_text(" "); + gnode->add_child(dummy); } + gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port); + slot_idx++; } - } - for (int i = 0; i < MAX(node->get_output_value_port_count(), MAX(mixed_seq_ports, node->get_input_value_port_count())); i++) { + int mixed_seq_ports = 0; - bool left_ok = false; - Variant::Type left_type = Variant::NIL; - String left_name; + if (!single_seq_output) { - if (i < node->get_input_value_port_count()) { - PropertyInfo pi = node->get_input_value_port_info(i); - left_ok = true; - left_type = pi.type; - left_name = pi.name; + if (node->has_mixed_input_and_sequence_ports()) { + mixed_seq_ports = node->get_output_sequence_port_count(); + } else { + for (int i = 0; i < node->get_output_sequence_port_count(); i++) { + + Label *text2 = memnew(Label); + text2->set_text(node->get_output_sequence_port_text(i)); + text2->set_align(Label::ALIGN_RIGHT); + gnode->add_child(text2); + gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port); + slot_idx++; + } + } } - bool right_ok = false; - Variant::Type right_type = Variant::NIL; - String right_name; + for (int i = 0; i < MAX(node->get_output_value_port_count(), MAX(mixed_seq_ports, node->get_input_value_port_count())); i++) { - if (i >= mixed_seq_ports && i < node->get_output_value_port_count() + mixed_seq_ports) { - PropertyInfo pi = node->get_output_value_port_info(i - mixed_seq_ports); - right_ok = true; - right_type = pi.type; - right_name = pi.name; - } + bool left_ok = false; + Variant::Type left_type = Variant::NIL; + String left_name; - HBoxContainer *hbc = memnew(HBoxContainer); + if (i < node->get_input_value_port_count()) { + PropertyInfo pi = node->get_input_value_port_info(i); + left_ok = true; + left_type = pi.type; + left_name = pi.name; + } - if (left_ok) { + bool right_ok = false; + Variant::Type right_type = Variant::NIL; + String right_name; - Ref<Texture> t; - if (left_type >= 0 && left_type < Variant::VARIANT_MAX) { - t = type_icons[left_type]; - } - if (t.is_valid()) { - TextureRect *tf = memnew(TextureRect); - tf->set_texture(t); - tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); - hbc->add_child(tf); + if (i >= mixed_seq_ports && i < node->get_output_value_port_count() + mixed_seq_ports) { + PropertyInfo pi = node->get_output_value_port_info(i - mixed_seq_ports); + right_ok = true; + right_type = pi.type; + right_name = pi.name; } + VBoxContainer *vbc = memnew(VBoxContainer); + HBoxContainer *hbc = memnew(HBoxContainer); + HBoxContainer *hbc2 = memnew(HBoxContainer); + vbc->add_child(hbc); + vbc->add_child(hbc2); + if (left_ok) { + + Ref<Texture> t; + if (left_type >= 0 && left_type < Variant::VARIANT_MAX) { + t = type_icons[left_type]; + } + if (t.is_valid()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(t); + tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + hbc->add_child(tf); + } - hbc->add_child(memnew(Label(left_name))); + if (is_vslist) { + if (nd_list->is_input_port_name_editable()) { + LineEdit *name_box = memnew(LineEdit); + hbc->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(left_name); + name_box->set_expand_to_text_length(true); + name_box->connect("resized", this, "_update_node_size", varray(E->get())); + name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, E->get(), i, true)); + } else { + hbc->add_child(memnew(Label(left_name))); + } - if (left_type != Variant::NIL && !script->is_input_value_port_connected(edited_func, E->get(), i)) { + if (nd_list->is_input_port_type_editable()) { + OptionButton *opbtn = memnew(OptionButton); + for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) { + opbtn->add_item(Variant::get_type_name(Variant::Type(j))); + } + opbtn->select(left_type); + opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + hbc->add_child(opbtn); + opbtn->connect("item_selected", this, "_change_port_type", varray(E->get(), i, true), CONNECT_DEFERRED); + } - PropertyInfo pi = node->get_input_value_port_info(i); - Button *button = memnew(Button); - Variant value = node->get_default_input_value(i); - if (value.get_type() != left_type) { - //different type? for now convert - //not the same, reconvert - Variant::CallError ce; - const Variant *existingp = &value; - value = Variant::construct(left_type, &existingp, 1, ce, false); + Button *rmbtn = memnew(Button); + rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + hbc->add_child(rmbtn); + rmbtn->connect("pressed", this, "_remove_input_port", varray(E->get(), i), CONNECT_DEFERRED); + } else { + hbc->add_child(memnew(Label(left_name))); } - if (left_type == Variant::COLOR) { - button->set_custom_minimum_size(Size2(30, 0) * EDSCALE); - button->connect("draw", this, "_draw_color_over_button", varray(button, value)); - } else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) { + if (left_type != Variant::NIL && !script->is_input_value_port_connected(F->get(), E->get(), i)) { + + PropertyInfo pi = node->get_input_value_port_info(i); + Button *button = memnew(Button); + Variant value = node->get_default_input_value(i); + if (value.get_type() != left_type) { + //different type? for now convert + //not the same, reconvert + Variant::CallError ce; + const Variant *existingp = &value; + value = Variant::construct(left_type, &existingp, 1, ce, false); + } - Ref<Resource> res = value; - Array arr; - arr.push_back(button->get_instance_id()); - arr.push_back(String(value)); - EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr); + if (left_type == Variant::COLOR) { + button->set_custom_minimum_size(Size2(30, 0) * EDSCALE); + button->connect("draw", this, "_draw_color_over_button", varray(button, value)); + } else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) { - } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) { + Ref<Resource> res = value; + Array arr; + arr.push_back(button->get_instance_id()); + arr.push_back(String(value)); + EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr); - button->set_text(pi.hint_string.get_slice(",", value)); - } else { + } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) { - button->set_text(value); + button->set_text(pi.hint_string.get_slice(",", value)); + } else { + + button->set_text(value); + } + button->connect("pressed", this, "_default_value_edited", varray(button, E->get(), i)); + hbc2->add_child(button); } - button->connect("pressed", this, "_default_value_edited", varray(button, E->get(), i)); - hbc->add_child(button); + } else { + Control *c = memnew(Control); + c->set_custom_minimum_size(Size2(10, 0) * EDSCALE); + hbc->add_child(c); } - } else { - Control *c = memnew(Control); - c->set_custom_minimum_size(Size2(10, 0) * EDSCALE); - hbc->add_child(c); - } - hbc->add_spacer(); + hbc->add_spacer(); + hbc2->add_spacer(); - if (i < mixed_seq_ports) { + if (i < mixed_seq_ports) { - Label *text2 = memnew(Label); - text2->set_text(node->get_output_sequence_port_text(i)); - text2->set_align(Label::ALIGN_RIGHT); - hbc->add_child(text2); - } + Label *text2 = memnew(Label); + text2->set_text(node->get_output_sequence_port_text(i)); + text2->set_align(Label::ALIGN_RIGHT); + hbc->add_child(text2); + } - if (right_ok) { + if (right_ok) { + + if (is_vslist) { + Button *rmbtn = memnew(Button); + rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + hbc->add_child(rmbtn); + rmbtn->connect("pressed", this, "_remove_output_port", varray(E->get(), i), CONNECT_DEFERRED); + + if (nd_list->is_output_port_type_editable()) { + OptionButton *opbtn = memnew(OptionButton); + for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) { + opbtn->add_item(Variant::get_type_name(Variant::Type(j))); + } + opbtn->select(right_type); + opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + hbc->add_child(opbtn); + opbtn->connect("item_selected", this, "_change_port_type", varray(E->get(), i, false), CONNECT_DEFERRED); + } - hbc->add_child(memnew(Label(right_name))); + if (nd_list->is_output_port_name_editable()) { + LineEdit *name_box = memnew(LineEdit); + hbc->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(right_name); + name_box->set_expand_to_text_length(true); + name_box->connect("resized", this, "_update_node_size", varray(E->get())); + name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, E->get(), i, false)); + } else { + hbc->add_child(memnew(Label(right_name))); + } + } else { + hbc->add_child(memnew(Label(right_name))); + } - Ref<Texture> t; - if (right_type >= 0 && right_type < Variant::VARIANT_MAX) { - t = type_icons[right_type]; + Ref<Texture> t; + if (right_type >= 0 && right_type < Variant::VARIANT_MAX) { + t = type_icons[right_type]; + } + if (t.is_valid()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(t); + tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + hbc->add_child(tf); + } } - if (t.is_valid()) { - TextureRect *tf = memnew(TextureRect); - tf->set_texture(t); - tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); - hbc->add_child(tf); + + gnode->add_child(vbc); + + bool dark_theme = get_constant("dark_theme", "Editor"); + if (i < mixed_seq_ports) { + gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), true, TYPE_SEQUENCE, mono_color, Ref<Texture>(), seq_port); + } else { + gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), right_ok, right_type, _color_from_type(right_type, dark_theme)); } + + slot_idx++; } - gnode->add_child(hbc); + graph->add_child(gnode); - bool dark_theme = get_constant("dark_theme", "Editor"); - if (i < mixed_seq_ports) { - gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), true, TYPE_SEQUENCE, mono_color, Ref<Texture>(), seq_port); - } else { - gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), right_ok, right_type, _color_from_type(right_type, dark_theme)); + if (gnode->is_comment()) { + graph->move_child(gnode, 0); } - - slot_idx++; } + } + _update_graph_connections(); + // use default_func instead of default_func for now I think that should be good stop gap solution to ensure not breaking anything + graph->call_deferred("set_scroll_ofs", script->get_function_scroll(default_func) * EDSCALE); + updating_graph = false; +} - graph->add_child(gnode); +void VisualScriptEditor::_change_port_type(int p_select, int p_id, int p_port, bool is_input) { - if (gnode->is_comment()) { - graph->move_child(gnode, 0); - } + StringName func = _get_function_of_node(p_id); + + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) + return; + + undo_redo->create_action("Change Port Type"); + if (is_input) { + undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_type", p_port, Variant::Type(p_select)); + undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_type", p_port, vsn->get_input_value_port_info(p_port).type); + } else { + undo_redo->add_do_method(vsn.ptr(), "set_output_data_port_type", p_port, Variant::Type(p_select)); + undo_redo->add_undo_method(vsn.ptr(), "set_output_data_port_type", p_port, vsn->get_output_value_port_info(p_port).type); } + undo_redo->commit_action(); +} - _update_graph_connections(); - graph->call_deferred("set_scroll_ofs", script->get_function_scroll(edited_func) * EDSCALE); //may need to adapt a bit, let it do so - updating_graph = false; +void VisualScriptEditor::_update_node_size(int p_id) { + + Node *node = graph->get_node(itos(p_id)); + if (Object::cast_to<Control>(node)) + Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); //shrink if text is smaller +} +void VisualScriptEditor::_port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input) { + StringName func = _get_function_of_node(p_id); + + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) + return; + + String text; + + if (Object::cast_to<LineEdit>(p_name_box)) + text = Object::cast_to<LineEdit>(p_name_box)->get_text(); + else + return; + + undo_redo->create_action("Change Port Name"); + if (is_input) { + undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_name", p_port, text); + undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_name", p_port, vsn->get_input_value_port_info(p_port).name); + } else { + undo_redo->add_do_method(vsn.ptr(), "set_output_data_port_name", p_port, text); + undo_redo->add_undo_method(vsn.ptr(), "set_output_data_port_name", p_port, vsn->get_output_value_port_info(p_port).name); + } + undo_redo->commit_action(); } void VisualScriptEditor::_update_members() { @@ -784,11 +934,16 @@ void VisualScriptEditor::_update_members() { List<StringName> func_names; script->get_function_list(&func_names); for (List<StringName>::Element *E = func_names.front(); E; E = E->next()) { + + if (E->get() == default_func) { + continue; + } + TreeItem *ti = members->create_item(functions); ti->set_text(0, E->get()); ti->set_selectable(0, true); - ti->set_editable(0, true); ti->set_metadata(0, E->get()); + ti->add_button(0, Control::get_icon("Edit", "EditorIcons"), 0); if (selected == E->get()) ti->select(0); } @@ -888,15 +1043,15 @@ void VisualScriptEditor::_member_selected() { if (ti->get_parent() == members->get_root()->get_children()) { - if (edited_func != selected) { - - revert_on_drag = edited_func; - edited_func = selected; - _update_members(); - _update_graph(); +#ifdef OSX_ENABLED + bool held_ctrl = Input::get_singleton()->is_key_pressed(KEY_META); +#else + bool held_ctrl = Input::get_singleton()->is_key_pressed(KEY_CONTROL); +#endif + if (held_ctrl) { + ERR_FAIL_COND(!script->has_function(selected)); + _center_on_node(selected, script->get_function_node_id(selected)); } - - return; //or crash because it will become invalid } } @@ -936,9 +1091,6 @@ void VisualScriptEditor::_member_edited() { if (ti->get_parent() == root->get_children()) { - if (edited_func == selected) { - edited_func = new_name; - } selected = new_name; int node_id = script->get_function_node_id(name); @@ -950,10 +1102,27 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name); undo_redo->add_undo_method(script.ptr(), "rename_function", new_name, name); if (func.is_valid()) { - undo_redo->add_do_method(func.ptr(), "set_name", new_name); undo_redo->add_undo_method(func.ptr(), "set_name", name); } + + // also fix all function calls + List<StringName> flst; + script->get_function_list(&flst); + for (List<StringName>::Element *E = flst.front(); E; E = E->next()) { + List<int> lst; + script->get_node_list(E->get(), &lst); + for (List<int>::Element *F = lst.front(); F; F = F->next()) { + Ref<VisualScriptFunctionCall> fncall = script->get_node(E->get(), F->get()); + if (!fncall.is_valid()) + continue; + if (fncall->get_function() == name) { + undo_redo->add_do_method(fncall.ptr(), "set_function", new_name); + undo_redo->add_undo_method(fncall.ptr(), "set_function", name); + } + } + } + undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); undo_redo->add_do_method(this, "_update_graph"); @@ -962,8 +1131,6 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); - // _update_graph(); - return; //or crash because it will become invalid } @@ -998,6 +1165,116 @@ void VisualScriptEditor::_member_edited() { } } +void VisualScriptEditor::_create_function_dialog() { + function_create_dialog->popup(); + function_create_dialog->set_position(graph->get_global_position() + Vector2(55, 80)); + func_name_box->set_text(""); + func_name_box->grab_focus(); + for (int i = 0; i < func_input_vbox->get_child_count(); i++) { + Node *nd = func_input_vbox->get_child(i); + func_input_vbox->remove_child(nd); + nd->queue_delete(); + } +} + +void VisualScriptEditor::_create_function() { + String name = _validate_name((func_name_box->get_text() == "") ? "new_func" : func_name_box->get_text()); + selected = name; + Vector2 ofs = _get_available_pos(); + + Ref<VisualScriptFunction> func_node; + func_node.instance(); + func_node->set_name(name); + + for (int i = 0; i < func_input_vbox->get_child_count(); i++) { + OptionButton *opbtn = Object::cast_to<OptionButton>(func_input_vbox->get_child(i)->get_child(3)); + LineEdit *lne = Object::cast_to<LineEdit>(func_input_vbox->get_child(i)->get_child(1)); + if (!opbtn || !lne) + continue; + Variant::Type arg_type = Variant::Type(opbtn->get_selected()); + String arg_name = lne->get_text(); + func_node->add_argument(arg_type, arg_name); + } + + undo_redo->create_action(TTR("Add Function")); + undo_redo->add_do_method(script.ptr(), "add_function", name); + undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_function", name); + 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->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); + undo_redo->commit_action(); + + _update_graph(); +} + +void VisualScriptEditor::_add_node_dialog() { + _generic_search(script->get_instance_base_type(), graph->get_global_position() + Vector2(55, 80), true); +} + +void VisualScriptEditor::_add_func_input() { + HBoxContainer *hbox = memnew(HBoxContainer); + hbox->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *name_label = memnew(Label); + name_label->set_text(TTR("Name:")); + hbox->add_child(name_label); + + LineEdit *name_box = memnew(LineEdit); + name_box->set_h_size_flags(SIZE_EXPAND_FILL); + name_box->set_text("input"); + name_box->connect("focus_entered", this, "_deselect_input_names"); + hbox->add_child(name_box); + + Label *type_label = memnew(Label); + type_label->set_text(TTR("Type:")); + hbox->add_child(type_label); + + OptionButton *type_box = memnew(OptionButton); + type_box->set_custom_minimum_size(Size2(120 * EDSCALE, 0)); + for (int i = Variant::NIL; i < Variant::VARIANT_MAX; i++) + type_box->add_item(Variant::get_type_name(Variant::Type(i))); + type_box->select(1); + hbox->add_child(type_box); + + Button *delete_button = memnew(Button); + delete_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + delete_button->set_tooltip(vformat(TTR("Delete input port"))); + hbox->add_child(delete_button); + + for (int i = 0; i < func_input_vbox->get_child_count(); i++) { + LineEdit *line_edit = (LineEdit *)func_input_vbox->get_child(i)->get_child(1); + line_edit->deselect(); + } + + func_input_vbox->add_child(hbox); + hbox->set_meta("id", hbox->get_position_in_parent()); + function_create_dialog->set_size(Size2(-1, -1)); + + delete_button->connect("pressed", this, "_remove_func_input", varray(hbox)); + + name_box->select_all(); + name_box->grab_focus(); +} + +void VisualScriptEditor::_remove_func_input(Node *p_node) { + func_input_vbox->remove_child(p_node); + p_node->queue_delete(); + function_create_dialog->set_size(Size2(-1, -1)); +} + +void VisualScriptEditor::_deselect_input_names() { + int cn = func_input_vbox->get_child_count(); + for (int i = 0; i < cn; i++) { + LineEdit *lne = Object::cast_to<LineEdit>(func_input_vbox->get_child(i)->get_child(1)); + if (lne) + lne->deselect(); + } +} + void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button) { TreeItem *ti = Object::cast_to<TreeItem>(p_item); @@ -1010,7 +1287,6 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt //add function, this one uses menu if (p_button == 1) { - new_virtual_method_select->select_method_from_base_type(script->get_instance_base_type(), String(), true); return; @@ -1018,7 +1294,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt String name = _validate_name("new_function"); selected = name; - edited_func = selected; + Vector2 ofs = _get_available_pos(); Ref<VisualScriptFunction> func_node; func_node.instance(); @@ -1026,7 +1302,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->create_action(TTR("Add Function")); undo_redo->add_do_method(script.ptr(), "add_function", name); - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node); + undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs); undo_redo->add_undo_method(script.ptr(), "remove_function", name); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); @@ -1073,135 +1349,191 @@ 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()) { + selected = ti->get_metadata(0); + function_name_edit->set_position(Input::get_singleton()->get_mouse_position() - Vector2(60, -10)); + function_name_edit->popup(); } } -void VisualScriptEditor::_expression_text_changed(const String &p_text, int p_id) { +void VisualScriptEditor::_add_input_port(int p_id) { - Ref<VisualScriptExpression> vse = script->get_node(edited_func, p_id); - if (!vse.is_valid()) + StringName func = _get_function_of_node(p_id); + + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) return; updating_graph = true; - undo_redo->create_action(TTR("Change Expression"), UndoRedo::MERGE_ENDS); - undo_redo->add_do_property(vse.ptr(), "expression", p_text); - undo_redo->add_undo_property(vse.ptr(), "expression", vse->get("expression")); + undo_redo->create_action(TTR("Add Input Port"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(vsn.ptr(), "add_input_data_port", Variant::NIL, "arg", -1); undo_redo->add_do_method(this, "_update_graph", p_id); + + undo_redo->add_undo_method(vsn.ptr(), "remove_input_data_port", vsn->get_input_value_port_count()); undo_redo->add_undo_method(this, "_update_graph", p_id); + + updating_graph = false; + undo_redo->commit_action(); +} - Node *node = graph->get_node(itos(p_id)); - if (Object::cast_to<Control>(node)) - Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); //shrink if text is smaller +void VisualScriptEditor::_add_output_port(int p_id) { + + StringName func = _get_function_of_node(p_id); + + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) + return; + + updating_graph = true; + + undo_redo->create_action(TTR("Add Output Port"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(vsn.ptr(), "add_output_data_port", Variant::NIL, "arg", -1); + undo_redo->add_do_method(this, "_update_graph", p_id); + + undo_redo->add_undo_method(vsn.ptr(), "remove_output_data_port", vsn->get_output_value_port_count()); + undo_redo->add_undo_method(this, "_update_graph", p_id); updating_graph = false; + + undo_redo->commit_action(); } -void VisualScriptEditor::_available_node_doubleclicked() { +void VisualScriptEditor::_remove_input_port(int p_id, int p_port) { + + StringName func = _get_function_of_node(p_id); - if (edited_func == String()) + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) return; - TreeItem *item = nodes->get_selected(); + updating_graph = true; - if (!item) - return; + undo_redo->create_action(TTR("Remove Input Port"), UndoRedo::MERGE_ENDS); + + int conn_from = -1, conn_port = -1; + script->get_input_value_port_connection_source(func, p_id, p_port, &conn_from, &conn_port); + + if (conn_from != -1) + undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_id, p_port); + + undo_redo->add_do_method(vsn.ptr(), "remove_input_data_port", p_port); + undo_redo->add_do_method(this, "_update_graph", p_id); + + if (conn_from != -1) + undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_id, p_port); + + undo_redo->add_undo_method(vsn.ptr(), "add_input_data_port", vsn->get_input_value_port_info(p_port).type, vsn->get_input_value_port_info(p_port).name, p_port); + undo_redo->add_undo_method(this, "_update_graph", p_id); + + updating_graph = false; + + undo_redo->commit_action(); +} - String which = item->get_metadata(0); - if (which == String()) +void VisualScriptEditor::_remove_output_port(int p_id, int p_port) { + + StringName func = _get_function_of_node(p_id); + + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) return; - Vector2 ofs = graph->get_scroll_ofs() + graph->get_size() * 0.5; - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } + updating_graph = true; - ofs /= EDSCALE; + undo_redo->create_action(TTR("Remove Output Port"), UndoRedo::MERGE_ENDS); - while (true) { - bool exists = false; - List<int> existing; - script->get_node_list(edited_func, &existing); - for (List<int>::Element *E = existing.front(); E; E = E->next()) { - Point2 pos = script->get_node_position(edited_func, E->get()); - if (pos.distance_to(ofs) < 15) { - ofs += Vector2(graph->get_snap(), graph->get_snap()); - exists = true; - break; - } + List<VisualScript::DataConnection> data_connections; + script->get_data_connection_list(func, &data_connections); + + HashMap<int, Set<int> > conn_map; + for (const List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { + if (E->get().from_node == p_id && E->get().from_port == p_port) { + // push into the connections map + if (!conn_map.has(E->get().to_node)) + conn_map.set(E->get().to_node, Set<int>()); + conn_map[E->get().to_node].insert(E->get().to_port); } + } - if (exists) - continue; - break; + undo_redo->add_do_method(vsn.ptr(), "remove_output_data_port", p_port); + undo_redo->add_do_method(this, "_update_graph", p_id); + + List<int> keys; + conn_map.get_key_list(&keys); + for (const List<int>::Element *E = keys.front(); E; E = E->next()) { + for (const Set<int>::Element *F = conn_map[E->get()].front(); F; F = F->next()) { + undo_redo->add_undo_method(script.ptr(), "data_connect", func, p_id, p_port, E->get(), F->get()); + } } - Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(which); - int new_id = script->get_available_id(); + undo_redo->add_undo_method(vsn.ptr(), "add_output_data_port", vsn->get_output_value_port_info(p_port).type, vsn->get_output_value_port_info(p_port).name, p_port); + undo_redo->add_undo_method(this, "_update_graph", p_id); - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + updating_graph = false; - Node *node = graph->get_node(itos(new_id)); - if (node) { - graph->set_selected(node); - _node_selected(node); - } + undo_redo->commit_action(); } -void VisualScriptEditor::_update_available_nodes() { +void VisualScriptEditor::_expression_text_changed(const String &p_text, int p_id) { - nodes->clear(); + StringName func = _get_function_of_node(p_id); - TreeItem *root = nodes->create_item(); + Ref<VisualScriptExpression> vse = script->get_node(func, p_id); + if (!vse.is_valid()) + return; - Map<String, TreeItem *> path_cache; + updating_graph = true; - String filter = node_filter->get_text(); + undo_redo->create_action(TTR("Change Expression"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_property(vse.ptr(), "expression", p_text); + undo_redo->add_undo_property(vse.ptr(), "expression", vse->get("expression")); + undo_redo->add_do_method(this, "_update_graph", p_id); + undo_redo->add_undo_method(this, "_update_graph", p_id); + undo_redo->commit_action(); - List<String> fnodes; - VisualScriptLanguage::singleton->get_registered_node_names(&fnodes); + Node *node = graph->get_node(itos(p_id)); + if (Object::cast_to<Control>(node)) + Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); //shrink if text is smaller - for (List<String>::Element *E = fnodes.front(); E; E = E->next()) { + updating_graph = false; +} - Vector<String> path = E->get().split("/"); +Vector2 VisualScriptEditor::_get_available_pos(bool centered, Vector2 ofs) const { + if (centered) + ofs = graph->get_scroll_ofs() + graph->get_size() * 0.5; - if (filter != String() && path.size() && path[path.size() - 1].findn(filter) == -1) - continue; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } + + ofs /= EDSCALE; - String sp; - TreeItem *parent = root; - - for (int i = 0; i < path.size() - 1; i++) { - - if (i > 0) - sp += ","; - sp += path[i]; - if (!path_cache.has(sp)) { - TreeItem *pathn = nodes->create_item(parent); - pathn->set_selectable(0, false); - pathn->set_text(0, path[i].capitalize()); - path_cache[sp] = pathn; - parent = pathn; - if (filter == String()) { - pathn->set_collapsed(true); //should remember state + while (true) { + bool exists = false; + List<StringName> all_fn; + script->get_function_list(&all_fn); + for (List<StringName>::Element *F = all_fn.front(); F; F = F->next()) { + StringName curr_fn = F->get(); + List<int> existing; + script->get_node_list(curr_fn, &existing); + for (List<int>::Element *E = existing.front(); E; E = E->next()) { + Point2 pos = script->get_node_position(curr_fn, E->get()); + if (pos.distance_to(ofs) < 50) { + ofs += Vector2(graph->get_snap(), graph->get_snap()); + exists = true; + break; } - } else { - parent = path_cache[sp]; } } - - TreeItem *item = nodes->create_item(parent); - item->set_text(0, path[path.size() - 1].capitalize()); - item->set_selectable(0, true); - item->set_metadata(0, E->get()); + if (exists) + continue; + break; } + + return ofs; } String VisualScriptEditor::_validate_name(const String &p_name) const { @@ -1227,6 +1559,8 @@ String VisualScriptEditor::_validate_name(const String &p_name) const { void VisualScriptEditor::_on_nodes_delete() { + // delete all the selected nodes + List<int> to_erase; for (int i = 0; i < graph->get_child_count(); i++) { @@ -1245,26 +1579,30 @@ void VisualScriptEditor::_on_nodes_delete() { for (List<int>::Element *F = to_erase.front(); F; F = F->next()) { - undo_redo->add_do_method(script.ptr(), "remove_node", edited_func, F->get()); - undo_redo->add_undo_method(script.ptr(), "add_node", edited_func, F->get(), script->get_node(edited_func, F->get()), script->get_node_position(edited_func, F->get())); + int cr_node = F->get(); + + StringName func = _get_function_of_node(cr_node); + + undo_redo->add_do_method(script.ptr(), "remove_node", func, cr_node); + undo_redo->add_undo_method(script.ptr(), "add_node", func, cr_node, script->get_node(func, cr_node), script->get_node_position(func, cr_node)); List<VisualScript::SequenceConnection> sequence_conns; - script->get_sequence_connection_list(edited_func, &sequence_conns); + script->get_sequence_connection_list(func, &sequence_conns); for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { - if (E->get().from_node == F->get() || E->get().to_node == F->get()) { - undo_redo->add_undo_method(script.ptr(), "sequence_connect", edited_func, E->get().from_node, E->get().from_output, E->get().to_node); + if (E->get().from_node == cr_node || E->get().to_node == cr_node) { + undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, E->get().from_node, E->get().from_output, E->get().to_node); } } List<VisualScript::DataConnection> data_conns; - script->get_data_connection_list(edited_func, &data_conns); + script->get_data_connection_list(func, &data_conns); for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { if (E->get().from_node == F->get() || E->get().to_node == F->get()) { - undo_redo->add_undo_method(script.ptr(), "data_connect", edited_func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); } } } @@ -1276,13 +1614,16 @@ void VisualScriptEditor::_on_nodes_delete() { void VisualScriptEditor::_on_nodes_duplicate() { - List<int> to_duplicate; + Set<int> to_duplicate; + List<StringName> funcs; for (int i = 0; i < graph->get_child_count(); i++) { GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); if (gn) { if (gn->is_selected() && gn->is_close_button_visible()) { - to_duplicate.push_back(gn->get_name().operator String().to_int()); + int id = gn->get_name().operator String().to_int(); + to_duplicate.insert(id); + funcs.push_back(_get_function_of_node(id)); } } } @@ -1294,18 +1635,42 @@ void VisualScriptEditor::_on_nodes_duplicate() { int idc = script->get_available_id() + 1; Set<int> to_select; + HashMap<int, int> remap; - for (List<int>::Element *F = to_duplicate.front(); F; F = F->next()) { + for (Set<int>::Element *F = to_duplicate.front(); F; F = F->next()) { - Ref<VisualScriptNode> node = script->get_node(edited_func, F->get()); + // duplicate from the specifc function but place it into the default func as it would lack the connections + StringName func = _get_function_of_node(F->get()); + Ref<VisualScriptNode> node = script->get_node(func, F->get()); Ref<VisualScriptNode> dupe = node->duplicate(true); int new_id = idc++; + remap.set(F->get(), new_id); + to_select.insert(new_id); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, dupe, script->get_node_position(edited_func, F->get()) + Vector2(20, 20)); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, dupe, script->get_node_position(func, F->get()) + Vector2(20, 20)); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); + } + + for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { + List<VisualScript::SequenceConnection> seqs; + script->get_sequence_connection_list(F->get(), &seqs); + for (List<VisualScript::SequenceConnection>::Element *E = seqs.front(); E; E = E->next()) { + if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", default_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); + } + } + + List<VisualScript::DataConnection> data; + script->get_data_connection_list(F->get(), &data); + for (List<VisualScript::DataConnection>::Element *E = data.front(); E; E = E->next()) { + if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) { + undo_redo->add_do_method(script.ptr(), "data_connect", default_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); + } + } } + undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -1320,22 +1685,45 @@ void VisualScriptEditor::_on_nodes_duplicate() { } if (to_select.size()) { - EditorNode::get_singleton()->push_item(script->get_node(edited_func, to_select.front()->get()).ptr()); + EditorNode::get_singleton()->push_item(script->get_node(default_func, to_select.front()->get()).ptr()); } } -void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) { +void VisualScriptEditor::_generic_search(String p_base_type, Vector2 pos, bool node_centered) { + if (node_centered) + port_action_pos = graph->get_size() / 2.0f; + else + port_action_pos = graph->get_viewport()->get_mouse_position() - graph->get_global_position(); + + new_connect_node_select->select_from_visual_script(p_base_type, false, false); // neither connecting nor reset text - Ref<InputEventMouseButton> mb = p_event; + // ensure that the dialog fits inside the graph + Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size(); + pos.x = pos.x > bounds.x ? bounds.x : pos.x; + pos.y = pos.y > bounds.y ? bounds.y : pos.y; - if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - revert_on_drag = String(); //so we can still drag functions + if (pos != Vector2()) + new_connect_node_select->set_position(pos); +} + +void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) { + // GUI input for VS Editor Plugin + Ref<InputEventMouseButton> key = p_event; + + if (key.is_valid() && !key->is_pressed()) { + mouse_up_position = Input::get_singleton()->get_mouse_position(); } } -void VisualScriptEditor::_generic_search(String p_base_type) { - port_action_pos = graph->get_viewport()->get_mouse_position() - graph->get_global_position(); - new_connect_node_select->select_from_visual_script(p_base_type, false); +void VisualScriptEditor::_graph_gui_input(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> key = p_event; + + if (key.is_valid() && key->is_pressed() && key->get_button_mask() == BUTTON_RIGHT) { + saved_position = graph->get_local_mouse_position(); + + Point2 gpos = Input::get_singleton()->get_mouse_position(); + _generic_search(script->get_instance_base_type(), gpos); + } } void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) { @@ -1365,28 +1753,83 @@ void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) { } } } + + Ref<InputEventMouseButton> btn = p_event; + if (btn.is_valid() && btn->is_doubleclick()) { + TreeItem *ti = members->get_selected(); + ERR_FAIL_COND(!ti); + if (ti->get_parent() == members->get_root()->get_children()) // to check if it's a function + _center_on_node(ti->get_metadata(0), script->get_function_node_id(ti->get_metadata(0))); + } } -Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { +void VisualScriptEditor::_rename_function(const String &name, const String &new_name) { - if (p_from == nodes) { + if (!new_name.is_valid_identifier()) { - TreeItem *it = nodes->get_item_at_position(p_point); - if (!it) - return Variant(); - String type = it->get_metadata(0); - if (type == String()) - return Variant(); + EditorNode::get_singleton()->show_warning(TTR("Name is not a valid identifier:") + " " + new_name); + return; + } - Dictionary dd; - dd["type"] = "visual_script_node_drag"; - dd["node_type"] = type; + if (script->has_function(new_name) || script->has_variable(new_name) || script->has_custom_signal(new_name)) { - Label *label = memnew(Label); - label->set_text(it->get_text(0)); - set_drag_preview(label); - return dd; + EditorNode::get_singleton()->show_warning(TTR("Name already in use by another func/var/signal:") + " " + new_name); + return; + } + + int node_id = script->get_function_node_id(name); + Ref<VisualScriptFunction> func; + if (script->has_node(name, node_id)) { + func = script->get_node(name, node_id); + } + undo_redo->create_action(TTR("Rename Function")); + undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name); + undo_redo->add_undo_method(script.ptr(), "rename_function", new_name, name); + if (func.is_valid()) { + undo_redo->add_do_method(func.ptr(), "set_name", new_name); + undo_redo->add_undo_method(func.ptr(), "set_name", name); + } + + // also fix all function calls + List<StringName> flst; + script->get_function_list(&flst); + for (List<StringName>::Element *E = flst.front(); E; E = E->next()) { + List<int> lst; + script->get_node_list(E->get(), &lst); + for (List<int>::Element *F = lst.front(); F; F = F->next()) { + Ref<VisualScriptFunctionCall> fncall = script->get_node(E->get(), F->get()); + if (!fncall.is_valid()) + continue; + if (fncall->get_function() == name) { + undo_redo->add_do_method(fncall.ptr(), "set_function", new_name); + undo_redo->add_undo_method(fncall.ptr(), "set_function", name); + } + } + } + + 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->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); + undo_redo->commit_action(); +} + +void VisualScriptEditor::_fn_name_box_input(const Ref<InputEvent> &p_event) { + + if (!function_name_edit->is_visible()) + return; + + Ref<InputEventKey> key = p_event; + if (key.is_valid() && key->is_pressed() && key->get_scancode() == KEY_ENTER) { + function_name_edit->hide(); + _rename_function(selected, function_name_box->get_text()); + function_name_box->clear(); } +} + +Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { if (p_from == members) { @@ -1406,11 +1849,6 @@ Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_f dd["type"] = "visual_script_function_drag"; dd["function"] = type; - if (revert_on_drag != String()) { - edited_func = revert_on_drag; //revert so function does not change - revert_on_drag = String(); - _update_graph(); - } } else if (it->get_parent() == root->get_children()->get_next()) { dd["type"] = "visual_script_variable_drag"; @@ -1530,15 +1968,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da ofs /= EDSCALE; - Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(d["node_type"]); - int new_id = script->get_available_id(); - - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + int new_id = _create_new_node_from_name(d["node_type"], ofs, default_func); Node *node = graph->get_node(itos(new_id)); if (node) { @@ -1579,8 +2009,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -1609,11 +2039,11 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs); undo_redo->add_do_method(vnode.ptr(), "set_base_type", script->get_instance_base_type()); undo_redo->add_do_method(vnode.ptr(), "set_function", d["function"]); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -1642,8 +2072,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -1672,8 +2102,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Preload Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, prnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, prnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -1713,8 +2143,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da prnode.instance(); prnode->set_preload(res); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, prnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, prnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); new_ids.push_back(new_id); new_id++; ofs += Vector2(20, 20) * EDSCALE; @@ -1740,7 +2170,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script); if (!sn) { - EditorNode::get_singleton()->show_warning("Can't drop nodes because script '" + get_name() + "' is not used in this scene."); + EditorNode::get_singleton()->show_warning(TTR("Can't drop nodes because script '" + get_name() + "' is not used in this scene.")); return; } @@ -1782,20 +2212,20 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da scene_node.instance(); scene_node->set_node_path(sn->get_path_to(node)); n = scene_node; - } else { + // ! Doesn't work properly Ref<VisualScriptFunctionCall> call; call.instance(); call->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH); call->set_base_path(sn->get_path_to(node)); call->set_base_type(node->get_class()); n = call; - method_select->select_from_instance(node); + method_select->select_from_instance(node, "", true, node->get_class()); selecting_method_id = base_id; } - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, n, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, n, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id); base_id++; ofs += Vector2(25, 25); @@ -1810,7 +2240,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script); if (!sn && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { - EditorNode::get_singleton()->show_warning("Can't drop properties because script '" + get_name() + "' is not used in this scene.\nDrop holding 'Shift' to just copy the signature."); + EditorNode::get_singleton()->show_warning(TTR("Can't drop properties because script '" + get_name() + "' is not used in this scene.\nDrop holding 'Shift' to just copy the signature.")); return; } @@ -1866,13 +2296,13 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da vnode = pget; } - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, vnode, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, vnode, ofs); undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); if (!use_get) { undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); } - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -1913,12 +2343,12 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } vnode = pget; } - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, vnode, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, vnode, ofs); undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); if (!use_get) { undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); } - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -1929,7 +2359,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type, const bool p_connecting) { - Ref<VisualScriptFunctionCall> vsfc = script->get_node(edited_func, selecting_method_id); + Ref<VisualScriptFunctionCall> vsfc = script->get_node(default_func, selecting_method_id); if (!vsfc.is_valid()) return; vsfc->set_function(p_method); @@ -1986,8 +2416,16 @@ void VisualScriptEditor::set_edited_resource(const RES &p_res) { script->connect("node_ports_changed", this, "_node_ports_changed"); + default_func = script->get_default_func(); + + if (!script->has_function(default_func)) // this is the supposed default function + { + script->add_function(default_func); + script->set_edited(true); //so that if a function was added it's saved + } + + _update_graph(); _update_members(); - _update_available_nodes(); } Vector<String> VisualScriptEditor::get_functions() { @@ -2032,7 +2470,7 @@ bool VisualScriptEditor::is_unsaved() { Variant VisualScriptEditor::get_edit_state() { Dictionary d; - d["function"] = edited_func; + d["function"] = default_func; d["scroll"] = graph->get_scroll_ofs(); d["zoom"] = graph->get_zoom(); d["using_snap"] = graph->is_using_snap(); @@ -2044,8 +2482,7 @@ void VisualScriptEditor::set_edit_state(const Variant &p_state) { Dictionary d = p_state; if (d.has("function")) { - edited_func = d["function"]; - selected = edited_func; + selected = default_func; } _update_graph(); @@ -2065,16 +2502,24 @@ void VisualScriptEditor::set_edit_state(const Variant &p_state) { } } -void VisualScriptEditor::_center_on_node(int p_id) { +void VisualScriptEditor::_center_on_node(const StringName &p_func, int p_id) { Node *n = graph->get_node(itos(p_id)); GraphNode *gn = Object::cast_to<GraphNode>(n); + + // clear selection + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gnd = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gnd) + gnd->set_selected(false); + } + if (gn) { gn->set_selected(true); Vector2 new_scroll = gn->get_offset() - graph->get_size() * 0.5 + gn->get_size() * 0.5; graph->set_scroll_ofs(new_scroll); - script->set_function_scroll(edited_func, new_scroll / EDSCALE); - script->set_edited(true); //so it's saved + script->set_function_scroll(p_func, new_scroll / EDSCALE); + script->set_edited(true); } } @@ -2091,13 +2536,10 @@ void VisualScriptEditor::goto_line(int p_line, bool p_with_error) { if (script->has_node(E->get(), p_line)) { - edited_func = E->get(); - selected = edited_func; _update_graph(); _update_members(); - call_deferred("call_deferred", "_center_on_node", p_line); //editor might be just created and size might not exist yet - + call_deferred("call_deferred", "_center_on_node", E->get(), p_line); //editor might be just created and size might not exist yet return; } } @@ -2132,6 +2574,7 @@ void VisualScriptEditor::tag_saved_version() { } void VisualScriptEditor::reload(bool p_soft) { + _update_graph(); } void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) { @@ -2155,10 +2598,9 @@ void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) { void VisualScriptEditor::add_callback(const String &p_function, PoolStringArray p_args) { if (script->has_function(p_function)) { - edited_func = p_function; - selected = edited_func; _update_members(); _update_graph(); + _center_on_node(p_function, script->get_function_node_id(p_function)); return; } @@ -2189,13 +2631,10 @@ void VisualScriptEditor::add_callback(const String &p_function, PoolStringArray script->add_function(p_function); script->add_node(p_function, script->get_available_id(), func); - edited_func = p_function; - selected = edited_func; _update_members(); _update_graph(); - graph->call_deferred("set_scroll_ofs", script->get_function_scroll(edited_func)); //for first time it might need to be later - //undo_redo->clear_history(); + _center_on_node(p_function, script->get_function_node_id(p_function)); } bool VisualScriptEditor::show_members_overview() { @@ -2227,6 +2666,10 @@ void VisualScriptEditor::_change_base_type() { select_base_type->popup_create(true, true); } +void VisualScriptEditor::_toggle_tool_script() { + script->set_tool_enabled(!script->is_tool()); +} + void VisualScriptEditor::clear_edit_menu() { memdelete(edit_menu); memdelete(left_vsplit); @@ -2293,46 +2736,66 @@ void VisualScriptEditor::_end_node_move() { undo_redo->commit_action(); } -void VisualScriptEditor::_move_node(String func, int p_id, const Vector2 &p_to) { +void VisualScriptEditor::_move_node(const StringName &p_func, int p_id, const Vector2 &p_to) { + + if (!script->has_function(p_func)) + return; + + Node *node = graph->get_node(itos(p_id)); + + if (Object::cast_to<GraphNode>(node)) + Object::cast_to<GraphNode>(node)->set_offset(p_to); + + script->set_node_position(p_func, p_id, p_to / EDSCALE); +} + +StringName VisualScriptEditor::_get_function_of_node(int p_id) const { - if (func == String(edited_func)) { - Node *node = graph->get_node(itos(p_id)); - if (Object::cast_to<GraphNode>(node)) - Object::cast_to<GraphNode>(node)->set_offset(p_to); + List<StringName> funcs; + script->get_function_list(&funcs); + for (List<StringName>::Element *E = funcs.front(); E; E = E->next()) { + if (script->has_node(E->get(), p_id)) { + return E->get(); + } } - script->set_node_position(edited_func, p_id, p_to / EDSCALE); + + return ""; // this is passed to avoid crash and is tested against later } void VisualScriptEditor::_node_moved(Vector2 p_from, Vector2 p_to, int p_id) { - undo_redo->add_do_method(this, "_move_node", String(edited_func), p_id, p_to); - undo_redo->add_undo_method(this, "_move_node", String(edited_func), p_id, p_from); + StringName func = _get_function_of_node(p_id); + + undo_redo->add_do_method(this, "_move_node", func, p_id, p_to); + undo_redo->add_undo_method(this, "_move_node", func, p_id, p_from); } void VisualScriptEditor::_remove_node(int p_id) { undo_redo->create_action(TTR("Remove VisualScript Node")); - undo_redo->add_do_method(script.ptr(), "remove_node", edited_func, p_id); - undo_redo->add_undo_method(script.ptr(), "add_node", edited_func, p_id, script->get_node(edited_func, p_id), script->get_node_position(edited_func, p_id)); + StringName func = _get_function_of_node(p_id); + + undo_redo->add_do_method(script.ptr(), "remove_node", func, p_id); + undo_redo->add_undo_method(script.ptr(), "add_node", func, p_id, script->get_node(func, p_id), script->get_node_position(func, p_id)); List<VisualScript::SequenceConnection> sequence_conns; - script->get_sequence_connection_list(edited_func, &sequence_conns); + script->get_sequence_connection_list(func, &sequence_conns); for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { if (E->get().from_node == p_id || E->get().to_node == p_id) { - undo_redo->add_undo_method(script.ptr(), "sequence_connect", edited_func, E->get().from_node, E->get().from_output, E->get().to_node); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, E->get().from_node, E->get().from_output, E->get().to_node); } } List<VisualScript::DataConnection> data_conns; - script->get_data_connection_list(edited_func, &data_conns); + script->get_data_connection_list(func, &data_conns); for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { if (E->get().from_node == p_id || E->get().to_node == p_id) { - undo_redo->add_undo_method(script.ptr(), "data_connect", edited_func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); } } @@ -2344,15 +2807,29 @@ void VisualScriptEditor::_remove_node(int p_id) { void VisualScriptEditor::_node_ports_changed(const String &p_func, int p_id) { - if (p_func != String(edited_func)) - return; - _update_graph(p_id); } +bool VisualScriptEditor::node_has_sequence_connections(const StringName &p_func, int p_id) { + List<VisualScript::SequenceConnection> sequence_conns; + script->get_sequence_connection_list(p_func, &sequence_conns); + + for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { + int from = E->get().from_node; + int to = E->get().to_node; + + if (to == p_id || from == p_id) + return true; + } + + return false; +} + void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) { - Ref<VisualScriptNode> from_node = script->get_node(edited_func, p_from.to_int()); + StringName from_func = _get_function_of_node(p_from.to_int()); + + Ref<VisualScriptNode> from_node = script->get_node(from_func, p_from.to_int()); ERR_FAIL_COND(!from_node.is_valid()); bool from_seq; @@ -2361,7 +2838,9 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq)) return; //can't connect this, it's invalid - Ref<VisualScriptNode> to_node = script->get_node(edited_func, p_to.to_int()); + StringName to_func = _get_function_of_node(p_to.to_int()); + + Ref<VisualScriptNode> to_node = script->get_node(to_func, p_to.to_int()); ERR_FAIL_COND(!to_node.is_valid()); bool to_seq; @@ -2372,29 +2851,170 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, ERR_FAIL_COND(from_seq != to_seq); + // Do all the checks here + StringName func; // this the func where we store the one the nodes at the end of the resolution on having multiple nodes + undo_redo->create_action(TTR("Connect Nodes")); + if (from_func == to_func) { + func = to_func; + } else if (from_seq) { + // this is a sequence connection + _move_nodes_with_rescan(to_func, from_func, p_to.to_int()); // this function moves the nodes from func1 to func2 + func = from_func; + } else { + if (node_has_sequence_connections(to_func, p_to.to_int())) { + if (node_has_sequence_connections(from_func, p_from.to_int())) { + ERR_PRINT("Trying to connect between different sequence node trees"); + return; + } else { + _move_nodes_with_rescan(from_func, to_func, p_from.to_int()); + func = to_func; + } + } else if (node_has_sequence_connections(from_func, p_from.to_int())) { + if (from_func == default_func) { + _move_nodes_with_rescan(from_func, to_func, p_from.to_int()); + func = to_func; + } else { + _move_nodes_with_rescan(to_func, from_func, p_to.to_int()); + func = from_func; + } + } else { + if (to_func == default_func) { + _move_nodes_with_rescan(to_func, from_func, p_to.to_int()); + func = from_func; + } else { + _move_nodes_with_rescan(from_func, to_func, p_from.to_int()); + func = to_func; + } + } + } + if (from_seq) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, p_from.to_int(), from_port, p_to.to_int()); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int()); + undo_redo->add_do_method(script.ptr(), "sequence_connect", func, p_from.to_int(), from_port, p_to.to_int()); + // this undo error on undo after move can't be removed without painful gymnastics + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, p_from.to_int(), from_port, p_to.to_int()); } else { + bool converted = false; + int conv_node = -1; + + Ref<VisualScriptOperator> oper = to_node; + if (oper.is_valid() && oper->get_typed() == Variant::NIL) { + // it's an operator Node and if the type is already nil + if (from_node->get_output_value_port_info(from_port).type != Variant::NIL) { + oper->set_typed(from_node->get_output_value_port_info(from_port).type); + } + } - // disconnect current, and connect the new one - if (script->is_input_value_port_connected(edited_func, p_to.to_int(), to_port)) { - int conn_from; - int conn_port; - script->get_input_value_port_connection_source(edited_func, p_to.to_int(), to_port, &conn_from, &conn_port); - undo_redo->add_do_method(script.ptr(), "data_disconnect", edited_func, conn_from, conn_port, p_to.to_int(), to_port); - undo_redo->add_undo_method(script.ptr(), "data_connect", edited_func, conn_from, conn_port, p_to.to_int(), to_port); + Ref<VisualScriptOperator> operf = from_node; + if (operf.is_valid() && operf->get_typed() == Variant::NIL) { + // it's an operator Node and if the type is already nil + if (to_node->get_input_value_port_info(to_port).type != Variant::NIL) { + operf->set_typed(to_node->get_input_value_port_info(to_port).type); + } } - undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port); - //update nodes in sgraph - undo_redo->add_do_method(this, "_update_graph", p_from.to_int()); - undo_redo->add_do_method(this, "_update_graph", p_to.to_int()); - undo_redo->add_undo_method(this, "_update_graph", p_from.to_int()); - undo_redo->add_undo_method(this, "_update_graph", p_to.to_int()); + Variant::Type to_type = to_node->get_input_value_port_info(to_port).type; + Variant::Type from_type = from_node->get_output_value_port_info(from_port).type; + + if (to_type != Variant::NIL && from_type != Variant::NIL && to_type != from_type) { + // add a constructor node between the ports + bool exceptions = false; // true if there are any exceptions + exceptions = exceptions || (to_type == Variant::INT && from_type == Variant::REAL); + exceptions = exceptions || (to_type == Variant::REAL && from_type == Variant::INT); + if (Variant::can_convert(from_type, to_type) && !exceptions) { + MethodInfo mi; + mi.name = Variant::get_type_name(to_type); + PropertyInfo pi; + pi.name = "from"; + pi.type = from_type; + mi.arguments.push_back(pi); + mi.return_val.type = to_type; + // we know that this is allowed so create a new constructor node + Ref<VisualScriptConstructor> constructor; + constructor.instance(); + constructor->set_constructor_type(to_type); + constructor->set_constructor(mi); + // add the new constructor node + + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(p_from)); + GraphNode *gn2 = Object::cast_to<GraphNode>(graph->get_node(p_to)); + if (gn && gn2) { + Vector2 from_node_size = gn->get_rect().get_size(); + Vector2 to_node_size = gn2->get_rect().get_size(); + Vector2 to_node_pos = script->get_node_position(func, p_to.to_int()); + Vector2 from_node_pos = script->get_node_position(func, p_from.to_int()); + Vector2 new_to_node_pos = from_node_pos; + Vector2 constructor_pos; + if ((to_node_pos.x - from_node_pos.x) < 0) { + // to is behind from node + if (to_node_pos.x > (from_node_pos.x - to_node_size.x - 240)) + new_to_node_pos.x = from_node_pos.x - to_node_size.x - 240; // approx size of construtor node + padding + else + new_to_node_pos.x = to_node_pos.x; + new_to_node_pos.y = to_node_pos.y; + constructor_pos.x = from_node_pos.x - 210; + constructor_pos.y = to_node_pos.y; + } else { + // to is ahead of from node + if (to_node_pos.x < (from_node_size.x + from_node_pos.x + 240)) + new_to_node_pos.x = from_node_size.x + from_node_pos.x + 240; // approx size of construtor node + padding + else + new_to_node_pos.x = to_node_pos.x; + new_to_node_pos.y = to_node_pos.y; + constructor_pos.x = from_node_size.x + from_node_pos.x + 10; + constructor_pos.y = to_node_pos.y; + } + undo_redo->add_do_method(this, "_move_node", func, p_to.to_int(), new_to_node_pos); + undo_redo->add_undo_method(this, "_move_node", func, p_to.to_int(), to_node_pos); + conv_node = script->get_available_id(); + undo_redo->add_do_method(script.ptr(), "add_node", func, conv_node, constructor, _get_available_pos(false, constructor_pos)); + undo_redo->add_undo_method(script.ptr(), "remove_node", func, conv_node); + converted = true; + } + } + } + + // disconnect current, and connect the new one + if (script->is_input_value_port_connected(func, p_to.to_int(), to_port)) { + if (can_swap && data_disconnect_node == p_to.to_int()) { + int conn_from; + int conn_port; + script->get_input_value_port_connection_source(func, p_to.to_int(), to_port, &conn_from, &conn_port); + undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_to.to_int(), to_port); + undo_redo->add_do_method(script.ptr(), "data_connect", func, conn_from, conn_port, data_disconnect_node, data_disconnect_port); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, data_disconnect_node, data_disconnect_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_to.to_int(), to_port); + can_swap = false; // swapped + } else { + int conn_from; + int conn_port; + script->get_input_value_port_connection_source(func, p_to.to_int(), to_port, &conn_from, &conn_port); + undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_to.to_int(), to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_to.to_int(), to_port); + } + } + if (!converted) { + undo_redo->add_do_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); + } else { + // this is noice + undo_redo->add_do_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, conv_node, 0); + undo_redo->add_do_method(script.ptr(), "data_connect", func, conv_node, 0, p_to.to_int(), to_port); + // I don't think this is needed but gonna leave it here for now... until I need to finalise it all + undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, conv_node, 0); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, conv_node, 0, p_to.to_int(), to_port); + } + //update nodes in graph + if (!converted) { + undo_redo->add_do_method(this, "_update_graph", p_from.to_int()); + undo_redo->add_do_method(this, "_update_graph", p_to.to_int()); + undo_redo->add_undo_method(this, "_update_graph", p_from.to_int()); + undo_redo->add_undo_method(this, "_update_graph", p_to.to_int()); + } else { + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + } } undo_redo->add_do_method(this, "_update_graph_connections"); @@ -2405,7 +3025,10 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) { - Ref<VisualScriptNode> from_node = script->get_node(edited_func, p_from.to_int()); + StringName func = _get_function_of_node(p_from.to_int()); + ERR_FAIL_COND(func != _get_function_of_node(p_to.to_int())); + + Ref<VisualScriptNode> from_node = script->get_node(func, p_from.to_int()); ERR_FAIL_COND(!from_node.is_valid()); bool from_seq; @@ -2414,7 +3037,7 @@ void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_sl if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq)) return; //can't connect this, it's invalid - Ref<VisualScriptNode> to_node = script->get_node(edited_func, p_to.to_int()); + Ref<VisualScriptNode> to_node = script->get_node(func, p_to.to_int()); ERR_FAIL_COND(!to_node.is_valid()); bool to_seq; @@ -2425,15 +3048,20 @@ void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_sl ERR_FAIL_COND(from_seq != to_seq); - undo_redo->create_action(TTR("Connect Nodes")); + undo_redo->create_action(TTR("Disconnect Nodes")); if (from_seq) { - undo_redo->add_do_method(script.ptr(), "sequence_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int()); - undo_redo->add_undo_method(script.ptr(), "sequence_connect", edited_func, p_from.to_int(), from_port, p_to.to_int()); + undo_redo->add_do_method(script.ptr(), "sequence_disconnect", func, p_from.to_int(), from_port, p_to.to_int()); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, p_from.to_int(), from_port, p_to.to_int()); } else { - undo_redo->add_do_method(script.ptr(), "data_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port); - undo_redo->add_undo_method(script.ptr(), "data_connect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port); - //update nodes in sgraph + + can_swap = true; + data_disconnect_node = p_to.to_int(); + data_disconnect_port = to_port; + + undo_redo->add_do_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); + //update relevant nodes in the graph undo_redo->add_do_method(this, "_update_graph", p_from.to_int()); undo_redo->add_do_method(this, "_update_graph", p_to.to_int()); undo_redo->add_undo_method(this, "_update_graph", p_from.to_int()); @@ -2445,6 +3073,216 @@ void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_sl undo_redo->commit_action(); } +void VisualScriptEditor::_move_nodes_with_rescan(const StringName &p_func_from, const StringName &p_func_to, int p_id) { + + Set<int> nodes_to_move; + HashMap<int, Map<int, int> > seqconns_to_move; // from => List(outp, to) + HashMap<int, Map<int, Pair<int, int> > > dataconns_to_move; // to => List(inp_p => from, outp) + + nodes_to_move.insert(p_id); + Set<int> sequence_connections; + { + List<VisualScript::SequenceConnection> sequence_conns; + script->get_sequence_connection_list(p_func_from, &sequence_conns); + + HashMap<int, Map<int, int> > seqcons; // from => List(out_p => to) + + for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { + int from = E->get().from_node; + int to = E->get().to_node; + int out_p = E->get().from_output; + if (!seqcons.has(from)) + seqcons.set(from, Map<int, int>()); + seqcons[from].insert(out_p, to); + sequence_connections.insert(to); + sequence_connections.insert(from); + } + + int conn = p_id; + List<int> stack; + HashMap<int, Set<int> > seen; // from, outp + while (seqcons.has(conn)) { + for (auto E = seqcons[conn].front(); E; E = E->next()) { + if (seen.has(conn) && seen[conn].has(E->key())) { + if (!E->next()) { + if (stack.size() > 0) { + conn = stack.back()->get(); + stack.pop_back(); + break; + } + conn = -101; + break; + } + continue; + } + if (!seen.has(conn)) + seen.set(conn, Set<int>()); + seen[conn].insert(E->key()); + stack.push_back(conn); + if (!seqconns_to_move.has(conn)) + seqconns_to_move.set(conn, Map<int, int>()); + seqconns_to_move[conn].insert(E->key(), E->get()); + conn = E->get(); + nodes_to_move.insert(conn); + break; + } + if (!seqcons.has(conn) && stack.size() > 0) { + conn = stack.back()->get(); + stack.pop_back(); + } + } + } + + { + List<VisualScript::DataConnection> data_connections; + script->get_data_connection_list(p_func_from, &data_connections); + + HashMap<int, Map<int, Pair<int, int> > > connections; + + for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { + int from = E->get().from_node; + int to = E->get().to_node; + int out_p = E->get().from_port; + int in_p = E->get().to_port; + + if (!connections.has(to)) + connections.set(to, Map<int, Pair<int, int> >()); + connections[to].insert(in_p, Pair<int, int>(from, out_p)); + } + + // go through the HashMap and do all sorts of crazy ass stuff now... + Set<int> nodes_to_be_added; + for (Set<int>::Element *F = nodes_to_move.front(); F; F = F->next()) { + HashMap<int, Set<int> > seen; + List<int> stack; + int id = F->get(); + while (connections.has(id)) { + for (auto E = connections[id].front(); E; E = E->next()) { + if (seen.has(id) && seen[id].has(E->key())) { + if (!E->next()) { + if (stack.size() > 0) { + id = stack.back()->get(); + stack.pop_back(); + break; + } + id = -11; // I assume ids can't be negative should confirm it... + break; + } + continue; + } + + if (sequence_connections.has(E->get().first)) { + if (!nodes_to_move.has(E->get().first)) { + if (stack.size() > 0) { + id = stack.back()->get(); + stack.pop_back(); + break; + } + id = -11; // I assume ids can't be negative should confirm it... + break; + } + } + + if (!seen.has(id)) + seen.set(id, Set<int>()); + seen[id].insert(E->key()); + stack.push_back(id); + if (!dataconns_to_move.has(id)) + dataconns_to_move.set(id, Map<int, Pair<int, int> >()); + dataconns_to_move[id].insert(E->key(), Pair<int, int>(E->get().first, E->get().second)); + id = E->get().first; + nodes_to_be_added.insert(id); + break; + } + if (!connections.has(id) && stack.size() > 0) { + id = stack.back()->get(); + stack.pop_back(); + } + } + } + for (Set<int>::Element *E = nodes_to_be_added.front(); E; E = E->next()) { + nodes_to_move.insert(E->get()); + } + } + + // * this is primarily for the sake of the having proper undo + List<VisualScript::SequenceConnection> seqext; + List<VisualScript::DataConnection> dataext; + + List<VisualScript::SequenceConnection> seq_connections; + script->get_sequence_connection_list(p_func_from, &seq_connections); + + for (List<VisualScript::SequenceConnection>::Element *E = seq_connections.front(); E; E = E->next()) { + if (!nodes_to_move.has(E->get().from_node) && nodes_to_move.has(E->get().to_node)) { + seqext.push_back(E->get()); + } else if (nodes_to_move.has(E->get().from_node) && !nodes_to_move.has(E->get().to_node)) { + seqext.push_back(E->get()); + } + } + + List<VisualScript::DataConnection> data_connections; + script->get_data_connection_list(p_func_from, &data_connections); + + for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { + if (!nodes_to_move.has(E->get().from_node) && nodes_to_move.has(E->get().to_node)) { + dataext.push_back(E->get()); + } else if (nodes_to_move.has(E->get().from_node) && !nodes_to_move.has(E->get().to_node)) { + dataext.push_back(E->get()); + } + } + + // undo_redo->create_action("Rescan Functions"); + + for (Set<int>::Element *E = nodes_to_move.front(); E; E = E->next()) { + int id = E->get(); + + undo_redo->add_do_method(script.ptr(), "remove_node", p_func_from, id); + undo_redo->add_do_method(script.ptr(), "add_node", p_func_to, id, script->get_node(p_func_from, id), script->get_node_position(p_func_from, id)); + + undo_redo->add_undo_method(script.ptr(), "remove_node", p_func_to, id); + undo_redo->add_undo_method(script.ptr(), "add_node", p_func_from, id, script->get_node(p_func_from, id), script->get_node_position(p_func_from, id)); + } + + List<int> skeys; + seqconns_to_move.get_key_list(&skeys); + for (List<int>::Element *E = skeys.front(); E; E = E->next()) { + int from_node = E->get(); + for (Map<int, int>::Element *F = seqconns_to_move[from_node].front(); F; F = F->next()) { + int from_port = F->key(); + int to_node = F->get(); + undo_redo->add_do_method(script.ptr(), "sequence_connect", p_func_to, from_node, from_port, to_node); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_func_from, from_node, from_port, to_node); + } + } + + List<int> keys; + dataconns_to_move.get_key_list(&keys); + for (List<int>::Element *E = keys.front(); E; E = E->next()) { + int to_node = E->get(); // to_node + for (Map<int, Pair<int, int> >::Element *F = dataconns_to_move[E->get()].front(); F; F = F->next()) { + int inp_p = F->key(); + Pair<int, int> fro = F->get(); + + undo_redo->add_do_method(script.ptr(), "data_connect", p_func_to, fro.first, fro.second, to_node, inp_p); + undo_redo->add_undo_method(script.ptr(), "data_connect", p_func_from, fro.first, fro.second, to_node, inp_p); + } + } + + // this to have proper undo operations + for (List<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) { + undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_func_from, E->get().from_node, E->get().from_output, E->get().to_node); + } + for (List<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) { + undo_redo->add_undo_method(script.ptr(), "data_connect", p_func_from, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + } + // this doesn't need do methods as they are handled by the subsequent do calls implicitly + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + // undo_redo->commit_action(); +} + void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_pos) { Node *node = graph->get_node(p_from); @@ -2452,7 +3290,9 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro if (!gn) return; - Ref<VisualScriptNode> vsn = script->get_node(edited_func, p_from.to_int()); + StringName func = _get_function_of_node(p_from.to_int()); + + Ref<VisualScriptNode> vsn = script->get_node(func, p_from.to_int()); if (!vsn.is_valid()) return; @@ -2462,12 +3302,11 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro port_action_node = p_from.to_int(); port_action_output = p_from_slot; - _port_action_menu(CREATE_ACTION); + _port_action_menu(CREATE_ACTION, func); } else { - port_action_output = p_from_slot - vsn->get_output_sequence_port_count(); port_action_node = p_from.to_int(); - _port_action_menu(CREATE_CALL_SET_GET); + _port_action_menu(CREATE_CALL_SET_GET, func); } } @@ -2481,7 +3320,9 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac visited_nodes.insert(p_port_action_node); - Ref<VisualScriptNode> node = script->get_node(edited_func, p_port_action_node); + StringName func = _get_function_of_node(p_port_action_node); + + Ref<VisualScriptNode> node = script->get_node(func, p_port_action_node); if (!node.is_valid()) { @@ -2500,7 +3341,7 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac int from_node; int from_port; - if (script->get_input_value_port_connection_source(edited_func, p_port_action_node, i, &from_node, &from_port)) { + if (script->get_input_value_port_connection_source(func, p_port_action_node, i, &from_node, &from_port)) { g = _guess_output_type(from_node, from_port, visited_nodes); } else { @@ -2525,7 +3366,7 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac return node->guess_output_type(in_guesses.ptrw(), p_port_action_output); } -void VisualScriptEditor::_port_action_menu(int p_option) { +void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func) { Vector2 ofs = graph->get_scroll_ofs() + port_action_pos; if (graph->is_using_snap()) { @@ -2549,8 +3390,10 @@ void VisualScriptEditor::_port_action_menu(int p_option) { } else { n->set_base_type("Object"); } - - String type_string = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + String type_string; + if (script->get_node(func, port_action_node)->get_output_value_port_count() > 0) { + type_string = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } if (tg.type == Variant::OBJECT) { if (tg.script.is_valid()) { new_connect_node_select->select_from_script(tg.script, ""); @@ -2564,10 +3407,19 @@ void VisualScriptEditor::_port_action_menu(int p_option) { } else { new_connect_node_select->select_from_basic_type(tg.type); } + // ensure that the dialog fits inside the graph + Vector2 pos = mouse_up_position; + Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size(); + pos.x = pos.x > bounds.x ? bounds.x : pos.x; + pos.y = pos.y > bounds.y ? bounds.y : pos.y; + new_connect_node_select->set_position(pos); } break; case CREATE_ACTION: { VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - PropertyInfo property_info = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output); + PropertyInfo property_info; + if (script->get_node(func, port_action_node)->get_output_value_port_count() > 0) { + property_info = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output); + } if (tg.type == Variant::OBJECT) { if (property_info.type == Variant::OBJECT && property_info.hint_string != String()) { new_connect_node_select->select_from_action(property_info.hint_string); @@ -2579,25 +3431,18 @@ void VisualScriptEditor::_port_action_menu(int p_option) { } else { new_connect_node_select->select_from_action(Variant::get_type_name(tg.type)); } + // ensure that the dialog fits inside the graph + Vector2 pos = mouse_up_position; + Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size(); + pos.x = pos.x > bounds.x ? bounds.x : pos.x; + pos.y = pos.y > bounds.y ? bounds.y : pos.y; + new_connect_node_select->set_position(pos); } break; } } -void VisualScriptEditor::new_node(Ref<VisualScriptNode> vnode, Vector2 ofs) { - Set<int> vn; - Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); - int new_id = script->get_available_id(); - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph", new_id); - undo_redo->add_undo_method(this, "_update_graph", new_id); - undo_redo->commit_action(); - - port_action_new_node = new_id; -} - void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id) { + undo_redo->create_action(TTR("Connect Node Data")); VisualScriptReturn *vnode_return = Object::cast_to<VisualScriptReturn>(vnode.ptr()); if (vnode_return != NULL && vnode_old->get_output_value_port_count() > 0) { @@ -2616,12 +3461,14 @@ void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<Visua if (port >= value_count) { port = 0; } - undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, port_action_node, port, new_id, 0); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, port_action_node, port, new_id, 0); + StringName func = _get_function_of_node(port_action_node); + undo_redo->add_do_method(script.ptr(), "data_connect", func, port_action_node, port, new_id, 0); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, port_action_node, port, new_id, 0); undo_redo->commit_action(); } void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting) { + Vector2 ofs = graph->get_scroll_ofs() + port_action_pos; if (graph->is_using_snap()) { int snap = graph->get_snap(); @@ -2631,19 +3478,29 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri Set<int> vn; + bool port_node_exists = true; + + StringName func = _get_function_of_node(port_action_node); + if (func == StringName()) { + func = default_func; + port_node_exists = false; + } + if (p_category == "visualscript") { Ref<VisualScriptNode> vnode_new = VisualScriptLanguage::singleton->create_node_from_name(p_text); - Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); + Ref<VisualScriptNode> vnode_old; + if (port_node_exists) + vnode_old = script->get_node(func, port_action_node); int new_id = script->get_available_id(); - if (Object::cast_to<VisualScriptOperator>(vnode_new.ptr()) && script->get_node(edited_func, port_action_node).is_valid()) { - Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type; + if (Object::cast_to<VisualScriptOperator>(vnode_new.ptr()) && vnode_old.is_valid()) { + Variant::Type type = vnode_old->get_output_value_port_info(port_action_output).type; Object::cast_to<VisualScriptOperator>(vnode_new.ptr())->set_typed(type); } - if (Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr()) && script->get_node(edited_func, port_action_node).is_valid()) { - Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type; - String hint_name = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + if (Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr()) && vnode_old.is_valid()) { + Variant::Type type = vnode_old->get_output_value_port_info(port_action_output).type; + String hint_name = vnode_old->get_output_value_port_info(port_action_output).hint_string; if (type == Variant::OBJECT) { Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(hint_name); @@ -2653,14 +3510,15 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(Variant::get_type_name(type)); } } + undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode_new, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode_new, ofs); if (vnode_old.is_valid() && p_connecting) { connect_seq(vnode_old, vnode_new, new_id); connect_data(vnode_old, vnode_new, new_id); } - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -2723,16 +3581,24 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } } - new_node(vnode, ofs); + int new_id = script->get_available_id(); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id); + undo_redo->add_do_method(this, "_update_graph", new_id); + undo_redo->add_undo_method(this, "_update_graph", new_id); + undo_redo->commit_action(); - Ref<VisualScriptNode> vsn = script->get_node(edited_func, port_action_new_node); + port_action_new_node = new_id; + + Ref<VisualScriptNode> vsn = script->get_node(func, port_action_new_node); if (Object::cast_to<VisualScriptFunctionCall>(vsn.ptr())) { Ref<VisualScriptFunctionCall> vsfc = vsn; vsfc->set_function(p_text); - if (p_connecting) { + if (port_node_exists && p_connecting) { VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); if (tg.type == Variant::OBJECT) { @@ -2741,9 +3607,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri if (tg.gdclass != StringName()) { vsfc->set_base_type(tg.gdclass); - } else if (script->get_node(edited_func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } else if (script->get_node(func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { vsfc->set_base_type(base_type); @@ -2765,8 +3631,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } } - // if connecting from another node the call mode shouldn't be self - if (p_connecting) { + if (port_node_exists && p_connecting) { if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) { Ref<VisualScriptPropertySet> vsp = vsn; @@ -2777,9 +3642,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri if (tg.gdclass != StringName()) { vsp->set_base_type(tg.gdclass); - } else if (script->get_node(edited_func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } else if (script->get_node(func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { vsp->set_base_type(base_type); @@ -2807,9 +3672,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri if (tg.gdclass != StringName()) { vsp->set_base_type(tg.gdclass); - } else if (script->get_node(edited_func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } else if (script->get_node(func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { vsp->set_base_type(base_type); } @@ -2826,16 +3691,20 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } } } - Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); - if (vnode_old.is_valid() && p_connecting) { - connect_seq(vnode_old, vnode, port_action_new_node); - connect_data(vnode_old, vnode, port_action_new_node); + if (port_node_exists) { + Ref<VisualScriptNode> vnode_old = script->get_node(func, port_action_node); + if (vnode_old.is_valid() && p_connecting) { + connect_seq(vnode_old, vnode, port_action_new_node); + connect_data(vnode_old, vnode, port_action_new_node); + } } _update_graph(port_action_new_node); - _update_graph_connections(); + if (port_node_exists) + _update_graph_connections(); } void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id) { + VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(vnode_new.ptr()); if (vnode_operator != NULL && !vnode_operator->has_input_sequence_port()) { return; @@ -2851,27 +3720,29 @@ void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<Visual return; } + StringName func = _get_function_of_node(port_action_node); + undo_redo->create_action(TTR("Connect Node Sequence")); int pass_port = -vnode_old->get_output_sequence_port_count() + 1; int return_port = port_action_output - 1; if (vnode_old->get_output_value_port_info(port_action_output).name == String("pass") && - !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(pass_port)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, pass_port, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, pass_port, new_id); + !script->get_output_sequence_ports_connected(func, port_action_node).has(pass_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, pass_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, pass_port, new_id); } else if (vnode_old->get_output_value_port_info(port_action_output).name == String("return") && - !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(return_port)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, return_port, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, return_port, new_id); + !script->get_output_sequence_ports_connected(func, port_action_node).has(return_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, return_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, return_port, new_id); } else { for (int port = 0; port < vnode_old->get_output_sequence_port_count(); port++) { int count = vnode_old->get_output_sequence_port_count(); - if (port_action_output < count && !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port_action_output)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port_action_output, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port_action_output, new_id); + if (port_action_output < count && !script->get_output_sequence_ports_connected(func, port_action_node).has(port_action_output)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, port_action_output, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, port_action_output, new_id); break; - } else if (!script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port, new_id); + } else if (!script->get_output_sequence_ports_connected(func, port_action_node).has(port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, port, new_id); break; } } @@ -2904,7 +3775,6 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons } selected = name; - edited_func = selected; Ref<VisualScriptFunction> func_node; func_node.instance(); func_node->set_name(name); @@ -2916,14 +3786,16 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons func_node->add_argument(minfo.arguments[i].type, minfo.arguments[i].name, -1, minfo.arguments[i].hint, minfo.arguments[i].hint_string); } - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node); + Vector2 ofs = _get_available_pos(); + + undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs); if (minfo.return_val.type != Variant::NIL || minfo.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { Ref<VisualScriptReturn> ret_node; ret_node.instance(); ret_node->set_return_type(minfo.return_val.type); ret_node->set_enable_return_value(true); ret_node->set_name(name); - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id() + 1, ret_node, Vector2(500, 0)); + undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id() + 1, ret_node, _get_available_pos(false, ofs + Vector2(500, 0))); } undo_redo->add_undo_method(script.ptr(), "remove_function", name); @@ -2938,31 +3810,30 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons } void VisualScriptEditor::_cancel_connect_node() { - // Causes crashes - //script->remove_node(edited_func, port_action_new_node); - _update_graph(); + // ensure the cancel is done + port_action_new_node = -1; } -void VisualScriptEditor::_create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point) { - Vector2 ofs = graph->get_scroll_ofs() + p_point; - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } - ofs /= EDSCALE; +int VisualScriptEditor::_create_new_node_from_name(const String &p_text, const Vector2 &p_point, const StringName &p_func) { + + StringName func = default_func; + if (p_func != StringName()) + func = p_func; + Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(p_text); int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode, p_point); + undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); + return new_id; } void VisualScriptEditor::_default_value_changed() { - Ref<VisualScriptNode> vsn = script->get_node(edited_func, editing_id); + Ref<VisualScriptNode> vsn = script->get_node(_get_function_of_node(editing_id), editing_id); if (vsn.is_null()) return; @@ -2977,7 +3848,7 @@ void VisualScriptEditor::_default_value_changed() { void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_input_port) { - Ref<VisualScriptNode> vsn = script->get_node(edited_func, p_id); + Ref<VisualScriptNode> vsn = script->get_node(_get_function_of_node(p_id), p_id); if (vsn.is_null()) return; @@ -2996,16 +3867,18 @@ void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_i if (pinfo.type == Variant::NODE_PATH) { Node *edited_scene = get_tree()->get_edited_scene_root(); - Node *script_node = _find_script_node(edited_scene, edited_scene, script); + if (edited_scene) { // Fixing an old crash bug ( Visual Script Crashes on editing NodePath with an empty scene open) + Node *script_node = _find_script_node(edited_scene, edited_scene, script); - if (script_node) { - //pick a node relative to the script, IF the script exists - pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE; - pinfo.hint_string = script_node->get_path(); - } else { - //pick a path relative to edited scene - pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE; - pinfo.hint_string = get_tree()->get_edited_scene_root()->get_path(); + if (script_node) { + //pick a node relative to the script, IF the script exists + pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE; + pinfo.hint_string = script_node->get_path(); + } else { + //pick a path relative to edited scene + pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE; + pinfo.hint_string = get_tree()->get_edited_scene_root()->get_path(); + } } } @@ -3032,18 +3905,9 @@ void VisualScriptEditor::_hide_timer() { hint_text->hide(); } -void VisualScriptEditor::_node_filter_changed(const String &p_text) { - - _update_available_nodes(); -} - void VisualScriptEditor::_notification(int p_what) { if (p_what == NOTIFICATION_READY || (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree())) { - - node_filter->set_right_icon(Control::get_icon("Search", "EditorIcons")); - node_filter->set_clear_button_enabled(true); - if (p_what == NOTIFICATION_READY) { variable_editor->connect("changed", this, "_update_members"); signal_editor->connect("changed", this, "_update_members"); @@ -3098,8 +3962,9 @@ void VisualScriptEditor::_graph_ofs_changed(const Vector2 &p_ofs) { updating_graph = true; - if (script->has_function(edited_func)) { - script->set_function_scroll(edited_func, graph->get_scroll_ofs() / EDSCALE); + // Just use the default func for all the properties that need to be handled for drawing rather than adding to the Visual Script Class + if (script->has_function(default_func)) { + script->set_function_scroll(default_func, graph->get_scroll_ofs() / EDSCALE); script->set_edited(true); } updating_graph = false; @@ -3110,7 +3975,9 @@ void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_ if (updating_graph) return; - Ref<VisualScriptComment> vsc = script->get_node(edited_func, p_node); + StringName func = _get_function_of_node(p_node); + + Ref<VisualScriptComment> vsc = script->get_node(func, p_node); if (vsc.is_null()) return; @@ -3128,7 +3995,7 @@ void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_ undo_redo->add_undo_method(vsc.ptr(), "set_size", vsc->get_size()); undo_redo->commit_action(); - gn->set_custom_minimum_size(p_new_size); //for this time since graph update is blocked + gn->set_custom_minimum_size(p_new_size); gn->set_size(Size2(1, 1)); graph->set_block_minimum_size_adjust(false); updating_graph = false; @@ -3148,7 +4015,8 @@ void VisualScriptEditor::_menu_option(int p_what) { if (gn) { if (gn->is_selected()) { int id = String(gn->get_name()).to_int(); - Ref<VisualScriptNode> vsn = script->get_node(edited_func, id); + StringName func = _get_function_of_node(id); + Ref<VisualScriptNode> vsn = script->get_node(func, id); if (vsn.is_valid()) { vsn->set_breakpoint(!vsn->is_breakpoint()); reselect.push_back(gn->get_name()); @@ -3170,28 +4038,30 @@ void VisualScriptEditor::_menu_option(int p_what) { } break; case EDIT_COPY_NODES: case EDIT_CUT_NODES: { - - if (!script->has_function(edited_func)) + if (!script->has_function(default_func)) break; clipboard->nodes.clear(); clipboard->data_connections.clear(); clipboard->sequence_connections.clear(); + Set<String> funcs; for (int i = 0; i < graph->get_child_count(); i++) { GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); if (gn) { if (gn->is_selected()) { int id = String(gn->get_name()).to_int(); - Ref<VisualScriptNode> node = script->get_node(edited_func, id); + StringName func = _get_function_of_node(id); + Ref<VisualScriptNode> node = script->get_node(func, id); if (Object::cast_to<VisualScriptFunction>(*node)) { EditorNode::get_singleton()->show_warning(TTR("Can't copy the function node.")); return; } if (node.is_valid()) { clipboard->nodes[id] = node->duplicate(true); - clipboard->nodes_positions[id] = script->get_node_position(edited_func, id); + clipboard->nodes_positions[id] = script->get_node_position(func, id); + funcs.insert(String(func)); } } } @@ -3200,37 +4070,38 @@ void VisualScriptEditor::_menu_option(int p_what) { if (clipboard->nodes.empty()) break; - List<VisualScript::SequenceConnection> sequence_connections; + for (Set<String>::Element *F = funcs.front(); F; F = F->next()) { + List<VisualScript::SequenceConnection> sequence_connections; - script->get_sequence_connection_list(edited_func, &sequence_connections); + script->get_sequence_connection_list(F->get(), &sequence_connections); - for (List<VisualScript::SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { + for (List<VisualScript::SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { - if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { + if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { - clipboard->sequence_connections.insert(E->get()); + clipboard->sequence_connections.insert(E->get()); + } } - } - List<VisualScript::DataConnection> data_connections; + List<VisualScript::DataConnection> data_connections; - script->get_data_connection_list(edited_func, &data_connections); + script->get_data_connection_list(F->get(), &data_connections); - for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { + for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { - if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { + if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { - clipboard->data_connections.insert(E->get()); + clipboard->data_connections.insert(E->get()); + } } } - if (p_what == EDIT_CUT_NODES) { _on_nodes_delete(); // oh yeah, also delete on cut } } break; case EDIT_PASTE_NODES: { - if (!script->has_function(edited_func)) + if (!script->has_function(default_func)) break; if (clipboard->nodes.empty()) { @@ -3248,11 +4119,15 @@ void VisualScriptEditor::_menu_option(int p_what) { Set<Vector2> existing_positions; { - List<int> nodes; - script->get_node_list(edited_func, &nodes); - for (List<int>::Element *E = nodes.front(); E; E = E->next()) { - Vector2 pos = script->get_node_position(edited_func, E->get()).snapped(Vector2(2, 2)); - existing_positions.insert(pos); + List<StringName> functions; + script->get_function_list(&functions); + for (List<StringName>::Element *F = functions.front(); F; F = F->next()) { + List<int> nodes; + script->get_node_list(F->get(), &nodes); + for (List<int>::Element *E = nodes.front(); E; E = E->next()) { + Vector2 pos = script->get_node_position(F->get(), E->get()).snapped(Vector2(2, 2)); + existing_positions.insert(pos); + } } } @@ -3271,20 +4146,20 @@ void VisualScriptEditor::_menu_option(int p_what) { paste_pos += Vector2(20, 20) * EDSCALE; } - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, node, paste_pos); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, node, paste_pos); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); } for (Set<VisualScript::SequenceConnection>::Element *E = clipboard->sequence_connections.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); + undo_redo->add_do_method(script.ptr(), "sequence_connect", default_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", default_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); } for (Set<VisualScript::DataConnection>::Element *E = clipboard->data_connections.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); + undo_redo->add_do_method(script.ptr(), "data_connect", default_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", default_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); } undo_redo->add_do_method(this, "_update_graph"); @@ -3300,6 +4175,275 @@ void VisualScriptEditor::_menu_option(int p_what) { } } } break; + case EDIT_CREATE_FUNCTION: { + + StringName function = ""; + Map<int, Ref<VisualScriptNode> > nodes; + Set<int> selections; + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + if (gn->is_selected()) { + int id = String(gn->get_name()).to_int(); + StringName func = _get_function_of_node(id); + Ref<VisualScriptNode> node = script->get_node(func, id); + if (Object::cast_to<VisualScriptFunction>(*node)) { + EditorNode::get_singleton()->show_warning(TTR("Can't create function with a function node.")); + return; + } + if (node.is_valid()) { + if (func != function && function != StringName("")) { + EditorNode::get_singleton()->show_warning(TTR("Can't create function of nodes from nodes of multiple functions.")); + return; + } + nodes.insert(id, node); + selections.insert(id); + function = func; + } + } + } + } + + if (nodes.size() == 0) { + return; // nothing to be done if there are no valid nodes selected + } + + Set<VisualScript::SequenceConnection> seqmove; + Set<VisualScript::DataConnection> datamove; + + Set<VisualScript::SequenceConnection> seqext; + Set<VisualScript::DataConnection> dataext; + + int start_node = -1; + Set<int> end_nodes; + if (nodes.size() == 1) { + Ref<VisualScriptNode> nd = script->get_node(function, nodes.front()->key()); + if (nd.is_valid() && nd->has_input_sequence_port()) + start_node = nodes.front()->key(); + else { + EditorNode::get_singleton()->show_warning(TTR("Select atleast one node with sequence port.")); + return; + } + } else { + List<VisualScript::SequenceConnection> seqs; + script->get_sequence_connection_list(function, &seqs); + + if (seqs.size() == 0) { + // in case there are no sequence connections + // select the top most node cause that's probably how + // the user wants to connect the nodes + int top_nd = -1; + Vector2 top; + for (Map<int, Ref<VisualScriptNode> >::Element *E = nodes.front(); E; E = E->next()) { + Ref<VisualScriptNode> nd = script->get_node(function, E->key()); + if (nd.is_valid() && nd->has_input_sequence_port()) { + if (top_nd < 0) { + top_nd = E->key(); + top = script->get_node_position(function, top_nd); + } + Vector2 pos = script->get_node_position(function, E->key()); + if (top.y > pos.y) { + top_nd = E->key(); + top = pos; + } + } + } + Ref<VisualScriptNode> nd = script->get_node(function, top_nd); + if (nd.is_valid() && nd->has_input_sequence_port()) + start_node = top_nd; + else { + EditorNode::get_singleton()->show_warning(TTR("Select atleast one node with sequence port.")); + return; + } + } else { + // pick the node with input sequence + Set<int> nodes_from; + Set<int> nodes_to; + for (List<VisualScript::SequenceConnection>::Element *E = seqs.front(); E; E = E->next()) { + if (nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) { + seqmove.insert(E->get()); + nodes_from.insert(E->get().from_node); + } else if (nodes.has(E->get().from_node) && !nodes.has(E->get().to_node)) { + seqext.insert(E->get()); + } else if (!nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) { + if (start_node == -1) { + seqext.insert(E->get()); + start_node = E->get().to_node; + } else { + EditorNode::get_singleton()->show_warning(TTR("Try to only have one sequence input in selection.")); + return; + } + } + nodes_to.insert(E->get().to_node); + } + + // to use to add return nodes + _get_ends(start_node, seqs, selections, end_nodes); + + if (start_node == -1) { + // if we still don't have a start node then + // run through the nodes and select the first tree node + // ie node without any input sequence but output sequence + for (Set<int>::Element *E = nodes_from.front(); E; E = E->next()) { + if (!nodes_to.has(E->get())) { + start_node = E->get(); + } + } + } + } + } + + if (start_node == -1) { + return; // this should not happen, but just in case something goes wrong + } + + List<Variant::Type> inputs; // input types + List<Pair<int, int> > input_connections; + { + List<VisualScript::DataConnection> dats; + script->get_data_connection_list(function, &dats); + for (List<VisualScript::DataConnection>::Element *E = dats.front(); E; E = E->next()) { + if (nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) { + datamove.insert(E->get()); + } else if (!nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) { + // add all these as inputs for the Function + Ref<VisualScriptNode> node = script->get_node(function, E->get().to_node); + if (node.is_valid()) { + dataext.insert(E->get()); + PropertyInfo pi = node->get_input_value_port_info(E->get().to_port); + inputs.push_back(pi.type); + input_connections.push_back(Pair<int, int>(E->get().to_node, E->get().to_port)); + } + } else if (nodes.has(E->get().from_node) && !nodes.has(E->get().to_node)) { + dataext.insert(E->get()); + } + } + } + + String new_fn = _validate_name("new_function"); + + Vector2 ofs = _get_available_pos(false, script->get_node_position(function, start_node) - Vector2(80, 150)); + + Ref<VisualScriptFunction> func_node; + func_node.instance(); + func_node->set_name(new_fn); + + undo_redo->create_action(TTR("Create Function")); + + undo_redo->add_do_method(script.ptr(), "add_function", new_fn); + int fn_id = script->get_available_id(); + undo_redo->add_do_method(script.ptr(), "add_node", new_fn, fn_id, func_node, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_function", new_fn); + undo_redo->add_do_method(this, "_update_members"); + undo_redo->add_undo_method(this, "_update_members"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); + + // Move the nodes + + for (Map<int, Ref<VisualScriptNode> >::Element *E = nodes.front(); E; E = E->next()) { + undo_redo->add_do_method(script.ptr(), "remove_node", function, E->key()); + undo_redo->add_do_method(script.ptr(), "add_node", new_fn, E->key(), E->get(), script->get_node_position(function, E->key())); + + // undo_redo->add_undo_method(script.ptr(), "remove_node", new_fn, E->key()); not needed cause we already remove the function :P + undo_redo->add_undo_method(script.ptr(), "add_node", function, E->key(), E->get(), script->get_node_position(function, E->key())); + } + + for (Set<VisualScript::SequenceConnection>::Element *E = seqmove.front(); E; E = E->next()) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, E->get().from_node, E->get().from_output, E->get().to_node); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", function, E->get().from_node, E->get().from_output, E->get().to_node); + } + + for (Set<VisualScript::DataConnection>::Element *E = datamove.front(); E; E = E->next()) { + undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", function, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + } + + // Add undo for external connections as well so that it's easier to revert back and forth + // these didn't require do methods as it's already handled internally by other do calls + for (Set<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) { + undo_redo->add_undo_method(script.ptr(), "sequence_connect", function, E->get().from_node, E->get().from_output, E->get().to_node); + } + for (Set<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) { + undo_redo->add_undo_method(script.ptr(), "data_connect", function, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + } + + // I don't really think we need support for non sequenced functions at this moment + undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, fn_id, 0, start_node); + + // end nodes are mapped to the return nodes with data connections if possible + int m = 1; + for (Set<int>::Element *G = end_nodes.front(); G; G = G->next()) { + Ref<VisualScriptReturn> ret_node; + ret_node.instance(); + + int ret_id = fn_id + (m++); + selections.insert(ret_id); + Vector2 ofsi = _get_available_pos(false, script->get_node_position(function, G->get()) + Vector2(80, -100)); + undo_redo->add_do_method(script.ptr(), "add_node", new_fn, ret_id, ret_node, ofsi); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_fn, ret_id); + + undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, G->get(), 0, ret_id); + // add data outputs from each of the end_nodes + Ref<VisualScriptNode> vsn = script->get_node(function, G->get()); + if (vsn.is_valid() && vsn->get_output_value_port_count() > 0) { + ret_node->set_enable_return_value(true); + // use the zeroth data port cause that's the likely one that is planned to be used + ret_node->set_return_type(vsn->get_output_value_port_info(0).type); + undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, G->get(), 0, ret_id, 0); + } + } + + // * might make the system more intelligent by checking port from info. + int i = 0; + List<Pair<int, int> >::Element *F = input_connections.front(); + for (List<Variant::Type>::Element *E = inputs.front(); E && F; E = E->next(), F = F->next()) { + func_node->add_argument(E->get(), "arg_" + String::num_int64(i), i); + undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, fn_id, i, F->get().first, F->get().second); + i++; // increment i + } + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + undo_redo->commit_action(); + + // make sure all Nodes get marked for selection so that they can be moved together + selections.insert(fn_id); + for (int k = 0; k < graph->get_child_count(); k++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(k)); + if (gn) { + int id = gn->get_name().operator String().to_int(); + gn->set_selected(selections.has(id)); + } + } + + // Ensure Preview Selection is of newly created function node + if (selections.size()) { + EditorNode::get_singleton()->push_item(func_node.ptr()); + } + + } break; + case REFRESH_GRAPH: { + _update_graph(); + } break; + } +} + +// this is likely going to be very slow and I am not sure if I should keep it +// but I hope that it will not be a problem considering that we won't be creating functions so frequently +// and cyclic connections would be a problem but hopefully we won't let them get to this point +void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes) { + for (const List<VisualScript::SequenceConnection>::Element *E = p_seqs.front(); E; E = E->next()) { + int from = E->get().from_node; + int to = E->get().to_node; + + if (from == p_node && p_selected.has(to)) { + // this is an interior connection move forward to the to node + _get_ends(to, p_seqs, p_selected, r_end_nodes); + } else if (from == p_node && !p_selected.has(to)) { + r_end_nodes.insert(from); + } } } @@ -3312,6 +4456,9 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) { member_popup->set_position(members->get_global_position() + p_pos); member_popup->set_size(Vector2()); + function_name_edit->set_position(members->get_global_position() + p_pos); + function_name_edit->set_size(Vector2()); + TreeItem *root = members->get_root(); Ref<Texture> del_icon = Control::get_icon("Remove", "EditorIcons"); @@ -3322,6 +4469,8 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) { member_type = MEMBER_FUNCTION; member_name = ti->get_text(0); + member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT); + member_popup->add_separator(); member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("visual_script_editor/delete_selected"), MEMBER_REMOVE); member_popup->popup(); return; @@ -3389,6 +4538,9 @@ void VisualScriptEditor::_member_option(int p_option) { undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); + } else if (p_option == MEMBER_EDIT) { + selected = members->get_selected()->get_text(0); + function_name_edit->popup(); } } break; case MEMBER_VARIABLE: { @@ -3425,7 +4577,6 @@ void VisualScriptEditor::_member_option(int p_option) { 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); @@ -3446,8 +4597,14 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_member_edited", &VisualScriptEditor::_member_edited); ClassDB::bind_method("_member_selected", &VisualScriptEditor::_member_selected); ClassDB::bind_method("_update_members", &VisualScriptEditor::_update_members); + ClassDB::bind_method("_members_gui_input", &VisualScriptEditor::_members_gui_input); + ClassDB::bind_method("_member_rmb_selected", &VisualScriptEditor::_member_rmb_selected); + ClassDB::bind_method("_member_option", &VisualScriptEditor::_member_option); + ClassDB::bind_method("_fn_name_box_input", &VisualScriptEditor::_fn_name_box_input); + ClassDB::bind_method("_change_base_type", &VisualScriptEditor::_change_base_type); ClassDB::bind_method("_change_base_type_callback", &VisualScriptEditor::_change_base_type_callback); + ClassDB::bind_method("_toggle_tool_script", &VisualScriptEditor::_toggle_tool_script); ClassDB::bind_method("_node_selected", &VisualScriptEditor::_node_selected); ClassDB::bind_method("_node_moved", &VisualScriptEditor::_node_moved); ClassDB::bind_method("_move_node", &VisualScriptEditor::_move_node); @@ -3456,7 +4613,14 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_remove_node", &VisualScriptEditor::_remove_node); ClassDB::bind_method("_update_graph", &VisualScriptEditor::_update_graph, DEFVAL(-1)); ClassDB::bind_method("_node_ports_changed", &VisualScriptEditor::_node_ports_changed); - ClassDB::bind_method("_available_node_doubleclicked", &VisualScriptEditor::_available_node_doubleclicked); + + ClassDB::bind_method("_create_function_dialog", &VisualScriptEditor::_create_function_dialog); + ClassDB::bind_method("_create_function", &VisualScriptEditor::_create_function); + ClassDB::bind_method("_add_node_dialog", &VisualScriptEditor::_add_node_dialog); + ClassDB::bind_method("_add_func_input", &VisualScriptEditor::_add_func_input); + ClassDB::bind_method("_remove_func_input", &VisualScriptEditor::_remove_func_input); + ClassDB::bind_method("_deselect_input_names", &VisualScriptEditor::_deselect_input_names); + ClassDB::bind_method("_default_value_edited", &VisualScriptEditor::_default_value_edited); ClassDB::bind_method("_default_value_changed", &VisualScriptEditor::_default_value_changed); ClassDB::bind_method("_menu_option", &VisualScriptEditor::_menu_option); @@ -3469,15 +4633,23 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_selected_new_virtual_method", &VisualScriptEditor::_selected_new_virtual_method); ClassDB::bind_method("_cancel_connect_node", &VisualScriptEditor::_cancel_connect_node); - ClassDB::bind_method("_create_new_node", &VisualScriptEditor::_create_new_node); + ClassDB::bind_method("_create_new_node_from_name", &VisualScriptEditor::_create_new_node_from_name); ClassDB::bind_method("_expression_text_changed", &VisualScriptEditor::_expression_text_changed); + ClassDB::bind_method("_add_input_port", &VisualScriptEditor::_add_input_port); + ClassDB::bind_method("_add_output_port", &VisualScriptEditor::_add_output_port); + ClassDB::bind_method("_remove_input_port", &VisualScriptEditor::_remove_input_port); + ClassDB::bind_method("_remove_output_port", &VisualScriptEditor::_remove_output_port); + ClassDB::bind_method("_change_port_type", &VisualScriptEditor::_change_port_type); + ClassDB::bind_method("_update_node_size", &VisualScriptEditor::_update_node_size); + ClassDB::bind_method("_port_name_focus_out", &VisualScriptEditor::_port_name_focus_out); ClassDB::bind_method("get_drag_data_fw", &VisualScriptEditor::get_drag_data_fw); ClassDB::bind_method("can_drop_data_fw", &VisualScriptEditor::can_drop_data_fw); ClassDB::bind_method("drop_data_fw", &VisualScriptEditor::drop_data_fw); ClassDB::bind_method("_input", &VisualScriptEditor::_input); - ClassDB::bind_method("_members_gui_input", &VisualScriptEditor::_members_gui_input); + ClassDB::bind_method("_graph_gui_input", &VisualScriptEditor::_graph_gui_input); + ClassDB::bind_method("_on_nodes_delete", &VisualScriptEditor::_on_nodes_delete); ClassDB::bind_method("_on_nodes_duplicate", &VisualScriptEditor::_on_nodes_duplicate); @@ -3488,17 +4660,10 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_graph_connect_to_empty", &VisualScriptEditor::_graph_connect_to_empty); ClassDB::bind_method("_update_graph_connections", &VisualScriptEditor::_update_graph_connections); - ClassDB::bind_method("_node_filter_changed", &VisualScriptEditor::_node_filter_changed); 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); - - ClassDB::bind_method("_update_available_nodes", &VisualScriptEditor::_update_available_nodes); - ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search); } @@ -3508,6 +4673,8 @@ VisualScriptEditor::VisualScriptEditor() { clipboard = memnew(Clipboard); } updating_graph = false; + saved_pos_dirty = false; + saved_position = Vector2(0, 0); edit_menu = memnew(MenuButton); edit_menu->set_text(TTR("Edit")); @@ -3519,7 +4686,9 @@ VisualScriptEditor::VisualScriptEditor() { edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/copy_nodes"), EDIT_COPY_NODES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/cut_nodes"), EDIT_CUT_NODES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/paste_nodes"), EDIT_PASTE_NODES); - + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/create_function"), EDIT_CREATE_FUNCTION); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/refresh_nodes"), REFRESH_GRAPH); edit_menu->get_popup()->connect("id_pressed", this, "_menu_option"); left_vsplit = memnew(VSplitContainer); @@ -3533,9 +4702,12 @@ VisualScriptEditor::VisualScriptEditor() { left_vb->set_v_size_flags(SIZE_EXPAND_FILL); //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); - base_type_select->connect("pressed", this, "_change_base_type"); + CheckButton *tool_script_check = memnew(CheckButton); + tool_script_check->set_text(TTR("Make Tool:")); + left_vb->add_child(tool_script_check); + tool_script_check->connect("pressed", this, "_toggle_tool_script"); + + /// Members /// members = memnew(Tree); left_vb->add_margin_child(TTR("Members:"), members, true); @@ -3544,31 +4716,29 @@ VisualScriptEditor::VisualScriptEditor() { members->connect("item_edited", this, "_member_edited"); members->connect("cell_selected", this, "_member_selected", varray(), CONNECT_DEFERRED); members->connect("gui_input", this, "_members_gui_input"); + members->connect("item_rmb_selected", this, "_member_rmb_selected"); + members->set_allow_rmb_select(true); members->set_allow_reselect(true); members->set_hide_folding(true); members->set_drag_forwarding(this); + member_popup = memnew(PopupMenu); + add_child(member_popup); + member_popup->connect("id_pressed", this, "_member_option"); + + function_name_edit = memnew(PopupDialog); + function_name_box = memnew(LineEdit); + function_name_edit->add_child(function_name_box); + function_name_edit->set_h_size_flags(SIZE_EXPAND); + function_name_box->connect("gui_input", this, "_fn_name_box_input"); + function_name_box->set_expand_to_text_length(true); + add_child(function_name_edit); + VBoxContainer *left_vb2 = memnew(VBoxContainer); left_vsplit->add_child(left_vb2); left_vb2->set_v_size_flags(SIZE_EXPAND_FILL); - VBoxContainer *vbc_nodes = memnew(VBoxContainer); - HBoxContainer *hbc_nodes = memnew(HBoxContainer); - node_filter = memnew(LineEdit); - node_filter->connect("text_changed", this, "_node_filter_changed"); - hbc_nodes->add_child(node_filter); - node_filter->set_h_size_flags(SIZE_EXPAND_FILL); - vbc_nodes->add_child(hbc_nodes); - - nodes = memnew(Tree); - vbc_nodes->add_child(nodes); - nodes->set_v_size_flags(SIZE_EXPAND_FILL); - - left_vb2->add_margin_child(TTR("Available Nodes:"), vbc_nodes, true); - - nodes->set_hide_root(true); - nodes->connect("item_activated", this, "_available_node_doubleclicked"); - nodes->set_drag_forwarding(this); + /// Actual Graph /// graph = memnew(GraphEdit); add_child(graph); @@ -3579,10 +4749,74 @@ VisualScriptEditor::VisualScriptEditor() { graph->connect("_end_node_move", this, "_end_node_move"); graph->connect("delete_nodes_request", this, "_on_nodes_delete"); graph->connect("duplicate_nodes_request", this, "_on_nodes_duplicate"); + graph->connect("gui_input", this, "_graph_gui_input"); graph->set_drag_forwarding(this); graph->hide(); graph->connect("scroll_offset_changed", this, "_graph_ofs_changed"); + /// Add Buttons to Top Bar / Zoom bar + HBoxContainer *graph_hbc = graph->get_zoom_hbox(); + + Label *base_lbl = memnew(Label); + base_lbl->set_text("Change Base Type: "); + graph_hbc->add_child(base_lbl); + + base_type_select = memnew(Button); + base_type_select->connect("pressed", this, "_change_base_type"); + graph_hbc->add_child(base_type_select); + + Button *add_nds = memnew(Button); + add_nds->set_text("Add Nodes..."); + graph_hbc->add_child(add_nds); + add_nds->connect("pressed", this, "_add_node_dialog"); + + Button *fn_btn = memnew(Button); + fn_btn->set_text("Add Function"); + graph_hbc->add_child(fn_btn); + fn_btn->connect("pressed", this, "_create_function_dialog"); + + // Add Function Dialog + VBoxContainer *function_vb = memnew(VBoxContainer); + function_vb->set_v_size_flags(SIZE_EXPAND_FILL); + + HBoxContainer *func_name_hbox = memnew(HBoxContainer); + function_vb->add_child(func_name_hbox); + + Label *func_name_label = memnew(Label); + func_name_label->set_text(TTR("Name:")); + func_name_hbox->add_child(func_name_label); + + func_name_box = memnew(LineEdit); + func_name_box->set_h_size_flags(SIZE_EXPAND_FILL); + func_name_box->set_placeholder(TTR("function_name")); + func_name_box->set_text(""); + func_name_box->connect("focus_entered", this, "_deselect_input_names"); + func_name_hbox->add_child(func_name_box); + + // add minor setting for function if needed, here! + + function_vb->add_child(memnew(HSeparator)); + + Button *add_input_button = memnew(Button); + add_input_button->set_h_size_flags(SIZE_EXPAND_FILL); + add_input_button->set_text(TTR("Add input +")); + add_input_button->connect("pressed", this, "_add_func_input"); + function_vb->add_child(add_input_button); + + func_input_vbox = memnew(VBoxContainer); + function_vb->add_child(func_input_vbox); + + function_vb->add_child(memnew(HSeparator)); + + function_create_dialog = memnew(ConfirmationDialog); + function_create_dialog->set_custom_minimum_size(Size2(450 * EDSCALE, 0)); + function_create_dialog->set_v_size_flags(SIZE_EXPAND_FILL); + function_create_dialog->set_title(TTR("Create Function")); + function_create_dialog->add_child(function_vb); + function_create_dialog->get_ok()->set_text(TTR("Create")); + function_create_dialog->get_ok()->connect("pressed", this, "_create_function"); + add_child(function_create_dialog); + select_func_text = memnew(Label); select_func_text->set_text(TTR("Select or create a function to edit its graph.")); select_func_text->set_align(Label::ALIGN_CENTER); @@ -3651,8 +4885,8 @@ VisualScriptEditor::VisualScriptEditor() { updating_members = false; - set_process_input(true); //for revert on drag - set_process_unhandled_input(true); //for revert on drag + set_process_input(true); + set_process_unhandled_input(true); default_value_edit = memnew(CustomPropertyEditor); add_child(default_value_edit); @@ -3665,20 +4899,13 @@ VisualScriptEditor::VisualScriptEditor() { new_connect_node_select = memnew(VisualScriptPropertySelector); add_child(new_connect_node_select); + new_connect_node_select->set_resizable(true); new_connect_node_select->connect("selected", this, "_selected_connect_node"); new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node"); new_virtual_method_select = memnew(VisualScriptPropertySelector); add_child(new_virtual_method_select); new_virtual_method_select->connect("selected", this, "_selected_new_virtual_method"); - - 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::get_singleton()->connect("custom_nodes_updated", this, "_update_available_nodes"); } VisualScriptEditor::~VisualScriptEditor() { @@ -3714,6 +4941,8 @@ static void register_editor_callback() { ED_SHORTCUT("visual_script_editor/copy_nodes", TTR("Copy Nodes"), KEY_MASK_CMD + KEY_C); ED_SHORTCUT("visual_script_editor/cut_nodes", TTR("Cut Nodes"), KEY_MASK_CMD + KEY_X); ED_SHORTCUT("visual_script_editor/paste_nodes", TTR("Paste Nodes"), KEY_MASK_CMD + KEY_V); + ED_SHORTCUT("visual_script_editor/create_function", TTR("Make Function"), KEY_MASK_CMD + KEY_G); + ED_SHORTCUT("visual_script_editor/refresh_nodes", TTR("Refresh Graph"), KEY_MASK_CMD + KEY_R); ED_SHORTCUT("visual_script_editor/edit_member", TTR("Edit Member"), KEY_MASK_CMD + KEY_E); } diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 4f302d1d72..fe27598d16 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -59,6 +59,8 @@ class VisualScriptEditor : public ScriptEditorBase { EDIT_COPY_NODES, EDIT_CUT_NODES, EDIT_PASTE_NODES, + EDIT_CREATE_FUNCTION, + REFRESH_GRAPH }; enum PortAction { @@ -86,10 +88,11 @@ class VisualScriptEditor : public ScriptEditorBase { Button *base_type_select; - GraphEdit *graph; + LineEdit *func_name_box; + VBoxContainer *func_input_vbox; + ConfirmationDialog *function_create_dialog; - LineEdit *node_filter; - TextureRect *node_filter_icon; + GraphEdit *graph; VisualScriptEditorSignalEdit *signal_editor; @@ -110,7 +113,8 @@ class VisualScriptEditor : public ScriptEditorBase { UndoRedo *undo_redo; Tree *members; - Tree *nodes; + PopupDialog *function_name_edit; + LineEdit *function_name_box; Label *hint_text; Timer *hint_text_timer; @@ -133,6 +137,7 @@ class VisualScriptEditor : public ScriptEditorBase { HashMap<StringName, Ref<StyleBox> > node_styles; StringName edited_func; + StringName default_func; void _update_graph_connections(); void _update_graph(int p_only_id = -1); @@ -165,9 +170,13 @@ class VisualScriptEditor : public ScriptEditorBase { int port_action_output; Vector2 port_action_pos; int port_action_new_node; - void _port_action_menu(int p_option); - void new_node(Ref<VisualScriptNode> vnode, Vector2 ofs); + bool saved_pos_dirty; + Vector2 saved_position; + + Vector2 mouse_up_position; + + void _port_action_menu(int p_option, const StringName &p_func); void connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id); @@ -175,23 +184,26 @@ class VisualScriptEditor : public ScriptEditorBase { void connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id); void _cancel_connect_node(); - void _create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point); + int _create_new_node_from_name(const String &p_text, const Vector2 &p_point, const StringName &p_func = StringName()); void _selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting); int error_line; void _node_selected(Node *p_node); - void _center_on_node(int p_id); + void _center_on_node(const StringName &p_func, int p_id); void _node_filter_changed(const String &p_text); void _change_base_type_callback(); void _change_base_type(); + void _toggle_tool_script(); void _member_selected(); void _member_edited(); void _begin_node_move(); void _end_node_move(); - void _move_node(String func, int p_id, const Vector2 &p_to); + void _move_node(const StringName &p_func, int p_id, const Vector2 &p_to); + + void _get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes); void _node_moved(Vector2 p_from, Vector2 p_to, int p_id); void _remove_node(int p_id); @@ -200,21 +212,44 @@ class VisualScriptEditor : public ScriptEditorBase { void _graph_connect_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_pos); void _node_ports_changed(const String &p_func, int p_id); - void _available_node_doubleclicked(); + void _node_create(); void _update_available_nodes(); void _member_button(Object *p_item, int p_column, int p_button); void _expression_text_changed(const String &p_text, int p_id); + void _add_input_port(int p_id); + void _add_output_port(int p_id); + void _remove_input_port(int p_id, int p_port); + void _remove_output_port(int p_id, int p_port); + void _change_port_type(int p_select, int p_id, int p_port, bool is_input); + void _update_node_size(int p_id); + void _port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input); - String revert_on_drag; + Vector2 _get_available_pos(bool centered = true, Vector2 pos = Vector2()) const; + StringName _get_function_of_node(int p_id) const; - void _input(const Ref<InputEvent> &p_event); + void _move_nodes_with_rescan(const StringName &p_func_from, const StringName &p_func_to, int p_id); + bool node_has_sequence_connections(const StringName &p_func, int p_id); - void _generic_search(String p_base_type = ""); + void _generic_search(String p_base_type = "", Vector2 pos = Vector2(), bool node_centered = false); + void _input(const Ref<InputEvent> &p_event); + void _graph_gui_input(const Ref<InputEvent> &p_event); void _members_gui_input(const Ref<InputEvent> &p_event); + void _fn_name_box_input(const Ref<InputEvent> &p_event); + void _rename_function(const String &p_name, const String &p_new_name); + + void _create_function_dialog(); + void _create_function(); + void _add_func_input(); + void _remove_func_input(Node *p_node); + void _deselect_input_names(); + void _add_node_dialog(); + void _node_item_selected(); + void _node_item_unselected(); + void _on_nodes_delete(); void _on_nodes_duplicate(); @@ -225,6 +260,10 @@ class VisualScriptEditor : public ScriptEditorBase { int editing_id; int editing_input; + bool can_swap; + int data_disconnect_node; + int data_disconnect_port; + void _default_value_changed(); void _default_value_edited(Node *p_button, int p_id, int p_input_port); @@ -239,7 +278,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _draw_color_over_button(Object *obj, Color p_color); void _button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, Variant p_ud); - VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &visited_nodes); + VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &p_visited_nodes); void _member_rmb_selected(const Vector2 &p_pos); void _member_option(int p_option); diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index c330fa1bc0..7f36549ae4 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -133,10 +133,12 @@ int VisualScriptFunctionCall::get_input_value_port_count() const { MethodBind *mb = ClassDB::get_method(_get_base_type(), function); if (mb) { - return mb->get_argument_count() + (call_mode == CALL_MODE_INSTANCE ? 1 : 0) + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) - use_default_args; + int defaulted_args = mb->get_argument_count() < use_default_args ? mb->get_argument_count() : use_default_args; + return mb->get_argument_count() + (call_mode == CALL_MODE_INSTANCE ? 1 : 0) + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) - defaulted_args; } - return method_cache.arguments.size() + (call_mode == CALL_MODE_INSTANCE ? 1 : 0) + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) - use_default_args; + int defaulted_args = method_cache.arguments.size() < use_default_args ? method_cache.arguments.size() : use_default_args; + return method_cache.arguments.size() + (call_mode == CALL_MODE_INSTANCE ? 1 : 0) + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) - defaulted_args; } } int VisualScriptFunctionCall::get_output_value_port_count() const { @@ -1056,13 +1058,6 @@ PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) cons if (call_mode == CALL_MODE_BASIC_TYPE) { return PropertyInfo(basic_type, "out"); } else if (call_mode == CALL_MODE_INSTANCE) { - List<PropertyInfo> props; - ClassDB::get_property_list(_get_base_type(), &props, true); - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - if (E->get().name == property) { - return PropertyInfo(E->get().type, "pass", PROPERTY_HINT_TYPE_STRING, E->get().hint_string); - } - } return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type()); } else { return PropertyInfo(); diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 65820b4c15..8c849b28c8 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -356,6 +356,441 @@ int VisualScriptFunction::get_stack_size() const { } ////////////////////////////////////////// +/////////////////LISTS//////////////////// +////////////////////////////////////////// + +int VisualScriptLists::get_output_sequence_port_count() const { + if (sequenced) + return 1; + return 0; +} +bool VisualScriptLists::has_input_sequence_port() const { + return sequenced; +} + +String VisualScriptLists::get_output_sequence_port_text(int p_port) const { + return ""; +} + +int VisualScriptLists::get_input_value_port_count() const { + return inputports.size(); +} +int VisualScriptLists::get_output_value_port_count() const { + return outputports.size(); +} + +PropertyInfo VisualScriptLists::get_input_value_port_info(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, inputports.size(), PropertyInfo()); + + PropertyInfo pi; + pi.name = inputports[p_idx].name; + pi.type = inputports[p_idx].type; + return pi; +} +PropertyInfo VisualScriptLists::get_output_value_port_info(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, outputports.size(), PropertyInfo()); + + PropertyInfo pi; + pi.name = outputports[p_idx].name; + pi.type = outputports[p_idx].type; + return pi; +} + +bool VisualScriptLists::is_input_port_editable() const { + return ((flags & INPUT_EDITABLE) == INPUT_EDITABLE); +} +bool VisualScriptLists::is_input_port_name_editable() const { + return ((flags & INPUT_NAME_EDITABLE) == INPUT_NAME_EDITABLE); +} +bool VisualScriptLists::is_input_port_type_editable() const { + return ((flags & INPUT_TYPE_EDITABLE) == INPUT_TYPE_EDITABLE); +} + +bool VisualScriptLists::is_output_port_editable() const { + return ((flags & OUTPUT_EDITABLE) == OUTPUT_EDITABLE); +} +bool VisualScriptLists::is_output_port_name_editable() const { + return ((flags & INPUT_NAME_EDITABLE) == INPUT_NAME_EDITABLE); +} +bool VisualScriptLists::is_output_port_type_editable() const { + return ((flags & INPUT_TYPE_EDITABLE) == INPUT_TYPE_EDITABLE); +} + +// for the inspector +bool VisualScriptLists::_set(const StringName &p_name, const Variant &p_value) { + + if (p_name == "input_count" && is_input_port_editable()) { + + int new_argc = p_value; + int argc = inputports.size(); + if (argc == new_argc) + return true; + + inputports.resize(new_argc); + + for (int i = argc; i < new_argc; i++) { + inputports.write[i].name = "arg" + itos(i + 1); + inputports.write[i].type = Variant::NIL; + } + ports_changed_notify(); + _change_notify(); + return true; + } + if (String(p_name).begins_with("input_") && is_input_port_editable()) { + int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1; + ERR_FAIL_INDEX_V(idx, inputports.size(), false); + String what = String(p_name).get_slice("/", 1); + if (what == "type") { + + Variant::Type new_type = Variant::Type(int(p_value)); + inputports.write[idx].type = new_type; + ports_changed_notify(); + + return true; + } + + if (what == "name") { + + inputports.write[idx].name = p_value; + ports_changed_notify(); + return true; + } + } + + if (p_name == "output_count" && is_output_port_editable()) { + + int new_argc = p_value; + int argc = outputports.size(); + if (argc == new_argc) + return true; + + outputports.resize(new_argc); + + for (int i = argc; i < new_argc; i++) { + outputports.write[i].name = "arg" + itos(i + 1); + outputports.write[i].type = Variant::NIL; + } + ports_changed_notify(); + _change_notify(); + return true; + } + if (String(p_name).begins_with("output_") && is_output_port_editable()) { + int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1; + ERR_FAIL_INDEX_V(idx, outputports.size(), false); + String what = String(p_name).get_slice("/", 1); + if (what == "type") { + + Variant::Type new_type = Variant::Type(int(p_value)); + outputports.write[idx].type = new_type; + ports_changed_notify(); + + return true; + } + + if (what == "name") { + + outputports.write[idx].name = p_value; + ports_changed_notify(); + return true; + } + } + + if (p_name == "sequenced/sequenced") { + sequenced = p_value; + ports_changed_notify(); + return true; + } + + return false; +} +bool VisualScriptLists::_get(const StringName &p_name, Variant &r_ret) const { + + if (p_name == "input_count" && is_input_port_editable()) { + r_ret = inputports.size(); + return true; + } + if (String(p_name).begins_with("input_") && is_input_port_editable()) { + int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1; + ERR_FAIL_INDEX_V(idx, inputports.size(), false); + String what = String(p_name).get_slice("/", 1); + if (what == "type") { + r_ret = inputports[idx].type; + return true; + } + if (what == "name") { + r_ret = inputports[idx].name; + return true; + } + } + + if (p_name == "output_count" && is_output_port_editable()) { + r_ret = outputports.size(); + return true; + } + if (String(p_name).begins_with("output_") && is_output_port_editable()) { + int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1; + ERR_FAIL_INDEX_V(idx, outputports.size(), false); + String what = String(p_name).get_slice("/", 1); + if (what == "type") { + r_ret = outputports[idx].type; + return true; + } + if (what == "name") { + r_ret = outputports[idx].name; + return true; + } + } + + if (p_name == "sequenced/sequenced") { + r_ret = sequenced; + return true; + } + + return false; +} +void VisualScriptLists::_get_property_list(List<PropertyInfo> *p_list) const { + + if (is_input_port_editable()) { + p_list->push_back(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,256")); + String argt = "Any"; + for (int i = 1; i < Variant::VARIANT_MAX; i++) { + argt += "," + Variant::get_type_name(Variant::Type(i)); + } + + for (int i = 0; i < inputports.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, "input_" + itos(i + 1) + "/type", PROPERTY_HINT_ENUM, argt)); + p_list->push_back(PropertyInfo(Variant::STRING, "input_" + itos(i + 1) + "/name")); + } + } + + if (is_output_port_editable()) { + p_list->push_back(PropertyInfo(Variant::INT, "output_count", PROPERTY_HINT_RANGE, "0,256")); + String argt = "Any"; + for (int i = 1; i < Variant::VARIANT_MAX; i++) { + argt += "," + Variant::get_type_name(Variant::Type(i)); + } + + for (int i = 0; i < outputports.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, "output_" + itos(i + 1) + "/type", PROPERTY_HINT_ENUM, argt)); + p_list->push_back(PropertyInfo(Variant::STRING, "output_" + itos(i + 1) + "/name")); + } + } + p_list->push_back(PropertyInfo(Variant::BOOL, "sequenced/sequenced")); +} + +// input data port interaction +void VisualScriptLists::add_input_data_port(Variant::Type p_type, const String &p_name, int p_index) { + + if (!is_input_port_editable()) + return; + + Port inp; + inp.name = p_name; + inp.type = p_type; + if (p_index >= 0) + inputports.insert(p_index, inp); + else + inputports.push_back(inp); + + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::set_input_data_port_type(int p_idx, Variant::Type p_type) { + + if (!is_input_port_type_editable()) + return; + + ERR_FAIL_INDEX(p_idx, inputports.size()); + + inputports.write[p_idx].type = p_type; + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::set_input_data_port_name(int p_idx, const String &p_name) { + + if (!is_input_port_name_editable()) + return; + + ERR_FAIL_INDEX(p_idx, inputports.size()); + + inputports.write[p_idx].name = p_name; + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::remove_input_data_port(int p_argidx) { + + if (!is_input_port_editable()) + return; + + ERR_FAIL_INDEX(p_argidx, inputports.size()); + + inputports.remove(p_argidx); + + ports_changed_notify(); + _change_notify(); +} + +// output data port interaction +void VisualScriptLists::add_output_data_port(Variant::Type p_type, const String &p_name, int p_index) { + + if (!is_output_port_editable()) + return; + + Port out; + out.name = p_name; + out.type = p_type; + if (p_index >= 0) + outputports.insert(p_index, out); + else + outputports.push_back(out); + + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::set_output_data_port_type(int p_idx, Variant::Type p_type) { + + if (!is_output_port_type_editable()) + return; + + ERR_FAIL_INDEX(p_idx, outputports.size()); + + outputports.write[p_idx].type = p_type; + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::set_output_data_port_name(int p_idx, const String &p_name) { + + if (!is_output_port_name_editable()) + return; + + ERR_FAIL_INDEX(p_idx, outputports.size()); + + outputports.write[p_idx].name = p_name; + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::remove_output_data_port(int p_argidx) { + + if (!is_output_port_editable()) + return; + + ERR_FAIL_INDEX(p_argidx, outputports.size()); + + outputports.remove(p_argidx); + + ports_changed_notify(); + _change_notify(); +} + +// sequences +void VisualScriptLists::set_sequenced(bool p_enable) { + if (sequenced == p_enable) + return; + sequenced = p_enable; + ports_changed_notify(); +} +bool VisualScriptLists::is_sequenced() const { + return sequenced; +} + +VisualScriptLists::VisualScriptLists() { + // initialize + sequenced = false; + flags = 0; +} + +void VisualScriptLists::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_input_data_port"), &VisualScriptLists::add_input_data_port); + ClassDB::bind_method(D_METHOD("set_input_data_port_name"), &VisualScriptLists::set_input_data_port_name); + ClassDB::bind_method(D_METHOD("set_input_data_port_type"), &VisualScriptLists::set_input_data_port_type); + ClassDB::bind_method(D_METHOD("remove_input_data_port"), &VisualScriptLists::remove_input_data_port); + + ClassDB::bind_method(D_METHOD("add_output_data_port"), &VisualScriptLists::add_output_data_port); + ClassDB::bind_method(D_METHOD("set_output_data_port_name"), &VisualScriptLists::set_output_data_port_name); + ClassDB::bind_method(D_METHOD("set_output_data_port_type"), &VisualScriptLists::set_output_data_port_type); + ClassDB::bind_method(D_METHOD("remove_output_data_port"), &VisualScriptLists::remove_output_data_port); +} + +////////////////////////////////////////// +//////////////COMPOSEARRAY//////////////// +////////////////////////////////////////// + +int VisualScriptComposeArray::get_output_sequence_port_count() const { + if (sequenced) + return 1; + return 0; +} +bool VisualScriptComposeArray::has_input_sequence_port() const { + return sequenced; +} + +String VisualScriptComposeArray::get_output_sequence_port_text(int p_port) const { + return ""; +} + +int VisualScriptComposeArray::get_input_value_port_count() const { + return inputports.size(); +} +int VisualScriptComposeArray::get_output_value_port_count() const { + return 1; +} + +PropertyInfo VisualScriptComposeArray::get_input_value_port_info(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, inputports.size(), PropertyInfo()); + + PropertyInfo pi; + pi.name = inputports[p_idx].name; + pi.type = inputports[p_idx].type; + return pi; +} +PropertyInfo VisualScriptComposeArray::get_output_value_port_info(int p_idx) const { + PropertyInfo pi; + pi.name = "out"; + pi.type = Variant::ARRAY; + return pi; +} + +String VisualScriptComposeArray::get_caption() const { + return "Compose Array"; +} +String VisualScriptComposeArray::get_text() const { + return ""; +} + +class VisualScriptComposeArrayNode : public VisualScriptNodeInstance { +public: + int input_count = 0; + 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) { + + if (input_count > 0) { + Array arr; + for (int i = 0; i < input_count; i++) + arr.push_back((*p_inputs[i])); + Variant va = Variant(arr); + + *p_outputs[0] = va; + } + + return 0; + } +}; + +VisualScriptNodeInstance *VisualScriptComposeArray::instance(VisualScriptInstance *p_instance) { + + VisualScriptComposeArrayNode *instance = memnew(VisualScriptComposeArrayNode); + instance->input_count = inputports.size(); + return instance; +} + +VisualScriptComposeArray::VisualScriptComposeArray() { + // initialize stuff here + sequenced = false; + flags = INPUT_EDITABLE; +} + +////////////////////////////////////////// ////////////////OPERATOR////////////////// ////////////////////////////////////////// @@ -3698,6 +4133,7 @@ void register_visual_script_nodes() { VisualScriptLanguage::singleton->add_register_func("operators/logic/select", create_node_generic<VisualScriptSelect>); VisualScriptLanguage::singleton->add_register_func("functions/deconstruct", create_node_generic<VisualScriptDeconstruct>); + VisualScriptLanguage::singleton->add_register_func("functions/compose_array", create_node_generic<VisualScriptComposeArray>); for (int i = 1; i < Variant::VARIANT_MAX; i++) { diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index 762a1bdfb6..c7354cb0d8 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -103,6 +103,103 @@ public: VisualScriptFunction(); }; +class VisualScriptLists : public VisualScriptNode { + + GDCLASS(VisualScriptLists, VisualScriptNode) + + struct Port { + String name; + Variant::Type type; + }; + +protected: + Vector<Port> inputports; + Vector<Port> outputports; + + enum { + OUTPUT_EDITABLE = 0x0001, + OUTPUT_NAME_EDITABLE = 0x0002, + OUTPUT_TYPE_EDITABLE = 0x0004, + INPUT_EDITABLE = 0x0008, + INPUT_NAME_EDITABLE = 0x000F, + INPUT_TYPE_EDITABLE = 0x0010, + }; + + int flags; + + bool sequenced; + + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + + static void _bind_methods(); + +public: + virtual bool is_output_port_editable() const; + virtual bool is_output_port_name_editable() const; + virtual bool is_output_port_type_editable() const; + + virtual bool is_input_port_editable() const; + virtual bool is_input_port_name_editable() const; + virtual bool is_input_port_type_editable() const; + + 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 = 0; + virtual String get_text() const = 0; + virtual String get_category() const = 0; + + void add_input_data_port(Variant::Type p_type, const String &p_name, int p_index = -1); + void set_input_data_port_type(int p_idx, Variant::Type p_type); + void set_input_data_port_name(int p_idx, const String &p_name); + void remove_input_data_port(int p_argidx); + + void add_output_data_port(Variant::Type p_type, const String &p_name, int p_index = -1); + void set_output_data_port_type(int p_idx, Variant::Type p_type); + void set_output_data_port_name(int p_idx, const String &p_name); + void remove_output_data_port(int p_argidx); + + void set_sequenced(bool p_enable); + bool is_sequenced() const; + + VisualScriptLists(); +}; + +class VisualScriptComposeArray : public VisualScriptLists { + + GDCLASS(VisualScriptComposeArray, VisualScriptLists) + +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 "functions"; } + + virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance); + + VisualScriptComposeArray(); +}; + class VisualScriptOperator : public VisualScriptNode { GDCLASS(VisualScriptOperator, VisualScriptNode); diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp index 764807cffd..0366801a11 100644 --- a/modules/visual_script/visual_script_property_selector.cpp +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -200,13 +200,10 @@ void VisualScriptPropertySelector::_update_search() { Object *obj = ObjectDB::get_instance(script); if (Object::cast_to<Script>(obj)) { - methods.push_back(MethodInfo("*Script Methods")); Object::cast_to<Script>(obj)->get_script_method_list(&methods); - - } else { - methods.push_back(MethodInfo("*" + String(E->get()))); - ClassDB::get_method_list(E->get(), &methods, true, true); } + + ClassDB::get_method_list(E->get(), &methods, true, true); } } for (List<MethodInfo>::Element *M = methods.front(); M; M = M->next()) { @@ -349,26 +346,37 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt continue; } - if (search_box->get_text() != String() && E->get().findn(search_box->get_text()) == -1) { + Vector<String> tx_filters = search_box->get_text().split(" "); + for (int i = 0; i < tx_filters.size(); i++) { + if (tx_filters[i] != String() && E->get().findn(tx_filters[i]) == -1) { + is_filter = true; + break; + } + } + if (is_filter) { continue; } TreeItem *item = search_options->create_item(root); - VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); + Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(E->get()); + Ref<VisualScriptOperator> vnode_operator = vnode; String type_name; - if (vnode_operator != NULL) { + if (vnode_operator.is_valid()) { String type; if (path.size() >= 2) { type = path[1]; } type_name = type.capitalize() + " "; } - VisualScriptFunctionCall *vnode_function_call = Object::cast_to<VisualScriptFunctionCall>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); - if (vnode_function_call != NULL) { + Ref<VisualScriptFunctionCall> vnode_function_call = vnode; + if (vnode_function_call.is_valid()) { String basic_type = Variant::get_type_name(vnode_function_call->get_basic_type()); type_name = basic_type.capitalize() + " "; } - - Vector<String> desc = path[path.size() - 1].replace("(", "( ").replace(")", " )").replace(",", ", ").split(" "); + Ref<VisualScriptConstructor> vnode_constructor = vnode; + if (vnode_constructor.is_valid()) { + type_name = "Construct "; + } + Vector<String> desc = path[path.size() - 1].replace("(", " ").replace(")", " ").replace(",", " ").split(" "); for (int i = 0; i < desc.size(); i++) { desc.write[i] = desc[i].capitalize(); if (desc[i].ends_with(",")) { @@ -504,7 +512,7 @@ void VisualScriptPropertySelector::_notification(int p_what) { } } -void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, const bool p_virtuals_only, const bool p_connecting) { +void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, const bool p_virtuals_only, const bool p_connecting, bool clear_text) { base_type = p_base; selected = p_current; @@ -515,7 +523,10 @@ void VisualScriptPropertySelector::select_method_from_base_type(const String &p_ virtuals_only = p_virtuals_only; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); connecting = p_connecting; @@ -526,7 +537,7 @@ void VisualScriptPropertySelector::set_type_filter(const Vector<Variant::Type> & type_filter = p_type_filter; } -void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only, bool p_seq_connect, const bool p_connecting) { +void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only, bool p_seq_connect, const bool p_connecting, bool clear_text) { base_type = p_base; selected = p_current; @@ -538,7 +549,10 @@ void VisualScriptPropertySelector::select_from_base_type(const String &p_base, c virtuals_only = p_virtuals_only; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); seq_connect = p_seq_connect; connecting = p_connecting; @@ -546,7 +560,7 @@ void VisualScriptPropertySelector::select_from_base_type(const String &p_base, c _update_search(); } -void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const String &p_current, const bool p_connecting) { +void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const String &p_current, const bool p_connecting, bool clear_text) { ERR_FAIL_COND(p_script.is_null()); base_type = p_script->get_instance_base_type(); @@ -559,7 +573,10 @@ void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_scrip virtuals_only = false; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); seq_connect = false; connecting = p_connecting; @@ -567,7 +584,7 @@ void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_scrip _update_search(); } -void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const String &p_current, const bool p_connecting) { +void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const String &p_current, const bool p_connecting, bool clear_text) { ERR_FAIL_COND(p_type == Variant::NIL); base_type = ""; selected = p_current; @@ -579,7 +596,10 @@ void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, virtuals_only = false; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); seq_connect = false; connecting = p_connecting; @@ -587,7 +607,7 @@ void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, _update_search(); } -void VisualScriptPropertySelector::select_from_action(const String &p_type, const String &p_current, const bool p_connecting) { +void VisualScriptPropertySelector::select_from_action(const String &p_type, const String &p_current, const bool p_connecting, bool clear_text) { base_type = p_type; selected = p_current; type = Variant::NIL; @@ -598,7 +618,10 @@ void VisualScriptPropertySelector::select_from_action(const String &p_type, cons virtuals_only = false; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); seq_connect = true; connecting = p_connecting; @@ -606,8 +629,8 @@ void VisualScriptPropertySelector::select_from_action(const String &p_type, cons _update_search(); } -void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const String &p_current, const bool p_connecting) { - base_type = ""; +void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const String &p_current, const bool p_connecting, const String &p_basetype, bool clear_text) { + base_type = p_basetype; selected = p_current; type = Variant::NIL; script = 0; @@ -617,7 +640,10 @@ void VisualScriptPropertySelector::select_from_instance(Object *p_instance, cons virtuals_only = false; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); seq_connect = false; connecting = p_connecting; @@ -625,7 +651,7 @@ void VisualScriptPropertySelector::select_from_instance(Object *p_instance, cons _update_search(); } -void VisualScriptPropertySelector::select_from_visual_script(const String &p_base, const bool p_connecting) { +void VisualScriptPropertySelector::select_from_visual_script(const String &p_base, const bool p_connecting, bool clear_text) { base_type = p_base; selected = ""; type = Variant::NIL; @@ -635,7 +661,10 @@ void VisualScriptPropertySelector::select_from_visual_script(const String &p_bas instance = NULL; virtuals_only = false; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); connecting = p_connecting; @@ -646,7 +675,7 @@ void VisualScriptPropertySelector::show_window(float p_screen_ratio) { Rect2 rect; Point2 window_size = get_viewport_rect().size; rect.size = (window_size * p_screen_ratio).floor(); - rect.size.x = rect.size.x / 1.25f; + rect.size.x = rect.size.x / 2.2f; rect.position = ((window_size - rect.size) / 2.0f).floor(); popup(rect); } diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h index 6235e4ba1d..13ce9bdca2 100644 --- a/modules/visual_script/visual_script_property_selector.h +++ b/modules/visual_script/visual_script_property_selector.h @@ -74,13 +74,13 @@ protected: static void _bind_methods(); public: - void select_method_from_base_type(const String &p_base, const String &p_current = "", const bool p_virtuals_only = false, const bool p_connecting = true); - void select_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false, bool p_seq_connect = false, const bool p_connecting = true); - void select_from_script(const Ref<Script> &p_script, const String &p_current = "", const bool p_connecting = true); - void select_from_basic_type(Variant::Type p_type, const String &p_current = "", const bool p_connecting = true); - void select_from_action(const String &p_type, const String &p_current = "", const bool p_connecting = true); - void select_from_instance(Object *p_instance, const String &p_current = "", const bool p_connecting = true); - void select_from_visual_script(const String &p_base, const bool p_connecting = true); + void select_method_from_base_type(const String &p_base, const String &p_current = "", const bool p_virtuals_only = false, const bool p_connecting = true, bool clear_text = true); + void select_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false, bool p_seq_connect = false, const bool p_connecting = true, bool clear_text = true); + void select_from_script(const Ref<Script> &p_script, const String &p_current = "", const bool p_connecting = true, bool clear_text = true); + void select_from_basic_type(Variant::Type p_type, const String &p_current = "", const bool p_connecting = true, bool clear_text = true); + void select_from_action(const String &p_type, const String &p_current = "", const bool p_connecting = true, bool clear_text = true); + void select_from_instance(Object *p_instance, const String &p_current = "", const bool p_connecting = true, const String &p_basetype = "", bool clear_text = true); + void select_from_visual_script(const String &p_base, const bool p_connecting = true, bool clear_text = true); void show_window(float p_screen_ratio); diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index 2f56e778b9..c330af60a4 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -242,10 +242,7 @@ Error AudioStreamPlaybackOGGVorbis::set_file(const String &p_file) { stream_valid = false; Error err; f = FileAccess::open(file, FileAccess::READ, &err); - - if (err) { - ERR_FAIL_COND_V(err, err); - } + ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + p_file + "'."); int errv = ov_open_callbacks(f, &vf, NULL, 0, _ov_callbacks); switch (errv) { @@ -294,9 +291,7 @@ Error AudioStreamPlaybackOGGVorbis::_load_stream() { Error err; f = FileAccess::open(file, FileAccess::READ, &err); - if (err) { - ERR_FAIL_COND_V(err, err); - } + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + file + "'."); int errv = ov_open_callbacks(f, &vf, NULL, 0, _ov_callbacks); switch (errv) { diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayer.xml index 2b0622fffa..605b1ef082 100644 --- a/modules/webrtc/doc_classes/WebRTCMultiplayer.xml +++ b/modules/webrtc/doc_classes/WebRTCMultiplayer.xml @@ -80,6 +80,10 @@ </description> </method> </methods> + <members> + <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" /> + <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="NetworkedMultiplayerPeer.TransferMode" default="2" /> + </members> <constants> </constants> </class> diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index b80a28e648..7070cfbdab 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -37,6 +37,10 @@ </description> </method> </methods> + <members> + <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" /> + <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="NetworkedMultiplayerPeer.TransferMode" default="2" /> + </members> <signals> <signal name="peer_packet"> <argument index="0" name="peer_source" type="int"> |