summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/assimp/editor_scene_importer_assimp.cpp109
-rw-r--r--modules/bmp/image_loader_bmp.cpp228
-rw-r--r--modules/csg/csg.cpp2
-rw-r--r--modules/csg/csg_gizmos.cpp2
-rw-r--r--modules/csg/csg_shape.cpp12
-rw-r--r--modules/cvtt/SCsub19
-rw-r--r--modules/dds/texture_loader_dds.cpp6
-rw-r--r--modules/enet/doc_classes/NetworkedMultiplayerENet.xml2
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp2
-rw-r--r--modules/etc/texture_loader_pkm.cpp4
-rw-r--r--modules/gdnative/doc_classes/GDNativeLibrary.xml15
-rw-r--r--modules/gdnative/gdnative.cpp2
-rw-r--r--modules/gdnative/gdnative/array.cpp9
-rw-r--r--modules/gdnative/gdnative_api.json11
-rw-r--r--modules/gdnative/include/gdnative/array.h2
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h4
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.cpp4
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.cpp4
-rw-r--r--modules/gdnative/register_types.cpp2
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.cpp16
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.h1
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml16
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp28
-rw-r--r--modules/gdscript/gdscript.cpp13
-rw-r--r--modules/gdscript/gdscript_compiler.cpp10
-rw-r--r--modules/gdscript/gdscript_function.cpp21
-rw-r--r--modules/gdscript/gdscript_functions.cpp44
-rw-r--r--modules/gdscript/gdscript_parser.cpp162
-rw-r--r--modules/gdscript/gdscript_parser.h8
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp2
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp113
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h8
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp12
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp4
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp90
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h4
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp35
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h4
-rw-r--r--modules/gdscript/language_server/lsp.hpp179
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp55
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h8
-rw-r--r--modules/jsonrpc/jsonrpc.cpp10
-rw-r--r--modules/mbedtls/crypto_mbedtls.cpp14
-rw-r--r--modules/mono/build_scripts/mono_configure.py2
-rw-r--r--modules/mono/class_db_api_json.cpp2
-rw-r--r--modules/mono/csharp_script.cpp6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs4
-rw-r--r--modules/mono/editor/bindings_generator.cpp80
-rw-r--r--modules/mono/editor/bindings_generator.h14
-rw-r--r--modules/mono/glue/Managed/Files/Rect2.cs8
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp72
-rw-r--r--modules/mono/mono_gd/gd_mono.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp2
-rw-r--r--modules/mono/utils/string_utils.cpp2
-rw-r--r--modules/opensimplex/doc_classes/NoiseTexture.xml1
-rw-r--r--modules/opus/audio_stream_opus.cpp8
-rw-r--r--modules/stb_vorbis/resource_importer_ogg_vorbis.cpp2
-rw-r--r--modules/theora/video_stream_theora.cpp2
-rw-r--r--modules/visual_script/config.py2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptComposeArray.xml15
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLists.xml95
-rw-r--r--modules/visual_script/register_types.cpp2
-rw-r--r--modules/visual_script/visual_script.cpp82
-rw-r--r--modules/visual_script/visual_script.h6
-rw-r--r--modules/visual_script/visual_script_editor.cpp2567
-rw-r--r--modules/visual_script/visual_script_editor.h70
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp13
-rw-r--r--modules/visual_script/visual_script_nodes.cpp455
-rw-r--r--modules/visual_script/visual_script_nodes.h97
-rw-r--r--modules/visual_script/visual_script_property_selector.cpp109
-rw-r--r--modules/visual_script/visual_script_property_selector.h14
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp9
-rw-r--r--modules/webrtc/doc_classes/WebRTCMultiplayer.xml4
-rw-r--r--modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml4
75 files changed, 3769 insertions, 1284 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/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml
index 7e1cac243a..aa48ab44f2 100644
--- a/modules/gdnative/doc_classes/GDNativeLibrary.xml
+++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml
@@ -1,35 +1,50 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.2">
<brief_description>
+ An external library containing functions or script classes to use in Godot.
</brief_description>
<description>
+ A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as [ARVRInterfaceGDNative]. The library must be compiled for each platform and architecture that the project will run on.
</description>
<tutorials>
+ <link>https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-cpp-example.html</link>
</tutorials>
<methods>
<method name="get_current_dependencies" qualifiers="const">
<return type="PoolStringArray">
</return>
<description>
+ Returns paths to all dependency libraries for the current platform and architecture.
</description>
</method>
<method name="get_current_library_path" qualifiers="const">
<return type="String">
</return>
<description>
+ Returns the path to the dynamic library file for the current platform and architecture.
</description>
</method>
</methods>
<members>
<member name="config_file" type="ConfigFile" setter="set_config_file" getter="get_config_file">
+ This resource in INI-style [ConfigFile] format, as in [code].gdnlib[/code] files.
</member>
<member name="load_once" type="bool" setter="set_load_once" getter="should_load_once" default="true">
+ If [code]true[/code], Godot loads only one copy of the library and each script that references the library will share static data like static or global variables.
+ If [code]false[/code], Godot loads a separate copy of the library into memory for each script that references it.
</member>
<member name="reloadable" type="bool" setter="set_reloadable" getter="is_reloadable" default="true">
+ If [code]true[/code], the editor will temporarily unload the library whenever the user switches away from the editor window, allowing the user to recompile the library without restarting Godot.
+ [b]Note:[/b] If the library defines tool scripts that run inside the editor, [code]reloadable[/code] must be [code]false[/code]. Otherwise, the editor will attempt to unload the tool scripts while they're in use and crash.
</member>
<member name="singleton" type="bool" setter="set_singleton" getter="is_singleton" default="false">
+ If [code]true[/code], Godot loads the library at startup rather than the first time a script uses the library, calling [code]{prefix}gdnative_singleton[/code] after initializing the library (where [code]{prefix}[/code] is the value of [member symbol_prefix]). The library remains loaded as long as Godot is running.
+ [b]Note:[/b] A singleton library cannot be [member reloadable].
</member>
<member name="symbol_prefix" type="String" setter="set_symbol_prefix" getter="get_symbol_prefix" default="&quot;godot_&quot;">
+ The prefix this library's entry point functions begin with. For example, a GDNativeLibrary would declare its [code]gdnative_init[/code] function as [code]godot_gdnative_init[/code] by default.
+ On platforms that require statically linking libraries (currently only iOS), each library must have a different [code]symbol_prefix[/code].
</member>
</members>
<constants>
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index 783ad4e147..ee9e71d4a0 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -339,7 +339,7 @@ bool GDNative::initialize() {
if (err || !library_init) {
OS::get_singleton()->close_dynamic_library(native_handle);
native_handle = NULL;
- ERR_PRINT("Failed to obtain godot_gdnative_init symbol");
+ ERR_PRINTS("Failed to obtain " + library->get_symbol_prefix() + "gdnative_init symbol");
return false;
}
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/register_types.cpp b/modules/gdnative/register_types.cpp
index 6ff6262b56..0194199133 100644
--- a/modules/gdnative/register_types.cpp
+++ b/modules/gdnative/register_types.cpp
@@ -277,7 +277,7 @@ void register_gdnative_types() {
proc_ptr);
if (err != OK) {
- ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton->get_library()->get_current_library_path()) + "\" found").utf8().get_data());
+ ERR_PRINTS("No " + lib->get_symbol_prefix() + "gdnative_singleton in \"" + singleton->get_library()->get_current_library_path() + "\" found");
} else {
singleton_gdnatives.push_back(singleton);
((void (*)())proc_ptr)();
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..1d0567dd8d 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -590,10 +590,10 @@
extends Sprite
var elapsed = 0.0
func _process(delta):
- var min_angle = deg2rad(0.0)
- var max_angle = deg2rad(90.0)
- rotation = lerp_angle(min_angle, max_angle, elapsed)
- elapsed += delta
+ var min_angle = deg2rad(0.0)
+ var max_angle = deg2rad(90.0)
+ rotation = lerp_angle(min_angle, max_angle, elapsed)
+ elapsed += delta
[/codeblock]
</description>
</method>
@@ -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/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 963b40529d..4d6279074c 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -247,7 +247,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
in_function_args = false;
}
- if (expect_type && prev_is_char) {
+ if (expect_type && (prev_is_char || str[j] == '=')) {
expect_type = false;
}
@@ -364,20 +364,28 @@ void GDScriptSyntaxHighlighter::_update_cache() {
number_color = text_editor->get_color("number_color");
member_color = text_editor->get_color("member_variable_color");
- EditorSettings *settings = EditorSettings::get_singleton();
- String text_editor_color_theme = settings->get("text_editor/theme/color_theme");
+ const String text_editor_color_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
+ const bool default_theme = text_editor_color_theme == "Default";
- bool default_theme = text_editor_color_theme == "Default";
- bool dark_theme = settings->is_dark_theme();
-
- function_definition_color = default_theme ? Color(0.0, 0.88, 1.0) : dark_theme ? Color(0.0, 0.88, 1.0) : Color(0.0, 0.65, 0.73);
- node_path_color = default_theme ? Color(0.39, 0.76, 0.35) : dark_theme ? Color(0.39, 0.76, 0.35) : Color(0.32, 0.55, 0.29);
+ if (default_theme || EditorSettings::get_singleton()->is_dark_theme()) {
+ function_definition_color = Color(0.4, 0.9, 1.0);
+ node_path_color = Color(0.39, 0.76, 0.35);
+ } else {
+ function_definition_color = Color(0.0, 0.65, 0.73);
+ node_path_color = Color(0.32, 0.55, 0.29);
+ }
EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color);
EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color);
if (text_editor_color_theme == "Adaptive" || default_theme) {
- settings->set_initial_value("text_editor/highlighting/gdscript/function_definition_color", function_definition_color, true);
- settings->set_initial_value("text_editor/highlighting/gdscript/node_path_color", node_path_color, true);
+ EditorSettings::get_singleton()->set_initial_value(
+ "text_editor/highlighting/gdscript/function_definition_color",
+ function_definition_color,
+ true);
+ EditorSettings::get_singleton()->set_initial_value(
+ "text_editor/highlighting/gdscript/node_path_color",
+ node_path_color,
+ true);
}
function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color");
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..83d02e4977 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -1419,7 +1419,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (!container->iter_init(*counter, valid)) {
#ifdef DEBUG_ENABLED
if (!valid) {
- err_text = "Unable to iterate on object of type " + Variant::get_type_name(container->get_type()) + "'.";
+ err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "'.";
OPCODE_BREAK;
}
#endif
@@ -1432,7 +1432,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
*iterator = container->iter_get(*counter, valid);
#ifdef DEBUG_ENABLED
if (!valid) {
- err_text = "Unable to obtain iterator object of type " + Variant::get_type_name(container->get_type()) + "'.";
+ err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "'.";
OPCODE_BREAK;
}
#endif
@@ -1452,7 +1452,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (!container->iter_next(*counter, valid)) {
#ifdef DEBUG_ENABLED
if (!valid) {
- err_text = "Unable to iterate on object of type " + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?).";
+ err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?).";
OPCODE_BREAK;
}
#endif
@@ -1465,7 +1465,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
*iterator = container->iter_get(*counter, valid);
#ifdef DEBUG_ENABLED
if (!valid) {
- err_text = "Unable to obtain iterator object of type " + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?).";
+ err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?).";
OPCODE_BREAK;
}
#endif
@@ -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_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index 97790e00bb..d9535d0f1f 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -572,37 +572,31 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
} break;
case OBJ_WEAKREF: {
VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::OBJECT) {
-
+ if (p_args[0]->get_type() == Variant::OBJECT) {
+ if (p_args[0]->is_ref()) {
+ Ref<WeakRef> wref = memnew(WeakRef);
+ REF r = *p_args[0];
+ if (r.is_valid()) {
+ wref->set_ref(r);
+ }
+ r_ret = wref;
+ } else {
+ Ref<WeakRef> wref = memnew(WeakRef);
+ Object *obj = *p_args[0];
+ if (obj) {
+ wref->set_obj(obj);
+ }
+ r_ret = wref;
+ }
+ } else if (p_args[0]->get_type() == Variant::NIL) {
+ r_ret = memnew(WeakRef);
+ } else {
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::OBJECT;
r_ret = Variant();
return;
}
-
- if (p_args[0]->is_ref()) {
-
- REF r = *p_args[0];
- if (!r.is_valid()) {
- r_ret = Variant();
- return;
- }
-
- Ref<WeakRef> wref = memnew(WeakRef);
- wref->set_ref(r);
- r_ret = wref;
- } else {
- Object *obj = *p_args[0];
- if (!obj) {
- r_ret = Variant();
- return;
- }
- Ref<WeakRef> wref = memnew(WeakRef);
- wref->set_obj(obj);
- r_ret = wref;
- }
-
} break;
case FUNC_FUNCREF: {
VALIDATE_ARG_COUNT(2);
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..03d731a5f0 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -105,6 +105,40 @@ void ExtendGDScriptParser::update_symbols() {
}
}
+void ExtendGDScriptParser::update_document_links(const String &p_code) {
+ document_links.clear();
+
+ GDScriptTokenizerText tokenizer;
+ FileAccessRef fs = FileAccess::create(FileAccess::ACCESS_RESOURCES);
+ tokenizer.set_code(p_code);
+ while (true) {
+ if (tokenizer.get_token() == GDScriptTokenizer::TK_EOF) {
+ break;
+ } else if (tokenizer.get_token() == GDScriptTokenizer::TK_CONSTANT) {
+ Variant const_val = tokenizer.get_token_constant();
+ if (const_val.get_type() == Variant::STRING) {
+ String path = const_val;
+ bool exists = fs->file_exists(path);
+ if (!exists) {
+ path = get_path().get_base_dir() + "/" + path;
+ exists = fs->file_exists(path);
+ }
+ if (exists) {
+ String value = const_val;
+ lsp::DocumentLink link;
+ link.target = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_uri(path);
+ link.range.start.line = LINE_NUMBER_TO_INDEX(tokenizer.get_token_line());
+ link.range.end.line = link.range.start.line;
+ link.range.end.character = LINE_NUMBER_TO_INDEX(tokenizer.get_token_column());
+ link.range.start.character = link.range.end.character - value.length();
+ document_links.push_back(link);
+ }
+ }
+ }
+ tokenizer.advance();
+ }
+}
+
void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p_class, lsp::DocumentSymbol &r_symbol) {
const String uri = get_uri();
@@ -123,7 +157,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
r_symbol.selectionRange.start.line = r_symbol.range.start.line;
r_symbol.detail = "class " + r_symbol.name;
bool is_root_class = &r_symbol == &class_symbol;
- r_symbol.documentation = parse_documentation_as_markdown(is_root_class ? 0 : LINE_NUMBER_TO_INDEX(p_class->line), is_root_class);
+ r_symbol.documentation = parse_documentation(is_root_class ? 0 : LINE_NUMBER_TO_INDEX(p_class->line), is_root_class);
for (int i = 0; i < p_class->variables.size(); ++i) {
@@ -150,7 +184,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
symbol.detail += " = " + JSON::print(m.default_value);
}
- symbol.documentation = parse_documentation_as_markdown(line);
+ symbol.documentation = parse_documentation(line);
symbol.uri = uri;
symbol.script_path = path;
@@ -170,7 +204,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
symbol.range.end.line = symbol.range.start.line;
symbol.range.end.character = lines[line].length();
symbol.selectionRange.start.line = symbol.range.start.line;
- symbol.documentation = parse_documentation_as_markdown(line);
+ symbol.documentation = parse_documentation(line);
symbol.uri = uri;
symbol.script_path = path;
symbol.detail = "signal " + signal.name + "(";
@@ -189,6 +223,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;
@@ -198,7 +233,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
symbol.range.end.line = symbol.range.start.line;
symbol.range.end.character = lines[line].length();
symbol.selectionRange.start.line = symbol.range.start.line;
- symbol.documentation = parse_documentation_as_markdown(line);
+ symbol.documentation = parse_documentation(line);
symbol.uri = uri;
symbol.script_path = path;
@@ -266,7 +301,7 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
r_symbol.range.end.line = MAX(p_func->body->end_line - 2, p_func->body->line);
r_symbol.range.end.character = lines[r_symbol.range.end.line].length();
r_symbol.selectionRange.start.line = r_symbol.range.start.line;
- r_symbol.documentation = parse_documentation_as_markdown(line);
+ r_symbol.documentation = parse_documentation(line);
r_symbol.uri = uri;
r_symbol.script_path = path;
@@ -324,67 +359,11 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
if (var->datatype.kind != GDScriptParser::DataType::UNRESOLVED) {
symbol.detail += ": " + var->datatype.to_string();
}
- symbol.documentation = parse_documentation_as_markdown(line);
+ symbol.documentation = parse_documentation(line);
r_symbol.children.push_back(symbol);
}
}
-String ExtendGDScriptParser::marked_documentation(const String &p_bbcode) {
-
- String markdown = p_bbcode.strip_edges();
-
- Vector<String> lines = markdown.split("\n");
- bool in_code_block = false;
- int code_block_indent = -1;
-
- markdown = "";
- for (int i = 0; i < lines.size(); i++) {
- String line = lines[i];
- int block_start = line.find("[codeblock]");
- if (block_start != -1) {
- code_block_indent = block_start;
- in_code_block = true;
- line = "'''gdscript";
- line = "\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";
- in_code_block = false;
- }
-
- if (!in_code_block) {
- line = line.strip_edges();
- line = line.replace("[code]", "`");
- line = line.replace("[/code]", "`");
- line = line.replace("[i]", "*");
- line = line.replace("[/i]", "*");
- line = line.replace("[b]", "**");
- line = line.replace("[/b]", "**");
- line = line.replace("[u]", "__");
- line = line.replace("[/u]", "__");
- line = line.replace("[method ", "`");
- line = line.replace("[member ", "`");
- line = line.replace("[signal ", "`");
- line = line.replace("[enum ", "`");
- line = line.replace("[constant ", "`");
- line = line.replace("[", "`");
- line = line.replace("]", "`");
- }
-
- if (!in_code_block && i < lines.size() - 1) {
- line += "\n\n";
- } else if (i < lines.size() - 1) {
- line += "\n";
- }
- markdown += line;
- }
- return markdown;
-}
-
String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) {
ERR_FAIL_INDEX_V(p_line, lines.size(), String());
@@ -543,10 +522,6 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(i
return ret;
}
-String ExtendGDScriptParser::parse_documentation_as_markdown(int p_line, bool p_docs_down) {
- return marked_documentation(parse_documentation(p_line, p_docs_down));
-}
-
const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int p_line) const {
if (p_line <= 0) {
return &class_symbol;
@@ -573,6 +548,10 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String
return NULL;
}
+const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const {
+ return document_links;
+}
+
const Array &ExtendGDScriptParser::get_member_completions() {
if (member_completions.empty()) {
@@ -674,6 +653,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();
@@ -755,5 +735,6 @@ Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) {
Error err = GDScriptParser::parse(p_code, p_path.get_base_dir(), false, p_path, false, NULL, false);
update_diagnostics();
update_symbols();
+ update_document_links(p_code);
return err;
}
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h
index dd0453d3ff..a6e0ca5534 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.h
+++ b/modules/gdscript/language_server/gdscript_extend_parser.h
@@ -56,12 +56,14 @@ class ExtendGDScriptParser : public GDScriptParser {
lsp::DocumentSymbol class_symbol;
Vector<lsp::Diagnostic> diagnostics;
+ List<lsp::DocumentLink> document_links;
ClassMembers members;
HashMap<String, ClassMembers> inner_classes;
void update_diagnostics();
void update_symbols();
+ void update_document_links(const String &p_code);
void parse_class_symbol(const GDScriptParser::ClassNode *p_class, lsp::DocumentSymbol &r_symbol);
void parse_function_symbol(const GDScriptParser::FunctionNode *p_func, lsp::DocumentSymbol &r_symbol);
@@ -73,11 +75,6 @@ class ExtendGDScriptParser : public GDScriptParser {
Array member_completions;
- String parse_documentation_as_markdown(int p_line, bool p_docs_down = false);
-
-public:
- static String marked_documentation(const String &p_bbcode);
-
public:
_FORCE_INLINE_ const String &get_path() const { return path; }
_FORCE_INLINE_ const Vector<String> &get_lines() const { return lines; }
@@ -93,6 +90,7 @@ public:
const lsp::DocumentSymbol *get_symbol_defined_at_line(int p_line) const;
const lsp::DocumentSymbol *get_member_symbol(const String &p_name, const String &p_subclass = "") const;
+ const List<lsp::DocumentLink> &get_document_links() const;
const Array &get_member_completions();
Dictionary generate_api() const;
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index afe461b68e..ae2aaf6aee 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");
@@ -151,7 +153,13 @@ Error GDScriptLanguageProtocol::start(int p_port) {
}
void GDScriptLanguageProtocol::stop() {
+ const int *ptr = clients.next(NULL);
+ while (ptr) {
+ clients.get(*ptr)->close();
+ ptr = clients.next(ptr);
+ }
server->stop();
+ clients.clear();
}
void GDScriptLanguageProtocol::notify_all_clients(const String &p_method, const Variant &p_params) {
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..b83db718b8 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -39,6 +39,7 @@
void GDScriptTextDocument::_bind_methods() {
ClassDB::bind_method(D_METHOD("didOpen"), &GDScriptTextDocument::didOpen);
ClassDB::bind_method(D_METHOD("didChange"), &GDScriptTextDocument::didChange);
+ ClassDB::bind_method(D_METHOD("nativeSymbol"), &GDScriptTextDocument::nativeSymbol);
ClassDB::bind_method(D_METHOD("documentSymbol"), &GDScriptTextDocument::documentSymbol);
ClassDB::bind_method(D_METHOD("completion"), &GDScriptTextDocument::completion);
ClassDB::bind_method(D_METHOD("resolve"), &GDScriptTextDocument::resolve);
@@ -75,6 +76,11 @@ lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_
return doc;
}
+void GDScriptTextDocument::notify_client_show_symbol(const lsp::DocumentSymbol *symbol) {
+ ERR_FAIL_NULL(symbol);
+ GDScriptLanguageProtocol::get_singleton()->notify_client("gdscript/show_native_symbol", symbol->to_json(true));
+}
+
void GDScriptTextDocument::initialize() {
if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
@@ -102,6 +108,21 @@ void GDScriptTextDocument::initialize() {
}
}
+Variant GDScriptTextDocument::nativeSymbol(const Dictionary &p_params) {
+
+ Variant ret;
+
+ lsp::NativeSymbolInspectParams params;
+ params.load(p_params);
+
+ if (const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_native_symbol(params)) {
+ ret = symbol->to_json(true);
+ notify_client_show_symbol(symbol);
+ }
+
+ return ret;
+}
+
Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) {
Dictionary params = p_params["textDocument"];
String uri = params["uri"];
@@ -271,8 +292,17 @@ Array GDScriptTextDocument::codeLens(const Dictionary &p_params) {
return arr;
}
-Variant GDScriptTextDocument::documentLink(const Dictionary &p_params) {
- Variant ret;
+Array GDScriptTextDocument::documentLink(const Dictionary &p_params) {
+ Array ret;
+
+ lsp::DocumentLinkParams params;
+ params.load(p_params);
+
+ List<lsp::DocumentLink> links;
+ GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_document_links(params.textDocument.uri, links);
+ for (const List<lsp::DocumentLink>::Element *E = links.front(); E; E = E->next()) {
+ ret.push_back(E->get().to_json());
+ }
return ret;
}
@@ -326,31 +356,35 @@ Array GDScriptTextDocument::definition(const Dictionary &p_params) {
const String &path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(symbol->uri);
if (file_checker->file_exists(path)) {
arr.push_back(location.to_json());
- } else if (!symbol->native_class.empty() && GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) {
- String id;
- switch (symbol->kind) {
- case lsp::SymbolKind::Class:
- id = "class_name:" + symbol->name;
- break;
- case lsp::SymbolKind::Constant:
- id = "class_constant:" + symbol->native_class + ":" + symbol->name;
- break;
- case lsp::SymbolKind::Property:
- case lsp::SymbolKind::Variable:
- id = "class_property:" + symbol->native_class + ":" + symbol->name;
- break;
- case lsp::SymbolKind::Enum:
- id = "class_enum:" + symbol->native_class + ":" + symbol->name;
- break;
- case lsp::SymbolKind::Method:
- case lsp::SymbolKind::Function:
- id = "class_method:" + symbol->native_class + ":" + symbol->name;
- break;
- default:
- id = "class_global:" + symbol->native_class + ":" + symbol->name;
- break;
+ } else if (!symbol->native_class.empty()) {
+ if (GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) {
+ String id;
+ switch (symbol->kind) {
+ case lsp::SymbolKind::Class:
+ id = "class_name:" + symbol->name;
+ break;
+ case lsp::SymbolKind::Constant:
+ id = "class_constant:" + symbol->native_class + ":" + symbol->name;
+ break;
+ case lsp::SymbolKind::Property:
+ case lsp::SymbolKind::Variable:
+ id = "class_property:" + symbol->native_class + ":" + symbol->name;
+ break;
+ case lsp::SymbolKind::Enum:
+ id = "class_enum:" + symbol->native_class + ":" + symbol->name;
+ break;
+ case lsp::SymbolKind::Method:
+ case lsp::SymbolKind::Function:
+ id = "class_method:" + symbol->native_class + ":" + symbol->name;
+ break;
+ default:
+ id = "class_global:" + symbol->native_class + ":" + symbol->name;
+ break;
+ }
+ call_deferred("show_native_symbol_in_editor", id);
+ } else {
+ notify_client_show_symbol(symbol);
}
- call_deferred("show_native_symbol_in_editor", id);
}
} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
@@ -380,8 +414,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/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index d15022d2c4..235e2c3f6e 100644
--- a/modules/gdscript/language_server/gdscript_text_document.h
+++ b/modules/gdscript/language_server/gdscript_text_document.h
@@ -52,14 +52,16 @@ protected:
private:
lsp::TextDocumentItem load_document_item(const Variant &p_param);
+ void notify_client_show_symbol(const lsp::DocumentSymbol *symbol);
public:
+ Variant nativeSymbol(const Dictionary &p_params);
Array documentSymbol(const Dictionary &p_params);
Array completion(const Dictionary &p_params);
Dictionary resolve(const Dictionary &p_params);
Array foldingRange(const Dictionary &p_params);
Array codeLens(const Dictionary &p_params);
- Variant documentLink(const Dictionary &p_params);
+ Array documentLink(const Dictionary &p_params);
Array colorPresentation(const Dictionary &p_params);
Variant hover(const Dictionary &p_params);
Array definition(const Dictionary &p_params);
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 1901daacff..6baa7e4219 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -198,7 +198,7 @@ Error GDScriptWorkspace::initialize() {
if (!class_data.inherits.empty()) {
class_symbol.detail += " extends " + class_data.inherits;
}
- class_symbol.documentation = ExtendGDScriptParser::marked_documentation(class_data.brief_description) + "\n" + ExtendGDScriptParser::marked_documentation(class_data.description);
+ class_symbol.documentation = class_data.brief_description + "\n" + class_data.description;
for (int i = 0; i < class_data.constants.size(); i++) {
const DocData::ConstantDoc &const_data = class_data.constants[i];
@@ -211,7 +211,7 @@ Error GDScriptWorkspace::initialize() {
symbol.detail += ": " + const_data.enumeration;
}
symbol.detail += " = " + const_data.value;
- symbol.documentation = ExtendGDScriptParser::marked_documentation(const_data.description);
+ symbol.documentation = const_data.description;
class_symbol.children.push_back(symbol);
}
@@ -232,7 +232,7 @@ Error GDScriptWorkspace::initialize() {
} else {
symbol.detail += ": " + data.type;
}
- symbol.documentation = ExtendGDScriptParser::marked_documentation(data.description);
+ symbol.documentation = data.description;
class_symbol.children.push_back(symbol);
}
@@ -270,7 +270,7 @@ Error GDScriptWorkspace::initialize() {
}
symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + data.return_type;
- symbol.documentation = ExtendGDScriptParser::marked_documentation(data.description);
+ symbol.documentation = data.description;
class_symbol.children.push_back(symbol);
}
@@ -475,6 +475,33 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
}
}
+const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) {
+
+ if (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(p_params.native_class)) {
+ const lsp::DocumentSymbol &symbol = E->get();
+ if (p_params.symbol_name.empty() || p_params.symbol_name == symbol.name) {
+ return &symbol;
+ }
+
+ for (int i = 0; i < symbol.children.size(); ++i) {
+ if (symbol.children[i].name == p_params.symbol_name) {
+ return &(symbol.children[i]);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void GDScriptWorkspace::resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list) {
+ if (const ExtendGDScriptParser *parser = get_parse_successed_script(get_file_path(p_uri))) {
+ const List<lsp::DocumentLink> &links = parser->get_document_links();
+ for (const List<lsp::DocumentLink>::Element *E = links.front(); E; E = E->next()) {
+ r_list.push_back(E->get());
+ }
+ }
+}
+
Dictionary GDScriptWorkspace::generate_script_api(const String &p_path) {
Dictionary api;
if (const ExtendGDScriptParser *parser = get_parse_successed_script(p_path)) {
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index adce169d4b..a416ae1075 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -53,7 +53,6 @@ protected:
ExtendGDScriptParser *get_parse_successed_script(const String &p_path);
ExtendGDScriptParser *get_parse_result(const String &p_path);
- void strip_flat_symbols(const String &p_branch);
void list_script_files(const String &p_root_dir, List<String> &r_files);
public:
@@ -81,7 +80,8 @@ public:
const lsp::DocumentSymbol *resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name = "", bool p_func_requred = false);
void resolve_related_symbols(const lsp::TextDocumentPositionParams &p_doc_pos, List<const lsp::DocumentSymbol *> &r_list);
-
+ const lsp::DocumentSymbol *resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params);
+ void resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list);
Dictionary generate_script_api(const String &p_path);
GDScriptWorkspace();
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 3e57b6ee7e..61a0980c41 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -37,6 +37,9 @@ namespace lsp {
typedef String DocumentUri;
+/** Format BBCode documentation from DocData to markdown */
+static String marked_documentation(const String &p_bbcode);
+
/**
* Text documents are identified using a URI. On the protocol level, URIs are passed as strings.
*/
@@ -199,6 +202,41 @@ struct TextDocumentPositionParams {
}
};
+struct DocumentLinkParams {
+ /**
+ * The document to provide document links for.
+ */
+ TextDocumentIdentifier textDocument;
+
+ _FORCE_INLINE_ void load(const Dictionary &p_params) {
+ textDocument.load(p_params["textDocument"]);
+ }
+};
+
+/**
+ * A document link is a range in a text document that links to an internal or external resource, like another
+ * text document or a web site.
+ */
+struct DocumentLink {
+
+ /**
+ * The range this link applies to.
+ */
+ Range range;
+
+ /**
+ * The uri this link points to. If missing a resolve request is sent later.
+ */
+ DocumentUri target;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+ dict["range"] = range.to_json();
+ dict["target"] = target;
+ return dict;
+ }
+};
+
/**
* A textual edit applicable to a text document.
*/
@@ -247,6 +285,9 @@ struct Command {
}
};
+// Use namespace instead of enumeration to follow the LSP specifications
+// lsp::EnumName::EnumValue is OK but lsp::EnumValue is not
+
namespace TextDocumentSyncKind {
/**
* Documents should not be synced at all.
@@ -557,6 +598,7 @@ struct TextDocumentContentChangeEvent {
}
};
+// Use namespace instead of enumeration to follow the LSP specifications
namespace DiagnosticSeverity {
/**
* Reports an error.
@@ -657,6 +699,7 @@ struct Diagnostic {
}
};
+// Use namespace instead of enumeration to follow the LSP specifications
/**
* Describes the content type that a client supports in various
* result literals like `Hover`, `ParameterInfo` or `CompletionItem`.
@@ -721,6 +764,9 @@ struct MarkupContent {
}
};
+// Use namespace instead of enumeration to follow the LSP specifications
+// lsp::EnumName::EnumValue is OK but lsp::EnumValue is not
+// And here C++ compilers are unhappy with our enumeration name like Color, File, Reference etc.
/**
* The kind of a completion entry.
*/
@@ -752,6 +798,7 @@ static const int Operator = 24;
static const int TypeParameter = 25;
}; // namespace CompletionItemKind
+// Use namespace instead of enumeration to follow the LSP specifications
/**
* Defines whether the insert text in a completion item should be interpreted as
* plain text or a snippet.
@@ -944,36 +991,39 @@ struct CompletionList {
Vector<CompletionItem> items;
};
+// Use namespace instead of enumeration to follow the LSP specifications
+// lsp::EnumName::EnumValue is OK but lsp::EnumValue is not
+// And here C++ compilers are unhappy with our enumeration name like String, Array, Object etc
/**
* A symbol kind.
*/
namespace SymbolKind {
-static const int File = 1;
-static const int Module = 2;
-static const int Namespace = 3;
-static const int Package = 4;
-static const int Class = 5;
-static const int Method = 6;
-static const int Property = 7;
-static const int Field = 8;
-static const int Constructor = 9;
-static const int Enum = 10;
-static const int Interface = 11;
-static const int Function = 12;
-static const int Variable = 13;
-static const int Constant = 14;
-static const int String = 15;
-static const int Number = 16;
-static const int Boolean = 17;
-static const int Array = 18;
-static const int Object = 19;
-static const int Key = 20;
-static const int Null = 21;
-static const int EnumMember = 22;
-static const int Struct = 23;
-static const int Event = 24;
-static const int Operator = 25;
-static const int TypeParameter = 26;
+static const int File = 0;
+static const int Module = 1;
+static const int Namespace = 2;
+static const int Package = 3;
+static const int Class = 4;
+static const int Method = 5;
+static const int Property = 6;
+static const int Field = 7;
+static const int Constructor = 8;
+static const int Enum = 9;
+static const int Interface = 10;
+static const int Function = 11;
+static const int Variable = 12;
+static const int Constant = 13;
+static const int String = 14;
+static const int Number = 15;
+static const int Boolean = 16;
+static const int Array = 17;
+static const int Object = 18;
+static const int Key = 19;
+static const int Null = 20;
+static const int EnumMember = 21;
+static const int Struct = 22;
+static const int Event = 23;
+static const int Operator = 24;
+static const int TypeParameter = 25;
}; // namespace SymbolKind
/**
@@ -1099,7 +1149,7 @@ struct DocumentSymbol {
*/
Vector<DocumentSymbol> children;
- _FORCE_INLINE_ Dictionary to_json() const {
+ Dictionary to_json(bool with_doc = false) const {
Dictionary dict;
dict["name"] = name;
dict["detail"] = detail;
@@ -1107,10 +1157,14 @@ struct DocumentSymbol {
dict["deprecated"] = deprecated;
dict["range"] = range.to_json();
dict["selectionRange"] = selectionRange.to_json();
+ if (with_doc) {
+ dict["documentation"] = documentation;
+ dict["native_class"] = native_class;
+ }
Array arr;
arr.resize(children.size());
for (int i = 0; i < children.size(); i++) {
- arr[i] = children[i].to_json();
+ arr[i] = children[i].to_json(with_doc);
}
dict["children"] = arr;
return dict;
@@ -1142,7 +1196,7 @@ struct DocumentSymbol {
markdown.value = "\t" + detail + "\n\n";
}
if (documentation.length()) {
- markdown.value += documentation + "\n\n";
+ markdown.value += marked_documentation(documentation) + "\n\n";
}
if (script_path.length()) {
markdown.value += "Defined in [" + script_path + "](" + uri + ")";
@@ -1194,6 +1248,17 @@ struct DocumentSymbol {
}
};
+struct NativeSymbolInspectParams {
+
+ String native_class;
+ String symbol_name;
+
+ void load(const Dictionary &p_params) {
+ native_class = p_params["native_class"];
+ symbol_name = p_params["symbol_name"];
+ }
+};
+
/**
* Enum of known range kinds
*/
@@ -1254,6 +1319,7 @@ struct FoldingRange {
}
};
+// Use namespace instead of enumeration to follow the LSP specifications
/**
* How a completion was triggered
*/
@@ -1501,6 +1567,61 @@ struct InitializeResult {
}
};
+/** Format BBCode documentation from DocData to markdown */
+static String marked_documentation(const String &p_bbcode) {
+
+ String markdown = p_bbcode.strip_edges();
+
+ Vector<String> lines = markdown.split("\n");
+ bool in_code_block = false;
+ int code_block_indent = -1;
+
+ markdown = "";
+ for (int i = 0; i < lines.size(); i++) {
+ String line = lines[i];
+ int block_start = line.find("[codeblock]");
+ if (block_start != -1) {
+ code_block_indent = block_start;
+ in_code_block = true;
+ line = "\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";
+ in_code_block = false;
+ }
+
+ if (!in_code_block) {
+ line = line.strip_edges();
+ line = line.replace("[code]", "`");
+ line = line.replace("[/code]", "`");
+ line = line.replace("[i]", "*");
+ line = line.replace("[/i]", "*");
+ line = line.replace("[b]", "**");
+ line = line.replace("[/b]", "**");
+ line = line.replace("[u]", "__");
+ line = line.replace("[/u]", "__");
+ line = line.replace("[method ", "`");
+ line = line.replace("[member ", "`");
+ line = line.replace("[signal ", "`");
+ line = line.replace("[enum ", "`");
+ line = line.replace("[constant ", "`");
+ line = line.replace("[", "`");
+ line = line.replace("]", "`");
+ }
+
+ if (!in_code_block && i < lines.size() - 1) {
+ line += "\n\n";
+ } else if (i < lines.size() - 1) {
+ line += "\n";
+ }
+ markdown += line;
+ }
+ return markdown;
+}
+
} // namespace lsp
#endif
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/jsonrpc/jsonrpc.cpp b/modules/jsonrpc/jsonrpc.cpp
index e1bba60f2f..ea90cce83d 100644
--- a/modules/jsonrpc/jsonrpc.cpp
+++ b/modules/jsonrpc/jsonrpc.cpp
@@ -47,11 +47,11 @@ void JSONRPC::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_notification", "method", "params"), &JSONRPC::make_notification);
ClassDB::bind_method(D_METHOD("make_response_error", "code", "message", "id"), &JSONRPC::make_response_error, DEFVAL(Variant()));
- BIND_ENUM_CONSTANT(PARSE_ERROR)
- BIND_ENUM_CONSTANT(INVALID_REQUEST)
- BIND_ENUM_CONSTANT(METHOD_NOT_FOUND)
- BIND_ENUM_CONSTANT(INVALID_PARAMS)
- BIND_ENUM_CONSTANT(INTERNAL_ERROR)
+ BIND_ENUM_CONSTANT(PARSE_ERROR);
+ BIND_ENUM_CONSTANT(INVALID_REQUEST);
+ BIND_ENUM_CONSTANT(METHOD_NOT_FOUND);
+ BIND_ENUM_CONSTANT(INVALID_PARAMS);
+ BIND_ENUM_CONSTANT(INTERNAL_ERROR);
}
Dictionary JSONRPC::make_response_error(int p_code, const String &p_message, const Variant &p_id) const {
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/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
index 4c1e47ecad..eb2c2dd77c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
@@ -34,7 +34,7 @@ namespace GodotTools.Build
if (_msbuildToolsPath.Empty())
{
- throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMsbuildVs}'. Tried with path: {_msbuildToolsPath}");
+ throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMsbuildVs}'.");
}
}
@@ -142,7 +142,7 @@ namespace GodotTools.Build
int exitCode = Godot.OS.Execute(vsWherePath, vsWhereArgs,
blocking: true, output: (Godot.Collections.Array) outputArray);
- if (exitCode == 0)
+ if (exitCode != 0)
return string.Empty;
if (outputArray.Count == 0)
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 1888bb3cb9..28cab2ab61 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -30,7 +30,7 @@
#include "bindings_generator.h"
-#ifdef DEBUG_METHODS_ENABLED
+#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
#include "core/engine.h"
#include "core/global_constants.h"
@@ -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..8f3676940b 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -36,7 +36,7 @@
#include "editor/doc/doc_data.h"
#include "editor/editor_help.h"
-#ifdef DEBUG_METHODS_ENABLED
+#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
#include "core/ustring.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/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..544bfc4615 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();
}
@@ -427,8 +430,8 @@ void GDMono::_register_internal_calls() {
}
void GDMono::_initialize_and_check_api_hashes() {
-
#ifdef MONO_GLUE_ENABLED
+#ifdef DEBUG_METHODS_ENABLED
if (get_api_core_hash() != GodotSharpBindings::get_core_api_hash()) {
ERR_PRINT("Mono: Core API hash mismatch.");
}
@@ -438,6 +441,7 @@ void GDMono::_initialize_and_check_api_hashes() {
ERR_PRINT("Mono: Editor API hash mismatch.");
}
#endif // TOOLS_ENABLED
+#endif // DEBUG_METHODS_ENABLED
#endif // MONO_GLUE_ENABLED
}
@@ -687,7 +691,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 +726,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 +765,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 +788,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.h b/modules/mono/mono_gd/gd_mono.h
index 4f7d3791f7..343d68bc2d 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -151,6 +151,7 @@ protected:
static GDMono *singleton;
public:
+#ifdef DEBUG_METHODS_ENABLED
uint64_t get_api_core_hash() {
if (api_core_hash == 0)
api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE);
@@ -162,7 +163,8 @@ public:
api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR);
return api_editor_hash;
}
-#endif
+#endif // TOOLS_ENABLED
+#endif // DEBUG_METHODS_ENABLED
#ifdef TOOLS_ENABLED
bool copy_prebuilt_api_assembly(APIAssembly::Type p_api_type, const String &p_config);
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
index 6d91075ce3..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);
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index 716c712ccc..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);
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/config.py b/modules/visual_script/config.py
index 04e1a40b81..087a13a200 100644
--- a/modules/visual_script/config.py
+++ b/modules/visual_script/config.py
@@ -11,6 +11,7 @@ def get_doc_classes():
"VisualScriptBuiltinFunc",
"VisualScriptClassConstant",
"VisualScriptComment",
+ "VisualScriptComposeArray",
"VisualScriptCondition",
"VisualScriptConstant",
"VisualScriptConstructor",
@@ -28,6 +29,7 @@ def get_doc_classes():
"VisualScriptIndexSet",
"VisualScriptInputAction",
"VisualScriptIterator",
+ "VisualScriptLists",
"VisualScriptLocalVarSet",
"VisualScriptLocalVar",
"VisualScriptMathConstant",
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..8cf3eb1d38
--- /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="type" type="int" enum="Variant.Type">
+ </argument>
+ <argument index="1" name="name" type="String">
+ </argument>
+ <argument index="2" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="add_output_data_port">
+ <return type="void">
+ </return>
+ <argument index="0" name="type" type="int" enum="Variant.Type">
+ </argument>
+ <argument index="1" name="name" type="String">
+ </argument>
+ <argument index="2" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="remove_input_data_port">
+ <return type="void">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="remove_output_data_port">
+ <return type="void">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_input_data_port_name">
+ <return type="void">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <argument index="1" name="name" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_input_data_port_type">
+ <return type="void">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <argument index="1" name="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="index" type="int">
+ </argument>
+ <argument index="1" name="name" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_output_data_port_type">
+ <return type="void">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <argument index="1" name="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..093901ad07 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,112 @@ void VisualScriptEditor::_member_edited() {
}
}
+void VisualScriptEditor::_create_function_dialog() {
+ function_create_dialog->popup_centered();
+ 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);
+ 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());
+
+ 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();
+}
+
+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 +1283,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 +1290,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 +1298,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 +1345,193 @@ 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_text(0);
+ function_name_edit->set_position(Input::get_singleton()->get_mouse_position() - Vector2(60, -10));
+ function_name_edit->popup();
+ function_name_box->set_text(selected);
+ function_name_box->select_all();
}
}
-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 +1557,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 +1577,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 +1612,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 +1633,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 +1683,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 +1751,82 @@ 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();
+ if (ti && 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 +1846,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 +1965,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 +2006,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 +2036,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 +2069,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 +2099,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 +2140,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 +2167,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 +2209,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 +2237,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 +2293,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 +2340,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 +2356,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 +2413,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 +2467,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 +2479,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 +2499,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 +2533,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 +2571,7 @@ void VisualScriptEditor::tag_saved_version() {
}
void VisualScriptEditor::reload(bool p_soft) {
+ _update_graph();
}
void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) {
@@ -2155,10 +2595,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 +2628,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,9 +2663,13 @@ 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);
+ memdelete(members_section);
}
void VisualScriptEditor::_change_base_type_callback() {
@@ -2293,46 +2733,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 +2804,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 +2835,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 +2848,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 +3022,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 +3034,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 +3045,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 +3070,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 +3287,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 +3299,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 +3317,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 +3338,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 +3363,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 +3387,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 +3404,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 +3428,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 +3458,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 +3475,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 +3507,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 +3578,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();
+
+ port_action_new_node = new_id;
- Ref<VisualScriptNode> vsn = script->get_node(edited_func, port_action_new_node);
+ 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 +3604,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 +3628,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 +3639,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 +3669,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 +3688,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 +3717,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 +3772,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 +3783,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 +3807,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 +3845,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 +3864,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,62 +3902,64 @@ 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) {
+ switch (p_what) {
+ case NOTIFICATION_READY: {
variable_editor->connect("changed", this, "_update_members");
signal_editor->connect("changed", this, "_update_members");
+ FALLTHROUGH;
}
+ case NOTIFICATION_THEME_CHANGED: {
+ if (p_what != NOTIFICATION_READY && !is_visible_in_tree()) {
+ return;
+ }
- Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme();
+ edit_variable_edit->add_style_override("bg", get_stylebox("bg", "Tree"));
+ edit_signal_edit->add_style_override("bg", get_stylebox("bg", "Tree"));
+ func_input_scroll->add_style_override("bg", get_stylebox("bg", "Tree"));
- bool dark_theme = tm->get_constant("dark_theme", "Editor");
+ Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme();
- List<Pair<String, Color> > colors;
- if (dark_theme) {
- colors.push_back(Pair<String, Color>("flow_control", Color(0.96, 0.96, 0.96)));
- colors.push_back(Pair<String, Color>("functions", Color(0.96, 0.52, 0.51)));
- colors.push_back(Pair<String, Color>("data", Color(0.5, 0.96, 0.81)));
- colors.push_back(Pair<String, Color>("operators", Color(0.67, 0.59, 0.87)));
- colors.push_back(Pair<String, Color>("custom", Color(0.5, 0.73, 0.96)));
- colors.push_back(Pair<String, Color>("constants", Color(0.96, 0.5, 0.69)));
- } else {
- colors.push_back(Pair<String, Color>("flow_control", Color(0.26, 0.26, 0.26)));
- colors.push_back(Pair<String, Color>("functions", Color(0.95, 0.4, 0.38)));
- colors.push_back(Pair<String, Color>("data", Color(0.07, 0.73, 0.51)));
- colors.push_back(Pair<String, Color>("operators", Color(0.51, 0.4, 0.82)));
- colors.push_back(Pair<String, Color>("custom", Color(0.31, 0.63, 0.95)));
- colors.push_back(Pair<String, Color>("constants", Color(0.94, 0.18, 0.49)));
- }
+ bool dark_theme = tm->get_constant("dark_theme", "Editor");
- for (List<Pair<String, Color> >::Element *E = colors.front(); E; E = E->next()) {
- Ref<StyleBoxFlat> sb = tm->get_stylebox("frame", "GraphNode");
- if (!sb.is_null()) {
- Ref<StyleBoxFlat> frame_style = sb->duplicate();
- Color c = sb->get_border_color();
- Color cn = E->get().second;
- cn.a = c.a;
- frame_style->set_border_color(cn);
- node_styles[E->get().first] = frame_style;
+ List<Pair<String, Color> > colors;
+ if (dark_theme) {
+ colors.push_back(Pair<String, Color>("flow_control", Color(0.96, 0.96, 0.96)));
+ colors.push_back(Pair<String, Color>("functions", Color(0.96, 0.52, 0.51)));
+ colors.push_back(Pair<String, Color>("data", Color(0.5, 0.96, 0.81)));
+ colors.push_back(Pair<String, Color>("operators", Color(0.67, 0.59, 0.87)));
+ colors.push_back(Pair<String, Color>("custom", Color(0.5, 0.73, 0.96)));
+ colors.push_back(Pair<String, Color>("constants", Color(0.96, 0.5, 0.69)));
+ } else {
+ colors.push_back(Pair<String, Color>("flow_control", Color(0.26, 0.26, 0.26)));
+ colors.push_back(Pair<String, Color>("functions", Color(0.95, 0.4, 0.38)));
+ colors.push_back(Pair<String, Color>("data", Color(0.07, 0.73, 0.51)));
+ colors.push_back(Pair<String, Color>("operators", Color(0.51, 0.4, 0.82)));
+ colors.push_back(Pair<String, Color>("custom", Color(0.31, 0.63, 0.95)));
+ colors.push_back(Pair<String, Color>("constants", Color(0.94, 0.18, 0.49)));
}
- }
- if (is_visible_in_tree() && script.is_valid()) {
- _update_members();
- _update_graph();
- }
- } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- left_vsplit->set_visible(is_visible_in_tree());
+ for (List<Pair<String, Color> >::Element *E = colors.front(); E; E = E->next()) {
+ Ref<StyleBoxFlat> sb = tm->get_stylebox("frame", "GraphNode");
+ if (!sb.is_null()) {
+ Ref<StyleBoxFlat> frame_style = sb->duplicate();
+ Color c = sb->get_border_color();
+ Color cn = E->get().second;
+ cn.a = c.a;
+ frame_style->set_border_color(cn);
+ node_styles[E->get().first] = frame_style;
+ }
+ }
+
+ if (is_visible_in_tree() && script.is_valid()) {
+ _update_members();
+ _update_graph();
+ }
+ } break;
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ members_section->set_visible(is_visible_in_tree());
+ } break;
}
}
@@ -3098,8 +3970,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 +3983,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 +4003,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 +4023,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 +4046,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 +4078,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 +4127,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 +4154,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 +4183,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 +4464,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 +4477,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 +4546,11 @@ 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();
+ function_name_box->set_text(selected);
+ function_name_box->select_all();
}
} break;
case MEMBER_VARIABLE: {
@@ -3425,7 +4587,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 +4607,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 +4623,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 +4643,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 +4670,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 +4683,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,56 +4696,50 @@ 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);
- ScriptEditor::get_singleton()->get_left_list_split()->call_deferred("add_child", left_vsplit); //add but wait until done settig up this
- left_vsplit->set_v_size_flags(SIZE_EXPAND_FILL);
- left_vsplit->set_stretch_ratio(2);
- left_vsplit->hide();
+ members_section = memnew(VBoxContainer);
+ // Add but wait until done setting up this.
+ ScriptEditor::get_singleton()->get_left_list_split()->call_deferred("add_child", members_section);
+ members_section->set_v_size_flags(SIZE_EXPAND_FILL);
- VBoxContainer *left_vb = memnew(VBoxContainer);
- left_vsplit->add_child(left_vb);
- left_vb->set_v_size_flags(SIZE_EXPAND_FILL);
- //left_vb->set_custom_minimum_size(Size2(230, 1) * EDSCALE);
+ CheckButton *tool_script_check = memnew(CheckButton);
+ tool_script_check->set_text(TTR("Make Tool:"));
+ members_section->add_child(tool_script_check);
+ tool_script_check->connect("pressed", this, "_toggle_tool_script");
- 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");
+ /// Members ///
members = memnew(Tree);
- left_vb->add_margin_child(TTR("Members:"), members, true);
+ members_section->add_margin_child(TTR("Members:"), members, true);
+ members->set_custom_minimum_size(Size2(0, 50 * EDSCALE));
members->set_hide_root(true);
members->connect("button_pressed", this, "_member_button");
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);
- 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);
+ member_popup = memnew(PopupMenu);
+ add_child(member_popup);
+ member_popup->connect("id_pressed", this, "_member_option");
- left_vb2->add_margin_child(TTR("Available Nodes:"), vbc_nodes, true);
+ 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);
- 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 +4750,77 @@ 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_scroll = memnew(ScrollContainer);
+ func_input_scroll->set_v_size_flags(SIZE_EXPAND_FILL);
+ function_vb->add_child(func_input_scroll);
+
+ func_input_vbox = memnew(VBoxContainer);
+ func_input_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
+ func_input_scroll->add_child(func_input_vbox);
+
+ function_create_dialog = memnew(ConfirmationDialog);
+ function_create_dialog->set_custom_minimum_size(Size2(450, 300) * EDSCALE);
+ 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);
@@ -3603,7 +4841,7 @@ VisualScriptEditor::VisualScriptEditor() {
hint_text_timer->connect("timeout", this, "_hide_timer");
add_child(hint_text_timer);
- //allowed casts (connections)
+ // Allowed casts (connections).
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
graph->add_valid_connection_type(Variant::NIL, i);
graph->add_valid_connection_type(i, Variant::NIL);
@@ -3643,7 +4881,7 @@ VisualScriptEditor::VisualScriptEditor() {
edit_variable_edit->edit(variable_editor);
select_base_type = memnew(CreateDialog);
- select_base_type->set_base_type("Object"); //anything goes
+ select_base_type->set_base_type("Object"); // Anything goes.
select_base_type->connect("create", this, "_change_base_type_callback");
add_child(select_base_type);
@@ -3651,8 +4889,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,25 +4903,18 @@ 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() {
- undo_redo->clear_history(); //avoid crashes
+ undo_redo->clear_history(); // Avoid crashes.
memdelete(signal_editor);
memdelete(variable_editor);
}
@@ -3714,12 +4945,14 @@ 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);
}
void VisualScriptEditor::register_editor() {
- //too early to register stuff here, request a callback
+ // Too early to register stuff here, request a callback.
EditorNode::add_plugin_init_callback(register_editor_callback);
}
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 4f302d1d72..5a00469eea 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 {
@@ -79,17 +81,19 @@ class VisualScriptEditor : public ScriptEditorBase {
MEMBER_SIGNAL
};
- VSplitContainer *left_vsplit;
+ VBoxContainer *members_section;
MenuButton *edit_menu;
Ref<VisualScript> script;
Button *base_type_select;
- GraphEdit *graph;
+ LineEdit *func_name_box;
+ ScrollContainer *func_input_scroll;
+ VBoxContainer *func_input_vbox;
+ ConfirmationDialog *function_create_dialog;
- LineEdit *node_filter;
- TextureRect *node_filter_icon;
+ GraphEdit *graph;
VisualScriptEditorSignalEdit *signal_editor;
@@ -110,7 +114,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 +138,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 +171,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 +185,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 +213,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 +261,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 +279,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..957127fe61 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", "type", "name", "index"), &VisualScriptLists::add_input_data_port);
+ ClassDB::bind_method(D_METHOD("set_input_data_port_name", "index", "name"), &VisualScriptLists::set_input_data_port_name);
+ ClassDB::bind_method(D_METHOD("set_input_data_port_type", "index", "type"), &VisualScriptLists::set_input_data_port_type);
+ ClassDB::bind_method(D_METHOD("remove_input_data_port", "index"), &VisualScriptLists::remove_input_data_port);
+
+ ClassDB::bind_method(D_METHOD("add_output_data_port", "type", "name", "index"), &VisualScriptLists::add_output_data_port);
+ ClassDB::bind_method(D_METHOD("set_output_data_port_name", "index", "name"), &VisualScriptLists::set_output_data_port_name);
+ ClassDB::bind_method(D_METHOD("set_output_data_port_type", "index", "type"), &VisualScriptLists::set_output_data_port_type);
+ ClassDB::bind_method(D_METHOD("remove_output_data_port", "index"), &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//////////////////
//////////////////////////////////////////
@@ -3640,6 +4075,14 @@ VisualScriptDeconstruct::VisualScriptDeconstruct() {
type = Variant::NIL;
}
+template <Variant::Type T>
+static Ref<VisualScriptNode> create_node_deconst_typed(const String &p_name) {
+ Ref<VisualScriptDeconstruct> node;
+ node.instance();
+ node->set_deconstruct_type(T);
+ return node;
+}
+
void register_visual_script_nodes() {
VisualScriptLanguage::singleton->add_register_func("data/set_variable", create_node_generic<VisualScriptVariableSet>);
@@ -3697,7 +4140,17 @@ void register_visual_script_nodes() {
VisualScriptLanguage::singleton->add_register_func("operators/logic/in", create_op_node<Variant::OP_IN>);
VisualScriptLanguage::singleton->add_register_func("operators/logic/select", create_node_generic<VisualScriptSelect>);
- VisualScriptLanguage::singleton->add_register_func("functions/deconstruct", create_node_generic<VisualScriptDeconstruct>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR2), create_node_deconst_typed<Variant::Type::VECTOR2>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR3), create_node_deconst_typed<Variant::Type::VECTOR3>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::COLOR), create_node_deconst_typed<Variant::Type::COLOR>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::RECT2), create_node_deconst_typed<Variant::Type::RECT2>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::TRANSFORM2D), create_node_deconst_typed<Variant::Type::TRANSFORM2D>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::PLANE), create_node_deconst_typed<Variant::Type::PLANE>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::QUAT), create_node_deconst_typed<Variant::Type::QUAT>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::AABB), create_node_deconst_typed<Variant::Type::AABB>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::BASIS), create_node_deconst_typed<Variant::Type::BASIS>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::TRANSFORM), create_node_deconst_typed<Variant::Type::TRANSFORM>);
+ 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..42d4c5e209 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()) {
@@ -274,6 +271,7 @@ void VisualScriptPropertySelector::_update_search() {
get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box);
get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box);
get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box);
+ get_visual_node_names("functions/deconstruct/" + Variant::get_type_name(type), Set<String>(), found, root, search_box);
get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box);
if (type == Variant::INT) {
get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box);
@@ -327,7 +325,7 @@ void VisualScriptPropertySelector::create_visualscript_item(const String &name,
}
}
-void VisualScriptPropertySelector::get_visual_node_names(const String &root_filter, const Set<String> &filter, bool &found, TreeItem *const root, LineEdit *const search_box) {
+void VisualScriptPropertySelector::get_visual_node_names(const String &root_filter, const Set<String> &p_modifiers, bool &found, TreeItem *const root, LineEdit *const search_box) {
Map<String, TreeItem *> path_cache;
List<String> fnodes;
@@ -338,37 +336,59 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt
continue;
}
Vector<String> path = E->get().split("/");
- bool is_filter = false;
- for (Set<String>::Element *F = filter.front(); F; F = F->next()) {
- if (path.size() >= 2 && path[1].findn(F->get()) != -1) {
- is_filter = true;
+
+ // check if the name has the filter
+ bool in_filter = false;
+ Vector<String> tx_filters = search_box->get_text().split(" ");
+ for (int i = 0; i < tx_filters.size(); i++) {
+ if (tx_filters[i] == "") {
+ in_filter = true;
+ } else {
+ in_filter = false;
+ }
+ if (E->get().findn(tx_filters[i]) != -1) {
+ in_filter = true;
break;
}
}
- if (is_filter) {
+ if (!in_filter) {
continue;
}
- if (search_box->get_text() != String() && E->get().findn(search_box->get_text()) == -1) {
+ bool in_modifier = false | p_modifiers.empty();
+ for (Set<String>::Element *F = p_modifiers.front(); F && in_modifier; F = F->next()) {
+ if (E->get().findn(F->get()) != -1)
+ in_modifier = true;
+ }
+ if (!in_modifier) {
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 ";
+ }
+ Ref<VisualScriptDeconstruct> vnode_deconstruct = vnode;
+ if (vnode_deconstruct.is_valid()) {
+ type_name = "Deconstruct ";
+ }
+ 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 +524,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 +535,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 +549,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 +561,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 +572,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 +585,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 +596,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 +608,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 +619,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 +630,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 +641,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 +652,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 +663,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 +673,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 +687,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">