summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/debugger/editor_debugger_node.cpp72
-rw-r--r--editor/debugger/editor_debugger_node.h1
-rw-r--r--editor/debugger/script_editor_debugger.cpp2
-rw-r--r--editor/dependency_editor.cpp2
-rw-r--r--editor/editor_export.cpp114
-rw-r--r--editor/editor_export.h59
-rw-r--r--editor/editor_inspector.cpp22
-rw-r--r--editor/editor_node.cpp21
-rw-r--r--editor/editor_path.cpp2
-rw-r--r--editor/editor_properties.cpp275
-rw-r--r--editor/editor_properties.h36
-rw-r--r--editor/editor_properties_array_dict.cpp80
-rw-r--r--editor/editor_properties_array_dict.h8
-rw-r--r--editor/editor_property_name_processor.cpp1
-rw-r--r--editor/editor_resource_picker.cpp2
-rw-r--r--editor/editor_run_native.cpp17
-rw-r--r--editor/editor_run_native.h5
-rw-r--r--editor/editor_spin_slider.cpp6
-rw-r--r--editor/editor_themes.cpp124
-rw-r--r--editor/editor_toaster.cpp14
-rw-r--r--editor/icons/TerrainConnect.svg1
-rw-r--r--editor/icons/TerrainPath.svg1
-rw-r--r--editor/icons/Unlinked.svg1
-rw-r--r--editor/import/dynamic_font_import_settings.cpp9
-rw-r--r--editor/import/resource_importer_dynamic_font.cpp7
-rw-r--r--editor/import/resource_importer_scene.cpp38
-rw-r--r--editor/import/resource_importer_texture.cpp6
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp41
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h2
-rw-r--r--editor/plugins/input_event_editor_plugin.cpp3
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp19
-rw-r--r--editor/plugins/node_3d_editor_plugin.h1
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp165
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp429
-rw-r--r--editor/plugins/tiles/tile_map_editor.h20
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp3
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp7
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp6
-rw-r--r--editor/project_converter_3_to_4.cpp3811
-rw-r--r--editor/project_converter_3_to_4.h83
-rw-r--r--editor/project_export.cpp48
-rw-r--r--editor/project_export.h3
-rw-r--r--editor/scene_tree_dock.cpp20
-rw-r--r--editor/scene_tree_dock.h1
45 files changed, 5005 insertions, 585 deletions
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index bc28b11a71..e13af59d69 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -103,6 +103,7 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
node->connect("remote_object_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_updated), varray(id));
node->connect("remote_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated), varray(id));
node->connect("remote_object_requested", callable_mp(this, &EditorDebuggerNode::_remote_object_requested), varray(id));
+ node->connect("errors_cleared", callable_mp(this, &EditorDebuggerNode::_update_errors));
if (tabs->get_tab_count() > 0) {
get_debugger(0)->clear_style();
@@ -267,40 +268,7 @@ void EditorDebuggerNode::_notification(int p_what) {
}
server->poll();
- // Errors and warnings
- int error_count = 0;
- int warning_count = 0;
- _for_all(tabs, [&](ScriptEditorDebugger *dbg) {
- error_count += dbg->get_error_count();
- warning_count += dbg->get_warning_count();
- });
-
- if (error_count != last_error_count || warning_count != last_warning_count) {
- _for_all(tabs, [&](ScriptEditorDebugger *dbg) {
- dbg->update_tabs();
- });
-
- if (error_count == 0 && warning_count == 0) {
- debugger_button->set_text(TTR("Debugger"));
- debugger_button->remove_theme_color_override("font_color");
- debugger_button->set_icon(Ref<Texture2D>());
- } else {
- debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")");
- if (error_count >= 1 && warning_count >= 1) {
- debugger_button->set_icon(get_theme_icon(SNAME("ErrorWarning"), SNAME("EditorIcons")));
- // Use error color to represent the highest level of severity reported.
- debugger_button->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
- } else if (error_count >= 1) {
- debugger_button->set_icon(get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
- debugger_button->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
- } else {
- debugger_button->set_icon(get_theme_icon(SNAME("Warning"), SNAME("EditorIcons")));
- debugger_button->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
- }
- }
- last_error_count = error_count;
- last_warning_count = warning_count;
- }
+ _update_errors();
// Remote scene tree update
remote_scene_tree_timeout -= get_process_delta_time();
@@ -361,6 +329,42 @@ void EditorDebuggerNode::_notification(int p_what) {
}
}
+void EditorDebuggerNode::_update_errors() {
+ int error_count = 0;
+ int warning_count = 0;
+ _for_all(tabs, [&](ScriptEditorDebugger *dbg) {
+ error_count += dbg->get_error_count();
+ warning_count += dbg->get_warning_count();
+ });
+
+ if (error_count != last_error_count || warning_count != last_warning_count) {
+ _for_all(tabs, [&](ScriptEditorDebugger *dbg) {
+ dbg->update_tabs();
+ });
+
+ if (error_count == 0 && warning_count == 0) {
+ debugger_button->set_text(TTR("Debugger"));
+ debugger_button->remove_theme_color_override("font_color");
+ debugger_button->set_icon(Ref<Texture2D>());
+ } else {
+ debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")");
+ if (error_count >= 1 && warning_count >= 1) {
+ debugger_button->set_icon(get_theme_icon(SNAME("ErrorWarning"), SNAME("EditorIcons")));
+ // Use error color to represent the highest level of severity reported.
+ debugger_button->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ } else if (error_count >= 1) {
+ debugger_button->set_icon(get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
+ debugger_button->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ } else {
+ debugger_button->set_icon(get_theme_icon(SNAME("Warning"), SNAME("EditorIcons")));
+ debugger_button->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
+ }
+ }
+ last_error_count = error_count;
+ last_warning_count = warning_count;
+ }
+}
+
void EditorDebuggerNode::_debugger_stopped(int p_id) {
ScriptEditorDebugger *dbg = get_debugger(p_id);
ERR_FAIL_COND(!dbg);
diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h
index 87457fc09a..8dc53690eb 100644
--- a/editor/debugger/editor_debugger_node.h
+++ b/editor/debugger/editor_debugger_node.h
@@ -116,6 +116,7 @@ private:
ScriptEditorDebugger *_add_debugger();
EditorDebuggerRemoteObject *get_inspected_remote_object();
+ void _update_errors();
friend class DebuggerEditorPlugin;
friend class DebugAdapterParser;
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index 9184846408..05409dbeeb 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -1465,6 +1465,7 @@ void ScriptEditorDebugger::_clear_errors_list() {
error_tree->clear();
error_count = 0;
warning_count = 0;
+ emit_signal(SNAME("errors_cleared"));
update_tabs();
expand_all_button->set_disabled(true);
@@ -1626,6 +1627,7 @@ void ScriptEditorDebugger::_bind_methods() {
ADD_SIGNAL(MethodInfo("debug_data", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::ARRAY, "data")));
ADD_SIGNAL(MethodInfo("set_breakpoint", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled")));
ADD_SIGNAL(MethodInfo("clear_breakpoints"));
+ ADD_SIGNAL(MethodInfo("errors_cleared"));
}
void ScriptEditorDebugger::add_debugger_plugin(const Ref<Script> &p_script) {
diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp
index 7b73a392b4..97699d0349 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -349,7 +349,7 @@ void DependencyEditorOwners::show(const String &p_path) {
_fill_owners(EditorFileSystem::get_singleton()->get_filesystem());
popup_centered_ratio(0.3);
- set_title(TTR("Owners Of:") + " " + p_path.get_file());
+ set_title(vformat(TTR("Owners of: %s (Total: %d)"), p_path.get_file(), owners->get_item_count()));
}
DependencyEditorOwners::DependencyEditorOwners() {
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index f4a81521df..bb9d930cf5 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -46,6 +46,7 @@
#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
+#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/plugins/script_editor_plugin.h"
#include "scene/resources/resource_format_text.h"
@@ -252,6 +253,83 @@ String EditorExportPreset::get_script_encryption_key() const {
///////////////////////////////////
+bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err) {
+ bool has_messages = false;
+
+ int msg_count = get_message_count();
+
+ p_log->add_text(TTR("Project export for platform:") + " ");
+ p_log->add_image(get_logo(), 16 * EDSCALE, 16 * EDSCALE, Color(1.0, 1.0, 1.0), INLINE_ALIGNMENT_CENTER);
+ p_log->add_text(" ");
+ p_log->add_text(get_name());
+ p_log->add_text(" - ");
+ if (p_err == OK) {
+ if (get_worst_message_type() >= EditorExportPlatform::EXPORT_MESSAGE_WARNING) {
+ p_log->add_image(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")), 16 * EDSCALE, 16 * EDSCALE, Color(1.0, 1.0, 1.0), INLINE_ALIGNMENT_CENTER);
+ p_log->add_text(" ");
+ p_log->add_text(TTR("Completed with errors."));
+ has_messages = true;
+ } else {
+ p_log->add_image(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")), 16 * EDSCALE, 16 * EDSCALE, Color(1.0, 1.0, 1.0), INLINE_ALIGNMENT_CENTER);
+ p_log->add_text(" ");
+ p_log->add_text(TTR("Completed sucessfully."));
+ if (msg_count > 0) {
+ has_messages = true;
+ }
+ }
+ } else {
+ p_log->add_image(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")), 16 * EDSCALE, 16 * EDSCALE, Color(1.0, 1.0, 1.0), INLINE_ALIGNMENT_CENTER);
+ p_log->add_text(" ");
+ p_log->add_text(TTR("Failed."));
+ has_messages = true;
+ }
+ p_log->add_newline();
+
+ if (msg_count) {
+ p_log->push_table(2);
+ p_log->set_table_column_expand(0, false);
+ p_log->set_table_column_expand(1, true);
+ for (int m = 0; m < msg_count; m++) {
+ EditorExportPlatform::ExportMessage msg = get_message(m);
+ Color color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("font_color"), SNAME("Label"));
+ Ref<Texture> icon;
+
+ switch (msg.msg_type) {
+ case EditorExportPlatform::EXPORT_MESSAGE_INFO: {
+ color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, 0.6);
+ } break;
+ case EditorExportPlatform::EXPORT_MESSAGE_WARNING: {
+ icon = EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Warning"), SNAME("EditorIcons"));
+ color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("warning_color"), SNAME("Editor"));
+ } break;
+ case EditorExportPlatform::EXPORT_MESSAGE_ERROR: {
+ icon = EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Error"), SNAME("EditorIcons"));
+ color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor"));
+ } break;
+ default:
+ break;
+ }
+
+ p_log->push_cell();
+ p_log->add_text("\t");
+ if (icon.is_valid()) {
+ p_log->add_image(icon);
+ }
+ p_log->pop();
+
+ p_log->push_cell();
+ p_log->push_color(color);
+ p_log->add_text(vformat("[%s]: %s", msg.category, msg.text));
+ p_log->pop();
+ p_log->pop();
+ }
+ p_log->pop();
+ p_log->add_newline();
+ }
+ p_log->add_newline();
+ return has_messages;
+}
+
void EditorExportPlatform::gen_debug_flags(Vector<String> &r_flags, int p_flags) {
String host = EditorSettings::get_singleton()->get("network/debug/remote_host");
int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
@@ -1134,7 +1212,10 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
String tmppath = EditorPaths::get_singleton()->get_cache_dir().plus_file("packtmp");
Ref<FileAccess> ftmp = FileAccess::open(tmppath, FileAccess::WRITE);
- ERR_FAIL_COND_V_MSG(ftmp.is_null(), ERR_CANT_CREATE, "Cannot create file '" + tmppath + "'.");
+ if (ftmp.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), vformat(TTR("Cannot create file \"%s\"."), tmppath));
+ return ERR_CANT_CREATE;
+ }
PackData pd;
pd.ep = &ep;
@@ -1149,7 +1230,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
if (err != OK) {
DirAccess::remove_file_or_error(tmppath);
- ERR_PRINT("Failed to export project files");
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Failed to export project files."));
return err;
}
@@ -1162,14 +1243,16 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
f = FileAccess::open(p_path, FileAccess::WRITE);
if (f.is_null()) {
DirAccess::remove_file_or_error(tmppath);
- ERR_FAIL_V(ERR_CANT_CREATE);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), vformat(TTR("Can't open file to read from path \"%s\"."), tmppath));
+ return ERR_CANT_CREATE;
}
} else {
// Append to executable
f = FileAccess::open(p_path, FileAccess::READ_WRITE);
if (f.is_null()) {
DirAccess::remove_file_or_error(tmppath);
- ERR_FAIL_V(ERR_FILE_CANT_OPEN);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), vformat(TTR("Can't open executable file from path \"%s\"."), tmppath));
+ return ERR_FILE_CANT_OPEN;
}
f->seek_end();
@@ -1245,10 +1328,16 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
}
}
fae.instantiate();
- ERR_FAIL_COND_V(fae.is_null(), ERR_SKIP);
+ if (fae.is_null()) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Can't create encrypted file."));
+ return ERR_CANT_CREATE;
+ }
err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_WRITE_AES256, false);
- ERR_FAIL_COND_V(err != OK, ERR_SKIP);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Can't open encrypted file to write."));
+ return ERR_CANT_CREATE;
+ }
fhead = fae;
}
@@ -1293,7 +1382,8 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
ftmp = FileAccess::open(tmppath, FileAccess::READ);
if (ftmp.is_null()) {
DirAccess::remove_file_or_error(tmppath);
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't open file to read from path '" + String(tmppath) + "'.");
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), vformat(TTR("Can't open file to read from path \"%s\"."), tmppath));
+ return ERR_CANT_CREATE;
}
const int bufsize = 16384;
@@ -1344,7 +1434,7 @@ Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bo
Error err = export_project_files(p_preset, p_debug, _save_zip_file, &zd);
if (err != OK && err != ERR_SKIP) {
- ERR_PRINT("Failed to export project files");
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Save ZIP"), TTR("Failed to export project files."));
}
zipClose(zip, nullptr);
@@ -1835,6 +1925,7 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr
Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
if (!DirAccess::exists(p_path.get_base_dir())) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("The given export path doesn't exist."));
return ERR_FILE_BAD_PATH;
}
@@ -1850,13 +1941,16 @@ Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_
}
if (!template_path.is_empty() && !FileAccess::exists(template_path)) {
- EditorNode::get_singleton()->show_warning(TTR("Template file not found:") + "\n" + template_path);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), vformat(TTR("Template file not found: \"%s\"."), template_path));
return ERR_FILE_NOT_FOUND;
}
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
da->make_dir_recursive(p_path.get_base_dir());
Error err = da->copy(template_path, p_path, get_chmod_flags());
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("Failed to copy export template."));
+ }
return err;
}
@@ -1876,7 +1970,7 @@ Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset>
Error err = save_pack(p_preset, p_debug, pck_path, &so_files, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size);
if (err == OK && p_preset->get("binary_format/embed_pck")) {
if (embedded_size >= 0x100000000 && !p_preset->get("binary_format/64_bits")) {
- EditorNode::get_singleton()->show_warning(TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB."));
return ERR_INVALID_PARAMETER;
}
diff --git a/editor/editor_export.h b/editor/editor_export.h
index daf6d8ef23..6f41736d2d 100644
--- a/editor/editor_export.h
+++ b/editor/editor_export.h
@@ -33,6 +33,7 @@
#include "core/io/dir_access.h"
#include "core/io/resource.h"
+#include "scene/gui/rich_text_label.h"
#include "scene/main/node.h"
#include "scene/main/timer.h"
#include "scene/resources/texture.h"
@@ -170,6 +171,19 @@ public:
typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so);
+ enum ExportMessageType {
+ EXPORT_MESSAGE_NONE,
+ EXPORT_MESSAGE_INFO,
+ EXPORT_MESSAGE_WARNING,
+ EXPORT_MESSAGE_ERROR,
+ };
+
+ struct ExportMessage {
+ ExportMessageType msg_type;
+ String category;
+ String text;
+ };
+
private:
struct SavedData {
uint64_t ofs = 0;
@@ -200,6 +214,8 @@ private:
Vector<String> features_pv;
};
+ Vector<ExportMessage> messages;
+
void _export_find_resources(EditorFileSystemDirectory *p_dir, HashSet<String> &p_paths);
void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths);
@@ -240,6 +256,47 @@ public:
virtual Ref<EditorExportPreset> create_preset();
+ virtual void clear_messages() { messages.clear(); }
+ virtual void add_message(ExportMessageType p_type, const String &p_category, const String &p_message) {
+ ExportMessage msg;
+ msg.category = p_category;
+ msg.text = p_message;
+ msg.msg_type = p_type;
+ messages.push_back(msg);
+ switch (p_type) {
+ case EXPORT_MESSAGE_INFO: {
+ print_line(vformat("%s: %s\n", msg.category, msg.text));
+ } break;
+ case EXPORT_MESSAGE_WARNING: {
+ WARN_PRINT(vformat("%s: %s\n", msg.category, msg.text));
+ } break;
+ case EXPORT_MESSAGE_ERROR: {
+ ERR_PRINT(vformat("%s: %s\n", msg.category, msg.text));
+ } break;
+ default:
+ break;
+ }
+ }
+
+ virtual int get_message_count() const {
+ return messages.size();
+ }
+
+ virtual ExportMessage get_message(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, messages.size(), ExportMessage());
+ return messages[p_index];
+ }
+
+ virtual ExportMessageType get_worst_message_type() const {
+ ExportMessageType worst_type = EXPORT_MESSAGE_NONE;
+ for (int i = 0; i < messages.size(); i++) {
+ worst_type = MAX(worst_type, messages[i].msg_type);
+ }
+ return worst_type;
+ }
+
+ virtual bool fill_log_messages(RichTextLabel *p_log, Error p_err);
+
virtual void get_export_options(List<ExportOption> *r_options) = 0;
virtual bool should_update_export_options() { return false; }
virtual bool get_export_option_visibility(const String &p_option, const HashMap<StringName, Variant> &p_options) const { return true; }
@@ -459,7 +516,7 @@ public:
int get_chmod_flags() const;
void set_chmod_flags(int p_flags);
- virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) const {
+ virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
return Error::OK;
}
};
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 92ea162962..0f31e3e7bb 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -62,7 +62,7 @@ Size2 EditorProperty::get_minimum_size() const {
Size2 ms;
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree"));
- ms.height = font->get_height(font_size);
+ ms.height = font->get_height(font_size) + 4 * EDSCALE;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
@@ -132,7 +132,7 @@ void EditorProperty::_notification(int p_what) {
int child_room = size.width * (1.0 - split_ratio);
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree"));
- int height = font->get_height(font_size);
+ int height = font->get_height(font_size) + 4 * EDSCALE;
bool no_children = true;
//compute room needed
@@ -236,30 +236,24 @@ void EditorProperty::_notification(int p_what) {
case NOTIFICATION_DRAW: {
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree"));
- Color dark_color = get_theme_color(SNAME("dark_color_2"), SNAME("Editor"));
bool rtl = is_layout_rtl();
Size2 size = get_size();
if (bottom_editor) {
- size.height = bottom_editor->get_offset(SIDE_TOP);
+ size.height = bottom_editor->get_offset(SIDE_TOP) - get_theme_constant(SNAME("v_separation"));
} else if (label_reference) {
size.height = label_reference->get_size().height;
}
- Ref<StyleBox> sb;
- if (selected) {
- sb = get_theme_stylebox(SNAME("bg_selected"));
- } else {
- sb = get_theme_stylebox(SNAME("bg"));
- }
-
+ Ref<StyleBox> sb = get_theme_stylebox(selected ? SNAME("bg_selected") : SNAME("bg"));
draw_style_box(sb, Rect2(Vector2(), size));
+ Ref<StyleBox> bg_stylebox = get_theme_stylebox(SNAME("child_bg"));
if (draw_top_bg && right_child_rect != Rect2()) {
- draw_rect(right_child_rect, dark_color);
+ draw_style_box(bg_stylebox, right_child_rect);
}
if (bottom_child_rect != Rect2()) {
- draw_rect(bottom_child_rect, dark_color);
+ draw_style_box(bg_stylebox, bottom_child_rect);
}
Color color;
@@ -1075,7 +1069,7 @@ void EditorInspectorPlugin::_bind_methods() {
void EditorInspectorCategory::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_DRAW: {
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("prop_category_style"), SNAME("Editor"));
+ Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg"));
draw_style_box(sb, Rect2(Vector2(), get_size()));
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index e23c36c1d4..e0a5cab167 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -968,21 +968,14 @@ void EditorNode::_fs_changed() {
ERR_PRINT(vformat("Cannot export project with preset \"%s\" due to configuration errors:\n%s", preset_name, config_error));
err = missing_templates ? ERR_FILE_NOT_FOUND : ERR_UNCONFIGURED;
} else {
+ platform->clear_messages();
err = platform->export_project(export_preset, export_defer.debug, export_path);
}
}
- switch (err) {
- case OK:
- break;
- case ERR_FILE_NOT_FOUND:
- export_error = vformat("Project export failed for preset \"%s\". The export template appears to be missing.", preset_name);
- break;
- case ERR_FILE_BAD_PATH:
- export_error = vformat("Project export failed for preset \"%s\". The target path \"%s\" appears to be invalid.", preset_name, export_path);
- break;
- default:
- export_error = vformat("Project export failed with error code %d for preset \"%s\".", (int)err, preset_name);
- break;
+ if (err != OK) {
+ export_error = vformat("Project export for preset \"%s\" failed.", preset_name);
+ } else if (platform->get_worst_message_type() >= EditorExportPlatform::EXPORT_MESSAGE_WARNING) {
+ export_error = vformat("Project export for preset \"%s\" completed with errors.", preset_name);
}
}
}
@@ -4870,7 +4863,7 @@ bool EditorNode::has_scenes_in_session() {
bool EditorNode::ensure_main_scene(bool p_from_native) {
pick_main_scene->set_meta("from_native", p_from_native); // Whether from play button or native run.
- String main_scene = GLOBAL_DEF("application/run/main_scene", "");
+ String main_scene = GLOBAL_DEF_BASIC("application/run/main_scene", "");
if (main_scene.is_empty()) {
current_menu_option = -1;
@@ -4936,7 +4929,7 @@ bool EditorNode::is_run_playing() const {
String EditorNode::get_run_playing_scene() const {
String run_filename = editor_run.get_running_scene();
if (run_filename.is_empty() && is_run_playing()) {
- run_filename = GLOBAL_DEF("application/run/main_scene", ""); // Must be the main scene then.
+ run_filename = GLOBAL_DEF_BASIC("application/run/main_scene", ""); // Must be the main scene then.
}
return run_filename;
diff --git a/editor/editor_path.cpp b/editor/editor_path.cpp
index 8747128962..6453db3b0b 100644
--- a/editor/editor_path.cpp
+++ b/editor/editor_path.cpp
@@ -72,7 +72,7 @@ void EditorPath::_add_children_to_popup(Object *p_obj, int p_depth) {
int index = sub_objects_menu->get_item_count();
sub_objects_menu->add_icon_item(icon, proper_name, objects.size());
- sub_objects_menu->set_item_h_offset(index, p_depth * 10 * EDSCALE);
+ sub_objects_menu->set_item_horizontal_offset(index, p_depth * 10 * EDSCALE);
objects.push_back(obj->get_instance_id());
_add_children_to_popup(obj, p_depth + 1);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index e3d4f1cab0..a5c02c70d9 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -173,6 +173,7 @@ void EditorPropertyMultilineText::_bind_methods() {
EditorPropertyMultilineText::EditorPropertyMultilineText() {
HBoxContainer *hb = memnew(HBoxContainer);
+ hb->add_theme_constant_override("separation", 0);
add_child(hb);
set_bottom_editor(hb);
text = memnew(TextEdit);
@@ -1258,12 +1259,13 @@ void EditorPropertyInteger::update_property() {
void EditorPropertyInteger::_bind_methods() {
}
-void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_allow_greater, bool p_allow_lesser) {
+void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) {
spin->set_min(p_min);
spin->set_max(p_max);
spin->set_step(p_step);
spin->set_allow_greater(p_allow_greater);
spin->set_allow_lesser(p_allow_lesser);
+ spin->set_suffix(p_suffix);
}
EditorPropertyInteger::EditorPropertyInteger() {
@@ -1595,6 +1597,18 @@ void EditorPropertyVector2::_value_changed(double val, const String &p_name) {
return;
}
+ if (linked->is_pressed()) {
+ setting = true;
+ if (p_name == "x") {
+ spin[1]->set_value(spin[0]->get_value() * ratio_yx);
+ }
+
+ if (p_name == "y") {
+ spin[0]->set_value(spin[1]->get_value() * ratio_xy);
+ }
+ setting = false;
+ }
+
Vector2 v2;
v2.x = spin[0]->get_value();
v2.y = spin[1]->get_value();
@@ -1607,12 +1621,28 @@ void EditorPropertyVector2::update_property() {
spin[0]->set_value(val.x);
spin[1]->set_value(val.y);
setting = false;
+ _update_ratio();
+}
+
+void EditorPropertyVector2::_update_ratio() {
+ linked->set_modulate(Color(1, 1, 1, linked->is_pressed() ? 1.0 : 0.5));
+
+ if (spin[0]->get_value() != 0 && spin[1]->get_value() != 0) {
+ ratio_xy = spin[0]->get_value() / spin[1]->get_value();
+ ratio_yx = spin[1]->get_value() / spin[0]->get_value();
+ } else {
+ ratio_xy = 1.0;
+ ratio_yx = 1.0;
+ }
}
void EditorPropertyVector2::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
+ linked->set_normal_texture(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons")));
+ linked->set_pressed_texture(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
+
const Color *colors = _get_property_colors();
for (int i = 0; i < 2; i++) {
spin[i]->add_theme_color_override("label_color", colors[i]);
@@ -1621,10 +1651,7 @@ void EditorPropertyVector2::_notification(int p_what) {
}
}
-void EditorPropertyVector2::_bind_methods() {
-}
-
-void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix) {
+void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_link, const String &p_suffix) {
for (int i = 0; i < 2; i++) {
spin[i]->set_min(p_min);
spin[i]->set_max(p_max);
@@ -1634,24 +1661,34 @@ void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, boo
spin[i]->set_allow_lesser(true);
spin[i]->set_suffix(p_suffix);
}
+
+ if (!p_link) {
+ linked->hide();
+ } else {
+ linked->set_pressed(true);
+ }
}
EditorPropertyVector2::EditorPropertyVector2(bool p_force_wide) {
bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector2_editing"));
+ HBoxContainer *hb = memnew(HBoxContainer);
+ hb->set_h_size_flags(SIZE_EXPAND_FILL);
+
BoxContainer *bc;
if (p_force_wide) {
bc = memnew(HBoxContainer);
- add_child(bc);
+ hb->add_child(bc);
} else if (horizontal) {
bc = memnew(HBoxContainer);
- add_child(bc);
- set_bottom_editor(bc);
+ hb->add_child(bc);
+ set_bottom_editor(hb);
} else {
bc = memnew(VBoxContainer);
- add_child(bc);
+ hb->add_child(bc);
}
+ bc->set_h_size_flags(SIZE_EXPAND_FILL);
static const char *desc[2] = { "x", "y" };
for (int i = 0; i < 2; i++) {
@@ -1666,6 +1703,13 @@ EditorPropertyVector2::EditorPropertyVector2(bool p_force_wide) {
}
}
+ linked = memnew(TextureButton);
+ linked->set_toggle_mode(true);
+ linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
+ linked->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyVector2::_update_ratio));
+ hb->add_child(linked);
+
+ add_child(hb);
if (!horizontal) {
set_label_reference(spin[0]); //show text and buttons around this
}
@@ -1787,6 +1831,25 @@ void EditorPropertyVector3::_value_changed(double val, const String &p_name) {
return;
}
+ if (linked->is_pressed()) {
+ setting = true;
+ if (p_name == "x") {
+ spin[1]->set_value(spin[0]->get_value() * ratio_yx);
+ spin[2]->set_value(spin[0]->get_value() * ratio_zx);
+ }
+
+ if (p_name == "y") {
+ spin[0]->set_value(spin[1]->get_value() * ratio_xy);
+ spin[2]->set_value(spin[1]->get_value() * ratio_zy);
+ }
+
+ if (p_name == "z") {
+ spin[0]->set_value(spin[2]->get_value() * ratio_xz);
+ spin[1]->set_value(spin[2]->get_value() * ratio_yz);
+ }
+ setting = false;
+ }
+
Vector3 v3;
v3.x = spin[0]->get_value();
v3.y = spin[1]->get_value();
@@ -1801,6 +1864,27 @@ void EditorPropertyVector3::_value_changed(double val, const String &p_name) {
void EditorPropertyVector3::update_property() {
update_using_vector(get_edited_object()->get(get_edited_property()));
+ _update_ratio();
+}
+
+void EditorPropertyVector3::_update_ratio() {
+ linked->set_modulate(Color(1, 1, 1, linked->is_pressed() ? 1.0 : 0.5));
+
+ if (spin[0]->get_value() != 0 && spin[1]->get_value() != 0) {
+ ratio_yx = spin[1]->get_value() / spin[0]->get_value();
+ ratio_zx = spin[2]->get_value() / spin[0]->get_value();
+ ratio_xy = spin[0]->get_value() / spin[1]->get_value();
+ ratio_zy = spin[2]->get_value() / spin[1]->get_value();
+ ratio_xz = spin[0]->get_value() / spin[2]->get_value();
+ ratio_yz = spin[1]->get_value() / spin[2]->get_value();
+ } else {
+ ratio_yx = 1.0;
+ ratio_zx = 1.0;
+ ratio_xy = 1.0;
+ ratio_zy = 1.0;
+ ratio_xz = 1.0;
+ ratio_yz = 1.0;
+ }
}
void EditorPropertyVector3::update_using_vector(Vector3 p_vector) {
@@ -1834,6 +1918,9 @@ void EditorPropertyVector3::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
+ linked->set_normal_texture(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons")));
+ linked->set_pressed_texture(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
+
const Color *colors = _get_property_colors();
for (int i = 0; i < 3; i++) {
spin[i]->add_theme_color_override("label_color", colors[i]);
@@ -1845,7 +1932,7 @@ void EditorPropertyVector3::_notification(int p_what) {
void EditorPropertyVector3::_bind_methods() {
}
-void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix, bool p_angle_in_radians) {
+void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_link, const String &p_suffix, bool p_angle_in_radians) {
angle_in_radians = p_angle_in_radians;
for (int i = 0; i < 3; i++) {
spin[i]->set_min(p_min);
@@ -1856,24 +1943,34 @@ void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, boo
spin[i]->set_allow_lesser(true);
spin[i]->set_suffix(p_suffix);
}
+
+ if (!p_link) {
+ linked->hide();
+ } else {
+ linked->set_pressed(true);
+ }
}
EditorPropertyVector3::EditorPropertyVector3(bool p_force_wide) {
bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));
+ HBoxContainer *hb = memnew(HBoxContainer);
+ hb->set_h_size_flags(SIZE_EXPAND_FILL);
+
BoxContainer *bc;
if (p_force_wide) {
bc = memnew(HBoxContainer);
- add_child(bc);
+ hb->add_child(bc);
} else if (horizontal) {
bc = memnew(HBoxContainer);
- add_child(bc);
- set_bottom_editor(bc);
+ hb->add_child(bc);
+ set_bottom_editor(hb);
} else {
bc = memnew(VBoxContainer);
- add_child(bc);
+ hb->add_child(bc);
}
+ bc->set_h_size_flags(SIZE_EXPAND_FILL);
static const char *desc[3] = { "x", "y", "z" };
for (int i = 0; i < 3; i++) {
@@ -1888,6 +1985,13 @@ EditorPropertyVector3::EditorPropertyVector3(bool p_force_wide) {
}
}
+ linked = memnew(TextureButton);
+ linked->set_toggle_mode(true);
+ linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
+ linked->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyVector3::_update_ratio));
+ hb->add_child(linked);
+
+ add_child(hb);
if (!horizontal) {
set_label_reference(spin[0]); //show text and buttons around this
}
@@ -1906,6 +2010,18 @@ void EditorPropertyVector2i::_value_changed(double val, const String &p_name) {
return;
}
+ if (linked->is_pressed()) {
+ setting = true;
+ if (p_name == "x") {
+ spin[1]->set_value(spin[0]->get_value() * ratio_yx);
+ }
+
+ if (p_name == "y") {
+ spin[0]->set_value(spin[1]->get_value() * ratio_xy);
+ }
+ setting = false;
+ }
+
Vector2i v2;
v2.x = spin[0]->get_value();
v2.y = spin[1]->get_value();
@@ -1918,12 +2034,28 @@ void EditorPropertyVector2i::update_property() {
spin[0]->set_value(val.x);
spin[1]->set_value(val.y);
setting = false;
+ _update_ratio();
+}
+
+void EditorPropertyVector2i::_update_ratio() {
+ linked->set_modulate(Color(1, 1, 1, linked->is_pressed() ? 1.0 : 0.5));
+
+ if (spin[0]->get_value() != 0 && spin[1]->get_value() != 0) {
+ ratio_xy = spin[0]->get_value() / spin[1]->get_value();
+ ratio_yx = spin[1]->get_value() / spin[0]->get_value();
+ } else {
+ ratio_xy = 1.0;
+ ratio_yx = 1.0;
+ }
}
void EditorPropertyVector2i::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
+ linked->set_normal_texture(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons")));
+ linked->set_pressed_texture(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
+
const Color *colors = _get_property_colors();
for (int i = 0; i < 2; i++) {
spin[i]->add_theme_color_override("label_color", colors[i]);
@@ -1932,10 +2064,7 @@ void EditorPropertyVector2i::_notification(int p_what) {
}
}
-void EditorPropertyVector2i::_bind_methods() {
-}
-
-void EditorPropertyVector2i::setup(int p_min, int p_max, bool p_no_slider, const String &p_suffix) {
+void EditorPropertyVector2i::setup(int p_min, int p_max, bool p_no_slider, bool p_link, const String &p_suffix) {
for (int i = 0; i < 2; i++) {
spin[i]->set_min(p_min);
spin[i]->set_max(p_max);
@@ -1945,24 +2074,34 @@ void EditorPropertyVector2i::setup(int p_min, int p_max, bool p_no_slider, const
spin[i]->set_allow_lesser(true);
spin[i]->set_suffix(p_suffix);
}
+
+ if (!p_link) {
+ linked->hide();
+ } else {
+ linked->set_pressed(true);
+ }
}
EditorPropertyVector2i::EditorPropertyVector2i(bool p_force_wide) {
bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector2_editing"));
+ HBoxContainer *hb = memnew(HBoxContainer);
+ hb->set_h_size_flags(SIZE_EXPAND_FILL);
+
BoxContainer *bc;
if (p_force_wide) {
bc = memnew(HBoxContainer);
- add_child(bc);
+ hb->add_child(bc);
} else if (horizontal) {
bc = memnew(HBoxContainer);
- add_child(bc);
- set_bottom_editor(bc);
+ hb->add_child(bc);
+ set_bottom_editor(hb);
} else {
bc = memnew(VBoxContainer);
- add_child(bc);
+ hb->add_child(bc);
}
+ bc->set_h_size_flags(SIZE_EXPAND_FILL);
static const char *desc[2] = { "x", "y" };
for (int i = 0; i < 2; i++) {
@@ -1977,6 +2116,13 @@ EditorPropertyVector2i::EditorPropertyVector2i(bool p_force_wide) {
}
}
+ linked = memnew(TextureButton);
+ linked->set_toggle_mode(true);
+ linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
+ linked->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyVector2i::_update_ratio));
+ hb->add_child(linked);
+
+ add_child(hb);
if (!horizontal) {
set_label_reference(spin[0]); //show text and buttons around this
}
@@ -2098,6 +2244,25 @@ void EditorPropertyVector3i::_value_changed(double val, const String &p_name) {
return;
}
+ if (linked->is_pressed()) {
+ setting = true;
+ if (p_name == "x") {
+ spin[1]->set_value(spin[0]->get_value() * ratio_yx);
+ spin[2]->set_value(spin[0]->get_value() * ratio_zx);
+ }
+
+ if (p_name == "y") {
+ spin[0]->set_value(spin[1]->get_value() * ratio_xy);
+ spin[2]->set_value(spin[1]->get_value() * ratio_zy);
+ }
+
+ if (p_name == "z") {
+ spin[0]->set_value(spin[2]->get_value() * ratio_xz);
+ spin[1]->set_value(spin[2]->get_value() * ratio_yz);
+ }
+ setting = false;
+ }
+
Vector3i v3;
v3.x = spin[0]->get_value();
v3.y = spin[1]->get_value();
@@ -2112,12 +2277,36 @@ void EditorPropertyVector3i::update_property() {
spin[1]->set_value(val.y);
spin[2]->set_value(val.z);
setting = false;
+ _update_ratio();
+}
+
+void EditorPropertyVector3i::_update_ratio() {
+ linked->set_modulate(Color(1, 1, 1, linked->is_pressed() ? 1.0 : 0.5));
+
+ if (spin[0]->get_value() != 0 && spin[1]->get_value() != 0) {
+ ratio_yx = spin[1]->get_value() / spin[0]->get_value();
+ ratio_zx = spin[2]->get_value() / spin[0]->get_value();
+ ratio_xy = spin[0]->get_value() / spin[1]->get_value();
+ ratio_zy = spin[2]->get_value() / spin[1]->get_value();
+ ratio_xz = spin[0]->get_value() / spin[2]->get_value();
+ ratio_yz = spin[1]->get_value() / spin[2]->get_value();
+ } else {
+ ratio_yx = 1.0;
+ ratio_zx = 1.0;
+ ratio_xy = 1.0;
+ ratio_zy = 1.0;
+ ratio_xz = 1.0;
+ ratio_yz = 1.0;
+ }
}
void EditorPropertyVector3i::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
+ linked->set_normal_texture(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons")));
+ linked->set_pressed_texture(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
+
const Color *colors = _get_property_colors();
for (int i = 0; i < 3; i++) {
spin[i]->add_theme_color_override("label_color", colors[i]);
@@ -2129,7 +2318,7 @@ void EditorPropertyVector3i::_notification(int p_what) {
void EditorPropertyVector3i::_bind_methods() {
}
-void EditorPropertyVector3i::setup(int p_min, int p_max, bool p_no_slider, const String &p_suffix) {
+void EditorPropertyVector3i::setup(int p_min, int p_max, bool p_no_slider, bool p_link, const String &p_suffix) {
for (int i = 0; i < 3; i++) {
spin[i]->set_min(p_min);
spin[i]->set_max(p_max);
@@ -2139,22 +2328,31 @@ void EditorPropertyVector3i::setup(int p_min, int p_max, bool p_no_slider, const
spin[i]->set_allow_lesser(true);
spin[i]->set_suffix(p_suffix);
}
+
+ if (!p_link) {
+ linked->hide();
+ } else {
+ linked->set_pressed(true);
+ }
}
EditorPropertyVector3i::EditorPropertyVector3i(bool p_force_wide) {
bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));
+ HBoxContainer *hb = memnew(HBoxContainer);
+ hb->set_h_size_flags(SIZE_EXPAND_FILL);
+
BoxContainer *bc;
if (p_force_wide) {
bc = memnew(HBoxContainer);
- add_child(bc);
+ hb->add_child(bc);
} else if (horizontal) {
bc = memnew(HBoxContainer);
- add_child(bc);
- set_bottom_editor(bc);
+ hb->add_child(bc);
+ set_bottom_editor(hb);
} else {
bc = memnew(VBoxContainer);
- add_child(bc);
+ hb->add_child(bc);
}
static const char *desc[3] = { "x", "y", "z" };
@@ -2169,7 +2367,15 @@ EditorPropertyVector3i::EditorPropertyVector3i(bool p_force_wide) {
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
}
}
+ bc->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ linked = memnew(TextureButton);
+ linked->set_toggle_mode(true);
+ linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
+ linked->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyVector3i::_update_ratio));
+ hb->add_child(linked);
+ add_child(hb);
if (!horizontal) {
set_label_reference(spin[0]); //show text and buttons around this
}
@@ -2948,6 +3154,7 @@ void EditorPropertyNodePath::_bind_methods() {
EditorPropertyNodePath::EditorPropertyNodePath() {
HBoxContainer *hbc = memnew(HBoxContainer);
+ hbc->add_theme_constant_override("separation", 0);
add_child(hbc);
assign = memnew(Button);
assign->set_flat(true);
@@ -3134,17 +3341,15 @@ void EditorPropertyResource::_update_property_bg() {
count_subinspectors = MIN(15, count_subinspectors);
add_theme_color_override("property_color", get_theme_color(SNAME("sub_inspector_property_color"), SNAME("Editor")));
- add_theme_style_override("bg_selected", get_theme_stylebox("sub_inspector_property_bg_selected" + itos(count_subinspectors), SNAME("Editor")));
+ add_theme_style_override("bg_selected", get_theme_stylebox("sub_inspector_property_bg" + itos(count_subinspectors), SNAME("Editor")));
add_theme_style_override("bg", get_theme_stylebox("sub_inspector_property_bg" + itos(count_subinspectors), SNAME("Editor")));
- add_theme_constant_override("font_offset", get_theme_constant(SNAME("sub_inspector_font_offset"), SNAME("Editor")));
add_theme_constant_override("v_separation", 0);
} else {
add_theme_color_override("property_color", get_theme_color(SNAME("property_color"), SNAME("EditorProperty")));
add_theme_style_override("bg_selected", get_theme_stylebox(SNAME("bg_selected"), SNAME("EditorProperty")));
add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("EditorProperty")));
add_theme_constant_override("v_separation", get_theme_constant(SNAME("v_separation"), SNAME("EditorProperty")));
- add_theme_constant_override("font_offset", get_theme_constant(SNAME("font_offset"), SNAME("EditorProperty")));
}
updating_theme = false;
@@ -3492,7 +3697,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
- editor->setup(hint.min, hint.max, hint.step, hint.greater, hint.lesser);
+ editor->setup(hint.min, hint.max, hint.step, hint.greater, hint.lesser, hint.suffix);
return editor;
}
@@ -3607,14 +3812,14 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
EditorPropertyVector2 *editor = memnew(EditorPropertyVector2(p_wide));
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
- editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
+ editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix);
return editor;
} break;
case Variant::VECTOR2I: {
EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide));
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
- editor->setup(hint.min, hint.max, hint.hide_slider, hint.suffix);
+ editor->setup(hint.min, hint.max, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix);
return editor;
} break;
@@ -3634,14 +3839,14 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
case Variant::VECTOR3: {
EditorPropertyVector3 *editor = memnew(EditorPropertyVector3(p_wide));
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
- editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix, hint.radians);
+ editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix, hint.radians);
return editor;
} break;
case Variant::VECTOR3I: {
EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide));
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1);
- editor->setup(hint.min, hint.max, hint.hide_slider, hint.suffix);
+ editor->setup(hint.min, hint.max, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix);
return editor;
} break;
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index a3990db678..a3b98b7724 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -368,7 +368,7 @@ protected:
public:
virtual void update_property() override;
- void setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_allow_greater, bool p_allow_lesser);
+ void setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix = String());
EditorPropertyInteger();
};
@@ -451,16 +451,19 @@ class EditorPropertyVector2 : public EditorProperty {
GDCLASS(EditorPropertyVector2, EditorProperty);
EditorSpinSlider *spin[2];
bool setting = false;
+ double ratio_xy = 1.0;
+ double ratio_yx = 1.0;
+ TextureButton *linked = nullptr;
+ void _update_ratio();
void _value_changed(double p_val, const String &p_name);
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
- void setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix = String());
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_link = false, const String &p_suffix = String());
EditorPropertyVector2(bool p_force_wide = false);
};
@@ -486,6 +489,14 @@ class EditorPropertyVector3 : public EditorProperty {
EditorSpinSlider *spin[3];
bool setting = false;
bool angle_in_radians = false;
+ double ratio_yx = 1.0;
+ double ratio_zx = 1.0;
+ double ratio_xy = 1.0;
+ double ratio_zy = 1.0;
+ double ratio_xz = 1.0;
+ double ratio_yz = 1.0;
+ TextureButton *linked = nullptr;
+ void _update_ratio();
void _value_changed(double p_val, const String &p_name);
protected:
@@ -497,7 +508,7 @@ public:
virtual void update_property() override;
virtual void update_using_vector(Vector3 p_vector);
virtual Vector3 get_vector();
- void setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix = String(), bool p_angle_in_radians = false);
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_link = false, const String &p_suffix = String(), bool p_angle_in_radians = false);
EditorPropertyVector3(bool p_force_wide = false);
};
@@ -505,16 +516,19 @@ class EditorPropertyVector2i : public EditorProperty {
GDCLASS(EditorPropertyVector2i, EditorProperty);
EditorSpinSlider *spin[2];
bool setting = false;
+ double ratio_xy = 1.0;
+ double ratio_yx = 1.0;
+ TextureButton *linked = nullptr;
+ void _update_ratio();
void _value_changed(double p_val, const String &p_name);
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
- void setup(int p_min, int p_max, bool p_no_slider, const String &p_suffix = String());
+ void setup(int p_min, int p_max, bool p_no_slider, bool p_link = false, const String &p_suffix = String());
EditorPropertyVector2i(bool p_force_wide = false);
};
@@ -539,6 +553,14 @@ class EditorPropertyVector3i : public EditorProperty {
GDCLASS(EditorPropertyVector3i, EditorProperty);
EditorSpinSlider *spin[3];
bool setting = false;
+ double ratio_yx = 1.0;
+ double ratio_zx = 1.0;
+ double ratio_xy = 1.0;
+ double ratio_zy = 1.0;
+ double ratio_xz = 1.0;
+ double ratio_yz = 1.0;
+ TextureButton *linked = nullptr;
+ void _update_ratio();
void _value_changed(double p_val, const String &p_name);
protected:
@@ -548,7 +570,7 @@ protected:
public:
virtual void update_property() override;
- void setup(int p_min, int p_max, bool p_no_slider, const String &p_suffix = String());
+ void setup(int p_min, int p_max, bool p_no_slider, bool p_link = false, const String &p_suffix = String());
EditorPropertyVector3i(bool p_force_wide = false);
};
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index b3a1f35218..f1a3fe0c57 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -217,11 +217,11 @@ void EditorPropertyArray::update_property() {
if (array.get_type() == Variant::NIL) {
edit->set_text(vformat(TTR("(Nil) %s"), array_type_name));
edit->set_pressed(false);
- if (vbox) {
+ if (container) {
set_bottom_editor(nullptr);
- memdelete(vbox);
+ memdelete(container);
button_add_item = nullptr;
- vbox = nullptr;
+ container = nullptr;
}
return;
}
@@ -241,10 +241,14 @@ void EditorPropertyArray::update_property() {
if (unfolded) {
updating = true;
- if (!vbox) {
- vbox = memnew(VBoxContainer);
- add_child(vbox);
- set_bottom_editor(vbox);
+ if (!container) {
+ container = memnew(MarginContainer);
+ container->set_theme_type_variation("MarginContainer4px");
+ add_child(container);
+ set_bottom_editor(container);
+
+ VBoxContainer *vbox = memnew(VBoxContainer);
+ container->add_child(vbox);
HBoxContainer *hbox = memnew(HBoxContainer);
vbox->add_child(hbox);
@@ -343,6 +347,7 @@ void EditorPropertyArray::update_property() {
prop->set_object_and_property(object.ptr(), prop_name);
prop->set_label(itos(i + offset));
prop->set_selectable(false);
+ prop->set_use_folding(is_using_folding());
prop->connect("property_changed", callable_mp(this, &EditorPropertyArray::_property_changed));
prop->connect("object_id_selected", callable_mp(this, &EditorPropertyArray::_object_id_selected));
prop->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -372,11 +377,11 @@ void EditorPropertyArray::update_property() {
updating = false;
} else {
- if (vbox) {
+ if (container) {
set_bottom_editor(nullptr);
- memdelete(vbox);
+ memdelete(container);
button_add_item = nullptr;
- vbox = nullptr;
+ container = nullptr;
}
}
}
@@ -687,7 +692,7 @@ EditorPropertyArray::EditorPropertyArray() {
add_child(edit);
add_focusable(edit);
- vbox = nullptr;
+ container = nullptr;
property_vbox = nullptr;
size_slider = nullptr;
button_add_item = nullptr;
@@ -791,11 +796,11 @@ void EditorPropertyDictionary::update_property() {
if (updated_val.get_type() == Variant::NIL) {
edit->set_text(TTR("Dictionary (Nil)")); // This provides symmetry with the array property.
edit->set_pressed(false);
- if (vbox) {
+ if (container) {
set_bottom_editor(nullptr);
- memdelete(vbox);
+ memdelete(container);
button_add_item = nullptr;
- vbox = nullptr;
+ container = nullptr;
}
return;
}
@@ -812,10 +817,14 @@ void EditorPropertyDictionary::update_property() {
if (unfolded) {
updating = true;
- if (!vbox) {
- vbox = memnew(VBoxContainer);
- add_child(vbox);
- set_bottom_editor(vbox);
+ if (!container) {
+ container = memnew(MarginContainer);
+ container->set_theme_type_variation("MarginContainer4px");
+ add_child(container);
+ set_bottom_editor(container);
+
+ VBoxContainer *vbox = memnew(VBoxContainer);
+ container->add_child(vbox);
property_vbox = memnew(VBoxContainer);
property_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -998,6 +1007,7 @@ void EditorPropertyDictionary::update_property() {
} else {
EditorPropertyResource *editor = memnew(EditorPropertyResource);
editor->setup(object.ptr(), prop_name, "Resource");
+ editor->set_use_folding(is_using_folding());
prop = editor;
}
@@ -1116,11 +1126,11 @@ void EditorPropertyDictionary::update_property() {
updating = false;
} else {
- if (vbox) {
+ if (container) {
set_bottom_editor(nullptr);
- memdelete(vbox);
+ memdelete(container);
button_add_item = nullptr;
- vbox = nullptr;
+ container = nullptr;
}
}
}
@@ -1188,7 +1198,7 @@ EditorPropertyDictionary::EditorPropertyDictionary() {
add_child(edit);
add_focusable(edit);
- vbox = nullptr;
+ container = nullptr;
button_add_item = nullptr;
paginator = nullptr;
change_type = memnew(PopupMenu);
@@ -1250,11 +1260,11 @@ void EditorPropertyLocalizableString::update_property() {
if (updated_val.get_type() == Variant::NIL) {
edit->set_text(TTR("Localizable String (Nil)")); // This provides symmetry with the array property.
edit->set_pressed(false);
- if (vbox) {
+ if (container) {
set_bottom_editor(nullptr);
- memdelete(vbox);
+ memdelete(container);
button_add_item = nullptr;
- vbox = nullptr;
+ container = nullptr;
}
return;
}
@@ -1271,10 +1281,14 @@ void EditorPropertyLocalizableString::update_property() {
if (unfolded) {
updating = true;
- if (!vbox) {
- vbox = memnew(VBoxContainer);
- add_child(vbox);
- set_bottom_editor(vbox);
+ if (!container) {
+ container = memnew(MarginContainer);
+ container->set_theme_type_variation("MarginContainer4px");
+ add_child(container);
+ set_bottom_editor(container);
+
+ VBoxContainer *vbox = memnew(VBoxContainer);
+ container->add_child(vbox);
property_vbox = memnew(VBoxContainer);
property_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -1351,11 +1365,11 @@ void EditorPropertyLocalizableString::update_property() {
updating = false;
} else {
- if (vbox) {
+ if (container) {
set_bottom_editor(nullptr);
- memdelete(vbox);
+ memdelete(container);
button_add_item = nullptr;
- vbox = nullptr;
+ container = nullptr;
}
}
}
@@ -1410,7 +1424,7 @@ EditorPropertyLocalizableString::EditorPropertyLocalizableString() {
add_child(edit);
add_focusable(edit);
- vbox = nullptr;
+ container = nullptr;
button_add_item = nullptr;
paginator = nullptr;
updating = false;
diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h
index 070353c538..44623149d0 100644
--- a/editor/editor_properties_array_dict.h
+++ b/editor/editor_properties_array_dict.h
@@ -89,7 +89,7 @@ class EditorPropertyArray : public EditorProperty {
int page_index = 0;
int changing_type_index;
Button *edit = nullptr;
- VBoxContainer *vbox = nullptr;
+ MarginContainer *container = nullptr;
VBoxContainer *property_vbox = nullptr;
EditorSpinSlider *size_slider = nullptr;
Button *button_add_item = nullptr;
@@ -146,9 +146,9 @@ class EditorPropertyDictionary : public EditorProperty {
int page_index = 0;
int changing_type_index;
Button *edit = nullptr;
- VBoxContainer *vbox = nullptr;
+ MarginContainer *container = nullptr;
VBoxContainer *property_vbox = nullptr;
- EditorSpinSlider *size_slider = nullptr;
+ EditorSpinSlider *size_sliderv;
Button *button_add_item = nullptr;
EditorPaginator *paginator = nullptr;
@@ -181,7 +181,7 @@ class EditorPropertyLocalizableString : public EditorProperty {
int page_length = 20;
int page_index = 0;
Button *edit = nullptr;
- VBoxContainer *vbox = nullptr;
+ MarginContainer *container = nullptr;
VBoxContainer *property_vbox = nullptr;
EditorSpinSlider *size_slider = nullptr;
Button *button_add_item = nullptr;
diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp
index 1e7638bf72..397afc0653 100644
--- a/editor/editor_property_name_processor.cpp
+++ b/editor/editor_property_name_processor.cpp
@@ -213,6 +213,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["stdout"] = "stdout";
capitalize_string_remaps["sv"] = "SV";
capitalize_string_remaps["svg"] = "SVG";
+ capitalize_string_remaps["taa"] = "TAA";
capitalize_string_remaps["tcp"] = "TCP";
capitalize_string_remaps["ui"] = "UI";
capitalize_string_remaps["url"] = "URL";
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 34aa7217fa..4d53ddb344 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -903,6 +903,8 @@ EditorResourcePicker::EditorResourcePicker() {
edit_button->connect("pressed", callable_mp(this, &EditorResourcePicker::_update_menu));
add_child(edit_button);
edit_button->connect("gui_input", callable_mp(this, &EditorResourcePicker::_button_input));
+
+ add_theme_constant_override("separation", 0);
}
// EditorScriptPicker
diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp
index 5db7b8673f..6ce9e5fa6f 100644
--- a/editor/editor_run_native.cpp
+++ b/editor/editor_run_native.cpp
@@ -151,7 +151,13 @@ Error EditorRunNative::run_native(int p_idx, int p_platform) {
flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_NAVIGATION;
}
- return eep->run(preset, p_idx, flags);
+ eep->clear_messages();
+ Error err = eep->run(preset, p_idx, flags);
+ result_dialog_log->clear();
+ if (eep->fill_log_messages(result_dialog_log, err)) {
+ result_dialog->popup_centered_ratio(0.5);
+ }
+ return err;
}
void EditorRunNative::resume_run_native() {
@@ -167,6 +173,15 @@ bool EditorRunNative::is_deploy_debug_remote_enabled() const {
}
EditorRunNative::EditorRunNative() {
+ result_dialog = memnew(AcceptDialog);
+ result_dialog->set_title(TTR("Project Run"));
+ result_dialog_log = memnew(RichTextLabel);
+ result_dialog_log->set_custom_minimum_size(Size2(300, 80) * EDSCALE);
+ result_dialog->add_child(result_dialog_log);
+
+ add_child(result_dialog);
+ result_dialog->hide();
+
set_process(true);
resume_idx = 0;
resume_platform = 0;
diff --git a/editor/editor_run_native.h b/editor/editor_run_native.h
index 798a0371a4..66d9b0402a 100644
--- a/editor/editor_run_native.h
+++ b/editor/editor_run_native.h
@@ -32,11 +32,16 @@
#define EDITOR_RUN_NATIVE_H
#include "scene/gui/box_container.h"
+#include "scene/gui/dialogs.h"
#include "scene/gui/menu_button.h"
+#include "scene/gui/rich_text_label.h"
class EditorRunNative : public HBoxContainer {
GDCLASS(EditorRunNative, HBoxContainer);
+ RichTextLabel *result_dialog_log = nullptr;
+ AcceptDialog *result_dialog = nullptr;
+
HashMap<int, MenuButton *> menus;
bool first = true;
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
index 83944cc553..a0c818ba84 100644
--- a/editor/editor_spin_slider.cpp
+++ b/editor/editor_spin_slider.cpp
@@ -303,11 +303,11 @@ void EditorSpinSlider::_draw_spin_slider() {
Color lc = get_theme_color(is_read_only() ? SNAME("read_only_label_color") : SNAME("label_color"));
if (flat && !label.is_empty()) {
- Color label_bg_color = get_theme_color(SNAME("dark_color_3"), SNAME("Editor"));
+ Ref<StyleBox> label_bg = get_theme_stylebox(SNAME("label_bg"), SNAME("EditorSpinSlider"));
if (rtl) {
- draw_rect(Rect2(Vector2(size.width - (sb->get_offset().x * 2 + label_width), 0), Vector2(sb->get_offset().x * 2 + label_width, size.height)), label_bg_color);
+ draw_style_box(label_bg, Rect2(Vector2(size.width - (sb->get_offset().x * 2 + label_width), 0), Vector2(sb->get_offset().x * 2 + label_width, size.height)));
} else {
- draw_rect(Rect2(Vector2(), Vector2(sb->get_offset().x * 2 + label_width, size.height)), label_bg_color);
+ draw_style_box(label_bg, Rect2(Vector2(), Vector2(sb->get_offset().x * 2 + label_width, size.height)));
}
}
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index fa106979be..7a80cf36a8 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -72,11 +72,13 @@ static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left =
style->set_bg_color(p_color);
// Adjust level of detail based on the corners' effective sizes.
style->set_corner_detail(Math::ceil(1.5 * p_corner_width * EDSCALE));
- style->set_corner_radius_all(p_corner_width);
+ style->set_corner_radius_all(p_corner_width * EDSCALE);
style->set_default_margin(SIDE_LEFT, p_margin_left * EDSCALE);
style->set_default_margin(SIDE_RIGHT, p_margin_right * EDSCALE);
style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * EDSCALE);
style->set_default_margin(SIDE_TOP, p_margin_top * EDSCALE);
+ // Work around issue about antialiased edges being blurrier (GH-35279).
+ style->set_anti_aliased(false);
return style;
}
@@ -564,8 +566,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Styleboxes
// This is the most commonly used stylebox, variations should be made as duplicate of this
Ref<StyleBoxFlat> style_default = make_flat_stylebox(base_color, default_margin_size, default_margin_size, default_margin_size, default_margin_size, corner_width);
- // Work around issue about antialiased edges being blurrier (GH-35279).
- style_default->set_anti_aliased(false);
style_default->set_border_width_all(border_width);
style_default->set_border_color(base_color);
style_default->set_draw_center(true);
@@ -692,6 +692,21 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_canvas_editor_info->set_expand_margin_size_all(4 * EDSCALE);
theme->set_stylebox("CanvasItemInfoOverlay", "EditorStyles", style_canvas_editor_info);
+ // 2D and 3D contextual toolbar.
+ // Use a custom stylebox to make contextual menu items stand out from the rest.
+ // This helps with editor usability as contextual menu items change when selecting nodes,
+ // even though it may not be immediately obvious at first.
+ Ref<StyleBoxFlat> toolbar_stylebox = memnew(StyleBoxFlat);
+ toolbar_stylebox->set_bg_color(accent_color * Color(1, 1, 1, 0.1));
+ toolbar_stylebox->set_corner_radius(CORNER_TOP_LEFT, corner_radius * EDSCALE);
+ toolbar_stylebox->set_corner_radius(CORNER_TOP_RIGHT, corner_radius * EDSCALE);
+ toolbar_stylebox->set_anti_aliased(false);
+ // Add an underline to the StyleBox, but prevent its minimum vertical size from changing.
+ toolbar_stylebox->set_border_color(accent_color);
+ toolbar_stylebox->set_border_width(SIDE_BOTTOM, Math::round(2 * EDSCALE));
+ toolbar_stylebox->set_default_margin(SIDE_BOTTOM, 0);
+ theme->set_stylebox("ContextualToolbar", "EditorStyles", toolbar_stylebox);
+
// Script Editor
theme->set_stylebox("ScriptEditorPanel", "EditorStyles", make_empty_stylebox(default_margin_size, 0, default_margin_size, default_margin_size));
theme->set_stylebox("ScriptEditor", "EditorStyles", make_empty_stylebox(0, 0, 0, 0));
@@ -910,6 +925,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("item_start_padding", "PopupMenu", popup_menu_margin_size * EDSCALE);
theme->set_constant("item_end_padding", "PopupMenu", popup_menu_margin_size * EDSCALE);
+ // Sub-inspectors
for (int i = 0; i < 16; i++) {
Color si_base_color = accent_color;
@@ -917,51 +933,53 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
si_base_color.set_hsv(Math::fmod(float(si_base_color.get_h() + hue_rotate), float(1.0)), si_base_color.get_s(), si_base_color.get_v());
si_base_color = accent_color.lerp(si_base_color, float(EDITOR_GET("docks/property_editor/subresource_hue_tint")));
- Ref<StyleBoxFlat> sub_inspector_bg;
-
- sub_inspector_bg = make_flat_stylebox(dark_color_1.lerp(si_base_color, 0.08), 2, 0, 2, 2);
-
- sub_inspector_bg->set_border_width(SIDE_LEFT, 2);
- sub_inspector_bg->set_border_width(SIDE_RIGHT, 2);
- sub_inspector_bg->set_border_width(SIDE_BOTTOM, 2);
- sub_inspector_bg->set_border_width(SIDE_TOP, 2);
- sub_inspector_bg->set_default_margin(SIDE_LEFT, 3);
- sub_inspector_bg->set_default_margin(SIDE_RIGHT, 3);
- sub_inspector_bg->set_default_margin(SIDE_BOTTOM, 10);
- sub_inspector_bg->set_default_margin(SIDE_TOP, 5);
+ // Sub-inspector background.
+ Ref<StyleBoxFlat> sub_inspector_bg = style_default->duplicate();
+ sub_inspector_bg->set_bg_color(dark_color_1.lerp(si_base_color, 0.08));
+ sub_inspector_bg->set_border_width_all(2 * EDSCALE);
sub_inspector_bg->set_border_color(si_base_color * Color(0.7, 0.7, 0.7, 0.8));
- sub_inspector_bg->set_draw_center(true);
+ sub_inspector_bg->set_default_margin(SIDE_LEFT, 4 * EDSCALE);
+ sub_inspector_bg->set_default_margin(SIDE_RIGHT, 4 * EDSCALE);
+ sub_inspector_bg->set_default_margin(SIDE_BOTTOM, 4 * EDSCALE);
+ sub_inspector_bg->set_default_margin(SIDE_TOP, 4 * EDSCALE);
+ sub_inspector_bg->set_corner_radius(CORNER_TOP_LEFT, 0);
+ sub_inspector_bg->set_corner_radius(CORNER_TOP_RIGHT, 0);
theme->set_stylebox("sub_inspector_bg" + itos(i), "Editor", sub_inspector_bg);
- Ref<StyleBoxFlat> bg_color;
- bg_color.instantiate();
- bg_color->set_bg_color(si_base_color * Color(0.7, 0.7, 0.7, 0.8));
- bg_color->set_border_width_all(0);
-
- Ref<StyleBoxFlat> bg_color_selected;
- bg_color_selected.instantiate();
- bg_color_selected->set_border_width_all(0);
- bg_color_selected->set_bg_color(si_base_color * Color(0.8, 0.8, 0.8, 0.8));
+ // EditorProperty background while it has a sub-inspector open.
+ Ref<StyleBoxFlat> bg_color = make_flat_stylebox(si_base_color * Color(0.7, 0.7, 0.7, 0.8), 0, 0, 0, 0, corner_radius);
+ bg_color->set_anti_aliased(false);
+ bg_color->set_corner_radius(CORNER_BOTTOM_LEFT, 0);
+ bg_color->set_corner_radius(CORNER_BOTTOM_RIGHT, 0);
theme->set_stylebox("sub_inspector_property_bg" + itos(i), "Editor", bg_color);
- theme->set_stylebox("sub_inspector_property_bg_selected" + itos(i), "Editor", bg_color_selected);
}
theme->set_color("sub_inspector_property_color", "Editor", dark_theme ? Color(1, 1, 1, 1) : Color(0, 0, 0, 1));
- theme->set_constant("sub_inspector_font_offset", "Editor", 4 * EDSCALE);
// EditorSpinSlider.
theme->set_color("label_color", "EditorSpinSlider", font_color);
theme->set_color("read_only_label_color", "EditorSpinSlider", font_readonly_color);
+ Ref<StyleBoxFlat> editor_spin_label_bg = style_default->duplicate();
+ editor_spin_label_bg->set_bg_color(dark_color_3);
+ editor_spin_label_bg->set_border_width_all(0);
+ theme->set_stylebox("label_bg", "EditorSpinSlider", editor_spin_label_bg);
+
+ // EditorProperty
Ref<StyleBoxFlat> style_property_bg = style_default->duplicate();
style_property_bg->set_bg_color(highlight_color);
style_property_bg->set_border_width_all(0);
+ Ref<StyleBoxFlat> style_property_child_bg = style_default->duplicate();
+ style_property_child_bg->set_bg_color(dark_color_2);
+ style_property_child_bg->set_border_width_all(0);
+
theme->set_constant("font_offset", "EditorProperty", 8 * EDSCALE);
theme->set_stylebox("bg_selected", "EditorProperty", style_property_bg);
theme->set_stylebox("bg", "EditorProperty", Ref<StyleBoxEmpty>(memnew(StyleBoxEmpty)));
+ theme->set_stylebox("child_bg", "EditorProperty", style_property_child_bg);
theme->set_constant("v_separation", "EditorProperty", (extra_spacing + default_margin_size) * EDSCALE);
theme->set_color("warning_color", "EditorProperty", warning_color);
theme->set_color("property_color", "EditorProperty", property_color);
@@ -974,6 +992,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_property_group_note->set_bg_color(property_group_note_color);
theme->set_stylebox("bg_group_note", "EditorProperty", style_property_group_note);
+ // EditorInspectorSection
Color inspector_section_color = font_color.lerp(Color(0.5, 0.5, 0.5), 0.35);
theme->set_color("font_color", "EditorInspectorSection", inspector_section_color);
@@ -1076,11 +1095,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("prop_subsection", "Editor", prop_subsection_color);
theme->set_color("drop_position_color", "Tree", accent_color);
+ // EditorInspectorCategory
Ref<StyleBoxFlat> category_bg = style_default->duplicate();
- // Make Trees easier to distinguish from other controls by using a darker background color.
category_bg->set_bg_color(prop_category_color);
category_bg->set_border_color(prop_category_color);
- theme->set_stylebox("prop_category_style", "Editor", category_bg);
+ theme->set_stylebox("bg", "EditorInspectorCategory", category_bg);
// ItemList
Ref<StyleBoxFlat> style_itemlist_bg = style_default->duplicate();
@@ -1241,20 +1260,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("selection_color", "TextEdit", selection_color);
theme->set_constant("line_spacing", "TextEdit", 4 * EDSCALE);
- // CodeEdit
- theme->set_font("font", "CodeEdit", theme->get_font(SNAME("source"), SNAME("EditorFonts")));
- theme->set_font_size("font_size", "CodeEdit", theme->get_font_size(SNAME("source_size"), SNAME("EditorFonts")));
- theme->set_stylebox("normal", "CodeEdit", style_widget);
- theme->set_stylebox("focus", "CodeEdit", style_widget_hover);
- theme->set_stylebox("read_only", "CodeEdit", style_widget_disabled);
- theme->set_icon("tab", "CodeEdit", theme->get_icon(SNAME("GuiTab"), SNAME("EditorIcons")));
- theme->set_icon("space", "CodeEdit", theme->get_icon(SNAME("GuiSpace"), SNAME("EditorIcons")));
- theme->set_icon("folded", "CodeEdit", theme->get_icon(SNAME("GuiTreeArrowRight"), SNAME("EditorIcons")));
- theme->set_icon("can_fold", "CodeEdit", theme->get_icon(SNAME("GuiTreeArrowDown"), SNAME("EditorIcons")));
- theme->set_icon("executing_line", "CodeEdit", theme->get_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
- theme->set_icon("breakpoint", "CodeEdit", theme->get_icon(SNAME("Breakpoint"), SNAME("EditorIcons")));
- theme->set_constant("line_spacing", "CodeEdit", EDITOR_GET("text_editor/appearance/whitespace/line_spacing"));
-
theme->set_icon("grabber", "VSplitContainer", theme->get_icon(SNAME("GuiVsplitter"), SNAME("EditorIcons")));
theme->set_icon("grabber", "HSplitContainer", theme->get_icon(SNAME("GuiHsplitter"), SNAME("EditorIcons")));
@@ -1278,6 +1283,13 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("h_separation", "VFlowContainer", default_margin_size * EDSCALE);
theme->set_constant("v_separation", "VFlowContainer", default_margin_size * EDSCALE);
+ // Custom theme type for MarginContainer with 4px margins.
+ theme->set_type_variation("MarginContainer4px", "MarginContainer");
+ theme->set_constant("margin_left", "MarginContainer4px", 4 * EDSCALE);
+ theme->set_constant("margin_top", "MarginContainer4px", 4 * EDSCALE);
+ theme->set_constant("margin_right", "MarginContainer4px", 4 * EDSCALE);
+ theme->set_constant("margin_bottom", "MarginContainer4px", 4 * EDSCALE);
+
// Window
// Prevent corner artifacts between window title and body.
@@ -1405,7 +1417,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_pressed_color", "LinkButton", accent_color);
theme->set_color("font_disabled_color", "LinkButton", font_disabled_color);
- // TooltipPanel
+ // TooltipPanel + TooltipLabel
+ // TooltipPanel is also used for custom tooltips, while TooltipLabel
+ // is only relevant for default tooltips.
Ref<StyleBoxFlat> style_tooltip = style_popup->duplicate();
style_tooltip->set_shadow_size(0);
style_tooltip->set_default_margin(SIDE_LEFT, default_margin_size * EDSCALE * 0.5);
@@ -1589,7 +1603,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// ColorPresetButton
Ref<StyleBoxFlat> preset_sb = make_flat_stylebox(Color(1, 1, 1), 2, 2, 2, 2, 2);
- preset_sb->set_anti_aliased(false);
theme->set_stylebox("preset_fg", "ColorPresetButton", preset_sb);
theme->set_icon("preset_bg", "ColorPresetButton", theme->get_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")));
theme->set_icon("overbright_indicator", "ColorPresetButton", theme->get_icon(SNAME("OverbrightIndicator"), SNAME("EditorIcons")));
@@ -1620,7 +1633,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("preview_picker_label", "ThemeEditor", theme_preview_picker_label_sb);
// Dictionary editor add item.
- theme->set_stylebox("DictionaryAddItem", "EditorStyles", make_flat_stylebox(prop_subsection_color, 4, 4, 4, 4, corner_radius));
+ // Expand to the left and right by 4px to compensate for the dictionary editor margins.
+ Ref<StyleBoxFlat> style_dictionary_add_item = make_flat_stylebox(prop_subsection_color, 0, 4, 0, 4, corner_radius);
+ style_dictionary_add_item->set_expand_margin_size(SIDE_LEFT, 4 * EDSCALE);
+ style_dictionary_add_item->set_expand_margin_size(SIDE_RIGHT, 4 * EDSCALE);
+ theme->set_stylebox("DictionaryAddItem", "EditorStyles", style_dictionary_add_item);
// adaptive script theme constants
// for comments and elements with lower relevance
@@ -1715,7 +1732,20 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
}
// Now theme is loaded, apply it to CodeEdit.
- theme->set_color("background_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/background_color"));
+ theme->set_font("font", "CodeEdit", theme->get_font(SNAME("source"), SNAME("EditorFonts")));
+ theme->set_font_size("font_size", "CodeEdit", theme->get_font_size(SNAME("source_size"), SNAME("EditorFonts")));
+ Ref<StyleBoxFlat> code_edit_stylebox = make_flat_stylebox(EDITOR_GET("text_editor/theme/highlighting/background_color"), widget_default_margin.x, widget_default_margin.y, widget_default_margin.x, widget_default_margin.y, corner_radius);
+ theme->set_stylebox("normal", "CodeEdit", code_edit_stylebox);
+ theme->set_stylebox("read_only", "CodeEdit", code_edit_stylebox);
+ theme->set_stylebox("focus", "CodeEdit", Ref<StyleBoxEmpty>(memnew(StyleBoxEmpty)));
+ theme->set_icon("tab", "CodeEdit", theme->get_icon(SNAME("GuiTab"), SNAME("EditorIcons")));
+ theme->set_icon("space", "CodeEdit", theme->get_icon(SNAME("GuiSpace"), SNAME("EditorIcons")));
+ theme->set_icon("folded", "CodeEdit", theme->get_icon(SNAME("GuiTreeArrowRight"), SNAME("EditorIcons")));
+ theme->set_icon("can_fold", "CodeEdit", theme->get_icon(SNAME("GuiTreeArrowDown"), SNAME("EditorIcons")));
+ theme->set_icon("executing_line", "CodeEdit", theme->get_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
+ theme->set_icon("breakpoint", "CodeEdit", theme->get_icon(SNAME("Breakpoint"), SNAME("EditorIcons")));
+ theme->set_constant("line_spacing", "CodeEdit", EDITOR_GET("text_editor/appearance/whitespace/line_spacing"));
+ theme->set_color("background_color", "CodeEdit", Color(0, 0, 0, 0));
theme->set_color("completion_background_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_background_color"));
theme->set_color("completion_selected_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_selected_color"));
theme->set_color("completion_existing_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_existing_color"));
diff --git a/editor/editor_toaster.cpp b/editor/editor_toaster.cpp
index 7ca88bd2a2..4986bccc35 100644
--- a/editor/editor_toaster.cpp
+++ b/editor/editor_toaster.cpp
@@ -234,6 +234,16 @@ void EditorToaster::_auto_hide_or_free_toasts() {
to_delete[i]->queue_delete();
toasts.erase(to_delete[i]);
}
+
+ if (toasts.is_empty()) {
+ main_button->set_tooltip(TTR("No notifications."));
+ main_button->set_modulate(Color(0.5, 0.5, 0.5));
+ main_button->set_disabled(true);
+ } else {
+ main_button->set_tooltip(TTR("Show notifications."));
+ main_button->set_modulate(Color(1, 1, 1));
+ main_button->set_disabled(false);
+ }
}
void EditorToaster::_draw_button() {
@@ -508,6 +518,9 @@ EditorToaster::EditorToaster() {
// Main button.
main_button = memnew(Button);
+ main_button->set_tooltip(TTR("No notifications."));
+ main_button->set_modulate(Color(0.5, 0.5, 0.5));
+ main_button->set_disabled(true);
main_button->set_flat(true);
main_button->connect("pressed", callable_mp(this, &EditorToaster::_set_notifications_enabled), varray(true));
main_button->connect("pressed", callable_mp(this, &EditorToaster::_repop_old));
@@ -521,6 +534,7 @@ EditorToaster::EditorToaster() {
add_child(disable_notifications_panel);
disable_notifications_button = memnew(Button);
+ disable_notifications_button->set_tooltip(TTR("Silence the notifications."));
disable_notifications_button->set_flat(true);
disable_notifications_button->connect("pressed", callable_mp(this, &EditorToaster::_set_notifications_enabled), varray(false));
disable_notifications_panel->add_child(disable_notifications_button);
diff --git a/editor/icons/TerrainConnect.svg b/editor/icons/TerrainConnect.svg
new file mode 100644
index 0000000000..3b6b8fd5a2
--- /dev/null
+++ b/editor/icons/TerrainConnect.svg
@@ -0,0 +1 @@
+<svg height="32" viewBox="0 0 16 16" width="32" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><circle cx="8" cy="8" r="2.000028" stroke-width="1.09711"/><g stroke-width=".5" transform="translate(0 -1036.4)"><path d="m7 1039.9h-1l2-2.5 2 2.5h-1v2h-2z"/><path d="m3.5606601 1041.3748-.7071068.707-.3535533-3.1818 3.1819804.3535-.7071067.7071.7071067.7071-1.4142135 1.4142z"/><path d="m11.0252 1039.9606-.707-.7071 3.1818-.3535-.3535 3.1819-.7071-.7071-.7071.7071-1.4142-1.4142z"/><path d="m12.43934 1047.4252.707107-.707.353553 3.1818-3.18198-.3535.707106-.7071-.707106-.7071 1.414214-1.4142z"/><path d="m4.9748005 1048.8394.707.7071-3.1818.3535.3535-3.1819.7071.7071.7071-.7071 1.4142 1.4142z"/><path d="m12.5 1043.4v-1l2.5 2-2.5 2v-1h-2v-2z"/><path d="m9 1048.9h1l-2 2.5-2-2.5h1v-2h2z"/><path d="m3.5 1045.4v1l-2.5-2 2.5-2v1h2v2z"/></g></g></svg>
diff --git a/editor/icons/TerrainPath.svg b/editor/icons/TerrainPath.svg
new file mode 100644
index 0000000000..6352bbd37e
--- /dev/null
+++ b/editor/icons/TerrainPath.svg
@@ -0,0 +1 @@
+<svg height="32" viewBox="0 0 16 16" width="32" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" transform="translate(0 -1036.4)"><circle cx="3" cy="1049.4" r="2.000028" stroke-width="1.09711"/><circle cx="13" cy="1039.4" r="2.000028" stroke-width="1.09711"/><path d="m7 1038.4v10h-4v2h4 2v-2-8h4v-2h-4z" stroke-width=".46291"/></g></svg>
diff --git a/editor/icons/Unlinked.svg b/editor/icons/Unlinked.svg
new file mode 100644
index 0000000000..6c831eacad
--- /dev/null
+++ b/editor/icons/Unlinked.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><defs><clipPath id="a"><path d="M0 0h16v16H0z"/></clipPath></defs><g clip-path="url(#a)" fill="#e0e0e0"><path d="M1.136 12.036a3.994 3.994 0 0 1-.137-1.047 4.007 4.007 0 0 1 2.965-3.853 1 1 0 0 1 1.225.707 1 1 0 0 1 .034.25 1 1 0 0 1-.741.975 2 2 0 0 0-1.483 1.926 1.994 1.994 0 0 0 .068.523 2 2 0 0 0 2.45 1.415 2 2 0 0 0 1.484-1.931 2 2 0 0 0-.068-.523 1 1 0 0 1-.034-.25 1 1 0 0 1 .742-.975 1 1 0 0 1 1.225.707 3.991 3.991 0 0 1 .137 1.046 4.007 4.007 0 0 1-2.965 3.852 3.993 3.993 0 0 1-1.035.137 4.006 4.006 0 0 1-3.867-2.959zM9.965 8.863a1 1 0 0 1-.742-.975 1 1 0 0 1 .034-.25 1 1 0 0 1 1.225-.706 2 2 0 0 0 2.449-1.415A1.994 1.994 0 0 0 13 4.994a2 2 0 0 0-1.483-1.926 2 2 0 0 0-2.45 1.414 1 1 0 0 1-1.224.707 1 1 0 0 1-.742-.975 1 1 0 0 1 .034-.25 4 4 0 0 1 4.9-2.829A4.008 4.008 0 0 1 15 4.988a3.993 3.993 0 0 1-.137 1.047 4.006 4.006 0 0 1-3.862 2.966 3.989 3.989 0 0 1-1.036-.138zM5.5 4a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5zM4.5 5a.5.5 0 0 1-.354-.146l-2-2a.5.5 0 0 1 0-.707.5.5 0 0 1 .707 0l2 2A.5.5 0 0 1 4.5 5zM3.5 6h-2a.5.5 0 0 1-.5-.5.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5.5.5 0 0 1-.5.5z"/></g></svg>
diff --git a/editor/import/dynamic_font_import_settings.cpp b/editor/import/dynamic_font_import_settings.cpp
index b361dcd036..5e203a3e39 100644
--- a/editor/import/dynamic_font_import_settings.cpp
+++ b/editor/import/dynamic_font_import_settings.cpp
@@ -454,7 +454,11 @@ void DynamicFontImportSettings::_add_glyph_range_item(int32_t p_start, int32_t p
void DynamicFontImportSettings::_main_prop_changed(const String &p_edited_property) {
// Update font preview.
- if (p_edited_property == "antialiased") {
+ if (p_edited_property == "face_index") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_face_index(import_settings_data->get("face_index"));
+ }
+ } else if (p_edited_property == "antialiased") {
if (font_preview->get_data_count() > 0) {
font_preview->get_data(0)->set_antialiased(import_settings_data->get("antialiased"));
}
@@ -945,6 +949,7 @@ void DynamicFontImportSettings::_notification(int p_what) {
void DynamicFontImportSettings::_re_import() {
HashMap<StringName, Variant> main_settings;
+ main_settings["face_index"] = import_settings_data->get("face_index");
main_settings["antialiased"] = import_settings_data->get("antialiased");
main_settings["generate_mipmaps"] = import_settings_data->get("generate_mipmaps");
main_settings["multichannel_signed_distance_field"] = import_settings_data->get("multichannel_signed_distance_field");
@@ -1299,6 +1304,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
import_settings_data->notify_property_list_changed();
if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_face_index(import_settings_data->get("face_index"));
font_preview->get_data(0)->set_antialiased(import_settings_data->get("antialiased"));
font_preview->get_data(0)->set_multichannel_signed_distance_field(import_settings_data->get("multichannel_signed_distance_field"));
font_preview->get_data(0)->set_msdf_pixel_range(import_settings_data->get("msdf_pixel_range"));
@@ -1360,6 +1366,7 @@ DynamicFontImportSettings *DynamicFontImportSettings::get_singleton() {
DynamicFontImportSettings::DynamicFontImportSettings() {
singleton = this;
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "face_index"), 0));
options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "antialiased"), true));
options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "generate_mipmaps"), false));
options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
diff --git a/editor/import/resource_importer_dynamic_font.cpp b/editor/import/resource_importer_dynamic_font.cpp
index 04f6a0b7af..a92b0fe280 100644
--- a/editor/import/resource_importer_dynamic_font.cpp
+++ b/editor/import/resource_importer_dynamic_font.cpp
@@ -50,7 +50,9 @@ void ResourceImporterDynamicFont::get_recognized_extensions(List<String> *p_exte
if (p_extensions) {
#ifdef MODULE_FREETYPE_ENABLED
p_extensions->push_back("ttf");
+ p_extensions->push_back("ttc");
p_extensions->push_back("otf");
+ p_extensions->push_back("otc");
p_extensions->push_back("woff");
p_extensions->push_back("woff2");
p_extensions->push_back("pfb");
@@ -101,6 +103,8 @@ String ResourceImporterDynamicFont::get_preset_name(int p_idx) const {
void ResourceImporterDynamicFont::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
bool msdf = p_preset == PRESET_MSDF;
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "face_index"), 0));
+
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "antialiased"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_mipmaps"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), (msdf) ? true : false));
@@ -179,6 +183,8 @@ void ResourceImporterDynamicFont::show_advanced_options(const String &p_path) {
Error ResourceImporterDynamicFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
print_verbose("Importing dynamic font from: " + p_source_file);
+ int face_index = p_options["face_index"];
+
bool antialiased = p_options["antialiased"];
bool generate_mipmaps = p_options["generate_mipmaps"];
bool msdf = p_options["multichannel_signed_distance_field"];
@@ -200,6 +206,7 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str
Ref<FontData> font;
font.instantiate();
font->set_data(data);
+ font->set_face_index(face_index);
font->set_antialiased(antialiased);
font->set_generate_mipmaps(generate_mipmaps);
font->set_multichannel_signed_distance_field(msdf);
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 80230bc316..f2975b1d7a 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -658,6 +658,44 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R
}
}
}
+ } else if (_teststr(name, "vehicle")) {
+ if (isroot) {
+ return p_node;
+ }
+
+ Node *owner = p_node->get_owner();
+ Node3D *s = Object::cast_to<Node3D>(p_node);
+ VehicleBody3D *bv = memnew(VehicleBody3D);
+ String n = _fixstr(p_node->get_name(), "vehicle");
+ bv->set_name(n);
+ p_node->replace_by(bv);
+ p_node->set_name(n);
+ bv->add_child(p_node);
+ bv->set_owner(owner);
+ p_node->set_owner(owner);
+ bv->set_transform(s->get_transform());
+ s->set_transform(Transform3D());
+
+ p_node = bv;
+ } else if (_teststr(name, "wheel")) {
+ if (isroot) {
+ return p_node;
+ }
+
+ Node *owner = p_node->get_owner();
+ Node3D *s = Object::cast_to<Node3D>(p_node);
+ VehicleWheel3D *bv = memnew(VehicleWheel3D);
+ String n = _fixstr(p_node->get_name(), "wheel");
+ bv->set_name(n);
+ p_node->replace_by(bv);
+ p_node->set_name(n);
+ bv->add_child(p_node);
+ bv->set_owner(owner);
+ p_node->set_owner(owner);
+ bv->set_transform(s->get_transform());
+ s->set_transform(Transform3D());
+
+ p_node = bv;
} else if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
//last attempt, maybe collision inside the mesh data
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 71b8c54422..deb3047864 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -104,13 +104,13 @@ void ResourceImporterTexture::update_imports() {
bool changed = false;
if (E.value.flags & MAKE_NORMAL_FLAG && int(cf->get_value("params", "compress/normal_map")) == 0) {
- print_line(vformat("%s: Texture detected as used as a normal map in 3D. Enabling red-green texture compression to reduce memory usage (blue channel is discarded).", String(E.key)));
+ print_line(vformat(TTR("%s: Texture detected as used as a normal map in 3D. Enabling red-green texture compression to reduce memory usage (blue channel is discarded)."), String(E.key)));
cf->set_value("params", "compress/normal_map", 1);
changed = true;
}
if (E.value.flags & MAKE_ROUGHNESS_FLAG && int(cf->get_value("params", "roughness/mode")) == 0) {
- print_line(vformat("%s: Texture detected as used as a roughness map in 3D. Enabling roughness limiter based on the detected associated normal map at %s.", String(E.key), E.value.normal_path_for_roughness));
+ print_line(vformat(TTR("%s: Texture detected as used as a roughness map in 3D. Enabling roughness limiter based on the detected associated normal map at %s."), String(E.key), E.value.normal_path_for_roughness));
cf->set_value("params", "roughness/mode", E.value.channel_for_roughness + 2);
cf->set_value("params", "roughness/src_normal", E.value.normal_path_for_roughness);
changed = true;
@@ -127,7 +127,7 @@ void ResourceImporterTexture::update_imports() {
cf->set_value("params", "compress/mode", COMPRESS_BASIS_UNIVERSAL);
compress_string = "Basis Universal";
}
- print_line(vformat("%s: Texture detected as used in 3D. Enabling mipmap generation and setting the texture compression mode to %s.", String(E.key), compress_string));
+ print_line(vformat(TTR("%s: Texture detected as used in 3D. Enabling mipmap generation and setting the texture compression mode to %s."), String(E.key), compress_string));
cf->set_value("params", "mipmaps/generate", true);
changed = true;
}
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 32c7a5db4d..98ccc1fdbe 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -1573,7 +1573,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug
frame = memnew(SpinBox);
hb->add_child(frame);
- frame->set_custom_minimum_size(Size2(60, 0));
+ frame->set_custom_minimum_size(Size2(80, 0) * EDSCALE);
frame->set_stretch_ratio(2);
frame->set_step(0.0001);
frame->set_tooltip(TTR("Animation position (in seconds)."));
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 40894cdb8b..1ea0299d4e 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1289,7 +1289,7 @@ void CanvasItemEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bo
zoom_widget->set_zoom_by_increments(-scroll_sign, p_alt);
if (!Math::is_equal_approx(ABS(p_scroll_vec.y), (real_t)1.0)) {
// Handle high-precision (analog) scrolling.
- zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * p_scroll_vec.y + 1.f));
+ zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * ABS(p_scroll_vec.y) + 1.f));
}
_zoom_on_position(zoom_widget->get_zoom(), p_origin);
}
@@ -3779,7 +3779,7 @@ void CanvasItemEditor::_update_editor_settings() {
key_auto_insert_button->add_theme_color_override("icon_pressed_color", key_auto_color.lerp(Color(1, 0, 0), 0.55));
animation_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
- _update_context_menu_stylebox();
+ context_menu_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles")));
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/2d_editor_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
pan_speed = int(EditorSettings::get_singleton()->get("editors/panning/2d_editor_pan_speed"));
@@ -3915,18 +3915,6 @@ void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
}
}
-void CanvasItemEditor::_update_context_menu_stylebox() {
- // This must be called when the theme changes to follow the new accent color.
- Ref<StyleBoxFlat> context_menu_stylebox = memnew(StyleBoxFlat);
- const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("accent_color"), SNAME("Editor"));
- context_menu_stylebox->set_bg_color(accent_color * Color(1, 1, 1, 0.1));
- // Add an underline to the StyleBox, but prevent its minimum vertical size from changing.
- context_menu_stylebox->set_border_color(accent_color);
- context_menu_stylebox->set_border_width(SIDE_BOTTOM, Math::round(2 * EDSCALE));
- context_menu_stylebox->set_default_margin(SIDE_BOTTOM, 0);
- context_menu_container->add_theme_style_override("panel", context_menu_stylebox);
-}
-
void CanvasItemEditor::_update_scrollbars() {
updating_scroll = true;
@@ -4101,6 +4089,8 @@ void CanvasItemEditor::_button_tool_select(int p_index) {
void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing) {
const HashMap<Node *, Object *> &selection = editor_selection->get_selection();
+ AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
+ te->make_insert_queue();
for (const KeyValue<Node *, Object *> &E : selection) {
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (!canvas_item || !canvas_item->is_visible_in_tree()) {
@@ -4115,13 +4105,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Node2D *n2d = Object::cast_to<Node2D>(canvas_item);
if (key_pos && p_location) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
+ te->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
}
if (key_rot && p_rotation) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing);
+ te->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing);
}
if (key_scale && p_scale) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
+ te->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
}
if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) {
@@ -4147,13 +4137,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
if (has_chain && ik_chain.size()) {
for (Node2D *&F : ik_chain) {
if (key_pos) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "position", F->get_position(), p_on_existing);
+ te->insert_node_value_key(F, "position", F->get_position(), p_on_existing);
}
if (key_rot) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing);
+ te->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing);
}
if (key_scale) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing);
+ te->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing);
}
}
}
@@ -4163,16 +4153,17 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Control *ctrl = Object::cast_to<Control>(canvas_item);
if (key_pos) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing);
+ te->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing);
}
if (key_rot) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing);
+ te->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing);
}
if (key_scale) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing);
+ te->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing);
}
}
}
+ te->commit_insert_queue();
}
void CanvasItemEditor::_update_override_camera_button(bool p_game_running) {
@@ -5213,11 +5204,7 @@ CanvasItemEditor::CanvasItemEditor() {
context_menu_container = memnew(PanelContainer);
hbc_context_menu = memnew(HBoxContainer);
context_menu_container->add_child(hbc_context_menu);
- // Use a custom stylebox to make contextual menu items stand out from the rest.
- // This helps with editor usability as contextual menu items change when selecting nodes,
- // even though it may not be immediately obvious at first.
hb->add_child(context_menu_container);
- _update_context_menu_stylebox();
// Animation controls.
animation_hb = memnew(HBoxContainer);
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index a4099079f3..c20a054800 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -492,8 +492,6 @@ private:
HSplitContainer *right_panel_split = nullptr;
VSplitContainer *bottom_split = nullptr;
- void _update_context_menu_stylebox();
-
void _set_owner_for_node_and_children(Node *p_node, Node *p_owner);
friend class CanvasItemEditorPlugin;
diff --git a/editor/plugins/input_event_editor_plugin.cpp b/editor/plugins/input_event_editor_plugin.cpp
index fb0e260388..2acd0fca6e 100644
--- a/editor/plugins/input_event_editor_plugin.cpp
+++ b/editor/plugins/input_event_editor_plugin.cpp
@@ -84,8 +84,7 @@ InputEventConfigContainer::InputEventConfigContainer() {
input_event_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
add_child(input_event_text);
- open_config_button = memnew(Button);
- open_config_button->set_text(TTR("Configure"));
+ open_config_button = EditorInspector::create_inspector_action_button(TTR("Configure"));
open_config_button->connect("pressed", callable_mp(this, &InputEventConfigContainer::_configure_pressed));
add_child(open_config_button);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index ef31e132b3..9f4842a5a1 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -6362,18 +6362,6 @@ void fragment() {
_generate_selection_boxes();
}
-void Node3DEditor::_update_context_menu_stylebox() {
- // This must be called when the theme changes to follow the new accent color.
- Ref<StyleBoxFlat> context_menu_stylebox = memnew(StyleBoxFlat);
- const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("accent_color"), SNAME("Editor"));
- context_menu_stylebox->set_bg_color(accent_color * Color(1, 1, 1, 0.1));
- // Add an underline to the StyleBox, but prevent its minimum vertical size from changing.
- context_menu_stylebox->set_border_color(accent_color);
- context_menu_stylebox->set_border_width(SIDE_BOTTOM, Math::round(2 * EDSCALE));
- context_menu_stylebox->set_default_margin(SIDE_BOTTOM, 0);
- context_menu_container->add_theme_style_override("panel", context_menu_stylebox);
-}
-
void Node3DEditor::_update_gizmos_menu() {
gizmos_menu->clear();
@@ -6965,6 +6953,8 @@ void Node3DEditor::_update_theme() {
sun_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor"))));
environ_sky_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor"))));
environ_ground_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor"))));
+
+ context_menu_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles")));
}
void Node3DEditor::_notification(int p_what) {
@@ -7003,7 +6993,6 @@ void Node3DEditor::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
_update_theme();
_update_gizmos_menu_theme();
- _update_context_menu_stylebox();
sun_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window")));
environ_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window")));
} break;
@@ -7724,11 +7713,7 @@ Node3DEditor::Node3DEditor() {
context_menu_container = memnew(PanelContainer);
hbc_context_menu = memnew(HBoxContainer);
context_menu_container->add_child(hbc_context_menu);
- // Use a custom stylebox to make contextual menu items stand out from the rest.
- // This helps with editor usability as contextual menu items change when selecting nodes,
- // even though it may not be immediately obvious at first.
hbc_menu->add_child(context_menu_container);
- _update_context_menu_stylebox();
// Get the view menu popup and have it stay open when a checkable item is selected
p = view_menu->get_popup();
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 894ec9a119..8a602be08b 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -676,7 +676,6 @@ private:
int camera_override_viewport_id;
void _init_indicators();
- void _update_context_menu_stylebox();
void _update_gizmos_menu();
void _update_gizmos_menu_theme();
void _init_grid();
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index d035c038d3..ec45341970 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -1676,10 +1676,15 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
Vector<Color> color;
color.push_back(Color(1.0, 1.0, 1.0, 0.5));
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
+ p_canvas_item->draw_set_transform_matrix(p_transform * xform);
+ p_canvas_item->draw_polygon(polygon, color);
+ }
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
p_canvas_item->draw_set_transform_matrix(p_transform * xform);
p_canvas_item->draw_polygon(polygon, color);
@@ -1806,10 +1811,19 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ for (int j = 0; j < polygon.size(); j++) {
+ polygon.write[j] += position;
+ }
+ if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) {
+ // Draw terrain.
+ p_canvas_item->draw_polygon(polygon, color);
+ }
+
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
for (int j = 0; j < polygon.size(); j++) {
polygon.write[j] += position;
}
@@ -1850,10 +1864,16 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
Vector<Color> color;
color.push_back(Color(1.0, 1.0, 1.0, 0.5));
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
+ p_canvas_item->draw_set_transform_matrix(p_transform * xform);
+ p_canvas_item->draw_polygon(polygon, color);
+ }
+
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
p_canvas_item->draw_set_transform_matrix(p_transform * xform);
p_canvas_item->draw_polygon(polygon, color);
@@ -1926,10 +1946,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
if (!drag_modified.has(cell)) {
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -1958,10 +1979,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
if (!drag_modified.has(cell)) {
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -1970,12 +1992,17 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
// Set the terrains bits.
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set());
+ if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
+ tile_data->set_terrain(terrain);
+ }
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit);
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(tile_data->get_terrain_set(), bit);
if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
- tile_data->set_peering_bit_terrain(bit, terrain);
+ tile_data->set_terrain_peering_bit(bit, terrain);
}
}
}
@@ -2000,12 +2027,17 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
dummy_object->set("terrain_set", terrain_set);
dummy_object->set("terrain", -1);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
+ dummy_object->set("terrain", tile_data->get_terrain());
+ }
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
- dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit));
+ dummy_object->set("terrain", tile_data->get_terrain_peering_bit(bit));
}
}
}
@@ -2044,10 +2076,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
// Save the old terrain_set and terrains bits.
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2085,10 +2118,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
// Save the old terrain_set and terrains bits.
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2097,12 +2131,16 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
+ tile_data->set_terrain(terrain);
+ }
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
- tile_data->set_peering_bit_terrain(bit, terrain);
+ tile_data->set_terrain_peering_bit(bit, terrain);
}
}
}
@@ -2138,10 +2176,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_set());
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), drag_painted_value);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain());
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_peering_bit_terrain(bit));
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_peering_bit(bit));
}
}
}
@@ -2154,10 +2193,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Vector2i coords = E.key.get_atlas_coords();
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set"], bit)) {
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
@@ -2172,13 +2212,15 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
Dictionary dict = E.value;
Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), terrain);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
}
- if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set"], bit)) {
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
@@ -2224,20 +2266,30 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Vector2i coords = E.get_atlas_coords();
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
+ Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ for (int j = 0; j < polygon.size(); j++) {
+ polygon.write[j] += position;
+ }
+ if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) {
+ // Draw terrain.
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), terrain);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain());
+ }
+
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
-
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
for (int j = 0; j < polygon.size(); j++) {
polygon.write[j] += position;
}
if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) {
// Draw bit.
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), terrain);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_peering_bit_terrain(bit));
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_peering_bit(bit));
}
}
}
@@ -2267,10 +2319,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
if (!drag_modified.has(cell)) {
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2300,10 +2353,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
if (!drag_modified.has(cell)) {
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2312,12 +2366,18 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
// Set the terrains bits.
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set());
+ if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
+ tile_data->set_terrain(terrain);
+ }
+
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit);
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(tile_data->get_terrain_set(), bit);
if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
- tile_data->set_peering_bit_terrain(bit, terrain);
+ tile_data->set_terrain_peering_bit(bit, terrain);
}
}
}
@@ -2343,12 +2403,18 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
dummy_object->set("terrain_set", terrain_set);
dummy_object->set("terrain", -1);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
+ dummy_object->set("terrain", tile_data->get_terrain());
+ }
+
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
- dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit));
+ dummy_object->set("terrain", tile_data->get_terrain_peering_bit(bit));
}
}
}
@@ -2380,7 +2446,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
Array array;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2405,10 +2471,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
// Save the old terrain_set and terrains bits.
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2416,12 +2483,17 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
// Set the terrain bit.
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
+ tile_data->set_terrain(terrain);
+ }
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
- tile_data->set_peering_bit_terrain(bit, terrain);
+ tile_data->set_terrain_peering_bit(bit, terrain);
}
}
}
@@ -2437,6 +2509,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
Vector2i coords = E.key.get_atlas_coords();
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
@@ -2452,13 +2525,15 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
Dictionary dict = E.value;
Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), terrain);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
}
- if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set"], bit)) {
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index cd8aca2a8e..d2f3eab31c 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -2028,7 +2028,6 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
// --- Toolbar ---
toolbar = memnew(HBoxContainer);
- toolbar->set_h_size_flags(Control::SIZE_EXPAND_FILL);
HBoxContainer *tilemap_tiles_tools_buttons = memnew(HBoxContainer);
@@ -2321,7 +2320,7 @@ Vector<TileMapEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() con
return tabs;
}
-HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const HashMap<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const {
+HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return HashMap<Vector2i, TileMapCell>();
@@ -2332,105 +2331,87 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const
return HashMap<Vector2i, TileMapCell>();
}
- HashMap<Vector2i, TileMapCell> output;
-
- // Add the constraints from the added tiles.
- RBSet<TileMap::TerrainConstraint> added_tiles_constraints_set;
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
- Vector2i coords = E_to_paint.key;
- TileSet::TerrainsPattern terrains_pattern = E_to_paint.value;
-
- RBSet<TileMap::TerrainConstraint> cell_constraints = tile_map->get_terrain_constraints_from_added_tile(coords, p_terrain_set, terrains_pattern);
- for (const TileMap::TerrainConstraint &E : cell_constraints) {
- added_tiles_constraints_set.insert(E);
- }
- }
-
- // Build the list of potential tiles to replace.
- RBSet<Vector2i> potential_to_replace;
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
- Vector2i coords = E_to_paint.key;
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- if (tile_map->is_existing_neighbor(TileSet::CellNeighbor(i))) {
- Vector2i neighbor = tile_map->get_neighbor_cell(coords, TileSet::CellNeighbor(i));
- if (!p_to_paint.has(neighbor)) {
- potential_to_replace.insert(neighbor);
- }
- }
- }
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output;
+ if (p_connect) {
+ terrain_fill_output = tile_map->terrain_fill_connect(tile_map_layer, p_to_paint, p_terrain_set, p_terrain, false);
+ } else {
+ terrain_fill_output = tile_map->terrain_fill_path(tile_map_layer, p_to_paint, p_terrain_set, p_terrain, false);
}
- // Set of tiles to replace
- RBSet<Vector2i> to_replace;
-
- // Add the central tiles to the one to replace.
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
- to_replace.insert(E_to_paint.key);
+ // Make the painted path a set for faster lookups
+ HashSet<Vector2i> painted_set;
+ for (Vector2i coords : p_to_paint) {
+ painted_set.insert(coords);
}
- // Add the constraints from the surroundings of the modified areas.
- RBSet<TileMap::TerrainConstraint> removed_cells_constraints_set;
- bool to_replace_modified = true;
- while (to_replace_modified) {
- // Get the constraints from the removed cells.
- removed_cells_constraints_set = tile_map->get_terrain_constraints_from_removed_cells_list(tile_map_layer, to_replace, p_terrain_set, false);
-
- // Filter the sources to make sure they are in the potential_to_replace.
- RBMap<TileMap::TerrainConstraint, RBSet<Vector2i>> per_constraint_tiles;
- for (const TileMap::TerrainConstraint &E : removed_cells_constraints_set) {
- HashMap<Vector2i, TileSet::CellNeighbor> sources_of_constraint = E.get_overlapping_coords_and_peering_bits();
- for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_source_tile_of_constraint : sources_of_constraint) {
- if (potential_to_replace.has(E_source_tile_of_constraint.key)) {
- per_constraint_tiles[E].insert(E_source_tile_of_constraint.key);
- }
- }
- }
-
- to_replace_modified = false;
- for (const TileMap::TerrainConstraint &E : added_tiles_constraints_set) {
- TileMap::TerrainConstraint c = E;
- // Check if we have a conflict in constraints.
- if (removed_cells_constraints_set.has(c) && removed_cells_constraints_set.find(c)->get().get_terrain() != c.get_terrain()) {
- // If we do, we search for a neighbor to remove.
- if (per_constraint_tiles.has(c) && !per_constraint_tiles[c].is_empty()) {
- // Remove it.
- Vector2i to_add_to_remove = per_constraint_tiles[c].front()->get();
- potential_to_replace.erase(to_add_to_remove);
- to_replace.insert(to_add_to_remove);
- to_replace_modified = true;
- for (KeyValue<TileMap::TerrainConstraint, RBSet<Vector2i>> &E_source_tiles_of_constraint : per_constraint_tiles) {
- E_source_tiles_of_constraint.value.erase(to_add_to_remove);
+ HashMap<Vector2i, TileMapCell> output;
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) {
+ if (painted_set.has(E.key)) {
+ // Paint a random tile with the correct terrain for the painted path.
+ output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
+ } else {
+ // Avoids updating the painted path from the output if the new pattern is the same as before.
+ bool keep_old = false;
+ TileMapCell cell = tile_map->get_cell(tile_map_layer, E.key);
+ if (cell.source_id != TileSet::INVALID_SOURCE) {
+ TileSetSource *source = *tile_set->get_source(cell.source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Get tile data.
+ TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
+ if (tile_data && tile_data->get_terrains_pattern() == E.value) {
+ keep_old = true;
}
- break;
}
}
+ if (!keep_old) {
+ output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
+ }
}
}
+ return output;
+}
- // Combine all constraints together.
- RBSet<TileMap::TerrainConstraint> constraints = removed_cells_constraints_set;
- for (const TileMap::TerrainConstraint &E : added_tiles_constraints_set) {
- constraints.insert(E);
+HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return HashMap<Vector2i, TileMapCell>();
}
- // Remove the central tiles from the ones to replace.
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
- to_replace.erase(E_to_paint.key);
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return HashMap<Vector2i, TileMapCell>();
}
- // Run WFC to fill the holes with the constraints.
- HashMap<Vector2i, TileSet::TerrainsPattern> wfc_output = tile_map->terrain_wave_function_collapse(to_replace, p_terrain_set, constraints);
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = tile_map->terrain_fill_pattern(tile_map_layer, p_to_paint, p_terrain_set, p_terrains_pattern, false);
- // Actually paint the tiles.
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
- output[E_to_paint.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E_to_paint.value);
+ // Make the painted path a set for faster lookups
+ HashSet<Vector2i> painted_set;
+ for (Vector2i coords : p_to_paint) {
+ painted_set.insert(coords);
}
- // Use the WFC run for the output.
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : wfc_output) {
- output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
+ HashMap<Vector2i, TileMapCell> output;
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) {
+ if (painted_set.has(E.key)) {
+ // Paint a random tile with the correct terrain for the painted path.
+ output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
+ } else {
+ // Avoids updating the painted path from the output if the new pattern is the same as before.
+ TileMapCell cell = tile_map->get_cell(tile_map_layer, E.key);
+ if (cell.source_id != TileSet::INVALID_SOURCE) {
+ TileSetSource *source = *tile_set->get_source(cell.source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Get tile data.
+ TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
+ if (tile_data && !(tile_data->get_terrains_pattern() == E.value)) {
+ output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
+ }
+ }
+ }
+ }
}
-
return output;
}
@@ -2445,19 +2426,21 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_line(Vector2i
return HashMap<Vector2i, TileMapCell>();
}
- TileSet::TerrainsPattern terrains_pattern;
- if (p_erase) {
- terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
- } else {
- terrains_pattern = selected_terrains_pattern;
- }
+ if (selected_type == SELECTED_TYPE_CONNECT) {
+ return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, true);
+ } else if (selected_type == SELECTED_TYPE_PATH) {
+ return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, false);
+ } else { // SELECTED_TYPE_PATTERN
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
+ }
- Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell);
- HashMap<Vector2i, TileSet::TerrainsPattern> to_draw;
- for (int i = 0; i < line.size(); i++) {
- to_draw[line[i]] = terrains_pattern;
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell);
+ return _draw_terrain_pattern(line, selected_terrain_set, terrains_pattern);
}
- return _draw_terrains(to_draw, selected_terrain_set);
}
HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
@@ -2471,25 +2454,29 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i
return HashMap<Vector2i, TileMapCell>();
}
- TileSet::TerrainsPattern terrains_pattern;
- if (p_erase) {
- terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
- } else {
- terrains_pattern = selected_terrains_pattern;
- }
-
Rect2i rect;
rect.set_position(p_start_cell);
rect.set_end(p_end_cell);
rect = rect.abs();
- HashMap<Vector2i, TileSet::TerrainsPattern> to_draw;
+ Vector<Vector2i> to_draw;
for (int x = rect.position.x; x <= rect.get_end().x; x++) {
for (int y = rect.position.y; y <= rect.get_end().y; y++) {
- to_draw[Vector2i(x, y)] = terrains_pattern;
+ to_draw.append(Vector2i(x, y));
}
}
- return _draw_terrains(to_draw, selected_terrain_set);
+
+ if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
+ return _draw_terrain_path_or_connect(to_draw, selected_terrain_set, selected_terrain, true);
+ } else { // SELECTED_TYPE_PATTERN
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
+ }
+ return _draw_terrain_pattern(to_draw, selected_terrain_set, terrains_pattern);
+ }
}
RBSet<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous) {
@@ -2614,20 +2601,23 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_bucket_fill(Ve
return HashMap<Vector2i, TileMapCell>();
}
- TileSet::TerrainsPattern terrains_pattern;
- if (p_erase) {
- terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
- } else {
- terrains_pattern = selected_terrains_pattern;
- }
-
RBSet<Vector2i> cells_to_draw = _get_cells_for_bucket_fill(p_coords, p_contiguous);
- HashMap<Vector2i, TileSet::TerrainsPattern> to_draw;
- for (const Vector2i &coords : cells_to_draw) {
- to_draw[coords] = terrains_pattern;
+ Vector<Vector2i> cells_to_draw_as_vector;
+ for (Vector2i cell : cells_to_draw) {
+ cells_to_draw_as_vector.append(cell);
}
- return _draw_terrains(to_draw, selected_terrain_set);
+ if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
+ return _draw_terrain_path_or_connect(cells_to_draw_as_vector, selected_terrain_set, selected_terrain, true);
+ } else { // SELECTED_TYPE_PATTERN
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
+ }
+ return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, terrains_pattern);
+ }
}
void TileMapEditorTerrainsPlugin::_stop_dragging() {
@@ -2696,11 +2686,13 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
if (tree_item) {
for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
- TileSet::TerrainsPattern in_meta_terrains_pattern(*tile_set, new_terrain_set);
- in_meta_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]);
- if (in_meta_terrains_pattern == terrains_pattern) {
- terrains_tile_list->select(i);
- break;
+ if (int(metadata_dict["type"]) == SELECTED_TYPE_PATTERN) {
+ TileSet::TerrainsPattern in_meta_terrains_pattern(*tile_set, new_terrain_set);
+ in_meta_terrains_pattern.from_array(metadata_dict["terrains_pattern"]);
+ if (in_meta_terrains_pattern == terrains_pattern) {
+ terrains_tile_list->select(i);
+ break;
+ }
}
}
} else {
@@ -2773,22 +2765,33 @@ void TileMapEditorTerrainsPlugin::_update_selection() {
}
// Get the selected terrain.
- selected_terrains_pattern = TileSet::TerrainsPattern();
selected_terrain_set = -1;
+ selected_terrains_pattern = TileSet::TerrainsPattern();
TreeItem *selected_tree_item = terrains_tree->get_selected();
if (selected_tree_item && selected_tree_item->get_metadata(0)) {
Dictionary metadata_dict = selected_tree_item->get_metadata(0);
// Selected terrain
selected_terrain_set = metadata_dict["terrain_set"];
+ selected_terrain = metadata_dict["terrain_id"];
- // Selected tile
+ // Selected mode/terrain pattern
if (erase_button->is_pressed()) {
+ selected_type = SELECTED_TYPE_PATTERN;
selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
} else if (terrains_tile_list->is_anything_selected()) {
metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
- selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
- selected_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]);
+ if (int(metadata_dict["type"]) == SELECTED_TYPE_CONNECT) {
+ selected_type = SELECTED_TYPE_CONNECT;
+ } else if (int(metadata_dict["type"]) == SELECTED_TYPE_PATH) {
+ selected_type = SELECTED_TYPE_PATH;
+ } else if (int(metadata_dict["type"]) == SELECTED_TYPE_PATTERN) {
+ selected_type = SELECTED_TYPE_PATTERN;
+ selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ selected_terrains_pattern.from_array(metadata_dict["terrains_pattern"]);
+ } else {
+ ERR_FAIL();
+ }
}
}
}
@@ -2865,7 +2868,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
} else {
// Paint otherwise.
if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
- if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
return true;
}
@@ -2880,21 +2883,21 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
} else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CTRL))) {
- if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
return true;
}
drag_type = DRAG_TYPE_LINE;
drag_start_mouse_pos = mpos;
drag_modified.clear();
} else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CTRL))) {
- if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
return true;
}
drag_type = DRAG_TYPE_RECT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
} else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
- if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
return true;
}
drag_type = DRAG_TYPE_BUCKET;
@@ -3105,11 +3108,18 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
cell.alternative_tile = alternative_id;
TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
+
+ // Terrain center bit
+ int terrain = terrains_pattern.get_terrain();
+ if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
+ per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
+ }
+
// Terrain bits.
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- int terrain = terrains_pattern.get_terrain(bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ terrain = terrains_pattern.get_terrain_peering_bit(bit);
if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
}
@@ -3191,6 +3201,19 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
ERR_FAIL_INDEX(selected_terrain_set, tile_set->get_terrain_sets_count());
ERR_FAIL_INDEX(selected_terrain_id, tile_set->get_terrains_count(selected_terrain_set));
+ // Add the two first generic modes
+ int item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_theme_icon(SNAME("TerrainConnect"), SNAME("EditorIcons")));
+ terrains_tile_list->set_item_tooltip(item_index, TTR("Connect mode: paints a terrain, then connects it with the surrounding tiles with the same terrain."));
+ Dictionary list_metadata_dict;
+ list_metadata_dict["type"] = SELECTED_TYPE_CONNECT;
+ terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
+
+ item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_theme_icon(SNAME("TerrainPath"), SNAME("EditorIcons")));
+ terrains_tile_list->set_item_tooltip(item_index, TTR("Path mode: paints a terrain, thens connects it to the previous tile painted withing the same stroke."));
+ list_metadata_dict = Dictionary();
+ list_metadata_dict["type"] = SELECTED_TYPE_PATH;
+ terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
+
// Sort the items in a map by the number of corresponding terrains.
RBMap<int, RBSet<TileSet::TerrainsPattern>> sorted;
@@ -3200,7 +3223,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(selected_terrain_set, bit) && E.get_terrain(bit) == selected_terrain_id) {
+ if (tile_set->is_valid_terrain_peering_bit(selected_terrain_set, bit) && E.get_terrain_peering_bit(bit) == selected_terrain_id) {
count++;
}
}
@@ -3241,12 +3264,13 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
}
// Create the ItemList's item.
- int item_index = terrains_tile_list->add_item("");
+ item_index = terrains_tile_list->add_item("");
terrains_tile_list->set_item_icon(item_index, icon);
terrains_tile_list->set_item_icon_region(item_index, region);
terrains_tile_list->set_item_icon_transposed(item_index, transpose);
- Dictionary list_metadata_dict;
- list_metadata_dict["terrains_pattern"] = terrains_pattern.get_terrains_as_array();
+ list_metadata_dict = Dictionary();
+ list_metadata_dict["type"] = SELECTED_TYPE_PATTERN;
+ list_metadata_dict["terrains_pattern"] = terrains_pattern.as_array();
terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
}
}
@@ -3264,6 +3288,8 @@ void TileMapEditorTerrainsPlugin::_update_theme() {
picker_button->set_icon(main_vbox_container->get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
erase_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
+
+ _update_tiles_list();
}
void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) {
@@ -3303,7 +3329,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
terrains_tile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
terrains_tile_list->set_max_columns(0);
terrains_tile_list->set_same_column_width(true);
- terrains_tile_list->set_fixed_icon_size(Size2(30, 30) * EDSCALE);
+ terrains_tile_list->set_fixed_icon_size(Size2(32, 32) * EDSCALE);
terrains_tile_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
tilemap_tab_terrains->add_child(terrains_tile_list);
@@ -3396,7 +3422,7 @@ void TileMapEditor::_notification(int p_what) {
advanced_menu_button->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons")));
toggle_grid_button->set_icon(get_theme_icon(SNAME("Grid"), SNAME("EditorIcons")));
toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid"));
- toogle_highlight_selected_layer_button->set_icon(get_theme_icon(SNAME("TileMapHighlightSelected"), SNAME("EditorIcons")));
+ toggle_highlight_selected_layer_button->set_icon(get_theme_icon(SNAME("TileMapHighlightSelected"), SNAME("EditorIcons")));
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
@@ -3431,64 +3457,13 @@ void TileMapEditor::_on_grid_toggled(bool p_pressed) {
CanvasItemEditor::get_singleton()->update_viewport();
}
-void TileMapEditor::_layers_selection_button_draw() {
- if (!has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) {
+void TileMapEditor::_layers_selection_item_selected(int p_index) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map || tile_map->get_layers_count() <= 0) {
return;
}
- RID ci = layers_selection_button->get_canvas_item();
- Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton"));
-
- Color clr = Color(1, 1, 1);
- if (get_theme_constant(SNAME("modulate_arrow"))) {
- switch (layers_selection_button->get_draw_mode()) {
- case BaseButton::DRAW_PRESSED:
- clr = get_theme_color(SNAME("font_pressed_color"));
- break;
- case BaseButton::DRAW_HOVER:
- clr = get_theme_color(SNAME("font_hover_color"));
- break;
- case BaseButton::DRAW_DISABLED:
- clr = get_theme_color(SNAME("font_disabled_color"));
- break;
- default:
- if (layers_selection_button->has_focus()) {
- clr = get_theme_color(SNAME("font_focus_color"));
- } else {
- clr = get_theme_color(SNAME("font_color"));
- }
- }
- }
-
- Size2 size = layers_selection_button->get_size();
-
- Point2 ofs;
- if (is_layout_rtl()) {
- ofs = Point2(get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2)));
- } else {
- ofs = Point2(size.width - arrow->get_width() - get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2)));
- }
- Rect2 dst_rect = Rect2(ofs, arrow->get_size());
- if (!layers_selection_button->is_pressed()) {
- dst_rect.size = -dst_rect.size;
- }
- arrow->draw_rect(ci, dst_rect, false, clr);
-}
-
-void TileMapEditor::_layers_selection_button_pressed() {
- if (!layers_selection_popup->is_visible()) {
- Size2 size = layers_selection_popup->get_contents_minimum_size();
- size.x = MAX(size.x, layers_selection_button->get_size().x);
- layers_selection_popup->set_position(layers_selection_button->get_screen_position() - Size2(0, size.y * get_global_transform().get_scale().y));
- layers_selection_popup->set_size(size);
- layers_selection_popup->popup();
- } else {
- layers_selection_popup->hide();
- }
-}
-
-void TileMapEditor::_layers_selection_id_pressed(int p_id) {
- tile_map_layer = p_id;
+ tile_map_layer = p_index;
_update_layers_selection();
}
@@ -3671,8 +3646,6 @@ void TileMapEditor::_layers_select_next_or_previous(bool p_next) {
}
void TileMapEditor::_update_layers_selection() {
- layers_selection_popup->clear();
-
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return;
@@ -3699,32 +3672,23 @@ void TileMapEditor::_update_layers_selection() {
} else {
tile_map_layer = -1;
}
- tile_map->set_selected_layer(toogle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1);
+ tile_map->set_selected_layer(toggle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1);
- // Build the list of layers.
- for (int i = 0; i < tile_map->get_layers_count(); i++) {
- String name = tile_map->get_layer_name(i);
- layers_selection_popup->add_item(name.is_empty() ? vformat(TTR("Layer #%d"), i) : name, i);
- layers_selection_popup->set_item_as_radio_checkable(i, true);
- layers_selection_popup->set_item_disabled(i, !tile_map->is_layer_enabled(i));
- layers_selection_popup->set_item_checked(i, i == tile_map_layer);
- }
+ layers_selection_button->clear();
+ if (tile_map->get_layers_count() > 0) {
+ // Build the list of layers.
+ for (int i = 0; i < tile_map->get_layers_count(); i++) {
+ String name = tile_map->get_layer_name(i);
+ layers_selection_button->add_item(name.is_empty() ? vformat(TTR("Layer %d"), i) : name, i);
+ layers_selection_button->set_item_disabled(i, !tile_map->is_layer_enabled(i));
+ }
- // Update the button label.
- if (tile_map_layer >= 0) {
- layers_selection_button->set_text(layers_selection_popup->get_item_text(tile_map_layer));
+ layers_selection_button->set_disabled(false);
+ layers_selection_button->select(tile_map_layer);
} else {
- layers_selection_button->set_text(TTR("Select a layer"));
- }
-
- // Set button minimum width.
- Size2 min_button_size = Size2(layers_selection_popup->get_contents_minimum_size().x, 0);
- if (has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) {
- Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton"));
- min_button_size.x += arrow->get_size().x;
+ layers_selection_button->set_disabled(true);
+ layers_selection_button->set_text(TTR("No Layers"));
}
- layers_selection_button->set_custom_minimum_size(min_button_size);
- layers_selection_button->update();
tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
}
@@ -4001,7 +3965,6 @@ TileMapEditor::TileMapEditor() {
// TabBar.
tabs_bar = memnew(TabBar);
- tabs_bar->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
tabs_bar->set_clip_tabs(false);
for (int plugin_index = 0; plugin_index < tile_map_editor_plugins.size(); plugin_index++) {
Vector<TileMapEditorPlugin::TabData> tabs_vector = tile_map_editor_plugins[plugin_index]->get_tabs();
@@ -4030,31 +3993,23 @@ TileMapEditor::TileMapEditor() {
}
// Wide empty separation control.
- Control *h_empty_space = memnew(Control);
- h_empty_space->set_h_size_flags(SIZE_EXPAND_FILL);
- tile_map_toolbar->add_child(h_empty_space);
+ tile_map_toolbar->add_spacer();
// Layer selector.
- layers_selection_popup = memnew(PopupMenu);
- layers_selection_popup->connect("id_pressed", callable_mp(this, &TileMapEditor::_layers_selection_id_pressed));
- layers_selection_popup->set_flag(Window::FLAG_POPUP, false);
-
- layers_selection_button = memnew(Button);
- layers_selection_button->set_toggle_mode(true);
- layers_selection_button->connect("draw", callable_mp(this, &TileMapEditor::_layers_selection_button_draw));
- layers_selection_button->connect("pressed", callable_mp(this, &TileMapEditor::_layers_selection_button_pressed));
- layers_selection_button->connect("hidden", callable_mp((Window *)layers_selection_popup, &Popup::hide));
- layers_selection_button->set_tooltip(TTR("Tile Map Layer"));
- layers_selection_button->add_child(layers_selection_popup);
+ layers_selection_button = memnew(OptionButton);
+ layers_selection_button->set_custom_minimum_size(Size2(200, 0));
+ layers_selection_button->set_text_overrun_behavior(TextParagraph::OVERRUN_TRIM_ELLIPSIS);
+ layers_selection_button->set_tooltip(TTR("TileMap Layers"));
+ layers_selection_button->connect("item_selected", callable_mp(this, &TileMapEditor::_layers_selection_item_selected));
tile_map_toolbar->add_child(layers_selection_button);
- toogle_highlight_selected_layer_button = memnew(Button);
- toogle_highlight_selected_layer_button->set_flat(true);
- toogle_highlight_selected_layer_button->set_toggle_mode(true);
- toogle_highlight_selected_layer_button->set_pressed(true);
- toogle_highlight_selected_layer_button->connect("pressed", callable_mp(this, &TileMapEditor::_update_layers_selection));
- toogle_highlight_selected_layer_button->set_tooltip(TTR("Highlight Selected TileMap Layer"));
- tile_map_toolbar->add_child(toogle_highlight_selected_layer_button);
+ toggle_highlight_selected_layer_button = memnew(Button);
+ toggle_highlight_selected_layer_button->set_flat(true);
+ toggle_highlight_selected_layer_button->set_toggle_mode(true);
+ toggle_highlight_selected_layer_button->set_pressed(true);
+ toggle_highlight_selected_layer_button->connect("pressed", callable_mp(this, &TileMapEditor::_update_layers_selection));
+ toggle_highlight_selected_layer_button->set_tooltip(TTR("Highlight Selected TileMap Layer"));
+ tile_map_toolbar->add_child(toggle_highlight_selected_layer_button);
tile_map_toolbar->add_child(memnew(VSeparator));
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index 7158ebff59..ff586ebbfe 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -40,6 +40,7 @@
#include "scene/gui/check_box.h"
#include "scene/gui/item_list.h"
#include "scene/gui/menu_button.h"
+#include "scene/gui/option_button.h"
#include "scene/gui/separator.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/split_container.h"
@@ -268,14 +269,22 @@ private:
HashMap<Vector2i, TileMapCell> drag_modified;
// Painting
- HashMap<Vector2i, TileMapCell> _draw_terrains(const HashMap<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const;
+ HashMap<Vector2i, TileMapCell> _draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const;
+ HashMap<Vector2i, TileMapCell> _draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
HashMap<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
RBSet<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous);
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
void _stop_dragging();
+ enum SelectedType {
+ SELECTED_TYPE_CONNECT = 0,
+ SELECTED_TYPE_PATH,
+ SELECTED_TYPE_PATTERN,
+ };
+ SelectedType selected_type;
int selected_terrain_set = -1;
+ int selected_terrain = -1;
TileSet::TerrainsPattern selected_terrains_pattern;
void _update_selection();
@@ -319,12 +328,9 @@ private:
// Toolbar.
HBoxContainer *tile_map_toolbar = nullptr;
- PopupMenu *layers_selection_popup = nullptr;
- Button *layers_selection_button = nullptr;
- Button *toogle_highlight_selected_layer_button = nullptr;
- void _layers_selection_button_draw();
- void _layers_selection_button_pressed();
- void _layers_selection_id_pressed(int p_id);
+ OptionButton *layers_selection_button = nullptr;
+ Button *toggle_highlight_selected_layer_button = nullptr;
+ void _layers_selection_item_selected(int p_index);
Button *toggle_grid_button = nullptr;
void _on_grid_toggled(bool p_pressed);
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index b87aedcf60..66459d3ef9 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2071,9 +2071,10 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
}
} else if (p_property == "terrain_set") {
int current_terrain_set = tile_data_proxy->get("terrain_set");
+ ADD_UNDO(tile_data_proxy, "terrain");
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(current_terrain_set, bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(current_terrain_set, bit)) {
ADD_UNDO(tile_data_proxy, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
}
}
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index fb4a563992..de373e121b 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -505,7 +505,7 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_
for (int terrain_set_index = begin; terrain_set_index < end; terrain_set_index++) {
for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
}
}
@@ -513,7 +513,7 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_
} else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
for (int terrain_index = 0; terrain_index < TileSet::CELL_NEIGHBOR_MAX; terrain_index++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(terrain_index);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[terrain_index]));
}
}
@@ -607,9 +607,10 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p
if (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") {
ADD_UNDO(tile_data, "terrain_set");
+ ADD_UNDO(tile_data, "terrain");
for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
}
}
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index fe418b72a5..ecf1bea6bb 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -5523,9 +5523,9 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Length2D", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Length3D", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Length4D", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp
new file mode 100644
index 0000000000..515d09c3fc
--- /dev/null
+++ b/editor/project_converter_3_to_4.cpp
@@ -0,0 +1,3811 @@
+/*************************************************************************/
+/* project_converter_3_to_4.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "project_converter_3_to_4.h"
+
+#include "modules/modules_enabled.gen.h"
+
+#ifdef MODULE_REGEX_ENABLED
+#include "modules/regex/regex.h"
+
+#include "core/os/time.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/list.h"
+
+const int ERROR_CODE = 77;
+const int CONVERSION_MAX_FILE_SIZE = 1024 * 1024 * 4; // 4 MB
+
+static const char *enum_renames[][2] = {
+ //// constants
+ { "TYPE_COLOR_ARRAY", "TYPE_PACKED_COLOR_ARRAY" },
+ { "TYPE_FLOAT64_ARRAY", "TYPE_PACKED_FLOAT64_ARRAY" },
+ { "TYPE_INT64_ARRAY", "TYPE_PACKED_INT64_ARRAY" },
+ { "TYPE_INT_ARRAY", "TYPE_PACKED_INT32_ARRAY" },
+ { "TYPE_QUAT", "TYPE_QUATERNION" },
+ { "TYPE_RAW_ARRAY", "TYPE_PACKED_BYTE_ARRAY" },
+ { "TYPE_REAL", "TYPE_FLOAT" },
+ { "TYPE_REAL_ARRAY", "TYPE_PACKED_FLOAT32_ARRAY" },
+ { "TYPE_STRING_ARRAY", "TYPE_PACKED_STRING_ARRAY" },
+ { "TYPE_TRANSFORM", "TYPE_TRANSFORM3D" },
+ { "TYPE_VECTOR2_ARRAY", "TYPE_PACKED_VECTOR2_ARRAY" },
+ { "TYPE_VECTOR3_ARRAY", "TYPE_PACKED_VECTOR3_ARRAY" },
+
+ // {"FLAG_MAX", "PARTICLE_FLAG_MAX"}, // CPUParticles2D - used in more classes
+ { "ALIGN_BEGIN", "ALIGNMENT_BEGIN" }, //AspectRatioContainer
+ { "ALIGN_CENTER", "ALIGNMENT_CENTER" }, //AspectRatioContainer
+ { "ALIGN_END", "ALIGNMENT_END" }, //AspectRatioContainer
+ { "ARRAY_COMPRESS_BASE", "ARRAY_COMPRESS_FLAGS_BASE" }, // Mesh
+ { "ARVR_AR", "XR_AR" }, // XRInterface
+ { "ARVR_EXCESSIVE_MOTION", "XR_EXCESSIVE_MOTION" }, // XRInterface
+ { "ARVR_EXTERNAL", "XR_EXTERNAL" }, // XRInterface
+ { "ARVR_INSUFFICIENT_FEATURES", "XR_INSUFFICIENT_FEATURES" }, // XRInterface
+ { "ARVR_MONO", "XR_MONO" }, // XRInterface
+ { "ARVR_NONE", "XR_NONE" }, // XRInterface
+ { "ARVR_NORMAL_TRACKING", "XR_NORMAL_TRACKING" }, // XRInterface
+ { "ARVR_NOT_TRACKING", "XR_NOT_TRACKING" }, // XRInterface
+ { "ARVR_STEREO", "XR_STEREO" }, // XRInterface
+ { "ARVR_UNKNOWN_TRACKING", "XR_UNKNOWN_TRACKING" }, // XRInterface
+ { "BAKE_ERROR_INVALID_MESH", "BAKE_ERROR_MESHES_INVALID" }, // LightmapGI
+ { "BODY_MODE_CHARACTER", "BODY_MODE_DYNAMIC" }, // PhysicsServer2D
+ { "BODY_MODE_DYNAMIC_LOCKED", "BODY_MODE_DYNAMIC_LINEAR" }, // PhysicsServer3D
+ { "BUTTON_LEFT", "MOUSE_BUTTON_LEFT" }, // Globals
+ { "BUTTON_MASK_LEFT", "MOUSE_BUTTON_MASK_LEFT" }, // Globals
+ { "BUTTON_MASK_MIDDLE", "MOUSE_BUTTON_MASK_MIDDLE" }, // Globals
+ { "BUTTON_MASK_RIGHT", "MOUSE_BUTTON_MASK_RIGHT" }, // Globals
+ { "BUTTON_MASK_XBUTTON1", "MOUSE_BUTTON_MASK_XBUTTON1" }, // Globals
+ { "BUTTON_MASK_XBUTTON2", "MOUSE_BUTTON_MASK_XBUTTON2" }, // Globals
+ { "BUTTON_MIDDLE", "MOUSE_BUTTON_MIDDLE" }, // Globals
+ { "BUTTON_RIGHT", "MOUSE_BUTTON_RIGHT" }, // Globals
+ { "BUTTON_WHEEL_DOWN", "MOUSE_BUTTON_WHEEL_DOWN" }, // Globals
+ { "BUTTON_WHEEL_LEFT", "MOUSE_BUTTON_WHEEL_LEFT" }, // Globals
+ { "BUTTON_WHEEL_RIGHT", "MOUSE_BUTTON_WHEEL_RIGHT" }, // Globals
+ { "BUTTON_WHEEL_UP", "MOUSE_BUTTON_WHEEL_UP" }, // Globals
+ { "BUTTON_XBUTTON1", "MOUSE_BUTTON_XBUTTON1" }, // Globals
+ { "BUTTON_XBUTTON2", "MOUSE_BUTTON_XBUTTON2" }, // Globals
+ { "CLEAR_MODE_ONLY_NEXT_FRAME", "CLEAR_MODE_ONCE" }, // SubViewport
+ { "COMPRESS_PVRTC4", "COMPRESS_PVRTC1_4" }, // Image
+ { "CUBEMAP_BACK", "CUBEMAP_LAYER_BACK" }, // RenderingServer
+ { "CUBEMAP_BOTTOM", "CUBEMAP_LAYER_BOTTOM" }, // RenderingServer
+ { "CUBEMAP_FRONT", "CUBEMAP_LAYER_FRONT" }, // RenderingServer
+ { "CUBEMAP_LEFT", "CUBEMAP_LAYER_LEFT" }, // RenderingServer
+ { "CUBEMAP_RIGHT", "CUBEMAP_LAYER_RIGHT" }, // RenderingServer
+ { "CUBEMAP_TOP", "CUBEMAP_LAYER_TOP" }, // RenderingServer
+ { "DAMPED_STRING_DAMPING", "DAMPED_SPRING_DAMPING" }, // PhysicsServer2D
+ { "DAMPED_STRING_REST_LENGTH", "DAMPED_SPRING_REST_LENGTH" }, // PhysicsServer2D
+ { "DAMPED_STRING_STIFFNESS", "DAMPED_SPRING_STIFFNESS" }, // PhysicsServer2D
+ { "FLAG_ALIGN_Y_TO_VELOCITY", "PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY" }, // CPUParticles2D
+ { "FLAG_DISABLE_Z", "PARTICLE_FLAG_DISABLE_Z" }, // CPUParticles2D
+ { "FLAG_ROTATE_Y", "PARTICLE_FLAG_ROTATE_Y" }, // CPUParticles2D
+ { "FLAG_USE_BAKED_LIGHT", "GI_MODE_BAKED" }, // GeometryInstance3D
+ { "FORMAT_PVRTC2", "FORMAT_PVRTC1_2" }, // Image
+ { "FORMAT_PVRTC2A", "FORMAT_PVRTC1_2A" }, // Image
+ { "FORMAT_PVRTC4", "FORMAT_PVRTC1_4" }, // Image
+ { "FORMAT_PVRTC4A", "FORMAT_PVRTC1_4A" }, // Image
+ { "INSTANCE_LIGHTMAP_CAPTURE", "INSTANCE_LIGHTMAP" }, // RenderingServer
+ { "JOINT_6DOF", "JOINT_TYPE_6DOF" }, // PhysicsServer3D
+ { "JOINT_CONE_TWIST", "JOINT_TYPE_CONE_TWIST" }, // PhysicsServer3D
+ { "JOINT_DAMPED_SPRING", "JOINT_TYPE_DAMPED_SPRING" }, // PhysicsServer2D
+ { "JOINT_GROOVE", "JOINT_TYPE_GROOVE" }, // PhysicsServer2D
+ { "JOINT_HINGE", "JOINT_TYPE_HINGE" }, // PhysicsServer3D
+ { "JOINT_PIN", "JOINT_TYPE_PIN" }, // PhysicsServer2D
+ { "JOINT_SLIDER", "JOINT_TYPE_SLIDER" }, // PhysicsServer3D
+ { "KEY_CONTROL", "KEY_CTRL" }, // Globals
+ { "LOOP_PING_PONG", "LOOP_PINGPONG" }, //AudioStreamSample
+ { "MATH_RAND", "MATH_RANDF_RANGE" }, // VisualScriptBuiltinFunc
+ { "MATH_RANDOM", "MATH_RANDI_RANGE" }, // VisualScriptBuiltinFunc
+ { "MATH_STEPIFY", "MATH_STEP_DECIMALS" }, // VisualScriptBuiltinFunc
+ { "MODE_CHARACTER", "MODE_DYNAMIC_LOCKED" }, // RigidBody2D, RigidBody3D
+ { "MODE_OPEN_ANY", "FILE_MODE_OPEN_ANY" }, // FileDialog
+ { "MODE_OPEN_DIR", "FILE_MODE_OPEN_DIR" }, // FileDialog
+ { "MODE_OPEN_FILE", "FILE_MODE_OPEN_FILE" }, // FileDialog
+ { "MODE_OPEN_FILES", "FILE_MODE_OPEN_FILES" }, // FileDialog
+ { "MODE_RIGID", "MODE_DYNAMIC" }, // RigidBody2D, RigidBody3D
+ { "MODE_SAVE_FILE", "FILE_MODE_SAVE_FILE" }, // FileDialog
+ { "NOTIFICATION_APP_PAUSED", "NOTIFICATION_APPLICATION_PAUSED" }, // MainLoop
+ { "NOTIFICATION_APP_RESUMED", "NOTIFICATION_APPLICATION_RESUMED" }, // MainLoop
+ { "NOTIFICATION_PATH_CHANGED", "NOTIFICATION_PATH_RENAMED" }, //Node
+ { "NOTIFICATION_WM_FOCUS_IN", "NOTIFICATION_APPLICATION_FOCUS_IN" }, // MainLoop
+ { "NOTIFICATION_WM_FOCUS_OUT", "NOTIFICATION_APPLICATION_FOCUS_OUT" }, // MainLoop
+ { "NOTIFICATION_WM_UNFOCUS_REQUEST", "NOTIFICATION_WM_WINDOW_FOCUS_OUT" }, //Node
+ { "PAUSE_MODE_INHERIT", "PROCESS_MODE_INHERIT" }, // Node
+ { "PAUSE_MODE_PROCESS", "PROCESS_MODE_ALWAYS" }, // Node
+ { "PAUSE_MODE_STOP", "PROCESS_MODE_PAUSABLE" }, // Node
+ { "RENDER_DRAW_CALLS_IN_FRAME", "RENDER_TOTAL_DRAW_CALLS_IN_FRAME" }, // Performance
+ { "RENDER_OBJECTS_IN_FRAME", "RENDER_TOTAL_OBJECTS_IN_FRAME" }, // Performance
+ { "SIDE_BOTTOM", "MARGIN_BOTTOM" }, // Globals
+ { "SIDE_LEFT", "MARGIN_LEFT" }, // Globals
+ { "SIDE_RIGHT", "MARGIN_RIGHT" }, // Globals
+ { "SIDE_TOP", "MARGIN_TOP" }, // Globals
+ { "TEXTURE_TYPE_2D_ARRAY", "TEXTURE_LAYERED_2D_ARRAY" }, // RenderingServer
+ { "TEXTURE_TYPE_CUBEMAP", "TEXTURE_LAYERED_CUBEMAP_ARRAY" }, // RenderingServer
+ { "TRACKER_LEFT_HAND", "TRACKER_HAND_LEFT" }, // XRPositionalTracker
+ { "TRACKER_RIGHT_HAND", "TRACKER_HAND_RIGHT" }, // XRPositionalTracker
+ { "TYPE_NORMALMAP", "TYPE_NORMAL_MAP" }, // VisualShaderNodeCubemap
+
+ /// enums
+ { "AlignMode", "AlignmentMode" }, //AspectRatioContainer
+ { "AnimationProcessMode", "AnimationProcessCallback" }, // AnimationTree, AnimationPlayer
+ { "Camera2DProcessMode", "Camera2DProcessCallback" }, // Camera2D
+ { "CubeMapSide", "CubeMapLayer" }, // RenderingServer
+ { "DampedStringParam", "DampedSpringParam" }, // PhysicsServer2D
+ { "FFT_Size", "FFTSize" }, // AudioEffectPitchShift,AudioEffectSpectrumAnalyzer
+ { "PauseMode", "ProcessMode" }, // Node
+ { "TimerProcessMode", "TimerProcessCallback" }, // Timer
+ { "Tracking_status", "TrackingStatus" }, // XRInterface
+ { nullptr, nullptr },
+};
+
+static const char *gdscript_function_renames[][2] = {
+ // { "_set_name", "get_tracker_name"}, // XRPositionalTracker - CameraFeed use this
+ // { "_unhandled_input", "_unhandled_key_input"}, // BaseButton, ViewportContainer broke Node, FileDialog,SubViewportContainer
+ // { "create_gizmo", "_create_gizmo"}, // EditorNode3DGizmoPlugin - may be used
+ // { "get_dependencies", "_get_dependencies" }, // ResourceFormatLoader broke ResourceLoader
+ // { "get_extents", "get_size" }, // BoxShape, RectangleShape broke Decal, VoxelGI, GPUParticlesCollisionBox, GPUParticlesCollisionSDF, GPUParticlesCollisionHeightField, GPUParticlesAttractorBox, GPUParticlesAttractorVectorField, FogVolume
+ // { "get_h_offset", "get_drag_horizontal_offset"}, // Camera2D, broke PathFollow, Camera
+ // { "get_mode", "get_file_mode"}, // FileDialog broke Panel, Shader, CSGPolygon, Tilemap
+ // { "get_motion", "get_travel"}, // PhysicsTestMotionResult2D broke ParalaxLayer
+ // { "get_name", "get_tracker_name"}, // XRPositionalTracker broke OS, Node
+ // { "get_network_connected_peers", "get_peers"}, // MultiplayerAPI broke SceneTree
+ // { "get_network_peer", "has_multiplayer_peer"}, // MultiplayerAPI broke SceneTree
+ // { "get_network_unique_id", "get_unique_id"}, // MultiplayerAPI broke SceneTree
+ // { "get_offset", "get_position_offset" }, // GraphNode broke Gradient
+ // { "get_peer_port", "get_peer" }, // ENetMultiplayerPeer broke WebSocketServer
+ // { "get_process_mode", "get_process_callback" }, // ClippedCamera3D broke Node, Sky
+ // { "get_render_info", "get_rendering_info" }, // RenderingServer broke Viewport
+ // { "get_type", "get_tracker_type"}, // XRPositionalTracker broke GLTFAccessor, GLTFLight
+ // { "get_v_offset", "get_drag_vertical_offset"}, // Camera2D, broke PathFollow, Camera
+ // { "has_network_peer", "has_multiplayer_peer"}, // MultiplayerAPI broke SceneTree
+ // { "instance", "instantiate" }, // PackedScene, ClassDB - Broke FileSystemDock signal and also tscn files - [instance=ExtResource( 17 )] - this is implemented as custom rule
+ // { "is_listening", "is_bound"}, // PacketPeerUDP broke TCPServer, UDPServer
+ // { "is_refusing_new_network_connections", "is_refusing_new_connections"}, // MultiplayerAPI broke SceneTree
+ // { "is_valid", "has_valid_event" }, // Shortcut broke e.g. Callable
+ // { "listen", "bound"}, // PacketPeerUDP broke TCPServer, UDPServer
+ // { "load", "_load"}, // ResourceFormatLoader broke ConfigFile, Image, StreamTexture2D
+ // { "make_current", "set_current" }, // Camera2D broke Camera3D, Listener2D
+ // { "process", "_process" }, // AnimationNode - This word is commonly used
+ // { "save", "_save"}, // ResourceFormatLoader broke ConfigFile, Image, StreamTexture2D
+ // { "set_autowrap", "set_autowrap_mode" }, // AcceptDialog broke Label - Cyclic Rename
+ // { "set_color", "surface_set_color"}, // ImmediateMesh broke Light2D, Theme, SurfaceTool
+ // { "set_event", "set_shortcut" }, // BaseButton - Cyclic Rename
+ // { "set_extents", "set_size"}, // BoxShape, RectangleShape broke ReflectionProbe
+ // { "set_flag", "set_particle_flag"}, // ParticlesMaterial broke Window, HingeJoint3D
+ // { "set_h_offset", "set_drag_horizontal_offset" }, // Camera2D broke Camera3D, PathFollow3D, PathFollow2D
+ // { "set_margin", "set_offset" }, // Control broke Shape3D, AtlasTexture
+ // { "set_mode", "set_mode_file_mode" }, // FileDialog broke Panel, Shader, CSGPolygon, Tilemap
+ // { "set_normal", "surface_set_normal"}, // ImmediateGeometry broke SurfaceTool, WorldMarginShape2D
+ // { "set_process_mode", "set_process_callback" }, // AnimationTree broke Node, Tween, Sky
+ // { "set_refuse_new_network_connections", "set_refuse_new_connections"}, // MultiplayerAPI broke SceneTree
+ // { "set_uv", "surface_set_uv" }, // ImmediateMesh broke Polygon2D
+ // { "set_v_offset", "set_drag_vertical_offset" }, // Camera2D broke Camera3D, PathFollow3D, PathFollow2D
+ // {"get_points","get_points_id"},// Astar, broke Line2D, Convexpolygonshape
+ // {"get_v_scroll","get_v_scroll_bar"},//ItemList, broke TextView
+ { "RenderingServer", "get_tab_alignment" }, // Tab
+ { "_about_to_show", "_about_to_popup" }, // ColorPickerButton
+ { "_get_configuration_warning", "_get_configuration_warnings" }, // Node
+ { "_set_current", "set_current" }, // Camera2D
+ { "_set_editor_description", "set_editor_description" }, // Node
+ { "_toplevel_raise_self", "_top_level_raise_self" }, // CanvasItem
+ { "_update_wrap_at", "_update_wrap_at_column" }, // TextEdit
+ { "add_animation", "add_animation_library" }, // AnimationPlayer
+ { "add_cancel", "add_cancel_button" }, // AcceptDialog
+ { "add_central_force", "add_constant_central_force" }, //RigidDynamicBody2D
+ { "add_child_below_node", "add_sibling" }, // Node
+ { "add_color_override", "add_theme_color_override" }, // Control
+ { "add_constant_override", "add_theme_constant_override" }, // Control
+ { "add_font_override", "add_theme_font_override" }, // Control
+ { "add_force", "add_constant_force" }, //RigidDynamicBody2D
+ { "add_icon_override", "add_theme_icon_override" }, // Control
+ { "add_scene_import_plugin", "add_scene_format_importer_plugin" }, //EditorPlugin
+ { "add_stylebox_override", "add_theme_stylebox_override" }, // Control
+ { "add_torque", "add_constant_torque" }, //RigidDynamicBody2D
+ { "bind_child_node_to_bone", "set_bone_children" }, // Skeleton3D
+ { "bumpmap_to_normalmap", "bump_map_to_normal_map" }, // Image
+ { "can_be_hidden", "_can_be_hidden" }, // EditorNode3DGizmoPlugin
+ { "can_drop_data_fw", "_can_drop_data_fw" }, // ScriptEditor
+ { "can_generate_small_preview", "_can_generate_small_preview" }, // EditorResourcePreviewGenerator
+ { "can_instance", "can_instantiate" }, // PackedScene, Script
+ { "canvas_light_set_scale", "canvas_light_set_texture_scale" }, // RenderingServer
+ { "center_viewport_to_cursor", "center_viewport_to_caret" }, // TextEdit
+ { "clip_polygons_2d", "clip_polygons" }, // Geometry2D
+ { "clip_polyline_with_polygon_2d", "clip_polyline_with_polygon" }, //Geometry2D
+ { "commit_handle", "_commit_handle" }, // EditorNode3DGizmo
+ { "convex_hull_2d", "convex_hull" }, // Geometry2D
+ { "cursor_get_blink_speed", "get_caret_blink_speed" }, // TextEdit
+ { "cursor_get_column", "get_caret_column" }, // TextEdit
+ { "cursor_get_line", "get_caret_line" }, // TextEdit
+ { "cursor_set_blink_enabled", "set_caret_blink_enabled" }, // TextEdit
+ { "cursor_set_blink_speed", "set_caret_blink_speed" }, // TextEdit
+ { "cursor_set_column", "set_caret_column" }, // TextEdit
+ { "cursor_set_line", "set_caret_line" }, // TextEdit
+ { "damped_spring_joint_create", "joint_make_damped_spring" }, // PhysicsServer2D
+ { "damped_string_joint_get_param", "damped_spring_joint_get_param" }, // PhysicsServer2D
+ { "damped_string_joint_set_param", "damped_spring_joint_set_param" }, // PhysicsServer2D
+ { "delete_char_at_cursor", "delete_char_at_caret" }, // LineEdit
+ { "deselect_items", "deselect_all" }, // FileDialog
+ { "drop_data_fw", "_drop_data_fw" }, // ScriptEditor
+ { "exclude_polygons_2d", "exclude_polygons" }, // Geometry2D
+ { "find_scancode_from_string", "find_keycode_from_string" }, // OS
+ { "forward_canvas_draw_over_viewport", "_forward_canvas_draw_over_viewport" }, // EditorPlugin
+ { "forward_canvas_force_draw_over_viewport", "_forward_canvas_force_draw_over_viewport" }, // EditorPlugin
+ { "forward_canvas_gui_input", "_forward_canvas_gui_input" }, // EditorPlugin
+ { "forward_spatial_draw_over_viewport", "_forward_3d_draw_over_viewport" }, // EditorPlugin
+ { "forward_spatial_force_draw_over_viewport", "_forward_3d_force_draw_over_viewport" }, // EditorPlugin
+ { "forward_spatial_gui_input", "_forward_3d_gui_input" }, // EditorPlugin
+ { "generate_from_path", "_generate_from_path" }, // EditorResourcePreviewGenerator
+ { "generate_small_preview_automatically", "_generate_small_preview_automatically" }, // EditorResourcePreviewGenerator
+ { "get_action_list", "action_get_events" }, // InputMap
+ { "get_alt", "is_alt_pressed" }, // InputEventWithModifiers
+ { "get_animation_process_mode", "get_process_callback" }, // AnimationPlayer
+ { "get_applied_force", "get_constant_force" }, //RigidDynamicBody2D
+ { "get_applied_torque", "get_constant_torque" }, //RigidDynamicBody2D
+ { "get_audio_bus", "get_audio_bus_name" }, // Area3D
+ { "get_bound_child_nodes_to_bone", "get_bone_children" }, // Skeleton3D
+ { "get_camera", "get_camera_3d" }, // Viewport -> this is also convertable to get_camera_2d, broke GLTFNode
+ { "get_cancel", "get_cancel_button" }, // ConfirmationDialog
+ { "get_caption", "_get_caption" }, // AnimationNode
+ { "get_cast_to", "get_target_position" }, // RayCast2D, RayCast3D
+ { "get_child_by_name", "_get_child_by_name" }, // AnimationNode
+ { "get_child_nodes", "_get_child_nodes" }, // AnimationNode
+ { "get_closest_point_to_segment_2d", "get_closest_point_to_segment" }, // Geometry2D
+ { "get_closest_point_to_segment_uncapped_2d", "get_closest_point_to_segment_uncapped" }, // Geometry2D
+ { "get_closest_points_between_segments_2d", "get_closest_point_to_segment" }, // Geometry2D
+ { "get_collision_layer_bit", "get_collision_layer_value" }, // CSGShape3D and a lot of others like GridMap
+ { "get_collision_mask_bit", "get_collision_mask_value" }, // CSGShape3D and a lot of others like GridMap
+ { "get_color_types", "get_color_type_list" }, // Theme
+ { "get_command", "is_command_pressed" }, // InputEventWithModifiers
+ { "get_constant_types", "get_constant_type_list" }, // Theme
+ { "get_control", "is_ctrl_pressed" }, // InputEventWithModifiers
+ { "get_cull_mask_bit", "get_cull_mask_value" }, // Camera3D
+ { "get_cursor_position", "get_caret_column" }, // LineEdit
+ { "get_d", "get_distance" }, // LineShape2D
+ { "get_drag_data_fw", "_get_drag_data_fw" }, // ScriptEditor
+ { "get_editor_viewport", "get_viewport" }, // EditorPlugin
+ { "get_enabled_focus_mode", "get_focus_mode" }, // BaseButton
+ { "get_endian_swap", "is_big_endian" }, // File
+ { "get_error_string", "get_error_message" }, // JSON
+ { "get_focus_neighbour", "get_focus_neighbor" }, // Control
+ { "get_font_types", "get_font_type_list" }, // Theme
+ { "get_frame_color", "get_color" }, // ColorRect
+ { "get_global_rate_scale", "get_playback_speed_scale" }, // AudioServer
+ { "get_gravity_distance_scale", "get_gravity_point_distance_scale" }, //Area2D
+ { "get_gravity_vector", "get_gravity_direction" }, //Area2D
+ { "get_h_scrollbar", "get_h_scroll_bar" }, //ScrollContainer
+ { "get_hand", "get_tracker_hand" }, // XRPositionalTracker
+ { "get_handle_name", "_get_handle_name" }, // EditorNode3DGizmo
+ { "get_handle_value", "_get_handle_value" }, // EditorNode3DGizmo
+ { "get_icon_align", "get_icon_alignment" }, // Button
+ { "get_icon_types", "get_icon_type_list" }, // Theme
+ { "get_idle_frames", "get_process_frames" }, // Engine
+ { "get_import_options", "_get_import_options" }, // EditorImportPlugin
+ { "get_import_order", "_get_import_order" }, // EditorImportPlugin
+ { "get_importer_name", "_get_importer_name" }, // EditorImportPlugin
+ { "get_interior_ambient", "get_ambient_color" }, // ReflectionProbe
+ { "get_interior_ambient_energy", "get_ambient_color_energy" }, // ReflectionProbe
+ { "get_iterations_per_second", "get_physics_ticks_per_second" }, // Engine
+ { "get_last_mouse_speed", "get_last_mouse_velocity" }, // Input
+ { "get_layer_mask_bit", "get_layer_mask_value" }, // VisualInstance3D
+ { "get_len", "get_length" }, // File
+ { "get_max_atlas_size", "get_max_texture_size" }, // LightmapGI
+ { "get_metakey", "is_meta_pressed" }, // InputEventWithModifiers
+ { "get_mid_height", "get_height" }, // CapsuleMesh
+ { "get_motion_remainder", "get_remainder" }, // PhysicsTestMotionResult2D
+ { "get_network_connected_peers", "get_peers" }, // Multiplayer API
+ { "get_network_master", "get_multiplayer_authority" }, // Node
+ { "get_network_peer", "get_multiplayer_peer" }, // Multiplayer API
+ { "get_network_unique_id", "get_unique_id" }, // Multiplayer API
+ { "get_ok", "get_ok_button" }, // AcceptDialog
+ { "get_option_visibility", "_get_option_visibility" }, // EditorImportPlugin
+ { "get_parameter_default_value", "_get_parameter_default_value" }, // AnimationNode
+ { "get_parameter_list", "_get_parameter_list" }, // AnimationNode
+ { "get_parent_spatial", "get_parent_node_3d" }, // Node3D
+ { "get_physical_scancode", "get_physical_keycode" }, // InputEventKey
+ { "get_physical_scancode_with_modifiers", "get_physical_keycode_with_modifiers" }, // InputEventKey
+ { "get_plugin_icon", "_get_plugin_icon" }, // EditorPlugin
+ { "get_plugin_name", "_get_plugin_name" }, // EditorPlugin
+ { "get_preset_count", "_get_preset_count" }, // EditorImportPlugin
+ { "get_preset_name", "_get_preset_name" }, // EditorImportPlugin
+ { "get_recognized_extensions", "_get_recognized_extensions" }, // ResourceFormatLoader, EditorImportPlugin broke ResourceSaver
+ { "get_render_info", "get_rendering_info" }, // RenderingServer
+ { "get_render_targetsize", "get_render_target_size" }, // XRInterface
+ { "get_resource_type", "_get_resource_type" }, // ResourceFormatLoader
+ { "get_result", "get_data" }, //JSON
+ { "get_rpc_sender_id", "get_remote_sender_id" }, // Multiplayer API
+ { "get_save_extension", "_get_save_extension" }, // EditorImportPlugin
+ { "get_scancode", "get_keycode" }, // InputEventKey
+ { "get_scancode_string", "get_keycode_string" }, // OS
+ { "get_scancode_with_modifiers", "get_keycode_with_modifiers" }, // InputEventKey
+ { "get_shift", "is_shift_pressed" }, // InputEventWithModifiers
+ { "get_size_override", "get_size_2d_override" }, // SubViewport
+ { "get_slips_on_slope", "get_slide_on_slope" }, // SeparationRayShape2D, SeparationRayShape3D
+ { "get_space_override_mode", "get_gravity_space_override_mode" }, // Area2D
+ { "get_speed", "get_velocity" }, // InputEventMouseMotion
+ { "get_stylebox_types", "get_stylebox_type_list" }, // Theme
+ { "get_surface_material", "get_surface_override_material" }, // MeshInstance3D broke ImporterMesh
+ { "get_surface_material_count", "get_surface_override_material_count" }, // MeshInstance3D
+ { "get_tab_disabled", "is_tab_disabled" }, // Tab
+ { "get_tab_hidden", "is_tab_hidden" }, // Tab
+ { "get_text_align", "get_text_alignment" }, // Button
+ { "get_theme_item_types", "get_theme_item_type_list" }, // Theme
+ { "get_timer_process_mode", "get_timer_process_callback" }, // Timer
+ { "get_translation", "get_position" }, // Node3D broke GLTFNode which is used rarely
+ { "get_use_in_baked_light", "is_baking_navigation" }, // GridMap
+ { "get_used_cells_by_id", "get_used_cells" }, // TileMap
+ { "get_v_scrollbar", "get_v_scroll_bar" }, //ScrollContainer
+ { "get_visible_name", "_get_visible_name" }, // EditorImportPlugin
+ { "get_window_layout", "_get_window_layout" }, // EditorPlugin
+ { "get_word_under_cursor", "get_word_under_caret" }, // TextEdit
+ { "get_world", "get_world_3d" }, // Viewport, Spatial
+ { "get_zfar", "get_far" }, // Camera3D broke GLTFCamera
+ { "get_znear", "get_near" }, // Camera3D broke GLTFCamera
+ { "groove_joint_create", "joint_make_groove" }, // PhysicsServer2D
+ { "handle_menu_selected", "_handle_menu_selected" }, // EditorResourcePicker
+ { "handles_type", "_handles_type" }, // ResourceFormatLoader
+ { "has_color", "has_theme_color" }, // Control broke Theme
+ { "has_color_override", "has_theme_color_override" }, // Control broke Theme
+ { "has_constant", "has_theme_constant" }, // Control
+ { "has_constant_override", "has_theme_constant_override" }, // Control
+ { "has_filter", "_has_filter" }, // AnimationNode
+ { "has_font", "has_theme_font" }, // Control broke Theme
+ { "has_font_override", "has_theme_font_override" }, // Control
+ { "has_icon", "has_theme_icon" }, // Control broke Theme
+ { "has_icon_override", "has_theme_icon_override" }, // Control
+ { "has_main_screen", "_has_main_screen" }, // EditorPlugin
+ { "has_network_peer", "has_multiplayer_peer" }, // Multiplayer API
+ { "has_stylebox", "has_theme_stylebox" }, // Control broke Theme
+ { "has_stylebox_override", "has_theme_stylebox_override" }, // Control
+ { "http_escape", "uri_encode" }, // String
+ { "http_unescape", "uri_decode" }, // String
+ { "import_animation_from_other_importer", "_import_animation" }, //EditorSceneFormatImporter
+ { "import_scene_from_other_importer", "_import_scene" }, //EditorSceneFormatImporter
+ { "instance_set_surface_material", "instance_set_surface_override_material" }, // RenderingServer
+ { "intersect_polygons_2d", "intersect_polygons" }, // Geometry2D
+ { "intersect_polyline_with_polygon_2d", "intersect_polyline_with_polygon" }, // Geometry2D
+ { "is_a_parent_of", "is_ancestor_of" }, // Node
+ { "is_commiting_action", "is_committing_action" }, // UndoRedo
+ { "is_doubleclick", "is_double_click" }, // InputEventMouseButton
+ { "is_h_drag_enabled", "is_drag_horizontal_enabled" }, // Camera2D
+ { "is_handle_highlighted", "_is_handle_highlighted" }, // EditorNode3DGizmo, EditorNode3DGizmoPlugin
+ { "is_network_master", "is_multiplayer_authority" }, // Node
+ { "is_network_server", "is_server" }, // Multiplayer API
+ { "is_normalmap", "is_normal_map" }, // NoiseTexture
+ { "is_refusing_new_network_connections", "is_refusing_new_connections" }, // Multiplayer API
+ { "is_region", "is_region_enabled" }, // Sprite2D
+ { "is_scancode_unicode", "is_keycode_unicode" }, // OS
+ { "is_selectable_when_hidden", "_is_selectable_when_hidden" }, // EditorNode3DGizmoPlugin
+ { "is_set_as_toplevel", "is_set_as_top_level" }, // CanvasItem
+ { "is_shortcut", "matches_event" }, // Shortcut
+ { "is_size_override_stretch_enabled", "is_size_2d_override_stretch_enabled" }, // SubViewport
+ { "is_sort_enabled", "is_y_sort_enabled" }, // Node2D
+ { "is_static_body", "is_able_to_sleep" }, // PhysicalBone3D - TODO - not sure
+ { "is_v_drag_enabled", "is_drag_vertical_enabled" }, // Camera2D
+ { "joint_create_cone_twist", "joint_make_cone_twist" }, // PhysicsServer3D
+ { "joint_create_generic_6dof", "joint_make_generic_6dof" }, // PhysicsServer3D
+ { "joint_create_hinge", "joint_make_hinge" }, // PhysicsServer3D
+ { "joint_create_pin", "joint_make_pin" }, // PhysicsServer3D
+ { "joint_create_slider", "joint_make_slider" }, // PhysicsServer3D
+ { "line_intersects_line_2d", "line_intersects_line" }, // Geometry2D
+ { "load_from_globals", "load_from_project_settings" }, // InputMap
+ { "make_convex_from_brothers", "make_convex_from_siblings" }, // CollisionShape3D
+ { "merge_polygons_2d", "merge_polygons" }, // Geometry2D
+ { "mesh_surface_get_format", "mesh_surface_get_format_attribute_stride" }, // RenderingServer
+ { "mesh_surface_update_region", "mesh_surface_update_attribute_region" }, // RenderingServer
+ { "move_to_bottom", "move_after" }, // Skeleton3D
+ { "move_to_top", "move_before" }, // Skeleton3D
+ { "multimesh_allocate", "multimesh_allocate_data" }, // RenderingServer
+ { "normalmap_to_xy", "normal_map_to_xy" }, // Image
+ { "offset_polygon_2d", "offset_polygon" }, // Geometry2D
+ { "offset_polyline_2d", "offset_polyline" }, // Geometry2D
+ { "percent_decode", "uri_decode" }, // String
+ { "percent_encode", "uri_encode" }, // String
+ { "pin_joint_create", "joint_make_pin" }, // PhysicsServer2D
+ { "popup_centered_minsize", "popup_centered_clamped" }, // Window
+ { "post_import", "_post_import" }, // EditorScenePostImport
+ { "print_stray_nodes", "print_orphan_nodes" }, // Node
+ { "property_list_changed_notify", "notify_property_list_changed" }, // Object
+ { "recognize", "_recognize" }, // ResourceFormatLoader
+ { "regen_normalmaps", "regen_normal_maps" }, // ArrayMesh
+ { "remove", "remove_at" }, // Array, broke Directory
+ { "remove_animation", "remove_animation_library" }, // AnimationPlayer
+ { "remove_color_override", "remove_theme_color_override" }, // Control
+ { "remove_constant_override", "remove_theme_constant_override" }, // Control
+ { "remove_font_override", "remove_theme_font_override" }, // Control
+ { "remove_scene_import_plugin", "remove_scene_format_importer_plugin" }, //EditorPlugin
+ { "remove_stylebox_override", "remove_theme_stylebox_override" }, // Control
+ { "rename_animation", "rename_animation_library" }, // AnimationPlayer
+ { "rename_dependencies", "_rename_dependencies" }, // ResourceFormatLoader
+ { "save_external_data", "_save_external_data" }, // EditorPlugin
+ { "segment_intersects_segment_2d", "segment_intersects_segment" }, // Geometry2D
+ { "set_adjustment_enable", "set_adjustment_enabled" }, // Environment
+ { "set_alt", "set_alt_pressed" }, // InputEventWithModifiers
+ { "set_anchor_and_margin", "set_anchor_and_offset" }, // Control
+ { "set_anchors_and_margins_preset", "set_anchors_and_offsets_preset" }, // Control
+ { "set_animation_process_mode", "set_process_callback" }, // AnimationPlayer
+ { "set_as_bulk_array", "set_buffer" }, // MultiMesh
+ { "set_as_normalmap", "set_as_normal_map" }, // NoiseTexture
+ { "set_as_toplevel", "set_as_top_level" }, // CanvasItem
+ { "set_audio_bus", "set_audio_bus_name" }, // Area3D
+ { "set_autowrap", "set_autowrap_mode" }, // Label broke AcceptDialog
+ { "set_cast_to", "set_target_position" }, // RayCast2D, RayCast3D
+ { "set_collision_layer_bit", "set_collision_layer_value" }, // CSGShape3D and a lot of others like GridMap
+ { "set_collision_mask_bit", "set_collision_mask_value" }, // CSGShape3D and a lot of others like GridMap
+ { "set_column_min_width", "set_column_custom_minimum_width" }, // Tree
+ { "set_command", "set_command_pressed" }, // InputEventWithModifiers
+ { "set_control", "set_ctrl_pressed" }, // InputEventWithModifiers
+ { "set_create_options", "_set_create_options" }, // EditorResourcePicker
+ { "set_cull_mask_bit", "set_cull_mask_value" }, // Camera3D
+ { "set_cursor_position", "set_caret_column" }, // LineEdit
+ { "set_d", "set_distance" }, // WorldMarginShape2D
+ { "set_doubleclick", "set_double_click" }, // InputEventMouseButton
+ { "set_enabled_focus_mode", "set_focus_mode" }, // BaseButton
+ { "set_endian_swap", "set_big_endian" }, // File
+ { "set_expand_to_text_length", "set_expand_to_text_length_enabled" }, // LineEdit
+ { "set_focus_neighbour", "set_focus_neighbor" }, // Control
+ { "set_frame_color", "set_color" }, // ColorRect
+ { "set_global_rate_scale", "set_playback_speed_scale" }, // AudioServer
+ { "set_gravity_distance_scale", "set_gravity_point_distance_scale" }, // Area2D
+ { "set_gravity_vector", "set_gravity_direction" }, // Area2D
+ { "set_h_drag_enabled", "set_drag_horizontal_enabled" }, // Camera2D
+ { "set_icon_align", "set_icon_alignment" }, // Button
+ { "set_interior_ambient", "set_ambient_color" }, // ReflectionProbe
+ { "set_interior_ambient_energy", "set_ambient_color_energy" }, // ReflectionProbe
+ { "set_is_initialized", "_is_initialized" }, // XRInterface
+ { "set_is_primary", "set_primary" }, // XRInterface
+ { "set_iterations_per_second", "set_physics_ticks_per_second" }, // Engine
+ { "set_layer_mask_bit", "set_layer_mask_value" }, // VisualInstance3D
+ { "set_margins_preset", "set_offsets_preset" }, // Control
+ { "set_max_atlas_size", "set_max_texture_size" }, // LightmapGI
+ { "set_metakey", "set_meta_pressed" }, // InputEventWithModifiers
+ { "set_mid_height", "set_height" }, // CapsuleMesh
+ { "set_network_master", "set_multiplayer_authority" }, // Node
+ { "set_network_peer", "set_multiplayer_peer" }, // Multiplayer API
+ { "set_physical_scancode", "set_physical_keycode" }, // InputEventKey
+ { "set_refuse_new_network_connections", "set_refuse_new_connections" }, // Multiplayer API
+ { "set_region", "set_region_enabled" }, // Sprite2D, Sprite broke AtlasTexture
+ { "set_region_filter_clip", "set_region_filter_clip_enabled" }, // Sprite2D
+ { "set_rotate", "set_rotates" }, // PathFollow2D
+ { "set_scancode", "set_keycode" }, // InputEventKey
+ { "set_shift", "set_shift_pressed" }, // InputEventWithModifiers
+ { "set_size_override", "set_size_2d_override" }, // SubViewport broke ImageTexture
+ { "set_size_override_stretch", "set_size_2d_override_stretch" }, // SubViewport
+ { "set_slips_on_slope", "set_slide_on_slope" }, // SeparationRayShape2D, SeparationRayShape3D
+ { "set_sort_enabled", "set_y_sort_enabled" }, // Node2D
+ { "set_space_override_mode", "set_gravity_space_override_mode" }, // Area2D
+ { "set_speed", "set_velocity" }, // InputEventMouseMotion
+ { "set_ssao_edge_sharpness", "set_ssao_sharpness" }, // Environment
+ { "set_surface_material", "set_surface_override_material" }, // MeshInstance3D broke ImporterMesh
+ { "set_tab_align", "set_tab_alignment" }, //TabContainer
+ { "set_tangent", "surface_set_tangent" }, // ImmediateGeometry broke SurfaceTool
+ { "set_text_align", "set_text_alignment" }, // Button
+ { "set_timer_process_mode", "set_timer_process_callback" }, // Timer
+ { "set_tonemap_auto_exposure", "set_tonemap_auto_exposure_enabled" }, // Environment
+ { "set_translation", "set_position" }, // Node3D - this broke GLTFNode which is used rarely
+ { "set_uv2", "surface_set_uv2" }, // ImmediateMesh broke Surffacetool
+ { "set_v_drag_enabled", "set_drag_vertical_enabled" }, // Camera2D
+ { "set_valign", "set_vertical_alignment" }, // Label
+ { "set_window_layout", "_set_window_layout" }, // EditorPlugin
+ { "set_zfar", "set_far" }, // Camera3D broke GLTFCamera
+ { "set_znear", "set_near" }, // Camera3D broke GLTFCamera
+ { "shortcut_match", "is_match" }, // InputEvent
+ { "skeleton_allocate", "skeleton_allocate_data" }, // RenderingServer
+ { "surface_update_region", "surface_update_attribute_region" }, // ArrayMesh
+ { "targeting_method", "tween_method" }, // Tween
+ { "targeting_property", "tween_property" }, // Tween
+ { "track_remove_key_at_position", "track_remove_key_at_time" }, // Animation
+ { "triangulate_delaunay_2d", "triangulate_delaunay" }, // Geometry2D
+ { "unbind_child_node_from_bone", "remove_bone_child" }, // Skeleton3D
+ { "unselect", "deselect" }, // ItemList
+ { "unselect_all", "deselect_all" }, // ItemList
+ { "update_configuration_warning", "update_configuration_warnings" }, // Node
+ { "update_gizmo", "update_gizmos" }, // Node3D
+ { "viewport_set_use_arvr", "viewport_set_use_xr" }, // RenderingServer
+ { "warp_mouse_position", "warp_mouse" }, // Input
+
+ // Builtin types
+ // { "empty", "is_empty" }, // Array - Used as custom rule // Be careful, this will be used everywhere
+ { "clamped", "clamp" }, // Vector2 // Be careful, this will be used everywhere
+ { "get_rotation_quat", "get_rotation_quaternion" }, // Basis
+ { "grow_margin", "grow_side" }, // Rect2
+ { "invert", "reverse" }, // Array - TODO check // Be careful, this will be used everywhere
+ { "is_abs_path", "is_absolute_path" }, // String
+ { "is_valid_integer", "is_valid_int" }, // String
+ { "linear_interpolate", "lerp" }, // Color
+ { "to_ascii", "to_ascii_buffer" }, // String
+ { "to_utf8", "to_utf8_buffer" }, // String
+ { "to_wchar", "to_utf32_buffer" }, // String // TODO - utf32 or utf16?
+
+ // Globals
+ { "rand_range", "randf_range" },
+ { "stepify", "snapped" },
+
+ { nullptr, nullptr },
+};
+
+// gdscript_function_renames clone with CamelCase
+static const char *csharp_function_renames[][2] = {
+ // { "_SetName", "GetTrackerName"}, // XRPositionalTracker - CameraFeed use this
+ // { "_UnhandledInput", "_UnhandledKeyInput"}, // BaseButton, ViewportContainer broke Node, FileDialog,SubViewportContainer
+ // { "CreateGizmo", "_CreateGizmo"}, // EditorNode3DGizmoPlugin - may be used
+ // { "GetDependencies", "_GetDependencies" }, // ResourceFormatLoader broke ResourceLoader
+ // { "GetExtents", "GetSize" }, // BoxShape, RectangleShape broke Decal, VoxelGI, GPUParticlesCollisionBox, GPUParticlesCollisionSDF, GPUParticlesCollisionHeightField, GPUParticlesAttractorBox, GPUParticlesAttractorVectorField, FogVolume
+ // { "GetHOffset", "GetDragHorizontalOffset"}, // Camera2D, broke PathFollow, Camera
+ // { "GetMode", "GetFileMode"}, // FileDialog broke Panel, Shader, CSGPolygon, Tilemap
+ // { "GetMotion", "GetTravel"}, // PhysicsTestMotionResult2D broke ParalaxLayer
+ // { "GetName", "GetTrackerName"}, // XRPositionalTracker broke OS, Node
+ // { "GetNetworkConnectedPeers", "GetPeers"}, // MultiplayerAPI broke SceneTree
+ // { "GetNetworkPeer", "HasMultiplayerPeer"}, // MultiplayerAPI broke SceneTree
+ // { "GetNetworkUniqueId", "GetUniqueId"}, // MultiplayerAPI broke SceneTree
+ // { "GetOffset", "GetPositionOffset" }, // GraphNode broke Gradient
+ // { "GetPeerPort", "GetPeer" }, // ENetMultiplayerPeer broke WebSocketServer
+ // { "GetProcessMode", "GetProcessCallback" }, // ClippedCamera3D broke Node, Sky
+ // { "GetRenderInfo", "GetRenderingInfo" }, // RenderingServer broke Viewport
+ // { "GetType", "GetTrackerType"}, // XRPositionalTracker broke GLTFAccessor, GLTFLight
+ // { "GetVOffset", "GetDragVerticalOffset"}, // Camera2D, broke PathFollow, Camera
+ // { "HasNetworkPeer", "HasMultiplayerPeer"}, // MultiplayerAPI broke SceneTree
+ // { "Instance", "Instantiate" }, // PackedScene, ClassDB - Broke FileSystemDock signal and also tscn files - [instance=ExtResource( 17 )] - this is implemented as custom rule
+ // { "IsListening", "IsBound"}, // PacketPeerUDP broke TCPServer, UDPServer
+ // { "IsRefusingNewNetworkConnections", "IsRefusingNewConnections"}, // MultiplayerAPI broke SceneTree
+ // { "IsValid", "HasValidEvent" }, // Shortcut broke e.g. Callable
+ // { "Listen", "Bound"}, // PacketPeerUDP broke TCPServer, UDPServer
+ // { "Load", "_Load"}, // ResourceFormatLoader broke ConfigFile, Image, StreamTexture2D
+ // { "MakeCurrent", "SetCurrent" }, // Camera2D broke Camera3D, Listener2D
+ // { "Process", "_Process" }, // AnimationNode - This word is commonly used
+ // { "Save", "_Save"}, // ResourceFormatLoader broke ConfigFile, Image, StreamTexture2D
+ // { "SetAutowrap", "SetAutowrapMode" }, // AcceptDialog broke Label - Cyclic Rename
+ // { "SetColor", "SurfaceSetColor"}, // ImmediateMesh broke Light2D, Theme, SurfaceTool
+ // { "SetEvent", "SetShortcut" }, // BaseButton - Cyclic Rename
+ // { "SetExtents", "SetSize"}, // BoxShape, RectangleShape broke ReflectionProbe
+ // { "SetFlag", "SetParticleFlag"}, // ParticlesMaterial broke Window, HingeJoint3D
+ // { "SetHOffset", "SetDragHorizontalOffset" }, // Camera2D broke Camera3D, PathFollow3D, PathFollow2D
+ // { "SetMargin", "SetOffset" }, // Control broke Shape3D, AtlasTexture
+ // { "SetMode", "SetModeFileMode" }, // FileDialog broke Panel, Shader, CSGPolygon, Tilemap
+ // { "SetNormal", "SurfaceSetNormal"}, // ImmediateGeometry broke SurfaceTool, WorldMarginShape2D
+ // { "SetProcessMode", "SetProcessCallback" }, // AnimationTree broke Node, Tween, Sky
+ // { "SetRefuseNewNetworkConnections", "SetRefuseNewConnections"}, // MultiplayerAPI broke SceneTree
+ // { "SetUv", "SurfaceSetUv" }, // ImmediateMesh broke Polygon2D
+ // { "SetVOffset", "SetDragVerticalOffset" }, // Camera2D broke Camera3D, PathFollow3D, PathFollow2D
+ // {"GetPoints","GetPointsId"},// Astar, broke Line2D, Convexpolygonshape
+ // {"GetVScroll","GetVScrollBar"},//ItemList, broke TextView
+ { "RenderingServer", "GetTabAlignment" }, // Tab
+ { "_AboutToShow", "_AboutToPopup" }, // ColorPickerButton
+ { "_GetConfigurationWarning", "_GetConfigurationWarnings" }, // Node
+ { "_SetCurrent", "SetCurrent" }, // Camera2D
+ { "_SetEditorDescription", "SetEditorDescription" }, // Node
+ { "_ToplevelRaiseSelf", "_TopLevelRaiseSelf" }, // CanvasItem
+ { "_UpdateWrapAt", "_UpdateWrapAtColumn" }, // TextEdit
+ { "AddAnimation", "AddAnimationLibrary" }, // AnimationPlayer
+ { "AddCancel", "AddCancelButton" }, // AcceptDialog
+ { "AddCentralForce", "AddConstantCentralForce" }, //RigidDynamicBody2D
+ { "AddChildBelowNode", "AddSibling" }, // Node
+ { "AddColorOverride", "AddThemeColorOverride" }, // Control
+ { "AddConstantOverride", "AddThemeConstantOverride" }, // Control
+ { "AddFontOverride", "AddThemeFontOverride" }, // Control
+ { "AddForce", "AddConstantForce" }, //RigidDynamicBody2D
+ { "AddIconOverride", "AddThemeIconOverride" }, // Control
+ { "AddSceneImportPlugin", "AddSceneFormatImporterPlugin" }, //EditorPlugin
+ { "AddStyleboxOverride", "AddThemeStyleboxOverride" }, // Control
+ { "AddTorque", "AddConstantTorque" }, //RigidDynamicBody2D
+ { "BindChildNodeToBone", "SetBoneChildren" }, // Skeleton3D
+ { "BumpmapToNormalmap", "BumpMapToNormalMap" }, // Image
+ { "CanBeHidden", "_CanBeHidden" }, // EditorNode3DGizmoPlugin
+ { "CanDropDataFw", "_CanDropDataFw" }, // ScriptEditor
+ { "CanGenerateSmallPreview", "_CanGenerateSmallPreview" }, // EditorResourcePreviewGenerator
+ { "CanInstance", "CanInstantiate" }, // PackedScene, Script
+ { "CanvasLightSetScale", "CanvasLightSetTextureScale" }, // RenderingServer
+ { "CenterViewportToCursor", "CenterViewportToCaret" }, // TextEdit
+ { "ClipPolygons2d", "ClipPolygons" }, // Geometry2D
+ { "ClipPolylineWithPolygon2d", "ClipPolylineWithPolygon" }, //Geometry2D
+ { "CommitHandle", "_CommitHandle" }, // EditorNode3DGizmo
+ { "ConvexHull2d", "ConvexHull" }, // Geometry2D
+ { "CursorGetBlinkSpeed", "GetCaretBlinkSpeed" }, // TextEdit
+ { "CursorGetColumn", "GetCaretColumn" }, // TextEdit
+ { "CursorGetLine", "GetCaretLine" }, // TextEdit
+ { "CursorSetBlinkEnabled", "SetCaretBlinkEnabled" }, // TextEdit
+ { "CursorSetBlinkSpeed", "SetCaretBlinkSpeed" }, // TextEdit
+ { "CursorSetColumn", "SetCaretColumn" }, // TextEdit
+ { "CursorSetLine", "SetCaretLine" }, // TextEdit
+ { "DampedSpringJointCreate", "JointMakeDampedSpring" }, // PhysicsServer2D
+ { "DampedStringJointGetParam", "DampedSpringJointGetParam" }, // PhysicsServer2D
+ { "DampedStringJointSetParam", "DampedSpringJointSetParam" }, // PhysicsServer2D
+ { "DeleteCharAtCursor", "DeleteCharAtCaret" }, // LineEdit
+ { "DeselectItems", "DeselectAll" }, // FileDialog
+ { "DropDataFw", "_DropDataFw" }, // ScriptEditor
+ { "ExcludePolygons2d", "ExcludePolygons" }, // Geometry2D
+ { "FindScancodeFromString", "FindKeycodeFromString" }, // OS
+ { "ForwardCanvasDrawOverViewport", "_ForwardCanvasDrawOverViewport" }, // EditorPlugin
+ { "ForwardCanvasForceDrawOverViewport", "_ForwardCanvasForceDrawOverViewport" }, // EditorPlugin
+ { "ForwardCanvasGuiInput", "_ForwardCanvasGuiInput" }, // EditorPlugin
+ { "ForwardSpatialDrawOverViewport", "_Forward3dDrawOverViewport" }, // EditorPlugin
+ { "ForwardSpatialForceDrawOverViewport", "_Forward3dForceDrawOverViewport" }, // EditorPlugin
+ { "ForwardSpatialGuiInput", "_Forward3dGuiInput" }, // EditorPlugin
+ { "GenerateFromPath", "_GenerateFromPath" }, // EditorResourcePreviewGenerator
+ { "GenerateSmallPreviewAutomatically", "_GenerateSmallPreviewAutomatically" }, // EditorResourcePreviewGenerator
+ { "GetActionList", "ActionGetEvents" }, // InputMap
+ { "GetAlt", "IsAltPressed" }, // InputEventWithModifiers
+ { "GetAnimationProcessMode", "GetProcessCallback" }, // AnimationPlayer
+ { "GetAppliedForce", "GetConstantForce" }, //RigidDynamicBody2D
+ { "GetAppliedTorque", "GetConstantTorque" }, //RigidDynamicBody2D
+ { "GetAudioBus", "GetAudioBusName" }, // Area3D
+ { "GetBoundChildNodesToBone", "GetBoneChildren" }, // Skeleton3D
+ { "GetCamera", "GetCamera3d" }, // Viewport -> this is also convertable to getCamera2d, broke GLTFNode
+ { "GetCancel", "GetCancelButton" }, // ConfirmationDialog
+ { "GetCaption", "_GetCaption" }, // AnimationNode
+ { "GetCastTo", "GetTargetPosition" }, // RayCast2D, RayCast3D
+ { "GetChildByName", "_GetChildByName" }, // AnimationNode
+ { "GetChildNodes", "_GetChildNodes" }, // AnimationNode
+ { "GetClosestPointToSegment2d", "GetClosestPointToSegment" }, // Geometry2D
+ { "GetClosestPointToSegmentUncapped2d", "GetClosestPointToSegmentUncapped" }, // Geometry2D
+ { "GetClosestPointsBetweenSegments2d", "GetClosestPointToSegment" }, // Geometry2D
+ { "GetCollisionLayerBit", "GetCollisionLayerValue" }, // CSGShape3D and a lot of others like GridMap
+ { "GetCollisionMaskBit", "GetCollisionMaskValue" }, // CSGShape3D and a lot of others like GridMap
+ { "GetColorTypes", "GetColorTypeList" }, // Theme
+ { "GetCommand", "IsCommandPressed" }, // InputEventWithModifiers
+ { "GetConstantTypes", "GetConstantTypeList" }, // Theme
+ { "GetControl", "IsCtrlPressed" }, // InputEventWithModifiers
+ { "GetCullMaskBit", "GetCullMaskValue" }, // Camera3D
+ { "GetCursorPosition", "GetCaretColumn" }, // LineEdit
+ { "GetD", "GetDistance" }, // LineShape2D
+ { "GetDragDataFw", "_GetDragDataFw" }, // ScriptEditor
+ { "GetEditorViewport", "GetViewport" }, // EditorPlugin
+ { "GetEnabledFocusMode", "GetFocusMode" }, // BaseButton
+ { "GetEndianSwap", "IsBigEndian" }, // File
+ { "GetErrorString", "GetErrorMessage" }, // JSON
+ { "GetFocusNeighbour", "GetFocusNeighbor" }, // Control
+ { "GetFontTypes", "GetFontTypeList" }, // Theme
+ { "GetFrameColor", "GetColor" }, // ColorRect
+ { "GetGlobalRateScale", "GetPlaybackSpeedScale" }, // AudioServer
+ { "GetGravityDistanceScale", "GetGravityPointDistanceScale" }, //Area2D
+ { "GetGravityVector", "GetGravityDirection" }, //Area2D
+ { "GetHScrollbar", "GetHScrollBar" }, //ScrollContainer
+ { "GetHand", "GetTrackerHand" }, // XRPositionalTracker
+ { "GetHandleName", "_GetHandleName" }, // EditorNode3DGizmo
+ { "GetHandleValue", "_GetHandleValue" }, // EditorNode3DGizmo
+ { "GetIconAlign", "GetIconAlignment" }, // Button
+ { "GetIconTypes", "GetIconTypeList" }, // Theme
+ { "GetIdleFrames", "GetProcessFrames" }, // Engine
+ { "GetImportOptions", "_GetImportOptions" }, // EditorImportPlugin
+ { "GetImportOrder", "_GetImportOrder" }, // EditorImportPlugin
+ { "GetImporterName", "_GetImporterName" }, // EditorImportPlugin
+ { "GetInteriorAmbient", "GetAmbientColor" }, // ReflectionProbe
+ { "GetInteriorAmbientEnergy", "GetAmbientColorEnergy" }, // ReflectionProbe
+ { "GetIterationsPerSecond", "GetPhysicsTicksPerSecond" }, // Engine
+ { "GetLastMouseSpeed", "GetLastMouseVelocity" }, // Input
+ { "GetLayerMaskBit", "GetLayerMaskValue" }, // VisualInstance3D
+ { "GetLen", "GetLength" }, // File
+ { "GetMaxAtlasSize", "GetMaxTextureSize" }, // LightmapGI
+ { "GetMetakey", "IsMetaPressed" }, // InputEventWithModifiers
+ { "GetMidHeight", "GetHeight" }, // CapsuleMesh
+ { "GetMotionRemainder", "GetRemainder" }, // PhysicsTestMotionResult2D
+ { "GetNetworkConnectedPeers", "GetPeers" }, // Multiplayer API
+ { "GetNetworkMaster", "GetMultiplayerAuthority" }, // Node
+ { "GetNetworkPeer", "GetMultiplayerPeer" }, // Multiplayer API
+ { "GetNetworkUniqueId", "GetUniqueId" }, // Multiplayer API
+ { "GetOk", "GetOkButton" }, // AcceptDialog
+ { "GetOptionVisibility", "_GetOptionVisibility" }, // EditorImportPlugin
+ { "GetParameterDefaultValue", "_GetParameterDefaultValue" }, // AnimationNode
+ { "GetParameterList", "_GetParameterList" }, // AnimationNode
+ { "GetParentSpatial", "GetParentNode3d" }, // Node3D
+ { "GetPhysicalScancode", "GetPhysicalKeycode" }, // InputEventKey
+ { "GetPhysicalScancodeWithModifiers", "GetPhysicalKeycodeWithModifiers" }, // InputEventKey
+ { "GetPluginIcon", "_GetPluginIcon" }, // EditorPlugin
+ { "GetPluginName", "_GetPluginName" }, // EditorPlugin
+ { "GetPresetCount", "_GetPresetCount" }, // EditorImportPlugin
+ { "GetPresetName", "_GetPresetName" }, // EditorImportPlugin
+ { "GetRecognizedExtensions", "_GetRecognizedExtensions" }, // ResourceFormatLoader, EditorImportPlugin broke ResourceSaver
+ { "GetRenderInfo", "GetRenderingInfo" }, // RenderingServer
+ { "GetRenderTargetsize", "GetRenderTargetSize" }, // XRInterface
+ { "GetResourceType", "_GetResourceType" }, // ResourceFormatLoader
+ { "GetResult", "GetData" }, //JSON
+ { "GetRpcSenderId", "GetRemoteSenderId" }, // Multiplayer API
+ { "GetSaveExtension", "_GetSaveExtension" }, // EditorImportPlugin
+ { "GetScancode", "GetKeycode" }, // InputEventKey
+ { "GetScancodeString", "GetKeycodeString" }, // OS
+ { "GetScancodeWithModifiers", "GetKeycodeWithModifiers" }, // InputEventKey
+ { "GetShift", "IsShiftPressed" }, // InputEventWithModifiers
+ { "GetSizeOverride", "GetSize2dOverride" }, // SubViewport
+ { "GetSlipsOnSlope", "GetSlideOnSlope" }, // SeparationRayShape2D, SeparationRayShape3D
+ { "GetSpaceOverrideMode", "GetGravitySpaceOverrideMode" }, // Area2D
+ { "GetSpeed", "GetVelocity" }, // InputEventMouseMotion
+ { "GetStyleboxTypes", "GetStyleboxTypeList" }, // Theme
+ { "GetSurfaceMaterial", "GetSurfaceOverrideMaterial" }, // MeshInstance3D broke ImporterMesh
+ { "GetSurfaceMaterialCount", "GetSurfaceOverrideMaterialCount" }, // MeshInstance3D
+ { "GetTabDisabled", "IsTabDisabled" }, // Tab
+ { "GetTabHidden", "IsTabHidden" }, // Tab
+ { "GetTextAlign", "GetTextAlignment" }, // Button
+ { "GetThemeItemTypes", "GetThemeItemTypeList" }, // Theme
+ { "GetTimerProcessMode", "GetTimerProcessCallback" }, // Timer
+ { "GetTranslation", "GetPosition" }, // Node3D broke GLTFNode which is used rarely
+ { "GetUseInBakedLight", "IsBakingNavigation" }, // GridMap
+ { "GetUsedCellsById", "GetUsedCells" }, // TileMap
+ { "GetVScrollbar", "GetVScrollBar" }, //ScrollContainer
+ { "GetVisibleName", "_GetVisibleName" }, // EditorImportPlugin
+ { "GetWindowLayout", "_GetWindowLayout" }, // EditorPlugin
+ { "GetWordUnderCursor", "GetWordUnderCaret" }, // TextEdit
+ { "GetWorld", "GetWorld3d" }, // Viewport, Spatial
+ { "GetZfar", "GetFar" }, // Camera3D broke GLTFCamera
+ { "GetZnear", "GetNear" }, // Camera3D broke GLTFCamera
+ { "GrooveJointCreate", "JointMakeGroove" }, // PhysicsServer2D
+ { "HandleMenuSelected", "_HandleMenuSelected" }, // EditorResourcePicker
+ { "HandlesType", "_HandlesType" }, // ResourceFormatLoader
+ { "HasColor", "HasThemeColor" }, // Control broke Theme
+ { "HasColorOverride", "HasThemeColorOverride" }, // Control broke Theme
+ { "HasConstant", "HasThemeConstant" }, // Control
+ { "HasConstantOverride", "HasThemeConstantOverride" }, // Control
+ { "HasFilter", "_HasFilter" }, // AnimationNode
+ { "HasFont", "HasThemeFont" }, // Control broke Theme
+ { "HasFontOverride", "HasThemeFontOverride" }, // Control
+ { "HasIcon", "HasThemeIcon" }, // Control broke Theme
+ { "HasIconOverride", "HasThemeIconOverride" }, // Control
+ { "HasMainScreen", "_HasMainScreen" }, // EditorPlugin
+ { "HasNetworkPeer", "HasMultiplayerPeer" }, // Multiplayer API
+ { "HasStylebox", "HasThemeStylebox" }, // Control broke Theme
+ { "HasStyleboxOverride", "HasThemeStyleboxOverride" }, // Control
+ { "HttpEscape", "UriEncode" }, // String
+ { "HttpUnescape", "UriDecode" }, // String
+ { "ImportAnimationFromOtherImporter", "_ImportAnimation" }, //EditorSceneFormatImporter
+ { "ImportSceneFromOtherImporter", "_ImportScene" }, //EditorSceneFormatImporter
+ { "InstanceSetSurfaceMaterial", "InstanceSetSurfaceOverrideMaterial" }, // RenderingServer
+ { "IntersectPolygons2d", "IntersectPolygons" }, // Geometry2D
+ { "IntersectPolylineWithPolygon2d", "IntersectPolylineWithPolygon" }, // Geometry2D
+ { "IsAParentOf", "IsAncestorOf" }, // Node
+ { "IsCommitingAction", "IsCommittingAction" }, // UndoRedo
+ { "IsDoubleclick", "IsDoubleClick" }, // InputEventMouseButton
+ { "IsHDragEnabled", "IsDragHorizontalEnabled" }, // Camera2D
+ { "IsHandleHighlighted", "_IsHandleHighlighted" }, // EditorNode3DGizmo, EditorNode3DGizmoPlugin
+ { "IsNetworkMaster", "IsMultiplayerAuthority" }, // Node
+ { "IsNetworkServer", "IsServer" }, // Multiplayer API
+ { "IsNormalmap", "IsNormalMap" }, // NoiseTexture
+ { "IsRefusingNewNetworkConnections", "IsRefusingNewConnections" }, // Multiplayer API
+ { "IsRegion", "IsRegionEnabled" }, // Sprite2D
+ { "IsScancodeUnicode", "IsKeycodeUnicode" }, // OS
+ { "IsSelectableWhenHidden", "_IsSelectableWhenHidden" }, // EditorNode3DGizmoPlugin
+ { "IsSetAsToplevel", "IsSetAsTopLevel" }, // CanvasItem
+ { "IsShortcut", "MatchesEvent" }, // Shortcut
+ { "IsSizeOverrideStretchEnabled", "IsSize2dOverrideStretchEnabled" }, // SubViewport
+ { "IsSortEnabled", "IsYSortEnabled" }, // Node2D
+ { "IsStaticBody", "IsAbleToSleep" }, // PhysicalBone3D - TODO - not sure
+ { "IsVDragEnabled", "IsDragVerticalEnabled" }, // Camera2D
+ { "JointCreateConeTwist", "JointMakeConeTwist" }, // PhysicsServer3D
+ { "JointCreateGeneric6dof", "JointMakeGeneric6dof" }, // PhysicsServer3D
+ { "JointCreateHinge", "JointMakeHinge" }, // PhysicsServer3D
+ { "JointCreatePin", "JointMakePin" }, // PhysicsServer3D
+ { "JointCreateSlider", "JointMakeSlider" }, // PhysicsServer3D
+ { "LineIntersectsLine2d", "LineIntersectsLine" }, // Geometry2D
+ { "LoadFromGlobals", "LoadFromProjectSettings" }, // InputMap
+ { "MakeConvexFromBrothers", "MakeConvexFromSiblings" }, // CollisionShape3D
+ { "MergePolygons2d", "MergePolygons" }, // Geometry2D
+ { "MeshSurfaceGetFormat", "MeshSurfaceGetFormatAttributeStride" }, // RenderingServer
+ { "MeshSurfaceUpdateRegion", "MeshSurfaceUpdateAttributeRegion" }, // RenderingServer
+ { "MoveToBottom", "MoveAfter" }, // Skeleton3D
+ { "MoveToTop", "MoveBefore" }, // Skeleton3D
+ { "MultimeshAllocate", "MultimeshAllocateData" }, // RenderingServer
+ { "NormalmapToXy", "NormalMapToXy" }, // Image
+ { "OffsetPolygon2d", "OffsetPolygon" }, // Geometry2D
+ { "OffsetPolyline2d", "OffsetPolyline" }, // Geometry2D
+ { "PercentDecode", "UriDecode" }, // String
+ { "PercentEncode", "UriEncode" }, // String
+ { "PinJointCreate", "JointMakePin" }, // PhysicsServer2D
+ { "PopupCenteredMinsize", "PopupCenteredClamped" }, // Window
+ { "PostImport", "_PostImport" }, // EditorScenePostImport
+ { "PrintStrayNodes", "PrintOrphanNodes" }, // Node
+ { "PropertyListChangedNotify", "NotifyPropertyListChanged" }, // Object
+ { "Recognize", "_Recognize" }, // ResourceFormatLoader
+ { "RegenNormalmaps", "RegenNormalMaps" }, // ArrayMesh
+ { "Remove", "RemoveAt" }, // Array, broke Directory
+ { "RemoveAnimation", "RemoveAnimationLibrary" }, // AnimationPlayer
+ { "RemoveColorOverride", "RemoveThemeColorOverride" }, // Control
+ { "RemoveConstantOverride", "RemoveThemeConstantOverride" }, // Control
+ { "RemoveFontOverride", "RemoveThemeFontOverride" }, // Control
+ { "RemoveSceneImportPlugin", "RemoveSceneFormatImporterPlugin" }, //EditorPlugin
+ { "RemoveStyleboxOverride", "RemoveThemeStyleboxOverride" }, // Control
+ { "RenameAnimation", "RenameAnimationLibrary" }, // AnimationPlayer
+ { "RenameDependencies", "_RenameDependencies" }, // ResourceFormatLoader
+ { "SaveExternalData", "_SaveExternalData" }, // EditorPlugin
+ { "SegmentIntersectsSegment2d", "SegmentIntersectsSegment" }, // Geometry2D
+ { "SetAdjustmentEnable", "SetAdjustmentEnabled" }, // Environment
+ { "SetAlt", "SetAltPressed" }, // InputEventWithModifiers
+ { "SetAnchorAndMargin", "SetAnchorAndOffset" }, // Control
+ { "SetAnchorsAndMarginsPreset", "SetAnchorsAndOffsetsPreset" }, // Control
+ { "SetAnimationProcessMode", "SetProcessCallback" }, // AnimationPlayer
+ { "SetAsBulkArray", "SetBuffer" }, // MultiMesh
+ { "SetAsNormalmap", "SetAsNormalMap" }, // NoiseTexture
+ { "SetAsToplevel", "SetAsTopLevel" }, // CanvasItem
+ { "SetAudioBus", "SetAudioBusName" }, // Area3D
+ { "SetAutowrap", "SetAutowrapMode" }, // Label broke AcceptDialog
+ { "SetCastTo", "SetTargetPosition" }, // RayCast2D, RayCast3D
+ { "SetCollisionLayerBit", "SetCollisionLayerValue" }, // CSGShape3D and a lot of others like GridMap
+ { "SetCollisionMaskBit", "SetCollisionMaskValue" }, // CSGShape3D and a lot of others like GridMap
+ { "SetColumnMinWidth", "SetColumnCustomMinimumWidth" }, // Tree
+ { "SetCommand", "SetCommandPressed" }, // InputEventWithModifiers
+ { "SetControl", "SetCtrlPressed" }, // InputEventWithModifiers
+ { "SetCreateOptions", "_SetCreateOptions" }, // EditorResourcePicker
+ { "SetCullMaskBit", "SetCullMaskValue" }, // Camera3D
+ { "SetCursorPosition", "SetCaretColumn" }, // LineEdit
+ { "SetD", "SetDistance" }, // WorldMarginShape2D
+ { "SetDoubleclick", "SetDoubleClick" }, // InputEventMouseButton
+ { "SetEnabledFocusMode", "SetFocusMode" }, // BaseButton
+ { "SetEndianSwap", "SetBigEndian" }, // File
+ { "SetExpandToTextLength", "SetExpandToTextLengthEnabled" }, // LineEdit
+ { "SetFocusNeighbour", "SetFocusNeighbor" }, // Control
+ { "SetFrameColor", "SetColor" }, // ColorRect
+ { "SetGlobalRateScale", "SetPlaybackSpeedScale" }, // AudioServer
+ { "SetGravityDistanceScale", "SetGravityPointDistanceScale" }, // Area2D
+ { "SetGravityVector", "SetGravityDirection" }, // Area2D
+ { "SetHDragEnabled", "SetDragHorizontalEnabled" }, // Camera2D
+ { "SetIconAlign", "SetIconAlignment" }, // Button
+ { "SetInteriorAmbient", "SetAmbientColor" }, // ReflectionProbe
+ { "SetInteriorAmbientEnergy", "SetAmbientColorEnergy" }, // ReflectionProbe
+ { "SetIsInitialized", "_IsInitialized" }, // XRInterface
+ { "SetIsPrimary", "SetPrimary" }, // XRInterface
+ { "SetIterationsPerSecond", "SetPhysicsTicksPerSecond" }, // Engine
+ { "SetLayerMaskBit", "SetLayerMaskValue" }, // VisualInstance3D
+ { "SetMarginsPreset", "SetOffsetsPreset" }, // Control
+ { "SetMaxAtlasSize", "SetMaxTextureSize" }, // LightmapGI
+ { "SetMetakey", "SetMetaPressed" }, // InputEventWithModifiers
+ { "SetMidHeight", "SetHeight" }, // CapsuleMesh
+ { "SetNetworkMaster", "SetMultiplayerAuthority" }, // Node
+ { "SetNetworkPeer", "SetMultiplayerPeer" }, // Multiplayer API
+ { "SetPhysicalScancode", "SetPhysicalKeycode" }, // InputEventKey
+ { "SetRefuseNewNetworkConnections", "SetRefuseNewConnections" }, // Multiplayer API
+ { "SetRegion", "SetRegionEnabled" }, // Sprite2D, Sprite broke AtlasTexture
+ { "SetRegionFilterClip", "SetRegionFilterClipEnabled" }, // Sprite2D
+ { "SetRotate", "SetRotates" }, // PathFollow2D
+ { "SetScancode", "SetKeycode" }, // InputEventKey
+ { "SetShift", "SetShiftPressed" }, // InputEventWithModifiers
+ { "SetSizeOverride", "SetSize2dOverride" }, // SubViewport broke ImageTexture
+ { "SetSizeOverrideStretch", "SetSize2dOverrideStretch" }, // SubViewport
+ { "SetSlipsOnSlope", "SetSlideOnSlope" }, // SeparationRayShape2D, SeparationRayShape3D
+ { "SetSortEnabled", "SetYSortEnabled" }, // Node2D
+ { "SetSpaceOverrideMode", "SetGravitySpaceOverrideMode" }, // Area2D
+ { "SetSpeed", "SetVelocity" }, // InputEventMouseMotion
+ { "SetSsaoEdgeSharpness", "SetSsaoSharpness" }, // Environment
+ { "SetSurfaceMaterial", "SetSurfaceOverrideMaterial" }, // MeshInstance3D broke ImporterMesh
+ { "SetTabAlign", "SetTabAlignment" }, //TabContainer
+ { "SetTangent", "SurfaceSetTangent" }, // ImmediateGeometry broke SurfaceTool
+ { "SetTextAlign", "SetTextAlignment" }, // Button
+ { "SetTimerProcessMode", "SetTimerProcessCallback" }, // Timer
+ { "SetTonemapAutoExposure", "SetTonemapAutoExposureEnabled" }, // Environment
+ { "SetTranslation", "SetPosition" }, // Node3D - this broke GLTFNode which is used rarely
+ { "SetUv2", "SurfaceSetUv2" }, // ImmediateMesh broke Surffacetool
+ { "SetVDragEnabled", "SetDragVerticalEnabled" }, // Camera2D
+ { "SetValign", "SetVerticalAlignment" }, // Label
+ { "SetWindowLayout", "_SetWindowLayout" }, // EditorPlugin
+ { "SetZfar", "SetFar" }, // Camera3D broke GLTFCamera
+ { "SetZnear", "SetNear" }, // Camera3D broke GLTFCamera
+ { "ShortcutMatch", "IsMatch" }, // InputEvent
+ { "SkeletonAllocate", "SkeletonAllocateData" }, // RenderingServer
+ { "SurfaceUpdateRegion", "SurfaceUpdateAttributeRegion" }, // ArrayMesh
+ { "TargetingMethod", "TweenMethod" }, // Tween
+ { "TargetingProperty", "TweenProperty" }, // Tween
+ { "TrackRemoveKeyAtPosition", "TrackRemoveKeyAtTime" }, // Animation
+ { "TriangulateDelaunay2d", "TriangulateDelaunay" }, // Geometry2D
+ { "UnbindChildNodeFromBone", "RemoveBoneChild" }, // Skeleton3D
+ { "Unselect", "Deselect" }, // ItemList
+ { "UnselectAll", "DeselectAll" }, // ItemList
+ { "UpdateConfigurationWarning", "UpdateConfigurationWarnings" }, // Node
+ { "UpdateGizmo", "UpdateGizmos" }, // Node3D
+ { "ViewportSetUseArvr", "ViewportSetUseXr" }, // RenderingServer
+ { "WarpMousePosition", "WarpMouse" }, // Input
+
+ // Builtin types
+ // { "Empty", "IsEmpty" }, // Array - Used as custom rule // Be careful, this will be used everywhere
+ { "Clamped", "Clamp" }, // Vector2 // Be careful, this will be used everywhere
+ { "GetRotationQuat", "GetRotationQuaternion" }, // Basis
+ { "GrowMargin", "GrowSide" }, // Rect2
+ { "Invert", "Reverse" }, // Array - TODO check // Be careful, this will be used everywhere
+ { "IsAbsPath", "IsAbsolutePath" }, // String
+ { "IsValidInteger", "IsValidInt" }, // String
+ { "LinearInterpolate", "Lerp" }, // Color
+ { "ToAscii", "ToAsciiBuffer" }, // String
+ { "ToUtf8", "ToUtf8Buffer" }, // String
+ { "ToWchar", "ToUtf32Buffer" }, // String // TODO - utf32 or utf16?
+
+ // Globals
+ { "RandRange", "RandfRange" },
+ { "Stepify", "Snapped" },
+
+ { nullptr, nullptr },
+};
+
+// Some needs to be disabled, because users can use this names as variables
+static const char *gdscript_properties_renames[][2] = {
+ // // { "d", "distance" }, //WorldMarginShape2D - TODO, looks that polish letters ą ę are treaten as space, not as letter, so `będą` are renamed to `będistanceą`
+ // // {"alt","alt_pressed"}, // This may broke a lot of comments and user variables
+ // // {"command","command_pressed"},// This may broke a lot of comments and user variables
+ // // {"control","ctrl_pressed"},// This may broke a lot of comments and user variables
+ // // {"extends","size"}, // BoxShape3D, LightmapGI broke ReflectionProbe
+ // // {"meta","meta_pressed"},// This may broke a lot of comments and user variables
+ // // {"pause_mode","process_mode"}, // Node - Cyclic rename, look for others
+ // // {"rotate","rotates"}, // PathFollow2D - probably function exists with same name
+ // // {"shift","shift_pressed"},// This may broke a lot of comments and user variables
+ // { "autowrap", "autowrap_mode" }, // Label
+ // { "cast_to", "target_position" }, // RayCast2D, RayCast3D
+ // { "doubleclick", "double_click" }, // InputEventMouseButton
+ // { "group", "button_group" }, // BaseButton
+ // { "process_mode", "process_callback" }, // AnimationTree, Camera2D
+ // { "scancode", "keycode" }, // InputEventKey
+ // { "toplevel", "top_level" }, // Node
+ // { "window_title", "title" }, // Window
+ // { "wrap_enabled", "wrap_mode" }, // TextEdit
+ // { "zfar", "far" }, // Camera3D
+ // { "znear", "near" }, // Camera3D
+ { "as_normalmap", "as_normal_map" }, // NoiseTexture
+ { "bbcode_text", "text" }, // RichTextLabel
+ { "caret_moving_by_right_click", "caret_move_on_right_click" }, // TextEdit
+ { "caret_position", "caret_column" }, // LineEdit
+ { "check_vadjust", "check_v_adjust" }, // Theme
+ { "close_h_ofs", "close_h_offset" }, // Theme
+ { "close_v_ofs", "close_v_offset" }, // Theme
+ { "commentfocus", "comment_focus" }, // Theme
+ { "drag_margin_bottom", "drag_bottom_margin" }, // Camera2D
+ { "drag_margin_h_enabled", "drag_horizontal_enabled" }, // Camera2D
+ { "drag_margin_left", "drag_left_margin" }, // Camera2D
+ { "drag_margin_right", "drag_right_margin" }, // Camera2D
+ { "drag_margin_top", "drag_top_margin" }, // Camera2D
+ { "drag_margin_v_enabled", "drag_vertical_enabled" }, // Camera2D
+ { "enabled_focus_mode", "focus_mode" }, // BaseButton - Removed
+ { "extra_spacing_bottom", "spacing_bottom" }, // Font
+ { "extra_spacing_top", "spacing_top" }, // Font
+ { "focus_neighbour_bottom", "focus_neighbor_bottom" }, // Control
+ { "focus_neighbour_left", "focus_neighbor_left" }, // Control
+ { "focus_neighbour_right", "focus_neighbor_right" }, // Control
+ { "focus_neighbour_top", "focus_neighbor_top" }, // Control
+ { "global_rate_scale", "playback_speed_scale" }, // AudioServer
+ { "gravity_distance_scale", "gravity_point_distance_scale" }, // Area2D
+ { "gravity_vec", "gravity_direction" }, // Area2D
+ { "hseparation", "h_separation" }, // Theme
+ { "iterations_per_second", "physics_ticks_per_second" }, // Engine
+ { "margin_bottom", "offset_bottom" }, // Control broke NinePatchRect, StyleBox
+ { "margin_left", "offset_left" }, // Control broke NinePatchRect, StyleBox
+ { "margin_right", "offset_right" }, // Control broke NinePatchRect, StyleBox
+ { "margin_top", "offset_top" }, // Control broke NinePatchRect, StyleBox
+ { "mid_height", "height" }, // CapsuleMesh
+ { "offset_h", "drag_horizontal_offset" }, // Camera2D
+ { "offset_v", "drag_vertical_offset" }, // Camera2D
+ { "ofs", "offset" }, // Theme
+ { "out_of_range_mode", "max_polyphony" }, // AudioStreamPlayer3D
+ { "pause_mode", "process_mode" }, // Node
+ { "physical_scancode", "physical_keycode" }, // InputEventKey
+ { "popup_exclusive", "exclusive" }, // Window
+ { "refuse_new_network_connections", "refuse_new_connections" }, // MultiplayerAPI
+ { "region_filter_clip", "region_filter_clip_enabled" }, // Sprite2D
+ { "selectedframe", "selected_frame" }, // Theme
+ { "size_override_stretch", "size_2d_override_stretch" }, // SubViewport
+ { "slips_on_slope", "slide_on_slope" }, // SeparationRayShape2D
+ { "ss_reflections_depth_tolerance", "ssr_depth_tolerance" }, // Environment
+ { "ss_reflections_enabled", "ssr_enabled" }, // Environment
+ { "ss_reflections_fade_in", "ssr_fade_in" }, // Environment
+ { "ss_reflections_fade_out", "ssr_fade_out" }, // Environment
+ { "ss_reflections_max_steps", "ssr_max_steps" }, // Environment
+ { "state_machine_selectedframe", "state_machine_selected_frame" }, // Theme
+ { "syntax_highlighting", "syntax_highlighter" }, // TextEdit
+ { "tab_align", "tab_alignment" }, // TabContainer
+ { "table_hseparation", "table_h_separation" }, // Theme
+ { "table_vseparation", "table_v_separation" }, // Theme
+ { "translation", "position" }, // Node3D - broke GLTFNode
+ { "vseparation", "v_separation" }, // Theme
+
+ { nullptr, nullptr },
+};
+
+// Some needs to be disabled, because users can use this names as variables
+static const char *csharp_properties_renames[][2] = {
+ // // { "D", "Distance" }, //WorldMarginShape2D - TODO, looks that polish letters ą ę are treaten as space, not as letter, so `będą` are renamed to `będistanceą`
+ // // {"Alt","AltPressed"}, // This may broke a lot of comments and user variables
+ // // {"Command","CommandPressed"},// This may broke a lot of comments and user variables
+ // // {"Control","CtrlPressed"},// This may broke a lot of comments and user variables
+ // // {"Extends","Size"}, // BoxShape3D, LightmapGI broke ReflectionProbe
+ // // {"Meta","MetaPressed"},// This may broke a lot of comments and user variables
+ // // {"PauseMode","ProcessMode"}, // Node - Cyclic rename, look for others
+ // // {"Rotate","Rotates"}, // PathFollow2D - probably function exists with same name
+ // // {"Shift","ShiftPressed"},// This may broke a lot of comments and user variables
+ // { "Autowrap", "AutowrapMode" }, // Label
+ // { "CastTo", "TargetPosition" }, // RayCast2D, RayCast3D
+ // { "Doubleclick", "DoubleClick" }, // InputEventMouseButton
+ // { "Group", "ButtonGroup" }, // BaseButton
+ // { "ProcessMode", "ProcessCallback" }, // AnimationTree, Camera2D
+ // { "Scancode", "Keycode" }, // InputEventKey
+ // { "Toplevel", "TopLevel" }, // Node
+ // { "WindowTitle", "Title" }, // Window
+ // { "WrapEnabled", "WrapMode" }, // TextEdit
+ // { "Zfar", "Far" }, // Camera3D
+ // { "Znear", "Near" }, // Camera3D
+ { "AsNormalmap", "AsNormalMap" }, // NoiseTexture
+ { "BbcodeText", "Text" }, // RichTextLabel
+ { "CaretMovingByRightClick", "CaretMoveOnRightClick" }, // TextEdit
+ { "CaretPosition", "CaretColumn" }, // LineEdit
+ { "CheckVadjust", "CheckVAdjust" }, // Theme
+ { "CloseHOfs", "CloseHOffset" }, // Theme
+ { "CloseVOfs", "CloseVOffset" }, // Theme
+ { "Commentfocus", "CommentFocus" }, // Theme
+ { "DragMarginBottom", "DragBottomMargin" }, // Camera2D
+ { "DragMarginHEnabled", "DragHorizontalEnabled" }, // Camera2D
+ { "DragMarginLeft", "DragLeftMargin" }, // Camera2D
+ { "DragMarginRight", "DragRightMargin" }, // Camera2D
+ { "DragMarginTop", "DragTopMargin" }, // Camera2D
+ { "DragMarginVEnabled", "DragVerticalEnabled" }, // Camera2D
+ { "EnabledFocusMode", "FocusMode" }, // BaseButton - Removed
+ { "ExtraSpacingBottom", "SpacingBottom" }, // Font
+ { "ExtraSpacingTop", "SpacingTop" }, // Font
+ { "FocusNeighbourBottom", "FocusNeighborBottom" }, // Control
+ { "FocusNeighbourLeft", "FocusNeighborLeft" }, // Control
+ { "FocusNeighbourRight", "FocusNeighborRight" }, // Control
+ { "FocusNeighbourTop", "FocusNeighborTop" }, // Control
+ { "GlobalRateScale", "PlaybackSpeedScale" }, // AudioServer
+ { "GravityDistanceScale", "GravityPointDistanceScale" }, // Area2D
+ { "GravityVec", "GravityDirection" }, // Area2D
+ { "Hseparation", "HSeparation" }, // Theme
+ { "IterationsPerSecond", "PhysicsTicksPerSecond" }, // Engine
+ { "MarginBottom", "OffsetBottom" }, // Control broke NinePatchRect, StyleBox
+ { "MarginLeft", "OffsetLeft" }, // Control broke NinePatchRect, StyleBox
+ { "MarginRight", "OffsetRight" }, // Control broke NinePatchRect, StyleBox
+ { "MarginTop", "OffsetTop" }, // Control broke NinePatchRect, StyleBox
+ { "MidHeight", "Height" }, // CapsuleMesh
+ { "OffsetH", "DragHorizontalOffset" }, // Camera2D
+ { "OffsetV", "DragVerticalOffset" }, // Camera2D
+ { "Ofs", "Offset" }, // Theme
+ { "OutOfRangeMode", "MaxPolyphony" }, // AudioStreamPlayer3D
+ { "PauseMode", "ProcessMode" }, // Node
+ { "PhysicalScancode", "PhysicalKeycode" }, // InputEventKey
+ { "PopupExclusive", "Exclusive" }, // Window
+ { "RefuseNewNetworkConnections", "RefuseNewConnections" }, // MultiplayerAPI
+ { "RegionFilterClip", "RegionFilterClipEnabled" }, // Sprite2D
+ { "Selectedframe", "SelectedFrame" }, // Theme
+ { "SizeOverrideStretch", "Size2dOverrideStretch" }, // SubViewport
+ { "SlipsOnSlope", "SlideOnSlope" }, // SeparationRayShape2D
+ { "SsReflectionsDepthTolerance", "SsrDepthTolerance" }, // Environment
+ { "SsReflectionsEnabled", "SsrEnabled" }, // Environment
+ { "SsReflectionsFadeIn", "SsrFadeIn" }, // Environment
+ { "SsReflectionsFadeOut", "SsrFadeOut" }, // Environment
+ { "SsReflectionsMaxSteps", "SsrMaxSteps" }, // Environment
+ { "StateMachineSelectedframe", "StateMachineSelectedFrame" }, // Theme
+ { "SyntaxHighlighting", "SyntaxHighlighter" }, // TextEdit
+ { "TabAlign", "TabAlignment" }, // TabContainer
+ { "TableHseparation", "TableHSeparation" }, // Theme
+ { "TableVseparation", "TableVSeparation" }, // Theme
+ { "Translation", "Position" }, // Node3D - broke GLTFNode
+ { "Vseparation", "VSeparation" }, // Theme
+
+ { nullptr, nullptr },
+};
+
+static const char *gdscript_signals_renames[][2] = {
+ // {"instantiate","instance"}, // FileSystemDock
+ // { "hide", "hidden" }, // CanvasItem - function with same name exists
+ // { "tween_all_completed","loop_finished"}, // Tween - TODO, not sure
+ // {"changed","settings_changed"}, // EditorSettings
+ { "about_to_show", "about_to_popup" }, // Popup
+ { "button_release", "button_released" }, // XRController3D
+ { "network_peer_connected", "peer_connected" }, // MultiplayerAPI
+ { "network_peer_disconnected", "peer_disconnected" }, // MultiplayerAPI
+ { "network_peer_packet", "peer_packet" }, // MultiplayerAPI
+ { "node_unselected", "node_deselected" }, // GraphEdit
+ { "offset_changed", "position_offset_changed" }, // GraphNode
+ { "settings_changed", "changed" }, // TileMap broke EditorSettings
+ { "skeleton_updated", "pose_updated" }, //
+ { "tab_close", "tab_closed" }, // TextEdit
+ { "tab_hover", "tab_hovered" }, // TextEdit
+ { "text_entered", "text_submitted" }, // LineEdit
+ { "tween_completed", "finished" }, // Tween
+ { "tween_step", "step_finished" }, // Tween
+
+ { nullptr, nullptr },
+};
+
+static const char *csharp_signals_renames[][2] = {
+ // {"Instantiate","Instance"}, // FileSystemDock
+ // { "Hide", "Hidden" }, // CanvasItem - function with same name exists
+ // { "TweenAllCompleted","LoopFinished"}, // Tween - TODO, not sure
+ // {"Changed","SettingsChanged"}, // EditorSettings
+ { "AboutToShow", "AboutToPopup" }, // Popup
+ { "ButtonRelease", "ButtonReleased" }, // XRController3D
+ { "NetworkPeerConnected", "PeerConnected" }, // MultiplayerAPI
+ { "NetworkPeerDisconnected", "PeerDisconnected" }, // MultiplayerAPI
+ { "NetworkPeerPacket", "PeerPacket" }, // MultiplayerAPI
+ { "NodeUnselected", "NodeDeselected" }, // GraphEdit
+ { "OffsetChanged", "PositionOffsetChanged" }, // GraphNode
+ { "SettingsChanged", "Changed" }, // TileMap broke EditorSettings
+ { "SkeletonUpdated", "PoseUpdated" }, //
+ { "TabClose", "TabClosed" }, // TextEdit
+ { "TabHover", "TabHovered" }, // TextEdit
+ { "TextEntered", "TextSubmitted" }, // LineEdit
+ { "TweenCompleted", "Finished" }, // Tween
+ { "TweenStep", "StepFinished" }, // Tween
+
+ { nullptr, nullptr },
+
+};
+
+static const char *project_settings_renames[][2] = {
+ { "audio/channel_disable_threshold_db", "audio/buses/channel_disable_threshold_db" },
+ { "audio/channel_disable_time", "audio/buses/channel_disable_time" },
+ { "audio/default_bus_layout", "audio/buses/default_bus_layout" },
+ { "audio/driver", "audio/driver/driver" },
+ { "audio/enable_audio_input", "audio/driver/enable_input" },
+ { "audio/mix_rate", "audio/driver/mix_rate" },
+ { "audio/output_latency", "audio/driver/output_latency" },
+ { "audio/output_latency.web", "audio/driver/output_latency.web" },
+ { "audio/video_delay_compensation_ms", "audio/video/video_delay_compensation_ms" },
+ { "display/window/vsync/use_vsync", "display/window/vsync/vsync_mode" },
+ { "editor/main_run_args", "editor/run/main_run_args" },
+ { "gui/common/swap_ok_cancel", "gui/common/swap_cancel_ok" },
+ { "network/limits/debugger_stdout/max_chars_per_second", "network/limits/debugger/max_chars_per_second" },
+ { "network/limits/debugger_stdout/max_errors_per_second", "network/limits/debugger/max_errors_per_second" },
+ { "network/limits/debugger_stdout/max_messages_per_frame", "network/limits/debugger/max_queued_messages" },
+ { "network/limits/debugger_stdout/max_warnings_per_second", "network/limits/debugger/max_warnings_per_second" },
+ { "network/ssl/certificates", "network/ssl/certificate_bundle_override" },
+ { "physics/2d/thread_model", "physics/2d/run_on_thread" }, // TODO not sure
+ { "rendering/environment/default_clear_color", "rendering/environment/defaults/default_clear_color" },
+ { "rendering/environment/default_environment", "rendering/environment/defaults/default_environment" },
+ { "rendering/quality/depth_prepass/disable_for_vendors", "rendering/driver/depth_prepass/disable_for_vendors" },
+ { "rendering/quality/depth_prepass/enable", "rendering/driver/depth_prepass/enable" },
+ { "rendering/quality/shading/force_blinn_over_ggx", "rendering/shading/overrides/force_blinn_over_ggx" },
+ { "rendering/quality/shading/force_blinn_over_ggx.mobile", "rendering/shading/overrides/force_blinn_over_ggx.mobile" },
+ { "rendering/quality/shading/force_lambert_over_burley", "rendering/shading/overrides/force_lambert_over_burley" },
+ { "rendering/quality/shading/force_lambert_over_burley.mobile", "rendering/shading/overrides/force_lambert_over_burley.mobile" },
+ { "rendering/quality/shading/force_vertex_shading", "rendering/shading/overrides/force_vertex_shading" },
+ { "rendering/quality/shading/force_vertex_shading.mobile", "rendering/shading/overrides/force_vertex_shading.mobile" },
+ { "rendering/quality/shadow_atlas/quadrant_0_subdiv", "rendering/shadows/shadow_atlas/quadrant_0_subdiv" },
+ { "rendering/quality/shadow_atlas/quadrant_1_subdiv", "rendering/shadows/shadow_atlas/quadrant_1_subdiv" },
+ { "rendering/quality/shadow_atlas/quadrant_2_subdiv", "rendering/shadows/shadow_atlas/quadrant_2_subdiv" },
+ { "rendering/quality/shadow_atlas/quadrant_3_subdiv", "rendering/shadows/shadow_atlas/quadrant_3_subdiv" },
+ { "rendering/quality/shadow_atlas/size", "rendering/shadows/shadow_atlas/size" },
+ { "rendering/quality/shadow_atlas/size.mobile", "rendering/shadows/shadow_atlas/size.mobile" },
+ { "rendering/vram_compression/import_bptc", "rendering/textures/vram_compression/import_bptc" },
+ { "rendering/vram_compression/import_etc", "rendering/textures/vram_compression/import_etc" },
+ { "rendering/vram_compression/import_etc2", "rendering/textures/vram_compression/import_etc2" },
+ { "rendering/vram_compression/import_pvrtc", "rendering/textures/vram_compression/import_pvrtc" },
+ { "rendering/vram_compression/import_s3tc", "rendering/textures/vram_compression/import_s3tc" },
+
+ { nullptr, nullptr },
+};
+
+static const char *builtin_types_renames[][2] = {
+ { "PoolByteArray", "PackedByteArray" },
+ { "PoolColorArray", "PackedColorArray" },
+ { "PoolIntArray", "PackedInt32Array" },
+ { "PoolRealArray", "PackedFloat32Array" },
+ { "PoolStringArray", "PackedStringArray" },
+ { "PoolVector2Array", "PackedVector2Array" },
+ { "PoolVector3Array", "PackedVector3Array" },
+ { "Quat", "Quaternion" },
+ { "Transform", "Transform3D" },
+
+ { nullptr, nullptr },
+};
+
+static const char *shaders_renames[][2] = {
+ { "ALPHA_SCISSOR", "ALPHA_SCISSOR_THRESHOLD" },
+ { "NORMALMAP", "NORMAL_MAP" },
+ { "NORMALMAP_DEPTH", "NORMAL_MAP_DEPTH" },
+ { "TRANSMISSION", "SSS_TRANSMITTANCE_COLOR" },
+ { nullptr, nullptr },
+};
+
+static const char *class_renames[][2] = {
+ // { "BulletPhysicsDirectBodyState", "BulletPhysicsDirectBodyState3D" }, // Class is not visible in ClassDB
+ // { "BulletPhysicsServer", "BulletPhysicsServer3D" }, // Class is not visible in ClassDB
+ // { "GDScriptFunctionState", "Node3D" }, // TODO - not sure to which should be changed
+ // { "GDScriptNativeClass", "Node3D" }, // TODO - not sure to which should be changed
+ // { "InputDefault",""}, // TODO ?
+ // { "Physics2DDirectBodyStateSW", "GodotPhysicsDirectBodyState2D" }, // Class is not visible in ClassDB
+ // { "Physics2DShapeQueryResult", "PhysicsShapeQueryResult2D" }, // Class is not visible in ClassDB
+ // { "PhysicsShapeQueryResult", "PhysicsShapeQueryResult3D" }, // Class is not visible in ClassDB
+ // { "NativeScript","NativeExtension"}, ??
+ { "AStar", "AStar3D" },
+ { "ARVRAnchor", "XRAnchor3D" },
+ { "ARVRCamera", "XRCamera3D" },
+ { "ARVRController", "XRController3D" },
+ { "ARVRInterface", "XRInterface" },
+ { "ARVRInterfaceGDNative", "Node3D" },
+ { "ARVROrigin", "XROrigin3D" },
+ { "ARVRPositionalTracker", "XRPositionalTracker" },
+ { "ARVRServer", "XRServer" },
+ { "AnimatedSprite", "AnimatedSprite2D" },
+ { "AnimationTreePlayer", "AnimationTree" },
+ { "Area", "Area3D" }, // Be careful, this will be used everywhere
+ { "AudioStreamRandomPitch", "AudioStreamRandomizer" },
+ { "BakedLightmap", "LightmapGI" },
+ { "BakedLightmapData", "LightmapGIData" },
+ { "BitmapFont", "Font" },
+ { "BoneAttachment", "BoneAttachment3D" },
+ { "BoxShape", "BoxShape3D" },
+ { "CPUParticles", "CPUParticles3D" },
+ { "CSGBox", "CSGBox3D" },
+ { "CSGCombiner", "CSGCombiner3D" },
+ { "CSGCylinder", "CSGCylinder3D" },
+ { "CSGMesh", "CSGMesh3D" },
+ { "CSGPolygon", "CSGPolygon3D" },
+ { "CSGPrimitive", "CSGPrimitive3D" },
+ { "CSGShape", "CSGShape3D" },
+ { "CSGSphere", "CSGSphere3D" },
+ { "CSGTorus", "CSGTorus3D" },
+ { "Camera", "Camera3D" }, // Be careful, this will be used everywhere
+ { "CapsuleShape", "CapsuleShape3D" },
+ { "ClippedCamera", "Camera3D" },
+ { "CollisionObject", "CollisionObject3D" },
+ { "CollisionPolygon", "CollisionPolygon3D" },
+ { "CollisionShape", "CollisionShape3D" },
+ { "ConcavePolygonShape", "ConcavePolygonShape3D" },
+ { "ConeTwistJoint", "ConeTwistJoint3D" },
+ { "ConvexPolygonShape", "ConvexPolygonShape3D" },
+ { "CubeMap", "Cubemap" },
+ { "CubeMesh", "BoxMesh" },
+ { "CylinderShape", "CylinderShape3D" },
+ { "DirectionalLight", "DirectionalLight3D" },
+ { "DynamicFont", "Font" },
+ { "DynamicFontData", "FontData" },
+ { "EditorNavigationMeshGenerator", "NavigationMeshGenerator" },
+ { "EditorSceneImporter", "EditorSceneFormatImporter" },
+ { "EditorSceneImporterFBX", "EditorSceneFormatImporterFBX" },
+ { "EditorSceneImporterGLTF", "EditorSceneFormatImporterGLTF" },
+ { "EditorSpatialGizmo", "EditorNode3DGizmo" },
+ { "EditorSpatialGizmoPlugin", "EditorNode3DGizmoPlugin" },
+ { "ExternalTexture", "ImageTexture" },
+ { "FuncRef", "Callable" },
+ { "GIProbe", "VoxelGI" },
+ { "GIProbeData", "VoxelGIData" },
+ { "Generic6DOFJoint", "Generic6DOFJoint3D" },
+ { "Geometry", "Geometry2D" }, // Geometry class is split between Geometry2D and Geometry3D so we need to choose one
+ { "GeometryInstance", "GeometryInstance3D" },
+ { "GradientTexture", "GradientTexture2D" },
+ { "HeightMapShape", "HeightMapShape3D" },
+ { "HingeJoint", "HingeJoint3D" },
+ { "IP_Unix", "IPUnix" },
+ { "ImmediateGeometry", "ImmediateMesh" },
+ { "ImmediateGeometry3D", "ImmediateMesh" },
+ { "InterpolatedCamera", "Camera3D" },
+ { "InterpolatedCamera3D", "Camera3D" },
+ { "JSONParseResult", "JSON" },
+ { "Joint", "Joint3D" },
+ { "KinematicBody", "CharacterBody3D" },
+ { "KinematicBody2D", "CharacterBody2D" },
+ { "KinematicCollision", "KinematicCollision3D" },
+ { "LargeTexture", "ImageTexture" },
+ { "Light", "Light3D" },
+ { "Light2D", "PointLight2D" },
+ { "LineShape2D", "WorldBoundaryShape2D" },
+ { "Listener", "AudioListener3D" },
+ { "Listener2D", "AudioListener2D" },
+ { "MeshInstance", "MeshInstance3D" },
+ { "MultiMeshInstance", "MultiMeshInstance3D" },
+ { "MultiplayerPeerGDNative", "MultiplayerPeerExtension" },
+ { "Navigation", "Node3D" },
+ { "Navigation2D", "Node2D" },
+ { "Navigation2DServer", "NavigationServer2D" },
+ { "Navigation3D", "Node3D" },
+ { "NavigationAgent", "NavigationAgent3D" },
+ { "NavigationMeshInstance", "NavigationRegion3D" },
+ { "NavigationObstacle", "NavigationObstacle3D" },
+ { "NavigationPolygonInstance", "NavigationRegion2D" },
+ { "NavigationRegion", "NavigationRegion3D" },
+ { "NavigationServer", "NavigationServer3D" },
+ { "NetworkedMultiplayerENet", "ENetMultiplayerPeer" },
+ { "NetworkedMultiplayerPeer", "MultiplayerPeer" },
+ { "Occluder", "OccluderInstance3D" },
+ { "OmniLight", "OmniLight3D" },
+ { "PHashTranslation", "OptimizedTranslation" },
+ { "PacketPeerGDNative", "PacketPeerExtension" },
+ { "PanoramaSky", "Sky" },
+ { "Particles", "GPUParticles3D" }, // Be careful, this will be used everywhere
+ { "Particles2D", "GPUParticles2D" },
+ { "Path", "Path3D" }, // Be careful, this will be used everywhere
+ { "PathFollow", "PathFollow3D" },
+ { "PhysicalBone", "PhysicalBone3D" },
+ { "Physics2DDirectBodyState", "PhysicsDirectBodyState2D" },
+ { "Physics2DDirectSpaceState", "PhysicsDirectSpaceState2D" },
+ { "Physics2DServer", "PhysicsServer2D" },
+ { "Physics2DServerSW", "GodotPhysicsServer2D" },
+ { "Physics2DShapeQueryParameters", "PhysicsShapeQueryParameters2D" },
+ { "Physics2DTestMotionResult", "PhysicsTestMotionResult2D" },
+ { "PhysicsBody", "PhysicsBody3D" },
+ { "PhysicsDirectBodyState", "PhysicsDirectBodyState3D" },
+ { "PhysicsDirectSpaceState", "PhysicsDirectSpaceState3D" },
+ { "PhysicsServer", "PhysicsServer3D" },
+ { "PhysicsShapeQueryParameters", "PhysicsShapeQueryParameters3D" },
+ { "PhysicsTestMotionResult", "PhysicsTestMotionResult3D" },
+ { "PinJoint", "PinJoint3D" },
+ { "PlaneShape", "WorldBoundaryShape3D" },
+ { "PopupDialog", "Popup" },
+ { "ProceduralSky", "Sky" },
+ { "RayCast", "RayCast3D" },
+ { "RayShape", "SeparationRayShape3D" },
+ { "RayShape2D", "SeparationRayShape2D" },
+ { "Reference", "RefCounted" }, // Be careful, this will be used everywhere
+ { "RemoteTransform", "RemoteTransform3D" },
+ { "ResourceInteractiveLoader", "ResourceLoader" },
+ { "RigidBody", "RigidDynamicBody3D" },
+ { "RigidBody2D", "RigidDynamicBody2D" },
+ { "Shape", "Shape3D" }, // Be careful, this will be used everywhere
+ { "ShortCut", "Shortcut" },
+ { "Skeleton", "Skeleton3D" },
+ { "SkeletonIK", "SkeletonIK3D" },
+ { "SliderJoint", "SliderJoint3D" },
+ { "SoftBody", "SoftDynamicBody3D" },
+ { "Spatial", "Node3D" },
+ { "SpatialGizmo", "Node3DGizmo" },
+ { "SpatialMaterial", "StandardMaterial3D" },
+ { "SpatialVelocityTracker", "VelocityTracker3D" },
+ { "SphereShape", "SphereShape3D" },
+ { "SpotLight", "SpotLight3D" },
+ { "SpringArm", "SpringArm3D" },
+ { "Sprite", "Sprite2D" },
+ { "StaticBody", "StaticBody3D" },
+ { "StreamCubemap", "CompressedCubemap" },
+ { "StreamCubemapArray", "CompressedCubemapArray" },
+ { "StreamPeerGDNative", "StreamPeerExtension" },
+ { "StreamTexture", "CompressedTexture2D" },
+ { "StreamTexture2D", "CompressedTexture2D" },
+ { "StreamTexture2DArray", "CompressedTexture2DArray" },
+ { "StreamTextureLayered", "CompressedTextureLayered" },
+ { "TCP_Server", "TCPServer" },
+ { "Tabs", "TabBar" }, // Be careful, this will be used everywhere
+ { "TextFile", "Node3D" },
+ { "Texture", "Texture2D" }, // May broke TextureRect
+ { "TextureArray", "Texture2DArray" },
+ { "TextureProgress", "TextureProgressBar" },
+ { "ToolButton", "Button" },
+ { "VehicleBody", "VehicleBody3D" },
+ { "VehicleWheel", "VehicleWheel3D" },
+ { "VideoPlayer", "VideoStreamPlayer" },
+ { "Viewport", "SubViewport" },
+ { "ViewportContainer", "SubViewportContainer" },
+ { "VisibilityEnabler", "VisibleOnScreenEnabler3D" },
+ { "VisibilityEnabler2D", "VisibleOnScreenEnabler2D" },
+ { "VisibilityNotifier", "VisibleOnScreenNotifier3D" },
+ { "VisibilityNotifier2D", "VisibleOnScreenNotifier2D" },
+ { "VisibilityNotifier3D", "VisibleOnScreenNotifier3D" },
+ { "VisualInstance", "VisualInstance3D" },
+ { "VisualServer", "RenderingServer" },
+ { "VisualShaderNodeCubeMap", "VisualShaderNodeCubemap" },
+ { "VisualShaderNodeCubeMapUniform", "VisualShaderNodeCubemapUniform" },
+ { "VisualShaderNodeScalarClamp", "VisualShaderNodeClamp" },
+ { "VisualShaderNodeScalarConstant", "VisualShaderNodeFloatConstant" },
+ { "VisualShaderNodeScalarFunc", "VisualShaderNodeFloatFunc" },
+ { "VisualShaderNodeScalarInterp", "VisualShaderNodeMix" },
+ { "VisualShaderNodeScalarOp", "VisualShaderNodeFloatOp" },
+ { "VisualShaderNodeScalarSmoothStep", "VisualShaderNodeSmoothStep" },
+ { "VisualShaderNodeScalarSwitch", "VisualShaderNodeSwitch" },
+ { "VisualShaderNodeScalarTransformMult", "VisualShaderNodeTransformOp" },
+ { "VisualShaderNodeScalarUniform", "VisualShaderNodeFloatUniform" },
+ { "VisualShaderNodeTransformMult", "VisualShaderNode" },
+ { "VisualShaderNodeVectorClamp", "VisualShaderNodeClamp" },
+ { "VisualShaderNodeVectorInterp", "VisualShaderNodeMix" },
+ { "VisualShaderNodeVectorScalarMix", "VisualShaderNodeMix" },
+ { "VisualShaderNodeVectorScalarSmoothStep", "VisualShaderNodeSmoothStep" },
+ { "VisualShaderNodeVectorScalarStep", "VisualShaderNodeStep" },
+ { "VisualShaderNodeVectorSmoothStep", "VisualShaderNodeSmoothStep" },
+ { "WebRTCDataChannelGDNative", "WebRTCDataChannelExtension" },
+ { "WebRTCMultiplayer", "WebRTCMultiplayerPeer" },
+ { "WebRTCPeerConnectionGDNative", "WebRTCPeerConnectionExtension" },
+ { "WindowDialog", "Window" },
+ { "World", "World3D" }, // Be careful, this will be used everywhere
+ { "XRAnchor", "XRAnchor3D" },
+ { "XRController", "XRController3D" },
+ { "XROrigin", "XROrigin3D" },
+ { "YSort", "Node2D" },
+
+ { "CullInstance", "Node3D" }, // Probably this type needs to be added to Godot 4.0, since it is for now only available only in Godot 3.x
+ { "RoomGroup", "Node3D" }, // Probably this type needs to be added to Godot 4.0, since it is for now only available only in Godot 3.x
+ { "Room", "Node3D" }, // Probably this type needs to be added to Godot 4.0, since it is for now only available only in Godot 3.x
+ { "RoomManager", "Node3D" }, // Probably this type needs to be added to Godot 4.0, since it is for now only available only in Godot 3.x
+ { "Portal", "Node3D" }, // Probably this type needs to be added to Godot 4.0, since it is for now only available only in Godot 3.x
+
+ { nullptr, nullptr },
+};
+
+// TODO - this colors needs to be validated(not all are valid)
+static const char *colors_renames[][2] = {
+ { "aliceblue", "ALICE_BLUE" },
+ { "antiquewhite", "ANTIQUE_WHITE" },
+ { "aqua", "AQUA" },
+ { "aquamarine", "AQUAMARINE" },
+ { "azure", "AZURE" },
+ { "beige", "BEIGE" },
+ { "bisque", "BISQUE" },
+ { "black", "BLACK" },
+ { "blanchedalmond", "BLANCHED_ALMOND" },
+ { "blue", "BLUE" },
+ { "blueviolet", "BLUE_VIOLET" },
+ { "brown", "BROWN" },
+ { "burlywood", "BURLYWOOD" },
+ { "cadetblue", "CADET_BLUE" },
+ { "chartreuse", "CHARTREUSE" },
+ { "chocolate", "CHOCOLATE" },
+ { "coral", "CORAL" },
+ { "cornflowerblue", "CORNFLOWER_BLUE" },
+ { "cornsilk", "CORNSILK" },
+ { "crimson", "CRIMSON" },
+ { "cyan", "CYAN" },
+ { "darkblue", "DARK_BLUE" },
+ { "darkcyan", "DARK_CYAN" },
+ { "darkgoldenrod", "DARK_GOLDENROD" },
+ { "darkgray", "DARK_GRAY" },
+ { "darkgreen", "DARK_GREEN" },
+ { "darkkhaki", "DARK_KHAKI" },
+ { "darkmagenta", "DARK_MAGENTA" },
+ { "darkolivegreen", "DARK_OLIVE_GREEN" },
+ { "darkorange", "DARK_ORANGE" },
+ { "darkorchid", "DARK_ORCHID" },
+ { "darkred", "DARK_RED" },
+ { "darksalmon", "DARK_SALMON" },
+ { "darkseagreen", "DARK_SEA_GREEN" },
+ { "darkslateblue", "DARK_SLATE_BLUE" },
+ { "darkslategray", "DARK_SLATE_GRAY" },
+ { "darkturquoise", "DARK_TURQUOISE" },
+ { "darkviolet", "DARK_VIOLET" },
+ { "deeppink", "DEEP_PINK" },
+ { "deepskyblue", "DEEP_SKY_BLUE" },
+ { "dimgray", "DIM_GRAY" },
+ { "dodgerblue", "DODGER_BLUE" },
+ { "firebrick", "FIREBRICK" },
+ { "floralwhite", "FLORAL_WHITE" },
+ { "forestgreen", "FOREST_GREEN" },
+ { "fuchsia", "FUCHSIA" },
+ { "gainsboro", "GAINSBORO" },
+ { "ghostwhite", "GHOST_WHITE" },
+ { "gold", "GOLD" },
+ { "goldenrod", "GOLDENROD" },
+ { "gray", "GRAY" },
+ { "green", "GREEN" },
+ { "greenyellow", "GREEN_YELLOW" },
+ { "honeydew", "HONEYDEW" },
+ { "hotpink", "HOT_PINK" },
+ { "indianred", "INDIAN_RED" },
+ { "indigo", "INDIGO" },
+ { "ivory", "IVORY" },
+ { "khaki", "KHAKI" },
+ { "lavender", "LAVENDER" },
+ { "lavenderblush", "LAVENDER_BLUSH" },
+ { "lawngreen", "LAWN_GREEN" },
+ { "lemonchiffon", "LEMON_CHIFFON" },
+ { "lightblue", "LIGHT_BLUE" },
+ { "lightcoral", "LIGHT_CORAL" },
+ { "lightcyan", "LIGHT_CYAN" },
+ { "lightgoldenrod", "LIGHT_GOLDENROD" },
+ { "lightgray", "LIGHT_GRAY" },
+ { "lightgreen", "LIGHT_GREEN" },
+ { "lightpink", "LIGHT_PINK" },
+ { "lightsalmon", "LIGHT_SALMON" },
+ { "lightseagreen", "LIGHT_SEA_GREEN" },
+ { "lightskyblue", "LIGHT_SKY_BLUE" },
+ { "lightslategray", "LIGHT_SLATE_GRAY" },
+ { "lightsteelblue", "LIGHT_STEEL_BLUE" },
+ { "lightyellow", "LIGHT_YELLOW" },
+ { "lime", "LIME" },
+ { "limegreen", "LIME_GREEN" },
+ { "linen", "LINEN" },
+ { "magenta", "MAGENTA" },
+ { "maroon", "MAROON" },
+ { "mediumaquamarine", "MEDIUM_AQUAMARINE" },
+ { "mediumblue", "MEDIUM_BLUE" },
+ { "mediumorchid", "MEDIUM_ORCHID" },
+ { "mediumpurple", "MEDIUM_PURPLE" },
+ { "mediumseagreen", "MEDIUM_SEA_GREEN" },
+ { "mediumslateblue", "MEDIUM_SLATE_BLUE" },
+ { "mediumspringgreen", "MEDIUM_SPRING_GREEN" },
+ { "mediumturquoise", "MEDIUM_TURQUOISE" },
+ { "mediumvioletred", "MEDIUM_VIOLET_RED" },
+ { "midnightblue", "MIDNIGHT_BLUE" },
+ { "mintcream", "MINT_CREAM" },
+ { "mistyrose", "MISTY_ROSE" },
+ { "moccasin", "MOCCASIN" },
+ { "navajowhite", "NAVAJO_WHITE" },
+ { "navyblue", "NAVY_BLUE" },
+ { "oldlace", "OLD_LACE" },
+ { "olive", "OLIVE" },
+ { "olivedrab", "OLIVE_DRAB" },
+ { "orange", "ORANGE" },
+ { "orangered", "ORANGE_RED" },
+ { "orchid", "ORCHID" },
+ { "palegoldenrod", "PALE_GOLDENROD" },
+ { "palegreen", "PALE_GREEN" },
+ { "paleturquoise", "PALE_TURQUOISE" },
+ { "palevioletred", "PALE_VIOLET_RED" },
+ { "papayawhip", "PAPAYA_WHIP" },
+ { "peachpuff", "PEACH_PUFF" },
+ { "peru", "PERU" },
+ { "pink", "PINK" },
+ { "plum", "PLUM" },
+ { "powderblue", "POWDER_BLUE" },
+ { "purple", "PURPLE" },
+ { "rebeccapurple", "REBECCA_PURPLE" },
+ { "red", "RED" },
+ { "rosybrown", "ROSY_BROWN" },
+ { "royalblue", "ROYAL_BLUE" },
+ { "saddlebrown", "SADDLE_BROWN" },
+ { "salmon", "SALMON" },
+ { "sandybrown", "SANDY_BROWN" },
+ { "seagreen", "SEA_GREEN" },
+ { "seashell", "SEASHELL" },
+ { "sienna", "SIENNA" },
+ { "silver", "SILVER" },
+ { "skyblue", "SKY_BLUE" },
+ { "slateblue", "SLATE_BLUE" },
+ { "slategray", "SLATE_GRAY" },
+ { "snow", "SNOW" },
+ { "springgreen", "SPRING_GREEN" },
+ { "steelblue", "STEEL_BLUE" },
+ { "tan", "TAN" },
+ { "teal", "TEAL" },
+ { "thistle", "THISTLE" },
+ { "tomato", "TOMATO" },
+ { "transparent", "TRANSPARENT" },
+ { "turquoise", "TURQUOISE" },
+ { "violet", "VIOLET" },
+ { "webgray", "WEB_GRAY" },
+ { "webgreen", "WEB_GREEN" },
+ { "webmaroon", "WEB_MAROON" },
+ { "webpurple", "WEB_PURPLE" },
+ { "wheat", "WHEAT" },
+ { "white", "WHITE" },
+ { "whitesmoke", "WHITE_SMOKE" },
+ { "yellow", "YELLOW" },
+ { "yellowgreen", "YELLOW_GREEN" },
+
+ { nullptr, nullptr },
+};
+
+// Function responsible for converting project
+int ProjectConverter3To4::convert() {
+ print_line("Starting conversion.");
+
+ ERR_FAIL_COND_V_MSG(!test_array_names(), ERROR_CODE, "Cannot start converting due to problems with data in arrays.");
+ ERR_FAIL_COND_V_MSG(!test_conversion(), ERROR_CODE, "Cannot start converting due to problems with converting arrays.");
+
+ // Checking if folder contains valid Godot 3 project.
+ // Project cannot be converted 2 times
+ {
+ String conventer_text = "; Project was converted by built-in tool to Godot 4.0";
+
+ ERR_FAIL_COND_V_MSG(!FileAccess::exists("project.godot"), ERROR_CODE, "Current directory doesn't contains any Godot 3 project");
+
+ Error err = OK;
+ String project_godot_content = FileAccess::get_file_as_string("project.godot", &err);
+
+ ERR_FAIL_COND_V_MSG(err != OK, ERROR_CODE, "Failed to read content of \"project.godot\" file.");
+ ERR_FAIL_COND_V_MSG(project_godot_content.find(conventer_text) != -1, ERROR_CODE, "Project already was converted with this tool.");
+
+ Ref<FileAccess> file = FileAccess::open("project.godot", FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(file.is_null(), ERROR_CODE, "Failed to open project.godot file.");
+
+ file->store_string(conventer_text + "\n" + project_godot_content);
+ }
+
+ Vector<String> collected_files = check_for_files();
+
+ uint32_t converted_files = 0;
+
+ // Check file by file
+ for (int i = 0; i < collected_files.size(); i++) {
+ String file_name = collected_files[i];
+ Error err = OK;
+ String file_content = FileAccess::get_file_as_string(file_name, &err);
+ ERR_CONTINUE_MSG(err != OK, "Failed to read content of \"" + file_name + "\".");
+ uint64_t hash_before = file_content.hash64();
+ uint64_t file_size = file_content.size();
+ print_line("Trying to convert\t" + itos(i + 1) + "/" + itos(collected_files.size()) + " file - \"" + file_name.trim_prefix("res://") + "\" with size - " + itos(file_size / 1024) + " KB");
+
+ Vector<String> reason;
+ bool is_ignored = false;
+ uint64_t start_time = Time::get_singleton()->get_ticks_msec();
+
+ if (file_name.ends_with(".shader")) {
+ DirAccess::remove_file_or_error(file_name);
+ file_name = file_name.replace(".shader", ".gdshader");
+ }
+
+ if (file_size < CONVERSION_MAX_FILE_SIZE) {
+ // TSCN must be the same work exactly same as .gd file because it may contains builtin script
+ if (file_name.ends_with(".gd")) {
+ rename_classes(file_content); // Using only specialized function
+
+ rename_common(enum_renames, file_content);
+ rename_enums(file_content); // Require to additional rename
+
+ rename_common(gdscript_function_renames, file_content);
+ rename_gdscript_functions(file_content); // Require to additional rename
+
+ rename_common(project_settings_renames, file_content);
+ rename_gdscript_keywords(file_content);
+ rename_common(gdscript_properties_renames, file_content);
+ rename_common(gdscript_signals_renames, file_content);
+ rename_common(shaders_renames, file_content);
+ rename_common(builtin_types_renames, file_content);
+
+ custom_rename(file_content, "\\.shader", ".gdshader");
+ custom_rename(file_content, "instance", "instantiate");
+ } else if (file_name.ends_with(".tscn")) {
+ rename_classes(file_content); // Using only specialized function
+
+ rename_common(enum_renames, file_content);
+ rename_enums(file_content); // Require to additional rename
+
+ rename_common(gdscript_function_renames, file_content);
+ rename_gdscript_functions(file_content); // Require to additional rename
+
+ rename_common(project_settings_renames, file_content);
+ rename_gdscript_keywords(file_content);
+ rename_common(gdscript_properties_renames, file_content);
+ rename_common(gdscript_signals_renames, file_content);
+ rename_common(shaders_renames, file_content);
+ rename_common(builtin_types_renames, file_content);
+
+ custom_rename(file_content, "\\.shader", ".gdshader");
+ } else if (file_name.ends_with(".cs")) { // TODO, C# should use different methods
+ rename_classes(file_content); // Using only specialized function
+ rename_common(csharp_function_renames, file_content);
+ rename_common(builtin_types_renames, file_content);
+ rename_common(csharp_properties_renames, file_content);
+ rename_common(csharp_signals_renames, file_content);
+ rename_csharp_functions(file_content);
+ custom_rename(file_content, "public class ", "public partial class ");
+ } else if (file_name.ends_with(".gdshader") || file_name.ends_with(".shader")) {
+ rename_common(shaders_renames, file_content);
+ } else if (file_name.ends_with("tres")) {
+ rename_classes(file_content); // Using only specialized function
+
+ rename_common(shaders_renames, file_content);
+ rename_common(builtin_types_renames, file_content);
+
+ custom_rename(file_content, "\\.shader", ".gdshader");
+ } else if (file_name.ends_with("project.godot")) {
+ rename_common(project_settings_renames, file_content);
+ rename_common(builtin_types_renames, file_content);
+ } else if (file_name.ends_with(".csproj")) {
+ // TODO
+ } else {
+ ERR_PRINT(file_name + " is not supported!");
+ continue;
+ }
+ } else {
+ reason.append(" ERROR: File has exceeded the maximum size allowed - 500 KB");
+ is_ignored = true;
+ }
+
+ uint64_t end_time = Time::get_singleton()->get_ticks_msec();
+
+ if (!is_ignored) {
+ uint64_t hash_after = file_content.hash64();
+ // Don't need to save file without any changes
+ // Save if this is a shader, because it was renamed
+ if (hash_before != hash_after || file_name.find(".gdshader") != -1) {
+ converted_files++;
+
+ Ref<FileAccess> file = FileAccess::open(file_name, FileAccess::WRITE);
+ ERR_CONTINUE_MSG(file.is_null(), "Failed to open \"" + file_name + "\" to save data to file.");
+ file->store_string(file_content);
+ reason.append(" File was changed, conversion took " + itos(end_time - start_time) + " ms.");
+ } else {
+ reason.append(" File was not changed, checking took " + itos(end_time - start_time) + " ms.");
+ }
+ }
+ for (int k = 0; k < reason.size(); k++) {
+ print_line(reason[k]);
+ }
+ }
+
+ print_line("Conversion ended - all files(" + itos(collected_files.size()) + "), converted files(" + itos(converted_files) + "), not converted files(" + itos(collected_files.size() - converted_files) + ").");
+ return 0;
+};
+
+// Function responsible for validating project conversion.
+int ProjectConverter3To4::validate_conversion() {
+ print_line("Starting checking if project conversion can be done.");
+
+ ERR_FAIL_COND_V_MSG(!test_array_names(), ERROR_CODE, "Cannot start converting due to problems with data in arrays.");
+ ERR_FAIL_COND_V_MSG(!test_conversion(), ERROR_CODE, "Cannot start converting due to problems with converting arrays.");
+
+ // Checking if folder contains valid Godot 3 project.
+ // Project cannot be converted 2 times
+ {
+ String conventer_text = "; Project was converted by built-in tool to Godot 4.0";
+
+ ERR_FAIL_COND_V_MSG(!FileAccess::exists("project.godot"), ERROR_CODE, "Current directory doesn't contains any Godot 3 project");
+
+ Error err = OK;
+ String project_godot_content = FileAccess::get_file_as_string("project.godot", &err);
+
+ ERR_FAIL_COND_V_MSG(err != OK, ERROR_CODE, "Failed to read content of \"project.godot\" file.");
+ ERR_FAIL_COND_V_MSG(project_godot_content.find(conventer_text) != -1, ERROR_CODE, "Project already was converted with this tool.");
+ }
+
+ Vector<String> collected_files = check_for_files();
+
+ uint32_t converted_files = 0;
+
+ // Check file by file
+ for (int i = 0; i < collected_files.size(); i++) {
+ String file_name = collected_files[i];
+ Vector<String> file_content;
+ uint64_t file_size = 0;
+ {
+ Ref<FileAccess> file = FileAccess::open(file_name, FileAccess::READ);
+ ERR_CONTINUE_MSG(file.is_null(), "Failed to read content of \"" + file_name + "\".");
+ while (!file->eof_reached()) {
+ String line = file->get_line();
+ file_size += line.size();
+ file_content.append(line);
+ }
+ }
+ print_line("Checking for conversion - " + itos(i + 1) + "/" + itos(collected_files.size()) + " file - \"" + file_name.trim_prefix("res://") + "\" with size - " + itos(file_size / 1024) + " KB");
+
+ Vector<String> changed_elements;
+ Vector<String> reason;
+ bool is_ignored = false;
+ uint64_t start_time = Time::get_singleton()->get_ticks_msec();
+
+ if (file_name.ends_with(".sader")) {
+ reason.append("\tFile extension will be renamed from `shader` to `gdshader`.");
+ }
+
+ if (file_size < CONVERSION_MAX_FILE_SIZE) {
+ if (file_name.ends_with(".gd")) {
+ changed_elements.append_array(check_for_rename_classes(file_content));
+
+ changed_elements.append_array(check_for_rename_common(enum_renames, file_content));
+ changed_elements.append_array(check_for_rename_enums(file_content));
+
+ changed_elements.append_array(check_for_rename_common(gdscript_function_renames, file_content));
+ changed_elements.append_array(check_for_rename_gdscript_functions(file_content));
+
+ changed_elements.append_array(check_for_rename_common(project_settings_renames, file_content));
+ changed_elements.append_array(check_for_rename_gdscript_keywords(file_content));
+ changed_elements.append_array(check_for_rename_common(gdscript_properties_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(gdscript_signals_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(shaders_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(builtin_types_renames, file_content));
+
+ changed_elements.append_array(check_for_custom_rename(file_content, "instance", "instantiate"));
+ changed_elements.append_array(check_for_custom_rename(file_content, "\\.shader", ".gdshader"));
+ } else if (file_name.ends_with(".tscn")) {
+ changed_elements.append_array(check_for_rename_classes(file_content));
+
+ changed_elements.append_array(check_for_rename_common(enum_renames, file_content));
+ changed_elements.append_array(check_for_rename_enums(file_content));
+
+ changed_elements.append_array(check_for_rename_common(gdscript_function_renames, file_content));
+ changed_elements.append_array(check_for_rename_gdscript_functions(file_content));
+
+ changed_elements.append_array(check_for_rename_common(project_settings_renames, file_content));
+ changed_elements.append_array(check_for_rename_gdscript_keywords(file_content));
+ changed_elements.append_array(check_for_rename_common(gdscript_properties_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(gdscript_signals_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(shaders_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(builtin_types_renames, file_content));
+
+ changed_elements.append_array(check_for_custom_rename(file_content, "\\.shader", ".gdshader"));
+ } else if (file_name.ends_with(".cs")) {
+ changed_elements.append_array(check_for_rename_common(class_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(csharp_function_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(builtin_types_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(csharp_properties_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(csharp_signals_renames, file_content));
+ changed_elements.append_array(check_for_rename_csharp_functions(file_content));
+ changed_elements.append_array(check_for_custom_rename(file_content, "public class ", "public partial class "));
+ } else if (file_name.ends_with(".gdshader") || file_name.ends_with(".shader")) {
+ changed_elements.append_array(check_for_rename_common(shaders_renames, file_content));
+ } else if (file_name.ends_with("tres")) {
+ changed_elements.append_array(check_for_rename_classes(file_content));
+
+ changed_elements.append_array(check_for_rename_common(shaders_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(builtin_types_renames, file_content));
+
+ changed_elements.append_array(check_for_custom_rename(file_content, "\\.shader", ".gdshader"));
+ } else if (file_name.ends_with("project.godot")) {
+ changed_elements.append_array(check_for_rename_common(project_settings_renames, file_content));
+ changed_elements.append_array(check_for_rename_common(builtin_types_renames, file_content));
+ } else if (file_name.ends_with(".csproj")) {
+ // TODO
+ } else {
+ ERR_PRINT(file_name + " is not supported!");
+ continue;
+ }
+ } else {
+ reason.append("\tERROR: File has exceeded the maximum size allowed - 500 KB");
+ is_ignored = true;
+ }
+
+ uint64_t end_time = Time::get_singleton()->get_ticks_msec();
+ print_line(" Checking file took " + itos(end_time - start_time) + " ms.");
+
+ for (int k = 0; k < reason.size(); k++) {
+ print_line(reason[k]);
+ }
+
+ if (changed_elements.size() > 0 && !is_ignored) {
+ converted_files++;
+
+ for (int k = 0; k < changed_elements.size(); k++) {
+ print_line(String("\t\t") + changed_elements[k]);
+ }
+ }
+ }
+
+ print_line("Checking for valid conversion ended - all files(" + itos(collected_files.size()) + "), files which would be converted(" + itos(converted_files) + "), files which would not be converted(" + itos(collected_files.size() - converted_files) + ").");
+ return 0;
+}
+
+// Collect files which will be checked, it will not touch txt, mp4, wav etc. files
+Vector<String> ProjectConverter3To4::check_for_files() {
+ Vector<String> collected_files = Vector<String>();
+
+ Vector<String> directories_to_check = Vector<String>();
+ directories_to_check.push_back("res://");
+
+ core_bind::Directory dir = core_bind::Directory();
+ while (!directories_to_check.is_empty()) {
+ String path = directories_to_check.get(directories_to_check.size() - 1); // Is there any pop_back function?
+ directories_to_check.resize(directories_to_check.size() - 1); // Remove last element
+ if (dir.open(path) == OK) {
+ dir.set_include_hidden(true);
+ dir.list_dir_begin();
+ String current_dir = dir.get_current_dir();
+ String file_name = dir.get_next();
+
+ while (file_name != "") {
+ if (file_name == ".git" || file_name == ".import" || file_name == ".godot") {
+ file_name = dir.get_next();
+ continue;
+ }
+ if (dir.current_is_dir()) {
+ directories_to_check.append(current_dir + file_name + "/");
+ } else {
+ bool proper_extension = false;
+ if (file_name.ends_with(".gd") || file_name.ends_with(".shader") || file_name.ends_with(".tscn") || file_name.ends_with(".tres") || file_name.ends_with(".godot") || file_name.ends_with(".cs") || file_name.ends_with(".csproj"))
+ proper_extension = true;
+
+ if (proper_extension) {
+ collected_files.append(current_dir + file_name);
+ }
+ }
+ file_name = dir.get_next();
+ }
+ } else {
+ print_verbose("Failed to open " + path);
+ }
+ }
+ return collected_files;
+}
+
+bool ProjectConverter3To4::test_conversion_single_additional(String name, String expected, void (ProjectConverter3To4::*func)(String &), String what) {
+ String got = name;
+ (this->*func)(got);
+ if (expected != got) {
+ ERR_PRINT("Failed to convert " + what + " `" + name + "` to `" + expected + "`, got instead `" + got + "`");
+ return false;
+ }
+
+ return true;
+}
+
+bool ProjectConverter3To4::test_conversion_single_normal(String name, String expected, const char *array[][2], String what) {
+ String got = name;
+ rename_common(array, got);
+ if (expected != got) {
+ ERR_PRINT("Failed to convert " + what + " `" + name + "` to `" + expected + "`, got instead `" + got + "`");
+ return false;
+ }
+ return true;
+}
+
+// Validate if conversions are proper
+bool ProjectConverter3To4::test_conversion() {
+ bool valid = true;
+
+ valid = valid & test_conversion_single_normal("Spatial", "Node3D", class_renames, "class");
+
+ valid = valid & test_conversion_single_normal("TYPE_REAL", "TYPE_FLOAT", enum_renames, "enum");
+
+ valid = valid & test_conversion_single_normal("can_instance", "can_instantiate", gdscript_function_renames, "gdscript function");
+
+ valid = valid & test_conversion_single_normal("CanInstance", "CanInstantiate", csharp_function_renames, "csharp function");
+
+ valid = valid & test_conversion_single_normal("translation", "position", gdscript_properties_renames, "gdscript property");
+
+ valid = valid & test_conversion_single_normal("Translation", "Position", csharp_properties_renames, "csharp property");
+
+ valid = valid & test_conversion_single_normal("NORMALMAP", "NORMAL_MAP", shaders_renames, "shader");
+
+ valid = valid & test_conversion_single_normal("text_entered", "text_submitted", gdscript_signals_renames, "gdscript signal");
+
+ valid = valid & test_conversion_single_normal("TextEntered", "TextSubmitted", csharp_signals_renames, "csharp signal");
+
+ valid = valid & test_conversion_single_normal("audio/channel_disable_threshold_db", "audio/buses/channel_disable_threshold_db", project_settings_renames, "project setting");
+
+ valid = valid & test_conversion_single_normal("Transform", "Transform3D", builtin_types_renames, "builtin type");
+
+ // Custom Renames
+
+ valid = valid & test_conversion_single_additional("(Connect(A,B,C,D,E,F,G) != OK):", "(Connect(A,new Callable(B,C),D,E,F,G) != OK):", &ProjectConverter3To4::rename_csharp_functions, "custom rename csharp");
+ valid = valid & test_conversion_single_additional("(Disconnect(A,B,C) != OK):", "(Disconnect(A,new Callable(B,C)) != OK):", &ProjectConverter3To4::rename_csharp_functions, "custom rename csharp");
+ valid = valid & test_conversion_single_additional("(IsConnected(A,B,C) != OK):", "(IsConnected(A,new Callable(B,C)) != OK):", &ProjectConverter3To4::rename_csharp_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("OS.window_fullscreen = Settings.fullscreen", "ProjectSettings.set(\"display/window/size/fullscreen\", Settings.fullscreen)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("\tvar aa = roman(r.move_and_slide( a, b, c, d, e, f )) # Roman", "\tr.set_motion_velocity(a)\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tvar aa = roman(r.move_and_slide()) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("\tvar aa = roman(r.move_and_slide_with_snap( a, g, b, c, d, e, f )) # Roman", "\tr.set_motion_velocity(a)\n\t# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `g`\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tvar aa = roman(r.move_and_slide()) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("list_dir_begin( a , b )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("list_dir_begin( a )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("list_dir_begin( )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("sort_custom( a , b )", "sort_custom(Callable(a,b))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("func c(var a, var b)", "func c(a, b)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("draw_line(1, 2, 3, 4, 5)", "draw_line(1,2,3,4)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("\timage.lock()", "\tfalse # image.lock() # TODOConverter40, image no longer require locking, `false` helps to not broke one line if/else, so can be freely removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("\timage.unlock()", "\tfalse # image.unlock() # TODOConverter40, image no longer require locking, `false` helps to not broke one line if/else, so can be freely removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("\troman.image.unlock()", "\tfalse # roman.image.unlock() # TODOConverter40, image no longer require locking, `false` helps to not broke one line if/else, so can be freely removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("\tmtx.lock()", "\tmtx.lock()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("\tmutex.unlock()", "\tmutex.unlock()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("\nonready", "\n@onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("onready", "@onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional(" onready", " onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\nexport", "\n@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\texport", "\t@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\texport_dialog", "\texport_dialog", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("export", "@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional(" export", " export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("tool", "@tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\n tool", "\n tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\n\ntool", "\n\n@tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\n\nmaster func", "\n\n@rpc(any) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\n\npuppet func", "\n\n@rpc(auth) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\n\nremote func", "\n\n@rpc(any) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\n\nremotesync func", "\n\n@rpc(any,sync) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\n\nsync func", "\n\n@rpc(any,sync) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\n\npuppetsync func", "\n\n@rpc(auth,sync) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+ valid = valid & test_conversion_single_additional("\n\nmastersync func", "\n\n@rpc(any,sync) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword");
+
+ valid = valid & test_conversion_single_additional("var size : Vector2 = Vector2() setget set_function , get_function", "var size : Vector2 = Vector2() :\n get:\n return size # TODOConverter40 Copy here content of get_function\n set(mod_value):\n mod_value # TODOConverter40 Copy here content of set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("var size : Vector2 = Vector2() setget set_function , ", "var size : Vector2 = Vector2() :\n get:\n return size # TODOConverter40 Non existent get function \n set(mod_value):\n mod_value # TODOConverter40 Copy here content of set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("var size : Vector2 = Vector2() setget set_function", "var size : Vector2 = Vector2() :\n get:\n return size # TODOConverter40 Non existent get function \n set(mod_value):\n mod_value # TODOConverter40 Copy here content of set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("var size : Vector2 = Vector2() setget , get_function", "var size : Vector2 = Vector2() :\n get:\n return size # TODOConverter40 Copy here content of get_function \n set(mod_value):\n mod_value # TODOConverter40 Non existent set function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("get_node(@", "get_node(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("yield(this, \"timeout\")", "await this.timeout", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional(" Transform.xform(Vector3(a,b,c)) ", " Transform * Vector3(a,b,c) ", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional(" Transform.xform_inv(Vector3(a,b,c)) ", " Vector3(a,b,c) * Transform ", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("export(float) var lifetime = 3.0", "export var lifetime: float = 3.0", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("export(String, 'AnonymousPro', 'CourierPrime') var _font_name = 'AnonymousPro'", "export var _font_name = 'AnonymousPro' # (String, 'AnonymousPro', 'CourierPrime')", &ProjectConverter3To4::rename_gdscript_functions, "custom rename"); // TODO, this is only a workaround
+ valid = valid & test_conversion_single_additional("export(PackedScene) var mob_scene", "export var mob_scene: PackedScene", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("var d = parse_json(roman(sfs))", "var test_json_conv = JSON.new()\ntest_json_conv.parse(roman(sfs))\nvar d = test_json_conv.get_data()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("to_json( AA ) szon", "JSON.new().stringify( AA ) szon", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("s to_json", "s JSON.new().stringify", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("AF to_json2", "AF to_json2", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("var rr = JSON.parse(a)", "var test_json_conv = JSON.new()\ntest_json_conv.parse(a)\nvar rr = test_json_conv.get_data()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("empty()", "is_empty()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional(".empty", ".empty", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional(").roman(", ").roman(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("\t.roman(", "\tsuper.roman(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional(" .roman(", " super.roman(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional(".1", ".1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional(" .1", " .1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("'.'", "'.'", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("'.a'", "'.a'", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("\t._input(_event)", "\tsuper._input(_event)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("(start(A,B,C,D,E,F,G) != OK):", "(start(A,Callable(B,C),D,E,F,G) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("(connect(A,B,C,D,E,F,G) != OK):", "(connect(A,Callable(B,C),D,E,F,G) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("(connect(A,B,C) != OK):", "(connect(A,Callable(B,C)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("disconnect(A,B,C) != OK):", "disconnect(A,Callable(B,C)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("is_connected(A,B,C) != OK):", "is_connected(A,Callable(B,C)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("is_connected(A,B,C))", "is_connected(A,Callable(B,C)))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("func _init(p_x:int)->void:", "func _init(p_x:int):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("q_PackedDataContainer._iter_init(variable1)", "q_PackedDataContainer._iter_init(variable1)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("assert(speed < 20, str(randi()%10))", "assert(speed < 20) #,str(randi()%10))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("assert(speed < 2)", "assert(speed < 2)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("assert(false, \"Missing type --\" + str(argument.type) + \"--, needs to be added to project\")", "assert(false) #,\"Missing type --\" + str(argument.type) + \"--, needs to be added to project\")", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("create_from_image(aa, bb)", "create_from_image(aa) #,bb", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("q_ImageTexture.create_from_image(variable1, variable2)", "q_ImageTexture.create_from_image(variable1) #,variable2", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("set_cell_item(a, b, c, d ,e) # AA", "set_cell_item( Vector3(a,b,c) ,d,e) # AA", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("set_cell_item(a, b)", "set_cell_item(a, b)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("get_cell_item_orientation(a, b,c)", "get_cell_item_orientation(Vector3i(a,b,c))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("get_cell_item(a, b,c)", "get_cell_item(Vector3i(a,b,c))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("PackedStringArray(req_godot).join('.')", "'.'.join(PackedStringArray(req_godot))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("=PackedStringArray(req_godot).join('.')", "='.'.join(PackedStringArray(req_godot))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional(" aa", " aa", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("\taa", "\taa", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional("\t aa", "\taa", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+ valid = valid & test_conversion_single_additional(" \taa", " \taa", &ProjectConverter3To4::rename_gdscript_functions, "custom rename");
+
+ valid = valid & test_conversion_single_additional("AAA Color.white AF", "AAA Color.WHITE AF", &ProjectConverter3To4::rename_enums, "custom rename");
+
+ // Custom rule conversion
+ {
+ String from = "instance";
+ String to = "instantiate";
+ String name = "AA.instance()";
+ String got = "AA.instance()";
+ String expected = "AA.instantiate()";
+ custom_rename(got, from, to);
+ if (got != expected) {
+ ERR_PRINT("Failed to convert custom rename `" + name + "` to `" + expected + "`, got instead `" + got + "`");
+ }
+ valid = valid & (got == expected);
+ }
+
+ // get_object_of_execution
+ {
+ { String base = "var roman = kieliszek.";
+ String expected = "kieliszek.";
+ String got = get_object_of_execution(base);
+ if (got != expected) {
+ ERR_PRINT("Failed to get proper data from get_object_of_execution `" + base + "` should return `" + expected + "`(" + itos(expected.size()) + "), got instead `" + got + "`(" + itos(got.size()) + ")");
+ }
+ valid = valid & (got == expected);
+}
+{
+ String base = "r.";
+ String expected = "r.";
+ String got = get_object_of_execution(base);
+ if (got != expected) {
+ ERR_PRINT("Failed to get proper data from get_object_of_execution `" + base + "` should return `" + expected + "`(" + itos(expected.size()) + "), got instead `" + got + "`(" + itos(got.size()) + ")");
+ }
+ valid = valid & (got == expected);
+}
+{
+ String base = "mortadela(";
+ String expected = "";
+ String got = get_object_of_execution(base);
+ if (got != expected) {
+ ERR_PRINT("Failed to get proper data from get_object_of_execution `" + base + "` should return `" + expected + "`(" + itos(expected.size()) + "), got instead `" + got + "`(" + itos(got.size()) + ")");
+ }
+ valid = valid & (got == expected);
+}
+}
+// get_starting_space
+{
+ String base = "\t\t\t var roman = kieliszek.";
+ String expected = "\t\t\t";
+ String got = get_starting_space(base);
+ if (got != expected) {
+ ERR_PRINT("Failed to get proper data from get_starting_space `" + base + "` should return `" + expected + "`(" + itos(expected.size()) + "), got instead `" + got + "`(" + itos(got.size()) + ")");
+ }
+ valid = valid & (got == expected);
+}
+// Parse Arguments
+{
+ String line = "( )";
+ Vector<String> got_vector = parse_arguments(line);
+ String got = "";
+ String expected = "";
+ for (String &part : got_vector) {
+ got += part + "|||";
+ }
+ if (got != expected) {
+ ERR_PRINT("Failed to get proper data from parse_arguments `" + line + "` should return `" + expected + "`(" + itos(expected.size()) + "), got instead `" + got + "`(" + itos(got.size()) + ")");
+ }
+ valid = valid & (got == expected);
+}
+{
+ String line = "(a , b , c)";
+ Vector<String> got_vector = parse_arguments(line);
+ String got = "";
+ String expected = "a|||b|||c|||";
+ for (String &part : got_vector) {
+ got += part + "|||";
+ }
+ if (got != expected) {
+ ERR_PRINT("Failed to get proper data from parse_arguments `" + line + "` should return `" + expected + "`(" + itos(expected.size()) + "), got instead `" + got + "`(" + itos(got.size()) + ")");
+ }
+ valid = valid & (got == expected);
+}
+{
+ String line = "(a , \"b,\" , c)";
+ Vector<String> got_vector = parse_arguments(line);
+ String got = "";
+ String expected = "a|||\"b,\"|||c|||";
+ for (String &part : got_vector) {
+ got += part + "|||";
+ }
+ if (got != expected) {
+ ERR_PRINT("Failed to get proper data from parse_arguments `" + line + "` should return `" + expected + "`(" + itos(expected.size()) + "), got instead `" + got + "`(" + itos(got.size()) + ")");
+ }
+ valid = valid & (got == expected);
+}
+{
+ String line = "(a , \"(,),,,,\" , c)";
+ Vector<String> got_vector = parse_arguments(line);
+ String got = "";
+ String expected = "a|||\"(,),,,,\"|||c|||";
+ for (String &part : got_vector) {
+ got += part + "|||";
+ }
+ if (got != expected) {
+ ERR_PRINT("Failed to get proper data from parse_arguments `" + line + "` should return `" + expected + "`(" + itos(expected.size()) + "), got instead `" + got + "`(" + itos(got.size()) + ")");
+ }
+ valid = valid & (got == expected);
+}
+
+return valid;
+}
+
+// Validate in all arrays if names don't do cyclic renames `Node` -> `Node2D` | `Node2D` -> `2DNode`
+bool ProjectConverter3To4::test_array_names() {
+ bool valid = true;
+ Vector<String> names = Vector<String>();
+
+ // Validate if all classes are valid
+ {
+ int current_index = 0;
+ while (class_renames[current_index][0]) {
+ const String old_class = class_renames[current_index][0];
+ const String new_class = class_renames[current_index][1];
+
+ // Light2D, Texture, Viewport are special classes(probably virtual ones)
+ if (ClassDB::class_exists(StringName(old_class)) && old_class != "Light2D" && old_class != "Texture" && old_class != "Viewport") {
+ ERR_PRINT(String("Class `") + old_class + "` exists in Godot 4.0, so cannot be renamed to something else.");
+ valid = false; // This probably should be only a warning, but not 100% sure - this would need to be added to CI
+ }
+
+ // Callable is special class, to which normal classes may be renamed
+ if (!ClassDB::class_exists(StringName(new_class)) && new_class != "Callable") {
+ ERR_PRINT(String("Class `") + new_class + "` doesn't exists in Godot 4.0, so cannot be used in convertion.");
+ valid = false; // This probably should be only a warning, but not 100% sure - this would need to be added to CI
+ }
+ current_index++;
+ }
+ }
+
+ // // TODO To be able to fully work, it needs https://github.com/godotengine/godot/pull/49053
+ // // TODO this needs to be changed to hashset when available https://github.com/godotengine/godot-proposals/issues/867, to speedup searchng
+ // {
+ // OrderedHashMap<String, bool> all_functions;
+
+ // List<StringName> classes_list;
+ // ClassDB::get_class_list(&classes_list);
+ // for (StringName &name_of_class : classes_list) {
+ // List<MethodInfo> method_list;
+ // ClassDB::get_method_list(name_of_class, &method_list, true);
+ // for (MethodInfo &function_data : method_list) {
+ // if (!all_functions.has(function_data.name)) {
+ // all_functions.insert(function_data.name, false);
+ // }
+ // }
+ // }
+
+ // for (int type = Variant::Type::NIL + 1; type < Variant::Type::VARIANT_MAX; type++) {
+ // List<MethodInfo> method_list;
+ // Variant::get_method_list_by_type(&method_list, Variant::Type(type));
+ // for (MethodInfo &function_data : method_list) {
+ // if (!all_functions.has(function_data.name)) {
+ // all_functions.insert(function_data.name, false);
+ // }
+ // }
+ // }
+
+ // int current_element = 0;
+ // while (gdscript_function_renames[current_element][0] != nullptr) {
+ // if (!all_functions.has(gdscript_function_renames[current_element][1])) {
+ // ERR_PRINT(String("Missing gdscript function in pair (") + gdscript_function_renames[current_element][0] + " - ===> " + gdscript_function_renames[current_element][1] + " <===)");
+ // valid = false;
+ // }
+ // // // DEBUG, disable below after tests
+ // // if (all_functions.has(gdscript_function_renames[current_element][0])) {
+ // // String used_in_classes = "";
+ // //
+ // // for (StringName &name_of_class : classes_list) {
+ // // List<MethodInfo> method_list;
+ // // ClassDB::get_method_list(name_of_class, &method_list, true);
+ // // for (MethodInfo &function_data : method_list) {
+ // // if (function_data.name == gdscript_function_renames[current_element][0]) {
+ // // used_in_classes += String(name_of_class) + ", ";
+ // // }
+ // // }
+ // // }
+ // // for (int type = Variant::Type::NIL + 1; type < Variant::Type::VARIANT_MAX; type++) {
+ // // List<MethodInfo> method_list;
+ // // Variant::get_method_list_by_type(&method_list, Variant::Type(type));
+ // // for (MethodInfo &function_data : method_list) {
+ // // if (function_data.name == gdscript_function_renames[current_element][0]) {
+ // // used_in_classes += Variant::get_type_name(Variant::Type(type)) + ", ";
+ // // }
+ // // }
+ // // }
+ // // used_in_classes = used_in_classes.trim_suffix(", ");
+ // //
+ // // WARN_PRINT(String("Godot contains function which will be renamed in pair ( ===> ") + gdscript_function_renames[current_element][0] + " <=== - " + gdscript_function_renames[current_element][1] + ") in class " + used_in_classes + " - check for possible invalid rule.");
+ // // }
+ // current_element++;
+ // }
+ // }
+
+ valid = valid & test_single_array(enum_renames);
+ valid = valid & test_single_array(class_renames, true);
+ valid = valid & test_single_array(gdscript_function_renames, true);
+ valid = valid & test_single_array(csharp_function_renames, true);
+ valid = valid & test_single_array(gdscript_properties_renames);
+ valid = valid & test_single_array(csharp_properties_renames);
+ valid = valid & test_single_array(shaders_renames);
+ valid = valid & test_single_array(gdscript_signals_renames);
+ valid = valid & test_single_array(project_settings_renames);
+ valid = valid & test_single_array(builtin_types_renames);
+ valid = valid & test_single_array(colors_renames);
+
+ return valid;
+}
+
+// Validate in one array if names don't do cyclic renames `Node` -> `Node2D` | `Node2D` -> `2DNode`
+// Also checks if in name contains spaces at the end or beggining
+bool ProjectConverter3To4::test_single_array(const char *array[][2], bool ignore_second_check) {
+ bool valid = true;
+ int current_index = 0;
+ Vector<String> names = Vector<String>();
+
+ while (array[current_index][0]) {
+ if (String(array[current_index][0]).begins_with(" ") || String(array[current_index][0]).ends_with(" ")) {
+ {
+ ERR_PRINT(String("Entry \"") + array[current_index][0] + "\" ends or stars with space.");
+ valid = false;
+ }
+ }
+ if (names.has(array[current_index][0])) {
+ ERR_PRINT(String("Found duplicated things, pair ( -> ") + array[current_index][0] + " , " + array[current_index][1] + ")");
+ valid = false;
+ }
+ names.append(array[current_index][0]);
+
+ if (String(array[current_index][1]).begins_with(" ") || String(array[current_index][1]).ends_with(" ")) {
+ {
+ ERR_PRINT(String("Entry \"") + array[current_index][1] + "\" ends or stars with space.");
+ valid = false;
+ }
+ }
+ if (names.has(array[current_index][1])) {
+ ERR_PRINT(String("Found duplicated things, pair (") + array[current_index][0] + " , ->" + array[current_index][1] + ")");
+ valid = false;
+ }
+ if (!ignore_second_check) {
+ names.append(array[current_index][1]);
+ }
+ current_index++;
+ }
+ return valid;
+};
+
+// Returns arguments from given function execution, this cannot be really done as regex
+// `abc(d,e(f,g),h)` -> [d], [e(f,g)], [h]
+Vector<String> ProjectConverter3To4::parse_arguments(const String &line) {
+ Vector<String> parts;
+ int string_size = line.length();
+ int current_index = 0;
+ int start_part = 0; // Index of beginning of start par
+ int parts_counter = 0;
+ char32_t previous_character = '\0';
+ bool is_inside_string = false; // if true, it ignore this 3 characters ( , ) inside string
+
+ if (line.count("(") != line.count(")")) {
+ ERR_PRINT("Bug: substring should have equal number of open and close parenthess - `" + line + "`");
+ return parts;
+ }
+
+ while (current_index < string_size) {
+ char32_t character = line.get(current_index);
+ switch (character) {
+ case '(': {
+ parts_counter++;
+ if (parts_counter == 1 && !is_inside_string) {
+ start_part = current_index;
+ }
+ break;
+ };
+ case ')': {
+ parts_counter--;
+ if (parts_counter == 0 && !is_inside_string) {
+ parts.append(line.substr(start_part + 1, current_index - start_part - 1));
+ start_part = current_index;
+ }
+ break;
+ };
+ case ',': {
+ if (parts_counter == 1 && !is_inside_string) {
+ parts.append(line.substr(start_part + 1, current_index - start_part - 1));
+ start_part = current_index;
+ }
+ break;
+ };
+ case '"': {
+ if (previous_character != '\\')
+ is_inside_string = !is_inside_string;
+ }
+ }
+ current_index++;
+ previous_character = character;
+ }
+
+ Vector<String> clean_parts;
+ for (String &part : parts) {
+ part = part.strip_edges();
+ if (!part.is_empty()) {
+ clean_parts.append(part);
+ }
+ }
+
+ return clean_parts;
+}
+
+// Finds latest parenthess owned by function
+// `function(abc(a,b),DD)):` finds this parenthess `function(abc(a,b),DD => ) <= ):`
+int ProjectConverter3To4::get_end_parenthess(const String &line) const {
+ int current_index = 0;
+ int current_state = 0;
+ while (line.length() > current_index) {
+ char32_t character = line.get(current_index);
+ if (character == '(') {
+ current_state++;
+ }
+ if (character == ')') {
+ current_state--;
+ if (current_state == 0) {
+ return current_index;
+ }
+ }
+ current_index++;
+ }
+ return -1;
+}
+
+// Connects arguments from vector to one string
+// Needed when after processing e.g. 2 arguments, later arguments are not changed in any way
+String ProjectConverter3To4::connect_arguments(const Vector<String> &arguments, int from, int to) const {
+ if (to == -1) {
+ to = arguments.size();
+ }
+
+ String value;
+ if (arguments.size() > 0 && from != 0 && from < to) {
+ value = ",";
+ }
+
+ for (int i = from; i < to; i++) {
+ value += arguments[i];
+ if (i != to - 1) {
+ value += ',';
+ }
+ }
+ return value;
+}
+
+// Return spaces or tabs which starts line e.g. `\t\tmove_this` will return `\t\t`
+String ProjectConverter3To4::get_starting_space(const String &line) const {
+ String empty_space;
+ int current_character = 0;
+
+ if (line.is_empty()) {
+ return empty_space;
+ }
+
+ if (line[0] == ' ') {
+ while (current_character < line.size()) {
+ if (line[current_character] == ' ') {
+ empty_space += ' ';
+ current_character++;
+ } else {
+ break;
+ }
+ }
+ }
+ if (line[0] == '\t') {
+ while (current_character < line.size()) {
+ if (line[current_character] == '\t') {
+ empty_space += '\t';
+ current_character++;
+ } else {
+ break;
+ }
+ }
+ }
+ return empty_space;
+}
+
+// Return object which execute specific function
+// e.g. in `var roman = kieliszek.funkcja()` to this function is passed everything before function which we want to check
+// so it is `var roman = kieliszek.` and this function return `kieliszek.`
+String ProjectConverter3To4::get_object_of_execution(const String &line) const {
+ int end = line.size() - 1; // Last one is \0
+ int start = end - 1;
+
+ while (start >= 0) {
+ char32_t character = line[start];
+ if ((character >= 'A' && character <= 'Z') || (character >= 'a' && character <= 'z') || character == '.' || character == '_') {
+ if (start == 0) {
+ break;
+ }
+ start--;
+ continue;
+ } else {
+ start++; // Found invalid character, needs to be ignored
+ break;
+ }
+ }
+ return line.substr(start, (end - start));
+}
+
+void ProjectConverter3To4::rename_enums(String &file_content) {
+ int current_index = 0;
+
+ // Rename colors
+ if (file_content.find("Color.") != -1) {
+ while (colors_renames[current_index][0]) {
+ RegEx reg = RegEx(String("\\bColor.") + colors_renames[current_index][0] + "\\b");
+ CRASH_COND(!reg.is_valid());
+ file_content = reg.sub(file_content, String("Color.") + colors_renames[current_index][1], true);
+ current_index++;
+ }
+ }
+};
+
+Vector<String> ProjectConverter3To4::check_for_rename_enums(Vector<String> &file_content) {
+ int current_index = 0;
+
+ Vector<String> found_things;
+
+ // Rename colors
+ if (file_content.find("Color.") != -1) {
+ while (colors_renames[current_index][0]) {
+ RegEx reg = RegEx(String("\\bColor.") + colors_renames[current_index][0] + "\\b");
+ CRASH_COND(!reg.is_valid());
+
+ int current_line = 1;
+ for (String &line : file_content) {
+ Array reg_match = reg.search_all(line);
+ if (reg_match.size() > 0) {
+ found_things.append(line_formatter(current_line, colors_renames[current_index][0], colors_renames[current_index][1], line));
+ }
+ current_line++;
+ }
+ current_index++;
+ }
+ }
+
+ return found_things;
+}
+
+void ProjectConverter3To4::rename_classes(String &file_content) {
+ int current_index = 0;
+
+ // TODO Maybe it is better way to not rename gd, tscn and other files which are named are classes
+ while (class_renames[current_index][0]) {
+ // Begin renaming workaround `Resource.gd` -> `RefCounter.gd`
+ RegEx reg_before = RegEx(String("\\b") + class_renames[current_index][0] + ".tscn\\b");
+ CRASH_COND(!reg_before.is_valid());
+ file_content = reg_before.sub(file_content, "TEMP_RENAMED_CLASS.tscn", true);
+ RegEx reg_before2 = RegEx(String("\\b") + class_renames[current_index][0] + ".gd\\b");
+ CRASH_COND(!reg_before2.is_valid());
+ file_content = reg_before2.sub(file_content, "TEMP_RENAMED_CLASS.gd", true);
+ RegEx reg_before3 = RegEx(String("\\b") + class_renames[current_index][0] + ".shader\\b");
+ CRASH_COND(!reg_before3.is_valid());
+ file_content = reg_before3.sub(file_content, "TEMP_RENAMED_CLASS.gd", true);
+ // End
+
+ RegEx reg = RegEx(String("\\b") + class_renames[current_index][0] + "\\b");
+ CRASH_COND(!reg.is_valid());
+ file_content = reg.sub(file_content, class_renames[current_index][1], true);
+
+ // Begin renaming workaround `Resource.gd` -> `RefCounter.gd`
+ RegEx reg_after = RegEx("\\bTEMP_RENAMED_CLASS.tscn\\b");
+ CRASH_COND(!reg_after.is_valid());
+ file_content = reg_after.sub(file_content, String(class_renames[current_index][0]) + ".tscn", true);
+ RegEx reg_after2 = RegEx("\\bTEMP_RENAMED_CLASS.gd\\b");
+ CRASH_COND(!reg_after2.is_valid());
+ file_content = reg_after2.sub(file_content, String(class_renames[current_index][0]) + ".gd", true);
+ RegEx reg_after3 = RegEx("\\bTEMP_RENAMED_CLASS.gd\\b");
+ CRASH_COND(!reg_after3.is_valid());
+ file_content = reg_after3.sub(file_content, String(class_renames[current_index][0]) + ".shader", true);
+ // End
+
+ current_index++;
+ }
+
+ // OS.get_ticks_msec -> Time.get_ticks_msec
+ RegEx reg_time1 = RegEx("OS.get_ticks_msec");
+ CRASH_COND(!reg_time1.is_valid());
+ file_content = reg_time1.sub(file_content, "Time.get_ticks_msec", true);
+ RegEx reg_time2 = RegEx("OS.get_ticks_usec");
+ CRASH_COND(!reg_time2.is_valid());
+ file_content = reg_time2.sub(file_content, "Time.get_ticks_usec", true);
+};
+
+Vector<String> ProjectConverter3To4::check_for_rename_classes(Vector<String> &file_content) {
+ int current_index = 0;
+
+ Vector<String> found_things;
+
+ while (class_renames[current_index][0]) {
+ RegEx reg_before = RegEx(String("\\b") + class_renames[current_index][0] + ".tscn\\b");
+ CRASH_COND(!reg_before.is_valid());
+ RegEx reg_before2 = RegEx(String("\\b") + class_renames[current_index][0] + ".gd\\b");
+ CRASH_COND(!reg_before2.is_valid());
+
+ RegEx reg = RegEx(String("\\b") + class_renames[current_index][0] + "\\b");
+ CRASH_COND(!reg.is_valid());
+
+ int current_line = 1;
+ for (String &line : file_content) {
+ line = reg_before.sub(line, "TEMP_RENAMED_CLASS.tscn", true);
+ line = reg_before2.sub(line, "TEMP_RENAMED_CLASS.gd", true);
+
+ Array reg_match = reg.search_all(line);
+ if (reg_match.size() > 0) {
+ found_things.append(line_formatter(current_line, class_renames[current_index][0], class_renames[current_index][1], line));
+ }
+ current_line++;
+ }
+ current_index++;
+ }
+
+ // TODO OS -> TIME
+ int current_line = 1;
+ RegEx reg_time1 = RegEx("OS.get_ticks_msec");
+ CRASH_COND(!reg_time1.is_valid());
+ RegEx reg_time2 = RegEx("OS.get_ticks_usec");
+ CRASH_COND(!reg_time2.is_valid());
+ for (String &line : file_content) {
+ String old = line;
+
+ line = reg_time1.sub(line, "Time.get_ticks_msec", true);
+ line = reg_time2.sub(line, "Time.get_ticks_usec", true);
+
+ if (old != line) {
+ found_things.append(simple_line_formatter(current_line, old, line));
+ }
+ current_line++;
+ }
+ return found_things;
+}
+
+void ProjectConverter3To4::rename_gdscript_functions(String &file_content) {
+ // Custom renaming, each rule needs to be set manually
+ // Don't forget to put validate each rule in validate_conversion function
+ Vector<String> lines = file_content.split("\n");
+
+ RegEx reg_is_empty = RegEx("\\bempty\\(");
+ RegEx reg_super = RegEx("([\t ])\\.([a-zA-Z_])");
+ RegEx reg_json_to = RegEx("\\bto_json\\b");
+ RegEx reg_json_parse = RegEx("([\t]{0,})([^\n]+)parse_json\\(([^\n]+)");
+ RegEx reg_json_non_new = RegEx("([\t]{0,})([^\n]+)JSON\\.parse\\(([^\n]+)");
+ RegEx reg_export = RegEx("export\\(([a-zA-Z0-9_]+)\\)[ ]+var[ ]+([a-zA-Z0-9_]+)");
+ RegEx reg_export_advanced = RegEx("export\\(([^)^\n]+)\\)[ ]+var[ ]+([a-zA-Z0-9_]+)([^\n]+)");
+ RegEx reg_setget_setget = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+)setget[ \t]+([a-zA-Z0-9_]+)[ \t]*,[ \t]*([a-zA-Z0-9_]+)");
+ RegEx reg_setget_set = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+)setget[ \t]+([a-zA-Z0-9_]+)[ \t]*[,]*[^a-z^A-Z^0-9^_]*$");
+ RegEx reg_setget_get = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+)setget[ \t]+,[ \t]*([a-zA-Z0-9_]+)[ \t]*$");
+ RegEx reg_join = RegEx("([\\(\\)a-zA-Z0-9_]+)\\.join\\(([^\n^\\)]+)\\)");
+ RegEx reg_mixed_tab_space = RegEx("([\t]+)([ ]+)");
+ RegEx reg_image_lock = RegEx("([a-zA-Z0-9_\\.]+)\\.lock\\(\\)");
+ RegEx reg_image_unlock = RegEx("([a-zA-Z0-9_\\.]+)\\.unlock\\(\\)");
+ RegEx reg_os_fullscreen = RegEx("OS.window_fullscreen[= ]+([^#^\n]+)");
+
+ CRASH_COND(!reg_is_empty.is_valid());
+ CRASH_COND(!reg_super.is_valid());
+ CRASH_COND(!reg_json_to.is_valid());
+ CRASH_COND(!reg_json_parse.is_valid());
+ CRASH_COND(!reg_json_non_new.is_valid());
+ CRASH_COND(!reg_export.is_valid());
+ CRASH_COND(!reg_export_advanced.is_valid());
+ CRASH_COND(!reg_setget_setget.is_valid());
+ CRASH_COND(!reg_setget_set.is_valid());
+ CRASH_COND(!reg_setget_get.is_valid());
+ CRASH_COND(!reg_join.is_valid());
+ CRASH_COND(!reg_mixed_tab_space.is_valid());
+ CRASH_COND(!reg_image_lock.is_valid());
+ CRASH_COND(!reg_image_unlock.is_valid());
+ CRASH_COND(!reg_os_fullscreen.is_valid());
+
+ for (String &line : lines) {
+ if (line.find("mtx") == -1 && line.find("mutex") == -1 && line.find("Mutex") == -1) {
+ line = reg_image_lock.sub(line, "false # $1.lock() # TODOConverter40, image no longer require locking, `false` helps to not broke one line if/else, so can be freely removed", true);
+ line = reg_image_unlock.sub(line, "false # $1.unlock() # TODOConverter40, image no longer require locking, `false` helps to not broke one line if/else, so can be freely removed", true);
+ }
+
+ // Mixed use of spaces and tabs - tabs as first - TODO, this probably is problem problem, but not sure
+ line = reg_mixed_tab_space.sub(line, "$1", true);
+
+ // PackedStringArray(req_godot).join('.') -> '.'.join(PackedStringArray(req_godot)) PoolStringArray
+ line = reg_join.sub(line, "$2.join($1)", true);
+
+ // -- empty() -> is_empty() Pool*Array
+ line = reg_is_empty.sub(line, "is_empty(", true);
+
+ // -- \t.func() -> \tsuper.func() Object
+ line = reg_super.sub(line, "$1super.$2", true); // TODO, not sure if possible, but for now this brake String text e.g. "Choosen .gitignore" -> "Choosen super.gitignore"
+
+ // -- JSON.parse(a) -> JSON.new().parse(a) etc. JSON
+ line = reg_json_non_new.sub(line, "$1var test_json_conv = JSON.new()\n$1test_json_conv.parse($3\n$1$2test_json_conv.get_data()", true);
+
+ // -- to_json(a) -> JSON.new().stringify(a) Object
+ line = reg_json_to.sub(line, "JSON.new().stringify", true);
+
+ // -- parse_json(a) -> JSON.get_data() etc. Object
+ line = reg_json_parse.sub(line, "$1var test_json_conv = JSON.new()\n$1test_json_conv.parse($3\n$1$2test_json_conv.get_data()", true);
+
+ // -- get_node(@ -> get_node( Node
+ line = line.replace("get_node(@", "get_node(");
+
+ // export(float) var lifetime = 3.0 -> export var lifetime: float = 3.0 GDScript
+ line = reg_export.sub(line, "export var $2: $1");
+
+ // export(String, 'AnonymousPro', 'CourierPrime') var _font_name = 'AnonymousPro' -> export var _font_name = 'AnonymousPro' #(String, 'AnonymousPro', 'CourierPrime') GDScript
+ line = reg_export_advanced.sub(line, "export var $2$3 # ($1)");
+
+ // Setget Setget
+ line = reg_setget_setget.sub(line, "var $1$2:\n\tget:\n\t\treturn $1 # TODOConverter40 Copy here content of $4\n\tset(mod_value):\n\t\tmod_value # TODOConverter40 Copy here content of $3", true);
+
+ // Setget set
+ line = reg_setget_set.sub(line, "var $1$2:\n\tget:\n\t\treturn $1 # TODOConverter40 Non existent get function \n\tset(mod_value):\n\t\tmod_value # TODOConverter40 Copy here content of $3", true);
+
+ // Setget get
+ line = reg_setget_get.sub(line, "var $1$2:\n\tget:\n\t\treturn $1 # TODOConverter40 Copy here content of $3 \n\tset(mod_value):\n\t\tmod_value # TODOConverter40 Non existent set function", true);
+
+ // OS.window_fullscreen = true -> ProjectSettings.set("display/window/size/fullscreen",true)
+ line = reg_os_fullscreen.sub(line, "ProjectSettings.set(\"display/window/size/fullscreen\", $1)", true);
+
+ // -- r.move_and_slide( a, b, c, d, e ) -> r.set_motion_velocity(a) ... r.move_and_slide() KinematicBody
+ if (line.find("move_and_slide(") != -1) {
+ int start = line.find("move_and_slide(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ String base_obj = get_object_of_execution(line.substr(0, start));
+ String starting_space = get_starting_space(line);
+
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() >= 1) {
+ String line_new;
+
+ // motion_velocity
+ line_new += starting_space + base_obj + "set_motion_velocity(" + parts[0] + ")\n";
+
+ // up_direction
+ if (parts.size() >= 2) {
+ line_new += starting_space + base_obj + "set_up_direction(" + parts[1] + ")\n";
+ }
+
+ // stop_on_slope
+ if (parts.size() >= 3) {
+ line_new += starting_space + base_obj + "set_floor_stop_on_slope_enabled(" + parts[2] + ")\n";
+ }
+
+ // max_slides
+ if (parts.size() >= 4) {
+ line_new += starting_space + base_obj + "set_max_slides(" + parts[3] + ")\n";
+ }
+
+ // floor_max_angle
+ if (parts.size() >= 5) {
+ line_new += starting_space + base_obj + "set_floor_max_angle(" + parts[4] + ")\n";
+ }
+
+ // infiinite_interia
+ if (parts.size() >= 6) {
+ line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[5] + "`\n";
+ }
+
+ line = line_new + line.substr(0, start) + "move_and_slide()" + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- r.move_and_slide_with_snap( a, b, c, d, e ) -> r.set_motion_velocity(a) ... r.move_and_slide() KinematicBody
+ if (line.find("move_and_slide_with_snap(") != -1) {
+ int start = line.find("move_and_slide_with_snap(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ String base_obj = get_object_of_execution(line.substr(0, start));
+ String starting_space = get_starting_space(line);
+
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() >= 1) {
+ String line_new;
+
+ // motion_velocity
+ line_new += starting_space + base_obj + "set_motion_velocity(" + parts[0] + ")\n";
+
+ // snap
+ if (parts.size() >= 2) {
+ line_new += starting_space + "# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `" + parts[1] + "`\n";
+ }
+
+ // up_direction
+ if (parts.size() >= 3) {
+ line_new += starting_space + base_obj + "set_up_direction(" + parts[2] + ")\n";
+ }
+
+ // stop_on_slope
+ if (parts.size() >= 4) {
+ line_new += starting_space + base_obj + "set_floor_stop_on_slope_enabled(" + parts[3] + ")\n";
+ }
+
+ // max_slides
+ if (parts.size() >= 5) {
+ line_new += starting_space + base_obj + "set_max_slides(" + parts[4] + ")\n";
+ }
+
+ // floor_max_angle
+ if (parts.size() >= 6) {
+ line_new += starting_space + base_obj + "set_floor_max_angle(" + parts[5] + ")\n";
+ }
+
+ // infiinite_interia
+ if (parts.size() >= 7) {
+ line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[6] + "`\n";
+ }
+
+ line = line_new + line.substr(0, start) + "move_and_slide()" + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- sort_custom( a , b ) -> sort_custom(Callable( a , b )) Object
+ if (line.find("sort_custom(") != -1) {
+ int start = line.find("sort_custom(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 2) {
+ line = line.substr(0, start) + "sort_custom(Callable(" + parts[0] + "," + parts[1] + "))" + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- list_dir_begin( ) -> list_dir_begin() Object
+ if (line.find("list_dir_begin(") != -1) {
+ int start = line.find("list_dir_begin(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ line = line.substr(0, start) + "list_dir_begin() " + line.substr(end + start) + "# TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547";
+ }
+ }
+
+ // -- draw_line(1,2,3,4,5) -> draw_line(1,2,3,4) CanvasItem
+ if (line.find("draw_line(") != -1) {
+ int start = line.find("draw_line(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 5) {
+ line = line.substr(0, start) + "draw_line(" + parts[0] + "," + parts[1] + "," + parts[2] + "," + parts[3] + ")" + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- func c(var a, var b) -> func c(a, b)
+ if (line.find("func ") != -1 && line.find("var ") != -1) {
+ int start = line.find("func ");
+ start = line.substr(start).find("(") + start;
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+
+ String start_string = line.substr(0, start) + "(";
+ for (int i = 0; i < parts.size(); i++) {
+ start_string += parts[i].strip_edges().trim_prefix("var ");
+ if (i != parts.size() - 1) {
+ start_string += ", ";
+ }
+ }
+ line = start_string + ")" + line.substr(end + start);
+ }
+ }
+
+ // -- yield(this, \"timeout\") -> await this.timeout GDScript
+ if (line.find("yield(") != -1) {
+ int start = line.find("yield(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 2) {
+ line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\"", "").replace("\'", "").replace(" ", "") + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- parse_json( AA ) -> TODO Object
+ if (line.find("parse_json(") != -1) {
+ int start = line.find("parse_json(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ line = line.substr(0, start) + "JSON.new().stringify(" + connect_arguments(parts, 0) + ")" + line.substr(end + start);
+ }
+ }
+
+ // -- .xform(Vector3(a,b,c)) -> * Vector3(a,b,c) Transform
+ if (line.find(".xform(") != -1) {
+ int start = line.find(".xform(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 1) {
+ line = line.substr(0, start) + " * " + parts[0] + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- .xform_inv(Vector3(a,b,c)) -> * Vector3(a,b,c) Transform
+ if (line.find(".xform_inv(") != -1) {
+ int start = line.find(".xform_inv(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ String object_exec = get_object_of_execution(line.substr(0, start));
+ if (line.find(object_exec + ".xform") != -1) {
+ int start2 = line.find(object_exec + ".xform");
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 1) {
+ line = line.substr(0, start2) + parts[0] + " * " + object_exec + line.substr(end + start);
+ }
+ }
+ }
+ }
+
+ // -- connect(,,,things) -> connect(,Callable(,),things) Object
+ if (line.find("connect(") != -1) {
+ int start = line.find("connect(");
+ // Protection from disconnect
+ if (start == 0 || line.get(start - 1) != 's') {
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() >= 3) {
+ line = line.substr(0, start) + "connect(" + parts[0] + ",Callable(" + parts[1] + "," + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);
+ }
+ }
+ }
+ }
+ // -- disconnect(a,b,c) -> disconnect(a,Callable(b,c)) Object
+ if (line.find("disconnect(") != -1) {
+ int start = line.find("disconnect(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "disconnect(" + parts[0] + ",Callable(" + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+ // -- is_connected(a,b,c) -> is_connected(a,Callable(b,c)) Object
+ if (line.find("is_connected(") != -1) {
+ int start = line.find("is_connected(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "is_connected(" + parts[0] + ",Callable(" + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+ // -- start(a,b,c) -> start(a,Callable(b,c)) Thread
+ if (line.find("start(") != -1) {
+ int start = line.find("start(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() >= 3) {
+ line = line.substr(0, start) + "start(" + parts[0] + ",Callable(" + parts[1] + "," + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);
+ }
+ }
+ }
+ // -- func _init(p_x:int)->void: -> func _init(p_x:int): Object # https://github.com/godotengine/godot/issues/50589
+ if (line.find(" _init(") != -1) {
+ int start = line.find(" _init(");
+ int end = line.rfind(":") + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ line = line.substr(0, start) + " _init(" + connect_arguments(parts, 0) + "):" + line.substr(end + start);
+ }
+ }
+ // assert(speed < 20, str(randi()%10)) -> assert(speed < 20) #,str(randi()%10)) GDScript - GDScript bug constant message
+ if (line.find("assert(") != -1) {
+ int start = line.find("assert(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 2) {
+ line = line.substr(0, start) + "assert(" + parts[0] + ") " + line.substr(end + start) + "#," + parts[1] + ")";
+ }
+ }
+ }
+ // create_from_image(aa, bb) -> create_from_image(aa) #, bb ImageTexture
+ if (line.find("create_from_image(") != -1) {
+ int start = line.find("create_from_image(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 2) {
+ line = line.substr(0, start) + "create_from_image(" + parts[0] + ") " + "#," + parts[1] + line.substr(end + start);
+ }
+ }
+ }
+ // set_cell_item(a, b, c, d ,e) -> set_cell_item(Vector3(a, b, c), d ,e)
+ if (line.find("set_cell_item(") != -1) {
+ int start = line.find("set_cell_item(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() > 2) {
+ line = line.substr(0, start) + "set_cell_item( Vector3(" + parts[0] + "," + parts[1] + "," + parts[2] + ") " + connect_arguments(parts, 3) + ")" + line.substr(end + start);
+ }
+ }
+ }
+ // get_cell_item(a, b, c) -> get_cell_item(Vector3i(a, b, c))
+ if (line.find("get_cell_item(") != -1) {
+ int start = line.find("get_cell_item(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "get_cell_item(Vector3i(" + parts[0] + "," + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+ // get_cell_item_orientation(a, b, c) -> get_cell_item_orientation(Vector3i(a, b, c))
+ if (line.find("get_cell_item_orientation(") != -1) {
+ int start = line.find("get_cell_item_orientation(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "get_cell_item_orientation(Vector3i(" + parts[0] + "," + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+
+ // TODO - add_surface_from_arrays - additional 4 argument
+ // ENetMultiplayerPeer.create_client - additional argument
+ // ENetMultiplayerPeer.create_server - additional argument
+ // Translation.get_message (and similar)
+ // TreeItem.move_after() - new argument
+ // TreeItem.move_before() - new argument
+ // UndoRedo.commit_action() - additional argument
+ }
+
+ // Collect vector to string
+ file_content = "";
+ for (int i = 0; i < lines.size(); i++) {
+ file_content += lines[i];
+
+ if (i != lines.size() - 1) {
+ file_content += "\n";
+ }
+ }
+};
+
+// This is almost 1:1 copy of function which rename gdscript functions
+Vector<String> ProjectConverter3To4::check_for_rename_gdscript_functions(Vector<String> &file_content) {
+ int current_line = 1;
+
+ Vector<String> found_things;
+
+ RegEx reg_is_empty = RegEx("\\bempty\\(");
+ RegEx reg_super = RegEx("([\t ])\\.([a-zA-Z_])");
+ RegEx reg_json_to = RegEx("\\bto_json\\b");
+ RegEx reg_json_parse = RegEx("([\t]{0,})([^\n]+)parse_json\\(([^\n]+)");
+ RegEx reg_json_non_new = RegEx("([\t]{0,})([^\n]+)JSON\\.parse\\(([^\n]+)");
+ RegEx reg_export = RegEx("export\\(([a-zA-Z0-9_]+)\\)[ ]+var[ ]+([a-zA-Z0-9_]+)");
+ RegEx reg_export_advanced = RegEx("export\\(([^)^\n]+)\\)[ ]+var[ ]+([a-zA-Z0-9_]+)([^\n]+)");
+ RegEx reg_setget_setget = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+)setget[ \t]+([a-zA-Z0-9_]+)[ \t]*,[ \t]*([a-zA-Z0-9_]+)");
+ RegEx reg_setget_set = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+)setget[ \t]+([a-zA-Z0-9_]+)[ \t]*[,]*[^a-z^A-Z^0-9^_]*$");
+ RegEx reg_setget_get = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+)setget[ \t]+,[ \t]*([a-zA-Z0-9_]+)[ \t]*$");
+ RegEx reg_join = RegEx("([\\(\\)a-zA-Z0-9_]+)\\.join\\(([^\n^\\)]+)\\)");
+ RegEx reg_mixed_tab_space = RegEx("([\t]+)([ ]+)");
+ RegEx reg_image_lock = RegEx("([a-zA-Z0-9_\\.]+)\\.lock\\(\\)");
+ RegEx reg_image_unlock = RegEx("([a-zA-Z0-9_\\.]+)\\.unlock\\(\\)");
+ RegEx reg_os_fullscreen = RegEx("OS.window_fullscreen[= ]+([^#^\n]+)");
+
+ CRASH_COND(!reg_is_empty.is_valid());
+ CRASH_COND(!reg_super.is_valid());
+ CRASH_COND(!reg_json_to.is_valid());
+ CRASH_COND(!reg_json_parse.is_valid());
+ CRASH_COND(!reg_json_non_new.is_valid());
+ CRASH_COND(!reg_export.is_valid());
+ CRASH_COND(!reg_export_advanced.is_valid());
+ CRASH_COND(!reg_setget_setget.is_valid());
+ CRASH_COND(!reg_setget_set.is_valid());
+ CRASH_COND(!reg_setget_get.is_valid());
+ CRASH_COND(!reg_join.is_valid());
+ CRASH_COND(!reg_mixed_tab_space.is_valid());
+ CRASH_COND(!reg_image_lock.is_valid());
+ CRASH_COND(!reg_image_unlock.is_valid());
+ CRASH_COND(!reg_os_fullscreen.is_valid());
+
+ for (String &line : file_content) {
+ String old_line = line;
+
+ if (line.find("mtx") == -1 && line.find("mutex") == -1 && line.find("Mutex") == -1) {
+ line = reg_image_lock.sub(line, "false # $1.lock() # TODOConverter40, image no longer require locking, `false` helps to not broke one line if/else, so can be freely removed", true);
+ line = reg_image_unlock.sub(line, "false # $1.unlock() # TODOConverter40, image no longer require locking, `false` helps to not broke one line if/else, so can be freely removed", true);
+ }
+
+ // Mixed use of spaces and tabs - tabs as first - TODO, this probably is problem problem, but not sure
+ line = reg_mixed_tab_space.sub(line, "$1", true);
+
+ // PackedStringArray(req_godot).join('.') -> '.'.join(PackedStringArray(req_godot)) PoolStringArray
+ line = reg_join.sub(line, "$2.join($1)", true);
+
+ // -- empty() -> is_empty() Pool*Array
+ line = reg_is_empty.sub(line, "is_empty(", true);
+
+ // -- \t.func() -> \tsuper.func() Object
+ line = reg_super.sub(line, "$1super.$2", true); // TODO, not sure if possible, but for now this brake String text e.g. "Choosen .gitignore" -> "Choosen super.gitignore"
+
+ // -- JSON.parse(a) -> JSON.new().parse(a) etc. JSON
+ line = reg_json_non_new.sub(line, "$1var test_json_conv = JSON.new()\n$1test_json_conv.parse($3\n$1$2test_json_conv.get_data()", true);
+
+ // -- to_json(a) -> JSON.new().stringify(a) Object
+ line = reg_json_to.sub(line, "JSON.new().stringify", true);
+
+ // -- parse_json(a) -> JSON.get_data() etc. Object
+ line = reg_json_parse.sub(line, "$1var test_json_conv = JSON.new()\n$1test_json_conv.parse($3\n$1$2test_json_conv.get_data()", true);
+
+ // -- get_node(@ -> get_node( Node
+ line = line.replace("get_node(@", "get_node(");
+
+ // export(float) var lifetime = 3.0 -> export var lifetime: float = 3.0 GDScript
+ line = reg_export.sub(line, "export var $2: $1");
+
+ // export(String, 'AnonymousPro', 'CourierPrime') var _font_name = 'AnonymousPro' -> export var _font_name = 'AnonymousPro' #(String, 'AnonymousPro', 'CourierPrime') GDScript
+ line = reg_export_advanced.sub(line, "export var $2$3 # ($1)");
+
+ // Setget Setget
+ line = reg_setget_setget.sub(line, "var $1$2:\n\tget:\n\t\treturn $1 # TODOConverter40 Copy here content of $4\n\tset(mod_value):\n\t\tmod_value # TODOConverter40 Copy here content of $3", true);
+
+ // Setget set
+ line = reg_setget_set.sub(line, "var $1$2:\n\tget:\n\t\treturn $1 # TODOConverter40 Non existent get function \n\tset(mod_value):\n\t\tmod_value # TODOConverter40 Copy here content of $3", true);
+
+ // Setget get
+ line = reg_setget_get.sub(line, "var $1$2:\n\tget:\n\t\treturn $1 # TODOConverter40 Copy here content of $3 \n\tset(mod_value):\n\t\tmod_value # TODOConverter40 Non existent set function", true);
+
+ // OS.window_fullscreen = true -> ProjectSettings.set("display/window/size/fullscreen",true)
+ line = reg_os_fullscreen.sub(line, "ProjectSettings.set(\"display/window/size/fullscreen\", $1)", true);
+
+ // -- r.move_and_slide( a, b, c, d, e ) -> r.set_motion_velocity(a) ... r.move_and_slide() KinematicBody
+ if (line.find("move_and_slide(") != -1) {
+ int start = line.find("move_and_slide(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ String base_obj = get_object_of_execution(line.substr(0, start));
+ String starting_space = get_starting_space(line);
+
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() >= 1) {
+ String line_new;
+
+ // motion_velocity
+ line_new += starting_space + base_obj + "set_motion_velocity(" + parts[0] + ")\n";
+
+ // up_direction
+ if (parts.size() >= 2) {
+ line_new += starting_space + base_obj + "set_up_direction(" + parts[1] + ")\n";
+ }
+
+ // stop_on_slope
+ if (parts.size() >= 3) {
+ line_new += starting_space + base_obj + "set_floor_stop_on_slope_enabled(" + parts[2] + ")\n";
+ }
+
+ // max_slides
+ if (parts.size() >= 4) {
+ line_new += starting_space + base_obj + "set_max_slides(" + parts[3] + ")\n";
+ }
+
+ // floor_max_angle
+ if (parts.size() >= 5) {
+ line_new += starting_space + base_obj + "set_floor_max_angle(" + parts[4] + ")\n";
+ }
+
+ // infiinite_interia
+ if (parts.size() >= 6) {
+ line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[5] + "`\n";
+ }
+
+ line = line_new + line.substr(0, start) + "move_and_slide()" + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- r.move_and_slide_with_snap( a, b, c, d, e ) -> r.set_motion_velocity(a) ... r.move_and_slide() KinematicBody
+ if (line.find("move_and_slide_with_snap(") != -1) {
+ int start = line.find("move_and_slide_with_snap(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ String base_obj = get_object_of_execution(line.substr(0, start));
+ String starting_space = get_starting_space(line);
+
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() >= 1) {
+ String line_new;
+
+ // motion_velocity
+ line_new += starting_space + base_obj + "set_motion_velocity(" + parts[0] + ")\n";
+
+ // snap
+ if (parts.size() >= 2) {
+ line_new += starting_space + "# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `" + parts[1] + "`\n";
+ }
+
+ // up_direction
+ if (parts.size() >= 3) {
+ line_new += starting_space + base_obj + "set_up_direction(" + parts[2] + ")\n";
+ }
+
+ // stop_on_slope
+ if (parts.size() >= 4) {
+ line_new += starting_space + base_obj + "set_floor_stop_on_slope_enabled(" + parts[3] + ")\n";
+ }
+
+ // max_slides
+ if (parts.size() >= 5) {
+ line_new += starting_space + base_obj + "set_max_slides(" + parts[4] + ")\n";
+ }
+
+ // floor_max_angle
+ if (parts.size() >= 6) {
+ line_new += starting_space + base_obj + "set_floor_max_angle(" + parts[5] + ")\n";
+ }
+
+ // infiinite_interia
+ if (parts.size() >= 7) {
+ line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[6] + "`\n";
+ }
+
+ line = line_new + line.substr(0, start) + "move_and_slide()" + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- sort_custom( a , b ) -> sort_custom(Callable( a , b )) Object
+ if (line.find("sort_custom(") != -1) {
+ int start = line.find("sort_custom(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 2) {
+ line = line.substr(0, start) + "sort_custom(Callable(" + parts[0] + "," + parts[1] + "))" + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- draw_line(1,2,3,4,5) -> draw_line(1,2,3,4) CanvasItem
+ if (line.find("draw_line(") != -1) {
+ int start = line.find("draw_line(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 5) {
+ line = line.substr(0, start) + "draw_line(" + parts[0] + "," + parts[1] + "," + parts[2] + "," + parts[3] + ")" + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- func c(var a, var b) -> func c(a, b)
+ if (line.find("func ") != -1 && line.find("var ") != -1) {
+ int start = line.find("func ");
+ start = line.substr(start).find("(") + start;
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+
+ String start_string = line.substr(0, start) + "(";
+ for (int i = 0; i < parts.size(); i++) {
+ start_string += parts[i].strip_edges().trim_prefix("var ");
+ if (i != parts.size() - 1) {
+ start_string += ", ";
+ }
+ }
+ line = start_string + ")" + line.substr(end + start);
+ }
+ }
+
+ // -- yield(this, \"timeout\") -> await this.timeout GDScript
+ if (line.find("yield(") != -1) {
+ int start = line.find("yield(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 2) {
+ line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\"", "").replace("\'", "").replace(" ", "") + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- parse_json( AA ) -> TODO Object
+ if (line.find("parse_json(") != -1) {
+ int start = line.find("parse_json(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ line = line.substr(0, start) + "JSON.new().stringify(" + connect_arguments(parts, 0) + ")" + line.substr(end + start);
+ }
+ }
+
+ // -- .xform(Vector3(a,b,c)) -> * Vector3(a,b,c) Transform
+ if (line.find(".xform(") != -1) {
+ int start = line.find(".xform(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 1) {
+ line = line.substr(0, start) + " * " + parts[0] + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- .xform_inv(Vector3(a,b,c)) -> / Vector3(a,b,c) Transform
+ if (line.find(".xform_inv(") != -1) {
+ int start = line.find(".xform_inv(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 1) {
+ line = line.substr(0, start) + " / " + parts[0] + line.substr(end + start);
+ }
+ }
+ }
+
+ // -- connect(,,,things) -> connect(,Callable(,),things) Object
+ if (line.find("connect(") != -1) {
+ int start = line.find("connect(");
+ // Protection from disconnect
+ if (start == 0 || line.get(start - 1) != 's') {
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() >= 3) {
+ line = line.substr(0, start) + "connect(" + parts[0] + ",Callable(" + parts[1] + "," + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);
+ }
+ }
+ }
+ }
+ // -- disconnect(a,b,c) -> disconnect(a,Callable(b,c)) Object
+ if (line.find("disconnect(") != -1) {
+ int start = line.find("disconnect(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "disconnect(" + parts[0] + ",Callable(" + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+ // -- is_connected(a,b,c) -> is_connected(a,Callable(b,c)) Object
+ if (line.find("is_connected(") != -1) {
+ int start = line.find("is_connected(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "is_connected(" + parts[0] + ",Callable(" + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+ // -- start(a,b,c) -> start(a,Callable(b,c)) Thread
+ if (line.find("start(") != -1) {
+ int start = line.find("start(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() >= 3) {
+ line = line.substr(0, start) + "start(" + parts[0] + ",Callable(" + parts[1] + "," + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);
+ }
+ }
+ }
+ // -- func _init(p_x:int)->void: -> func _init(p_x:int): Object # https://github.com/godotengine/godot/issues/50589
+ if (line.find(" _init(") != -1) {
+ int start = line.find(" _init(");
+ int end = line.rfind(":") + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ line = line.substr(0, start) + " _init(" + connect_arguments(parts, 0) + "):" + line.substr(end + start);
+ }
+ }
+ // assert(speed < 20, str(randi()%10)) -> assert(speed < 20) #,str(randi()%10)) GDScript - GDScript bug constant message
+ if (line.find("assert(") != -1) {
+ int start = line.find("assert(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 2) {
+ line = line.substr(0, start) + "assert(" + parts[0] + ") " + line.substr(end + start) + "#," + parts[1] + ")";
+ }
+ }
+ }
+ // create_from_image(aa, bb) -> create_from_image(aa) #, bb ImageTexture
+ if (line.find("create_from_image(") != -1) {
+ int start = line.find("create_from_image(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 2) {
+ line = line.substr(0, start) + "create_from_image(" + parts[0] + ") " + "#," + parts[1] + line.substr(end + start);
+ }
+ }
+ }
+ // set_cell_item(a, b, c, d ,e) -> set_cell_item(Vector3(a, b, c), d ,e)
+ if (line.find("set_cell_item(") != -1) {
+ int start = line.find("set_cell_item(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() > 2) {
+ line = line.substr(0, start) + "set_cell_item( Vector3(" + parts[0] + "," + parts[1] + "," + parts[2] + ") " + connect_arguments(parts, 3) + ")" + line.substr(end + start);
+ }
+ }
+ }
+ // get_cell_item(a, b, c) -> get_cell_item(Vector3i(a, b, c))
+ if (line.find("get_cell_item(") != -1) {
+ int start = line.find("get_cell_item(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "get_cell_item(Vector3i(" + parts[0] + "," + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+ // get_cell_item_orientation(a, b, c) -> get_cell_item_orientation(Vector3i(a, b, c))
+ if (line.find("get_cell_item_orientation(") != -1) {
+ int start = line.find("get_cell_item_orientation(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "get_cell_item_orientation(Vector3i(" + parts[0] + "," + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+
+ // TODO - add_surface_from_arrays - additional 4 argument
+ // ENetMultiplayerPeer.create_client - additional argument
+ // ENetMultiplayerPeer.create_server - additional argument
+ // Translation.get_message (and similar)
+ // TreeItem.move_after() - new argument
+ // TreeItem.move_before() - new argument
+ // UndoRedo.commit_action() - additional argument
+
+ if (old_line != line) {
+ found_things.append(simple_line_formatter(current_line, old_line, line));
+ }
+ }
+
+ return found_things;
+}
+
+void ProjectConverter3To4::rename_csharp_functions(String &file_content) {
+ // Custom renaming, each rule needs to be set manually
+ // Don't forget to put validate each rule in validate_conversion function
+ Vector<String> lines = file_content.split("\n");
+
+ for (String &line : lines) {
+ // TODO maybe this can be changed to normal rule
+ line = line.replace("OS.GetWindowSafeArea()", "DisplayServer.ScreenGetUsableRect()");
+
+ // -- Connect(,,,things) -> Connect(,Callable(,),things) Object
+ if (line.find("Connect(") != -1) {
+ int start = line.find("Connect(");
+ // Protection from disconnect
+ if (start == 0 || line.get(start - 1) != 's') {
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() >= 3) {
+ line = line.substr(0, start) + "Connect(" + parts[0] + ",new Callable(" + parts[1] + "," + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);
+ }
+ }
+ }
+ }
+ // -- Disconnect(a,b,c) -> Disconnect(a,Callable(b,c)) Object
+ if (line.find("Disconnect(") != -1) {
+ int start = line.find("Disconnect(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "Disconnect(" + parts[0] + ",new Callable(" + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+ // -- IsConnected(a,b,c) -> IsConnected(a,Callable(b,c)) Object
+ if (line.find("IsConnected(") != -1) {
+ int start = line.find("IsConnected(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "IsConnected(" + parts[0] + ",new Callable(" + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+ }
+
+ // Collect vector to string
+ file_content = "";
+ for (int i = 0; i < lines.size(); i++) {
+ file_content += lines[i];
+
+ if (i != lines.size() - 1) {
+ file_content += "\n";
+ }
+ }
+};
+
+// This is almost 1:1 copy of function which rename gdscript functions
+Vector<String> ProjectConverter3To4::check_for_rename_csharp_functions(Vector<String> &file_content) {
+ int current_line = 1;
+
+ Vector<String> found_things;
+
+ for (String &line : file_content) {
+ String old_line = line;
+
+ // TODO maybe this can be changed to normal rule
+ line = line.replace("OS.GetWindowSafeArea()", "DisplayServer.ScreenGetUsableRect()");
+
+ // -- Connect(,,,things) -> connect(,Callable(,),things) Object
+ if (line.find("Connect(") != -1) {
+ int start = line.find("Connect(");
+ // Protection from disconnect
+ if (start == 0 || line.get(start - 1) != 's') {
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() >= 3) {
+ line = line.substr(0, start) + "Connect(" + parts[0] + ",new Callable(" + parts[1] + "," + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);
+ }
+ }
+ }
+ }
+ // -- Disconnect(a,b,c) -> Disconnect(a,Callable(b,c)) Object
+ if (line.find("Disconnect(") != -1) {
+ int start = line.find("Disconnect(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "Disconnect(" + parts[0] + ",Callable(" + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+ // -- IsConnected(a,b,c) -> IsConnected(a,Callable(b,c)) Object
+ if (line.find("IsConnected(") != -1) {
+ int start = line.find("IsConnected(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 3) {
+ line = line.substr(0, start) + "IsConnected(" + parts[0] + ",Callable(" + parts[1] + "," + parts[2] + "))" + line.substr(end + start);
+ }
+ }
+ }
+
+ if (old_line != line) {
+ found_things.append(simple_line_formatter(current_line, old_line, line));
+ }
+ }
+
+ return found_things;
+}
+
+void ProjectConverter3To4::rename_gdscript_keywords(String &file_content){
+ { RegEx reg_tool = RegEx("([\n]+)tool");
+CRASH_COND(!reg_tool.is_valid());
+file_content = reg_tool.sub(file_content, "$1@tool", true);
+RegEx reg_tool2 = RegEx("^tool");
+CRASH_COND(!reg_tool2.is_valid());
+file_content = reg_tool2.sub(file_content, "@tool", true);
+}
+{
+ RegEx reg_export = RegEx("([\n\t]+)export\\b");
+ CRASH_COND(!reg_export.is_valid());
+ file_content = reg_export.sub(file_content, "$1@export", true);
+ RegEx reg_export2 = RegEx("^export");
+ CRASH_COND(!reg_export2.is_valid());
+ file_content = reg_export2.sub(file_content, "@export", true);
+}
+{
+ RegEx reg_onready = RegEx("([\n]+)onready");
+ CRASH_COND(!reg_onready.is_valid());
+ file_content = reg_onready.sub(file_content, "$1@onready", true);
+ RegEx reg_onready2 = RegEx("^onready");
+ CRASH_COND(!reg_onready2.is_valid());
+ file_content = reg_onready2.sub(file_content, "@onready", true);
+}
+{
+ RegEx reg_master = RegEx("([\n]+)master func");
+ CRASH_COND(!reg_master.is_valid());
+ file_content = reg_master.sub(file_content, "$1@rpc(any) func", true);
+ RegEx reg_master2 = RegEx("^master func");
+ CRASH_COND(!reg_master2.is_valid());
+ file_content = reg_master2.sub(file_content, "@rpc(any) func", true);
+}
+{
+ RegEx reg_puppet = RegEx("([\n]+)puppet func");
+ CRASH_COND(!reg_puppet.is_valid());
+ file_content = reg_puppet.sub(file_content, "$1@rpc(auth) func", true);
+ RegEx reg_puppet2 = RegEx("^puppet func");
+ CRASH_COND(!reg_puppet2.is_valid());
+ file_content = reg_puppet2.sub(file_content, "@rpc(auth) func", true);
+}
+{
+ RegEx reg_remote = RegEx("([\n]+)remote func");
+ CRASH_COND(!reg_remote.is_valid());
+ file_content = reg_remote.sub(file_content, "$1@rpc(any) func", true);
+ RegEx reg_remote2 = RegEx("^remote func");
+ CRASH_COND(!reg_remote2.is_valid());
+ file_content = reg_remote2.sub(file_content, "@rpc(any) func", true);
+}
+{
+ RegEx reg_remotesync = RegEx("([\n]+)remotesync func");
+ CRASH_COND(!reg_remotesync.is_valid());
+ file_content = reg_remotesync.sub(file_content, "$1@rpc(any,sync) func", true);
+ RegEx reg_remotesync2 = RegEx("^remotesync func");
+ CRASH_COND(!reg_remotesync2.is_valid());
+ file_content = reg_remotesync2.sub(file_content, "@rpc(any,sync) func", true);
+}
+{
+ RegEx reg_sync = RegEx("([\n]+)sync func");
+ CRASH_COND(!reg_sync.is_valid());
+ file_content = reg_sync.sub(file_content, "$1@rpc(any,sync) func", true);
+ RegEx reg_sync2 = RegEx("^sync func");
+ CRASH_COND(!reg_sync2.is_valid());
+ file_content = reg_sync2.sub(file_content, "@rpc(any,sync) func", true);
+}
+{
+ RegEx reg_puppetsync = RegEx("([\n]+)puppetsync func");
+ CRASH_COND(!reg_puppetsync.is_valid());
+ file_content = reg_puppetsync.sub(file_content, "$1@rpc(auth,sync) func", true);
+ RegEx reg_puppetsync2 = RegEx("^puppetsync func");
+ CRASH_COND(!reg_puppetsync2.is_valid());
+ file_content = reg_puppetsync2.sub(file_content, "@rpc(auth,sync) func", true);
+}
+{
+ RegEx reg_mastersync = RegEx("([\n]+)mastersync func");
+ CRASH_COND(!reg_mastersync.is_valid());
+ file_content = reg_mastersync.sub(file_content, "$1@rpc(any,sync) func", true);
+ RegEx reg_mastersync2 = RegEx("^mastersync func");
+ CRASH_COND(!reg_mastersync2.is_valid());
+ file_content = reg_mastersync2.sub(file_content, "@rpc(any,sync) func", true);
+}
+}
+;
+
+Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<String> &file_content) {
+ Vector<String> found_things;
+
+ int current_line = 1;
+
+ for (String &line : file_content) {
+ String old;
+ old = line;
+ {
+ RegEx reg_tool2 = RegEx("^tool");
+ CRASH_COND(!reg_tool2.is_valid());
+ line = reg_tool2.sub(line, "@tool", true);
+ }
+ if (old != line) {
+ found_things.append(line_formatter(current_line, "tool", "@tool", line));
+ }
+ old = line;
+ {
+ RegEx reg_export = RegEx("([\t]+)export\\b");
+ CRASH_COND(!reg_export.is_valid());
+ line = reg_export.sub(line, "$1@export", true);
+ RegEx reg_export2 = RegEx("^export");
+ CRASH_COND(!reg_export2.is_valid());
+ line = reg_export2.sub(line, "@export", true);
+ }
+ if (old != line) {
+ found_things.append(line_formatter(current_line, "export", "@export", line));
+ }
+ old = line;
+ {
+ RegEx reg_onready2 = RegEx("^onready");
+ CRASH_COND(!reg_onready2.is_valid());
+ line = reg_onready2.sub(line, "@onready", true);
+ }
+ if (old != line) {
+ found_things.append(line_formatter(current_line, "onready", "@onready", line));
+ }
+ old = line;
+ {
+ RegEx reg_master2 = RegEx("^master func");
+ CRASH_COND(!reg_master2.is_valid());
+ line = reg_master2.sub(line, "@rpc(any) func", true);
+ }
+ if (old != line) {
+ found_things.append(line_formatter(current_line, "master func", "@rpc(any) func", line));
+ }
+ old = line;
+ {
+ RegEx reg_puppet2 = RegEx("^puppet func");
+ CRASH_COND(!reg_puppet2.is_valid());
+ line = reg_puppet2.sub(line, "@rpc(auth) func", true);
+ }
+ if (old != line) {
+ found_things.append(line_formatter(current_line, "puppet func", "@rpc(auth) func", line));
+ }
+ old = line;
+ {
+ RegEx reg_remote2 = RegEx("^remote func");
+ CRASH_COND(!reg_remote2.is_valid());
+ line = reg_remote2.sub(line, "@rpc(any) func", true);
+ }
+ if (old != line) {
+ found_things.append(line_formatter(current_line, "remote func", "@rpc(any) func", line));
+ }
+ old = line;
+ {
+ RegEx reg_remotesync2 = RegEx("^remotesync func");
+ CRASH_COND(!reg_remotesync2.is_valid());
+ line = reg_remotesync2.sub(line, "@rpc(any,sync) func", true);
+ }
+ if (old != line) {
+ found_things.append(line_formatter(current_line, "remotesync func", "@rpc(any,sync) func", line));
+ }
+ old = line;
+ {
+ RegEx reg_sync2 = RegEx("^sync func");
+ CRASH_COND(!reg_sync2.is_valid());
+ line = reg_sync2.sub(line, "@rpc(any,sync) func", true);
+ }
+ if (old != line) {
+ found_things.append(line_formatter(current_line, "sync func", "@rpc(any,sync) func", line));
+ }
+ old = line;
+ {
+ RegEx reg_puppetsync2 = RegEx("^puppetsync func");
+ CRASH_COND(!reg_puppetsync2.is_valid());
+ line = reg_puppetsync2.sub(line, "@rpc(auth,sync) func", true);
+ }
+ if (old != line) {
+ found_things.append(line_formatter(current_line, "puppetsync func", "@rpc(any,sync) func", line));
+ }
+ old = line;
+ {
+ RegEx reg_mastersync2 = RegEx("^mastersync func");
+ CRASH_COND(!reg_mastersync2.is_valid());
+ line = reg_mastersync2.sub(line, "@rpc(any,sync) func", true);
+ }
+ if (old != line) {
+ found_things.append(line_formatter(current_line, "mastersync", "@rpc(any,sync) func", line));
+ }
+ old = line;
+
+ current_line++;
+ }
+
+ return found_things;
+}
+
+void ProjectConverter3To4::custom_rename(String &file_content, String from, String to) {
+ RegEx reg = RegEx(String("\\b") + from + "\\b");
+ CRASH_COND(!reg.is_valid());
+ file_content = reg.sub(file_content, to, true);
+};
+
+Vector<String> ProjectConverter3To4::check_for_custom_rename(Vector<String> &file_content, String from, String to) {
+ Vector<String> found_things;
+
+ RegEx reg = RegEx(String("\\b") + from + "\\b");
+ CRASH_COND(!reg.is_valid());
+
+ int current_line = 1;
+ for (String &line : file_content) {
+ Array reg_match = reg.search_all(line);
+ if (reg_match.size() > 0) {
+ found_things.append(line_formatter(current_line, from.replace("\\.", "."), to, line)); // Without replacing it will print "\.shader" instead ".shader"
+ }
+ current_line++;
+ }
+ return found_things;
+}
+
+void ProjectConverter3To4::rename_common(const char *array[][2], String &file_content) {
+ int current_index = 0;
+ while (array[current_index][0]) {
+ RegEx reg = RegEx(String("\\b") + array[current_index][0] + "\\b");
+ CRASH_COND(!reg.is_valid());
+ file_content = reg.sub(file_content, array[current_index][1], true);
+ current_index++;
+ }
+}
+
+// Common renaming,
+Vector<String> ProjectConverter3To4::check_for_rename_common(const char *array[][2], Vector<String> &file_content) {
+ int current_index = 0;
+
+ Vector<String> found_things;
+
+ while (array[current_index][0]) {
+ RegEx reg = RegEx(String("\\b") + array[current_index][0] + "\\b");
+ CRASH_COND(!reg.is_valid());
+
+ int current_line = 1;
+ for (String &line : file_content) {
+ Array reg_match = reg.search_all(line);
+ if (reg_match.size() > 0) {
+ found_things.append(line_formatter(current_line, array[current_index][0], array[current_index][1], line));
+ }
+ current_line++;
+ }
+ current_index++;
+ }
+ return found_things;
+}
+
+// Formats data to print them into user console when trying to convert data
+String ProjectConverter3To4::line_formatter(int current_line, String from, String to, String line) {
+ if (from.size() > 200) {
+ from = from.substr(0, 197) + "...";
+ }
+ if (to.size() > 200) {
+ to = to.substr(0, 197) + "...";
+ }
+ if (line.size() > 400) {
+ line = line.substr(0, 397) + "...";
+ }
+ return String("Line (") + itos(current_line) + ") " + from.replace("\r", "").replace("\n", "") + " -> " + to.replace("\r", "").replace("\n", "") + " - LINE \"\"\" " + line.replace("\r", "").replace("\n", "").strip_edges() + " \"\"\"";
+}
+
+String ProjectConverter3To4::simple_line_formatter(int current_line, String old_line, String line) {
+ if (old_line.size() > 1000) {
+ old_line = old_line.substr(0, 997) + "...";
+ }
+ if (line.size() > 1000) {
+ line = line.substr(0, 997) + "...";
+ }
+ return String("Line (") + itos(current_line) + ") - FULL LINES - \"\"\"" + old_line.replace("\r", "").replace("\n", "").strip_edges() + "\"\"\" =====> \"\"\" " + line.replace("\r", "").replace("\n", "").strip_edges() + " \"\"\"";
+}
+
+#else // No regex.
+
+int ProjectConverter3To4::convert() {
+ ERR_FAIL_V_MSG(ERROR_CODE, "Can't run converter for Godot 3.x projects as RegEx module is disabled.");
+}
+
+int ProjectConverter3To4::validate_conversion() {
+ ERR_FAIL_V_MSG(ERROR_CODE, "Can't validate conversion for Godot 3.x projects as RegEx module is disabled.");
+}
+
+#endif // MODULE_REGEX_ENABLED
diff --git a/editor/project_converter_3_to_4.h b/editor/project_converter_3_to_4.h
new file mode 100644
index 0000000000..95239666e0
--- /dev/null
+++ b/editor/project_converter_3_to_4.h
@@ -0,0 +1,83 @@
+/*************************************************************************/
+/* project_converter_3_to_4.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef PROJECT_CONVERTER_3_TO_4_H
+#define PROJECT_CONVERTER_3_TO_4_H
+
+#include "core/core_bind.h"
+#include "core/io/file_access.h"
+#include "core/object/ref_counted.h"
+#include "core/string/ustring.h"
+
+class ProjectConverter3To4 {
+ void rename_enums(String &file_content);
+ Vector<String> check_for_rename_enums(Vector<String> &file_content);
+
+ void rename_classes(String &file_content);
+ Vector<String> check_for_rename_classes(Vector<String> &file_content);
+
+ void rename_gdscript_functions(String &file_content);
+ Vector<String> check_for_rename_gdscript_functions(Vector<String> &file_content);
+
+ void rename_csharp_functions(String &file_content);
+ Vector<String> check_for_rename_csharp_functions(Vector<String> &file_content);
+
+ void rename_gdscript_keywords(String &file_content);
+ Vector<String> check_for_rename_gdscript_keywords(Vector<String> &file_content);
+
+ void custom_rename(String &file_content, String from, String to);
+ Vector<String> check_for_custom_rename(Vector<String> &file_content, String from, String to);
+
+ void rename_common(const char *array[][2], String &file_content);
+ Vector<String> check_for_rename_common(const char *array[][2], Vector<String> &file_content);
+
+ Vector<String> check_for_files();
+
+ Vector<String> parse_arguments(const String &line);
+ int get_end_parenthess(const String &line) const;
+ String connect_arguments(const Vector<String> &line, int from, int to = -1) const;
+ String get_starting_space(const String &line) const;
+ String get_object_of_execution(const String &line) const;
+
+ String line_formatter(int current_line, String from, String to, String line);
+ String simple_line_formatter(int current_line, String old_line, String line);
+
+ bool test_single_array(const char *array[][2], bool ignore_second_check = false);
+ bool test_conversion_single_additional(String name, String expected, void (ProjectConverter3To4::*func)(String &), String what);
+ bool test_conversion_single_normal(String name, String expected, const char *array[][2], String what);
+ bool test_array_names();
+ bool test_conversion();
+
+public:
+ int validate_conversion();
+ int convert();
+};
+
+#endif // PROJECT_CONVERTER_3_TO_4_H
diff --git a/editor/project_export.cpp b/editor/project_export.cpp
index 70627d5e82..030337a4bc 100644
--- a/editor/project_export.cpp
+++ b/editor/project_export.cpp
@@ -928,17 +928,13 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) {
ERR_FAIL_COND(platform.is_null());
current->set_export_path(p_path);
+ platform->clear_messages();
Error err = platform->export_project(current, export_debug->is_pressed(), p_path, 0);
- if (err != OK && err != ERR_SKIP) {
- if (err == ERR_FILE_NOT_FOUND) {
- error_dialog->set_text(vformat(TTR("Failed to export the project for platform '%s'.\nExport templates seem to be missing or invalid."), platform->get_name()));
- } else { // Assume misconfiguration. FIXME: Improve error handling and preset config validation.
- error_dialog->set_text(vformat(TTR("Failed to export the project for platform '%s'.\nThis might be due to a configuration issue in the export preset or your export settings."), platform->get_name()));
+ result_dialog_log->clear();
+ if (err != ERR_SKIP) {
+ if (platform->fill_log_messages(result_dialog_log, err)) {
+ result_dialog->popup_centered_ratio(0.5);
}
-
- ERR_PRINT(vformat("Failed to export the project for platform '%s'.", platform->get_name()));
- error_dialog->show();
- error_dialog->popup_centered(Size2(300, 80));
}
}
@@ -957,6 +953,8 @@ void ProjectExportDialog::_export_all(bool p_debug) {
String mode = p_debug ? TTR("Debug") : TTR("Release");
EditorProgress ep("exportall", TTR("Exporting All") + " " + mode, EditorExport::get_singleton()->get_export_preset_count(), true);
+ bool show_dialog = false;
+ result_dialog_log->clear();
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
ERR_FAIL_COND(preset.is_null());
@@ -965,17 +963,16 @@ void ProjectExportDialog::_export_all(bool p_debug) {
ep.step(preset->get_name(), i);
+ platform->clear_messages();
Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0);
- if (err != OK && err != ERR_SKIP) {
- if (err == ERR_FILE_BAD_PATH) {
- error_dialog->set_text(TTR("The given export path doesn't exist:") + "\n" + preset->get_export_path().get_base_dir());
- } else {
- error_dialog->set_text(TTR("Export templates for this platform are missing/corrupted:") + " " + platform->get_name());
- }
- error_dialog->show();
- error_dialog->popup_centered(Size2(300, 80));
- ERR_PRINT("Failed to export project");
+ if (err == ERR_SKIP) {
+ return;
}
+ bool has_messages = platform->fill_log_messages(result_dialog_log, err);
+ show_dialog = show_dialog || has_messages;
+ }
+ if (show_dialog) {
+ result_dialog->popup_centered_ratio(0.5);
}
}
@@ -1030,10 +1027,12 @@ ProjectExportDialog::ProjectExportDialog() {
mc->add_child(presets);
presets->connect("item_selected", callable_mp(this, &ProjectExportDialog::_edit_preset));
duplicate_preset = memnew(Button);
+ duplicate_preset->set_tooltip(TTR("Duplicate"));
duplicate_preset->set_flat(true);
preset_hb->add_child(duplicate_preset);
duplicate_preset->connect("pressed", callable_mp(this, &ProjectExportDialog::_duplicate_preset));
delete_preset = memnew(Button);
+ delete_preset->set_tooltip(TTR("Delete"));
delete_preset->set_flat(true);
preset_hb->add_child(delete_preset);
delete_preset->connect("pressed", callable_mp(this, &ProjectExportDialog::_delete_preset));
@@ -1248,11 +1247,14 @@ ProjectExportDialog::ProjectExportDialog() {
export_error2->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor")));
export_error2->set_text(String::utf8("• ") + TTR("Export templates for this platform are missing:") + " ");
- error_dialog = memnew(AcceptDialog);
- error_dialog->set_title(TTR("Error"));
- error_dialog->set_text(TTR("Export templates for this platform are missing/corrupted:") + " ");
- main_vb->add_child(error_dialog);
- error_dialog->hide();
+ result_dialog = memnew(AcceptDialog);
+ result_dialog->set_title(TTR("Project Export"));
+ result_dialog_log = memnew(RichTextLabel);
+ result_dialog_log->set_custom_minimum_size(Size2(300, 80) * EDSCALE);
+ result_dialog->add_child(result_dialog_log);
+
+ main_vb->add_child(result_dialog);
+ result_dialog->hide();
LinkButton *download_templates = memnew(LinkButton);
download_templates->set_text(TTR("Manage Export Templates"));
diff --git a/editor/project_export.h b/editor/project_export.h
index 4d1719d6eb..6b10642495 100644
--- a/editor/project_export.h
+++ b/editor/project_export.h
@@ -73,7 +73,8 @@ private:
Button *button_export = nullptr;
bool updating = false;
- AcceptDialog *error_dialog = nullptr;
+ RichTextLabel *result_dialog_log = nullptr;
+ AcceptDialog *result_dialog = nullptr;
ConfirmationDialog *delete_confirm = nullptr;
OptionButton *export_filter = nullptr;
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 1ddefeba77..08df4cdf3c 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -71,9 +71,17 @@ void SceneTreeDock::input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
- if (pending_click_select && mb.is_valid() && !mb->is_pressed() && (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT)) {
- _push_item(pending_click_select);
- pending_click_select = nullptr;
+ if (mb.is_valid() && (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT)) {
+ if (mb->is_pressed() && scene_tree->get_rect().has_point(mb->get_position())) {
+ tree_clicked = true;
+ } else if (!mb->is_pressed()) {
+ tree_clicked = false;
+ }
+
+ if (!mb->is_pressed() && pending_click_select) {
+ _push_item(pending_click_select);
+ pending_click_select = nullptr;
+ }
}
}
@@ -1371,10 +1379,10 @@ void SceneTreeDock::_push_item(Object *p_object) {
}
void SceneTreeDock::_handle_select(Node *p_node) {
- if ((Input::get_singleton()->get_mouse_button_mask() & (MouseButton::MASK_LEFT | MouseButton::MASK_RIGHT)) != MouseButton::NONE) {
+ if (tree_clicked) {
pending_click_select = p_node;
} else {
- EditorNode::get_singleton()->push_item(p_node);
+ _push_item(p_node);
}
}
@@ -2647,7 +2655,7 @@ void SceneTreeDock::_add_children_to_popup(Object *p_obj, int p_depth) {
}
int index = menu_subresources->get_item_count();
menu_subresources->add_icon_item(icon, E.name.capitalize(), EDIT_SUBRESOURCE_BASE + subresources.size());
- menu_subresources->set_item_h_offset(index, p_depth * 10 * EDSCALE);
+ menu_subresources->set_item_horizontal_offset(index, p_depth * 10 * EDSCALE);
subresources.push_back(obj->get_instance_id());
_add_children_to_popup(obj, p_depth + 1);
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 54e6108d84..dc7f3476ee 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -172,6 +172,7 @@ class SceneTreeDock : public VBoxContainer {
Node *scene_root = nullptr;
Node *edited_scene = nullptr;
Node *pending_click_select = nullptr;
+ bool tree_clicked = false;
VBoxContainer *create_root_dialog = nullptr;
String selected_favorite_root;