summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/debugger/remote_debugger.cpp31
-rw-r--r--core/debugger/remote_debugger.h1
-rw-r--r--core/input/input_builders.py4
-rw-r--r--core/object/script_language.h1
-rw-r--r--core/object/script_language_extension.h1
-rw-r--r--doc/classes/AABB.xml217
-rw-r--r--doc/classes/ProjectSettings.xml1
-rwxr-xr-xdoc/tools/make_rst.py18
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp2
-rw-r--r--editor/connections_dialog.cpp21
-rw-r--r--editor/connections_dialog.h2
-rw-r--r--editor/debugger/editor_debugger_node.cpp10
-rw-r--r--editor/debugger/editor_debugger_node.h3
-rw-r--r--editor/debugger/script_editor_debugger.cpp8
-rw-r--r--editor/debugger/script_editor_debugger.h3
-rw-r--r--editor/editor_help_search.cpp148
-rw-r--r--editor/editor_help_search.h16
-rw-r--r--editor/filesystem_dock.cpp13
-rw-r--r--editor/icons/TileMapLayer.svg1
-rw-r--r--editor/plugins/script_editor_plugin.cpp26
-rw-r--r--editor/plugins/script_editor_plugin.h5
-rw-r--r--editor/plugins/script_text_editor.cpp2
-rw-r--r--editor/scene_tree_dock.cpp1
-rw-r--r--main/main.cpp2
-rw-r--r--misc/extension_api_validation/4.2-stable.expected8
-rw-r--r--modules/gdscript/gdscript.cpp29
-rw-r--r--modules/gdscript/gdscript.h1
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h2
-rw-r--r--modules/gdscript/gdscript_editor.cpp74
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp2
-rw-r--r--modules/gdscript/tests/README.md41
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg2
-rw-r--r--modules/gdscript/tests/test_completion.h18
-rw-r--r--modules/mono/csharp_script.cpp29
-rw-r--r--modules/mono/csharp_script.h1
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/GlobalClass.cs14
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj1
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MustBeVariantSamples.cs164
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpAnalyzerVerifier.cs56
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/GlobalClassAnalyzerTests.cs20
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/MustBeVariantAnalyzerTests.cs20
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0401.cs22
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0402.cs15
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0301.cs71
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0302.cs27
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs4
-rw-r--r--modules/mono/glue/runtime_interop.cpp2
-rw-r--r--modules/mono/utils/path_utils.cpp2
-rw-r--r--modules/openxr/doc_classes/OpenXRHand.xml13
-rw-r--r--modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp2
-rw-r--r--modules/openxr/extensions/openxr_opengl_extension.h6
-rw-r--r--modules/openxr/extensions/openxr_vulkan_extension.h2
-rw-r--r--modules/openxr/scene/openxr_hand.cpp26
-rw-r--r--modules/openxr/scene/openxr_hand.h11
-rw-r--r--scene/2d/tile_map.compat.inc10
-rw-r--r--scene/2d/tile_map.cpp808
-rw-r--r--scene/2d/tile_map.h16
-rw-r--r--scene/2d/tile_map_layer.cpp646
-rw-r--r--scene/2d/tile_map_layer.h60
-rw-r--r--scene/3d/collision_object_3d.cpp3
-rw-r--r--scene/main/canvas_item.cpp3
-rw-r--r--scene/main/canvas_item.h4
-rw-r--r--scene/main/node.cpp4
-rw-r--r--scene/resources/tile_set.cpp626
-rw-r--r--scene/resources/tile_set.h7
-rw-r--r--tests/scene/test_audio_stream_wav.h2
71 files changed, 2159 insertions, 1274 deletions
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index ce675d6b06..d3b0039e72 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -36,6 +36,7 @@
#include "core/debugger/engine_profiler.h"
#include "core/debugger/script_debugger.h"
#include "core/input/input.h"
+#include "core/io/resource_loader.h"
#include "core/object/script_language.h"
#include "core/os/os.h"
@@ -513,8 +514,9 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
_send_stack_vars(globals, globals_vals, 2);
} else if (command == "reload_scripts") {
+ script_paths_to_reload = data;
+ } else if (command == "reload_all_scripts") {
reload_all_scripts = true;
-
} else if (command == "breakpoint") {
ERR_FAIL_COND(data.size() < 3);
bool set = data[2];
@@ -589,19 +591,36 @@ void RemoteDebugger::poll_events(bool p_is_idle) {
}
// Reload scripts during idle poll only.
- if (p_is_idle && reload_all_scripts) {
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->reload_all_scripts();
+ if (p_is_idle) {
+ if (reload_all_scripts) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->reload_all_scripts();
+ }
+ reload_all_scripts = false;
+ } else if (!script_paths_to_reload.is_empty()) {
+ Array scripts_to_reload;
+ for (int i = 0; i < script_paths_to_reload.size(); ++i) {
+ String path = script_paths_to_reload[i];
+ Error err = OK;
+ Ref<Script> script = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
+ ERR_CONTINUE_MSG(err != OK, vformat("Could not reload script '%s': %s", path, error_names[err]));
+ ERR_CONTINUE_MSG(script.is_null(), vformat("Could not reload script '%s': Not a script!", path, error_names[err]));
+ scripts_to_reload.push_back(script);
+ }
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->reload_scripts(scripts_to_reload, true);
+ }
}
- reload_all_scripts = false;
+ script_paths_to_reload.clear();
}
}
Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bool &r_captured) {
r_captured = true;
if (p_cmd == "reload_scripts") {
+ script_paths_to_reload = p_data;
+ } else if (p_cmd == "reload_all_scripts") {
reload_all_scripts = true;
-
} else if (p_cmd == "breakpoint") {
ERR_FAIL_COND_V(p_data.size() < 3, ERR_INVALID_DATA);
bool set = p_data[2];
diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h
index 7c399178c6..519a90e7cc 100644
--- a/core/debugger/remote_debugger.h
+++ b/core/debugger/remote_debugger.h
@@ -74,6 +74,7 @@ private:
int warn_count = 0;
int last_reset = 0;
bool reload_all_scripts = false;
+ Array script_paths_to_reload;
// Make handlers and send_message thread safe.
Mutex mutex;
diff --git a/core/input/input_builders.py b/core/input/input_builders.py
index e98e2441e2..94c566493e 100644
--- a/core/input/input_builders.py
+++ b/core/input/input_builders.py
@@ -45,10 +45,10 @@ def make_default_controller_mappings(target, source, env):
platform_mappings[current_platform][guid] = line
platform_variables = {
- "Linux": "#if LINUXBSD_ENABLED",
+ "Linux": "#ifdef LINUXBSD_ENABLED",
"Windows": "#ifdef WINDOWS_ENABLED",
"Mac OS X": "#ifdef MACOS_ENABLED",
- "Android": "#if defined(__ANDROID__)",
+ "Android": "#ifdef ANDROID_ENABLED",
"iOS": "#ifdef IOS_ENABLED",
"Web": "#ifdef WEB_ENABLED",
}
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 66106bf139..bb714d5bc3 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -371,6 +371,7 @@ public:
virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); }
virtual void reload_all_scripts() = 0;
+ virtual void reload_scripts(const Array &p_scripts, bool p_soft_reload) = 0;
virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) = 0;
/* LOADER FUNCTIONS */
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index 8b01667519..5b10739486 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -562,6 +562,7 @@ public:
}
EXBIND0(reload_all_scripts)
+ EXBIND2(reload_scripts, const Array &, bool)
EXBIND2(reload_tool_script, const Ref<Script> &, bool)
/* LOADER FUNCTIONS */
diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml
index c1c637d2d6..427d38d421 100644
--- a/doc/classes/AABB.xml
+++ b/doc/classes/AABB.xml
@@ -4,10 +4,10 @@
A 3D axis-aligned bounding box.
</brief_description>
<description>
- [AABB] consists of a position, a size, and several utility functions. It is typically used for fast overlap tests.
- It uses floating-point coordinates. The 2D counterpart to [AABB] is [Rect2].
- Negative values for [member size] are not supported and will not work for most methods. Use [method abs] to get an AABB with a positive size.
- [b]Note:[/b] Unlike [Rect2], [AABB] does not have a variant that uses integer coordinates.
+ The [AABB] built-in [Variant] type represents an axis-aligned bounding box in a 3D space. It is defined by its [member position] and [member size], which are [Vector3]. It is frequently used for fast overlap tests (see [method intersects]). Although [AABB] itself is axis-aligned, it can be combined with [Transform3D] to represent a rotated or skewed bounding box.
+ It uses floating-point coordinates. The 2D counterpart to [AABB] is [Rect2]. There is no version of [AABB] that uses integer coordinates.
+ [b]Note:[/b] Negative values for [member size] are not supported. With negative size, most [AABB] methods do not work correctly. Use [method abs] to get an equivalent [AABB] with a non-negative size.
+ [b]Note:[/b] In a boolean context, a [AABB] evaluates to [code]false[/code] if both [member position] and [member size] are zero (equal to [constant Vector3.ZERO]). Otherwise, it always evaluates to [code]true[/code].
</description>
<tutorials>
<link title="Math documentation index">$DOCS_URL/tutorials/math/index.html</link>
@@ -18,7 +18,7 @@
<constructor name="AABB">
<return type="AABB" />
<description>
- Constructs a default-initialized [AABB] with default (zero) values of [member position] and [member size].
+ Constructs an [AABB] with its [member position] and [member size] set to [constant Vector3.ZERO].
</description>
</constructor>
<constructor name="AABB">
@@ -33,7 +33,7 @@
<param index="0" name="position" type="Vector3" />
<param index="1" name="size" type="Vector3" />
<description>
- Constructs an [AABB] from a position and size.
+ Constructs an [AABB] by [param position] and [param size].
</description>
</constructor>
</constructors>
@@ -41,34 +41,78 @@
<method name="abs" qualifiers="const">
<return type="AABB" />
<description>
- Returns an AABB with equivalent position and size, modified so that the most-negative corner is the origin and the size is positive.
+ Returns an [AABB] equivalent to this bounding box, with its width, height, and depth modified to be non-negative values.
+ [codeblocks]
+ [gdscript]
+ var box = AABB(Vector3(5, 0, 5), Vector3(-20, -10, -5))
+ var absolute = box.abs()
+ print(absolute.position) # Prints (-15, -10, 0)
+ print(absolute.size) # Prints (20, 10, 5)
+ [/gdscript]
+ [csharp]
+ var box = new Aabb(new Vector3(5, 0, 5), new Vector3(-20, -10, -5));
+ var absolute = box.Abs();
+ GD.Print(absolute.Position); // Prints (-15, -10, 0)
+ GD.Print(absolute.Size); // Prints (20, 10, 5)
+ [/csharp]
+ [/codeblocks]
+ [b]Note:[/b] It's recommended to use this method when [member size] is negative, as most other methods in Godot assume that the [member size]'s components are greater than [code]0[/code].
</description>
</method>
<method name="encloses" qualifiers="const">
<return type="bool" />
<param index="0" name="with" type="AABB" />
<description>
- Returns [code]true[/code] if this [AABB] completely encloses another one.
+ Returns [code]true[/code] if this bounding box [i]completely[/i] encloses the [param with] box. The edges of both boxes are included.
+ [codeblocks]
+ [gdscript]
+ var a = AABB(Vector3(0, 0, 0), Vector3(4, 4, 4))
+ var b = AABB(Vector3(1, 1, 1), Vector3(3, 3, 3))
+ var c = AABB(Vector3(2, 2, 2), Vector3(8, 8, 8))
+
+ print(a.encloses(a)) # Prints true
+ print(a.encloses(b)) # Prints true
+ print(a.encloses(c)) # Prints false
+ [/gdscript]
+ [csharp]
+ var a = new Aabb(new Vector3(0, 0, 0), new Vector3(4, 4, 4));
+ var b = new Aabb(new Vector3(1, 1, 1), new Vector3(3, 3, 3));
+ var c = new Aabb(new Vector3(2, 2, 2), new Vector3(8, 8, 8));
+
+ GD.Print(a.Encloses(a)); // Prints True
+ GD.Print(a.Encloses(b)); // Prints True
+ GD.Print(a.Encloses(c)); // Prints False
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="expand" qualifiers="const">
<return type="AABB" />
<param index="0" name="to_point" type="Vector3" />
<description>
- Returns a copy of this [AABB] expanded to include a given point.
- [b]Example:[/b]
+ Returns a copy of this bounding box expanded to align the edges with the given [param to_point], if necessary.
[codeblocks]
[gdscript]
- # position (-3, 2, 0), size (1, 1, 1)
- var box = AABB(Vector3(-3, 2, 0), Vector3(1, 1, 1))
- # position (-3, -1, 0), size (3, 4, 2), so we fit both the original AABB and Vector3(0, -1, 2)
- var box2 = box.expand(Vector3(0, -1, 2))
+ var box = AABB(Vector3(0, 0, 0), Vector3(5, 2, 5))
+
+ box = box.expand(Vector3(10, 0, 0))
+ print(box.position) # Prints (0, 0, 0)
+ print(box.size) # Prints (10, 2, 5)
+
+ box = box.expand(Vector3(-5, 0, 5))
+ print(box.position) # Prints (-5, 0, 0)
+ print(box.size) # Prints (15, 2, 5)
[/gdscript]
[csharp]
- // position (-3, 2, 0), size (1, 1, 1)
- var box = new Aabb(new Vector3(-3, 2, 0), new Vector3(1, 1, 1));
- // position (-3, -1, 0), size (3, 4, 2), so we fit both the original AABB and Vector3(0, -1, 2)
- var box2 = box.Expand(new Vector3(0, -1, 2));
+ var box = new Aabb(new Vector3(0, 0, 0), new Vector3(5, 2, 5));
+
+ box = box.Expand(new Vector3(10, 0, 0));
+ GD.Print(box.Position); // Prints (0, 0, 0)
+ GD.Print(box.Size); // Prints (10, 2, 5)
+
+ box = box.Expand(new Vector3(-5, 0, 5));
+ GD.Print(box.Position); // Prints (-5, 0, 0)
+ GD.Print(box.Size); // Prints (15, 2, 5)
[/csharp]
[/codeblocks]
</description>
@@ -76,111 +120,188 @@
<method name="get_center" qualifiers="const">
<return type="Vector3" />
<description>
- Returns the center of the [AABB], which is equal to [member position] + ([member size] / 2).
+ Returns the center point of the bounding box. This is the same as [code]position + (size / 2.0)[/code].
</description>
</method>
<method name="get_endpoint" qualifiers="const">
<return type="Vector3" />
<param index="0" name="idx" type="int" />
<description>
- Gets the position of the 8 endpoints of the [AABB] in space.
+ Returns the position of one of the 8 vertices that compose this bounding box. With a [param idx] of [code]0[/code] this is the same as [member position], and a [param idx] of [code]7[/code] is the same as [member end].
</description>
</method>
<method name="get_longest_axis" qualifiers="const">
<return type="Vector3" />
<description>
- Returns the normalized longest axis of the [AABB].
+ Returns the longest normalized axis of this bounding box's [member size], as a [Vector3] ([constant Vector3.RIGHT], [constant Vector3.UP], or [constant Vector3.BACK]).
+ [codeblocks]
+ [gdscript]
+ var box = AABB(Vector3(0, 0, 0), Vector3(2, 4, 8))
+
+ print(box.get_longest_axis()) # Prints (0, 0, 1)
+ print(box.get_longest_axis_index()) # Prints 2
+ print(box.get_longest_axis_size()) # Prints 8
+ [/gdscript]
+ [csharp]
+ var box = new Aabb(new Vector3(0, 0, 0), new Vector3(2, 4, 8));
+
+ GD.Print(box.GetLongestAxis()); // Prints (0, 0, 1)
+ GD.Print(box.GetLongestAxisIndex()); // Prints 2
+ GD.Print(box.GetLongestAxisSize()); // Prints 8
+ [/csharp]
+ [/codeblocks]
+ See also [method get_longest_axis_index] and [method get_longest_axis_size].
</description>
</method>
<method name="get_longest_axis_index" qualifiers="const">
<return type="int" />
<description>
- Returns the index of the longest axis of the [AABB] (according to [Vector3]'s [code]AXIS_*[/code] constants).
+ Returns the index to the longest axis of this bounding box's [member size] (see [constant Vector3.AXIS_X], [constant Vector3.AXIS_Y], and [constant Vector3.AXIS_Z]).
+ For an example, see [method get_longest_axis].
</description>
</method>
<method name="get_longest_axis_size" qualifiers="const">
<return type="float" />
<description>
- Returns the scalar length of the longest axis of the [AABB].
+ Returns the longest dimension of this bounding box's [member size].
+ For an example, see [method get_longest_axis].
</description>
</method>
<method name="get_shortest_axis" qualifiers="const">
<return type="Vector3" />
<description>
- Returns the normalized shortest axis of the [AABB].
+ Returns the shortest normaalized axis of this bounding box's [member size], as a [Vector3] ([constant Vector3.RIGHT], [constant Vector3.UP], or [constant Vector3.BACK]).
+ [codeblocks]
+ [gdscript]
+ var box = AABB(Vector3(0, 0, 0), Vector3(2, 4, 8))
+
+ print(box.get_shortest_axis()) # Prints (1, 0, 0)
+ print(box.get_shortest_axis_index()) # Prints 0
+ print(box.get_shortest_axis_size()) # Prints 2
+ [/gdscript]
+ [csharp]
+ var box = new Aabb(new Vector3(0, 0, 0), new Vector3(2, 4, 8));
+
+ GD.Print(box.GetShortestAxis()); // Prints (1, 0, 0)
+ GD.Print(box.GetShortestAxisIndex()); // Prints 0
+ GD.Print(box.GetShortestAxisSize()); // Prints 2
+ [/csharp]
+ [/codeblocks]
+ See also [method get_shortest_axis_index] and [method get_shortest_axis_size].
</description>
</method>
<method name="get_shortest_axis_index" qualifiers="const">
<return type="int" />
<description>
- Returns the index of the shortest axis of the [AABB] (according to [Vector3]::AXIS* enum).
+ Returns the index to the shortest axis of this bounding box's [member size] (see [constant Vector3.AXIS_X], [constant Vector3.AXIS_Y], and [constant Vector3.AXIS_Z]).
+ For an example, see [method get_shortest_axis].
</description>
</method>
<method name="get_shortest_axis_size" qualifiers="const">
<return type="float" />
<description>
- Returns the scalar length of the shortest axis of the [AABB].
+ Returns the shortest dimension of this bounding box's [member size].
+ For an example, see [method get_shortest_axis].
</description>
</method>
<method name="get_support" qualifiers="const">
<return type="Vector3" />
<param index="0" name="dir" type="Vector3" />
<description>
- Returns the vertex of the AABB that's the farthest in a given direction. This point is commonly known as the support point in collision detection algorithms.
+ Returns the vertex's position of this bounding box that's the farthest in the given direction. This point is commonly known as the support point in collision detection algorithms.
</description>
</method>
<method name="get_volume" qualifiers="const">
<return type="float" />
<description>
- Returns the volume of the [AABB].
+ Returns the bounding box's volume. This is equivalent to [code]size.x * size.y * size.z[/code]. See also [method has_volume].
</description>
</method>
<method name="grow" qualifiers="const">
<return type="AABB" />
<param index="0" name="by" type="float" />
<description>
- Returns a copy of the [AABB] grown a given number of units towards all the sides.
+ Returns a copy of this bounding box extended on all sides by the given amount [param by]. A negative amount shrinks the box instead.
+ [codeblocks]
+ [gdscript]
+ var a = AABB(Vector3(4, 4, 4), Vector3(8, 8, 8)).grow(4)
+ print(a.position) # Prints (0, 0, 0)
+ print(a.size) # Prints (16, 16, 16)
+
+ var b = AABB(Vector3(0, 0, 0), Vector3(8, 4, 2)).grow(2)
+ print(b.position) # Prints (-2, -2, -2)
+ print(b.size) # Prints (12, 8, 6)
+ [/gdscript]
+ [csharp]
+ var a = new Aabb(new Vector3(4, 4, 4), new Vector3(8, 8, 8)).Grow(4);
+ GD.Print(a.Position); // Prints (0, 0, 0)
+ GD.Print(a.Size); // Prints (16, 16, 16)
+
+ var b = new Aabb(new Vector3(0, 0, 0), new Vector3(8, 4, 2)).Grow(2);
+ GD.Print(b.Position); // Prints (-2, -2, -2)
+ GD.Print(b.Size); // Prints (12, 8, 6)
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="has_point" qualifiers="const">
<return type="bool" />
<param index="0" name="point" type="Vector3" />
<description>
- Returns [code]true[/code] if the [AABB] contains a point. Points on the faces of the AABB are considered included, though float-point precision errors may impact the accuracy of such checks.
- [b]Note:[/b] This method is not reliable for [AABB] with a [i]negative size[/i]. Use [method abs] to get a positive sized equivalent [AABB] to check for contained points.
+ Returns [code]true[/code] if the bounding box contains the given [param point]. By convention, points exactly on the right, top, and front sides are [b]not[/b] included.
+ [b]Note:[/b] This method is not reliable for [AABB] with a [i]negative[/i] [member size]. Use [method abs] first to get a valid bounding box.
</description>
</method>
<method name="has_surface" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if the [AABB] has a surface or a length, and [code]false[/code] if the [AABB] is empty (all components of [member size] are zero or negative).
+ Returns [code]true[/code] if this bounding box has a surface or a length, that is, at least one component of [member size] is greater than [code]0[/code]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="has_volume" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if the [AABB] has a volume, and [code]false[/code] if the [AABB] is flat, empty, or has a negative [member size].
+ Returns [code]true[/code] if this bounding box's width, height, and depth are all positive. See also [method get_volume].
</description>
</method>
<method name="intersection" qualifiers="const">
<return type="AABB" />
<param index="0" name="with" type="AABB" />
<description>
- Returns the intersection between two [AABB]. An empty AABB (size [code](0, 0, 0)[/code]) is returned on failure.
+ Returns the intersection between this bounding box and [param with]. If the boxes do not intersect, returns an empty [AABB]. If the boxes intersect at the edge, returns a flat [AABB] with no volume (see [method has_surface] and [method has_volume]).
+ [codeblocks]
+ [gdscript]
+ var box1 = AABB(Vector3(0, 0, 0), Vector3(5, 2, 8))
+ var box2 = AABB(Vector3(2, 0, 2), Vector3(8, 4, 4))
+
+ var intersection = box1.intersection(box2)
+ print(intersection.position) # Prints (2, 0, 2)
+ print(intersection.size) # Prints (3, 2, 4)
+ [/gdscript]
+ [csharp]
+ var box1 = new Aabb(new Vector3(0, 0, 0), new Vector3(5, 2, 8));
+ var box2 = new Aabb(new Vector3(2, 0, 2), new Vector3(8, 4, 4));
+
+ var intersection = box1.Intersection(box2);
+ GD.Print(intersection.Position); // Prints (2, 0, 2)
+ GD.Print(intersection.Size); // Prints (3, 2, 4)
+ [/csharp]
+ [/codeblocks]
+ [b]Note:[/b] If you only need to know whether two bounding boxes are intersecting, use [method intersects], instead.
</description>
</method>
<method name="intersects" qualifiers="const">
<return type="bool" />
<param index="0" name="with" type="AABB" />
<description>
- Returns [code]true[/code] if the [AABB] overlaps with another.
+ Returns [code]true[/code] if this bounding box overlaps with the box [param with]. The edges of both boxes are [i]always[/i] excluded.
</description>
</method>
<method name="intersects_plane" qualifiers="const">
<return type="bool" />
<param index="0" name="plane" type="Plane" />
<description>
- Returns [code]true[/code] if the [AABB] is on both sides of a plane.
+ Returns [code]true[/code] if this bounding box is on both sides of the given [param plane].
</description>
</method>
<method name="intersects_ray" qualifiers="const">
@@ -188,7 +309,8 @@
<param index="0" name="from" type="Vector3" />
<param index="1" name="dir" type="Vector3" />
<description>
- Returns the point of intersection of the given ray with this [AABB] or [code]null[/code] if there is no intersection. Ray length is infinite.
+ Returns the first point where this bounding box and the given ray intersect, as a [Vector3]. If no intersection occurs, returns [code]null[/code].
+ The ray begin at [param from], faces [param dir] and extends towards infinity.
</description>
</method>
<method name="intersects_segment" qualifiers="const">
@@ -196,40 +318,41 @@
<param index="0" name="from" type="Vector3" />
<param index="1" name="to" type="Vector3" />
<description>
- Returns the point of intersection between [param from] and [param to] with this [AABB] or [code]null[/code] if there is no intersection.
+ Returns the first point where this bounding box and the given segment intersect, as a [Vector3]. If no intersection occurs, returns [code]null[/code].
+ The segment begins at [param from] and ends at [param to].
</description>
</method>
<method name="is_equal_approx" qualifiers="const">
<return type="bool" />
<param index="0" name="aabb" type="AABB" />
<description>
- Returns [code]true[/code] if this [AABB] and [param aabb] are approximately equal, by calling [method @GlobalScope.is_equal_approx] on each component.
+ Returns [code]true[/code] if this bounding box and [param aabb] are approximately equal, by calling [method Vector2.is_equal_approx] on the [member position] and the [member size].
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if this [AABB] is finite, by calling [method @GlobalScope.is_finite] on each component.
+ Returns [code]true[/code] if this bounding box's values are finite, by calling [method Vector2.is_finite] on the [member position] and the [member size].
</description>
</method>
<method name="merge" qualifiers="const">
<return type="AABB" />
<param index="0" name="with" type="AABB" />
<description>
- Returns a larger [AABB] that contains both this [AABB] and [param with].
+ Returns an [AABB] that encloses both this bounding box and [param with] around the edges. See also [method encloses].
</description>
</method>
</methods>
<members>
<member name="end" type="Vector3" setter="" getter="" default="Vector3(0, 0, 0)">
- Ending corner. This is calculated as [code]position + size[/code]. Setting this value will change the size.
+ The ending point. This is usually the corner on the top-right and forward of the bounding box, and is equivalent to [code]position + size[/code]. Setting this point affects the [member size].
</member>
<member name="position" type="Vector3" setter="" getter="" default="Vector3(0, 0, 0)">
- Beginning corner. Typically has values lower than [member end].
+ The origin point. This is usually the corner on the bottom-left and back of the bounding box.
</member>
<member name="size" type="Vector3" setter="" getter="" default="Vector3(0, 0, 0)">
- Size from [member position] to [member end]. Typically, all components are positive.
- If the size is negative, you can use [method abs] to fix it.
+ The bounding box's width, height, and depth starting from [member position]. Setting this value also affects the [member end] point.
+ [b]Note:[/b] It's recommended setting the width, height, and depth to non-negative values. This is because most methods in Godot assume that the [member position] is the bottom-left-back corner, and the [member end] is the top-right-forward corner. To get an equivalent bounding box with non-negative size, use [method abs].
</member>
</members>
<operators>
@@ -237,7 +360,7 @@
<return type="bool" />
<param index="0" name="right" type="AABB" />
<description>
- Returns [code]true[/code] if the AABBs are not equal.
+ Returns [code]true[/code] if the [member position] or [member size] of both bounding boxes are not equal.
[b]Note:[/b] Due to floating-point precision errors, consider using [method is_equal_approx] instead, which is more reliable.
</description>
</operator>
@@ -254,7 +377,7 @@
<return type="bool" />
<param index="0" name="right" type="AABB" />
<description>
- Returns [code]true[/code] if the AABBs are exactly equal.
+ Returns [code]true[/code] if both [member position] and [member size] of the bounding boxes are exactly equal, respectively.
[b]Note:[/b] Due to floating-point precision errors, consider using [method is_equal_approx] instead, which is more reliable.
</description>
</operator>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index acff58dadf..e0d41ab90a 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2551,6 +2551,7 @@
[b]Note:[/b] This setting is only effective when using the Compatibility rendering method, not Forward+ and Mobile.
</member>
<member name="rendering/limits/spatial_indexer/threaded_cull_minimum_instances" type="int" setter="" getter="" default="1000">
+ The minimum number of instances that must be present in a scene to enable culling computations on multiple threads. If a scene has fewer instances than this number, culling is done on a single thread.
</member>
<member name="rendering/limits/spatial_indexer/update_iterations_per_frame" type="int" setter="" getter="" default="10">
</member>
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index 2f6340e572..4435d52527 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -68,6 +68,8 @@ BASE_STRINGS = [
"This value is an integer composed as a bitmask of the following flags.",
"There is currently no description for this class. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
"There is currently no description for this signal. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
+ "There is currently no description for this enum. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
+ "There is currently no description for this constant. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
"There is currently no description for this annotation. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
"There is currently no description for this property. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
"There is currently no description for this constructor. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
@@ -1102,6 +1104,14 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
if value.text is not None and value.text.strip() != "":
f.write(f"{format_text_block(value.text.strip(), value, state)}")
+ else:
+ f.write(".. container:: contribute\n\n\t")
+ f.write(
+ translate(
+ "There is currently no description for this enum. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!"
+ )
+ + "\n\n"
+ )
f.write("\n\n")
@@ -1125,6 +1135,14 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
if constant.text is not None and constant.text.strip() != "":
f.write(f"{format_text_block(constant.text.strip(), constant, state)}")
+ else:
+ f.write(".. container:: contribute\n\n\t")
+ f.write(
+ translate(
+ "There is currently no description for this constant. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!"
+ )
+ + "\n\n"
+ )
f.write("\n\n")
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index 1d1dc6bec8..aed82b4ce8 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -5170,7 +5170,7 @@ void RenderingDeviceDriverD3D12::command_timestamp_write(CommandBufferID p_cmd_b
TimestampQueryPoolInfo *tqp_info = (TimestampQueryPoolInfo *)p_pool_id.id;
ID3D12Resource *results_buffer = tqp_info->results_buffer_allocation->GetResource();
cmd_buf_info->cmd_list->EndQuery(tqp_info->query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, p_index);
- cmd_buf_info->cmd_list->ResolveQueryData(tqp_info->query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, p_index, tqp_info->query_count, results_buffer, p_index * sizeof(uint64_t));
+ cmd_buf_info->cmd_list->ResolveQueryData(tqp_info->query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, p_index, 1, results_buffer, p_index * sizeof(uint64_t));
}
void RenderingDeviceDriverD3D12::command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) {
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index a5af7ef134..0285692ab7 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -1202,7 +1202,22 @@ void ConnectionsDock::_slot_menu_about_to_popup() {
slot_menu->set_item_disabled(slot_menu->get_item_index(SLOT_MENU_DISCONNECT), connection_is_inherited);
}
-void ConnectionsDock::_rmb_pressed(const Ref<InputEvent> &p_event) {
+void ConnectionsDock::_tree_gui_input(const Ref<InputEvent> &p_event) {
+ // Handle Delete press.
+ if (ED_IS_SHORTCUT("connections_editor/disconnect", p_event)) {
+ TreeItem *item = tree->get_selected();
+ if (item && _get_item_type(*item) == TREE_ITEM_TYPE_CONNECTION) {
+ Connection connection = item->get_metadata(0);
+ _disconnect(connection);
+ update_tree();
+
+ // Stop the Delete input from propagating elsewhere.
+ accept_event();
+ return;
+ }
+ }
+
+ // Handle RMB press.
const Ref<InputEventMouseButton> &mb_event = p_event;
if (mb_event.is_null() || !mb_event->is_pressed() || mb_event->get_button_index() != MouseButton::RIGHT) {
return;
@@ -1536,13 +1551,13 @@ ConnectionsDock::ConnectionsDock() {
slot_menu->connect("about_to_popup", callable_mp(this, &ConnectionsDock::_slot_menu_about_to_popup));
slot_menu->add_item(TTR("Edit..."), SLOT_MENU_EDIT);
slot_menu->add_item(TTR("Go to Method"), SLOT_MENU_GO_TO_METHOD);
- slot_menu->add_item(TTR("Disconnect"), SLOT_MENU_DISCONNECT);
+ slot_menu->add_shortcut(ED_SHORTCUT("connections_editor/disconnect", TTR("Disconnect"), Key::KEY_DELETE), SLOT_MENU_DISCONNECT);
add_child(slot_menu);
connect_dialog->connect("connected", callable_mp(this, &ConnectionsDock::_make_or_edit_connection));
tree->connect("item_selected", callable_mp(this, &ConnectionsDock::_tree_item_selected));
tree->connect("item_activated", callable_mp(this, &ConnectionsDock::_tree_item_activated));
- tree->connect("gui_input", callable_mp(this, &ConnectionsDock::_rmb_pressed));
+ tree->connect("gui_input", callable_mp(this, &ConnectionsDock::_tree_gui_input));
add_theme_constant_override("separation", 3 * EDSCALE);
}
diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h
index 2fd4778389..a99f0dd0fe 100644
--- a/editor/connections_dialog.h
+++ b/editor/connections_dialog.h
@@ -252,7 +252,7 @@ class ConnectionsDock : public VBoxContainer {
void _signal_menu_about_to_popup();
void _handle_slot_menu_option(int p_option);
void _slot_menu_about_to_popup();
- void _rmb_pressed(const Ref<InputEvent> &p_event);
+ void _tree_gui_input(const Ref<InputEvent> &p_event);
void _close();
protected:
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 372d558aab..6471bd449e 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -593,9 +593,15 @@ void EditorDebuggerNode::set_breakpoints(const String &p_path, Array p_lines) {
}
}
-void EditorDebuggerNode::reload_scripts() {
+void EditorDebuggerNode::reload_all_scripts() {
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
- dbg->reload_scripts();
+ dbg->reload_all_scripts();
+ });
+}
+
+void EditorDebuggerNode::reload_scripts(const Vector<String> &p_script_paths) {
+ _for_all(tabs, [&](ScriptEditorDebugger *dbg) {
+ dbg->reload_scripts(p_script_paths);
});
}
diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h
index 4338f144b8..d30f29c7c6 100644
--- a/editor/debugger/editor_debugger_node.h
+++ b/editor/debugger/editor_debugger_node.h
@@ -187,7 +187,8 @@ public:
bool is_skip_breakpoints() const;
void set_breakpoint(const String &p_path, int p_line, bool p_enabled);
void set_breakpoints(const String &p_path, Array p_lines);
- void reload_scripts();
+ void reload_all_scripts();
+ void reload_scripts(const Vector<String> &p_script_paths);
// Remote inspector/edit.
void request_remote_tree();
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index 6cc3769976..3c863bdc19 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -1518,8 +1518,12 @@ void ScriptEditorDebugger::set_breakpoint(const String &p_path, int p_line, bool
}
}
-void ScriptEditorDebugger::reload_scripts() {
- _put_msg("reload_scripts", Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
+void ScriptEditorDebugger::reload_all_scripts() {
+ _put_msg("reload_all_scripts", Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
+}
+
+void ScriptEditorDebugger::reload_scripts(const Vector<String> &p_script_paths) {
+ _put_msg("reload_scripts", Variant(p_script_paths).operator Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
}
bool ScriptEditorDebugger::is_skip_breakpoints() {
diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h
index 79224061ff..589e82ef25 100644
--- a/editor/debugger/script_editor_debugger.h
+++ b/editor/debugger/script_editor_debugger.h
@@ -300,7 +300,8 @@ public:
void update_live_edit_root();
- void reload_scripts();
+ void reload_all_scripts();
+ void reload_scripts(const Vector<String> &p_script_paths);
bool is_skip_breakpoints();
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
index 190e8c2445..229eb79e11 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -48,7 +48,7 @@ void EditorHelpSearch::_update_results() {
search_flags |= SEARCH_SHOW_HIERARCHY;
}
- search = Ref<Runner>(memnew(Runner(results_tree, results_tree, term, search_flags)));
+ search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags)));
set_process(true);
}
@@ -96,6 +96,7 @@ void EditorHelpSearch::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible()) {
+ tree_cache.clear();
callable_mp(results_tree, &Tree::clear).call_deferred(); // Wait for the Tree's mouse event propagation.
get_ok_button()->set_disabled(true);
EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "search_help", Rect2(get_position(), get_size()));
@@ -258,6 +259,13 @@ EditorHelpSearch::EditorHelpSearch() {
vbox->add_child(results_tree, true);
}
+void EditorHelpSearch::TreeCache::clear() {
+ for (const KeyValue<String, TreeItem *> &E : item_cache) {
+ memdelete(E.value);
+ }
+ item_cache.clear();
+}
+
bool EditorHelpSearch::Runner::_is_class_disabled_by_feature_profile(const StringName &p_class) {
Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
if (profile.is_null()) {
@@ -436,11 +444,42 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
return iterator_stack.is_empty();
}
+void EditorHelpSearch::Runner::_populate_cache() {
+ root_item = results_tree->get_root();
+
+ if (root_item) {
+ LocalVector<TreeItem *> stack;
+
+ // Add children of root item to stack.
+ for (TreeItem *child = root_item->get_first_child(); child; child = child->get_next()) {
+ stack.push_back(child);
+ }
+
+ // Traverse stack and cache items.
+ while (!stack.is_empty()) {
+ TreeItem *cur_item = stack[stack.size() - 1];
+ stack.resize(stack.size() - 1);
+
+ // Add to the cache.
+ tree_cache->item_cache.insert(cur_item->get_metadata(0).operator String(), cur_item);
+
+ // Add any children to the stack.
+ for (TreeItem *child = cur_item->get_first_child(); child; child = child->get_next()) {
+ stack.push_back(child);
+ }
+
+ // Remove from parent.
+ cur_item->get_parent()->remove_child(cur_item);
+ }
+ } else {
+ root_item = results_tree->create_item();
+ }
+}
+
bool EditorHelpSearch::Runner::_phase_class_items_init() {
iterator_match = matches.begin();
- results_tree->clear();
- root_item = results_tree->create_item();
+ _populate_cache();
class_items.clear();
return true;
@@ -618,27 +657,54 @@ TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_
return class_item;
}
+bool EditorHelpSearch::Runner::_find_or_create_item(TreeItem *p_parent, const String &p_item_meta, TreeItem *&r_item) {
+ // Attempt to find in cache.
+ if (tree_cache->item_cache.has(p_item_meta)) {
+ r_item = tree_cache->item_cache[p_item_meta];
+
+ // Remove from cache.
+ tree_cache->item_cache.erase(p_item_meta);
+
+ // Add to tree.
+ p_parent->add_child(r_item);
+
+ return false;
+ } else {
+ // Otherwise create item.
+ r_item = results_tree->create_item(p_parent);
+
+ return true;
+ }
+}
+
TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray) {
String tooltip = DTR(p_doc->brief_description.strip_edges());
- TreeItem *item = results_tree->create_item(p_parent);
- item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_doc->name));
- item->set_text(0, p_doc->name);
- item->set_text(1, TTR("Class"));
- item->set_tooltip_text(0, tooltip);
- item->set_tooltip_text(1, tooltip);
- item->set_metadata(0, "class_name:" + p_doc->name);
+ const String item_meta = "class_name:" + p_doc->name;
+
+ TreeItem *item = nullptr;
+ if (_find_or_create_item(p_parent, item_meta, item)) {
+ item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_doc->name));
+ item->set_text(0, p_doc->name);
+ item->set_text(1, TTR("Class"));
+ item->set_tooltip_text(0, tooltip);
+ item->set_tooltip_text(1, tooltip);
+ item->set_metadata(0, item_meta);
+ if (p_doc->is_deprecated) {
+ Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon(SNAME("StatusError"));
+ item->add_button(0, error_icon, 0, false, TTR("This class is marked as deprecated."));
+ } else if (p_doc->is_experimental) {
+ Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon(SNAME("NodeWarning"));
+ item->add_button(0, warning_icon, 0, false, TTR("This class is marked as experimental."));
+ }
+ }
+
if (p_gray) {
item->set_custom_color(0, disabled_color);
item->set_custom_color(1, disabled_color);
- }
-
- if (p_doc->is_deprecated) {
- Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon("StatusError");
- item->add_button(0, error_icon, 0, false, TTR("This class is marked as deprecated."));
- } else if (p_doc->is_experimental) {
- Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon("NodeWarning");
- item->add_button(0, warning_icon, 0, false, TTR("This class is marked as experimental."));
+ } else {
+ item->clear_custom_color(0);
+ item->clear_custom_color(1);
}
_match_item(item, p_doc->name);
@@ -679,30 +745,29 @@ TreeItem *EditorHelpSearch::Runner::_create_theme_property_item(TreeItem *p_pare
}
TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, bool is_deprecated, bool is_experimental) {
- Ref<Texture2D> icon;
- String text;
- if (search_flags & SEARCH_SHOW_HIERARCHY) {
- icon = ui_service->get_editor_theme_icon(p_icon);
- text = p_text;
- } else {
- icon = ui_service->get_editor_theme_icon(p_icon);
- text = p_class_name + "." + p_text;
+ const String item_meta = "class_" + p_metatype + ":" + p_class_name + ":" + p_name;
+
+ TreeItem *item = nullptr;
+ if (_find_or_create_item(p_parent, item_meta, item)) {
+ item->set_icon(0, ui_service->get_editor_theme_icon(p_icon));
+ item->set_text(1, TTRGET(p_type));
+ item->set_tooltip_text(0, p_tooltip);
+ item->set_tooltip_text(1, p_tooltip);
+ item->set_metadata(0, item_meta);
+
+ if (is_deprecated) {
+ Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon(SNAME("StatusError"));
+ item->add_button(0, error_icon, 0, false, TTR("This member is marked as deprecated."));
+ } else if (is_experimental) {
+ Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon(SNAME("NodeWarning"));
+ item->add_button(0, warning_icon, 0, false, TTR("This member is marked as experimental."));
+ }
}
- TreeItem *item = results_tree->create_item(p_parent);
- item->set_icon(0, icon);
- item->set_text(0, text);
- item->set_text(1, TTRGET(p_type));
- item->set_tooltip_text(0, p_tooltip);
- item->set_tooltip_text(1, p_tooltip);
- item->set_metadata(0, "class_" + p_metatype + ":" + p_class_name + ":" + p_name);
-
- if (is_deprecated) {
- Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon("StatusError");
- item->add_button(0, error_icon, 0, false, TTR("This member is marked as deprecated."));
- } else if (is_experimental) {
- Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon("NodeWarning");
- item->add_button(0, warning_icon, 0, false, TTR("This member is marked as experimental."));
+ if (search_flags & SEARCH_SHOW_HIERARCHY) {
+ item->set_text(0, p_text);
+ } else {
+ item->set_text(0, p_class_name + "." + p_text);
}
_match_item(item, p_name);
@@ -721,9 +786,10 @@ bool EditorHelpSearch::Runner::work(uint64_t slot) {
return true;
}
-EditorHelpSearch::Runner::Runner(Control *p_icon_service, Tree *p_results_tree, const String &p_term, int p_search_flags) :
+EditorHelpSearch::Runner::Runner(Control *p_icon_service, Tree *p_results_tree, TreeCache *p_tree_cache, const String &p_term, int p_search_flags) :
ui_service(p_icon_service),
results_tree(p_results_tree),
+ tree_cache(p_tree_cache),
term((p_search_flags & SEARCH_CASE_SENSITIVE) == 0 ? p_term.strip_edges().to_lower() : p_term.strip_edges()),
search_flags(p_search_flags),
disabled_color(ui_service->get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor))) {
diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h
index 1d7f2b81df..e4980d6ff7 100644
--- a/editor/editor_help_search.h
+++ b/editor/editor_help_search.h
@@ -67,6 +67,16 @@ class EditorHelpSearch : public ConfirmationDialog {
class Runner;
Ref<Runner> search;
+ struct TreeCache {
+ HashMap<String, TreeItem *> item_cache;
+
+ void clear();
+
+ ~TreeCache() {
+ clear();
+ }
+ } tree_cache;
+
void _update_results();
void _search_box_gui_input(const Ref<InputEvent> &p_event);
@@ -117,6 +127,7 @@ class EditorHelpSearch::Runner : public RefCounted {
Control *ui_service = nullptr;
Tree *results_tree = nullptr;
+ TreeCache *tree_cache = nullptr;
String term;
Vector<String> terms;
int search_flags;
@@ -134,6 +145,9 @@ class EditorHelpSearch::Runner : public RefCounted {
bool _is_class_disabled_by_feature_profile(const StringName &p_class);
+ void _populate_cache();
+ bool _find_or_create_item(TreeItem *p_parent, const String &p_item_meta, TreeItem *&r_item);
+
bool _slice();
bool _phase_match_classes_init();
bool _phase_match_classes();
@@ -162,7 +176,7 @@ class EditorHelpSearch::Runner : public RefCounted {
public:
bool work(uint64_t slot = 100000);
- Runner(Control *p_icon_service, Tree *p_results_tree, const String &p_term, int p_search_flags);
+ Runner(Control *p_icon_service, Tree *p_results_tree, TreeCache *p_tree_cache, const String &p_term, int p_search_flags);
};
#endif // EDITOR_HELP_SEARCH_H
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index a46dffb1f3..0aa9a3bfee 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -865,18 +865,7 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> *
struct FileSystemDock::FileInfoTypeComparator {
bool operator()(const FileInfo &p_a, const FileInfo &p_b) const {
- // Uses the extension, then the icon name to distinguish file types.
- String icon_path_a = "";
- String icon_path_b = "";
- Ref<Texture2D> icon_a = EditorNode::get_singleton()->get_class_icon(p_a.type);
- if (icon_a.is_valid()) {
- icon_path_a = icon_a->get_name();
- }
- Ref<Texture2D> icon_b = EditorNode::get_singleton()->get_class_icon(p_b.type);
- if (icon_b.is_valid()) {
- icon_path_b = icon_b->get_name();
- }
- return NaturalNoCaseComparator()(p_a.name.get_extension() + icon_path_a + p_a.name.get_basename(), p_b.name.get_extension() + icon_path_b + p_b.name.get_basename());
+ return NaturalNoCaseComparator()(p_a.name.get_extension() + p_a.type + p_a.name.get_basename(), p_b.name.get_extension() + p_b.type + p_b.name.get_basename());
}
};
diff --git a/editor/icons/TileMapLayer.svg b/editor/icons/TileMapLayer.svg
new file mode 100644
index 0000000000..1903a87e3b
--- /dev/null
+++ b/editor/icons/TileMapLayer.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 7v2h2v-2zm3 0v2h2v-2zm3 0v2h2v-2zm3 0v2h2v-2zm3 0v2h2v-2z" fill="#8da5f3"/></svg> \ No newline at end of file
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index ad82801692..55191f44d4 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -1001,7 +1001,10 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
}
_update_script_names();
- trigger_live_script_reload();
+ Ref<Script> scr = p_res;
+ if (scr.is_valid()) {
+ trigger_live_script_reload(scr->get_path());
+ }
}
void ScriptEditor::_scene_saved_callback(const String &p_path) {
@@ -1029,16 +1032,33 @@ void ScriptEditor::_scene_saved_callback(const String &p_path) {
}
}
-void ScriptEditor::trigger_live_script_reload() {
+void ScriptEditor::trigger_live_script_reload(const String &p_script_path) {
+ if (!script_paths_to_reload.has(p_script_path)) {
+ script_paths_to_reload.append(p_script_path);
+ }
if (!pending_auto_reload && auto_reload_running_scripts) {
callable_mp(this, &ScriptEditor::_live_auto_reload_running_scripts).call_deferred();
pending_auto_reload = true;
}
}
+void ScriptEditor::trigger_live_script_reload_all() {
+ if (!pending_auto_reload && auto_reload_running_scripts) {
+ call_deferred(SNAME("_live_auto_reload_running_scripts"));
+ pending_auto_reload = true;
+ reload_all_scripts = true;
+ }
+}
+
void ScriptEditor::_live_auto_reload_running_scripts() {
pending_auto_reload = false;
- EditorDebuggerNode::get_singleton()->reload_scripts();
+ if (reload_all_scripts) {
+ EditorDebuggerNode::get_singleton()->reload_all_scripts();
+ } else {
+ EditorDebuggerNode::get_singleton()->reload_scripts(script_paths_to_reload);
+ }
+ reload_all_scripts = false;
+ script_paths_to_reload.clear();
}
bool ScriptEditor::_test_script_times_on_disk(Ref<Resource> p_for_script) {
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 4a814ea1bc..68eb23c838 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -382,6 +382,8 @@ class ScriptEditor : public PanelContainer {
bool pending_auto_reload;
bool auto_reload_running_scripts;
+ bool reload_all_scripts = false;
+ Vector<String> script_paths_to_reload;
void _live_auto_reload_running_scripts();
void _update_selected_editor_menu();
@@ -542,7 +544,8 @@ public:
void clear_docs_from_script(const Ref<Script> &p_script);
void update_docs_from_script(const Ref<Script> &p_script);
- void trigger_live_script_reload();
+ void trigger_live_script_reload(const String &p_script_path);
+ void trigger_live_script_reload_all();
bool can_take_away_focus() const;
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 45fb531d37..5bd6f83616 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -824,7 +824,7 @@ void ScriptEditor::_update_modified_scripts_for_external_editor(Ref<Script> p_fo
scr->set_last_modified_time(rel_scr->get_last_modified_time());
scr->update_exports();
- trigger_live_script_reload();
+ trigger_live_script_reload(scr->get_path());
}
}
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 9d1c0d6c62..9c7ba827b7 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -3998,6 +3998,7 @@ void SceneTreeDock::_list_all_subresources(PopupMenu *p_menu) {
}
p_menu->add_item(display_text);
+ p_menu->set_item_tooltip(-1, pair.first->get_path());
p_menu->set_item_metadata(-1, pair.first->get_instance_id());
}
}
diff --git a/main/main.cpp b/main/main.cpp
index b50def9cec..50ec7d82b4 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -475,7 +475,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" --profiling Enable profiling in the script debugger.\n");
OS::get_singleton()->print(" --gpu-profile Show a GPU profile of the tasks that took the most time during frame rendering.\n");
OS::get_singleton()->print(" --gpu-validation Enable graphics API validation layers for debugging.\n");
-#if DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
OS::get_singleton()->print(" --gpu-abort Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n");
#endif
OS::get_singleton()->print(" --generate-spirv-debug-info Generate SPIR-V debug information. This allows source-level shader debugging with RenderDoc.\n");
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index 2c52144896..25094dda77 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -51,3 +51,11 @@ Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_u
Barrier arguments have been removed from all relevant functions as they're no longer required.
Draw and compute list overlap no longer needs to be specified.
Initial and final actions have been simplified into fewer options.
+
+
+GH-87115
+--------
+Validate extension JSON: Error: Field 'classes/TileMap/methods/get_collision_visibility_mode': is_const changed value in new API, from false to true.
+Validate extension JSON: Error: Field 'classes/TileMap/methods/get_navigation_visibility_mode': is_const changed value in new API, from false to true.
+
+Two TileMap getters were made const. No adjustments should be necessary.
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 1f0830aa17..920aa63fbe 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2320,14 +2320,13 @@ struct GDScriptDepSort {
void GDScriptLanguage::reload_all_scripts() {
#ifdef DEBUG_ENABLED
print_verbose("GDScript: Reloading all scripts");
- List<Ref<GDScript>> scripts;
+ Array scripts;
{
MutexLock lock(this->mutex);
SelfList<GDScript> *elem = script_list.first();
while (elem) {
- // Scripts will reload all subclasses, so only reload root scripts.
- if (elem->self()->is_root_script() && elem->self()->get_path().is_resource_file()) {
+ if (elem->self()->get_path().is_resource_file()) {
print_verbose("GDScript: Found: " + elem->self()->get_path());
scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident
}
@@ -2348,19 +2347,11 @@ void GDScriptLanguage::reload_all_scripts() {
#endif
}
- //as scripts are going to be reloaded, must proceed without locking here
-
- scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
-
- for (Ref<GDScript> &scr : scripts) {
- print_verbose("GDScript: Reloading: " + scr->get_path());
- scr->load_source_code(scr->get_path());
- scr->reload(true);
- }
+ reload_scripts(scripts, true);
#endif
}
-void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
+void GDScriptLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload) {
#ifdef DEBUG_ENABLED
List<Ref<GDScript>> scripts;
@@ -2386,7 +2377,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
for (Ref<GDScript> &scr : scripts) {
- bool reload = scr == p_script || to_reload.has(scr->get_base());
+ bool reload = p_scripts.has(scr) || to_reload.has(scr->get_base());
if (!reload) {
continue;
@@ -2409,7 +2400,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
}
}
-//same thing for placeholders
+ //same thing for placeholders
#ifdef TOOLS_ENABLED
while (scr->placeholders.size()) {
@@ -2437,6 +2428,8 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
for (KeyValue<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) {
Ref<GDScript> scr = E.key;
+ print_verbose("GDScript: Reloading: " + scr->get_path());
+ scr->load_source_code(scr->get_path());
scr->reload(p_soft_reload);
//restore state if saved
@@ -2484,6 +2477,12 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
#endif
}
+void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
+ Array scripts;
+ scripts.push_back(p_script);
+ reload_scripts(scripts, p_soft_reload);
+}
+
void GDScriptLanguage::frame() {
calls = 0;
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 7b0e2136ed..2da9b89eb9 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -575,6 +575,7 @@ public:
virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) override;
virtual void reload_all_scripts() override;
+ virtual void reload_scripts(const Array &p_scripts, bool p_soft_reload) override;
virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override;
virtual void frame() override;
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 9bface6136..f902cb10cc 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -121,7 +121,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
RBMap<MethodBind *, int> method_bind_map;
RBMap<GDScriptFunction *, int> lambdas_map;
-#if DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
// Keep method and property names for pointer and validated operations.
// Used when disassembling the bytecode.
Vector<String> operator_names;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 9ad2ba1914..c330037bd6 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -1370,7 +1370,7 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
}
}
-static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) {
+static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value, GDScriptParser::CompletionContext &p_context) {
GDScriptCompletionIdentifier ci;
ci.value = p_value;
ci.type.is_constant = true;
@@ -1392,8 +1392,22 @@ static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) {
scr = obj->get_script();
}
if (scr.is_valid()) {
- ci.type.script_type = scr;
+ ci.type.script_path = scr->get_path();
+
+ if (scr->get_path().ends_with(".gd")) {
+ Error err;
+ Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(scr->get_path(), GDScriptParserRef::INTERFACE_SOLVED, err);
+ if (err == OK) {
+ ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ ci.type.class_type = parser->get_parser()->get_tree();
+ ci.type.kind = GDScriptParser::DataType::CLASS;
+ p_context.dependent_parsers.push_back(parser);
+ return ci;
+ }
+ }
+
ci.type.kind = GDScriptParser::DataType::SCRIPT;
+ ci.type.script_type = scr;
ci.type.native_type = scr->get_instance_base_type();
} else {
ci.type.kind = GDScriptParser::DataType::NATIVE;
@@ -1481,7 +1495,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
if (p_expression->is_constant) {
// Already has a value, so just use that.
- r_type = _type_from_variant(p_expression->reduced_value);
+ r_type = _type_from_variant(p_expression->reduced_value, p_context);
switch (p_expression->get_datatype().kind) {
case GDScriptParser::DataType::ENUM:
case GDScriptParser::DataType::CLASS:
@@ -1495,7 +1509,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
switch (p_expression->type) {
case GDScriptParser::Node::LITERAL: {
const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(p_expression);
- r_type = _type_from_variant(literal->value);
+ r_type = _type_from_variant(literal->value, p_context);
found = true;
} break;
case GDScriptParser::Node::SELF: {
@@ -1676,7 +1690,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
if (!which.is_empty()) {
// Try singletons first
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(which)) {
- r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]);
+ r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which], p_context);
found = true;
} else {
for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
@@ -1727,7 +1741,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
if (ce.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) {
if (ret.get_type() != Variant::OBJECT || ret.operator Object *() != nullptr) {
- r_type = _type_from_variant(ret);
+ r_type = _type_from_variant(ret, p_context);
found = true;
}
}
@@ -1755,7 +1769,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
if (base.value.get_type() == Variant::DICTIONARY && base.value.operator Dictionary().has(String(subscript->attribute->name))) {
Variant value = base.value.operator Dictionary()[String(subscript->attribute->name)];
- r_type = _type_from_variant(value);
+ r_type = _type_from_variant(value, p_context);
found = true;
break;
}
@@ -1807,7 +1821,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
if (base.value.in(index.value)) {
Variant value = base.value.get(index.value);
- r_type = _type_from_variant(value);
+ r_type = _type_from_variant(value, p_context);
found = true;
break;
}
@@ -1862,7 +1876,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
bool valid = false;
Variant res = base_val.get(index.value, &valid);
if (valid) {
- r_type = _type_from_variant(res);
+ r_type = _type_from_variant(res, p_context);
r_type.value = Variant();
r_type.type.is_constant = false;
found = true;
@@ -1920,7 +1934,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
found = false;
break;
}
- r_type = _type_from_variant(res);
+ r_type = _type_from_variant(res, p_context);
if (!v1_use_value || !v2_use_value) {
r_type.value = Variant();
r_type.type.is_constant = false;
@@ -2155,7 +2169,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
} else {
Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier->name));
if (scr.is_valid()) {
- r_type = _type_from_variant(scr);
+ r_type = _type_from_variant(scr, p_context);
r_type.type.is_meta_type = true;
return true;
}
@@ -2165,7 +2179,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
// Check global variables (including autoloads).
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier->name)) {
- r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier->name]);
+ r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier->name], p_context);
return true;
}
@@ -2218,7 +2232,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
const GDScriptParser::ExpressionNode *init = member.variable->initializer;
if (init->is_constant) {
r_type.value = init->reduced_value;
- r_type = _type_from_variant(init->reduced_value);
+ r_type = _type_from_variant(init->reduced_value, p_context);
return true;
} else if (init->start_line == p_context.current_line) {
return false;
@@ -2245,7 +2259,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
r_type.enumeration = member.m_enum->identifier->name;
return true;
case GDScriptParser::ClassNode::Member::ENUM_VALUE:
- r_type = _type_from_variant(member.enum_value.value);
+ r_type = _type_from_variant(member.enum_value.value, p_context);
return true;
case GDScriptParser::ClassNode::Member::SIGNAL:
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -2281,7 +2295,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
HashMap<StringName, Variant> constants;
scr->get_constants(&constants);
if (constants.has(p_identifier)) {
- r_type = _type_from_variant(constants[p_identifier]);
+ r_type = _type_from_variant(constants[p_identifier], p_context);
return true;
}
@@ -2345,7 +2359,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
bool valid = false;
Variant res = tmp.get(p_identifier, &valid);
if (valid) {
- r_type = _type_from_variant(res);
+ r_type = _type_from_variant(res, p_context);
r_type.value = Variant();
r_type.type.is_constant = false;
return true;
@@ -3197,6 +3211,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
List<String> opts;
p_owner->get_argument_options("get_node", 0, &opts);
+ bool for_unique_name = false;
+ if (completion_context.node != nullptr && completion_context.node->type == GDScriptParser::Node::GET_NODE && !static_cast<GDScriptParser::GetNodeNode *>(completion_context.node)->use_dollar) {
+ for_unique_name = true;
+ }
+
for (const String &E : opts) {
r_forced = true;
String opt = E.strip_edges();
@@ -3205,6 +3224,14 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
// or handle NodePaths which are valid identifiers and don't need quotes.
opt = opt.unquote();
}
+
+ if (for_unique_name) {
+ if (!opt.begins_with("%")) {
+ continue;
+ }
+ opt = opt.substr(1);
+ }
+
// The path needs quotes if it's not a valid identifier (with an exception
// for "/" as path separator, which also doesn't require quotes).
if (!opt.replace("/", "_").is_valid_identifier()) {
@@ -3216,11 +3243,13 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
options.insert(option.display, option);
}
- // Get autoloads.
- for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
- String path = "/root/" + E.key;
- ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);
- options.insert(option.display, option);
+ if (!for_unique_name) {
+ // Get autoloads.
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ String path = "/root/" + E.key;
+ ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);
+ options.insert(option.display, option);
+ }
}
}
} break;
@@ -3564,7 +3593,8 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
case GDScriptParser::COMPLETION_ASSIGN:
case GDScriptParser::COMPLETION_CALL_ARGUMENTS:
case GDScriptParser::COMPLETION_IDENTIFIER:
- case GDScriptParser::COMPLETION_PROPERTY_METHOD: {
+ case GDScriptParser::COMPLETION_PROPERTY_METHOD:
+ case GDScriptParser::COMPLETION_SUBSCRIPT: {
GDScriptParser::DataType base_type;
if (context.current_class) {
if (context.type != GDScriptParser::COMPLETION_SUPER_METHOD) {
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index 95b3be2811..e00b92b752 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -114,7 +114,7 @@ void GDScriptTextDocument::didSave(const Variant &p_param) {
scr->update_exports();
ScriptEditor::get_singleton()->reload_scripts(true);
ScriptEditor::get_singleton()->update_docs_from_script(scr);
- ScriptEditor::get_singleton()->trigger_live_script_reload();
+ ScriptEditor::get_singleton()->trigger_live_script_reload(scr->get_path());
}
}
diff --git a/modules/gdscript/tests/README.md b/modules/gdscript/tests/README.md
index 361d586d32..cea251bab5 100644
--- a/modules/gdscript/tests/README.md
+++ b/modules/gdscript/tests/README.md
@@ -6,3 +6,44 @@ and output files.
See the
[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/contributing/development/core_and_modules/unit_testing.html#integration-tests-for-gdscript)
for information about creating and running GDScript integration tests.
+
+# GDScript Autocompletion tests
+
+The `script/completion` folder contains test for the GDScript autocompletion.
+
+Each test case consists of at least one `.gd` file, which contains the code, and one `.cfg` file, which contains expected results and configuration. Inside of the GDScript file the character `➡` represents the cursor position, at which autocompletion is invoked.
+
+The config file contains two section:
+
+`[input]` contains keys that configure the test environment. The following keys are possible:
+
+- `cs: boolean = false`: If `true`, the test will be skipped when running a non C# build.
+- `use_single_quotes: boolean = false`: Configures the corresponding editor setting for the test.
+- `scene: String`: Allows to specify a scene which is opened while autocompletion is performed. If this is not set the test runner will search for a `.tscn` file with the same basename as the GDScript file. If that isn't found either, autocompletion will behave as if no scene was opened.
+
+`[output]` specifies the expected results for the test. The following key are supported:
+
+- `include: Array`: An unordered list of suggestions that should be in the result. Each entry is one dictionary with the following keys: `display`, `insert_text`, `kind`, `location`, which correspond to the suggestion struct which is used in the code. The runner only tests against specified keys, so in most cases `display` will suffice.
+- `exclude: Array`: An array of suggestions which should not be in the result. The entries take the same form as for `include`.
+- `call_hint: String`: The expected call hint returned by autocompletion.
+- `forced: boolean`: Whether autocompletion is expected to force opening a completion window.
+
+Tests will only test against entries in `[output]` that were specified.
+
+## Writing autocompletion tests
+
+To avoid failing edge cases a certain behaviour needs to be tested multiple times. Some things that tests should account for:
+
+- All possible types: Test with all possible types that apply to the tested behaviour. (For the last points testing against `SCRIPT` and `CLASS` should suffice. `CLASS` can be obtained through C#, `SCRIPT` through GDScript. Relying on autoloads to be of type `SCRIPT` is not good, since this might change in the future.)
+
+ - `BUILTIN`
+ - `NATIVE`
+ - GDScripts (with `class_name` as well as `preload`ed)
+ - C# (as standin for all other language bindings) (with `class_name` as well as `preload`ed)
+ - Autoloads
+
+- Possible contexts: the completion might be placed in different places of the program. e.g:
+ - initializers of class members
+ - directly inside a suite
+ - assignments inside a suite
+ - as parameter to a call
diff --git a/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg b/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg
index 4edee46039..27e695d245 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg
+++ b/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg
@@ -1,4 +1,4 @@
[output]
-expected=[
+include=[
{"display": "autoplay"},
]
diff --git a/modules/gdscript/tests/test_completion.h b/modules/gdscript/tests/test_completion.h
index abc34bd4bf..fd6b5321e6 100644
--- a/modules/gdscript/tests/test_completion.h
+++ b/modules/gdscript/tests/test_completion.h
@@ -128,19 +128,23 @@ static void test_directory(const String &p_dir) {
EditorSettings::get_singleton()->set_setting("text_editor/completion/use_single_quotes", conf.get_value("input", "use_single_quotes", false));
List<Dictionary> include;
- to_dict_list(conf.get_value("result", "include", Array()), include);
+ to_dict_list(conf.get_value("output", "include", Array()), include);
List<Dictionary> exclude;
- to_dict_list(conf.get_value("result", "exclude", Array()), exclude);
+ to_dict_list(conf.get_value("output", "exclude", Array()), exclude);
List<ScriptLanguage::CodeCompletionOption> options;
String call_hint;
bool forced;
Node *owner = nullptr;
- if (dir->file_exists(next.get_basename() + ".tscn")) {
- String project_path = "res://completion";
- Ref<PackedScene> scene = ResourceLoader::load(project_path.path_join(next.get_basename() + ".tscn"), "PackedScene");
+ if (conf.has_section_key("input", "scene")) {
+ Ref<PackedScene> scene = ResourceLoader::load(conf.get_value("input", "scene"), "PackedScene");
+ if (scene.is_valid()) {
+ owner = scene->instantiate();
+ }
+ } else if (dir->file_exists(next.get_basename() + ".tscn")) {
+ Ref<PackedScene> scene = ResourceLoader::load(path.path_join(next.get_basename() + ".tscn"), "PackedScene");
if (scene.is_valid()) {
owner = scene->instantiate();
}
@@ -169,8 +173,8 @@ static void test_directory(const String &p_dir) {
CHECK_MESSAGE(contains_excluded.is_empty(), "Autocompletion suggests illegal option '", contains_excluded, "' for '", path.path_join(next), "'.");
CHECK(include.is_empty());
- String expected_call_hint = conf.get_value("result", "call_hint", call_hint);
- bool expected_forced = conf.get_value("result", "forced", forced);
+ String expected_call_hint = conf.get_value("output", "call_hint", call_hint);
+ bool expected_forced = conf.get_value("output", "forced", forced);
CHECK(expected_call_hint == call_hint);
CHECK(expected_forced == forced);
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index ac6977504a..ef24dc35ca 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -720,11 +720,22 @@ void CSharpLanguage::reload_all_scripts() {
#endif
}
-void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
- (void)p_script; // UNUSED
-
+void CSharpLanguage::reload_scripts(const Array &p_scripts, bool p_soft_reload) {
CRASH_COND(!Engine::get_singleton()->is_editor_hint());
+ bool has_csharp_script = false;
+ for (int i = 0; i < p_scripts.size(); ++i) {
+ Ref<CSharpScript> cs_script = p_scripts[i];
+ if (cs_script.is_valid()) {
+ has_csharp_script = true;
+ break;
+ }
+ }
+
+ if (!has_csharp_script) {
+ return;
+ }
+
#ifdef TOOLS_ENABLED
get_godotsharp_editor()->get_node(NodePath("HotReloadAssemblyWatcher"))->call("RestartTimer");
#endif
@@ -736,6 +747,12 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft
#endif
}
+void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
+ Array scripts;
+ scripts.push_back(p_script);
+ reload_scripts(scripts, p_soft_reload);
+}
+
#ifdef GD_MONO_HOT_RELOAD
bool CSharpLanguage::is_assembly_reloading_needed() {
ERR_FAIL_NULL_V(gdmono, false);
@@ -1074,7 +1091,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
// The script instance could not be instantiated or wasn't in the list of placeholders to replace.
obj->set_script(scr);
-#if DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
// If we reached here, the instantiated script must be a placeholder.
CRASH_COND(!obj->get_script_instance()->is_placeholder());
#endif
@@ -2310,7 +2327,7 @@ void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) {
p_script->_update_exports();
-#if TOOLS_ENABLED
+#ifdef TOOLS_ENABLED
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem *efs = EditorFileSystem::get_singleton();
@@ -2666,7 +2683,7 @@ Error CSharpScript::reload(bool p_keep_state) {
_update_exports();
-#if TOOLS_ENABLED
+#ifdef TOOLS_ENABLED
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem *efs = EditorFileSystem::get_singleton();
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 41e8d63be1..310cb81929 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -478,6 +478,7 @@ public:
/* TODO? */ void get_public_annotations(List<MethodInfo> *p_annotations) const override {}
void reload_all_scripts() override;
+ void reload_scripts(const Array &p_scripts, bool p_soft_reload) override;
void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override;
/* LOADER FUNCTIONS */
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
index 03a7dc453c..9674626183 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
@@ -6,6 +6,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators", "G
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Sample", "Godot.SourceGenerators.Sample\Godot.SourceGenerators.Sample.csproj", "{7297A614-8DF5-43DE-9EAD-99671B26BD1F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Tests", "Godot.SourceGenerators.Tests\Godot.SourceGenerators.Tests.csproj", "{07E6D201-35C9-4463-9B29-D16621EA733D}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharp", "..\..\glue\GodotSharp\GodotSharp\GodotSharp.csproj", "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}"
EndProject
Global
@@ -26,6 +28,10 @@ Global
{7297A614-8DF5-43DE-9EAD-99671B26BD1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7297A614-8DF5-43DE-9EAD-99671B26BD1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7297A614-8DF5-43DE-9EAD-99671B26BD1F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {07E6D201-35C9-4463-9B29-D16621EA733D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {07E6D201-35C9-4463-9B29-D16621EA733D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {07E6D201-35C9-4463-9B29-D16621EA733D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {07E6D201-35C9-4463-9B29-D16621EA733D}.Release|Any CPU.Build.0 = Release|Any CPU
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/GlobalClass.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/GlobalClass.cs
new file mode 100644
index 0000000000..b9f11908e1
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/GlobalClass.cs
@@ -0,0 +1,14 @@
+namespace Godot.SourceGenerators.Sample;
+
+[GlobalClass]
+public partial class CustomGlobalClass : GodotObject
+{
+}
+
+// This doesn't works because global classes can't have any generic type parameter.
+/*
+[GlobalClass]
+public partial class CustomGlobalClass<T> : Node
+{
+}
+*/
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
index 3f569ebac3..d0907c1cd4 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
@@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
+ <LangVersion>11</LangVersion>
</PropertyGroup>
<PropertyGroup>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MustBeVariantSamples.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MustBeVariantSamples.cs
new file mode 100644
index 0000000000..1e06091e80
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MustBeVariantSamples.cs
@@ -0,0 +1,164 @@
+using System;
+using Godot.Collections;
+using Array = Godot.Collections.Array;
+
+namespace Godot.SourceGenerators.Sample;
+
+public class MustBeVariantMethods
+{
+ public void MustBeVariantMethodCalls()
+ {
+ Method<bool>();
+ Method<char>();
+ Method<sbyte>();
+ Method<byte>();
+ Method<short>();
+ Method<ushort>();
+ Method<int>();
+ Method<uint>();
+ Method<long>();
+ Method<ulong>();
+ Method<float>();
+ Method<double>();
+ Method<string>();
+ Method<Vector2>();
+ Method<Vector2I>();
+ Method<Rect2>();
+ Method<Rect2I>();
+ Method<Transform2D>();
+ Method<Vector3>();
+ Method<Vector3I>();
+ Method<Vector4>();
+ Method<Vector4I>();
+ Method<Basis>();
+ Method<Quaternion>();
+ Method<Transform3D>();
+ Method<Projection>();
+ Method<Aabb>();
+ Method<Color>();
+ Method<Plane>();
+ Method<Callable>();
+ Method<Signal>();
+ Method<GodotObject>();
+ Method<StringName>();
+ Method<NodePath>();
+ Method<Rid>();
+ Method<Dictionary>();
+ Method<Array>();
+ Method<byte[]>();
+ Method<int[]>();
+ Method<long[]>();
+ Method<float[]>();
+ Method<double[]>();
+ Method<string[]>();
+ Method<Vector2[]>();
+ Method<Vector3[]>();
+ Method<Color[]>();
+ Method<GodotObject[]>();
+ Method<StringName[]>();
+ Method<NodePath[]>();
+ Method<Rid[]>();
+
+ // This call fails because generic type is not Variant-compatible.
+ //Method<object>();
+ }
+
+ public void Method<[MustBeVariant] T>()
+ {
+ }
+
+ public void MustBeVariantClasses()
+ {
+ new ClassWithGenericVariant<bool>();
+ new ClassWithGenericVariant<char>();
+ new ClassWithGenericVariant<sbyte>();
+ new ClassWithGenericVariant<byte>();
+ new ClassWithGenericVariant<short>();
+ new ClassWithGenericVariant<ushort>();
+ new ClassWithGenericVariant<int>();
+ new ClassWithGenericVariant<uint>();
+ new ClassWithGenericVariant<long>();
+ new ClassWithGenericVariant<ulong>();
+ new ClassWithGenericVariant<float>();
+ new ClassWithGenericVariant<double>();
+ new ClassWithGenericVariant<string>();
+ new ClassWithGenericVariant<Vector2>();
+ new ClassWithGenericVariant<Vector2I>();
+ new ClassWithGenericVariant<Rect2>();
+ new ClassWithGenericVariant<Rect2I>();
+ new ClassWithGenericVariant<Transform2D>();
+ new ClassWithGenericVariant<Vector3>();
+ new ClassWithGenericVariant<Vector3I>();
+ new ClassWithGenericVariant<Vector4>();
+ new ClassWithGenericVariant<Vector4I>();
+ new ClassWithGenericVariant<Basis>();
+ new ClassWithGenericVariant<Quaternion>();
+ new ClassWithGenericVariant<Transform3D>();
+ new ClassWithGenericVariant<Projection>();
+ new ClassWithGenericVariant<Aabb>();
+ new ClassWithGenericVariant<Color>();
+ new ClassWithGenericVariant<Plane>();
+ new ClassWithGenericVariant<Callable>();
+ new ClassWithGenericVariant<Signal>();
+ new ClassWithGenericVariant<GodotObject>();
+ new ClassWithGenericVariant<StringName>();
+ new ClassWithGenericVariant<NodePath>();
+ new ClassWithGenericVariant<Rid>();
+ new ClassWithGenericVariant<Dictionary>();
+ new ClassWithGenericVariant<Array>();
+ new ClassWithGenericVariant<byte[]>();
+ new ClassWithGenericVariant<int[]>();
+ new ClassWithGenericVariant<long[]>();
+ new ClassWithGenericVariant<float[]>();
+ new ClassWithGenericVariant<double[]>();
+ new ClassWithGenericVariant<string[]>();
+ new ClassWithGenericVariant<Vector2[]>();
+ new ClassWithGenericVariant<Vector3[]>();
+ new ClassWithGenericVariant<Color[]>();
+ new ClassWithGenericVariant<GodotObject[]>();
+ new ClassWithGenericVariant<StringName[]>();
+ new ClassWithGenericVariant<NodePath[]>();
+ new ClassWithGenericVariant<Rid[]>();
+
+ // This class fails because generic type is not Variant-compatible.
+ //new ClassWithGenericVariant<object>();
+ }
+}
+
+public class ClassWithGenericVariant<[MustBeVariant] T>
+{
+}
+
+public class MustBeVariantAnnotatedMethods
+{
+ [GenericTypeAttribute<string>()]
+ public void MethodWithAttributeOk()
+ {
+ }
+
+ // This method definition fails because generic type is not Variant-compatible.
+ /*
+ [GenericTypeAttribute<object>()]
+ public void MethodWithWrongAttribute()
+ {
+ }
+ */
+}
+
+[GenericTypeAttribute<string>()]
+public class ClassVariantAnnotated
+{
+}
+
+// This class definition fails because generic type is not Variant-compatible.
+/*
+[GenericTypeAttribute<object>()]
+public class ClassNonVariantAnnotated
+{
+}
+*/
+
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
+public class GenericTypeAttribute<[MustBeVariant] T> : Attribute
+{
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpAnalyzerVerifier.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpAnalyzerVerifier.cs
new file mode 100644
index 0000000000..e3e7373b2e
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpAnalyzerVerifier.cs
@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators.Tests;
+
+public static class CSharpAnalyzerVerifier<TAnalyzer>
+where TAnalyzer : DiagnosticAnalyzer, new()
+{
+ public class Test : CSharpAnalyzerTest<TAnalyzer, XUnitVerifier>
+ {
+ public Test()
+ {
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net60;
+
+ SolutionTransforms.Add((Solution solution, ProjectId projectId) =>
+ {
+ Project project =
+ solution.GetProject(projectId)!.AddMetadataReference(Constants.GodotSharpAssembly
+ .CreateMetadataReference());
+
+ return project.Solution;
+ });
+ }
+ }
+
+ public static Task Verify(string sources, params DiagnosticResult[] expected)
+ {
+ return MakeVerifier(new string[] { sources }, expected).RunAsync();
+ }
+
+ public static Test MakeVerifier(ICollection<string> sources, params DiagnosticResult[] expected)
+ {
+ var verifier = new Test();
+
+ verifier.TestState.AnalyzerConfigFiles.Add(("/.globalconfig", $"""
+ is_global = true
+ build_property.GodotProjectDir = {Constants.ExecutingAssemblyPath}
+ """));
+
+ verifier.TestState.Sources.AddRange(sources.Select(source =>
+ {
+ return (source, SourceText.From(File.ReadAllText(Path.Combine(Constants.SourceFolderPath, source))));
+ }));
+
+ verifier.ExpectedDiagnostics.AddRange(expected);
+ return verifier;
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/GlobalClassAnalyzerTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/GlobalClassAnalyzerTests.cs
new file mode 100644
index 0000000000..74d6afceb3
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/GlobalClassAnalyzerTests.cs
@@ -0,0 +1,20 @@
+using Xunit;
+
+namespace Godot.SourceGenerators.Tests;
+
+public class GlobalClassAnalyzerTests
+{
+ [Fact]
+ public async void GlobalClassMustDeriveFromGodotObjectTest()
+ {
+ const string GlobalClassGD0401 = "GlobalClass.GD0401.cs";
+ await CSharpAnalyzerVerifier<GlobalClassAnalyzer>.Verify(GlobalClassGD0401);
+ }
+
+ [Fact]
+ public async void GlobalClassMustNotBeGenericTest()
+ {
+ const string GlobalClassGD0402 = "GlobalClass.GD0402.cs";
+ await CSharpAnalyzerVerifier<GlobalClassAnalyzer>.Verify(GlobalClassGD0402);
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj
index e39c14f049..13e54a543f 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj
@@ -17,6 +17,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
+ <PackageReference Include="Microsoft.CodeAnalysis.Testing.Verifiers.XUnit" Version="1.1.1" />
+ <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/MustBeVariantAnalyzerTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/MustBeVariantAnalyzerTests.cs
new file mode 100644
index 0000000000..62c602efbb
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/MustBeVariantAnalyzerTests.cs
@@ -0,0 +1,20 @@
+using Xunit;
+
+namespace Godot.SourceGenerators.Tests;
+
+public class MustBeVariantAnalyzerTests
+{
+ [Fact]
+ public async void GenericTypeArgumentMustBeVariantTest()
+ {
+ const string MustBeVariantGD0301 = "MustBeVariant.GD0301.cs";
+ await CSharpAnalyzerVerifier<MustBeVariantAnalyzer>.Verify(MustBeVariantGD0301);
+ }
+
+ [Fact]
+ public async void GenericTypeParameterMustBeVariantAnnotatedTest()
+ {
+ const string MustBeVariantGD0302 = "MustBeVariant.GD0302.cs";
+ await CSharpAnalyzerVerifier<MustBeVariantAnalyzer>.Verify(MustBeVariantGD0302);
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0401.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0401.cs
new file mode 100644
index 0000000000..6e6d3a6f39
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0401.cs
@@ -0,0 +1,22 @@
+using Godot;
+
+// This works because it inherits from GodotObject.
+[GlobalClass]
+public partial class CustomGlobalClass1 : GodotObject
+{
+
+}
+
+// This works because it inherits from an object that inherits from GodotObject
+[GlobalClass]
+public partial class CustomGlobalClass2 : Node
+{
+
+}
+
+// This raises a GD0401 diagnostic error: global classes must inherit from GodotObject
+{|GD0401:[GlobalClass]
+public partial class CustomGlobalClass3
+{
+
+}|}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0402.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0402.cs
new file mode 100644
index 0000000000..1c0a169841
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0402.cs
@@ -0,0 +1,15 @@
+using Godot;
+
+// This works because it inherits from GodotObject and it doesn't have any generic type parameter.
+[GlobalClass]
+public partial class CustomGlobalClass : GodotObject
+{
+
+}
+
+// This raises a GD0402 diagnostic error: global classes can't have any generic type parameter
+{|GD0402:[GlobalClass]
+public partial class CustomGlobalClass<T> : GodotObject
+{
+
+}|}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0301.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0301.cs
new file mode 100644
index 0000000000..031039cba1
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0301.cs
@@ -0,0 +1,71 @@
+using System;
+using Godot;
+using Godot.Collections;
+using Array = Godot.Collections.Array;
+
+public class MustBeVariantGD0301
+{
+ public void MethodCallsError()
+ {
+ // This raises a GD0301 diagnostic error: object is not Variant (and Method<T> requires a variant generic type).
+ Method<{|GD0301:object|}>();
+ }
+ public void MethodCallsOk()
+ {
+ // All these calls are valid because they are Variant types.
+ Method<bool>();
+ Method<char>();
+ Method<sbyte>();
+ Method<byte>();
+ Method<short>();
+ Method<ushort>();
+ Method<int>();
+ Method<uint>();
+ Method<long>();
+ Method<ulong>();
+ Method<float>();
+ Method<double>();
+ Method<string>();
+ Method<Vector2>();
+ Method<Vector2I>();
+ Method<Rect2>();
+ Method<Rect2I>();
+ Method<Transform2D>();
+ Method<Vector3>();
+ Method<Vector3I>();
+ Method<Vector4>();
+ Method<Vector4I>();
+ Method<Basis>();
+ Method<Quaternion>();
+ Method<Transform3D>();
+ Method<Projection>();
+ Method<Aabb>();
+ Method<Color>();
+ Method<Plane>();
+ Method<Callable>();
+ Method<Signal>();
+ Method<GodotObject>();
+ Method<StringName>();
+ Method<NodePath>();
+ Method<Rid>();
+ Method<Dictionary>();
+ Method<Array>();
+ Method<byte[]>();
+ Method<int[]>();
+ Method<long[]>();
+ Method<float[]>();
+ Method<double[]>();
+ Method<string[]>();
+ Method<Vector2[]>();
+ Method<Vector3[]>();
+ Method<Color[]>();
+ Method<GodotObject[]>();
+ Method<StringName[]>();
+ Method<NodePath[]>();
+ Method<Rid[]>();
+ }
+
+ public void Method<[MustBeVariant] T>()
+ {
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0302.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0302.cs
new file mode 100644
index 0000000000..ce182e8c62
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MustBeVariant.GD0302.cs
@@ -0,0 +1,27 @@
+using Godot;
+
+public class MustBeVariantGD0302
+{
+ public void MethodOk<[MustBeVariant] T>()
+ {
+ // T is guaranteed to be a Variant-compatible type because it's annotated with the [MustBeVariant] attribute, so it can be used here.
+ new ExampleClass<T>();
+ Method<T>();
+ }
+
+ public void MethodFail<T>()
+ {
+ // These two calls raise a GD0302 diagnostic error: T is not valid here because it may not a Variant type and method call and class require it.
+ new ExampleClass<{|GD0302:T|}>();
+ Method<{|GD0302:T|}>();
+ }
+
+ public void Method<[MustBeVariant] T>()
+ {
+ }
+}
+
+public class ExampleClass<[MustBeVariant] T>
+{
+
+}
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 322078423f..05dacd28fb 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -148,7 +148,7 @@ void godot_icall_Internal_ReloadAssemblies(bool p_soft_reload) {
}
void godot_icall_Internal_EditorDebuggerNodeReloadScripts() {
- EditorDebuggerNode::get_singleton()->reload_scripts();
+ EditorDebuggerNode::get_singleton()->reload_all_scripts();
}
bool godot_icall_Internal_ScriptEditorEdit(Resource *p_resource, int32_t p_line, int32_t p_col, bool p_grab_focus) {
@@ -175,7 +175,7 @@ void godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar(Control *p_contr
void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
if (ed) {
- ed->reload_scripts();
+ ed->reload_all_scripts();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
index 57b5b09ebb..63af6ee6e8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
@@ -98,11 +98,11 @@ namespace Godot
Vector3 dstMax = with._position + with._size;
return srcMin.X <= dstMin.X &&
- srcMax.X > dstMax.X &&
+ srcMax.X >= dstMax.X &&
srcMin.Y <= dstMin.Y &&
- srcMax.Y > dstMax.Y &&
+ srcMax.Y >= dstMax.Y &&
srcMin.Z <= dstMin.Z &&
- srcMax.Z > dstMax.Z;
+ srcMax.Z >= dstMax.Z;
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
index 71a35ab809..cf4ac45a9f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -123,8 +123,8 @@ namespace Godot
public readonly bool Encloses(Rect2 b)
{
return b._position.X >= _position.X && b._position.Y >= _position.Y &&
- b._position.X + b._size.X < _position.X + _size.X &&
- b._position.Y + b._size.Y < _position.Y + _size.Y;
+ b._position.X + b._size.X <= _position.X + _size.X &&
+ b._position.Y + b._size.Y <= _position.Y + _size.Y;
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs
index ef7e9eacd8..58560df0c5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2I.cs
@@ -113,8 +113,8 @@ namespace Godot
public readonly bool Encloses(Rect2I b)
{
return b._position.X >= _position.X && b._position.Y >= _position.Y &&
- b._position.X + b._size.X < _position.X + _size.X &&
- b._position.Y + b._size.Y < _position.Y + _size.Y;
+ b._position.X + b._size.X <= _position.X + _size.X &&
+ b._position.Y + b._size.Y <= _position.Y + _size.Y;
}
/// <summary>
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index 3518507f8c..0089e9c2a2 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -316,7 +316,7 @@ void godotsharp_internal_new_csharp_script(Ref<CSharpScript> *r_dest) {
}
void godotsharp_internal_editor_file_system_update_file(const String *p_script_path) {
-#if TOOLS_ENABLED
+#ifdef TOOLS_ENABLED
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem *efs = EditorFileSystem::get_singleton();
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index aa97534675..ee17a668d7 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -152,7 +152,7 @@ String realpath(const String &p_path) {
}
return result.simplify_path();
-#elif UNIX_ENABLED
+#elif defined(UNIX_ENABLED)
char *resolved_path = ::realpath(p_path.utf8().get_data(), nullptr);
if (!resolved_path) {
diff --git a/modules/openxr/doc_classes/OpenXRHand.xml b/modules/openxr/doc_classes/OpenXRHand.xml
index eb7decd30d..1c4da83138 100644
--- a/modules/openxr/doc_classes/OpenXRHand.xml
+++ b/modules/openxr/doc_classes/OpenXRHand.xml
@@ -7,10 +7,14 @@
This node enables OpenXR's hand tracking functionality. The node should be a child node of an [XROrigin3D] node, tracking will update its position to the player's tracked hand Palm joint location (the center of the middle finger's metacarpal bone). This node also updates the skeleton of a properly skinned hand or avatar model.
If the skeleton is a hand (one of the hand bones is the root node of the skeleton), then the skeleton will be placed relative to the hand palm location and the hand mesh and skeleton should be children of the OpenXRHand node.
If the hand bones are part of a full skeleton, then the root of the hand will keep its location with the assumption that IK is used to position the hand and arm.
+ By default the skeleton hand bones are repositioned to match the size of the tracked hand. To preserve the modeled bone sizes change [member bone_update] to apply rotation only.
</description>
<tutorials>
</tutorials>
<members>
+ <member name="bone_update" type="int" setter="set_bone_update" getter="get_bone_update" enum="OpenXRHand.BoneUpdate" default="0">
+ Specify the type of updates to perform on the bone.
+ </member>
<member name="hand" type="int" setter="set_hand" getter="get_hand" enum="OpenXRHand.Hands" default="0">
Specifies whether this node tracks the left or right hand of the player.
</member>
@@ -52,5 +56,14 @@
<constant name="SKELETON_RIG_MAX" value="2" enum="SkeletonRig">
Maximum supported hands.
</constant>
+ <constant name="BONE_UPDATE_FULL" value="0" enum="BoneUpdate">
+ The skeletons bones are fully updated (both position and rotation) to match the tracked bones.
+ </constant>
+ <constant name="BONE_UPDATE_ROTATION_ONLY" value="1" enum="BoneUpdate">
+ The skeletons bones are only rotated to align with the tracked bones, preserving bone length.
+ </constant>
+ <constant name="BONE_UPDATE_MAX" value="2" enum="BoneUpdate">
+ Maximum supported bone update mode.
+ </constant>
</constants>
</class>
diff --git a/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp
index 1289183ea4..c3f692185b 100644
--- a/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp
+++ b/modules/openxr/extensions/openxr_fb_update_swapchain_extension.cpp
@@ -30,7 +30,7 @@
#include "openxr_fb_update_swapchain_extension.h"
-// always include this as late as possible
+// Always include this as late as possible.
#include "../openxr_platform_inc.h"
OpenXRFBUpdateSwapchainExtension *OpenXRFBUpdateSwapchainExtension::singleton = nullptr;
diff --git a/modules/openxr/extensions/openxr_opengl_extension.h b/modules/openxr/extensions/openxr_opengl_extension.h
index 3b0aa0bce9..5f529829a7 100644
--- a/modules/openxr/extensions/openxr_opengl_extension.h
+++ b/modules/openxr/extensions/openxr_opengl_extension.h
@@ -39,7 +39,7 @@
#include "core/templates/vector.h"
-// always include this as late as possible
+// Always include this as late as possible.
#include "../openxr_platform_inc.h"
class OpenXROpenGLExtension : public OpenXRGraphicsExtensionWrapper {
@@ -65,9 +65,9 @@ private:
#ifdef WIN32
static XrGraphicsBindingOpenGLWin32KHR graphics_binding_gl;
-#elif ANDROID_ENABLED
+#elif defined(ANDROID_ENABLED)
static XrGraphicsBindingOpenGLESAndroidKHR graphics_binding_gl;
-#else
+#else // Linux/X11
static XrGraphicsBindingOpenGLXlibKHR graphics_binding_gl;
#endif
diff --git a/modules/openxr/extensions/openxr_vulkan_extension.h b/modules/openxr/extensions/openxr_vulkan_extension.h
index f31621fda0..86c0f327dd 100644
--- a/modules/openxr/extensions/openxr_vulkan_extension.h
+++ b/modules/openxr/extensions/openxr_vulkan_extension.h
@@ -37,7 +37,7 @@
#include "core/templates/vector.h"
-// always include this as late as possible
+// Always include this as late as possible.
#include "../openxr_platform_inc.h"
class OpenXRVulkanExtension : public OpenXRGraphicsExtensionWrapper, VulkanHooks {
diff --git a/modules/openxr/scene/openxr_hand.cpp b/modules/openxr/scene/openxr_hand.cpp
index 8ce33b55c3..2a4104f6ee 100644
--- a/modules/openxr/scene/openxr_hand.cpp
+++ b/modules/openxr/scene/openxr_hand.cpp
@@ -49,10 +49,14 @@ void OpenXRHand::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_skeleton_rig", "skeleton_rig"), &OpenXRHand::set_skeleton_rig);
ClassDB::bind_method(D_METHOD("get_skeleton_rig"), &OpenXRHand::get_skeleton_rig);
+ ClassDB::bind_method(D_METHOD("set_bone_update", "bone_update"), &OpenXRHand::set_bone_update);
+ ClassDB::bind_method(D_METHOD("get_bone_update"), &OpenXRHand::get_bone_update);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand");
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_range", PROPERTY_HINT_ENUM, "Unobstructed,Conform to controller"), "set_motion_range", "get_motion_range");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "hand_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_hand_skeleton", "get_hand_skeleton");
ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton_rig", PROPERTY_HINT_ENUM, "OpenXR,Humanoid"), "set_skeleton_rig", "get_skeleton_rig");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update");
BIND_ENUM_CONSTANT(HAND_LEFT);
BIND_ENUM_CONSTANT(HAND_RIGHT);
@@ -65,6 +69,10 @@ void OpenXRHand::_bind_methods() {
BIND_ENUM_CONSTANT(SKELETON_RIG_OPENXR);
BIND_ENUM_CONSTANT(SKELETON_RIG_HUMANOID);
BIND_ENUM_CONSTANT(SKELETON_RIG_MAX);
+
+ BIND_ENUM_CONSTANT(BONE_UPDATE_FULL);
+ BIND_ENUM_CONSTANT(BONE_UPDATE_ROTATION_ONLY);
+ BIND_ENUM_CONSTANT(BONE_UPDATE_MAX);
}
OpenXRHand::OpenXRHand() {
@@ -134,6 +142,16 @@ OpenXRHand::SkeletonRig OpenXRHand::get_skeleton_rig() const {
return skeleton_rig;
}
+void OpenXRHand::set_bone_update(BoneUpdate p_bone_update) {
+ ERR_FAIL_INDEX(p_bone_update, BONE_UPDATE_MAX);
+
+ bone_update = p_bone_update;
+}
+
+OpenXRHand::BoneUpdate OpenXRHand::get_bone_update() const {
+ return bone_update;
+}
+
Skeleton3D *OpenXRHand::get_skeleton() {
if (!has_node(hand_skeleton)) {
return nullptr;
@@ -346,8 +364,12 @@ void OpenXRHand::_update_skeleton() {
const Quaternion q = inv_quaternions[parent_joint] * quaternions[joint];
const Vector3 p = inv_quaternions[parent_joint].xform(positions[joint] - positions[parent_joint]);
- // and set our pose
- skeleton->set_bone_pose_position(joints[joint].bone, p);
+ // Update the bone position if enabled by update mode.
+ if (bone_update == BONE_UPDATE_FULL) {
+ skeleton->set_bone_pose_position(joints[joint].bone, p);
+ }
+
+ // Always update the bone rotation.
skeleton->set_bone_pose_rotation(joints[joint].bone, q);
}
diff --git a/modules/openxr/scene/openxr_hand.h b/modules/openxr/scene/openxr_hand.h
index 14eb893bcc..4c77e7277c 100644
--- a/modules/openxr/scene/openxr_hand.h
+++ b/modules/openxr/scene/openxr_hand.h
@@ -61,6 +61,12 @@ public:
SKELETON_RIG_MAX
};
+ enum BoneUpdate {
+ BONE_UPDATE_FULL,
+ BONE_UPDATE_ROTATION_ONLY,
+ BONE_UPDATE_MAX
+ };
+
private:
struct JointData {
int bone = -1;
@@ -74,6 +80,7 @@ private:
MotionRange motion_range = MOTION_RANGE_UNOBSTRUCTED;
NodePath hand_skeleton;
SkeletonRig skeleton_rig = SKELETON_RIG_OPENXR;
+ BoneUpdate bone_update = BONE_UPDATE_FULL;
JointData joints[XR_HAND_JOINT_COUNT_EXT];
@@ -101,11 +108,15 @@ public:
void set_skeleton_rig(SkeletonRig p_skeleton_rig);
SkeletonRig get_skeleton_rig() const;
+ void set_bone_update(BoneUpdate p_bone_update);
+ BoneUpdate get_bone_update() const;
+
void _notification(int p_what);
};
VARIANT_ENUM_CAST(OpenXRHand::Hands)
VARIANT_ENUM_CAST(OpenXRHand::MotionRange)
VARIANT_ENUM_CAST(OpenXRHand::SkeletonRig)
+VARIANT_ENUM_CAST(OpenXRHand::BoneUpdate)
#endif // OPENXR_HAND_H
diff --git a/scene/2d/tile_map.compat.inc b/scene/2d/tile_map.compat.inc
index ed216173c7..04937bdf7e 100644
--- a/scene/2d/tile_map.compat.inc
+++ b/scene/2d/tile_map.compat.inc
@@ -42,10 +42,20 @@ int TileMap::_get_quadrant_size_compat_81070() const {
return get_rendering_quadrant_size();
}
+TileMap::VisibilityMode TileMap::_get_collision_visibility_mode_bind_compat_87115() {
+ return get_collision_visibility_mode();
+}
+
+TileMap::VisibilityMode TileMap::_get_navigation_visibility_mode_bind_compat_87115() {
+ return get_navigation_visibility_mode();
+}
+
void TileMap::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("get_used_rect"), &TileMap::_get_used_rect_bind_compat_78328);
ClassDB::bind_compatibility_method(D_METHOD("set_quadrant_size", "quadrant_size"), &TileMap::_set_quadrant_size_compat_81070);
ClassDB::bind_compatibility_method(D_METHOD("get_quadrant_size"), &TileMap::_get_quadrant_size_compat_81070);
+ ClassDB::bind_compatibility_method(D_METHOD("get_collision_visibility_mode"), &TileMap::_get_collision_visibility_mode_bind_compat_87115);
+ ClassDB::bind_compatibility_method(D_METHOD("get_navigation_visibility_mode"), &TileMap::_get_navigation_visibility_mode_bind_compat_87115);
}
#endif
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index decbbab476..24eefce99d 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -167,7 +167,7 @@ void TileMap::set_selected_layer(int p_layer_id) {
emit_signal(CoreStringNames::get_singleton()->changed);
// Update the layers modulation.
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_SELECTED_LAYER);
}
}
@@ -178,45 +178,9 @@ int TileMap::get_selected_layer() const {
void TileMap::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- for (Ref<TileMapLayer> &layer : layers) {
- layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_IN_TREE);
- }
- } break;
-
- case NOTIFICATION_EXIT_TREE: {
- for (Ref<TileMapLayer> &layer : layers) {
- layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_IN_TREE);
- }
- } break;
-
- case TileMap::NOTIFICATION_ENTER_CANVAS: {
- for (Ref<TileMapLayer> &layer : layers) {
- layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_IN_CANVAS);
- }
- } break;
-
- case TileMap::NOTIFICATION_EXIT_CANVAS: {
- for (Ref<TileMapLayer> &layer : layers) {
- layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_IN_CANVAS);
- }
- } break;
-
- case NOTIFICATION_DRAW: {
- // Rendering.
- if (tile_set.is_valid()) {
- RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), is_y_sort_enabled());
- }
- } break;
-
- case TileMap::NOTIFICATION_VISIBILITY_CHANGED: {
- for (Ref<TileMapLayer> &layer : layers) {
- layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_VISIBILITY);
- }
- } break;
-
case TileMap::NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- // Physics.
+ // This is only executed when collision_animatable is enabled.
+
bool in_editor = false;
#ifdef TOOLS_ENABLED
in_editor = Engine::get_singleton()->is_editor_hint();
@@ -224,39 +188,30 @@ void TileMap::_notification(int p_what) {
if (is_inside_tree() && collision_animatable && !in_editor) {
// Update transform on the physics tick when in animatable mode.
last_valid_transform = new_transform;
+ print_line("Physics: ", new_transform);
set_notify_local_transform(false);
set_global_transform(new_transform);
- _update_notify_local_transform();
- }
- } break;
-
- case NOTIFICATION_TRANSFORM_CHANGED: {
- // Physics.
- for (Ref<TileMapLayer> &layer : layers) {
- layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_XFORM);
+ set_notify_local_transform(true);
}
} break;
case TileMap::NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- for (Ref<TileMapLayer> &layer : layers) {
- layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_LOCAL_XFORM);
- }
+ // This is only executed when collision_animatable is enabled.
- // Physics.
bool in_editor = false;
#ifdef TOOLS_ENABLED
in_editor = Engine::get_singleton()->is_editor_hint();
#endif
- // Only active when animatable. Send the new transform to the physics...
if (is_inside_tree() && collision_animatable && !in_editor) {
// Store last valid transform.
new_transform = get_global_transform();
+ print_line("local XFORM: ", last_valid_transform);
// ... but then revert changes.
set_notify_local_transform(false);
set_global_transform(last_valid_transform);
- _update_notify_local_transform();
+ set_notify_local_transform(true);
}
} break;
}
@@ -285,7 +240,7 @@ void TileMap::_internal_update() {
}
// Update dirty quadrants on layers.
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->internal_update();
}
@@ -308,7 +263,7 @@ void TileMap::set_tileset(const Ref<TileSet> &p_tileset) {
tile_set->connect_changed(callable_mp(this, &TileMap::_tile_set_changed));
}
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_TILE_SET);
}
@@ -323,7 +278,7 @@ void TileMap::set_rendering_quadrant_size(int p_size) {
ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1.");
rendering_quadrant_size = p_size;
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_QUADRANT_SIZE);
}
emit_signal(CoreStringNames::get_singleton()->changed);
@@ -429,10 +384,11 @@ void TileMap::add_layer(int p_to_pos) {
ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
// Must clear before adding the layer.
- Ref<TileMapLayer> new_layer;
- new_layer.instantiate();
- new_layer->set_tile_map(this);
+ TileMapLayer *new_layer = memnew(TileMapLayer);
layers.insert(p_to_pos, new_layer);
+ add_child(new_layer);
+ new_layer->set_name(vformat("Layer%d", p_to_pos));
+ move_child(new_layer, p_to_pos);
for (uint32_t i = 0; i < layers.size(); i++) {
layers[i]->set_layer_index_in_tile_map_node(i);
}
@@ -449,10 +405,11 @@ void TileMap::move_layer(int p_layer, int p_to_pos) {
ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
// Clear before shuffling layers.
- Ref<TileMapLayer> layer = layers[p_layer];
+ TileMapLayer *layer = layers[p_layer];
layers.insert(p_to_pos, layer);
layers.remove_at(p_to_pos < p_layer ? p_layer + 1 : p_layer);
for (uint32_t i = 0; i < layers.size(); i++) {
+ move_child(layer, i);
layers[i]->set_layer_index_in_tile_map_node(i);
}
queue_internal_update();
@@ -471,6 +428,7 @@ void TileMap::remove_layer(int p_layer) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
// Clear before removing the layer.
+ layers[p_layer]->queue_free();
layers.remove_at(p_layer);
for (uint32_t i = 0; i < layers.size(); i++) {
layers[i]->set_layer_index_in_tile_map_node(i);
@@ -513,7 +471,6 @@ Color TileMap::get_layer_modulate(int p_layer) const {
void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) {
TILEMAP_CALL_FOR_LAYER(p_layer, set_y_sort_enabled, p_y_sort_enabled);
- _update_notify_local_transform();
}
bool TileMap::is_layer_y_sort_enabled(int p_layer) const {
@@ -552,17 +509,16 @@ RID TileMap::get_layer_navigation_map(int p_layer) const {
TILEMAP_CALL_FOR_LAYER_V(p_layer, RID(), get_navigation_map);
}
-void TileMap::set_collision_animatable(bool p_enabled) {
- if (collision_animatable == p_enabled) {
+void TileMap::set_collision_animatable(bool p_collision_animatable) {
+ if (collision_animatable == p_collision_animatable) {
return;
}
- collision_animatable = p_enabled;
- _update_notify_local_transform();
- set_physics_process_internal(p_enabled);
- for (Ref<TileMapLayer> &layer : layers) {
- layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_COLLISION_ANIMATABLE);
+ collision_animatable = p_collision_animatable;
+ set_notify_local_transform(p_collision_animatable);
+ set_physics_process_internal(p_collision_animatable);
+ for (TileMapLayer *layer : layers) {
+ layer->set_use_kinematic_bodies(layer);
}
- emit_signal(CoreStringNames::get_singleton()->changed);
}
bool TileMap::is_collision_animatable() const {
@@ -574,13 +530,13 @@ void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_colli
return;
}
collision_visibility_mode = p_show_collision;
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_COLLISION_VISIBILITY_MODE);
}
emit_signal(CoreStringNames::get_singleton()->changed);
}
-TileMap::VisibilityMode TileMap::get_collision_visibility_mode() {
+TileMap::VisibilityMode TileMap::get_collision_visibility_mode() const {
return collision_visibility_mode;
}
@@ -589,13 +545,13 @@ void TileMap::set_navigation_visibility_mode(TileMap::VisibilityMode p_show_navi
return;
}
navigation_visibility_mode = p_show_navigation;
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_NAVIGATION_VISIBILITY_MODE);
}
emit_signal(CoreStringNames::get_singleton()->changed);
}
-TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() {
+TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() const {
return navigation_visibility_mode;
}
@@ -604,7 +560,7 @@ void TileMap::set_y_sort_enabled(bool p_enable) {
return;
}
Node2D::set_y_sort_enabled(p_enable);
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_Y_SORT_ENABLED);
}
emit_signal(CoreStringNames::get_singleton()->changed);
@@ -640,27 +596,8 @@ Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coo
}
Vector2i TileMap::map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) {
- ERR_FAIL_COND_V(p_pattern.is_null(), Vector2i());
- ERR_FAIL_COND_V(!p_pattern->has_cell(p_coords_in_pattern), Vector2i());
-
- Vector2i output = p_position_in_tilemap + p_coords_in_pattern;
- if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) {
- if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) {
- output.x += 1;
- } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) {
- output.y += 1;
- }
- } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) {
- output.x -= 1;
- } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) {
- output.y -= 1;
- }
- }
- }
-
- return output;
+ ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2i());
+ return tile_set->map_pattern(p_position_in_tilemap, p_coords_in_pattern, p_pattern);
}
void TileMap::set_pattern(int p_layer, const Vector2i &p_position, const Ref<TileMapPattern> p_pattern) {
@@ -700,7 +637,7 @@ TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_
}
Vector2i TileMap::get_coords_for_body_rid(RID p_physics_body) {
- for (const Ref<TileMapLayer> &layer : layers) {
+ for (const TileMapLayer *layer : layers) {
if (layer->has_body_rid(p_physics_body)) {
return layer->get_coords_for_body_rid(p_physics_body);
}
@@ -718,7 +655,7 @@ int TileMap::get_layer_for_body_rid(RID p_physics_body) {
}
void TileMap::fix_invalid_tiles() {
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->fix_invalid_tiles();
}
}
@@ -728,7 +665,7 @@ void TileMap::clear_layer(int p_layer) {
}
void TileMap::clear() {
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->clear();
}
}
@@ -742,7 +679,7 @@ void TileMap::notify_runtime_tile_data_update(int p_layer) {
if (p_layer >= 0) {
TILEMAP_CALL_FOR_LAYER(p_layer, notify_tile_map_change, TileMapLayer::DIRTY_FLAGS_TILE_MAP_RUNTIME_UPDATE);
} else {
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_RUNTIME_UPDATE);
}
}
@@ -780,9 +717,9 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
else if (p_name == "tile_data") { // Kept for compatibility reasons.
if (p_value.is_array()) {
if (layers.size() == 0) {
- Ref<TileMapLayer> new_layer;
- new_layer.instantiate();
- new_layer->set_tile_map(this);
+ TileMapLayer *new_layer = memnew(TileMapLayer);
+ add_child(new_layer);
+ new_layer->set_name("Layer0");
new_layer->set_layer_index_in_tile_map_node(0);
layers.push_back(new_layer);
}
@@ -804,9 +741,9 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
if (index >= (int)layers.size()) {
while (index >= (int)layers.size()) {
- Ref<TileMapLayer> new_layer;
- new_layer.instantiate();
- new_layer->set_tile_map(this);
+ TileMapLayer *new_layer = memnew(TileMapLayer);
+ add_child(new_layer);
+ new_layer->set_name(vformat("Layer%d", index));
new_layer->set_layer_index_in_tile_map_node(index);
layers.push_back(new_layer);
}
@@ -985,623 +922,23 @@ bool TileMap::_property_get_revert(const StringName &p_name, Variant &r_property
}
Vector2 TileMap::map_to_local(const Vector2i &p_pos) const {
- // SHOULD RETURN THE CENTER OF THE CELL.
ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2());
-
- Vector2 ret = p_pos;
- TileSet::TileShape tile_shape = tile_set->get_tile_shape();
- TileSet::TileOffsetAxis tile_offset_axis = tile_set->get_tile_offset_axis();
-
- if (tile_shape == TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE || tile_shape == TileSet::TILE_SHAPE_HEXAGON || tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- // Technically, those 3 shapes are equivalent, as they are basically half-offset, but with different levels or overlap.
- // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap.
- if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- switch (tile_set->get_tile_layout()) {
- case TileSet::TILE_LAYOUT_STACKED:
- ret = Vector2(ret.x + (Math::posmod(ret.y, 2) == 0 ? 0.0 : 0.5), ret.y);
- break;
- case TileSet::TILE_LAYOUT_STACKED_OFFSET:
- ret = Vector2(ret.x + (Math::posmod(ret.y, 2) == 1 ? 0.0 : 0.5), ret.y);
- break;
- case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
- ret = Vector2(ret.x + ret.y / 2, ret.y);
- break;
- case TileSet::TILE_LAYOUT_STAIRS_DOWN:
- ret = Vector2(ret.x / 2, ret.y * 2 + ret.x);
- break;
- case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
- ret = Vector2((ret.x + ret.y) / 2, ret.y - ret.x);
- break;
- case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
- ret = Vector2((ret.x - ret.y) / 2, ret.y + ret.x);
- break;
- }
- } else { // TILE_OFFSET_AXIS_VERTICAL.
- switch (tile_set->get_tile_layout()) {
- case TileSet::TILE_LAYOUT_STACKED:
- ret = Vector2(ret.x, ret.y + (Math::posmod(ret.x, 2) == 0 ? 0.0 : 0.5));
- break;
- case TileSet::TILE_LAYOUT_STACKED_OFFSET:
- ret = Vector2(ret.x, ret.y + (Math::posmod(ret.x, 2) == 1 ? 0.0 : 0.5));
- break;
- case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
- ret = Vector2(ret.x * 2 + ret.y, ret.y / 2);
- break;
- case TileSet::TILE_LAYOUT_STAIRS_DOWN:
- ret = Vector2(ret.x, ret.y + ret.x / 2);
- break;
- case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
- ret = Vector2(ret.x + ret.y, (ret.y - ret.x) / 2);
- break;
- case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
- ret = Vector2(ret.x - ret.y, (ret.y + ret.x) / 2);
- break;
- }
- }
- }
-
- // Multiply by the overlapping ratio.
- double overlapping_ratio = 1.0;
- if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- overlapping_ratio = 0.5;
- } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) {
- overlapping_ratio = 0.75;
- }
- ret.y *= overlapping_ratio;
- } else { // TILE_OFFSET_AXIS_VERTICAL.
- if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- overlapping_ratio = 0.5;
- } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) {
- overlapping_ratio = 0.75;
- }
- ret.x *= overlapping_ratio;
- }
-
- return (ret + Vector2(0.5, 0.5)) * tile_set->get_tile_size();
+ return tile_set->map_to_local(p_pos);
}
-Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const {
+Vector2i TileMap::local_to_map(const Vector2 &p_pos) const {
ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2i());
-
- Vector2 ret = p_local_position;
- ret /= tile_set->get_tile_size();
-
- TileSet::TileShape tile_shape = tile_set->get_tile_shape();
- TileSet::TileOffsetAxis tile_offset_axis = tile_set->get_tile_offset_axis();
- TileSet::TileLayout tile_layout = tile_set->get_tile_layout();
-
- // Divide by the overlapping ratio.
- double overlapping_ratio = 1.0;
- if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- overlapping_ratio = 0.5;
- } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) {
- overlapping_ratio = 0.75;
- }
- ret.y /= overlapping_ratio;
- } else { // TILE_OFFSET_AXIS_VERTICAL.
- if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- overlapping_ratio = 0.5;
- } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) {
- overlapping_ratio = 0.75;
- }
- ret.x /= overlapping_ratio;
- }
-
- // For each half-offset shape, we check if we are in the corner of the tile, and thus should correct the local position accordingly.
- if (tile_shape == TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE || tile_shape == TileSet::TILE_SHAPE_HEXAGON || tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- // Technically, those 3 shapes are equivalent, as they are basically half-offset, but with different levels or overlap.
- // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap.
- if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- // Smart floor of the position
- Vector2 raw_pos = ret;
- if (Math::posmod(Math::floor(ret.y), 2) ^ (tile_layout == TileSet::TILE_LAYOUT_STACKED_OFFSET)) {
- ret = Vector2(Math::floor(ret.x + 0.5) - 0.5, Math::floor(ret.y));
- } else {
- ret = ret.floor();
- }
-
- // Compute the tile offset, and if we might the output for a neighbor top tile.
- Vector2 in_tile_pos = raw_pos - ret;
- bool in_top_left_triangle = (in_tile_pos - Vector2(0.5, 0.0)).cross(Vector2(-0.5, 1.0 / overlapping_ratio - 1)) <= 0;
- bool in_top_right_triangle = (in_tile_pos - Vector2(0.5, 0.0)).cross(Vector2(0.5, 1.0 / overlapping_ratio - 1)) > 0;
-
- switch (tile_layout) {
- case TileSet::TILE_LAYOUT_STACKED:
- ret = ret.floor();
- if (in_top_left_triangle) {
- ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? 0 : -1, -1);
- } else if (in_top_right_triangle) {
- ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? 1 : 0, -1);
- }
- break;
- case TileSet::TILE_LAYOUT_STACKED_OFFSET:
- ret = ret.floor();
- if (in_top_left_triangle) {
- ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? -1 : 0, -1);
- } else if (in_top_right_triangle) {
- ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? 0 : 1, -1);
- }
- break;
- case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
- ret = Vector2(ret.x - ret.y / 2, ret.y).floor();
- if (in_top_left_triangle) {
- ret += Vector2i(0, -1);
- } else if (in_top_right_triangle) {
- ret += Vector2i(1, -1);
- }
- break;
- case TileSet::TILE_LAYOUT_STAIRS_DOWN:
- ret = Vector2(ret.x * 2, ret.y / 2 - ret.x).floor();
- if (in_top_left_triangle) {
- ret += Vector2i(-1, 0);
- } else if (in_top_right_triangle) {
- ret += Vector2i(1, -1);
- }
- break;
- case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
- ret = Vector2(ret.x - ret.y / 2, ret.y / 2 + ret.x).floor();
- if (in_top_left_triangle) {
- ret += Vector2i(0, -1);
- } else if (in_top_right_triangle) {
- ret += Vector2i(1, 0);
- }
- break;
- case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
- ret = Vector2(ret.x + ret.y / 2, ret.y / 2 - ret.x).floor();
- if (in_top_left_triangle) {
- ret += Vector2i(-1, 0);
- } else if (in_top_right_triangle) {
- ret += Vector2i(0, -1);
- }
- break;
- }
- } else { // TILE_OFFSET_AXIS_VERTICAL.
- // Smart floor of the position.
- Vector2 raw_pos = ret;
- if (Math::posmod(Math::floor(ret.x), 2) ^ (tile_layout == TileSet::TILE_LAYOUT_STACKED_OFFSET)) {
- ret = Vector2(Math::floor(ret.x), Math::floor(ret.y + 0.5) - 0.5);
- } else {
- ret = ret.floor();
- }
-
- // Compute the tile offset, and if we might the output for a neighbor top tile.
- Vector2 in_tile_pos = raw_pos - ret;
- bool in_top_left_triangle = (in_tile_pos - Vector2(0.0, 0.5)).cross(Vector2(1.0 / overlapping_ratio - 1, -0.5)) > 0;
- bool in_bottom_left_triangle = (in_tile_pos - Vector2(0.0, 0.5)).cross(Vector2(1.0 / overlapping_ratio - 1, 0.5)) <= 0;
-
- switch (tile_layout) {
- case TileSet::TILE_LAYOUT_STACKED:
- ret = ret.floor();
- if (in_top_left_triangle) {
- ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? 0 : -1);
- } else if (in_bottom_left_triangle) {
- ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? 1 : 0);
- }
- break;
- case TileSet::TILE_LAYOUT_STACKED_OFFSET:
- ret = ret.floor();
- if (in_top_left_triangle) {
- ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? -1 : 0);
- } else if (in_bottom_left_triangle) {
- ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? 0 : 1);
- }
- break;
- case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
- ret = Vector2(ret.x / 2 - ret.y, ret.y * 2).floor();
- if (in_top_left_triangle) {
- ret += Vector2i(0, -1);
- } else if (in_bottom_left_triangle) {
- ret += Vector2i(-1, 1);
- }
- break;
- case TileSet::TILE_LAYOUT_STAIRS_DOWN:
- ret = Vector2(ret.x, ret.y - ret.x / 2).floor();
- if (in_top_left_triangle) {
- ret += Vector2i(-1, 0);
- } else if (in_bottom_left_triangle) {
- ret += Vector2i(-1, 1);
- }
- break;
- case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
- ret = Vector2(ret.x / 2 - ret.y, ret.y + ret.x / 2).floor();
- if (in_top_left_triangle) {
- ret += Vector2i(0, -1);
- } else if (in_bottom_left_triangle) {
- ret += Vector2i(-1, 0);
- }
- break;
- case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
- ret = Vector2(ret.x / 2 + ret.y, ret.y - ret.x / 2).floor();
- if (in_top_left_triangle) {
- ret += Vector2i(-1, 0);
- } else if (in_bottom_left_triangle) {
- ret += Vector2i(0, 1);
- }
- break;
- }
- }
- } else {
- ret = (ret + Vector2(0.00005, 0.00005)).floor();
- }
- return Vector2i(ret);
+ return tile_set->local_to_map(p_pos);
}
bool TileMap::is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const {
ERR_FAIL_COND_V(!tile_set.is_valid(), false);
-
- TileSet::TileShape shape = tile_set->get_tile_shape();
- if (shape == TileSet::TILE_SHAPE_SQUARE) {
- return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
-
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- } else {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- } else {
- return p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE ||
- p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- }
- }
+ return tile_set->is_existing_neighbor(p_cell_neighbor);
}
Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const {
- ERR_FAIL_COND_V(!tile_set.is_valid(), p_coords);
-
- TileSet::TileShape shape = tile_set->get_tile_shape();
- if (shape == TileSet::TILE_SHAPE_SQUARE) {
- switch (p_cell_neighbor) {
- case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
- return p_coords + Vector2i(1, 0);
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
- return p_coords + Vector2i(1, 1);
- case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
- return p_coords + Vector2i(0, 1);
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
- return p_coords + Vector2i(-1, 1);
- case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
- return p_coords + Vector2i(-1, 0);
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
- return p_coords + Vector2i(-1, -1);
- case TileSet::CELL_NEIGHBOR_TOP_SIDE:
- return p_coords + Vector2i(0, -1);
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
- return p_coords + Vector2i(1, -1);
- default:
- ERR_FAIL_V(p_coords);
- }
- } else { // Half-offset shapes (square and hexagon).
- switch (tile_set->get_tile_layout()) {
- case TileSet::TILE_LAYOUT_STACKED: {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- bool is_offset = p_coords.y % 2;
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
- return p_coords + Vector2i(1, 0);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(is_offset ? 1 : 0, 1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
- return p_coords + Vector2i(0, 2);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(is_offset ? 0 : -1, 1);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
- return p_coords + Vector2i(-1, 0);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(is_offset ? 0 : -1, -1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
- return p_coords + Vector2i(0, -2);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(is_offset ? 1 : 0, -1);
- } else {
- ERR_FAIL_V(p_coords);
- }
- } else {
- bool is_offset = p_coords.x % 2;
-
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
- return p_coords + Vector2i(0, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(1, is_offset ? 1 : 0);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
- return p_coords + Vector2i(2, 0);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(1, is_offset ? 0 : -1);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
- return p_coords + Vector2i(0, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(-1, is_offset ? 0 : -1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
- return p_coords + Vector2i(-2, 0);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(-1, is_offset ? 1 : 0);
- } else {
- ERR_FAIL_V(p_coords);
- }
- }
- } break;
- case TileSet::TILE_LAYOUT_STACKED_OFFSET: {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- bool is_offset = p_coords.y % 2;
-
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
- return p_coords + Vector2i(1, 0);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(is_offset ? 0 : 1, 1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
- return p_coords + Vector2i(0, 2);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(is_offset ? -1 : 0, 1);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
- return p_coords + Vector2i(-1, 0);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(is_offset ? -1 : 0, -1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
- return p_coords + Vector2i(0, -2);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(is_offset ? 0 : 1, -1);
- } else {
- ERR_FAIL_V(p_coords);
- }
- } else {
- bool is_offset = p_coords.x % 2;
-
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
- return p_coords + Vector2i(0, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(1, is_offset ? 0 : 1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
- return p_coords + Vector2i(2, 0);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(1, is_offset ? -1 : 0);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
- return p_coords + Vector2i(0, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(-1, is_offset ? -1 : 0);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
- return p_coords + Vector2i(-2, 0);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(-1, is_offset ? 0 : 1);
- } else {
- ERR_FAIL_V(p_coords);
- }
- }
- } break;
- case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
- case TileSet::TILE_LAYOUT_STAIRS_DOWN: {
- if ((tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
- return p_coords + Vector2i(1, 0);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(0, 1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
- return p_coords + Vector2i(-1, 2);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(-1, 1);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
- return p_coords + Vector2i(-1, 0);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(0, -1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
- return p_coords + Vector2i(1, -2);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(1, -1);
- } else {
- ERR_FAIL_V(p_coords);
- }
-
- } else {
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
- return p_coords + Vector2i(0, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(1, 0);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
- return p_coords + Vector2i(2, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(1, -1);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
- return p_coords + Vector2i(0, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(-1, 0);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
- return p_coords + Vector2i(-2, 1);
-
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(-1, 1);
- } else {
- ERR_FAIL_V(p_coords);
- }
- }
- } else {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
- return p_coords + Vector2i(2, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(1, 0);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
- return p_coords + Vector2i(0, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(-1, 1);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
- return p_coords + Vector2i(-2, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(-1, 0);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
- return p_coords + Vector2i(0, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(1, -1);
- } else {
- ERR_FAIL_V(p_coords);
- }
-
- } else {
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
- return p_coords + Vector2i(-1, 2);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(0, 1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
- return p_coords + Vector2i(1, 0);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(1, -1);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
- return p_coords + Vector2i(1, -2);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(0, -1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
- return p_coords + Vector2i(-1, 0);
-
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(-1, 1);
- } else {
- ERR_FAIL_V(p_coords);
- }
- }
- }
- } break;
- case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
- case TileSet::TILE_LAYOUT_DIAMOND_DOWN: {
- if ((tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
- return p_coords + Vector2i(1, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(0, 1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
- return p_coords + Vector2i(-1, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(-1, 0);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
- return p_coords + Vector2i(-1, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(0, -1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
- return p_coords + Vector2i(1, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(1, 0);
- } else {
- ERR_FAIL_V(p_coords);
- }
-
- } else {
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
- return p_coords + Vector2i(1, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(1, 0);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
- return p_coords + Vector2i(1, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(0, -1);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
- return p_coords + Vector2i(-1, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(-1, 0);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
- return p_coords + Vector2i(-1, 1);
-
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(0, 1);
- } else {
- ERR_FAIL_V(p_coords);
- }
- }
- } else {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
- return p_coords + Vector2i(1, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(1, 0);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
- return p_coords + Vector2i(1, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(0, 1);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
- return p_coords + Vector2i(-1, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(-1, 0);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
- return p_coords + Vector2i(-1, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(0, -1);
- } else {
- ERR_FAIL_V(p_coords);
- }
-
- } else {
- if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
- return p_coords + Vector2i(-1, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
- return p_coords + Vector2i(0, 1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
- return p_coords + Vector2i(1, 1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
- return p_coords + Vector2i(1, 0);
- } else if ((shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
- (shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
- return p_coords + Vector2i(1, -1);
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
- return p_coords + Vector2i(0, -1);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
- return p_coords + Vector2i(-1, -1);
-
- } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
- return p_coords + Vector2i(-1, 0);
- } else {
- ERR_FAIL_V(p_coords);
- }
- }
- }
- } break;
- default:
- ERR_FAIL_V(p_coords);
- }
- }
+ ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2i());
+ return tile_set->get_neighbor_cell(p_coords, p_cell_neighbor);
}
TypedArray<Vector2i> TileMap::get_used_cells(int p_layer) const {
@@ -1616,7 +953,7 @@ Rect2i TileMap::get_used_rect() const {
// Return the visible rect of the tilemap.
bool first = true;
Rect2i rect = Rect2i();
- for (const Ref<TileMapLayer> &layer : layers) {
+ for (const TileMapLayer *layer : layers) {
Rect2i layer_rect = layer->get_used_rect();
if (layer_rect == Rect2i()) {
continue;
@@ -1636,7 +973,7 @@ Rect2i TileMap::get_used_rect() const {
void TileMap::set_light_mask(int p_light_mask) {
// Occlusion: set light mask.
CanvasItem::set_light_mask(p_light_mask);
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_LIGHT_MASK);
}
}
@@ -1646,7 +983,7 @@ void TileMap::set_material(const Ref<Material> &p_material) {
CanvasItem::set_material(p_material);
// Update material for the whole tilemap.
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_MATERIAL);
}
}
@@ -1656,7 +993,7 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) {
CanvasItem::set_use_parent_material(p_use_parent_material);
// Update use_parent_material for the whole tilemap.
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_USE_PARENT_MATERIAL);
}
}
@@ -1664,7 +1001,7 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) {
void TileMap::set_texture_filter(TextureFilter p_texture_filter) {
// Set a default texture filter for the whole tilemap.
CanvasItem::set_texture_filter(p_texture_filter);
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_TEXTURE_FILTER);
}
}
@@ -1672,7 +1009,7 @@ void TileMap::set_texture_filter(TextureFilter p_texture_filter) {
void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) {
// Set a default texture repeat for the whole tilemap.
CanvasItem::set_texture_repeat(p_texture_repeat);
- for (Ref<TileMapLayer> &layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_TEXTURE_REPEAT);
}
}
@@ -1771,14 +1108,14 @@ PackedStringArray TileMap::get_configuration_warnings() const {
// Retrieve the set of Z index values with a Y-sorted layer.
RBSet<int> y_sorted_z_index;
- for (const Ref<TileMapLayer> &layer : layers) {
+ for (const TileMapLayer *layer : layers) {
if (layer->is_y_sort_enabled()) {
y_sorted_z_index.insert(layer->get_z_index());
}
}
// Check if we have a non-sorted layer in a Z-index with a Y-sorted layer.
- for (const Ref<TileMapLayer> &layer : layers) {
+ for (const TileMapLayer *layer : layers) {
if (!layer->is_y_sort_enabled() && y_sorted_z_index.has(layer->get_z_index())) {
warnings.push_back(RTR("A Y-sorted layer has the same Z-index value as a not Y-sorted layer.\nThis may lead to unwanted behaviors, as a layer that is not Y-sorted will be Y-sorted as a whole with tiles from Y-sorted layers."));
break;
@@ -1787,7 +1124,7 @@ PackedStringArray TileMap::get_configuration_warnings() const {
if (!is_y_sort_enabled()) {
// Check if Y-sort is enabled on a layer but not on the node.
- for (const Ref<TileMapLayer> &layer : layers) {
+ for (const TileMapLayer *layer : layers) {
if (layer->is_y_sort_enabled()) {
warnings.push_back(RTR("A TileMap layer is set as Y-sorted, but Y-sort is not enabled on the TileMap node itself."));
break;
@@ -1796,7 +1133,7 @@ PackedStringArray TileMap::get_configuration_warnings() const {
} else {
// Check if Y-sort is enabled on the node, but not on any of the layers.
bool need_warning = true;
- for (const Ref<TileMapLayer> &layer : layers) {
+ for (const TileMapLayer *layer : layers) {
if (layer->is_y_sort_enabled()) {
need_warning = false;
break;
@@ -1811,7 +1148,7 @@ PackedStringArray TileMap::get_configuration_warnings() const {
if (tile_set.is_valid() && tile_set->get_tile_shape() == TileSet::TILE_SHAPE_ISOMETRIC) {
bool warn = !is_y_sort_enabled();
if (!warn) {
- for (const Ref<TileMapLayer> &layer : layers) {
+ for (const TileMapLayer *layer : layers) {
if (!layer->is_y_sort_enabled()) {
warn = true;
break;
@@ -1926,42 +1263,27 @@ void TileMap::_bind_methods() {
void TileMap::_tile_set_changed() {
emit_signal(CoreStringNames::get_singleton()->changed);
- for (Ref<TileMapLayer> layer : layers) {
+ for (TileMapLayer *layer : layers) {
layer->notify_tile_map_change(TileMapLayer::DIRTY_FLAGS_TILE_MAP_TILE_SET);
}
update_configuration_warnings();
}
-void TileMap::_update_notify_local_transform() {
- bool notify = collision_animatable || is_y_sort_enabled();
- if (!notify) {
- for (const Ref<TileMapLayer> &layer : layers) {
- if (layer->is_y_sort_enabled()) {
- notify = true;
- break;
- }
- }
- }
- set_notify_local_transform(notify);
-}
-
TileMap::TileMap() {
- set_notify_transform(true);
- _update_notify_local_transform();
-
- Ref<TileMapLayer> new_layer;
- new_layer.instantiate();
- new_layer->set_tile_map(this);
+ TileMapLayer *new_layer = memnew(TileMapLayer);
+ add_child(new_layer);
+ new_layer->set_name("Layer0");
new_layer->set_layer_index_in_tile_map_node(0);
layers.push_back(new_layer);
- default_layer.instantiate();
+ default_layer = memnew(TileMapLayer);
}
TileMap::~TileMap() {
if (tile_set.is_valid()) {
tile_set->disconnect_changed(callable_mp(this, &TileMap::_tile_set_changed));
}
+ memdelete(default_layer);
}
#undef TILEMAP_CALL_FOR_LAYER
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 29af0ad2b1..f2b481cdd1 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -71,18 +71,17 @@ private:
VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT;
// Layers.
- LocalVector<Ref<TileMapLayer>> layers;
- Ref<TileMapLayer> default_layer; // Dummy layer to fetch default values.
+ LocalVector<TileMapLayer *> layers;
+ TileMapLayer *default_layer; // Dummy layer to fetch default values.
int selected_layer = -1;
bool pending_update = false;
+ // Transforms for collision_animatable.
Transform2D last_valid_transform;
Transform2D new_transform;
void _tile_set_changed();
- void _update_notify_local_transform();
-
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -97,6 +96,8 @@ protected:
Rect2i _get_used_rect_bind_compat_78328();
void _set_quadrant_size_compat_81070(int p_quadrant_size);
int _get_quadrant_size_compat_81070() const;
+ VisibilityMode _get_collision_visibility_mode_bind_compat_87115();
+ VisibilityMode _get_navigation_visibility_mode_bind_compat_87115();
static void _bind_compatibility_methods();
#endif
@@ -150,15 +151,15 @@ public:
void set_selected_layer(int p_layer_id); // For editor use.
int get_selected_layer() const;
- void set_collision_animatable(bool p_enabled);
+ void set_collision_animatable(bool p_collision_animatable);
bool is_collision_animatable() const;
// Debug visibility modes.
void set_collision_visibility_mode(VisibilityMode p_show_collision);
- VisibilityMode get_collision_visibility_mode();
+ VisibilityMode get_collision_visibility_mode() const;
void set_navigation_visibility_mode(VisibilityMode p_show_navigation);
- VisibilityMode get_navigation_visibility_mode();
+ VisibilityMode get_navigation_visibility_mode() const;
// Cells accessors.
void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0);
@@ -192,7 +193,6 @@ public:
Vector2 map_to_local(const Vector2i &p_pos) const;
Vector2i local_to_map(const Vector2 &p_pos) const;
-
bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const;
Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const;
diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp
index ed93b8d051..df79b3fee6 100644
--- a/scene/2d/tile_map_layer.cpp
+++ b/scene/2d/tile_map_layer.cpp
@@ -40,6 +40,18 @@
#include "servers/navigation_server_3d.h"
#endif // DEBUG_ENABLED
+TileMap *TileMapLayer::_fetch_tilemap() const {
+ return TileMap::cast_to<TileMap>(get_parent());
+}
+
+Ref<TileSet> TileMapLayer::_fetch_tileset() const {
+ TileMap *tile_map_node = _fetch_tilemap();
+ if (!tile_map_node) {
+ return Ref<TileSet>();
+ }
+ return tile_map_node->get_tileset();
+}
+
#ifdef DEBUG_ENABLED
/////////////////////////////// Debug //////////////////////////////////////////
constexpr int TILE_MAP_DEBUG_QUADRANT_SIZE = 16;
@@ -51,11 +63,11 @@ Vector2i TileMapLayer::_coords_to_debug_quadrant_coords(const Vector2i &p_coords
}
void TileMapLayer::_debug_update() {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
RenderingServer *rs = RenderingServer::get_singleton();
// Check if we should cleanup everything.
- bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid() || !tile_map_node->is_visible_in_tree();
+ bool forced_cleanup = in_destructor || !enabled || !tile_set.is_valid() || !is_visible_in_tree();
if (forced_cleanup) {
for (KeyValue<Vector2i, Ref<DebugQuadrant>> &kv : debug_quadrant_map) {
@@ -120,10 +132,10 @@ void TileMapLayer::_debug_update() {
} else {
ci = rs->canvas_item_create();
rs->canvas_item_set_z_index(ci, RS::CANVAS_ITEM_Z_MAX - 1);
- rs->canvas_item_set_parent(ci, tile_map_node->get_canvas_item());
+ rs->canvas_item_set_parent(ci, get_canvas_item());
}
- const Vector2 quadrant_pos = tile_map_node->map_to_local(debug_quadrant.quadrant_coords * TILE_MAP_DEBUG_QUADRANT_SIZE);
+ const Vector2 quadrant_pos = tile_set->map_to_local(debug_quadrant.quadrant_coords * TILE_MAP_DEBUG_QUADRANT_SIZE);
Transform2D xform(0, quadrant_pos);
rs->canvas_item_set_transform(ci, xform);
@@ -179,48 +191,33 @@ void TileMapLayer::_debug_quadrants_update_cell(CellData &r_cell_data, SelfList<
/////////////////////////////// Rendering //////////////////////////////////////
void TileMapLayer::_rendering_update() {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const TileMap *tile_map_node = _fetch_tilemap();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
RenderingServer *rs = RenderingServer::get_singleton();
// Check if we should cleanup everything.
- bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid() || !tile_map_node->is_visible_in_tree();
+ bool forced_cleanup = in_destructor || !enabled || !tile_set.is_valid() || !is_visible_in_tree();
// ----------- Layer level processing -----------
- if (forced_cleanup) {
- // Cleanup.
- if (canvas_item.is_valid()) {
- rs->free(canvas_item);
- canvas_item = RID();
- }
- } else {
- // Create/Update the layer's CanvasItem.
- if (!canvas_item.is_valid()) {
- RID ci = rs->canvas_item_create();
- rs->canvas_item_set_parent(ci, tile_map_node->get_canvas_item());
- canvas_item = ci;
- }
- RID &ci = canvas_item;
- rs->canvas_item_set_draw_index(ci, layer_index_in_tile_map_node - (int64_t)0x80000000);
- rs->canvas_item_set_sort_children_by_y(ci, y_sort_enabled);
- rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid());
- rs->canvas_item_set_z_index(ci, z_index);
- rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree()));
- rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree()));
- rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask());
+ if (!forced_cleanup) {
+ // Update the layer's CanvasItem.
+ set_use_parent_material(true);
+ set_light_mask(tile_map_node->get_light_mask());
// Modulate the layer.
- Color layer_modulate = modulate;
+ Color layer_modulate = get_modulate();
int selected_layer = tile_map_node->get_selected_layer();
if (selected_layer >= 0 && layer_index_in_tile_map_node != selected_layer) {
int z_selected = tile_map_node->get_layer_z_index(selected_layer);
- if (z_index < z_selected || (z_index == z_selected && layer_index_in_tile_map_node < selected_layer)) {
+ int layer_z_index = get_z_index();
+ if (layer_z_index < z_selected || (layer_z_index == z_selected && layer_index_in_tile_map_node < selected_layer)) {
layer_modulate = layer_modulate.darkened(0.5);
- } else if (z_index > z_selected || (z_index == z_selected && layer_index_in_tile_map_node > selected_layer)) {
+ } else if (layer_z_index > z_selected || (layer_z_index == z_selected && layer_index_in_tile_map_node > selected_layer)) {
layer_modulate = layer_modulate.darkened(0.5);
layer_modulate.a *= 0.3;
}
}
- rs->canvas_item_set_modulate(ci, layer_modulate);
+ rs->canvas_item_set_modulate(get_canvas_item(), layer_modulate);
}
// ----------- Quadrants processing -----------
@@ -231,7 +228,7 @@ void TileMapLayer::_rendering_update() {
// Check if anything changed that might change the quadrant shape.
// If so, recreate everything.
bool quandrant_shape_changed = dirty.flags[DIRTY_FLAGS_TILE_MAP_QUADRANT_SIZE] ||
- (tile_map_node->is_y_sort_enabled() && y_sort_enabled && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_TILE_MAP_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_TILE_MAP_LOCAL_XFORM] || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]));
+ (is_y_sort_enabled() && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_TILE_MAP_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM] || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]));
// Free all quadrants.
if (forced_cleanup || quandrant_shape_changed) {
@@ -250,7 +247,7 @@ void TileMapLayer::_rendering_update() {
if (!forced_cleanup) {
// List all quadrants to update, recreating them if needed.
- if (dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || _rendering_was_cleaned_up) {
+ if (dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE] || _rendering_was_cleaned_up) {
// Update all cells.
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
CellData &cell_data = kv.value;
@@ -290,7 +287,7 @@ void TileMapLayer::_rendering_update() {
rendering_quadrant->canvas_items.clear();
// Sort the quadrant cells.
- if (tile_map_node->is_y_sort_enabled() && is_y_sort_enabled()) {
+ if (is_y_sort_enabled()) {
// For compatibility reasons, we use another comparator for Y-sorted layers.
rendering_quadrant->cells.sort_custom<CellDataYSortedComparator>();
} else {
@@ -330,8 +327,8 @@ void TileMapLayer::_rendering_update() {
if (mat.is_valid()) {
rs->canvas_item_set_material(ci, mat->get_rid());
}
- rs->canvas_item_set_parent(ci, canvas_item);
- rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid());
+ rs->canvas_item_set_parent(ci, get_canvas_item());
+ rs->canvas_item_set_use_parent_material(ci, true);
Transform2D xform(0, rendering_quadrant->canvas_items_position);
rs->canvas_item_set_transform(ci, xform);
@@ -340,8 +337,8 @@ void TileMapLayer::_rendering_update() {
rs->canvas_item_set_z_as_relative_to_parent(ci, true);
rs->canvas_item_set_z_index(ci, tile_z_index);
- rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree()));
- rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree()));
+ rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter_in_tree()));
+ rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat_in_tree()));
rendering_quadrant->canvas_items.push_back(ci);
@@ -354,7 +351,7 @@ void TileMapLayer::_rendering_update() {
ci = prev_ci;
}
- const Vector2 local_tile_pos = tile_map_node->map_to_local(cell_data.coords);
+ const Vector2 local_tile_pos = tile_set->map_to_local(cell_data.coords);
// Random animation offset.
real_t random_animation_offset = 0.0;
@@ -366,7 +363,7 @@ void TileMapLayer::_rendering_update() {
}
// Drawing the tile in the canvas item.
- tile_map_node->draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, tile_map_node->get_self_modulate(), tile_data, random_animation_offset);
+ TileMap::draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, get_self_modulate(), tile_data, random_animation_offset);
}
} else {
// Free the quadrant.
@@ -393,7 +390,7 @@ void TileMapLayer::_rendering_update() {
RBMap<Vector2, Ref<RenderingQuadrant>, RenderingQuadrant::CoordsWorldComparator> local_to_map;
for (KeyValue<Vector2i, Ref<RenderingQuadrant>> &kv : rendering_quadrant_map) {
Ref<RenderingQuadrant> &rendering_quadrant = kv.value;
- local_to_map[tile_map_node->map_to_local(rendering_quadrant->quadrant_coords)] = rendering_quadrant;
+ local_to_map[tile_set->map_to_local(rendering_quadrant->quadrant_coords)] = rendering_quadrant;
}
// Sort the quadrants.
@@ -409,14 +406,15 @@ void TileMapLayer::_rendering_update() {
dirty.flags[DIRTY_FLAGS_TILE_MAP_USE_PARENT_MATERIAL] ||
dirty.flags[DIRTY_FLAGS_TILE_MAP_MATERIAL] ||
dirty.flags[DIRTY_FLAGS_TILE_MAP_TEXTURE_FILTER] ||
- dirty.flags[DIRTY_FLAGS_TILE_MAP_TEXTURE_REPEAT]) {
+ dirty.flags[DIRTY_FLAGS_TILE_MAP_TEXTURE_REPEAT] ||
+ dirty.flags[DIRTY_FLAGS_LAYER_SELF_MODULATE]) {
for (KeyValue<Vector2i, Ref<RenderingQuadrant>> &kv : rendering_quadrant_map) {
Ref<RenderingQuadrant> &rendering_quadrant = kv.value;
for (const RID &ci : rendering_quadrant->canvas_items) {
rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask());
- rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid());
- rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree()));
- rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree()));
+ rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter_in_tree()));
+ rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat_in_tree()));
+ rs->canvas_item_set_self_modulate(ci, get_self_modulate());
}
}
}
@@ -441,30 +439,37 @@ void TileMapLayer::_rendering_update() {
_rendering_occluders_update_cell(cell_data);
}
}
+ }
- // Updates on TileMap changes.
- if (dirty.flags[DIRTY_FLAGS_TILE_MAP_IN_CANVAS] || dirty.flags[DIRTY_FLAGS_TILE_MAP_VISIBILITY]) {
+ // -----------
+ // Mark the rendering state as up to date.
+ _rendering_was_cleaned_up = forced_cleanup;
+}
+
+void TileMapLayer::_rendering_notification(int p_what) {
+ RenderingServer *rs = RenderingServer::get_singleton();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
+ if (p_what == NOTIFICATION_TRANSFORM_CHANGED || p_what == NOTIFICATION_ENTER_CANVAS || p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ if (tile_set.is_valid()) {
+ Transform2D tilemap_xform = get_global_transform();
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
- CellData &cell_data = kv.value;
+ const CellData &cell_data = kv.value;
for (const RID &occluder : cell_data.occluders) {
if (occluder.is_null()) {
continue;
}
- Transform2D xform(0, tile_map_node->map_to_local(kv.key));
- rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas());
- rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform);
+ Transform2D xform(0, tile_set->map_to_local(kv.key));
+ rs->canvas_light_occluder_attach_to_canvas(occluder, get_canvas());
+ rs->canvas_light_occluder_set_transform(occluder, tilemap_xform * xform);
}
}
}
}
-
- // -----------
- // Mark the rendering state as up to date.
- _rendering_was_cleaned_up = forced_cleanup;
}
void TileMapLayer::_rendering_quadrants_update_cell(CellData &r_cell_data, SelfList<RenderingQuadrant>::List &r_dirty_rendering_quadrant_list) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const TileMap *tile_map_node = _fetch_tilemap();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
// Check if the cell is valid and retrieve its y_sort_origin.
bool is_valid = false;
@@ -489,8 +494,8 @@ void TileMapLayer::_rendering_quadrants_update_cell(CellData &r_cell_data, SelfL
// Get the quadrant coords.
Vector2 canvas_items_position;
Vector2i quadrant_coords;
- if (tile_map_node->is_y_sort_enabled() && is_y_sort_enabled()) {
- canvas_items_position = Vector2(0, tile_map_node->map_to_local(r_cell_data.coords).y + tile_y_sort_origin + y_sort_origin);
+ if (is_y_sort_enabled()) {
+ canvas_items_position = Vector2(0, tile_set->map_to_local(r_cell_data.coords).y + tile_y_sort_origin + y_sort_origin);
quadrant_coords = canvas_items_position * 100;
} else {
int quad_size = tile_map_node->get_rendering_quadrant_size();
@@ -500,7 +505,7 @@ void TileMapLayer::_rendering_quadrants_update_cell(CellData &r_cell_data, SelfL
quadrant_coords = Vector2i(
coords.x > 0 ? coords.x / quad_size : (coords.x - (quad_size - 1)) / quad_size,
coords.y > 0 ? coords.y / quad_size : (coords.y - (quad_size - 1)) / quad_size);
- canvas_items_position = tile_map_node->map_to_local(quad_size * quadrant_coords);
+ canvas_items_position = tile_set->map_to_local(quad_size * quadrant_coords);
}
Ref<RenderingQuadrant> rendering_quadrant;
@@ -564,8 +569,7 @@ void TileMapLayer::_rendering_occluders_clear_cell(CellData &r_cell_data) {
}
void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) {
- bool node_visible = tile_map_node->is_visible_in_tree();
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
RenderingServer *rs = RenderingServer::get_singleton();
// Free unused occluders then resize the occluders array.
@@ -606,14 +610,13 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) {
if (occluder_polygon.is_valid()) {
// Create or update occluder.
Transform2D xform;
- xform.set_origin(tile_map_node->map_to_local(r_cell_data.coords));
+ xform.set_origin(tile_set->map_to_local(r_cell_data.coords));
if (!occluder.is_valid()) {
occluder = rs->canvas_light_occluder_create();
}
- rs->canvas_light_occluder_set_enabled(occluder, node_visible);
- rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform);
+ rs->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform);
rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder(occlusion_layer_index, flip_h, flip_v, transpose)->get_rid());
- rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas());
+ rs->canvas_light_occluder_attach_to_canvas(occluder, get_canvas());
rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index));
} else {
// Clear occluder.
@@ -635,7 +638,7 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) {
#ifdef DEBUG_ENABLED
void TileMapLayer::_rendering_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
if (!Engine::get_singleton()->is_editor_hint()) {
@@ -671,7 +674,7 @@ void TileMapLayer::_rendering_draw_cell_debug(const RID &p_canvas_item, const Ve
// Draw a placeholder tile.
Transform2D cell_to_quadrant;
- cell_to_quadrant.set_origin(tile_map_node->map_to_local(r_cell_data.coords) - p_quadrant_pos);
+ cell_to_quadrant.set_origin(tile_set->map_to_local(r_cell_data.coords) - p_quadrant_pos);
rs->canvas_item_add_set_transform(p_canvas_item, cell_to_quadrant);
rs->canvas_item_add_circle(p_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
}
@@ -684,17 +687,17 @@ void TileMapLayer::_rendering_draw_cell_debug(const RID &p_canvas_item, const Ve
/////////////////////////////// Physics //////////////////////////////////////
void TileMapLayer::_physics_update() {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
// Check if we should cleanup everything.
- bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid();
+ bool forced_cleanup = in_destructor || !enabled || !is_inside_tree() || !tile_set.is_valid();
if (forced_cleanup) {
// Clean everything.
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
_physics_clear_cell(kv.value);
}
} else {
- if (_physics_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || dirty.flags[DIRTY_FLAGS_TILE_MAP_COLLISION_ANIMATABLE]) {
+ if (_physics_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) {
// Update all cells.
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
_physics_update_cell(kv.value);
@@ -713,60 +716,43 @@ void TileMapLayer::_physics_update() {
_physics_was_cleaned_up = forced_cleanup;
}
-void TileMapLayer::_physics_notify_tilemap_change(TileMapLayer::DirtyFlags p_what) {
- Transform2D gl_transform = tile_map_node->get_global_transform();
+void TileMapLayer::_physics_notification(int p_what) {
+ const Ref<TileSet> &tile_set = _fetch_tileset();
+ Transform2D gl_transform = get_global_transform();
PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
- bool in_editor = false;
-#ifdef TOOLS_ENABLED
- in_editor = Engine::get_singleton()->is_editor_hint();
-#endif
-
- if (p_what == DIRTY_FLAGS_TILE_MAP_XFORM) {
- if (tile_map_node->is_inside_tree() && (!tile_map_node->is_collision_animatable() || in_editor)) {
+ switch (p_what) {
+ case NOTIFICATION_TRANSFORM_CHANGED:
// Move the collisison shapes along with the TileMap.
- for (KeyValue<Vector2i, CellData> &kv : tile_map) {
- const CellData &cell_data = kv.value;
+ if (is_inside_tree() && tile_set.is_valid()) {
+ for (KeyValue<Vector2i, CellData> &kv : tile_map) {
+ const CellData &cell_data = kv.value;
- for (RID body : cell_data.bodies) {
- if (body.is_valid()) {
- Transform2D xform(0, tile_map_node->map_to_local(bodies_coords[body]));
- xform = gl_transform * xform;
- ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
- }
- }
- }
- }
- } else if (p_what == DIRTY_FLAGS_TILE_MAP_LOCAL_XFORM) {
- // With collisions animatable, move the collisison shapes along with the TileMap only on local xform change (they are synchornized on physics tick instead).
- if (tile_map_node->is_inside_tree() && tile_map_node->is_collision_animatable() && !in_editor) {
- for (KeyValue<Vector2i, CellData> &kv : tile_map) {
- const CellData &cell_data = kv.value;
-
- for (RID body : cell_data.bodies) {
- if (body.is_valid()) {
- Transform2D xform(0, tile_map_node->map_to_local(bodies_coords[body]));
- xform = gl_transform * xform;
- ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ for (RID body : cell_data.bodies) {
+ if (body.is_valid()) {
+ Transform2D xform(0, tile_set->map_to_local(kv.key));
+ xform = gl_transform * xform;
+ ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ }
}
}
}
- }
- } else if (p_what == DIRTY_FLAGS_TILE_MAP_IN_TREE) {
- // Changes in the tree may cause the space to change (e.g. when reparenting to a SubViewport).
- if (tile_map_node->is_inside_tree()) {
- RID space = tile_map_node->get_world_2d()->get_space();
+ break;
+ case NOTIFICATION_ENTER_TREE:
+ // Changes in the tree may cause the space to change (e.g. when reparenting to a SubViewport).
+ if (is_inside_tree()) {
+ RID space = get_world_2d()->get_space();
- for (KeyValue<Vector2i, CellData> &kv : tile_map) {
- const CellData &cell_data = kv.value;
+ for (KeyValue<Vector2i, CellData> &kv : tile_map) {
+ const CellData &cell_data = kv.value;
- for (RID body : cell_data.bodies) {
- if (body.is_valid()) {
- ps->body_set_space(body, space);
+ for (RID body : cell_data.bodies) {
+ if (body.is_valid()) {
+ ps->body_set_space(body, space);
+ }
}
}
}
- }
}
}
@@ -784,9 +770,10 @@ void TileMapLayer::_physics_clear_cell(CellData &r_cell_data) {
}
void TileMapLayer::_physics_update_cell(CellData &r_cell_data) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
- Transform2D gl_transform = tile_map_node->get_global_transform();
- RID space = tile_map_node->get_world_2d()->get_space();
+ const TileMap *tile_map_node = _fetch_tilemap();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
+ Transform2D gl_transform = get_global_transform();
+ RID space = get_world_2d()->get_space();
PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
// Recreate bodies and shapes.
@@ -813,10 +800,11 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) {
// Free unused bodies then resize the bodies array.
for (uint32_t i = tile_set->get_physics_layers_count(); i < r_cell_data.bodies.size(); i++) {
- RID body = r_cell_data.bodies[i];
+ RID &body = r_cell_data.bodies[i];
if (body.is_valid()) {
bodies_coords.erase(body);
ps->free(body);
+ body = RID();
}
}
r_cell_data.bodies.resize(tile_set->get_physics_layers_count());
@@ -844,7 +832,7 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) {
ps->body_set_space(body, space);
Transform2D xform;
- xform.set_origin(tile_map_node->map_to_local(r_cell_data.coords));
+ xform.set_origin(tile_set->map_to_local(r_cell_data.coords));
xform = gl_transform * xform;
ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
@@ -900,17 +888,18 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) {
#ifdef DEBUG_ENABLED
void TileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data) {
// Draw the debug collision shapes.
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ TileMap *tile_map_node = _fetch_tilemap();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
- if (!tile_map_node->get_tree()) {
+ if (!get_tree()) {
return;
}
bool show_collision = false;
switch (tile_map_node->get_collision_visibility_mode()) {
case TileMap::VISIBILITY_MODE_DEFAULT:
- show_collision = !Engine::get_singleton()->is_editor_hint() && tile_map_node->get_tree()->is_debugging_collisions_hint();
+ show_collision = !Engine::get_singleton()->is_editor_hint() && get_tree()->is_debugging_collisions_hint();
break;
case TileMap::VISIBILITY_MODE_FORCE_HIDE:
show_collision = false;
@@ -926,12 +915,12 @@ void TileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, const Vect
RenderingServer *rs = RenderingServer::get_singleton();
PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
- Color debug_collision_color = tile_map_node->get_tree()->get_debug_collisions_color();
+ Color debug_collision_color = get_tree()->get_debug_collisions_color();
Vector<Color> color;
color.push_back(debug_collision_color);
Transform2D quadrant_to_local(0, p_quadrant_pos);
- Transform2D global_to_quadrant = (tile_map_node->get_global_transform() * quadrant_to_local).affine_inverse();
+ Transform2D global_to_quadrant = (get_global_transform() * quadrant_to_local).affine_inverse();
for (RID body : r_cell_data.bodies) {
if (body.is_valid()) {
@@ -956,11 +945,11 @@ void TileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, const Vect
void TileMapLayer::_navigation_update() {
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
NavigationServer2D *ns = NavigationServer2D::get_singleton();
// Check if we should cleanup everything.
- bool forced_cleanup = in_destructor || !enabled || !navigation_enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid();
+ bool forced_cleanup = in_destructor || !enabled || !navigation_enabled || !is_inside_tree() || !tile_set.is_valid();
// ----------- Layer level processing -----------
if (forced_cleanup) {
@@ -973,7 +962,7 @@ void TileMapLayer::_navigation_update() {
if (!navigation_map.is_valid()) {
if (layer_index_in_tile_map_node == 0) {
// Use the default World2D navigation map for the first layer when empty.
- navigation_map = tile_map_node->get_world_2d()->get_navigation_map();
+ navigation_map = get_world_2d()->get_navigation_map();
uses_world_navigation_map = true;
} else {
RID new_layer_map = ns->map_create();
@@ -993,7 +982,7 @@ void TileMapLayer::_navigation_update() {
_navigation_clear_cell(kv.value);
}
} else {
- if (_navigation_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) {
+ if (_navigation_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) {
// Update all cells.
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
_navigation_update_cell(kv.value);
@@ -1005,9 +994,18 @@ void TileMapLayer::_navigation_update() {
_navigation_update_cell(cell_data);
}
}
+ }
+
+ // -----------
+ // Mark the navigation state as up to date.
+ _navigation_was_cleaned_up = forced_cleanup;
+}
- if (dirty.flags[DIRTY_FLAGS_TILE_MAP_XFORM]) {
- Transform2D tilemap_xform = tile_map_node->get_global_transform();
+void TileMapLayer::_navigation_notification(int p_what) {
+ const Ref<TileSet> &tile_set = _fetch_tileset();
+ if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
+ if (tile_set.is_valid()) {
+ Transform2D tilemap_xform = get_global_transform();
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
const CellData &cell_data = kv.value;
// Update navigation regions transform.
@@ -1016,16 +1014,12 @@ void TileMapLayer::_navigation_update() {
continue;
}
Transform2D tile_transform;
- tile_transform.set_origin(tile_map_node->map_to_local(kv.key));
+ tile_transform.set_origin(tile_set->map_to_local(kv.key));
NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
}
}
}
}
-
- // -----------
- // Mark the navigation state as up to date.
- _navigation_was_cleaned_up = forced_cleanup;
}
void TileMapLayer::_navigation_clear_cell(CellData &r_cell_data) {
@@ -1042,9 +1036,10 @@ void TileMapLayer::_navigation_clear_cell(CellData &r_cell_data) {
}
void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const TileMap *tile_map_node = _fetch_tilemap();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
NavigationServer2D *ns = NavigationServer2D::get_singleton();
- Transform2D tilemap_xform = tile_map_node->get_global_transform();
+ Transform2D gl_xform = get_global_transform();
// Get the navigation polygons and create regions.
TileMapCell &c = r_cell_data.cell;
@@ -1088,13 +1083,13 @@ void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) {
if (navigation_polygon.is_valid() && (navigation_polygon->get_polygon_count() > 0 || navigation_polygon->get_outline_count() > 0)) {
// Create or update regions.
Transform2D tile_transform;
- tile_transform.set_origin(tile_map_node->map_to_local(r_cell_data.coords));
+ tile_transform.set_origin(tile_set->map_to_local(r_cell_data.coords));
if (!region.is_valid()) {
region = ns->region_create();
}
ns->region_set_owner_id(region, tile_map_node->get_instance_id());
ns->region_set_map(region, navigation_map);
- ns->region_set_transform(region, tilemap_xform * tile_transform);
+ ns->region_set_transform(region, gl_xform * tile_transform);
ns->region_set_navigation_layers(region, tile_set->get_navigation_layer_layers(navigation_layer_index));
ns->region_set_navigation_polygon(region, navigation_polygon);
} else {
@@ -1119,10 +1114,11 @@ void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) {
#ifdef DEBUG_ENABLED
void TileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data) {
// Draw the debug collision shapes.
+ const TileMap *tile_map_node = _fetch_tilemap();
bool show_navigation = false;
switch (tile_map_node->get_navigation_visibility_mode()) {
case TileMap::VISIBILITY_MODE_DEFAULT:
- show_navigation = !Engine::get_singleton()->is_editor_hint() && tile_map_node->get_tree()->is_debugging_navigation_hint();
+ show_navigation = !Engine::get_singleton()->is_editor_hint() && get_tree()->is_debugging_navigation_hint();
break;
case TileMap::VISIBILITY_MODE_FORCE_HIDE:
show_navigation = false;
@@ -1140,7 +1136,7 @@ void TileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, const V
return;
}
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
RenderingServer *rs = RenderingServer::get_singleton();
const NavigationServer2D *ns2d = NavigationServer2D::get_singleton();
@@ -1170,7 +1166,7 @@ void TileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, const V
}
Transform2D cell_to_quadrant;
- cell_to_quadrant.set_origin(tile_map_node->map_to_local(r_cell_data.coords) - p_quadrant_pos);
+ cell_to_quadrant.set_origin(tile_set->map_to_local(r_cell_data.coords) - p_quadrant_pos);
rs->canvas_item_add_set_transform(p_canvas_item, cell_to_quadrant);
for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
@@ -1226,10 +1222,10 @@ void TileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, const V
/////////////////////////////// Scenes //////////////////////////////////////
void TileMapLayer::_scenes_update() {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
// Check if we should cleanup everything.
- bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid();
+ bool forced_cleanup = in_destructor || !enabled || !is_inside_tree() || !tile_set.is_valid();
if (forced_cleanup) {
// Clean everything.
@@ -1237,7 +1233,7 @@ void TileMapLayer::_scenes_update() {
_scenes_clear_cell(kv.value);
}
} else {
- if (_scenes_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) {
+ if (_scenes_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) {
// Update all cells.
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
_scenes_update_cell(kv.value);
@@ -1257,6 +1253,11 @@ void TileMapLayer::_scenes_update() {
}
void TileMapLayer::_scenes_clear_cell(CellData &r_cell_data) {
+ const TileMap *tile_map_node = _fetch_tilemap();
+ if (!tile_map_node) {
+ return;
+ }
+
// Cleanup existing scene.
Node *node = tile_map_node->get_node_or_null(r_cell_data.scene);
if (node) {
@@ -1266,7 +1267,8 @@ void TileMapLayer::_scenes_clear_cell(CellData &r_cell_data) {
}
void TileMapLayer::_scenes_update_cell(CellData &r_cell_data) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ TileMap *tile_map_node = _fetch_tilemap();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
// Clear the scene in any case.
_scenes_clear_cell(r_cell_data);
@@ -1287,10 +1289,10 @@ void TileMapLayer::_scenes_update_cell(CellData &r_cell_data) {
Control *scene_as_control = Object::cast_to<Control>(scene);
Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene);
if (scene_as_control) {
- scene_as_control->set_position(tile_map_node->map_to_local(r_cell_data.coords) + scene_as_control->get_position());
+ scene_as_control->set_position(tile_set->map_to_local(r_cell_data.coords) + scene_as_control->get_position());
} else if (scene_as_node2d) {
Transform2D xform;
- xform.set_origin(tile_map_node->map_to_local(r_cell_data.coords));
+ xform.set_origin(tile_set->map_to_local(r_cell_data.coords));
scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform());
}
tile_map_node->add_child(scene);
@@ -1303,7 +1305,7 @@ void TileMapLayer::_scenes_update_cell(CellData &r_cell_data) {
#ifdef DEBUG_ENABLED
void TileMapLayer::_scenes_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
if (!Engine::get_singleton()->is_editor_hint()) {
@@ -1341,7 +1343,7 @@ void TileMapLayer::_scenes_draw_cell_debug(const RID &p_canvas_item, const Vecto
// Draw a placeholder tile.
Transform2D cell_to_quadrant;
- cell_to_quadrant.set_origin(tile_map_node->map_to_local(r_cell_data.coords) - p_quadrant_pos);
+ cell_to_quadrant.set_origin(tile_set->map_to_local(r_cell_data.coords) - p_quadrant_pos);
rs->canvas_item_add_set_transform(p_canvas_item, cell_to_quadrant);
rs->canvas_item_add_circle(p_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
}
@@ -1353,10 +1355,11 @@ void TileMapLayer::_scenes_draw_cell_debug(const RID &p_canvas_item, const Vecto
/////////////////////////////////////////////////////////////////////
void TileMapLayer::_build_runtime_update_tile_data() {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const TileMap *tile_map_node = _fetch_tilemap();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
// Check if we should cleanup everything.
- bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid() || !tile_map_node->is_visible_in_tree();
+ bool forced_cleanup = in_destructor || !enabled || !tile_set.is_valid() || !is_visible_in_tree();
if (!forced_cleanup) {
if (tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_use_tile_data_runtime_update) && tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_tile_data_runtime_update)) {
if (_runtime_update_tile_data_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) {
@@ -1382,7 +1385,8 @@ void TileMapLayer::_build_runtime_update_tile_data() {
}
void TileMapLayer::_build_runtime_update_tile_data_for_cell(CellData &r_cell_data, bool p_auto_add_to_dirty_list) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ TileMap *tile_map_node = _fetch_tilemap();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
TileMapCell &c = r_cell_data.cell;
TileSetSource *source;
@@ -1425,7 +1429,7 @@ void TileMapLayer::_clear_runtime_update_tile_data() {
}
TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
if (!tile_set.is_valid()) {
return TileSet::TerrainsPattern();
}
@@ -1437,7 +1441,7 @@ TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints
int score = 0;
// Check the center bit constraint.
- TerrainConstraint terrain_constraint = TerrainConstraint(tile_map_node, p_position, terrain_pattern.get_terrain());
+ TerrainConstraint terrain_constraint = TerrainConstraint(tile_set, p_position, terrain_pattern.get_terrain());
const RBSet<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_constraint);
if (in_set_constraint_element) {
if (in_set_constraint_element->get().get_terrain() != terrain_constraint.get_terrain()) {
@@ -1453,7 +1457,7 @@ TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
// Check if the bit is compatible with the constraints.
- TerrainConstraint terrain_bit_constraint = TerrainConstraint(tile_map_node, p_position, bit, terrain_pattern.get_terrain_peering_bit(bit));
+ TerrainConstraint terrain_bit_constraint = TerrainConstraint(tile_set, p_position, bit, terrain_pattern.get_terrain_peering_bit(bit));
in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
if (in_set_constraint_element) {
if (in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
@@ -1486,19 +1490,19 @@ TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints
}
RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
if (!tile_set.is_valid()) {
return RBSet<TerrainConstraint>();
}
// Compute the constraints needed from the surrounding tiles.
RBSet<TerrainConstraint> output;
- output.insert(TerrainConstraint(tile_map_node, p_position, p_terrains_pattern.get_terrain()));
+ output.insert(TerrainConstraint(tile_set, p_position, p_terrains_pattern.get_terrain()));
for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, side)) {
- TerrainConstraint c = TerrainConstraint(tile_map_node, p_position, side, p_terrains_pattern.get_terrain_peering_bit(side));
+ TerrainConstraint c = TerrainConstraint(tile_set, p_position, side, p_terrains_pattern.get_terrain_peering_bit(side));
output.insert(c);
}
}
@@ -1507,7 +1511,7 @@ RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_added_patte
}
RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
if (!tile_set.is_valid()) {
return RBSet<TerrainConstraint>();
}
@@ -1520,7 +1524,7 @@ RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_painted_cel
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over neighbor bits.
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
- dummy_constraints.insert(TerrainConstraint(tile_map_node, E, bit, -1));
+ dummy_constraints.insert(TerrainConstraint(tile_set, E, bit, -1));
}
}
}
@@ -1587,27 +1591,89 @@ RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_painted_cel
int terrain = (tile_data && tile_data->get_terrain_set() == p_terrain_set) ? tile_data->get_terrain() : -1;
if (!p_ignore_empty_terrains || terrain >= 0) {
- constraints.insert(TerrainConstraint(tile_map_node, E_coords, terrain));
+ constraints.insert(TerrainConstraint(tile_set, E_coords, terrain));
}
}
return constraints;
}
-void TileMapLayer::set_tile_map(TileMap *p_tile_map) {
- tile_map_node = p_tile_map;
+void TileMapLayer::_renamed() {
+ TileMap *tile_map_node = _fetch_tilemap();
+ tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+void TileMapLayer::_update_notify_local_transform() {
+ TileMap *tile_map_node = _fetch_tilemap();
+ bool notify = tile_map_node->is_collision_animatable() || is_y_sort_enabled();
+ if (!notify) {
+ if (is_y_sort_enabled()) {
+ notify = true;
+ }
+ }
+ set_notify_local_transform(notify);
+}
+
+void TileMapLayer::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_POSTINITIALIZE: {
+ connect(SNAME("renamed"), callable_mp(this, &TileMapLayer::_renamed));
+ break;
+ }
+ case NOTIFICATION_ENTER_TREE: {
+ _update_notify_local_transform();
+ dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE] = true;
+ TileMap *tile_map_node = _fetch_tilemap();
+ tile_map_node->queue_internal_update();
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE] = true;
+ TileMap *tile_map_node = _fetch_tilemap();
+ tile_map_node->queue_internal_update();
+ } break;
+
+ case TileMap::NOTIFICATION_ENTER_CANVAS: {
+ dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true;
+ TileMap *tile_map_node = _fetch_tilemap();
+ tile_map_node->queue_internal_update();
+ } break;
+
+ case TileMap::NOTIFICATION_EXIT_CANVAS: {
+ dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true;
+ TileMap *tile_map_node = _fetch_tilemap();
+ tile_map_node->queue_internal_update();
+ } break;
+
+ case TileMap::NOTIFICATION_VISIBILITY_CHANGED: {
+ dirty.flags[DIRTY_FLAGS_LAYER_VISIBILITY] = true;
+ TileMap *tile_map_node = _fetch_tilemap();
+ tile_map_node->queue_internal_update();
+ } break;
+ }
+
+ _rendering_notification(p_what);
+ _physics_notification(p_what);
+ _navigation_notification(p_what);
}
void TileMapLayer::set_layer_index_in_tile_map_node(int p_index) {
if (p_index == layer_index_in_tile_map_node) {
return;
}
+ TileMap *tile_map_node = _fetch_tilemap();
layer_index_in_tile_map_node = p_index;
dirty.flags[DIRTY_FLAGS_LAYER_INDEX_IN_TILE_MAP_NODE] = true;
tile_map_node->queue_internal_update();
}
Rect2 TileMapLayer::get_rect(bool &r_changed) const {
+ const Ref<TileSet> &tile_set = _fetch_tileset();
+ if (tile_set.is_null()) {
+ r_changed = rect_cache != Rect2();
+ return Rect2();
+ }
+
// Compute the displayed area of the tilemap.
r_changed = false;
#ifdef DEBUG_ENABLED
@@ -1617,7 +1683,7 @@ Rect2 TileMapLayer::get_rect(bool &r_changed) const {
bool first = true;
for (const KeyValue<Vector2i, CellData> &E : tile_map) {
Rect2 r;
- r.position = tile_map_node->map_to_local(E.key);
+ r.position = tile_set->map_to_local(E.key);
r.size = Size2();
if (first) {
r_total = r;
@@ -1637,7 +1703,7 @@ Rect2 TileMapLayer::get_rect(bool &r_changed) const {
}
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
if (!tile_set.is_valid()) {
return HashMap<Vector2i, TileSet::TerrainsPattern>();
}
@@ -1686,7 +1752,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_constrain
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
HashMap<Vector2i, TileSet::TerrainsPattern> output;
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
@@ -1704,8 +1770,8 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(c
// Find the adequate neighbor.
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- if (tile_map_node->is_existing_neighbor(bit)) {
- Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit);
+ if (tile_set->is_existing_neighbor(bit)) {
+ Vector2i neighbor = tile_set->get_neighbor_cell(coords, bit);
if (!can_modify_set.has(neighbor)) {
can_modify_list.push_back(neighbor);
can_modify_set.insert(neighbor);
@@ -1746,7 +1812,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(c
// Add new constraints from the path drawn.
for (Vector2i coords : p_coords_array) {
// Constraints on the center bit.
- TerrainConstraint c = TerrainConstraint(tile_map_node, coords, p_terrain);
+ TerrainConstraint c = TerrainConstraint(tile_set, coords, p_terrain);
c.set_priority(10);
constraints.insert(c);
@@ -1754,11 +1820,11 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(c
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
- c = TerrainConstraint(tile_map_node, coords, bit, p_terrain);
+ c = TerrainConstraint(tile_set, coords, bit, p_terrain);
c.set_priority(10);
if ((int(bit) % 2) == 0) {
// Side peering bits: add the constraint if the center is of the same terrain.
- Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit);
+ Vector2i neighbor = tile_set->get_neighbor_cell(coords, bit);
if (cells_with_terrain_center_bit.has(neighbor)) {
constraints.insert(c);
}
@@ -1792,7 +1858,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(c
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
HashMap<Vector2i, TileSet::TerrainsPattern> output;
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
@@ -1803,8 +1869,8 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(cons
TileSet::CellNeighbor found_bit = TileSet::CELL_NEIGHBOR_MAX;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- if (tile_map_node->is_existing_neighbor(bit)) {
- if (tile_map_node->get_neighbor_cell(p_coords_array[i], bit) == p_coords_array[i + 1]) {
+ if (tile_set->is_existing_neighbor(bit)) {
+ if (tile_set->get_neighbor_cell(p_coords_array[i], bit) == p_coords_array[i + 1]) {
found_bit = bit;
break;
}
@@ -1829,7 +1895,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(cons
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
- Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit);
+ Vector2i neighbor = tile_set->get_neighbor_cell(coords, bit);
if (!can_modify_set.has(neighbor)) {
can_modify_list.push_back(neighbor);
can_modify_set.insert(neighbor);
@@ -1843,13 +1909,13 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(cons
// Add new constraints from the path drawn.
for (Vector2i coords : p_coords_array) {
// Constraints on the center bit.
- TerrainConstraint c = TerrainConstraint(tile_map_node, coords, p_terrain);
+ TerrainConstraint c = TerrainConstraint(tile_set, coords, p_terrain);
c.set_priority(10);
constraints.insert(c);
}
for (int i = 0; i < p_coords_array.size() - 1; i++) {
// Constraints on the peering bits.
- TerrainConstraint c = TerrainConstraint(tile_map_node, p_coords_array[i], neighbor_list[i], p_terrain);
+ TerrainConstraint c = TerrainConstraint(tile_set, p_coords_array[i], neighbor_list[i], p_terrain);
c.set_priority(10);
constraints.insert(c);
}
@@ -1866,7 +1932,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(cons
HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) {
HashMap<Vector2i, TileSet::TerrainsPattern> output;
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
@@ -1885,7 +1951,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_pattern(c
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
- Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit);
+ Vector2i neighbor = tile_set->get_neighbor_cell(coords, bit);
if (!can_modify_set.has(neighbor)) {
can_modify_list.push_back(neighbor);
can_modify_set.insert(neighbor);
@@ -1922,7 +1988,7 @@ TileMapCell TileMapLayer::get_cell(const Vector2i &p_coords, bool p_use_proxies)
return TileMapCell();
} else {
TileMapCell c = tile_map.find(p_coords)->value.cell;
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
if (p_use_proxies && tile_set.is_valid()) {
Array proxyed = tile_set->map_tile_proxy(c.source_id, c.get_atlas_coords(), c.alternative_tile);
c.source_id = proxyed[0];
@@ -1998,7 +2064,7 @@ void TileMapLayer::set_tile_data(TileMapDataFormat p_format, const Vector<int> &
coord_y = decode_uint16(&local[10]);
}
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
if (tile_set.is_valid()) {
Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose);
if (a.size() == 3) {
@@ -2039,9 +2105,9 @@ Vector<int> TileMapLayer::get_tile_data() const {
}
void TileMapLayer::notify_tile_map_change(DirtyFlags p_what) {
+ TileMap *tile_map_node = _fetch_tilemap();
dirty.flags[p_what] = true;
tile_map_node->queue_internal_update();
- _physics_notify_tilemap_change(p_what);
}
void TileMapLayer::internal_update() {
@@ -2124,7 +2190,10 @@ void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vec
if (!E->value.dirty_list_element.in_list()) {
dirty.cell_list.add(&(E->value.dirty_list_element));
}
- tile_map_node->queue_internal_update();
+ TileMap *tile_map_node = _fetch_tilemap();
+ if (tile_map_node) { // Needed to avoid crashes in destructor.
+ tile_map_node->queue_internal_update();
+ }
used_rect_cache_dirty = true;
}
@@ -2141,7 +2210,7 @@ int TileMapLayer::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxie
return TileSet::INVALID_SOURCE;
}
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
if (p_use_proxies && tile_set.is_valid()) {
Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile);
return proxyed[0];
@@ -2158,7 +2227,7 @@ Vector2i TileMapLayer::get_cell_atlas_coords(const Vector2i &p_coords, bool p_us
return TileSetSource::INVALID_ATLAS_COORDS;
}
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
if (p_use_proxies && tile_set.is_valid()) {
Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile);
return proxyed[1];
@@ -2175,7 +2244,7 @@ int TileMapLayer::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use
return TileSetSource::INVALID_TILE_ALTERNATIVE;
}
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
if (p_use_proxies && tile_set.is_valid()) {
Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile);
return proxyed[2];
@@ -2190,7 +2259,7 @@ TileData *TileMapLayer::get_cell_tile_data(const Vector2i &p_coords, bool p_use_
return nullptr;
}
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
Ref<TileSetAtlasSource> source = tile_set->get_source(source_id);
if (source.is_valid()) {
return source->get_tile_data(get_cell_atlas_coords(p_coords, p_use_proxies), get_cell_alternative_tile(p_coords, p_use_proxies));
@@ -2208,7 +2277,7 @@ void TileMapLayer::clear() {
}
Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_array) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr);
Ref<TileMapPattern> output;
@@ -2262,19 +2331,19 @@ Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_arra
}
void TileMapLayer::set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
ERR_FAIL_COND(tile_set.is_null());
ERR_FAIL_COND(p_pattern.is_null());
TypedArray<Vector2i> used_cells = p_pattern->get_used_cells();
for (int i = 0; i < used_cells.size(); i++) {
- Vector2i coords = tile_map_node->map_pattern(p_position, used_cells[i], p_pattern);
+ Vector2i coords = tile_set->map_pattern(p_position, used_cells[i], p_pattern);
set_cell(coords, p_pattern->get_cell_source_id(used_cells[i]), p_pattern->get_cell_atlas_coords(used_cells[i]), p_pattern->get_cell_alternative_tile(used_cells[i]));
}
}
void TileMapLayer::set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
@@ -2314,7 +2383,7 @@ void TileMapLayer::set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p
}
void TileMapLayer::set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
- const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ const Ref<TileSet> &tile_set = _fetch_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
@@ -2415,24 +2484,13 @@ Rect2i TileMapLayer::get_used_rect() const {
return used_rect_cache;
}
-void TileMapLayer::set_name(String p_name) {
- if (name == p_name) {
- return;
- }
- name = p_name;
- tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
-}
-
-String TileMapLayer::get_name() const {
- return name;
-}
-
void TileMapLayer::set_enabled(bool p_enabled) {
if (enabled == p_enabled) {
return;
}
enabled = p_enabled;
dirty.flags[DIRTY_FLAGS_LAYER_ENABLED] = true;
+ TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
@@ -2443,34 +2501,29 @@ bool TileMapLayer::is_enabled() const {
return enabled;
}
-void TileMapLayer::set_modulate(Color p_modulate) {
- if (modulate == p_modulate) {
+void TileMapLayer::set_self_modulate(const Color &p_self_modulate) {
+ if (get_self_modulate() == p_self_modulate) {
return;
}
- modulate = p_modulate;
- dirty.flags[DIRTY_FLAGS_LAYER_MODULATE] = true;
+ CanvasItem::set_self_modulate(p_self_modulate);
+ dirty.flags[DIRTY_FLAGS_LAYER_SELF_MODULATE] = true;
+ TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
}
-Color TileMapLayer::get_modulate() const {
- return modulate;
-}
-
void TileMapLayer::set_y_sort_enabled(bool p_y_sort_enabled) {
- if (y_sort_enabled == p_y_sort_enabled) {
+ if (is_y_sort_enabled() == p_y_sort_enabled) {
return;
}
- y_sort_enabled = p_y_sort_enabled;
+ CanvasItem::set_y_sort_enabled(p_y_sort_enabled);
dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] = true;
+ TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
tile_map_node->update_configuration_warnings();
-}
-
-bool TileMapLayer::is_y_sort_enabled() const {
- return y_sort_enabled;
+ _update_notify_local_transform();
}
void TileMapLayer::set_y_sort_origin(int p_y_sort_origin) {
@@ -2479,6 +2532,7 @@ void TileMapLayer::set_y_sort_origin(int p_y_sort_origin) {
}
y_sort_origin = p_y_sort_origin;
dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] = true;
+ TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
}
@@ -2488,19 +2542,28 @@ int TileMapLayer::get_y_sort_origin() const {
}
void TileMapLayer::set_z_index(int p_z_index) {
- if (z_index == p_z_index) {
+ if (get_z_index() == p_z_index) {
return;
}
- z_index = p_z_index;
+ CanvasItem::set_z_index(p_z_index);
dirty.flags[DIRTY_FLAGS_LAYER_Z_INDEX] = true;
+ TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
tile_map_node->update_configuration_warnings();
}
-int TileMapLayer::get_z_index() const {
- return z_index;
+void TileMapLayer::set_use_kinematic_bodies(bool p_use_kinematic_bodies) {
+ use_kinematic_bodies = p_use_kinematic_bodies;
+ dirty.flags[DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES] = p_use_kinematic_bodies;
+ TileMap *tile_map_node = _fetch_tilemap();
+ tile_map_node->queue_internal_update();
+ tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+bool TileMapLayer::is_using_kinematic_bodies() const {
+ return use_kinematic_bodies;
}
void TileMapLayer::set_navigation_enabled(bool p_enabled) {
@@ -2509,6 +2572,7 @@ void TileMapLayer::set_navigation_enabled(bool p_enabled) {
}
navigation_enabled = p_enabled;
dirty.flags[DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED] = true;
+ TileMap *tile_map_node = _fetch_tilemap();
tile_map_node->queue_internal_update();
tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
}
@@ -2518,9 +2582,9 @@ bool TileMapLayer::is_navigation_enabled() const {
}
void TileMapLayer::set_navigation_map(RID p_map) {
- ERR_FAIL_COND_MSG(!tile_map_node->is_inside_tree(), "A TileMap navigation map can only be changed while inside the SceneTree.");
+ ERR_FAIL_COND_MSG(!is_inside_tree(), "A TileMap navigation map can only be changed while inside the SceneTree.");
navigation_map = p_map;
- uses_world_navigation_map = p_map == tile_map_node->get_world_2d()->get_navigation_map();
+ uses_world_navigation_map = p_map == get_world_2d()->get_navigation_map();
}
RID TileMapLayer::get_navigation_map() const {
@@ -2531,7 +2595,7 @@ RID TileMapLayer::get_navigation_map() const {
}
void TileMapLayer::fix_invalid_tiles() {
- Ref<TileSet> tileset = tile_map_node->get_tileset();
+ Ref<TileSet> tileset = _fetch_tileset();
ERR_FAIL_COND_MSG(tileset.is_null(), "Cannot call fix_invalid_tiles() on a TileMap without a valid TileSet.");
RBSet<Vector2i> coords;
@@ -2554,12 +2618,11 @@ Vector2i TileMapLayer::get_coords_for_body_rid(RID p_physics_body) const {
return bodies_coords[p_physics_body];
}
-TileMapLayer::~TileMapLayer() {
- if (!tile_map_node) {
- // Temporary layer.
- return;
- }
+TileMapLayer::TileMapLayer() {
+ set_notify_transform(true);
+}
+TileMapLayer::~TileMapLayer() {
in_destructor = true;
clear();
internal_update();
@@ -2569,26 +2632,24 @@ HashMap<Vector2i, TileSet::CellNeighbor> TerrainConstraint::get_overlapping_coor
HashMap<Vector2i, TileSet::CellNeighbor> output;
ERR_FAIL_COND_V(is_center_bit(), output);
+ ERR_FAIL_COND_V(!tile_set.is_valid(), output);
- Ref<TileSet> ts = tile_map->get_tileset();
- ERR_FAIL_COND_V(!ts.is_valid(), output);
-
- TileSet::TileShape shape = ts->get_tile_shape();
+ TileSet::TileShape shape = tile_set->get_tile_shape();
if (shape == TileSet::TILE_SHAPE_SQUARE) {
switch (bit) {
case 1:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
break;
case 2:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
break;
case 3:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
break;
default:
ERR_FAIL_V(output);
@@ -2597,47 +2658,47 @@ HashMap<Vector2i, TileSet::CellNeighbor> TerrainConstraint::get_overlapping_coor
switch (bit) {
case 1:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
break;
case 2:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
break;
case 3:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
break;
default:
ERR_FAIL_V(output);
}
} else {
// Half offset shapes.
- TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis();
+ TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
switch (bit) {
case 1:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
break;
case 2:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
break;
case 3:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
break;
case 4:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
break;
case 5:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
break;
default:
ERR_FAIL_V(output);
@@ -2646,25 +2707,25 @@ HashMap<Vector2i, TileSet::CellNeighbor> TerrainConstraint::get_overlapping_coor
switch (bit) {
case 1:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
break;
case 2:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
break;
case 3:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
break;
case 4:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
break;
case 5:
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ output[tile_set->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
break;
default:
ERR_FAIL_V(output);
@@ -2674,25 +2735,20 @@ HashMap<Vector2i, TileSet::CellNeighbor> TerrainConstraint::get_overlapping_coor
return output;
}
-TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain) {
- tile_map = p_tile_map;
-
- Ref<TileSet> ts = tile_map->get_tileset();
- ERR_FAIL_COND(!ts.is_valid());
-
+TerrainConstraint::TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, int p_terrain) {
+ ERR_FAIL_COND(!p_tile_set.is_valid());
+ tile_set = p_tile_set;
bit = 0;
base_cell_coords = p_position;
terrain = p_terrain;
}
-TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
+TerrainConstraint::TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
// The way we build the constraint make it easy to detect conflicting constraints.
- tile_map = p_tile_map;
-
- Ref<TileSet> ts = tile_map->get_tileset();
- ERR_FAIL_COND(!ts.is_valid());
+ ERR_FAIL_COND(!p_tile_set.is_valid());
+ tile_set = p_tile_set;
- TileSet::TileShape shape = ts->get_tile_shape();
+ TileSet::TileShape shape = tile_set->get_tile_shape();
if (shape == TileSet::TILE_SHAPE_SQUARE) {
switch (p_bit) {
case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
@@ -2709,23 +2765,23 @@ TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &
break;
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER);
break;
case TileSet::CELL_NEIGHBOR_TOP_SIDE:
bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
break;
default:
ERR_FAIL();
@@ -2735,7 +2791,7 @@ TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &
switch (p_bit) {
case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
bit = 1;
@@ -2751,19 +2807,19 @@ TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &
break;
case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_CORNER:
bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER);
break;
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
break;
default:
ERR_FAIL();
@@ -2771,7 +2827,7 @@ TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &
}
} else {
// Half-offset shapes.
- TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis();
+ TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
switch (p_bit) {
case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
@@ -2796,31 +2852,31 @@ TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &
break;
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
bit = 4;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_CORNER:
bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
bit = 5;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
bit = 4;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
break;
default:
ERR_FAIL();
@@ -2846,7 +2902,7 @@ TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &
break;
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
bit = 5;
@@ -2854,27 +2910,27 @@ TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &
break;
case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_SIDE:
bit = 4;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
break;
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
bit = 5;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ base_cell_coords = tile_set->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
break;
default:
ERR_FAIL();
diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h
index f31c5406a4..2cc57f50d1 100644
--- a/scene/2d/tile_map_layer.h
+++ b/scene/2d/tile_map_layer.h
@@ -38,7 +38,7 @@ class TileSetAtlasSource;
class TerrainConstraint {
private:
- const TileMap *tile_map = nullptr;
+ Ref<TileSet> tile_set;
Vector2i base_cell_coords;
int bit = -1;
int terrain = -1;
@@ -83,8 +83,8 @@ public:
return priority;
}
- TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain); // For the center terrain bit
- TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); // For peering bits
+ TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, int p_terrain); // For the center terrain bit
+ TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); // For peering bits
TerrainConstraint(){};
};
@@ -214,23 +214,24 @@ public:
}
};
-class TileMapLayer : public RefCounted {
- GDCLASS(TileMapLayer, RefCounted);
+class TileMapLayer : public Node2D {
+ GDCLASS(TileMapLayer, Node2D);
public:
enum DirtyFlags {
DIRTY_FLAGS_LAYER_ENABLED = 0,
- DIRTY_FLAGS_LAYER_MODULATE,
+ DIRTY_FLAGS_LAYER_IN_TREE,
+ DIRTY_FLAGS_LAYER_IN_CANVAS,
+ DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM,
+ DIRTY_FLAGS_LAYER_VISIBILITY,
+ DIRTY_FLAGS_LAYER_SELF_MODULATE,
DIRTY_FLAGS_LAYER_Y_SORT_ENABLED,
DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN,
DIRTY_FLAGS_LAYER_Z_INDEX,
+ DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES,
DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED,
DIRTY_FLAGS_LAYER_INDEX_IN_TILE_MAP_NODE,
- DIRTY_FLAGS_TILE_MAP_IN_TREE,
- DIRTY_FLAGS_TILE_MAP_IN_CANVAS,
- DIRTY_FLAGS_TILE_MAP_VISIBILITY,
- DIRTY_FLAGS_TILE_MAP_XFORM,
- DIRTY_FLAGS_TILE_MAP_LOCAL_XFORM,
+
DIRTY_FLAGS_TILE_MAP_SELECTED_LAYER,
DIRTY_FLAGS_TILE_MAP_LIGHT_MASK,
DIRTY_FLAGS_TILE_MAP_MATERIAL,
@@ -239,7 +240,6 @@ public:
DIRTY_FLAGS_TILE_MAP_TEXTURE_REPEAT,
DIRTY_FLAGS_TILE_MAP_TILE_SET,
DIRTY_FLAGS_TILE_MAP_QUADRANT_SIZE,
- DIRTY_FLAGS_TILE_MAP_COLLISION_ANIMATABLE,
DIRTY_FLAGS_TILE_MAP_COLLISION_VISIBILITY_MODE,
DIRTY_FLAGS_TILE_MAP_NAVIGATION_VISIBILITY_MODE,
DIRTY_FLAGS_TILE_MAP_Y_SORT_ENABLED,
@@ -249,20 +249,15 @@ public:
private:
// Exposed properties.
- String name;
bool enabled = true;
- Color modulate = Color(1, 1, 1, 1);
- bool y_sort_enabled = false;
int y_sort_origin = 0;
- int z_index = 0;
+ bool use_kinematic_bodies = false;
bool navigation_enabled = true;
RID navigation_map;
bool uses_world_navigation_map = false;
// Internal.
- TileMap *tile_map_node = nullptr;
int layer_index_in_tile_map_node = -1;
- RID canvas_item;
HashMap<Vector2i, CellData> tile_map;
// Dirty flag. Allows knowing what was modified since the last update.
@@ -278,6 +273,10 @@ private:
mutable Rect2i used_rect_cache;
mutable bool used_rect_cache_dirty = true;
+ // Method to fetch the TileSet to use
+ TileMap *_fetch_tilemap() const;
+ Ref<TileSet> _fetch_tileset() const;
+
// Runtime tile data.
bool _runtime_update_tile_data_was_cleaned_up = false;
void _build_runtime_update_tile_data();
@@ -296,6 +295,7 @@ private:
HashMap<Vector2i, Ref<RenderingQuadrant>> rendering_quadrant_map;
bool _rendering_was_cleaned_up = false;
void _rendering_update();
+ void _rendering_notification(int p_what);
void _rendering_quadrants_update_cell(CellData &r_cell_data, SelfList<RenderingQuadrant>::List &r_dirty_rendering_quadrant_list);
void _rendering_occluders_clear_cell(CellData &r_cell_data);
void _rendering_occluders_update_cell(CellData &r_cell_data);
@@ -306,7 +306,7 @@ private:
HashMap<RID, Vector2i> bodies_coords; // Mapping for RID to coords.
bool _physics_was_cleaned_up = false;
void _physics_update();
- void _physics_notify_tilemap_change(DirtyFlags p_what);
+ void _physics_notification(int p_what);
void _physics_clear_cell(CellData &r_cell_data);
void _physics_update_cell(CellData &r_cell_data);
#ifdef DEBUG_ENABLED
@@ -315,6 +315,7 @@ private:
bool _navigation_was_cleaned_up = false;
void _navigation_update();
+ void _navigation_notification(int p_what);
void _navigation_clear_cell(CellData &r_cell_data);
void _navigation_update_cell(CellData &r_cell_data);
#ifdef DEBUG_ENABLED
@@ -334,9 +335,14 @@ private:
RBSet<TerrainConstraint> _get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
RBSet<TerrainConstraint> _get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const;
+ void _renamed();
+ void _update_notify_local_transform();
+
+protected:
+ void _notification(int p_what);
+
public:
// TileMap node.
- void set_tile_map(TileMap *p_tile_map);
void set_layer_index_in_tile_map_node(int p_index);
// Rect caching.
@@ -383,18 +389,15 @@ public:
Rect2i get_used_rect() const;
// Layer properties.
- void set_name(String p_name);
- String get_name() const;
void set_enabled(bool p_enabled);
bool is_enabled() const;
- void set_modulate(Color p_modulate);
- Color get_modulate() const;
- void set_y_sort_enabled(bool p_y_sort_enabled);
- bool is_y_sort_enabled() const;
+ virtual void set_self_modulate(const Color &p_self_modulate) override;
+ virtual void set_y_sort_enabled(bool p_y_sort_enabled) override;
void set_y_sort_origin(int p_y_sort_origin);
int get_y_sort_origin() const;
- void set_z_index(int p_z_index);
- int get_z_index() const;
+ virtual void set_z_index(int p_z_index) override;
+ void set_use_kinematic_bodies(bool p_use_kinematic_bodies);
+ bool is_using_kinematic_bodies() const;
void set_navigation_enabled(bool p_enabled);
bool is_navigation_enabled() const;
void set_navigation_map(RID p_map);
@@ -407,6 +410,7 @@ public:
bool has_body_rid(RID p_physics_body) const;
Vector2i get_coords_for_body_rid(RID p_physics_body) const; // For finding tiles from collision.
+ TileMapLayer();
~TileMapLayer();
};
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index 97b1e282ad..0cfe0f8cb7 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -624,6 +624,7 @@ void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape3
total_subshapes++;
_update_shape_data(p_owner);
+ update_gizmos();
}
int CollisionObject3D::shape_owner_get_shape_count(uint32_t p_owner) const {
@@ -687,6 +688,8 @@ void CollisionObject3D::shape_owner_clear_shapes(uint32_t p_owner) {
while (shape_owner_get_shape_count(p_owner) > 0) {
shape_owner_remove_shape(p_owner, 0);
}
+
+ update_gizmos();
}
uint32_t CollisionObject3D::shape_find_owner(int p_shape_index) const {
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index a35ee17868..7fff2f90fb 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -277,6 +277,8 @@ void CanvasItem::_notification(int p_what) {
ERR_MAIN_THREAD_GUARD;
ERR_FAIL_COND(!is_inside_tree());
+ _set_global_invalid(true);
+
Node *parent = get_parent();
if (parent) {
CanvasItem *ci = Object::cast_to<CanvasItem>(parent);
@@ -314,6 +316,7 @@ void CanvasItem::_notification(int p_what) {
}
}
+ _set_global_invalid(true);
_enter_canvas();
RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, is_visible_in_tree()); // The visibility of the parent may change.
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index 36f0e17924..ce1dbce6c3 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -237,7 +237,7 @@ public:
Color get_modulate() const;
Color get_modulate_in_tree() const;
- void set_self_modulate(const Color &p_self_modulate);
+ virtual void set_self_modulate(const Color &p_self_modulate);
Color get_self_modulate() const;
void set_visibility_layer(uint32_t p_visibility_layer);
@@ -248,7 +248,7 @@ public:
/* ORDERING */
- void set_z_index(int p_z);
+ virtual void set_z_index(int p_z);
int get_z_index() const;
int get_effective_z_index() const;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index a6b7ca8188..f7d695bf31 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -3088,6 +3088,10 @@ static void _add_nodes_to_options(const Node *p_base, const Node *p_node, List<S
if (p_node != p_base && !p_node->get_owner()) {
return;
}
+ if (p_node->is_unique_name_in_owner() && p_node->get_owner() == p_base) {
+ String n = "%" + p_node->get_name();
+ r_options->push_back(n.quote());
+ }
String n = p_base->get_path_to(p_node);
r_options->push_back(n.quote());
for (int i = 0; i < p_node->get_child_count(); i++) {
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index a99102e847..66e1b7bcf9 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -1563,6 +1563,632 @@ void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform
}
}
+Vector2 TileSet::map_to_local(const Vector2i &p_pos) const {
+ // SHOULD RETURN THE CENTER OF THE CELL.
+ Vector2 ret = p_pos;
+
+ if (tile_shape == TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE || tile_shape == TileSet::TILE_SHAPE_HEXAGON || tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ // Technically, those 3 shapes are equivalent, as they are basically half-offset, but with different levels or overlap.
+ // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap.
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (tile_layout) {
+ case TileSet::TILE_LAYOUT_STACKED:
+ ret = Vector2(ret.x + (Math::posmod(ret.y, 2) == 0 ? 0.0 : 0.5), ret.y);
+ break;
+ case TileSet::TILE_LAYOUT_STACKED_OFFSET:
+ ret = Vector2(ret.x + (Math::posmod(ret.y, 2) == 1 ? 0.0 : 0.5), ret.y);
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
+ ret = Vector2(ret.x + ret.y / 2, ret.y);
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_DOWN:
+ ret = Vector2(ret.x / 2, ret.y * 2 + ret.x);
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
+ ret = Vector2((ret.x + ret.y) / 2, ret.y - ret.x);
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
+ ret = Vector2((ret.x - ret.y) / 2, ret.y + ret.x);
+ break;
+ }
+ } else { // TILE_OFFSET_AXIS_VERTICAL.
+ switch (tile_layout) {
+ case TileSet::TILE_LAYOUT_STACKED:
+ ret = Vector2(ret.x, ret.y + (Math::posmod(ret.x, 2) == 0 ? 0.0 : 0.5));
+ break;
+ case TileSet::TILE_LAYOUT_STACKED_OFFSET:
+ ret = Vector2(ret.x, ret.y + (Math::posmod(ret.x, 2) == 1 ? 0.0 : 0.5));
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
+ ret = Vector2(ret.x * 2 + ret.y, ret.y / 2);
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_DOWN:
+ ret = Vector2(ret.x, ret.y + ret.x / 2);
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
+ ret = Vector2(ret.x + ret.y, (ret.y - ret.x) / 2);
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
+ ret = Vector2(ret.x - ret.y, (ret.y + ret.x) / 2);
+ break;
+ }
+ }
+ }
+
+ // Multiply by the overlapping ratio.
+ double overlapping_ratio = 1.0;
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ overlapping_ratio = 0.5;
+ } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) {
+ overlapping_ratio = 0.75;
+ }
+ ret.y *= overlapping_ratio;
+ } else { // TILE_OFFSET_AXIS_VERTICAL.
+ if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ overlapping_ratio = 0.5;
+ } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) {
+ overlapping_ratio = 0.75;
+ }
+ ret.x *= overlapping_ratio;
+ }
+
+ return (ret + Vector2(0.5, 0.5)) * tile_size;
+}
+
+Vector2i TileSet::local_to_map(const Vector2 &p_local_position) const {
+ Vector2 ret = p_local_position;
+ ret /= tile_size;
+
+ // Divide by the overlapping ratio.
+ double overlapping_ratio = 1.0;
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ overlapping_ratio = 0.5;
+ } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) {
+ overlapping_ratio = 0.75;
+ }
+ ret.y /= overlapping_ratio;
+ } else { // TILE_OFFSET_AXIS_VERTICAL.
+ if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ overlapping_ratio = 0.5;
+ } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) {
+ overlapping_ratio = 0.75;
+ }
+ ret.x /= overlapping_ratio;
+ }
+
+ // For each half-offset shape, we check if we are in the corner of the tile, and thus should correct the local position accordingly.
+ if (tile_shape == TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE || tile_shape == TileSet::TILE_SHAPE_HEXAGON || tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ // Technically, those 3 shapes are equivalent, as they are basically half-offset, but with different levels or overlap.
+ // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap.
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ // Smart floor of the position
+ Vector2 raw_pos = ret;
+ if (Math::posmod(Math::floor(ret.y), 2) ^ (tile_layout == TileSet::TILE_LAYOUT_STACKED_OFFSET)) {
+ ret = Vector2(Math::floor(ret.x + 0.5) - 0.5, Math::floor(ret.y));
+ } else {
+ ret = ret.floor();
+ }
+
+ // Compute the tile offset, and if we might the output for a neighbor top tile.
+ Vector2 in_tile_pos = raw_pos - ret;
+ bool in_top_left_triangle = (in_tile_pos - Vector2(0.5, 0.0)).cross(Vector2(-0.5, 1.0 / overlapping_ratio - 1)) <= 0;
+ bool in_top_right_triangle = (in_tile_pos - Vector2(0.5, 0.0)).cross(Vector2(0.5, 1.0 / overlapping_ratio - 1)) > 0;
+
+ switch (tile_layout) {
+ case TileSet::TILE_LAYOUT_STACKED:
+ ret = ret.floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? 0 : -1, -1);
+ } else if (in_top_right_triangle) {
+ ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? 1 : 0, -1);
+ }
+ break;
+ case TileSet::TILE_LAYOUT_STACKED_OFFSET:
+ ret = ret.floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? -1 : 0, -1);
+ } else if (in_top_right_triangle) {
+ ret += Vector2i(Math::posmod(Math::floor(ret.y), 2) ? 0 : 1, -1);
+ }
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
+ ret = Vector2(ret.x - ret.y / 2, ret.y).floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(0, -1);
+ } else if (in_top_right_triangle) {
+ ret += Vector2i(1, -1);
+ }
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_DOWN:
+ ret = Vector2(ret.x * 2, ret.y / 2 - ret.x).floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(-1, 0);
+ } else if (in_top_right_triangle) {
+ ret += Vector2i(1, -1);
+ }
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
+ ret = Vector2(ret.x - ret.y / 2, ret.y / 2 + ret.x).floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(0, -1);
+ } else if (in_top_right_triangle) {
+ ret += Vector2i(1, 0);
+ }
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
+ ret = Vector2(ret.x + ret.y / 2, ret.y / 2 - ret.x).floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(-1, 0);
+ } else if (in_top_right_triangle) {
+ ret += Vector2i(0, -1);
+ }
+ break;
+ }
+ } else { // TILE_OFFSET_AXIS_VERTICAL.
+ // Smart floor of the position.
+ Vector2 raw_pos = ret;
+ if (Math::posmod(Math::floor(ret.x), 2) ^ (tile_layout == TileSet::TILE_LAYOUT_STACKED_OFFSET)) {
+ ret = Vector2(Math::floor(ret.x), Math::floor(ret.y + 0.5) - 0.5);
+ } else {
+ ret = ret.floor();
+ }
+
+ // Compute the tile offset, and if we might the output for a neighbor top tile.
+ Vector2 in_tile_pos = raw_pos - ret;
+ bool in_top_left_triangle = (in_tile_pos - Vector2(0.0, 0.5)).cross(Vector2(1.0 / overlapping_ratio - 1, -0.5)) > 0;
+ bool in_bottom_left_triangle = (in_tile_pos - Vector2(0.0, 0.5)).cross(Vector2(1.0 / overlapping_ratio - 1, 0.5)) <= 0;
+
+ switch (tile_layout) {
+ case TileSet::TILE_LAYOUT_STACKED:
+ ret = ret.floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? 0 : -1);
+ } else if (in_bottom_left_triangle) {
+ ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? 1 : 0);
+ }
+ break;
+ case TileSet::TILE_LAYOUT_STACKED_OFFSET:
+ ret = ret.floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? -1 : 0);
+ } else if (in_bottom_left_triangle) {
+ ret += Vector2i(-1, Math::posmod(Math::floor(ret.x), 2) ? 0 : 1);
+ }
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
+ ret = Vector2(ret.x / 2 - ret.y, ret.y * 2).floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(0, -1);
+ } else if (in_bottom_left_triangle) {
+ ret += Vector2i(-1, 1);
+ }
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_DOWN:
+ ret = Vector2(ret.x, ret.y - ret.x / 2).floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(-1, 0);
+ } else if (in_bottom_left_triangle) {
+ ret += Vector2i(-1, 1);
+ }
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
+ ret = Vector2(ret.x / 2 - ret.y, ret.y + ret.x / 2).floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(0, -1);
+ } else if (in_bottom_left_triangle) {
+ ret += Vector2i(-1, 0);
+ }
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
+ ret = Vector2(ret.x / 2 + ret.y, ret.y - ret.x / 2).floor();
+ if (in_top_left_triangle) {
+ ret += Vector2i(-1, 0);
+ } else if (in_bottom_left_triangle) {
+ ret += Vector2i(0, 1);
+ }
+ break;
+ }
+ }
+ } else {
+ ret = (ret + Vector2(0.00005, 0.00005)).floor();
+ }
+ return Vector2i(ret);
+}
+
+bool TileSet::is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const {
+ if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
+ return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ } else {
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ return p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ } else {
+ return p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE ||
+ p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ }
+ }
+}
+
+Vector2i TileSet::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const {
+ if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
+ switch (p_cell_neighbor) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ return p_coords + Vector2i(1, 0);
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ return p_coords + Vector2i(1, 1);
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ return p_coords + Vector2i(0, 1);
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ return p_coords + Vector2i(-1, 1);
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ return p_coords + Vector2i(-1, 0);
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ return p_coords + Vector2i(-1, -1);
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ return p_coords + Vector2i(0, -1);
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ return p_coords + Vector2i(1, -1);
+ default:
+ ERR_FAIL_V(p_coords);
+ }
+ } else { // Half-offset shapes (square and hexagon).
+ switch (tile_layout) {
+ case TileSet::TILE_LAYOUT_STACKED: {
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ bool is_offset = p_coords.y % 2;
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
+ return p_coords + Vector2i(1, 0);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(is_offset ? 1 : 0, 1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
+ return p_coords + Vector2i(0, 2);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(is_offset ? 0 : -1, 1);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
+ return p_coords + Vector2i(-1, 0);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(is_offset ? 0 : -1, -1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
+ return p_coords + Vector2i(0, -2);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(is_offset ? 1 : 0, -1);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+ } else {
+ bool is_offset = p_coords.x % 2;
+
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
+ return p_coords + Vector2i(0, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, is_offset ? 1 : 0);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
+ return p_coords + Vector2i(2, 0);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, is_offset ? 0 : -1);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
+ return p_coords + Vector2i(0, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, is_offset ? 0 : -1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
+ return p_coords + Vector2i(-2, 0);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, is_offset ? 1 : 0);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+ }
+ } break;
+ case TileSet::TILE_LAYOUT_STACKED_OFFSET: {
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ bool is_offset = p_coords.y % 2;
+
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
+ return p_coords + Vector2i(1, 0);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(is_offset ? 0 : 1, 1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
+ return p_coords + Vector2i(0, 2);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(is_offset ? -1 : 0, 1);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
+ return p_coords + Vector2i(-1, 0);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(is_offset ? -1 : 0, -1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
+ return p_coords + Vector2i(0, -2);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(is_offset ? 0 : 1, -1);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+ } else {
+ bool is_offset = p_coords.x % 2;
+
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
+ return p_coords + Vector2i(0, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, is_offset ? 0 : 1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
+ return p_coords + Vector2i(2, 0);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, is_offset ? -1 : 0);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
+ return p_coords + Vector2i(0, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, is_offset ? -1 : 0);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
+ return p_coords + Vector2i(-2, 0);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, is_offset ? 0 : 1);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+ }
+ } break;
+ case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
+ case TileSet::TILE_LAYOUT_STAIRS_DOWN: {
+ if ((tile_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
+ return p_coords + Vector2i(1, 0);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(0, 1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
+ return p_coords + Vector2i(-1, 2);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, 1);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
+ return p_coords + Vector2i(-1, 0);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(0, -1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
+ return p_coords + Vector2i(1, -2);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, -1);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+
+ } else {
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
+ return p_coords + Vector2i(0, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, 0);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
+ return p_coords + Vector2i(2, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, -1);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
+ return p_coords + Vector2i(0, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, 0);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
+ return p_coords + Vector2i(-2, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, 1);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+ }
+ } else {
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
+ return p_coords + Vector2i(2, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, 0);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
+ return p_coords + Vector2i(0, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, 1);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
+ return p_coords + Vector2i(-2, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, 0);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
+ return p_coords + Vector2i(0, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, -1);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+
+ } else {
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
+ return p_coords + Vector2i(-1, 2);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(0, 1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
+ return p_coords + Vector2i(1, 0);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, -1);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
+ return p_coords + Vector2i(1, -2);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(0, -1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
+ return p_coords + Vector2i(-1, 0);
+
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, 1);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+ }
+ }
+ } break;
+ case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
+ case TileSet::TILE_LAYOUT_DIAMOND_DOWN: {
+ if ((tile_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
+ return p_coords + Vector2i(1, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(0, 1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
+ return p_coords + Vector2i(-1, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, 0);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
+ return p_coords + Vector2i(-1, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(0, -1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
+ return p_coords + Vector2i(1, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, 0);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+
+ } else {
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
+ return p_coords + Vector2i(1, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, 0);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
+ return p_coords + Vector2i(1, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(0, -1);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
+ return p_coords + Vector2i(-1, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, 0);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
+ return p_coords + Vector2i(-1, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(0, 1);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+ }
+ } else {
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
+ return p_coords + Vector2i(1, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, 0);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) {
+ return p_coords + Vector2i(1, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(0, 1);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
+ return p_coords + Vector2i(-1, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, 0);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
+ return p_coords + Vector2i(-1, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(0, -1);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+
+ } else {
+ if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
+ return p_coords + Vector2i(-1, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) {
+ return p_coords + Vector2i(0, 1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_RIGHT_CORNER) {
+ return p_coords + Vector2i(1, 1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return p_coords + Vector2i(1, 0);
+ } else if ((tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_CORNER) ||
+ (tile_shape != TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
+ return p_coords + Vector2i(1, -1);
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) {
+ return p_coords + Vector2i(0, -1);
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC && p_cell_neighbor == TileSet::CELL_NEIGHBOR_LEFT_CORNER) {
+ return p_coords + Vector2i(-1, -1);
+
+ } else if (p_cell_neighbor == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) {
+ return p_coords + Vector2i(-1, 0);
+ } else {
+ ERR_FAIL_V(p_coords);
+ }
+ }
+ }
+ } break;
+ default:
+ ERR_FAIL_V(p_coords);
+ }
+ }
+}
+
+Vector2i TileSet::map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) {
+ ERR_FAIL_COND_V(p_pattern.is_null(), Vector2i());
+ ERR_FAIL_COND_V(!p_pattern->has_cell(p_coords_in_pattern), Vector2i());
+
+ Vector2i output = p_position_in_tilemap + p_coords_in_pattern;
+ if (tile_shape != TileSet::TILE_SHAPE_SQUARE) {
+ if (tile_layout == TileSet::TILE_LAYOUT_STACKED) {
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) {
+ output.x += 1;
+ } else if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) {
+ output.y += 1;
+ }
+ } else if (tile_layout == TileSet::TILE_LAYOUT_STACKED_OFFSET) {
+ if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) {
+ output.x -= 1;
+ } else if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) {
+ output.y -= 1;
+ }
+ }
+ }
+
+ return output;
+}
+
Vector<Point2> TileSet::get_terrain_polygon(int p_terrain_set) {
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
return _get_square_terrain_polygon(tile_size);
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index a71982cd56..0a6d879047 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -530,6 +530,13 @@ public:
Vector<Vector2> get_tile_shape_polygon();
void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
+ // Used by TileMap/TileMapLayer
+ Vector2 map_to_local(const Vector2i &p_pos) const;
+ Vector2i local_to_map(const Vector2 &p_pos) const;
+ bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const;
+ Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const;
+ Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
+
Vector<Point2> get_terrain_polygon(int p_terrain_set);
Vector<Point2> get_terrain_peering_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit);
void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data);
diff --git a/tests/scene/test_audio_stream_wav.h b/tests/scene/test_audio_stream_wav.h
index e36f049136..ed1697929e 100644
--- a/tests/scene/test_audio_stream_wav.h
+++ b/tests/scene/test_audio_stream_wav.h
@@ -148,7 +148,7 @@ void run_test(String file_name, AudioStreamWAV::Format data_format, bool stereo,
Ref<FileAccess> wav_file = FileAccess::open(save_path, FileAccess::READ, &error);
REQUIRE(error == OK);
-#if TOOLS_ENABLED
+#ifdef TOOLS_ENABLED
// The WAV importer can be used if enabled to check that the saved file is valid.
Ref<ResourceImporterWAV> wav_importer = memnew(ResourceImporterWAV);