diff --git a/core/object/object.cpp b/core/object/object.cpp index ef1ca8132cbd..0f803cdccee3 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -931,6 +931,7 @@ void Object::set_script(const Variant &p_script) { if (!p_script.is_null()) { ERR_FAIL_COND_MSG(s.is_null(), "Cannot set object script. Parameter should be null or a reference to a valid script."); ERR_FAIL_COND_MSG(s->is_abstract(), vformat("Cannot set object script. Script '%s' should not be abstract.", s->get_path())); + ERR_FAIL_COND_MSG(!s->is_attachable(), vformat("Cannot set object script. Script '%s' is not attachable.", s->get_path())); } script = p_script; diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 5afe45ca359f..7faaf6316678 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -173,6 +173,7 @@ void Script::_bind_methods() { ClassDB::bind_method(D_METHOD("is_tool"), &Script::is_tool); ClassDB::bind_method(D_METHOD("is_abstract"), &Script::is_abstract); + ClassDB::bind_method(D_METHOD("is_attachable"), &Script::is_attachable); ClassDB::bind_method(D_METHOD("get_rpc_config"), &Script::get_rpc_config); diff --git a/core/object/script_language.h b/core/object/script_language.h index 31d6638e58e8..13395042f50e 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -166,6 +166,7 @@ class Script : public Resource { virtual bool is_tool() const = 0; virtual bool is_valid() const = 0; virtual bool is_abstract() const = 0; + virtual bool is_attachable() const { return true; } virtual ScriptLanguage *get_language() const = 0; @@ -214,6 +215,7 @@ class ScriptLanguage : public Object { virtual void init() = 0; virtual String get_type() const = 0; virtual String get_extension() const = 0; + virtual bool is_language_script_attachable() const { return true; } virtual void finish() = 0; /* EDITOR FUNCTIONS */ diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml index 80aad9d30d1a..c6d4457d5969 100644 --- a/doc/classes/Script.xml +++ b/doc/classes/Script.xml @@ -6,7 +6,7 @@ A class stored as a resource. A script extends the functionality of all objects that instantiate it. This is the base class for all scripts and should not be used directly. Trying to create a new script with this class will result in an error. - The [code]new[/code] method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. + The [code]new[/code] method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes and if script is attachable check by [method is_attachable]. $DOCS_URL/tutorials/scripting/index.html @@ -115,6 +115,12 @@ Returns [code]true[/code] if the script is an abstract script. An abstract script does not have a constructor and cannot be instantiated. + + + + Returns [code]true[/code] if the script is able to be attached to [Object]. + + diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 56b75b0b2f0b..3b0024a8a3c4 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -459,7 +459,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread stack_dump_info.push_back(d); s->set_metadata(0, d); - String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]) + " - at function: " + String(d["function"]); + String line = itos(i) + " - " + String(d["file"]) + ":" + itos(d["line"]); + if (!String(d["function"]).is_empty()) { + line += " - at function: " + String(d["function"]); + } s->set_text(0, line); if (i == 0) { diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index a9b2a50ae8e7..04e9fa52fd69 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -4266,6 +4266,7 @@ FileSystemDock::FileSystemDock() { make_scene_dialog->connect(SceneStringName(confirmed), callable_mp(this, &FileSystemDock::_make_scene_confirm)); make_script_dialog = memnew(ScriptCreateDialog); + make_script_dialog->set_languages_list(false); make_script_dialog->set_title(TTR("Create Script")); add_child(make_script_dialog); diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index c5ee8e7b3f92..261d1aa70325 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -129,32 +129,16 @@ void ScriptCreateDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)); - - EditorData &ed = EditorNode::get_editor_data(); - - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - // Check if the extension has an icon first. - String script_type = ScriptServer::get_language(i)->get_type(); - Ref language_icon = get_editor_theme_icon(script_type); - if (language_icon.is_null() || language_icon == ThemeDB::get_singleton()->get_fallback_icon()) { - // The theme doesn't have an icon for this language, ask the extensions. - Ref extension_language_icon = ed.extension_class_get_icon(script_type); - if (extension_language_icon.is_valid()) { - language_menu->get_popup()->set_item_icon_max_width(i, icon_size); - language_icon = extension_language_icon; - } - } - - if (language_icon.is_valid()) { - language_menu->set_item_icon(i, language_icon); - } - } + set_languages_list(is_languages_list_only_attachable); path_button->set_button_icon(get_editor_theme_icon(SNAME("Folder"))); parent_browse_button->set_button_icon(get_editor_theme_icon(SNAME("Folder"))); parent_search_button->set_button_icon(get_editor_theme_icon(SNAME("ClassList"))); } break; + + case NOTIFICATION_POSTINITIALIZE: { + set_languages_list(is_languages_list_only_attachable); + } break; } } @@ -210,6 +194,51 @@ void ScriptCreateDialog::set_inheritance_base_type(const String &p_base) { base_type = p_base; } +void ScriptCreateDialog::set_languages_list(const bool p_only_attachable) { + const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)); + EditorData &ed = EditorNode::get_editor_data(); + is_languages_list_only_attachable = p_only_attachable; + String previous_default; + if (language_menu->get_selected_id() > -1) { + previous_default = language_menu->get_item_text(language_menu->get_selected_id()); + } + default_language = -1; + language_menu->clear(); + language_list.clear(); + int menu_location = 0; + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptLanguage *lang = ScriptServer::get_language(i); + if (p_only_attachable && !lang->is_language_script_attachable()) { + continue; + } + language_menu->add_item(lang->get_name()); + String script_type = lang->get_type(); + Ref language_icon = get_editor_theme_icon(script_type); + if (language_icon.is_null() || language_icon == ThemeDB::get_singleton()->get_fallback_icon()) { + // The theme doesn't have an icon for this language, ask the extensions. + Ref extension_language_icon = ed.extension_class_get_icon(script_type); + if (extension_language_icon.is_valid()) { + language_menu->get_popup()->set_item_icon_max_width(menu_location, icon_size); + language_icon = extension_language_icon; + } + } + if (language_icon.is_valid()) { + language_menu->set_item_icon(menu_location, language_icon); + } + if (lang->get_name() == previous_default) { + default_language = menu_location; + } + if (default_language == -1 && lang->get_name() == "GDScript") { + default_language = menu_location; + } + language_list.append(lang); + menu_location++; + } + if (default_language >= 0) { + language_menu->select(default_language); + } +} + bool ScriptCreateDialog::_validate_parent(const String &p_string) { if (p_string.length() == 0) { return false; @@ -278,7 +307,7 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must for (const String &E : extensions) { if (E.nocasecmp_to(extension) == 0) { found = true; - if (E == ScriptServer::get_language(language_menu->get_selected())->get_extension()) { + if (E == language->get_extension()) { match = true; } break; @@ -288,12 +317,25 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must if (!found) { return TTR("Invalid extension."); } - if (!match) { + if (!match && _extention_update_selected_language(p.get_extension()) != OK) { return TTR("Extension doesn't match chosen language."); } // Let ScriptLanguage do custom validation. - return ScriptServer::get_language(language_menu->get_selected())->validate_path(p); + return language->validate_path(p); +} + +Error ScriptCreateDialog::_extention_update_selected_language(const String &p_extention) { + for (int i = 0; i < language_list.size(); i++) { + ScriptLanguage *lang = language_list[i]; + if (p_extention == lang->get_extension()) { + language_menu->select(i); + _language_changed(i); + file_path->set_caret_column(file_path->get_text().length()); + return OK; + } + } + return FAILED; } void ScriptCreateDialog::_parent_name_changed(const String &p_parent) { @@ -360,7 +402,7 @@ void ScriptCreateDialog::_create_new() { } String class_name = file_path->get_text().get_file().get_basename(); - scr = ScriptServer::get_language(language_menu->get_selected())->make_template(sinfo.content, class_name, parent_class); + scr = language->make_template(sinfo.content, class_name, parent_class); if (is_built_in) { scr->set_name(built_in_name->get_text()); @@ -396,7 +438,7 @@ void ScriptCreateDialog::_load_exist() { } void ScriptCreateDialog::_language_changed(int l) { - language = ScriptServer::get_language(l); + language = language_list[l]; can_inherit_from_file = language->can_inherit_from_file(); supports_built_in = language->supports_builtin_mode(); @@ -872,18 +914,6 @@ ScriptCreateDialog::ScriptCreateDialog() { gc->add_child(memnew(Label(TTR("Language:")))); gc->add_child(language_menu); - default_language = -1; - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - String lang = ScriptServer::get_language(i)->get_name(); - language_menu->add_item(lang); - if (lang == "GDScript") { - default_language = i; - } - } - if (default_language >= 0) { - language_menu->select(default_language); - } - language_menu->connect(SceneStringName(item_selected), callable_mp(this, &ScriptCreateDialog::_language_changed)); /* Inherits */ diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h index c8f043b42013..702e06a0dfd3 100644 --- a/editor/script_create_dialog.h +++ b/editor/script_create_dialog.h @@ -68,6 +68,7 @@ class ScriptCreateDialog : public ConfirmationDialog { AcceptDialog *alert = nullptr; CreateDialog *select_class = nullptr; + bool is_languages_list_only_attachable = true; bool is_browsing_parent = false; String path_error; String template_inactive_message; @@ -89,6 +90,7 @@ class ScriptCreateDialog : public ConfirmationDialog { Vector template_list; ScriptLanguage *language = nullptr; + Vector language_list; String base_type; @@ -100,6 +102,7 @@ class ScriptCreateDialog : public ConfirmationDialog { void _use_template_pressed(); bool _validate_parent(const String &p_string); String _validate_path(const String &p_path, bool p_file_must_exist); + Error _extention_update_selected_language(const String &p_extention); void _parent_name_changed(const String &p_parent); void _template_changed(int p_template = 0); void _browse_path(bool browse_parent, bool p_save); @@ -124,6 +127,7 @@ class ScriptCreateDialog : public ConfirmationDialog { public: void config(const String &p_base_name, const String &p_base_path, bool p_built_in_enabled = true, bool p_load_enabled = true); void set_inheritance_base_type(const String &p_base); + void set_languages_list(const bool p_only_attachable); ScriptCreateDialog(); }; diff --git a/modules/gdscript/.editorconfig b/modules/gdscript/.editorconfig index b380846f8634..b0aefb374049 100644 --- a/modules/gdscript/.editorconfig +++ b/modules/gdscript/.editorconfig @@ -1,3 +1,7 @@ [*.gd] indent_size = 4 trim_trailing_whitespace = true + +[*.gdt] +indent_size = 4 +trim_trailing_whitespace = true diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py index ecd33a5daccc..e92f5f36c966 100644 --- a/modules/gdscript/config.py +++ b/modules/gdscript/config.py @@ -10,7 +10,9 @@ def configure(env): def get_doc_classes(): return [ "@GDScript", + "@GDTrait", "GDScript", + "GDTrait", "GDScriptSyntaxHighlighter", ] diff --git a/modules/gdscript/doc_classes/@GDTrait.xml b/modules/gdscript/doc_classes/@GDTrait.xml new file mode 100644 index 000000000000..ac6229be04a5 --- /dev/null +++ b/modules/gdscript/doc_classes/@GDTrait.xml @@ -0,0 +1,788 @@ + + + + Built-in GDScript constants, functions, and annotations. + + + A list of GDScript-specific utility functions and annotations accessible from any script. + For the list of the global functions and constants see [@GlobalScope]. + + + $DOCS_URL/tutorials/scripting/gdscript/gdscript_exports.html + + + + + + + + + + Returns a [Color] constructed from red ([param r8]), green ([param g8]), blue ([param b8]), and optionally alpha ([param a8]) integer channels, each divided by [code]255.0[/code] for their final value. Using [method Color8] instead of the standard [Color] constructor is useful when you need to match exact color values in an [Image]. + [codeblock] + var red = Color8(255, 0, 0) # Same as Color(1, 0, 0). + var dark_blue = Color8(0, 0, 51) # Same as Color(0, 0, 0.2). + var my_color = Color8(306, 255, 0, 102) # Same as Color(1.2, 1, 0, 0.4). + [/codeblock] + [b]Note:[/b] Due to the lower precision of [method Color8] compared to the standard [Color] constructor, a color created with [method Color8] will generally not be equal to the same color created with the standard [Color] constructor. Use [method Color.is_equal_approx] for comparisons to avoid issues with floating-point precision error. + + + + + + + + Asserts that the [param condition] is [code]true[/code]. If the [param condition] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method @GlobalScope.push_error] for reporting errors to project developers or add-on users. + An optional [param message] can be shown in addition to the generic "Assertion failed" message. You can use this to provide additional details about why the assertion failed. + [b]Warning:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode. + [codeblock] + # Imagine we always want speed to be between 0 and 20. + var speed = -10 + assert(speed < 20) # True, the program will continue. + assert(speed >= 0) # False, the program will stop. + assert(speed >= 0 and speed < 20) # You can also combine the two conditional statements in one check. + assert(speed < 20, "the speed limit is 20") # Show a message. + [/codeblock] + [b]Note:[/b] [method assert] is a keyword, not a function. So you cannot access it as a [Callable] or use it inside expressions. + + + + + + + Returns a single character (as a [String]) of the given Unicode code point (which is compatible with ASCII code). + [codeblock] + var upper = char(65) # upper is "A" + var lower = char(65 + 32) # lower is "a" + var euro = char(8364) # euro is "€" + [/codeblock] + + + + + + + + Converts [param what] to [param type] in the best way possible. The [param type] uses the [enum Variant.Type] values. + [codeblock] + var a = [4, 2.5, 1.2] + print(a is Array) # Prints true + + var b = convert(a, TYPE_PACKED_BYTE_ARRAY) + print(b) # Prints [4, 2, 1] + print(b is Array) # Prints false + [/codeblock] + + + + + + + Converts a [param dictionary] (created with [method inst_to_dict]) back to an Object instance. Can be useful for deserializing. + + + + + + Returns an array of dictionaries representing the current call stack. See also [method print_stack]. + [codeblock] + func _ready(): + foo() + + func foo(): + bar() + + func bar(): + print(get_stack()) + [/codeblock] + Starting from [code]_ready()[/code], [code]bar()[/code] would print: + [codeblock lang=text] + [{function:bar, line:12, source:res://script.gd}, {function:foo, line:9, source:res://script.gd}, {function:_ready, line:6, source:res://script.gd}] + [/codeblock] + [b]Note:[/b] This function only works if the running instance is connected to a debugging server (i.e. an editor instance). [method get_stack] will not work in projects exported in release mode, or in projects exported in debug mode if not connected to a debugging server. + [b]Note:[/b] Calling this function from a [Thread] is not supported. Doing so will return an empty array. + + + + + + + Returns the passed [param instance] converted to a Dictionary. Can be useful for serializing. + [b]Note:[/b] Cannot be used to serialize objects with built-in scripts attached or objects allocated within built-in scripts. + [codeblock] + var foo = "bar" + func _ready(): + var d = inst_to_dict(self) + print(d.keys()) + print(d.values()) + [/codeblock] + Prints out: + [codeblock lang=text] + [@subpath, @path, foo] + [, res://test.gd, bar] + [/codeblock] + + + + + + + + Returns [code]true[/code] if [param value] is an instance of [param type]. The [param type] value must be one of the following: + - A constant from the [enum Variant.Type] enumeration, for example [constant TYPE_INT]. + - An [Object]-derived class which exists in [ClassDB], for example [Node]. + - A [Script] (you can use any class, including inner one). + Unlike the right operand of the [code]is[/code] operator, [param type] can be a non-constant value. The [code]is[/code] operator supports more features (such as typed arrays). Use the operator instead of this method if you do not need dynamic type checking. + [b]Examples:[/b] + [codeblock] + print(is_instance_of(a, TYPE_INT)) + print(is_instance_of(a, Node)) + print(is_instance_of(a, MyClass)) + print(is_instance_of(a, MyClass.InnerClass)) + [/codeblock] + [b]Note:[/b] If [param value] and/or [param type] are freed objects (see [method @GlobalScope.is_instance_valid]), or [param type] is not one of the above options, this method will raise a runtime error. + See also [method @GlobalScope.typeof], [method type_exists], [method Array.is_same_typed] (and other [Array] methods). + + + + + + + Returns the length of the given Variant [param var]. The length can be the character count of a [String] or [StringName], the element count of any array type, or the size of a [Dictionary]. For every other Variant type, a run-time error is generated and execution is stopped. + [codeblock] + var a = [1, 2, 3, 4] + len(a) # Returns 4 + + var b = "Hello!" + len(b) # Returns 6 + [/codeblock] + + + + + + + Returns a [Resource] from the filesystem located at the absolute [param path]. Unless it's already referenced elsewhere (such as in another script or in the scene), the resource is loaded from disk on function call, which might cause a slight delay, especially when loading large scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload]. This method is equivalent of using [method ResourceLoader.load] with [constant ResourceLoader.CACHE_MODE_REUSE]. + [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing "Copy Path", or by dragging the file from the FileSystem dock into the current script. + [codeblock] + # Load a scene called "main" located in the root of the project directory and cache it in a variable. + var main = load("res://main.tscn") # main will contain a PackedScene resource. + [/codeblock] + [b]Important:[/b] Relative paths are [i]not[/i] relative to the script calling this method, instead it is prefixed with [code]"res://"[/code]. Loading from relative paths might not work as expected. + This function is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios. + [b]Note:[/b] Files have to be imported into the engine first to load them using this function. If you want to load [Image]s at run-time, you may use [method Image.load]. If you want to import audio files, you can use the snippet described in [member AudioStreamMP3.data]. + [b]Note:[/b] If [member ProjectSettings.editor/export/convert_text_resources_to_binary] is [code]true[/code], [method @GDScript.load] will not be able to read converted files in an exported project. If you rely on run-time loading of files present within the PCK, set [member ProjectSettings.editor/export/convert_text_resources_to_binary] to [code]false[/code]. + + + + + + + Returns a [Resource] from the filesystem located at [param path]. During run-time, the resource is loaded when the script is being parsed. This function effectively acts as a reference to that resource. Note that this function requires [param path] to be a constant [String]. If you want to load a resource from a dynamic/variable path, use [method load]. + [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the Assets Panel and choosing "Copy Path", or by dragging the file from the FileSystem dock into the current script. + [codeblock] + # Create instance of a scene. + var diamond = preload("res://diamond.tscn").instantiate() + [/codeblock] + [b]Note:[/b] [method preload] is a keyword, not a function. So you cannot access it as a [Callable]. + + + + + + Like [method @GlobalScope.print], but includes the current stack frame when running with the debugger turned on. + The output in the console may look like the following: + [codeblock lang=text] + Test print + At: res://test.gd:15:_process() + [/codeblock] + [b]Note:[/b] Calling this function from a [Thread] is not supported. Doing so will instead print the thread ID. + + + + + + Prints a stack trace at the current code location. See also [method get_stack]. + The output in the console may look like the following: + [codeblock lang=text] + Frame 0 - res://test.gd:16 in function '_process' + [/codeblock] + [b]Note:[/b] This function only works if the running instance is connected to a debugging server (i.e. an editor instance). [method print_stack] will not work in projects exported in release mode, or in projects exported in debug mode if not connected to a debugging server. + [b]Note:[/b] Calling this function from a [Thread] is not supported. Doing so will instead print the thread ID. + + + + + + Returns an array with the given range. [method range] can be called in three ways: + [code]range(n: int)[/code]: Starts from 0, increases by steps of 1, and stops [i]before[/i] [code]n[/code]. The argument [code]n[/code] is [b]exclusive[/b]. + [code]range(b: int, n: int)[/code]: Starts from [code]b[/code], increases by steps of 1, and stops [i]before[/i] [code]n[/code]. The arguments [code]b[/code] and [code]n[/code] are [b]inclusive[/b] and [b]exclusive[/b], respectively. + [code]range(b: int, n: int, s: int)[/code]: Starts from [code]b[/code], increases/decreases by steps of [code]s[/code], and stops [i]before[/i] [code]n[/code]. The arguments [code]b[/code] and [code]n[/code] are [b]inclusive[/b] and [b]exclusive[/b], respectively. The argument [code]s[/code] [b]can[/b] be negative, but not [code]0[/code]. If [code]s[/code] is [code]0[/code], an error message is printed. + [method range] converts all arguments to [int] before processing. + [b]Note:[/b] Returns an empty array if no value meets the value constraint (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]). + [b]Examples:[/b] + [codeblock] + print(range(4)) # Prints [0, 1, 2, 3] + print(range(2, 5)) # Prints [2, 3, 4] + print(range(0, 6, 2)) # Prints [0, 2, 4] + print(range(4, 1, -1)) # Prints [4, 3, 2] + [/codeblock] + To iterate over an [Array] backwards, use: + [codeblock] + var array = [3, 6, 9] + for i in range(array.size() - 1, -1, -1): + print(array[i]) + [/codeblock] + Output: + [codeblock lang=text] + 9 + 6 + 3 + [/codeblock] + To iterate over [float], convert them in the loop. + [codeblock] + for i in range (3, 0, -1): + print(i / 10.0) + [/codeblock] + Output: + [codeblock lang=text] + 0.3 + 0.2 + 0.1 + [/codeblock] + + + + + + + Returns [code]true[/code] if the given [Object]-derived class exists in [ClassDB]. Note that [Variant] data types are not registered in [ClassDB]. + [codeblock] + type_exists("Sprite2D") # Returns true + type_exists("NonExistentClass") # Returns false + [/codeblock] + + + + + + Constant that represents how many times the diameter of a circle fits around its perimeter. This is equivalent to [code]TAU / 2[/code], or 180 degrees in rotations. + + + The circle constant, the circumference of the unit circle in radians. This is equivalent to [code]PI * 2[/code], or 360 degrees in rotations. + + + Positive floating-point infinity. This is the result of floating-point division when the divisor is [code]0.0[/code]. For negative infinity, use [code]-INF[/code]. Dividing by [code]-0.0[/code] will result in negative infinity if the numerator is positive, so dividing by [code]0.0[/code] is not the same as dividing by [code]-0.0[/code] (despite [code]0.0 == -0.0[/code] returning [code]true[/code]). + [b]Warning:[/b] Numeric infinity is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer number by [code]0[/code] will not result in [constant INF] and will result in a run-time error instead. + + + "Not a Number", an invalid floating-point value. [constant NAN] has special properties, including that [code]!=[/code] always returns [code]true[/code], while other comparison operators always return [code]false[/code]. This is true even when comparing with itself ([code]NAN == NAN[/code] returns [code]false[/code] and [code]NAN != NAN[/code] returns [code]true[/code]). It is returned by some invalid operations, such as dividing floating-point [code]0.0[/code] by [code]0.0[/code]. + [b]Warning:[/b] "Not a Number" is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer [code]0[/code] by [code]0[/code] will not result in [constant NAN] and will result in a run-time error instead. + + + + + + + Mark the following property as exported (editable in the Inspector dock and saved to disk). To control the type of the exported property, use the type hint notation. + [codeblock] + extends Node + + enum Direction {LEFT, RIGHT, UP, DOWN} + + # Built-in types. + @export var string = "" + @export var int_number = 5 + @export var float_number: float = 5 + + # Enums. + @export var type: Variant.Type + @export var format: Image.Format + @export var direction: Direction + + # Resources. + @export var image: Image + @export var custom_resource: CustomResource + + # Nodes. + @export var node: Node + @export var custom_node: CustomNode + + # Typed arrays. + @export var int_array: Array[int] + @export var direction_array: Array[Direction] + @export var image_array: Array[Image] + @export var node_array: Array[Node] + [/codeblock] + [b]Note:[/b] Custom resources and nodes should be registered as global classes using [code]class_name[/code], since the Inspector currently only supports global classes. Otherwise, a less specific type will be exported instead. + [b]Note:[/b] Node export is only supported in [Node]-derived classes and has a number of other limitations. + + + + + + + Define a new category for the following exported properties. This helps to organize properties in the Inspector dock. + See also [constant PROPERTY_USAGE_CATEGORY]. + [codeblock] + @export_category("Statistics") + @export var hp = 30 + @export var speed = 1.25 + [/codeblock] + [b]Note:[/b] Categories in the Inspector dock's list usually divide properties coming from different classes (Node, Node2D, Sprite, etc.). For better clarity, it's recommended to use [annotation @export_group] and [annotation @export_subgroup], instead. + + + + + + Export a [Color], [Array][lb][Color][rb], or [PackedColorArray] property without allowing its transparency ([member Color.a]) to be edited. + See also [constant PROPERTY_HINT_COLOR_NO_ALPHA]. + [codeblock] + @export_color_no_alpha var dye_color: Color + @export_color_no_alpha var dye_colors: Array[Color] + [/codeblock] + + + + + + + + + Allows you to set a custom hint, hint string, and usage flags for the exported property. Note that there's no validation done in GDScript, it will just pass the parameters to the editor. + [codeblock] + @export_custom(PROPERTY_HINT_NONE, "suffix:m") var suffix: Vector3 + [/codeblock] + [b]Note:[/b] Regardless of the [param usage] value, the [constant PROPERTY_USAGE_SCRIPT_VARIABLE] flag is always added, as with any explicitly declared script variable. + + + + + + Export a [String], [Array][lb][String][rb], or [PackedStringArray] property as a path to a directory. The path will be limited to the project folder and its subfolders. See [annotation @export_global_dir] to allow picking from the entire filesystem. + See also [constant PROPERTY_HINT_DIR]. + [codeblock] + @export_dir var sprite_folder_path: String + @export_dir var sprite_folder_paths: Array[String] + [/codeblock] + + + + + + + Export an [int], [String], [Array][lb][int][rb], [Array][lb][String][rb], [PackedByteArray], [PackedInt32Array], [PackedInt64Array], or [PackedStringArray] property as an enumerated list of options (or an array of options). If the property is an [int], then the index of the value is stored, in the same order the values are provided. You can add explicit values using a colon. If the property is a [String], then the value is stored. + See also [constant PROPERTY_HINT_ENUM]. + [codeblock] + @export_enum("Warrior", "Magician", "Thief") var character_class: int + @export_enum("Slow:30", "Average:60", "Very Fast:200") var character_speed: int + @export_enum("Rebecca", "Mary", "Leah") var character_name: String + + @export_enum("Sword", "Spear", "Mace") var character_items: Array[int] + @export_enum("double_jump", "climb", "dash") var character_skills: Array[String] + [/codeblock] + If you want to set an initial value, you must specify it explicitly: + [codeblock] + @export_enum("Rebecca", "Mary", "Leah") var character_name: String = "Rebecca" + [/codeblock] + If you want to use named GDScript enums, then use [annotation @export] instead: + [codeblock] + enum CharacterName {REBECCA, MARY, LEAH} + @export var character_name: CharacterName + + enum CharacterItem {SWORD, SPEAR, MACE} + @export var character_items: Array[CharacterItem] + [/codeblock] + + + + + + + Export a floating-point property with an easing editor widget. Additional hints can be provided to adjust the behavior of the widget. [code]"attenuation"[/code] flips the curve, which makes it more intuitive for editing attenuation properties. [code]"positive_only"[/code] limits values to only be greater than or equal to zero. + See also [constant PROPERTY_HINT_EXP_EASING]. + [codeblock] + @export_exp_easing var transition_speed + @export_exp_easing("attenuation") var fading_attenuation + @export_exp_easing("positive_only") var effect_power + @export_exp_easing var speeds: Array[float] + [/codeblock] + + + + + + + Export a [String], [Array][lb][String][rb], or [PackedStringArray] property as a path to a file. The path will be limited to the project folder and its subfolders. See [annotation @export_global_file] to allow picking from the entire filesystem. + If [param filter] is provided, only matching files will be available for picking. + See also [constant PROPERTY_HINT_FILE]. + [codeblock] + @export_file var sound_effect_path: String + @export_file("*.txt") var notes_path: String + @export_file var level_paths: Array[String] + [/codeblock] + + + + + + + Export an integer property as a bit flag field. This allows to store several "checked" or [code]true[/code] values with one property, and comfortably select them from the Inspector dock. + See also [constant PROPERTY_HINT_FLAGS]. + [codeblock] + @export_flags("Fire", "Water", "Earth", "Wind") var spell_elements = 0 + [/codeblock] + You can add explicit values using a colon: + [codeblock] + @export_flags("Self:4", "Allies:8", "Foes:16") var spell_targets = 0 + [/codeblock] + You can also combine several flags: + [codeblock] + @export_flags("Self:4", "Allies:8", "Self and Allies:12", "Foes:16") + var spell_targets = 0 + [/codeblock] + [b]Note:[/b] A flag value must be at least [code]1[/code] and at most [code]2 ** 32 - 1[/code]. + [b]Note:[/b] Unlike [annotation @export_enum], the previous explicit value is not taken into account. In the following example, A is 16, B is 2, C is 4. + [codeblock] + @export_flags("A:16", "B", "C") var x + [/codeblock] + You can also use the annotation on [Array][lb][int][rb], [PackedByteArray], [PackedInt32Array], and [PackedInt64Array] + [codeblock] + @export_flags("Fire", "Water", "Earth", "Wind") var phase_elements: Array[int] + [/codeblock] + + + + + + Export an integer property as a bit flag field for 2D navigation layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/2d_navigation/layer_1]. + See also [constant PROPERTY_HINT_LAYERS_2D_NAVIGATION]. + [codeblock] + @export_flags_2d_navigation var navigation_layers: int + @export_flags_2d_navigation var navigation_layers_array: Array[int] + [/codeblock] + + + + + + Export an integer property as a bit flag field for 2D physics layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/2d_physics/layer_1]. + See also [constant PROPERTY_HINT_LAYERS_2D_PHYSICS]. + [codeblock] + @export_flags_2d_physics var physics_layers: int + @export_flags_2d_physics var physics_layers_array: Array[int] + [/codeblock] + + + + + + Export an integer property as a bit flag field for 2D render layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/2d_render/layer_1]. + See also [constant PROPERTY_HINT_LAYERS_2D_RENDER]. + [codeblock] + @export_flags_2d_render var render_layers: int + @export_flags_2d_render var render_layers_array: Array[int] + [/codeblock] + + + + + + Export an integer property as a bit flag field for 3D navigation layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/3d_navigation/layer_1]. + See also [constant PROPERTY_HINT_LAYERS_3D_NAVIGATION]. + [codeblock] + @export_flags_3d_navigation var navigation_layers: int + @export_flags_3d_navigation var navigation_layers_array: Array[int] + [/codeblock] + + + + + + Export an integer property as a bit flag field for 3D physics layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/3d_physics/layer_1]. + See also [constant PROPERTY_HINT_LAYERS_3D_PHYSICS]. + [codeblock] + @export_flags_3d_physics var physics_layers: int + @export_flags_3d_physics var physics_layers_array: Array[int] + [/codeblock] + + + + + + Export an integer property as a bit flag field for 3D render layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/3d_render/layer_1]. + See also [constant PROPERTY_HINT_LAYERS_3D_RENDER]. + [codeblock] + @export_flags_3d_render var render_layers: int + @export_flags_3d_render var render_layers_array: Array[int] + [/codeblock] + + + + + + Export an integer property as a bit flag field for navigation avoidance layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/avoidance/layer_1]. + See also [constant PROPERTY_HINT_LAYERS_AVOIDANCE]. + [codeblock] + @export_flags_avoidance var avoidance_layers: int + @export_flags_avoidance var avoidance_layers_array: Array[int] + [/codeblock] + + + + + + Export a [String], [Array][lb][String][rb], or [PackedStringArray] property as an absolute path to a directory. The path can be picked from the entire filesystem. See [annotation @export_dir] to limit it to the project folder and its subfolders. + See also [constant PROPERTY_HINT_GLOBAL_DIR]. + [codeblock] + @export_global_dir var sprite_folder_path: String + @export_global_dir var sprite_folder_paths: Array[String] + [/codeblock] + + + + + + + Export a [String], [Array][lb][String][rb], or [PackedStringArray] property as an absolute path to a file. The path can be picked from the entire filesystem. See [annotation @export_file] to limit it to the project folder and its subfolders. + If [param filter] is provided, only matching files will be available for picking. + See also [constant PROPERTY_HINT_GLOBAL_FILE]. + [codeblock] + @export_global_file var sound_effect_path: String + @export_global_file("*.txt") var notes_path: String + @export_global_file var multiple_paths: Array[String] + [/codeblock] + + + + + + + + Define a new group for the following exported properties. This helps to organize properties in the Inspector dock. Groups can be added with an optional [param prefix], which would make group to only consider properties that have this prefix. The grouping will break on the first property that doesn't have a prefix. The prefix is also removed from the property's name in the Inspector dock. + If no [param prefix] is provided, then every following property will be added to the group. The group ends when then next group or category is defined. You can also force end a group by using this annotation with empty strings for parameters, [code]@export_group("", "")[/code]. + Groups cannot be nested, use [annotation @export_subgroup] to add subgroups within groups. + See also [constant PROPERTY_USAGE_GROUP]. + [codeblock] + @export_group("Racer Properties") + @export var nickname = "Nick" + @export var age = 26 + + @export_group("Car Properties", "car_") + @export var car_label = "Speedy" + @export var car_number = 3 + + @export_group("", "") + @export var ungrouped_number = 3 + [/codeblock] + + + + + + Export a [String], [Array][lb][String][rb], [PackedStringArray], [Dictionary] or [Array][lb][Dictionary][rb] property with a large [TextEdit] widget instead of a [LineEdit]. This adds support for multiline content and makes it easier to edit large amount of text stored in the property. + See also [constant PROPERTY_HINT_MULTILINE_TEXT]. + [codeblock] + @export_multiline var character_biography + @export_multiline var npc_dialogs: Array[String] + [/codeblock] + + + + + + + Export a [NodePath] or [Array][lb][NodePath][rb] property with a filter for allowed node types. + See also [constant PROPERTY_HINT_NODE_PATH_VALID_TYPES]. + [codeblock] + @export_node_path("Button", "TouchScreenButton") var some_button + @export_node_path("Button", "TouchScreenButton") var many_buttons: Array[NodePath] + [/codeblock] + [b]Note:[/b] The type must be a native class or a globally registered script (using the [code]class_name[/code] keyword) that inherits [Node]. + + + + + + + Export a [String], [Array][lb][String][rb], or [PackedStringArray] property with a placeholder text displayed in the editor widget when no value is present. + See also [constant PROPERTY_HINT_PLACEHOLDER_TEXT]. + [codeblock] + @export_placeholder("Name in lowercase") var character_id: String + @export_placeholder("Name in lowercase") var friend_ids: Array[String] + [/codeblock] + + + + + + + + + + Export an [int], [float], [Array][lb][int][rb], [Array][lb][float][rb], [PackedByteArray], [PackedInt32Array], [PackedInt64Array], [PackedFloat32Array], or [PackedFloat64Array] property as a range value. The range must be defined by [param min] and [param max], as well as an optional [param step] and a variety of extra hints. The [param step] defaults to [code]1[/code] for integer properties. For floating-point numbers this value depends on your [member EditorSettings.interface/inspector/default_float_step] setting. + If hints [code]"or_greater"[/code] and [code]"or_less"[/code] are provided, the editor widget will not cap the value at range boundaries. The [code]"exp"[/code] hint will make the edited values on range to change exponentially. The [code]"hide_slider"[/code] hint will hide the slider element of the editor widget. + Hints also allow to indicate the units for the edited value. Using [code]"radians_as_degrees"[/code] you can specify that the actual value is in radians, but should be displayed in degrees in the Inspector dock (the range values are also in degrees). [code]"degrees"[/code] allows to add a degree sign as a unit suffix (the value is unchanged). Finally, a custom suffix can be provided using [code]"suffix:unit"[/code], where "unit" can be any string. + See also [constant PROPERTY_HINT_RANGE]. + [codeblock] + @export_range(0, 20) var number + @export_range(-10, 20) var number + @export_range(-10, 20, 0.2) var number: float + @export_range(0, 20) var numbers: Array[float] + + @export_range(0, 100, 1, "or_greater") var power_percent + @export_range(0, 100, 1, "or_greater", "or_less") var health_delta + + @export_range(-180, 180, 0.001, "radians_as_degrees") var angle_radians + @export_range(0, 360, 1, "degrees") var angle_degrees + @export_range(-8, 8, 2, "suffix:px") var target_offset + [/codeblock] + + + + + + Export a property with [constant PROPERTY_USAGE_STORAGE] flag. The property is not displayed in the editor, but it is serialized and stored in the scene or resource file. This can be useful for [annotation @tool] scripts. Also the property value is copied when [method Resource.duplicate] or [method Node.duplicate] is called, unlike non-exported variables. + [codeblock] + var a # Not stored in the file, not displayed in the editor. + @export_storage var b # Stored in the file, not displayed in the editor. + @export var c: int # Stored in the file, displayed in the editor. + [/codeblock] + + + + + + + + Define a new subgroup for the following exported properties. This helps to organize properties in the Inspector dock. Subgroups work exactly like groups, except they need a parent group to exist. See [annotation @export_group]. + See also [constant PROPERTY_USAGE_SUBGROUP]. + [codeblock] + @export_group("Racer Properties") + @export var nickname = "Nick" + @export var age = 26 + + @export_subgroup("Car Properties", "car_") + @export var car_label = "Speedy" + @export var car_number = 3 + [/codeblock] + [b]Note:[/b] Subgroups cannot be nested, they only provide one extra level of depth. Just like the next group ends the previous group, so do the subsequent subgroups. + + + + + + + + Export a [Callable] property as a clickable button with the label [param text]. When the button is pressed, the callable is called. + If [param icon] is specified, it is used to fetch an icon for the button via [method Control.get_theme_icon], from the [code]"EditorIcons"[/code] theme type. If [param icon] is omitted, the default [code]"Callable"[/code] icon is used instead. + Consider using the [EditorUndoRedoManager] to allow the action to be reverted safely. + See also [constant PROPERTY_HINT_TOOL_BUTTON]. + [codeblock] + @tool + extends Sprite2D + + @export_tool_button("Hello") var hello_action = hello + @export_tool_button("Randomize the color!", "ColorRect") + var randomize_color_action = randomize_color + + func hello(): + print("Hello world!") + + func randomize_color(): + var undo_redo = EditorInterface.get_editor_undo_redo() + undo_redo.create_action("Randomized Sprite2D Color") + undo_redo.add_do_property(self, &"self_modulate", Color(randf(), randf(), randf())) + undo_redo.add_undo_property(self, &"self_modulate", self_modulate) + undo_redo.commit_action() + [/codeblock] + [b]Note:[/b] The property is exported without the [constant PROPERTY_USAGE_STORAGE] flag because a [Callable] cannot be properly serialized and stored in a file. + [b]Note:[/b] In an exported project neither [EditorInterface] nor [EditorUndoRedoManager] exist, which may cause some scripts to break. To prevent this, you can use [method Engine.get_singleton] and omit the static type from the variable declaration: + [codeblock] + var undo_redo = Engine.get_singleton(&"EditorInterface").get_editor_undo_redo() + [/codeblock] + [b]Note:[/b] Avoid storing lambda callables in member variables of [RefCounted]-based classes (e.g. resources), as this can lead to memory leaks. Use only method callables and optionally [method Callable.bind] or [method Callable.unbind]. + + + + + + + Add a custom icon to the current script. The icon specified at [param icon_path] is displayed in the Scene dock for every node of that class, as well as in various editor dialogs. + [codeblock] + @icon("res://path/to/class/icon.svg") + [/codeblock] + [b]Note:[/b] Only the script can have a custom icon. Inner classes are not supported. + [b]Note:[/b] As annotations describe their subject, the [annotation @icon] annotation must be placed before the class definition and inheritance. + [b]Note:[/b] Unlike other annotations, the argument of the [annotation @icon] annotation must be a string literal (constant expressions are not supported). + + + + + + Mark the following property as assigned when the [Node] is ready. Values for these properties are not assigned immediately when the node is initialized ([method Object._init]), and instead are computed and stored right before [method Node._ready]. + [codeblock] + @onready var character_name: Label = $Label + [/codeblock] + + + + + + + + + + Mark the following method for remote procedure calls. See [url=$DOCS_URL/tutorials/networking/high_level_multiplayer.html]High-level multiplayer[/url]. + If [param mode] is set as [code]"any_peer"[/code], allows any peer to call this RPC function. Otherwise, only the authority peer is allowed to call it and [param mode] should be kept as [code]"authority"[/code]. When configuring functions as RPCs with [method Node.rpc_config], each of these modes respectively corresponds to the [constant MultiplayerAPI.RPC_MODE_AUTHORITY] and [constant MultiplayerAPI.RPC_MODE_ANY_PEER] RPC modes. See [enum MultiplayerAPI.RPCMode]. If a peer that is not the authority tries to call a function that is only allowed for the authority, the function will not be executed. If the error can be detected locally (when the RPC configuration is consistent between the local and the remote peer), an error message will be displayed on the sender peer. Otherwise, the remote peer will detect the error and print an error there. + If [param sync] is set as [code]"call_remote"[/code], the function will only be executed on the remote peer, but not locally. To run this function locally too, set [param sync] to [code]"call_local"[/code]. When configuring functions as RPCs with [method Node.rpc_config], this is equivalent to setting [code]call_local[/code] to [code]true[/code]. + The [param transfer_mode] accepted values are [code]"unreliable"[/code], [code]"unreliable_ordered"[/code], or [code]"reliable"[/code]. It sets the transfer mode of the underlying [MultiplayerPeer]. See [member MultiplayerPeer.transfer_mode]. + The [param transfer_channel] defines the channel of the underlying [MultiplayerPeer]. See [member MultiplayerPeer.transfer_channel]. + The order of [param mode], [param sync] and [param transfer_mode] does not matter, but values related to the same argument must not be used more than once. [param transfer_channel] always has to be the 4th argument (you must specify 3 preceding arguments). + [codeblock] + @rpc + func fn(): pass + + @rpc("any_peer", "unreliable_ordered") + func fn_update_pos(): pass + + @rpc("authority", "call_remote", "unreliable", 0) # Equivalent to @rpc + func fn_default(): pass + [/codeblock] + + + + + + Make a script with static variables to not persist after all references are lost. If the script is loaded again the static variables will revert to their default values. + [b]Note:[/b] As annotations describe their subject, the [annotation @static_unload] annotation must be placed before the class definition and inheritance. + [b]Warning:[/b] Currently, due to a bug, scripts are never freed, even if [annotation @static_unload] annotation is used. + + + + + + Mark the current script as a tool script, allowing it to be loaded and executed by the editor. See [url=$DOCS_URL/tutorials/plugins/running_code_in_the_editor.html]Running code in the editor[/url]. + [codeblock] + @tool + extends Node + [/codeblock] + [b]Note:[/b] As annotations describe their subject, the [annotation @tool] annotation must be placed before the class definition and inheritance. + + + + + + + Mark the following statement to ignore the specified [param warning]. See [url=$DOCS_URL/tutorials/scripting/gdscript/warning_system.html]GDScript warning system[/url]. + [codeblock] + func test(): + print("hello") + return + @warning_ignore("unreachable_code") + print("unreachable") + [/codeblock] + + + + diff --git a/modules/gdscript/doc_classes/GDTrait.xml b/modules/gdscript/doc_classes/GDTrait.xml new file mode 100644 index 000000000000..3d2f92eb8b41 --- /dev/null +++ b/modules/gdscript/doc_classes/GDTrait.xml @@ -0,0 +1,13 @@ + + + + A script implemented in the GDScript programming language. + + + See [GDScript] or [@GDScript] instead. + Helper Script to store Traits for GDScript, its not attachable to objects [method Object.set_script].Saved with the [code].gdt[/code] extension, for GDScript programming language external Traits. + + + $DOCS_URL/tutorials/scripting/gdscript/index.html + + diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp index a6742820b579..19cd3de1cd5d 100644 --- a/modules/gdscript/editor/gdscript_docgen.cpp +++ b/modules/gdscript/editor/gdscript_docgen.cpp @@ -124,6 +124,7 @@ void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type } r_type = "Object"; return; + case GDType::TRAIT: case GDType::CLASS: if (p_gdtype.is_meta_type) { r_type = GDScript::get_class_static(); diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index b8851e5bf21c..a65f15b32dcd 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -679,6 +679,7 @@ String GDScriptSyntaxHighlighter::_get_name() const { PackedStringArray GDScriptSyntaxHighlighter::_get_supported_languages() const { PackedStringArray languages; languages.push_back("GDScript"); + languages.push_back("GDTrait"); return languages; } diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp index 172ad6be9f7b..e22e66defe32 100644 --- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp +++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp @@ -155,6 +155,7 @@ void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser const GDScriptParser::ClassNode::Member &m = p_class->members[i]; // Other member types can't contain translatable strings. switch (m.type) { + case GDScriptParser::ClassNode::Member::TRAIT: case GDScriptParser::ClassNode::Member::CLASS: _traverse_class(m.m_class); break; diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index a638adeaee99..870c1b453901 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1214,6 +1214,10 @@ bool GDScript::inherits_script(const Ref