summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYRIGHT.txt5
-rw-r--r--README.md2
-rw-r--r--SConstruct1
-rw-r--r--core/config/project_settings.cpp2
-rw-r--r--core/debugger/remote_debugger_peer.cpp2
-rw-r--r--core/io/file_access_network.cpp2
-rw-r--r--core/io/http_client.cpp4
-rw-r--r--core/io/ip.cpp30
-rw-r--r--core/io/ip.h10
-rw-r--r--core/io/ip_address.cpp24
-rw-r--r--core/io/ip_address.h12
-rw-r--r--core/io/net_socket.h16
-rw-r--r--core/io/packed_data_container.cpp5
-rw-r--r--core/io/packet_peer_udp.cpp20
-rw-r--r--core/io/packet_peer_udp.h20
-rw-r--r--core/io/resource_loader.cpp11
-rw-r--r--core/io/stream_peer_tcp.cpp12
-rw-r--r--core/io/stream_peer_tcp.h10
-rw-r--r--core/io/tcp_server.cpp32
-rw-r--r--core/io/tcp_server.h10
-rw-r--r--core/io/udp_server.cpp6
-rw-r--r--core/io/udp_server.h6
-rw-r--r--core/math/color.cpp4
-rw-r--r--core/math/color_names.inc295
-rw-r--r--core/math/geometry_2d.h39
-rw-r--r--core/math/vector2.h8
-rw-r--r--core/object/script_language.h1
-rw-r--r--core/register_core_types.cpp2
-rw-r--r--core/variant/array.cpp73
-rw-r--r--core/variant/array.h3
-rw-r--r--core/variant/method_ptrcall.h4
-rw-r--r--core/variant/type_info.h2
-rw-r--r--core/variant/variant.cpp8
-rw-r--r--core/variant/variant.h4
-rw-r--r--core/variant/variant_call.cpp3
-rw-r--r--doc/classes/Array.xml56
-rw-r--r--doc/classes/AudioStreamPlayer.xml1
-rw-r--r--doc/classes/AudioStreamPlayer2D.xml4
-rw-r--r--doc/classes/AudioStreamPlayer3D.xml4
-rw-r--r--doc/classes/ButtonGroup.xml9
-rw-r--r--doc/classes/Color.xml302
-rw-r--r--doc/classes/DisplayServer.xml60
-rw-r--r--doc/classes/File.xml1
-rw-r--r--doc/classes/IP.xml2
-rw-r--r--doc/classes/OS.xml3
-rw-r--r--doc/classes/ProjectSettings.xml8
-rw-r--r--doc/classes/Shape3D.xml7
-rw-r--r--doc/classes/Skeleton3D.xml9
-rw-r--r--doc/classes/TCPServer.xml (renamed from doc/classes/TCP_Server.xml)2
-rw-r--r--doc/classes/TileData.xml245
-rw-r--r--doc/classes/TileMap.xml278
-rw-r--r--doc/classes/TileSet.xml705
-rw-r--r--doc/classes/TileSetAtlasSource.xml207
-rw-r--r--doc/classes/TileSetSource.xml13
-rw-r--r--drivers/dummy/rasterizer_dummy.h9
-rw-r--r--drivers/unix/ip_unix.cpp28
-rw-r--r--drivers/unix/ip_unix.h8
-rw-r--r--drivers/unix/net_socket_posix.cpp28
-rw-r--r--drivers/unix/net_socket_posix.h24
-rw-r--r--drivers/unix/os_unix.cpp2
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp4
-rw-r--r--drivers/vulkan/vulkan_context.cpp108
-rw-r--r--drivers/vulkan/vulkan_context.h8
-rw-r--r--editor/action_map_editor.cpp46
-rw-r--r--editor/action_map_editor.h10
-rw-r--r--editor/animation_bezier_editor.cpp10
-rw-r--r--editor/animation_track_editor.cpp28
-rw-r--r--editor/animation_track_editor_plugins.cpp2
-rw-r--r--editor/create_dialog.cpp1
-rw-r--r--editor/debugger/editor_debugger_server.cpp2
-rw-r--r--editor/editor_log.cpp69
-rw-r--r--editor/editor_log.h18
-rw-r--r--editor/editor_node.cpp83
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_properties.cpp2
-rw-r--r--editor/editor_properties_array_dict.cpp2
-rw-r--r--editor/editor_run.cpp45
-rw-r--r--editor/editor_settings.cpp19
-rw-r--r--editor/editor_themes.cpp342
-rw-r--r--editor/editor_zoom_widget.cpp163
-rw-r--r--editor/editor_zoom_widget.h (renamed from platform/iphone/native_video_view.h)39
-rw-r--r--editor/fileserver/editor_file_server.h2
-rw-r--r--editor/filesystem_dock.cpp2
-rw-r--r--editor/icons/AddAtlasTile.svg1
-rw-r--r--editor/icons/AddAutotile.svg1
-rw-r--r--editor/icons/AddSingleTile.svg1
-rw-r--r--editor/icons/CollapseTree.svg1
-rw-r--r--editor/icons/EditAddRemove.svg1
-rw-r--r--editor/icons/EditorHandleDisabled.svg1
-rw-r--r--editor/icons/Eraser.svg1
-rw-r--r--editor/icons/ExpandTree.svg1
-rw-r--r--editor/icons/FontSize.svg2
-rw-r--r--editor/icons/RectangleAddRemove.svg1
-rw-r--r--editor/icons/TerrainMatchCorners.svg1
-rw-r--r--editor/icons/TerrainMatchCornersAndSides.svg1
-rw-r--r--editor/icons/TerrainMatchSides.svg1
-rw-r--r--editor/icons/ThemeDeselectAll.svg1
-rw-r--r--editor/icons/ThemeRemoveAllItems.svg2
-rw-r--r--editor/icons/ThemeRemoveCustomItems.svg2
-rw-r--r--editor/icons/ThemeSelectAll.svg1
-rw-r--r--editor/icons/ThemeSelectFull.svg1
-rw-r--r--editor/icons/WarningPattern.svg1
-rw-r--r--editor/inspector_dock.cpp2
-rw-r--r--editor/plugins/SCsub2
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp22
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp1
-rw-r--r--editor/plugins/audio_stream_editor_plugin.cpp17
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp136
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h13
-rw-r--r--editor/plugins/editor_preview_plugins.cpp20
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp7
-rw-r--r--editor/plugins/root_motion_editor_plugin.cpp3
-rw-r--r--editor/plugins/script_editor_plugin.cpp124
-rw-r--r--editor/plugins/script_editor_plugin.h1
-rw-r--r--editor/plugins/shader_editor_plugin.cpp7
-rw-r--r--editor/plugins/theme_editor_plugin.cpp1698
-rw-r--r--editor/plugins/theme_editor_plugin.h172
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp2335
-rw-r--r--editor/plugins/tile_map_editor_plugin.h242
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp3680
-rw-r--r--editor/plugins/tile_set_editor_plugin.h298
-rw-r--r--editor/plugins/tiles/SCsub5
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp649
-rw-r--r--editor/plugins/tiles/tile_atlas_view.h153
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp218
-rw-r--r--editor/plugins/tiles/tile_data_editors.h110
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp3368
-rw-r--r--editor/plugins/tiles/tile_map_editor.h328
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp1858
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h260
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp520
-rw-r--r--editor/plugins/tiles/tile_set_editor.h94
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp276
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h114
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp41
-rw-r--r--editor/project_settings_editor.cpp2
-rw-r--r--editor/property_editor.cpp1
-rw-r--r--editor/scene_tree_dock.cpp8
-rw-r--r--editor/scene_tree_editor.cpp4
-rw-r--r--editor/script_create_dialog.cpp2
-rw-r--r--editor/settings_config_dialog.cpp21
-rw-r--r--icon_outlined.pngbin0 -> 9282 bytes
-rw-r--r--icon_outlined.svg159
-rw-r--r--logo_outlined.pngbin0 -> 17898 bytes
-rw-r--r--logo_outlined.svg185
-rw-r--r--main/main.cpp2
-rw-r--r--misc/dist/html/fixed-size.html389
-rw-r--r--modules/SCsub2
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp16
-rw-r--r--modules/enet/networked_multiplayer_enet.h6
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp4
-rw-r--r--modules/gdnative/nativescript/nativescript.h1
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.cpp4
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.h1
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp7
-rw-r--r--modules/gdscript/gdscript.cpp13
-rw-r--r--modules/gdscript/gdscript.h1
-rw-r--r--modules/gdscript/gdscript_editor.cpp1
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp4
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp88
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp2
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h4
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp2
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp11
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h1
-rw-r--r--modules/gdscript/language_server/lsp.hpp114
-rw-r--r--modules/mbedtls/packet_peer_mbed_dtls.cpp2
-rw-r--r--modules/mono/csharp_script.cpp20
-rw-r--r--modules/mono/csharp_script.h1
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs586
-rw-r--r--modules/raycast/SCsub169
-rw-r--r--modules/raycast/lightmap_raycaster.cpp29
-rw-r--r--modules/visual_script/visual_script.cpp4
-rw-r--r--modules/visual_script/visual_script.h1
-rw-r--r--modules/visual_script/visual_script_editor.cpp56
-rw-r--r--modules/visual_script/visual_script_editor.h1
-rw-r--r--modules/websocket/emws_client.cpp4
-rw-r--r--modules/websocket/emws_client.h2
-rw-r--r--modules/websocket/emws_peer.cpp4
-rw-r--r--modules/websocket/emws_peer.h2
-rw-r--r--modules/websocket/emws_server.cpp4
-rw-r--r--modules/websocket/emws_server.h2
-rw-r--r--modules/websocket/websocket_client.h2
-rw-r--r--modules/websocket/websocket_peer.h2
-rw-r--r--modules/websocket/websocket_server.cpp6
-rw-r--r--modules/websocket/websocket_server.h8
-rw-r--r--modules/websocket/wsl_client.cpp6
-rw-r--r--modules/websocket/wsl_client.h2
-rw-r--r--modules/websocket/wsl_peer.cpp4
-rw-r--r--modules/websocket/wsl_peer.h2
-rw-r--r--modules/websocket/wsl_server.cpp4
-rw-r--r--modules/websocket/wsl_server.h4
-rw-r--r--platform/android/display_server_android.cpp1
-rw-r--r--platform/android/net_socket_android.cpp4
-rw-r--r--platform/android/net_socket_android.h4
-rw-r--r--platform/iphone/SCsub1
-rw-r--r--platform/iphone/display_server_iphone.h6
-rw-r--r--platform/iphone/display_server_iphone.mm65
-rw-r--r--platform/iphone/native_video_view.m266
-rw-r--r--platform/iphone/os_iphone.mm8
-rw-r--r--platform/iphone/view_controller.h5
-rw-r--r--platform/iphone/view_controller.mm24
-rw-r--r--platform/javascript/display_server_javascript.cpp1
-rw-r--r--platform/javascript/export/export.cpp6
-rw-r--r--platform/javascript/http_client.h.inc3
-rw-r--r--platform/javascript/javascript_main.cpp5
-rw-r--r--platform/linuxbsd/detect.py4
-rw-r--r--platform/linuxbsd/display_server_x11.cpp3
-rw-r--r--platform/uwp/os_uwp.cpp2
-rw-r--r--platform/windows/os_windows.cpp2
-rw-r--r--scene/2d/tile_map.cpp2656
-rw-r--r--scene/2d/tile_map.h413
-rw-r--r--scene/3d/skeleton_3d.cpp87
-rw-r--r--scene/3d/skeleton_3d.h2
-rw-r--r--scene/3d/skeleton_ik_3d.cpp69
-rw-r--r--scene/animation/animation_tree.cpp4
-rw-r--r--scene/gui/base_button.cpp7
-rw-r--r--scene/gui/color_picker.cpp53
-rw-r--r--scene/gui/color_picker.h7
-rw-r--r--scene/gui/item_list.cpp3
-rw-r--r--scene/gui/line_edit.cpp2
-rw-r--r--scene/gui/rich_text_label.cpp1
-rw-r--r--scene/gui/tab_container.cpp1
-rw-r--r--scene/gui/text_edit.cpp2
-rw-r--r--scene/gui/tree.cpp21
-rw-r--r--scene/gui/tree.h1
-rw-r--r--scene/main/canvas_item.cpp2
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/register_scene_types.cpp3
-rw-r--r--scene/resources/material.cpp2
-rw-r--r--scene/resources/shape_3d.cpp2
-rw-r--r--scene/resources/surface_tool.cpp8
-rw-r--r--scene/resources/tile_set.cpp4797
-rw-r--r--scene/resources/tile_set.h686
-rw-r--r--servers/display_server.cpp28
-rw-r--r--servers/display_server.h8
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp35
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h3
-rw-r--r--servers/rendering/rendering_device.h4
-rw-r--r--servers/rendering/shader_language.cpp14
-rw-r--r--servers/rendering/shader_language.h1
-rw-r--r--tests/test_string.h8
-rw-r--r--thirdparty/enet/godot.cpp52
244 files changed, 18863 insertions, 13020 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 9e63da3fc4..cfd983bb0a 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -131,6 +131,11 @@ Comment: doctest
Copyright: 2016-2020, Viktor Kirilov
License: Expat
+Files: ./thirdparty/embree-aarch64/
+Comment: Embree-aarch64
+Copyright: 2009-2021 Intel Corporation
+License: Apache-2.0
+
Files: ./thirdparty/enet/
Comment: ENet
Copyright: 2002-2020, Lee Salzman
diff --git a/README.md b/README.md
index 72a85492fd..8ddddf0e63 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
<p align="center">
<a href="https://godotengine.org">
- <img src="logo.svg" width="400" alt="Godot Engine logo">
+ <img src="logo_outlined.svg" width="400" alt="Godot Engine logo">
</a>
</p>
diff --git a/SConstruct b/SConstruct
index 2d9802f293..6516b42e59 100644
--- a/SConstruct
+++ b/SConstruct
@@ -145,6 +145,7 @@ opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise e
# Thirdparty libraries
opts.Add(BoolVariable("builtin_bullet", "Use the built-in Bullet library", True))
opts.Add(BoolVariable("builtin_certs", "Use the built-in SSL certificates bundles", True))
+opts.Add(BoolVariable("builtin_embree", "Use the built-in Embree library", True))
opts.Add(BoolVariable("builtin_enet", "Use the built-in ENet library", True))
opts.Add(BoolVariable("builtin_freetype", "Use the built-in FreeType library", True))
opts.Add(BoolVariable("builtin_glslang", "Use the built-in glslang library", True))
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 25dd408dce..0d699cdacb 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1104,6 +1104,8 @@ ProjectSettings::ProjectSettings() {
}
extensions.push_back("shader");
+ GLOBAL_DEF("editor/run/main_run_args", "");
+
GLOBAL_DEF("editor/script/search_in_file_extensions", extensions);
custom_prop_info["editor/script/search_in_file_extensions"] = PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions");
diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp
index 90b0975159..39113eda14 100644
--- a/core/debugger/remote_debugger_peer.cpp
+++ b/core/debugger/remote_debugger_peer.cpp
@@ -152,7 +152,7 @@ void RemoteDebuggerPeerTCP::_read_in() {
}
Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_port) {
- IP_Address ip;
+ IPAddress ip;
if (p_host.is_valid_ip_address()) {
ip = p_host;
} else {
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
index 31b7d658d0..4cc73bcd22 100644
--- a/core/io/file_access_network.cpp
+++ b/core/io/file_access_network.cpp
@@ -171,7 +171,7 @@ void FileAccessNetworkClient::_thread_func(void *s) {
}
Error FileAccessNetworkClient::connect(const String &p_host, int p_port, const String &p_password) {
- IP_Address ip;
+ IPAddress ip;
if (p_host.is_valid_ip_address()) {
ip = p_host;
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 4b053d576c..0cf870e7e7 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -77,7 +77,7 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl,
if (conn_host.is_valid_ip_address()) {
// Host contains valid IP
- Error err = tcp_connection->connect_to_host(IP_Address(conn_host), p_port);
+ Error err = tcp_connection->connect_to_host(IPAddress(conn_host), p_port);
if (err) {
status = STATUS_CANT_CONNECT;
return err;
@@ -328,7 +328,7 @@ Error HTTPClient::poll() {
return OK; // Still resolving
case IP::RESOLVER_STATUS_DONE: {
- IP_Address host = IP::get_singleton()->get_resolve_item_address(resolving);
+ IPAddress host = IP::get_singleton()->get_resolve_item_address(resolving);
Error err = tcp_connection->connect_to_host(host, conn_port);
IP::get_singleton()->erase_resolve_item(resolving);
resolving = IP::RESOLVER_INVALID_ID;
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index e1d9c19f10..eb7814054b 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -41,13 +41,13 @@ VARIANT_ENUM_CAST(IP::ResolverStatus);
struct _IP_ResolverPrivate {
struct QueueItem {
SafeNumeric<IP::ResolverStatus> status;
- IP_Address response;
+ IPAddress response;
String hostname;
IP::Type type;
void clear() {
status.set(IP::RESOLVER_STATUS_NONE);
- response = IP_Address();
+ response = IPAddress();
type = IP::TYPE_NONE;
hostname = "";
};
@@ -101,23 +101,23 @@ struct _IP_ResolverPrivate {
}
}
- HashMap<String, IP_Address> cache;
+ HashMap<String, IPAddress> cache;
static String get_cache_key(String p_hostname, IP::Type p_type) {
return itos(p_type) + p_hostname;
}
};
-IP_Address IP::resolve_hostname(const String &p_hostname, IP::Type p_type) {
+IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) {
MutexLock lock(resolver->mutex);
String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type);
if (resolver->cache.has(key) && resolver->cache[key].is_valid()) {
- IP_Address res = resolver->cache[key];
+ IPAddress res = resolver->cache[key];
return res;
}
- IP_Address res = _resolve_hostname(p_hostname, p_type);
+ IPAddress res = _resolve_hostname(p_hostname, p_type);
resolver->cache[key] = res;
return res;
}
@@ -139,7 +139,7 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ
resolver->queue[id].response = resolver->cache[key];
resolver->queue[id].status.set(IP::RESOLVER_STATUS_DONE);
} else {
- resolver->queue[id].response = IP_Address();
+ resolver->queue[id].response = IPAddress();
resolver->queue[id].status.set(IP::RESOLVER_STATUS_WAITING);
if (resolver->thread.is_started()) {
resolver->sem.post();
@@ -164,15 +164,15 @@ IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const {
return resolver->queue[p_id].status.get();
}
-IP_Address IP::get_resolve_item_address(ResolverID p_id) const {
- ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IP_Address());
+IPAddress IP::get_resolve_item_address(ResolverID p_id) const {
+ ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IPAddress());
MutexLock lock(resolver->mutex);
if (resolver->queue[p_id].status.get() != IP::RESOLVER_STATUS_DONE) {
ERR_PRINT("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet.");
resolver->mutex.unlock();
- return IP_Address();
+ return IPAddress();
}
return resolver->queue[p_id].response;
@@ -201,9 +201,9 @@ void IP::clear_cache(const String &p_hostname) {
Array IP::_get_local_addresses() const {
Array addresses;
- List<IP_Address> ip_addresses;
+ List<IPAddress> ip_addresses;
get_local_addresses(&ip_addresses);
- for (List<IP_Address>::Element *E = ip_addresses.front(); E; E = E->next()) {
+ for (List<IPAddress>::Element *E = ip_addresses.front(); E; E = E->next()) {
addresses.push_back(E->get());
}
@@ -222,7 +222,7 @@ Array IP::_get_local_interfaces() const {
rc["index"] = c.index;
Array ips;
- for (const List<IP_Address>::Element *F = c.ip_addresses.front(); F; F = F->next()) {
+ for (const List<IPAddress>::Element *F = c.ip_addresses.front(); F; F = F->next()) {
ips.push_front(F->get());
}
rc["addresses"] = ips;
@@ -233,11 +233,11 @@ Array IP::_get_local_interfaces() const {
return results;
}
-void IP::get_local_addresses(List<IP_Address> *r_addresses) const {
+void IP::get_local_addresses(List<IPAddress> *r_addresses) const {
Map<String, Interface_Info> interfaces;
get_local_interfaces(&interfaces);
for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) {
- for (const List<IP_Address>::Element *F = E->get().ip_addresses.front(); F; F = F->next()) {
+ for (const List<IPAddress>::Element *F = E->get().ip_addresses.front(); F; F = F->next()) {
r_addresses->push_front(F->get());
}
}
diff --git a/core/io/ip.h b/core/io/ip.h
index ae080b8e26..0c4a83257d 100644
--- a/core/io/ip.h
+++ b/core/io/ip.h
@@ -69,7 +69,7 @@ protected:
static IP *singleton;
static void _bind_methods();
- virtual IP_Address _resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY) = 0;
+ virtual IPAddress _resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY) = 0;
Array _get_local_addresses() const;
Array _get_local_interfaces() const;
@@ -80,15 +80,15 @@ public:
String name;
String name_friendly;
String index;
- List<IP_Address> ip_addresses;
+ List<IPAddress> ip_addresses;
};
- IP_Address resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY);
+ IPAddress resolve_hostname(const String &p_hostname, Type p_type = TYPE_ANY);
// async resolver hostname
ResolverID resolve_hostname_queue_item(const String &p_hostname, Type p_type = TYPE_ANY);
ResolverStatus get_resolve_item_status(ResolverID p_id) const;
- IP_Address get_resolve_item_address(ResolverID p_id) const;
- virtual void get_local_addresses(List<IP_Address> *r_addresses) const;
+ IPAddress get_resolve_item_address(ResolverID p_id) const;
+ virtual void get_local_addresses(List<IPAddress> *r_addresses) const;
virtual void get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const = 0;
void erase_resolve_item(ResolverID p_id);
diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp
index 5f98eb69e8..1c1ac8a88f 100644
--- a/core/io/ip_address.cpp
+++ b/core/io/ip_address.cpp
@@ -30,14 +30,14 @@
#include "ip_address.h"
/*
-IP_Address::operator Variant() const {
+IPAddress::operator Variant() const {
return operator String();
}*/
#include <stdio.h>
#include <string.h>
-IP_Address::operator String() const {
+IPAddress::operator String() const {
if (wildcard) {
return "*";
}
@@ -90,7 +90,7 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) {
p_dst[1] = ret & 0xff;
}
-void IP_Address::_parse_ipv6(const String &p_string) {
+void IPAddress::_parse_ipv6(const String &p_string) {
static const int parts_total = 8;
int parts[parts_total] = { 0 };
int parts_count = 0;
@@ -146,7 +146,7 @@ void IP_Address::_parse_ipv6(const String &p_string) {
}
}
-void IP_Address::_parse_ipv4(const String &p_string, int p_start, uint8_t *p_ret) {
+void IPAddress::_parse_ipv4(const String &p_string, int p_start, uint8_t *p_ret) {
String ip;
if (p_start != 0) {
ip = p_string.substr(p_start, p_string.length() - p_start);
@@ -161,33 +161,33 @@ void IP_Address::_parse_ipv4(const String &p_string, int p_start, uint8_t *p_ret
}
}
-void IP_Address::clear() {
+void IPAddress::clear() {
memset(&field8[0], 0, sizeof(field8));
valid = false;
wildcard = false;
}
-bool IP_Address::is_ipv4() const {
+bool IPAddress::is_ipv4() const {
return (field32[0] == 0 && field32[1] == 0 && field16[4] == 0 && field16[5] == 0xffff);
}
-const uint8_t *IP_Address::get_ipv4() const {
+const uint8_t *IPAddress::get_ipv4() const {
ERR_FAIL_COND_V_MSG(!is_ipv4(), &(field8[12]), "IPv4 requested, but current IP is IPv6."); // Not the correct IPv4 (it's an IPv6), but we don't want to return a null pointer risking an engine crash.
return &(field8[12]);
}
-void IP_Address::set_ipv4(const uint8_t *p_ip) {
+void IPAddress::set_ipv4(const uint8_t *p_ip) {
clear();
valid = true;
field16[5] = 0xffff;
field32[3] = *((const uint32_t *)p_ip);
}
-const uint8_t *IP_Address::get_ipv6() const {
+const uint8_t *IPAddress::get_ipv6() const {
return field8;
}
-void IP_Address::set_ipv6(const uint8_t *p_buf) {
+void IPAddress::set_ipv6(const uint8_t *p_buf) {
clear();
valid = true;
for (int i = 0; i < 16; i++) {
@@ -195,7 +195,7 @@ void IP_Address::set_ipv6(const uint8_t *p_buf) {
}
}
-IP_Address::IP_Address(const String &p_string) {
+IPAddress::IPAddress(const String &p_string) {
clear();
if (p_string == "*") {
@@ -225,7 +225,7 @@ _FORCE_INLINE_ static void _32_to_buf(uint8_t *p_dst, uint32_t p_n) {
p_dst[3] = (p_n >> 0) & 0xff;
}
-IP_Address::IP_Address(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d, bool is_v6) {
+IPAddress::IPAddress(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d, bool is_v6) {
clear();
valid = true;
if (!is_v6) {
diff --git a/core/io/ip_address.h b/core/io/ip_address.h
index 49bf83d72f..05da675704 100644
--- a/core/io/ip_address.h
+++ b/core/io/ip_address.h
@@ -33,7 +33,7 @@
#include "core/string/ustring.h"
-struct IP_Address {
+struct IPAddress {
private:
union {
uint8_t field8[16];
@@ -50,7 +50,7 @@ protected:
public:
//operator Variant() const;
- bool operator==(const IP_Address &p_ip) const {
+ bool operator==(const IPAddress &p_ip) const {
if (p_ip.valid != valid) {
return false;
}
@@ -65,7 +65,7 @@ public:
return true;
}
- bool operator!=(const IP_Address &p_ip) const {
+ bool operator!=(const IPAddress &p_ip) const {
if (p_ip.valid != valid) {
return true;
}
@@ -91,9 +91,9 @@ public:
void set_ipv6(const uint8_t *p_buf);
operator String() const;
- IP_Address(const String &p_string);
- IP_Address(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d, bool is_v6 = false);
- IP_Address() { clear(); }
+ IPAddress(const String &p_string);
+ IPAddress(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d, bool is_v6 = false);
+ IPAddress() { clear(); }
};
#endif // IP_ADDRESS_H
diff --git a/core/io/net_socket.h b/core/io/net_socket.h
index a632ad2ea7..98ff9562d9 100644
--- a/core/io/net_socket.h
+++ b/core/io/net_socket.h
@@ -55,27 +55,27 @@ public:
virtual Error open(Type p_type, IP::Type &ip_type) = 0;
virtual void close() = 0;
- virtual Error bind(IP_Address p_addr, uint16_t p_port) = 0;
+ virtual Error bind(IPAddress p_addr, uint16_t p_port) = 0;
virtual Error listen(int p_max_pending) = 0;
- virtual Error connect_to_host(IP_Address p_addr, uint16_t p_port) = 0;
+ virtual Error connect_to_host(IPAddress p_addr, uint16_t p_port) = 0;
virtual Error poll(PollType p_type, int timeout) const = 0;
virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) = 0;
- virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port, bool p_peek = false) = 0;
+ virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) = 0;
virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) = 0;
- virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) = 0;
- virtual Ref<NetSocket> accept(IP_Address &r_ip, uint16_t &r_port) = 0;
+ virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) = 0;
+ virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) = 0;
virtual bool is_open() const = 0;
virtual int get_available_bytes() const = 0;
- virtual Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) const = 0;
+ virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const = 0;
virtual Error set_broadcasting_enabled(bool p_enabled) = 0; // Returns OK if the socket option has been set successfully.
virtual void set_blocking_enabled(bool p_enabled) = 0;
virtual void set_ipv6_only_enabled(bool p_enabled) = 0;
virtual void set_tcp_no_delay_enabled(bool p_enabled) = 0;
virtual void set_reuse_address_enabled(bool p_enabled) = 0;
- virtual Error join_multicast_group(const IP_Address &p_multi_address, String p_if_name) = 0;
- virtual Error leave_multicast_group(const IP_Address &p_multi_address, String p_if_name) = 0;
+ virtual Error join_multicast_group(const IPAddress &p_multi_address, String p_if_name) = 0;
+ virtual Error leave_multicast_group(const IPAddress &p_multi_address, String p_if_name) = 0;
};
#endif // NET_SOCKET_H
diff --git a/core/io/packed_data_container.cpp b/core/io/packed_data_container.cpp
index c6354b11b7..52169987fd 100644
--- a/core/io/packed_data_container.cpp
+++ b/core/io/packed_data_container.cpp
@@ -123,6 +123,7 @@ Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, b
uint32_t PackedDataContainer::_type_at_ofs(uint32_t p_ofs) const {
const uint8_t *rd = data.ptr();
+ ERR_FAIL_COND_V(!rd, 0);
const uint8_t *r = &rd[p_ofs];
uint32_t type = decode_uint32(r);
@@ -149,6 +150,10 @@ int PackedDataContainer::_size(uint32_t p_ofs) const {
Variant PackedDataContainer::_key_at_ofs(uint32_t p_ofs, const Variant &p_key, bool &err) const {
const uint8_t *rd = data.ptr();
+ if (!rd) {
+ err = true;
+ ERR_FAIL_COND_V(!rd, Variant());
+ }
const uint8_t *r = &rd[p_ofs];
uint32_t type = decode_uint32(r);
diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp
index 40e4ce4f77..f951a5158c 100644
--- a/core/io/packet_peer_udp.cpp
+++ b/core/io/packet_peer_udp.cpp
@@ -45,7 +45,7 @@ void PacketPeerUDP::set_broadcast_enabled(bool p_enabled) {
}
}
-Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_if_name) {
+Error PacketPeerUDP::join_multicast_group(IPAddress p_multi_address, String p_if_name) {
ERR_FAIL_COND_V(udp_server, ERR_LOCKED);
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(!p_multi_address.is_valid(), ERR_INVALID_PARAMETER);
@@ -60,7 +60,7 @@ Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_i
return _sock->join_multicast_group(p_multi_address, p_if_name);
}
-Error PacketPeerUDP::leave_multicast_group(IP_Address p_multi_address, String p_if_name) {
+Error PacketPeerUDP::leave_multicast_group(IPAddress p_multi_address, String p_if_name) {
ERR_FAIL_COND_V(udp_server, ERR_LOCKED);
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(!_sock->is_open(), ERR_UNCONFIGURED);
@@ -72,7 +72,7 @@ String PacketPeerUDP::_get_packet_ip() const {
}
Error PacketPeerUDP::_set_dest_address(const String &p_address, int p_port) {
- IP_Address ip;
+ IPAddress ip;
if (p_address.is_valid_ip_address()) {
ip = p_address;
} else {
@@ -159,7 +159,7 @@ int PacketPeerUDP::get_max_packet_size() const {
return 512; // uhm maybe not
}
-Error PacketPeerUDP::bind(int p_port, const IP_Address &p_bind_address, int p_recv_buffer_size) {
+Error PacketPeerUDP::bind(int p_port, const IPAddress &p_bind_address, int p_recv_buffer_size) {
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER);
@@ -190,7 +190,7 @@ Error PacketPeerUDP::bind(int p_port, const IP_Address &p_bind_address, int p_re
return OK;
}
-Error PacketPeerUDP::connect_shared_socket(Ref<NetSocket> p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *p_server) {
+Error PacketPeerUDP::connect_shared_socket(Ref<NetSocket> p_sock, IPAddress p_ip, uint16_t p_port, UDPServer *p_server) {
udp_server = p_server;
connected = true;
_sock = p_sock;
@@ -207,7 +207,7 @@ void PacketPeerUDP::disconnect_shared_socket() {
close();
}
-Error PacketPeerUDP::connect_to_host(const IP_Address &p_host, int p_port) {
+Error PacketPeerUDP::connect_to_host(const IPAddress &p_host, int p_port) {
ERR_FAIL_COND_V(udp_server, ERR_LOCKED);
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER);
@@ -276,7 +276,7 @@ Error PacketPeerUDP::_poll() {
Error err;
int read;
- IP_Address ip;
+ IPAddress ip;
uint16_t port;
while (true) {
@@ -306,7 +306,7 @@ Error PacketPeerUDP::_poll() {
return OK;
}
-Error PacketPeerUDP::store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size) {
+Error PacketPeerUDP::store_packet(IPAddress p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size) {
if (rb.space_left() < p_buf_size + 24) {
return ERR_OUT_OF_MEMORY;
}
@@ -322,7 +322,7 @@ bool PacketPeerUDP::is_bound() const {
return _sock.is_valid() && _sock->is_open();
}
-IP_Address PacketPeerUDP::get_packet_address() const {
+IPAddress PacketPeerUDP::get_packet_address() const {
return packet_ip;
}
@@ -336,7 +336,7 @@ int PacketPeerUDP::get_local_port() const {
return local_port;
}
-void PacketPeerUDP::set_dest_address(const IP_Address &p_address, int p_port) {
+void PacketPeerUDP::set_dest_address(const IPAddress &p_address, int p_port) {
ERR_FAIL_COND_MSG(connected, "Destination address cannot be set for connected sockets");
peer_addr = p_address;
peer_port = p_port;
diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h
index b9d11c465c..40d3c44e40 100644
--- a/core/io/packet_peer_udp.h
+++ b/core/io/packet_peer_udp.h
@@ -48,11 +48,11 @@ protected:
RingBuffer<uint8_t> rb;
uint8_t recv_buffer[PACKET_BUFFER_SIZE];
uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- IP_Address packet_ip;
+ IPAddress packet_ip;
int packet_port = 0;
int queue_count = 0;
- IP_Address peer_addr;
+ IPAddress peer_addr;
int peer_port = 0;
bool connected = false;
bool blocking = true;
@@ -70,29 +70,29 @@ protected:
public:
void set_blocking_mode(bool p_enable);
- Error bind(int p_port, const IP_Address &p_bind_address = IP_Address("*"), int p_recv_buffer_size = 65536);
+ Error bind(int p_port, const IPAddress &p_bind_address = IPAddress("*"), int p_recv_buffer_size = 65536);
void close();
Error wait();
bool is_bound() const;
- Error connect_shared_socket(Ref<NetSocket> p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *ref); // Used by UDPServer
+ Error connect_shared_socket(Ref<NetSocket> p_sock, IPAddress p_ip, uint16_t p_port, UDPServer *ref); // Used by UDPServer
void disconnect_shared_socket(); // Used by UDPServer
- Error store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size); // Used internally and by UDPServer
- Error connect_to_host(const IP_Address &p_host, int p_port);
+ Error store_packet(IPAddress p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size); // Used internally and by UDPServer
+ Error connect_to_host(const IPAddress &p_host, int p_port);
bool is_connected_to_host() const;
- IP_Address get_packet_address() const;
+ IPAddress get_packet_address() const;
int get_packet_port() const;
int get_local_port() const;
- void set_dest_address(const IP_Address &p_address, int p_port);
+ void set_dest_address(const IPAddress &p_address, int p_port);
Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
int get_available_packet_count() const override;
int get_max_packet_size() const override;
void set_broadcast_enabled(bool p_enabled);
- Error join_multicast_group(IP_Address p_multi_address, String p_if_name);
- Error leave_multicast_group(IP_Address p_multi_address, String p_if_name);
+ Error join_multicast_group(IPAddress p_multi_address, String p_if_name);
+ Error leave_multicast_group(IPAddress p_multi_address, String p_if_name);
PacketPeerUDP();
~PacketPeerUDP();
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index dcf71bb4a9..040e55b9db 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -114,25 +114,24 @@ void ResourceFormatLoader::get_recognized_extensions(List<String> *p_extensions)
}
RES ResourceFormatLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ // Check user-defined loader if there's any. Hard fail if it returns an error.
if (get_script_instance() && get_script_instance()->has_method("load")) {
Variant res = get_script_instance()->call("load", p_path, p_original_path, p_use_sub_threads, p_cache_mode);
- if (res.get_type() == Variant::INT) {
+ if (res.get_type() == Variant::INT) { // Error code, abort.
if (r_error) {
*r_error = (Error)res.operator int64_t();
}
-
- } else {
+ return RES();
+ } else { // Success, pass on result.
if (r_error) {
*r_error = OK;
}
return res;
}
-
- return res;
}
- ERR_FAIL_V_MSG(RES(), "Failed to load resource '" + p_path + "', ResourceFormatLoader::load was not implemented for this resource type.");
+ ERR_FAIL_V_MSG(RES(), "Failed to load resource '" + p_path + "'. ResourceFormatLoader::load was not implemented for this resource type.");
}
void ResourceFormatLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp
index 9906b9e4c3..5b794274ca 100644
--- a/core/io/stream_peer_tcp.cpp
+++ b/core/io/stream_peer_tcp.cpp
@@ -56,7 +56,7 @@ Error StreamPeerTCP::_poll_connection() {
return ERR_CONNECTION_ERROR;
}
-void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint16_t p_port) {
+void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IPAddress p_host, uint16_t p_port) {
_sock = p_sock;
_sock->set_blocking_enabled(false);
@@ -67,7 +67,7 @@ void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint
peer_port = p_port;
}
-Error StreamPeerTCP::bind(int p_port, const IP_Address &p_host) {
+Error StreamPeerTCP::bind(int p_port, const IPAddress &p_host) {
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");
@@ -84,7 +84,7 @@ Error StreamPeerTCP::bind(int p_port, const IP_Address &p_host) {
return _sock->bind(p_host, p_port);
}
-Error StreamPeerTCP::connect_to_host(const IP_Address &p_host, int p_port) {
+Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(status != STATUS_NONE, ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER);
@@ -283,7 +283,7 @@ void StreamPeerTCP::disconnect_from_host() {
timeout = 0;
status = STATUS_NONE;
- peer_host = IP_Address();
+ peer_host = IPAddress();
peer_port = 0;
}
@@ -315,7 +315,7 @@ int StreamPeerTCP::get_available_bytes() const {
return _sock->get_available_bytes();
}
-IP_Address StreamPeerTCP::get_connected_host() const {
+IPAddress StreamPeerTCP::get_connected_host() const {
return peer_host;
}
@@ -330,7 +330,7 @@ int StreamPeerTCP::get_local_port() const {
}
Error StreamPeerTCP::_connect(const String &p_address, int p_port) {
- IP_Address ip;
+ IPAddress ip;
if (p_address.is_valid_ip_address()) {
ip = p_address;
} else {
diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h
index 3bc7b252dc..a2a7f447d8 100644
--- a/core/io/stream_peer_tcp.h
+++ b/core/io/stream_peer_tcp.h
@@ -52,7 +52,7 @@ protected:
Ref<NetSocket> _sock;
uint64_t timeout = 0;
Status status = STATUS_NONE;
- IP_Address peer_host;
+ IPAddress peer_host;
uint16_t peer_port = 0;
Error _connect(const String &p_address, int p_port);
@@ -63,12 +63,12 @@ protected:
static void _bind_methods();
public:
- void accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint16_t p_port);
+ void accept_socket(Ref<NetSocket> p_sock, IPAddress p_host, uint16_t p_port);
- Error bind(int p_port, const IP_Address &p_host);
- Error connect_to_host(const IP_Address &p_host, int p_port);
+ Error bind(int p_port, const IPAddress &p_host);
+ Error connect_to_host(const IPAddress &p_host, int p_port);
bool is_connected_to_host() const;
- IP_Address get_connected_host() const;
+ IPAddress get_connected_host() const;
int get_connected_port() const;
int get_local_port() const;
void disconnect_from_host();
diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp
index 348be66ba4..b760a9ef80 100644
--- a/core/io/tcp_server.cpp
+++ b/core/io/tcp_server.cpp
@@ -30,16 +30,16 @@
#include "tcp_server.h"
-void TCP_Server::_bind_methods() {
- ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCP_Server::listen, DEFVAL("*"));
- ClassDB::bind_method(D_METHOD("is_connection_available"), &TCP_Server::is_connection_available);
- ClassDB::bind_method(D_METHOD("is_listening"), &TCP_Server::is_listening);
- ClassDB::bind_method(D_METHOD("get_local_port"), &TCP_Server::get_local_port);
- ClassDB::bind_method(D_METHOD("take_connection"), &TCP_Server::take_connection);
- ClassDB::bind_method(D_METHOD("stop"), &TCP_Server::stop);
+void TCPServer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCPServer::listen, DEFVAL("*"));
+ ClassDB::bind_method(D_METHOD("is_connection_available"), &TCPServer::is_connection_available);
+ ClassDB::bind_method(D_METHOD("is_listening"), &TCPServer::is_listening);
+ ClassDB::bind_method(D_METHOD("get_local_port"), &TCPServer::get_local_port);
+ ClassDB::bind_method(D_METHOD("take_connection"), &TCPServer::take_connection);
+ ClassDB::bind_method(D_METHOD("stop"), &TCPServer::stop);
}
-Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) {
+Error TCPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER);
@@ -76,19 +76,19 @@ Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) {
return OK;
}
-int TCP_Server::get_local_port() const {
+int TCPServer::get_local_port() const {
uint16_t local_port;
_sock->get_socket_address(nullptr, &local_port);
return local_port;
}
-bool TCP_Server::is_listening() const {
+bool TCPServer::is_listening() const {
ERR_FAIL_COND_V(!_sock.is_valid(), false);
return _sock->is_open();
}
-bool TCP_Server::is_connection_available() const {
+bool TCPServer::is_connection_available() const {
ERR_FAIL_COND_V(!_sock.is_valid(), false);
if (!_sock->is_open()) {
@@ -99,14 +99,14 @@ bool TCP_Server::is_connection_available() const {
return (err == OK);
}
-Ref<StreamPeerTCP> TCP_Server::take_connection() {
+Ref<StreamPeerTCP> TCPServer::take_connection() {
Ref<StreamPeerTCP> conn;
if (!is_connection_available()) {
return conn;
}
Ref<NetSocket> ns;
- IP_Address ip;
+ IPAddress ip;
uint16_t port = 0;
ns = _sock->accept(ip, port);
if (!ns.is_valid()) {
@@ -118,16 +118,16 @@ Ref<StreamPeerTCP> TCP_Server::take_connection() {
return conn;
}
-void TCP_Server::stop() {
+void TCPServer::stop() {
if (_sock.is_valid()) {
_sock->close();
}
}
-TCP_Server::TCP_Server() :
+TCPServer::TCPServer() :
_sock(Ref<NetSocket>(NetSocket::create())) {
}
-TCP_Server::~TCP_Server() {
+TCPServer::~TCPServer() {
stop();
}
diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h
index 58c04d87ec..abefa53c6f 100644
--- a/core/io/tcp_server.h
+++ b/core/io/tcp_server.h
@@ -36,8 +36,8 @@
#include "core/io/stream_peer.h"
#include "core/io/stream_peer_tcp.h"
-class TCP_Server : public Reference {
- GDCLASS(TCP_Server, Reference);
+class TCPServer : public Reference {
+ GDCLASS(TCPServer, Reference);
protected:
enum {
@@ -48,7 +48,7 @@ protected:
static void _bind_methods();
public:
- Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*"));
+ Error listen(uint16_t p_port, const IPAddress &p_bind_address = IPAddress("*"));
int get_local_port() const;
bool is_listening() const;
bool is_connection_available() const;
@@ -56,8 +56,8 @@ public:
void stop(); // Stop listening
- TCP_Server();
- ~TCP_Server();
+ TCPServer();
+ ~TCPServer();
};
#endif // TCP_SERVER_H
diff --git a/core/io/udp_server.cpp b/core/io/udp_server.cpp
index 99642f4af4..6a1af0c2a9 100644
--- a/core/io/udp_server.cpp
+++ b/core/io/udp_server.cpp
@@ -50,7 +50,7 @@ Error UDPServer::poll() {
}
Error err;
int read;
- IP_Address ip;
+ IPAddress ip;
uint16_t port;
while (true) {
err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port);
@@ -87,7 +87,7 @@ Error UDPServer::poll() {
return OK;
}
-Error UDPServer::listen(uint16_t p_port, const IP_Address &p_bind_address) {
+Error UDPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER);
@@ -168,7 +168,7 @@ Ref<PacketPeerUDP> UDPServer::take_connection() {
return peer.peer;
}
-void UDPServer::remove_peer(IP_Address p_ip, int p_port) {
+void UDPServer::remove_peer(IPAddress p_ip, int p_port) {
Peer peer;
peer.ip = p_ip;
peer.port = p_port;
diff --git a/core/io/udp_server.h b/core/io/udp_server.h
index 298d4d4b63..60d03f37f0 100644
--- a/core/io/udp_server.h
+++ b/core/io/udp_server.h
@@ -44,7 +44,7 @@ protected:
struct Peer {
PacketPeerUDP *peer;
- IP_Address ip;
+ IPAddress ip;
uint16_t port = 0;
bool operator==(const Peer &p_other) const {
@@ -61,8 +61,8 @@ protected:
static void _bind_methods();
public:
- void remove_peer(IP_Address p_ip, int p_port);
- Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*"));
+ void remove_peer(IPAddress p_ip, int p_port);
+ Error listen(uint16_t p_port, const IPAddress &p_bind_address = IPAddress("*"));
Error poll();
int get_local_port() const;
bool is_listening() const;
diff --git a/core/math/color.cpp b/core/math/color.cpp
index 8affb07e8c..64abd6dd08 100644
--- a/core/math/color.cpp
+++ b/core/math/color.cpp
@@ -379,11 +379,11 @@ int Color::find_named_color(const String &p_name) {
name = name.replace("_", "");
name = name.replace("'", "");
name = name.replace(".", "");
- name = name.to_lower();
+ name = name.to_upper();
int idx = 0;
while (named_colors[idx].name != nullptr) {
- if (name == named_colors[idx].name) {
+ if (name == String(named_colors[idx].name).replace("_", "")) {
return idx;
}
idx++;
diff --git a/core/math/color_names.inc b/core/math/color_names.inc
index e5b935ea9c..2020bdbfca 100644
--- a/core/math/color_names.inc
+++ b/core/math/color_names.inc
@@ -9,152 +9,155 @@ struct NamedColor {
Color color;
};
+// NOTE: This data is duplicated in the file:
+// modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
+
static NamedColor named_colors[] = {
- { "aliceblue", Color(0.94, 0.97, 1.00) },
- { "antiquewhite", Color(0.98, 0.92, 0.84) },
- { "aqua", Color(0.00, 1.00, 1.00) },
- { "aquamarine", Color(0.50, 1.00, 0.83) },
- { "azure", Color(0.94, 1.00, 1.00) },
- { "beige", Color(0.96, 0.96, 0.86) },
- { "bisque", Color(1.00, 0.89, 0.77) },
- { "black", Color(0.00, 0.00, 0.00) },
- { "blanchedalmond", Color(1.00, 0.92, 0.80) },
- { "blue", Color(0.00, 0.00, 1.00) },
- { "blueviolet", Color(0.54, 0.17, 0.89) },
- { "brown", Color(0.65, 0.16, 0.16) },
- { "burlywood", Color(0.87, 0.72, 0.53) },
- { "cadetblue", Color(0.37, 0.62, 0.63) },
- { "chartreuse", Color(0.50, 1.00, 0.00) },
- { "chocolate", Color(0.82, 0.41, 0.12) },
- { "coral", Color(1.00, 0.50, 0.31) },
- { "cornflower", Color(0.39, 0.58, 0.93) },
- { "cornsilk", Color(1.00, 0.97, 0.86) },
- { "crimson", Color(0.86, 0.08, 0.24) },
- { "cyan", Color(0.00, 1.00, 1.00) },
- { "darkblue", Color(0.00, 0.00, 0.55) },
- { "darkcyan", Color(0.00, 0.55, 0.55) },
- { "darkgoldenrod", Color(0.72, 0.53, 0.04) },
- { "darkgray", Color(0.66, 0.66, 0.66) },
- { "darkgreen", Color(0.00, 0.39, 0.00) },
- { "darkkhaki", Color(0.74, 0.72, 0.42) },
- { "darkmagenta", Color(0.55, 0.00, 0.55) },
- { "darkolivegreen", Color(0.33, 0.42, 0.18) },
- { "darkorange", Color(1.00, 0.55, 0.00) },
- { "darkorchid", Color(0.60, 0.20, 0.80) },
- { "darkred", Color(0.55, 0.00, 0.00) },
- { "darksalmon", Color(0.91, 0.59, 0.48) },
- { "darkseagreen", Color(0.56, 0.74, 0.56) },
- { "darkslateblue", Color(0.28, 0.24, 0.55) },
- { "darkslategray", Color(0.18, 0.31, 0.31) },
- { "darkturquoise", Color(0.00, 0.81, 0.82) },
- { "darkviolet", Color(0.58, 0.00, 0.83) },
- { "deeppink", Color(1.00, 0.08, 0.58) },
- { "deepskyblue", Color(0.00, 0.75, 1.00) },
- { "dimgray", Color(0.41, 0.41, 0.41) },
- { "dodgerblue", Color(0.12, 0.56, 1.00) },
- { "firebrick", Color(0.70, 0.13, 0.13) },
- { "floralwhite", Color(1.00, 0.98, 0.94) },
- { "forestgreen", Color(0.13, 0.55, 0.13) },
- { "fuchsia", Color(1.00, 0.00, 1.00) },
- { "gainsboro", Color(0.86, 0.86, 0.86) },
- { "ghostwhite", Color(0.97, 0.97, 1.00) },
- { "gold", Color(1.00, 0.84, 0.00) },
- { "goldenrod", Color(0.85, 0.65, 0.13) },
- { "gray", Color(0.75, 0.75, 0.75) },
- { "green", Color(0.00, 1.00, 0.00) },
- { "greenyellow", Color(0.68, 1.00, 0.18) },
- { "honeydew", Color(0.94, 1.00, 0.94) },
- { "hotpink", Color(1.00, 0.41, 0.71) },
- { "indianred", Color(0.80, 0.36, 0.36) },
- { "indigo", Color(0.29, 0.00, 0.51) },
- { "ivory", Color(1.00, 1.00, 0.94) },
- { "khaki", Color(0.94, 0.90, 0.55) },
- { "lavender", Color(0.90, 0.90, 0.98) },
- { "lavenderblush", Color(1.00, 0.94, 0.96) },
- { "lawngreen", Color(0.49, 0.99, 0.00) },
- { "lemonchiffon", Color(1.00, 0.98, 0.80) },
- { "lightblue", Color(0.68, 0.85, 0.90) },
- { "lightcoral", Color(0.94, 0.50, 0.50) },
- { "lightcyan", Color(0.88, 1.00, 1.00) },
- { "lightgoldenrod", Color(0.98, 0.98, 0.82) },
- { "lightgray", Color(0.83, 0.83, 0.83) },
- { "lightgreen", Color(0.56, 0.93, 0.56) },
- { "lightpink", Color(1.00, 0.71, 0.76) },
- { "lightsalmon", Color(1.00, 0.63, 0.48) },
- { "lightseagreen", Color(0.13, 0.70, 0.67) },
- { "lightskyblue", Color(0.53, 0.81, 0.98) },
- { "lightslategray", Color(0.47, 0.53, 0.60) },
- { "lightsteelblue", Color(0.69, 0.77, 0.87) },
- { "lightyellow", Color(1.00, 1.00, 0.88) },
- { "lime", Color(0.00, 1.00, 0.00) },
- { "limegreen", Color(0.20, 0.80, 0.20) },
- { "linen", Color(0.98, 0.94, 0.90) },
- { "magenta", Color(1.00, 0.00, 1.00) },
- { "maroon", Color(0.69, 0.19, 0.38) },
- { "mediumaquamarine", Color(0.40, 0.80, 0.67) },
- { "mediumblue", Color(0.00, 0.00, 0.80) },
- { "mediumorchid", Color(0.73, 0.33, 0.83) },
- { "mediumpurple", Color(0.58, 0.44, 0.86) },
- { "mediumseagreen", Color(0.24, 0.70, 0.44) },
- { "mediumslateblue", Color(0.48, 0.41, 0.93) },
- { "mediumspringgreen", Color(0.00, 0.98, 0.60) },
- { "mediumturquoise", Color(0.28, 0.82, 0.80) },
- { "mediumvioletred", Color(0.78, 0.08, 0.52) },
- { "midnightblue", Color(0.10, 0.10, 0.44) },
- { "mintcream", Color(0.96, 1.00, 0.98) },
- { "mistyrose", Color(1.00, 0.89, 0.88) },
- { "moccasin", Color(1.00, 0.89, 0.71) },
- { "navajowhite", Color(1.00, 0.87, 0.68) },
- { "navyblue", Color(0.00, 0.00, 0.50) },
- { "oldlace", Color(0.99, 0.96, 0.90) },
- { "olive", Color(0.50, 0.50, 0.00) },
- { "olivedrab", Color(0.42, 0.56, 0.14) },
- { "orange", Color(1.00, 0.65, 0.00) },
- { "orangered", Color(1.00, 0.27, 0.00) },
- { "orchid", Color(0.85, 0.44, 0.84) },
- { "palegoldenrod", Color(0.93, 0.91, 0.67) },
- { "palegreen", Color(0.60, 0.98, 0.60) },
- { "paleturquoise", Color(0.69, 0.93, 0.93) },
- { "palevioletred", Color(0.86, 0.44, 0.58) },
- { "papayawhip", Color(1.00, 0.94, 0.84) },
- { "peachpuff", Color(1.00, 0.85, 0.73) },
- { "peru", Color(0.80, 0.52, 0.25) },
- { "pink", Color(1.00, 0.75, 0.80) },
- { "plum", Color(0.87, 0.63, 0.87) },
- { "powderblue", Color(0.69, 0.88, 0.90) },
- { "purple", Color(0.63, 0.13, 0.94) },
- { "rebeccapurple", Color(0.40, 0.20, 0.60) },
- { "red", Color(1.00, 0.00, 0.00) },
- { "rosybrown", Color(0.74, 0.56, 0.56) },
- { "royalblue", Color(0.25, 0.41, 0.88) },
- { "saddlebrown", Color(0.55, 0.27, 0.07) },
- { "salmon", Color(0.98, 0.50, 0.45) },
- { "sandybrown", Color(0.96, 0.64, 0.38) },
- { "seagreen", Color(0.18, 0.55, 0.34) },
- { "seashell", Color(1.00, 0.96, 0.93) },
- { "sienna", Color(0.63, 0.32, 0.18) },
- { "silver", Color(0.75, 0.75, 0.75) },
- { "skyblue", Color(0.53, 0.81, 0.92) },
- { "slateblue", Color(0.42, 0.35, 0.80) },
- { "slategray", Color(0.44, 0.50, 0.56) },
- { "snow", Color(1.00, 0.98, 0.98) },
- { "springgreen", Color(0.00, 1.00, 0.50) },
- { "steelblue", Color(0.27, 0.51, 0.71) },
- { "tan", Color(0.82, 0.71, 0.55) },
- { "teal", Color(0.00, 0.50, 0.50) },
- { "thistle", Color(0.85, 0.75, 0.85) },
- { "tomato", Color(1.00, 0.39, 0.28) },
- { "transparent", Color(1.00, 1.00, 1.00, 0.00) },
- { "turquoise", Color(0.25, 0.88, 0.82) },
- { "violet", Color(0.93, 0.51, 0.93) },
- { "webgray", Color(0.50, 0.50, 0.50) },
- { "webgreen", Color(0.00, 0.50, 0.00) },
- { "webmaroon", Color(0.50, 0.00, 0.00) },
- { "webpurple", Color(0.50, 0.00, 0.50) },
- { "wheat", Color(0.96, 0.87, 0.70) },
- { "white", Color(1.00, 1.00, 1.00) },
- { "whitesmoke", Color(0.96, 0.96, 0.96) },
- { "yellow", Color(1.00, 1.00, 0.00) },
- { "yellowgreen", Color(0.60, 0.80, 0.20) },
+ { "ALICE_BLUE", Color(0.94, 0.97, 1.00) },
+ { "ANTIQUE_WHITE", Color(0.98, 0.92, 0.84) },
+ { "AQUA", Color(0.00, 1.00, 1.00) },
+ { "AQUAMARINE", Color(0.50, 1.00, 0.83) },
+ { "AZURE", Color(0.94, 1.00, 1.00) },
+ { "BEIGE", Color(0.96, 0.96, 0.86) },
+ { "BISQUE", Color(1.00, 0.89, 0.77) },
+ { "BLACK", Color(0.00, 0.00, 0.00) },
+ { "BLANCHED_ALMOND", Color(1.00, 0.92, 0.80) },
+ { "BLUE", Color(0.00, 0.00, 1.00) },
+ { "BLUE_VIOLET", Color(0.54, 0.17, 0.89) },
+ { "BROWN", Color(0.65, 0.16, 0.16) },
+ { "BURLYWOOD", Color(0.87, 0.72, 0.53) },
+ { "CADET_BLUE", Color(0.37, 0.62, 0.63) },
+ { "CHARTREUSE", Color(0.50, 1.00, 0.00) },
+ { "CHOCOLATE", Color(0.82, 0.41, 0.12) },
+ { "CORAL", Color(1.00, 0.50, 0.31) },
+ { "CORNFLOWER_BLUE", Color(0.39, 0.58, 0.93) },
+ { "CORNSILK", Color(1.00, 0.97, 0.86) },
+ { "CRIMSON", Color(0.86, 0.08, 0.24) },
+ { "CYAN", Color(0.00, 1.00, 1.00) },
+ { "DARK_BLUE", Color(0.00, 0.00, 0.55) },
+ { "DARK_CYAN", Color(0.00, 0.55, 0.55) },
+ { "DARK_GOLDENROD", Color(0.72, 0.53, 0.04) },
+ { "DARK_GRAY", Color(0.66, 0.66, 0.66) },
+ { "DARK_GREEN", Color(0.00, 0.39, 0.00) },
+ { "DARK_KHAKI", Color(0.74, 0.72, 0.42) },
+ { "DARK_MAGENTA", Color(0.55, 0.00, 0.55) },
+ { "DARK_OLIVE_GREEN", Color(0.33, 0.42, 0.18) },
+ { "DARK_ORANGE", Color(1.00, 0.55, 0.00) },
+ { "DARK_ORCHID", Color(0.60, 0.20, 0.80) },
+ { "DARK_RED", Color(0.55, 0.00, 0.00) },
+ { "DARK_SALMON", Color(0.91, 0.59, 0.48) },
+ { "DARK_SEA_GREEN", Color(0.56, 0.74, 0.56) },
+ { "DARK_SLATE_BLUE", Color(0.28, 0.24, 0.55) },
+ { "DARK_SLATE_GRAY", Color(0.18, 0.31, 0.31) },
+ { "DARK_TURQUOISE", Color(0.00, 0.81, 0.82) },
+ { "DARK_VIOLET", Color(0.58, 0.00, 0.83) },
+ { "DEEP_PINK", Color(1.00, 0.08, 0.58) },
+ { "DEEP_SKY_BLUE", Color(0.00, 0.75, 1.00) },
+ { "DIM_GRAY", Color(0.41, 0.41, 0.41) },
+ { "DODGER_BLUE", Color(0.12, 0.56, 1.00) },
+ { "FIREBRICK", Color(0.70, 0.13, 0.13) },
+ { "FLORAL_WHITE", Color(1.00, 0.98, 0.94) },
+ { "FOREST_GREEN", Color(0.13, 0.55, 0.13) },
+ { "FUCHSIA", Color(1.00, 0.00, 1.00) },
+ { "GAINSBORO", Color(0.86, 0.86, 0.86) },
+ { "GHOST_WHITE", Color(0.97, 0.97, 1.00) },
+ { "GOLD", Color(1.00, 0.84, 0.00) },
+ { "GOLDENROD", Color(0.85, 0.65, 0.13) },
+ { "GRAY", Color(0.75, 0.75, 0.75) },
+ { "GREEN", Color(0.00, 1.00, 0.00) },
+ { "GREEN_YELLOW", Color(0.68, 1.00, 0.18) },
+ { "HONEYDEW", Color(0.94, 1.00, 0.94) },
+ { "HOT_PINK", Color(1.00, 0.41, 0.71) },
+ { "INDIAN_RED", Color(0.80, 0.36, 0.36) },
+ { "INDIGO", Color(0.29, 0.00, 0.51) },
+ { "IVORY", Color(1.00, 1.00, 0.94) },
+ { "KHAKI", Color(0.94, 0.90, 0.55) },
+ { "LAVENDER", Color(0.90, 0.90, 0.98) },
+ { "LAVENDER_BLUSH", Color(1.00, 0.94, 0.96) },
+ { "LAWN_GREEN", Color(0.49, 0.99, 0.00) },
+ { "LEMON_CHIFFON", Color(1.00, 0.98, 0.80) },
+ { "LIGHT_BLUE", Color(0.68, 0.85, 0.90) },
+ { "LIGHT_CORAL", Color(0.94, 0.50, 0.50) },
+ { "LIGHT_CYAN", Color(0.88, 1.00, 1.00) },
+ { "LIGHT_GOLDENROD", Color(0.98, 0.98, 0.82) },
+ { "LIGHT_GRAY", Color(0.83, 0.83, 0.83) },
+ { "LIGHT_GREEN", Color(0.56, 0.93, 0.56) },
+ { "LIGHT_PINK", Color(1.00, 0.71, 0.76) },
+ { "LIGHT_SALMON", Color(1.00, 0.63, 0.48) },
+ { "LIGHT_SEA_GREEN", Color(0.13, 0.70, 0.67) },
+ { "LIGHT_SKY_BLUE", Color(0.53, 0.81, 0.98) },
+ { "LIGHT_SLATE_GRAY", Color(0.47, 0.53, 0.60) },
+ { "LIGHT_STEEL_BLUE", Color(0.69, 0.77, 0.87) },
+ { "LIGHT_YELLOW", Color(1.00, 1.00, 0.88) },
+ { "LIME", Color(0.00, 1.00, 0.00) },
+ { "LIME_GREEN", Color(0.20, 0.80, 0.20) },
+ { "LINEN", Color(0.98, 0.94, 0.90) },
+ { "MAGENTA", Color(1.00, 0.00, 1.00) },
+ { "MAROON", Color(0.69, 0.19, 0.38) },
+ { "MEDIUM_AQUAMARINE", Color(0.40, 0.80, 0.67) },
+ { "MEDIUM_BLUE", Color(0.00, 0.00, 0.80) },
+ { "MEDIUM_ORCHID", Color(0.73, 0.33, 0.83) },
+ { "MEDIUM_PURPLE", Color(0.58, 0.44, 0.86) },
+ { "MEDIUM_SEA_GREEN", Color(0.24, 0.70, 0.44) },
+ { "MEDIUM_SLATE_BLUE", Color(0.48, 0.41, 0.93) },
+ { "MEDIUM_SPRING_GREEN", Color(0.00, 0.98, 0.60) },
+ { "MEDIUM_TURQUOISE", Color(0.28, 0.82, 0.80) },
+ { "MEDIUM_VIOLET_RED", Color(0.78, 0.08, 0.52) },
+ { "MIDNIGHT_BLUE", Color(0.10, 0.10, 0.44) },
+ { "MINT_CREAM", Color(0.96, 1.00, 0.98) },
+ { "MISTY_ROSE", Color(1.00, 0.89, 0.88) },
+ { "MOCCASIN", Color(1.00, 0.89, 0.71) },
+ { "NAVAJO_WHITE", Color(1.00, 0.87, 0.68) },
+ { "NAVY_BLUE", Color(0.00, 0.00, 0.50) },
+ { "OLD_LACE", Color(0.99, 0.96, 0.90) },
+ { "OLIVE", Color(0.50, 0.50, 0.00) },
+ { "OLIVE_DRAB", Color(0.42, 0.56, 0.14) },
+ { "ORANGE", Color(1.00, 0.65, 0.00) },
+ { "ORANGE_RED", Color(1.00, 0.27, 0.00) },
+ { "ORCHID", Color(0.85, 0.44, 0.84) },
+ { "PALE_GOLDENROD", Color(0.93, 0.91, 0.67) },
+ { "PALE_GREEN", Color(0.60, 0.98, 0.60) },
+ { "PALE_TURQUOISE", Color(0.69, 0.93, 0.93) },
+ { "PALE_VIOLET_RED", Color(0.86, 0.44, 0.58) },
+ { "PAPAYA_WHIP", Color(1.00, 0.94, 0.84) },
+ { "PEACH_PUFF", Color(1.00, 0.85, 0.73) },
+ { "PERU", Color(0.80, 0.52, 0.25) },
+ { "PINK", Color(1.00, 0.75, 0.80) },
+ { "PLUM", Color(0.87, 0.63, 0.87) },
+ { "POWDER_BLUE", Color(0.69, 0.88, 0.90) },
+ { "PURPLE", Color(0.63, 0.13, 0.94) },
+ { "REBECCA_PURPLE", Color(0.40, 0.20, 0.60) },
+ { "RED", Color(1.00, 0.00, 0.00) },
+ { "ROSY_BROWN", Color(0.74, 0.56, 0.56) },
+ { "ROYAL_BLUE", Color(0.25, 0.41, 0.88) },
+ { "SADDLE_BROWN", Color(0.55, 0.27, 0.07) },
+ { "SALMON", Color(0.98, 0.50, 0.45) },
+ { "SANDY_BROWN", Color(0.96, 0.64, 0.38) },
+ { "SEA_GREEN", Color(0.18, 0.55, 0.34) },
+ { "SEASHELL", Color(1.00, 0.96, 0.93) },
+ { "SIENNA", Color(0.63, 0.32, 0.18) },
+ { "SILVER", Color(0.75, 0.75, 0.75) },
+ { "SKY_BLUE", Color(0.53, 0.81, 0.92) },
+ { "SLATE_BLUE", Color(0.42, 0.35, 0.80) },
+ { "SLATE_GRAY", Color(0.44, 0.50, 0.56) },
+ { "SNOW", Color(1.00, 0.98, 0.98) },
+ { "SPRING_GREEN", Color(0.00, 1.00, 0.50) },
+ { "STEEL_BLUE", Color(0.27, 0.51, 0.71) },
+ { "TAN", Color(0.82, 0.71, 0.55) },
+ { "TEAL", Color(0.00, 0.50, 0.50) },
+ { "THISTLE", Color(0.85, 0.75, 0.85) },
+ { "TOMATO", Color(1.00, 0.39, 0.28) },
+ { "TRANSPARENT", Color(1.00, 1.00, 1.00, 0.00) },
+ { "TURQUOISE", Color(0.25, 0.88, 0.82) },
+ { "VIOLET", Color(0.93, 0.51, 0.93) },
+ { "WEB_GRAY", Color(0.50, 0.50, 0.50) },
+ { "WEB_GREEN", Color(0.00, 0.50, 0.00) },
+ { "WEB_MAROON", Color(0.50, 0.00, 0.00) },
+ { "WEB_PURPLE", Color(0.50, 0.00, 0.50) },
+ { "WHEAT", Color(0.96, 0.87, 0.70) },
+ { "WHITE", Color(1.00, 1.00, 1.00) },
+ { "WHITE_SMOKE", Color(0.96, 0.96, 0.96) },
+ { "YELLOW", Color(1.00, 1.00, 0.00) },
+ { "YELLOW_GREEN", Color(0.60, 0.80, 0.20) },
{ nullptr, Color() },
};
diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h
index 4b5aef352f..4958b5ac6a 100644
--- a/core/math/geometry_2d.h
+++ b/core/math/geometry_2d.h
@@ -395,6 +395,45 @@ public:
H.resize(k);
return H;
}
+
+ static Vector<Point2i> bresenham_line(const Point2i &p_start, const Point2i &p_end) {
+ Vector<Point2i> points;
+
+ Vector2i delta = (p_end - p_start).abs() * 2;
+ Vector2i step = (p_end - p_start).sign();
+ Vector2i current = p_start;
+
+ if (delta.x > delta.y) {
+ int err = delta.x / 2;
+
+ for (; current.x != p_end.x; current.x += step.x) {
+ points.push_back(current);
+
+ err -= delta.y;
+ if (err < 0) {
+ current.y += step.y;
+ err += delta.x;
+ }
+ }
+ } else {
+ int err = delta.y / 2;
+
+ for (; current.y != p_end.y; current.y += step.y) {
+ points.push_back(current);
+
+ err -= delta.x;
+ if (err < 0) {
+ current.x += step.x;
+ err += delta.y;
+ }
+ }
+ }
+
+ points.push_back(current);
+
+ return points;
+ }
+
static Vector<Vector<Vector2>> decompose_polygon_in_convex(Vector<Point2> polygon);
static void make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size);
diff --git a/core/math/vector2.h b/core/math/vector2.h
index 81bc71d590..edc6e3a3ef 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -280,6 +280,14 @@ struct Vector2i {
return p_idx ? y : x;
}
+ Vector2i min(const Vector2i &p_vector2i) const {
+ return Vector2(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y));
+ }
+
+ Vector2i max(const Vector2i &p_vector2i) const {
+ return Vector2(MAX(x, p_vector2i.x), MAX(y, p_vector2i.y));
+ }
+
Vector2i operator+(const Vector2i &p_v) const;
void operator+=(const Vector2i &p_v);
Vector2i operator-(const Vector2i &p_v) const;
diff --git a/core/object/script_language.h b/core/object/script_language.h
index f9898ccd0c..bb46c718b2 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -303,6 +303,7 @@ public:
void get_core_type_words(List<String> *p_core_type_words) const;
virtual void get_reserved_words(List<String> *p_words) const = 0;
+ virtual bool is_control_flow_keyword(String p_string) const = 0;
virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0;
virtual void get_string_delimiters(List<String> *p_delimiters) const = 0;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0;
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index d6a5eff10d..f1b1b98bea 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -156,7 +156,7 @@ void register_core_types() {
ClassDB::register_virtual_class<StreamPeer>();
ClassDB::register_class<StreamPeerBuffer>();
ClassDB::register_class<StreamPeerTCP>();
- ClassDB::register_class<TCP_Server>();
+ ClassDB::register_class<TCPServer>();
ClassDB::register_class<PacketPeerUDP>();
ClassDB::register_class<UDPServer>();
ClassDB::register_custom_instance_class<PacketPeerDTLS>();
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 2fb2dd4a30..3c7e2a0719 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -361,6 +361,79 @@ Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { // l
return new_arr;
}
+Array Array::filter(const Callable &p_callable) const {
+ Array new_arr;
+ new_arr.resize(size());
+ int accepted_count = 0;
+
+ for (int i = 0; i < size(); i++) {
+ const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *));
+ argptrs[0] = &get(i);
+
+ Variant result;
+ Callable::CallError ce;
+ p_callable.call(argptrs, 1, result, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_V_MSG(Array(), "Error calling method from 'filter': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce));
+ }
+
+ if (result.operator bool()) {
+ new_arr[accepted_count] = get(i);
+ accepted_count++;
+ }
+ }
+
+ new_arr.resize(accepted_count);
+
+ return new_arr;
+}
+
+Array Array::map(const Callable &p_callable) const {
+ Array new_arr;
+ new_arr.resize(size());
+
+ for (int i = 0; i < size(); i++) {
+ const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *));
+ argptrs[0] = &get(i);
+
+ Variant result;
+ Callable::CallError ce;
+ p_callable.call(argptrs, 1, result, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_V_MSG(Array(), "Error calling method from 'map': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce));
+ }
+
+ new_arr[i] = result;
+ }
+
+ return new_arr;
+}
+
+Variant Array::reduce(const Callable &p_callable, const Variant &p_accum) const {
+ int start = 0;
+ Variant ret = p_accum;
+ if (ret == Variant() && size() > 0) {
+ ret = front();
+ start = 1;
+ }
+
+ for (int i = start; i < size(); i++) {
+ const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * 2);
+ argptrs[0] = &ret;
+ argptrs[1] = &get(i);
+
+ Variant result;
+ Callable::CallError ce;
+ p_callable.call(argptrs, 2, result, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_V_MSG(Variant(), "Error calling method from 'reduce': " + Variant::get_callable_error_text(p_callable, argptrs, 2, ce));
+ }
+ ret = result;
+ }
+
+ return ret;
+}
+
struct _ArrayVariantSort {
_FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const {
bool valid = false;
diff --git a/core/variant/array.h b/core/variant/array.h
index 5ce977ee4b..540dcb1f4e 100644
--- a/core/variant/array.h
+++ b/core/variant/array.h
@@ -101,6 +101,9 @@ public:
Array duplicate(bool p_deep = false) const;
Array slice(int p_begin, int p_end, int p_step = 1, bool p_deep = false) const;
+ Array filter(const Callable &p_callable) const;
+ Array map(const Callable &p_callable) const;
+ Variant reduce(const Callable &p_callable, const Variant &p_accum) const;
bool operator<(const Array &p_array) const;
bool operator<=(const Array &p_array) const;
diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h
index c294592b63..e91029f330 100644
--- a/core/variant/method_ptrcall.h
+++ b/core/variant/method_ptrcall.h
@@ -364,7 +364,7 @@ MAKE_VECARR(Plane);
} \
}
-// Special case for IP_Address.
+// Special case for IPAddress.
#define MAKE_STRINGCONV_BY_REFERENCE(m_type) \
template <> \
@@ -387,7 +387,7 @@ MAKE_VECARR(Plane);
} \
}
-MAKE_STRINGCONV_BY_REFERENCE(IP_Address);
+MAKE_STRINGCONV_BY_REFERENCE(IPAddress);
template <>
struct PtrToArg<Vector<Face3>> {
diff --git a/core/variant/type_info.h b/core/variant/type_info.h
index f61ff29b8f..d5b6d85dfb 100644
--- a/core/variant/type_info.h
+++ b/core/variant/type_info.h
@@ -168,7 +168,7 @@ MAKE_TYPE_INFO(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY)
MAKE_TYPE_INFO(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY)
MAKE_TYPE_INFO(PackedColorArray, Variant::PACKED_COLOR_ARRAY)
-MAKE_TYPE_INFO(IP_Address, Variant::STRING)
+MAKE_TYPE_INFO(IPAddress, Variant::STRING)
//objectID
template <>
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 015cee09a7..333dd8e8d1 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -2346,15 +2346,15 @@ Variant::operator Orientation() const {
return (Orientation) operator int();
}
-Variant::operator IP_Address() const {
+Variant::operator IPAddress() const {
if (type == PACKED_FLOAT32_ARRAY || type == PACKED_INT32_ARRAY || type == PACKED_FLOAT64_ARRAY || type == PACKED_INT64_ARRAY || type == PACKED_BYTE_ARRAY) {
Vector<int> addr = operator Vector<int>();
if (addr.size() == 4) {
- return IP_Address(addr.get(0), addr.get(1), addr.get(2), addr.get(3));
+ return IPAddress(addr.get(0), addr.get(1), addr.get(2), addr.get(3));
}
}
- return IP_Address(operator String());
+ return IPAddress(operator String());
}
Variant::Variant(bool p_bool) {
@@ -2831,7 +2831,7 @@ void Variant::operator=(const Variant &p_variant) {
}
}
-Variant::Variant(const IP_Address &p_address) {
+Variant::Variant(const IPAddress &p_address) {
type = STRING;
memnew_placement(_data._mem, String(p_address));
}
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 0acafc64fa..7f3c3477fc 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -359,7 +359,7 @@ public:
operator Side() const;
operator Orientation() const;
- operator IP_Address() const;
+ operator IPAddress() const;
Object *get_validated_object() const;
Object *get_validated_object_with_check(bool &r_previously_freed) const;
@@ -421,7 +421,7 @@ public:
Variant(const Vector<::RID> &p_array); // helper
Variant(const Vector<Vector2> &p_array); // helper
- Variant(const IP_Address &p_address);
+ Variant(const IPAddress &p_address);
// If this changes the table in variant_op must be updated
enum Operator {
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 455e924568..efaaa8cd19 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1745,6 +1745,9 @@ static void _register_variant_builtin_methods() {
bind_method(Array, reverse, sarray(), varray());
bind_method(Array, duplicate, sarray("deep"), varray(false));
bind_method(Array, slice, sarray("begin", "end", "step", "deep"), varray(1, false));
+ bind_method(Array, filter, sarray("method"), varray());
+ bind_method(Array, map, sarray("method"), varray());
+ bind_method(Array, reduce, sarray("method", "accum"), varray(Variant()));
bind_method(Array, max, sarray(), varray());
bind_method(Array, min, sarray(), varray());
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index 38b74cb436..624b51e463 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -258,6 +258,24 @@
[/codeblocks]
</description>
</method>
+ <method name="filter" qualifiers="const">
+ <return type="Array">
+ </return>
+ <argument index="0" name="method" type="Callable">
+ </argument>
+ <description>
+ Calls the provided [Callable] on each element in the array and returns a new array with the elements for which the method returned [code]true[/code].
+ The callable's method should take one [Variant] parameter (the current array element) and return a boolean value.
+ [codeblock]
+ func _ready():
+ print([1, 2, 3].filter(remove_1)) # Prints [2, 3].
+ print([1, 2, 3].filter(func(number): return number != 1)) # Same as above, but using lambda function.
+
+ func remove_1(number):
+ return number != 1
+ [/codeblock]
+ </description>
+ </method>
<method name="find" qualifiers="const">
<return type="int">
</return>
@@ -356,6 +374,24 @@
Returns [code]true[/code] if the array is empty.
</description>
</method>
+ <method name="map" qualifiers="const">
+ <return type="Array">
+ </return>
+ <argument index="0" name="method" type="Callable">
+ </argument>
+ <description>
+ Calls the provided [Callable] for each element in the array and returns a new array filled with values returned by the method.
+ The callable's method should take one [Variant] parameter (the current array element) and can return any [Variant].
+ [codeblock]
+ func _ready():
+ print([1, 2, 3].map(negate)) # Prints [-1, -2, -3].
+ print([1, 2, 3].map(func(number): return -number)) # Same as above, but using lambda function.
+
+ func negate(number):
+ return -number
+ [/codeblock]
+ </description>
+ </method>
<method name="max" qualifiers="const">
<return type="Variant">
</return>
@@ -468,6 +504,26 @@
[b]Note:[/b] On large arrays, this method is much slower than [method push_back] as it will reindex all the array's elements every time it's called. The larger the array, the slower [method push_front] will be.
</description>
</method>
+ <method name="reduce" qualifiers="const">
+ <return type="Variant">
+ </return>
+ <argument index="0" name="method" type="Callable">
+ </argument>
+ <argument index="1" name="accum" type="Variant" default="null">
+ </argument>
+ <description>
+ Calls the provided [Callable] for each element in array and accumulates the result in [code]accum[/code].
+ The callable's method takes two arguments: the current value of [code]accum[/code] and the current array element. If [code]accum[/code] is [code]null[/code] (default value), the iteration will start from the second element, with the first one used as initial value of [code]accum[/code].
+ [codeblock]
+ func _ready():
+ print([1, 2, 3].reduce(sum, 10)) # Prints 16.
+ print([1, 2, 3].reduce(func(accum, number): return accum + number, 10)) # Same as above, but using lambda function.
+
+ func sum(accum, number):
+ return accum + number
+ [/codeblock]
+ </description>
+ </method>
<method name="remove">
<return type="void">
</return>
diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml
index 55190c5f9f..113cc64b9d 100644
--- a/doc/classes/AudioStreamPlayer.xml
+++ b/doc/classes/AudioStreamPlayer.xml
@@ -5,6 +5,7 @@
</brief_description>
<description>
Plays an audio stream non-positionally.
+ To play audio positionally, use [AudioStreamPlayer2D] or [AudioStreamPlayer3D] instead of [AudioStreamPlayer].
</description>
<tutorials>
<link title="Audio streams">https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link>
diff --git a/doc/classes/AudioStreamPlayer2D.xml b/doc/classes/AudioStreamPlayer2D.xml
index d8b9385736..e7c276f463 100644
--- a/doc/classes/AudioStreamPlayer2D.xml
+++ b/doc/classes/AudioStreamPlayer2D.xml
@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioStreamPlayer2D" inherits="Node2D" version="4.0">
<brief_description>
- Plays audio in 2D.
+ Plays positional sound in 2D space.
</brief_description>
<description>
Plays audio that dampens with distance from screen center.
+ See also [AudioStreamPlayer] to play a sound non-positionally.
+ [b]Note:[/b] Hiding an [AudioStreamPlayer2D] node does not disable its audio output. To temporarily disable an [AudioStreamPlayer2D]'s audio output, set [member volume_db] to a very low value like [code]-100[/code] (which isn't audible to human hearing).
</description>
<tutorials>
<link title="Audio streams">https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link>
diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml
index 7a8c4b2cc7..db46ed2778 100644
--- a/doc/classes/AudioStreamPlayer3D.xml
+++ b/doc/classes/AudioStreamPlayer3D.xml
@@ -1,11 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioStreamPlayer3D" inherits="Node3D" version="4.0">
<brief_description>
- Plays 3D sound in 3D space.
+ Plays positional sound in 3D space.
</brief_description>
<description>
Plays a sound effect with directed sound effects, dampens with distance if needed, generates effect of hearable position in space. For greater realism, a low-pass filter is automatically applied to distant sounds. This can be disabled by setting [member attenuation_filter_cutoff_hz] to [code]20500[/code].
By default, audio is heard from the camera position. This can be changed by adding a [Listener3D] node to the scene and enabling it by calling [method Listener3D.make_current] on it.
+ See also [AudioStreamPlayer] to play a sound non-positionally.
+ [b]Note:[/b] Hiding an [AudioStreamPlayer3D] node does not disable its audio output. To temporarily disable an [AudioStreamPlayer3D]'s audio output, set [member unit_db] to a very low value like [code]-100[/code] (which isn't audible to human hearing).
</description>
<tutorials>
<link title="Audio streams">https://docs.godotengine.org/en/latest/tutorials/audio/audio_streams.html</link>
diff --git a/doc/classes/ButtonGroup.xml b/doc/classes/ButtonGroup.xml
index 5aa2d699a8..0b31352611 100644
--- a/doc/classes/ButtonGroup.xml
+++ b/doc/classes/ButtonGroup.xml
@@ -28,6 +28,15 @@
<members>
<member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" override="true" default="true" />
</members>
+ <signals>
+ <signal name="pressed">
+ <argument index="0" name="button" type="Object">
+ </argument>
+ <description>
+ Emitted when one of the buttons of the group is pressed.
+ </description>
+ </signal>
+ </signals>
<constants>
</constants>
</class>
diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml
index d645588af2..6133bb8d8c 100644
--- a/doc/classes/Color.xml
+++ b/doc/classes/Color.xml
@@ -564,442 +564,442 @@
</member>
</members>
<constants>
- <constant name="aliceblue" value="Color( 0.94, 0.97, 1, 1 )">
+ <constant name="ALICE_BLUE" value="Color( 0.94, 0.97, 1, 1 )">
Alice blue color.
</constant>
- <constant name="antiquewhite" value="Color( 0.98, 0.92, 0.84, 1 )">
+ <constant name="ANTIQUE_WHITE" value="Color( 0.98, 0.92, 0.84, 1 )">
Antique white color.
</constant>
- <constant name="aqua" value="Color( 0, 1, 1, 1 )">
+ <constant name="AQUA" value="Color( 0, 1, 1, 1 )">
Aqua color.
</constant>
- <constant name="aquamarine" value="Color( 0.5, 1, 0.83, 1 )">
+ <constant name="AQUAMARINE" value="Color( 0.5, 1, 0.83, 1 )">
Aquamarine color.
</constant>
- <constant name="azure" value="Color( 0.94, 1, 1, 1 )">
+ <constant name="AZURE" value="Color( 0.94, 1, 1, 1 )">
Azure color.
</constant>
- <constant name="beige" value="Color( 0.96, 0.96, 0.86, 1 )">
+ <constant name="BEIGE" value="Color( 0.96, 0.96, 0.86, 1 )">
Beige color.
</constant>
- <constant name="bisque" value="Color( 1, 0.89, 0.77, 1 )">
+ <constant name="BISQUE" value="Color( 1, 0.89, 0.77, 1 )">
Bisque color.
</constant>
- <constant name="black" value="Color( 0, 0, 0, 1 )">
+ <constant name="BLACK" value="Color( 0, 0, 0, 1 )">
Black color.
</constant>
- <constant name="blanchedalmond" value="Color( 1, 0.92, 0.8, 1 )">
- Blanche almond color.
+ <constant name="BLANCHED_ALMOND" value="Color( 1, 0.92, 0.8, 1 )">
+ Blanched almond color.
</constant>
- <constant name="blue" value="Color( 0, 0, 1, 1 )">
+ <constant name="BLUE" value="Color( 0, 0, 1, 1 )">
Blue color.
</constant>
- <constant name="blueviolet" value="Color( 0.54, 0.17, 0.89, 1 )">
+ <constant name="BLUE_VIOLET" value="Color( 0.54, 0.17, 0.89, 1 )">
Blue violet color.
</constant>
- <constant name="brown" value="Color( 0.65, 0.16, 0.16, 1 )">
+ <constant name="BROWN" value="Color( 0.65, 0.16, 0.16, 1 )">
Brown color.
</constant>
- <constant name="burlywood" value="Color( 0.87, 0.72, 0.53, 1 )">
- Burly wood color.
+ <constant name="BURLYWOOD" value="Color( 0.87, 0.72, 0.53, 1 )">
+ Burlywood color.
</constant>
- <constant name="cadetblue" value="Color( 0.37, 0.62, 0.63, 1 )">
+ <constant name="CADET_BLUE" value="Color( 0.37, 0.62, 0.63, 1 )">
Cadet blue color.
</constant>
- <constant name="chartreuse" value="Color( 0.5, 1, 0, 1 )">
+ <constant name="CHARTREUSE" value="Color( 0.5, 1, 0, 1 )">
Chartreuse color.
</constant>
- <constant name="chocolate" value="Color( 0.82, 0.41, 0.12, 1 )">
+ <constant name="CHOCOLATE" value="Color( 0.82, 0.41, 0.12, 1 )">
Chocolate color.
</constant>
- <constant name="coral" value="Color( 1, 0.5, 0.31, 1 )">
+ <constant name="CORAL" value="Color( 1, 0.5, 0.31, 1 )">
Coral color.
</constant>
- <constant name="cornflower" value="Color( 0.39, 0.58, 0.93, 1 )">
- Cornflower color.
+ <constant name="CORNFLOWER_BLUE" value="Color( 0.39, 0.58, 0.93, 1 )">
+ Cornflower blue color.
</constant>
- <constant name="cornsilk" value="Color( 1, 0.97, 0.86, 1 )">
- Corn silk color.
+ <constant name="CORNSILK" value="Color( 1, 0.97, 0.86, 1 )">
+ Cornsilk color.
</constant>
- <constant name="crimson" value="Color( 0.86, 0.08, 0.24, 1 )">
+ <constant name="CRIMSON" value="Color( 0.86, 0.08, 0.24, 1 )">
Crimson color.
</constant>
- <constant name="cyan" value="Color( 0, 1, 1, 1 )">
+ <constant name="CYAN" value="Color( 0, 1, 1, 1 )">
Cyan color.
</constant>
- <constant name="darkblue" value="Color( 0, 0, 0.55, 1 )">
+ <constant name="DARK_BLUE" value="Color( 0, 0, 0.55, 1 )">
Dark blue color.
</constant>
- <constant name="darkcyan" value="Color( 0, 0.55, 0.55, 1 )">
+ <constant name="DARK_CYAN" value="Color( 0, 0.55, 0.55, 1 )">
Dark cyan color.
</constant>
- <constant name="darkgoldenrod" value="Color( 0.72, 0.53, 0.04, 1 )">
+ <constant name="DARK_GOLDENROD" value="Color( 0.72, 0.53, 0.04, 1 )">
Dark goldenrod color.
</constant>
- <constant name="darkgray" value="Color( 0.66, 0.66, 0.66, 1 )">
+ <constant name="DARK_GRAY" value="Color( 0.66, 0.66, 0.66, 1 )">
Dark gray color.
</constant>
- <constant name="darkgreen" value="Color( 0, 0.39, 0, 1 )">
+ <constant name="DARK_GREEN" value="Color( 0, 0.39, 0, 1 )">
Dark green color.
</constant>
- <constant name="darkkhaki" value="Color( 0.74, 0.72, 0.42, 1 )">
+ <constant name="DARK_KHAKI" value="Color( 0.74, 0.72, 0.42, 1 )">
Dark khaki color.
</constant>
- <constant name="darkmagenta" value="Color( 0.55, 0, 0.55, 1 )">
+ <constant name="DARK_MAGENTA" value="Color( 0.55, 0, 0.55, 1 )">
Dark magenta color.
</constant>
- <constant name="darkolivegreen" value="Color( 0.33, 0.42, 0.18, 1 )">
+ <constant name="DARK_OLIVE_GREEN" value="Color( 0.33, 0.42, 0.18, 1 )">
Dark olive green color.
</constant>
- <constant name="darkorange" value="Color( 1, 0.55, 0, 1 )">
+ <constant name="DARK_ORANGE" value="Color( 1, 0.55, 0, 1 )">
Dark orange color.
</constant>
- <constant name="darkorchid" value="Color( 0.6, 0.2, 0.8, 1 )">
+ <constant name="DARK_ORCHID" value="Color( 0.6, 0.2, 0.8, 1 )">
Dark orchid color.
</constant>
- <constant name="darkred" value="Color( 0.55, 0, 0, 1 )">
+ <constant name="DARK_RED" value="Color( 0.55, 0, 0, 1 )">
Dark red color.
</constant>
- <constant name="darksalmon" value="Color( 0.91, 0.59, 0.48, 1 )">
+ <constant name="DARK_SALMON" value="Color( 0.91, 0.59, 0.48, 1 )">
Dark salmon color.
</constant>
- <constant name="darkseagreen" value="Color( 0.56, 0.74, 0.56, 1 )">
+ <constant name="DARK_SEA_GREEN" value="Color( 0.56, 0.74, 0.56, 1 )">
Dark sea green color.
</constant>
- <constant name="darkslateblue" value="Color( 0.28, 0.24, 0.55, 1 )">
+ <constant name="DARK_SLATE_BLUE" value="Color( 0.28, 0.24, 0.55, 1 )">
Dark slate blue color.
</constant>
- <constant name="darkslategray" value="Color( 0.18, 0.31, 0.31, 1 )">
+ <constant name="DARK_SLATE_GRAY" value="Color( 0.18, 0.31, 0.31, 1 )">
Dark slate gray color.
</constant>
- <constant name="darkturquoise" value="Color( 0, 0.81, 0.82, 1 )">
+ <constant name="DARK_TURQUOISE" value="Color( 0, 0.81, 0.82, 1 )">
Dark turquoise color.
</constant>
- <constant name="darkviolet" value="Color( 0.58, 0, 0.83, 1 )">
+ <constant name="DARK_VIOLET" value="Color( 0.58, 0, 0.83, 1 )">
Dark violet color.
</constant>
- <constant name="deeppink" value="Color( 1, 0.08, 0.58, 1 )">
+ <constant name="DEEP_PINK" value="Color( 1, 0.08, 0.58, 1 )">
Deep pink color.
</constant>
- <constant name="deepskyblue" value="Color( 0, 0.75, 1, 1 )">
+ <constant name="DEEP_SKY_BLUE" value="Color( 0, 0.75, 1, 1 )">
Deep sky blue color.
</constant>
- <constant name="dimgray" value="Color( 0.41, 0.41, 0.41, 1 )">
+ <constant name="DIM_GRAY" value="Color( 0.41, 0.41, 0.41, 1 )">
Dim gray color.
</constant>
- <constant name="dodgerblue" value="Color( 0.12, 0.56, 1, 1 )">
+ <constant name="DODGER_BLUE" value="Color( 0.12, 0.56, 1, 1 )">
Dodger blue color.
</constant>
- <constant name="firebrick" value="Color( 0.7, 0.13, 0.13, 1 )">
+ <constant name="FIREBRICK" value="Color( 0.7, 0.13, 0.13, 1 )">
Firebrick color.
</constant>
- <constant name="floralwhite" value="Color( 1, 0.98, 0.94, 1 )">
+ <constant name="FLORAL_WHITE" value="Color( 1, 0.98, 0.94, 1 )">
Floral white color.
</constant>
- <constant name="forestgreen" value="Color( 0.13, 0.55, 0.13, 1 )">
+ <constant name="FOREST_GREEN" value="Color( 0.13, 0.55, 0.13, 1 )">
Forest green color.
</constant>
- <constant name="fuchsia" value="Color( 1, 0, 1, 1 )">
+ <constant name="FUCHSIA" value="Color( 1, 0, 1, 1 )">
Fuchsia color.
</constant>
- <constant name="gainsboro" value="Color( 0.86, 0.86, 0.86, 1 )">
+ <constant name="GAINSBORO" value="Color( 0.86, 0.86, 0.86, 1 )">
Gainsboro color.
</constant>
- <constant name="ghostwhite" value="Color( 0.97, 0.97, 1, 1 )">
+ <constant name="GHOST_WHITE" value="Color( 0.97, 0.97, 1, 1 )">
Ghost white color.
</constant>
- <constant name="gold" value="Color( 1, 0.84, 0, 1 )">
+ <constant name="GOLD" value="Color( 1, 0.84, 0, 1 )">
Gold color.
</constant>
- <constant name="goldenrod" value="Color( 0.85, 0.65, 0.13, 1 )">
+ <constant name="GOLDENROD" value="Color( 0.85, 0.65, 0.13, 1 )">
Goldenrod color.
</constant>
- <constant name="gray" value="Color( 0.75, 0.75, 0.75, 1 )">
+ <constant name="GRAY" value="Color( 0.75, 0.75, 0.75, 1 )">
Gray color.
</constant>
- <constant name="green" value="Color( 0, 1, 0, 1 )">
+ <constant name="GREEN" value="Color( 0, 1, 0, 1 )">
Green color.
</constant>
- <constant name="greenyellow" value="Color( 0.68, 1, 0.18, 1 )">
+ <constant name="GREEN_YELLOW" value="Color( 0.68, 1, 0.18, 1 )">
Green yellow color.
</constant>
- <constant name="honeydew" value="Color( 0.94, 1, 0.94, 1 )">
+ <constant name="HONEYDEW" value="Color( 0.94, 1, 0.94, 1 )">
Honeydew color.
</constant>
- <constant name="hotpink" value="Color( 1, 0.41, 0.71, 1 )">
+ <constant name="HOT_PINK" value="Color( 1, 0.41, 0.71, 1 )">
Hot pink color.
</constant>
- <constant name="indianred" value="Color( 0.8, 0.36, 0.36, 1 )">
+ <constant name="INDIAN_RED" value="Color( 0.8, 0.36, 0.36, 1 )">
Indian red color.
</constant>
- <constant name="indigo" value="Color( 0.29, 0, 0.51, 1 )">
+ <constant name="INDIGO" value="Color( 0.29, 0, 0.51, 1 )">
Indigo color.
</constant>
- <constant name="ivory" value="Color( 1, 1, 0.94, 1 )">
+ <constant name="IVORY" value="Color( 1, 1, 0.94, 1 )">
Ivory color.
</constant>
- <constant name="khaki" value="Color( 0.94, 0.9, 0.55, 1 )">
+ <constant name="KHAKI" value="Color( 0.94, 0.9, 0.55, 1 )">
Khaki color.
</constant>
- <constant name="lavender" value="Color( 0.9, 0.9, 0.98, 1 )">
+ <constant name="LAVENDER" value="Color( 0.9, 0.9, 0.98, 1 )">
Lavender color.
</constant>
- <constant name="lavenderblush" value="Color( 1, 0.94, 0.96, 1 )">
+ <constant name="LAVENDER_BLUSH" value="Color( 1, 0.94, 0.96, 1 )">
Lavender blush color.
</constant>
- <constant name="lawngreen" value="Color( 0.49, 0.99, 0, 1 )">
+ <constant name="LAWN_GREEN" value="Color( 0.49, 0.99, 0, 1 )">
Lawn green color.
</constant>
- <constant name="lemonchiffon" value="Color( 1, 0.98, 0.8, 1 )">
+ <constant name="LEMON_CHIFFON" value="Color( 1, 0.98, 0.8, 1 )">
Lemon chiffon color.
</constant>
- <constant name="lightblue" value="Color( 0.68, 0.85, 0.9, 1 )">
+ <constant name="LIGHT_BLUE" value="Color( 0.68, 0.85, 0.9, 1 )">
Light blue color.
</constant>
- <constant name="lightcoral" value="Color( 0.94, 0.5, 0.5, 1 )">
+ <constant name="LIGHT_CORAL" value="Color( 0.94, 0.5, 0.5, 1 )">
Light coral color.
</constant>
- <constant name="lightcyan" value="Color( 0.88, 1, 1, 1 )">
+ <constant name="LIGHT_CYAN" value="Color( 0.88, 1, 1, 1 )">
Light cyan color.
</constant>
- <constant name="lightgoldenrod" value="Color( 0.98, 0.98, 0.82, 1 )">
+ <constant name="LIGHT_GOLDENROD" value="Color( 0.98, 0.98, 0.82, 1 )">
Light goldenrod color.
</constant>
- <constant name="lightgray" value="Color( 0.83, 0.83, 0.83, 1 )">
+ <constant name="LIGHT_GRAY" value="Color( 0.83, 0.83, 0.83, 1 )">
Light gray color.
</constant>
- <constant name="lightgreen" value="Color( 0.56, 0.93, 0.56, 1 )">
+ <constant name="LIGHT_GREEN" value="Color( 0.56, 0.93, 0.56, 1 )">
Light green color.
</constant>
- <constant name="lightpink" value="Color( 1, 0.71, 0.76, 1 )">
+ <constant name="LIGHT_PINK" value="Color( 1, 0.71, 0.76, 1 )">
Light pink color.
</constant>
- <constant name="lightsalmon" value="Color( 1, 0.63, 0.48, 1 )">
+ <constant name="LIGHT_SALMON" value="Color( 1, 0.63, 0.48, 1 )">
Light salmon color.
</constant>
- <constant name="lightseagreen" value="Color( 0.13, 0.7, 0.67, 1 )">
+ <constant name="LIGHT_SEA_GREEN" value="Color( 0.13, 0.7, 0.67, 1 )">
Light sea green color.
</constant>
- <constant name="lightskyblue" value="Color( 0.53, 0.81, 0.98, 1 )">
+ <constant name="LIGHT_SKY_BLUE" value="Color( 0.53, 0.81, 0.98, 1 )">
Light sky blue color.
</constant>
- <constant name="lightslategray" value="Color( 0.47, 0.53, 0.6, 1 )">
+ <constant name="LIGHT_SLATE_GRAY" value="Color( 0.47, 0.53, 0.6, 1 )">
Light slate gray color.
</constant>
- <constant name="lightsteelblue" value="Color( 0.69, 0.77, 0.87, 1 )">
+ <constant name="LIGHT_STEEL_BLUE" value="Color( 0.69, 0.77, 0.87, 1 )">
Light steel blue color.
</constant>
- <constant name="lightyellow" value="Color( 1, 1, 0.88, 1 )">
+ <constant name="LIGHT_YELLOW" value="Color( 1, 1, 0.88, 1 )">
Light yellow color.
</constant>
- <constant name="lime" value="Color( 0, 1, 0, 1 )">
+ <constant name="LIME" value="Color( 0, 1, 0, 1 )">
Lime color.
</constant>
- <constant name="limegreen" value="Color( 0.2, 0.8, 0.2, 1 )">
+ <constant name="LIME_GREEN" value="Color( 0.2, 0.8, 0.2, 1 )">
Lime green color.
</constant>
- <constant name="linen" value="Color( 0.98, 0.94, 0.9, 1 )">
+ <constant name="LINEN" value="Color( 0.98, 0.94, 0.9, 1 )">
Linen color.
</constant>
- <constant name="magenta" value="Color( 1, 0, 1, 1 )">
+ <constant name="MAGENTA" value="Color( 1, 0, 1, 1 )">
Magenta color.
</constant>
- <constant name="maroon" value="Color( 0.69, 0.19, 0.38, 1 )">
+ <constant name="MAROON" value="Color( 0.69, 0.19, 0.38, 1 )">
Maroon color.
</constant>
- <constant name="mediumaquamarine" value="Color( 0.4, 0.8, 0.67, 1 )">
+ <constant name="MEDIUM_AQUAMARINE" value="Color( 0.4, 0.8, 0.67, 1 )">
Medium aquamarine color.
</constant>
- <constant name="mediumblue" value="Color( 0, 0, 0.8, 1 )">
+ <constant name="MEDIUM_BLUE" value="Color( 0, 0, 0.8, 1 )">
Medium blue color.
</constant>
- <constant name="mediumorchid" value="Color( 0.73, 0.33, 0.83, 1 )">
+ <constant name="MEDIUM_ORCHID" value="Color( 0.73, 0.33, 0.83, 1 )">
Medium orchid color.
</constant>
- <constant name="mediumpurple" value="Color( 0.58, 0.44, 0.86, 1 )">
+ <constant name="MEDIUM_PURPLE" value="Color( 0.58, 0.44, 0.86, 1 )">
Medium purple color.
</constant>
- <constant name="mediumseagreen" value="Color( 0.24, 0.7, 0.44, 1 )">
+ <constant name="MEDIUM_SEA_GREEN" value="Color( 0.24, 0.7, 0.44, 1 )">
Medium sea green color.
</constant>
- <constant name="mediumslateblue" value="Color( 0.48, 0.41, 0.93, 1 )">
+ <constant name="MEDIUM_SLATE_BLUE" value="Color( 0.48, 0.41, 0.93, 1 )">
Medium slate blue color.
</constant>
- <constant name="mediumspringgreen" value="Color( 0, 0.98, 0.6, 1 )">
+ <constant name="MEDIUM_SPRING_GREEN" value="Color( 0, 0.98, 0.6, 1 )">
Medium spring green color.
</constant>
- <constant name="mediumturquoise" value="Color( 0.28, 0.82, 0.8, 1 )">
+ <constant name="MEDIUM_TURQUOISE" value="Color( 0.28, 0.82, 0.8, 1 )">
Medium turquoise color.
</constant>
- <constant name="mediumvioletred" value="Color( 0.78, 0.08, 0.52, 1 )">
+ <constant name="MEDIUM_VIOLET_RED" value="Color( 0.78, 0.08, 0.52, 1 )">
Medium violet red color.
</constant>
- <constant name="midnightblue" value="Color( 0.1, 0.1, 0.44, 1 )">
+ <constant name="MIDNIGHT_BLUE" value="Color( 0.1, 0.1, 0.44, 1 )">
Midnight blue color.
</constant>
- <constant name="mintcream" value="Color( 0.96, 1, 0.98, 1 )">
+ <constant name="MINT_CREAM" value="Color( 0.96, 1, 0.98, 1 )">
Mint cream color.
</constant>
- <constant name="mistyrose" value="Color( 1, 0.89, 0.88, 1 )">
+ <constant name="MISTY_ROSE" value="Color( 1, 0.89, 0.88, 1 )">
Misty rose color.
</constant>
- <constant name="moccasin" value="Color( 1, 0.89, 0.71, 1 )">
+ <constant name="MOCCASIN" value="Color( 1, 0.89, 0.71, 1 )">
Moccasin color.
</constant>
- <constant name="navajowhite" value="Color( 1, 0.87, 0.68, 1 )">
+ <constant name="NAVAJO_WHITE" value="Color( 1, 0.87, 0.68, 1 )">
Navajo white color.
</constant>
- <constant name="navyblue" value="Color( 0, 0, 0.5, 1 )">
+ <constant name="NAVY_BLUE" value="Color( 0, 0, 0.5, 1 )">
Navy blue color.
</constant>
- <constant name="oldlace" value="Color( 0.99, 0.96, 0.9, 1 )">
+ <constant name="OLD_LACE" value="Color( 0.99, 0.96, 0.9, 1 )">
Old lace color.
</constant>
- <constant name="olive" value="Color( 0.5, 0.5, 0, 1 )">
+ <constant name="OLIVE" value="Color( 0.5, 0.5, 0, 1 )">
Olive color.
</constant>
- <constant name="olivedrab" value="Color( 0.42, 0.56, 0.14, 1 )">
+ <constant name="OLIVE_DRAB" value="Color( 0.42, 0.56, 0.14, 1 )">
Olive drab color.
</constant>
- <constant name="orange" value="Color( 1, 0.65, 0, 1 )">
+ <constant name="ORANGE" value="Color( 1, 0.65, 0, 1 )">
Orange color.
</constant>
- <constant name="orangered" value="Color( 1, 0.27, 0, 1 )">
+ <constant name="ORANGE_RED" value="Color( 1, 0.27, 0, 1 )">
Orange red color.
</constant>
- <constant name="orchid" value="Color( 0.85, 0.44, 0.84, 1 )">
+ <constant name="ORCHID" value="Color( 0.85, 0.44, 0.84, 1 )">
Orchid color.
</constant>
- <constant name="palegoldenrod" value="Color( 0.93, 0.91, 0.67, 1 )">
+ <constant name="PALE_GOLDENROD" value="Color( 0.93, 0.91, 0.67, 1 )">
Pale goldenrod color.
</constant>
- <constant name="palegreen" value="Color( 0.6, 0.98, 0.6, 1 )">
+ <constant name="PALE_GREEN" value="Color( 0.6, 0.98, 0.6, 1 )">
Pale green color.
</constant>
- <constant name="paleturquoise" value="Color( 0.69, 0.93, 0.93, 1 )">
+ <constant name="PALE_TURQUOISE" value="Color( 0.69, 0.93, 0.93, 1 )">
Pale turquoise color.
</constant>
- <constant name="palevioletred" value="Color( 0.86, 0.44, 0.58, 1 )">
+ <constant name="PALE_VIOLET_RED" value="Color( 0.86, 0.44, 0.58, 1 )">
Pale violet red color.
</constant>
- <constant name="papayawhip" value="Color( 1, 0.94, 0.84, 1 )">
+ <constant name="PAPAYA_WHIP" value="Color( 1, 0.94, 0.84, 1 )">
Papaya whip color.
</constant>
- <constant name="peachpuff" value="Color( 1, 0.85, 0.73, 1 )">
+ <constant name="PEACH_PUFF" value="Color( 1, 0.85, 0.73, 1 )">
Peach puff color.
</constant>
- <constant name="peru" value="Color( 0.8, 0.52, 0.25, 1 )">
+ <constant name="PERU" value="Color( 0.8, 0.52, 0.25, 1 )">
Peru color.
</constant>
- <constant name="pink" value="Color( 1, 0.75, 0.8, 1 )">
+ <constant name="PINK" value="Color( 1, 0.75, 0.8, 1 )">
Pink color.
</constant>
- <constant name="plum" value="Color( 0.87, 0.63, 0.87, 1 )">
+ <constant name="PLUM" value="Color( 0.87, 0.63, 0.87, 1 )">
Plum color.
</constant>
- <constant name="powderblue" value="Color( 0.69, 0.88, 0.9, 1 )">
+ <constant name="POWDER_BLUE" value="Color( 0.69, 0.88, 0.9, 1 )">
Powder blue color.
</constant>
- <constant name="purple" value="Color( 0.63, 0.13, 0.94, 1 )">
+ <constant name="PURPLE" value="Color( 0.63, 0.13, 0.94, 1 )">
Purple color.
</constant>
- <constant name="rebeccapurple" value="Color( 0.4, 0.2, 0.6, 1 )">
+ <constant name="REBECCA_PURPLE" value="Color( 0.4, 0.2, 0.6, 1 )">
Rebecca purple color.
</constant>
- <constant name="red" value="Color( 1, 0, 0, 1 )">
+ <constant name="RED" value="Color( 1, 0, 0, 1 )">
Red color.
</constant>
- <constant name="rosybrown" value="Color( 0.74, 0.56, 0.56, 1 )">
+ <constant name="ROSY_BROWN" value="Color( 0.74, 0.56, 0.56, 1 )">
Rosy brown color.
</constant>
- <constant name="royalblue" value="Color( 0.25, 0.41, 0.88, 1 )">
+ <constant name="ROYAL_BLUE" value="Color( 0.25, 0.41, 0.88, 1 )">
Royal blue color.
</constant>
- <constant name="saddlebrown" value="Color( 0.55, 0.27, 0.07, 1 )">
+ <constant name="SADDLE_BROWN" value="Color( 0.55, 0.27, 0.07, 1 )">
Saddle brown color.
</constant>
- <constant name="salmon" value="Color( 0.98, 0.5, 0.45, 1 )">
+ <constant name="SALMON" value="Color( 0.98, 0.5, 0.45, 1 )">
Salmon color.
</constant>
- <constant name="sandybrown" value="Color( 0.96, 0.64, 0.38, 1 )">
+ <constant name="SANDY_BROWN" value="Color( 0.96, 0.64, 0.38, 1 )">
Sandy brown color.
</constant>
- <constant name="seagreen" value="Color( 0.18, 0.55, 0.34, 1 )">
+ <constant name="SEA_GREEN" value="Color( 0.18, 0.55, 0.34, 1 )">
Sea green color.
</constant>
- <constant name="seashell" value="Color( 1, 0.96, 0.93, 1 )">
+ <constant name="SEASHELL" value="Color( 1, 0.96, 0.93, 1 )">
Seashell color.
</constant>
- <constant name="sienna" value="Color( 0.63, 0.32, 0.18, 1 )">
+ <constant name="SIENNA" value="Color( 0.63, 0.32, 0.18, 1 )">
Sienna color.
</constant>
- <constant name="silver" value="Color( 0.75, 0.75, 0.75, 1 )">
+ <constant name="SILVER" value="Color( 0.75, 0.75, 0.75, 1 )">
Silver color.
</constant>
- <constant name="skyblue" value="Color( 0.53, 0.81, 0.92, 1 )">
+ <constant name="SKY_BLUE" value="Color( 0.53, 0.81, 0.92, 1 )">
Sky blue color.
</constant>
- <constant name="slateblue" value="Color( 0.42, 0.35, 0.8, 1 )">
+ <constant name="SLATE_BLUE" value="Color( 0.42, 0.35, 0.8, 1 )">
Slate blue color.
</constant>
- <constant name="slategray" value="Color( 0.44, 0.5, 0.56, 1 )">
+ <constant name="SLATE_GRAY" value="Color( 0.44, 0.5, 0.56, 1 )">
Slate gray color.
</constant>
- <constant name="snow" value="Color( 1, 0.98, 0.98, 1 )">
+ <constant name="SNOW" value="Color( 1, 0.98, 0.98, 1 )">
Snow color.
</constant>
- <constant name="springgreen" value="Color( 0, 1, 0.5, 1 )">
+ <constant name="SPRING_GREEN" value="Color( 0, 1, 0.5, 1 )">
Spring green color.
</constant>
- <constant name="steelblue" value="Color( 0.27, 0.51, 0.71, 1 )">
+ <constant name="STEEL_BLUE" value="Color( 0.27, 0.51, 0.71, 1 )">
Steel blue color.
</constant>
- <constant name="tan" value="Color( 0.82, 0.71, 0.55, 1 )">
+ <constant name="TAN" value="Color( 0.82, 0.71, 0.55, 1 )">
Tan color.
</constant>
- <constant name="teal" value="Color( 0, 0.5, 0.5, 1 )">
+ <constant name="TEAL" value="Color( 0, 0.5, 0.5, 1 )">
Teal color.
</constant>
- <constant name="thistle" value="Color( 0.85, 0.75, 0.85, 1 )">
+ <constant name="THISTLE" value="Color( 0.85, 0.75, 0.85, 1 )">
Thistle color.
</constant>
- <constant name="tomato" value="Color( 1, 0.39, 0.28, 1 )">
+ <constant name="TOMATO" value="Color( 1, 0.39, 0.28, 1 )">
Tomato color.
</constant>
- <constant name="transparent" value="Color( 1, 1, 1, 0 )">
- Transparent color (white with no alpha).
+ <constant name="TRANSPARENT" value="Color( 1, 1, 1, 0 )">
+ Transparent color (white with zero alpha).
</constant>
- <constant name="turquoise" value="Color( 0.25, 0.88, 0.82, 1 )">
+ <constant name="TURQUOISE" value="Color( 0.25, 0.88, 0.82, 1 )">
Turquoise color.
</constant>
- <constant name="violet" value="Color( 0.93, 0.51, 0.93, 1 )">
+ <constant name="VIOLET" value="Color( 0.93, 0.51, 0.93, 1 )">
Violet color.
</constant>
- <constant name="webgray" value="Color( 0.5, 0.5, 0.5, 1 )">
+ <constant name="WEB_GRAY" value="Color( 0.5, 0.5, 0.5, 1 )">
Web gray color.
</constant>
- <constant name="webgreen" value="Color( 0, 0.5, 0, 1 )">
+ <constant name="WEB_GREEN" value="Color( 0, 0.5, 0, 1 )">
Web green color.
</constant>
- <constant name="webmaroon" value="Color( 0.5, 0, 0, 1 )">
+ <constant name="WEB_MAROON" value="Color( 0.5, 0, 0, 1 )">
Web maroon color.
</constant>
- <constant name="webpurple" value="Color( 0.5, 0, 0.5, 1 )">
+ <constant name="WEB_PURPLE" value="Color( 0.5, 0, 0.5, 1 )">
Web purple color.
</constant>
- <constant name="wheat" value="Color( 0.96, 0.87, 0.7, 1 )">
+ <constant name="WHEAT" value="Color( 0.96, 0.87, 0.7, 1 )">
Wheat color.
</constant>
- <constant name="white" value="Color( 1, 1, 1, 1 )">
+ <constant name="WHITE" value="Color( 1, 1, 1, 1 )">
White color.
</constant>
- <constant name="whitesmoke" value="Color( 0.96, 0.96, 0.96, 1 )">
+ <constant name="WHITE_SMOKE" value="Color( 0.96, 0.96, 0.96, 1 )">
White smoke color.
</constant>
- <constant name="yellow" value="Color( 1, 1, 0, 1 )">
+ <constant name="YELLOW" value="Color( 1, 1, 0, 1 )">
Yellow color.
</constant>
- <constant name="yellowgreen" value="Color( 0.6, 0.8, 0.2, 1 )">
+ <constant name="YELLOW_GREEN" value="Color( 0.6, 0.8, 0.2, 1 )">
Yellow green color.
</constant>
</constants>
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 91e90d051d..0c9df071a7 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -469,46 +469,6 @@
<description>
</description>
</method>
- <method name="native_video_is_playing" qualifiers="const">
- <return type="bool">
- </return>
- <description>
- </description>
- </method>
- <method name="native_video_pause">
- <return type="void">
- </return>
- <description>
- </description>
- </method>
- <method name="native_video_play">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="path" type="String">
- </argument>
- <argument index="1" name="volume" type="float">
- </argument>
- <argument index="2" name="audio_track" type="String">
- </argument>
- <argument index="3" name="subtitle_track" type="String">
- </argument>
- <argument index="4" name="screen" type="int">
- </argument>
- <description>
- </description>
- </method>
- <method name="native_video_stop">
- <return type="void">
- </return>
- <description>
- </description>
- </method>
- <method name="native_video_unpause">
- <return type="void">
- </return>
- <description>
- </description>
- </method>
<method name="process_events">
<return type="void">
</return>
@@ -1065,25 +1025,23 @@
</constant>
<constant name="FEATURE_CUSTOM_CURSOR_SHAPE" value="8" enum="Feature">
</constant>
- <constant name="FEATURE_NATIVE_VIDEO" value="9" enum="Feature">
- </constant>
- <constant name="FEATURE_NATIVE_DIALOG" value="10" enum="Feature">
+ <constant name="FEATURE_NATIVE_DIALOG" value="9" enum="Feature">
</constant>
- <constant name="FEATURE_CONSOLE_WINDOW" value="11" enum="Feature">
+ <constant name="FEATURE_CONSOLE_WINDOW" value="10" enum="Feature">
</constant>
- <constant name="FEATURE_IME" value="12" enum="Feature">
+ <constant name="FEATURE_IME" value="11" enum="Feature">
</constant>
- <constant name="FEATURE_WINDOW_TRANSPARENCY" value="13" enum="Feature">
+ <constant name="FEATURE_WINDOW_TRANSPARENCY" value="12" enum="Feature">
</constant>
- <constant name="FEATURE_HIDPI" value="14" enum="Feature">
+ <constant name="FEATURE_HIDPI" value="13" enum="Feature">
</constant>
- <constant name="FEATURE_ICON" value="15" enum="Feature">
+ <constant name="FEATURE_ICON" value="14" enum="Feature">
</constant>
- <constant name="FEATURE_NATIVE_ICON" value="16" enum="Feature">
+ <constant name="FEATURE_NATIVE_ICON" value="15" enum="Feature">
</constant>
- <constant name="FEATURE_ORIENTATION" value="17" enum="Feature">
+ <constant name="FEATURE_ORIENTATION" value="16" enum="Feature">
</constant>
- <constant name="FEATURE_SWAP_BUFFERS" value="18" enum="Feature">
+ <constant name="FEATURE_SWAP_BUFFERS" value="17" enum="Feature">
</constant>
<constant name="MOUSE_MODE_VISIBLE" value="0" enum="MouseMode">
</constant>
diff --git a/doc/classes/File.xml b/doc/classes/File.xml
index e0781e807f..f0b9156b89 100644
--- a/doc/classes/File.xml
+++ b/doc/classes/File.xml
@@ -275,6 +275,7 @@
</argument>
<description>
Opens a compressed file for reading or writing.
+ [b]Note:[/b] [method open_compressed] can only read files that were saved by Godot, not third-party compression formats. See [url=https://github.com/godotengine/godot/issues/28999]GitHub issue #28999[/url] for a workaround.
</description>
</method>
<method name="open_encrypted">
diff --git a/doc/classes/IP.xml b/doc/classes/IP.xml
index 849f036bbd..44da913492 100644
--- a/doc/classes/IP.xml
+++ b/doc/classes/IP.xml
@@ -4,7 +4,7 @@
Internet protocol (IP) support functions such as DNS resolution.
</brief_description>
<description>
- IP contains support functions for the Internet Protocol (IP). TCP/IP support is in different classes (see [StreamPeerTCP] and [TCP_Server]). IP provides DNS hostname resolution support, both blocking and threaded.
+ IP contains support functions for the Internet Protocol (IP). TCP/IP support is in different classes (see [StreamPeerTCP] and [TCPServer]). IP provides DNS hostname resolution support, both blocking and threaded.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 8c90108aef..cb95deb9a0 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -143,7 +143,7 @@
Returns the command-line arguments passed to the engine.
Command-line arguments can be written in any form, including both [code]--key value[/code] and [code]--key=value[/code] forms so they can be properly parsed, as long as custom command-line arguments do not conflict with engine arguments.
You can also incorporate environment variables using the [method get_environment] method.
- You can set [code]editor/main_run_args[/code] in the Project Settings to define command-line arguments to be passed by the editor when running the project.
+ You can set [member ProjectSettings.editor/run/main_run_args] to define command-line arguments to be passed by the editor when running the project.
Here's a minimal example on how to parse command-line arguments into a dictionary using the [code]--key=value[/code] form for arguments:
[codeblocks]
[gdscript]
@@ -348,6 +348,7 @@
</return>
<description>
Returns a string that is unique to the device.
+ [b]Note:[/b] This string may change without notice if the user reinstalls/upgrades their operating system or changes their hardware. This means it should generally not be used to encrypt persistent data as the data saved prior to an unexpected ID change would become inaccessible. The returned string may also be falsified using external programs, so do not rely on the string returned by [method get_unique_id] for security purposes.
[b]Note:[/b] Returns an empty string on HTML5 and UWP, as this method isn't implemented on those platforms yet.
</description>
</method>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 005873c2ff..3200f789b8 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -542,6 +542,14 @@
<member name="editor/node_naming/name_num_separator" type="int" setter="" getter="" default="0">
What to use to separate node name from number. This is mostly an editor setting.
</member>
+ <member name="editor/run/main_run_args" type="String" setter="" getter="" default="&quot;&quot;">
+ The command-line arguments to append to Godot's own command line when running the project. This doesn't affect the editor itself.
+ It is possible to make another executable run Godot by using the [code]%command%[/code] placeholder. The placeholder will be replaced with Godot's own command line. Program-specific arguments should be placed [i]before[/i] the placeholder, whereas Godot-specific arguments should be placed [i]after[/i] the placeholder.
+ For example, this can be used to force the project to run on the dedicated GPU in a NVIDIA Optimus system on Linux:
+ [codeblock]
+ prime-run %command%
+ [/codeblock]
+ </member>
<member name="editor/script/search_in_file_extensions" type="PackedStringArray" setter="" getter="" default="PackedStringArray( &quot;gd&quot;, &quot;shader&quot; )">
Text-based file extensions to include in the script editor's "Find in Files" feature. You can add e.g. [code]tscn[/code] if you wish to also parse your scene files, especially if you use built-in scripts which are serialized in the scene files.
</member>
diff --git a/doc/classes/Shape3D.xml b/doc/classes/Shape3D.xml
index f8b749aebf..b5f70132d7 100644
--- a/doc/classes/Shape3D.xml
+++ b/doc/classes/Shape3D.xml
@@ -10,6 +10,13 @@
<link title="Physics introduction">https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link>
</tutorials>
<methods>
+ <method name="get_debug_mesh">
+ <return type="ArrayMesh">
+ </return>
+ <description>
+ Returns the [ArrayMesh] used to draw the debug collision for this [Shape3D].
+ </description>
+ </method>
</methods>
<members>
<member name="margin" type="float" setter="set_margin" getter="get_margin" default="0.04">
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index c6dd6fb142..0b278d7d25 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -91,6 +91,15 @@
Returns the overall transform of the specified bone, with respect to the skeleton. Being relative to the skeleton frame, this is not the actual "global" transform of the bone.
</description>
</method>
+ <method name="get_bone_global_pose_no_override" qualifiers="const">
+ <return type="Transform">
+ </return>
+ <argument index="0" name="bone_idx" type="int">
+ </argument>
+ <description>
+ Returns the overall transform of the specified bone, with respect to the skeleton, but without any global pose overrides. Being relative to the skeleton frame, this is not the actual "global" transform of the bone.
+ </description>
+ </method>
<method name="get_bone_name" qualifiers="const">
<return type="String">
</return>
diff --git a/doc/classes/TCP_Server.xml b/doc/classes/TCPServer.xml
index ec91d75d47..28f06ad3ae 100644
--- a/doc/classes/TCP_Server.xml
+++ b/doc/classes/TCPServer.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="TCP_Server" inherits="Reference" version="4.0">
+<class name="TCPServer" inherits="Reference" version="4.0">
<brief_description>
A TCP server.
</brief_description>
diff --git a/doc/classes/TileData.xml b/doc/classes/TileData.xml
new file mode 100644
index 0000000000..e3bc910ab6
--- /dev/null
+++ b/doc/classes/TileData.xml
@@ -0,0 +1,245 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="TileData" inherits="Object" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="add_collision_shape">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_collision_shape_one_way_margin" qualifiers="const">
+ <return type="float">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <argument index="1" name="shape_index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_collision_shape_shape" qualifiers="const">
+ <return type="Shape2D">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <argument index="1" name="shape_index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_collision_shapes_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_custom_data" qualifiers="const">
+ <return type="Variant">
+ </return>
+ <argument index="0" name="layer_name" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_custom_data_by_layer_id" qualifiers="const">
+ <return type="Variant">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_navigation_polygon" qualifiers="const">
+ <return type="NavigationPolygon">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_occluder" qualifiers="const">
+ <return type="OccluderPolygon2D">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_peering_bit_terrain" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="peering_bit" type="int" enum="TileSet.CellNeighbor">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="is_collision_shape_one_way" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <argument index="1" name="shape_index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="remove_collision_shape">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <argument index="1" name="shape_index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_collision_shape_one_way">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <argument index="1" name="shape_index" type="int">
+ </argument>
+ <argument index="2" name="one_way" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_collision_shape_one_way_margin">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <argument index="1" name="shape_index" type="int">
+ </argument>
+ <argument index="2" name="one_way_margin" type="float">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_collision_shape_shape">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <argument index="1" name="shape_index" type="int">
+ </argument>
+ <argument index="2" name="shape" type="Shape2D">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_collision_shapes_count">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <argument index="1" name="shapes_count" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_custom_data">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer_name" type="String">
+ </argument>
+ <argument index="1" name="value" type="Variant">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_custom_data_by_layer_id">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <argument index="1" name="value" type="Variant">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_navigation_polygon">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <argument index="1" name="navigation_polygon" type="NavigationPolygon">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_occluder">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer_id" type="int">
+ </argument>
+ <argument index="1" name="occluder_polygon" type="OccluderPolygon2D">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_peering_bit_terrain">
+ <return type="void">
+ </return>
+ <argument index="0" name="peering_bit" type="int" enum="TileSet.CellNeighbor">
+ </argument>
+ <argument index="1" name="terrain" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="tile_get_material" qualifiers="const">
+ <return type="ShaderMaterial">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="tile_set_material">
+ <return type="void">
+ </return>
+ <argument index="0" name="material" type="ShaderMaterial">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="flip_h" type="bool" setter="set_flip_h" getter="get_flip_h" default="false">
+ </member>
+ <member name="flip_v" type="bool" setter="set_flip_v" getter="get_flip_v" default="false">
+ </member>
+ <member name="modulate" type="Color" setter="set_modulate" getter="get_modulate" default="Color( 1, 1, 1, 1 )">
+ </member>
+ <member name="probability" type="float" setter="set_probability" getter="get_probability" default="1.0">
+ </member>
+ <member name="terrain_set" type="int" setter="set_terrain_set" getter="get_terrain_set" default="-1">
+ </member>
+ <member name="texture_offset" type="Vector2i" setter="set_texture_offset" getter="get_texture_offset" default="Vector2i( 0, 0 )">
+ </member>
+ <member name="transpose" type="bool" setter="set_transpose" getter="get_transpose" default="false">
+ </member>
+ <member name="y_sort_origin" type="Vector2i" setter="set_y_sort_origin" getter="get_y_sort_origin" default="Vector2i( 0, 0 )">
+ </member>
+ <member name="z_index" type="int" setter="set_z_index" getter="get_z_index" default="0">
+ </member>
+ </members>
+ <signals>
+ <signal name="changed">
+ <description>
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 205b342ba8..76d8f1840f 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -31,53 +31,46 @@
Clears cells that do not exist in the tileset.
</description>
</method>
- <method name="get_cell" qualifiers="const">
+ <method name="get_cell_alternative_tile" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="x" type="int">
- </argument>
- <argument index="1" name="y" type="int">
+ <argument index="0" name="coords" type="Vector2i">
</argument>
<description>
- Returns the tile index of the given cell. If no tile exists in the cell, returns [constant INVALID_CELL].
</description>
</method>
- <method name="get_cell_autotile_coord" qualifiers="const">
- <return type="Vector2">
+ <method name="get_cell_atlas_coords" qualifiers="const">
+ <return type="Vector2i">
</return>
- <argument index="0" name="x" type="int">
- </argument>
- <argument index="1" name="y" type="int">
+ <argument index="0" name="coords" type="Vector2i">
</argument>
<description>
- Returns the coordinate (subtile column and row) of the autotile variation in the tileset. Returns a zero vector if the cell doesn't have autotiling.
</description>
</method>
- <method name="get_cellv" qualifiers="const">
+ <method name="get_cell_source_id" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="position" type="Vector2">
+ <argument index="0" name="coords" type="Vector2i">
</argument>
<description>
- Returns the tile index of the cell given by a Vector2. If no tile exists in the cell, returns [constant INVALID_CELL].
</description>
</method>
- <method name="get_collision_layer_bit" qualifiers="const">
- <return type="bool">
+ <method name="get_neighbor_cell" qualifiers="const">
+ <return type="Vector2i">
</return>
- <argument index="0" name="bit" type="int">
+ <argument index="0" name="coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="neighbor" type="int" enum="TileSet.CellNeighbor">
</argument>
<description>
- Returns [code]true[/code] if the given collision layer bit is set.
</description>
</method>
- <method name="get_collision_mask_bit" qualifiers="const">
- <return type="bool">
+ <method name="get_surrounding_tiles">
+ <return type="Vector2i[]">
</return>
- <argument index="0" name="bit" type="int">
+ <argument index="0" name="coords" type="Vector2i">
</argument>
<description>
- Returns [code]true[/code] if the given collision mask bit is set.
</description>
</method>
<method name="get_used_cells" qualifiers="const">
@@ -87,15 +80,6 @@
Returns a [Vector2] array with the positions of all cells containing a tile from the tileset (i.e. a tile index different from [code]-1[/code]).
</description>
</method>
- <method name="get_used_cells_by_index" qualifiers="const">
- <return type="Vector2i[]">
- </return>
- <argument index="0" name="index" type="int">
- </argument>
- <description>
- Returns an array of all cells with the given tile [code]index[/code].
- </description>
- </method>
<method name="get_used_rect">
<return type="Rect2">
</return>
@@ -103,155 +87,26 @@
Returns a rectangle enclosing the used (non-empty) tiles of the map.
</description>
</method>
- <method name="is_cell_transposed" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="x" type="int">
- </argument>
- <argument index="1" name="y" type="int">
- </argument>
- <description>
- Returns [code]true[/code] if the given cell is transposed, i.e. the X and Y axes are swapped.
- </description>
- </method>
- <method name="is_cell_x_flipped" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="x" type="int">
- </argument>
- <argument index="1" name="y" type="int">
- </argument>
- <description>
- Returns [code]true[/code] if the given cell is flipped in the X axis.
- </description>
- </method>
- <method name="is_cell_y_flipped" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="x" type="int">
- </argument>
- <argument index="1" name="y" type="int">
- </argument>
- <description>
- Returns [code]true[/code] if the given cell is flipped in the Y axis.
- </description>
- </method>
<method name="map_to_world" qualifiers="const">
<return type="Vector2">
</return>
<argument index="0" name="map_position" type="Vector2">
</argument>
- <argument index="1" name="ignore_half_ofs" type="bool" default="false">
- </argument>
<description>
- Returns the local position corresponding to the given tilemap (grid-based) coordinates.
- Optionally, the tilemap's half offset can be ignored.
</description>
</method>
<method name="set_cell">
<return type="void">
</return>
- <argument index="0" name="x" type="int">
- </argument>
- <argument index="1" name="y" type="int">
- </argument>
- <argument index="2" name="tile" type="int">
- </argument>
- <argument index="3" name="flip_x" type="bool" default="false">
- </argument>
- <argument index="4" name="flip_y" type="bool" default="false">
- </argument>
- <argument index="5" name="transpose" type="bool" default="false">
- </argument>
- <argument index="6" name="autotile_coord" type="Vector2" default="Vector2( 0, 0 )">
- </argument>
- <description>
- Sets the tile index for the cell given by a Vector2.
- An index of [code]-1[/code] clears the cell.
- Optionally, the tile can also be flipped, transposed, or given autotile coordinates. The autotile coordinate refers to the column and row of the subtile.
- [b]Note:[/b] Data such as navigation polygons and collision shapes are not immediately updated for performance reasons.
- If you need these to be immediately updated, you can call [method update_dirty_quadrants].
- Overriding this method also overrides it internally, allowing custom logic to be implemented when tiles are placed/removed:
- [codeblocks]
- [gdscript]
- func set_cell(x, y, tile, flip_x=false, flip_y=false, transpose=false, autotile_coord=Vector2()):
- # Write your custom logic here.
- # To call the default method:
- .set_cell(x, y, tile, flip_x, flip_y, transpose, autotile_coord)
- [/gdscript]
- [csharp]
- public void SetCell(int x, int y, int tile, bool flipX = false, bool flipY = false, bool transpose = false, Vector2 autotileCoord = new Vector2())
- {
- // Write your custom logic here.
- // To call the default method:
- base.SetCell(x, y, tile, flipX, flipY, transpose, autotileCoord);
- }
- [/csharp]
- [/codeblocks]
- </description>
- </method>
- <method name="set_cellv">
- <return type="void">
- </return>
- <argument index="0" name="position" type="Vector2">
- </argument>
- <argument index="1" name="tile" type="int">
- </argument>
- <argument index="2" name="flip_x" type="bool" default="false">
- </argument>
- <argument index="3" name="flip_y" type="bool" default="false">
- </argument>
- <argument index="4" name="transpose" type="bool" default="false">
- </argument>
- <description>
- Sets the tile index for the given cell.
- An index of [code]-1[/code] clears the cell.
- Optionally, the tile can also be flipped or transposed.
- [b]Note:[/b] Data such as navigation polygons and collision shapes are not immediately updated for performance reasons.
- If you need these to be immediately updated, you can call [method update_dirty_quadrants].
- </description>
- </method>
- <method name="set_collision_layer_bit">
- <return type="void">
- </return>
- <argument index="0" name="bit" type="int">
- </argument>
- <argument index="1" name="value" type="bool">
- </argument>
- <description>
- Sets the given collision layer bit.
- </description>
- </method>
- <method name="set_collision_mask_bit">
- <return type="void">
- </return>
- <argument index="0" name="bit" type="int">
+ <argument index="0" name="coords" type="Vector2i">
</argument>
- <argument index="1" name="value" type="bool">
+ <argument index="1" name="source_id" type="int" default="-1">
</argument>
- <description>
- Sets the given collision mask bit.
- </description>
- </method>
- <method name="update_bitmask_area">
- <return type="void">
- </return>
- <argument index="0" name="position" type="Vector2">
- </argument>
- <description>
- Applies autotiling rules to the cell (and its adjacent cells) referenced by its grid-based X and Y coordinates.
- </description>
- </method>
- <method name="update_bitmask_region">
- <return type="void">
- </return>
- <argument index="0" name="start" type="Vector2" default="Vector2( 0, 0 )">
+ <argument index="2" name="atlas_coords" type="Vector2i" default="Vector2i( -1, -1 )">
</argument>
- <argument index="1" name="end" type="Vector2" default="Vector2( 0, 0 )">
+ <argument index="3" name="alternative_tile" type="int" default="-1">
</argument>
<description>
- Applies autotiling rules to the cells in the given region (specified by grid-based X and Y coordinates).
- Calling with invalid (or missing) parameters applies autotiling rules for the entire tilemap.
</description>
</method>
<method name="update_dirty_quadrants">
@@ -262,7 +117,7 @@
</description>
</method>
<method name="world_to_map" qualifiers="const">
- <return type="Vector2">
+ <return type="Vector2i">
</return>
<argument index="0" name="world_position" type="Vector2">
</argument>
@@ -272,110 +127,19 @@
</method>
</methods>
<members>
- <member name="bake_navigation" type="bool" setter="set_bake_navigation" getter="is_baking_navigation" default="false">
- If [code]true[/code], this TileMap bakes a navigation region.
- </member>
- <member name="cell_clip_uv" type="bool" setter="set_clip_uv" getter="get_clip_uv" default="false">
- If [code]true[/code], the cell's UVs will be clipped.
- </member>
- <member name="cell_custom_transform" type="Transform2D" setter="set_custom_transform" getter="get_custom_transform" default="Transform2D( 64, 0, 0, 64, 0, 0 )">
- The custom [Transform2D] to be applied to the TileMap's cells.
- </member>
- <member name="cell_half_offset" type="int" setter="set_half_offset" getter="get_half_offset" enum="TileMap.HalfOffset" default="2">
- Amount to offset alternating tiles. See [enum HalfOffset] for possible values.
- </member>
<member name="cell_quadrant_size" type="int" setter="set_quadrant_size" getter="get_quadrant_size" default="16">
The TileMap's quadrant size. Optimizes drawing by batching, using chunks of this size.
</member>
- <member name="cell_size" type="Vector2" setter="set_cell_size" getter="get_cell_size" default="Vector2( 64, 64 )">
- The TileMap's cell size.
- </member>
- <member name="cell_tile_origin" type="int" setter="set_tile_origin" getter="get_tile_origin" enum="TileMap.TileOrigin" default="0">
- Position for tile origin. See [enum TileOrigin] for possible values.
- </member>
- <member name="cell_y_sort" type="bool" setter="set_y_sort_enabled" getter="is_y_sort_enabled" default="false">
- If [code]true[/code], the TileMap's direct children will be drawn in order of their Y coordinate.
- </member>
- <member name="centered_textures" type="bool" setter="set_centered_textures" getter="is_centered_textures_enabled" default="false">
- If [code]true[/code], the textures will be centered in the middle of each tile. This is useful for certain isometric or top-down modes when textures are made larger or smaller than the tiles (e.g. to avoid flickering on tile edges). The offset is still applied, but from the center of the tile. If used, [member compatibility_mode] is ignored.
- If [code]false[/code], the texture position start in the top-left corner unless [member compatibility_mode] is enabled.
- </member>
- <member name="collision_bounce" type="float" setter="set_collision_bounce" getter="get_collision_bounce" default="0.0">
- Bounce value for static body collisions (see [code]collision_use_kinematic[/code]).
- </member>
- <member name="collision_friction" type="float" setter="set_collision_friction" getter="get_collision_friction" default="1.0">
- Friction value for static body collisions (see [code]collision_use_kinematic[/code]).
- </member>
- <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1">
- The collision layer(s) for all colliders in the TileMap. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
- </member>
- <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
- The collision mask(s) for all colliders in the TileMap. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
- </member>
- <member name="collision_use_kinematic" type="bool" setter="set_collision_use_kinematic" getter="get_collision_use_kinematic" default="false">
- If [code]true[/code], TileMap collisions will be handled as a kinematic body. If [code]false[/code], collisions will be handled as static body.
- </member>
- <member name="collision_use_parent" type="bool" setter="set_collision_use_parent" getter="get_collision_use_parent" default="false">
- If [code]true[/code], this tilemap's collision shape will be added to the collision shape of the parent. The parent has to be a [CollisionObject2D].
- </member>
- <member name="compatibility_mode" type="bool" setter="set_compatibility_mode" getter="is_compatibility_mode_enabled" default="false">
- If [code]true[/code], the compatibility with the tilemaps made in Godot 3.1 or earlier is maintained (textures move when the tile origin changes and rotate if the texture size is not homogeneous). This mode presents problems when doing [code]flip_h[/code], [code]flip_v[/code] and [code]transpose[/code] tile operations on non-homogeneous isometric tiles (e.g. 2:1), in which the texture could not coincide with the collision, thus it is not recommended for isometric or non-square tiles.
- If [code]false[/code], the textures do not move when doing [code]flip_h[/code], [code]flip_v[/code] operations if no offset is used, nor when changing the tile origin.
- The compatibility mode doesn't work with the [member centered_textures] option, because displacing textures with the [member cell_tile_origin] option or in irregular tiles is not relevant when centering those textures.
- </member>
- <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="TileMap.Mode" default="0">
- The TileMap orientation mode. See [enum Mode] for possible values.
- </member>
- <member name="occluder_light_mask" type="int" setter="set_occluder_light_mask" getter="get_occluder_light_mask" default="1">
- The light mask assigned to all light occluders in the TileMap. The TileSet's light occluders will cast shadows only from Light2D(s) that have the same light mask(s).
- </member>
<member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset">
The assigned [TileSet].
</member>
</members>
<signals>
- <signal name="settings_changed">
+ <signal name="changed">
<description>
- Emitted when a tilemap setting has changed.
</description>
</signal>
</signals>
<constants>
- <constant name="INVALID_CELL" value="-1">
- Returned when a cell doesn't exist.
- </constant>
- <constant name="MODE_SQUARE" value="0" enum="Mode">
- Orthogonal orientation mode.
- </constant>
- <constant name="MODE_ISOMETRIC" value="1" enum="Mode">
- Isometric orientation mode.
- </constant>
- <constant name="MODE_CUSTOM" value="2" enum="Mode">
- Custom orientation mode.
- </constant>
- <constant name="HALF_OFFSET_X" value="0" enum="HalfOffset">
- Half offset on the X coordinate.
- </constant>
- <constant name="HALF_OFFSET_Y" value="1" enum="HalfOffset">
- Half offset on the Y coordinate.
- </constant>
- <constant name="HALF_OFFSET_DISABLED" value="2" enum="HalfOffset">
- Half offset disabled.
- </constant>
- <constant name="HALF_OFFSET_NEGATIVE_X" value="3" enum="HalfOffset">
- Half offset on the X coordinate (negative).
- </constant>
- <constant name="HALF_OFFSET_NEGATIVE_Y" value="4" enum="HalfOffset">
- Half offset on the Y coordinate (negative).
- </constant>
- <constant name="TILE_ORIGIN_TOP_LEFT" value="0" enum="TileOrigin">
- Tile origin at its top-left corner.
- </constant>
- <constant name="TILE_ORIGIN_CENTER" value="1" enum="TileOrigin">
- Tile origin at its center.
- </constant>
- <constant name="TILE_ORIGIN_BOTTOM_LEFT" value="2" enum="TileOrigin">
- Tile origin at its bottom-left corner.
- </constant>
</constants>
</class>
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index adc5880c71..13c6b9070a 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -17,752 +17,347 @@
<link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/113</link>
</tutorials>
<methods>
- <method name="_forward_atlas_subtile_selection" qualifiers="virtual">
- <return type="Vector2">
- </return>
- <argument index="0" name="atlastile_id" type="int">
- </argument>
- <argument index="1" name="tilemap" type="Object">
- </argument>
- <argument index="2" name="tile_location" type="Vector2">
- </argument>
- <description>
- </description>
- </method>
- <method name="_forward_subtile_selection" qualifiers="virtual">
- <return type="Vector2">
- </return>
- <argument index="0" name="autotile_id" type="int">
- </argument>
- <argument index="1" name="bitmask" type="int">
- </argument>
- <argument index="2" name="tilemap" type="Object">
- </argument>
- <argument index="3" name="tile_location" type="Vector2">
- </argument>
- <description>
- </description>
- </method>
- <method name="_is_tile_bound" qualifiers="virtual">
- <return type="bool">
- </return>
- <argument index="0" name="drawn_id" type="int">
- </argument>
- <argument index="1" name="neighbor_id" type="int">
- </argument>
- <description>
- Determines when the auto-tiler should consider two different auto-tile IDs to be bound together.
- [b]Note:[/b] [code]neighbor_id[/code] will be [code]-1[/code] ([constant TileMap.INVALID_CELL]) when checking a tile against an empty neighbor tile.
- </description>
- </method>
- <method name="autotile_clear_bitmask_map">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Clears all bitmask information of the autotile.
- </description>
- </method>
- <method name="autotile_get_bitmask">
+ <method name="add_source">
<return type="int">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="coord" type="Vector2">
+ <argument index="0" name="atlas_source_id_override" type="TileSetAtlasSource">
</argument>
- <description>
- Returns the bitmask of the subtile from an autotile given its coordinates.
- The value is the sum of the values in [enum AutotileBindings] present in the subtile (e.g. a value of 5 means the bitmask has bindings in both the top left and top right).
- </description>
- </method>
- <method name="autotile_get_bitmask_mode" qualifiers="const">
- <return type="int" enum="TileSet.BitmaskMode">
- </return>
- <argument index="0" name="id" type="int">
+ <argument index="1" name="arg1" type="int" default="-1">
</argument>
<description>
- Returns the [enum BitmaskMode] of the autotile.
</description>
</method>
- <method name="autotile_get_icon_coordinate" qualifiers="const">
- <return type="Vector2">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the subtile that's being used as an icon in an atlas/autotile given its coordinates.
- The subtile defined as the icon will be used as a fallback when the atlas/autotile's bitmask information is incomplete. It will also be used to represent it in the TileSet editor.
- </description>
- </method>
- <method name="autotile_get_light_occluder" qualifiers="const">
- <return type="OccluderPolygon2D">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="coord" type="Vector2">
- </argument>
- <description>
- Returns the light occluder of the subtile from an atlas/autotile given its coordinates.
- </description>
- </method>
- <method name="autotile_get_navigation_polygon" qualifiers="const">
- <return type="NavigationPolygon">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="coord" type="Vector2">
- </argument>
- <description>
- Returns the navigation polygon of the subtile from an atlas/autotile given its coordinates.
- </description>
- </method>
- <method name="autotile_get_size" qualifiers="const">
- <return type="Vector2">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the size of the subtiles in an atlas/autotile.
- </description>
- </method>
- <method name="autotile_get_spacing" qualifiers="const">
+ <method name="get_navigation_layer_layers" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="layer_index" type="int">
</argument>
<description>
- Returns the spacing between subtiles of the atlas/autotile.
</description>
</method>
- <method name="autotile_get_subtile_priority">
+ <method name="get_next_source_id" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="coord" type="Vector2">
- </argument>
<description>
- Returns the priority of the subtile from an autotile given its coordinates.
- When more than one subtile has the same bitmask value, one of them will be picked randomly for drawing. Its priority will define how often it will be picked.
</description>
</method>
- <method name="autotile_get_z_index">
+ <method name="get_occlusion_layer_light_mask" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="coord" type="Vector2">
+ <argument index="0" name="arg0" type="int">
</argument>
<description>
- Returns the drawing index of the subtile from an atlas/autotile given its coordinates.
</description>
</method>
- <method name="autotile_set_bitmask">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="bitmask" type="Vector2">
- </argument>
- <argument index="2" name="flag" type="int">
- </argument>
- <description>
- Sets the bitmask of the subtile from an autotile given its coordinates.
- The value is the sum of the values in [enum AutotileBindings] present in the subtile (e.g. a value of 5 means the bitmask has bindings in both the top left and top right).
- </description>
- </method>
- <method name="autotile_set_bitmask_mode">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="mode" type="int" enum="TileSet.BitmaskMode">
- </argument>
- <description>
- Sets the [enum BitmaskMode] of the autotile.
- </description>
- </method>
- <method name="autotile_set_icon_coordinate">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="coord" type="Vector2">
- </argument>
- <description>
- Sets the subtile that will be used as an icon in an atlas/autotile given its coordinates.
- The subtile defined as the icon will be used as a fallback when the atlas/autotile's bitmask information is incomplete. It will also be used to represent it in the TileSet editor.
- </description>
- </method>
- <method name="autotile_set_light_occluder">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="light_occluder" type="OccluderPolygon2D">
- </argument>
- <argument index="2" name="coord" type="Vector2">
- </argument>
- <description>
- Sets the light occluder of the subtile from an atlas/autotile given its coordinates.
- </description>
- </method>
- <method name="autotile_set_navigation_polygon">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="navigation_polygon" type="NavigationPolygon">
- </argument>
- <argument index="2" name="coord" type="Vector2">
- </argument>
- <description>
- Sets the navigation polygon of the subtile from an atlas/autotile given its coordinates.
- </description>
- </method>
- <method name="autotile_set_size">
- <return type="void">
+ <method name="get_occlusion_layer_sdf_collision" qualifiers="const">
+ <return type="bool">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="size" type="Vector2">
+ <argument index="0" name="arg0" type="int">
</argument>
<description>
- Sets the size of the subtiles in an atlas/autotile.
</description>
</method>
- <method name="autotile_set_spacing">
- <return type="void">
+ <method name="get_physics_layer_collision_layer" qualifiers="const">
+ <return type="int">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="spacing" type="int">
+ <argument index="0" name="layer_index" type="int">
</argument>
<description>
- Sets the spacing between subtiles of the atlas/autotile.
</description>
</method>
- <method name="autotile_set_subtile_priority">
- <return type="void">
+ <method name="get_physics_layer_collision_mask" qualifiers="const">
+ <return type="int">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="coord" type="Vector2">
- </argument>
- <argument index="2" name="priority" type="int">
+ <argument index="0" name="layer_index" type="int">
</argument>
<description>
- Sets the priority of the subtile from an autotile given its coordinates.
- When more than one subtile has the same bitmask value, one of them will be picked randomly for drawing. Its priority will define how often it will be picked.
</description>
</method>
- <method name="autotile_set_z_index">
- <return type="void">
+ <method name="get_physics_layer_physics_material" qualifiers="const">
+ <return type="PhysicsMaterial">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="coord" type="Vector2">
- </argument>
- <argument index="2" name="z_index" type="int">
+ <argument index="0" name="layer_index" type="int">
</argument>
<description>
- Sets the drawing index of the subtile from an atlas/autotile given its coordinates.
</description>
</method>
- <method name="clear">
- <return type="void">
+ <method name="get_source" qualifiers="const">
+ <return type="TileSetSource">
</return>
- <description>
- Clears all tiles.
- </description>
- </method>
- <method name="create_tile">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="index" type="int">
</argument>
<description>
- Creates a new tile with the given ID.
</description>
</method>
- <method name="find_tile_by_name" qualifiers="const">
+ <method name="get_source_count" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="name" type="String">
- </argument>
<description>
- Returns the first tile matching the given name.
</description>
</method>
- <method name="get_last_unused_tile_id" qualifiers="const">
+ <method name="get_source_id" qualifiers="const">
<return type="int">
</return>
- <description>
- Returns the ID following the last currently used ID, useful when creating a new tile.
- </description>
- </method>
- <method name="get_tiles_ids" qualifiers="const">
- <return type="Array">
- </return>
- <description>
- Returns an array of all currently used tile IDs.
- </description>
- </method>
- <method name="remove_tile">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="index" type="int">
</argument>
<description>
- Removes the given tile ID.
</description>
</method>
- <method name="tile_add_shape">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="shape" type="Shape2D">
- </argument>
- <argument index="2" name="shape_transform" type="Transform2D">
- </argument>
- <argument index="3" name="one_way" type="bool" default="false">
- </argument>
- <argument index="4" name="autotile_coord" type="Vector2" default="Vector2( 0, 0 )">
- </argument>
- <description>
- Adds a shape to the tile.
- </description>
- </method>
- <method name="tile_get_light_occluder" qualifiers="const">
- <return type="OccluderPolygon2D">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the tile's light occluder.
- </description>
- </method>
- <method name="tile_get_material" qualifiers="const">
- <return type="ShaderMaterial">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the tile's material.
- </description>
- </method>
- <method name="tile_get_modulate" qualifiers="const">
+ <method name="get_terrain_color" qualifiers="const">
<return type="Color">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the tile's modulation color.
- </description>
- </method>
- <method name="tile_get_name" qualifiers="const">
- <return type="String">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the tile's name.
- </description>
- </method>
- <method name="tile_get_navigation_polygon" qualifiers="const">
- <return type="NavigationPolygon">
- </return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="terrain_set" type="int">
</argument>
- <description>
- Returns the navigation polygon of the tile.
- </description>
- </method>
- <method name="tile_get_navigation_polygon_offset" qualifiers="const">
- <return type="Vector2">
- </return>
- <argument index="0" name="id" type="int">
+ <argument index="1" name="terrain_index" type="int">
</argument>
<description>
- Returns the offset of the tile's navigation polygon.
</description>
</method>
- <method name="tile_get_occluder_offset" qualifiers="const">
- <return type="Vector2">
+ <method name="get_terrain_name" qualifiers="const">
+ <return type="String">
</return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="terrain_set" type="int">
</argument>
- <description>
- Returns the offset of the tile's light occluder.
- </description>
- </method>
- <method name="tile_get_region" qualifiers="const">
- <return type="Rect2">
- </return>
- <argument index="0" name="id" type="int">
+ <argument index="1" name="terrain_index" type="int">
</argument>
<description>
- Returns the tile sub-region in the texture.
</description>
</method>
- <method name="tile_get_shape" qualifiers="const">
- <return type="Shape2D">
+ <method name="get_terrain_set_mode" qualifiers="const">
+ <return type="int" enum="TileSet.TerrainMode">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="shape_id" type="int">
+ <argument index="0" name="terrain_set" type="int">
</argument>
<description>
- Returns a tile's given shape.
</description>
</method>
- <method name="tile_get_shape_count" qualifiers="const">
+ <method name="get_terrains_count" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the number of shapes assigned to a tile.
- </description>
- </method>
- <method name="tile_get_shape_offset" qualifiers="const">
- <return type="Vector2">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="shape_id" type="int">
+ <argument index="0" name="terrain_set" type="int">
</argument>
<description>
- Returns the offset of a tile's shape.
</description>
</method>
- <method name="tile_get_shape_one_way" qualifiers="const">
+ <method name="has_source" qualifiers="const">
<return type="bool">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="shape_id" type="int">
- </argument>
- <description>
- Returns the one-way collision value of a tile's shape.
- </description>
- </method>
- <method name="tile_get_shape_one_way_margin" qualifiers="const">
- <return type="float">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="shape_id" type="int">
- </argument>
- <description>
- </description>
- </method>
- <method name="tile_get_shape_transform" qualifiers="const">
- <return type="Transform2D">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="shape_id" type="int">
- </argument>
- <description>
- Returns the [Transform2D] of a tile's shape.
- </description>
- </method>
- <method name="tile_get_shapes" qualifiers="const">
- <return type="Array">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns an array of dictionaries describing the tile's shapes.
- [b]Dictionary structure in the array returned by this method:[/b]
- [codeblock]
- {
- "autotile_coord": Vector2,
- "one_way": bool,
- "one_way_margin": int,
- "shape": CollisionShape2D,
- "shape_transform": Transform2D,
- }
- [/codeblock]
- </description>
- </method>
- <method name="tile_get_texture" qualifiers="const">
- <return type="Texture2D">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the tile's texture.
- </description>
- </method>
- <method name="tile_get_texture_offset" qualifiers="const">
- <return type="Vector2">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the texture offset of the tile.
- </description>
- </method>
- <method name="tile_get_tile_mode" qualifiers="const">
- <return type="int" enum="TileSet.TileMode">
- </return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="index" type="int">
</argument>
<description>
- Returns the tile's [enum TileMode].
</description>
</method>
- <method name="tile_get_z_index" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the tile's Z index (drawing layer).
- </description>
- </method>
- <method name="tile_set_light_occluder">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="light_occluder" type="OccluderPolygon2D">
- </argument>
- <description>
- Sets a light occluder for the tile.
- </description>
- </method>
- <method name="tile_set_material">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="material" type="ShaderMaterial">
- </argument>
- <description>
- Sets the tile's material.
- </description>
- </method>
- <method name="tile_set_modulate">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="color" type="Color">
- </argument>
- <description>
- Sets the tile's modulation color.
- </description>
- </method>
- <method name="tile_set_name">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="name" type="String">
- </argument>
- <description>
- Sets the tile's name.
- </description>
- </method>
- <method name="tile_set_navigation_polygon">
+ <method name="remove_source">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="navigation_polygon" type="NavigationPolygon">
+ <argument index="0" name="source_id" type="int">
</argument>
<description>
- Sets the tile's navigation polygon.
</description>
</method>
- <method name="tile_set_navigation_polygon_offset">
+ <method name="set_navigation_layer_layers">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="layer_index" type="int">
</argument>
- <argument index="1" name="navigation_polygon_offset" type="Vector2">
+ <argument index="1" name="layers" type="int">
</argument>
<description>
- Sets an offset for the tile's navigation polygon.
</description>
</method>
- <method name="tile_set_occluder_offset">
+ <method name="set_occlusion_layer_light_mask">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="layer_index" type="int">
</argument>
- <argument index="1" name="occluder_offset" type="Vector2">
+ <argument index="1" name="light_mask" type="int">
</argument>
<description>
- Sets an offset for the tile's light occluder.
</description>
</method>
- <method name="tile_set_region">
+ <method name="set_occlusion_layer_sdf_collision">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="layer_index" type="int">
</argument>
- <argument index="1" name="region" type="Rect2">
+ <argument index="1" name="sdf_collision" type="int">
</argument>
<description>
- Sets the tile's sub-region in the texture. This is common in texture atlases.
</description>
</method>
- <method name="tile_set_shape">
+ <method name="set_physics_layer_collision_layer">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="shape_id" type="int">
+ <argument index="0" name="layer_index" type="int">
</argument>
- <argument index="2" name="shape" type="Shape2D">
+ <argument index="1" name="layer" type="int">
</argument>
<description>
- Sets a shape for the tile, enabling collision.
</description>
</method>
- <method name="tile_set_shape_offset">
+ <method name="set_physics_layer_collision_mask">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="shape_id" type="int">
+ <argument index="0" name="layer_index" type="int">
</argument>
- <argument index="2" name="shape_offset" type="Vector2">
+ <argument index="1" name="mask" type="int">
</argument>
<description>
- Sets the offset of a tile's shape.
</description>
</method>
- <method name="tile_set_shape_one_way">
+ <method name="set_physics_layer_physics_material">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="layer_index" type="int">
</argument>
- <argument index="1" name="shape_id" type="int">
- </argument>
- <argument index="2" name="one_way" type="bool">
+ <argument index="1" name="physics_material" type="PhysicsMaterial">
</argument>
<description>
- Enables one-way collision on a tile's shape.
</description>
</method>
- <method name="tile_set_shape_one_way_margin">
+ <method name="set_source_id">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="shape_id" type="int">
+ <argument index="0" name="source_id" type="int">
</argument>
- <argument index="2" name="one_way" type="float">
+ <argument index="1" name="arg1" type="int">
</argument>
<description>
</description>
</method>
- <method name="tile_set_shape_transform">
+ <method name="set_terrain_color">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="terrain_set" type="int">
</argument>
- <argument index="1" name="shape_id" type="int">
+ <argument index="1" name="terrain_index" type="int">
</argument>
- <argument index="2" name="shape_transform" type="Transform2D">
+ <argument index="2" name="color" type="Color">
</argument>
<description>
- Sets a [Transform2D] on a tile's shape.
</description>
</method>
- <method name="tile_set_shapes">
+ <method name="set_terrain_name">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="terrain_set" type="int">
</argument>
- <argument index="1" name="shapes" type="Array">
- </argument>
- <description>
- Sets an array of shapes for the tile, enabling collision.
- </description>
- </method>
- <method name="tile_set_texture">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="texture" type="Texture2D">
- </argument>
- <description>
- Sets the tile's texture.
- </description>
- </method>
- <method name="tile_set_texture_offset">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
+ <argument index="1" name="terrain_index" type="int">
</argument>
- <argument index="1" name="texture_offset" type="Vector2">
+ <argument index="2" name="name" type="String">
</argument>
<description>
- Sets the tile's texture offset.
</description>
</method>
- <method name="tile_set_tile_mode">
+ <method name="set_terrain_set_mode">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="terrain_set" type="int">
</argument>
- <argument index="1" name="tilemode" type="int" enum="TileSet.TileMode">
+ <argument index="1" name="mode" type="int" enum="TileSet.TerrainMode">
</argument>
<description>
- Sets the tile's [enum TileMode].
</description>
</method>
- <method name="tile_set_z_index">
+ <method name="set_terrains_count">
<return type="void">
</return>
- <argument index="0" name="id" type="int">
+ <argument index="0" name="terrain_set" type="int">
</argument>
- <argument index="1" name="z_index" type="int">
+ <argument index="1" name="terrains_count" type="int">
</argument>
<description>
- Sets the tile's drawing index.
</description>
</method>
</methods>
+ <members>
+ <member name="custom_data_layers_count" type="int" setter="set_custom_data_layers_count" getter="get_custom_data_layers_count" default="0">
+ </member>
+ <member name="navigation_layers_count" type="int" setter="set_navigation_layers_count" getter="get_navigation_layers_count" default="0">
+ </member>
+ <member name="occlusion_layers_count" type="int" setter="set_occlusion_layers_count" getter="get_occlusion_layers_count" default="0">
+ </member>
+ <member name="physics_layers_count" type="int" setter="set_physics_layers_count" getter="get_physics_layers_count" default="0">
+ </member>
+ <member name="terrains_sets_count" type="int" setter="set_terrain_sets_count" getter="get_terrain_sets_count" default="0">
+ </member>
+ <member name="tile_layout" type="int" setter="set_tile_layout" getter="get_tile_layout" enum="TileSet.TileLayout" default="0">
+ </member>
+ <member name="tile_offset_axis" type="int" setter="set_tile_offset_axis" getter="get_tile_offset_axis" enum="TileSet.TileOffsetAxis" default="0">
+ </member>
+ <member name="tile_shape" type="int" setter="set_tile_shape" getter="get_tile_shape" enum="TileSet.TileShape" default="0">
+ </member>
+ <member name="tile_size" type="Vector2i" setter="set_tile_size" getter="get_tile_size" default="Vector2i( 16, 16 )">
+ </member>
+ <member name="tile_skew" type="Vector2" setter="set_tile_skew" getter="get_tile_skew" default="Vector2( 0, 0 )">
+ </member>
+ <member name="uv_clipping" type="bool" setter="set_uv_clipping" getter="is_uv_clipping" default="false">
+ </member>
+ <member name="y_sorting" type="bool" setter="set_y_sorting" getter="is_y_sorting" default="false">
+ </member>
+ </members>
<constants>
- <constant name="BITMASK_2X2" value="0" enum="BitmaskMode">
+ <constant name="TILE_SHAPE_SQUARE" value="0" enum="TileShape">
+ </constant>
+ <constant name="TILE_SHAPE_ISOMETRIC" value="1" enum="TileShape">
+ </constant>
+ <constant name="TILE_SHAPE_HALF_OFFSET_SQUARE" value="2" enum="TileShape">
+ </constant>
+ <constant name="TILE_SHAPE_HEXAGON" value="3" enum="TileShape">
+ </constant>
+ <constant name="TILE_LAYOUT_STACKED" value="0" enum="TileLayout">
+ </constant>
+ <constant name="TILE_LAYOUT_STACKED_OFFSET" value="1" enum="TileLayout">
+ </constant>
+ <constant name="TILE_LAYOUT_STAIRS_RIGHT" value="2" enum="TileLayout">
+ </constant>
+ <constant name="TILE_LAYOUT_STAIRS_DOWN" value="3" enum="TileLayout">
+ </constant>
+ <constant name="TILE_LAYOUT_DIAMOND_RIGHT" value="4" enum="TileLayout">
+ </constant>
+ <constant name="TILE_LAYOUT_DIAMOND_DOWN" value="5" enum="TileLayout">
+ </constant>
+ <constant name="TILE_OFFSET_AXIS_HORIZONTAL" value="0" enum="TileOffsetAxis">
+ </constant>
+ <constant name="TILE_OFFSET_AXIS_VERTICAL" value="1" enum="TileOffsetAxis">
+ </constant>
+ <constant name="TileSet::CELL_NEIGHBOR_RIGHT_SIDE" value="0" enum="CellNeighbor">
+ </constant>
+ <constant name="TileSet::CELL_NEIGHBOR_RIGHT_CORNER" value="1" enum="CellNeighbor">
+ </constant>
+ <constant name="TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE" value="2" enum="CellNeighbor">
+ </constant>
+ <constant name="TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER" value="3" enum="CellNeighbor">
+ </constant>
+ <constant name="TileSet::CELL_NEIGHBOR_BOTTOM_SIDE" value="4" enum="CellNeighbor">
</constant>
- <constant name="BITMASK_3X3_MINIMAL" value="1" enum="BitmaskMode">
+ <constant name="TileSet::CELL_NEIGHBOR_BOTTOM_CORNER" value="5" enum="CellNeighbor">
</constant>
- <constant name="BITMASK_3X3" value="2" enum="BitmaskMode">
+ <constant name="TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE" value="6" enum="CellNeighbor">
</constant>
- <constant name="BIND_TOPLEFT" value="1" enum="AutotileBindings">
+ <constant name="TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER" value="7" enum="CellNeighbor">
</constant>
- <constant name="BIND_TOP" value="2" enum="AutotileBindings">
+ <constant name="TileSet::CELL_NEIGHBOR_LEFT_SIDE" value="8" enum="CellNeighbor">
</constant>
- <constant name="BIND_TOPRIGHT" value="4" enum="AutotileBindings">
+ <constant name="TileSet::CELL_NEIGHBOR_LEFT_CORNER" value="9" enum="CellNeighbor">
</constant>
- <constant name="BIND_LEFT" value="8" enum="AutotileBindings">
+ <constant name="TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE" value="10" enum="CellNeighbor">
</constant>
- <constant name="BIND_CENTER" value="16" enum="AutotileBindings">
+ <constant name="TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER" value="11" enum="CellNeighbor">
</constant>
- <constant name="BIND_RIGHT" value="32" enum="AutotileBindings">
+ <constant name="TileSet::CELL_NEIGHBOR_TOP_SIDE" value="12" enum="CellNeighbor">
</constant>
- <constant name="BIND_BOTTOMLEFT" value="64" enum="AutotileBindings">
+ <constant name="TileSet::CELL_NEIGHBOR_TOP_CORNER" value="13" enum="CellNeighbor">
</constant>
- <constant name="BIND_BOTTOM" value="128" enum="AutotileBindings">
+ <constant name="TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE" value="14" enum="CellNeighbor">
</constant>
- <constant name="BIND_BOTTOMRIGHT" value="256" enum="AutotileBindings">
+ <constant name="TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER" value="15" enum="CellNeighbor">
</constant>
- <constant name="SINGLE_TILE" value="0" enum="TileMode">
+ <constant name="TERRAIN_MODE_MATCH_CORNERS_AND_SIDES" value="0" enum="TerrainMode">
</constant>
- <constant name="AUTO_TILE" value="1" enum="TileMode">
+ <constant name="TERRAIN_MODE_MATCH_CORNERS" value="1" enum="TerrainMode">
</constant>
- <constant name="ATLAS_TILE" value="2" enum="TileMode">
+ <constant name="TERRAIN_MODE_MATCH_SIDES" value="2" enum="TerrainMode">
</constant>
</constants>
</class>
diff --git a/doc/classes/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml
new file mode 100644
index 0000000000..a7a304ca27
--- /dev/null
+++ b/doc/classes/TileSetAtlasSource.xml
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="TileSetAtlasSource" inherits="TileSetSource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="can_move_tile_in_atlas" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="new_atlas_coords" type="Vector2i" default="Vector2i( -1, -1 )">
+ </argument>
+ <argument index="2" name="new_size" type="Vector2i" default="Vector2i( -1, -1 )">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="clear_tiles_outside_texture">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="create_alternative_tile">
+ <return type="int">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="alternative_id_override" type="int" default="-1">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="create_tile">
+ <return type="void">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="size" type="Vector2i" default="Vector2i( 1, 1 )">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_alternative_tile_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_alternative_tiles_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_atlas_grid_size" qualifiers="const">
+ <return type="Vector2i">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_next_alternative_tile_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_tile_at_coords" qualifiers="const">
+ <return type="Vector2i">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_tile_data" qualifiers="const">
+ <return type="Object">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_tile_id" qualifiers="const">
+ <return type="Vector2i">
+ </return>
+ <argument index="0" name="index" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_tile_size_in_atlas" qualifiers="const">
+ <return type="Vector2i">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_tile_texture_region" qualifiers="const">
+ <return type="Rect2i">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_tiles_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="has_alternative_tile" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="alternative_tile" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="has_tile" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="has_tiles_outside_texture">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="move_tile_in_atlas">
+ <return type="void">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="new_atlas_coords" type="Vector2i" default="Vector2i( -1, -1 )">
+ </argument>
+ <argument index="2" name="new_size" type="Vector2i" default="Vector2i( -1, -1 )">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="remove_alternative_tile">
+ <return type="void">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="alternative_tile" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="remove_tile">
+ <return type="void">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_alternative_tile_id">
+ <return type="void">
+ </return>
+ <argument index="0" name="atlas_coords" type="Vector2i">
+ </argument>
+ <argument index="1" name="alternative_tile" type="int">
+ </argument>
+ <argument index="2" name="new_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="margins" type="Vector2i" setter="set_margins" getter="get_margins" default="Vector2i( 0, 0 )">
+ </member>
+ <member name="separation" type="Vector2i" setter="set_separation" getter="get_separation" default="Vector2i( 0, 0 )">
+ </member>
+ <member name="texture" type="Texture2D" setter="set_texture" getter="get_texture">
+ </member>
+ <member name="tile_size" type="Vector2i" setter="set_texture_region_size" getter="get_texture_region_size" default="Vector2i( 16, 16 )">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/TileSetSource.xml b/doc/classes/TileSetSource.xml
new file mode 100644
index 0000000000..6a3029bb3f
--- /dev/null
+++ b/doc/classes/TileSetSource.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="TileSetSource" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h
index 64582eb784..1a27c54757 100644
--- a/drivers/dummy/rasterizer_dummy.h
+++ b/drivers/dummy/rasterizer_dummy.h
@@ -567,10 +567,17 @@ public:
void particles_set_use_local_coordinates(RID p_particles, bool p_enable) override {}
void particles_set_process_material(RID p_particles, RID p_material) override {}
void particles_set_fixed_fps(RID p_particles, int p_fps) override {}
+ void particles_set_interpolate(RID p_particles, bool p_enable) override {}
void particles_set_fractional_delta(RID p_particles, bool p_enable) override {}
void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) override {}
- void particles_set_view_axis(RID p_particles, const Vector3 &p_axis) override {}
+ void particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) override {}
void particles_set_collision_base_size(RID p_particles, float p_size) override {}
+
+ void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) override {}
+
+ void particles_set_trails(RID p_particles, bool p_enable, float p_length) override {}
+ void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform> &p_bind_poses) override {}
+
void particles_restart(RID p_particles) override {}
void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) override {}
diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp
index 8ec1de4386..053e3fd9d6 100644
--- a/drivers/unix/ip_unix.cpp
+++ b/drivers/unix/ip_unix.cpp
@@ -75,8 +75,8 @@
#include <net/if.h> // Order is important on OpenBSD, leave as last
#endif
-static IP_Address _sockaddr2ip(struct sockaddr *p_addr) {
- IP_Address ip;
+static IPAddress _sockaddr2ip(struct sockaddr *p_addr) {
+ IPAddress ip;
if (p_addr->sa_family == AF_INET) {
struct sockaddr_in *addr = (struct sockaddr_in *)p_addr;
@@ -89,7 +89,7 @@ static IP_Address _sockaddr2ip(struct sockaddr *p_addr) {
return ip;
};
-IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) {
+IPAddress IPUnix::_resolve_hostname(const String &p_hostname, Type p_type) {
struct addrinfo hints;
struct addrinfo *result = nullptr;
@@ -108,7 +108,7 @@ IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) {
int s = getaddrinfo(p_hostname.utf8().get_data(), nullptr, &hints, &result);
if (s != 0) {
ERR_PRINT("getaddrinfo failed! Cannot resolve hostname.");
- return IP_Address();
+ return IPAddress();
};
if (result == nullptr || result->ai_addr == nullptr) {
@@ -116,10 +116,10 @@ IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) {
if (result) {
freeaddrinfo(result);
}
- return IP_Address();
+ return IPAddress();
};
- IP_Address ip = _sockaddr2ip(result->ai_addr);
+ IPAddress ip = _sockaddr2ip(result->ai_addr);
freeaddrinfo(result);
@@ -130,7 +130,7 @@ IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) {
#if defined(UWP_ENABLED)
-void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
+void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
using namespace Windows::Networking;
using namespace Windows::Networking::Connectivity;
@@ -156,14 +156,14 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co
Interface_Info &info = E->get();
- IP_Address ip = IP_Address(hostname->CanonicalName->Data());
+ IPAddress ip = IPAddress(hostname->CanonicalName->Data());
info.ip_addresses.push_front(ip);
}
}
#else
-void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
+void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
ULONG buf_size = 1024;
IP_ADAPTER_ADDRESSES *addrs;
@@ -211,7 +211,7 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co
#else // UNIX
-void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
+void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
struct ifaddrs *ifAddrStruct = nullptr;
struct ifaddrs *ifa = nullptr;
int family;
@@ -249,15 +249,15 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co
}
#endif
-void IP_Unix::make_default() {
+void IPUnix::make_default() {
_create = _create_unix;
}
-IP *IP_Unix::_create_unix() {
- return memnew(IP_Unix);
+IP *IPUnix::_create_unix() {
+ return memnew(IPUnix);
}
-IP_Unix::IP_Unix() {
+IPUnix::IPUnix() {
}
#endif
diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h
index ca2ee17f4e..e6479be6e5 100644
--- a/drivers/unix/ip_unix.h
+++ b/drivers/unix/ip_unix.h
@@ -35,10 +35,10 @@
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
-class IP_Unix : public IP {
- GDCLASS(IP_Unix, IP);
+class IPUnix : public IP {
+ GDCLASS(IPUnix, IP);
- virtual IP_Address _resolve_hostname(const String &p_hostname, IP::Type p_type) override;
+ virtual IPAddress _resolve_hostname(const String &p_hostname, IP::Type p_type) override;
static IP *_create_unix();
@@ -46,7 +46,7 @@ public:
virtual void get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const override;
static void make_default();
- IP_Unix();
+ IPUnix();
};
#endif
diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp
index e2ad352c10..222aef998c 100644
--- a/drivers/unix/net_socket_posix.cpp
+++ b/drivers/unix/net_socket_posix.cpp
@@ -95,7 +95,7 @@
#endif
-size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type) {
+size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) {
memset(p_addr, 0, sizeof(struct sockaddr_storage));
if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket
@@ -130,7 +130,7 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const
}
}
-void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IP_Address *r_ip, uint16_t *r_port) {
+void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) {
if (p_addr->ss_family == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
if (r_ip) {
@@ -233,7 +233,7 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
#pragma GCC diagnostic pop
#endif
-bool NetSocketPosix::_can_use_ip(const IP_Address &p_ip, const bool p_for_bind) const {
+bool NetSocketPosix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const {
if (p_for_bind && !(p_ip.is_valid() || p_ip.is_wildcard())) {
return false;
} else if (!p_for_bind && !p_ip.is_valid()) {
@@ -244,7 +244,7 @@ bool NetSocketPosix::_can_use_ip(const IP_Address &p_ip, const bool p_for_bind)
return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type);
}
-_FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, String p_if_name, bool p_add) {
+_FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER);
@@ -254,7 +254,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, St
int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
int ret = -1;
- IP_Address if_ip;
+ IPAddress if_ip;
uint32_t if_v6id = 0;
Map<String, IP::Interface_Info> if_info;
IP::get_singleton()->get_local_interfaces(&if_info);
@@ -269,7 +269,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, St
break; // IPv6 uses index.
}
- for (List<IP_Address>::Element *F = c.ip_addresses.front(); F; F = F->next()) {
+ for (List<IPAddress>::Element *F = c.ip_addresses.front(); F; F = F->next()) {
if (!F->get().is_ipv4()) {
continue; // Wrong IP type
}
@@ -395,7 +395,7 @@ void NetSocketPosix::close() {
_is_stream = false;
}
-Error NetSocketPosix::bind(IP_Address p_addr, uint16_t p_port) {
+Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER);
@@ -425,7 +425,7 @@ Error NetSocketPosix::listen(int p_max_pending) {
return OK;
}
-Error NetSocketPosix::connect_to_host(IP_Address p_host, uint16_t p_port) {
+Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER);
@@ -559,7 +559,7 @@ Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
return OK;
}
-Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port, bool p_peek) {
+Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
struct sockaddr_storage from;
@@ -616,7 +616,7 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
return OK;
}
-Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) {
+Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
struct sockaddr_storage addr;
@@ -735,7 +735,7 @@ int NetSocketPosix::get_available_bytes() const {
return len;
}
-Error NetSocketPosix::get_socket_address(IP_Address *r_ip, uint16_t *r_port) const {
+Error NetSocketPosix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
ERR_FAIL_COND_V(!is_open(), FAILED);
struct sockaddr_storage saddr;
@@ -749,7 +749,7 @@ Error NetSocketPosix::get_socket_address(IP_Address *r_ip, uint16_t *r_port) con
return OK;
}
-Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) {
+Ref<NetSocket> NetSocketPosix::accept(IPAddress &r_ip, uint16_t &r_port) {
Ref<NetSocket> out;
ERR_FAIL_COND_V(!is_open(), out);
@@ -770,11 +770,11 @@ Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) {
return Ref<NetSocket>(ns);
}
-Error NetSocketPosix::join_multicast_group(const IP_Address &p_multi_address, String p_if_name) {
+Error NetSocketPosix::join_multicast_group(const IPAddress &p_multi_address, String p_if_name) {
return _change_multicast_group(p_multi_address, p_if_name, true);
}
-Error NetSocketPosix::leave_multicast_group(const IP_Address &p_multi_address, String p_if_name) {
+Error NetSocketPosix::leave_multicast_group(const IPAddress &p_multi_address, String p_if_name) {
return _change_multicast_group(p_multi_address, p_if_name, false);
}
#endif
diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h
index dbfe3a524e..38c8170a52 100644
--- a/drivers/unix/net_socket_posix.h
+++ b/drivers/unix/net_socket_posix.h
@@ -61,35 +61,35 @@ private:
NetError _get_socket_error() const;
void _set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream);
- _FORCE_INLINE_ Error _change_multicast_group(IP_Address p_ip, String p_if_name, bool p_add);
+ _FORCE_INLINE_ Error _change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add);
_FORCE_INLINE_ void _set_close_exec_enabled(bool p_enabled);
protected:
static NetSocket *_create_func();
- bool _can_use_ip(const IP_Address &p_ip, const bool p_for_bind) const;
+ bool _can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const;
public:
static void make_default();
static void cleanup();
- static void _set_ip_port(struct sockaddr_storage *p_addr, IP_Address *r_ip, uint16_t *r_port);
- static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type);
+ static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port);
+ static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type);
virtual Error open(Type p_sock_type, IP::Type &ip_type);
virtual void close();
- virtual Error bind(IP_Address p_addr, uint16_t p_port);
+ virtual Error bind(IPAddress p_addr, uint16_t p_port);
virtual Error listen(int p_max_pending);
- virtual Error connect_to_host(IP_Address p_host, uint16_t p_port);
+ virtual Error connect_to_host(IPAddress p_host, uint16_t p_port);
virtual Error poll(PollType p_type, int timeout) const;
virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read);
- virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port, bool p_peek = false);
+ virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false);
virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent);
- virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port);
- virtual Ref<NetSocket> accept(IP_Address &r_ip, uint16_t &r_port);
+ virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port);
+ virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port);
virtual bool is_open() const;
virtual int get_available_bytes() const;
- virtual Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) const;
+ virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const;
virtual Error set_broadcasting_enabled(bool p_enabled);
virtual void set_blocking_enabled(bool p_enabled);
@@ -97,8 +97,8 @@ public:
virtual void set_tcp_no_delay_enabled(bool p_enabled);
virtual void set_reuse_address_enabled(bool p_enabled);
virtual void set_reuse_port_enabled(bool p_enabled);
- virtual Error join_multicast_group(const IP_Address &p_multi_address, String p_if_name);
- virtual Error leave_multicast_group(const IP_Address &p_multi_address, String p_if_name);
+ virtual Error join_multicast_group(const IPAddress &p_multi_address, String p_if_name);
+ virtual Error leave_multicast_group(const IPAddress &p_multi_address, String p_if_name);
NetSocketPosix();
~NetSocketPosix();
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index b9bd773c2e..15cd7bee92 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -129,7 +129,7 @@ void OS_Unix::initialize_core() {
#ifndef NO_NETWORK
NetSocketPosix::make_default();
- IP_Unix::make_default();
+ IPUnix::make_default();
#endif
_setup_clock();
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 30cc01fd10..43b2a24172 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -7844,6 +7844,10 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de
device_capabilities.subgroup_size = subgroup_capabilities.size;
device_capabilities.subgroup_in_shaders = subgroup_capabilities.supported_stages_flags_rd();
device_capabilities.subgroup_operations = subgroup_capabilities.supported_operations_flags_rd();
+
+ // get info about further features
+ VulkanContext::MultiviewCapabilities multiview_capabilies = p_context->get_multiview_capabilities();
+ device_capabilities.supports_multiview = multiview_capabilies.is_supported && multiview_capabilies.max_view_count > 1;
}
context = p_context;
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index 0a8a5c746f..4649cee17f 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -352,8 +352,6 @@ Error VulkanContext::_initialize_extensions() {
return OK;
}
-typedef void(VKAPI_PTR *_vkGetPhysicalDeviceProperties2)(VkPhysicalDevice, VkPhysicalDeviceProperties2 *);
-
uint32_t VulkanContext::SubgroupCapabilities::supported_stages_flags_rd() const {
uint32_t flags = 0;
@@ -496,20 +494,70 @@ String VulkanContext::SubgroupCapabilities::supported_operations_desc() const {
}
Error VulkanContext::_check_capabilities() {
- // check subgroups
+ // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_multiview.html
// https://www.khronos.org/blog/vulkan-subgroup-tutorial
+
// for Vulkan 1.0 vkGetPhysicalDeviceProperties2 is not available, including not in the loader we compile against on Android.
- _vkGetPhysicalDeviceProperties2 func = (_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2");
- if (func != nullptr) {
+
+ // so we check if the functions are accessible by getting their function pointers and skipping if not
+ // (note that the desktop loader does a better job here but the android loader doesn't)
+
+ // assume not supported until proven otherwise
+ multiview_capabilities.is_supported = false;
+ multiview_capabilities.max_view_count = 0;
+ multiview_capabilities.max_instance_count = 0;
+ subgroup_capabilities.size = 0;
+ subgroup_capabilities.supportedStages = 0;
+ subgroup_capabilities.supportedOperations = 0;
+ subgroup_capabilities.quadOperationsInAllStages = false;
+
+ // check for extended features
+ PFN_vkGetPhysicalDeviceFeatures2 device_features_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2");
+ if (device_features_func == nullptr) {
+ // In Vulkan 1.0 might be accessible under its original extension name
+ device_features_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR");
+ }
+ if (device_features_func != nullptr) {
+ // check our extended features
+ VkPhysicalDeviceMultiviewFeatures multiview_features;
+ multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
+ multiview_features.pNext = NULL;
+
+ VkPhysicalDeviceFeatures2 device_features;
+ device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ device_features.pNext = &multiview_features;
+
+ device_features_func(gpu, &device_features);
+ multiview_capabilities.is_supported = multiview_features.multiview;
+ // For now we ignore if multiview is available in geometry and tesselation as we do not currently support those
+ }
+
+ // check extended properties
+ PFN_vkGetPhysicalDeviceProperties2 device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2");
+ if (device_properties_func == nullptr) {
+ // In Vulkan 1.0 might be accessible under its original extension name
+ device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2KHR");
+ }
+ if (device_properties_func != nullptr) {
+ VkPhysicalDeviceMultiviewProperties multiviewProperties;
VkPhysicalDeviceSubgroupProperties subgroupProperties;
+ VkPhysicalDeviceProperties2 physicalDeviceProperties;
+
subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
subgroupProperties.pNext = nullptr;
- VkPhysicalDeviceProperties2 physicalDeviceProperties;
physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
- physicalDeviceProperties.pNext = &subgroupProperties;
- func(gpu, &physicalDeviceProperties);
+ if (multiview_capabilities.is_supported) {
+ multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES;
+ multiviewProperties.pNext = &subgroupProperties;
+
+ physicalDeviceProperties.pNext = &multiviewProperties;
+ } else {
+ physicalDeviceProperties.pNext = &subgroupProperties;
+ }
+
+ device_properties_func(gpu, &physicalDeviceProperties);
subgroup_capabilities.size = subgroupProperties.subgroupSize;
subgroup_capabilities.supportedStages = subgroupProperties.supportedStages;
@@ -519,18 +567,30 @@ Error VulkanContext::_check_capabilities() {
// - supportedOperations has VK_SUBGROUP_FEATURE_QUAD_BIT
subgroup_capabilities.quadOperationsInAllStages = subgroupProperties.quadOperationsInAllStages;
- // only output this when debugging?
- print_line("- Vulkan subgroup size " + itos(subgroup_capabilities.size));
- print_line("- Vulkan subgroup stages " + subgroup_capabilities.supported_stages_desc());
- print_line("- Vulkan subgroup supported ops " + subgroup_capabilities.supported_operations_desc());
+ if (multiview_capabilities.is_supported) {
+ multiview_capabilities.max_view_count = multiviewProperties.maxMultiviewViewCount;
+ multiview_capabilities.max_instance_count = multiviewProperties.maxMultiviewInstanceIndex;
+
+#ifdef DEBUG_ENABLED
+ print_line("- Vulkan multiview supported:");
+ print_line(" max views: " + itos(multiview_capabilities.max_view_count));
+ print_line(" max instances: " + itos(multiview_capabilities.max_instance_count));
+ } else {
+ print_line("- Vulkan multiview not supported");
+#endif
+ }
+
+#ifdef DEBUG_ENABLED
+ print_line("- Vulkan subgroup:");
+ print_line(" size: " + itos(subgroup_capabilities.size));
+ print_line(" stages: " + subgroup_capabilities.supported_stages_desc());
+ print_line(" supported ops: " + subgroup_capabilities.supported_operations_desc());
if (subgroup_capabilities.quadOperationsInAllStages) {
- print_line("- Vulkan subgroup quad operations in all stages");
+ print_line(" quad operations in all stages");
}
} else {
- subgroup_capabilities.size = 0;
- subgroup_capabilities.supportedStages = 0;
- subgroup_capabilities.supportedOperations = 0;
- subgroup_capabilities.quadOperationsInAllStages = false;
+ print_line("- Couldn't call vkGetPhysicalDeviceProperties2");
+#endif
}
return OK;
@@ -1596,13 +1656,13 @@ Error VulkanContext::prepare_buffers() {
if (err == VK_ERROR_OUT_OF_DATE_KHR) {
// swapchain is out of date (e.g. the window was resized) and
// must be recreated:
- print_line("early out of data");
+ print_verbose("Vulkan: Early out of date swapchain, recreating.");
//resize_notify();
_update_swap_chain(w);
} else if (err == VK_SUBOPTIMAL_KHR) {
- print_line("early suboptimal");
// swapchain is not as optimal as it could be, but the platform's
// presentation engine will still present the image correctly.
+ print_verbose("Vulkan: Early suboptimal swapchain.");
break;
} else {
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
@@ -1810,12 +1870,12 @@ Error VulkanContext::swap_buffers() {
if (err == VK_ERROR_OUT_OF_DATE_KHR) {
// swapchain is out of date (e.g. the window was resized) and
// must be recreated:
- print_line("out of date");
+ print_verbose("Vulkan: Swapchain is out of date, recreating.");
resize_notify();
} else if (err == VK_SUBOPTIMAL_KHR) {
// swapchain is not as optimal as it could be, but the platform's
// presentation engine will still present the image correctly.
- print_line("suboptimal");
+ print_verbose("Vulkan: Swapchain is suboptimal.");
} else {
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
}
@@ -1911,13 +1971,13 @@ void VulkanContext::local_device_push_command_buffers(RID p_local_device, const
VkResult err = vkQueueSubmit(ld->queue, 1, &submit_info, VK_NULL_HANDLE);
if (err == VK_ERROR_OUT_OF_HOST_MEMORY) {
- print_line("out of host memory");
+ print_line("Vulkan: Out of host memory!");
}
if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
- print_line("out of device memory");
+ print_line("Vulkan: Out of device memory!");
}
if (err == VK_ERROR_DEVICE_LOST) {
- print_line("device lost");
+ print_line("Vulkan: Device lost!");
}
ERR_FAIL_COND(err);
diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h
index 88e4f26bb1..3d9b295c5a 100644
--- a/drivers/vulkan/vulkan_context.h
+++ b/drivers/vulkan/vulkan_context.h
@@ -54,6 +54,12 @@ public:
String supported_operations_desc() const;
};
+ struct MultiviewCapabilities {
+ bool is_supported;
+ int32_t max_view_count;
+ int32_t max_instance_count;
+ };
+
private:
enum {
MAX_EXTENSIONS = 128,
@@ -75,6 +81,7 @@ private:
uint32_t vulkan_major = 1;
uint32_t vulkan_minor = 0;
SubgroupCapabilities subgroup_capabilities;
+ MultiviewCapabilities multiview_capabilities;
String device_vendor;
String device_name;
@@ -227,6 +234,7 @@ public:
uint32_t get_vulkan_major() const { return vulkan_major; };
uint32_t get_vulkan_minor() const { return vulkan_minor; };
SubgroupCapabilities get_subgroup_capabilities() const { return subgroup_capabilities; };
+ MultiviewCapabilities get_multiview_capabilities() const { return multiview_capabilities; };
VkDevice get_device();
VkPhysicalDevice get_physical_device();
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp
index 9949fd8199..c0d5716c4e 100644
--- a/editor/action_map_editor.cpp
+++ b/editor/action_map_editor.cpp
@@ -730,10 +730,6 @@ void ActionMapEditor::_add_action_pressed() {
}
void ActionMapEditor::_add_action(const String &p_name) {
- if (!allow_editing_actions) {
- return;
- }
-
if (p_name == "" || !_is_action_name_valid(p_name)) {
show_message(TTR("Invalid action name. it cannot be.is_empty()() nor contain '/', ':', '=', '\\' or '\"'"));
return;
@@ -744,10 +740,6 @@ void ActionMapEditor::_add_action(const String &p_name) {
}
void ActionMapEditor::_action_edited() {
- if (!allow_editing_actions) {
- return;
- }
-
TreeItem *ti = action_tree->get_edited();
if (!ti) {
return;
@@ -812,10 +804,6 @@ void ActionMapEditor::_tree_button_pressed(Object *p_item, int p_column, int p_i
} break;
case ActionMapEditor::BUTTON_REMOVE_ACTION: {
- if (!allow_editing_actions) {
- break;
- }
-
// Send removed action name
String name = item->get_meta("__name");
emit_signal("action_removed", name);
@@ -848,11 +836,11 @@ void ActionMapEditor::_tree_item_activated() {
_tree_button_pressed(item, 2, BUTTON_EDIT_EVENT);
}
-void ActionMapEditor::set_show_uneditable(bool p_show) {
- show_uneditable = p_show;
- show_uneditable_actions_checkbox->set_pressed(p_show);
+void ActionMapEditor::set_show_builtin_actions(bool p_show) {
+ show_builtin_actions = p_show;
+ show_builtin_actions_checkbutton->set_pressed(p_show);
- // Prevent unnecessary updates of action list when cache is.is_empty()().
+ // Prevent unnecessary updates of action list when cache is empty.
if (!actions_cache.is_empty()) {
update_action_list();
}
@@ -1022,7 +1010,7 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
continue;
}
- if (!action_info.editable && !show_uneditable) {
+ if (!action_info.editable && !show_builtin_actions) {
continue;
}
@@ -1080,15 +1068,6 @@ void ActionMapEditor::show_message(const String &p_message) {
message->popup_centered(Size2(300, 100) * EDSCALE);
}
-void ActionMapEditor::set_allow_editing_actions(bool p_allow) {
- allow_editing_actions = p_allow;
- add_hbox->set_visible(p_allow);
-}
-
-void ActionMapEditor::set_toggle_editable_label(const String &p_label) {
- show_uneditable_actions_checkbox->set_text(p_label);
-}
-
void ActionMapEditor::use_external_search_box(LineEdit *p_searchbox) {
memdelete(action_list_search);
action_list_search = p_searchbox;
@@ -1096,8 +1075,7 @@ void ActionMapEditor::use_external_search_box(LineEdit *p_searchbox) {
}
ActionMapEditor::ActionMapEditor() {
- allow_editing_actions = true;
- show_uneditable = true;
+ show_builtin_actions = false;
// Main Vbox Container
VBoxContainer *main_vbox = memnew(VBoxContainer);
@@ -1114,11 +1092,11 @@ ActionMapEditor::ActionMapEditor() {
action_list_search->connect("text_changed", callable_mp(this, &ActionMapEditor::_search_term_updated));
top_hbox->add_child(action_list_search);
- show_uneditable_actions_checkbox = memnew(CheckBox);
- show_uneditable_actions_checkbox->set_pressed(false);
- show_uneditable_actions_checkbox->set_text(TTR("Show Uneditable Actions"));
- show_uneditable_actions_checkbox->connect("toggled", callable_mp(this, &ActionMapEditor::set_show_uneditable));
- top_hbox->add_child(show_uneditable_actions_checkbox);
+ show_builtin_actions_checkbutton = memnew(CheckButton);
+ show_builtin_actions_checkbutton->set_pressed(false);
+ show_builtin_actions_checkbutton->set_text(TTR("Show Built-in Actions"));
+ show_builtin_actions_checkbutton->connect("toggled", callable_mp(this, &ActionMapEditor::set_show_builtin_actions));
+ top_hbox->add_child(show_builtin_actions_checkbutton);
// Adding Action line edit + button
add_hbox = memnew(HBoxContainer);
@@ -1132,7 +1110,7 @@ ActionMapEditor::ActionMapEditor() {
add_hbox->add_child(add_edit);
Button *add_button = memnew(Button);
- add_button->set_text("Add");
+ add_button->set_text(TTR("Add"));
add_button->connect("pressed", callable_mp(this, &ActionMapEditor::_add_action_pressed));
add_hbox->add_child(add_button);
diff --git a/editor/action_map_editor.h b/editor/action_map_editor.h
index f1f7bffef4..fb097ddfdd 100644
--- a/editor/action_map_editor.h
+++ b/editor/action_map_editor.h
@@ -156,11 +156,10 @@ private:
// Filtering and Adding actions
- bool show_uneditable;
- CheckBox *show_uneditable_actions_checkbox;
+ bool show_builtin_actions;
+ CheckButton *show_builtin_actions_checkbutton;
LineEdit *action_list_search;
- bool allow_editing_actions;
HBoxContainer *add_hbox;
LineEdit *add_edit;
@@ -190,10 +189,7 @@ public:
void update_action_list(const Vector<ActionInfo> &p_action_infos = Vector<ActionInfo>());
void show_message(const String &p_message);
- void set_show_uneditable(bool p_show);
- void set_allow_editing_actions(bool p_allow);
-
- void set_toggle_editable_label(const String &p_label);
+ void set_show_builtin_actions(bool p_show);
void use_external_search_box(LineEdit *p_searchbox);
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index ab8ae71904..67b52bf0ca 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -762,7 +762,7 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Add Bezier Point"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, time);
undo_redo->commit_action();
//then attempt to move
@@ -857,7 +857,7 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
continue; //already in selection, don't save
}
- undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", track, newtime);
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track, newtime);
AnimMoveRestore amr;
amr.key = animation->track_get_key_value(track, idx);
@@ -888,7 +888,7 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
if (newpos<0)
continue; //no remove what no inserted
*/
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, newpos);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, newpos);
}
// 5-(undo) reinsert keys
@@ -1038,7 +1038,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) {
undo_redo->create_action(TTR("Add Bezier Point"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, time);
undo_redo->commit_action();
} break;
@@ -1074,7 +1074,7 @@ void AnimationBezierTrackEdit::duplicate_selection() {
int existing_idx = animation->track_find_key(track, dst_time, true);
undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, dst_time, animation->track_get_key_value(track, E->get()), animation->track_get_key_transition(track, E->get()));
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, dst_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, dst_time);
Pair<int, float> p;
p.first = track;
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 9db2f0a287..89f77f3c77 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -134,7 +134,7 @@ public:
undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key);
undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans);
undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, new_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, new_time);
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans);
undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs);
@@ -758,7 +758,7 @@ public:
undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key);
undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans);
undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, new_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, new_time);
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans);
undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs);
@@ -3979,7 +3979,7 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
p_next_tracks.normal++;
} else {
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_id.track_idx, time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_id.track_idx, time);
int existing = animation->track_find_key(p_id.track_idx, time, true);
if (existing != -1) {
Variant v = animation->track_get_key_value(p_id.track_idx, existing);
@@ -4568,7 +4568,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
undo_redo->create_action(TTR("Add Transform Track Key"));
undo_redo->add_do_method(animation.ptr(), "transform_track_insert_key", p_track, p_ofs, loc, rot, scale);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
} break;
@@ -4580,7 +4580,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
undo_redo->create_action(TTR("Add Track Key"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, value);
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
} break;
@@ -4611,7 +4611,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
undo_redo->create_action(TTR("Add Track Key"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, arr);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
} break;
@@ -4623,7 +4623,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
undo_redo->create_action(TTR("Add Track Key"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, ak);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
} break;
case Animation::TYPE_ANIMATION: {
@@ -4631,7 +4631,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
undo_redo->create_action(TTR("Add Track Key"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, anim);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
} break;
}
@@ -4669,7 +4669,7 @@ void AnimationTrackEditor::_add_method_key(const String &p_method) {
undo_redo->create_action(TTR("Add Method Track Key"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", insert_key_from_track_call_track, insert_key_from_track_call_ofs, d);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", insert_key_from_track_call_track, insert_key_from_track_call_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", insert_key_from_track_call_track, insert_key_from_track_call_ofs);
undo_redo->commit_action();
return;
@@ -4879,7 +4879,7 @@ void AnimationTrackEditor::_move_selection_commit() {
continue; //already in selection, don't save
}
- undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newtime);
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newtime);
_AnimMoveRestore amr;
amr.key = animation->track_get_key_value(E->key().track, idx);
@@ -4899,7 +4899,7 @@ void AnimationTrackEditor::_move_selection_commit() {
// 4 - (undo) remove inserted keys
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newpos = snap_time(E->get().pos + motion);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newpos);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newpos);
}
// 5 - (undo) reinsert keys
@@ -5096,7 +5096,7 @@ void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) {
int existing_idx = animation->track_find_key(dst_track, dst_time, true);
undo_redo->add_do_method(animation.ptr(), "track_insert_key", dst_track, dst_time, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", dst_track, dst_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", dst_track, dst_time);
Pair<int, float> p;
p.first = dst_track;
@@ -5344,7 +5344,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
continue; //already in selection, don't save
}
- undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newtime);
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newtime);
_AnimMoveRestore amr;
amr.key = animation->track_get_key_value(E->key().track, idx);
@@ -5365,7 +5365,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
// 4-(undo) remove inserted keys
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newpos = _NEW_POS(E->get().pos);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newpos);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newpos);
}
// 5-(undo) reinsert keys
diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp
index 506a327ffc..ae79299331 100644
--- a/editor/animation_track_editor_plugins.cpp
+++ b/editor/animation_track_editor_plugins.cpp
@@ -1024,7 +1024,7 @@ void AnimationTrackEditTypeAudio::drop_data(const Point2 &p_point, const Variant
get_undo_redo()->create_action(TTR("Add Audio Track Clip"));
get_undo_redo()->add_do_method(get_animation().ptr(), "audio_track_insert_key", get_track(), ofs, stream);
- get_undo_redo()->add_undo_method(get_animation().ptr(), "track_remove_key_at_position", get_track(), ofs);
+ get_undo_redo()->add_undo_method(get_animation().ptr(), "track_remove_key_at_time", get_track(), ofs);
get_undo_redo()->commit_action();
update();
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 711072f4b2..1c0a55e4ec 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -708,7 +708,6 @@ CreateDialog::CreateDialog() {
search_hb->add_child(search_box);
favorite = memnew(Button);
- favorite->set_flat(true);
favorite->set_toggle_mode(true);
favorite->set_tooltip(TTR("(Un)favorite selected item."));
favorite->connect("pressed", callable_mp(this, &CreateDialog::_favorite_toggled));
diff --git a/editor/debugger/editor_debugger_server.cpp b/editor/debugger/editor_debugger_server.cpp
index 4add891bcb..662f247062 100644
--- a/editor/debugger/editor_debugger_server.cpp
+++ b/editor/debugger/editor_debugger_server.cpp
@@ -40,7 +40,7 @@
class EditorDebuggerServerTCP : public EditorDebuggerServer {
private:
- Ref<TCP_Server> server;
+ Ref<TCPServer> server;
public:
static EditorDebuggerServer *create(const String &p_protocol);
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 3957f981be..7c485d53c5 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -75,6 +75,8 @@ void EditorLog::_notification(int p_what) {
collapse_button->set_icon(get_theme_icon("CombineLines", "EditorIcons"));
show_search_button->set_icon(get_theme_icon("Search", "EditorIcons"));
+ _load_state();
+
} else if (p_what == NOTIFICATION_THEME_CHANGED) {
Ref<Font> df_output_code = get_theme_font("output_source", "EditorFonts");
if (df_output_code.is_valid()) {
@@ -89,9 +91,56 @@ void EditorLog::_notification(int p_what) {
void EditorLog::_set_collapse(bool p_collapse) {
collapse = p_collapse;
+ _start_state_save_timer();
_rebuild_log();
}
+void EditorLog::_start_state_save_timer() {
+ if (!is_loading_state) {
+ save_state_timer->start();
+ }
+}
+
+void EditorLog::_save_state() {
+ Ref<ConfigFile> config;
+ config.instance();
+ // Load and amend existing config if it exists.
+ config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg"));
+
+ const String section = "editor_log";
+ for (Map<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next()) {
+ config->set_value(section, "log_filter_" + itos(E->key()), E->get()->is_active());
+ }
+
+ config->set_value(section, "collapse", collapse);
+ config->set_value(section, "show_search", search_box->is_visible());
+
+ config->save(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg"));
+}
+
+void EditorLog::_load_state() {
+ is_loading_state = true;
+
+ Ref<ConfigFile> config;
+ config.instance();
+ Error err = config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg"));
+
+ if (err == OK) {
+ const String section = "editor_log";
+ for (Map<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next()) {
+ E->get()->set_active(config->get_value(section, "log_filter_" + itos(E->key()), false));
+ }
+
+ collapse = config->get_value(section, "collapse", false);
+ collapse_button->set_pressed(collapse);
+ bool show_search = config->get_value(section, "show_search", true);
+ search_box->set_visible(show_search);
+ show_search_button->set_pressed(show_search);
+ }
+
+ is_loading_state = false;
+}
+
void EditorLog::_clear_request() {
log->clear();
messages.clear();
@@ -175,7 +224,7 @@ void EditorLog::_rebuild_log() {
void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
// Only add the message to the log if it passes the filters.
- bool filter_active = type_filter_map[p_message.type]->active;
+ bool filter_active = type_filter_map[p_message.type]->is_active();
String search_text = search_box->get_text();
bool search_match = search_text == String() || p_message.text.findn(search_text) > -1;
@@ -187,8 +236,6 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
// Remove last line if replacing, as it will be replace by the next added line.
log->remove_line(log->get_line_count() - 1);
log->increment_line_count();
- } else {
- log->add_newline();
}
switch (p_message.type) {
@@ -222,16 +269,18 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
}
log->add_text(p_message.text);
+ log->add_newline();
// Need to use pop() to exit out of the RichTextLabels current "push" stack.
- // We only "push" in the above swicth when message type != STD, so only pop when that is the case.
+ // We only "push" in the above switch when message type != STD, so only pop when that is the case.
if (p_message.type != MSG_TYPE_STD) {
log->pop();
}
}
void EditorLog::_set_filter_active(bool p_active, MessageType p_message_type) {
- type_filter_map[p_message_type]->active = p_active;
+ type_filter_map[p_message_type]->set_active(p_active);
+ _start_state_save_timer();
_rebuild_log();
}
@@ -240,6 +289,7 @@ void EditorLog::_set_search_visible(bool p_visible) {
if (p_visible) {
search_box->grab_focus();
}
+ _start_state_save_timer();
}
void EditorLog::_search_changed(const String &p_text) {
@@ -258,6 +308,12 @@ void EditorLog::_bind_methods() {
}
EditorLog::EditorLog() {
+ save_state_timer = memnew(Timer);
+ save_state_timer->set_wait_time(2);
+ save_state_timer->set_one_shot(true);
+ save_state_timer->connect("timeout", callable_mp(this, &EditorLog::_save_state));
+ add_child(save_state_timer);
+
HBoxContainer *hb = this;
VBoxContainer *vb_left = memnew(VBoxContainer);
@@ -298,6 +354,7 @@ EditorLog::EditorLog() {
clear_button->set_flat(true);
clear_button->set_focus_mode(FOCUS_NONE);
clear_button->set_shortcut(ED_SHORTCUT("editor/clear_output", TTR("Clear Output"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_K));
+ clear_button->set_shortcut_context(this);
clear_button->connect("pressed", callable_mp(this, &EditorLog::_clear_request));
hb_tools->add_child(clear_button);
@@ -306,6 +363,7 @@ EditorLog::EditorLog() {
copy_button->set_flat(true);
copy_button->set_focus_mode(FOCUS_NONE);
copy_button->set_shortcut(ED_SHORTCUT("editor/copy_output", TTR("Copy Selection"), KEY_MASK_CMD | KEY_C));
+ copy_button->set_shortcut_context(this);
copy_button->connect("pressed", callable_mp(this, &EditorLog::_copy_request));
hb_tools->add_child(copy_button);
@@ -331,6 +389,7 @@ EditorLog::EditorLog() {
show_search_button->set_toggle_mode(true);
show_search_button->set_pressed(true);
show_search_button->set_shortcut(ED_SHORTCUT("editor/open_search", TTR("Open the search box."), KEY_MASK_CMD | KEY_F));
+ show_search_button->set_shortcut_context(this);
show_search_button->connect("toggled", callable_mp(this, &EditorLog::_set_search_visible));
hb_tools2->add_child(show_search_button);
diff --git a/editor/editor_log.h b/editor/editor_log.h
index 89d00d0fa0..3b6476634a 100644
--- a/editor/editor_log.h
+++ b/editor/editor_log.h
@@ -72,11 +72,11 @@ private:
private:
// Force usage of set method since it has functionality built-in.
int message_count = 0;
+ bool active = true;
public:
MessageType type;
Button *toggle_button = nullptr;
- bool active = true;
void initialize_button(const String &p_tooltip, Callable p_toggled_callback) {
toggle_button = memnew(Button);
@@ -100,6 +100,15 @@ private:
toggle_button->set_text(itos(message_count));
}
+ bool is_active() {
+ return active;
+ }
+
+ void set_active(bool p_active) {
+ toggle_button->set_pressed(p_active);
+ active = p_active;
+ }
+
LogFilter(MessageType p_type) :
type(p_type) {
}
@@ -124,6 +133,9 @@ private:
// Warnings or Errors are encounetered.
Button *tool_button;
+ bool is_loading_state = false; // Used to disable saving requests while loading (some signals from buttons will try trigger a save, which happens during loading).
+ Timer *save_state_timer;
+
static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type);
ErrorHandlerList eh;
@@ -147,6 +159,10 @@ private:
void _set_collapse(bool p_collapse);
+ void _start_state_save_timer();
+ void _save_state();
+ void _load_state();
+
protected:
static void _bind_methods();
void _notification(int p_what);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 0f0b2da6ab..7aed5b2b7f 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -170,8 +170,7 @@
#include "editor/plugins/texture_layered_editor_plugin.h"
#include "editor/plugins/texture_region_editor_plugin.h"
#include "editor/plugins/theme_editor_plugin.h"
-#include "editor/plugins/tile_map_editor_plugin.h"
-#include "editor/plugins/tile_set_editor_plugin.h"
+#include "editor/plugins/tiles/tiles_editor_plugin.h"
#include "editor/plugins/version_control_editor_plugin.h"
#include "editor/plugins/visual_shader_editor_plugin.h"
#include "editor/progress_dialog.h"
@@ -1408,8 +1407,9 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) {
} else {
// The 3D editor may be disabled as a feature, but scenes can still be opened.
// This check prevents the preview from regenerating in case those scenes are then saved.
+ // The preview will be generated if no feature profile is set (as the 3D editor is enabled by default).
Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile();
- if (profile.is_valid() && !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) {
+ if (!profile.is_valid() || !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) {
img = Node3DEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node()->get_texture()->get_image();
}
}
@@ -1803,28 +1803,6 @@ void EditorNode::_dialog_action(String p_file) {
}
} break;
- case FILE_EXPORT_TILESET: {
- Ref<TileSet> tileset;
- if (FileAccess::exists(p_file) && file_export_lib_merge->is_pressed()) {
- tileset = ResourceLoader::load(p_file, "TileSet");
-
- if (tileset.is_null()) {
- show_accept(TTR("Can't load TileSet for merging!"), TTR("OK"));
- return;
- }
-
- } else {
- tileset = Ref<TileSet>(memnew(TileSet));
- }
-
- TileSetEditor::update_library_file(editor_data.get_edited_scene_root(), tileset, true);
-
- Error err = ResourceSaver::save(p_file, tileset);
- if (err) {
- show_accept(TTR("Error saving TileSet!"), TTR("OK"));
- return;
- }
- } break;
case RESOURCE_SAVE:
case RESOURCE_SAVE_AS: {
@@ -2469,15 +2447,24 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
Node *scene = editor_data.get_edited_scene_root(scene_idx);
if (!scene) {
- int saved = _save_external_resources();
- String err_text;
+ if (p_option == FILE_SAVE_SCENE) {
+ // Pressing Ctrl + S saves the current script if a scene is currently open, but it won't if the scene has no root node.
+ // Work around this by explicitly saving the script in this case (similar to pressing Ctrl + Alt + S).
+ ScriptEditor::get_singleton()->save_current_script();
+ }
+
+ const int saved = _save_external_resources();
if (saved > 0) {
- err_text = vformat(TTR("Saved %s modified resource(s)."), itos(saved));
- } else {
- err_text = TTR("A root node is required to save the scene.");
+ show_accept(
+ vformat(TTR("The current scene has no root node, but %d modified external resource(s) were saved anyway."), saved),
+ TTR("OK"));
+ } else if (p_option == FILE_SAVE_AS_SCENE) {
+ // Don't show this dialog when pressing Ctrl + S to avoid interfering with script saving.
+ show_accept(
+ TTR("A root node is required to save the scene. You can add a root node using the Scene tree dock."),
+ TTR("OK"));
}
- show_accept(err_text, TTR("OK"));
break;
}
@@ -2539,25 +2526,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
file_export_lib->set_title(TTR("Export Mesh Library"));
} break;
- case FILE_EXPORT_TILESET: {
- //Make sure that the scene has a root before trying to convert to tileset
- if (!editor_data.get_edited_scene_root()) {
- show_accept(TTR("This operation can't be done without a root node."), TTR("OK"));
- break;
- }
-
- List<String> extensions;
- Ref<TileSet> ml(memnew(TileSet));
- ResourceSaver::get_recognized_extensions(ml, &extensions);
- file_export_lib->clear_filters();
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- file_export_lib->add_filter("*." + E->get());
- }
-
- file_export_lib->popup_file_dialog();
- file_export_lib->set_title(TTR("Export Tile Set"));
-
- } break;
case FILE_EXTERNAL_OPEN_SCENE: {
if (unsaved_cache && !p_confirmed) {
@@ -4361,6 +4329,8 @@ void EditorNode::_save_docks() {
}
Ref<ConfigFile> config;
config.instance();
+ // Load and amend existing config if it exists.
+ config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg"));
_save_docks_to_config(config, "docks");
_save_open_scenes_to_config(config, "EditorNode");
@@ -5876,8 +5846,6 @@ EditorNode::EditorNode() {
register_exporters();
- GLOBAL_DEF("editor/run/main_run_args", "");
-
ClassDB::set_class_enabled("RootMotionView", true);
//defs here, use EDITOR_GET in logic
@@ -5901,7 +5869,7 @@ EditorNode::EditorNode() {
EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false);
EDITOR_DEF("interface/inspector/horizontal_vector_types_editing", true);
EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true);
- EDITOR_DEF("interface/inspector/resources_to_open_in_new_inspector", "Script,MeshLibrary,TileSet");
+ EDITOR_DEF("interface/inspector/resources_to_open_in_new_inspector", "Script,MeshLibrary");
EDITOR_DEF("interface/inspector/default_color_picker_mode", 0);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_color_picker_mode", PROPERTY_HINT_ENUM, "RGB,HSV,RAW", PROPERTY_USAGE_DEFAULT));
EDITOR_DEF("interface/inspector/default_color_picker_shape", (int32_t)ColorPicker::SHAPE_VHS_CIRCLE);
@@ -6230,8 +6198,8 @@ EditorNode::EditorNode() {
p = file_menu->get_popup();
- p->add_shortcut(ED_SHORTCUT("editor/new_scene", TTR("New Scene")), FILE_NEW_SCENE);
- p->add_shortcut(ED_SHORTCUT("editor/new_inherited_scene", TTR("New Inherited Scene...")), FILE_NEW_INHERITED_SCENE);
+ p->add_shortcut(ED_SHORTCUT("editor/new_scene", TTR("New Scene"), KEY_MASK_CMD + KEY_N), FILE_NEW_SCENE);
+ p->add_shortcut(ED_SHORTCUT("editor/new_inherited_scene", TTR("New Inherited Scene..."), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_N), FILE_NEW_INHERITED_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/open_scene", TTR("Open Scene..."), KEY_MASK_CMD + KEY_O), FILE_OPEN_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/reopen_closed_scene", TTR("Reopen Closed Scene"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_T), FILE_OPEN_PREV);
p->add_submenu_item(TTR("Open Recent"), "RecentScenes", FILE_OPEN_RECENT);
@@ -6253,7 +6221,6 @@ EditorNode::EditorNode() {
p->add_child(pm_export);
p->add_submenu_item(TTR("Convert To..."), "Export");
pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_MeshLibrary", TTR("MeshLibrary...")), FILE_EXPORT_MESH_LIBRARY);
- pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_TileSet", TTR("TileSet...")), FILE_EXPORT_TILESET);
pm_export->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option));
p->add_separator();
@@ -6490,7 +6457,6 @@ EditorNode::EditorNode() {
// Toggle for video driver
video_driver = memnew(OptionButton);
- video_driver->set_flat(true);
video_driver->set_focus_mode(Control::FOCUS_NONE);
video_driver->connect("item_selected", callable_mp(this, &EditorNode::_video_driver_selected));
video_driver->add_theme_font_override("font", gui_base->get_theme_font("bold", "EditorFonts"));
@@ -6826,8 +6792,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(ItemListEditorPlugin(this)));
add_editor_plugin(memnew(Polygon3DEditorPlugin(this)));
add_editor_plugin(memnew(CollisionPolygon2DEditorPlugin(this)));
- add_editor_plugin(memnew(TileSetEditorPlugin(this)));
- add_editor_plugin(memnew(TileMapEditorPlugin(this)));
+ add_editor_plugin(memnew(TilesEditorPlugin(this)));
add_editor_plugin(memnew(SpriteFramesEditorPlugin(this)));
add_editor_plugin(memnew(TextureRegionEditorPlugin(this)));
add_editor_plugin(memnew(GIProbeEditorPlugin(this)));
diff --git a/editor/editor_node.h b/editor/editor_node.h
index d06851cb4f..07de183719 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -135,7 +135,6 @@ private:
FILE_EXPORT_MESH_LIBRARY,
FILE_INSTALL_ANDROID_SOURCE,
FILE_EXPLORE_ANDROID_BUILD_TEMPLATES,
- FILE_EXPORT_TILESET,
FILE_SAVE_OPTIMIZED,
FILE_OPEN_RECENT,
FILE_OPEN_OLD_SCENE,
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 47c0e31da6..6638962c72 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -3254,6 +3254,8 @@ EditorPropertyResource::EditorPropertyResource() {
preview->set_offset(SIDE_TOP, 1);
preview->set_offset(SIDE_BOTTOM, -1);
preview->set_offset(SIDE_RIGHT, -1);
+ // This is required to draw the focus outline in front of the preview, rather than behind.
+ preview->set_draw_behind_parent(true);
assign->add_child(preview);
assign->connect("gui_input", callable_mp(this, &EditorPropertyResource::_button_input));
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index de688f2709..fb2980c948 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -575,7 +575,6 @@ EditorPropertyArray::EditorPropertyArray() {
object.instance();
page_len = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
edit = memnew(Button);
- edit->set_flat(true);
edit->set_h_size_flags(SIZE_EXPAND_FILL);
edit->set_clip_text(true);
edit->connect("pressed", callable_mp(this, &EditorPropertyArray::_edit_pressed));
@@ -1070,7 +1069,6 @@ EditorPropertyDictionary::EditorPropertyDictionary() {
object.instance();
page_len = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
edit = memnew(Button);
- edit->set_flat(true);
edit->set_h_size_flags(SIZE_EXPAND_FILL);
edit->set_clip_text(true);
edit->connect("pressed", callable_mp(this, &EditorPropertyDictionary::_edit_pressed));
diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp
index e46f4eb65a..5e6d2ab69c 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -183,15 +183,50 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L
args.push_back(p_scene);
}
+ String exec = OS::get_singleton()->get_executable_path();
+
if (p_custom_args != "") {
- Vector<String> cargs = p_custom_args.split(" ", false);
- for (int i = 0; i < cargs.size(); i++) {
- args.push_back(cargs[i].replace(" ", "%20"));
+ // Allow the user to specify a command to run, similar to Steam's launch options.
+ // In this case, Godot will no longer be run directly; it's up to the underlying command
+ // to run it. For instance, this can be used on Linux to force a running project
+ // to use Optimus using `prime-run` or similar.
+ // Example: `prime-run %command% --time-scale 0.5`
+ const int placeholder_pos = p_custom_args.find("%command%");
+
+ Vector<String> custom_args;
+
+ if (placeholder_pos != -1) {
+ // Prepend executable-specific custom arguments.
+ // If nothing is placed before `%command%`, behave as if no placeholder was specified.
+ Vector<String> exec_args = p_custom_args.substr(0, placeholder_pos).split(" ", false);
+ if (exec_args.size() >= 1) {
+ exec = exec_args[0];
+ exec_args.remove(0);
+
+ // Append the Godot executable name before we append executable arguments
+ // (since the order is reversed when using `push_front()`).
+ args.push_front(OS::get_singleton()->get_executable_path());
+ }
+
+ for (int i = exec_args.size() - 1; i >= 0; i--) {
+ // Iterate backwards as we're pushing items in the reverse order.
+ args.push_front(exec_args[i].replace(" ", "%20"));
+ }
+
+ // Append Godot-specific custom arguments.
+ custom_args = p_custom_args.substr(placeholder_pos + String("%command%").size()).split(" ", false);
+ for (int i = 0; i < custom_args.size(); i++) {
+ args.push_back(custom_args[i].replace(" ", "%20"));
+ }
+ } else {
+ // Append Godot-specific custom arguments.
+ custom_args = p_custom_args.split(" ", false);
+ for (int i = 0; i < custom_args.size(); i++) {
+ args.push_back(custom_args[i].replace(" ", "%20"));
+ }
}
}
- String exec = OS::get_singleton()->get_executable_path();
-
printf("Running: %s", exec.utf8().get_data());
for (List<String>::Element *E = args.front(); E; E = E->next()) {
printf(" %s", E->get().utf8().get_data());
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 4ddae7c062..aba14df812 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -437,27 +437,27 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// Theme
_initial_set("interface/theme/preset", "Default");
- hints["interface/theme/preset"] = PropertyInfo(Variant::STRING, "interface/theme/preset", PROPERTY_HINT_ENUM, "Default,Alien,Arc,Godot 2,Grey,Light,Solarized (Dark),Solarized (Light),Custom", PROPERTY_USAGE_DEFAULT);
+ hints["interface/theme/preset"] = PropertyInfo(Variant::STRING, "interface/theme/preset", PROPERTY_HINT_ENUM, "Default,Breeze Dark,Godot 2,Grey,Light,Solarized (Dark),Solarized (Light),Custom", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/theme/icon_and_font_color", 0);
hints["interface/theme/icon_and_font_color"] = PropertyInfo(Variant::INT, "interface/theme/icon_and_font_color", PROPERTY_HINT_ENUM, "Auto,Dark,Light", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/theme/base_color", Color(0.2, 0.23, 0.31));
hints["interface/theme/base_color"] = PropertyInfo(Variant::COLOR, "interface/theme/base_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/theme/accent_color", Color(0.41, 0.61, 0.91));
hints["interface/theme/accent_color"] = PropertyInfo(Variant::COLOR, "interface/theme/accent_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
- _initial_set("interface/theme/contrast", 0.25);
- hints["interface/theme/contrast"] = PropertyInfo(Variant::FLOAT, "interface/theme/contrast", PROPERTY_HINT_RANGE, "0.01, 1, 0.01");
+ _initial_set("interface/theme/contrast", 0.3);
+ hints["interface/theme/contrast"] = PropertyInfo(Variant::FLOAT, "interface/theme/contrast", PROPERTY_HINT_RANGE, "-1, 1, 0.01");
_initial_set("interface/theme/icon_saturation", 1.0);
hints["interface/theme/icon_saturation"] = PropertyInfo(Variant::FLOAT, "interface/theme/icon_saturation", PROPERTY_HINT_RANGE, "0,2,0.01", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/theme/relationship_line_opacity", 0.1);
hints["interface/theme/relationship_line_opacity"] = PropertyInfo(Variant::FLOAT, "interface/theme/relationship_line_opacity", PROPERTY_HINT_RANGE, "0.00, 1, 0.01");
- _initial_set("interface/theme/highlight_tabs", false);
- _initial_set("interface/theme/border_size", 1);
- _initial_set("interface/theme/use_graph_node_headers", false);
+ _initial_set("interface/theme/border_size", 0);
hints["interface/theme/border_size"] = PropertyInfo(Variant::INT, "interface/theme/border_size", PROPERTY_HINT_RANGE, "0,2,1", PROPERTY_USAGE_DEFAULT);
+ _initial_set("interface/theme/corner_radius", 3);
+ hints["interface/theme/corner_radius"] = PropertyInfo(Variant::INT, "interface/theme/corner_radius", PROPERTY_HINT_RANGE, "0,6,1", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/theme/additional_spacing", 0);
hints["interface/theme/additional_spacing"] = PropertyInfo(Variant::FLOAT, "interface/theme/additional_spacing", PROPERTY_HINT_RANGE, "0,5,0.1", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/theme/custom_theme", "");
- hints["interface/theme/custom_theme"] = PropertyInfo(Variant::STRING, "interface/theme/custom_theme", PROPERTY_HINT_GLOBAL_FILE, "*.res,*.tres,*.theme", PROPERTY_USAGE_DEFAULT);
+ hints["interface/theme/custom_theme"] = PropertyInfo(Variant::STRING, "interface/theme/custom_theme", PROPERTY_HINT_GLOBAL_FILE, "*.res,*.tres,*.theme", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
// Scene tabs
_initial_set("interface/scene_tabs/show_thumbnail_on_hover", true);
@@ -787,6 +787,7 @@ void EditorSettings::_load_default_text_editor_theme() {
_initial_set("text_editor/highlighting/symbol_color", Color(0.73, 0.87, 1.0));
_initial_set("text_editor/highlighting/keyword_color", Color(1.0, 1.0, 0.7));
+ _initial_set("text_editor/highlighting/control_flow_keyword_color", Color(1.0, 0.85, 0.7));
_initial_set("text_editor/highlighting/base_type_color", Color(0.64, 1.0, 0.83));
_initial_set("text_editor/highlighting/engine_type_color", Color(0.51, 0.83, 1.0));
_initial_set("text_editor/highlighting/user_type_color", Color(0.42, 0.67, 0.93));
@@ -1138,14 +1139,14 @@ void EditorSettings::setup_language() {
}
void EditorSettings::setup_network() {
- List<IP_Address> local_ip;
+ List<IPAddress> local_ip;
IP::get_singleton()->get_local_addresses(&local_ip);
String hint;
String current = has_setting("network/debug/remote_host") ? get("network/debug/remote_host") : "";
String selected = "127.0.0.1";
// Check that current remote_host is a valid interface address and populate hints.
- for (List<IP_Address>::Element *E = local_ip.front(); E; E = E->next()) {
+ for (List<IPAddress>::Element *E = local_ip.front(); E; E = E->next()) {
String ip = E->get();
// link-local IPv6 addresses don't work, skipping them
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 7cc9ebd63e..d5ad638436 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -65,9 +65,12 @@ static Ref<StyleBoxEmpty> make_empty_stylebox(float p_margin_left = -1, float p_
return style;
}
-static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
+static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1, int p_corner_width = 0) {
Ref<StyleBoxFlat> style(memnew(StyleBoxFlat));
style->set_bg_color(p_color);
+ // Adjust level of detail based on the corners' effective sizes.
+ style->set_corner_detail(Math::ceil(1.5 * p_corner_width * EDSCALE));
+ style->set_corner_radius_all(p_corner_width);
style->set_default_margin(SIDE_LEFT, p_margin_left * EDSCALE);
style->set_default_margin(SIDE_RIGHT, p_margin_right * EDSCALE);
style->set_default_margin(SIDE_BOTTOM, p_margin_bottom * EDSCALE);
@@ -286,9 +289,9 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Ref<Theme> theme = Ref<Theme>(memnew(Theme));
- const float default_contrast = 0.25;
+ const float default_contrast = 0.3;
- //Theme settings
+ // Theme settings
Color accent_color = EDITOR_GET("interface/theme/accent_color");
Color base_color = EDITOR_GET("interface/theme/base_color");
float contrast = EDITOR_GET("interface/theme/contrast");
@@ -297,56 +300,47 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
String preset = EDITOR_GET("interface/theme/preset");
- bool highlight_tabs = EDITOR_GET("interface/theme/highlight_tabs");
int border_size = EDITOR_GET("interface/theme/border_size");
-
- bool use_gn_headers = EDITOR_GET("interface/theme/use_graph_node_headers");
+ int corner_radius = EDITOR_GET("interface/theme/corner_radius");
Color preset_accent_color;
Color preset_base_color;
float preset_contrast = 0;
- // Please, use alphabet order if you've added new theme here(After "Default" and "Custom")
+ // Please use alphabetical order if you're adding a new theme here
+ // (after "Custom")
- if (preset == "Default") {
- preset_accent_color = Color(0.41, 0.61, 0.91);
- preset_base_color = Color(0.2, 0.23, 0.31);
- preset_contrast = default_contrast;
- } else if (preset == "Custom") {
+ if (preset == "Custom") {
accent_color = EDITOR_GET("interface/theme/accent_color");
base_color = EDITOR_GET("interface/theme/base_color");
contrast = EDITOR_GET("interface/theme/contrast");
- } else if (preset == "Alien") {
- preset_accent_color = Color(0.11, 1.0, 0.6);
- preset_base_color = Color(0.18, 0.22, 0.25);
- preset_contrast = 0.25;
- } else if (preset == "Arc") {
- preset_accent_color = Color(0.32, 0.58, 0.89);
- preset_base_color = Color(0.22, 0.24, 0.29);
- preset_contrast = 0.25;
+ } else if (preset == "Breeze Dark") {
+ preset_accent_color = Color(0.26, 0.76, 1.00);
+ preset_base_color = Color(0.24, 0.26, 0.28);
+ preset_contrast = default_contrast;
} else if (preset == "Godot 2") {
preset_accent_color = Color(0.53, 0.67, 0.89);
preset_base_color = Color(0.24, 0.23, 0.27);
- preset_contrast = 0.25;
+ preset_contrast = default_contrast;
} else if (preset == "Grey") {
- preset_accent_color = Color(0.72, 0.89, 1.0);
+ preset_accent_color = Color(0.72, 0.89, 1.00);
preset_base_color = Color(0.24, 0.24, 0.24);
- preset_contrast = 0.2;
+ preset_contrast = default_contrast;
} else if (preset == "Light") {
- preset_accent_color = Color(0.13, 0.44, 1.0);
- preset_base_color = Color(1, 1, 1);
+ preset_accent_color = Color(0.18, 0.50, 1.00);
+ preset_base_color = Color(1.00, 1.00, 1.00);
preset_contrast = 0.08;
} else if (preset == "Solarized (Dark)") {
preset_accent_color = Color(0.15, 0.55, 0.82);
- preset_base_color = Color(0.03, 0.21, 0.26);
- preset_contrast = 0.23;
+ preset_base_color = Color(0.04, 0.23, 0.27);
+ preset_contrast = default_contrast;
} else if (preset == "Solarized (Light)") {
preset_accent_color = Color(0.15, 0.55, 0.82);
preset_base_color = Color(0.99, 0.96, 0.89);
- preset_contrast = 0.06;
+ preset_contrast = 0.08;
} else { // Default
- preset_accent_color = Color(0.41, 0.61, 0.91);
- preset_base_color = Color(0.2, 0.23, 0.31);
+ preset_accent_color = Color(0.44, 0.73, 0.98);
+ preset_base_color = Color(0.21, 0.24, 0.29);
preset_contrast = default_contrast;
}
@@ -358,12 +352,13 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
EditorSettings::get_singleton()->set_initial_value("interface/theme/base_color", base_color);
EditorSettings::get_singleton()->set_initial_value("interface/theme/contrast", contrast);
}
+
EditorSettings::get_singleton()->set_manually("interface/theme/preset", preset);
EditorSettings::get_singleton()->set_manually("interface/theme/accent_color", accent_color);
EditorSettings::get_singleton()->set_manually("interface/theme/base_color", base_color);
EditorSettings::get_singleton()->set_manually("interface/theme/contrast", contrast);
- //Colors
+ // Colors
bool dark_theme = EditorSettings::get_singleton()->is_dark_theme();
const Color dark_color_1 = base_color.lerp(Color(0, 0, 0, 1), contrast);
@@ -372,14 +367,14 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color background_color = dark_color_2;
- // white (dark theme) or black (light theme), will be used to generate the rest of the colors
+ // White (dark theme) or black (light theme), will be used to generate the rest of the colors
const Color mono_color = dark_theme ? Color(1, 1, 1) : Color(0, 0, 0);
const Color contrast_color_1 = base_color.lerp(mono_color, MAX(contrast, default_contrast));
const Color contrast_color_2 = base_color.lerp(mono_color, MAX(contrast * 1.5, default_contrast * 1.5));
const Color font_color = mono_color.lerp(base_color, 0.25);
- const Color font_hover_color = mono_color.lerp(base_color, 0.15);
+ const Color font_hover_color = mono_color.lerp(base_color, 0.125);
const Color font_disabled_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.3);
const Color selection_color = accent_color * Color(1, 1, 1, 0.4);
const Color disabled_color = mono_color.inverted().lerp(base_color, 0.7);
@@ -393,8 +388,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
icon_pressed_color.a = 1.0;
const Color separator_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.1);
-
- const Color highlight_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.2);
+ const Color highlight_color = Color(accent_color.r, accent_color.g, accent_color.b, 0.275);
float prev_icon_saturation = theme->has_color("icon_saturation", "Editor") ? theme->get_color("icon_saturation", "Editor").r : 1.0;
@@ -447,35 +441,35 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("thumb_size", "Editor", thumb_size);
theme->set_constant("dark_theme", "Editor", dark_theme);
- //Register icons + font
+ // Register icons + font
- // the resolution and the icon color (dark_theme bool) has not changed, so we do not regenerate the icons
+ // The resolution and the icon color (dark_theme bool) has not changed, so we do not regenerate the icons.
if (p_theme != nullptr && fabs(p_theme->get_constant("scale", "Editor") - EDSCALE) < 0.00001 && (bool)p_theme->get_constant("dark_theme", "Editor") == dark_theme && prev_icon_saturation == icon_saturation) {
- // register already generated icons
+ // Register already generated icons.
for (int i = 0; i < editor_icons_count; i++) {
theme->set_icon(editor_icons_names[i], "EditorIcons", p_theme->get_icon(editor_icons_names[i], "EditorIcons"));
}
} else {
editor_register_and_generate_icons(theme, dark_theme, thumb_size, false, icon_saturation);
}
- // thumbnail size has changed, so we regenerate the medium sizes
+ // Thumbnail size has changed, so we regenerate the medium sizes
if (p_theme != nullptr && fabs((double)p_theme->get_constant("thumb_size", "Editor") - thumb_size) > 0.00001) {
editor_register_and_generate_icons(p_theme, dark_theme, thumb_size, true);
}
editor_register_fonts(theme);
- // Highlighted tabs and border width
- Color tab_color = highlight_tabs ? base_color.lerp(font_color, contrast) : base_color;
// Ensure borders are visible when using an editor scale below 100%.
- const int border_width = CLAMP(border_size, 0, 3) * MAX(1, EDSCALE);
-
+ const int border_width = CLAMP(border_size, 0, 2) * MAX(1, EDSCALE);
+ const int corner_width = CLAMP(corner_radius, 0, 6) * EDSCALE;
const int default_margin_size = 4;
- const int margin_size_extra = default_margin_size + CLAMP(border_size, 0, 3);
+ const int margin_size_extra = default_margin_size + CLAMP(border_size, 0, 2);
- // styleboxes
- // this is the most commonly used stylebox, variations should be made as duplicate of this
- Ref<StyleBoxFlat> style_default = make_flat_stylebox(base_color, default_margin_size, default_margin_size, default_margin_size, default_margin_size);
+ // Styleboxes
+ // This is the most commonly used stylebox, variations should be made as duplicate of this
+ Ref<StyleBoxFlat> style_default = make_flat_stylebox(base_color, default_margin_size, default_margin_size, default_margin_size, default_margin_size, corner_width);
+ // Work around issue about antialiased edges being blurrier (GH-35279).
+ style_default->set_anti_aliased(false);
style_default->set_border_width_all(border_width);
style_default->set_border_color(base_color);
style_default->set_draw_center(true);
@@ -483,11 +477,13 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Button and widgets
const float extra_spacing = EDITOR_GET("interface/theme/additional_spacing");
+ const Vector2 widget_default_margin = Vector2(extra_spacing + 6, extra_spacing + default_margin_size + 1) * EDSCALE;
+
Ref<StyleBoxFlat> style_widget = style_default->duplicate();
- style_widget->set_default_margin(SIDE_LEFT, (extra_spacing + 6) * EDSCALE);
- style_widget->set_default_margin(SIDE_TOP, (extra_spacing + default_margin_size) * EDSCALE);
- style_widget->set_default_margin(SIDE_RIGHT, (extra_spacing + 6) * EDSCALE);
- style_widget->set_default_margin(SIDE_BOTTOM, (extra_spacing + default_margin_size) * EDSCALE);
+ style_widget->set_default_margin(SIDE_LEFT, widget_default_margin.x);
+ style_widget->set_default_margin(SIDE_TOP, widget_default_margin.y);
+ style_widget->set_default_margin(SIDE_RIGHT, widget_default_margin.x);
+ style_widget->set_default_margin(SIDE_BOTTOM, widget_default_margin.y);
style_widget->set_bg_color(dark_color_1);
style_widget->set_border_color(dark_color_2);
@@ -496,69 +492,82 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_widget_disabled->set_bg_color(disabled_bg_color);
Ref<StyleBoxFlat> style_widget_focus = style_widget->duplicate();
+ style_widget_focus->set_draw_center(false);
+ style_widget_focus->set_border_width_all(Math::round(2 * MAX(1, EDSCALE)));
style_widget_focus->set_border_color(accent_color);
Ref<StyleBoxFlat> style_widget_pressed = style_widget->duplicate();
- style_widget_pressed->set_border_color(accent_color);
+ style_widget_pressed->set_bg_color(dark_color_1.darkened(0.125));
Ref<StyleBoxFlat> style_widget_hover = style_widget->duplicate();
- style_widget_hover->set_border_color(contrast_color_1);
+ style_widget_hover->set_bg_color(mono_color * Color(1, 1, 1, 0.11));
+ style_widget_hover->set_border_color(mono_color * Color(1, 1, 1, 0.05));
- // style for windows, popups, etc..
+ // Style for windows, popups, etc..
Ref<StyleBoxFlat> style_popup = style_default->duplicate();
- const int popup_margin_size = default_margin_size * EDSCALE * 2;
+ const int popup_margin_size = default_margin_size * EDSCALE * 3;
style_popup->set_default_margin(SIDE_LEFT, popup_margin_size);
style_popup->set_default_margin(SIDE_TOP, popup_margin_size);
style_popup->set_default_margin(SIDE_RIGHT, popup_margin_size);
style_popup->set_default_margin(SIDE_BOTTOM, popup_margin_size);
style_popup->set_border_color(contrast_color_1);
- style_popup->set_border_width_all(MAX(EDSCALE, border_width));
const Color shadow_color = Color(0, 0, 0, dark_theme ? 0.3 : 0.1);
style_popup->set_shadow_color(shadow_color);
style_popup->set_shadow_size(4 * EDSCALE);
Ref<StyleBoxLine> style_popup_separator(memnew(StyleBoxLine));
style_popup_separator->set_color(separator_color);
- style_popup_separator->set_grow_begin(popup_margin_size - MAX(EDSCALE, border_width));
- style_popup_separator->set_grow_end(popup_margin_size - MAX(EDSCALE, border_width));
- style_popup_separator->set_thickness(MAX(EDSCALE, border_width));
+ style_popup_separator->set_grow_begin(popup_margin_size - MAX(Math::round(EDSCALE), border_width));
+ style_popup_separator->set_grow_end(popup_margin_size - MAX(Math::round(EDSCALE), border_width));
+ style_popup_separator->set_thickness(MAX(Math::round(EDSCALE), border_width));
Ref<StyleBoxLine> style_popup_labeled_separator_left(memnew(StyleBoxLine));
- style_popup_labeled_separator_left->set_grow_begin(popup_margin_size - MAX(EDSCALE, border_width));
+ style_popup_labeled_separator_left->set_grow_begin(popup_margin_size - MAX(Math::round(EDSCALE), border_width));
style_popup_labeled_separator_left->set_color(separator_color);
- style_popup_labeled_separator_left->set_thickness(MAX(EDSCALE, border_width));
+ style_popup_labeled_separator_left->set_thickness(MAX(Math::round(EDSCALE), border_width));
Ref<StyleBoxLine> style_popup_labeled_separator_right(memnew(StyleBoxLine));
- style_popup_labeled_separator_right->set_grow_end(popup_margin_size - MAX(EDSCALE, border_width));
+ style_popup_labeled_separator_right->set_grow_end(popup_margin_size - MAX(Math::round(EDSCALE), border_width));
style_popup_labeled_separator_right->set_color(separator_color);
- style_popup_labeled_separator_right->set_thickness(MAX(EDSCALE, border_width));
+ style_popup_labeled_separator_right->set_thickness(MAX(Math::round(EDSCALE), border_width));
Ref<StyleBoxEmpty> style_empty = make_empty_stylebox(default_margin_size, default_margin_size, default_margin_size, default_margin_size);
// Tabs
- const int tab_default_margin_side = 10 * EDSCALE + extra_spacing * EDSCALE;
- const int tab_default_margin_vertical = 5 * EDSCALE + extra_spacing * EDSCALE;
-
Ref<StyleBoxFlat> style_tab_selected = style_widget->duplicate();
- style_tab_selected->set_border_width_all(border_width);
- style_tab_selected->set_border_width(SIDE_BOTTOM, 0);
- style_tab_selected->set_border_color(dark_color_3);
- style_tab_selected->set_expand_margin_size(SIDE_BOTTOM, border_width);
- style_tab_selected->set_default_margin(SIDE_LEFT, tab_default_margin_side);
- style_tab_selected->set_default_margin(SIDE_RIGHT, tab_default_margin_side);
- style_tab_selected->set_default_margin(SIDE_BOTTOM, tab_default_margin_vertical);
- style_tab_selected->set_default_margin(SIDE_TOP, tab_default_margin_vertical);
- style_tab_selected->set_bg_color(tab_color);
+ // Add a highlight line at the top of the selected tab.
+ style_tab_selected->set_border_width_all(0);
+ style_tab_selected->set_border_width(SIDE_TOP, Math::round(2 * EDSCALE));
+ // Make the highlight line prominent, but not too prominent as to not be distracting.
+ style_tab_selected->set_border_color(dark_color_2.lerp(accent_color, 0.75));
+ // Don't round the top corners to avoid creating a small blank space between the tabs and the main panel.
+ // This also makes the top highlight look better.
+ style_tab_selected->set_corner_radius_all(0);
+
+ // Prevent visible artifacts and cover the top-left rounded corner of the panel below the tab if selected
+ // We can't prevent them with both rounded corners and non-zero border width, though
+ style_tab_selected->set_expand_margin_size(SIDE_BOTTOM, corner_width > 0 ? corner_width : border_width);
+
+ style_tab_selected->set_default_margin(SIDE_LEFT, widget_default_margin.x + 2 * EDSCALE);
+ style_tab_selected->set_default_margin(SIDE_RIGHT, widget_default_margin.x + 2 * EDSCALE);
+ style_tab_selected->set_default_margin(SIDE_BOTTOM, widget_default_margin.y);
+ style_tab_selected->set_default_margin(SIDE_TOP, widget_default_margin.y);
+ style_tab_selected->set_bg_color(base_color);
Ref<StyleBoxFlat> style_tab_unselected = style_tab_selected->duplicate();
style_tab_unselected->set_bg_color(dark_color_1);
+ style_tab_unselected->set_expand_margin_size(SIDE_BOTTOM, 0);
+ // Add some spacing between unselected tabs to make them easier to distinguish from each other
style_tab_unselected->set_border_color(dark_color_2);
+ style_tab_unselected->set_border_width(SIDE_LEFT, Math::round(2 * EDSCALE));
+ style_tab_unselected->set_border_width(SIDE_RIGHT, Math::round(2 * EDSCALE));
Ref<StyleBoxFlat> style_tab_disabled = style_tab_selected->duplicate();
style_tab_disabled->set_bg_color(disabled_bg_color);
- style_tab_disabled->set_border_color(disabled_color);
+ style_tab_disabled->set_expand_margin_size(SIDE_BOTTOM, 0);
+ style_tab_disabled->set_border_color(disabled_bg_color);
// Editor background
Color background_color_opaque = background_color;
@@ -566,10 +575,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("Background", "EditorStyles", make_flat_stylebox(background_color_opaque, default_margin_size, default_margin_size, default_margin_size, default_margin_size));
// Focus
- Ref<StyleBoxFlat> style_focus = style_default->duplicate();
- style_focus->set_draw_center(false);
- style_focus->set_border_color(contrast_color_2);
- theme->set_stylebox("Focus", "EditorStyles", style_focus);
+ theme->set_stylebox("Focus", "EditorStyles", style_widget_focus);
// Menu
Ref<StyleBoxFlat> style_menu = style_widget->duplicate();
@@ -585,33 +591,25 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Play button group
theme->set_stylebox("PlayButtonPanel", "EditorStyles", style_empty);
- //MenuButton
- Ref<StyleBoxFlat> style_menu_hover_border = style_widget->duplicate();
- style_menu_hover_border->set_draw_center(false);
- style_menu_hover_border->set_border_width_all(0);
- style_menu_hover_border->set_border_width(SIDE_BOTTOM, border_width);
- style_menu_hover_border->set_border_color(accent_color);
-
- Ref<StyleBoxFlat> style_menu_hover_bg = style_widget->duplicate();
- style_menu_hover_bg->set_border_width_all(0);
- style_menu_hover_bg->set_bg_color(dark_color_1);
-
theme->set_stylebox("normal", "MenuButton", style_menu);
- theme->set_stylebox("hover", "MenuButton", style_menu);
+ theme->set_stylebox("hover", "MenuButton", style_widget_hover);
theme->set_stylebox("pressed", "MenuButton", style_menu);
theme->set_stylebox("focus", "MenuButton", style_menu);
theme->set_stylebox("disabled", "MenuButton", style_menu);
theme->set_stylebox("normal", "PopupMenu", style_menu);
- theme->set_stylebox("hover", "PopupMenu", style_menu_hover_bg);
- theme->set_stylebox("pressed", "PopupMenu", style_menu);
+
+ Ref<StyleBoxFlat> style_menu_hover = style_widget_hover->duplicate();
+ // Don't use rounded corners for hover highlights since the StyleBox touches the PopupMenu's edges.
+ style_menu_hover->set_corner_radius_all(0);
+ theme->set_stylebox("hover", "PopupMenu", style_menu_hover);
theme->set_stylebox("focus", "PopupMenu", style_menu);
theme->set_stylebox("disabled", "PopupMenu", style_menu);
theme->set_color("font_color", "MenuButton", font_color);
theme->set_color("font_hover_color", "MenuButton", font_hover_color);
- theme->set_stylebox("MenuHover", "EditorStyles", style_menu_hover_border);
+ theme->set_stylebox("MenuHover", "EditorStyles", style_widget_hover);
// Buttons
theme->set_stylebox("normal", "Button", style_widget);
@@ -646,7 +644,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_disabled_color", "OptionButton", font_disabled_color);
theme->set_color("icon_hover_color", "OptionButton", icon_hover_color);
theme->set_icon("arrow", "OptionButton", theme->get_icon("GuiOptionArrow", "EditorIcons"));
- theme->set_constant("arrow_margin", "OptionButton", default_margin_size * EDSCALE);
+ theme->set_constant("arrow_margin", "OptionButton", widget_default_margin.x - 2 * EDSCALE);
theme->set_constant("modulate_arrow", "OptionButton", true);
theme->set_constant("hseparation", "OptionButton", 4 * EDSCALE);
@@ -672,7 +670,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_disabled_color", "CheckButton", font_disabled_color);
theme->set_color("icon_hover_color", "CheckButton", icon_hover_color);
- theme->set_constant("hseparation", "CheckButton", 4 * EDSCALE);
+ theme->set_constant("hseparation", "CheckButton", 8 * EDSCALE);
theme->set_constant("check_vadjust", "CheckButton", 0 * EDSCALE);
// Checkbox
@@ -697,7 +695,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_disabled_color", "CheckBox", font_disabled_color);
theme->set_color("icon_hover_color", "CheckBox", icon_hover_color);
- theme->set_constant("hseparation", "CheckBox", 4 * EDSCALE);
+ theme->set_constant("hseparation", "CheckBox", 8 * EDSCALE);
theme->set_constant("check_vadjust", "CheckBox", 0 * EDSCALE);
// PopupDialog
@@ -794,12 +792,13 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Tree & ItemList background
Ref<StyleBoxFlat> style_tree_bg = style_default->duplicate();
- style_tree_bg->set_bg_color(dark_color_1);
+ // Make Trees easier to distinguish from other controls by using a darker background color.
+ style_tree_bg->set_bg_color(dark_color_1.lerp(dark_color_2, 0.5));
style_tree_bg->set_border_color(dark_color_3);
theme->set_stylebox("bg", "Tree", style_tree_bg);
- const Color guide_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.05);
- Color relationship_line_color = Color(mono_color.r, mono_color.g, mono_color.b, relationship_line_opacity);
+ const Color guide_color = mono_color * Color(1, 1, 1, 0.05);
+ Color relationship_line_color = mono_color * Color(1, 1, 1, relationship_line_opacity);
// Tree
theme->set_icon("checked", "Tree", theme->get_icon("GuiChecked", "EditorIcons"));
theme->set_icon("unchecked", "Tree", theme->get_icon("GuiUnchecked", "EditorIcons"));
@@ -808,7 +807,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("arrow_collapsed_mirrored", "Tree", theme->get_icon("GuiTreeArrowLeft", "EditorIcons"));
theme->set_icon("updown", "Tree", theme->get_icon("GuiTreeUpdown", "EditorIcons"));
theme->set_icon("select_arrow", "Tree", theme->get_icon("GuiDropdown", "EditorIcons"));
- theme->set_stylebox("bg_focus", "Tree", style_focus);
+ theme->set_stylebox("bg_focus", "Tree", style_widget_focus);
theme->set_stylebox("custom_button", "Tree", make_empty_stylebox());
theme->set_stylebox("custom_button_pressed", "Tree", make_empty_stylebox());
theme->set_stylebox("custom_button_hover", "Tree", style_widget);
@@ -819,8 +818,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("guide_color", "Tree", guide_color);
theme->set_color("relationship_line_color", "Tree", relationship_line_color);
theme->set_color("drop_position_color", "Tree", accent_color);
- theme->set_constant("vseparation", "Tree", (extra_spacing + default_margin_size) * EDSCALE);
- theme->set_constant("hseparation", "Tree", (extra_spacing + default_margin_size) * EDSCALE);
+ theme->set_constant("vseparation", "Tree", widget_default_margin.y - EDSCALE);
+ theme->set_constant("hseparation", "Tree", 6 * EDSCALE);
+ theme->set_constant("guide_width", "Tree", border_width);
theme->set_constant("item_margin", "Tree", 3 * default_margin_size * EDSCALE);
theme->set_constant("button_margin", "Tree", default_margin_size * EDSCALE);
theme->set_constant("draw_relationship_lines", "Tree", relationship_line_opacity >= 0.01);
@@ -829,7 +829,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("scroll_speed", "Tree", 12);
Ref<StyleBoxFlat> style_tree_btn = style_default->duplicate();
- style_tree_btn->set_bg_color(contrast_color_1);
+ style_tree_btn->set_bg_color(highlight_color);
style_tree_btn->set_border_width_all(0);
theme->set_stylebox("button_pressed", "Tree", style_tree_btn);
@@ -882,14 +882,14 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("cursor_unfocused", "ItemList", style_itemlist_cursor);
theme->set_stylebox("selected_focus", "ItemList", style_tree_focus);
theme->set_stylebox("selected", "ItemList", style_tree_selected);
- theme->set_stylebox("bg_focus", "ItemList", style_focus);
+ theme->set_stylebox("bg_focus", "ItemList", style_widget_focus);
theme->set_stylebox("bg", "ItemList", style_itemlist_bg);
theme->set_color("font_color", "ItemList", font_color);
theme->set_color("font_selected_color", "ItemList", mono_color);
theme->set_color("guide_color", "ItemList", guide_color);
- theme->set_constant("vseparation", "ItemList", 3 * EDSCALE);
- theme->set_constant("hseparation", "ItemList", 3 * EDSCALE);
- theme->set_constant("icon_margin", "ItemList", default_margin_size * EDSCALE);
+ theme->set_constant("vseparation", "ItemList", widget_default_margin.y - EDSCALE);
+ theme->set_constant("hseparation", "ItemList", 6 * EDSCALE);
+ theme->set_constant("icon_margin", "ItemList", 6 * EDSCALE);
theme->set_constant("line_separation", "ItemList", 3 * EDSCALE);
// Tabs & TabContainer
@@ -940,8 +940,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("Content", "EditorStyles", style_content_panel_vp);
// Separators
- theme->set_stylebox("separator", "HSeparator", make_line_stylebox(separator_color, border_width));
- theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, border_width, 0, 0, true));
+ theme->set_stylebox("separator", "HSeparator", make_line_stylebox(separator_color, MAX(Math::round(EDSCALE), border_width)));
+ theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, MAX(Math::round(EDSCALE), border_width), 0, 0, true));
// Debugger
@@ -956,9 +956,23 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("BottomPanelDebuggerOverride", "EditorStyles", style_panel_invisible_top);
// LineEdit
- theme->set_stylebox("normal", "LineEdit", style_widget);
+
+ Ref<StyleBoxFlat> style_line_edit = style_widget->duplicate();
+ // Add a bottom line to make LineEdits more visible, especially in sectioned inspectors
+ // such as the Project Settings.
+ style_line_edit->set_border_width(SIDE_BOTTOM, Math::round(2 * EDSCALE));
+ style_line_edit->set_border_color(dark_color_2);
+ // Don't round the bottom corner to make the line look sharper.
+ style_tab_selected->set_corner_radius(CORNER_BOTTOM_LEFT, 0);
+ style_tab_selected->set_corner_radius(CORNER_BOTTOM_RIGHT, 0);
+
+ Ref<StyleBoxFlat> style_line_edit_disabled = style_line_edit->duplicate();
+ style_line_edit_disabled->set_border_color(disabled_color);
+ style_line_edit_disabled->set_bg_color(disabled_bg_color);
+
+ theme->set_stylebox("normal", "LineEdit", style_line_edit);
theme->set_stylebox("focus", "LineEdit", style_widget_focus);
- theme->set_stylebox("read_only", "LineEdit", style_widget_disabled);
+ theme->set_stylebox("read_only", "LineEdit", style_line_edit_disabled);
theme->set_icon("clear", "LineEdit", theme->get_icon("GuiClose", "EditorIcons"));
theme->set_color("read_only", "LineEdit", font_disabled_color);
theme->set_color("font_color", "LineEdit", font_color);
@@ -969,9 +983,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("clear_button_color_pressed", "LineEdit", accent_color);
// TextEdit
- theme->set_stylebox("normal", "TextEdit", style_widget);
- theme->set_stylebox("focus", "TextEdit", style_widget_hover);
- theme->set_stylebox("read_only", "TextEdit", style_widget_disabled);
+ theme->set_stylebox("normal", "TextEdit", style_line_edit);
+ theme->set_stylebox("focus", "TextEdit", style_widget_focus);
+ theme->set_stylebox("read_only", "TextEdit", style_line_edit_disabled);
theme->set_constant("side_margin", "TabContainer", 0);
theme->set_icon("tab", "TextEdit", theme->get_icon("GuiTab", "EditorIcons"));
theme->set_icon("space", "TextEdit", theme->get_icon("GuiSpace", "EditorIcons"));
@@ -1014,14 +1028,22 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("hseparation", "GridContainer", default_margin_size * EDSCALE);
theme->set_constant("vseparation", "GridContainer", default_margin_size * EDSCALE);
- // WindowDialog
+ // Window
+
+ // Prevent corner artifacts between window title and body.
+ Ref<StyleBoxFlat> style_window_title = style_default->duplicate();
+ style_window_title->set_corner_radius(CORNER_TOP_LEFT, 0);
+ style_window_title->set_corner_radius(CORNER_TOP_RIGHT, 0);
+ // Prevent visible line between window title and body.
+ style_window_title->set_expand_margin_size(SIDE_BOTTOM, 2 * EDSCALE);
+ theme->set_stylebox("panel", "Window", style_window_title);
+
Ref<StyleBoxFlat> style_window = style_popup->duplicate();
- style_window->set_border_color(tab_color);
+ style_window->set_border_color(base_color);
style_window->set_border_width(SIDE_TOP, 24 * EDSCALE);
style_window->set_expand_margin_size(SIDE_TOP, 24 * EDSCALE);
-
- theme->set_stylebox("panel", "Window", style_default);
theme->set_stylebox("panel_window", "Window", style_window);
+
theme->set_color("title_color", "Window", font_color);
theme->set_icon("close", "Window", theme->get_icon("GuiClose", "EditorIcons"));
theme->set_icon("close_highlight", "Window", theme->get_icon("GuiClose", "EditorIcons"));
@@ -1032,10 +1054,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_font("title_font", "Window", theme->get_font("title", "EditorFonts"));
theme->set_font_size("title_font_size", "Window", theme->get_font_size("title_size", "EditorFonts"));
- // complex window, for now only Editor settings and Project settings
+ // Complex window (currently only Editor Settings and Project Settings)
Ref<StyleBoxFlat> style_complex_window = style_window->duplicate();
style_complex_window->set_bg_color(dark_color_2);
- style_complex_window->set_border_color(highlight_tabs ? tab_color : dark_color_2);
+ style_complex_window->set_border_color(dark_color_2);
theme->set_stylebox("panel", "EditorSettingsDialog", style_complex_window);
theme->set_stylebox("panel", "ProjectSettingsEditor", style_complex_window);
theme->set_stylebox("panel", "EditorAbout", style_complex_window);
@@ -1069,18 +1091,18 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// HSlider
theme->set_icon("grabber_highlight", "HSlider", theme->get_icon("GuiSliderGrabberHl", "EditorIcons"));
theme->set_icon("grabber", "HSlider", theme->get_icon("GuiSliderGrabber", "EditorIcons"));
- theme->set_stylebox("slider", "HSlider", make_flat_stylebox(dark_color_3, 0, default_margin_size / 2, 0, default_margin_size / 2));
- theme->set_stylebox("grabber_area", "HSlider", make_flat_stylebox(contrast_color_1, 0, default_margin_size / 2, 0, default_margin_size / 2));
+ theme->set_stylebox("slider", "HSlider", make_flat_stylebox(dark_color_3, 0, default_margin_size / 2, 0, default_margin_size / 2, corner_width));
+ theme->set_stylebox("grabber_area", "HSlider", make_flat_stylebox(contrast_color_1, 0, default_margin_size / 2, 0, default_margin_size / 2, corner_width));
theme->set_stylebox("grabber_area_highlight", "HSlider", make_flat_stylebox(contrast_color_1, 0, default_margin_size / 2, 0, default_margin_size / 2));
// VSlider
theme->set_icon("grabber", "VSlider", theme->get_icon("GuiSliderGrabber", "EditorIcons"));
theme->set_icon("grabber_highlight", "VSlider", theme->get_icon("GuiSliderGrabberHl", "EditorIcons"));
- theme->set_stylebox("slider", "VSlider", make_flat_stylebox(dark_color_3, default_margin_size / 2, 0, default_margin_size / 2, 0));
- theme->set_stylebox("grabber_area", "VSlider", make_flat_stylebox(contrast_color_1, default_margin_size / 2, 0, default_margin_size / 2, 0));
+ theme->set_stylebox("slider", "VSlider", make_flat_stylebox(dark_color_3, default_margin_size / 2, 0, default_margin_size / 2, 0, corner_width));
+ theme->set_stylebox("grabber_area", "VSlider", make_flat_stylebox(contrast_color_1, default_margin_size / 2, 0, default_margin_size / 2, 0, corner_width));
theme->set_stylebox("grabber_area_highlight", "VSlider", make_flat_stylebox(contrast_color_1, default_margin_size / 2, 0, default_margin_size / 2, 0));
- //RichTextLabel
+ // RichTextLabel
theme->set_color("default_color", "RichTextLabel", font_color);
theme->set_color("font_shadow_color", "RichTextLabel", Color(0, 0, 0, 0));
theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * EDSCALE);
@@ -1092,7 +1114,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("headline_color", "EditorHelp", mono_color);
// Panel
- theme->set_stylebox("panel", "Panel", make_flat_stylebox(dark_color_1, 6, 4, 6, 4));
+ theme->set_stylebox("panel", "Panel", make_flat_stylebox(dark_color_1, 6, 4, 6, 4, corner_width));
theme->set_stylebox("panel_fg", "Panel", style_default);
// Label
@@ -1113,16 +1135,15 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// TooltipPanel
Ref<StyleBoxFlat> style_tooltip = style_popup->duplicate();
- float v = MAX(border_size * EDSCALE, 1.0);
- style_tooltip->set_default_margin(SIDE_LEFT, v);
- style_tooltip->set_default_margin(SIDE_TOP, v);
- style_tooltip->set_default_margin(SIDE_RIGHT, v);
- style_tooltip->set_default_margin(SIDE_BOTTOM, v);
- style_tooltip->set_bg_color(Color(mono_color.r, mono_color.g, mono_color.b, 0.9));
- style_tooltip->set_border_width_all(border_width);
- style_tooltip->set_border_color(mono_color);
- theme->set_color("font_color", "TooltipLabel", font_color.inverted());
- theme->set_color("font_shadow_color", "TooltipLabel", mono_color.inverted() * Color(1, 1, 1, 0.1));
+ style_tooltip->set_shadow_size(0);
+ style_tooltip->set_default_margin(SIDE_LEFT, default_margin_size * EDSCALE);
+ style_tooltip->set_default_margin(SIDE_TOP, default_margin_size * EDSCALE * 0.5);
+ style_tooltip->set_default_margin(SIDE_RIGHT, default_margin_size * EDSCALE);
+ style_tooltip->set_default_margin(SIDE_BOTTOM, default_margin_size * EDSCALE * 0.5);
+ style_tooltip->set_bg_color(mono_color.inverted() * Color(1, 1, 1, 0.8));
+ style_tooltip->set_border_width_all(0);
+ theme->set_color("font_color", "TooltipLabel", font_hover_color);
+ theme->set_color("font_color_shadow", "TooltipLabel", Color(0, 0, 0, 0));
theme->set_stylebox("panel", "TooltipPanel", style_tooltip);
// PopupPanel
@@ -1189,23 +1210,20 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("resizer_color", "GraphEditMinimap", minimap_resizer_color);
// GraphNode
- const float mv = dark_theme ? 0.0 : 1.0;
- const float mv2 = 1.0 - mv;
const int gn_margin_side = 28;
- Ref<StyleBoxFlat> graphsb = make_flat_stylebox(Color(mv, mv, mv, 0.7), gn_margin_side, 24, gn_margin_side, 5);
+
+ Ref<StyleBoxFlat> graphsb = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.7), gn_margin_side, 24, gn_margin_side, 5, corner_width);
graphsb->set_border_width_all(border_width);
- graphsb->set_border_color(Color(mv2, mv2, mv2, 0.9));
- Ref<StyleBoxFlat> graphsbselected = make_flat_stylebox(Color(mv, mv, mv, 0.9), gn_margin_side, 24, gn_margin_side, 5);
- graphsbselected->set_border_width_all(border_width);
+ graphsb->set_border_color(dark_color_3);
+ Ref<StyleBoxFlat> graphsbselected = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.9), gn_margin_side, 24, gn_margin_side, 5, corner_width);
+ graphsbselected->set_border_width_all(2 * EDSCALE + border_width);
graphsbselected->set_border_color(Color(accent_color.r, accent_color.g, accent_color.b, 0.9));
- graphsbselected->set_shadow_size(8 * EDSCALE);
- graphsbselected->set_shadow_color(shadow_color);
- Ref<StyleBoxFlat> graphsbcomment = make_flat_stylebox(Color(mv, mv, mv, 0.3), gn_margin_side, 24, gn_margin_side, 5);
+ Ref<StyleBoxFlat> graphsbcomment = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.3), gn_margin_side, 24, gn_margin_side, 5, corner_width);
graphsbcomment->set_border_width_all(border_width);
- graphsbcomment->set_border_color(Color(mv2, mv2, mv2, 0.9));
- Ref<StyleBoxFlat> graphsbcommentselected = make_flat_stylebox(Color(mv, mv, mv, 0.4), gn_margin_side, 24, gn_margin_side, 5);
+ graphsbcomment->set_border_color(dark_color_3);
+ Ref<StyleBoxFlat> graphsbcommentselected = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.4), gn_margin_side, 24, gn_margin_side, 5, corner_width);
graphsbcommentselected->set_border_width_all(border_width);
- graphsbcommentselected->set_border_color(Color(mv2, mv2, mv2, 0.9));
+ graphsbcommentselected->set_border_color(dark_color_3);
Ref<StyleBoxFlat> graphsbbreakpoint = graphsbselected->duplicate();
graphsbbreakpoint->set_draw_center(false);
graphsbbreakpoint->set_border_color(warning_color);
@@ -1214,21 +1232,19 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
graphsbposition->set_draw_center(false);
graphsbposition->set_border_color(error_color);
graphsbposition->set_shadow_color(error_color * Color(1.0, 1.0, 1.0, 0.2));
- Ref<StyleBoxFlat> smgraphsb = make_flat_stylebox(Color(mv, mv, mv, 0.7), gn_margin_side, 24, gn_margin_side, 5);
+ Ref<StyleBoxFlat> smgraphsb = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.7), gn_margin_side, 24, gn_margin_side, 5, corner_width);
smgraphsb->set_border_width_all(border_width);
- smgraphsb->set_border_color(Color(mv2, mv2, mv2, 0.9));
- Ref<StyleBoxFlat> smgraphsbselected = make_flat_stylebox(Color(mv, mv, mv, 0.9), gn_margin_side, 24, gn_margin_side, 5);
- smgraphsbselected->set_border_width_all(border_width);
+ smgraphsb->set_border_color(dark_color_3);
+ Ref<StyleBoxFlat> smgraphsbselected = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.9), gn_margin_side, 24, gn_margin_side, 5, corner_width);
+ smgraphsbselected->set_border_width_all(2 * EDSCALE + border_width);
smgraphsbselected->set_border_color(Color(accent_color.r, accent_color.g, accent_color.b, 0.9));
smgraphsbselected->set_shadow_size(8 * EDSCALE);
smgraphsbselected->set_shadow_color(shadow_color);
- if (use_gn_headers) {
- graphsb->set_border_width(SIDE_TOP, 24 * EDSCALE);
- graphsbselected->set_border_width(SIDE_TOP, 24 * EDSCALE);
- graphsbcomment->set_border_width(SIDE_TOP, 24 * EDSCALE);
- graphsbcommentselected->set_border_width(SIDE_TOP, 24 * EDSCALE);
- }
+ graphsb->set_border_width(SIDE_TOP, 24 * EDSCALE);
+ graphsbselected->set_border_width(SIDE_TOP, 24 * EDSCALE);
+ graphsbcomment->set_border_width(SIDE_TOP, 24 * EDSCALE);
+ graphsbcommentselected->set_border_width(SIDE_TOP, 24 * EDSCALE);
theme->set_stylebox("frame", "GraphNode", graphsb);
theme->set_stylebox("selectedframe", "GraphNode", graphsbselected);
@@ -1239,7 +1255,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("state_machine_frame", "GraphNode", smgraphsb);
theme->set_stylebox("state_machine_selectedframe", "GraphNode", smgraphsbselected);
- Color default_node_color = Color(mv2, mv2, mv2);
+ Color default_node_color = dark_color_1.inverted();
theme->set_color("title_color", "GraphNode", default_node_color);
default_node_color.a = 0.7;
theme->set_color("close_color", "GraphNode", default_node_color);
@@ -1257,7 +1273,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("port", "GraphNode", theme->get_icon("GuiGraphNodePort", "EditorIcons"));
// GridContainer
- theme->set_constant("vseparation", "GridContainer", (extra_spacing + default_margin_size) * EDSCALE);
+ theme->set_constant("vseparation", "GridContainer", Math::round(widget_default_margin.y - 2 * EDSCALE));
// FileDialog
theme->set_icon("folder", "FileDialog", theme->get_icon("Folder", "EditorIcons"));
@@ -1306,6 +1322,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color symbol_color = Color(0.34, 0.57, 1.0).lerp(mono_color, dark_theme ? 0.5 : 0.3);
const Color keyword_color = Color(1.0, 0.44, 0.52);
+ const Color control_flow_keyword_color = dark_theme ? Color(1.0, 0.55, 0.8) : Color(0.8, 0.4, 0.6);
const Color basetype_color = dark_theme ? Color(0.26, 1.0, 0.76) : Color(0.0, 0.76, 0.38);
const Color type_color = basetype_color.lerp(mono_color, dark_theme ? 0.4 : 0.3);
const Color usertype_color = basetype_color.lerp(mono_color, dark_theme ? 0.7 : 0.5);
@@ -1344,6 +1361,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
if (text_editor_color_theme == "Adaptive") {
setting->set_initial_value("text_editor/highlighting/symbol_color", symbol_color, true);
setting->set_initial_value("text_editor/highlighting/keyword_color", keyword_color, true);
+ setting->set_initial_value("text_editor/highlighting/control_flow_keyword_color", control_flow_keyword_color, true);
setting->set_initial_value("text_editor/highlighting/base_type_color", basetype_color, true);
setting->set_initial_value("text_editor/highlighting/engine_type_color", type_color, true);
setting->set_initial_value("text_editor/highlighting/user_type_color", usertype_color, true);
diff --git a/editor/editor_zoom_widget.cpp b/editor/editor_zoom_widget.cpp
new file mode 100644
index 0000000000..f9be829493
--- /dev/null
+++ b/editor/editor_zoom_widget.cpp
@@ -0,0 +1,163 @@
+/*************************************************************************/
+/* editor_zoom_widget.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "editor_zoom_widget.h"
+
+#include "core/os/keyboard.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
+
+void EditorZoomWidget::_update_zoom_label() {
+ String zoom_text;
+ // The zoom level displayed is relative to the editor scale
+ // (like in most image editors). Its lower bound is clamped to 1 as some people
+ // lower the editor scale to increase the available real estate,
+ // even if their display doesn't have a particularly low DPI.
+ if (zoom >= 10) {
+ // Don't show a decimal when the zoom level is higher than 1000 %.
+ zoom_text = TS->format_number(rtos(Math::round((zoom / MAX(1, EDSCALE)) * 100))) + " " + TS->percent_sign();
+ } else {
+ zoom_text = TS->format_number(rtos(Math::snapped((zoom / MAX(1, EDSCALE)) * 100, 0.1))) + " " + TS->percent_sign();
+ }
+
+ zoom_reset->set_text(zoom_text);
+}
+
+void EditorZoomWidget::_button_zoom_minus() {
+ set_zoom_by_increments(-6);
+ emit_signal("zoom_changed", zoom);
+}
+
+void EditorZoomWidget::_button_zoom_reset() {
+ set_zoom(1.0);
+ emit_signal("zoom_changed", zoom);
+}
+
+void EditorZoomWidget::_button_zoom_plus() {
+ set_zoom_by_increments(6);
+ emit_signal("zoom_changed", zoom);
+}
+
+float EditorZoomWidget::get_zoom() {
+ return zoom;
+}
+
+void EditorZoomWidget::set_zoom(float p_zoom) {
+ if (p_zoom > 0 && p_zoom != zoom) {
+ zoom = p_zoom;
+ _update_zoom_label();
+ }
+}
+
+void EditorZoomWidget::set_zoom_by_increments(int p_increment_count) {
+ // Base increment factor defined as the twelveth root of two.
+ // This allow a smooth geometric evolution of the zoom, with the advantage of
+ // visiting all integer power of two scale factors.
+ // note: this is analogous to the 'semitones' interval in the music world
+ // In order to avoid numerical imprecisions, we compute and edit a zoom index
+ // with the following relation: zoom = 2 ^ (index / 12)
+
+ if (zoom < CMP_EPSILON || p_increment_count == 0) {
+ return;
+ }
+
+ // Remove Editor scale from the index computation
+ float zoom_noscale = zoom / MAX(1, EDSCALE);
+
+ // zoom = 2**(index/12) => log2(zoom) = index/12
+ float closest_zoom_index = Math::round(Math::log(zoom_noscale) * 12.f / Math::log(2.f));
+
+ float new_zoom_index = closest_zoom_index + p_increment_count;
+ float new_zoom = Math::pow(2.f, new_zoom_index / 12.f);
+
+ // Restore Editor scale transformation
+ new_zoom *= MAX(1, EDSCALE);
+
+ set_zoom(new_zoom);
+}
+
+void EditorZoomWidget::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED:
+ zoom_minus->set_icon(get_theme_icon("ZoomLess", "EditorIcons"));
+ zoom_plus->set_icon(get_theme_icon("ZoomMore", "EditorIcons"));
+ break;
+ default:
+ break;
+ }
+}
+
+void EditorZoomWidget::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &EditorZoomWidget::set_zoom);
+ ClassDB::bind_method(D_METHOD("get_zoom"), &EditorZoomWidget::get_zoom);
+ ClassDB::bind_method(D_METHOD("set_zoom_by_increments", "increment"), &EditorZoomWidget::set_zoom_by_increments);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom");
+
+ ADD_SIGNAL(MethodInfo("zoom_changed", PropertyInfo(Variant::FLOAT, "zoom")));
+}
+
+EditorZoomWidget::EditorZoomWidget() {
+ // Zoom buttons
+ zoom_minus = memnew(Button);
+ zoom_minus->set_flat(true);
+ add_child(zoom_minus);
+ zoom_minus->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_minus));
+ zoom_minus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_minus", TTR("Zoom Out"), KEY_MASK_CMD | KEY_MINUS));
+ zoom_minus->set_shortcut_context(this);
+ zoom_minus->set_focus_mode(FOCUS_NONE);
+
+ zoom_reset = memnew(Button);
+ zoom_reset->set_flat(true);
+ add_child(zoom_reset);
+ zoom_reset->add_theme_constant_override("outline_size", 1);
+ zoom_reset->add_theme_color_override("font_outline_color", Color(0, 0, 0));
+ zoom_reset->add_theme_color_override("font_color", Color(1, 1, 1));
+ zoom_reset->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_reset));
+ zoom_reset->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", TTR("Zoom Reset"), KEY_MASK_CMD | KEY_0));
+ zoom_reset->set_shortcut_context(this);
+ zoom_reset->set_focus_mode(FOCUS_NONE);
+ zoom_reset->set_text_align(Button::TextAlign::ALIGN_CENTER);
+ // Prevent the button's size from changing when the text size changes
+ zoom_reset->set_custom_minimum_size(Size2(75 * EDSCALE, 0));
+
+ zoom_plus = memnew(Button);
+ zoom_plus->set_flat(true);
+ add_child(zoom_plus);
+ zoom_plus->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_plus));
+ zoom_plus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", TTR("Zoom In"), KEY_MASK_CMD | KEY_EQUAL)); // Usually direct access key for PLUS
+ zoom_plus->set_shortcut_context(this);
+ zoom_plus->set_focus_mode(FOCUS_NONE);
+
+ _update_zoom_label();
+
+ add_theme_constant_override("separation", Math::round(-8 * EDSCALE));
+}
diff --git a/platform/iphone/native_video_view.h b/editor/editor_zoom_widget.h
index 2df5e27fa4..4e95018e52 100644
--- a/platform/iphone/native_video_view.h
+++ b/editor/editor_zoom_widget.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* native_video_view.h */
+/* editor_zoom_widget.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,14 +28,35 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#import <UIKit/UIKit.h>
+#ifndef EDITOR_ZOOM_WIDGET_H
+#define EDITOR_ZOOM_WIDGET_H
-@interface GodotNativeVideoView : UIView
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
-- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
-- (BOOL)isVideoPlaying;
-- (void)pauseVideo;
-- (void)unpauseVideo;
-- (void)stopVideo;
+class EditorZoomWidget : public HBoxContainer {
+ GDCLASS(EditorZoomWidget, HBoxContainer);
-@end
+ Button *zoom_minus;
+ Button *zoom_reset;
+ Button *zoom_plus;
+
+ float zoom = 1.0;
+ void _update_zoom_label();
+ void _button_zoom_minus();
+ void _button_zoom_reset();
+ void _button_zoom_plus();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ EditorZoomWidget();
+
+ float get_zoom();
+ void set_zoom(float p_zoom);
+ void set_zoom_by_increments(int p_increment_count);
+};
+
+#endif // EDITOR_ZOOM_WIDGET_H
diff --git a/editor/fileserver/editor_file_server.h b/editor/fileserver/editor_file_server.h
index 8ffd4f9692..d0405e0bb7 100644
--- a/editor/fileserver/editor_file_server.h
+++ b/editor/fileserver/editor_file_server.h
@@ -54,7 +54,7 @@ class EditorFileServer : public Object {
bool quit = false;
};
- Ref<TCP_Server> server;
+ Ref<TCPServer> server;
Set<Thread *> to_wait;
static void _close_client(ClientData *cd);
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 09424647fe..6cd45fdf97 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -2806,7 +2806,6 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
toolbar_hbc->add_child(current_path);
button_reload = memnew(Button);
- button_reload->set_flat(true);
button_reload->connect("pressed", callable_mp(this, &FileSystemDock::_rescan));
button_reload->set_focus_mode(FOCUS_NONE);
button_reload->set_tooltip(TTR("Re-Scan Filesystem"));
@@ -2814,7 +2813,6 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
toolbar_hbc->add_child(button_reload);
button_toggle_display_mode = memnew(Button);
- button_toggle_display_mode->set_flat(true);
button_toggle_display_mode->set_toggle_mode(true);
button_toggle_display_mode->connect("toggled", callable_mp(this, &FileSystemDock::_toggle_split_mode));
button_toggle_display_mode->set_focus_mode(FOCUS_NONE);
diff --git a/editor/icons/AddAtlasTile.svg b/editor/icons/AddAtlasTile.svg
deleted file mode 100644
index a6d94005a8..0000000000
--- a/editor/icons/AddAtlasTile.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6z" fill="#c9cfd4"/></svg>
diff --git a/editor/icons/AddAutotile.svg b/editor/icons/AddAutotile.svg
deleted file mode 100644
index 52664b3eb6..0000000000
--- a/editor/icons/AddAutotile.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6z" fill="#4490fc"/></svg>
diff --git a/editor/icons/AddSingleTile.svg b/editor/icons/AddSingleTile.svg
deleted file mode 100644
index 64bf1c99c0..0000000000
--- a/editor/icons/AddSingleTile.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6z" fill="#fce844"/></svg>
diff --git a/editor/icons/CollapseTree.svg b/editor/icons/CollapseTree.svg
new file mode 100644
index 0000000000..ece9071e03
--- /dev/null
+++ b/editor/icons/CollapseTree.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-rule="nonzero"><path d="m8 9.669-3.536 2.583h2.536v2.537h2v-2.537h2.536z"/><path d="m8 6.355-3.536-2.583h2.536v-2.537h2v2.537h2.536z"/><path d="m.704 7.085h14.591v1.831h-14.591z"/></g></svg>
diff --git a/editor/icons/EditAddRemove.svg b/editor/icons/EditAddRemove.svg
new file mode 100644
index 0000000000..307557cbfc
--- /dev/null
+++ b/editor/icons/EditAddRemove.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.5105509 1c-.554 0-1 .446-1 1v2h4v-2c0-.554-.446-1-1-1zm5.4894491 12.5v1.5h6v-1.5zm-6.4894491-8.5v7l2 3 2-3v-7zm1 1h1v5h-1zm7.7394491 0v2.25h-2.25v1.5h2.25v2.25h1.5v-2.25h2.25v-1.5h-2.25v-2.25z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/EditorHandleDisabled.svg b/editor/icons/EditorHandleDisabled.svg
new file mode 100644
index 0000000000..483a25a571
--- /dev/null
+++ b/editor/icons/EditorHandleDisabled.svg
@@ -0,0 +1 @@
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><path d="m5 0c-2.7614237 0-5 2.2385763-5 5s2.2385763 5 5 5 5-2.2385763 5-5-2.2385763-5-5-5zm-.0421327 2.2165215c.014044-.0001063.0280887-.0001063.0421327 0 1.5372727 0 2.7834785 1.2462058 2.7834785 2.7834785s-1.2462058 2.7834785-2.7834785 2.7834785-2.7834785-1.2462058-2.7834785-2.7834785c-.0001743-1.5209681 1.2205519-2.760456 2.7413458-2.7834785z" fill="#fff" fill-opacity=".294118"/></svg>
diff --git a/editor/icons/Eraser.svg b/editor/icons/Eraser.svg
new file mode 100644
index 0000000000..4995fa863c
--- /dev/null
+++ b/editor/icons/Eraser.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m10.228155 1.5447161-9.60250173 9.6107429 1.41421353 1.414213 1.6134635 1.885612.00693-.0069 4.2288056.000024 7.4852811-7.4852817zm-4.4456043 7.2823178 2.3136653 2.5858141-1.0357479 1.035746h-2.5853592l-1.0209853-1.293133z" fill="#e0e0e0" fill-opacity=".996078" stroke-width="1.02405"/></svg>
diff --git a/editor/icons/ExpandTree.svg b/editor/icons/ExpandTree.svg
new file mode 100644
index 0000000000..abdc1f9458
--- /dev/null
+++ b/editor/icons/ExpandTree.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-rule="nonzero"><path d="m8 16-3.536-2.597h2.536v-2.523h2v2.523h2.536z"/><path d="m8 0-3.536 2.583h2.536v2.537h2v-2.537h2.536z"/><path d="m.704 7.085h14.591v1.831h-14.591z"/></g></svg>
diff --git a/editor/icons/FontSize.svg b/editor/icons/FontSize.svg
index e608d89b6a..3e148009ce 100644
--- a/editor/icons/FontSize.svg
+++ b/editor/icons/FontSize.svg
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g id="SmallerT"><rect x="1.047" y="7.127" width="6.025" height="1.2" style="fill:#e0e0e0;fill-rule:nonzero;"/><rect x="3.452" y="7.127" width="1.214" height="6.508" style="fill:#e0e0e0;fill-rule:nonzero;"/><rect x="2.238" y="13.171" width="3.643" height="0.465" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M1.477,7.127l0,2.4l-0.43,0l-0,-2.4l0.43,0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M7.071,7.127l0,2.4l-0.43,0l0,-2.4l0.43,0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M1.477,8.327l0,1.2c0,-0.658 0.389,-1.2 0.861,-1.2l-0.861,0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M5.78,8.327c0.473,0 0.861,0.542 0.861,1.2l0,-1.2l-0.861,0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M2.238,13.171c0.666,-0 1.214,-0.42 1.214,-0.93l0,0.93l-1.214,-0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M5.88,13.171c-0.666,-0 -1.214,-0.42 -1.214,-0.93l0,0.93l1.214,-0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/></g><g id="BiggerT"><rect x="4.563" y="2.873" width="10.773" height="1.539" style="fill:#e0e0e0;fill-rule:nonzero;"/><rect x="9.18" y="2.873" width="1.539" height="10.773" style="fill:#e0e0e0;fill-rule:nonzero;"/><rect x="7.641" y="12.877" width="4.617" height="0.769" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M5.332,2.873l0,3.078l-0.769,0l-0,-3.078l0.769,0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M15.336,2.873l-0,3.078l-0.77,0l0,-3.078l0.77,0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M5.332,4.412l0,1.539c0,-0.844 0.695,-1.539 1.539,-1.539l-1.539,0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M13.027,4.412c0.844,0 1.539,0.695 1.539,1.539l0,-1.539l-1.539,0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M7.641,12.877c0.844,-0 1.539,-0.695 1.539,-1.539l-0,1.539l-1.539,-0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/><path d="M12.258,12.877c-0.845,-0 -1.539,-0.695 -1.539,-1.539l-0,1.539l1.539,-0Z" style="fill:#e0e0e0;fill-rule:nonzero;"/></g></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-rule="nonzero"><path d="m1.047 7.127h6.025v1.2h-6.025z"/><path d="m3.452 7.127h1.214v6.508h-1.214z"/><path d="m2.238 13.171h3.643v.465h-3.643z"/><path d="m1.477 7.127v2.4h-.43v-2.4z"/><path d="m7.071 7.127v2.4h-.43v-2.4z"/><path d="m1.477 8.327v1.2c0-.658.389-1.2.861-1.2z"/><path d="m5.78 8.327c.473 0 .861.542.861 1.2v-1.2z"/><path d="m2.238 13.171c.666 0 1.214-.42 1.214-.93v.93z"/><path d="m5.88 13.171c-.666 0-1.214-.42-1.214-.93v.93z"/><path d="m4.563 2.873h10.773v1.539h-10.773z"/><path d="m9.18 2.873h1.539v10.773h-1.539z"/><path d="m7.641 12.877h4.617v.769h-4.617z"/><path d="m5.332 2.873v3.078h-.769v-3.078z"/><path d="m15.336 2.873v3.078h-.77v-3.078z"/><path d="m5.332 4.412v1.539c0-.844.695-1.539 1.539-1.539z"/><path d="m13.027 4.412c.844 0 1.539.695 1.539 1.539v-1.539z"/><path d="m7.641 12.877c.844 0 1.539-.695 1.539-1.539v1.539z"/><path d="m12.258 12.877c-.845 0-1.539-.695-1.539-1.539v1.539z"/></g></svg>
diff --git a/editor/icons/RectangleAddRemove.svg b/editor/icons/RectangleAddRemove.svg
new file mode 100644
index 0000000000..87e2155a0d
--- /dev/null
+++ b/editor/icons/RectangleAddRemove.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 2c-.5522619.0000552-.9999448.4477381-1 1v10c.0000552.552262.4477381.999945 1 1h6v-2h-5v-8h10v1h2v-2c-.000055-.5522619-.447738-.9999448-1-1zm9.25 4v2.25h-2.25v1.5h2.25v2.25h1.5v-2.25h2.25v-1.5h-2.25v-2.25zm-2.25 7.5v1.5h6v-1.5z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/TerrainMatchCorners.svg b/editor/icons/TerrainMatchCorners.svg
new file mode 100644
index 0000000000..b9dfcf67d2
--- /dev/null
+++ b/editor/icons/TerrainMatchCorners.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke-width="70.7093"><path d="m15 1h-6v3h3v3h3z" fill="#ffa62a"/><path d="m1 1h6v3h-2.9999996l-.0000004 3h-3.0000004z" fill="#1aab1a"/><path d="m1 15h5.9999999v-3h-3v-3h-2.9999999z" fill="#ffa62a"/><path d="m15.000001 15h-6v-3h2.999999l.000001-2.9999997h3z" fill="#1aab1a"/></g></svg>
diff --git a/editor/icons/TerrainMatchCornersAndSides.svg b/editor/icons/TerrainMatchCornersAndSides.svg
new file mode 100644
index 0000000000..81153005bd
--- /dev/null
+++ b/editor/icons/TerrainMatchCornersAndSides.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1h4v3h-1l-1 1-1-1h-1z" fill="#ffa62a" stroke-width="70.7093"/><path d="m1 15h4v-3h-1v-1h-3z" fill="#1aab1a" stroke-width="70.7093"/><path d="m6 15h4v-3h-1l-1-1-1 1h-1z" fill="#ffa62a" stroke-width="99.998"/><g stroke-width="70.7093"><path d="m1 10v-4h3v1l1 1-1 1v1z" fill="#ffa62a"/><path d="m15 10v-4h-3v1l-1 1 1 1v1z" fill="#ffa62a"/><g fill="#1aab1a"><path d="m15 15h-4v-3h1v-1h3z"/><path d="m15 1h-4v3h1v1h3z"/><path d="m1 1h4.0000004v3h-1v1h-3.0000004z"/></g></g></svg>
diff --git a/editor/icons/TerrainMatchSides.svg b/editor/icons/TerrainMatchSides.svg
new file mode 100644
index 0000000000..1e2ec75ea7
--- /dev/null
+++ b/editor/icons/TerrainMatchSides.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g stroke-width="70.7093"><path d="m1 14v-12l3 3v2l1 1-1 1v2z" fill="#1aab1a"/><path d="m15 14v-12l-3 2.7057075v2l-1 1.0000001 1 1v1.9999994z" fill="#1aab1a"/><g fill="#ffa62a"><path d="m2 15h12l-3-3h-2l-1-1-1 1h-2z"/><path d="m14 1h-12l2.9999992 3h1.9999998l1.000001.9999999 1-.9999999h1.999999z"/></g></g></svg>
diff --git a/editor/icons/ThemeDeselectAll.svg b/editor/icons/ThemeDeselectAll.svg
new file mode 100644
index 0000000000..d43ca85163
--- /dev/null
+++ b/editor/icons/ThemeDeselectAll.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m5.952 6.976h8.063v2.005h-8.063z" fill-rule="nonzero"/><path d="m.989 5.995h4.011v4.01h-4.011zm.968.968h2.075v2.074h-2.075z"/><path d="m5.952 1.956h8.063v2.005h-8.063z" fill-rule="nonzero"/><path d="m.989.974h4.011v4.011h-4.011zm.968.968h2.075v2.075h-2.075z"/><path d="m5.952 11.996h8.063v2.005h-8.063z" fill-rule="nonzero"/><path d="m.989 11.015h4.011v4.011h-4.011zm.968.968h2.075v2.075h-2.075z"/></g></svg>
diff --git a/editor/icons/ThemeRemoveAllItems.svg b/editor/icons/ThemeRemoveAllItems.svg
index 47ed624d04..652274a0e7 100644
--- a/editor/icons/ThemeRemoveAllItems.svg
+++ b/editor/icons/ThemeRemoveAllItems.svg
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path d="M8,1.745c-0.595,0 -1.084,0.489 -1.084,1.084l0,3.699l-3.851,-1.927c-0.163,-0.08 -0.343,-0.119 -0.525,-0.112c-0.395,0.015 -0.752,0.244 -0.929,0.597c-0.076,0.151 -0.115,0.317 -0.115,0.485c0,0.41 0.233,0.786 0.599,0.97l3.481,1.74l-3.481,1.74c-0.366,0.184 -0.599,0.56 -0.599,0.97c0,0.168 0.039,0.334 0.115,0.485c0.183,0.367 0.559,0.599 0.969,0.599c0.168,0 0.334,-0.039 0.485,-0.114l3.851,-1.927l0,3.111c0,0.594 0.489,1.084 1.084,1.084c0.595,-0 1.084,-0.49 1.084,-1.084l-0,-3.111l3.851,1.927c0.151,0.075 0.317,0.114 0.485,0.114c0.41,0 0.786,-0.232 0.969,-0.599c0.076,-0.151 0.115,-0.317 0.115,-0.485c-0,-0.41 -0.233,-0.786 -0.599,-0.97l-3.481,-1.74l3.481,-1.74c0.366,-0.184 0.599,-0.56 0.599,-0.97c-0,-0.168 -0.039,-0.334 -0.115,-0.485c-0.182,-0.364 -0.554,-0.596 -0.961,-0.599c-0.171,-0.001 -0.34,0.038 -0.493,0.114l-3.851,1.927l-0,-3.699c-0,-0.595 -0.489,-1.084 -1.084,-1.084Z" style="fill:#a5efac;"/><path d="M8,1.745c-0,0 -0,1.783 -0,1.783l-1.084,0l0,-0.699c0,-0.595 0.489,-1.084 1.084,-1.084Z" style="fill:#ff7070;fill-rule:nonzero;"/><path d="M1.528,5.312l2.957,-0l-1.42,-0.711c-0.163,-0.08 -0.343,-0.119 -0.525,-0.112c-0.395,0.015 -0.752,0.244 -0.929,0.597c-0.036,0.072 -0.064,0.148 -0.083,0.226Zm5.388,-1.784l1.084,0l-0,1.784l-1.084,-0l0,-1.784Z" style="fill:#ffeb70;fill-rule:nonzero;"/><path d="M6.916,5.312l1.084,-0l-0,1.783l-4.796,0l-1.109,-0.554c-0.366,-0.184 -0.599,-0.56 -0.599,-0.97c0,-0.088 0.011,-0.175 0.032,-0.259l2.957,-0l2.431,1.216l0,-1.216Z" style="fill:#9dff70;fill-rule:nonzero;"/><path d="M3.204,7.095l4.796,0l-0,1.783l-3.619,0l1.195,-0.597l-2.372,-1.186Z" style="fill:#70ffb9;fill-rule:nonzero;"/><path d="M4.381,8.878l3.619,0l-0,1.784l-1.084,-0l0,-0.628l-1.255,0.628l-4.114,-0c0.088,-0.274 0.283,-0.508 0.548,-0.641l2.286,-1.143Z" style="fill:#70deff;fill-rule:nonzero;"/><path d="M6.916,12.445l1.084,0l-0,1.784l-0,-0c-0.595,-0.001 -1.084,-0.49 -1.084,-1.084l0,-0.7Z" style="fill:#ff70ac;fill-rule:nonzero;"/><path d="M6.916,10.662l1.084,-0l-0,1.783l-1.084,0l0,-1.783Zm-1.255,-0l-4.114,-0c-0.033,0.105 -0.051,0.216 -0.051,0.329c0,0.168 0.039,0.334 0.115,0.485c0.183,0.367 0.559,0.599 0.969,0.599c0.168,0 0.334,-0.039 0.485,-0.114l2.596,-1.299Z" style="fill:#9f70ff;fill-rule:nonzero;"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1.745c-.595 0-1.084.489-1.084 1.084v3.699l-3.851-1.927c-.163-.08-.343-.119-.525-.112-.395.015-.752.244-.929.597-.076.151-.115.317-.115.485 0 .41.233.786.599.97l3.481 1.74-3.481 1.74c-.366.184-.599.56-.599.97 0 .168.039.334.115.485.183.367.559.599.969.599.168 0 .334-.039.485-.114l3.851-1.927v3.111c0 .594.489 1.084 1.084 1.084s1.084-.49 1.084-1.084v-3.111l3.851 1.927c.151.075.317.114.485.114.41 0 .786-.232.969-.599.076-.151.115-.317.115-.485 0-.41-.233-.786-.599-.97l-3.481-1.74 3.481-1.74c.366-.184.599-.56.599-.97 0-.168-.039-.334-.115-.485-.182-.364-.554-.596-.961-.599-.171-.001-.34.038-.493.114l-3.851 1.927v-3.699c0-.595-.489-1.084-1.084-1.084z" fill="#a5efac"/><g fill-rule="nonzero"><path d="m8 1.745v1.783h-1.084v-.699c0-.595.489-1.084 1.084-1.084z" fill="#ff7070"/><path d="m1.528 5.312h2.957l-1.42-.711c-.163-.08-.343-.119-.525-.112-.395.015-.752.244-.929.597-.036.072-.064.148-.083.226zm5.388-1.784h1.084v1.784h-1.084z" fill="#ffeb70"/><path d="m6.916 5.312h1.084v1.783h-4.796l-1.109-.554c-.366-.184-.599-.56-.599-.97 0-.088.011-.175.032-.259h2.957l2.431 1.216z" fill="#9dff70"/><path d="m3.204 7.095h4.796v1.783h-3.619l1.195-.597z" fill="#70ffb9"/><path d="m4.381 8.878h3.619v1.784h-1.084v-.628l-1.255.628h-4.114c.088-.274.283-.508.548-.641z" fill="#70deff"/><path d="m6.916 12.445h1.084v1.784c-.595-.001-1.084-.49-1.084-1.084z" fill="#ff70ac"/><path d="m6.916 10.662h1.084v1.783h-1.084zm-1.255 0h-4.114c-.033.105-.051.216-.051.329 0 .168.039.334.115.485.183.367.559.599.969.599.168 0 .334-.039.485-.114z" fill="#9f70ff"/></g></svg>
diff --git a/editor/icons/ThemeRemoveCustomItems.svg b/editor/icons/ThemeRemoveCustomItems.svg
index bb8a8bd026..839f584fce 100644
--- a/editor/icons/ThemeRemoveCustomItems.svg
+++ b/editor/icons/ThemeRemoveCustomItems.svg
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path d="M11.299,3c0.772,0.513 1.42,1.199 1.888,2l-2.553,0c-0.706,-0.621 -1.629,-1 -2.634,-1c-1.005,0 -1.928,0.379 -2.634,1l-2.553,0c0.468,-0.801 1.116,-1.487 1.888,-2l6.598,-0Z" style="fill:#ffeb70;fill-rule:nonzero;"/><path d="M5.366,5c-0.593,0.522 -1.033,1.216 -1.238,2l-2.043,0c0.122,-0.717 0.373,-1.392 0.728,-2l2.553,-0Zm7.821,-0c0.355,0.608 0.606,1.283 0.728,2l-2.043,0c-0.205,-0.784 -0.645,-1.478 -1.238,-2l2.553,-0Z" style="fill:#9dff70;fill-rule:nonzero;"/><path d="M13.915,7c0.056,0.326 0.085,0.66 0.085,1c-0,0.34 -0.029,0.674 -0.085,1l-2.043,0c0.083,-0.32 0.128,-0.655 0.128,-1c0,-0.345 -0.045,-0.68 -0.128,-1l2.043,-0Zm-9.787,0c-0.083,0.32 -0.128,0.655 -0.128,1c0,0.345 0.045,0.68 0.128,1l-2.043,-0c-0.056,-0.326 -0.085,-0.66 -0.085,-1c0,-0.34 0.029,-0.674 0.085,-1l2.043,0Z" style="fill:#70ffb9;fill-rule:nonzero;"/><path d="M4.128,9c0.205,0.784 0.645,1.478 1.238,2l-2.553,0c-0.355,-0.608 -0.606,-1.283 -0.728,-2l2.043,0Zm9.787,0c-0.122,0.717 -0.373,1.392 -0.728,2l-2.553,0c0.593,-0.522 1.033,-1.216 1.238,-2l2.043,0Z" style="fill:#70deff;fill-rule:nonzero;"/><path d="M11.299,13l-6.598,0c0.949,0.631 2.084,1 3.299,1c1.215,0 2.35,-0.369 3.299,-1Z" style="fill:#ff70ac;fill-rule:nonzero;"/><path d="M13.187,11c-0.468,0.801 -1.116,1.487 -1.888,2l-6.598,0c-0.772,-0.513 -1.42,-1.199 -1.888,-2l2.553,0c0.706,0.621 1.629,1 2.634,1c1.005,0 1.928,-0.379 2.634,-1l2.553,0Z" style="fill:#9f70ff;fill-rule:nonzero;"/><path d="M4.701,3l6.598,0c-0.949,-0.631 -2.084,-1 -3.299,-1c-1.215,0 -2.35,0.369 -3.299,1Z" style="fill:#ff7070;fill-rule:nonzero;"/></svg>
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m11.299 3c.772.513 1.42 1.199 1.888 2h-2.553c-.706-.621-1.629-1-2.634-1s-1.928.379-2.634 1h-2.553c.468-.801 1.116-1.487 1.888-2z" fill="#ffeb70"/><path d="m5.366 5c-.593.522-1.033 1.216-1.238 2h-2.043c.122-.717.373-1.392.728-2zm7.821 0c.355.608.606 1.283.728 2h-2.043c-.205-.784-.645-1.478-1.238-2z" fill="#9dff70"/><path d="m13.915 7c.056.326.085.66.085 1s-.029.674-.085 1h-2.043c.083-.32.128-.655.128-1s-.045-.68-.128-1zm-9.787 0c-.083.32-.128.655-.128 1s.045.68.128 1h-2.043c-.056-.326-.085-.66-.085-1s.029-.674.085-1z" fill="#70ffb9"/><path d="m4.128 9c.205.784.645 1.478 1.238 2h-2.553c-.355-.608-.606-1.283-.728-2zm9.787 0c-.122.717-.373 1.392-.728 2h-2.553c.593-.522 1.033-1.216 1.238-2z" fill="#70deff"/><path d="m11.299 13h-6.598c.949.631 2.084 1 3.299 1s2.35-.369 3.299-1z" fill="#ff70ac"/><path d="m13.187 11c-.468.801-1.116 1.487-1.888 2h-6.598c-.772-.513-1.42-1.199-1.888-2h2.553c.706.621 1.629 1 2.634 1s1.928-.379 2.634-1z" fill="#9f70ff"/><path d="m4.701 3h6.598c-.949-.631-2.084-1-3.299-1s-2.35.369-3.299 1z" fill="#ff7070"/></g></svg>
diff --git a/editor/icons/ThemeSelectAll.svg b/editor/icons/ThemeSelectAll.svg
new file mode 100644
index 0000000000..59d9fb3387
--- /dev/null
+++ b/editor/icons/ThemeSelectAll.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m5.952 6.976h8.049v2.005h-8.049z" fill="#e0e0e0" fill-rule="nonzero"/><path d="m.989 5.995h4.011v4.011h-4.011z" fill="#fff" fill-rule="nonzero"/><path d="m.989 5.995h4.011v4.01h-4.011zm.968.968h2.075v2.074h-2.075z" fill="#e0e0e0"/><path d="m5.952 1.956h8.049v2.005h-8.049z" fill="#e0e0e0" fill-rule="nonzero"/><path d="m.989.974h4.011v4.011h-4.011z" fill="#fff" fill-rule="nonzero"/><path d="m.989.974h4.011v4.011h-4.011zm.968.968h2.075v2.075h-2.075z" fill="#e0e0e0"/><path d="m5.952 11.996h8.049v2.005h-8.049z" fill="#e0e0e0" fill-rule="nonzero"/><path d="m.989 11.015h4.011v4.011h-4.011z" fill="#fff" fill-rule="nonzero"/><path d="m.989 11.015h4.011v4.011h-4.011zm.968.968h2.075v2.075h-2.075z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/ThemeSelectFull.svg b/editor/icons/ThemeSelectFull.svg
new file mode 100644
index 0000000000..0fabb9961a
--- /dev/null
+++ b/editor/icons/ThemeSelectFull.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m11 6.976h3.015v2.005h-3.015z" fill="#e0e0e0" fill-rule="nonzero"/><path d="m.989 5.995h4.011v4.011h-4.011z" fill="#fff" fill-rule="nonzero"/><path d="m.989 5.995h4.011v4.01h-4.011zm.968.968h2.075v2.074h-2.075z" fill="#e0e0e0"/><path d="m11 1.956h3.015v2.005h-3.015z" fill="#e0e0e0" fill-rule="nonzero"/><path d="m.989.974h4.011v4.011h-4.011z" fill="#fff" fill-rule="nonzero"/><path d="m.989.974h4.011v4.011h-4.011zm.968.968h2.075v2.075h-2.075z" fill="#e0e0e0"/><path d="m11 11.996h3.015v2.005h-3.015z" fill="#e0e0e0" fill-rule="nonzero"/><path d="m.989 11.015h4.011v4.011h-4.011z" fill="#fff" fill-rule="nonzero"/><path d="m.989 11.015h4.011v4.011h-4.011zm.968.968h2.075v2.075h-2.075z" fill="#e0e0e0"/><path d="m5.995 5.995h4.011v4.011h-4.011z" fill="#fff" fill-rule="nonzero"/><path d="m5.995 5.995h4.01v4.01h-4.01zm.968.968h2.074v2.074h-2.074z" fill="#e0e0e0"/><path d="m5.995.974h4.011v4.011h-4.011z" fill="#fff" fill-rule="nonzero"/><path d="m5.995.974h4.01v4.011h-4.01zm.968.968h2.074v2.075h-2.074z" fill="#e0e0e0"/><path d="m5.995 11.015h4.011v4.011h-4.011z" fill="#fff" fill-rule="nonzero"/><path d="m5.995 11.015h4.01v4.011h-4.01zm.968.968h2.074v2.075h-2.074z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/WarningPattern.svg b/editor/icons/WarningPattern.svg
new file mode 100644
index 0000000000..8ef2c14041
--- /dev/null
+++ b/editor/icons/WarningPattern.svg
@@ -0,0 +1 @@
+<svg height="64" viewBox="0 0 64 64" width="64" xmlns="http://www.w3.org/2000/svg"><path d="m0 0v8l8-8zm16 0-16 16v8l24-24zm16 0-32 32v8l40-40zm16 0-48 48v8l56-56zm16 0-64 64h8l56-56zm0 16-48 48h8l40-40zm0 16-32 32h8s24-23 24-24zm0 16-16 16h8l8-8z" fill="#fff"/></svg>
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index fbcd76a95f..6265dfc2e4 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -535,7 +535,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
} else {
backward_button->set_icon(get_theme_icon("Back", "EditorIcons"));
}
- backward_button->set_flat(true);
backward_button->set_tooltip(TTR("Go to the previous edited object in history."));
backward_button->set_disabled(true);
backward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_back));
@@ -548,7 +547,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
} else {
forward_button->set_icon(get_theme_icon("Forward", "EditorIcons"));
}
- forward_button->set_flat(true);
forward_button->set_tooltip(TTR("Go to the next edited object in history."));
forward_button->set_disabled(true);
forward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_forward));
diff --git a/editor/plugins/SCsub b/editor/plugins/SCsub
index 359d04e5df..10a65b427e 100644
--- a/editor/plugins/SCsub
+++ b/editor/plugins/SCsub
@@ -3,3 +3,5 @@
Import("env")
env.add_source_files(env.editor_sources, "*.cpp")
+
+SConscript("tiles/SCsub")
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 48fb507bb1..78c30df04b 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -231,18 +231,16 @@ void AnimationNodeBlendTreeEditor::_update_graph() {
mb->get_popup()->connect("index_pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_anim_selected), varray(options, E->get()), CONNECT_DEFERRED);
}
- if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) {
- Ref<StyleBoxFlat> sb = node->get_theme_stylebox("frame", "GraphNode");
- Color c = sb->get_border_color();
- Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0);
- mono_color.a = 0.85;
- c = mono_color;
-
- node->add_theme_color_override("title_color", c);
- c.a = 0.7;
- node->add_theme_color_override("close_color", c);
- node->add_theme_color_override("resizer_color", c);
- }
+ Ref<StyleBoxFlat> sb = node->get_theme_stylebox("frame", "GraphNode");
+ Color c = sb->get_border_color();
+ Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0);
+ mono_color.a = 0.85;
+ c = mono_color;
+
+ node->add_theme_color_override("title_color", c);
+ c.a = 0.7;
+ node->add_theme_color_override("close_color", c);
+ node->add_theme_color_override("resizer_color", c);
}
List<AnimationNodeBlendTree::NodeConnection> connections;
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index fd47d9964e..a0d9afee74 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -240,7 +240,6 @@ void EditorAssetLibraryItemDescription::add_preview(int p_id, bool p_video, cons
preview.video_link = p_url;
preview.is_video = p_video;
preview.button = memnew(Button);
- preview.button->set_flat(true);
preview.button->set_icon(previews->get_theme_icon("ThumbnailWait", "EditorIcons"));
preview.button->set_toggle_mode(true);
preview.button->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDescription::_preview_click), varray(p_id));
diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp
index 3553450672..3b54b30b54 100644
--- a/editor/plugins/audio_stream_editor_plugin.cpp
+++ b/editor/plugins/audio_stream_editor_plugin.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
+#include "core/os/keyboard.h"
#include "editor/audio_stream_preview.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
@@ -144,23 +145,26 @@ void AudioStreamEditor::_draw_indicator() {
Rect2 rect = _preview->get_rect();
float len = stream->get_length();
float ofs_x = _current / len * rect.size.width;
- _indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), get_theme_color("accent_color", "Editor"), 1);
+ const Color color = get_theme_color("accent_color", "Editor");
+ _indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), color, Math::round(2 * EDSCALE));
+ _indicator->draw_texture(
+ get_theme_icon("TimelineIndicator", "EditorIcons"),
+ Point2(ofs_x - get_theme_icon("TimelineIndicator", "EditorIcons")->get_width() * 0.5, 0),
+ color);
_current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /");
}
void AudioStreamEditor::_on_input_indicator(Ref<InputEvent> p_event) {
- Ref<InputEventMouseButton> mb = p_event;
-
- if (mb.is_valid()) {
+ const Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (mb->is_pressed()) {
_seek_to(mb->get_position().x);
}
_dragging = mb->is_pressed();
}
- Ref<InputEventMouseMotion> mm = p_event;
-
+ const Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
if (_dragging) {
_seek_to(mm->get_position().x);
@@ -228,6 +232,7 @@ AudioStreamEditor::AudioStreamEditor() {
hbox->add_child(_play_button);
_play_button->set_focus_mode(Control::FOCUS_NONE);
_play_button->connect("pressed", callable_mp(this, &AudioStreamEditor::_play));
+ _play_button->set_shortcut(ED_SHORTCUT("inspector/audio_preview_play_pause", TTR("Audio Preview Play/Pause"), KEY_SPACE));
_stop_button = memnew(Button);
_stop_button->set_flat(true);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 261621e10a..91022ccb2c 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1296,11 +1296,11 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
update_viewport();
} else {
- float new_zoom = _get_next_zoom_value(-1);
+ zoom_widget->set_zoom_by_increments(-1);
if (b->get_factor() != 1.f) {
- new_zoom = zoom * ((new_zoom / zoom - 1.f) * b->get_factor() + 1.f);
+ zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * b->get_factor() + 1.f));
}
- _zoom_on_position(new_zoom, b->get_position());
+ _zoom_on_position(zoom_widget->get_zoom(), b->get_position());
}
return true;
}
@@ -1311,11 +1311,11 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
update_viewport();
} else {
- float new_zoom = _get_next_zoom_value(1);
+ zoom_widget->set_zoom_by_increments(1);
if (b->get_factor() != 1.f) {
- new_zoom = zoom * ((new_zoom / zoom - 1.f) * b->get_factor() + 1.f);
+ zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * b->get_factor() + 1.f));
}
- _zoom_on_position(new_zoom, b->get_position());
+ _zoom_on_position(zoom_widget->get_zoom(), b->get_position());
}
return true;
}
@@ -1391,12 +1391,13 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
// If control key pressed, then zoom instead of pan
if (pan_gesture->get_control()) {
const float factor = pan_gesture->get_delta().y;
- float new_zoom = _get_next_zoom_value(-1);
+ zoom_widget->set_zoom_by_increments(1);
if (factor != 1.f) {
- new_zoom = zoom * ((new_zoom / zoom - 1.f) * factor + 1.f);
+ zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * factor + 1.f));
}
- _zoom_on_position(new_zoom, pan_gesture->get_position());
+ _zoom_on_position(zoom_widget->get_zoom(), pan_gesture->get_position());
+
return true;
}
@@ -4219,9 +4220,6 @@ void CanvasItemEditor::_notification(int p_what) {
key_auto_insert_button->add_theme_color_override("icon_pressed_color", key_auto_color.lerp(Color(1, 0, 0), 0.55));
animation_menu->set_icon(get_theme_icon("GuiTabMenuHl", "EditorIcons"));
- zoom_minus->set_icon(get_theme_icon("ZoomLess", "EditorIcons"));
- zoom_plus->set_icon(get_theme_icon("ZoomMore", "EditorIcons"));
-
presets_menu->set_icon(get_theme_icon("ControlLayout", "EditorIcons"));
PopupMenu *p = presets_menu->get_popup();
@@ -4579,33 +4577,6 @@ void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) {
undo_redo->commit_action();
}
-float CanvasItemEditor::_get_next_zoom_value(int p_increment_count) const {
- // Base increment factor defined as the twelveth root of two.
- // This allow a smooth geometric evolution of the zoom, with the advantage of
- // visiting all integer power of two scale factors.
- // note: this is analogous to the 'semitones' interval in the music world
- // In order to avoid numerical imprecisions, we compute and edit a zoom index
- // with the following relation: zoom = 2 ^ (index / 12)
-
- if (zoom < CMP_EPSILON || p_increment_count == 0) {
- return 1.f;
- }
-
- // Remove Editor scale from the index computation
- float zoom_noscale = zoom / MAX(1, EDSCALE);
-
- // zoom = 2**(index/12) => log2(zoom) = index/12
- float closest_zoom_index = Math::round(Math::log(zoom_noscale) * 12.f / Math::log(2.f));
-
- float new_zoom_index = closest_zoom_index + p_increment_count;
- float new_zoom = Math::pow(2.f, new_zoom_index / 12.f);
-
- // Restore Editor scale transformation
- new_zoom *= MAX(1, EDSCALE);
-
- return new_zoom;
-}
-
void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) {
p_zoom = CLAMP(p_zoom, MIN_ZOOM, MAX_ZOOM);
@@ -4630,36 +4601,12 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) {
view_offset = view_offset_int + (view_offset_frac * closest_zoom_factor).round() / closest_zoom_factor;
}
- _update_zoom_label();
+ zoom_widget->set_zoom(zoom);
update_viewport();
}
-void CanvasItemEditor::_update_zoom_label() {
- String zoom_text;
- // The zoom level displayed is relative to the editor scale
- // (like in most image editors). Its lower bound is clamped to 1 as some people
- // lower the editor scale to increase the available real estate,
- // even if their display doesn't have a particularly low DPI.
- if (zoom >= 10) {
- // Don't show a decimal when the zoom level is higher than 1000 %.
- zoom_text = TS->format_number(rtos(Math::round((zoom / MAX(1, EDSCALE)) * 100))) + " " + TS->percent_sign();
- } else {
- zoom_text = TS->format_number(rtos(Math::snapped((zoom / MAX(1, EDSCALE)) * 100, 0.1))) + " " + TS->percent_sign();
- }
-
- zoom_reset->set_text(zoom_text);
-}
-
-void CanvasItemEditor::_button_zoom_minus() {
- _zoom_on_position(_get_next_zoom_value(-6), viewport_scrollable->get_size() / 2.0);
-}
-
-void CanvasItemEditor::_button_zoom_reset() {
- _zoom_on_position(1.0 * MAX(1, EDSCALE), viewport_scrollable->get_size() / 2.0);
-}
-
-void CanvasItemEditor::_button_zoom_plus() {
- _zoom_on_position(_get_next_zoom_value(6), viewport_scrollable->get_size() / 2.0);
+void CanvasItemEditor::_update_zoom(float p_zoom) {
+ _zoom_on_position(p_zoom, viewport_scrollable->get_size() / 2.0);
}
void CanvasItemEditor::_button_toggle_smart_snap(bool p_status) {
@@ -5401,7 +5348,7 @@ void CanvasItemEditor::_focus_selection(int p_op) {
zoom = scale_x < scale_y ? scale_x : scale_y;
zoom *= 0.90;
viewport->update();
- _update_zoom_label();
+ zoom_widget->set_zoom(zoom);
call_deferred("_popup_callback", VIEW_CENTER_TO_SELECTION);
}
}
@@ -5446,7 +5393,7 @@ Dictionary CanvasItemEditor::get_state() const {
state["show_rulers"] = show_rulers;
state["show_guides"] = show_guides;
state["show_helpers"] = show_helpers;
- state["show_zoom_control"] = zoom_hb->is_visible();
+ state["show_zoom_control"] = zoom_widget->is_visible();
state["show_edit_locks"] = show_edit_locks;
state["show_transformation_gizmos"] = show_transformation_gizmos;
state["snap_rotation"] = snap_rotation;
@@ -5464,7 +5411,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
// Compensate the editor scale, so that the editor scale can be changed
// and the zoom level will still be the same (relative to the editor scale).
zoom = float(p_state["zoom"]) * MAX(1, EDSCALE);
- _update_zoom_label();
+ zoom_widget->set_zoom(zoom);
}
if (state.has("ofs")) {
@@ -5594,7 +5541,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
if (state.has("show_zoom_control")) {
// This one is not user-controllable, but instrumentable
- zoom_hb->set_visible(state["show_zoom_control"]);
+ zoom_widget->set_visible(state["show_zoom_control"]);
}
if (state.has("snap_rotation")) {
@@ -5770,11 +5717,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
controls_vb = memnew(VBoxContainer);
controls_vb->set_begin(Point2(5, 5));
- zoom_hb = memnew(HBoxContainer);
- // Bring the zoom percentage closer to the zoom buttons
- zoom_hb->add_theme_constant_override("separation", Math::round(-8 * EDSCALE));
- controls_vb->add_child(zoom_hb);
-
viewport = memnew(CanvasItemEditorViewport(p_editor, this));
viewport_scrollable->add_child(viewport);
viewport->set_mouse_filter(MOUSE_FILTER_PASS);
@@ -5821,38 +5763,20 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
viewport->add_child(controls_vb);
- zoom_minus = memnew(Button);
- zoom_minus->set_flat(true);
- zoom_hb->add_child(zoom_minus);
- zoom_minus->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_zoom_minus));
- zoom_minus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_minus", TTR("Zoom Out"), KEY_MASK_CMD | KEY_MINUS));
- zoom_minus->set_shortcut_context(this);
- zoom_minus->set_focus_mode(FOCUS_NONE);
-
- zoom_reset = memnew(Button);
- zoom_reset->set_flat(true);
- zoom_hb->add_child(zoom_reset);
- zoom_reset->add_theme_constant_override("outline_size", 1);
- zoom_reset->add_theme_color_override("font_outline_color", Color(0, 0, 0));
- zoom_reset->add_theme_color_override("font_color", Color(1, 1, 1));
- zoom_reset->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_zoom_reset));
- zoom_reset->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", TTR("Zoom Reset"), KEY_MASK_CMD | KEY_0));
- zoom_reset->set_shortcut_context(this);
- zoom_reset->set_focus_mode(FOCUS_NONE);
- zoom_reset->set_text_align(Button::TextAlign::ALIGN_CENTER);
- // Prevent the button's size from changing when the text size changes
- zoom_reset->set_custom_minimum_size(Size2(75 * EDSCALE, 0));
-
- zoom_plus = memnew(Button);
- zoom_plus->set_flat(true);
- zoom_hb->add_child(zoom_plus);
- zoom_plus->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_zoom_plus));
- zoom_plus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", TTR("Zoom In"), KEY_MASK_CMD | KEY_EQUAL)); // Usually direct access key for PLUS
- zoom_plus->set_shortcut_context(this);
- zoom_plus->set_focus_mode(FOCUS_NONE);
+ zoom_widget = memnew(EditorZoomWidget);
+ controls_vb->add_child(zoom_widget);
+ zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
+ zoom_widget->connect("zoom_changed", callable_mp(this, &CanvasItemEditor::_update_zoom));
updating_scroll = false;
+ // Add some margin to the left for better aesthetics.
+ // This prevents the first button's hover/pressed effect from "touching" the panel's border,
+ // which looks ugly.
+ Control *margin_left = memnew(Control);
+ hb->add_child(margin_left);
+ margin_left->set_custom_minimum_size(Size2(2, 0) * EDSCALE);
+
select_button = memnew(Button);
select_button->set_flat(true);
hb->add_child(select_button);
@@ -6089,7 +6013,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
key_loc_button = memnew(Button);
key_loc_button->set_toggle_mode(true);
- key_loc_button->set_flat(true);
key_loc_button->set_pressed(true);
key_loc_button->set_focus_mode(FOCUS_NONE);
key_loc_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(ANIM_INSERT_POS));
@@ -6098,7 +6021,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
key_rot_button = memnew(Button);
key_rot_button->set_toggle_mode(true);
- key_rot_button->set_flat(true);
key_rot_button->set_pressed(true);
key_rot_button->set_focus_mode(FOCUS_NONE);
key_rot_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(ANIM_INSERT_ROT));
@@ -6107,14 +6029,12 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
key_scale_button = memnew(Button);
key_scale_button->set_toggle_mode(true);
- key_scale_button->set_flat(true);
key_scale_button->set_focus_mode(FOCUS_NONE);
key_scale_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(ANIM_INSERT_SCALE));
key_scale_button->set_tooltip(TTR("Scale mask for inserting keys."));
animation_hb->add_child(key_scale_button);
key_insert_button = memnew(Button);
- key_insert_button->set_flat(true);
key_insert_button->set_focus_mode(FOCUS_NONE);
key_insert_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(ANIM_INSERT_KEY));
key_insert_button->set_tooltip(TTR("Insert keys (based on mask)."));
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 62a9b1e162..21ef3f88df 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -33,6 +33,7 @@
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
+#include "editor/editor_zoom_widget.h"
#include "scene/gui/box_container.h"
#include "scene/gui/check_box.h"
#include "scene/gui/label.h"
@@ -233,10 +234,6 @@ private:
VScrollBar *v_scroll;
HBoxContainer *hb;
- Button *zoom_minus;
- Button *zoom_reset;
- Button *zoom_plus;
-
Map<Control *, Timer *> popup_temporarily_timers;
Label *warning_child_of_container;
@@ -536,13 +533,9 @@ private:
void _button_toggle_anchor_mode(bool p_status);
VBoxContainer *controls_vb;
- HBoxContainer *zoom_hb;
- float _get_next_zoom_value(int p_increment_count) const;
+ EditorZoomWidget *zoom_widget;
+ void _update_zoom(float p_zoom);
void _zoom_on_position(float p_zoom, Point2 p_position = Point2());
- void _update_zoom_label();
- void _button_zoom_minus();
- void _button_zoom_reset();
- void _button_zoom_plus();
void _button_toggle_smart_snap(bool p_status);
void _button_toggle_grid_snap(bool p_status);
void _button_override_camera(bool p_pressed);
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index a319a595c7..18cc5d43fb 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -487,10 +487,15 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
List<String> kwors;
scr->get_language()->get_reserved_words(&kwors);
+ Set<String> control_flow_keywords;
Set<String> keywords;
for (List<String>::Element *E = kwors.front(); E; E = E->next()) {
- keywords.insert(E->get());
+ if (scr->get_language()->is_control_flow_keyword(E->get())) {
+ control_flow_keywords.insert(E->get());
+ } else {
+ keywords.insert(E->get());
+ }
}
int line = 0;
@@ -502,6 +507,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
Color bg_color = EditorSettings::get_singleton()->get("text_editor/highlighting/background_color");
Color keyword_color = EditorSettings::get_singleton()->get("text_editor/highlighting/keyword_color");
+ Color control_flow_keyword_color = EditorSettings::get_singleton()->get("text_editor/highlighting/control_flow_keyword_color");
Color text_color = EditorSettings::get_singleton()->get("text_editor/highlighting/text_color");
Color symbol_color = EditorSettings::get_singleton()->get("text_editor/highlighting/symbol_color");
Color comment_color = EditorSettings::get_singleton()->get("text_editor/highlighting/comment_color");
@@ -523,6 +529,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
col = x0;
bool prev_is_text = false;
+ bool in_control_flow_keyword = false;
bool in_keyword = false;
bool in_comment = false;
for (int i = 0; i < code.length(); i++) {
@@ -541,6 +548,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
if (c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t')) {
//make symbol a little visible
color = symbol_color;
+ in_control_flow_keyword = false;
in_keyword = false;
} else if (!prev_is_text && _is_text_char(c)) {
int pos = i;
@@ -549,7 +557,9 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
pos++;
}
String word = code.substr(i, pos - i);
- if (keywords.has(word)) {
+ if (control_flow_keywords.has(word)) {
+ in_control_flow_keyword = true;
+ } else if (keywords.has(word)) {
in_keyword = true;
}
@@ -557,11 +567,12 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
in_keyword = false;
}
- if (in_keyword) {
+ if (in_control_flow_keyword) {
+ color = control_flow_keyword_color;
+ } else if (in_keyword) {
color = keyword_color;
}
}
-
Color ul = color;
ul.a *= 0.5;
img->set_pixel(col, y0 + line * 2, bg_color.blend(ul));
@@ -572,6 +583,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
col++;
} else {
prev_is_text = false;
+ in_control_flow_keyword = false;
in_keyword = false;
if (c == '\n') {
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 023d91be30..81f1852fba 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -6692,6 +6692,13 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
button_binds.resize(1);
String sct;
+ // Add some margin to the left for better aesthetics.
+ // This prevents the first button's hover/pressed effect from "touching" the panel's border,
+ // which looks ugly.
+ Control *margin_left = memnew(Control);
+ hbc_menu->add_child(margin_left);
+ margin_left->set_custom_minimum_size(Size2(2, 0) * EDSCALE);
+
tool_button[TOOL_MODE_SELECT] = memnew(Button);
hbc_menu->add_child(tool_button[TOOL_MODE_SELECT]);
tool_button[TOOL_MODE_SELECT]->set_toggle_mode(true);
diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp
index 50f4d8493f..1e6237ced1 100644
--- a/editor/plugins/root_motion_editor_plugin.cpp
+++ b/editor/plugins/root_motion_editor_plugin.cpp
@@ -205,7 +205,6 @@ void EditorPropertyRootMotion::update_property() {
assign->set_flat(false);
return;
}
- assign->set_flat(true);
Node *base_node = nullptr;
if (base_hint != NodePath()) {
@@ -247,14 +246,12 @@ EditorPropertyRootMotion::EditorPropertyRootMotion() {
HBoxContainer *hbc = memnew(HBoxContainer);
add_child(hbc);
assign = memnew(Button);
- assign->set_flat(true);
assign->set_h_size_flags(SIZE_EXPAND_FILL);
assign->set_clip_text(true);
assign->connect("pressed", callable_mp(this, &EditorPropertyRootMotion::_node_assign));
hbc->add_child(assign);
clear = memnew(Button);
- clear->set_flat(true);
clear->connect("pressed", callable_mp(this, &EditorPropertyRootMotion::_node_clear));
hbc->add_child(clear);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 58e6717a3d..8c4c5a3461 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -140,10 +140,15 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
/* Reserved words. */
const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+ const Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
List<String> keywords;
script->get_language()->get_reserved_words(&keywords);
for (List<String>::Element *E = keywords.front(); E; E = E->next()) {
- highlighter->add_keyword_color(E->get(), keyword_color);
+ if (script->get_language()->is_control_flow_keyword(E->get())) {
+ highlighter->add_keyword_color(E->get(), control_flow_keyword_color);
+ } else {
+ highlighter->add_keyword_color(E->get(), keyword_color);
+ }
}
/* Member types. */
@@ -698,7 +703,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
// Do not try to save internal scripts, but prompt to save in-memory
// scripts which are not saved to disk yet (have empty path).
if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) {
- _menu_option(FILE_SAVE);
+ save_current_script();
}
}
if (script.is_valid()) {
@@ -1224,55 +1229,7 @@ void ScriptEditor::_menu_option(int p_option) {
if (current) {
switch (p_option) {
case FILE_SAVE: {
- if (_test_script_times_on_disk()) {
- return;
- }
-
- if (trim_trailing_whitespace_on_save) {
- current->trim_trailing_whitespace();
- }
-
- current->insert_final_newline();
-
- if (convert_indent_on_save) {
- if (use_space_indentation) {
- current->convert_indent_to_spaces();
- } else {
- current->convert_indent_to_tabs();
- }
- }
-
- RES resource = current->get_edited_resource();
- Ref<TextFile> text_file = resource;
- Ref<Script> script = resource;
-
- if (text_file != nullptr) {
- current->apply_code();
- _save_text_file(text_file, text_file->get_path());
- break;
- }
-
- if (script != nullptr) {
- const Vector<DocData::ClassDoc> &documentations = script->get_documentation();
- for (int j = 0; j < documentations.size(); j++) {
- const DocData::ClassDoc &doc = documentations.get(j);
- if (EditorHelp::get_doc_data()->has_doc(doc.name)) {
- EditorHelp::get_doc_data()->remove_doc(doc.name);
- }
- }
- }
-
- editor->save_resource(resource);
-
- if (script != nullptr) {
- const Vector<DocData::ClassDoc> &documentations = script->get_documentation();
- for (int j = 0; j < documentations.size(); j++) {
- const DocData::ClassDoc &doc = documentations.get(j);
- EditorHelp::get_doc_data()->add_doc(doc);
- update_doc(doc.name);
- }
- }
-
+ save_current_script();
} break;
case FILE_SAVE_AS: {
if (trim_trailing_whitespace_on_save) {
@@ -1827,7 +1784,6 @@ void ScriptEditor::_update_help_overview() {
void ScriptEditor::_update_script_colors() {
bool script_temperature_enabled = EditorSettings::get_singleton()->get("text_editor/script_list/script_temperature_enabled");
- bool highlight_current = EditorSettings::get_singleton()->get("text_editor/script_list/highlight_current_script");
int hist_size = EditorSettings::get_singleton()->get("text_editor/script_list/script_temperature_history_size");
Color hot_color = get_theme_color("accent_color", "Editor");
@@ -1842,11 +1798,7 @@ void ScriptEditor::_update_script_colors() {
script_list->set_item_custom_bg_color(i, Color(0, 0, 0, 0));
- bool current = tab_container->get_current_tab() == c;
- if (current && highlight_current) {
- script_list->set_item_custom_bg_color(i, EditorSettings::get_singleton()->get("text_editor/script_list/current_script_background_color"));
-
- } else if (script_temperature_enabled) {
+ if (script_temperature_enabled) {
if (!n->has_meta("__editor_pass")) {
continue;
}
@@ -2325,6 +2277,59 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
return true;
}
+void ScriptEditor::save_current_script() {
+ ScriptEditorBase *current = _get_current_editor();
+
+ if (_test_script_times_on_disk()) {
+ return;
+ }
+
+ if (trim_trailing_whitespace_on_save) {
+ current->trim_trailing_whitespace();
+ }
+
+ current->insert_final_newline();
+
+ if (convert_indent_on_save) {
+ if (use_space_indentation) {
+ current->convert_indent_to_spaces();
+ } else {
+ current->convert_indent_to_tabs();
+ }
+ }
+
+ RES resource = current->get_edited_resource();
+ Ref<TextFile> text_file = resource;
+ Ref<Script> script = resource;
+
+ if (text_file != nullptr) {
+ current->apply_code();
+ _save_text_file(text_file, text_file->get_path());
+ return;
+ }
+
+ if (script != nullptr) {
+ const Vector<DocData::ClassDoc> &documentations = script->get_documentation();
+ for (int j = 0; j < documentations.size(); j++) {
+ const DocData::ClassDoc &doc = documentations.get(j);
+ if (EditorHelp::get_doc_data()->has_doc(doc.name)) {
+ EditorHelp::get_doc_data()->remove_doc(doc.name);
+ }
+ }
+ }
+
+ editor->save_resource(resource);
+
+ if (script != nullptr) {
+ const Vector<DocData::ClassDoc> &documentations = script->get_documentation();
+ for (int j = 0; j < documentations.size(); j++) {
+ const DocData::ClassDoc &doc = documentations.get(j);
+ EditorHelp::get_doc_data()->add_doc(doc);
+ update_doc(doc.name);
+ }
+ }
+}
+
void ScriptEditor::save_all_scripts() {
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
@@ -2439,6 +2444,9 @@ void ScriptEditor::_add_callback(Object *p_obj, const String &p_function, const
script_list->select(script_list->find_metadata(i));
+ // Save the current script so the changes can be picked up by an external editor.
+ save_current_script();
+
break;
}
}
@@ -3672,9 +3680,7 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
EDITOR_DEF("text_editor/external/use_external_editor", false);
EDITOR_DEF("text_editor/external/exec_path", "");
EDITOR_DEF("text_editor/script_list/script_temperature_enabled", true);
- EDITOR_DEF("text_editor/script_list/highlight_current_script", true);
EDITOR_DEF("text_editor/script_list/script_temperature_history_size", 15);
- EDITOR_DEF("text_editor/script_list/current_script_background_color", Color(1, 1, 1, 0.3));
EDITOR_DEF("text_editor/script_list/group_help_pages", true);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/script_list/sort_scripts_by", PROPERTY_HINT_ENUM, "Name,Path,None"));
EDITOR_DEF("text_editor/script_list/sort_scripts_by", 0);
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index b2172e7f10..570ebfba4e 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -463,6 +463,7 @@ public:
void get_breakpoints(List<String> *p_breakpoints);
+ void save_current_script();
void save_all_scripts();
void set_window_layout(Ref<ConfigFile> p_layout);
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index ed3b746678..3cdba9cf16 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -141,9 +141,14 @@ void ShaderTextEditor::_load_theme_settings() {
List<String> keywords;
ShaderLanguage::get_keyword_list(&keywords);
const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+ const Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
for (List<String>::Element *E = keywords.front(); E; E = E->next()) {
- syntax_highlighter->add_keyword_color(E->get(), keyword_color);
+ if (ShaderLanguage::is_control_flow_keyword(E->get())) {
+ syntax_highlighter->add_keyword_color(E->get(), control_flow_keyword_color);
+ } else {
+ syntax_highlighter->add_keyword_color(E->get(), keyword_color);
+ }
}
// Colorize built-ins like `COLOR` differently to make them easier
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index c765aa0319..36c1bf3a09 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -34,12 +34,1187 @@
#include "core/os/keyboard.h"
#include "core/version.h"
#include "editor/editor_scale.h"
+#include "editor/progress_dialog.h"
#include "scene/gui/progress_bar.h"
+void ThemeItemImportTree::_update_items_tree() {
+ import_items_tree->clear();
+ TreeItem *root = import_items_tree->create_item();
+
+ if (base_theme.is_null()) {
+ return;
+ }
+
+ String filter_text = import_items_filter->get_text();
+
+ List<StringName> types;
+ List<StringName> names;
+ List<StringName> filtered_names;
+ base_theme->get_type_list(&types);
+ types.sort_custom<StringName::AlphCompare>();
+
+ int color_amount = 0;
+ int constant_amount = 0;
+ int font_amount = 0;
+ int font_size_amount = 0;
+ int icon_amount = 0;
+ int stylebox_amount = 0;
+
+ tree_color_items.clear();
+ tree_constant_items.clear();
+ tree_font_items.clear();
+ tree_font_size_items.clear();
+ tree_icon_items.clear();
+ tree_stylebox_items.clear();
+
+ for (List<StringName>::Element *E = types.front(); E; E = E->next()) {
+ String type_name = (String)E->get();
+
+ TreeItem *type_node = import_items_tree->create_item(root);
+ type_node->set_meta("_can_be_imported", false);
+ type_node->set_collapsed(true);
+ type_node->set_text(0, type_name);
+ type_node->set_cell_mode(IMPORT_ITEM, TreeItem::CELL_MODE_CHECK);
+ type_node->set_checked(IMPORT_ITEM, false);
+ type_node->set_editable(IMPORT_ITEM, true);
+ type_node->set_cell_mode(IMPORT_ITEM_DATA, TreeItem::CELL_MODE_CHECK);
+ type_node->set_checked(IMPORT_ITEM_DATA, false);
+ type_node->set_editable(IMPORT_ITEM_DATA, true);
+
+ bool is_matching_filter = (filter_text.is_empty() || type_name.findn(filter_text) > -1);
+ bool has_filtered_items = false;
+ bool any_checked = false;
+ bool any_checked_with_data = false;
+
+ for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
+ Theme::DataType dt = (Theme::DataType)i;
+
+ names.clear();
+ filtered_names.clear();
+ base_theme->get_theme_item_list(dt, E->get(), &names);
+
+ bool data_type_has_filtered_items = false;
+
+ for (List<StringName>::Element *F = names.front(); F; F = F->next()) {
+ String item_name = (String)F->get();
+ bool is_item_matching_filter = (item_name.findn(filter_text) > -1);
+ if (!filter_text.is_empty() && !is_matching_filter && !is_item_matching_filter) {
+ continue;
+ }
+
+ // Only mark this if actual items match the filter and not just the type group.
+ if (!filter_text.is_empty() && is_item_matching_filter) {
+ has_filtered_items = true;
+ data_type_has_filtered_items = true;
+ }
+ filtered_names.push_back(F->get());
+ }
+
+ if (filtered_names.size() == 0) {
+ continue;
+ }
+
+ TreeItem *data_type_node = import_items_tree->create_item(type_node);
+ data_type_node->set_meta("_can_be_imported", false);
+ data_type_node->set_metadata(0, i);
+ data_type_node->set_collapsed(!data_type_has_filtered_items);
+ data_type_node->set_cell_mode(IMPORT_ITEM, TreeItem::CELL_MODE_CHECK);
+ data_type_node->set_checked(IMPORT_ITEM, false);
+ data_type_node->set_editable(IMPORT_ITEM, true);
+ data_type_node->set_cell_mode(IMPORT_ITEM_DATA, TreeItem::CELL_MODE_CHECK);
+ data_type_node->set_checked(IMPORT_ITEM_DATA, false);
+ data_type_node->set_editable(IMPORT_ITEM_DATA, true);
+
+ List<TreeItem *> *item_list;
+
+ switch (dt) {
+ case Theme::DATA_TYPE_COLOR:
+ data_type_node->set_icon(0, get_theme_icon("Color", "EditorIcons"));
+ data_type_node->set_text(0, TTR("Colors"));
+
+ item_list = &tree_color_items;
+ color_amount += filtered_names.size();
+ break;
+
+ case Theme::DATA_TYPE_CONSTANT:
+ data_type_node->set_icon(0, get_theme_icon("MemberConstant", "EditorIcons"));
+ data_type_node->set_text(0, TTR("Constants"));
+
+ item_list = &tree_constant_items;
+ constant_amount += filtered_names.size();
+ break;
+
+ case Theme::DATA_TYPE_FONT:
+ data_type_node->set_icon(0, get_theme_icon("Font", "EditorIcons"));
+ data_type_node->set_text(0, TTR("Fonts"));
+
+ item_list = &tree_font_items;
+ font_amount += filtered_names.size();
+ break;
+
+ case Theme::DATA_TYPE_FONT_SIZE:
+ data_type_node->set_icon(0, get_theme_icon("FontSize", "EditorIcons"));
+ data_type_node->set_text(0, TTR("Font Sizes"));
+
+ item_list = &tree_font_size_items;
+ font_size_amount += filtered_names.size();
+ break;
+
+ case Theme::DATA_TYPE_ICON:
+ data_type_node->set_icon(0, get_theme_icon("ImageTexture", "EditorIcons"));
+ data_type_node->set_text(0, TTR("Icons"));
+
+ item_list = &tree_icon_items;
+ icon_amount += filtered_names.size();
+ break;
+
+ case Theme::DATA_TYPE_STYLEBOX:
+ data_type_node->set_icon(0, get_theme_icon("StyleBoxFlat", "EditorIcons"));
+ data_type_node->set_text(0, TTR("Styleboxes"));
+
+ item_list = &tree_stylebox_items;
+ stylebox_amount += filtered_names.size();
+ break;
+
+ case Theme::DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+
+ bool data_type_any_checked = false;
+ bool data_type_any_checked_with_data = false;
+
+ filtered_names.sort_custom<StringName::AlphCompare>();
+ for (List<StringName>::Element *F = filtered_names.front(); F; F = F->next()) {
+ TreeItem *item_node = import_items_tree->create_item(data_type_node);
+ item_node->set_meta("_can_be_imported", true);
+ item_node->set_text(0, F->get());
+ item_node->set_cell_mode(IMPORT_ITEM, TreeItem::CELL_MODE_CHECK);
+ item_node->set_checked(IMPORT_ITEM, false);
+ item_node->set_editable(IMPORT_ITEM, true);
+ item_node->set_cell_mode(IMPORT_ITEM_DATA, TreeItem::CELL_MODE_CHECK);
+ item_node->set_checked(IMPORT_ITEM_DATA, false);
+ item_node->set_editable(IMPORT_ITEM_DATA, true);
+
+ _restore_selected_item(item_node);
+ if (item_node->is_checked(IMPORT_ITEM)) {
+ data_type_any_checked = true;
+ any_checked = true;
+ }
+ if (item_node->is_checked(IMPORT_ITEM_DATA)) {
+ data_type_any_checked_with_data = true;
+ any_checked_with_data = true;
+ }
+
+ item_list->push_back(item_node);
+ }
+
+ data_type_node->set_checked(IMPORT_ITEM, data_type_any_checked);
+ data_type_node->set_checked(IMPORT_ITEM_DATA, data_type_any_checked && data_type_any_checked_with_data);
+ }
+
+ // Remove the item if it doesn't match the filter in any way.
+ if (!is_matching_filter && !has_filtered_items) {
+ root->remove_child(type_node);
+ memdelete(type_node);
+ continue;
+ }
+
+ // Show one level inside of a type group if there are matches in items.
+ if (!filter_text.is_empty() && has_filtered_items) {
+ type_node->set_collapsed(false);
+ }
+
+ type_node->set_checked(IMPORT_ITEM, any_checked);
+ type_node->set_checked(IMPORT_ITEM_DATA, any_checked && any_checked_with_data);
+ }
+
+ if (color_amount > 0) {
+ Array arr;
+ arr.push_back(color_amount);
+ select_colors_label->set_text(TTRN("One color", "{num} colors", color_amount).format(arr, "{num}"));
+ select_all_colors_button->set_visible(true);
+ select_full_colors_button->set_visible(true);
+ deselect_all_colors_button->set_visible(true);
+ } else {
+ select_colors_label->set_text(TTR("No colors found."));
+ select_all_colors_button->set_visible(false);
+ select_full_colors_button->set_visible(false);
+ deselect_all_colors_button->set_visible(false);
+ }
+
+ if (constant_amount > 0) {
+ Array arr;
+ arr.push_back(constant_amount);
+ select_constants_label->set_text(TTRN("One constant", "{num} constants", constant_amount).format(arr, "{num}"));
+ select_all_constants_button->set_visible(true);
+ select_full_constants_button->set_visible(true);
+ deselect_all_constants_button->set_visible(true);
+ } else {
+ select_constants_label->set_text(TTR("No constants found."));
+ select_all_constants_button->set_visible(false);
+ select_full_constants_button->set_visible(false);
+ deselect_all_constants_button->set_visible(false);
+ }
+
+ if (font_amount > 0) {
+ Array arr;
+ arr.push_back(font_amount);
+ select_fonts_label->set_text(TTRN("One font", "{num} fonts", font_amount).format(arr, "{num}"));
+ select_all_fonts_button->set_visible(true);
+ select_full_fonts_button->set_visible(true);
+ deselect_all_fonts_button->set_visible(true);
+ } else {
+ select_fonts_label->set_text(TTR("No fonts found."));
+ select_all_fonts_button->set_visible(false);
+ select_full_fonts_button->set_visible(false);
+ deselect_all_fonts_button->set_visible(false);
+ }
+
+ if (font_size_amount > 0) {
+ Array arr;
+ arr.push_back(font_size_amount);
+ select_font_sizes_label->set_text(TTRN("One font size", "{num} font sizes", font_size_amount).format(arr, "{num}"));
+ select_all_font_sizes_button->set_visible(true);
+ select_full_font_sizes_button->set_visible(true);
+ deselect_all_font_sizes_button->set_visible(true);
+ } else {
+ select_font_sizes_label->set_text(TTR("No font sizes found."));
+ select_all_font_sizes_button->set_visible(false);
+ select_full_font_sizes_button->set_visible(false);
+ deselect_all_font_sizes_button->set_visible(false);
+ }
+
+ if (icon_amount > 0) {
+ Array arr;
+ arr.push_back(icon_amount);
+ select_icons_label->set_text(TTRN("One icon", "{num} icons", icon_amount).format(arr, "{num}"));
+ select_all_icons_button->set_visible(true);
+ select_full_icons_button->set_visible(true);
+ deselect_all_icons_button->set_visible(true);
+ select_icons_warning_hb->set_visible(true);
+ } else {
+ select_icons_label->set_text(TTR("No icons found."));
+ select_all_icons_button->set_visible(false);
+ select_full_icons_button->set_visible(false);
+ deselect_all_icons_button->set_visible(false);
+ select_icons_warning_hb->set_visible(false);
+ }
+
+ if (stylebox_amount > 0) {
+ Array arr;
+ arr.push_back(stylebox_amount);
+ select_styleboxes_label->set_text(TTRN("One stylebox", "{num} styleboxes", stylebox_amount).format(arr, "{num}"));
+ select_all_styleboxes_button->set_visible(true);
+ select_full_styleboxes_button->set_visible(true);
+ deselect_all_styleboxes_button->set_visible(true);
+ } else {
+ select_styleboxes_label->set_text(TTR("No styleboxes found."));
+ select_all_styleboxes_button->set_visible(false);
+ select_full_styleboxes_button->set_visible(false);
+ deselect_all_styleboxes_button->set_visible(false);
+ }
+}
+
+void ThemeItemImportTree::_toggle_type_items(bool p_collapse) {
+ TreeItem *root = import_items_tree->get_root();
+ if (!root) {
+ return;
+ }
+
+ TreeItem *type_node = root->get_children();
+ while (type_node) {
+ type_node->set_collapsed(p_collapse);
+ type_node = type_node->get_next();
+ }
+}
+
+void ThemeItemImportTree::_filter_text_changed(const String &p_value) {
+ _update_items_tree();
+}
+
+void ThemeItemImportTree::_store_selected_item(TreeItem *p_tree_item) {
+ if (!p_tree_item->get_meta("_can_be_imported")) {
+ return;
+ }
+
+ TreeItem *data_type_node = p_tree_item->get_parent();
+ if (!data_type_node || data_type_node == import_items_tree->get_root()) {
+ return;
+ }
+
+ TreeItem *type_node = data_type_node->get_parent();
+ if (!type_node || type_node == import_items_tree->get_root()) {
+ return;
+ }
+
+ ThemeItem ti;
+ ti.item_name = p_tree_item->get_text(0);
+ ti.data_type = (Theme::DataType)(int)data_type_node->get_metadata(0);
+ ti.type_name = type_node->get_text(0);
+
+ bool import = p_tree_item->is_checked(IMPORT_ITEM);
+ bool with_data = p_tree_item->is_checked(IMPORT_ITEM_DATA);
+
+ if (import && with_data) {
+ selected_items[ti] = SELECT_IMPORT_FULL;
+ } else if (import) {
+ selected_items[ti] = SELECT_IMPORT_DEFINITION;
+ } else {
+ selected_items.erase(ti);
+ }
+
+ _update_total_selected(ti.data_type);
+}
+
+void ThemeItemImportTree::_restore_selected_item(TreeItem *p_tree_item) {
+ if (!p_tree_item->get_meta("_can_be_imported")) {
+ return;
+ }
+
+ TreeItem *data_type_node = p_tree_item->get_parent();
+ if (!data_type_node || data_type_node == import_items_tree->get_root()) {
+ return;
+ }
+
+ TreeItem *type_node = data_type_node->get_parent();
+ if (!type_node || type_node == import_items_tree->get_root()) {
+ return;
+ }
+
+ ThemeItem ti;
+ ti.item_name = p_tree_item->get_text(0);
+ ti.data_type = (Theme::DataType)(int)data_type_node->get_metadata(0);
+ ti.type_name = type_node->get_text(0);
+
+ if (!selected_items.has(ti)) {
+ p_tree_item->set_checked(IMPORT_ITEM, false);
+ p_tree_item->set_checked(IMPORT_ITEM_DATA, false);
+ return;
+ }
+
+ if (selected_items[ti] == SELECT_IMPORT_FULL) {
+ p_tree_item->set_checked(IMPORT_ITEM, true);
+ p_tree_item->set_checked(IMPORT_ITEM_DATA, true);
+ } else if (selected_items[ti] == SELECT_IMPORT_DEFINITION) {
+ p_tree_item->set_checked(IMPORT_ITEM, true);
+ p_tree_item->set_checked(IMPORT_ITEM_DATA, false);
+ }
+}
+
+void ThemeItemImportTree::_update_total_selected(Theme::DataType p_data_type) {
+ ERR_FAIL_INDEX_MSG(p_data_type, Theme::DATA_TYPE_MAX, "Theme item data type is out of bounds.");
+
+ Label *total_selected_items_label;
+ switch (p_data_type) {
+ case Theme::DATA_TYPE_COLOR:
+ total_selected_items_label = total_selected_colors_label;
+ break;
+
+ case Theme::DATA_TYPE_CONSTANT:
+ total_selected_items_label = total_selected_constants_label;
+ break;
+
+ case Theme::DATA_TYPE_FONT:
+ total_selected_items_label = total_selected_fonts_label;
+ break;
+
+ case Theme::DATA_TYPE_FONT_SIZE:
+ total_selected_items_label = total_selected_font_sizes_label;
+ break;
+
+ case Theme::DATA_TYPE_ICON:
+ total_selected_items_label = total_selected_icons_label;
+ break;
+
+ case Theme::DATA_TYPE_STYLEBOX:
+ total_selected_items_label = total_selected_styleboxes_label;
+ break;
+
+ case Theme::DATA_TYPE_MAX:
+ return; // Can't happen, but silences warning.
+ }
+
+ if (!total_selected_items_label) {
+ return;
+ }
+
+ int count = 0;
+ for (Map<ThemeItem, ItemCheckedState>::Element *E = selected_items.front(); E; E = E->next()) {
+ ThemeItem ti = E->key();
+ if (ti.data_type == p_data_type) {
+ count++;
+ }
+ }
+
+ if (count == 0) {
+ total_selected_items_label->hide();
+ } else {
+ Array arr;
+ arr.push_back(count);
+ total_selected_items_label->set_text(TTRN("{num} currently selected", "{num} currently selected", count).format(arr, "{num}"));
+ total_selected_items_label->show();
+ }
+}
+
+void ThemeItemImportTree::_tree_item_edited() {
+ if (updating_tree) {
+ return;
+ }
+
+ TreeItem *edited_item = import_items_tree->get_edited();
+ if (!edited_item) {
+ return;
+ }
+
+ updating_tree = true;
+
+ int edited_column = import_items_tree->get_edited_column();
+ bool is_checked = edited_item->is_checked(edited_column);
+ if (is_checked) {
+ if (edited_column == IMPORT_ITEM_DATA) {
+ edited_item->set_checked(IMPORT_ITEM, true);
+ }
+
+ _select_all_subitems(edited_item, (edited_column == IMPORT_ITEM_DATA));
+ } else {
+ if (edited_column == IMPORT_ITEM) {
+ edited_item->set_checked(IMPORT_ITEM_DATA, false);
+ }
+
+ _deselect_all_subitems(edited_item, (edited_column == IMPORT_ITEM));
+ }
+
+ _update_parent_items(edited_item);
+ _store_selected_item(edited_item);
+
+ updating_tree = false;
+}
+
+void ThemeItemImportTree::_select_all_subitems(TreeItem *p_root_item, bool p_select_with_data) {
+ TreeItem *child_item = p_root_item->get_children();
+ while (child_item) {
+ child_item->set_checked(IMPORT_ITEM, true);
+ if (p_select_with_data) {
+ child_item->set_checked(IMPORT_ITEM_DATA, true);
+ }
+ _store_selected_item(child_item);
+
+ _select_all_subitems(child_item, p_select_with_data);
+ child_item = child_item->get_next();
+ }
+}
+
+void ThemeItemImportTree::_deselect_all_subitems(TreeItem *p_root_item, bool p_deselect_completely) {
+ TreeItem *child_item = p_root_item->get_children();
+ while (child_item) {
+ child_item->set_checked(IMPORT_ITEM_DATA, false);
+ if (p_deselect_completely) {
+ child_item->set_checked(IMPORT_ITEM, false);
+ }
+ _store_selected_item(child_item);
+
+ _deselect_all_subitems(child_item, p_deselect_completely);
+ child_item = child_item->get_next();
+ }
+}
+
+void ThemeItemImportTree::_update_parent_items(TreeItem *p_root_item) {
+ TreeItem *parent_item = p_root_item->get_parent();
+ if (!parent_item) {
+ return;
+ }
+
+ bool any_checked = false;
+ bool any_checked_with_data = false;
+
+ TreeItem *child_item = parent_item->get_children();
+ while (child_item) {
+ if (child_item->is_checked(IMPORT_ITEM)) {
+ any_checked = true;
+ }
+ if (child_item->is_checked(IMPORT_ITEM_DATA)) {
+ any_checked_with_data = true;
+ }
+
+ child_item = child_item->get_next();
+ }
+
+ parent_item->set_checked(IMPORT_ITEM, any_checked);
+ parent_item->set_checked(IMPORT_ITEM_DATA, any_checked && any_checked_with_data);
+ _update_parent_items(parent_item);
+}
+
+void ThemeItemImportTree::_select_all_items_pressed() {
+ if (updating_tree) {
+ return;
+ }
+
+ updating_tree = true;
+
+ TreeItem *root = import_items_tree->get_root();
+ _select_all_subitems(root, false);
+
+ updating_tree = false;
+}
+
+void ThemeItemImportTree::_select_full_items_pressed() {
+ if (updating_tree) {
+ return;
+ }
+
+ updating_tree = true;
+
+ TreeItem *root = import_items_tree->get_root();
+ _select_all_subitems(root, true);
+
+ updating_tree = false;
+}
+
+void ThemeItemImportTree::_deselect_all_items_pressed() {
+ if (updating_tree) {
+ return;
+ }
+
+ updating_tree = true;
+
+ TreeItem *root = import_items_tree->get_root();
+ _deselect_all_subitems(root, true);
+
+ updating_tree = false;
+}
+
+void ThemeItemImportTree::_select_all_data_type_pressed(int p_data_type) {
+ ERR_FAIL_INDEX_MSG(p_data_type, Theme::DATA_TYPE_MAX, "Theme item data type is out of bounds.");
+
+ if (updating_tree) {
+ return;
+ }
+
+ Theme::DataType data_type = (Theme::DataType)p_data_type;
+ List<TreeItem *> *item_list;
+
+ switch (data_type) {
+ case Theme::DATA_TYPE_COLOR:
+ item_list = &tree_color_items;
+ break;
+
+ case Theme::DATA_TYPE_CONSTANT:
+ item_list = &tree_constant_items;
+ break;
+
+ case Theme::DATA_TYPE_FONT:
+ item_list = &tree_font_items;
+ break;
+
+ case Theme::DATA_TYPE_FONT_SIZE:
+ item_list = &tree_font_size_items;
+ break;
+
+ case Theme::DATA_TYPE_ICON:
+ item_list = &tree_icon_items;
+ break;
+
+ case Theme::DATA_TYPE_STYLEBOX:
+ item_list = &tree_stylebox_items;
+ break;
+
+ case Theme::DATA_TYPE_MAX:
+ return; // Can't happen, but silences warning.
+ }
+
+ updating_tree = true;
+
+ for (List<TreeItem *>::Element *E = item_list->front(); E; E = E->next()) {
+ TreeItem *child_item = E->get();
+ if (!child_item) {
+ continue;
+ }
+
+ child_item->set_checked(IMPORT_ITEM, true);
+ _update_parent_items(child_item);
+ _store_selected_item(child_item);
+ }
+
+ updating_tree = false;
+}
+
+void ThemeItemImportTree::_select_full_data_type_pressed(int p_data_type) {
+ ERR_FAIL_INDEX_MSG(p_data_type, Theme::DATA_TYPE_MAX, "Theme item data type is out of bounds.");
+
+ if (updating_tree) {
+ return;
+ }
+
+ Theme::DataType data_type = (Theme::DataType)p_data_type;
+ List<TreeItem *> *item_list;
+
+ switch (data_type) {
+ case Theme::DATA_TYPE_COLOR:
+ item_list = &tree_color_items;
+ break;
+
+ case Theme::DATA_TYPE_CONSTANT:
+ item_list = &tree_constant_items;
+ break;
+
+ case Theme::DATA_TYPE_FONT:
+ item_list = &tree_font_items;
+ break;
+
+ case Theme::DATA_TYPE_FONT_SIZE:
+ item_list = &tree_font_size_items;
+ break;
+
+ case Theme::DATA_TYPE_ICON:
+ item_list = &tree_icon_items;
+ break;
+
+ case Theme::DATA_TYPE_STYLEBOX:
+ item_list = &tree_stylebox_items;
+ break;
+
+ case Theme::DATA_TYPE_MAX:
+ return; // Can't happen, but silences warning.
+ }
+
+ updating_tree = true;
+
+ for (List<TreeItem *>::Element *E = item_list->front(); E; E = E->next()) {
+ TreeItem *child_item = E->get();
+ if (!child_item) {
+ continue;
+ }
+
+ child_item->set_checked(IMPORT_ITEM, true);
+ child_item->set_checked(IMPORT_ITEM_DATA, true);
+ _update_parent_items(child_item);
+ _store_selected_item(child_item);
+ }
+
+ updating_tree = false;
+}
+
+void ThemeItemImportTree::_deselect_all_data_type_pressed(int p_data_type) {
+ ERR_FAIL_INDEX_MSG(p_data_type, Theme::DATA_TYPE_MAX, "Theme item data type is out of bounds.");
+
+ if (updating_tree) {
+ return;
+ }
+
+ Theme::DataType data_type = (Theme::DataType)p_data_type;
+ List<TreeItem *> *item_list;
+
+ switch (data_type) {
+ case Theme::DATA_TYPE_COLOR:
+ item_list = &tree_color_items;
+ break;
+
+ case Theme::DATA_TYPE_CONSTANT:
+ item_list = &tree_constant_items;
+ break;
+
+ case Theme::DATA_TYPE_FONT:
+ item_list = &tree_font_items;
+ break;
+
+ case Theme::DATA_TYPE_FONT_SIZE:
+ item_list = &tree_font_size_items;
+ break;
+
+ case Theme::DATA_TYPE_ICON:
+ item_list = &tree_icon_items;
+ break;
+
+ case Theme::DATA_TYPE_STYLEBOX:
+ item_list = &tree_stylebox_items;
+ break;
+
+ case Theme::DATA_TYPE_MAX:
+ return; // Can't happen, but silences warning.
+ }
+
+ updating_tree = true;
+
+ for (List<TreeItem *>::Element *E = item_list->front(); E; E = E->next()) {
+ TreeItem *child_item = E->get();
+ if (!child_item) {
+ continue;
+ }
+
+ child_item->set_checked(IMPORT_ITEM, false);
+ child_item->set_checked(IMPORT_ITEM_DATA, false);
+ _update_parent_items(child_item);
+ _store_selected_item(child_item);
+ }
+
+ updating_tree = false;
+}
+
+void ThemeItemImportTree::_import_selected() {
+ if (selected_items.size() == 0) {
+ EditorNode::get_singleton()->show_accept(TTR("Nothing was selected for the import."), TTR("OK"));
+ return;
+ }
+
+ ProgressDialog::get_singleton()->add_task("import_theme_items", TTR("Importing Theme Items"), selected_items.size());
+
+ int idx = 0;
+ for (Map<ThemeItem, ItemCheckedState>::Element *E = selected_items.front(); E; E = E->next()) {
+ // Arbitrary number of items to skip from reporting.
+ // Reduces the number of UI updates that this causes when copying large themes.
+ if (idx % 10 == 0) {
+ Array arr;
+ arr.push_back(idx + 1);
+ arr.push_back(selected_items.size());
+ ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Importing items {n}/{n}").format(arr, "{n}"), idx);
+ }
+
+ ItemCheckedState cs = E->get();
+ ThemeItem ti = E->key();
+
+ if (cs == SELECT_IMPORT_DEFINITION || cs == SELECT_IMPORT_FULL) {
+ Variant item_value = Variant();
+
+ if (cs == SELECT_IMPORT_FULL) {
+ item_value = base_theme->get_theme_item(ti.data_type, ti.item_name, ti.type_name);
+ } else {
+ switch (ti.data_type) {
+ case Theme::DATA_TYPE_COLOR:
+ item_value = Color();
+ break;
+
+ case Theme::DATA_TYPE_CONSTANT:
+ item_value = 0;
+ break;
+
+ case Theme::DATA_TYPE_FONT:
+ item_value = Ref<Font>();
+ break;
+
+ case Theme::DATA_TYPE_FONT_SIZE:
+ item_value = -1;
+ break;
+
+ case Theme::DATA_TYPE_ICON:
+ item_value = Ref<Texture2D>();
+ break;
+
+ case Theme::DATA_TYPE_STYLEBOX:
+ item_value = Ref<StyleBox>();
+ break;
+
+ case Theme::DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+ }
+
+ edited_theme->set_theme_item(ti.data_type, ti.item_name, ti.type_name, item_value);
+ }
+
+ idx++;
+ }
+
+ ProgressDialog::get_singleton()->end_task("import_theme_items");
+ emit_signal("items_imported");
+}
+
+void ThemeItemImportTree::set_edited_theme(const Ref<Theme> &p_theme) {
+ edited_theme = p_theme;
+}
+
+void ThemeItemImportTree::set_base_theme(const Ref<Theme> &p_theme) {
+ base_theme = p_theme;
+}
+
+void ThemeItemImportTree::reset_item_tree() {
+ import_items_filter->clear();
+ selected_items.clear();
+
+ total_selected_colors_label->hide();
+ total_selected_constants_label->hide();
+ total_selected_fonts_label->hide();
+ total_selected_font_sizes_label->hide();
+ total_selected_icons_label->hide();
+ total_selected_styleboxes_label->hide();
+
+ _update_items_tree();
+}
+
+bool ThemeItemImportTree::has_selected_items() const {
+ return (selected_items.size() > 0);
+}
+
+void ThemeItemImportTree::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ select_icons_warning_icon->set_texture(get_theme_icon("StatusWarning", "EditorIcons"));
+ select_icons_warning->add_theme_color_override("font_color", get_theme_color("disabled_font_color", "Editor"));
+
+ // Bottom panel buttons.
+ import_collapse_types_button->set_icon(get_theme_icon("CollapseTree", "EditorIcons"));
+ import_expand_types_button->set_icon(get_theme_icon("ExpandTree", "EditorIcons"));
+
+ import_select_all_button->set_icon(get_theme_icon("ThemeSelectAll", "EditorIcons"));
+ import_select_full_button->set_icon(get_theme_icon("ThemeSelectFull", "EditorIcons"));
+ import_deselect_all_button->set_icon(get_theme_icon("ThemeDeselectAll", "EditorIcons"));
+
+ // Side panel buttons.
+ select_colors_icon->set_texture(get_theme_icon("Color", "EditorIcons"));
+ deselect_all_colors_button->set_icon(get_theme_icon("ThemeDeselectAll", "EditorIcons"));
+ select_all_colors_button->set_icon(get_theme_icon("ThemeSelectAll", "EditorIcons"));
+ select_full_colors_button->set_icon(get_theme_icon("ThemeSelectFull", "EditorIcons"));
+
+ select_constants_icon->set_texture(get_theme_icon("MemberConstant", "EditorIcons"));
+ deselect_all_constants_button->set_icon(get_theme_icon("ThemeDeselectAll", "EditorIcons"));
+ select_all_constants_button->set_icon(get_theme_icon("ThemeSelectAll", "EditorIcons"));
+ select_full_constants_button->set_icon(get_theme_icon("ThemeSelectFull", "EditorIcons"));
+
+ select_fonts_icon->set_texture(get_theme_icon("Font", "EditorIcons"));
+ deselect_all_fonts_button->set_icon(get_theme_icon("ThemeDeselectAll", "EditorIcons"));
+ select_all_fonts_button->set_icon(get_theme_icon("ThemeSelectAll", "EditorIcons"));
+ select_full_fonts_button->set_icon(get_theme_icon("ThemeSelectFull", "EditorIcons"));
+
+ select_font_sizes_icon->set_texture(get_theme_icon("FontSize", "EditorIcons"));
+ deselect_all_font_sizes_button->set_icon(get_theme_icon("ThemeDeselectAll", "EditorIcons"));
+ select_all_font_sizes_button->set_icon(get_theme_icon("ThemeSelectAll", "EditorIcons"));
+ select_full_font_sizes_button->set_icon(get_theme_icon("ThemeSelectFull", "EditorIcons"));
+
+ select_icons_icon->set_texture(get_theme_icon("ImageTexture", "EditorIcons"));
+ deselect_all_icons_button->set_icon(get_theme_icon("ThemeDeselectAll", "EditorIcons"));
+ select_all_icons_button->set_icon(get_theme_icon("ThemeSelectAll", "EditorIcons"));
+ select_full_icons_button->set_icon(get_theme_icon("ThemeSelectFull", "EditorIcons"));
+
+ select_styleboxes_icon->set_texture(get_theme_icon("StyleBoxFlat", "EditorIcons"));
+ deselect_all_styleboxes_button->set_icon(get_theme_icon("ThemeDeselectAll", "EditorIcons"));
+ select_all_styleboxes_button->set_icon(get_theme_icon("ThemeSelectAll", "EditorIcons"));
+ select_full_styleboxes_button->set_icon(get_theme_icon("ThemeSelectFull", "EditorIcons"));
+ } break;
+ }
+}
+
+void ThemeItemImportTree::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("items_imported"));
+}
+
+ThemeItemImportTree::ThemeItemImportTree() {
+ HBoxContainer *import_items_filter_hb = memnew(HBoxContainer);
+ add_child(import_items_filter_hb);
+ Label *import_items_filter_label = memnew(Label);
+ import_items_filter_label->set_text(TTR("Filter:"));
+ import_items_filter_hb->add_child(import_items_filter_label);
+ import_items_filter = memnew(LineEdit);
+ import_items_filter->set_clear_button_enabled(true);
+ import_items_filter->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ import_items_filter_hb->add_child(import_items_filter);
+ import_items_filter->connect("text_changed", callable_mp(this, &ThemeItemImportTree::_filter_text_changed));
+
+ HBoxContainer *import_main_hb = memnew(HBoxContainer);
+ import_main_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ add_child(import_main_hb);
+
+ import_items_tree = memnew(Tree);
+ import_items_tree->set_hide_root(true);
+ import_items_tree->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ import_main_hb->add_child(import_items_tree);
+ import_items_tree->connect("item_edited", callable_mp(this, &ThemeItemImportTree::_tree_item_edited));
+
+ import_items_tree->set_columns(3);
+ import_items_tree->set_column_titles_visible(true);
+ import_items_tree->set_column_title(IMPORT_ITEM, TTR("Import"));
+ import_items_tree->set_column_title(IMPORT_ITEM_DATA, TTR("With Data"));
+ import_items_tree->set_column_expand(0, true);
+ import_items_tree->set_column_expand(IMPORT_ITEM, false);
+ import_items_tree->set_column_expand(IMPORT_ITEM_DATA, false);
+ import_items_tree->set_column_min_width(0, 160 * EDSCALE);
+ import_items_tree->set_column_min_width(IMPORT_ITEM, 80 * EDSCALE);
+ import_items_tree->set_column_min_width(IMPORT_ITEM_DATA, 80 * EDSCALE);
+
+ ScrollContainer *import_bulk_sc = memnew(ScrollContainer);
+ import_bulk_sc->set_custom_minimum_size(Size2(260.0, 0.0) * EDSCALE);
+ import_bulk_sc->set_enable_h_scroll(false);
+ import_main_hb->add_child(import_bulk_sc);
+ VBoxContainer *import_bulk_vb = memnew(VBoxContainer);
+ import_bulk_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ import_bulk_sc->add_child(import_bulk_vb);
+
+ Label *import_bulk_label = memnew(Label);
+ import_bulk_label->set_text(TTR("Select by data type:"));
+ import_bulk_vb->add_child(import_bulk_label);
+
+ select_colors_icon = memnew(TextureRect);
+ select_colors_label = memnew(Label);
+ deselect_all_colors_button = memnew(Button);
+ select_all_colors_button = memnew(Button);
+ select_full_colors_button = memnew(Button);
+ total_selected_colors_label = memnew(Label);
+
+ select_constants_icon = memnew(TextureRect);
+ select_constants_label = memnew(Label);
+ deselect_all_constants_button = memnew(Button);
+ select_all_constants_button = memnew(Button);
+ select_full_constants_button = memnew(Button);
+ total_selected_constants_label = memnew(Label);
+
+ select_fonts_icon = memnew(TextureRect);
+ select_fonts_label = memnew(Label);
+ deselect_all_fonts_button = memnew(Button);
+ select_all_fonts_button = memnew(Button);
+ select_full_fonts_button = memnew(Button);
+ total_selected_fonts_label = memnew(Label);
+
+ select_font_sizes_icon = memnew(TextureRect);
+ select_font_sizes_label = memnew(Label);
+ deselect_all_font_sizes_button = memnew(Button);
+ select_all_font_sizes_button = memnew(Button);
+ select_full_font_sizes_button = memnew(Button);
+ total_selected_font_sizes_label = memnew(Label);
+
+ select_icons_icon = memnew(TextureRect);
+ select_icons_label = memnew(Label);
+ deselect_all_icons_button = memnew(Button);
+ select_all_icons_button = memnew(Button);
+ select_full_icons_button = memnew(Button);
+ total_selected_icons_label = memnew(Label);
+
+ select_styleboxes_icon = memnew(TextureRect);
+ select_styleboxes_label = memnew(Label);
+ deselect_all_styleboxes_button = memnew(Button);
+ select_all_styleboxes_button = memnew(Button);
+ select_full_styleboxes_button = memnew(Button);
+ total_selected_styleboxes_label = memnew(Label);
+
+ for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
+ Theme::DataType dt = (Theme::DataType)i;
+
+ TextureRect *select_items_icon;
+ Label *select_items_label;
+ Button *deselect_all_items_button;
+ Button *select_all_items_button;
+ Button *select_full_items_button;
+ Label *total_selected_items_label;
+
+ String items_title = "";
+ String select_all_items_tooltip = "";
+ String select_full_items_tooltip = "";
+ String deselect_all_items_tooltip = "";
+
+ switch (dt) {
+ case Theme::DATA_TYPE_COLOR:
+ select_items_icon = select_colors_icon;
+ select_items_label = select_colors_label;
+ deselect_all_items_button = deselect_all_colors_button;
+ select_all_items_button = select_all_colors_button;
+ select_full_items_button = select_full_colors_button;
+ total_selected_items_label = total_selected_colors_label;
+
+ items_title = TTR("Colors");
+ select_all_items_tooltip = TTR("Select all visible color items.");
+ select_full_items_tooltip = TTR("Select all visible color items and their data.");
+ deselect_all_items_tooltip = TTR("Deselect all visible color items.");
+ break;
+
+ case Theme::DATA_TYPE_CONSTANT:
+ select_items_icon = select_constants_icon;
+ select_items_label = select_constants_label;
+ deselect_all_items_button = deselect_all_constants_button;
+ select_all_items_button = select_all_constants_button;
+ select_full_items_button = select_full_constants_button;
+ total_selected_items_label = total_selected_constants_label;
+
+ items_title = TTR("Constants");
+ select_all_items_tooltip = TTR("Select all visible constant items.");
+ select_full_items_tooltip = TTR("Select all visible constant items and their data.");
+ deselect_all_items_tooltip = TTR("Deselect all visible constant items.");
+ break;
+
+ case Theme::DATA_TYPE_FONT:
+ select_items_icon = select_fonts_icon;
+ select_items_label = select_fonts_label;
+ deselect_all_items_button = deselect_all_fonts_button;
+ select_all_items_button = select_all_fonts_button;
+ select_full_items_button = select_full_fonts_button;
+ total_selected_items_label = total_selected_fonts_label;
+
+ items_title = TTR("Fonts");
+ select_all_items_tooltip = TTR("Select all visible font items.");
+ select_full_items_tooltip = TTR("Select all visible font items and their data.");
+ deselect_all_items_tooltip = TTR("Deselect all visible font items.");
+ break;
+
+ case Theme::DATA_TYPE_FONT_SIZE:
+ select_items_icon = select_font_sizes_icon;
+ select_items_label = select_font_sizes_label;
+ deselect_all_items_button = deselect_all_font_sizes_button;
+ select_all_items_button = select_all_font_sizes_button;
+ select_full_items_button = select_full_font_sizes_button;
+ total_selected_items_label = total_selected_font_sizes_label;
+
+ items_title = TTR("Font sizes");
+ select_all_items_tooltip = TTR("Select all visible font size items.");
+ select_full_items_tooltip = TTR("Select all visible font size items and their data.");
+ deselect_all_items_tooltip = TTR("Deselect all visible font size items.");
+ break;
+
+ case Theme::DATA_TYPE_ICON:
+ select_items_icon = select_icons_icon;
+ select_items_label = select_icons_label;
+ deselect_all_items_button = deselect_all_icons_button;
+ select_all_items_button = select_all_icons_button;
+ select_full_items_button = select_full_icons_button;
+ total_selected_items_label = total_selected_icons_label;
+
+ items_title = TTR("Icons");
+ select_all_items_tooltip = TTR("Select all visible icon items.");
+ select_full_items_tooltip = TTR("Select all visible icon items and their data.");
+ deselect_all_items_tooltip = TTR("Deselect all visible icon items.");
+ break;
+
+ case Theme::DATA_TYPE_STYLEBOX:
+ select_items_icon = select_styleboxes_icon;
+ select_items_label = select_styleboxes_label;
+ deselect_all_items_button = deselect_all_styleboxes_button;
+ select_all_items_button = select_all_styleboxes_button;
+ select_full_items_button = select_full_styleboxes_button;
+ total_selected_items_label = total_selected_styleboxes_label;
+
+ items_title = TTR("Styleboxes");
+ select_all_items_tooltip = TTR("Select all visible stylebox items.");
+ select_full_items_tooltip = TTR("Select all visible stylebox items and their data.");
+ deselect_all_items_tooltip = TTR("Deselect all visible stylebox items.");
+ break;
+
+ case Theme::DATA_TYPE_MAX:
+ continue; // Can't happen, but silences warning.
+ }
+
+ if (i > 0) {
+ import_bulk_vb->add_child(memnew(HSeparator));
+ }
+
+ HBoxContainer *all_set = memnew(HBoxContainer);
+ import_bulk_vb->add_child(all_set);
+
+ HBoxContainer *label_set = memnew(HBoxContainer);
+ label_set->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ all_set->add_child(label_set);
+ select_items_icon->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
+ label_set->add_child(select_items_icon);
+ select_items_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ select_items_label->set_clip_text(true);
+ select_items_label->set_text(items_title);
+ label_set->add_child(select_items_label);
+
+ HBoxContainer *button_set = memnew(HBoxContainer);
+ button_set->set_alignment(BoxContainer::ALIGN_END);
+ all_set->add_child(button_set);
+ select_all_items_button->set_flat(true);
+ select_all_items_button->set_tooltip(select_all_items_tooltip);
+ button_set->add_child(select_all_items_button);
+ select_all_items_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_select_all_data_type_pressed), varray(i));
+ select_full_items_button->set_flat(true);
+ select_full_items_button->set_tooltip(select_full_items_tooltip);
+ button_set->add_child(select_full_items_button);
+ select_full_items_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_select_full_data_type_pressed), varray(i));
+ deselect_all_items_button->set_flat(true);
+ deselect_all_items_button->set_tooltip(deselect_all_items_tooltip);
+ button_set->add_child(deselect_all_items_button);
+ deselect_all_items_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_deselect_all_data_type_pressed), varray(i));
+
+ total_selected_items_label->set_align(Label::ALIGN_RIGHT);
+ total_selected_items_label->hide();
+ import_bulk_vb->add_child(total_selected_items_label);
+
+ if (dt == Theme::DATA_TYPE_ICON) {
+ select_icons_warning_hb = memnew(HBoxContainer);
+ import_bulk_vb->add_child(select_icons_warning_hb);
+
+ select_icons_warning_icon = memnew(TextureRect);
+ select_icons_warning_icon->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
+ select_icons_warning_hb->add_child(select_icons_warning_icon);
+
+ select_icons_warning = memnew(Label);
+ select_icons_warning->set_text(TTR("Caution: Adding icon data may considerably increase the size of your Theme resource."));
+ select_icons_warning->set_autowrap(true);
+ select_icons_warning->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ select_icons_warning_hb->add_child(select_icons_warning);
+ }
+ }
+
+ add_child(memnew(HSeparator));
+
+ HBoxContainer *import_buttons = memnew(HBoxContainer);
+ add_child(import_buttons);
+
+ import_collapse_types_button = memnew(Button);
+ import_collapse_types_button->set_flat(true);
+ import_collapse_types_button->set_tooltip(TTR("Collapse types."));
+ import_buttons->add_child(import_collapse_types_button);
+ import_collapse_types_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_toggle_type_items), varray(true));
+ import_expand_types_button = memnew(Button);
+ import_expand_types_button->set_flat(true);
+ import_expand_types_button->set_tooltip(TTR("Expand types."));
+ import_buttons->add_child(import_expand_types_button);
+ import_expand_types_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_toggle_type_items), varray(false));
+
+ import_buttons->add_child(memnew(VSeparator));
+
+ import_select_all_button = memnew(Button);
+ import_select_all_button->set_flat(true);
+ import_select_all_button->set_text(TTR("Select All"));
+ import_select_all_button->set_tooltip(TTR("Select all Theme items."));
+ import_buttons->add_child(import_select_all_button);
+ import_select_all_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_select_all_items_pressed));
+ import_select_full_button = memnew(Button);
+ import_select_full_button->set_flat(true);
+ import_select_full_button->set_text(TTR("Select With Data"));
+ import_select_full_button->set_tooltip(TTR("Select all Theme items with item data."));
+ import_buttons->add_child(import_select_full_button);
+ import_select_full_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_select_full_items_pressed));
+ import_deselect_all_button = memnew(Button);
+ import_deselect_all_button->set_flat(true);
+ import_deselect_all_button->set_text(TTR("Deselect All"));
+ import_deselect_all_button->set_tooltip(TTR("Deselect all Theme items."));
+ import_buttons->add_child(import_deselect_all_button);
+ import_deselect_all_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_deselect_all_items_pressed));
+
+ import_buttons->add_spacer();
+
+ Button *import_add_selected_button = memnew(Button);
+ import_add_selected_button->set_text(TTR("Import Selected"));
+ import_buttons->add_child(import_add_selected_button);
+ import_add_selected_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_import_selected));
+}
+
+void ThemeItemEditorDialog::ok_pressed() {
+ if (import_default_theme_items->has_selected_items() || import_editor_theme_items->has_selected_items() || import_other_theme_items->has_selected_items()) {
+ confirm_closing_dialog->set_text(TTR("Import Items tab has some items selected. Selection will be lost upon closing this window.\nClose anyway?"));
+ confirm_closing_dialog->popup_centered(Size2i(380, 120) * EDSCALE);
+ return;
+ }
+
+ hide();
+}
+
+void ThemeItemEditorDialog::_close_dialog() {
+ hide();
+}
+
void ThemeItemEditorDialog::_dialog_about_to_show() {
ERR_FAIL_COND(edited_theme.is_null());
_update_edit_types();
+
+ import_default_theme_items->set_edited_theme(edited_theme);
+ import_default_theme_items->set_base_theme(Theme::get_default());
+ import_default_theme_items->reset_item_tree();
+
+ import_editor_theme_items->set_edited_theme(edited_theme);
+ import_editor_theme_items->set_base_theme(EditorNode::get_singleton()->get_theme_base()->get_theme());
+ import_editor_theme_items->reset_item_tree();
+
+ import_other_theme_items->set_edited_theme(edited_theme);
+ import_other_theme_items->reset_item_tree();
}
void ThemeItemEditorDialog::_update_edit_types() {
@@ -79,11 +1254,6 @@ void ThemeItemEditorDialog::_update_edit_types() {
base_theme->get_type_list(&default_types);
default_types.sort_custom<StringName::AlphCompare>();
- edit_add_class_options->clear();
- for (List<StringName>::Element *E = default_types.front(); E; E = E->next()) {
- edit_add_class_options->add_item(E->get());
- }
-
String selected_type = "";
Vector<int> selected_ids = edit_type_list->get_selected_items();
if (selected_ids.size() > 0) {
@@ -127,7 +1297,7 @@ void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) {
List<StringName> names;
- {
+ { // Colors.
names.clear();
edited_theme->get_color_list(p_item_type, &names);
@@ -148,7 +1318,7 @@ void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) {
}
}
- {
+ { // Constants.
names.clear();
edited_theme->get_constant_list(p_item_type, &names);
@@ -169,7 +1339,7 @@ void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) {
}
}
- {
+ { // Fonts.
names.clear();
edited_theme->get_font_list(p_item_type, &names);
@@ -190,7 +1360,7 @@ void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) {
}
}
- {
+ { // Font sizes.
names.clear();
edited_theme->get_font_size_list(p_item_type, &names);
@@ -211,7 +1381,7 @@ void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) {
}
}
- {
+ { // Icons.
names.clear();
edited_theme->get_icon_list(p_item_type, &names);
@@ -232,7 +1402,7 @@ void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) {
}
}
- {
+ { // Styleboxes.
names.clear();
edited_theme->get_stylebox_list(p_item_type, &names);
@@ -280,64 +1450,13 @@ void ThemeItemEditorDialog::_item_tree_button_pressed(Object *p_item, int p_colu
_update_edit_item_tree(edited_item_type);
}
-void ThemeItemEditorDialog::_add_class_type_items() {
- int selected_idx = edit_add_class_options->get_selected();
- String type_name = edit_add_class_options->get_item_text(selected_idx);
- List<StringName> names;
-
- {
- names.clear();
- Theme::get_default()->get_icon_list(type_name, &names);
- for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
- edited_theme->set_icon(E->get(), type_name, Ref<Texture2D>());
- }
- }
- {
- names.clear();
- Theme::get_default()->get_stylebox_list(type_name, &names);
- for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
- edited_theme->set_stylebox(E->get(), type_name, Ref<StyleBox>());
- }
- }
- {
- names.clear();
- Theme::get_default()->get_font_list(type_name, &names);
- for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
- edited_theme->set_font(E->get(), type_name, Ref<Font>());
- }
- }
- {
- names.clear();
- Theme::get_default()->get_font_size_list(type_name, &names);
- for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
- edited_theme->set_font_size(E->get(), type_name, Theme::get_default()->get_font_size(E->get(), type_name));
- }
- }
- {
- names.clear();
- Theme::get_default()->get_color_list(type_name, &names);
- for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
- edited_theme->set_color(E->get(), type_name, Theme::get_default()->get_color(E->get(), type_name));
- }
- }
- {
- names.clear();
- Theme::get_default()->get_constant_list(type_name, &names);
- for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
- edited_theme->set_constant(E->get(), type_name, Theme::get_default()->get_constant(E->get(), type_name));
- }
- }
-
- _update_edit_types();
-}
-
-void ThemeItemEditorDialog::_add_custom_type() {
- edited_theme->add_icon_type(edit_add_custom_value->get_text());
- edited_theme->add_stylebox_type(edit_add_custom_value->get_text());
- edited_theme->add_font_type(edit_add_custom_value->get_text());
- edited_theme->add_font_size_type(edit_add_custom_value->get_text());
- edited_theme->add_color_type(edit_add_custom_value->get_text());
- edited_theme->add_constant_type(edit_add_custom_value->get_text());
+void ThemeItemEditorDialog::_add_theme_type() {
+ edited_theme->add_icon_type(edit_add_type_value->get_text());
+ edited_theme->add_stylebox_type(edit_add_type_value->get_text());
+ edited_theme->add_font_type(edit_add_type_value->get_text());
+ edited_theme->add_font_size_type(edit_add_type_value->get_text());
+ edited_theme->add_color_type(edit_add_type_value->get_text());
+ edited_theme->add_constant_type(edit_add_type_value->get_text());
_update_edit_types();
}
@@ -536,6 +1655,26 @@ void ThemeItemEditorDialog::_edit_theme_item_gui_input(const Ref<InputEvent> &p_
}
}
+void ThemeItemEditorDialog::_open_select_another_theme() {
+ import_another_theme_dialog->popup_file_dialog();
+}
+
+void ThemeItemEditorDialog::_select_another_theme_cbk(const String &p_path) {
+ Ref<Theme> loaded_theme = ResourceLoader::load(p_path);
+ if (loaded_theme.is_null()) {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid file, not a Theme resource."));
+ return;
+ }
+ if (loaded_theme == edited_theme) {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid file, same as the edited Theme resource."));
+ return;
+ }
+
+ import_another_theme_value->set_text(p_path);
+ import_other_theme_items->set_base_theme(loaded_theme);
+ import_other_theme_items->reset_item_tree();
+}
+
void ThemeItemEditorDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -553,6 +1692,8 @@ void ThemeItemEditorDialog::_notification(int p_what) {
edit_items_remove_class->set_icon(get_theme_icon("Control", "EditorIcons"));
edit_items_remove_custom->set_icon(get_theme_icon("ThemeRemoveCustomItems", "EditorIcons"));
edit_items_remove_all->set_icon(get_theme_icon("ThemeRemoveAllItems", "EditorIcons"));
+
+ import_another_theme_button->set_icon(get_theme_icon("Folder", "EditorIcons"));
} break;
}
}
@@ -562,10 +1703,18 @@ void ThemeItemEditorDialog::set_edited_theme(const Ref<Theme> &p_theme) {
}
ThemeItemEditorDialog::ThemeItemEditorDialog() {
- set_title(TTR("Edit Theme Items"));
+ set_title(TTR("Manage Theme Items"));
+ get_ok_button()->set_text(TTR("Close"));
+ set_hide_on_ok(false); // Closing may require a confirmation in some cases.
+ TabContainer *tc = memnew(TabContainer);
+ tc->set_tab_align(TabContainer::TabAlign::ALIGN_LEFT);
+ add_child(tc);
+
+ // Edit Items tab.
HSplitContainer *edit_dialog_hs = memnew(HSplitContainer);
- add_child(edit_dialog_hs);
+ tc->add_child(edit_dialog_hs);
+ tc->set_tab_title(0, TTR("Edit Items"));
VBoxContainer *edit_dialog_side_vb = memnew(VBoxContainer);
edit_dialog_side_vb->set_custom_minimum_size(Size2(200.0, 0.0) * EDSCALE);
@@ -580,33 +1729,19 @@ ThemeItemEditorDialog::ThemeItemEditorDialog() {
edit_dialog_side_vb->add_child(edit_type_list);
edit_type_list->connect("item_selected", callable_mp(this, &ThemeItemEditorDialog::_edited_type_selected));
- Label *edit_add_class_label = memnew(Label);
- edit_add_class_label->set_text(TTR("Add Type from Class:"));
- edit_dialog_side_vb->add_child(edit_add_class_label);
-
- HBoxContainer *edit_add_class = memnew(HBoxContainer);
- edit_dialog_side_vb->add_child(edit_add_class);
- edit_add_class_options = memnew(OptionButton);
- edit_add_class_options->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- edit_add_class->add_child(edit_add_class_options);
- Button *edit_add_class_button = memnew(Button);
- edit_add_class_button->set_text(TTR("Add"));
- edit_add_class->add_child(edit_add_class_button);
- edit_add_class_button->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_add_class_type_items));
-
- Label *edit_add_custom_label = memnew(Label);
- edit_add_custom_label->set_text(TTR("Add Custom Type:"));
- edit_dialog_side_vb->add_child(edit_add_custom_label);
-
- HBoxContainer *edit_add_custom = memnew(HBoxContainer);
- edit_dialog_side_vb->add_child(edit_add_custom);
- edit_add_custom_value = memnew(LineEdit);
- edit_add_custom_value->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- edit_add_custom->add_child(edit_add_custom_value);
- Button *edit_add_custom_button = memnew(Button);
- edit_add_custom_button->set_text(TTR("Add"));
- edit_add_custom->add_child(edit_add_custom_button);
- edit_add_custom_button->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_add_custom_type));
+ Label *edit_add_type_label = memnew(Label);
+ edit_add_type_label->set_text(TTR("Add Type:"));
+ edit_dialog_side_vb->add_child(edit_add_type_label);
+
+ HBoxContainer *edit_add_type_hb = memnew(HBoxContainer);
+ edit_dialog_side_vb->add_child(edit_add_type_hb);
+ edit_add_type_value = memnew(LineEdit);
+ edit_add_type_value->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ edit_add_type_hb->add_child(edit_add_type_value);
+ Button *edit_add_type_button = memnew(Button);
+ edit_add_type_button->set_text(TTR("Add"));
+ edit_add_type_hb->add_child(edit_add_type_button);
+ edit_add_type_button->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_add_theme_type));
VBoxContainer *edit_items_vb = memnew(VBoxContainer);
edit_items_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -616,7 +1751,7 @@ ThemeItemEditorDialog::ThemeItemEditorDialog() {
edit_items_vb->add_child(edit_items_toolbar);
Label *edit_items_toolbar_add_label = memnew(Label);
- edit_items_toolbar_add_label->set_text(TTR("Add:"));
+ edit_items_toolbar_add_label->set_text(TTR("Add Item:"));
edit_items_toolbar->add_child(edit_items_toolbar_add_label);
edit_items_add_color = memnew(Button);
@@ -664,7 +1799,7 @@ ThemeItemEditorDialog::ThemeItemEditorDialog() {
edit_items_toolbar->add_child(memnew(VSeparator));
Label *edit_items_toolbar_remove_label = memnew(Label);
- edit_items_toolbar_remove_label->set_text(TTR("Remove:"));
+ edit_items_toolbar_remove_label->set_text(TTR("Remove Items:"));
edit_items_toolbar->add_child(edit_items_toolbar_remove_label);
edit_items_remove_class = memnew(Button);
@@ -716,6 +1851,57 @@ ThemeItemEditorDialog::ThemeItemEditorDialog() {
edit_theme_item_vb->add_child(theme_item_name);
theme_item_name->connect("gui_input", callable_mp(this, &ThemeItemEditorDialog::_edit_theme_item_gui_input));
edit_theme_item_dialog->connect("confirmed", callable_mp(this, &ThemeItemEditorDialog::_confirm_edit_theme_item));
+
+ // Import Items tab.
+ TabContainer *import_tc = memnew(TabContainer);
+ tc->add_child(import_tc);
+ tc->set_tab_title(1, TTR("Import Items"));
+
+ import_default_theme_items = memnew(ThemeItemImportTree);
+ import_tc->add_child(import_default_theme_items);
+ import_tc->set_tab_title(0, TTR("Default Theme"));
+ import_default_theme_items->connect("items_imported", callable_mp(this, &ThemeItemEditorDialog::_update_edit_types));
+
+ import_editor_theme_items = memnew(ThemeItemImportTree);
+ import_tc->add_child(import_editor_theme_items);
+ import_tc->set_tab_title(1, TTR("Editor Theme"));
+ import_editor_theme_items->connect("items_imported", callable_mp(this, &ThemeItemEditorDialog::_update_edit_types));
+
+ VBoxContainer *import_another_theme_vb = memnew(VBoxContainer);
+
+ HBoxContainer *import_another_file_hb = memnew(HBoxContainer);
+ import_another_theme_vb->add_child(import_another_file_hb);
+ import_another_theme_value = memnew(LineEdit);
+ import_another_theme_value->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ import_another_theme_value->set_editable(false);
+ import_another_file_hb->add_child(import_another_theme_value);
+ import_another_theme_button = memnew(Button);
+ import_another_file_hb->add_child(import_another_theme_button);
+ import_another_theme_button->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_open_select_another_theme));
+
+ import_another_theme_dialog = memnew(EditorFileDialog);
+ import_another_theme_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
+ import_another_theme_dialog->set_title(TTR("Select Another Theme Resource:"));
+ List<String> ext;
+ ResourceLoader::get_recognized_extensions_for_type("Theme", &ext);
+ for (List<String>::Element *E = ext.front(); E; E = E->next()) {
+ import_another_theme_dialog->add_filter("*." + E->get() + "; Theme Resource");
+ }
+ import_another_file_hb->add_child(import_another_theme_dialog);
+ import_another_theme_dialog->connect("file_selected", callable_mp(this, &ThemeItemEditorDialog::_select_another_theme_cbk));
+
+ import_other_theme_items = memnew(ThemeItemImportTree);
+ import_other_theme_items->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ import_another_theme_vb->add_child(import_other_theme_items);
+
+ import_tc->add_child(import_another_theme_vb);
+ import_tc->set_tab_title(2, TTR("Another Theme"));
+ import_other_theme_items->connect("items_imported", callable_mp(this, &ThemeItemEditorDialog::_update_edit_types));
+
+ confirm_closing_dialog = memnew(ConfirmationDialog);
+ confirm_closing_dialog->set_autowrap(true);
+ add_child(confirm_closing_dialog);
+ confirm_closing_dialog->connect("confirmed", callable_mp(this, &ThemeItemEditorDialog::_close_dialog));
}
void ThemeEditor::edit(const Ref<Theme> &p_theme) {
@@ -742,299 +1928,8 @@ void ThemeEditor::_refresh_interval() {
_propagate_redraw(main_container);
}
-struct _TECategory {
- template <class T>
- struct RefItem {
- Ref<T> item;
- StringName name;
- bool operator<(const RefItem<T> &p) const { return item->get_instance_id() < p.item->get_instance_id(); }
- };
-
- template <class T>
- struct Item {
- T item;
- String name;
- bool operator<(const Item<T> &p) const { return name < p.name; }
- };
-
- Set<RefItem<StyleBox>> stylebox_items;
- Set<RefItem<Font>> font_items;
- Set<Item<int>> font_size_items;
- Set<RefItem<Texture2D>> icon_items;
-
- Set<Item<Color>> color_items;
- Set<Item<int>> constant_items;
-};
-
-void ThemeEditor::_save_template_cbk(String fname) {
- String filename = file_dialog->get_current_path();
-
- Map<String, _TECategory> categories;
-
- // Fill types.
- List<StringName> type_list;
- Theme::get_default()->get_type_list(&type_list);
- for (List<StringName>::Element *E = type_list.front(); E; E = E->next()) {
- categories.insert(E->get(), _TECategory());
- }
-
- // Fill default theme.
- for (Map<String, _TECategory>::Element *E = categories.front(); E; E = E->next()) {
- _TECategory &tc = E->get();
-
- List<StringName> stylebox_list;
- Theme::get_default()->get_stylebox_list(E->key(), &stylebox_list);
- for (List<StringName>::Element *F = stylebox_list.front(); F; F = F->next()) {
- _TECategory::RefItem<StyleBox> it;
- it.name = F->get();
- it.item = Theme::get_default()->get_stylebox(F->get(), E->key());
- tc.stylebox_items.insert(it);
- }
-
- List<StringName> font_list;
- Theme::get_default()->get_font_list(E->key(), &font_list);
- for (List<StringName>::Element *F = font_list.front(); F; F = F->next()) {
- _TECategory::RefItem<Font> it;
- it.name = F->get();
- it.item = Theme::get_default()->get_font(F->get(), E->key());
- tc.font_items.insert(it);
- }
-
- List<StringName> font_size_list;
- Theme::get_default()->get_font_size_list(E->key(), &font_list);
- for (List<StringName>::Element *F = font_size_list.front(); F; F = F->next()) {
- _TECategory::Item<int> it;
- it.name = F->get();
- it.item = Theme::get_default()->get_font_size(F->get(), E->key());
- tc.font_size_items.insert(it);
- }
-
- List<StringName> icon_list;
- Theme::get_default()->get_icon_list(E->key(), &icon_list);
- for (List<StringName>::Element *F = icon_list.front(); F; F = F->next()) {
- _TECategory::RefItem<Texture2D> it;
- it.name = F->get();
- it.item = Theme::get_default()->get_icon(F->get(), E->key());
- tc.icon_items.insert(it);
- }
-
- List<StringName> color_list;
- Theme::get_default()->get_color_list(E->key(), &color_list);
- for (List<StringName>::Element *F = color_list.front(); F; F = F->next()) {
- _TECategory::Item<Color> it;
- it.name = F->get();
- it.item = Theme::get_default()->get_color(F->get(), E->key());
- tc.color_items.insert(it);
- }
-
- List<StringName> constant_list;
- Theme::get_default()->get_constant_list(E->key(), &constant_list);
- for (List<StringName>::Element *F = constant_list.front(); F; F = F->next()) {
- _TECategory::Item<int> it;
- it.name = F->get();
- it.item = Theme::get_default()->get_constant(F->get(), E->key());
- tc.constant_items.insert(it);
- }
- }
-
- FileAccess *file = FileAccess::open(filename, FileAccess::WRITE);
-
- ERR_FAIL_COND_MSG(!file, "Can't save theme to file '" + filename + "'.");
-
- file->store_line("; ******************* ");
- file->store_line("; Template Theme File ");
- file->store_line("; ******************* ");
- file->store_line("; ");
- file->store_line("; Theme Syntax: ");
- file->store_line("; ------------- ");
- file->store_line("; ");
- file->store_line("; Must be placed in section [theme]");
- file->store_line("; ");
- file->store_line("; Type.item = [value] ");
- file->store_line("; ");
- file->store_line("; [value] examples:");
- file->store_line("; ");
- file->store_line("; Type.item = 6 ; numeric constant. ");
- file->store_line("; Type.item = #FF00FF ; HTML color (magenta).");
- file->store_line("; Type.item = #FF00FF55 ; HTML color (magenta with alpha 0x55).");
- file->store_line("; Type.item = icon(image.png) ; icon in a png file (relative to theme file).");
- file->store_line("; Type.item = font(font.xres) ; font in a resource (relative to theme file).");
- file->store_line("; Type.item = sbox(stylebox.xres) ; stylebox in a resource (relative to theme file).");
- file->store_line("; Type.item = sboxf(2,#FF00FF) ; flat stylebox with margin 2.");
- file->store_line("; Type.item = sboxf(2,#FF00FF,#FFFFFF) ; flat stylebox with margin 2 and border.");
- file->store_line("; Type.item = sboxf(2,#FF00FF,#FFFFFF,#000000) ; flat stylebox with margin 2, light & dark borders.");
- file->store_line("; Type.item = sboxt(base.png,2,2,2,2) ; textured stylebox with 3x3 stretch and stretch margins.");
- file->store_line("; -Additionally, 4 extra integers can be added to sboxf and sboxt to specify custom padding of contents:");
- file->store_line("; Type.item = sboxt(base.png,2,2,2,2,5,4,2,4) ;");
- file->store_line("; -Order for all is always left, top, right, bottom.");
- file->store_line("; ");
- file->store_line("; Special values:");
- file->store_line("; Type.item = default ; use the value in the default theme (must exist there).");
- file->store_line("; Type.item = @somebutton_color ; reference to a library value previously defined.");
- file->store_line("; ");
- file->store_line("; Library Syntax: ");
- file->store_line("; --------------- ");
- file->store_line("; ");
- file->store_line("; Must be placed in section [library], but usage is optional.");
- file->store_line("; ");
- file->store_line("; item = [value] ; same as Theme, but assign to library.");
- file->store_line("; ");
- file->store_line("; examples:");
- file->store_line("; ");
- file->store_line("; [library]");
- file->store_line("; ");
- file->store_line("; default_button_color = #FF00FF");
- file->store_line("; ");
- file->store_line("; [theme]");
- file->store_line("; ");
- file->store_line("; Button.color = @default_button_color ; used reference.");
- file->store_line("; ");
- file->store_line("; ******************* ");
- file->store_line("; ");
- file->store_line("; Template Generated Using: " + String(VERSION_FULL_BUILD));
- file->store_line("; ");
- file->store_line("; ");
- file->store_line("");
- file->store_line("[library]");
- file->store_line("");
- file->store_line("; place library stuff here");
- file->store_line("");
- file->store_line("[theme]");
- file->store_line("");
- file->store_line("");
-
- // Write default theme.
- for (Map<String, _TECategory>::Element *E = categories.front(); E; E = E->next()) {
- _TECategory &tc = E->get();
-
- String underline = "; ";
- for (int i = 0; i < E->key().length(); i++) {
- underline += "*";
- }
-
- file->store_line("");
- file->store_line(underline);
- file->store_line("; " + E->key());
- file->store_line(underline);
-
- if (tc.stylebox_items.size()) {
- file->store_line("\n; StyleBox Items:\n");
- }
-
- for (Set<_TECategory::RefItem<StyleBox>>::Element *F = tc.stylebox_items.front(); F; F = F->next()) {
- file->store_line(E->key() + "." + F->get().name + " = default");
- }
-
- if (tc.font_items.size()) {
- file->store_line("\n; Font Items:\n");
- }
-
- for (Set<_TECategory::RefItem<Font>>::Element *F = tc.font_items.front(); F; F = F->next()) {
- file->store_line(E->key() + "." + F->get().name + " = default");
- }
-
- if (tc.font_size_items.size()) {
- file->store_line("\n; Font Size Items:\n");
- }
-
- for (Set<_TECategory::Item<int>>::Element *F = tc.font_size_items.front(); F; F = F->next()) {
- file->store_line(E->key() + "." + F->get().name + " = default");
- }
-
- if (tc.icon_items.size()) {
- file->store_line("\n; Icon Items:\n");
- }
-
- for (Set<_TECategory::RefItem<Texture2D>>::Element *F = tc.icon_items.front(); F; F = F->next()) {
- file->store_line(E->key() + "." + F->get().name + " = default");
- }
-
- if (tc.color_items.size()) {
- file->store_line("\n; Color Items:\n");
- }
-
- for (Set<_TECategory::Item<Color>>::Element *F = tc.color_items.front(); F; F = F->next()) {
- file->store_line(E->key() + "." + F->get().name + " = default");
- }
-
- if (tc.constant_items.size()) {
- file->store_line("\n; Constant Items:\n");
- }
-
- for (Set<_TECategory::Item<int>>::Element *F = tc.constant_items.front(); F; F = F->next()) {
- file->store_line(E->key() + "." + F->get().name + " = default");
- }
- }
-
- file->close();
- memdelete(file);
-}
-
-void ThemeEditor::_theme_create_menu_cbk(int p_option) {
- bool import = (p_option == POPUP_IMPORT_EDITOR_THEME);
-
- Ref<Theme> base_theme;
-
- if (p_option == POPUP_CREATE_EMPTY) {
- base_theme = Theme::get_default();
- } else {
- base_theme = EditorNode::get_singleton()->get_theme_base()->get_theme();
- }
-
- {
- List<StringName> types;
- base_theme->get_type_list(&types);
-
- for (List<StringName>::Element *T = types.front(); T; T = T->next()) {
- StringName type = T->get();
-
- List<StringName> icons;
- base_theme->get_icon_list(type, &icons);
-
- for (List<StringName>::Element *E = icons.front(); E; E = E->next()) {
- theme->set_icon(E->get(), type, import ? base_theme->get_icon(E->get(), type) : Ref<Texture2D>());
- }
-
- List<StringName> styleboxs;
- base_theme->get_stylebox_list(type, &styleboxs);
-
- for (List<StringName>::Element *E = styleboxs.front(); E; E = E->next()) {
- theme->set_stylebox(E->get(), type, import ? base_theme->get_stylebox(E->get(), type) : Ref<StyleBox>());
- }
-
- List<StringName> fonts;
- base_theme->get_font_list(type, &fonts);
-
- for (List<StringName>::Element *E = fonts.front(); E; E = E->next()) {
- theme->set_font(E->get(), type, Ref<Font>());
- }
-
- List<StringName> font_sizes;
- base_theme->get_font_size_list(type, &font_sizes);
-
- for (List<StringName>::Element *E = font_sizes.front(); E; E = E->next()) {
- theme->set_font_size(E->get(), type, base_theme->get_font_size(E->get(), type));
- }
-
- List<StringName> colors;
- base_theme->get_color_list(type, &colors);
-
- for (List<StringName>::Element *E = colors.front(); E; E = E->next()) {
- theme->set_color(E->get(), type, import ? base_theme->get_color(E->get(), type) : Color());
- }
-
- List<StringName> constants;
- base_theme->get_constant_list(type, &constants);
-
- for (List<StringName>::Element *E = constants.front(); E; E = E->next()) {
- theme->set_constant(E->get(), type, base_theme->get_constant(E->get(), type));
- }
- }
- }
-}
-
void ThemeEditor::_theme_edit_button_cbk() {
- theme_edit_dialog->popup_centered(Size2(800, 640) * EDSCALE);
+ theme_edit_dialog->popup_centered(Size2(850, 760) * EDSCALE);
}
void ThemeEditor::_notification(int p_what) {
@@ -1059,19 +1954,9 @@ ThemeEditor::ThemeEditor() {
top_menu->add_child(memnew(Label(TTR("Preview:"))));
top_menu->add_spacer(false);
- theme_create_menu = memnew(MenuButton);
- theme_create_menu->set_text(TTR("Create Theme..."));
- theme_create_menu->set_tooltip(TTR("Create a new Theme."));
- theme_create_menu->get_popup()->add_item(TTR("Empty Template"), POPUP_CREATE_EMPTY);
- theme_create_menu->get_popup()->add_separator();
- theme_create_menu->get_popup()->add_item(TTR("Empty Editor Template"), POPUP_CREATE_EDITOR_EMPTY);
- theme_create_menu->get_popup()->add_item(TTR("From Current Editor Theme"), POPUP_IMPORT_EDITOR_THEME);
- top_menu->add_child(theme_create_menu);
- theme_create_menu->get_popup()->connect("id_pressed", callable_mp(this, &ThemeEditor::_theme_create_menu_cbk));
-
theme_edit_button = memnew(Button);
- theme_edit_button->set_text(TTR("Edit Theme Items"));
- theme_edit_button->set_tooltip(TTR("Customize Theme items."));
+ theme_edit_button->set_text(TTR("Manage Items"));
+ theme_edit_button->set_tooltip(TTR("Add, remove, organize and import Theme items."));
theme_edit_button->set_flat(true);
theme_edit_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_edit_button_cbk));
top_menu->add_child(theme_edit_button);
@@ -1260,11 +2145,6 @@ ThemeEditor::ThemeEditor() {
theme_edit_dialog = memnew(ThemeItemEditorDialog);
theme_edit_dialog->hide();
add_child(theme_edit_dialog);
-
- file_dialog = memnew(EditorFileDialog);
- file_dialog->add_filter("*.theme ; " + TTR("Theme File"));
- add_child(file_dialog);
- file_dialog->connect("file_selected", callable_mp(this, &ThemeEditor::_save_template_cbk));
}
void ThemeEditorPlugin::edit(Object *p_node) {
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index 0a840aecd7..5f4de68f45 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -41,14 +41,148 @@
#include "editor/editor_node.h"
+class ThemeItemImportTree : public VBoxContainer {
+ GDCLASS(ThemeItemImportTree, VBoxContainer);
+
+ Ref<Theme> edited_theme;
+ Ref<Theme> base_theme;
+
+ struct ThemeItem {
+ String type_name;
+ Theme::DataType data_type;
+ String item_name;
+
+ bool operator<(const ThemeItem &p_item) const {
+ if (type_name == p_item.type_name && data_type == p_item.data_type) {
+ return item_name < p_item.item_name;
+ }
+ if (type_name == p_item.type_name) {
+ return data_type < p_item.data_type;
+ }
+ return type_name < p_item.type_name;
+ }
+ };
+
+ enum ItemCheckedState {
+ SELECT_IMPORT_DEFINITION,
+ SELECT_IMPORT_FULL,
+ };
+
+ Map<ThemeItem, ItemCheckedState> selected_items;
+
+ LineEdit *import_items_filter;
+
+ Tree *import_items_tree;
+ List<TreeItem *> tree_color_items;
+ List<TreeItem *> tree_constant_items;
+ List<TreeItem *> tree_font_items;
+ List<TreeItem *> tree_font_size_items;
+ List<TreeItem *> tree_icon_items;
+ List<TreeItem *> tree_stylebox_items;
+
+ bool updating_tree = false;
+
+ enum ItemActionFlag {
+ IMPORT_ITEM = 1,
+ IMPORT_ITEM_DATA = 2,
+ };
+
+ TextureRect *select_colors_icon;
+ Label *select_colors_label;
+ Button *select_all_colors_button;
+ Button *select_full_colors_button;
+ Button *deselect_all_colors_button;
+ Label *total_selected_colors_label;
+
+ TextureRect *select_constants_icon;
+ Label *select_constants_label;
+ Button *select_all_constants_button;
+ Button *select_full_constants_button;
+ Button *deselect_all_constants_button;
+ Label *total_selected_constants_label;
+
+ TextureRect *select_fonts_icon;
+ Label *select_fonts_label;
+ Button *select_all_fonts_button;
+ Button *select_full_fonts_button;
+ Button *deselect_all_fonts_button;
+ Label *total_selected_fonts_label;
+
+ TextureRect *select_font_sizes_icon;
+ Label *select_font_sizes_label;
+ Button *select_all_font_sizes_button;
+ Button *select_full_font_sizes_button;
+ Button *deselect_all_font_sizes_button;
+ Label *total_selected_font_sizes_label;
+
+ TextureRect *select_icons_icon;
+ Label *select_icons_label;
+ Button *select_all_icons_button;
+ Button *select_full_icons_button;
+ Button *deselect_all_icons_button;
+ Label *total_selected_icons_label;
+
+ TextureRect *select_styleboxes_icon;
+ Label *select_styleboxes_label;
+ Button *select_all_styleboxes_button;
+ Button *select_full_styleboxes_button;
+ Button *deselect_all_styleboxes_button;
+ Label *total_selected_styleboxes_label;
+
+ HBoxContainer *select_icons_warning_hb;
+ TextureRect *select_icons_warning_icon;
+ Label *select_icons_warning;
+
+ Button *import_collapse_types_button;
+ Button *import_expand_types_button;
+ Button *import_select_all_button;
+ Button *import_select_full_button;
+ Button *import_deselect_all_button;
+
+ void _update_items_tree();
+ void _toggle_type_items(bool p_collapse);
+ void _filter_text_changed(const String &p_value);
+
+ void _store_selected_item(TreeItem *p_tree_item);
+ void _restore_selected_item(TreeItem *p_tree_item);
+ void _update_total_selected(Theme::DataType p_data_type);
+
+ void _tree_item_edited();
+ void _select_all_subitems(TreeItem *p_root_item, bool p_select_with_data);
+ void _deselect_all_subitems(TreeItem *p_root_item, bool p_deselect_completely);
+ void _update_parent_items(TreeItem *p_root_item);
+
+ void _select_all_items_pressed();
+ void _select_full_items_pressed();
+ void _deselect_all_items_pressed();
+
+ void _select_all_data_type_pressed(int p_data_type);
+ void _select_full_data_type_pressed(int p_data_type);
+ void _deselect_all_data_type_pressed(int p_data_type);
+
+ void _import_selected();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_edited_theme(const Ref<Theme> &p_theme);
+ void set_base_theme(const Ref<Theme> &p_theme);
+ void reset_item_tree();
+
+ bool has_selected_items() const;
+
+ ThemeItemImportTree();
+};
+
class ThemeItemEditorDialog : public AcceptDialog {
GDCLASS(ThemeItemEditorDialog, AcceptDialog);
Ref<Theme> edited_theme;
ItemList *edit_type_list;
- OptionButton *edit_add_class_options;
- LineEdit *edit_add_custom_value;
+ LineEdit *edit_add_type_value;
String edited_item_type;
Button *edit_items_add_color;
@@ -83,6 +217,19 @@ class ThemeItemEditorDialog : public AcceptDialog {
String edit_item_old_name;
Theme::DataType edit_item_data_type = Theme::DATA_TYPE_MAX;
+ ThemeItemImportTree *import_default_theme_items;
+ ThemeItemImportTree *import_editor_theme_items;
+ ThemeItemImportTree *import_other_theme_items;
+
+ LineEdit *import_another_theme_value;
+ Button *import_another_theme_button;
+ EditorFileDialog *import_another_theme_dialog;
+
+ ConfirmationDialog *confirm_closing_dialog;
+
+ void ok_pressed() override;
+ void _close_dialog();
+
void _dialog_about_to_show();
void _update_edit_types();
void _edited_type_selected(int p_item_idx);
@@ -90,8 +237,7 @@ class ThemeItemEditorDialog : public AcceptDialog {
void _update_edit_item_tree(String p_item_type);
void _item_tree_button_pressed(Object *p_item, int p_column, int p_id);
- void _add_class_type_items();
- void _add_custom_type();
+ void _add_theme_type();
void _add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type);
void _remove_data_type_items(Theme::DataType p_data_type, String p_item_type);
void _remove_class_items();
@@ -103,6 +249,9 @@ class ThemeItemEditorDialog : public AcceptDialog {
void _confirm_edit_theme_item();
void _edit_theme_item_gui_input(const Ref<InputEvent> &p_event);
+ void _open_select_another_theme();
+ void _select_another_theme_cbk(const String &p_path);
+
protected:
void _notification(int p_what);
@@ -115,29 +264,18 @@ public:
class ThemeEditor : public VBoxContainer {
GDCLASS(ThemeEditor, VBoxContainer);
- Panel *main_panel;
- MarginContainer *main_container;
Ref<Theme> theme;
- EditorFileDialog *file_dialog;
-
double time_left = 0;
Button *theme_edit_button;
- MenuButton *theme_create_menu;
ThemeItemEditorDialog *theme_edit_dialog;
- enum CreatePopupMode {
- POPUP_CREATE_EMPTY,
- POPUP_CREATE_EDITOR_EMPTY,
- POPUP_IMPORT_EDITOR_THEME,
- };
-
+ Panel *main_panel;
+ MarginContainer *main_container;
Tree *test_tree;
- void _save_template_cbk(String fname);
void _theme_edit_button_cbk();
- void _theme_create_menu_cbk(int p_option);
void _propagate_redraw(Control *p_at);
void _refresh_interval();
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
deleted file mode 100644
index 1d6ff92e0c..0000000000
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ /dev/null
@@ -1,2335 +0,0 @@
-/*************************************************************************/
-/* tile_map_editor_plugin.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "tile_map_editor_plugin.h"
-
-#include "canvas_item_editor_plugin.h"
-#include "core/input/input.h"
-#include "core/math/math_funcs.h"
-#include "core/os/keyboard.h"
-#include "editor/editor_scale.h"
-#include "editor/editor_settings.h"
-#include "scene/gui/split_container.h"
-
-void TileMapEditor::_node_removed(Node *p_node) {
- if (p_node == node && node) {
- Callable callable_tileset_settings_changed = callable_mp(this, &TileMapEditor::_tileset_settings_changed);
-
- if (node->is_connected("settings_changed", callable_tileset_settings_changed)) {
- // Fixes #44824, which describes a situation where you can reselect a TileMap node without first de-selecting it when switching scenes.
- node->disconnect("settings_changed", callable_tileset_settings_changed);
- }
- node = nullptr;
- }
-}
-
-void TileMapEditor::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_PROCESS: {
- if (bucket_queue.size()) {
- CanvasItemEditor::get_singleton()->update_viewport();
- }
-
- } break;
-
- case NOTIFICATION_ENTER_TREE: {
- get_tree()->connect("node_removed", callable_mp(this, &TileMapEditor::_node_removed));
- [[fallthrough]];
- }
-
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- if (is_visible_in_tree()) {
- _update_palette();
- }
-
- paint_button->set_icon(get_theme_icon("Edit", "EditorIcons"));
- line_button->set_icon(get_theme_icon("CurveLinear", "EditorIcons"));
- rectangle_button->set_icon(get_theme_icon("Rectangle", "EditorIcons"));
- bucket_fill_button->set_icon(get_theme_icon("Bucket", "EditorIcons"));
- picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons"));
- select_button->set_icon(get_theme_icon("ActionCopy", "EditorIcons"));
-
- rotate_left_button->set_icon(get_theme_icon("RotateLeft", "EditorIcons"));
- rotate_right_button->set_icon(get_theme_icon("RotateRight", "EditorIcons"));
- flip_horizontal_button->set_icon(get_theme_icon("MirrorX", "EditorIcons"));
- flip_vertical_button->set_icon(get_theme_icon("MirrorY", "EditorIcons"));
- clear_transform_button->set_icon(get_theme_icon("Clear", "EditorIcons"));
-
- search_box->set_right_icon(get_theme_icon("Search", "EditorIcons"));
- search_box->set_clear_button_enabled(true);
-
- PopupMenu *p = options->get_popup();
- p->set_item_icon(p->get_item_index(OPTION_CUT), get_theme_icon("ActionCut", "EditorIcons"));
- p->set_item_icon(p->get_item_index(OPTION_COPY), get_theme_icon("Duplicate", "EditorIcons"));
- p->set_item_icon(p->get_item_index(OPTION_ERASE_SELECTION), get_theme_icon("Remove", "EditorIcons"));
-
- } break;
-
- case NOTIFICATION_EXIT_TREE: {
- get_tree()->disconnect("node_removed", callable_mp(this, &TileMapEditor::_node_removed));
- } break;
-
- case NOTIFICATION_APPLICATION_FOCUS_OUT: {
- if (tool == TOOL_PAINTING) {
- Vector<int> ids = get_selected_tiles();
-
- if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- _set_cell(over_tile, ids, flip_h, flip_v, transpose);
- _finish_undo();
-
- paint_undo.clear();
- }
-
- tool = TOOL_NONE;
- _update_button_tool();
- }
-
- // set flag to ignore over_tile on refocus
- refocus_over_tile = true;
- } break;
- }
-}
-
-void TileMapEditor::_update_button_tool() {
- Button *tb[6] = { paint_button, line_button, rectangle_button, bucket_fill_button, picker_button, select_button };
-
- // Unpress all buttons
- for (int i = 0; i < 6; i++) {
- tb[i]->set_pressed(false);
- }
-
- // Press the good button
- switch (tool) {
- case TOOL_NONE:
- case TOOL_PAINTING: {
- paint_button->set_pressed(true);
- } break;
- case TOOL_LINE_PAINT: {
- line_button->set_pressed(true);
- } break;
- case TOOL_RECTANGLE_PAINT: {
- rectangle_button->set_pressed(true);
- } break;
- case TOOL_BUCKET: {
- bucket_fill_button->set_pressed(true);
- } break;
- case TOOL_PICKING: {
- picker_button->set_pressed(true);
- } break;
- case TOOL_SELECTING: {
- select_button->set_pressed(true);
- } break;
- default:
- break;
- }
-
- if (tool != TOOL_PICKING) {
- last_tool = tool;
- }
-}
-
-void TileMapEditor::_button_tool_select(int p_tool) {
- tool = (Tool)p_tool;
- _update_button_tool();
- switch (tool) {
- case TOOL_SELECTING: {
- selection_active = false;
- } break;
- default:
- break;
- }
- CanvasItemEditor::get_singleton()->update_viewport();
-}
-
-void TileMapEditor::_menu_option(int p_option) {
- switch (p_option) {
- case OPTION_COPY: {
- _update_copydata();
-
- if (selection_active) {
- tool = TOOL_PASTING;
-
- CanvasItemEditor::get_singleton()->update_viewport();
- }
- } break;
- case OPTION_ERASE_SELECTION: {
- if (!selection_active) {
- return;
- }
-
- _start_undo(TTR("Erase Selection"));
- _erase_selection();
- _finish_undo();
-
- selection_active = false;
- copydata.clear();
-
- CanvasItemEditor::get_singleton()->update_viewport();
- } break;
- case OPTION_FIX_INVALID: {
- undo_redo->create_action(TTR("Fix Invalid Tiles"));
- undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
- node->fix_invalid_tiles();
- undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
- undo_redo->commit_action();
-
- } break;
- case OPTION_CUT: {
- if (selection_active) {
- _update_copydata();
-
- _start_undo(TTR("Cut Selection"));
- _erase_selection();
- _finish_undo();
-
- selection_active = false;
-
- tool = TOOL_PASTING;
-
- CanvasItemEditor::get_singleton()->update_viewport();
- }
- } break;
- }
- _update_button_tool();
-}
-
-void TileMapEditor::_palette_selected(int index) {
- _update_palette();
-}
-
-void TileMapEditor::_palette_multi_selected(int index, bool selected) {
- _update_palette();
-}
-
-void TileMapEditor::_palette_input(const Ref<InputEvent> &p_event) {
- const Ref<InputEventMouseButton> mb = p_event;
-
- // Zoom in/out using Ctrl + mouse wheel.
- if (mb.is_valid() && mb->is_pressed() && mb->get_command()) {
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
- size_slider->set_value(size_slider->get_value() + 0.2);
- }
-
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
- size_slider->set_value(size_slider->get_value() - 0.2);
- }
- }
-}
-
-void TileMapEditor::_canvas_mouse_enter() {
- mouse_over = true;
- CanvasItemEditor::get_singleton()->update_viewport();
-}
-
-void TileMapEditor::_canvas_mouse_exit() {
- mouse_over = false;
- CanvasItemEditor::get_singleton()->update_viewport();
-}
-
-Vector<int> TileMapEditor::get_selected_tiles() const {
- Vector<int> items = palette->get_selected_items();
-
- if (items.size() == 0) {
- items.push_back(TileMap::INVALID_CELL);
- return items;
- }
-
- for (int i = items.size() - 1; i >= 0; i--) {
- items.write[i] = palette->get_item_metadata(items[i]);
- }
- return items;
-}
-
-void TileMapEditor::set_selected_tiles(Vector<int> p_tiles) {
- palette->deselect_all();
-
- for (int i = p_tiles.size() - 1; i >= 0; i--) {
- int idx = palette->find_metadata(p_tiles[i]);
-
- if (idx >= 0) {
- palette->select(idx, false);
- }
- }
-
- palette->ensure_current_is_visible();
-}
-
-Dictionary TileMapEditor::_create_cell_dictionary(int tile, bool flip_x, bool flip_y, bool transpose, Vector2 autotile_coord) {
- Dictionary cell;
-
- cell["id"] = tile;
- cell["flip_h"] = flip_x;
- cell["flip_y"] = flip_y;
- cell["transpose"] = transpose;
- cell["auto_coord"] = autotile_coord;
-
- return cell;
-}
-
-void TileMapEditor::_create_set_cell_undo_redo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new) {
- Dictionary cell_old = _create_cell_dictionary(p_cell_old.idx, p_cell_old.xf, p_cell_old.yf, p_cell_old.tr, p_cell_old.ac);
- Dictionary cell_new = _create_cell_dictionary(p_cell_new.idx, p_cell_new.xf, p_cell_new.yf, p_cell_new.tr, p_cell_new.ac);
-
- undo_redo->add_undo_method(node, "_set_celld", p_vec, cell_old);
- undo_redo->add_do_method(node, "_set_celld", p_vec, cell_new);
-}
-
-void TileMapEditor::_start_undo(const String &p_action) {
- undo_data.clear();
- undo_redo->create_action(p_action);
-}
-
-void TileMapEditor::_finish_undo() {
- if (undo_data.size()) {
- for (Map<Point2i, CellOp>::Element *E = undo_data.front(); E; E = E->next()) {
- _create_set_cell_undo_redo(E->key(), E->get(), _get_op_from_cell(E->key()));
- }
-
- undo_data.clear();
- }
-
- undo_redo->commit_action();
-}
-
-void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i &p_autotile_coord) {
- ERR_FAIL_COND(!node);
-
- if (p_values.size() == 0) {
- return;
- }
-
- int p_value = p_values[Math::rand() % p_values.size()];
- int prev_val = node->get_cell(p_pos.x, p_pos.y);
-
- bool prev_flip_h = node->is_cell_x_flipped(p_pos.x, p_pos.y);
- bool prev_flip_v = node->is_cell_y_flipped(p_pos.x, p_pos.y);
- bool prev_transpose = node->is_cell_transposed(p_pos.x, p_pos.y);
- Vector2 prev_position = node->get_cell_autotile_coord(p_pos.x, p_pos.y);
-
- Vector2 position;
- int current = manual_palette->get_current();
- if (current != -1) {
- if (tool != TOOL_PASTING) {
- position = manual_palette->get_item_metadata(current);
- } else {
- position = p_autotile_coord;
- }
- } else {
- // If there is no manual tile selected, that either means that
- // autotiling is enabled, or the given tile is not autotiling. Either
- // way, the coordinate of the tile does not matter, so assigning it to
- // the coordinate of the existing tile works fine.
- position = prev_position;
- }
-
- if (p_value == prev_val && p_flip_h == prev_flip_h && p_flip_v == prev_flip_v && p_transpose == prev_transpose && prev_position == position) {
- return; // Check that it's actually different.
- }
-
- for (int y = p_pos.y - 1; y <= p_pos.y + 1; y++) {
- for (int x = p_pos.x - 1; x <= p_pos.x + 1; x++) {
- Point2i p = Point2i(x, y);
- if (!undo_data.has(p)) {
- undo_data[p] = _get_op_from_cell(p);
- }
- }
- }
-
- node->_set_celld(p_pos, _create_cell_dictionary(p_value, p_flip_h, p_flip_v, p_transpose, p_autotile_coord));
-
- if (tool == TOOL_PASTING) {
- return;
- }
-
- if (manual_autotile || (p_value != -1 && node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE)) {
- if (current != -1) {
- node->set_cell_autotile_coord(p_pos.x, p_pos.y, position);
- } else if (node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE && priority_atlastile) {
- // BIND_CENTER is used to indicate that bitmask should not update for this tile cell.
- node->get_tileset()->autotile_set_bitmask(p_value, Vector2(p_pos.x, p_pos.y), TileSet::BIND_CENTER);
- node->update_cell_bitmask(p_pos.x, p_pos.y);
- }
- } else {
- node->update_bitmask_area(Point2(p_pos));
- }
-}
-
-void TileMapEditor::_manual_toggled(bool p_enabled) {
- manual_autotile = p_enabled;
- _update_palette();
-}
-
-void TileMapEditor::_priority_toggled(bool p_enabled) {
- priority_atlastile = p_enabled;
- _update_palette();
-}
-
-void TileMapEditor::_text_entered(const String &p_text) {
- canvas_item_editor_viewport->grab_focus();
-}
-
-void TileMapEditor::_text_changed(const String &p_text) {
- _update_palette();
-}
-
-void TileMapEditor::_sbox_input(const Ref<InputEvent> &p_ie) {
- Ref<InputEventKey> k = p_ie;
-
- if (k.is_valid() && (k->get_keycode() == KEY_UP ||
- k->get_keycode() == KEY_DOWN ||
- k->get_keycode() == KEY_PAGEUP ||
- k->get_keycode() == KEY_PAGEDOWN)) {
- palette->call("_gui_input", k);
- search_box->accept_event();
- }
-}
-
-// Implementation detail of TileMapEditor::_update_palette();
-// In modern C++ this could have been inside its body.
-namespace {
-struct _PaletteEntry {
- int id = 0;
- String name;
-
- bool operator<(const _PaletteEntry &p_rhs) const {
- // Natural no case comparison will compare strings based on CharType
- // order (except digits) and on numbers that start on the same position.
- return name.naturalnocasecmp_to(p_rhs.name) < 0;
- }
-};
-} // namespace
-
-void TileMapEditor::_update_palette() {
- if (!node) {
- return;
- }
-
- // Update the clear button.
- clear_transform_button->set_disabled(!flip_h && !flip_v && !transpose);
-
- // Update the palette.
- Vector<int> selected = get_selected_tiles();
- int selected_single = palette->get_current();
- int selected_manual = manual_palette->get_current();
- palette->clear();
- manual_palette->clear();
- manual_palette->hide();
-
- Ref<TileSet> tileset = node->get_tileset();
- if (tileset.is_null()) {
- search_box->set_text("");
- search_box->set_editable(false);
- info_message->show();
- return;
- }
-
- search_box->set_editable(true);
- info_message->hide();
-
- List<int> tiles;
- tileset->get_tile_list(&tiles);
- if (tiles.is_empty()) {
- return;
- }
-
- float min_size = EDITOR_DEF("editors/tile_map/preview_size", 64);
- min_size *= EDSCALE;
- int hseparation = EDITOR_DEF("editors/tile_map/palette_item_hseparation", 8);
- bool show_tile_names = bool(EDITOR_DEF("editors/tile_map/show_tile_names", true));
- bool show_tile_ids = bool(EDITOR_DEF("editors/tile_map/show_tile_ids", false));
- bool sort_by_name = bool(EDITOR_DEF("editors/tile_map/sort_tiles_by_name", true));
-
- palette->add_theme_constant_override("hseparation", hseparation * EDSCALE);
-
- palette->set_fixed_icon_size(Size2(min_size, min_size));
- palette->set_fixed_column_width(min_size * MAX(size_slider->get_value(), 1));
- palette->set_same_column_width(true);
- manual_palette->set_fixed_icon_size(Size2(min_size, min_size));
- manual_palette->set_same_column_width(true);
-
- String filter = search_box->get_text().strip_edges();
-
- Vector<_PaletteEntry> entries;
-
- for (List<int>::Element *E = tiles.front(); E; E = E->next()) {
- String name = tileset->tile_get_name(E->get());
-
- if (name != "") {
- if (show_tile_ids) {
- if (sort_by_name) {
- name = name + " - " + itos(E->get());
- } else {
- name = itos(E->get()) + " - " + name;
- }
- }
- } else {
- name = "#" + itos(E->get());
- }
-
- if (filter != "" && !filter.is_subsequence_ofi(name)) {
- continue;
- }
-
- const _PaletteEntry entry = { E->get(), name };
- entries.push_back(entry);
- }
-
- if (sort_by_name) {
- entries.sort();
- }
-
- for (int i = 0; i < entries.size(); i++) {
- if (show_tile_names) {
- palette->add_item(entries[i].name);
- } else {
- palette->add_item(String());
- }
-
- Ref<Texture2D> tex = tileset->tile_get_texture(entries[i].id);
-
- if (tex.is_valid()) {
- Rect2 region = tileset->tile_get_region(entries[i].id);
-
- if (tileset->tile_get_tile_mode(entries[i].id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(entries[i].id) == TileSet::ATLAS_TILE) {
- int spacing = tileset->autotile_get_spacing(entries[i].id);
- region.size = tileset->autotile_get_size(entries[i].id);
- region.position += (region.size + Vector2(spacing, spacing)) * tileset->autotile_get_icon_coordinate(entries[i].id);
- }
-
- // Transpose and flip.
- palette->set_item_icon_transposed(palette->get_item_count() - 1, transpose);
- if (flip_h) {
- region.size.x = -region.size.x;
- }
- if (flip_v) {
- region.size.y = -region.size.y;
- }
-
- // Set region.
- if (region.size != Size2()) {
- palette->set_item_icon_region(palette->get_item_count() - 1, region);
- }
-
- // Set icon.
- palette->set_item_icon(palette->get_item_count() - 1, tex);
-
- // Modulation.
- Color color = tileset->tile_get_modulate(entries[i].id);
- palette->set_item_icon_modulate(palette->get_item_count() - 1, color);
- }
-
- palette->set_item_metadata(palette->get_item_count() - 1, entries[i].id);
- }
-
- int sel_tile = selected.get(0);
- if (selected.get(0) != TileMap::INVALID_CELL) {
- set_selected_tiles(selected);
- sel_tile = selected.get(Math::rand() % selected.size());
- } else if (palette->get_item_count() > 0) {
- palette->select(0);
- sel_tile = palette->get_selected_items().get(0);
- }
-
- if (sel_tile != TileMap::INVALID_CELL && ((manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) || (!priority_atlastile && tileset->tile_get_tile_mode(sel_tile) == TileSet::ATLAS_TILE))) {
- const Map<Vector2, uint32_t> &tiles2 = tileset->autotile_get_bitmask_map(sel_tile);
-
- Vector<Vector2> entries2;
- for (const Map<Vector2, uint32_t>::Element *E = tiles2.front(); E; E = E->next()) {
- entries2.push_back(E->key());
- }
- // Sort tiles in row-major order.
- struct SwapComparator {
- _FORCE_INLINE_ bool operator()(const Vector2 &v_l, const Vector2 &v_r) const {
- return v_l.y != v_r.y ? v_l.y < v_r.y : v_l.x < v_r.x;
- }
- };
- entries2.sort_custom<SwapComparator>();
-
- Ref<Texture2D> tex = tileset->tile_get_texture(sel_tile);
- Color modulate = tileset->tile_get_modulate(sel_tile);
-
- for (int i = 0; i < entries2.size(); i++) {
- manual_palette->add_item(String());
-
- if (tex.is_valid()) {
- Rect2 region = tileset->tile_get_region(sel_tile);
- int spacing = tileset->autotile_get_spacing(sel_tile);
- region.size = tileset->autotile_get_size(sel_tile); // !!
- region.position += (region.size + Vector2(spacing, spacing)) * entries2[i];
-
- if (!region.has_no_area()) {
- manual_palette->set_item_icon_region(manual_palette->get_item_count() - 1, region);
- }
-
- manual_palette->set_item_icon(manual_palette->get_item_count() - 1, tex);
- manual_palette->set_item_icon_modulate(manual_palette->get_item_count() - 1, modulate);
- }
-
- manual_palette->set_item_metadata(manual_palette->get_item_count() - 1, entries2[i]);
- }
- }
-
- if (manual_palette->get_item_count() > 0) {
- // Only show the manual palette if at least tile exists in it.
- if (selected_manual == -1 || selected_single != palette->get_current()) {
- selected_manual = 0;
- }
- if (selected_manual < manual_palette->get_item_count()) {
- manual_palette->set_current(selected_manual);
- }
- manual_palette->show();
- }
-
- if (sel_tile != TileMap::INVALID_CELL && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) {
- manual_button->show();
- priority_button->hide();
- } else {
- manual_button->hide();
- priority_button->show();
- }
-}
-
-void TileMapEditor::_pick_tile(const Point2 &p_pos) {
- int id = node->get_cell(p_pos.x, p_pos.y);
-
- if (id == TileMap::INVALID_CELL) {
- return;
- }
-
- if (search_box->get_text() != "") {
- search_box->set_text("");
- _update_palette();
- }
-
- flip_h = node->is_cell_x_flipped(p_pos.x, p_pos.y);
- flip_v = node->is_cell_y_flipped(p_pos.x, p_pos.y);
- transpose = node->is_cell_transposed(p_pos.x, p_pos.y);
- autotile_coord = node->get_cell_autotile_coord(p_pos.x, p_pos.y);
-
- Vector<int> selected;
- selected.push_back(id);
- set_selected_tiles(selected);
- _update_palette();
-
- if ((manual_autotile && node->get_tileset()->tile_get_tile_mode(id) == TileSet::AUTO_TILE) || (!priority_atlastile && node->get_tileset()->tile_get_tile_mode(id) == TileSet::ATLAS_TILE)) {
- manual_palette->select(manual_palette->find_metadata((Point2)autotile_coord));
- }
-
- CanvasItemEditor::get_singleton()->update_viewport();
-}
-
-Vector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool erase, bool preview) {
- int prev_id = node->get_cell(p_start.x, p_start.y);
- Vector<int> ids;
- ids.push_back(TileMap::INVALID_CELL);
- if (!erase) {
- ids = get_selected_tiles();
-
- if (ids.size() == 0 || ids[0] == TileMap::INVALID_CELL) {
- return Vector<Vector2>();
- }
- } else if (prev_id == TileMap::INVALID_CELL) {
- return Vector<Vector2>();
- }
-
- // Check if the tile variation is the same
- if (ids.size() == 1 && ids[0] == prev_id) {
- int current = manual_palette->get_current();
- if (current == -1) {
- // Same ID, no variation selected, nothing to change
- return Vector<Vector2>();
- }
- Vector2 prev_autotile_coord = node->get_cell_autotile_coord(p_start.x, p_start.y);
- Vector2 autotile_coord = manual_palette->get_item_metadata(current);
- if (autotile_coord == prev_autotile_coord) {
- // Same ID and variation, nothing to change
- return Vector<Vector2>();
- }
- }
-
- Rect2i r = node->get_used_rect();
-
- int area = r.get_area();
- if (preview) {
- // Test if we can re-use the result from preview bucket fill
- bool invalidate_cache = false;
- // Area changed
- if (r != bucket_cache_rect) {
- _clear_bucket_cache();
- }
- // Cache grid is not initialized
- if (bucket_cache_visited == nullptr) {
- bucket_cache_visited = new bool[area];
- invalidate_cache = true;
- }
- // Tile ID changed or position wasn't visited by the previous fill
- const int loc = (p_start.x - r.position.x) + (p_start.y - r.position.y) * r.get_size().x;
- const bool in_range = 0 <= loc && loc < area;
- if (prev_id != bucket_cache_tile || (in_range && !bucket_cache_visited[loc])) {
- invalidate_cache = true;
- }
- if (invalidate_cache) {
- for (int i = 0; i < area; ++i) {
- bucket_cache_visited[i] = false;
- }
- bucket_cache = Vector<Vector2>();
- bucket_cache_tile = prev_id;
- bucket_cache_rect = r;
- bucket_queue.clear();
- }
- }
-
- Vector<Vector2> points;
- Vector<Vector2> non_preview_cache;
- int count = 0;
- int limit = 0;
-
- if (preview) {
- limit = 1024;
- } else {
- bucket_queue.clear();
- }
-
- bucket_queue.push_back(p_start);
-
- while (bucket_queue.size()) {
- Point2i n = bucket_queue.front()->get();
- bucket_queue.pop_front();
-
- if (!r.has_point(n)) {
- continue;
- }
-
- if (node->get_cell(n.x, n.y) == prev_id) {
- if (preview) {
- int loc = (n.x - r.position.x) + (n.y - r.position.y) * r.get_size().x;
- if (bucket_cache_visited[loc]) {
- continue;
- }
- bucket_cache_visited[loc] = true;
- bucket_cache.push_back(n);
- } else {
- if (non_preview_cache.find(n) >= 0) {
- continue;
- }
- points.push_back(n);
- non_preview_cache.push_back(n);
- }
-
- bucket_queue.push_back(Point2i(n.x, n.y + 1));
- bucket_queue.push_back(Point2i(n.x, n.y - 1));
- bucket_queue.push_back(Point2i(n.x + 1, n.y));
- bucket_queue.push_back(Point2i(n.x - 1, n.y));
- count++;
- }
-
- if (limit > 0 && count >= limit) {
- break;
- }
- }
-
- return preview ? bucket_cache : points;
-}
-
-void TileMapEditor::_fill_points(const Vector<Vector2> &p_points, const Dictionary &p_op) {
- int len = p_points.size();
- const Vector2 *pr = p_points.ptr();
-
- Vector<int> ids = p_op["id"];
- bool xf = p_op["flip_h"];
- bool yf = p_op["flip_v"];
- bool tr = p_op["transpose"];
-
- for (int i = 0; i < len; i++) {
- _set_cell(pr[i], ids, xf, yf, tr);
- node->make_bitmask_area_dirty(pr[i]);
- }
- if (!manual_autotile) {
- node->update_dirty_bitmask();
- }
-}
-
-void TileMapEditor::_erase_points(const Vector<Vector2> &p_points) {
- int len = p_points.size();
- const Vector2 *pr = p_points.ptr();
-
- for (int i = 0; i < len; i++) {
- _set_cell(pr[i], invalid_cell);
- }
-}
-
-void TileMapEditor::_select(const Point2i &p_from, const Point2i &p_to) {
- Point2i begin = p_from;
- Point2i end = p_to;
-
- if (begin.x > end.x) {
- SWAP(begin.x, end.x);
- }
- if (begin.y > end.y) {
- SWAP(begin.y, end.y);
- }
-
- rectangle.position = begin;
- rectangle.size = end - begin;
-
- CanvasItemEditor::get_singleton()->update_viewport();
-}
-
-void TileMapEditor::_erase_selection() {
- if (!selection_active) {
- return;
- }
-
- for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
- for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _set_cell(Point2i(j, i), invalid_cell, false, false, false);
- }
- }
-}
-
-void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i &p_autotile_coord, const Transform2D &p_xform) {
- Ref<Texture2D> t = node->get_tileset()->tile_get_texture(p_cell);
-
- if (t.is_null()) {
- return;
- }
-
- Vector2 tile_ofs = node->get_tileset()->tile_get_texture_offset(p_cell);
-
- Rect2 r = node->get_tileset()->tile_get_region(p_cell);
- if (node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::AUTO_TILE || node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::ATLAS_TILE) {
- Vector2 offset;
- if (tool != TOOL_PASTING) {
- int selected = manual_palette->get_current();
- if ((manual_autotile || (node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::ATLAS_TILE && !priority_atlastile)) && selected != -1) {
- offset = manual_palette->get_item_metadata(selected);
- } else {
- offset = node->get_tileset()->autotile_get_icon_coordinate(p_cell);
- }
- } else {
- offset = p_autotile_coord;
- }
-
- int spacing = node->get_tileset()->autotile_get_spacing(p_cell);
- r.size = node->get_tileset()->autotile_get_size(p_cell);
- r.position += (r.size + Vector2(spacing, spacing)) * offset;
- }
- Size2 cell_size = node->get_cell_size();
- bool centered_texture = node->is_centered_textures_enabled();
- bool compatibility_mode_enabled = node->is_compatibility_mode_enabled();
- Rect2 rect = Rect2();
- rect.position = node->map_to_world(p_point) + node->get_cell_draw_offset();
-
- if (r.has_no_area()) {
- rect.size = t->get_size();
- } else {
- rect.size = r.size;
- }
-
- if (compatibility_mode_enabled && !centered_texture) {
- if (rect.size.y > rect.size.x) {
- if ((p_flip_h && (p_flip_v || p_transpose)) || (p_flip_v && !p_transpose)) {
- tile_ofs.y += rect.size.y - rect.size.x;
- }
- } else if (rect.size.y < rect.size.x) {
- if ((p_flip_v && (p_flip_h || p_transpose)) || (p_flip_h && !p_transpose)) {
- tile_ofs.x += rect.size.x - rect.size.y;
- }
- }
- }
-
- if (p_transpose) {
- SWAP(tile_ofs.x, tile_ofs.y);
- if (centered_texture) {
- rect.position.x += cell_size.x / 2 - rect.size.y / 2;
- rect.position.y += cell_size.y / 2 - rect.size.x / 2;
- }
- } else if (centered_texture) {
- rect.position += cell_size / 2 - rect.size / 2;
- }
-
- if (p_flip_h) {
- rect.size.x *= -1.0;
- tile_ofs.x *= -1.0;
- }
-
- if (p_flip_v) {
- rect.size.y *= -1.0;
- tile_ofs.y *= -1.0;
- }
-
- if (compatibility_mode_enabled && !centered_texture) {
- if (node->get_tile_origin() == TileMap::TILE_ORIGIN_TOP_LEFT) {
- rect.position += tile_ofs;
- } else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_BOTTOM_LEFT) {
- rect.position += tile_ofs;
-
- if (p_transpose) {
- if (p_flip_h) {
- rect.position.x -= cell_size.x;
- } else {
- rect.position.x += cell_size.x;
- }
- } else {
- if (p_flip_v) {
- rect.position.y -= cell_size.y;
- } else {
- rect.position.y += cell_size.y;
- }
- }
-
- } else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_CENTER) {
- rect.position += tile_ofs;
-
- if (p_flip_h) {
- rect.position.x -= cell_size.x / 2;
- } else {
- rect.position.x += cell_size.x / 2;
- }
-
- if (p_flip_v) {
- rect.position.y -= cell_size.y / 2;
- } else {
- rect.position.y += cell_size.y / 2;
- }
- }
- } else {
- rect.position += tile_ofs;
- }
-
- Color modulate = node->get_tileset()->tile_get_modulate(p_cell);
- modulate.a = 0.5;
-
- Transform2D old_transform = p_viewport->get_viewport_transform();
- p_viewport->draw_set_transform_matrix(p_xform); // Take into account TileMap transformation when displaying cell
- if (r.has_no_area()) {
- p_viewport->draw_texture_rect(t, rect, false, modulate, p_transpose);
- } else {
- p_viewport->draw_texture_rect_region(t, rect, r, modulate, p_transpose);
- }
- p_viewport->draw_set_transform_matrix(old_transform);
-}
-
-void TileMapEditor::_draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i &p_autotile_coord, const Transform2D &p_xform) {
- Vector<Vector2> points = _bucket_fill(p_point, false, true);
- const Vector2 *pr = points.ptr();
- int len = points.size();
-
- for (int i = 0; i < len; ++i) {
- _draw_cell(p_viewport, p_cell, pr[i], p_flip_h, p_flip_v, p_transpose, p_autotile_coord, p_xform);
- }
-}
-
-void TileMapEditor::_clear_bucket_cache() {
- if (bucket_cache_visited) {
- delete[] bucket_cache_visited;
- bucket_cache_visited = nullptr;
- }
-}
-
-void TileMapEditor::_update_copydata() {
- copydata.clear();
-
- if (!selection_active) {
- return;
- }
-
- for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
- for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- TileData tcd;
-
- tcd.cell = node->get_cell(j, i);
- if (tcd.cell != TileMap::INVALID_CELL) {
- tcd.pos = Point2i(j, i);
- tcd.flip_h = node->is_cell_x_flipped(j, i);
- tcd.flip_v = node->is_cell_y_flipped(j, i);
- tcd.transpose = node->is_cell_transposed(j, i);
- tcd.autotile_coord = node->get_cell_autotile_coord(j, i);
-
- copydata.push_back(tcd);
- }
- }
- }
-}
-
-static inline Vector<Point2i> line(int x0, int x1, int y0, int y1) {
- Vector<Point2i> points;
-
- float dx = ABS(x1 - x0);
- float dy = ABS(y1 - y0);
-
- int x = x0;
- int y = y0;
-
- int sx = x0 > x1 ? -1 : 1;
- int sy = y0 > y1 ? -1 : 1;
-
- if (dx > dy) {
- float err = dx / 2;
-
- for (; x != x1; x += sx) {
- points.push_back(Vector2(x, y));
-
- err -= dy;
- if (err < 0) {
- y += sy;
- err += dx;
- }
- }
- } else {
- float err = dy / 2;
-
- for (; y != y1; y += sy) {
- points.push_back(Vector2(x, y));
-
- err -= dx;
- if (err < 0) {
- x += sx;
- err += dy;
- }
- }
- }
-
- points.push_back(Vector2(x, y));
-
- return points;
-}
-
-bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
- if (!node || !node->get_tileset().is_valid() || !node->is_visible_in_tree() || CanvasItemEditor::get_singleton()->get_current_tool() != CanvasItemEditor::TOOL_SELECT) {
- return false;
- }
-
- Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform();
- Transform2D xform_inv = xform.affine_inverse();
-
- Ref<InputEventMouseButton> mb = p_event;
-
- if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
- if (mb->is_pressed()) {
- if (Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
- return false; // Drag.
- }
-
- if (tool == TOOL_NONE) {
- tool = TOOL_PAINTING;
- _update_button_tool();
-
- if (mb->get_command()) {
- tool = TOOL_PICKING;
- _pick_tile(over_tile);
- _update_button_tool();
-
- return true;
- }
- }
-
- if (tool == TOOL_LINE_PAINT || tool == TOOL_RECTANGLE_PAINT) {
- selection_active = false;
- rectangle_begin = over_tile;
-
- mouse_down = true;
- } else if (tool == TOOL_PAINTING) {
- Vector<int> ids = get_selected_tiles();
-
- if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- tool = TOOL_PAINTING;
-
- _start_undo(TTR("Paint TileMap"));
- }
- } else if (tool == TOOL_PICKING) {
- _pick_tile(over_tile);
- } else if (tool == TOOL_SELECTING) {
- selection_active = true;
- rectangle_begin = over_tile;
- }
-
- _update_button_tool();
- return true;
-
- } else {
- // Mousebutton was released.
- if (tool != TOOL_NONE) {
- if (tool == TOOL_PAINTING) {
- Vector<int> ids = get_selected_tiles();
-
- if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- _set_cell(over_tile, ids, flip_h, flip_v, transpose);
- _finish_undo();
-
- paint_undo.clear();
- }
- } else if (tool == TOOL_LINE_PAINT) {
- if (!mouse_down) {
- return true;
- }
-
- Vector<int> ids = get_selected_tiles();
-
- if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- _start_undo(TTR("Line Draw"));
- for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _set_cell(E->key(), ids, flip_h, flip_v, transpose);
- }
- _finish_undo();
-
- paint_undo.clear();
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- mouse_down = false;
- return true;
- }
-
- mouse_down = false;
- } else if (tool == TOOL_RECTANGLE_PAINT) {
- if (!mouse_down) {
- return true;
- }
-
- Vector<int> ids = get_selected_tiles();
-
- if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- _start_undo(TTR("Rectangle Paint"));
- for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
- for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _set_cell(Point2i(j, i), ids, flip_h, flip_v, transpose);
- }
- }
- _finish_undo();
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- mouse_down = false;
- return true;
- }
-
- mouse_down = false;
- } else if (tool == TOOL_PASTING) {
- Point2 ofs = over_tile - rectangle.position;
- Vector<int> ids;
-
- _start_undo(TTR("Paste"));
- ids.push_back(0);
- for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
- ids.write[0] = E->get().cell;
- _set_cell(E->get().pos + ofs, ids, E->get().flip_h, E->get().flip_v, E->get().transpose, E->get().autotile_coord);
- }
- _finish_undo();
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- return true; // We want to keep the Pasting tool.
- } else if (tool == TOOL_SELECTING) {
- CanvasItemEditor::get_singleton()->update_viewport();
-
- } else if (tool == TOOL_BUCKET) {
- Vector<Vector2> points = _bucket_fill(over_tile);
-
- if (points.size() == 0) {
- return false;
- }
-
- _start_undo(TTR("Bucket Fill"));
-
- Dictionary op;
- op["id"] = get_selected_tiles();
- op["flip_h"] = flip_h;
- op["flip_v"] = flip_v;
- op["transpose"] = transpose;
-
- _fill_points(points, op);
-
- _finish_undo();
-
- // So the fill preview is cleared right after the click.
- CanvasItemEditor::get_singleton()->update_viewport();
-
- // We want to keep the bucket-tool active.
- return true;
- }
-
- tool = TOOL_NONE;
- _update_button_tool();
-
- return true;
- }
- }
- } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
- if (mb->is_pressed()) {
- if (tool == TOOL_SELECTING || selection_active) {
- tool = TOOL_NONE;
- selection_active = false;
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- _update_button_tool();
- return true;
- }
-
- if (tool == TOOL_PASTING) {
- tool = TOOL_NONE;
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- _update_button_tool();
- return true;
- }
-
- if (tool == TOOL_LINE_PAINT) {
- tool = TOOL_LINE_ERASE;
- mouse_down = true;
- rectangle_begin = over_tile;
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- _update_button_tool();
- return true;
- }
-
- if (tool == TOOL_RECTANGLE_PAINT) {
- tool = TOOL_RECTANGLE_ERASE;
- mouse_down = true;
- rectangle_begin = over_tile;
- copydata.clear();
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- _update_button_tool();
- return true;
- }
-
- if (tool == TOOL_NONE) {
- paint_undo.clear();
-
- Point2 local = node->world_to_map(xform_inv.xform(mb->get_position()));
-
- _start_undo(TTR("Erase TileMap"));
- tool = TOOL_ERASING;
- _set_cell(local, invalid_cell);
-
- _update_button_tool();
- return true;
- }
-
- } else {
- if (tool == TOOL_LINE_ERASE) {
- if (!mouse_down) {
- return true;
- }
-
- tool = TOOL_LINE_PAINT;
- _update_button_tool();
-
- Vector<int> ids = get_selected_tiles();
-
- if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- _start_undo(TTR("Line Erase"));
- for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _set_cell(E->key(), invalid_cell, flip_h, flip_v, transpose);
- }
- _finish_undo();
- paint_undo.clear();
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- mouse_down = false;
- return true;
- }
-
- mouse_down = false;
- } else if (tool == TOOL_RECTANGLE_ERASE) {
- if (!mouse_down) {
- return true;
- }
-
- tool = TOOL_RECTANGLE_PAINT;
- _update_button_tool();
-
- Vector<int> ids = get_selected_tiles();
-
- if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- _start_undo(TTR("Rectangle Erase"));
- for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
- for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _set_cell(Point2i(j, i), invalid_cell, flip_h, flip_v, transpose);
- }
- }
- _finish_undo();
- paint_undo.clear();
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- mouse_down = false;
- return true;
- }
-
- mouse_down = false;
- tool = TOOL_RECTANGLE_PAINT;
- }
-
- if (tool == TOOL_ERASING) {
- tool = TOOL_NONE;
- _update_button_tool();
-
- return true;
- } else if (tool == TOOL_BUCKET) {
- Vector<int> ids;
- ids.push_back(node->get_cell(over_tile.x, over_tile.y));
- Dictionary pop;
- pop["id"] = ids;
- pop["flip_h"] = node->is_cell_x_flipped(over_tile.x, over_tile.y);
- pop["flip_v"] = node->is_cell_y_flipped(over_tile.x, over_tile.y);
- pop["transpose"] = node->is_cell_transposed(over_tile.x, over_tile.y);
-
- Vector<Vector2> points = _bucket_fill(over_tile, true);
-
- if (points.size() == 0) {
- return false;
- }
-
- undo_redo->create_action(TTR("Bucket Fill"));
-
- undo_redo->add_do_method(this, "_erase_points", points);
- undo_redo->add_undo_method(this, "_fill_points", points, pop);
-
- undo_redo->commit_action();
- }
- }
- }
- }
-
- Ref<InputEventMouseMotion> mm = p_event;
-
- if (mm.is_valid()) {
- Point2i new_over_tile = node->world_to_map(xform_inv.xform(mm->get_position()));
- Point2i old_over_tile = over_tile;
-
- if (new_over_tile != over_tile) {
- over_tile = new_over_tile;
- CanvasItemEditor::get_singleton()->update_viewport();
- }
-
- if (refocus_over_tile) {
- // editor lost focus; forget last tile position
- old_over_tile = new_over_tile;
- refocus_over_tile = false;
- }
-
- int tile_under = node->get_cell(over_tile.x, over_tile.y);
- String tile_name = "none";
-
- if (node->get_tileset()->has_tile(tile_under)) {
- tile_name = node->get_tileset()->tile_get_name(tile_under);
- }
- tile_info->show();
- tile_info->set_text(String::num(over_tile.x) + ", " + String::num(over_tile.y) + " [" + tile_name + "]");
-
- if (tool == TOOL_PAINTING) {
- // Paint using bresenham line to prevent holes in painting if the user moves fast.
-
- Vector<Point2i> points = line(old_over_tile.x, over_tile.x, old_over_tile.y, over_tile.y);
- Vector<int> ids = get_selected_tiles();
-
- for (int i = 0; i < points.size(); ++i) {
- Point2i pos = points[i];
-
- if (!paint_undo.has(pos)) {
- paint_undo[pos] = _get_op_from_cell(pos);
- }
-
- _set_cell(pos, ids, flip_h, flip_v, transpose);
- }
-
- return true;
- }
-
- if (tool == TOOL_ERASING) {
- // Erase using bresenham line to prevent holes in painting if the user moves fast.
-
- Vector<Point2i> points = line(old_over_tile.x, over_tile.x, old_over_tile.y, over_tile.y);
-
- for (int i = 0; i < points.size(); ++i) {
- Point2i pos = points[i];
-
- _set_cell(pos, invalid_cell);
- }
-
- return true;
- }
-
- if (tool == TOOL_SELECTING) {
- _select(rectangle_begin, over_tile);
-
- return true;
- }
-
- if (tool == TOOL_LINE_PAINT || tool == TOOL_LINE_ERASE) {
- Vector<int> ids = get_selected_tiles();
- Vector<int> tmp_cell;
- bool erasing = (tool == TOOL_LINE_ERASE);
-
- if (!mouse_down) {
- return true;
- }
-
- tmp_cell.push_back(0);
- if (erasing && paint_undo.size()) {
- for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- tmp_cell.write[0] = E->get().idx;
- _set_cell(E->key(), tmp_cell, E->get().xf, E->get().yf, E->get().tr);
- }
- }
-
- paint_undo.clear();
-
- if (ids.size() > 0 && ids[0] != TileMap::INVALID_CELL) {
- Vector<Point2i> points = line(rectangle_begin.x, over_tile.x, rectangle_begin.y, over_tile.y);
-
- for (int i = 0; i < points.size(); i++) {
- paint_undo[points[i]] = _get_op_from_cell(points[i]);
-
- if (erasing) {
- _set_cell(points[i], invalid_cell);
- }
- }
-
- CanvasItemEditor::get_singleton()->update_viewport();
- }
-
- return true;
- }
- if (tool == TOOL_RECTANGLE_PAINT || tool == TOOL_RECTANGLE_ERASE) {
- Vector<int> tmp_cell;
- tmp_cell.push_back(0);
-
- Point2i end_tile = over_tile;
-
- if (!mouse_down) {
- return true;
- }
-
- if (mm->get_shift()) {
- int size = fmax(ABS(end_tile.x - rectangle_begin.x), ABS(end_tile.y - rectangle_begin.y));
- int xDirection = MAX(MIN(end_tile.x - rectangle_begin.x, 1), -1);
- int yDirection = MAX(MIN(end_tile.y - rectangle_begin.y, 1), -1);
- end_tile = rectangle_begin + Point2i(xDirection * size, yDirection * size);
- }
-
- _select(rectangle_begin, end_tile);
-
- if (tool == TOOL_RECTANGLE_ERASE) {
- if (paint_undo.size()) {
- for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- tmp_cell.write[0] = E->get().idx;
- _set_cell(E->key(), tmp_cell, E->get().xf, E->get().yf, E->get().tr);
- }
- }
-
- paint_undo.clear();
-
- for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
- for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- Point2i tile = Point2i(j, i);
- paint_undo[tile] = _get_op_from_cell(tile);
-
- _set_cell(tile, invalid_cell);
- }
- }
- }
-
- return true;
- }
- if (tool == TOOL_PICKING && Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT)) {
- _pick_tile(over_tile);
-
- return true;
- }
- }
-
- Ref<InputEventKey> k = p_event;
-
- if (k.is_valid() && k->is_pressed()) {
- if (last_tool == TOOL_NONE && tool == TOOL_PICKING && k->get_keycode() == KEY_SHIFT && k->get_command()) {
- // trying to draw a rectangle with the painting tool, so change to the correct tool
- tool = last_tool;
-
- CanvasItemEditor::get_singleton()->update_viewport();
- _update_button_tool();
- }
-
- if (k->get_keycode() == KEY_ESCAPE) {
- if (tool == TOOL_PASTING) {
- copydata.clear();
- } else if (tool == TOOL_SELECTING || selection_active) {
- selection_active = false;
- }
-
- tool = TOOL_NONE;
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- _update_button_tool();
- return true;
- }
-
- if (!mouse_over) {
- // Editor shortcuts should not fire if mouse not in viewport.
- return false;
- }
-
- if (ED_IS_SHORTCUT("tile_map_editor/paint_tile", p_event)) {
- // NOTE: We do not set tool = TOOL_PAINTING as this begins painting
- // immediately without pressing the left mouse button first.
- tool = TOOL_NONE;
- CanvasItemEditor::get_singleton()->update_viewport();
-
- _update_button_tool();
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/line_fill", p_event)) {
- tool = TOOL_LINE_PAINT;
- CanvasItemEditor::get_singleton()->update_viewport();
-
- _update_button_tool();
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/rectangle_fill", p_event)) {
- tool = TOOL_RECTANGLE_PAINT;
- CanvasItemEditor::get_singleton()->update_viewport();
-
- _update_button_tool();
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/bucket_fill", p_event)) {
- tool = TOOL_BUCKET;
- CanvasItemEditor::get_singleton()->update_viewport();
-
- _update_button_tool();
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/erase_selection", p_event)) {
- _menu_option(OPTION_ERASE_SELECTION);
-
- _update_button_tool();
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/select", p_event)) {
- tool = TOOL_SELECTING;
- selection_active = false;
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- _update_button_tool();
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/copy_selection", p_event)) {
- _update_copydata();
-
- if (selection_active) {
- tool = TOOL_PASTING;
-
- CanvasItemEditor::get_singleton()->update_viewport();
-
- _update_button_tool();
- return true;
- }
- }
- if (ED_IS_SHORTCUT("tile_map_editor/cut_selection", p_event)) {
- if (selection_active) {
- _update_copydata();
-
- _start_undo(TTR("Cut Selection"));
- _erase_selection();
- _finish_undo();
-
- selection_active = false;
-
- tool = TOOL_PASTING;
-
- CanvasItemEditor::get_singleton()->update_viewport();
- _update_button_tool();
- return true;
- }
- }
- if (ED_IS_SHORTCUT("tile_map_editor/find_tile", p_event)) {
- search_box->select_all();
- search_box->grab_focus();
-
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/rotate_left", p_event)) {
- _rotate(-1);
- CanvasItemEditor::get_singleton()->update_viewport();
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/rotate_right", p_event)) {
- _rotate(1);
- CanvasItemEditor::get_singleton()->update_viewport();
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/flip_horizontal", p_event)) {
- _flip_horizontal();
- CanvasItemEditor::get_singleton()->update_viewport();
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/flip_vertical", p_event)) {
- _flip_vertical();
- CanvasItemEditor::get_singleton()->update_viewport();
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/clear_transform", p_event)) {
- _clear_transform();
- CanvasItemEditor::get_singleton()->update_viewport();
- return true;
- }
- if (ED_IS_SHORTCUT("tile_map_editor/transpose", p_event)) {
- transpose = !transpose;
- _update_palette();
- CanvasItemEditor::get_singleton()->update_viewport();
- return true;
- }
- } else if (k.is_valid()) { // Release event.
-
- if (tool == TOOL_NONE) {
- if (k->get_keycode() == KEY_SHIFT && k->get_command()) {
- tool = TOOL_PICKING;
- _update_button_tool();
- }
- } else if (tool == TOOL_PICKING) {
-#ifdef APPLE_STYLE_KEYS
- if (k->get_keycode() == KEY_META) {
-#else
- if (k->get_keycode() == KEY_CONTROL) {
-#endif
- // Go back to that last tool if KEY_CONTROL was released.
- tool = last_tool;
-
- CanvasItemEditor::get_singleton()->update_viewport();
- _update_button_tool();
- }
- }
- }
- return false;
-}
-
-void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
- if (!node || CanvasItemEditor::get_singleton()->get_current_tool() != CanvasItemEditor::TOOL_SELECT) {
- return;
- }
-
- Transform2D cell_xf = node->get_cell_transform();
- Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform();
- Transform2D xform_inv = xform.affine_inverse();
-
- Size2 screen_size = p_overlay->get_size();
- {
- Rect2 aabb;
- aabb.position = node->world_to_map(xform_inv.xform(Vector2()));
- aabb.expand_to(node->world_to_map(xform_inv.xform(Vector2(0, screen_size.height))));
- aabb.expand_to(node->world_to_map(xform_inv.xform(Vector2(screen_size.width, 0))));
- aabb.expand_to(node->world_to_map(xform_inv.xform(screen_size)));
- Rect2i si = aabb.grow(1.0);
-
- if (node->get_half_offset() != TileMap::HALF_OFFSET_X && node->get_half_offset() != TileMap::HALF_OFFSET_NEGATIVE_X) {
- int max_lines = 2000; //avoid crash if size too small
-
- for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) {
- Vector2 from = xform.xform(node->map_to_world(Vector2(i, si.position.y)));
- Vector2 to = xform.xform(node->map_to_world(Vector2(i, si.position.y + si.size.y + 1)));
-
- Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2);
- p_overlay->draw_line(from, to, col, 1);
- if (max_lines-- == 0) {
- break;
- }
- }
- } else {
- int max_lines = 10000; //avoid crash if size too small
-
- for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) {
- for (int j = (si.position.y) - 1; j <= (si.position.y + si.size.y); j++) {
- Vector2 ofs;
- if (ABS(j) & 1) {
- ofs = cell_xf[0] * (node->get_half_offset() == TileMap::HALF_OFFSET_X ? 0.5 : -0.5);
- }
-
- Vector2 from = xform.xform(node->map_to_world(Vector2(i, j), true) + ofs);
- Vector2 to = xform.xform(node->map_to_world(Vector2(i, j + 1), true) + ofs);
-
- Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2);
- p_overlay->draw_line(from, to, col, 1);
-
- if (--max_lines == 0) {
- break;
- }
- }
- if (max_lines == 0) {
- break;
- }
- }
- }
-
- int max_lines = 10000; //avoid crash if size too small
-
- if (node->get_half_offset() != TileMap::HALF_OFFSET_Y && node->get_half_offset() != TileMap::HALF_OFFSET_NEGATIVE_Y) {
- for (int i = (si.position.y) - 1; i <= (si.position.y + si.size.y); i++) {
- Vector2 from = xform.xform(node->map_to_world(Vector2(si.position.x, i)));
- Vector2 to = xform.xform(node->map_to_world(Vector2(si.position.x + si.size.x + 1, i)));
-
- Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2);
- p_overlay->draw_line(from, to, col, 1);
-
- if (max_lines-- == 0) {
- break;
- }
- }
- } else {
- for (int i = (si.position.y) - 1; i <= (si.position.y + si.size.y); i++) {
- for (int j = (si.position.x) - 1; j <= (si.position.x + si.size.x); j++) {
- Vector2 ofs;
- if (ABS(j) & 1) {
- ofs = cell_xf[1] * (node->get_half_offset() == TileMap::HALF_OFFSET_Y ? 0.5 : -0.5);
- }
-
- Vector2 from = xform.xform(node->map_to_world(Vector2(j, i), true) + ofs);
- Vector2 to = xform.xform(node->map_to_world(Vector2(j + 1, i), true) + ofs);
-
- Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2);
- p_overlay->draw_line(from, to, col, 1);
-
- if (--max_lines == 0) {
- break;
- }
- }
- if (max_lines == 0) {
- break;
- }
- }
- }
- }
-
- if (selection_active) {
- Vector<Vector2> points;
- points.push_back(xform.xform(node->map_to_world((rectangle.position))));
- points.push_back(xform.xform(node->map_to_world((rectangle.position + Point2(rectangle.size.x + 1, 0)))));
- points.push_back(xform.xform(node->map_to_world((rectangle.position + Point2(rectangle.size.x + 1, rectangle.size.y + 1)))));
- points.push_back(xform.xform(node->map_to_world((rectangle.position + Point2(0, rectangle.size.y + 1)))));
-
- p_overlay->draw_colored_polygon(points, Color(0.2, 0.8, 1, 0.4));
- }
-
- if (mouse_over && node->get_tileset().is_valid()) {
- Vector2 endpoints[4] = {
- node->map_to_world(over_tile, true),
- node->map_to_world((over_tile + Point2(1, 0)), true),
- node->map_to_world((over_tile + Point2(1, 1)), true),
- node->map_to_world((over_tile + Point2(0, 1)), true)
- };
-
- for (int i = 0; i < 4; i++) {
- if (node->get_half_offset() == TileMap::HALF_OFFSET_X && ABS(over_tile.y) & 1) {
- endpoints[i] += cell_xf[0] * 0.5;
- }
- if (node->get_half_offset() == TileMap::HALF_OFFSET_NEGATIVE_X && ABS(over_tile.y) & 1) {
- endpoints[i] += cell_xf[0] * -0.5;
- }
- if (node->get_half_offset() == TileMap::HALF_OFFSET_Y && ABS(over_tile.x) & 1) {
- endpoints[i] += cell_xf[1] * 0.5;
- }
- if (node->get_half_offset() == TileMap::HALF_OFFSET_NEGATIVE_Y && ABS(over_tile.x) & 1) {
- endpoints[i] += cell_xf[1] * -0.5;
- }
- endpoints[i] = xform.xform(endpoints[i]);
- }
- Color col;
- if (node->get_cell(over_tile.x, over_tile.y) != TileMap::INVALID_CELL) {
- col = Color(0.2, 0.8, 1.0, 0.8);
- } else {
- col = Color(1.0, 0.4, 0.2, 0.8);
- }
-
- for (int i = 0; i < 4; i++) {
- p_overlay->draw_line(endpoints[i], endpoints[(i + 1) % 4], col, 2);
- }
-
- bool bucket_preview = EditorSettings::get_singleton()->get("editors/tile_map/bucket_fill_preview");
- if (tool == TOOL_SELECTING || tool == TOOL_PICKING || !bucket_preview) {
- return;
- }
-
- if (tool == TOOL_LINE_PAINT) {
- if (!mouse_down) {
- return;
- }
-
- if (paint_undo.is_empty()) {
- return;
- }
-
- Vector<int> ids = get_selected_tiles();
-
- if (ids.size() == 1 && ids[0] == TileMap::INVALID_CELL) {
- return;
- }
-
- for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _draw_cell(p_overlay, ids[0], E->key(), flip_h, flip_v, transpose, autotile_coord, xform);
- }
-
- } else if (tool == TOOL_RECTANGLE_PAINT) {
- if (!mouse_down) {
- return;
- }
-
- Vector<int> ids = get_selected_tiles();
-
- if (ids.size() == 1 && ids[0] == TileMap::INVALID_CELL) {
- return;
- }
-
- for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
- for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _draw_cell(p_overlay, ids[0], Point2i(j, i), flip_h, flip_v, transpose, autotile_coord, xform);
- }
- }
- } else if (tool == TOOL_PASTING) {
- if (copydata.is_empty()) {
- return;
- }
-
- Ref<TileSet> ts = node->get_tileset();
-
- if (ts.is_null()) {
- return;
- }
-
- Point2 ofs = over_tile - rectangle.position;
-
- for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
- if (!ts->has_tile(E->get().cell)) {
- continue;
- }
-
- TileData tcd = E->get();
-
- _draw_cell(p_overlay, tcd.cell, tcd.pos + ofs, tcd.flip_h, tcd.flip_v, tcd.transpose, tcd.autotile_coord, xform);
- }
-
- Rect2i duplicate = rectangle;
- duplicate.position = over_tile;
-
- Vector<Vector2> points;
- points.push_back(xform.xform(node->map_to_world(duplicate.position)));
- points.push_back(xform.xform(node->map_to_world((duplicate.position + Point2(duplicate.size.x + 1, 0)))));
- points.push_back(xform.xform(node->map_to_world((duplicate.position + Point2(duplicate.size.x + 1, duplicate.size.y + 1)))));
- points.push_back(xform.xform(node->map_to_world((duplicate.position + Point2(0, duplicate.size.y + 1)))));
-
- p_overlay->draw_colored_polygon(points, Color(0.2, 1.0, 0.8, 0.2));
-
- } else if (tool == TOOL_BUCKET) {
- Vector<int> tiles = get_selected_tiles();
- _draw_fill_preview(p_overlay, tiles[0], over_tile, flip_h, flip_v, transpose, autotile_coord, xform);
-
- } else {
- Vector<int> st = get_selected_tiles();
-
- if (st.size() == 1 && st[0] == TileMap::INVALID_CELL) {
- return;
- }
-
- _draw_cell(p_overlay, st[0], over_tile, flip_h, flip_v, transpose, autotile_coord, xform);
- }
- }
-}
-
-void TileMapEditor::edit(Node *p_tile_map) {
- search_box->set_text("");
-
- if (!canvas_item_editor_viewport) {
- canvas_item_editor_viewport = CanvasItemEditor::get_singleton()->get_viewport_control();
- }
-
- if (node) {
- Callable callable_tileset_settings_changed = callable_mp(this, &TileMapEditor::_tileset_settings_changed);
-
- if (node->is_connected("settings_changed", callable_tileset_settings_changed)) {
- node->disconnect("settings_changed", callable_tileset_settings_changed);
- }
- }
- if (p_tile_map) {
- node = Object::cast_to<TileMap>(p_tile_map);
- if (!canvas_item_editor_viewport->is_connected("mouse_entered", callable_mp(this, &TileMapEditor::_canvas_mouse_enter))) {
- canvas_item_editor_viewport->connect("mouse_entered", callable_mp(this, &TileMapEditor::_canvas_mouse_enter));
- }
- if (!canvas_item_editor_viewport->is_connected("mouse_exited", callable_mp(this, &TileMapEditor::_canvas_mouse_exit))) {
- canvas_item_editor_viewport->connect("mouse_exited", callable_mp(this, &TileMapEditor::_canvas_mouse_exit));
- }
-
- _update_palette();
-
- } else {
- node = nullptr;
-
- if (canvas_item_editor_viewport->is_connected("mouse_entered", callable_mp(this, &TileMapEditor::_canvas_mouse_enter))) {
- canvas_item_editor_viewport->disconnect("mouse_entered", callable_mp(this, &TileMapEditor::_canvas_mouse_enter));
- }
- if (canvas_item_editor_viewport->is_connected("mouse_exited", callable_mp(this, &TileMapEditor::_canvas_mouse_exit))) {
- canvas_item_editor_viewport->disconnect("mouse_exited", callable_mp(this, &TileMapEditor::_canvas_mouse_exit));
- }
-
- _update_palette();
- }
-
- if (node) {
- Callable callable_tileset_settings_changed = callable_mp(this, &TileMapEditor::_tileset_settings_changed);
-
- if (!node->is_connected("settings_changed", callable_tileset_settings_changed)) {
- node->connect("settings_changed", callable_tileset_settings_changed);
- }
- }
-
- _clear_bucket_cache();
-}
-
-void TileMapEditor::_tileset_settings_changed() {
- _update_palette();
- CanvasItemEditor::get_singleton()->update_viewport();
-}
-
-void TileMapEditor::_icon_size_changed(float p_value) {
- if (node) {
- palette->set_icon_scale(p_value);
- manual_palette->set_icon_scale(p_value);
- _update_palette();
- }
-}
-
-void TileMapEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_fill_points"), &TileMapEditor::_fill_points);
- ClassDB::bind_method(D_METHOD("_erase_points"), &TileMapEditor::_erase_points);
-}
-
-TileMapEditor::CellOp TileMapEditor::_get_op_from_cell(const Point2i &p_pos) {
- CellOp op;
- op.idx = node->get_cell(p_pos.x, p_pos.y);
- if (op.idx != TileMap::INVALID_CELL) {
- if (node->is_cell_x_flipped(p_pos.x, p_pos.y)) {
- op.xf = true;
- }
- if (node->is_cell_y_flipped(p_pos.x, p_pos.y)) {
- op.yf = true;
- }
- if (node->is_cell_transposed(p_pos.x, p_pos.y)) {
- op.tr = true;
- }
- op.ac = node->get_cell_autotile_coord(p_pos.x, p_pos.y);
- }
- return op;
-}
-
-void TileMapEditor::_rotate(int steps) {
- const bool normal_rotation_matrix[][3] = {
- { false, false, false },
- { true, true, false },
- { false, true, true },
- { true, false, true }
- };
-
- const bool mirrored_rotation_matrix[][3] = {
- { false, true, false },
- { true, true, true },
- { false, false, true },
- { true, false, false }
- };
-
- if (transpose ^ flip_h ^ flip_v) {
- // Odd number of flags activated = mirrored rotation
- for (int i = 0; i < 4; i++) {
- if (transpose == mirrored_rotation_matrix[i][0] &&
- flip_h == mirrored_rotation_matrix[i][1] &&
- flip_v == mirrored_rotation_matrix[i][2]) {
- int new_id = Math::wrapi(i + steps, 0, 4);
- transpose = mirrored_rotation_matrix[new_id][0];
- flip_h = mirrored_rotation_matrix[new_id][1];
- flip_v = mirrored_rotation_matrix[new_id][2];
- break;
- }
- }
- } else {
- // Even number of flags activated = normal rotation
- for (int i = 0; i < 4; i++) {
- if (transpose == normal_rotation_matrix[i][0] &&
- flip_h == normal_rotation_matrix[i][1] &&
- flip_v == normal_rotation_matrix[i][2]) {
- int new_id = Math::wrapi(i + steps, 0, 4);
- transpose = normal_rotation_matrix[new_id][0];
- flip_h = normal_rotation_matrix[new_id][1];
- flip_v = normal_rotation_matrix[new_id][2];
- break;
- }
- }
- }
-
- _update_palette();
-}
-
-void TileMapEditor::_flip_horizontal() {
- flip_h = !flip_h;
- _update_palette();
-}
-
-void TileMapEditor::_flip_vertical() {
- flip_v = !flip_v;
- _update_palette();
-}
-
-void TileMapEditor::_clear_transform() {
- transpose = false;
- flip_h = false;
- flip_v = false;
- _update_palette();
-}
-
-TileMapEditor::TileMapEditor(EditorNode *p_editor) {
- node = nullptr;
- manual_autotile = false;
- priority_atlastile = false;
- manual_position = Vector2(0, 0);
- canvas_item_editor_viewport = nullptr;
- editor = p_editor;
- undo_redo = EditorNode::get_undo_redo();
-
- tool = TOOL_NONE;
- selection_active = false;
- mouse_over = false;
- mouse_down = false;
-
- flip_h = false;
- flip_v = false;
- transpose = false;
-
- bucket_cache_tile = -1;
- bucket_cache_visited = nullptr;
-
- invalid_cell.resize(1);
- invalid_cell.write[0] = TileMap::INVALID_CELL;
-
- ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase Selection"), KEY_DELETE);
- ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find Tile"), KEY_MASK_CMD + KEY_F);
- ED_SHORTCUT("tile_map_editor/transpose", TTR("Transpose"), KEY_T);
-
- HBoxContainer *tool_hb = memnew(HBoxContainer);
- add_child(tool_hb);
-
- manual_button = memnew(CheckBox);
- manual_button->set_text(TTR("Disable Autotile"));
- manual_button->connect("toggled", callable_mp(this, &TileMapEditor::_manual_toggled));
- add_child(manual_button);
-
- priority_button = memnew(CheckBox);
- priority_button->set_text(TTR("Enable Priority"));
- priority_button->connect("toggled", callable_mp(this, &TileMapEditor::_priority_toggled));
- add_child(priority_button);
-
- search_box = memnew(LineEdit);
- search_box->set_placeholder(TTR("Filter tiles"));
- search_box->set_h_size_flags(SIZE_EXPAND_FILL);
- search_box->connect("text_entered", callable_mp(this, &TileMapEditor::_text_entered));
- search_box->connect("text_changed", callable_mp(this, &TileMapEditor::_text_changed));
- search_box->connect("gui_input", callable_mp(this, &TileMapEditor::_sbox_input));
- add_child(search_box);
-
- size_slider = memnew(HSlider);
- size_slider->set_h_size_flags(SIZE_EXPAND_FILL);
- size_slider->set_min(0.1f);
- size_slider->set_max(4.0f);
- size_slider->set_step(0.1f);
- size_slider->set_value(1.0f);
- size_slider->connect("value_changed", callable_mp(this, &TileMapEditor::_icon_size_changed));
- add_child(size_slider);
-
- int mw = EDITOR_DEF("editors/tile_map/palette_min_width", 80);
-
- VSplitContainer *palette_container = memnew(VSplitContainer);
- palette_container->set_v_size_flags(SIZE_EXPAND_FILL);
- palette_container->set_custom_minimum_size(Size2(mw, 0));
- add_child(palette_container);
-
- // Add tile palette.
- palette = memnew(ItemList);
- palette->set_h_size_flags(SIZE_EXPAND_FILL);
- palette->set_v_size_flags(SIZE_EXPAND_FILL);
- palette->set_max_columns(0);
- palette->set_icon_mode(ItemList::ICON_MODE_TOP);
- palette->set_max_text_lines(2);
- palette->set_select_mode(ItemList::SELECT_MULTI);
- palette->add_theme_constant_override("vseparation", 8 * EDSCALE);
- palette->connect("item_selected", callable_mp(this, &TileMapEditor::_palette_selected));
- palette->connect("multi_selected", callable_mp(this, &TileMapEditor::_palette_multi_selected));
- palette->connect("gui_input", callable_mp(this, &TileMapEditor::_palette_input));
- palette_container->add_child(palette);
-
- // Add message for when no texture is selected.
- info_message = memnew(Label);
- info_message->set_text(TTR("Give a TileSet resource to this TileMap to use its tiles."));
- info_message->set_valign(Label::VALIGN_CENTER);
- info_message->set_align(Label::ALIGN_CENTER);
- info_message->set_autowrap(true);
- info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
- info_message->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
- palette->add_child(info_message);
-
- // Add autotile override palette.
- manual_palette = memnew(ItemList);
- manual_palette->set_h_size_flags(SIZE_EXPAND_FILL);
- manual_palette->set_v_size_flags(SIZE_EXPAND_FILL);
- manual_palette->set_max_columns(0);
- manual_palette->set_icon_mode(ItemList::ICON_MODE_TOP);
- manual_palette->set_max_text_lines(2);
- manual_palette->hide();
- palette_container->add_child(manual_palette);
-
- // Add menu items.
- toolbar = memnew(HBoxContainer);
- toolbar->hide();
- CanvasItemEditor::get_singleton()->add_control_to_menu_panel(toolbar);
-
- toolbar->add_child(memnew(VSeparator));
-
- // Tools.
- paint_button = memnew(Button);
- paint_button->set_flat(true);
- paint_button->set_shortcut(ED_SHORTCUT("tile_map_editor/paint_tile", TTR("Paint Tile"), KEY_P));
- paint_button->set_shortcut_context(this);
- paint_button->set_tooltip(TTR("RMB: Erase"));
- paint_button->connect("pressed", callable_mp(this, &TileMapEditor::_button_tool_select), make_binds(TOOL_NONE));
- paint_button->set_toggle_mode(true);
- toolbar->add_child(paint_button);
-
- line_button = memnew(Button);
- line_button->set_flat(true);
- line_button->set_shortcut(ED_SHORTCUT("tile_map_editor/line_fill", TTR("Line Fill"), KEY_L));
- line_button->set_shortcut_context(this);
- line_button->set_tooltip(TTR("RMB: Erase"));
- line_button->connect("pressed", callable_mp(this, &TileMapEditor::_button_tool_select), make_binds(TOOL_LINE_PAINT));
- line_button->set_toggle_mode(true);
- toolbar->add_child(line_button);
-
- rectangle_button = memnew(Button);
- rectangle_button->set_flat(true);
- rectangle_button->set_shortcut(ED_SHORTCUT("tile_map_editor/rectangle_fill", TTR("Rectangle Fill"), KEY_O));
- rectangle_button->set_shortcut_context(this);
- rectangle_button->set_tooltip(TTR("Shift+LMB: Keep 1:1 proporsions\nRMB: Erase"));
- rectangle_button->connect("pressed", callable_mp(this, &TileMapEditor::_button_tool_select), make_binds(TOOL_RECTANGLE_PAINT));
- rectangle_button->set_toggle_mode(true);
- toolbar->add_child(rectangle_button);
-
- bucket_fill_button = memnew(Button);
- bucket_fill_button->set_flat(true);
- bucket_fill_button->set_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_B));
- bucket_fill_button->set_shortcut_context(this);
- bucket_fill_button->connect("pressed", callable_mp(this, &TileMapEditor::_button_tool_select), make_binds(TOOL_BUCKET));
- bucket_fill_button->set_toggle_mode(true);
- toolbar->add_child(bucket_fill_button);
-
- picker_button = memnew(Button);
- picker_button->set_flat(true);
- picker_button->set_shortcut(ED_SHORTCUT("tile_map_editor/pick_tile", TTR("Pick Tile"), KEY_I));
- picker_button->set_shortcut_context(this);
- picker_button->connect("pressed", callable_mp(this, &TileMapEditor::_button_tool_select), make_binds(TOOL_PICKING));
- picker_button->set_toggle_mode(true);
- toolbar->add_child(picker_button);
-
- select_button = memnew(Button);
- select_button->set_flat(true);
- select_button->set_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_M));
- select_button->set_shortcut_context(this);
- select_button->connect("pressed", callable_mp(this, &TileMapEditor::_button_tool_select), make_binds(TOOL_SELECTING));
- select_button->set_toggle_mode(true);
- toolbar->add_child(select_button);
-
- _update_button_tool();
-
- // Container to the right of the toolbar.
- toolbar_right = memnew(HBoxContainer);
- toolbar_right->hide();
- toolbar_right->set_h_size_flags(SIZE_EXPAND_FILL);
- toolbar_right->set_alignment(BoxContainer::ALIGN_END);
- CanvasItemEditor::get_singleton()->add_control_to_menu_panel(toolbar_right);
-
- // Tile position.
- tile_info = memnew(Label);
- tile_info->set_modulate(Color(1, 1, 1, 0.8));
- tile_info->set_mouse_filter(MOUSE_FILTER_IGNORE);
- tile_info->add_theme_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_theme_font("main", "EditorFonts"));
- tile_info->add_theme_font_size_override("font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size("main_size", "EditorFonts"));
- // The tile info is only displayed after a tile has been hovered.
- tile_info->hide();
- CanvasItemEditor::get_singleton()->add_control_to_info_overlay(tile_info);
-
- // Menu.
- options = memnew(MenuButton);
- options->set_shortcut_context(this);
- options->set_text("TileMap");
- options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("TileMap", "EditorIcons"));
- toolbar_right->add_child(options);
-
- PopupMenu *p = options->get_popup();
- p->add_shortcut(ED_SHORTCUT("tile_map_editor/cut_selection", TTR("Cut Selection"), KEY_MASK_CMD + KEY_X), OPTION_CUT);
- p->add_shortcut(ED_SHORTCUT("tile_map_editor/copy_selection", TTR("Copy Selection"), KEY_MASK_CMD + KEY_C), OPTION_COPY);
- p->add_shortcut(ED_GET_SHORTCUT("tile_map_editor/erase_selection"), OPTION_ERASE_SELECTION);
- p->add_separator();
- p->add_item(TTR("Fix Invalid Tiles"), OPTION_FIX_INVALID);
- p->connect("id_pressed", callable_mp(this, &TileMapEditor::_menu_option));
-
- rotate_left_button = memnew(Button);
- rotate_left_button->set_flat(true);
- rotate_left_button->set_tooltip(TTR("Rotate Left"));
- rotate_left_button->set_focus_mode(FOCUS_NONE);
- rotate_left_button->connect("pressed", callable_mp(this, &TileMapEditor::_rotate), varray(-1));
- rotate_left_button->set_shortcut(ED_SHORTCUT("tile_map_editor/rotate_left", TTR("Rotate Left"), KEY_A));
- rotate_left_button->set_shortcut_context(this);
- tool_hb->add_child(rotate_left_button);
-
- rotate_right_button = memnew(Button);
- rotate_right_button->set_flat(true);
- rotate_right_button->set_tooltip(TTR("Rotate Right"));
- rotate_right_button->set_focus_mode(FOCUS_NONE);
- rotate_right_button->connect("pressed", callable_mp(this, &TileMapEditor::_rotate), varray(1));
- rotate_right_button->set_shortcut(ED_SHORTCUT("tile_map_editor/rotate_right", TTR("Rotate Right"), KEY_S));
- rotate_right_button->set_shortcut_context(this);
- tool_hb->add_child(rotate_right_button);
-
- flip_horizontal_button = memnew(Button);
- flip_horizontal_button->set_flat(true);
- flip_horizontal_button->set_tooltip(TTR("Flip Horizontally"));
- flip_horizontal_button->set_focus_mode(FOCUS_NONE);
- flip_horizontal_button->connect("pressed", callable_mp(this, &TileMapEditor::_flip_horizontal));
- flip_horizontal_button->set_shortcut(ED_SHORTCUT("tile_map_editor/flip_horizontal", TTR("Flip Horizontally"), KEY_X));
- flip_horizontal_button->set_shortcut_context(this);
- tool_hb->add_child(flip_horizontal_button);
-
- flip_vertical_button = memnew(Button);
- flip_vertical_button->set_flat(true);
- flip_vertical_button->set_tooltip(TTR("Flip Vertically"));
- flip_vertical_button->set_focus_mode(FOCUS_NONE);
- flip_vertical_button->connect("pressed", callable_mp(this, &TileMapEditor::_flip_vertical));
- flip_vertical_button->set_shortcut(ED_SHORTCUT("tile_map_editor/flip_vertical", TTR("Flip Vertically"), KEY_Z));
- flip_vertical_button->set_shortcut_context(this);
- tool_hb->add_child(flip_vertical_button);
-
- clear_transform_button = memnew(Button);
- clear_transform_button->set_flat(true);
- clear_transform_button->set_tooltip(TTR("Clear Transform"));
- clear_transform_button->set_focus_mode(FOCUS_NONE);
- clear_transform_button->connect("pressed", callable_mp(this, &TileMapEditor::_clear_transform));
- clear_transform_button->set_shortcut(ED_SHORTCUT("tile_map_editor/clear_transform", TTR("Clear Transform"), KEY_W));
- clear_transform_button->set_shortcut_context(this);
- tool_hb->add_child(clear_transform_button);
-
- clear_transform_button->set_disabled(true);
-}
-
-TileMapEditor::~TileMapEditor() {
- _clear_bucket_cache();
- copydata.clear();
-}
-
-///////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////
-
-void TileMapEditorPlugin::_notification(int p_what) {
- if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
- switch ((int)EditorSettings::get_singleton()->get("editors/tile_map/editor_side")) {
- case 0: { // Left.
- CanvasItemEditor::get_singleton()->get_palette_split()->move_child(tile_map_editor, 0);
- } break;
- case 1: { // Right.
- CanvasItemEditor::get_singleton()->get_palette_split()->move_child(tile_map_editor, 1);
- } break;
- }
- }
-}
-
-void TileMapEditorPlugin::edit(Object *p_object) {
- tile_map_editor->edit(Object::cast_to<Node>(p_object));
-}
-
-bool TileMapEditorPlugin::handles(Object *p_object) const {
- return p_object->is_class("TileMap");
-}
-
-void TileMapEditorPlugin::make_visible(bool p_visible) {
- if (p_visible) {
- tile_map_editor->show();
- tile_map_editor->get_toolbar()->show();
- tile_map_editor->get_toolbar_right()->show();
- // `tile_info` isn't shown here, as it's displayed after a tile has been hovered.
- // Otherwise, a translucent black rectangle would be visible as there would be an
- // empty Label in the CanvasItemEditor's info overlay.
-
- // Change to TOOL_SELECT when TileMap node is selected, to prevent accidental movement.
- CanvasItemEditor::get_singleton()->set_current_tool(CanvasItemEditor::TOOL_SELECT);
- } else {
- tile_map_editor->hide();
- tile_map_editor->get_toolbar()->hide();
- tile_map_editor->get_toolbar_right()->hide();
- tile_map_editor->get_tile_info()->hide();
- tile_map_editor->edit(nullptr);
- }
-}
-
-TileMapEditorPlugin::TileMapEditorPlugin(EditorNode *p_node) {
- EDITOR_DEF("editors/tile_map/preview_size", 64);
- EDITOR_DEF("editors/tile_map/palette_item_hseparation", 8);
- EDITOR_DEF("editors/tile_map/show_tile_names", true);
- EDITOR_DEF("editors/tile_map/show_tile_ids", false);
- EDITOR_DEF("editors/tile_map/sort_tiles_by_name", true);
- EDITOR_DEF("editors/tile_map/bucket_fill_preview", true);
- EDITOR_DEF("editors/tile_map/editor_side", 1);
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/tile_map/editor_side", PROPERTY_HINT_ENUM, "Left,Right"));
-
- tile_map_editor = memnew(TileMapEditor(p_node));
- switch ((int)EditorSettings::get_singleton()->get("editors/tile_map/editor_side")) {
- case 0: { // Left.
- add_control_to_container(CONTAINER_CANVAS_EDITOR_SIDE_LEFT, tile_map_editor);
- } break;
- case 1: { // Right.
- add_control_to_container(CONTAINER_CANVAS_EDITOR_SIDE_RIGHT, tile_map_editor);
- } break;
- }
- tile_map_editor->hide();
-}
-
-TileMapEditorPlugin::~TileMapEditorPlugin() {
-}
diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h
deleted file mode 100644
index 421a3b3f68..0000000000
--- a/editor/plugins/tile_map_editor_plugin.h
+++ /dev/null
@@ -1,242 +0,0 @@
-/*************************************************************************/
-/* tile_map_editor_plugin.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef TILE_MAP_EDITOR_PLUGIN_H
-#define TILE_MAP_EDITOR_PLUGIN_H
-
-#include "editor/editor_node.h"
-#include "editor/editor_plugin.h"
-#include "scene/2d/tile_map.h"
-#include "scene/gui/check_box.h"
-#include "scene/gui/label.h"
-#include "scene/gui/line_edit.h"
-#include "scene/gui/menu_button.h"
-
-class TileMapEditor : public VBoxContainer {
- GDCLASS(TileMapEditor, VBoxContainer);
-
- enum Tool {
- TOOL_NONE,
- TOOL_PAINTING,
- TOOL_ERASING,
- TOOL_RECTANGLE_PAINT,
- TOOL_RECTANGLE_ERASE,
- TOOL_LINE_PAINT,
- TOOL_LINE_ERASE,
- TOOL_SELECTING,
- TOOL_BUCKET,
- TOOL_PICKING,
- TOOL_PASTING
- };
-
- enum Options {
- OPTION_COPY,
- OPTION_ERASE_SELECTION,
- OPTION_FIX_INVALID,
- OPTION_CUT
- };
-
- TileMap *node;
- bool manual_autotile;
- bool priority_atlastile;
- Vector2 manual_position;
-
- EditorNode *editor;
- UndoRedo *undo_redo;
- Control *canvas_item_editor_viewport;
-
- LineEdit *search_box;
- HSlider *size_slider;
- ItemList *palette;
- ItemList *manual_palette;
-
- Label *info_message;
-
- HBoxContainer *toolbar;
- HBoxContainer *toolbar_right;
-
- Label *tile_info;
- MenuButton *options;
-
- Button *paint_button;
- Button *line_button;
- Button *rectangle_button;
- Button *bucket_fill_button;
- Button *picker_button;
- Button *select_button;
-
- Button *flip_horizontal_button;
- Button *flip_vertical_button;
- Button *rotate_left_button;
- Button *rotate_right_button;
- Button *clear_transform_button;
-
- CheckBox *manual_button;
- CheckBox *priority_button;
-
- Tool tool;
- Tool last_tool;
-
- bool selection_active;
- bool mouse_over;
- bool mouse_down;
-
- bool flip_h;
- bool flip_v;
- bool transpose;
- Point2i autotile_coord;
-
- Point2i rectangle_begin;
- Rect2i rectangle;
-
- Point2i over_tile;
- bool refocus_over_tile = false;
-
- bool *bucket_cache_visited;
- Rect2i bucket_cache_rect;
- int bucket_cache_tile;
- Vector<Vector2> bucket_cache;
- List<Point2i> bucket_queue;
-
- struct CellOp {
- int idx = TileMap::INVALID_CELL;
- bool xf = false;
- bool yf = false;
- bool tr = false;
- Vector2 ac;
- };
-
- Map<Point2i, CellOp> paint_undo;
-
- struct TileData {
- Point2i pos;
- int cell = TileMap::INVALID_CELL;
- bool flip_h = false;
- bool flip_v = false;
- bool transpose = false;
- Point2i autotile_coord;
- };
-
- List<TileData> copydata;
-
- Map<Point2i, CellOp> undo_data;
- Vector<int> invalid_cell;
-
- void _pick_tile(const Point2 &p_pos);
-
- Vector<Vector2> _bucket_fill(const Point2i &p_start, bool erase = false, bool preview = false);
-
- void _fill_points(const Vector<Vector2> &p_points, const Dictionary &p_op);
- void _erase_points(const Vector<Vector2> &p_points);
-
- void _select(const Point2i &p_from, const Point2i &p_to);
- void _erase_selection();
-
- void _draw_cell(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i &p_autotile_coord, const Transform2D &p_xform);
- void _draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i &p_autotile_coord, const Transform2D &p_xform);
- void _clear_bucket_cache();
-
- void _update_copydata();
-
- Vector<int> get_selected_tiles() const;
- void set_selected_tiles(Vector<int> p_tile);
-
- void _manual_toggled(bool p_enabled);
- void _priority_toggled(bool p_enabled);
- void _text_entered(const String &p_text);
- void _text_changed(const String &p_text);
- void _sbox_input(const Ref<InputEvent> &p_ie);
- void _update_palette();
- void _update_button_tool();
- void _button_tool_select(int p_tool);
- void _menu_option(int p_option);
- void _palette_selected(int index);
- void _palette_multi_selected(int index, bool selected);
- void _palette_input(const Ref<InputEvent> &p_event);
-
- Dictionary _create_cell_dictionary(int tile, bool flip_x, bool flip_y, bool transpose, Vector2 autotile_coord);
- void _start_undo(const String &p_action);
- void _finish_undo();
- void _create_set_cell_undo_redo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new);
- void _set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false, const Point2i &p_autotile_coord = Point2());
-
- void _canvas_mouse_enter();
- void _canvas_mouse_exit();
- void _tileset_settings_changed();
- void _icon_size_changed(float p_value);
-
- void _clear_transform();
- void _flip_horizontal();
- void _flip_vertical();
- void _rotate(int steps);
-
-protected:
- void _notification(int p_what);
- void _node_removed(Node *p_node);
- static void _bind_methods();
- CellOp _get_op_from_cell(const Point2i &p_pos);
-
-public:
- HBoxContainer *get_toolbar() const { return toolbar; }
- HBoxContainer *get_toolbar_right() const { return toolbar_right; }
- Label *get_tile_info() const { return tile_info; }
-
- bool forward_gui_input(const Ref<InputEvent> &p_event);
- void forward_canvas_draw_over_viewport(Control *p_overlay);
-
- void edit(Node *p_tile_map);
-
- TileMapEditor(EditorNode *p_editor);
- ~TileMapEditor();
-};
-
-class TileMapEditorPlugin : public EditorPlugin {
- GDCLASS(TileMapEditorPlugin, EditorPlugin);
-
- TileMapEditor *tile_map_editor;
-
-protected:
- void _notification(int p_what);
-
-public:
- virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return tile_map_editor->forward_gui_input(p_event); }
- virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { tile_map_editor->forward_canvas_draw_over_viewport(p_overlay); }
-
- virtual String get_name() const override { return "TileMap"; }
- bool has_main_screen() const override { return false; }
- virtual void edit(Object *p_object) override;
- virtual bool handles(Object *p_object) const override;
- virtual void make_visible(bool p_visible) override;
-
- TileMapEditorPlugin(EditorNode *p_node);
- ~TileMapEditorPlugin();
-};
-
-#endif // TILE_MAP_EDITOR_PLUGIN_H
diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
deleted file mode 100644
index f683c4b10d..0000000000
--- a/editor/plugins/tile_set_editor_plugin.cpp
+++ /dev/null
@@ -1,3680 +0,0 @@
-/*************************************************************************/
-/* tile_set_editor_plugin.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "tile_set_editor_plugin.h"
-
-#include "core/input/input.h"
-#include "core/os/keyboard.h"
-#include "editor/editor_scale.h"
-#include "editor/plugins/canvas_item_editor_plugin.h"
-#include "scene/2d/physics_body_2d.h"
-#include "scene/2d/sprite_2d.h"
-
-void TileSetEditor::edit(const Ref<TileSet> &p_tileset) {
- tileset = p_tileset;
-
- texture_list->clear();
- texture_map.clear();
- update_texture_list();
-}
-
-void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) {
- for (int i = 0; i < p_node->get_child_count(); i++) {
- Node *child = p_node->get_child(i);
-
- if (!Object::cast_to<Sprite2D>(child)) {
- if (child->get_child_count() > 0) {
- _import_node(child, p_library);
- }
-
- continue;
- }
-
- Sprite2D *mi = Object::cast_to<Sprite2D>(child);
- Ref<Texture2D> texture = mi->get_texture();
- Ref<ShaderMaterial> material = mi->get_material();
-
- if (texture.is_null()) {
- continue;
- }
-
- int id = p_library->find_tile_by_name(mi->get_name());
- if (id < 0) {
- id = p_library->get_last_unused_tile_id();
- p_library->create_tile(id);
- p_library->tile_set_name(id, mi->get_name());
- }
-
- p_library->tile_set_texture(id, texture);
- p_library->tile_set_material(id, material);
-
- p_library->tile_set_modulate(id, mi->get_modulate());
-
- Vector2 phys_offset;
- Size2 s;
-
- if (mi->is_region_enabled()) {
- s = mi->get_region_rect().size;
- p_library->tile_set_region(id, mi->get_region_rect());
- } else {
- const int frame = mi->get_frame();
- const int hframes = mi->get_hframes();
- s = texture->get_size() / Size2(hframes, mi->get_vframes());
- p_library->tile_set_region(id, Rect2(Vector2(frame % hframes, frame / hframes) * s, s));
- }
-
- if (mi->is_centered()) {
- phys_offset += -s / 2;
- }
-
- Vector<TileSet::ShapeData> collisions;
- Ref<NavigationPolygon> nav_poly;
- Ref<OccluderPolygon2D> occluder;
- bool found_collisions = false;
-
- for (int j = 0; j < mi->get_child_count(); j++) {
- Node *child2 = mi->get_child(j);
-
- if (Object::cast_to<NavigationRegion2D>(child2)) {
- nav_poly = Object::cast_to<NavigationRegion2D>(child2)->get_navigation_polygon();
- }
-
- if (Object::cast_to<LightOccluder2D>(child2)) {
- occluder = Object::cast_to<LightOccluder2D>(child2)->get_occluder_polygon();
- }
-
- if (!Object::cast_to<StaticBody2D>(child2)) {
- continue;
- }
-
- found_collisions = true;
-
- StaticBody2D *sb = Object::cast_to<StaticBody2D>(child2);
-
- List<uint32_t> shapes;
- sb->get_shape_owners(&shapes);
-
- for (List<uint32_t>::Element *E = shapes.front(); E; E = E->next()) {
- if (sb->is_shape_owner_disabled(E->get())) {
- continue;
- }
-
- Transform2D shape_transform = sb->get_transform() * sb->shape_owner_get_transform(E->get());
- bool one_way = sb->is_shape_owner_one_way_collision_enabled(E->get());
-
- shape_transform[2] -= phys_offset;
-
- for (int k = 0; k < sb->shape_owner_get_shape_count(E->get()); k++) {
- Ref<Shape2D> shape = sb->shape_owner_get_shape(E->get(), k);
- TileSet::ShapeData shape_data;
- shape_data.shape = shape;
- shape_data.shape_transform = shape_transform;
- shape_data.one_way_collision = one_way;
- collisions.push_back(shape_data);
- }
- }
- }
-
- if (found_collisions) {
- p_library->tile_set_shapes(id, collisions);
- }
-
- p_library->tile_set_texture_offset(id, mi->get_offset());
- p_library->tile_set_navigation_polygon(id, nav_poly);
- p_library->tile_set_light_occluder(id, occluder);
- p_library->tile_set_occluder_offset(id, -phys_offset);
- p_library->tile_set_navigation_polygon_offset(id, -phys_offset);
- p_library->tile_set_z_index(id, mi->get_z_index());
- }
-}
-
-void TileSetEditor::_import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge) {
- if (!p_merge) {
- p_library->clear();
- }
-
- _import_node(p_scene, p_library);
-}
-
-void TileSetEditor::_undo_redo_import_scene(Node *p_scene, bool p_merge) {
- _import_scene(p_scene, tileset, p_merge);
-}
-
-Error TileSetEditor::update_library_file(Node *p_base_scene, Ref<TileSet> ml, bool p_merge) {
- _import_scene(p_base_scene, ml, p_merge);
- return OK;
-}
-
-Variant TileSetEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
- return false;
-}
-
-bool TileSetEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
- Dictionary d = p_data;
-
- if (!d.has("type")) {
- return false;
- }
-
- if (d.has("from") && (Object *)(d["from"]) == texture_list) {
- return false;
- }
-
- if (String(d["type"]) == "resource" && d.has("resource")) {
- RES r = d["resource"];
-
- Ref<Texture2D> texture = r;
-
- if (texture.is_valid()) {
- return true;
- }
- }
-
- if (String(d["type"]) == "files") {
- Vector<String> files = d["files"];
-
- if (files.size() == 0) {
- return false;
- }
-
- for (int i = 0; i < files.size(); i++) {
- String file = files[i];
- String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
-
- if (!ClassDB::is_parent_class(ftype, "Texture")) {
- return false;
- }
- }
-
- return true;
- }
- return false;
-}
-
-void TileSetEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
- if (!can_drop_data_fw(p_point, p_data, p_from)) {
- return;
- }
-
- Dictionary d = p_data;
-
- if (!d.has("type")) {
- return;
- }
-
- if (String(d["type"]) == "resource" && d.has("resource")) {
- RES r = d["resource"];
-
- Ref<Texture2D> texture = r;
-
- if (texture.is_valid()) {
- add_texture(texture);
- }
-
- if (texture_list->get_item_count() > 0) {
- update_texture_list_icon();
- texture_list->select(texture_list->get_item_count() - 1);
- _on_texture_list_selected(texture_list->get_item_count() - 1);
- }
- }
-
- if (String(d["type"]) == "files") {
- Vector<String> files = d["files"];
-
- _on_textures_added(files);
- }
-}
-
-void TileSetEditor::_bind_methods() {
- ClassDB::bind_method("_undo_redo_import_scene", &TileSetEditor::_undo_redo_import_scene);
- ClassDB::bind_method("_on_workspace_process", &TileSetEditor::_on_workspace_process); // Still used by some connect_compat.
- ClassDB::bind_method("_set_snap_step", &TileSetEditor::_set_snap_step);
- ClassDB::bind_method("_set_snap_off", &TileSetEditor::_set_snap_off);
- ClassDB::bind_method("_set_snap_sep", &TileSetEditor::_set_snap_sep);
- ClassDB::bind_method("_validate_current_tile_id", &TileSetEditor::_validate_current_tile_id);
- ClassDB::bind_method("_select_edited_shape_coord", &TileSetEditor::_select_edited_shape_coord);
- ClassDB::bind_method("_sort_tiles", &TileSetEditor::_sort_tiles);
-
- ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &TileSetEditor::get_drag_data_fw);
- ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &TileSetEditor::can_drop_data_fw);
- ClassDB::bind_method(D_METHOD("drop_data_fw"), &TileSetEditor::drop_data_fw);
-
- ClassDB::bind_method("edit", &TileSetEditor::edit);
- ClassDB::bind_method("add_texture", &TileSetEditor::add_texture);
- ClassDB::bind_method("remove_texture", &TileSetEditor::remove_texture);
- ClassDB::bind_method("update_texture_list_icon", &TileSetEditor::update_texture_list_icon);
- ClassDB::bind_method("update_workspace_minsize", &TileSetEditor::update_workspace_minsize);
-}
-
-void TileSetEditor::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_READY: {
- add_theme_constant_override("autohide", 1); // Fixes the dragger always showing up.
- } break;
- case NOTIFICATION_TRANSLATION_CHANGED:
- case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
- case NOTIFICATION_ENTER_TREE:
- case NOTIFICATION_THEME_CHANGED: {
- tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->set_icon(get_theme_icon("ToolAddNode", "EditorIcons"));
- tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->set_icon(get_theme_icon("Remove", "EditorIcons"));
- tileset_toolbar_tools->set_icon(get_theme_icon("Tools", "EditorIcons"));
-
- tool_workspacemode[WORKSPACE_EDIT]->set_icon(get_theme_icon("Edit", "EditorIcons"));
- tool_workspacemode[WORKSPACE_CREATE_SINGLE]->set_icon(get_theme_icon("AddSingleTile", "EditorIcons"));
- tool_workspacemode[WORKSPACE_CREATE_AUTOTILE]->set_icon(get_theme_icon("AddAutotile", "EditorIcons"));
- tool_workspacemode[WORKSPACE_CREATE_ATLAS]->set_icon(get_theme_icon("AddAtlasTile", "EditorIcons"));
-
- tools[TOOL_SELECT]->set_icon(get_theme_icon("ToolSelect", "EditorIcons"));
- tools[BITMASK_COPY]->set_icon(get_theme_icon("Duplicate", "EditorIcons"));
- tools[BITMASK_PASTE]->set_icon(get_theme_icon("Override", "EditorIcons"));
- tools[BITMASK_CLEAR]->set_icon(get_theme_icon("Clear", "EditorIcons"));
- tools[SHAPE_NEW_POLYGON]->set_icon(get_theme_icon("CollisionPolygon2D", "EditorIcons"));
- tools[SHAPE_NEW_RECTANGLE]->set_icon(get_theme_icon("CollisionShape2D", "EditorIcons"));
- if (is_layout_rtl()) {
- tools[SELECT_PREVIOUS]->set_icon(get_theme_icon("ArrowLeft", "EditorIcons"));
- tools[SELECT_NEXT]->set_icon(get_theme_icon("ArrowRight", "EditorIcons"));
- } else {
- tools[SELECT_PREVIOUS]->set_icon(get_theme_icon("ArrowRight", "EditorIcons"));
- tools[SELECT_NEXT]->set_icon(get_theme_icon("ArrowLeft", "EditorIcons"));
- }
- tools[SHAPE_DELETE]->set_icon(get_theme_icon("Remove", "EditorIcons"));
- tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_theme_icon("Snap", "EditorIcons"));
- tools[TOOL_GRID_SNAP]->set_icon(get_theme_icon("SnapGrid", "EditorIcons"));
- tools[ZOOM_OUT]->set_icon(get_theme_icon("ZoomLess", "EditorIcons"));
- tools[ZOOM_1]->set_icon(get_theme_icon("ZoomReset", "EditorIcons"));
- tools[ZOOM_IN]->set_icon(get_theme_icon("ZoomMore", "EditorIcons"));
- tools[VISIBLE_INFO]->set_icon(get_theme_icon("InformationSign", "EditorIcons"));
- _update_toggle_shape_button();
-
- tool_editmode[EDITMODE_REGION]->set_icon(get_theme_icon("RegionEdit", "EditorIcons"));
- tool_editmode[EDITMODE_COLLISION]->set_icon(get_theme_icon("StaticBody2D", "EditorIcons"));
- tool_editmode[EDITMODE_OCCLUSION]->set_icon(get_theme_icon("LightOccluder2D", "EditorIcons"));
- tool_editmode[EDITMODE_NAVIGATION]->set_icon(get_theme_icon("Navigation2D", "EditorIcons"));
- tool_editmode[EDITMODE_BITMASK]->set_icon(get_theme_icon("PackedDataContainer", "EditorIcons"));
- tool_editmode[EDITMODE_PRIORITY]->set_icon(get_theme_icon("MaterialPreviewLight1", "EditorIcons"));
- tool_editmode[EDITMODE_ICON]->set_icon(get_theme_icon("Image", "EditorIcons"));
- tool_editmode[EDITMODE_Z_INDEX]->set_icon(get_theme_icon("Sort", "EditorIcons"));
-
- scroll->add_theme_style_override("bg", get_theme_stylebox("bg", "Tree"));
- } break;
- }
-}
-
-TileSetEditor::TileSetEditor(EditorNode *p_editor) {
- editor = p_editor;
- undo_redo = EditorNode::get_undo_redo();
- current_tile = -1;
-
- VBoxContainer *left_container = memnew(VBoxContainer);
- add_child(left_container);
-
- texture_list = memnew(ItemList);
- left_container->add_child(texture_list);
- texture_list->set_v_size_flags(SIZE_EXPAND_FILL);
- texture_list->set_custom_minimum_size(Size2(200, 0));
- texture_list->connect("item_selected", callable_mp(this, &TileSetEditor::_on_texture_list_selected));
- texture_list->set_drag_forwarding(this);
-
- HBoxContainer *tileset_toolbar_container = memnew(HBoxContainer);
- left_container->add_child(tileset_toolbar_container);
-
- tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE] = memnew(Button);
- tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->set_flat(true);
- tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tileset_toolbar_button_pressed), varray(TOOL_TILESET_ADD_TEXTURE));
- tileset_toolbar_container->add_child(tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]);
- tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->set_tooltip(TTR("Add Texture(s) to TileSet."));
-
- tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE] = memnew(Button);
- tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->set_flat(true);
- tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tileset_toolbar_button_pressed), varray(TOOL_TILESET_REMOVE_TEXTURE));
- tileset_toolbar_container->add_child(tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]);
- tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->set_tooltip(TTR("Remove selected Texture from TileSet."));
-
- Control *toolbar_separator = memnew(Control);
- toolbar_separator->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- tileset_toolbar_container->add_child(toolbar_separator);
-
- tileset_toolbar_tools = memnew(MenuButton);
- tileset_toolbar_tools->set_text(TTR("Tools"));
- tileset_toolbar_tools->get_popup()->add_item(TTR("Create from Scene"), TOOL_TILESET_CREATE_SCENE);
- tileset_toolbar_tools->get_popup()->add_item(TTR("Merge from Scene"), TOOL_TILESET_MERGE_SCENE);
-
- tileset_toolbar_tools->get_popup()->connect("id_pressed", callable_mp(this, &TileSetEditor::_on_tileset_toolbar_button_pressed));
- tileset_toolbar_container->add_child(tileset_toolbar_tools);
-
- //---------------
- VBoxContainer *right_container = memnew(VBoxContainer);
- right_container->set_v_size_flags(SIZE_EXPAND_FILL);
- add_child(right_container);
-
- dragging_point = -1;
- creating_shape = false;
- snap_step = Vector2(32, 32);
- snap_offset = WORKSPACE_MARGIN;
-
- set_custom_minimum_size(Size2(0, 150));
-
- VBoxContainer *main_vb = memnew(VBoxContainer);
- right_container->add_child(main_vb);
- main_vb->set_v_size_flags(SIZE_EXPAND_FILL);
-
- HBoxContainer *tool_hb = memnew(HBoxContainer);
- Ref<ButtonGroup> g(memnew(ButtonGroup));
-
- String workspace_label[WORKSPACE_MODE_MAX] = {
- TTR("Edit"),
- TTR("New Single Tile"),
- TTR("New Autotile"),
- TTR("New Atlas")
- };
- for (int i = 0; i < (int)WORKSPACE_MODE_MAX; i++) {
- tool_workspacemode[i] = memnew(Button);
- tool_workspacemode[i]->set_text(workspace_label[i]);
- tool_workspacemode[i]->set_toggle_mode(true);
- tool_workspacemode[i]->set_button_group(g);
- tool_workspacemode[i]->connect("pressed", callable_mp(this, &TileSetEditor::_on_workspace_mode_changed), varray(i));
- tool_hb->add_child(tool_workspacemode[i]);
- }
-
- Control *spacer = memnew(Control);
- spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- tool_hb->add_child(spacer);
- tool_hb->move_child(spacer, WORKSPACE_CREATE_SINGLE);
-
- tools[SELECT_NEXT] = memnew(Button);
- tool_hb->add_child(tools[SELECT_NEXT]);
- tool_hb->move_child(tools[SELECT_NEXT], WORKSPACE_CREATE_SINGLE);
- tools[SELECT_NEXT]->set_flat(true);
- tools[SELECT_NEXT]->set_shortcut(ED_SHORTCUT("tileset_editor/next_shape", TTR("Next Coordinate"), KEY_PAGEDOWN));
- tools[SELECT_NEXT]->set_shortcut_context(this);
- tools[SELECT_NEXT]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(SELECT_NEXT));
- tools[SELECT_NEXT]->set_tooltip(TTR("Select the next shape, subtile, or Tile."));
- tools[SELECT_PREVIOUS] = memnew(Button);
- tool_hb->add_child(tools[SELECT_PREVIOUS]);
- tool_hb->move_child(tools[SELECT_PREVIOUS], WORKSPACE_CREATE_SINGLE);
- tools[SELECT_PREVIOUS]->set_flat(true);
- tools[SELECT_PREVIOUS]->set_shortcut(ED_SHORTCUT("tileset_editor/previous_shape", TTR("Previous Coordinate"), KEY_PAGEUP));
- tools[SELECT_PREVIOUS]->set_shortcut_context(this);
- tools[SELECT_PREVIOUS]->set_tooltip(TTR("Select the previous shape, subtile, or Tile."));
- tools[SELECT_PREVIOUS]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(SELECT_PREVIOUS));
-
- VSeparator *separator_shape_selection = memnew(VSeparator);
- tool_hb->add_child(separator_shape_selection);
- tool_hb->move_child(separator_shape_selection, WORKSPACE_CREATE_SINGLE);
-
- tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true);
- workspace_mode = WORKSPACE_EDIT;
-
- main_vb->add_child(tool_hb);
- main_vb->add_child(memnew(HSeparator));
-
- tool_hb = memnew(HBoxContainer);
-
- g = Ref<ButtonGroup>(memnew(ButtonGroup));
- String label[EDITMODE_MAX] = {
- TTR("Region"),
- TTR("Collision"),
- TTR("Occlusion"),
- TTR("Navigation"),
- TTR("Bitmask"),
- TTR("Priority"),
- TTR("Icon"),
- TTR("Z Index")
- };
- for (int i = 0; i < (int)EDITMODE_MAX; i++) {
- tool_editmode[i] = memnew(Button);
- tool_editmode[i]->set_text(label[i]);
- tool_editmode[i]->set_toggle_mode(true);
- tool_editmode[i]->set_button_group(g);
- tool_editmode[i]->connect("pressed", callable_mp(this, &TileSetEditor::_on_edit_mode_changed), varray(i));
- tool_hb->add_child(tool_editmode[i]);
- }
- tool_editmode[EDITMODE_COLLISION]->set_pressed(true);
- edit_mode = EDITMODE_COLLISION;
-
- tool_editmode[EDITMODE_REGION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_region", TTR("Region Mode"), KEY_1));
- tool_editmode[EDITMODE_COLLISION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_collision", TTR("Collision Mode"), KEY_2));
- tool_editmode[EDITMODE_OCCLUSION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_occlusion", TTR("Occlusion Mode"), KEY_3));
- tool_editmode[EDITMODE_NAVIGATION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_navigation", TTR("Navigation Mode"), KEY_4));
- tool_editmode[EDITMODE_BITMASK]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_bitmask", TTR("Bitmask Mode"), KEY_5));
- tool_editmode[EDITMODE_PRIORITY]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_priority", TTR("Priority Mode"), KEY_6));
- tool_editmode[EDITMODE_ICON]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_icon", TTR("Icon Mode"), KEY_7));
- tool_editmode[EDITMODE_Z_INDEX]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_z_index", TTR("Z Index Mode"), KEY_8));
-
- tool_editmode[EDITMODE_REGION]->set_shortcut_context(this);
- tool_editmode[EDITMODE_REGION]->set_shortcut_context(this);
- tool_editmode[EDITMODE_COLLISION]->set_shortcut_context(this);
- tool_editmode[EDITMODE_OCCLUSION]->set_shortcut_context(this);
- tool_editmode[EDITMODE_NAVIGATION]->set_shortcut_context(this);
- tool_editmode[EDITMODE_BITMASK]->set_shortcut_context(this);
- tool_editmode[EDITMODE_PRIORITY]->set_shortcut_context(this);
- tool_editmode[EDITMODE_ICON]->set_shortcut_context(this);
- tool_editmode[EDITMODE_Z_INDEX]->set_shortcut_context(this);
-
- main_vb->add_child(tool_hb);
- separator_editmode = memnew(HSeparator);
- main_vb->add_child(separator_editmode);
-
- toolbar = memnew(HBoxContainer);
- Ref<ButtonGroup> tg(memnew(ButtonGroup));
-
- tools[TOOL_SELECT] = memnew(Button);
- toolbar->add_child(tools[TOOL_SELECT]);
- tools[TOOL_SELECT]->set_flat(true);
- tools[TOOL_SELECT]->set_toggle_mode(true);
- tools[TOOL_SELECT]->set_button_group(tg);
- tools[TOOL_SELECT]->set_pressed(true);
- tools[TOOL_SELECT]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(TOOL_SELECT));
-
- separator_bitmask = memnew(VSeparator);
- toolbar->add_child(separator_bitmask);
- tools[BITMASK_COPY] = memnew(Button);
- tools[BITMASK_COPY]->set_flat(true);
- tools[BITMASK_COPY]->set_tooltip(TTR("Copy bitmask."));
- tools[BITMASK_COPY]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(BITMASK_COPY));
- toolbar->add_child(tools[BITMASK_COPY]);
- tools[BITMASK_PASTE] = memnew(Button);
- tools[BITMASK_PASTE]->set_flat(true);
- tools[BITMASK_PASTE]->set_tooltip(TTR("Paste bitmask."));
- tools[BITMASK_PASTE]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(BITMASK_PASTE));
- toolbar->add_child(tools[BITMASK_PASTE]);
- tools[BITMASK_CLEAR] = memnew(Button);
- tools[BITMASK_CLEAR]->set_flat(true);
- tools[BITMASK_CLEAR]->set_tooltip(TTR("Erase bitmask."));
- tools[BITMASK_CLEAR]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(BITMASK_CLEAR));
- toolbar->add_child(tools[BITMASK_CLEAR]);
-
- tools[SHAPE_NEW_RECTANGLE] = memnew(Button);
- toolbar->add_child(tools[SHAPE_NEW_RECTANGLE]);
- tools[SHAPE_NEW_RECTANGLE]->set_flat(true);
- tools[SHAPE_NEW_RECTANGLE]->set_toggle_mode(true);
- tools[SHAPE_NEW_RECTANGLE]->set_button_group(tg);
- tools[SHAPE_NEW_RECTANGLE]->set_tooltip(TTR("Create a new rectangle."));
- tools[SHAPE_NEW_RECTANGLE]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(SHAPE_NEW_RECTANGLE));
- tools[SHAPE_NEW_RECTANGLE]->set_shortcut(ED_SHORTCUT("tileset_editor/shape_new_rectangle", TTR("New Rectangle"), KEY_MASK_SHIFT | KEY_R));
-
- tools[SHAPE_NEW_POLYGON] = memnew(Button);
- toolbar->add_child(tools[SHAPE_NEW_POLYGON]);
- tools[SHAPE_NEW_POLYGON]->set_flat(true);
- tools[SHAPE_NEW_POLYGON]->set_toggle_mode(true);
- tools[SHAPE_NEW_POLYGON]->set_button_group(tg);
- tools[SHAPE_NEW_POLYGON]->set_tooltip(TTR("Create a new polygon."));
- tools[SHAPE_NEW_POLYGON]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(SHAPE_NEW_POLYGON));
- tools[SHAPE_NEW_POLYGON]->set_shortcut(ED_SHORTCUT("tileset_editor/shape_new_polygon", TTR("New Polygon"), KEY_MASK_SHIFT | KEY_P));
-
- separator_shape_toggle = memnew(VSeparator);
- toolbar->add_child(separator_shape_toggle);
- tools[SHAPE_TOGGLE_TYPE] = memnew(Button);
- tools[SHAPE_TOGGLE_TYPE]->set_flat(true);
- tools[SHAPE_TOGGLE_TYPE]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(SHAPE_TOGGLE_TYPE));
- toolbar->add_child(tools[SHAPE_TOGGLE_TYPE]);
-
- separator_delete = memnew(VSeparator);
- toolbar->add_child(separator_delete);
- tools[SHAPE_DELETE] = memnew(Button);
- tools[SHAPE_DELETE]->set_flat(true);
- tools[SHAPE_DELETE]->connect("pressed", callable_mp(this, &TileSetEditor::_on_tool_clicked), varray(SHAPE_DELETE));
- tools[SHAPE_DELETE]->set_shortcut(ED_SHORTCUT("tileset_editor/shape_delete", TTR("Delete Selected Shape"), KEY_MASK_SHIFT | KEY_BACKSPACE));
- toolbar->add_child(tools[SHAPE_DELETE]);
-
- spin_priority = memnew(SpinBox);
- spin_priority->set_min(1);
- spin_priority->set_max(255);
- spin_priority->set_step(1);
- spin_priority->set_custom_minimum_size(Size2(100, 0));
- spin_priority->connect("value_changed", callable_mp(this, &TileSetEditor::_on_priority_changed));
- spin_priority->hide();
- toolbar->add_child(spin_priority);
-
- spin_z_index = memnew(SpinBox);
- spin_z_index->set_min(RS::CANVAS_ITEM_Z_MIN);
- spin_z_index->set_max(RS::CANVAS_ITEM_Z_MAX);
- spin_z_index->set_step(1);
- spin_z_index->set_custom_minimum_size(Size2(100, 0));
- spin_z_index->connect("value_changed", callable_mp(this, &TileSetEditor::_on_z_index_changed));
- spin_z_index->hide();
- toolbar->add_child(spin_z_index);
-
- separator_grid = memnew(VSeparator);
- toolbar->add_child(separator_grid);
- tools[SHAPE_KEEP_INSIDE_TILE] = memnew(Button);
- tools[SHAPE_KEEP_INSIDE_TILE]->set_flat(true);
- tools[SHAPE_KEEP_INSIDE_TILE]->set_toggle_mode(true);
- tools[SHAPE_KEEP_INSIDE_TILE]->set_pressed(true);
- tools[SHAPE_KEEP_INSIDE_TILE]->set_tooltip(TTR("Keep polygon inside region Rect."));
- toolbar->add_child(tools[SHAPE_KEEP_INSIDE_TILE]);
- tools[TOOL_GRID_SNAP] = memnew(Button);
- tools[TOOL_GRID_SNAP]->set_flat(true);
- tools[TOOL_GRID_SNAP]->set_toggle_mode(true);
- tools[TOOL_GRID_SNAP]->set_tooltip(TTR("Enable snap and show grid (configurable via the Inspector)."));
- tools[TOOL_GRID_SNAP]->connect("toggled", callable_mp(this, &TileSetEditor::_on_grid_snap_toggled));
- toolbar->add_child(tools[TOOL_GRID_SNAP]);
-
- Control *separator = memnew(Control);
- separator->set_h_size_flags(SIZE_EXPAND_FILL);
- toolbar->add_child(separator);
-
- tools[ZOOM_OUT] = memnew(Button);
- tools[ZOOM_OUT]->set_flat(true);
- tools[ZOOM_OUT]->connect("pressed", callable_mp(this, &TileSetEditor::_zoom_out));
- toolbar->add_child(tools[ZOOM_OUT]);
- tools[ZOOM_OUT]->set_tooltip(TTR("Zoom Out"));
- tools[ZOOM_1] = memnew(Button);
- tools[ZOOM_1]->set_flat(true);
- tools[ZOOM_1]->connect("pressed", callable_mp(this, &TileSetEditor::_zoom_reset));
- toolbar->add_child(tools[ZOOM_1]);
- tools[ZOOM_1]->set_tooltip(TTR("Zoom Reset"));
- tools[ZOOM_IN] = memnew(Button);
- tools[ZOOM_IN]->set_flat(true);
- tools[ZOOM_IN]->connect("pressed", callable_mp(this, &TileSetEditor::_zoom_in));
- toolbar->add_child(tools[ZOOM_IN]);
- tools[ZOOM_IN]->set_tooltip(TTR("Zoom In"));
-
- tools[VISIBLE_INFO] = memnew(Button);
- tools[VISIBLE_INFO]->set_flat(true);
- tools[VISIBLE_INFO]->set_toggle_mode(true);
- tools[VISIBLE_INFO]->set_tooltip(TTR("Display Tile Names (Hold Alt Key)"));
- toolbar->add_child(tools[VISIBLE_INFO]);
-
- main_vb->add_child(toolbar);
-
- scroll = memnew(ScrollContainer);
- main_vb->add_child(scroll);
- scroll->set_v_size_flags(SIZE_EXPAND_FILL);
- scroll->connect("gui_input", callable_mp(this, &TileSetEditor::_on_scroll_container_input));
- scroll->set_clip_contents(true);
-
- empty_message = memnew(Label);
- empty_message->set_text(TTR("Add or select a texture on the left panel to edit the tiles bound to it."));
- empty_message->set_valign(Label::VALIGN_CENTER);
- empty_message->set_align(Label::ALIGN_CENTER);
- empty_message->set_autowrap(true);
- empty_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
- empty_message->set_v_size_flags(SIZE_EXPAND_FILL);
- main_vb->add_child(empty_message);
-
- workspace_container = memnew(Control);
- scroll->add_child(workspace_container);
-
- workspace_overlay = memnew(Control);
- workspace_overlay->connect("draw", callable_mp(this, &TileSetEditor::_on_workspace_overlay_draw));
- workspace_container->add_child(workspace_overlay);
-
- workspace = memnew(Control);
- workspace->set_focus_mode(FOCUS_ALL);
- workspace->connect("draw", callable_mp(this, &TileSetEditor::_on_workspace_draw));
- workspace->connect("gui_input", callable_mp(this, &TileSetEditor::_on_workspace_input));
- workspace->set_draw_behind_parent(true);
- workspace_overlay->add_child(workspace);
-
- preview = memnew(Sprite2D);
- workspace->add_child(preview);
- preview->set_centered(false);
- preview->set_draw_behind_parent(true);
- preview->set_position(WORKSPACE_MARGIN);
-
- //---------------
- cd = memnew(ConfirmationDialog);
- add_child(cd);
- cd->connect("confirmed", callable_mp(this, &TileSetEditor::_on_tileset_toolbar_confirm));
-
- //---------------
- err_dialog = memnew(AcceptDialog);
- add_child(err_dialog);
-
- //---------------
- texture_dialog = memnew(EditorFileDialog);
- texture_dialog->set_access(EditorFileDialog::ACCESS_RESOURCES);
- texture_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILES);
- texture_dialog->clear_filters();
- List<String> extensions;
-
- ResourceLoader::get_recognized_extensions_for_type("Texture2D", &extensions);
- for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- texture_dialog->add_filter("*." + E->get() + " ; " + E->get().to_upper());
- }
- add_child(texture_dialog);
- texture_dialog->connect("files_selected", callable_mp(this, &TileSetEditor::_on_textures_added));
-
- //---------------
- helper = memnew(TilesetEditorContext(this));
- tile_names_visible = false;
-
- // Config scale.
- max_scale = 16.0f;
- min_scale = 0.01f;
- scale_ratio = 1.2f;
-}
-
-TileSetEditor::~TileSetEditor() {
- if (helper) {
- memdelete(helper);
- }
-}
-
-void TileSetEditor::_on_tileset_toolbar_button_pressed(int p_index) {
- option = p_index;
- switch (option) {
- case TOOL_TILESET_ADD_TEXTURE: {
- texture_dialog->popup_file_dialog();
- } break;
- case TOOL_TILESET_REMOVE_TEXTURE: {
- if (get_current_texture().is_valid()) {
- cd->set_text(TTR("Remove selected texture? This will remove all tiles which use it."));
- cd->popup_centered(Size2(300, 60));
- } else {
- err_dialog->set_text(TTR("You haven't selected a texture to remove."));
- err_dialog->popup_centered(Size2(300, 60));
- }
- } break;
- case TOOL_TILESET_CREATE_SCENE: {
- cd->set_text(TTR("Create from scene? This will overwrite all current tiles."));
- cd->popup_centered(Size2(300, 60));
- } break;
- case TOOL_TILESET_MERGE_SCENE: {
- cd->set_text(TTR("Merge from scene?"));
- cd->popup_centered(Size2(300, 60));
- } break;
- }
-}
-
-void TileSetEditor::_on_tileset_toolbar_confirm() {
- switch (option) {
- case TOOL_TILESET_REMOVE_TEXTURE: {
- RID current_rid = get_current_texture()->get_rid();
- List<int> ids;
- tileset->get_tile_list(&ids);
-
- undo_redo->create_action(TTR("Remove Texture"));
- for (List<int>::Element *E = ids.front(); E; E = E->next()) {
- if (tileset->tile_get_texture(E->get())->get_rid() == current_rid) {
- undo_redo->add_do_method(tileset.ptr(), "remove_tile", E->get());
- _undo_tile_removal(E->get());
- }
- }
- undo_redo->add_do_method(this, "remove_texture", get_current_texture());
- undo_redo->add_undo_method(this, "add_texture", get_current_texture());
- undo_redo->add_undo_method(this, "update_texture_list_icon");
- undo_redo->commit_action();
- } break;
- case TOOL_TILESET_MERGE_SCENE:
- case TOOL_TILESET_CREATE_SCENE: {
- EditorNode *en = editor;
- Node *scene = en->get_edited_scene();
- if (!scene) {
- break;
- }
-
- List<int> ids;
- tileset->get_tile_list(&ids);
-
- undo_redo->create_action(option == TOOL_TILESET_MERGE_SCENE ? TTR("Merge Tileset from Scene") : TTR("Create Tileset from Scene"));
- undo_redo->add_do_method(this, "_undo_redo_import_scene", scene, option == TOOL_TILESET_MERGE_SCENE);
- undo_redo->add_undo_method(tileset.ptr(), "clear");
- for (List<int>::Element *E = ids.front(); E; E = E->next()) {
- _undo_tile_removal(E->get());
- }
- undo_redo->add_do_method(this, "edit", tileset);
- undo_redo->add_undo_method(this, "edit", tileset);
- undo_redo->commit_action();
- } break;
- }
-}
-
-void TileSetEditor::_on_texture_list_selected(int p_index) {
- if (get_current_texture().is_valid()) {
- current_item_index = p_index;
- preview->set_texture(get_current_texture());
- update_workspace_tile_mode();
- update_workspace_minsize();
- } else {
- current_item_index = -1;
- preview->set_texture(nullptr);
- workspace->set_custom_minimum_size(Size2i());
- update_workspace_tile_mode();
- }
-
- set_current_tile(-1);
- workspace->update();
-}
-
-void TileSetEditor::_on_textures_added(const PackedStringArray &p_paths) {
- int invalid_count = 0;
- for (int i = 0; i < p_paths.size(); i++) {
- Ref<Texture2D> t = Ref<Texture2D>(ResourceLoader::load(p_paths[i]));
-
- ERR_CONTINUE_MSG(!t.is_valid(), "'" + p_paths[i] + "' is not a valid texture.");
-
- if (texture_map.has(t->get_rid())) {
- invalid_count++;
- } else {
- add_texture(t);
- }
- }
-
- if (texture_list->get_item_count() > 0) {
- update_texture_list_icon();
- texture_list->select(texture_list->get_item_count() - 1);
- _on_texture_list_selected(texture_list->get_item_count() - 1);
- }
-
- if (invalid_count > 0) {
- err_dialog->set_text(vformat(TTR("%s file(s) were not added because was already on the list."), String::num(invalid_count, 0)));
- err_dialog->popup_centered(Size2(300, 60));
- }
-}
-
-void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) {
- draw_handles = false;
- creating_shape = false;
- edit_mode = (EditMode)p_edit_mode;
- switch (edit_mode) {
- case EDITMODE_REGION: {
- tools[TOOL_SELECT]->show();
-
- separator_bitmask->hide();
- tools[BITMASK_COPY]->hide();
- tools[BITMASK_PASTE]->hide();
- tools[BITMASK_CLEAR]->hide();
- tools[SHAPE_NEW_POLYGON]->hide();
- tools[SHAPE_NEW_RECTANGLE]->hide();
-
- if (workspace_mode == WORKSPACE_EDIT) {
- separator_delete->show();
- tools[SHAPE_DELETE]->show();
- } else {
- separator_delete->hide();
- tools[SHAPE_DELETE]->hide();
- }
-
- separator_grid->show();
- tools[SHAPE_KEEP_INSIDE_TILE]->hide();
- tools[TOOL_GRID_SNAP]->show();
-
- tools[TOOL_SELECT]->set_pressed(true);
- tools[TOOL_SELECT]->set_tooltip(TTR("Drag handles to edit Rect.\nClick on another Tile to edit it."));
- tools[SHAPE_DELETE]->set_tooltip(TTR("Delete selected Rect."));
- spin_priority->hide();
- spin_z_index->hide();
- } break;
- case EDITMODE_COLLISION:
- case EDITMODE_OCCLUSION:
- case EDITMODE_NAVIGATION: {
- tools[TOOL_SELECT]->show();
-
- separator_bitmask->hide();
- tools[BITMASK_COPY]->hide();
- tools[BITMASK_PASTE]->hide();
- tools[BITMASK_CLEAR]->hide();
- tools[SHAPE_NEW_POLYGON]->show();
- tools[SHAPE_NEW_RECTANGLE]->show();
-
- separator_delete->show();
- tools[SHAPE_DELETE]->show();
-
- separator_grid->show();
- tools[SHAPE_KEEP_INSIDE_TILE]->show();
- tools[TOOL_GRID_SNAP]->show();
-
- tools[TOOL_SELECT]->set_tooltip(TTR("Select current edited sub-tile.\nClick on another Tile to edit it."));
- tools[SHAPE_DELETE]->set_tooltip(TTR("Delete polygon."));
- spin_priority->hide();
- spin_z_index->hide();
-
- _select_edited_shape_coord();
- } break;
- case EDITMODE_BITMASK: {
- tools[TOOL_SELECT]->show();
-
- separator_bitmask->show();
- tools[BITMASK_COPY]->show();
- tools[BITMASK_PASTE]->show();
- tools[BITMASK_CLEAR]->show();
- tools[SHAPE_NEW_POLYGON]->hide();
- tools[SHAPE_NEW_RECTANGLE]->hide();
-
- separator_delete->hide();
- tools[SHAPE_DELETE]->hide();
-
- tools[SHAPE_KEEP_INSIDE_TILE]->hide();
-
- tools[TOOL_SELECT]->set_pressed(true);
- tools[TOOL_SELECT]->set_tooltip(TTR("LMB: Set bit on.\nRMB: Set bit off.\nShift+LMB: Set wildcard bit.\nClick on another Tile to edit it."));
- spin_priority->hide();
- } break;
- case EDITMODE_Z_INDEX:
- case EDITMODE_PRIORITY:
- case EDITMODE_ICON: {
- tools[TOOL_SELECT]->show();
-
- separator_bitmask->hide();
- tools[BITMASK_COPY]->hide();
- tools[BITMASK_PASTE]->hide();
- tools[BITMASK_CLEAR]->hide();
- tools[SHAPE_NEW_POLYGON]->hide();
- tools[SHAPE_NEW_RECTANGLE]->hide();
-
- separator_delete->hide();
- tools[SHAPE_DELETE]->hide();
-
- separator_grid->show();
- tools[SHAPE_KEEP_INSIDE_TILE]->hide();
- tools[TOOL_GRID_SNAP]->show();
-
- if (edit_mode == EDITMODE_ICON) {
- tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to use as icon, this will be also used on invalid autotile bindings.\nClick on another Tile to edit it."));
- spin_priority->hide();
- spin_z_index->hide();
- } else if (edit_mode == EDITMODE_PRIORITY) {
- tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to change its priority.\nClick on another Tile to edit it."));
- spin_priority->show();
- spin_z_index->hide();
- } else {
- tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to change its z index.\nClick on another Tile to edit it."));
- spin_priority->hide();
- spin_z_index->show();
- }
- } break;
- default: {
- }
- }
- _update_toggle_shape_button();
- workspace->update();
-}
-
-void TileSetEditor::_on_workspace_mode_changed(int p_workspace_mode) {
- workspace_mode = (WorkspaceMode)p_workspace_mode;
- if (p_workspace_mode == WORKSPACE_EDIT) {
- update_workspace_tile_mode();
- } else {
- for (int i = 0; i < EDITMODE_MAX; i++) {
- tool_editmode[i]->hide();
- }
- tool_editmode[EDITMODE_REGION]->show();
- tool_editmode[EDITMODE_REGION]->set_pressed(true);
- _on_edit_mode_changed(EDITMODE_REGION);
- separator_editmode->show();
- }
-}
-
-void TileSetEditor::_on_workspace_draw() {
- if (tileset.is_null() || !get_current_texture().is_valid()) {
- return;
- }
-
- const Color COLOR_AUTOTILE = Color(0.3, 0.6, 1);
- const Color COLOR_SINGLE = Color(1, 1, 0.3);
- const Color COLOR_ATLAS = Color(0.8, 0.8, 0.8);
- const Color COLOR_SUBDIVISION = Color(0.3, 0.7, 0.6);
-
- draw_handles = false;
-
- draw_highlight_current_tile();
-
- draw_grid_snap();
- if (get_current_tile() >= 0) {
- int spacing = tileset->autotile_get_spacing(get_current_tile());
- Vector2 size = tileset->autotile_get_size(get_current_tile());
- Rect2i region = tileset->tile_get_region(get_current_tile());
-
- switch (edit_mode) {
- case EDITMODE_ICON: {
- Vector2 coord = tileset->autotile_get_icon_coordinate(get_current_tile());
- draw_highlight_subtile(coord);
- } break;
- case EDITMODE_BITMASK: {
- Color c(1, 0, 0, 0.5);
- Color ci(0.3, 0.6, 1, 0.5);
- for (int x = 0; x < region.size.x / (spacing + size.x); x++) {
- for (int y = 0; y < region.size.y / (spacing + size.y); y++) {
- Vector2 coord(x, y);
- Point2 anchor(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
- anchor += WORKSPACE_MARGIN;
- anchor += region.position;
- uint32_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord);
- if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
- if (mask & TileSet::BIND_IGNORE_TOPLEFT) {
- workspace->draw_rect(Rect2(anchor, size / 4), ci);
- workspace->draw_rect(Rect2(anchor + size / 4, size / 4), ci);
- } else if (mask & TileSet::BIND_TOPLEFT) {
- workspace->draw_rect(Rect2(anchor, size / 2), c);
- }
- if (mask & TileSet::BIND_IGNORE_TOPRIGHT) {
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, 0), size / 4), ci);
- workspace->draw_rect(Rect2(anchor + Vector2(size.x * 3 / 4, size.y / 4), size / 4), ci);
- } else if (mask & TileSet::BIND_TOPRIGHT) {
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, 0), size / 2), c);
- }
- if (mask & TileSet::BIND_IGNORE_BOTTOMLEFT) {
- workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 2), size / 4), ci);
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 4, size.y * 3 / 4), size / 4), ci);
- } else if (mask & TileSet::BIND_BOTTOMLEFT) {
- workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 2), size / 2), c);
- }
- if (mask & TileSet::BIND_IGNORE_BOTTOMRIGHT) {
- workspace->draw_rect(Rect2(anchor + size / 2, size / 4), ci);
- workspace->draw_rect(Rect2(anchor + size * 3 / 4, size / 4), ci);
- } else if (mask & TileSet::BIND_BOTTOMRIGHT) {
- workspace->draw_rect(Rect2(anchor + size / 2, size / 2), c);
- }
- } else {
- if (mask & TileSet::BIND_IGNORE_TOPLEFT) {
- workspace->draw_rect(Rect2(anchor, size / 6), ci);
- workspace->draw_rect(Rect2(anchor + size / 6, size / 6), ci);
- } else if (mask & TileSet::BIND_TOPLEFT) {
- workspace->draw_rect(Rect2(anchor, size / 3), c);
- }
- if (mask & TileSet::BIND_IGNORE_TOP) {
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, 0), size / 6), ci);
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, size.y / 6), size / 6), ci);
- } else if (mask & TileSet::BIND_TOP) {
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, 0), size / 3), c);
- }
- if (mask & TileSet::BIND_IGNORE_TOPRIGHT) {
- workspace->draw_rect(Rect2(anchor + Vector2(size.x * 4 / 6, 0), size / 6), ci);
- workspace->draw_rect(Rect2(anchor + Vector2(size.x * 5 / 6, size.y / 6), size / 6), ci);
- } else if (mask & TileSet::BIND_TOPRIGHT) {
- workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, 0), size / 3), c);
- }
- if (mask & TileSet::BIND_IGNORE_LEFT) {
- workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 3), size / 6), ci);
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 6, size.y / 2), size / 6), ci);
- } else if (mask & TileSet::BIND_LEFT) {
- workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 3), size / 3), c);
- }
- if (mask & TileSet::BIND_IGNORE_CENTER) {
- workspace->draw_rect(Rect2(anchor + size / 3, size / 6), ci);
- workspace->draw_rect(Rect2(anchor + size / 2, size / 6), ci);
- } else if (mask & TileSet::BIND_CENTER) {
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, size.y / 3), size / 3), c);
- }
- if (mask & TileSet::BIND_IGNORE_RIGHT) {
- workspace->draw_rect(Rect2(anchor + Vector2(size.x * 4 / 6, size.y / 3), size / 6), ci);
- workspace->draw_rect(Rect2(anchor + Vector2(size.x * 5 / 6, size.y / 2), size / 6), ci);
- } else if (mask & TileSet::BIND_RIGHT) {
- workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, size.y / 3), size / 3), c);
- }
- if (mask & TileSet::BIND_IGNORE_BOTTOMLEFT) {
- workspace->draw_rect(Rect2(anchor + Vector2(0, size.y * 4 / 6), size / 6), ci);
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 6, size.y * 5 / 6), size / 6), ci);
- } else if (mask & TileSet::BIND_BOTTOMLEFT) {
- workspace->draw_rect(Rect2(anchor + Vector2(0, (size.y / 3) * 2), size / 3), c);
- }
- if (mask & TileSet::BIND_IGNORE_BOTTOM) {
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, size.y * 4 / 6), size / 6), ci);
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, size.y * 5 / 6), size / 6), ci);
- } else if (mask & TileSet::BIND_BOTTOM) {
- workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, (size.y / 3) * 2), size / 3), c);
- }
- if (mask & TileSet::BIND_IGNORE_BOTTOMRIGHT) {
- workspace->draw_rect(Rect2(anchor + size * 4 / 6, size / 6), ci);
- workspace->draw_rect(Rect2(anchor + size * 5 / 6, size / 6), ci);
- } else if (mask & TileSet::BIND_BOTTOMRIGHT) {
- workspace->draw_rect(Rect2(anchor + (size / 3) * 2, size / 3), c);
- }
- }
- }
- }
- } break;
- case EDITMODE_COLLISION:
- case EDITMODE_OCCLUSION:
- case EDITMODE_NAVIGATION: {
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
- draw_highlight_subtile(edited_shape_coord);
- }
- draw_polygon_shapes();
- draw_grid_snap();
- } break;
- case EDITMODE_PRIORITY: {
- spin_priority->set_value(tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord));
- uint32_t mask = tileset->autotile_get_bitmask(get_current_tile(), edited_shape_coord);
- Vector<Vector2> queue_others;
- int total = 0;
- for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) {
- if (E->value() == mask) {
- total += tileset->autotile_get_subtile_priority(get_current_tile(), E->key());
- if (E->key() != edited_shape_coord) {
- queue_others.push_back(E->key());
- }
- }
- }
- spin_priority->set_suffix(" / " + String::num(total, 0));
- draw_highlight_subtile(edited_shape_coord, queue_others);
- } break;
- case EDITMODE_Z_INDEX: {
- spin_z_index->set_value(tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord));
- draw_highlight_subtile(edited_shape_coord);
- } break;
- default: {
- }
- }
- }
-
- RID current_texture_rid = get_current_texture()->get_rid();
- List<int> *tiles = new List<int>();
- tileset->get_tile_list(tiles);
- for (List<int>::Element *E = tiles->front(); E; E = E->next()) {
- int t_id = E->get();
- if (tileset->tile_get_texture(t_id)->get_rid() == current_texture_rid && (t_id != get_current_tile() || edit_mode != EDITMODE_REGION || workspace_mode != WORKSPACE_EDIT)) {
- Rect2i region = tileset->tile_get_region(t_id);
- region.position += WORKSPACE_MARGIN;
- Color c;
- if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) {
- c = COLOR_SINGLE;
- } else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) {
- c = COLOR_AUTOTILE;
- } else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) {
- c = COLOR_ATLAS;
- }
- draw_tile_subdivision(t_id, COLOR_SUBDIVISION);
- workspace->draw_rect(region, c, false);
- }
- }
- delete tiles;
-
- if (edit_mode == EDITMODE_REGION) {
- if (workspace_mode != WORKSPACE_EDIT) {
- Rect2i region = edited_region;
- Color c;
- if (workspace_mode == WORKSPACE_CREATE_SINGLE) {
- c = COLOR_SINGLE;
- } else if (workspace_mode == WORKSPACE_CREATE_AUTOTILE) {
- c = COLOR_AUTOTILE;
- } else if (workspace_mode == WORKSPACE_CREATE_ATLAS) {
- c = COLOR_ATLAS;
- }
- workspace->draw_rect(region, c, false);
- draw_edited_region_subdivision();
- } else {
- int t_id = get_current_tile();
- if (t_id < 0) {
- return;
- }
-
- Rect2i region;
- if (draw_edited_region) {
- region = edited_region;
- } else {
- region = tileset->tile_get_region(t_id);
- region.position += WORKSPACE_MARGIN;
- }
-
- if (draw_edited_region) {
- draw_edited_region_subdivision();
- } else {
- draw_tile_subdivision(t_id, COLOR_SUBDIVISION);
- }
-
- Color c;
- if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) {
- c = COLOR_SINGLE;
- } else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) {
- c = COLOR_AUTOTILE;
- } else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) {
- c = COLOR_ATLAS;
- }
- workspace->draw_rect(region, c, false);
- }
- }
-
- workspace_overlay->update();
-}
-
-void TileSetEditor::_on_workspace_process() {
- if (Input::get_singleton()->is_key_pressed(KEY_ALT) || tools[VISIBLE_INFO]->is_pressed()) {
- if (!tile_names_visible) {
- tile_names_visible = true;
- workspace_overlay->update();
- }
- } else if (tile_names_visible) {
- tile_names_visible = false;
- workspace_overlay->update();
- }
-}
-
-void TileSetEditor::_on_workspace_overlay_draw() {
- if (!tileset.is_valid() || !get_current_texture().is_valid()) {
- return;
- }
-
- const Color COLOR_AUTOTILE = Color(0.266373, 0.565288, 0.988281);
- const Color COLOR_SINGLE = Color(0.988281, 0.909323, 0.266373);
- const Color COLOR_ATLAS = Color(0.78653, 0.812835, 0.832031);
-
- if (tile_names_visible) {
- RID current_texture_rid = get_current_texture()->get_rid();
- List<int> *tiles = new List<int>();
- tileset->get_tile_list(tiles);
- for (List<int>::Element *E = tiles->front(); E; E = E->next()) {
- int t_id = E->get();
- if (tileset->tile_get_texture(t_id)->get_rid() != current_texture_rid) {
- continue;
- }
-
- Rect2 region = tileset->tile_get_region(t_id);
- region.position += WORKSPACE_MARGIN;
- region.position *= workspace->get_scale().x;
- Color c;
- if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) {
- c = COLOR_SINGLE;
- } else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) {
- c = COLOR_AUTOTILE;
- } else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) {
- c = COLOR_ATLAS;
- }
- String tile_id_name = String::num(t_id, 0) + ": " + tileset->tile_get_name(t_id);
- Ref<Font> font = get_theme_font("font", "Label");
- int font_size = get_theme_font_size("font_size", "Label");
- region.set_size(font->get_string_size(tile_id_name, font_size));
- workspace_overlay->draw_rect(region, c);
- region.position.y += region.size.y - 2;
- c = Color(0.1, 0.1, 0.1);
- workspace_overlay->draw_string(font, region.position, tile_id_name, HALIGN_LEFT, -1, font_size, c);
- }
- delete tiles;
- }
-
- int t_id = get_current_tile();
- if (t_id < 0) {
- return;
- }
-
- Ref<Texture2D> handle = get_theme_icon("EditorHandle", "EditorIcons");
- if (draw_handles) {
- for (int i = 0; i < current_shape.size(); i++) {
- workspace_overlay->draw_texture(handle, current_shape[i] * workspace->get_scale().x - handle->get_size() * 0.5);
- }
- }
-}
-
-int TileSetEditor::get_grabbed_point(const Vector2 &p_mouse_pos, real_t p_grab_threshold) {
- Transform2D xform = workspace->get_transform();
-
- int grabbed_point = -1;
- real_t min_distance = 1e10;
-
- for (int i = 0; i < current_shape.size(); i++) {
- const real_t distance = xform.xform(current_shape[i]).distance_to(xform.xform(p_mouse_pos));
- if (distance < p_grab_threshold && distance < min_distance) {
- min_distance = distance;
- grabbed_point = i;
- }
- }
-
- return grabbed_point;
-}
-
-bool TileSetEditor::is_within_grabbing_distance_of_first_point(const Vector2 &p_pos, real_t p_grab_threshold) {
- Transform2D xform = workspace->get_transform();
-
- const real_t distance = xform.xform(current_shape[0]).distance_to(xform.xform(p_pos));
-
- return distance < p_grab_threshold;
-}
-
-void TileSetEditor::_on_scroll_container_input(const Ref<InputEvent> &p_event) {
- const Ref<InputEventMouseButton> mb = p_event;
-
- if (mb.is_valid()) {
- // Zoom in/out using Ctrl + mouse wheel. This is done on the ScrollContainer
- // to allow performing this action anywhere, even if the cursor isn't
- // hovering the texture in the workspace.
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed() && mb->get_control()) {
- print_line("zooming in");
- _zoom_in();
- // Don't scroll up after zooming in.
- accept_event();
- } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed() && mb->get_control()) {
- print_line("zooming out");
- _zoom_out();
- // Don't scroll down after zooming out.
- accept_event();
- }
- }
-}
-
-void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
- if (tileset.is_null() || !get_current_texture().is_valid()) {
- return;
- }
-
- static bool dragging;
- static bool erasing;
- static bool alternative;
- draw_edited_region = false;
-
- Rect2 current_tile_region = Rect2();
- if (get_current_tile() >= 0) {
- current_tile_region = tileset->tile_get_region(get_current_tile());
- }
- current_tile_region.position += WORKSPACE_MARGIN;
-
- const Ref<InputEventMouseButton> mb = p_ie;
- const Ref<InputEventMouseMotion> mm = p_ie;
-
- if (mb.is_valid()) {
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT && !creating_shape) {
- if (!current_tile_region.has_point(mb->get_position())) {
- List<int> *tiles = new List<int>();
- tileset->get_tile_list(tiles);
- for (List<int>::Element *E = tiles->front(); E; E = E->next()) {
- int t_id = E->get();
- if (get_current_texture()->get_rid() == tileset->tile_get_texture(t_id)->get_rid()) {
- Rect2 r = tileset->tile_get_region(t_id);
- r.position += WORKSPACE_MARGIN;
- if (r.has_point(mb->get_position())) {
- set_current_tile(t_id);
- workspace->update();
- workspace_overlay->update();
- delete tiles;
- return;
- }
- }
- }
- delete tiles;
- }
- }
- }
- // Drag Middle Mouse
- if (mm.is_valid()) {
- if (mm->get_button_mask() & MOUSE_BUTTON_MASK_MIDDLE) {
- Vector2 dragged(mm->get_relative().x, mm->get_relative().y);
- scroll->set_h_scroll(scroll->get_h_scroll() - dragged.x * workspace->get_scale().x);
- scroll->set_v_scroll(scroll->get_v_scroll() - dragged.y * workspace->get_scale().x);
- }
- }
-
- if (edit_mode == EDITMODE_REGION) {
- if (mb.is_valid()) {
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
- if (get_current_tile() >= 0 || workspace_mode != WORKSPACE_EDIT) {
- dragging = true;
- region_from = mb->get_position();
- edited_region = Rect2(region_from, Size2());
- workspace->update();
- workspace_overlay->update();
- return;
- }
- } else if (dragging && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
- dragging = false;
- edited_region = Rect2();
- workspace->update();
- workspace_overlay->update();
- return;
- } else if (dragging && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
- dragging = false;
- update_edited_region(mb->get_position());
- edited_region.position -= WORKSPACE_MARGIN;
- if (!edited_region.has_no_area()) {
- if (get_current_tile() >= 0 && workspace_mode == WORKSPACE_EDIT) {
- undo_redo->create_action(TTR("Set Tile Region"));
- undo_redo->add_do_method(tileset.ptr(), "tile_set_region", get_current_tile(), edited_region);
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_region", get_current_tile(), tileset->tile_get_region(get_current_tile()));
-
- Size2 tile_workspace_size = edited_region.position + edited_region.size + WORKSPACE_MARGIN * 2;
- Size2 workspace_minsize = workspace->get_custom_minimum_size();
- // If the new region is bigger, just directly change the workspace size to avoid checking all other tiles.
- if (tile_workspace_size.x > workspace_minsize.x || tile_workspace_size.y > workspace_minsize.y) {
- Size2 max_workspace_size = Size2(MAX(tile_workspace_size.x, workspace_minsize.x), MAX(tile_workspace_size.y, workspace_minsize.y));
- undo_redo->add_do_method(workspace, "set_custom_minimum_size", max_workspace_size);
- undo_redo->add_undo_method(workspace, "set_custom_minimum_size", workspace_minsize);
- undo_redo->add_do_method(workspace_container, "set_custom_minimum_size", max_workspace_size);
- undo_redo->add_undo_method(workspace_container, "set_custom_minimum_size", workspace_minsize);
- undo_redo->add_do_method(workspace_overlay, "set_custom_minimum_size", max_workspace_size);
- undo_redo->add_undo_method(workspace_overlay, "set_custom_minimum_size", workspace_minsize);
- } else if (workspace_minsize.x > get_current_texture()->get_size().x + WORKSPACE_MARGIN.x * 2 || workspace_minsize.y > get_current_texture()->get_size().y + WORKSPACE_MARGIN.y * 2) {
- undo_redo->add_do_method(this, "update_workspace_minsize");
- undo_redo->add_undo_method(this, "update_workspace_minsize");
- }
-
- edited_region = Rect2();
-
- undo_redo->add_do_method(workspace, "update");
- undo_redo->add_undo_method(workspace, "update");
- undo_redo->add_do_method(workspace_overlay, "update");
- undo_redo->add_undo_method(workspace_overlay, "update");
- undo_redo->commit_action();
- } else {
- int t_id = tileset->get_last_unused_tile_id();
- undo_redo->create_action(TTR("Create Tile"));
- undo_redo->add_do_method(tileset.ptr(), "create_tile", t_id);
- undo_redo->add_undo_method(tileset.ptr(), "remove_tile", t_id);
- undo_redo->add_undo_method(this, "_validate_current_tile_id");
- undo_redo->add_do_method(tileset.ptr(), "tile_set_texture", t_id, get_current_texture());
- undo_redo->add_do_method(tileset.ptr(), "tile_set_region", t_id, edited_region);
- undo_redo->add_do_method(tileset.ptr(), "tile_set_name", t_id, get_current_texture()->get_path().get_file() + " " + String::num(t_id, 0));
- if (workspace_mode != WORKSPACE_CREATE_SINGLE) {
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_size", t_id, snap_step);
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_spacing", t_id, snap_separation.x);
- undo_redo->add_do_method(tileset.ptr(), "tile_set_tile_mode", t_id, workspace_mode == WORKSPACE_CREATE_AUTOTILE ? TileSet::AUTO_TILE : TileSet::ATLAS_TILE);
- }
-
- tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true);
- tool_editmode[EDITMODE_COLLISION]->set_pressed(true);
- edit_mode = EDITMODE_COLLISION;
-
- Size2 tile_workspace_size = edited_region.position + edited_region.size + WORKSPACE_MARGIN * 2;
- Size2 workspace_minsize = workspace->get_custom_minimum_size();
- if (tile_workspace_size.x > workspace_minsize.x || tile_workspace_size.y > workspace_minsize.y) {
- Size2 new_workspace_minsize = Size2(MAX(tile_workspace_size.x, workspace_minsize.x), MAX(tile_workspace_size.y, workspace_minsize.y));
- undo_redo->add_do_method(workspace, "set_custom_minimum_size", new_workspace_minsize);
- undo_redo->add_undo_method(workspace, "set_custom_minimum_size", workspace_minsize);
- undo_redo->add_do_method(workspace_container, "set_custom_minimum_size", new_workspace_minsize);
- undo_redo->add_undo_method(workspace_container, "set_custom_minimum_size", workspace_minsize);
- undo_redo->add_do_method(workspace_overlay, "set_custom_minimum_size", new_workspace_minsize);
- undo_redo->add_undo_method(workspace_overlay, "set_custom_minimum_size", workspace_minsize);
- }
-
- edited_region = Rect2();
-
- undo_redo->add_do_method(workspace, "update");
- undo_redo->add_undo_method(workspace, "update");
- undo_redo->add_do_method(workspace_overlay, "update");
- undo_redo->add_undo_method(workspace_overlay, "update");
- undo_redo->commit_action();
-
- set_current_tile(t_id);
- _on_workspace_mode_changed(WORKSPACE_EDIT);
- }
- } else {
- edited_region = Rect2();
- workspace->update();
- workspace_overlay->update();
- }
- return;
- }
- } else if (mm.is_valid()) {
- if (dragging) {
- update_edited_region(mm->get_position());
- draw_edited_region = true;
- workspace->update();
- workspace_overlay->update();
- return;
- }
- }
- }
-
- if (workspace_mode == WORKSPACE_EDIT) {
- if (get_current_tile() >= 0) {
- int spacing = tileset->autotile_get_spacing(get_current_tile());
- Vector2 size = tileset->autotile_get_size(get_current_tile());
- switch (edit_mode) {
- case EDITMODE_ICON: {
- if (mb.is_valid()) {
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT && current_tile_region.has_point(mb->get_position())) {
- Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y)));
- undo_redo->create_action(TTR("Set Tile Icon"));
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_icon_coordinate", get_current_tile(), coord);
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_icon_coordinate", get_current_tile(), tileset->autotile_get_icon_coordinate(get_current_tile()));
- undo_redo->add_do_method(workspace, "update");
- undo_redo->add_undo_method(workspace, "update");
- undo_redo->commit_action();
- }
- }
- } break;
- case EDITMODE_BITMASK: {
- if (mb.is_valid()) {
- if (mb->is_pressed()) {
- if (dragging) {
- return;
- }
- if ((mb->get_button_index() == MOUSE_BUTTON_RIGHT || mb->get_button_index() == MOUSE_BUTTON_LEFT) && current_tile_region.has_point(mb->get_position())) {
- dragging = true;
- erasing = (mb->get_button_index() == MOUSE_BUTTON_RIGHT);
- alternative = Input::get_singleton()->is_key_pressed(KEY_SHIFT);
- Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y)));
- Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
- pos = mb->get_position() - (pos + current_tile_region.position);
- uint32_t bit = 0;
- if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
- if (pos.x < size.x / 2) {
- if (pos.y < size.y / 2) {
- bit = TileSet::BIND_TOPLEFT;
- } else {
- bit = TileSet::BIND_BOTTOMLEFT;
- }
- } else {
- if (pos.y < size.y / 2) {
- bit = TileSet::BIND_TOPRIGHT;
- } else {
- bit = TileSet::BIND_BOTTOMRIGHT;
- }
- }
- } else {
- if (pos.x < size.x / 3) {
- if (pos.y < size.y / 3) {
- bit = TileSet::BIND_TOPLEFT;
- } else if (pos.y > (size.y / 3) * 2) {
- bit = TileSet::BIND_BOTTOMLEFT;
- } else {
- bit = TileSet::BIND_LEFT;
- }
- } else if (pos.x > (size.x / 3) * 2) {
- if (pos.y < size.y / 3) {
- bit = TileSet::BIND_TOPRIGHT;
- } else if (pos.y > (size.y / 3) * 2) {
- bit = TileSet::BIND_BOTTOMRIGHT;
- } else {
- bit = TileSet::BIND_RIGHT;
- }
- } else {
- if (pos.y < size.y / 3) {
- bit = TileSet::BIND_TOP;
- } else if (pos.y > (size.y / 3) * 2) {
- bit = TileSet::BIND_BOTTOM;
- } else {
- bit = TileSet::BIND_CENTER;
- }
- }
- }
-
- uint32_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord);
- uint32_t new_mask = old_mask;
- if (alternative) {
- new_mask &= ~bit;
- new_mask |= (bit << 16);
- } else if (erasing) {
- new_mask &= ~bit;
- new_mask &= ~(bit << 16);
- } else {
- new_mask |= bit;
- new_mask &= ~(bit << 16);
- }
-
- if (old_mask != new_mask) {
- undo_redo->create_action(TTR("Edit Tile Bitmask"));
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, new_mask);
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, old_mask);
- undo_redo->add_do_method(workspace, "update");
- undo_redo->add_undo_method(workspace, "update");
- undo_redo->commit_action();
- }
- }
- } else {
- if ((erasing && mb->get_button_index() == MOUSE_BUTTON_RIGHT) || (!erasing && mb->get_button_index() == MOUSE_BUTTON_LEFT)) {
- dragging = false;
- erasing = false;
- alternative = false;
- }
- }
- }
- if (mm.is_valid()) {
- if (dragging && current_tile_region.has_point(mm->get_position())) {
- Vector2 coord((int)((mm->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mm->get_position().y - current_tile_region.position.y) / (spacing + size.y)));
- Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
- pos = mm->get_position() - (pos + current_tile_region.position);
- uint32_t bit = 0;
- if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
- if (pos.x < size.x / 2) {
- if (pos.y < size.y / 2) {
- bit = TileSet::BIND_TOPLEFT;
- } else {
- bit = TileSet::BIND_BOTTOMLEFT;
- }
- } else {
- if (pos.y < size.y / 2) {
- bit = TileSet::BIND_TOPRIGHT;
- } else {
- bit = TileSet::BIND_BOTTOMRIGHT;
- }
- }
- } else {
- if (pos.x < size.x / 3) {
- if (pos.y < size.y / 3) {
- bit = TileSet::BIND_TOPLEFT;
- } else if (pos.y > (size.y / 3) * 2) {
- bit = TileSet::BIND_BOTTOMLEFT;
- } else {
- bit = TileSet::BIND_LEFT;
- }
- } else if (pos.x > (size.x / 3) * 2) {
- if (pos.y < size.y / 3) {
- bit = TileSet::BIND_TOPRIGHT;
- } else if (pos.y > (size.y / 3) * 2) {
- bit = TileSet::BIND_BOTTOMRIGHT;
- } else {
- bit = TileSet::BIND_RIGHT;
- }
- } else {
- if (pos.y < size.y / 3) {
- bit = TileSet::BIND_TOP;
- } else if (pos.y > (size.y / 3) * 2) {
- bit = TileSet::BIND_BOTTOM;
- } else {
- bit = TileSet::BIND_CENTER;
- }
- }
- }
-
- uint32_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord);
- uint32_t new_mask = old_mask;
- if (alternative) {
- new_mask &= ~bit;
- new_mask |= (bit << 16);
- } else if (erasing) {
- new_mask &= ~bit;
- new_mask &= ~(bit << 16);
- } else {
- new_mask |= bit;
- new_mask &= ~(bit << 16);
- }
- if (old_mask != new_mask) {
- undo_redo->create_action(TTR("Edit Tile Bitmask"));
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, new_mask);
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, old_mask);
- undo_redo->add_do_method(workspace, "update");
- undo_redo->add_undo_method(workspace, "update");
- undo_redo->commit_action();
- }
- }
- }
- } break;
- case EDITMODE_COLLISION:
- case EDITMODE_OCCLUSION:
- case EDITMODE_NAVIGATION:
- case EDITMODE_PRIORITY:
- case EDITMODE_Z_INDEX: {
- Vector2 shape_anchor = Vector2(0, 0);
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
- shape_anchor = edited_shape_coord;
- shape_anchor.x *= (size.x + spacing);
- shape_anchor.y *= (size.y + spacing);
- }
-
- const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
- shape_anchor += current_tile_region.position;
- if (tools[TOOL_SELECT]->is_pressed()) {
- if (mb.is_valid()) {
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
- if (edit_mode != EDITMODE_PRIORITY && current_shape.size() > 0) {
- int grabbed_point = get_grabbed_point(mb->get_position(), grab_threshold);
-
- if (grabbed_point >= 0) {
- dragging_point = grabbed_point;
- workspace->update();
- return;
- }
- }
- if ((tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) && current_tile_region.has_point(mb->get_position())) {
- Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y)));
- if (edited_shape_coord != coord) {
- edited_shape_coord = coord;
- _select_edited_shape_coord();
- }
- }
- workspace->update();
- } else if (!mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
- if (edit_mode == EDITMODE_COLLISION) {
- if (dragging_point >= 0) {
- dragging_point = -1;
-
- Vector<Vector2> points;
-
- for (int i = 0; i < current_shape.size(); i++) {
- Vector2 p = current_shape[i];
- if (tools[TOOL_GRID_SNAP]->is_pressed() || tools[SHAPE_KEEP_INSIDE_TILE]->is_pressed()) {
- p = snap_point(p);
- }
- points.push_back(p - shape_anchor);
- }
-
- undo_redo->create_action(TTR("Edit Collision Polygon"));
- _set_edited_shape_points(points);
- undo_redo->add_do_method(this, "_select_edited_shape_coord");
- undo_redo->add_undo_method(this, "_select_edited_shape_coord");
- undo_redo->commit_action();
- }
- } else if (edit_mode == EDITMODE_OCCLUSION) {
- if (dragging_point >= 0) {
- dragging_point = -1;
-
- Vector<Vector2> polygon;
- polygon.resize(current_shape.size());
- Vector2 *w = polygon.ptrw();
-
- for (int i = 0; i < current_shape.size(); i++) {
- w[i] = current_shape[i] - shape_anchor;
- }
-
- undo_redo->create_action(TTR("Edit Occlusion Polygon"));
- undo_redo->add_do_method(edited_occlusion_shape.ptr(), "set_polygon", polygon);
- undo_redo->add_undo_method(edited_occlusion_shape.ptr(), "set_polygon", edited_occlusion_shape->get_polygon());
- undo_redo->add_do_method(this, "_select_edited_shape_coord");
- undo_redo->add_undo_method(this, "_select_edited_shape_coord");
- undo_redo->commit_action();
- }
- } else if (edit_mode == EDITMODE_NAVIGATION) {
- if (dragging_point >= 0) {
- dragging_point = -1;
-
- Vector<Vector2> polygon;
- Vector<int> indices;
- polygon.resize(current_shape.size());
- Vector2 *w = polygon.ptrw();
-
- for (int i = 0; i < current_shape.size(); i++) {
- w[i] = current_shape[i] - shape_anchor;
- indices.push_back(i);
- }
-
- undo_redo->create_action(TTR("Edit Navigation Polygon"));
- undo_redo->add_do_method(edited_navigation_shape.ptr(), "set_vertices", polygon);
- undo_redo->add_undo_method(edited_navigation_shape.ptr(), "set_vertices", edited_navigation_shape->get_vertices());
- undo_redo->add_do_method(edited_navigation_shape.ptr(), "clear_polygons");
- undo_redo->add_undo_method(edited_navigation_shape.ptr(), "clear_polygons");
- undo_redo->add_do_method(edited_navigation_shape.ptr(), "add_polygon", indices);
- undo_redo->add_undo_method(edited_navigation_shape.ptr(), "add_polygon", edited_navigation_shape->get_polygon(0));
- undo_redo->add_do_method(this, "_select_edited_shape_coord");
- undo_redo->add_undo_method(this, "_select_edited_shape_coord");
- undo_redo->commit_action();
- }
- }
- }
- } else if (mm.is_valid()) {
- if (dragging_point >= 0) {
- current_shape.set(dragging_point, snap_point(mm->get_position()));
- workspace->update();
- }
- }
- } else if (tools[SHAPE_NEW_POLYGON]->is_pressed()) {
- if (mb.is_valid()) {
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
- Vector2 pos = mb->get_position();
- pos = snap_point(pos);
- if (creating_shape) {
- if (current_shape.size() > 2) {
- if (is_within_grabbing_distance_of_first_point(mb->get_position(), grab_threshold)) {
- close_shape(shape_anchor);
- workspace->update();
- return;
- }
- }
- current_shape.push_back(pos);
- workspace->update();
- } else {
- creating_shape = true;
- _set_edited_collision_shape(Ref<ConvexPolygonShape2D>());
- current_shape.resize(0);
- current_shape.push_back(snap_point(pos));
- workspace->update();
- }
- } else if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
- if (creating_shape) {
- creating_shape = false;
- _select_edited_shape_coord();
- workspace->update();
- }
- }
- } else if (mm.is_valid()) {
- if (creating_shape) {
- workspace->update();
- }
- }
- } else if (tools[SHAPE_NEW_RECTANGLE]->is_pressed()) {
- if (mb.is_valid()) {
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
- _set_edited_collision_shape(Ref<ConvexPolygonShape2D>());
- current_shape.resize(0);
- Vector2 pos = mb->get_position();
- pos = snap_point(pos);
- current_shape.push_back(pos);
- current_shape.push_back(pos);
- current_shape.push_back(pos);
- current_shape.push_back(pos);
- creating_shape = true;
- workspace->update();
- return;
- } else if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
- if (creating_shape) {
- creating_shape = false;
- _select_edited_shape_coord();
- workspace->update();
- }
- } else if (!mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
- if (creating_shape) {
- // if the first two corners are within grabbing distance of one another, expand the rect to fill the tile
- if (is_within_grabbing_distance_of_first_point(current_shape[1], grab_threshold)) {
- current_shape.set(0, snap_point(shape_anchor));
- current_shape.set(1, snap_point(shape_anchor + Vector2(current_tile_region.size.x, 0)));
- current_shape.set(2, snap_point(shape_anchor + current_tile_region.size));
- current_shape.set(3, snap_point(shape_anchor + Vector2(0, current_tile_region.size.y)));
- }
-
- close_shape(shape_anchor);
- workspace->update();
- return;
- }
- }
- } else if (mm.is_valid()) {
- if (creating_shape) {
- Vector2 pos = mm->get_position();
- pos = snap_point(pos);
- Vector2 p = current_shape[2];
- current_shape.set(3, snap_point(Vector2(pos.x, p.y)));
- current_shape.set(0, snap_point(pos));
- current_shape.set(1, snap_point(Vector2(p.x, pos.y)));
- workspace->update();
- }
- }
- }
- } break;
- default: {
- }
- }
- }
- }
-}
-
-void TileSetEditor::_on_tool_clicked(int p_tool) {
- if (p_tool == BITMASK_COPY) {
- bitmask_map_copy = tileset->autotile_get_bitmask_map(get_current_tile());
- } else if (p_tool == BITMASK_PASTE) {
- undo_redo->create_action(TTR("Paste Tile Bitmask"));
- undo_redo->add_do_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile());
- undo_redo->add_undo_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile());
- for (Map<Vector2, uint32_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) {
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value());
- }
- for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) {
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value());
- }
- undo_redo->add_do_method(workspace, "update");
- undo_redo->add_undo_method(workspace, "update");
- undo_redo->commit_action();
- } else if (p_tool == BITMASK_CLEAR) {
- undo_redo->create_action(TTR("Clear Tile Bitmask"));
- undo_redo->add_do_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile());
- for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) {
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value());
- }
- undo_redo->add_do_method(workspace, "update");
- undo_redo->add_undo_method(workspace, "update");
- undo_redo->commit_action();
- } else if (p_tool == SHAPE_TOGGLE_TYPE) {
- if (edited_collision_shape.is_valid()) {
- Ref<ConvexPolygonShape2D> convex = edited_collision_shape;
- Ref<ConcavePolygonShape2D> concave = edited_collision_shape;
- Ref<Shape2D> previous_shape = edited_collision_shape;
- Array sd = tileset->call("tile_get_shapes", get_current_tile());
-
- if (convex.is_valid()) {
- // Make concave.
- undo_redo->create_action(TTR("Make Polygon Concave"));
- Ref<ConcavePolygonShape2D> _concave = memnew(ConcavePolygonShape2D);
- edited_collision_shape = _concave;
- _set_edited_shape_points(_get_collision_shape_points(convex));
- } else if (concave.is_valid()) {
- // Make convex.
- undo_redo->create_action(TTR("Make Polygon Convex"));
- Ref<ConvexPolygonShape2D> _convex = memnew(ConvexPolygonShape2D);
- edited_collision_shape = _convex;
- _set_edited_shape_points(_get_collision_shape_points(concave));
- }
- for (int i = 0; i < sd.size(); i++) {
- if (sd[i].get("shape") == previous_shape) {
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd.duplicate());
- sd.remove(i);
- break;
- }
- }
-
- undo_redo->add_do_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd);
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
- undo_redo->add_do_method(tileset.ptr(), "tile_add_shape", get_current_tile(), edited_collision_shape, Transform2D(), false, edited_shape_coord);
- } else {
- undo_redo->add_do_method(tileset.ptr(), "tile_add_shape", get_current_tile(), edited_collision_shape, Transform2D());
- }
- undo_redo->add_do_method(this, "_select_edited_shape_coord");
- undo_redo->add_undo_method(this, "_select_edited_shape_coord");
- undo_redo->commit_action();
-
- _update_toggle_shape_button();
- workspace->update();
- workspace_container->update();
- helper->notify_property_list_changed();
- }
- } else if (p_tool == SELECT_NEXT) {
- _select_next_shape();
- } else if (p_tool == SELECT_PREVIOUS) {
- _select_previous_shape();
- } else if (p_tool == SHAPE_DELETE) {
- if (creating_shape) {
- creating_shape = false;
- current_shape.resize(0);
- workspace->update();
- } else {
- switch (edit_mode) {
- case EDITMODE_REGION: {
- int t_id = get_current_tile();
- if (workspace_mode == WORKSPACE_EDIT && t_id >= 0) {
- undo_redo->create_action(TTR("Remove Tile"));
- undo_redo->add_do_method(tileset.ptr(), "remove_tile", t_id);
- _undo_tile_removal(t_id);
- undo_redo->add_do_method(this, "_validate_current_tile_id");
-
- Rect2 tile_region = tileset->tile_get_region(get_current_tile());
- Size2 tile_workspace_size = tile_region.position + tile_region.size;
- if (tile_workspace_size.x > get_current_texture()->get_size().x || tile_workspace_size.y > get_current_texture()->get_size().y) {
- undo_redo->add_do_method(this, "update_workspace_minsize");
- undo_redo->add_undo_method(this, "update_workspace_minsize");
- }
-
- undo_redo->add_do_method(workspace, "update");
- undo_redo->add_undo_method(workspace, "update");
- undo_redo->add_do_method(workspace_overlay, "update");
- undo_redo->add_undo_method(workspace_overlay, "update");
- undo_redo->commit_action();
- }
- tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true);
- workspace_mode = WORKSPACE_EDIT;
- update_workspace_tile_mode();
- } break;
- case EDITMODE_COLLISION: {
- if (!edited_collision_shape.is_null()) {
- // Necessary to get the version that returns a Array instead of a Vector.
- Array sd = tileset->call("tile_get_shapes", get_current_tile());
- for (int i = 0; i < sd.size(); i++) {
- if (sd[i].get("shape") == edited_collision_shape) {
- undo_redo->create_action(TTR("Remove Collision Polygon"));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd.duplicate());
- sd.remove(i);
- undo_redo->add_do_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd);
- undo_redo->add_do_method(this, "_select_edited_shape_coord");
- undo_redo->add_undo_method(this, "_select_edited_shape_coord");
- undo_redo->commit_action();
- break;
- }
- }
- }
- } break;
- case EDITMODE_OCCLUSION: {
- if (!edited_occlusion_shape.is_null()) {
- undo_redo->create_action(TTR("Remove Occlusion Polygon"));
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- undo_redo->add_do_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), Ref<OccluderPolygon2D>());
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), tileset->tile_get_light_occluder(get_current_tile()));
- } else {
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord);
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), tileset->autotile_get_light_occluder(get_current_tile(), edited_shape_coord), edited_shape_coord);
- }
- undo_redo->add_do_method(this, "_select_edited_shape_coord");
- undo_redo->add_undo_method(this, "_select_edited_shape_coord");
- undo_redo->commit_action();
- }
- } break;
- case EDITMODE_NAVIGATION: {
- if (!edited_navigation_shape.is_null()) {
- undo_redo->create_action(TTR("Remove Navigation Polygon"));
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- undo_redo->add_do_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), Ref<NavigationPolygon>());
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), tileset->tile_get_navigation_polygon(get_current_tile()));
- } else {
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), Ref<NavigationPolygon>(), edited_shape_coord);
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), tileset->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord), edited_shape_coord);
- }
- undo_redo->add_do_method(this, "_select_edited_shape_coord");
- undo_redo->add_undo_method(this, "_select_edited_shape_coord");
- undo_redo->commit_action();
- }
- } break;
- default: {
- }
- }
- }
- } else if (p_tool == TOOL_SELECT || p_tool == SHAPE_NEW_POLYGON || p_tool == SHAPE_NEW_RECTANGLE) {
- if (creating_shape) {
- // Cancel Creation
- creating_shape = false;
- current_shape.resize(0);
- workspace->update();
- }
- }
-}
-
-void TileSetEditor::_on_priority_changed(float val) {
- if ((int)val == tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord)) {
- return;
- }
-
- undo_redo->create_action(TTR("Edit Tile Priority"));
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_subtile_priority", get_current_tile(), edited_shape_coord, (int)val);
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_subtile_priority", get_current_tile(), edited_shape_coord, tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord));
- undo_redo->add_do_method(workspace, "update");
- undo_redo->add_undo_method(workspace, "update");
- undo_redo->commit_action();
-}
-
-void TileSetEditor::_on_z_index_changed(float val) {
- if ((int)val == tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord)) {
- return;
- }
-
- undo_redo->create_action(TTR("Edit Tile Z Index"));
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_z_index", get_current_tile(), edited_shape_coord, (int)val);
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_z_index", get_current_tile(), edited_shape_coord, tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord));
- undo_redo->add_do_method(workspace, "update");
- undo_redo->add_undo_method(workspace, "update");
- undo_redo->commit_action();
-}
-
-void TileSetEditor::_on_grid_snap_toggled(bool p_val) {
- helper->set_snap_options_visible(p_val);
- workspace->update();
-}
-
-Vector<Vector2> TileSetEditor::_get_collision_shape_points(const Ref<Shape2D> &p_shape) {
- Ref<ConvexPolygonShape2D> convex = p_shape;
- Ref<ConcavePolygonShape2D> concave = p_shape;
- if (convex.is_valid()) {
- return convex->get_points();
- } else if (concave.is_valid()) {
- Vector<Vector2> points;
- for (int i = 0; i < concave->get_segments().size(); i += 2) {
- points.push_back(concave->get_segments()[i]);
- }
- return points;
- } else {
- return Vector<Vector2>();
- }
-}
-
-Vector<Vector2> TileSetEditor::_get_edited_shape_points() {
- return _get_collision_shape_points(edited_collision_shape);
-}
-
-void TileSetEditor::_set_edited_shape_points(const Vector<Vector2> &points) {
- Ref<ConvexPolygonShape2D> convex = edited_collision_shape;
- Ref<ConcavePolygonShape2D> concave = edited_collision_shape;
- if (convex.is_valid()) {
- undo_redo->add_do_method(convex.ptr(), "set_points", points);
- undo_redo->add_undo_method(convex.ptr(), "set_points", _get_edited_shape_points());
- } else if (concave.is_valid() && points.size() > 1) {
- PackedVector2Array segments;
- for (int i = 0; i < points.size() - 1; i++) {
- segments.push_back(points[i]);
- segments.push_back(points[i + 1]);
- }
- segments.push_back(points[points.size() - 1]);
- segments.push_back(points[0]);
- undo_redo->add_do_method(concave.ptr(), "set_segments", segments);
- undo_redo->add_undo_method(concave.ptr(), "set_segments", concave->get_segments());
- }
-}
-
-void TileSetEditor::_update_tile_data() {
- current_tile_data.clear();
- if (get_current_tile() < 0) {
- return;
- }
-
- Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile());
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- SubtileData data;
- for (int i = 0; i < sd.size(); i++) {
- data.collisions.push_back(sd[i].shape);
- }
- data.navigation_shape = tileset->tile_get_navigation_polygon(get_current_tile());
- data.occlusion_shape = tileset->tile_get_light_occluder(get_current_tile());
- current_tile_data[Vector2i()] = data;
- } else {
- int spacing = tileset->autotile_get_spacing(get_current_tile());
- Vector2 size = tileset->tile_get_region(get_current_tile()).size;
- Vector2 cell_count = (size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing))).floor();
- for (int y = 0; y < cell_count.y; y++) {
- for (int x = 0; x < cell_count.x; x++) {
- SubtileData data;
- Vector2i coord(x, y);
- for (int i = 0; i < sd.size(); i++) {
- if (sd[i].autotile_coord == coord) {
- data.collisions.push_back(sd[i].shape);
- }
- }
- data.navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), coord);
- data.occlusion_shape = tileset->tile_get_light_occluder(get_current_tile());
- current_tile_data[coord] = data;
- }
- }
- }
-}
-
-void TileSetEditor::_update_toggle_shape_button() {
- Ref<ConvexPolygonShape2D> convex = edited_collision_shape;
- Ref<ConcavePolygonShape2D> concave = edited_collision_shape;
- separator_shape_toggle->show();
- tools[SHAPE_TOGGLE_TYPE]->show();
- if (edit_mode != EDITMODE_COLLISION || !edited_collision_shape.is_valid()) {
- separator_shape_toggle->hide();
- tools[SHAPE_TOGGLE_TYPE]->hide();
- } else if (concave.is_valid()) {
- tools[SHAPE_TOGGLE_TYPE]->set_icon(get_theme_icon("ConvexPolygonShape2D", "EditorIcons"));
- tools[SHAPE_TOGGLE_TYPE]->set_text(TTR("Make Convex"));
- } else if (convex.is_valid()) {
- tools[SHAPE_TOGGLE_TYPE]->set_icon(get_theme_icon("ConcavePolygonShape2D", "EditorIcons"));
- tools[SHAPE_TOGGLE_TYPE]->set_text(TTR("Make Concave"));
- } else {
- // Shouldn't happen
- separator_shape_toggle->hide();
- tools[SHAPE_TOGGLE_TYPE]->hide();
- }
-}
-
-void TileSetEditor::_select_next_tile() {
- Array tiles = _get_tiles_in_current_texture(true);
- if (tiles.size() == 0) {
- set_current_tile(-1);
- } else if (get_current_tile() == -1) {
- set_current_tile(tiles[0]);
- } else {
- int index = tiles.find(get_current_tile());
- if (index < 0) {
- set_current_tile(tiles[0]);
- } else if (index == tiles.size() - 1) {
- set_current_tile(tiles[0]);
- } else {
- set_current_tile(tiles[index + 1]);
- }
- }
- if (get_current_tile() == -1) {
- return;
- } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- return;
- } else {
- switch (edit_mode) {
- case EDITMODE_COLLISION:
- case EDITMODE_OCCLUSION:
- case EDITMODE_NAVIGATION:
- case EDITMODE_PRIORITY:
- case EDITMODE_Z_INDEX: {
- edited_shape_coord = Vector2();
- _select_edited_shape_coord();
- } break;
- default: {
- }
- }
- }
-}
-
-void TileSetEditor::_select_previous_tile() {
- Array tiles = _get_tiles_in_current_texture(true);
- if (tiles.size() == 0) {
- set_current_tile(-1);
- } else if (get_current_tile() == -1) {
- set_current_tile(tiles[tiles.size() - 1]);
- } else {
- int index = tiles.find(get_current_tile());
- if (index <= 0) {
- set_current_tile(tiles[tiles.size() - 1]);
- } else {
- set_current_tile(tiles[index - 1]);
- }
- }
- if (get_current_tile() == -1) {
- return;
- } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- return;
- } else {
- switch (edit_mode) {
- case EDITMODE_COLLISION:
- case EDITMODE_OCCLUSION:
- case EDITMODE_NAVIGATION:
- case EDITMODE_PRIORITY:
- case EDITMODE_Z_INDEX: {
- int spacing = tileset->autotile_get_spacing(get_current_tile());
- Vector2 size = tileset->tile_get_region(get_current_tile()).size;
- Vector2 cell_count = (size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing))).floor();
- cell_count -= Vector2(1, 1);
- edited_shape_coord = cell_count;
- _select_edited_shape_coord();
- } break;
- default: {
- }
- }
- }
-}
-
-Array TileSetEditor::_get_tiles_in_current_texture(bool sorted) {
- Array a;
- List<int> all_tiles;
- if (!get_current_texture().is_valid()) {
- return a;
- }
- tileset->get_tile_list(&all_tiles);
- for (int i = 0; i < all_tiles.size(); i++) {
- if (tileset->tile_get_texture(all_tiles[i]) == get_current_texture()) {
- a.push_back(all_tiles[i]);
- }
- }
- if (sorted) {
- a.sort_custom(callable_mp(this, &TileSetEditor::_sort_tiles));
- }
- return a;
-}
-
-bool TileSetEditor::_sort_tiles(Variant p_a, Variant p_b) {
- int a = p_a;
- int b = p_b;
-
- Vector2 pos_a = tileset->tile_get_region(a).position;
- Vector2 pos_b = tileset->tile_get_region(b).position;
- if (pos_a.y < pos_b.y) {
- return true;
-
- } else if (pos_a.y == pos_b.y) {
- return (pos_a.x < pos_b.x);
- } else {
- return false;
- }
-}
-
-void TileSetEditor::_select_next_subtile() {
- if (get_current_tile() == -1) {
- _select_next_tile();
- return;
- }
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- _select_next_tile();
- } else if (edit_mode == EDITMODE_REGION || edit_mode == EDITMODE_BITMASK || edit_mode == EDITMODE_ICON) {
- _select_next_tile();
- } else {
- int spacing = tileset->autotile_get_spacing(get_current_tile());
- Vector2 size = tileset->tile_get_region(get_current_tile()).size;
- Vector2 cell_count = (size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing))).floor();
- if (edited_shape_coord.x >= cell_count.x - 1 && edited_shape_coord.y >= cell_count.y - 1) {
- _select_next_tile();
- } else {
- edited_shape_coord.x++;
- if (edited_shape_coord.x >= cell_count.x) {
- edited_shape_coord.x = 0;
- edited_shape_coord.y++;
- }
- _select_edited_shape_coord();
- }
- }
-}
-
-void TileSetEditor::_select_previous_subtile() {
- if (get_current_tile() == -1) {
- _select_previous_tile();
- return;
- }
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- _select_previous_tile();
- } else if (edit_mode == EDITMODE_REGION || edit_mode == EDITMODE_BITMASK || edit_mode == EDITMODE_ICON) {
- _select_previous_tile();
- } else {
- int spacing = tileset->autotile_get_spacing(get_current_tile());
- Vector2 size = tileset->tile_get_region(get_current_tile()).size;
- Vector2 cell_count = (size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing))).floor();
- if (edited_shape_coord.x <= 0 && edited_shape_coord.y <= 0) {
- _select_previous_tile();
- } else {
- edited_shape_coord.x--;
- if (edited_shape_coord.x == -1) {
- edited_shape_coord.x = cell_count.x - 1;
- edited_shape_coord.y--;
- }
- _select_edited_shape_coord();
- }
- }
-}
-
-void TileSetEditor::_select_next_shape() {
- if (get_current_tile() == -1) {
- _select_next_subtile();
- } else if (edit_mode != EDITMODE_COLLISION) {
- _select_next_subtile();
- } else {
- Vector2i edited_coord = Vector2i();
- if (tileset->tile_get_tile_mode(get_current_tile()) != TileSet::SINGLE_TILE) {
- edited_coord = Vector2i(edited_shape_coord);
- }
- SubtileData data = current_tile_data[edited_coord];
- if (data.collisions.size() == 0) {
- _select_next_subtile();
- } else {
- int index = data.collisions.find(edited_collision_shape);
- if (index < 0) {
- _set_edited_collision_shape(data.collisions[0]);
- } else if (index == data.collisions.size() - 1) {
- _select_next_subtile();
- } else {
- _set_edited_collision_shape(data.collisions[index + 1]);
- }
- }
- current_shape.resize(0);
- Rect2 current_tile_region = tileset->tile_get_region(get_current_tile());
- current_tile_region.position += WORKSPACE_MARGIN;
-
- int spacing = tileset->autotile_get_spacing(get_current_tile());
- Vector2 size = tileset->autotile_get_size(get_current_tile());
- Vector2 shape_anchor = edited_shape_coord;
- shape_anchor.x *= (size.x + spacing);
- shape_anchor.y *= (size.y + spacing);
- current_tile_region.position += shape_anchor;
-
- if (edited_collision_shape.is_valid()) {
- for (int i = 0; i < _get_edited_shape_points().size(); i++) {
- current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position);
- }
- }
- workspace->update();
- workspace_container->update();
- helper->notify_property_list_changed();
- }
-}
-
-void TileSetEditor::_select_previous_shape() {
- if (get_current_tile() == -1) {
- _select_previous_subtile();
- if (get_current_tile() != -1 && edit_mode == EDITMODE_COLLISION) {
- SubtileData data = current_tile_data[Vector2i(edited_shape_coord)];
- if (data.collisions.size() > 1) {
- _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
- }
- } else {
- return;
- }
- } else if (edit_mode != EDITMODE_COLLISION) {
- _select_previous_subtile();
- } else {
- Vector2i edited_coord = Vector2i();
- if (tileset->tile_get_tile_mode(get_current_tile()) != TileSet::SINGLE_TILE) {
- edited_coord = Vector2i(edited_shape_coord);
- }
- SubtileData data = current_tile_data[edited_coord];
- if (data.collisions.size() == 0) {
- _select_previous_subtile();
- data = current_tile_data[Vector2i(edited_shape_coord)];
- if (data.collisions.size() > 1) {
- _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
- }
- } else {
- int index = data.collisions.find(edited_collision_shape);
- if (index < 0) {
- _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
- } else if (index == 0) {
- _select_previous_subtile();
- data = current_tile_data[Vector2i(edited_shape_coord)];
- if (data.collisions.size() > 1) {
- _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
- }
- } else {
- _set_edited_collision_shape(data.collisions[index - 1]);
- }
- }
-
- current_shape.resize(0);
- Rect2 current_tile_region = tileset->tile_get_region(get_current_tile());
- current_tile_region.position += WORKSPACE_MARGIN;
-
- int spacing = tileset->autotile_get_spacing(get_current_tile());
- Vector2 size = tileset->autotile_get_size(get_current_tile());
- Vector2 shape_anchor = edited_shape_coord;
- shape_anchor.x *= (size.x + spacing);
- shape_anchor.y *= (size.y + spacing);
- current_tile_region.position += shape_anchor;
-
- if (edited_collision_shape.is_valid()) {
- for (int i = 0; i < _get_edited_shape_points().size(); i++) {
- current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position);
- }
- }
- workspace->update();
- workspace_container->update();
- helper->notify_property_list_changed();
- }
-}
-
-void TileSetEditor::_set_edited_collision_shape(const Ref<Shape2D> &p_shape) {
- edited_collision_shape = p_shape;
- _update_toggle_shape_button();
-}
-
-void TileSetEditor::_set_snap_step(Vector2 p_val) {
- snap_step.x = CLAMP(p_val.x, 1, 256);
- snap_step.y = CLAMP(p_val.y, 1, 256);
- workspace->update();
-}
-
-void TileSetEditor::_set_snap_off(Vector2 p_val) {
- snap_offset.x = CLAMP(p_val.x, 0, 256 + WORKSPACE_MARGIN.x);
- snap_offset.y = CLAMP(p_val.y, 0, 256 + WORKSPACE_MARGIN.y);
- workspace->update();
-}
-
-void TileSetEditor::_set_snap_sep(Vector2 p_val) {
- snap_separation.x = CLAMP(p_val.x, 0, 256);
- snap_separation.y = CLAMP(p_val.y, 0, 256);
- workspace->update();
-}
-
-void TileSetEditor::_validate_current_tile_id() {
- if (get_current_tile() >= 0 && !tileset->has_tile(get_current_tile())) {
- set_current_tile(-1);
- }
-}
-
-void TileSetEditor::_select_edited_shape_coord() {
- select_coord(edited_shape_coord);
-}
-
-void TileSetEditor::_undo_tile_removal(int p_id) {
- undo_redo->add_undo_method(tileset.ptr(), "create_tile", p_id);
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_name", p_id, tileset->tile_get_name(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_texture_offset", p_id, tileset->tile_get_texture_offset(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_material", p_id, tileset->tile_get_material(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_modulate", p_id, tileset->tile_get_modulate(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_occluder_offset", p_id, tileset->tile_get_occluder_offset(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon_offset", p_id, tileset->tile_get_navigation_polygon_offset(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_shape_offset", p_id, 0, tileset->tile_get_shape_offset(p_id, 0));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_shape_transform", p_id, 0, tileset->tile_get_shape_transform(p_id, 0));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_z_index", p_id, tileset->tile_get_z_index(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_texture", p_id, tileset->tile_get_texture(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_region", p_id, tileset->tile_get_region(p_id));
- // Necessary to get the version that returns a Array instead of a Vector.
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", p_id, tileset->call("tile_get_shapes", p_id));
- if (tileset->tile_get_tile_mode(p_id) == TileSet::SINGLE_TILE) {
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_light_occluder", p_id, tileset->tile_get_light_occluder(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon", p_id, tileset->tile_get_navigation_polygon(p_id));
- } else {
- Map<Vector2, Ref<OccluderPolygon2D>> oclusion_map = tileset->autotile_get_light_oclusion_map(p_id);
- for (Map<Vector2, Ref<OccluderPolygon2D>>::Element *E = oclusion_map.front(); E; E = E->next()) {
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_light_occluder", p_id, E->value(), E->key());
- }
- Map<Vector2, Ref<NavigationPolygon>> navigation_map = tileset->autotile_get_navigation_map(p_id);
- for (Map<Vector2, Ref<NavigationPolygon>>::Element *E = navigation_map.front(); E; E = E->next()) {
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_navigation_polygon", p_id, E->value(), E->key());
- }
- Map<Vector2, uint32_t> bitmask_map = tileset->autotile_get_bitmask_map(p_id);
- for (Map<Vector2, uint32_t>::Element *E = bitmask_map.front(); E; E = E->next()) {
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", p_id, E->key(), E->value());
- }
- Map<Vector2, int> priority_map = tileset->autotile_get_priority_map(p_id);
- for (Map<Vector2, int>::Element *E = priority_map.front(); E; E = E->next()) {
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_subtile_priority", p_id, E->key(), E->value());
- }
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_icon_coordinate", p_id, tileset->autotile_get_icon_coordinate(p_id));
- Map<Vector2, int> z_map = tileset->autotile_get_z_index_map(p_id);
- for (Map<Vector2, int>::Element *E = z_map.front(); E; E = E->next()) {
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_z_index", p_id, E->key(), E->value());
- }
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_tile_mode", p_id, tileset->tile_get_tile_mode(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_size", p_id, tileset->autotile_get_size(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_spacing", p_id, tileset->autotile_get_spacing(p_id));
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask_mode", p_id, tileset->autotile_get_bitmask_mode(p_id));
- }
-}
-
-void TileSetEditor::_zoom_in() {
- float scale = workspace->get_scale().x;
- if (scale < max_scale) {
- scale *= scale_ratio;
- workspace->set_scale(Vector2(scale, scale));
- workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale);
- workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale);
- }
-}
-
-void TileSetEditor::_zoom_out() {
- float scale = workspace->get_scale().x;
- if (scale > min_scale) {
- scale /= scale_ratio;
- workspace->set_scale(Vector2(scale, scale));
- workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale);
- workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale);
- }
-}
-
-void TileSetEditor::_zoom_reset() {
- workspace->set_scale(Vector2(1, 1));
- workspace_container->set_custom_minimum_size(workspace->get_rect().size);
- workspace_overlay->set_custom_minimum_size(workspace->get_rect().size);
-}
-
-void TileSetEditor::draw_highlight_current_tile() {
- Color shadow_color = Color(0.3, 0.3, 0.3, 0.3);
- if ((workspace_mode == WORKSPACE_EDIT && get_current_tile() >= 0) || !edited_region.has_no_area()) {
- Rect2 region;
- if (edited_region.has_no_area()) {
- region = tileset->tile_get_region(get_current_tile());
- region.position += WORKSPACE_MARGIN;
- } else {
- region = edited_region;
- }
-
- if (region.position.y >= 0) {
- workspace->draw_rect(Rect2(0, 0, workspace->get_rect().size.x, region.position.y), shadow_color);
- }
- if (region.position.x >= 0) {
- workspace->draw_rect(Rect2(0, MAX(0, region.position.y), region.position.x, MIN(workspace->get_rect().size.y - region.position.y, MIN(region.size.y, region.position.y + region.size.y))), shadow_color);
- }
- if (region.position.x + region.size.x <= workspace->get_rect().size.x) {
- workspace->draw_rect(Rect2(region.position.x + region.size.x, MAX(0, region.position.y), workspace->get_rect().size.x - region.position.x - region.size.x, MIN(workspace->get_rect().size.y - region.position.y, MIN(region.size.y, region.position.y + region.size.y))), shadow_color);
- }
- if (region.position.y + region.size.y <= workspace->get_rect().size.y) {
- workspace->draw_rect(Rect2(0, region.position.y + region.size.y, workspace->get_rect().size.x, workspace->get_rect().size.y - region.size.y - region.position.y), shadow_color);
- }
- } else {
- workspace->draw_rect(Rect2(Point2(0, 0), workspace->get_rect().size), shadow_color);
- }
-}
-
-void TileSetEditor::draw_highlight_subtile(Vector2 coord, const Vector<Vector2> &other_highlighted) {
- Color shadow_color = Color(0.3, 0.3, 0.3, 0.3);
- Vector2 size = tileset->autotile_get_size(get_current_tile());
- int spacing = tileset->autotile_get_spacing(get_current_tile());
- Rect2 region = tileset->tile_get_region(get_current_tile());
- coord.x *= (size.x + spacing);
- coord.y *= (size.y + spacing);
- coord += region.position;
- coord += WORKSPACE_MARGIN;
-
- if (coord.y >= 0) {
- workspace->draw_rect(Rect2(0, 0, workspace->get_rect().size.x, coord.y), shadow_color);
- }
- if (coord.x >= 0) {
- workspace->draw_rect(Rect2(0, MAX(0, coord.y), coord.x, MIN(workspace->get_rect().size.y - coord.y, MIN(size.y, coord.y + size.y))), shadow_color);
- }
- if (coord.x + size.x <= workspace->get_rect().size.x) {
- workspace->draw_rect(Rect2(coord.x + size.x, MAX(0, coord.y), workspace->get_rect().size.x - coord.x - size.x, MIN(workspace->get_rect().size.y - coord.y, MIN(size.y, coord.y + size.y))), shadow_color);
- }
- if (coord.y + size.y <= workspace->get_rect().size.y) {
- workspace->draw_rect(Rect2(0, coord.y + size.y, workspace->get_rect().size.x, workspace->get_rect().size.y - size.y - coord.y), shadow_color);
- }
-
- coord += Vector2(1, 1) / workspace->get_scale().x;
- workspace->draw_rect(Rect2(coord, size - Vector2(2, 2) / workspace->get_scale().x), Color(1, 0, 0), false);
- for (int i = 0; i < other_highlighted.size(); i++) {
- coord = other_highlighted[i];
- coord.x *= (size.x + spacing);
- coord.y *= (size.y + spacing);
- coord += region.position;
- coord += WORKSPACE_MARGIN;
- coord += Vector2(1, 1) / workspace->get_scale().x;
- workspace->draw_rect(Rect2(coord, size - Vector2(2, 2) / workspace->get_scale().x), Color(1, 0.5, 0.5), false);
- }
-}
-
-void TileSetEditor::draw_tile_subdivision(int p_id, Color p_color) const {
- Color c = p_color;
- if (tileset->tile_get_tile_mode(p_id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(p_id) == TileSet::ATLAS_TILE) {
- Rect2 region = tileset->tile_get_region(p_id);
- Size2 size = tileset->autotile_get_size(p_id);
- int spacing = tileset->autotile_get_spacing(p_id);
- float j = size.x;
-
- while (j < region.size.x) {
- if (spacing <= 0) {
- workspace->draw_line(region.position + WORKSPACE_MARGIN + Point2(j, 0), region.position + WORKSPACE_MARGIN + Point2(j, region.size.y), c);
- } else {
- workspace->draw_rect(Rect2(region.position + WORKSPACE_MARGIN + Point2(j, 0), Size2(spacing, region.size.y)), c);
- }
- j += spacing + size.x;
- }
- j = size.y;
- while (j < region.size.y) {
- if (spacing <= 0) {
- workspace->draw_line(region.position + WORKSPACE_MARGIN + Point2(0, j), region.position + WORKSPACE_MARGIN + Point2(region.size.x, j), c);
- } else {
- workspace->draw_rect(Rect2(region.position + WORKSPACE_MARGIN + Point2(0, j), Size2(region.size.x, spacing)), c);
- }
- j += spacing + size.y;
- }
- }
-}
-
-void TileSetEditor::draw_edited_region_subdivision() const {
- Color c = Color(0.3, 0.7, 0.6);
- Rect2 region = edited_region;
- Size2 size;
- int spacing;
- bool draw;
-
- if (workspace_mode == WORKSPACE_EDIT) {
- int p_id = get_current_tile();
- size = tileset->autotile_get_size(p_id);
- spacing = tileset->autotile_get_spacing(p_id);
- draw = tileset->tile_get_tile_mode(p_id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(p_id) == TileSet::ATLAS_TILE;
- } else {
- size = snap_step;
- spacing = snap_separation.x;
- draw = workspace_mode != WORKSPACE_CREATE_SINGLE;
- }
-
- if (draw) {
- float j = size.x;
- while (j < region.size.x) {
- if (spacing <= 0) {
- workspace->draw_line(region.position + Point2(j, 0), region.position + Point2(j, region.size.y), c);
- } else {
- workspace->draw_rect(Rect2(region.position + Point2(j, 0), Size2(spacing, region.size.y)), c);
- }
- j += spacing + size.x;
- }
- j = size.y;
- while (j < region.size.y) {
- if (spacing <= 0) {
- workspace->draw_line(region.position + Point2(0, j), region.position + Point2(region.size.x, j), c);
- } else {
- workspace->draw_rect(Rect2(region.position + Point2(0, j), Size2(region.size.x, spacing)), c);
- }
- j += spacing + size.y;
- }
- }
-}
-
-void TileSetEditor::draw_grid_snap() {
- if (tools[TOOL_GRID_SNAP]->is_pressed()) {
- Color grid_color = Color(0.4, 0, 1);
- Size2 s = workspace->get_size();
-
- int width_count = Math::floor((s.width - WORKSPACE_MARGIN.x) / (snap_step.x + snap_separation.x));
- int height_count = Math::floor((s.height - WORKSPACE_MARGIN.y) / (snap_step.y + snap_separation.y));
-
- int last_p = 0;
- if (snap_step.x != 0) {
- for (int i = 0; i <= width_count; i++) {
- if (i == 0 && snap_offset.x != 0) {
- last_p = snap_offset.x;
- }
- if (snap_separation.x != 0) {
- if (i != 0) {
- workspace->draw_rect(Rect2(last_p, 0, snap_separation.x, s.height), grid_color);
- last_p += snap_separation.x;
- } else {
- workspace->draw_rect(Rect2(last_p, 0, -snap_separation.x, s.height), grid_color);
- }
- } else {
- workspace->draw_line(Point2(last_p, 0), Point2(last_p, s.height), grid_color);
- }
- last_p += snap_step.x;
- }
- }
- last_p = 0;
- if (snap_step.y != 0) {
- for (int i = 0; i <= height_count; i++) {
- if (i == 0 && snap_offset.y != 0) {
- last_p = snap_offset.y;
- }
- if (snap_separation.y != 0) {
- if (i != 0) {
- workspace->draw_rect(Rect2(0, last_p, s.width, snap_separation.y), grid_color);
- last_p += snap_separation.y;
- } else {
- workspace->draw_rect(Rect2(0, last_p, s.width, -snap_separation.y), grid_color);
- }
- } else {
- workspace->draw_line(Point2(0, last_p), Point2(s.width, last_p), grid_color);
- }
- last_p += snap_step.y;
- }
- }
- }
-}
-
-void TileSetEditor::draw_polygon_shapes() {
- int t_id = get_current_tile();
- if (t_id < 0) {
- return;
- }
-
- switch (edit_mode) {
- case EDITMODE_COLLISION: {
- Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(t_id);
- for (int i = 0; i < sd.size(); i++) {
- Vector2 coord = Vector2(0, 0);
- Vector2 anchor = Vector2(0, 0);
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
- coord = sd[i].autotile_coord;
- anchor = tileset->autotile_get_size(t_id);
- anchor.x += tileset->autotile_get_spacing(t_id);
- anchor.y += tileset->autotile_get_spacing(t_id);
- anchor.x *= coord.x;
- anchor.y *= coord.y;
- }
- anchor += WORKSPACE_MARGIN;
- anchor += tileset->tile_get_region(t_id).position;
- Ref<Shape2D> shape = sd[i].shape;
- if (shape.is_valid()) {
- Color c_bg;
- Color c_border;
- Ref<ConvexPolygonShape2D> convex = shape;
- bool is_convex = convex.is_valid();
- if ((tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE || coord == edited_shape_coord) && sd[i].shape == edited_collision_shape) {
- if (is_convex) {
- c_bg = Color(0, 1, 1, 0.5);
- c_border = Color(0, 1, 1);
- } else {
- c_bg = Color(0.8, 0, 1, 0.5);
- c_border = Color(0.8, 0, 1);
- }
- } else {
- if (is_convex) {
- c_bg = Color(0.9, 0.7, 0.07, 0.5);
- c_border = Color(0.9, 0.7, 0.07, 1);
-
- } else {
- c_bg = Color(0.9, 0.45, 0.075, 0.5);
- c_border = Color(0.9, 0.45, 0.075);
- }
- }
- Vector<Vector2> polygon;
- Vector<Color> colors;
- if (!creating_shape && shape == edited_collision_shape && current_shape.size() > 2) {
- for (int j = 0; j < current_shape.size(); j++) {
- polygon.push_back(current_shape[j]);
- colors.push_back(c_bg);
- }
- } else {
- for (int j = 0; j < _get_collision_shape_points(shape).size(); j++) {
- polygon.push_back(_get_collision_shape_points(shape)[j] + anchor);
- colors.push_back(c_bg);
- }
- }
-
- if (polygon.size() < 3) {
- continue;
- }
-
- workspace->draw_polygon(polygon, colors);
-
- if (coord == edited_shape_coord || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- if (!creating_shape && polygon.size() > 1) {
- for (int j = 0; j < polygon.size() - 1; j++) {
- workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1);
- }
- workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1);
- }
- if (shape == edited_collision_shape) {
- draw_handles = true;
- }
- }
- }
- }
- } break;
- case EDITMODE_OCCLUSION: {
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- Ref<OccluderPolygon2D> shape = edited_occlusion_shape;
- if (shape.is_valid()) {
- Color c_bg = Color(0, 1, 1, 0.5);
- Color c_border = Color(0, 1, 1);
-
- Vector<Vector2> polygon;
- Vector<Color> colors;
- Vector2 anchor = WORKSPACE_MARGIN;
- anchor += tileset->tile_get_region(get_current_tile()).position;
- if (!creating_shape && shape == edited_occlusion_shape && current_shape.size() > 2) {
- for (int j = 0; j < current_shape.size(); j++) {
- polygon.push_back(current_shape[j]);
- colors.push_back(c_bg);
- }
- } else {
- for (int j = 0; j < shape->get_polygon().size(); j++) {
- polygon.push_back(shape->get_polygon()[j] + anchor);
- colors.push_back(c_bg);
- }
- }
- workspace->draw_polygon(polygon, colors);
-
- if (!creating_shape && polygon.size() > 1) {
- for (int j = 0; j < polygon.size() - 1; j++) {
- workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1);
- }
- workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1);
- }
- if (shape == edited_occlusion_shape) {
- draw_handles = true;
- }
- }
- } else {
- Map<Vector2, Ref<OccluderPolygon2D>> map = tileset->autotile_get_light_oclusion_map(t_id);
- for (Map<Vector2, Ref<OccluderPolygon2D>>::Element *E = map.front(); E; E = E->next()) {
- Vector2 coord = E->key();
- Vector2 anchor = tileset->autotile_get_size(t_id);
- anchor.x += tileset->autotile_get_spacing(t_id);
- anchor.y += tileset->autotile_get_spacing(t_id);
- anchor.x *= coord.x;
- anchor.y *= coord.y;
- anchor += WORKSPACE_MARGIN;
- anchor += tileset->tile_get_region(t_id).position;
- Ref<OccluderPolygon2D> shape = E->value();
- if (shape.is_valid()) {
- Color c_bg;
- Color c_border;
- if (coord == edited_shape_coord && shape == edited_occlusion_shape) {
- c_bg = Color(0, 1, 1, 0.5);
- c_border = Color(0, 1, 1);
- } else {
- c_bg = Color(0.9, 0.7, 0.07, 0.5);
- c_border = Color(0.9, 0.7, 0.07, 1);
- }
- Vector<Vector2> polygon;
- Vector<Color> colors;
- if (!creating_shape && shape == edited_occlusion_shape && current_shape.size() > 2) {
- for (int j = 0; j < current_shape.size(); j++) {
- polygon.push_back(current_shape[j]);
- colors.push_back(c_bg);
- }
- } else {
- for (int j = 0; j < shape->get_polygon().size(); j++) {
- polygon.push_back(shape->get_polygon()[j] + anchor);
- colors.push_back(c_bg);
- }
- }
- workspace->draw_polygon(polygon, colors);
-
- if (coord == edited_shape_coord) {
- if (!creating_shape && polygon.size() > 1) {
- for (int j = 0; j < polygon.size() - 1; j++) {
- workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1);
- }
- workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1);
- }
- if (shape == edited_occlusion_shape) {
- draw_handles = true;
- }
- }
- }
- }
- }
- } break;
- case EDITMODE_NAVIGATION: {
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- Ref<NavigationPolygon> shape = edited_navigation_shape;
-
- if (shape.is_valid()) {
- Color c_bg = Color(0, 1, 1, 0.5);
- Color c_border = Color(0, 1, 1);
-
- Vector<Vector2> polygon;
- Vector<Color> colors;
- Vector2 anchor = WORKSPACE_MARGIN;
- anchor += tileset->tile_get_region(get_current_tile()).position;
- if (!creating_shape && shape == edited_navigation_shape && current_shape.size() > 2) {
- for (int j = 0; j < current_shape.size(); j++) {
- polygon.push_back(current_shape[j]);
- colors.push_back(c_bg);
- }
- } else {
- Vector<Vector2> vertices = shape->get_vertices();
- for (int j = 0; j < shape->get_polygon(0).size(); j++) {
- polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor);
- colors.push_back(c_bg);
- }
- }
- workspace->draw_polygon(polygon, colors);
-
- if (!creating_shape && polygon.size() > 1) {
- for (int j = 0; j < polygon.size() - 1; j++) {
- workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1);
- }
- workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1);
- }
- if (shape == edited_navigation_shape) {
- draw_handles = true;
- }
- }
- } else {
- Map<Vector2, Ref<NavigationPolygon>> map = tileset->autotile_get_navigation_map(t_id);
- for (Map<Vector2, Ref<NavigationPolygon>>::Element *E = map.front(); E; E = E->next()) {
- Vector2 coord = E->key();
- Vector2 anchor = tileset->autotile_get_size(t_id);
- anchor.x += tileset->autotile_get_spacing(t_id);
- anchor.y += tileset->autotile_get_spacing(t_id);
- anchor.x *= coord.x;
- anchor.y *= coord.y;
- anchor += WORKSPACE_MARGIN;
- anchor += tileset->tile_get_region(t_id).position;
- Ref<NavigationPolygon> shape = E->value();
- if (shape.is_valid()) {
- Color c_bg;
- Color c_border;
- if (coord == edited_shape_coord && shape == edited_navigation_shape) {
- c_bg = Color(0, 1, 1, 0.5);
- c_border = Color(0, 1, 1);
- } else {
- c_bg = Color(0.9, 0.7, 0.07, 0.5);
- c_border = Color(0.9, 0.7, 0.07, 1);
- }
- Vector<Vector2> polygon;
- Vector<Color> colors;
- if (!creating_shape && shape == edited_navigation_shape && current_shape.size() > 2) {
- for (int j = 0; j < current_shape.size(); j++) {
- polygon.push_back(current_shape[j]);
- colors.push_back(c_bg);
- }
- } else {
- Vector<Vector2> vertices = shape->get_vertices();
- for (int j = 0; j < shape->get_polygon(0).size(); j++) {
- polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor);
- colors.push_back(c_bg);
- }
- }
- workspace->draw_polygon(polygon, colors);
-
- if (coord == edited_shape_coord) {
- if (!creating_shape && polygon.size() > 1) {
- for (int j = 0; j < polygon.size() - 1; j++) {
- workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1);
- }
- workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1);
- }
- if (shape == edited_navigation_shape) {
- draw_handles = true;
- }
- }
- }
- }
- }
- } break;
- default: {
- }
- }
-
- if (creating_shape && current_shape.size() > 1) {
- for (int j = 0; j < current_shape.size() - 1; j++) {
- workspace->draw_line(current_shape[j], current_shape[j + 1], Color(0, 1, 1), 1);
- }
- workspace->draw_line(current_shape[current_shape.size() - 1], snap_point(workspace->get_local_mouse_position()), Color(0, 1, 1), 1);
- draw_handles = true;
- }
-}
-
-void TileSetEditor::close_shape(const Vector2 &shape_anchor) {
- creating_shape = false;
-
- if (edit_mode == EDITMODE_COLLISION) {
- if (current_shape.size() >= 3) {
- Ref<ConvexPolygonShape2D> shape = memnew(ConvexPolygonShape2D);
-
- Vector<Vector2> points;
- float p_total = 0;
-
- for (int i = 0; i < current_shape.size(); i++) {
- points.push_back(current_shape[i] - shape_anchor);
-
- if (i != current_shape.size() - 1) {
- p_total += ((current_shape[i + 1].x - current_shape[i].x) * (-current_shape[i + 1].y + (-current_shape[i].y)));
- } else {
- p_total += ((current_shape[0].x - current_shape[i].x) * (-current_shape[0].y + (-current_shape[i].y)));
- }
- }
-
- if (p_total < 0) {
- points.reverse();
- }
-
- shape->set_points(points);
-
- undo_redo->create_action(TTR("Create Collision Polygon"));
- // Necessary to get the version that returns a Array instead of a Vector.
- Array sd = tileset->call("tile_get_shapes", get_current_tile());
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd.duplicate());
- for (int i = 0; i < sd.size(); i++) {
- if (sd[i].get("shape") == edited_collision_shape) {
- sd.remove(i);
- break;
- }
- }
- undo_redo->add_do_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd);
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
- undo_redo->add_do_method(tileset.ptr(), "tile_add_shape", get_current_tile(), shape, Transform2D(), false, edited_shape_coord);
- } else {
- undo_redo->add_do_method(tileset.ptr(), "tile_add_shape", get_current_tile(), shape, Transform2D());
- }
- tools[TOOL_SELECT]->set_pressed(true);
- undo_redo->add_do_method(this, "_select_edited_shape_coord");
- undo_redo->add_undo_method(this, "_select_edited_shape_coord");
- undo_redo->commit_action();
- } else {
- tools[TOOL_SELECT]->set_pressed(true);
- workspace->update();
- }
- } else if (edit_mode == EDITMODE_OCCLUSION) {
- Ref<OccluderPolygon2D> shape = memnew(OccluderPolygon2D);
-
- Vector<Vector2> polygon;
- polygon.resize(current_shape.size());
- Vector2 *w = polygon.ptrw();
-
- for (int i = 0; i < current_shape.size(); i++) {
- w[i] = current_shape[i] - shape_anchor;
- }
-
- shape->set_polygon(polygon);
-
- undo_redo->create_action(TTR("Create Occlusion Polygon"));
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), shape, edited_shape_coord);
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), tileset->autotile_get_light_occluder(get_current_tile(), edited_shape_coord), edited_shape_coord);
- } else {
- undo_redo->add_do_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), shape);
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), tileset->tile_get_light_occluder(get_current_tile()));
- }
- tools[TOOL_SELECT]->set_pressed(true);
- undo_redo->add_do_method(this, "_select_edited_shape_coord");
- undo_redo->add_undo_method(this, "_select_edited_shape_coord");
- undo_redo->commit_action();
- } else if (edit_mode == EDITMODE_NAVIGATION) {
- Ref<NavigationPolygon> shape = memnew(NavigationPolygon);
-
- Vector<Vector2> polygon;
- Vector<int> indices;
- polygon.resize(current_shape.size());
- Vector2 *w = polygon.ptrw();
-
- for (int i = 0; i < current_shape.size(); i++) {
- w[i] = current_shape[i] - shape_anchor;
- indices.push_back(i);
- }
-
- shape->set_vertices(polygon);
- shape->add_polygon(indices);
-
- undo_redo->create_action(TTR("Create Navigation Polygon"));
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
- undo_redo->add_do_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), shape, edited_shape_coord);
- undo_redo->add_undo_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), tileset->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord), edited_shape_coord);
- } else {
- undo_redo->add_do_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), shape);
- undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), tileset->tile_get_navigation_polygon(get_current_tile()));
- }
- tools[TOOL_SELECT]->set_pressed(true);
- undo_redo->add_do_method(this, "_select_edited_shape_coord");
- undo_redo->add_undo_method(this, "_select_edited_shape_coord");
- undo_redo->commit_action();
- }
- tileset->notify_property_list_changed();
-}
-
-void TileSetEditor::select_coord(const Vector2 &coord) {
- _update_tile_data();
- current_shape = PackedVector2Array();
- if (get_current_tile() == -1) {
- return;
- }
- Rect2 current_tile_region = tileset->tile_get_region(get_current_tile());
- current_tile_region.position += WORKSPACE_MARGIN;
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- if (edited_collision_shape != tileset->tile_get_shape(get_current_tile(), 0)) {
- _set_edited_collision_shape(tileset->tile_get_shape(get_current_tile(), 0));
- }
- if (edited_occlusion_shape != tileset->tile_get_light_occluder(get_current_tile())) {
- edited_occlusion_shape = tileset->tile_get_light_occluder(get_current_tile());
- }
- if (edited_navigation_shape != tileset->tile_get_navigation_polygon(get_current_tile())) {
- edited_navigation_shape = tileset->tile_get_navigation_polygon(get_current_tile());
- }
-
- if (edit_mode == EDITMODE_COLLISION) {
- current_shape.resize(0);
- if (edited_collision_shape.is_valid()) {
- for (int i = 0; i < _get_edited_shape_points().size(); i++) {
- current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position);
- }
- }
- } else if (edit_mode == EDITMODE_OCCLUSION) {
- current_shape.resize(0);
- if (edited_occlusion_shape.is_valid()) {
- for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) {
- current_shape.push_back(edited_occlusion_shape->get_polygon()[i] + current_tile_region.position);
- }
- }
- } else if (edit_mode == EDITMODE_NAVIGATION) {
- current_shape.resize(0);
- if (edited_navigation_shape.is_valid()) {
- if (edited_navigation_shape->get_polygon_count() > 0) {
- Vector<Vector2> vertices = edited_navigation_shape->get_vertices();
- for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) {
- current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]] + current_tile_region.position);
- }
- }
- }
- }
- } else {
- Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile());
- bool found_collision_shape = false;
- for (int i = 0; i < sd.size(); i++) {
- if (sd[i].autotile_coord == coord) {
- if (edited_collision_shape != sd[i].shape) {
- _set_edited_collision_shape(sd[i].shape);
- }
- found_collision_shape = true;
- break;
- }
- }
- if (!found_collision_shape) {
- _set_edited_collision_shape(Ref<ConvexPolygonShape2D>(nullptr));
- }
- if (edited_occlusion_shape != tileset->autotile_get_light_occluder(get_current_tile(), coord)) {
- edited_occlusion_shape = tileset->autotile_get_light_occluder(get_current_tile(), coord);
- }
- if (edited_navigation_shape != tileset->autotile_get_navigation_polygon(get_current_tile(), coord)) {
- edited_navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), coord);
- }
-
- int spacing = tileset->autotile_get_spacing(get_current_tile());
- Vector2 size = tileset->autotile_get_size(get_current_tile());
- Vector2 shape_anchor = coord;
- shape_anchor.x *= (size.x + spacing);
- shape_anchor.y *= (size.y + spacing);
- shape_anchor += current_tile_region.position;
- if (edit_mode == EDITMODE_COLLISION) {
- current_shape.resize(0);
- if (edited_collision_shape.is_valid()) {
- for (int j = 0; j < _get_edited_shape_points().size(); j++) {
- current_shape.push_back(_get_edited_shape_points()[j] + shape_anchor);
- }
- }
- } else if (edit_mode == EDITMODE_OCCLUSION) {
- current_shape.resize(0);
- if (edited_occlusion_shape.is_valid()) {
- for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) {
- current_shape.push_back(edited_occlusion_shape->get_polygon()[i] + shape_anchor);
- }
- }
- } else if (edit_mode == EDITMODE_NAVIGATION) {
- current_shape.resize(0);
- if (edited_navigation_shape.is_valid()) {
- if (edited_navigation_shape->get_polygon_count() > 0) {
- Vector<Vector2> vertices = edited_navigation_shape->get_vertices();
- for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) {
- current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]] + shape_anchor);
- }
- }
- }
- }
- }
- workspace->update();
- workspace_container->update();
- helper->notify_property_list_changed();
-}
-
-Vector2 TileSetEditor::snap_point(const Vector2 &point) {
- Vector2 p = point;
- Vector2 coord = edited_shape_coord;
- Vector2 tile_size = tileset->autotile_get_size(get_current_tile());
- int spacing = tileset->autotile_get_spacing(get_current_tile());
- Vector2 anchor = coord;
- anchor.x *= (tile_size.x + spacing);
- anchor.y *= (tile_size.y + spacing);
- anchor += tileset->tile_get_region(get_current_tile()).position;
- anchor += WORKSPACE_MARGIN;
- Rect2 region(anchor, tile_size);
- Rect2 tile_region(tileset->tile_get_region(get_current_tile()).position + WORKSPACE_MARGIN, tileset->tile_get_region(get_current_tile()).size);
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- region.position = tileset->tile_get_region(get_current_tile()).position + WORKSPACE_MARGIN;
- region.size = tileset->tile_get_region(get_current_tile()).size;
- }
-
- if (tools[TOOL_GRID_SNAP]->is_pressed()) {
- p.x = Math::snap_scalar_separation(snap_offset.x, snap_step.x, p.x, snap_separation.x);
- p.y = Math::snap_scalar_separation(snap_offset.y, snap_step.y, p.y, snap_separation.y);
- }
-
- if (tools[SHAPE_KEEP_INSIDE_TILE]->is_pressed()) {
- if (p.x < region.position.x) {
- p.x = region.position.x;
- }
- if (p.y < region.position.y) {
- p.y = region.position.y;
- }
- if (p.x > region.position.x + region.size.x) {
- p.x = region.position.x + region.size.x;
- }
- if (p.y > region.position.y + region.size.y) {
- p.y = region.position.y + region.size.y;
- }
- }
-
- if (p.x < tile_region.position.x) {
- p.x = tile_region.position.x;
- }
- if (p.y < tile_region.position.y) {
- p.y = tile_region.position.y;
- }
- if (p.x > (tile_region.position.x + tile_region.size.x)) {
- p.x = (tile_region.position.x + tile_region.size.x);
- }
- if (p.y > (tile_region.position.y + tile_region.size.y)) {
- p.y = (tile_region.position.y + tile_region.size.y);
- }
-
- return p;
-}
-
-void TileSetEditor::add_texture(Ref<Texture2D> p_texture) {
- texture_list->add_item(p_texture->get_path().get_file());
- texture_map.insert(p_texture->get_rid(), p_texture);
- texture_list->set_item_metadata(texture_list->get_item_count() - 1, p_texture->get_rid());
-}
-
-void TileSetEditor::remove_texture(Ref<Texture2D> p_texture) {
- texture_list->remove_item(texture_list->find_metadata(p_texture->get_rid()));
- texture_map.erase(p_texture->get_rid());
-
- _validate_current_tile_id();
-
- if (!get_current_texture().is_valid()) {
- _on_texture_list_selected(-1);
- workspace_overlay->update();
- }
-}
-
-void TileSetEditor::update_texture_list() {
- Ref<Texture2D> selected_texture = get_current_texture();
-
- helper->set_tileset(tileset);
-
- List<int> ids;
- tileset->get_tile_list(&ids);
- Vector<int> ids_to_remove;
- for (List<int>::Element *E = ids.front(); E; E = E->next()) {
- // Clear tiles referencing gone textures (user has been already given the chance to fix broken deps)
- if (!tileset->tile_get_texture(E->get()).is_valid()) {
- ids_to_remove.push_back(E->get());
- ERR_CONTINUE(!tileset->tile_get_texture(E->get()).is_valid());
- }
-
- if (!texture_map.has(tileset->tile_get_texture(E->get())->get_rid())) {
- add_texture(tileset->tile_get_texture(E->get()));
- }
- }
- for (int i = 0; i < ids_to_remove.size(); i++) {
- tileset->remove_tile(ids_to_remove[i]);
- }
-
- if (texture_list->get_item_count() > 0 && selected_texture.is_valid()) {
- texture_list->select(texture_list->find_metadata(selected_texture->get_rid()));
- if (texture_list->get_selected_items().size() > 0) {
- _on_texture_list_selected(texture_list->get_selected_items()[0]);
- }
- } else if (get_current_texture().is_valid()) {
- _on_texture_list_selected(texture_list->find_metadata(get_current_texture()->get_rid()));
- } else {
- _validate_current_tile_id();
- _on_texture_list_selected(-1);
- workspace_overlay->update();
- }
- update_texture_list_icon();
- helper->notify_property_list_changed();
-}
-
-void TileSetEditor::update_texture_list_icon() {
- for (int current_idx = 0; current_idx < texture_list->get_item_count(); current_idx++) {
- RID rid = texture_list->get_item_metadata(current_idx);
- texture_list->set_item_icon(current_idx, texture_map[rid]);
- Size2 texture_size = texture_map[rid]->get_size();
- texture_list->set_item_icon_region(current_idx, Rect2(0, 0, MIN(texture_size.x, 150), MIN(texture_size.y, 100)));
- }
- texture_list->update();
-}
-
-void TileSetEditor::update_workspace_tile_mode() {
- if (!get_current_texture().is_valid()) {
- tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true);
- workspace_mode = WORKSPACE_EDIT;
- for (int i = 1; i < WORKSPACE_MODE_MAX; i++) {
- tool_workspacemode[i]->set_disabled(true);
- }
- tools[SELECT_NEXT]->set_disabled(true);
- tools[SELECT_PREVIOUS]->set_disabled(true);
-
- tools[ZOOM_OUT]->hide();
- tools[ZOOM_1]->hide();
- tools[ZOOM_IN]->hide();
- tools[VISIBLE_INFO]->hide();
-
- scroll->hide();
- empty_message->show();
- } else {
- for (int i = 1; i < WORKSPACE_MODE_MAX; i++) {
- tool_workspacemode[i]->set_disabled(false);
- }
- tools[SELECT_NEXT]->set_disabled(false);
- tools[SELECT_PREVIOUS]->set_disabled(false);
-
- tools[ZOOM_OUT]->show();
- tools[ZOOM_1]->show();
- tools[ZOOM_IN]->show();
- tools[VISIBLE_INFO]->show();
-
- scroll->show();
- empty_message->hide();
- }
-
- if (workspace_mode != WORKSPACE_EDIT) {
- for (int i = 0; i < EDITMODE_MAX; i++) {
- tool_editmode[i]->hide();
- }
- tool_editmode[EDITMODE_REGION]->show();
- tool_editmode[EDITMODE_REGION]->set_pressed(true);
- _on_edit_mode_changed(EDITMODE_REGION);
- separator_editmode->show();
- return;
- }
-
- if (get_current_tile() < 0) {
- for (int i = 0; i < EDITMODE_MAX; i++) {
- tool_editmode[i]->hide();
- }
- for (int i = TOOL_SELECT; i < ZOOM_OUT; i++) {
- tools[i]->hide();
- }
-
- separator_editmode->hide();
- separator_bitmask->hide();
- separator_delete->hide();
- separator_grid->hide();
- return;
- }
-
- for (int i = 0; i < EDITMODE_MAX; i++) {
- tool_editmode[i]->show();
- }
- separator_editmode->show();
-
- if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
- if (tool_editmode[EDITMODE_ICON]->is_pressed() || tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed() || tool_editmode[EDITMODE_Z_INDEX]->is_pressed()) {
- tool_editmode[EDITMODE_COLLISION]->set_pressed(true);
- edit_mode = EDITMODE_COLLISION;
- }
- select_coord(Vector2(0, 0));
-
- tool_editmode[EDITMODE_ICON]->hide();
- tool_editmode[EDITMODE_BITMASK]->hide();
- tool_editmode[EDITMODE_PRIORITY]->hide();
- tool_editmode[EDITMODE_Z_INDEX]->hide();
- } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) {
- if (edit_mode == EDITMODE_ICON) {
- select_coord(tileset->autotile_get_icon_coordinate(get_current_tile()));
- } else {
- _select_edited_shape_coord();
- }
- } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
- if (tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed()) {
- tool_editmode[EDITMODE_COLLISION]->set_pressed(true);
- edit_mode = EDITMODE_COLLISION;
- }
- if (edit_mode == EDITMODE_ICON) {
- select_coord(tileset->autotile_get_icon_coordinate(get_current_tile()));
- } else {
- _select_edited_shape_coord();
- }
-
- tool_editmode[EDITMODE_BITMASK]->hide();
- }
- _on_edit_mode_changed(edit_mode);
-}
-
-void TileSetEditor::update_workspace_minsize() {
- Size2 workspace_min_size = get_current_texture()->get_size();
- RID current_texture_rid = get_current_texture()->get_rid();
- List<int> *tiles = new List<int>();
- tileset->get_tile_list(tiles);
- for (List<int>::Element *E = tiles->front(); E; E = E->next()) {
- if (tileset->tile_get_texture(E->get())->get_rid() != current_texture_rid) {
- continue;
- }
-
- Rect2i region = tileset->tile_get_region(E->get());
- if (region.position.x + region.size.x > workspace_min_size.x) {
- workspace_min_size.x = region.position.x + region.size.x;
- }
- if (region.position.y + region.size.y > workspace_min_size.y) {
- workspace_min_size.y = region.position.y + region.size.y;
- }
- }
- delete tiles;
-
- workspace->set_custom_minimum_size(workspace_min_size + WORKSPACE_MARGIN * 2);
- workspace_container->set_custom_minimum_size(workspace_min_size * workspace->get_scale() + WORKSPACE_MARGIN * 2);
- workspace_overlay->set_custom_minimum_size(workspace_min_size * workspace->get_scale() + WORKSPACE_MARGIN * 2);
-}
-
-void TileSetEditor::update_edited_region(const Vector2 &end_point) {
- edited_region = Rect2(region_from, Size2());
- if (tools[TOOL_GRID_SNAP]->is_pressed()) {
- Vector2 grid_coord;
- grid_coord = ((region_from - snap_offset) / (snap_step + snap_separation)).floor();
- grid_coord *= (snap_step + snap_separation);
- grid_coord += snap_offset;
- edited_region.expand_to(grid_coord);
- grid_coord += snap_step;
- edited_region.expand_to(grid_coord);
-
- grid_coord = ((end_point - snap_offset) / (snap_step + snap_separation)).floor();
- grid_coord *= (snap_step + snap_separation);
- grid_coord += snap_offset;
- edited_region.expand_to(grid_coord);
- grid_coord += snap_step;
- edited_region.expand_to(grid_coord);
- } else {
- edited_region.expand_to(end_point);
- }
-}
-
-int TileSetEditor::get_current_tile() const {
- return current_tile;
-}
-
-void TileSetEditor::set_current_tile(int p_id) {
- if (current_tile != p_id) {
- current_tile = p_id;
- helper->notify_property_list_changed();
- select_coord(Vector2(0, 0));
- update_workspace_tile_mode();
- if (p_id == -1) {
- editor->get_inspector()->edit(tileset.ptr());
- } else {
- editor->get_inspector()->edit(helper);
- }
- }
-}
-
-Ref<Texture2D> TileSetEditor::get_current_texture() {
- if (texture_list->get_selected_items().size() == 0) {
- return Ref<Texture2D>();
- } else {
- return texture_map[texture_list->get_item_metadata(texture_list->get_selected_items()[0])];
- }
-}
-
-void TilesetEditorContext::set_tileset(const Ref<TileSet> &p_tileset) {
- tileset = p_tileset;
-}
-
-void TilesetEditorContext::set_snap_options_visible(bool p_visible) {
- snap_options_visible = p_visible;
- notify_property_list_changed();
-}
-
-bool TilesetEditorContext::_set(const StringName &p_name, const Variant &p_value) {
- String name = p_name.operator String();
-
- if (name == "options_offset") {
- Vector2 snap = p_value;
- tileset_editor->_set_snap_off(snap + WORKSPACE_MARGIN);
- return true;
- } else if (name == "options_step") {
- Vector2 snap = p_value;
- tileset_editor->_set_snap_step(snap);
- return true;
- } else if (name == "options_separation") {
- Vector2 snap = p_value;
- tileset_editor->_set_snap_sep(snap);
- return true;
- } else if (p_name.operator String().left(5) == "tile_") {
- String name2 = p_name.operator String().right(5);
- bool v = false;
-
- if (tileset_editor->get_current_tile() < 0 || tileset.is_null()) {
- return false;
- }
-
- if (name2 == "autotile_bitmask_mode") {
- tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", p_value, &v);
- } else if (name2 == "subtile_size") {
- tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/tile_size", p_value, &v);
- } else if (name2 == "subtile_spacing") {
- tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/spacing", p_value, &v);
- } else {
- tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/" + name2, p_value, &v);
- }
- if (v) {
- tileset->notify_property_list_changed();
- tileset_editor->workspace->update();
- tileset_editor->workspace_overlay->update();
- }
- return v;
- } else if (name == "tileset_script") {
- tileset->set_script(p_value);
- return true;
- } else if (name == "selected_collision_one_way") {
- Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile());
- for (int index = 0; index < sd.size(); index++) {
- if (sd[index].shape == tileset_editor->edited_collision_shape) {
- tileset->tile_set_shape_one_way(tileset_editor->get_current_tile(), index, p_value);
- return true;
- }
- }
- return false;
- } else if (name == "selected_collision_one_way_margin") {
- Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile());
- for (int index = 0; index < sd.size(); index++) {
- if (sd[index].shape == tileset_editor->edited_collision_shape) {
- tileset->tile_set_shape_one_way_margin(tileset_editor->get_current_tile(), index, p_value);
- return true;
- }
- }
- return false;
- }
-
- tileset_editor->err_dialog->set_text(TTR("This property can't be changed."));
- tileset_editor->err_dialog->popup_centered(Size2(300, 60));
- return false;
-}
-
-bool TilesetEditorContext::_get(const StringName &p_name, Variant &r_ret) const {
- String name = p_name.operator String();
- bool v = false;
-
- if (name == "options_offset") {
- r_ret = tileset_editor->snap_offset - WORKSPACE_MARGIN;
- v = true;
- } else if (name == "options_step") {
- r_ret = tileset_editor->snap_step;
- v = true;
- } else if (name == "options_separation") {
- r_ret = tileset_editor->snap_separation;
- v = true;
- } else if (name.left(5) == "tile_") {
- name = name.right(5);
-
- if (tileset_editor->get_current_tile() < 0 || tileset.is_null()) {
- return false;
- }
- if (!tileset->has_tile(tileset_editor->get_current_tile())) {
- return false;
- }
-
- if (name == "autotile_bitmask_mode") {
- r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", &v);
- } else if (name == "subtile_size") {
- r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/tile_size", &v);
- } else if (name == "subtile_spacing") {
- r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/spacing", &v);
- } else {
- r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/" + name, &v);
- }
- return v;
- } else if (name == "selected_collision") {
- r_ret = tileset_editor->edited_collision_shape;
- v = true;
- } else if (name == "selected_collision_one_way") {
- Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile());
- for (int index = 0; index < sd.size(); index++) {
- if (sd[index].shape == tileset_editor->edited_collision_shape) {
- r_ret = sd[index].one_way_collision;
- v = true;
- break;
- }
- }
- } else if (name == "selected_collision_one_way_margin") {
- Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile());
- for (int index = 0; index < sd.size(); index++) {
- if (sd[index].shape == tileset_editor->edited_collision_shape) {
- r_ret = sd[index].one_way_collision_margin;
- v = true;
- break;
- }
- }
- } else if (name == "selected_navigation") {
- r_ret = tileset_editor->edited_navigation_shape;
- v = true;
- } else if (name == "selected_occlusion") {
- r_ret = tileset_editor->edited_occlusion_shape;
- v = true;
- } else if (name == "tileset_script") {
- r_ret = tileset->get_script();
- v = true;
- }
- return v;
-}
-
-void TilesetEditorContext::_get_property_list(List<PropertyInfo> *p_list) const {
- if (snap_options_visible) {
- p_list->push_back(PropertyInfo(Variant::NIL, "Snap Options", PROPERTY_HINT_NONE, "options_", PROPERTY_USAGE_GROUP));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "options_offset"));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "options_step"));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "options_separation"));
- }
- if (tileset_editor->get_current_tile() >= 0 && !tileset.is_null()) {
- int id = tileset_editor->get_current_tile();
- p_list->push_back(PropertyInfo(Variant::NIL, "Selected Tile", PROPERTY_HINT_NONE, "tile_", PROPERTY_USAGE_GROUP));
- p_list->push_back(PropertyInfo(Variant::STRING, "tile_name"));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_tex_offset"));
- p_list->push_back(PropertyInfo(Variant::OBJECT, "tile_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial"));
- p_list->push_back(PropertyInfo(Variant::COLOR, "tile_modulate"));
- p_list->push_back(PropertyInfo(Variant::INT, "tile_tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE,ATLAS_TILE"));
- if (tileset->tile_get_tile_mode(id) == TileSet::AUTO_TILE) {
- p_list->push_back(PropertyInfo(Variant::INT, "tile_autotile_bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3 (minimal),3X3"));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_subtile_size"));
- p_list->push_back(PropertyInfo(Variant::INT, "tile_subtile_spacing", PROPERTY_HINT_RANGE, "0, 256, 1"));
- } else if (tileset->tile_get_tile_mode(id) == TileSet::ATLAS_TILE) {
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_subtile_size"));
- p_list->push_back(PropertyInfo(Variant::INT, "tile_subtile_spacing", PROPERTY_HINT_RANGE, "0, 256, 1"));
- }
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_occluder_offset"));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_navigation_offset"));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_shape_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_shape_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
- p_list->push_back(PropertyInfo(Variant::INT, "tile_z_index", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"));
- }
- if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_COLLISION && tileset_editor->edited_collision_shape.is_valid()) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_collision", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_collision_shape->get_class()));
- if (tileset_editor->edited_collision_shape.is_valid()) {
- p_list->push_back(PropertyInfo(Variant::BOOL, "selected_collision_one_way", PROPERTY_HINT_NONE));
- p_list->push_back(PropertyInfo(Variant::FLOAT, "selected_collision_one_way_margin", PROPERTY_HINT_NONE));
- }
- }
- if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_NAVIGATION && tileset_editor->edited_navigation_shape.is_valid()) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_navigation", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_navigation_shape->get_class()));
- }
- if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_OCCLUSION && tileset_editor->edited_occlusion_shape.is_valid()) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_occlusion", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_occlusion_shape->get_class()));
- }
- if (!tileset.is_null()) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "tileset_script", PROPERTY_HINT_RESOURCE_TYPE, "Script"));
- }
-}
-
-void TilesetEditorContext::_bind_methods() {
- ClassDB::bind_method("_hide_script_from_inspector", &TilesetEditorContext::_hide_script_from_inspector);
-}
-
-TilesetEditorContext::TilesetEditorContext(TileSetEditor *p_tileset_editor) {
- tileset_editor = p_tileset_editor;
- snap_options_visible = false;
-}
-
-void TileSetEditorPlugin::edit(Object *p_node) {
- if (Object::cast_to<TileSet>(p_node)) {
- tileset_editor->edit(Object::cast_to<TileSet>(p_node));
- }
-}
-
-bool TileSetEditorPlugin::handles(Object *p_node) const {
- return p_node->is_class("TileSet") || p_node->is_class("TilesetEditorContext");
-}
-
-void TileSetEditorPlugin::make_visible(bool p_visible) {
- if (p_visible) {
- tileset_editor_button->show();
- editor->make_bottom_panel_item_visible(tileset_editor);
- get_tree()->connect("idle_frame", Callable(tileset_editor, "_on_workspace_process"));
- } else {
- editor->hide_bottom_panel();
- tileset_editor_button->hide();
- get_tree()->disconnect("idle_frame", Callable(tileset_editor, "_on_workspace_process"));
- }
-}
-
-Dictionary TileSetEditorPlugin::get_state() const {
- Dictionary state;
- state["snap_offset"] = tileset_editor->snap_offset;
- state["snap_step"] = tileset_editor->snap_step;
- state["snap_separation"] = tileset_editor->snap_separation;
- state["snap_enabled"] = tileset_editor->tools[TileSetEditor::TOOL_GRID_SNAP]->is_pressed();
- state["keep_inside_tile"] = tileset_editor->tools[TileSetEditor::SHAPE_KEEP_INSIDE_TILE]->is_pressed();
- state["show_information"] = tileset_editor->tools[TileSetEditor::VISIBLE_INFO]->is_pressed();
- return state;
-}
-
-void TileSetEditorPlugin::set_state(const Dictionary &p_state) {
- Dictionary state = p_state;
- if (state.has("snap_step")) {
- tileset_editor->_set_snap_step(state["snap_step"]);
- }
-
- if (state.has("snap_offset")) {
- tileset_editor->_set_snap_off(state["snap_offset"]);
- }
-
- if (state.has("snap_separation")) {
- tileset_editor->_set_snap_sep(state["snap_separation"]);
- }
-
- if (state.has("snap_enabled")) {
- tileset_editor->tools[TileSetEditor::TOOL_GRID_SNAP]->set_pressed(state["snap_enabled"]);
- if (tileset_editor->helper) {
- tileset_editor->_on_grid_snap_toggled(state["snap_enabled"]);
- }
- }
-
- if (state.has("keep_inside_tile")) {
- tileset_editor->tools[TileSetEditor::SHAPE_KEEP_INSIDE_TILE]->set_pressed(state["keep_inside_tile"]);
- }
-
- if (state.has("show_information")) {
- tileset_editor->tools[TileSetEditor::VISIBLE_INFO]->set_pressed(state["show_information"]);
- }
-}
-
-TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) {
- editor = p_node;
- tileset_editor = memnew(TileSetEditor(p_node));
-
- tileset_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
- tileset_editor->hide();
-
- tileset_editor_button = p_node->add_bottom_panel_item(TTR("TileSet"), tileset_editor);
- tileset_editor_button->hide();
-}
diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h
deleted file mode 100644
index e778c18f44..0000000000
--- a/editor/plugins/tile_set_editor_plugin.h
+++ /dev/null
@@ -1,298 +0,0 @@
-/*************************************************************************/
-/* tile_set_editor_plugin.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef TILE_SET_EDITOR_PLUGIN_H
-#define TILE_SET_EDITOR_PLUGIN_H
-
-#include "editor/editor_node.h"
-#include "scene/2d/sprite_2d.h"
-#include "scene/resources/concave_polygon_shape_2d.h"
-#include "scene/resources/convex_polygon_shape_2d.h"
-#include "scene/resources/tile_set.h"
-
-#define WORKSPACE_MARGIN Vector2(10, 10)
-class TilesetEditorContext;
-
-class TileSetEditor : public HSplitContainer {
- friend class TileSetEditorPlugin;
- friend class TilesetEditorContext;
-
- GDCLASS(TileSetEditor, HSplitContainer);
-
- enum TextureButtons {
- TOOL_TILESET_ADD_TEXTURE,
- TOOL_TILESET_REMOVE_TEXTURE,
- TOOL_TILESET_CREATE_SCENE,
- TOOL_TILESET_MERGE_SCENE,
- TOOL_TILESET_MAX
- };
-
- enum WorkspaceMode {
- WORKSPACE_EDIT,
- WORKSPACE_CREATE_SINGLE,
- WORKSPACE_CREATE_AUTOTILE,
- WORKSPACE_CREATE_ATLAS,
- WORKSPACE_MODE_MAX
- };
-
- enum EditMode {
- EDITMODE_REGION,
- EDITMODE_COLLISION,
- EDITMODE_OCCLUSION,
- EDITMODE_NAVIGATION,
- EDITMODE_BITMASK,
- EDITMODE_PRIORITY,
- EDITMODE_ICON,
- EDITMODE_Z_INDEX,
- EDITMODE_MAX
- };
-
- enum TileSetTools {
- SELECT_PREVIOUS,
- SELECT_NEXT,
- TOOL_SELECT,
- BITMASK_COPY,
- BITMASK_PASTE,
- BITMASK_CLEAR,
- SHAPE_NEW_POLYGON,
- SHAPE_NEW_RECTANGLE,
- SHAPE_TOGGLE_TYPE,
- SHAPE_DELETE,
- SHAPE_KEEP_INSIDE_TILE,
- TOOL_GRID_SNAP,
- ZOOM_OUT,
- ZOOM_1,
- ZOOM_IN,
- VISIBLE_INFO,
- TOOL_MAX
- };
-
- struct SubtileData {
- Array collisions;
- Ref<OccluderPolygon2D> occlusion_shape;
- Ref<NavigationPolygon> navigation_shape;
- };
-
- Ref<TileSet> tileset;
- TilesetEditorContext *helper;
- EditorNode *editor;
- UndoRedo *undo_redo;
-
- ConfirmationDialog *cd;
- AcceptDialog *err_dialog;
- EditorFileDialog *texture_dialog;
-
- ItemList *texture_list;
- int option;
- Button *tileset_toolbar_buttons[TOOL_TILESET_MAX];
- MenuButton *tileset_toolbar_tools;
- Map<RID, Ref<Texture2D>> texture_map;
-
- bool creating_shape;
- int dragging_point;
- bool tile_names_visible;
- Vector2 region_from;
- Rect2 edited_region;
- bool draw_edited_region;
- Vector2 edited_shape_coord;
- PackedVector2Array current_shape;
- Map<Vector2i, SubtileData> current_tile_data;
- Map<Vector2, uint32_t> bitmask_map_copy;
-
- Vector2 snap_step;
- Vector2 snap_offset;
- Vector2 snap_separation;
-
- Ref<Shape2D> edited_collision_shape;
- Ref<OccluderPolygon2D> edited_occlusion_shape;
- Ref<NavigationPolygon> edited_navigation_shape;
-
- int current_item_index;
- Sprite2D *preview;
- ScrollContainer *scroll;
- Label *empty_message;
- Control *workspace_container;
- bool draw_handles;
- Control *workspace_overlay;
- Control *workspace;
- Button *tool_workspacemode[WORKSPACE_MODE_MAX];
- Button *tool_editmode[EDITMODE_MAX];
- HSeparator *separator_editmode;
- HBoxContainer *toolbar;
- Button *tools[TOOL_MAX];
- VSeparator *separator_shape_toggle;
- VSeparator *separator_bitmask;
- VSeparator *separator_delete;
- VSeparator *separator_grid;
- SpinBox *spin_priority;
- SpinBox *spin_z_index;
- WorkspaceMode workspace_mode;
- EditMode edit_mode;
- int current_tile;
-
- float max_scale;
- float min_scale;
- float scale_ratio;
-
- void update_texture_list();
- void update_texture_list_icon();
-
- void add_texture(Ref<Texture2D> p_texture);
- void remove_texture(Ref<Texture2D> p_texture);
-
- Ref<Texture2D> get_current_texture();
-
- static void _import_node(Node *p_node, Ref<TileSet> p_library);
- static void _import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge);
- void _undo_redo_import_scene(Node *p_scene, bool p_merge);
-
- bool _is_drop_valid(const Dictionary &p_drag_data, const Dictionary &p_item_data) const;
- Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
- bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
- void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
- void _file_load_request(const Vector<String> &p_path, int p_at_pos = -1);
-
-protected:
- static void _bind_methods();
- void _notification(int p_what);
-
-public:
- void edit(const Ref<TileSet> &p_tileset);
- static Error update_library_file(Node *p_base_scene, Ref<TileSet> ml, bool p_merge = true);
-
- TileSetEditor(EditorNode *p_editor);
- ~TileSetEditor();
-
-private:
- void _on_tileset_toolbar_button_pressed(int p_index);
- void _on_tileset_toolbar_confirm();
- void _on_texture_list_selected(int p_index);
- void _on_textures_added(const PackedStringArray &p_paths);
- void _on_edit_mode_changed(int p_edit_mode);
- void _on_workspace_mode_changed(int p_workspace_mode);
- void _on_workspace_overlay_draw();
- void _on_workspace_draw();
- void _on_workspace_process();
- void _on_scroll_container_input(const Ref<InputEvent> &p_event);
- void _on_workspace_input(const Ref<InputEvent> &p_ie);
- void _on_tool_clicked(int p_tool);
- void _on_priority_changed(float val);
- void _on_z_index_changed(float val);
- void _on_grid_snap_toggled(bool p_val);
- Vector<Vector2> _get_collision_shape_points(const Ref<Shape2D> &p_shape);
- Vector<Vector2> _get_edited_shape_points();
- void _set_edited_shape_points(const Vector<Vector2> &points);
- void _update_tile_data();
- void _update_toggle_shape_button();
- void _select_next_tile();
- void _select_previous_tile();
- Array _get_tiles_in_current_texture(bool sorted = false);
- bool _sort_tiles(Variant p_a, Variant p_b);
- void _select_next_subtile();
- void _select_previous_subtile();
- void _select_next_shape();
- void _select_previous_shape();
- void _set_edited_collision_shape(const Ref<Shape2D> &p_shape);
- void _set_snap_step(Vector2 p_val);
- void _set_snap_off(Vector2 p_val);
- void _set_snap_sep(Vector2 p_val);
-
- void _validate_current_tile_id();
- void _select_edited_shape_coord();
- void _undo_tile_removal(int p_id);
-
- void _zoom_in();
- void _zoom_out();
- void _zoom_reset();
-
- void draw_highlight_current_tile();
- void draw_highlight_subtile(Vector2 coord, const Vector<Vector2> &other_highlighted = Vector<Vector2>());
- void draw_tile_subdivision(int p_id, Color p_color) const;
- void draw_edited_region_subdivision() const;
- void draw_grid_snap();
- void draw_polygon_shapes();
- void close_shape(const Vector2 &shape_anchor);
- void select_coord(const Vector2 &coord);
- Vector2 snap_point(const Vector2 &point);
- void update_workspace_tile_mode();
- void update_workspace_minsize();
- void update_edited_region(const Vector2 &end_point);
- int get_grabbed_point(const Vector2 &p_mouse_pos, real_t grab_threshold);
- bool is_within_grabbing_distance_of_first_point(const Vector2 &p_pos, real_t p_grab_threshold);
-
- int get_current_tile() const;
- void set_current_tile(int p_id);
-};
-
-class TilesetEditorContext : public Object {
- friend class TileSetEditor;
- GDCLASS(TilesetEditorContext, Object);
-
- Ref<TileSet> tileset;
- TileSetEditor *tileset_editor;
- bool snap_options_visible;
-
-public:
- bool _hide_script_from_inspector() { return true; }
- void set_tileset(const Ref<TileSet> &p_tileset);
-
-private:
- void set_snap_options_visible(bool p_visible);
-
-protected:
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
- static void _bind_methods();
-
-public:
- TilesetEditorContext(TileSetEditor *p_tileset_editor);
-};
-
-class TileSetEditorPlugin : public EditorPlugin {
- GDCLASS(TileSetEditorPlugin, EditorPlugin);
-
- TileSetEditor *tileset_editor;
- Button *tileset_editor_button;
- EditorNode *editor;
-
-public:
- virtual String get_name() const override { return "TileSet"; }
- bool has_main_screen() const override { return false; }
- virtual void edit(Object *p_node) override;
- virtual bool handles(Object *p_node) const override;
- virtual void make_visible(bool p_visible) override;
- void set_state(const Dictionary &p_state) override;
- Dictionary get_state() const override;
-
- TileSetEditorPlugin(EditorNode *p_node);
-};
-
-#endif // TILE_SET_EDITOR_PLUGIN_H
diff --git a/editor/plugins/tiles/SCsub b/editor/plugins/tiles/SCsub
new file mode 100644
index 0000000000..359d04e5df
--- /dev/null
+++ b/editor/plugins/tiles/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env.add_source_files(env.editor_sources, "*.cpp")
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
new file mode 100644
index 0000000000..4de2f962bc
--- /dev/null
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -0,0 +1,649 @@
+/*************************************************************************/
+/* tile_atlas_view.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "tile_atlas_view.h"
+
+#include "core/input/input.h"
+#include "core/os/keyboard.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/center_container.h"
+#include "scene/gui/label.h"
+#include "scene/gui/panel.h"
+#include "scene/gui/texture_rect.h"
+
+#include "editor/editor_scale.h"
+
+void TileAtlasView::_gui_input(const Ref<InputEvent> &p_event) {
+ bool ctrl = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
+
+ Ref<InputEventMouseButton> b = p_event;
+ if (b.is_valid()) {
+ if (ctrl && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
+ // Zoom out
+ zoom_widget->set_zoom_by_increments(-2);
+ emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()));
+ _update_zoom(zoom_widget->get_zoom(), true);
+ accept_event();
+ }
+
+ if (ctrl && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
+ // Zoom in
+ zoom_widget->set_zoom_by_increments(2);
+ emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()));
+ _update_zoom(zoom_widget->get_zoom(), true);
+ accept_event();
+ }
+ }
+}
+
+Size2i TileAtlasView::_compute_base_tiles_control_size() {
+ // Update the texture.
+ Vector2i size;
+ Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
+ if (texture.is_valid()) {
+ size = texture->get_size();
+ }
+
+ // Extend the size to all existing tiles.
+ Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
+ for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
+ Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
+ grid_size = grid_size.max(tile_id + Vector2i(1, 1));
+ }
+ size = size.max(grid_size * (tile_set_atlas_source->get_texture_region_size() + tile_set_atlas_source->get_separation()) + tile_set_atlas_source->get_margins());
+
+ return size;
+}
+
+Size2i TileAtlasView::_compute_alternative_tiles_control_size() {
+ Vector2i size;
+ for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
+ Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
+ int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
+ Vector2i line_size;
+ Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
+ for (int j = 1; j < alternatives_count; j++) {
+ int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
+ bool transposed = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(tile_id, alternative_id))->get_transpose();
+ line_size.x += transposed ? texture_region_size.y : texture_region_size.x;
+ line_size.y = MAX(line_size.y, transposed ? texture_region_size.x : texture_region_size.y);
+ }
+ size.x = MAX(size.x, line_size.x);
+ size.y += line_size.y;
+ }
+
+ return size;
+}
+
+void TileAtlasView::_update_zoom(float p_zoom, bool p_zoom_on_mouse_pos, Vector2i p_scroll) {
+ // Compute the minimum sizes.
+ Size2i base_tiles_control_size = _compute_base_tiles_control_size();
+ base_tiles_root_control->set_custom_minimum_size(Vector2(base_tiles_control_size) * p_zoom);
+
+ Size2i alternative_tiles_control_size = _compute_alternative_tiles_control_size();
+ alternative_tiles_root_control->set_custom_minimum_size(Vector2(alternative_tiles_control_size) * p_zoom);
+
+ // Set the texture for the base tiles.
+ Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
+
+ // Set the scales.
+ if (base_tiles_control_size.x > 0 && base_tiles_control_size.y > 0) {
+ base_tiles_drawing_root->set_scale(Vector2(p_zoom, p_zoom));
+ } else {
+ base_tiles_drawing_root->set_scale(Vector2(1, 1));
+ }
+ if (alternative_tiles_control_size.x > 0 && alternative_tiles_control_size.y > 0) {
+ alternative_tiles_drawing_root->set_scale(Vector2(p_zoom, p_zoom));
+ } else {
+ alternative_tiles_drawing_root->set_scale(Vector2(1, 1));
+ }
+
+ // Update the margin container's margins.
+ const char *constants[] = { "margin_left", "margin_top", "margin_right", "margin_bottom" };
+ for (int i = 0; i < 4; i++) {
+ margin_container->add_theme_constant_override(constants[i], margin_container_paddings[i] * p_zoom);
+ }
+
+ // Update the backgrounds.
+ background_left->update();
+ background_right->update();
+
+ if (p_scroll != Vector2i(-1, -1)) {
+ scroll_container->set_h_scroll(p_scroll.x);
+ scroll_container->set_v_scroll(p_scroll.y);
+ }
+
+ // Zoom on the position.
+ if (previous_zoom != p_zoom) {
+ // TODO: solve this.
+ // There is however an issue with scrollcainter preventing this, as it seems
+ // that the scrollbars are not updated right aways after its children update.
+
+ // Compute point on previous area.
+ /*Vector2 max = Vector2(scroll_container->get_h_scrollbar()->get_max(), scroll_container->get_v_scrollbar()->get_max());
+ Vector2 min = Vector2(scroll_container->get_h_scrollbar()->get_min(), scroll_container->get_v_scrollbar()->get_min());
+ Vector2 value = Vector2(scroll_container->get_h_scrollbar()->get_value(), scroll_container->get_v_scrollbar()->get_value());
+
+ Vector2 old_max = max * previous_zoom / p_zoom;
+
+ Vector2 max_pixel_change = max - old_max;
+ Vector2 ratio = ((value + scroll_container->get_local_mouse_position()) / old_max).max(Vector2()).min(Vector2(1,1));
+ Vector2 offset = max_pixel_change * ratio;
+
+ print_line("--- ZOOMED ---");
+ print_line(vformat("max: %s", max));
+ print_line(vformat("min: %s", min));
+ print_line(vformat("value: %s", value));
+ print_line(vformat("size: %s", scroll_container->get_size()));
+ print_line(vformat("mouse_pos: %s", scroll_container->get_local_mouse_position()));
+
+ print_line(vformat("ratio: %s", ratio));
+ print_line(vformat("max_pixel_change: %s", max_pixel_change));
+ print_line(vformat("offset: %s", offset));
+
+
+ print_line(vformat("value before: %s", Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())));
+ scroll_container->set_h_scroll(10000);//scroll_container->get_h_scroll()+offset.x);
+ scroll_container->set_v_scroll(10000);//scroll_container->get_v_scroll()+offset.y);
+ print_line(vformat("value after: %s", Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())));
+ */
+
+ previous_zoom = p_zoom;
+ }
+}
+
+void TileAtlasView::_scroll_changed() {
+ emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()));
+}
+
+void TileAtlasView::_zoom_widget_changed() {
+ _update_zoom(zoom_widget->get_zoom());
+ emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()));
+}
+
+void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
+ base_tiles_root_control->set_tooltip("");
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ Transform2D xform = base_tiles_drawing_root->get_transform().affine_inverse();
+ Vector2i coords = get_atlas_tile_coords_at_pos(xform.xform(mm->get_position()));
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ coords = tile_set_atlas_source->get_tile_at_coords(coords);
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ base_tiles_root_control->set_tooltip(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: 0"), source_id, coords));
+ }
+ }
+ }
+}
+
+void TileAtlasView::_draw_base_tiles() {
+ Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
+ if (texture.is_valid()) {
+ Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
+
+ // Draw the texture, square by square.
+ Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
+ for (int x = 0; x < grid_size.x; x++) {
+ for (int y = 0; y < grid_size.y; y++) {
+ Vector2i coords = Vector2i(x, y);
+ if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ Rect2i rect = Rect2i(texture_region_size * coords + margins, texture_region_size);
+ base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ }
+ }
+ }
+
+ // Draw the texture around the grid.
+ Rect2i rect;
+ // Top.
+ rect.position = Vector2i();
+ rect.set_end(Vector2i(texture->get_size().x, margins.y));
+ base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ // Bottom
+ rect.position = Vector2i(0, margins.y + (grid_size.y * texture_region_size.y));
+ rect.set_end(texture->get_size());
+ base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ // Left
+ rect.position = Vector2i(0, margins.y);
+ rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * texture_region_size.y)));
+ base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ // Right.
+ rect.position = Vector2i(margins.x + (grid_size.x * texture_region_size.x), margins.y);
+ rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * texture_region_size.y)));
+ base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+
+ // Draw actual tiles, using their properties (modulation, etc...)
+ for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
+ Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
+
+ // Update the y to max value.
+ Vector2i offset_pos = (margins + (atlas_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0));
+
+ // Draw the tile.
+ TileSetAtlasPluginRendering::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0);
+ }
+ }
+}
+
+void TileAtlasView::_draw_base_tiles_texture_grid() {
+ Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
+ if (texture.is_valid()) {
+ Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
+ Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
+
+ Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
+
+ // Draw each tile texture region.
+ for (int x = 0; x < grid_size.x; x++) {
+ for (int y = 0; y < grid_size.y; y++) {
+ Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));
+ Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
+ if (base_tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (base_tile_coords == Vector2i(x, y)) {
+ // Draw existing tile.
+ Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(base_tile_coords);
+ Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1));
+ base_tiles_texture_grid->draw_rect(Rect2i(origin, region_size), Color(1.0, 1.0, 1.0, 0.8), false);
+ }
+ } else {
+ // Draw the grid.
+ base_tiles_texture_grid->draw_rect(Rect2i(origin, texture_region_size), Color(0.7, 0.7, 0.7, 0.1), false);
+ }
+ }
+ }
+ }
+}
+
+void TileAtlasView::_draw_base_tiles_dark() {
+ Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
+ if (texture.is_valid()) {
+ Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
+ Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
+
+ Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
+
+ // Draw each tile texture region.
+ for (int x = 0; x < grid_size.x; x++) {
+ for (int y = 0; y < grid_size.y; y++) {
+ Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));
+ Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
+
+ if (base_tile_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ // Draw the grid.
+ base_tiles_dark->draw_rect(Rect2i(origin, texture_region_size), Color(0.0, 0.0, 0.0, 0.5), true);
+ }
+ }
+ }
+ }
+}
+
+void TileAtlasView::_draw_base_tiles_shape_grid() {
+ // Draw the shapes.
+ Vector2i tile_shape_size = tile_set->get_tile_size();
+ for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
+ Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
+ Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_effective_texture_offset(tile_id, 0);
+ Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id);
+ Vector2 origin = texture_region.position + (texture_region.size - tile_shape_size) / 2 + in_tile_base_offset;
+
+ // Draw only if the tile shape fits in the texture region
+ tile_set->draw_tile_shape(base_tiles_shape_grid, Rect2(origin, tile_shape_size), Color(1.0, 0.5, 0.2, 0.8));
+ }
+}
+
+void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) {
+ alternative_tiles_root_control->set_tooltip("");
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ Transform2D xform = alternative_tiles_drawing_root->get_transform().affine_inverse();
+ Vector3i coords3 = get_alternative_tile_at_pos(xform.xform(mm->get_position()));
+ Vector2i coords = Vector2i(coords3.x, coords3.y);
+ int alternative_id = coords3.z;
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && alternative_id != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ alternative_tiles_root_control->set_tooltip(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: %d"), source_id, coords, alternative_id));
+ }
+ }
+}
+
+void TileAtlasView::_draw_alternatives() {
+ // Draw the alternative tiles.
+ Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
+ if (texture.is_valid()) {
+ Vector2 current_pos;
+ for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
+ Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
+ current_pos.x = 0;
+ int y_increment = 0;
+ Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(atlas_coords);
+ int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords);
+ for (int j = 1; j < alternatives_count; j++) {
+ int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j);
+ TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id));
+ bool transposed = tile_data->get_transpose();
+
+ // Update the y to max value.
+ Vector2i offset_pos = current_pos;
+ if (transposed) {
+ offset_pos = (current_pos + Vector2(texture_region.size.y, texture_region.size.x) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
+ y_increment = MAX(y_increment, texture_region.size.x);
+ } else {
+ offset_pos = (current_pos + texture_region.size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
+ y_increment = MAX(y_increment, texture_region.size.y);
+ }
+
+ // Draw the tile.
+ TileSetAtlasPluginRendering::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id);
+
+ // Increment the x position.
+ current_pos.x += transposed ? texture_region.size.y : texture_region.size.x;
+ }
+ if (alternatives_count > 1) {
+ current_pos.y += y_increment;
+ }
+ }
+ }
+}
+
+void TileAtlasView::_draw_background_left() {
+ Ref<Texture2D> texture = get_theme_icon("Checkerboard", "EditorIcons");
+ background_left->set_size(base_tiles_root_control->get_custom_minimum_size());
+ background_left->draw_texture_rect(texture, Rect2(Vector2(), background_left->get_size()), true);
+}
+
+void TileAtlasView::_draw_background_right() {
+ Ref<Texture2D> texture = get_theme_icon("Checkerboard", "EditorIcons");
+ background_right->set_size(alternative_tiles_root_control->get_custom_minimum_size());
+ background_right->draw_texture_rect(texture, Rect2(Vector2(), background_right->get_size()), true);
+}
+
+void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
+ ERR_FAIL_COND(!p_tile_set);
+ ERR_FAIL_COND(!p_tile_set_atlas_source);
+ ERR_FAIL_COND(p_source_id < 0);
+ ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
+
+ tile_set = p_tile_set;
+ tile_set_atlas_source = p_tile_set_atlas_source;
+ source_id = p_source_id;
+
+ // Show or hide the view.
+ bool valid = tile_set_atlas_source->get_texture().is_valid();
+ hbox->set_visible(valid);
+ missing_source_label->set_visible(!valid);
+
+ // Update the rect cache.
+ _update_alternative_tiles_rect_cache();
+
+ // Update everything.
+ _update_zoom(zoom_widget->get_zoom());
+
+ // Change children control size.
+ Size2i base_tiles_control_size = _compute_base_tiles_control_size();
+ for (int i = 0; i < base_tiles_drawing_root->get_child_count(); i++) {
+ Control *control = Object::cast_to<Control>(base_tiles_drawing_root->get_child(i));
+ if (control) {
+ control->set_size(base_tiles_control_size);
+ }
+ }
+
+ Size2i alternative_control_size = _compute_alternative_tiles_control_size();
+ for (int i = 0; i < alternative_tiles_drawing_root->get_child_count(); i++) {
+ Control *control = Object::cast_to<Control>(alternative_tiles_drawing_root->get_child(i));
+ if (control) {
+ control->set_size(alternative_control_size);
+ }
+ }
+
+ // Update.
+ base_tiles_draw->update();
+ base_tiles_texture_grid->update();
+ base_tiles_shape_grid->update();
+ base_tiles_dark->update();
+ alternatives_draw->update();
+ background_left->update();
+ background_right->update();
+}
+
+float TileAtlasView::get_zoom() const {
+ return zoom_widget->get_zoom();
+};
+
+void TileAtlasView::set_transform(float p_zoom, Vector2i p_scroll) {
+ zoom_widget->set_zoom(p_zoom);
+ _update_zoom(zoom_widget->get_zoom(), false, p_scroll);
+};
+
+void TileAtlasView::set_padding(Side p_side, int p_padding) {
+ ERR_FAIL_COND(p_padding < 0);
+ margin_container_paddings[p_side] = p_padding;
+}
+
+Vector2i TileAtlasView::get_atlas_tile_coords_at_pos(const Vector2 p_pos) const {
+ Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
+ if (texture.is_valid()) {
+ Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
+ Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
+
+ // Compute index in atlas
+ Vector2 pos = p_pos - margins;
+ Vector2i ret = (pos / (texture_region_size + separation)).floor();
+
+ return ret;
+ }
+
+ return TileSetAtlasSource::INVALID_ATLAS_COORDS;
+}
+
+void TileAtlasView::_update_alternative_tiles_rect_cache() {
+ alternative_tiles_rect_cache.clear();
+
+ Rect2i current;
+ for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
+ Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
+ int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
+ Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(tile_id).size;
+ int line_height = 0;
+ for (int j = 1; j < alternatives_count; j++) {
+ int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
+ TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(tile_id, alternative_id));
+ bool transposed = tile_data->get_transpose();
+ current.size = transposed ? Vector2i(texture_region_size.y, texture_region_size.x) : texture_region_size;
+
+ // Update the rect.
+ if (!alternative_tiles_rect_cache.has(tile_id)) {
+ alternative_tiles_rect_cache[tile_id] = Map<int, Rect2i>();
+ }
+ alternative_tiles_rect_cache[tile_id][alternative_id] = current;
+
+ current.position.x += transposed ? texture_region_size.y : texture_region_size.x;
+ line_height = MAX(line_height, transposed ? texture_region_size.x : texture_region_size.y);
+ }
+
+ current.position.x = 0;
+ current.position.y += line_height;
+ }
+}
+
+Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const {
+ for (Map<Vector2, Map<int, Rect2i>>::Element *E_coords = alternative_tiles_rect_cache.front(); E_coords; E_coords = E_coords->next()) {
+ for (Map<int, Rect2i>::Element *E_alternative = E_coords->value().front(); E_alternative; E_alternative = E_alternative->next()) {
+ if (E_alternative->value().has_point(p_pos)) {
+ return Vector3i(E_coords->key().x, E_coords->key().y, E_alternative->key());
+ }
+ }
+ }
+
+ return Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+}
+
+Rect2i TileAtlasView::get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile) {
+ ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache.has(p_coords), Rect2i(), vformat("No cached rect for tile coords:%s", p_coords));
+ ERR_FAIL_COND_V_MSG(!alternative_tiles_rect_cache[p_coords].has(p_alternative_tile), Rect2i(), vformat("No cached rect for tile coords:%s alternative_id:%d", p_coords, p_alternative_tile));
+
+ return alternative_tiles_rect_cache[p_coords][p_alternative_tile];
+}
+
+void TileAtlasView::update() {
+ scroll_container->update();
+ base_tiles_texture_grid->update();
+ base_tiles_shape_grid->update();
+ base_tiles_dark->update();
+ alternatives_draw->update();
+ background_left->update();
+ background_right->update();
+}
+
+void TileAtlasView::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("transform_changed", PropertyInfo(Variant::FLOAT, "zoom"), PropertyInfo(Variant::VECTOR2, "scroll")));
+}
+
+TileAtlasView::TileAtlasView() {
+ Panel *panel_container = memnew(Panel);
+ panel_container->set_h_size_flags(SIZE_EXPAND_FILL);
+ panel_container->set_v_size_flags(SIZE_EXPAND_FILL);
+ panel_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ add_child(panel_container);
+
+ //Scrolling
+ scroll_container = memnew(ScrollContainer);
+ scroll_container->get_h_scrollbar()->connect("value_changed", callable_mp(this, &TileAtlasView::_scroll_changed).unbind(1));
+ scroll_container->get_v_scrollbar()->connect("value_changed", callable_mp(this, &TileAtlasView::_scroll_changed).unbind(1));
+ panel_container->add_child(scroll_container);
+ scroll_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+
+ zoom_widget = memnew(EditorZoomWidget);
+ add_child(zoom_widget);
+ zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
+ zoom_widget->connect("zoom_changed", callable_mp(this, &TileAtlasView::_zoom_widget_changed).unbind(1));
+
+ CenterContainer *center_container = memnew(CenterContainer);
+ center_container->set_h_size_flags(SIZE_EXPAND_FILL);
+ center_container->set_v_size_flags(SIZE_EXPAND_FILL);
+ center_container->connect("gui_input", callable_mp(this, &TileAtlasView::_gui_input));
+ scroll_container->add_child(center_container);
+
+ missing_source_label = memnew(Label);
+ missing_source_label->set_text(TTR("No atlas source with a valid texture selected."));
+ center_container->add_child(missing_source_label);
+
+ margin_container = memnew(MarginContainer);
+ center_container->add_child(margin_container);
+
+ hbox = memnew(HBoxContainer);
+ hbox->add_theme_constant_override("separation", 10);
+ hbox->hide();
+ margin_container->add_child(hbox);
+
+ VBoxContainer *left_vbox = memnew(VBoxContainer);
+ hbox->add_child(left_vbox);
+
+ VBoxContainer *right_vbox = memnew(VBoxContainer);
+ hbox->add_child(right_vbox);
+
+ // Base tiles.
+ Label *base_tile_label = memnew(Label);
+ base_tile_label->set_text(TTR("Base tiles"));
+ base_tile_label->set_align(Label::ALIGN_CENTER);
+ left_vbox->add_child(base_tile_label);
+
+ base_tiles_root_control = memnew(Control);
+ base_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ base_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_base_tiles_root_control_gui_input));
+ left_vbox->add_child(base_tiles_root_control);
+
+ background_left = memnew(Control);
+ background_left->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
+ background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ background_left->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_left));
+ base_tiles_root_control->add_child(background_left);
+
+ base_tiles_drawing_root = memnew(Control);
+ base_tiles_drawing_root->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
+ base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ base_tiles_root_control->add_child(base_tiles_drawing_root);
+
+ base_tiles_draw = memnew(Control);
+ base_tiles_draw->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_tiles_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles));
+ base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ base_tiles_drawing_root->add_child(base_tiles_draw);
+
+ base_tiles_texture_grid = memnew(Control);
+ base_tiles_texture_grid->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_tiles_texture_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_texture_grid));
+ base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ base_tiles_drawing_root->add_child(base_tiles_texture_grid);
+
+ base_tiles_shape_grid = memnew(Control);
+ base_tiles_shape_grid->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));
+ base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ base_tiles_drawing_root->add_child(base_tiles_shape_grid);
+
+ base_tiles_dark = memnew(Control);
+ base_tiles_dark->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_tiles_dark->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_dark));
+ base_tiles_dark->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ base_tiles_drawing_root->add_child(base_tiles_dark);
+
+ // Alternative tiles.
+ Label *alternative_tiles_label = memnew(Label);
+ alternative_tiles_label->set_text(TTR("Alternative tiles"));
+ alternative_tiles_label->set_align(Label::ALIGN_CENTER);
+ right_vbox->add_child(alternative_tiles_label);
+
+ alternative_tiles_root_control = memnew(Control);
+ alternative_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_alternative_tiles_root_control_gui_input));
+ right_vbox->add_child(alternative_tiles_root_control);
+
+ background_right = memnew(Control);
+ background_right->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
+ background_right->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_right));
+ background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ alternative_tiles_root_control->add_child(background_right);
+
+ alternative_tiles_drawing_root = memnew(Control);
+ alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
+ alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ alternative_tiles_root_control->add_child(alternative_tiles_drawing_root);
+
+ alternatives_draw = memnew(Control);
+ alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives));
+ alternatives_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ alternative_tiles_drawing_root->add_child(alternatives_draw);
+}
diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h
new file mode 100644
index 0000000000..28fd3ed1e0
--- /dev/null
+++ b/editor/plugins/tiles/tile_atlas_view.h
@@ -0,0 +1,153 @@
+/*************************************************************************/
+/* tile_atlas_view.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TILE_ATLAS_VIEW_H
+#define TILE_ATLAS_VIEW_H
+
+#include "editor/editor_zoom_widget.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/label.h"
+#include "scene/gui/margin_container.h"
+#include "scene/gui/scroll_container.h"
+#include "scene/gui/texture_rect.h"
+#include "scene/resources/tile_set.h"
+
+class TileAtlasView : public Control {
+ GDCLASS(TileAtlasView, Control);
+
+private:
+ TileSet *tile_set;
+ TileSetAtlasSource *tile_set_atlas_source;
+ int source_id = -1;
+
+ float previous_zoom = 1.0;
+ EditorZoomWidget *zoom_widget;
+ void _zoom_widget_changed();
+ void _scroll_changed();
+ void _update_zoom(float p_zoom, bool p_zoom_on_mouse_pos = false, Vector2i p_scroll = Vector2i(-1, -1));
+ void _gui_input(const Ref<InputEvent> &p_event);
+
+ Map<Vector2, Map<int, Rect2i>> alternative_tiles_rect_cache;
+ void _update_alternative_tiles_rect_cache();
+
+ ScrollContainer *scroll_container;
+ MarginContainer *margin_container;
+ int margin_container_paddings[4] = { 0, 0, 0, 0 };
+ HBoxContainer *hbox;
+ Label *missing_source_label;
+
+ // Background
+ Control *background_left;
+ void _draw_background_left();
+ Control *background_right;
+ void _draw_background_right();
+
+ // Left side.
+ Control *base_tiles_root_control;
+ void _base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event);
+
+ Control *base_tiles_drawing_root;
+
+ Control *base_tiles_draw;
+ void _draw_base_tiles();
+
+ Control *base_tiles_texture_grid;
+ void _draw_base_tiles_texture_grid();
+
+ Control *base_tiles_shape_grid;
+ void _draw_base_tiles_shape_grid();
+
+ Control *base_tiles_dark;
+ void _draw_base_tiles_dark();
+
+ Size2i _compute_base_tiles_control_size();
+
+ // Right side.
+ Control *alternative_tiles_root_control;
+ void _alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event);
+
+ Control *alternative_tiles_drawing_root;
+
+ Control *alternatives_draw;
+ void _draw_alternatives();
+
+ Size2i _compute_alternative_tiles_control_size();
+
+protected:
+ static void _bind_methods();
+
+public:
+ // Global.
+ void set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id);
+
+ ScrollContainer *get_scroll_container() { return scroll_container; };
+
+ float get_zoom() const;
+ void set_transform(float p_zoom, Vector2i p_scroll);
+
+ void set_padding(Side p_side, int p_padding);
+
+ // Left side.
+ void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); };
+ void set_dark_visible(bool p_visible) { base_tiles_dark->set_visible(p_visible); };
+ void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); };
+
+ Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos) const;
+
+ void add_control_over_atlas_tiles(Control *p_control, bool scaled = true) {
+ if (scaled) {
+ base_tiles_drawing_root->add_child(p_control);
+ } else {
+ base_tiles_root_control->add_child(p_control);
+ }
+ p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ };
+
+ // Right side.
+ Vector3i get_alternative_tile_at_pos(const Vector2 p_pos) const;
+ Rect2i get_alternative_tile_rect(const Vector2i p_coords, int p_alternative_tile);
+
+ void add_control_over_alternative_tiles(Control *p_control, bool scaled = true) {
+ if (scaled) {
+ alternative_tiles_drawing_root->add_child(p_control);
+ } else {
+ alternative_tiles_root_control->add_child(p_control);
+ }
+ p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ };
+
+ // Update everything.
+ void update();
+
+ TileAtlasView();
+};
+
+#endif // TILE_ATLAS_VIEW
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
new file mode 100644
index 0000000000..0b674bec39
--- /dev/null
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -0,0 +1,218 @@
+/*************************************************************************/
+/* tile_data_editors.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "tile_data_editors.h"
+
+#include "tile_set_editor.h"
+
+TileData *TileDataEditor::_get_tile_data(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile) {
+ ERR_FAIL_COND_V(!p_tile_set, nullptr);
+ ERR_FAIL_COND_V(!p_tile_set->has_source(p_atlas_source_id), nullptr);
+
+ TileData *td = nullptr;
+ TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ ERR_FAIL_COND_V(!atlas_source->has_tile(p_atlas_coords), nullptr);
+ ERR_FAIL_COND_V(!atlas_source->has_alternative_tile(p_atlas_coords, p_alternative_tile), nullptr);
+ td = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile));
+ }
+
+ return td;
+}
+
+void TileDataEditor::edit(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
+}
+
+void TileDataTextureOffsetEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
+ TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
+ ERR_FAIL_COND(!tile_data);
+
+ bool valid;
+ Variant value = tile_data->get(p_property, &valid);
+ if (!valid) {
+ return;
+ }
+ ERR_FAIL_COND(value.get_type() != Variant::VECTOR2I);
+
+ Vector2i tile_set_tile_size = p_tile_set->get_tile_size();
+ Rect2i rect = Rect2i(-tile_set_tile_size / 2, tile_set_tile_size);
+ p_tile_set->draw_tile_shape(p_canvas_item, p_transform.xform(rect), Color(1.0, 0.0, 0.0));
+}
+
+void TileDataIntegerEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
+ TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
+ ERR_FAIL_COND(!tile_data);
+
+ bool valid;
+ Variant value = tile_data->get(p_property, &valid);
+ if (!valid) {
+ return;
+ }
+ ERR_FAIL_COND(value.get_type() != Variant::INT);
+
+ Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts");
+ int height = font->get_height();
+ int width = 200;
+ p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-width / 2, height / 2), vformat("%d", value), HALIGN_CENTER, width, -1, Color(1, 1, 1), 1, Color(0, 0, 0, 1));
+}
+
+void TileDataFloatEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
+ TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
+ ERR_FAIL_COND(!tile_data);
+
+ bool valid;
+ Variant value = tile_data->get(p_property, &valid);
+ if (!valid) {
+ return;
+ }
+ ERR_FAIL_COND(value.get_type() != Variant::FLOAT);
+
+ Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts");
+ int height = font->get_height();
+ int width = 200;
+ p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-width / 2, height / 2), vformat("%.2f", value), HALIGN_CENTER, width, -1, Color(1, 1, 1), 1, Color(0, 0, 0, 1));
+}
+
+void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
+ TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
+ ERR_FAIL_COND(!tile_data);
+
+ bool valid;
+ Variant value = tile_data->get(p_property, &valid);
+ if (!valid) {
+ return;
+ }
+ ERR_FAIL_COND(value.get_type() != Variant::VECTOR2I && value.get_type() != Variant::VECTOR2);
+
+ Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_theme_icon("EditorPosition", "EditorIcons");
+ p_canvas_item->draw_texture(position_icon, p_transform.get_origin() + Vector2(value) - position_icon->get_size() / 2);
+}
+
+void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
+ TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
+ ERR_FAIL_COND(!tile_data);
+
+ Vector<String> components = String(p_property).split("/", true);
+ if (components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) {
+ int occlusion_layer = components[0].trim_prefix("occlusion_layer_").to_int();
+ if (occlusion_layer >= 0 && occlusion_layer < p_tile_set->get_occlusion_layers_count()) {
+ // Draw all shapes.
+ Vector<Color> debug_occlusion_color;
+ debug_occlusion_color.push_back(Color(0.5, 0, 0, 0.6));
+
+ RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform);
+ Ref<OccluderPolygon2D> occluder = tile_data->get_occluder(occlusion_layer);
+ if (occluder.is_valid() && occluder->get_polygon().size() >= 3) {
+ p_canvas_item->draw_polygon(Variant(occluder->get_polygon()), debug_occlusion_color);
+ }
+ RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D());
+ }
+ }
+}
+
+void TileDataCollisionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
+ TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
+ ERR_FAIL_COND(!tile_data);
+
+ Vector<String> components = String(p_property).split("/", true);
+ if (components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) {
+ int physics_layer = components[0].trim_prefix("physics_layer_").to_int();
+ if (physics_layer >= 0 && physics_layer < p_tile_set->get_physics_layers_count()) {
+ // Draw all shapes.
+ Color debug_collision_color = p_canvas_item->get_tree()->get_debug_collisions_color();
+ RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform);
+ for (int i = 0; i < tile_data->get_collision_shapes_count(physics_layer); i++) {
+ Ref<Shape2D> shape = tile_data->get_collision_shape_shape(physics_layer, i);
+ if (shape.is_valid()) {
+ shape->draw(p_canvas_item->get_canvas_item(), debug_collision_color);
+ }
+ }
+ RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D());
+ }
+ }
+}
+
+void TileDataTerrainsEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
+ TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
+ ERR_FAIL_COND(!tile_data);
+
+ Vector<String> components = String(p_property).split("/", true);
+ if (components[0] == "terrain_mode" || components[0] == "terrain" || components[0] == "terrains_peering_bit") {
+ TileSetAtlasPluginTerrain::draw_terrains(p_canvas_item, p_transform, p_tile_set, tile_data);
+ }
+}
+
+void TileDataNavigationPolygonEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) {
+ TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile);
+ ERR_FAIL_COND(!tile_data);
+
+ Vector<String> components = String(p_property).split("/", true);
+ if (components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) {
+ int navigation_layer = components[0].trim_prefix("navigation_layer_").to_int();
+ if (navigation_layer >= 0 && navigation_layer < p_tile_set->get_navigation_layers_count()) {
+ // Draw all shapes.
+ RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform);
+
+ Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer);
+ if (navigation_polygon.is_valid()) {
+ Vector<Vector2> verts = navigation_polygon->get_vertices();
+ if (verts.size() < 3) {
+ return;
+ }
+
+ Color color = p_canvas_item->get_tree()->get_debug_navigation_color();
+
+ RandomPCG rand;
+ for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) {
+ // An array of vertices for this polygon.
+ Vector<int> polygon = navigation_polygon->get_polygon(i);
+ Vector<Vector2> vertices;
+ vertices.resize(polygon.size());
+ for (int j = 0; j < polygon.size(); j++) {
+ ERR_FAIL_INDEX(polygon[j], verts.size());
+ vertices.write[j] = verts[polygon[j]];
+ }
+
+ // Generate the polygon color, slightly randomly modified from the settings one.
+ Color random_variation_color;
+ random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1);
+ random_variation_color.a = color.a;
+ Vector<Color> colors;
+ colors.push_back(random_variation_color);
+
+ RenderingServer::get_singleton()->canvas_item_add_polygon(p_canvas_item->get_canvas_item(), vertices, colors);
+ }
+ }
+
+ RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D());
+ }
+ }
+}
diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h
new file mode 100644
index 0000000000..f4f9c25157
--- /dev/null
+++ b/editor/plugins/tiles/tile_data_editors.h
@@ -0,0 +1,110 @@
+/*************************************************************************/
+/* tile_data_editors.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TILE_DATA_EDITORS_H
+#define TILE_DATA_EDITORS_H
+
+#include "scene/gui/control.h"
+#include "scene/resources/tile_set.h"
+
+class TileDataEditor : public Control {
+ GDCLASS(TileDataEditor, Control);
+
+protected:
+ TileData *tile_data;
+ String property;
+
+ TileData *_get_tile_data(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile);
+
+public:
+ // Edits a TileData property.
+ void edit(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property);
+
+ // Used to draw the value over a tile.
+ virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property){};
+};
+
+class TileDataTextureOffsetEditor : public TileDataEditor {
+ GDCLASS(TileDataTextureOffsetEditor, TileDataEditor);
+
+public:
+ virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
+};
+
+class TileDataIntegerEditor : public TileDataEditor {
+ GDCLASS(TileDataIntegerEditor, TileDataEditor);
+
+public:
+ virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
+};
+
+class TileDataFloatEditor : public TileDataEditor {
+ GDCLASS(TileDataFloatEditor, TileDataEditor);
+
+public:
+ virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
+};
+
+class TileDataPositionEditor : public TileDataEditor {
+ GDCLASS(TileDataPositionEditor, TileDataEditor);
+
+public:
+ virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
+};
+
+class TileDataOcclusionShapeEditor : public TileDataEditor {
+ GDCLASS(TileDataOcclusionShapeEditor, TileDataEditor);
+
+public:
+ virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
+};
+
+class TileDataCollisionShapeEditor : public TileDataEditor {
+ GDCLASS(TileDataCollisionShapeEditor, TileDataEditor);
+
+public:
+ virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
+};
+
+class TileDataTerrainsEditor : public TileDataEditor {
+ GDCLASS(TileDataTerrainsEditor, TileDataEditor);
+
+public:
+ virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
+};
+
+class TileDataNavigationPolygonEditor : public TileDataEditor {
+ GDCLASS(TileDataNavigationPolygonEditor, TileDataEditor);
+
+public:
+ virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override;
+};
+
+#endif // TILE_DATA_EDITORS_H
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
new file mode 100644
index 0000000000..cfc4dff329
--- /dev/null
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -0,0 +1,3368 @@
+/*************************************************************************/
+/* tile_map_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "tile_map_editor.h"
+
+#include "tiles_editor_plugin.h"
+
+#include "editor/editor_scale.h"
+#include "editor/plugins/canvas_item_editor_plugin.h"
+
+#include "scene/gui/center_container.h"
+#include "scene/gui/split_container.h"
+
+#include "core/input/input.h"
+#include "core/math/geometry_2d.h"
+#include "core/os/keyboard.h"
+
+void TileMapEditorTilesPlugin::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED:
+ select_tool_button->set_icon(get_theme_icon("ToolSelect", "EditorIcons"));
+ paint_tool_button->set_icon(get_theme_icon("Edit", "EditorIcons"));
+ line_tool_button->set_icon(get_theme_icon("CurveLinear", "EditorIcons"));
+ rect_tool_button->set_icon(get_theme_icon("Rectangle", "EditorIcons"));
+ bucket_tool_button->set_icon(get_theme_icon("Bucket", "EditorIcons"));
+
+ picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons"));
+ erase_button->set_icon(get_theme_icon("Eraser", "EditorIcons"));
+
+ missing_texture_texture = get_theme_icon("TileSet", "EditorIcons");
+ break;
+ case NOTIFICATION_VISIBILITY_CHANGED:
+ _stop_dragging();
+ }
+}
+
+void TileMapEditorTilesPlugin::tile_set_changed() {
+ _update_fix_selected_and_hovered();
+ _update_bottom_panel();
+ _update_tile_set_sources_list();
+ _update_atlas_view();
+}
+
+void TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled(bool p_pressed) {
+ scatter_spinbox->set_editable(p_pressed);
+}
+
+void TileMapEditorTilesPlugin::_on_scattering_spinbox_changed(double p_value) {
+ scattering = p_value;
+}
+
+void TileMapEditorTilesPlugin::_update_toolbar() {
+ // Stop draggig if needed.
+ _stop_dragging();
+
+ // Hide all settings.
+ for (int i = 0; i < tools_settings->get_child_count(); i++) {
+ Object::cast_to<CanvasItem>(tools_settings->get_child(i))->hide();
+ }
+
+ // Show only the correct settings.
+ if (tool_buttons_group->get_pressed_button() == select_tool_button) {
+ } else if (tool_buttons_group->get_pressed_button() == paint_tool_button) {
+ tools_settings_vsep->show();
+ picker_button->show();
+ erase_button->show();
+ tools_settings_vsep_2->show();
+ random_tile_checkbox->show();
+ scatter_label->show();
+ scatter_spinbox->show();
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
+ tools_settings_vsep->show();
+ picker_button->show();
+ erase_button->show();
+ tools_settings_vsep_2->show();
+ random_tile_checkbox->show();
+ scatter_label->show();
+ scatter_spinbox->show();
+ } else if (tool_buttons_group->get_pressed_button() == rect_tool_button) {
+ tools_settings_vsep->show();
+ picker_button->show();
+ erase_button->show();
+ tools_settings_vsep_2->show();
+ random_tile_checkbox->show();
+ scatter_label->show();
+ scatter_spinbox->show();
+ } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
+ tools_settings_vsep->show();
+ picker_button->show();
+ erase_button->show();
+ tools_settings_vsep_2->show();
+ bucket_continuous_checkbox->show();
+ random_tile_checkbox->show();
+ scatter_label->show();
+ scatter_spinbox->show();
+ }
+}
+
+Control *TileMapEditorTilesPlugin::get_toolbar() const {
+ return toolbar;
+}
+
+void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
+ // Update the sources.
+ int old_current = sources_list->get_current();
+ sources_list->clear();
+
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ for (int i = 0; i < tile_set->get_source_count(); i++) {
+ int source_id = tile_set->get_source_id(i);
+
+ // TODO: handle with virtual functions
+ TileSetSource *source = *tile_set->get_source(source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ Ref<Texture2D> texture = atlas_source->get_texture();
+ if (texture.is_valid()) {
+ sources_list->add_item(vformat("%s - (id:%d)", texture->get_path().get_file(), source_id), texture);
+ } else {
+ sources_list->add_item(vformat("No texture atlas source - (id:%d)", source_id), missing_texture_texture);
+ }
+ } else {
+ sources_list->add_item(vformat("Unknown type source - (id:%d)", source_id), missing_texture_texture);
+ }
+ sources_list->set_item_metadata(i, source_id);
+ }
+
+ if (sources_list->get_item_count() > 0) {
+ if (old_current > 0) {
+ // Keep the current selected item if needed.
+ sources_list->set_current(CLAMP(old_current, 0, sources_list->get_item_count() - 1));
+ } else {
+ sources_list->set_current(0);
+ }
+ sources_list->emit_signal("item_selected", sources_list->get_current());
+ }
+
+ // Synchronize
+ TilesEditor::get_singleton()->set_atlas_sources_lists_current(sources_list->get_current());
+}
+
+void TileMapEditorTilesPlugin::_update_atlas_view() {
+ // Update the atlas display.
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ tile_atlas_view->hide();
+ return;
+ }
+
+ int source_index = sources_list->get_current();
+ if (source_index >= 0 && source_index < sources_list->get_item_count()) {
+ int source_id = sources_list->get_item_metadata(source_index);
+ TileSetSource *source = *tile_set->get_source(source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ tile_atlas_view->set_atlas_source(*tile_map->get_tileset(), atlas_source, source_id);
+ tile_atlas_view->show();
+ }
+ } else {
+ tile_atlas_view->hide();
+ }
+
+ // Synchronize atlas view.
+ TilesEditor::get_singleton()->synchronize_atlas_view(tile_atlas_view);
+
+ tile_atlas_control->update();
+}
+
+void TileMapEditorTilesPlugin::_update_bottom_panel() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+
+ // Update the tabs.
+ missing_source_label->set_visible(tile_set.is_valid() && tile_set->get_source_count() == 0);
+ atlas_sources_split_container->set_visible(tile_set.is_valid() && tile_set->get_source_count() > 0);
+}
+
+bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
+ if (!is_visible_in_tree()) {
+ // If the bottom editor is not visible, we ignore inputs.
+ return false;
+ }
+
+ if (CanvasItemEditor::get_singleton()->get_current_tool() != CanvasItemEditor::TOOL_SELECT) {
+ return false;
+ }
+
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return false;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return false;
+ }
+
+ // Shortcuts
+ if (ED_IS_SHORTCUT("tiles_editor/cut", p_event) || ED_IS_SHORTCUT("tiles_editor/copy", p_event)) {
+ // Fill in the clipboard.
+ if (!tile_map_selection.is_empty()) {
+ memdelete(tile_map_clipboard);
+ TypedArray<Vector2i> coords_array;
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ coords_array.push_back(E->get());
+ }
+ tile_map_clipboard = tile_map->get_pattern(coords_array);
+ }
+
+ if (ED_IS_SHORTCUT("tiles_editor/cut", p_event)) {
+ // Delete selected tiles.
+ if (!tile_map_selection.is_empty()) {
+ undo_redo->create_action(TTR("Delete tiles"));
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get()));
+ }
+ undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+ tile_map_selection.clear();
+ undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+ undo_redo->commit_action();
+ }
+ }
+
+ return true;
+ }
+ if (ED_IS_SHORTCUT("tiles_editor/paste", p_event)) {
+ if (drag_type == DRAG_TYPE_NONE) {
+ drag_type = DRAG_TYPE_CLIPBOARD_PASTE;
+ }
+ CanvasItemEditor::get_singleton()->update_viewport();
+ return true;
+ }
+ if (ED_IS_SHORTCUT("tiles_editor/cancel", p_event)) {
+ if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
+ drag_type = DRAG_TYPE_NONE;
+ CanvasItemEditor::get_singleton()->update_viewport();
+ return true;
+ }
+ }
+ if (ED_IS_SHORTCUT("tiles_editor/delete", p_event)) {
+ // Delete selected tiles.
+ if (!tile_map_selection.is_empty()) {
+ undo_redo->create_action(TTR("Delete tiles"));
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get()));
+ }
+ undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+ tile_map_selection.clear();
+ undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+ undo_redo->commit_action();
+ }
+ return true;
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ has_mouse = true;
+ Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
+ Vector2 mpos = xform.affine_inverse().xform(mm->get_position());
+
+ switch (drag_type) {
+ case DRAG_TYPE_PAINT: {
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_last_mouse_pos, mpos);
+ for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
+ if (!erase_button->is_pressed() && E->get().source_id == -1) {
+ continue;
+ }
+ Vector2i coords = E->key();
+ if (!drag_modified.has(coords)) {
+ drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords)));
+ }
+ tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ } break;
+ case DRAG_TYPE_BUCKET: {
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
+ for (int i = 0; i < line.size(); i++) {
+ if (!drag_modified.has(line[i])) {
+ Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed());
+ for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
+ if (!erase_button->is_pressed() && E->get().source_id == -1) {
+ continue;
+ }
+ Vector2i coords = E->key();
+ if (!drag_modified.has(coords)) {
+ drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords)));
+ }
+ tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ drag_last_mouse_pos = mpos;
+ CanvasItemEditor::get_singleton()->update_viewport();
+
+ return true;
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid()) {
+ has_mouse = true;
+ Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
+ Vector2 mpos = xform.affine_inverse().xform(mb->get_position());
+
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->is_pressed()) {
+ // Pressed
+ if (tool_buttons_group->get_pressed_button() == select_tool_button) {
+ drag_start_mouse_pos = mpos;
+ if (tile_map_selection.has(tile_map->world_to_map(drag_start_mouse_pos)) && !mb->get_shift()) {
+ // Move the selection
+ drag_type = DRAG_TYPE_MOVE;
+ drag_modified.clear();
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ Vector2i coords = E->get();
+ drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords)));
+ tile_map->set_cell(coords, -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ }
+ } else {
+ // Select tiles
+ drag_type = DRAG_TYPE_SELECT;
+ }
+ } else {
+ // Check if we are picking a tile.
+ if (picker_button->is_pressed()) {
+ drag_type = DRAG_TYPE_PICK;
+ drag_start_mouse_pos = mpos;
+ } else {
+ // Paint otherwise.
+ if (tool_buttons_group->get_pressed_button() == paint_tool_button) {
+ drag_type = DRAG_TYPE_PAINT;
+ drag_start_mouse_pos = mpos;
+ drag_modified.clear();
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, mpos, mpos);
+ for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
+ if (!erase_button->is_pressed() && E->get().source_id == -1) {
+ continue;
+ }
+ Vector2i coords = E->key();
+ if (!drag_modified.has(coords)) {
+ drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords)));
+ }
+ tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
+ drag_type = DRAG_TYPE_LINE;
+ drag_start_mouse_pos = mpos;
+ drag_modified.clear();
+ } else if (tool_buttons_group->get_pressed_button() == rect_tool_button) {
+ drag_type = DRAG_TYPE_RECT;
+ drag_start_mouse_pos = mpos;
+ drag_modified.clear();
+ } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
+ drag_type = DRAG_TYPE_BUCKET;
+ drag_start_mouse_pos = mpos;
+ drag_modified.clear();
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
+ for (int i = 0; i < line.size(); i++) {
+ if (!drag_modified.has(line[i])) {
+ Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed());
+ for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
+ if (!erase_button->is_pressed() && E->get().source_id == -1) {
+ continue;
+ }
+ Vector2i coords = E->key();
+ if (!drag_modified.has(coords)) {
+ drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords)));
+ }
+ tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+ // Released
+ _stop_dragging();
+ }
+
+ CanvasItemEditor::get_singleton()->update_viewport();
+
+ return true;
+ }
+ drag_last_mouse_pos = mpos;
+ }
+
+ return false;
+}
+
+void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ if (!tile_map->is_visible_in_tree()) {
+ return;
+ }
+
+ Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
+ Vector2i tile_shape_size = tile_set->get_tile_size();
+
+ // Draw the selection.
+ if (is_visible_in_tree() && tool_buttons_group->get_pressed_button() == select_tool_button) {
+ // In select mode, we only draw the current selection if we are modifying it (pressing control or shift).
+ if (drag_type == DRAG_TYPE_MOVE || (drag_type == DRAG_TYPE_SELECT && !Input::get_singleton()->is_key_pressed(KEY_CONTROL) && !Input::get_singleton()->is_key_pressed(KEY_SHIFT))) {
+ // Do nothing
+ } else {
+ tile_map->draw_cells_outline(p_overlay, tile_map_selection, Color(0.0, 0.0, 1.0), xform);
+ }
+ }
+
+ // handle the preview of the tiles to be placed.
+ if (is_visible_in_tree() && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered.
+ Map<Vector2i, TileMapCell> preview;
+ Rect2i drawn_grid_rect;
+
+ if (drag_type == DRAG_TYPE_PICK) {
+ // Draw the area being picvked.
+ Rect2i rect = Rect2i(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos) - tile_map->world_to_map(drag_start_mouse_pos)).abs();
+ rect.size += Vector2i(1, 1);
+ for (int x = rect.position.x; x < rect.get_end().x; x++) {
+ for (int y = rect.position.y; y < rect.get_end().y; y++) {
+ Vector2i coords = Vector2i(x, y);
+ if (tile_map->get_cell_source_id(coords) != -1) {
+ Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - tile_shape_size / 2, tile_shape_size));
+ tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0), false);
+ }
+ }
+ }
+ } else if (drag_type == DRAG_TYPE_SELECT) {
+ // Draw the area being selected.
+ Rect2i rect = Rect2i(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos) - tile_map->world_to_map(drag_start_mouse_pos)).abs();
+ rect.size += Vector2i(1, 1);
+ Set<Vector2i> to_draw;
+ for (int x = rect.position.x; x < rect.get_end().x; x++) {
+ for (int y = rect.position.y; y < rect.get_end().y; y++) {
+ Vector2i coords = Vector2i(x, y);
+ if (tile_map->get_cell_source_id(coords) != -1) {
+ to_draw.insert(coords);
+ }
+ }
+ }
+ tile_map->draw_cells_outline(p_overlay, to_draw, Color(1.0, 1.0, 1.0), xform);
+ } else if (drag_type == DRAG_TYPE_MOVE) {
+ // Preview when moving.
+ Vector2i top_left;
+ if (!tile_map_selection.is_empty()) {
+ top_left = tile_map_selection.front()->get();
+ }
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ top_left = top_left.min(E->get());
+ }
+ Vector2i offset = drag_start_mouse_pos - tile_map->map_to_world(top_left);
+ offset = tile_map->world_to_map(drag_last_mouse_pos - offset) - tile_map->world_to_map(drag_start_mouse_pos - offset);
+
+ TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ Vector2i coords = tile_map->map_pattern(offset + top_left, selection_used_cells[i], selection_pattern);
+ preview[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
+ }
+ } else if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
+ // Preview when pasting.
+ Vector2 mouse_offset = (Vector2(tile_map_clipboard->get_size()) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size();
+ TypedArray<Vector2i> clipboard_used_cells = tile_map_clipboard->get_used_cells();
+ for (int i = 0; i < clipboard_used_cells.size(); i++) {
+ Vector2i coords = tile_map->map_pattern(tile_map->world_to_map(drag_last_mouse_pos - mouse_offset), clipboard_used_cells[i], tile_map_clipboard);
+ preview[coords] = TileMapCell(tile_map_clipboard->get_cell_source_id(clipboard_used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(clipboard_used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(clipboard_used_cells[i]));
+ }
+ } else if (!picker_button->is_pressed()) {
+ bool expand_grid = false;
+ if (tool_buttons_group->get_pressed_button() == paint_tool_button && drag_type == DRAG_TYPE_NONE) {
+ // Preview for a single pattern.
+ preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos);
+ expand_grid = true;
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
+ if (drag_type == DRAG_TYPE_NONE) {
+ // Preview for a single pattern.
+ preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos);
+ expand_grid = true;
+ } else if (drag_type == DRAG_TYPE_LINE) {
+ // Preview for a line pattern.
+ preview = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, drag_last_mouse_pos);
+ expand_grid = true;
+ }
+ } else if (tool_buttons_group->get_pressed_button() == rect_tool_button && drag_type == DRAG_TYPE_RECT) {
+ // Preview for a line pattern.
+ preview = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos));
+ expand_grid = true;
+ } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button && drag_type == DRAG_TYPE_NONE) {
+ // Preview for a line pattern.
+ preview = _draw_bucket_fill(tile_map->world_to_map(drag_last_mouse_pos), bucket_continuous_checkbox->is_pressed());
+ }
+
+ // Expand the grid if needed
+ if (expand_grid && !preview.is_empty()) {
+ drawn_grid_rect = Rect2i(preview.front()->key(), Vector2i(1, 1));
+ for (Map<Vector2i, TileMapCell>::Element *E = preview.front(); E; E = E->next()) {
+ drawn_grid_rect.expand_to(E->key());
+ }
+ }
+ }
+
+ if (!preview.is_empty()) {
+ const int fading = 5;
+
+ // Draw the lines of the grid behind the preview.
+ if (drawn_grid_rect.size.x > 0 && drawn_grid_rect.size.y > 0) {
+ drawn_grid_rect = drawn_grid_rect.grow(fading);
+ for (int x = drawn_grid_rect.position.x; x < (drawn_grid_rect.position.x + drawn_grid_rect.size.x); x++) {
+ for (int y = drawn_grid_rect.position.y; y < (drawn_grid_rect.position.y + drawn_grid_rect.size.y); y++) {
+ Vector2i pos_in_rect = Vector2i(x, y) - drawn_grid_rect.position;
+
+ // Fade out the border of the grid.
+ float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
+ float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f);
+ float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
+ float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
+ float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
+
+ Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size));
+ tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 0.5, 0.2, 0.5 * opacity), false);
+ }
+ }
+ }
+
+ // Draw the preview.
+ for (Map<Vector2i, TileMapCell>::Element *E = preview.front(); E; E = E->next()) {
+ if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ Vector2i size = tile_set->get_tile_size();
+ Vector2 position = tile_map->map_to_world(E->key()) - size / 2;
+ Rect2 cell_region = xform.xform(Rect2(position, size));
+
+ tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0, 0.5), true);
+ } else {
+ if (tile_set->has_source(E->get().source_id)) {
+ TileSetSource *source = *tile_set->get_source(E->get().source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Get tile data.
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+
+ // Compute the offset
+ Rect2i source_rect = atlas_source->get_tile_texture_region(E->get().get_atlas_coords());
+ Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(E->get().get_atlas_coords(), E->get().alternative_tile);
+
+ // Compute the destination rectangle in the CanvasItem.
+ Rect2 dest_rect;
+ dest_rect.size = source_rect.size;
+
+ bool transpose = tile_data->get_transpose();
+ if (transpose) {
+ dest_rect.position = (tile_map->map_to_world(E->key()) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
+ } else {
+ dest_rect.position = (tile_map->map_to_world(E->key()) - dest_rect.size / 2 - tile_offset);
+ }
+
+ dest_rect = xform.xform(dest_rect);
+
+ if (tile_data->get_flip_h()) {
+ dest_rect.size.x = -dest_rect.size.x;
+ }
+
+ if (tile_data->get_flip_v()) {
+ dest_rect.size.y = -dest_rect.size.y;
+ }
+
+ // Get the tile modulation.
+ Color modulate = tile_data->get_modulate();
+ Color self_modulate = tile_map->get_self_modulate();
+ modulate = Color(modulate.r * self_modulate.r, modulate.g * self_modulate.g, modulate.b * self_modulate.b, modulate.a * self_modulate.a);
+
+ // Draw the tile.
+ p_overlay->draw_texture_rect_region(atlas_source->get_texture(), dest_rect, source_rect, modulate * Color(1.0, 1.0, 1.0, 0.5), transpose, tile_set->is_uv_clipping());
+ }
+ } else {
+ Vector2i size = tile_set->get_tile_size();
+ Vector2 position = tile_map->map_to_world(E->key()) - size / 2;
+ Rect2 cell_region = xform.xform(Rect2(position, size));
+
+ tile_set->draw_tile_shape(p_overlay, cell_region, Color(0.0, 0.0, 0.0, 0.5), true);
+ }
+ }
+ }
+ }
+ }
+}
+
+void TileMapEditorTilesPlugin::_mouse_exited_viewport() {
+ has_mouse = false;
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
+TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(const TileMapPattern *p_pattern) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return TileMapCell();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return TileMapCell();
+ }
+
+ TypedArray<Vector2i> used_cells = p_pattern->get_used_cells();
+ double sum = 0.0;
+ for (int i = 0; i < used_cells.size(); i++) {
+ int source_id = p_pattern->get_cell_source_id(used_cells[i]);
+ Vector2i atlas_coords = p_pattern->get_cell_atlas_coords(used_cells[i]);
+ int alternative_tile = p_pattern->get_cell_alternative_tile(used_cells[i]);
+
+ TileSetSource *source = *tile_set->get_source(source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ sum += Object::cast_to<TileData>(atlas_source->get_tile_data(atlas_coords, alternative_tile))->get_probability();
+ } else {
+ sum += 1.0;
+ }
+ }
+
+ double empty_probability = sum * scattering;
+ double current = 0.0;
+ double rand = Math::random(0.0, sum + empty_probability);
+ for (int i = 0; i < used_cells.size(); i++) {
+ int source_id = p_pattern->get_cell_source_id(used_cells[i]);
+ Vector2i atlas_coords = p_pattern->get_cell_atlas_coords(used_cells[i]);
+ int alternative_tile = p_pattern->get_cell_alternative_tile(used_cells[i]);
+
+ TileSetSource *source = *tile_set->get_source(source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ current += Object::cast_to<TileData>(atlas_source->get_tile_data(atlas_coords, alternative_tile))->get_probability();
+ } else {
+ current += 1.0;
+ }
+
+ if (current >= rand) {
+ return TileMapCell(source_id, atlas_coords, alternative_tile);
+ }
+ }
+ return TileMapCell();
+}
+
+Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2i p_to_mouse_pos) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ // Get or create the pattern.
+ TileMapPattern erase_pattern;
+ erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
+
+ Map<Vector2i, TileMapCell> output;
+ if (!pattern->is_empty()) {
+ // Paint the tiles on the tile map.
+ if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ // Paint a random tile.
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(p_from_mouse_pos), tile_map->world_to_map(p_to_mouse_pos));
+ for (int i = 0; i < line.size(); i++) {
+ output.insert(line[i], _pick_random_tile(pattern));
+ }
+ } else {
+ // Paint the pattern.
+ // If we paint several tiles, we virtually move the mouse as if it was in the center of the "brush"
+ Vector2 mouse_offset = (Vector2(pattern->get_size()) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size();
+ Vector2i last_hovered_cell = tile_map->world_to_map(p_from_mouse_pos - mouse_offset);
+ Vector2i new_hovered_cell = tile_map->world_to_map(p_to_mouse_pos - mouse_offset);
+ Vector2i drag_start_cell = tile_map->world_to_map(p_start_drag_mouse_pos - mouse_offset);
+
+ TypedArray<Vector2i> used_cells = pattern->get_used_cells();
+ Vector2i offset = Vector2i(Math::posmod(drag_start_cell.x, pattern->get_size().x), Math::posmod(drag_start_cell.y, pattern->get_size().y)); // Note: no posmodv for Vector2i for now. Meh.s
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, (last_hovered_cell - offset) / pattern->get_size(), (new_hovered_cell - offset) / pattern->get_size());
+ for (int i = 0; i < line.size(); i++) {
+ Vector2i top_left = line[i] * pattern->get_size() + offset;
+ for (int j = 0; j < used_cells.size(); j++) {
+ Vector2i coords = tile_map->map_pattern(top_left, used_cells[j], pattern);
+ output.insert(coords, TileMapCell(pattern->get_cell_source_id(used_cells[j]), pattern->get_cell_atlas_coords(used_cells[j]), pattern->get_cell_alternative_tile(used_cells[j])));
+ }
+ }
+ }
+ }
+ return output;
+}
+
+Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ // Create the rect to draw.
+ Rect2i rect = Rect2i(p_start_cell, p_end_cell - p_start_cell).abs();
+ rect.size += Vector2i(1, 1);
+
+ // Get or create the pattern.
+ TileMapPattern erase_pattern;
+ erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
+
+ // Compute the offset to align things to the bottom or right.
+ bool aligned_right = p_end_cell.x < p_start_cell.x;
+ bool valigned_bottom = p_end_cell.y < p_start_cell.y;
+ Vector2i offset = Vector2i(aligned_right ? -(pattern->get_size().x - (rect.get_size().x % pattern->get_size().x)) : 0, valigned_bottom ? -(pattern->get_size().y - (rect.get_size().y % pattern->get_size().y)) : 0);
+
+ Map<Vector2i, TileMapCell> output;
+ if (!pattern->is_empty()) {
+ if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ // Paint a random tile.
+ for (int x = 0; x < rect.size.x; x++) {
+ for (int y = 0; y < rect.size.y; y++) {
+ Vector2i coords = rect.position + Vector2i(x, y);
+ output.insert(coords, _pick_random_tile(pattern));
+ }
+ }
+ } else {
+ // Paint the pattern.
+ TypedArray<Vector2i> used_cells = pattern->get_used_cells();
+ for (int x = 0; x <= rect.size.x / pattern->get_size().x; x++) {
+ for (int y = 0; y <= rect.size.y / pattern->get_size().y; y++) {
+ Vector2i pattern_coords = rect.position + Vector2i(x, y) * pattern->get_size() + offset;
+ for (int j = 0; j < used_cells.size(); j++) {
+ Vector2i coords = pattern_coords + used_cells[j];
+ if (rect.has_point(coords)) {
+ output.insert(coords, TileMapCell(pattern->get_cell_source_id(used_cells[j]), pattern->get_cell_atlas_coords(used_cells[j]), pattern->get_cell_alternative_tile(used_cells[j])));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return output;
+}
+
+Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ // Get or create the pattern.
+ TileMapPattern erase_pattern;
+ erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
+
+ Map<Vector2i, TileMapCell> output;
+ if (!pattern->is_empty()) {
+ TileMapCell source = tile_map->get_cell(p_coords);
+
+ // If we are filling empty tiles, compute the tilemap boundaries.
+ Rect2i boundaries;
+ if (source.source_id == -1) {
+ boundaries = tile_map->get_used_rect();
+ }
+
+ if (p_contiguous) {
+ // Replace continuous tiles like the source.
+ Set<Vector2i> already_checked;
+ List<Vector2i> to_check;
+ to_check.push_back(p_coords);
+ while (!to_check.is_empty()) {
+ Vector2i coords = to_check.back()->get();
+ to_check.pop_back();
+ if (!already_checked.has(coords)) {
+ if (source.source_id == tile_map->get_cell_source_id(coords) &&
+ source.get_atlas_coords() == tile_map->get_cell_atlas_coords(coords) &&
+ source.alternative_tile == tile_map->get_cell_alternative_tile(coords) &&
+ (source.source_id != -1 || boundaries.has_point(coords))) {
+ if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ // Paint a random tile.
+ output.insert(coords, _pick_random_tile(pattern));
+ } else {
+ // Paint the pattern.
+ Vector2i pattern_coords = (coords - p_coords) % pattern->get_size(); // Note: it would be good to have posmodv for Vector2i.
+ pattern_coords.x = pattern_coords.x < 0 ? pattern_coords.x + pattern->get_size().x : pattern_coords.x;
+ pattern_coords.y = pattern_coords.y < 0 ? pattern_coords.y + pattern->get_size().y : pattern_coords.y;
+ if (pattern->has_cell(pattern_coords)) {
+ output.insert(coords, TileMapCell(pattern->get_cell_source_id(pattern_coords), pattern->get_cell_atlas_coords(pattern_coords), pattern->get_cell_alternative_tile(pattern_coords)));
+ } else {
+ output.insert(coords, TileMapCell());
+ }
+ }
+
+ // Get surrounding tiles (handles different tile shapes).
+ TypedArray<Vector2i> around = tile_map->get_surrounding_tiles(coords);
+ for (int i = 0; i < around.size(); i++) {
+ to_check.push_back(around[i]);
+ }
+ }
+ already_checked.insert(coords);
+ }
+ }
+ } else {
+ // Replace all tiles like the source.
+ TypedArray<Vector2i> to_check;
+ if (source.source_id == -1) {
+ Rect2i rect = tile_map->get_used_rect();
+ if (rect.size.x <= 0 || rect.size.y <= 0) {
+ rect = Rect2i(p_coords, Vector2i(1, 1));
+ }
+ for (int x = boundaries.position.x; x < boundaries.get_end().x; x++) {
+ for (int y = boundaries.position.y; y < boundaries.get_end().y; y++) {
+ to_check.append(Vector2i(x, y));
+ }
+ }
+ } else {
+ to_check = tile_map->get_used_cells();
+ }
+ for (int i = 0; i < to_check.size(); i++) {
+ Vector2i coords = to_check[i];
+ if (source.source_id == tile_map->get_cell_source_id(coords) &&
+ source.get_atlas_coords() == tile_map->get_cell_atlas_coords(coords) &&
+ source.alternative_tile == tile_map->get_cell_alternative_tile(coords) &&
+ (source.source_id != -1 || boundaries.has_point(coords))) {
+ if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ // Paint a random tile.
+ output.insert(coords, _pick_random_tile(pattern));
+ } else {
+ // Paint the pattern.
+ Vector2i pattern_coords = (coords - p_coords) % pattern->get_size(); // Note: it would be good to have posmodv for Vector2i.
+ pattern_coords.x = pattern_coords.x < 0 ? pattern_coords.x + pattern->get_size().x : pattern_coords.x;
+ pattern_coords.y = pattern_coords.y < 0 ? pattern_coords.y + pattern->get_size().y : pattern_coords.y;
+ if (pattern->has_cell(pattern_coords)) {
+ output.insert(coords, TileMapCell(pattern->get_cell_source_id(pattern_coords), pattern->get_cell_atlas_coords(pattern_coords), pattern->get_cell_alternative_tile(pattern_coords)));
+ } else {
+ output.insert(coords, TileMapCell());
+ }
+ }
+ }
+ }
+ }
+ }
+ return output;
+}
+
+void TileMapEditorTilesPlugin::_stop_dragging() {
+ if (drag_type == DRAG_TYPE_NONE) {
+ return;
+ }
+
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
+ Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position());
+
+ switch (drag_type) {
+ case DRAG_TYPE_SELECT: {
+ undo_redo->create_action(TTR("Change selection"));
+ undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+
+ if (!Input::get_singleton()->is_key_pressed(KEY_SHIFT) && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) {
+ tile_map_selection.clear();
+ }
+ Rect2i rect = Rect2i(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos) - tile_map->world_to_map(drag_start_mouse_pos)).abs();
+ for (int x = rect.position.x; x <= rect.get_end().x; x++) {
+ for (int y = rect.position.y; y <= rect.get_end().y; y++) {
+ Vector2i coords = Vector2i(x, y);
+ if (Input::get_singleton()->is_key_pressed(KEY_CONTROL)) {
+ if (tile_map_selection.has(coords)) {
+ tile_map_selection.erase(coords);
+ }
+ } else {
+ if (tile_map->get_cell_source_id(coords) != -1) {
+ tile_map_selection.insert(coords);
+ }
+ }
+ }
+ }
+ undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+ undo_redo->commit_action(false);
+
+ _update_selection_pattern_from_tilemap_selection();
+ _update_tileset_selection_from_selection_pattern();
+ } break;
+ case DRAG_TYPE_MOVE: {
+ Vector2i top_left;
+ if (!tile_map_selection.is_empty()) {
+ top_left = tile_map_selection.front()->get();
+ }
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ top_left = top_left.min(E->get());
+ }
+
+ Vector2i offset = drag_start_mouse_pos - tile_map->map_to_world(top_left);
+ offset = tile_map->world_to_map(mpos - offset) - tile_map->world_to_map(drag_start_mouse_pos - offset);
+
+ TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
+
+ Vector2i coords;
+ Map<Vector2i, TileMapCell> cells_undo;
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
+ cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile);
+ coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
+ cells_undo[coords] = TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords));
+ }
+
+ Map<Vector2i, TileMapCell> cells_do;
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
+ cells_do[coords] = TileMapCell();
+ }
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
+ cells_do[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
+ }
+ undo_redo->create_action(TTR("Move tiles"));
+ // Move the tiles.
+ for (Map<Vector2i, TileMapCell>::Element *E = cells_do.front(); E; E = E->next()) {
+ undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ for (Map<Vector2i, TileMapCell>::Element *E = cells_undo.front(); E; E = E->next()) {
+ undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+
+ // Update the selection.
+ undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+ tile_map_selection.clear();
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
+ tile_map_selection.insert(coords);
+ }
+ undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+ undo_redo->commit_action();
+ } break;
+ case DRAG_TYPE_PICK: {
+ Rect2i rect = Rect2i(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos) - tile_map->world_to_map(drag_start_mouse_pos)).abs();
+ rect.size += Vector2i(1, 1);
+ memdelete(selection_pattern);
+ TypedArray<Vector2i> coords_array;
+ for (int x = rect.position.x; x < rect.get_end().x; x++) {
+ for (int y = rect.position.y; y < rect.get_end().y; y++) {
+ Vector2i coords = Vector2i(x, y);
+ if (tile_map->get_cell_source_id(coords) != -1) {
+ coords_array.push_back(coords);
+ }
+ }
+ }
+ selection_pattern = tile_map->get_pattern(coords_array);
+ if (!selection_pattern->is_empty()) {
+ _update_tileset_selection_from_selection_pattern();
+ } else {
+ _update_selection_pattern_from_tileset_selection();
+ }
+ picker_button->set_pressed(false);
+ } break;
+ case DRAG_TYPE_PAINT: {
+ undo_redo->create_action(TTR("Paint tiles"));
+ for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
+ undo_redo->add_do_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key()));
+ undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ undo_redo->commit_action(false);
+ } break;
+ case DRAG_TYPE_LINE: {
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, mpos);
+ undo_redo->create_action(TTR("Paint tiles"));
+ for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
+ if (!erase_button->is_pressed() && E->get().source_id == -1) {
+ continue;
+ }
+ undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key()));
+ }
+ undo_redo->commit_action();
+ } break;
+ case DRAG_TYPE_RECT: {
+ Map<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos));
+ undo_redo->create_action(TTR("Paint tiles"));
+ for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
+ if (!erase_button->is_pressed() && E->get().source_id == -1) {
+ continue;
+ }
+ undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key()));
+ }
+ undo_redo->commit_action();
+ } break;
+ case DRAG_TYPE_BUCKET: {
+ undo_redo->create_action(TTR("Paint tiles"));
+ for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
+ if (!erase_button->is_pressed() && E->get().source_id == -1) {
+ continue;
+ }
+ undo_redo->add_do_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key()));
+ undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ undo_redo->commit_action(false);
+ } break;
+ case DRAG_TYPE_CLIPBOARD_PASTE: {
+ Vector2 mouse_offset = (Vector2(tile_map_clipboard->get_size()) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size();
+ undo_redo->create_action(TTR("Paste tiles"));
+ TypedArray<Vector2i> used_cells = tile_map_clipboard->get_used_cells();
+ for (int i = 0; i < used_cells.size(); i++) {
+ Vector2i coords = tile_map->map_pattern(tile_map->world_to_map(mpos - mouse_offset), used_cells[i], tile_map_clipboard);
+ undo_redo->add_do_method(tile_map, "set_cell", coords, tile_map_clipboard->get_cell_source_id(used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(used_cells[i]));
+ undo_redo->add_undo_method(tile_map, "set_cell", coords, tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords));
+ }
+ undo_redo->commit_action();
+ } break;
+ default:
+ break;
+ }
+ drag_type = DRAG_TYPE_NONE;
+}
+
+void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ hovered_tile.source_id = -1;
+ hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ tile_set_selection.clear();
+ tile_map_selection.clear();
+ selection_pattern->clear();
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ hovered_tile.source_id = -1;
+ hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ tile_set_selection.clear();
+ tile_map_selection.clear();
+ selection_pattern->clear();
+ return;
+ }
+
+ int source_index = sources_list->get_current();
+ if (source_index < 0 || source_index >= sources_list->get_item_count()) {
+ hovered_tile.source_id = -1;
+ hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ tile_set_selection.clear();
+ tile_map_selection.clear();
+ selection_pattern->clear();
+ return;
+ }
+
+ int source_id = sources_list->get_item_metadata(source_index);
+
+ // Clear hovered if needed.
+ if (source_id != hovered_tile.source_id ||
+ !tile_set->has_source(hovered_tile.source_id) ||
+ !tile_set->get_source(hovered_tile.source_id)->has_tile(hovered_tile.get_atlas_coords()) ||
+ !tile_set->get_source(hovered_tile.source_id)->has_alternative_tile(hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile)) {
+ hovered_tile.source_id = -1;
+ hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ }
+
+ // Selection if needed.
+ for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) {
+ const TileMapCell *selected = &(E->get());
+ if (!tile_set->has_source(selected->source_id) ||
+ !tile_set->get_source(selected->source_id)->has_tile(selected->get_atlas_coords()) ||
+ !tile_set->get_source(selected->source_id)->has_alternative_tile(selected->get_atlas_coords(), selected->alternative_tile)) {
+ tile_set_selection.erase(E);
+ }
+ }
+
+ if (!tile_map_selection.is_empty()) {
+ _update_selection_pattern_from_tilemap_selection();
+ } else {
+ _update_selection_pattern_from_tileset_selection();
+ }
+}
+
+void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ memdelete(selection_pattern);
+
+ TypedArray<Vector2i> coords_array;
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ coords_array.push_back(E->get());
+ }
+ selection_pattern = tile_map->get_pattern(coords_array);
+}
+
+void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ // Clear the tilemap selection.
+ tile_map_selection.clear();
+
+ // Clear the selected pattern.
+ selection_pattern->clear();
+
+ // Group per source.
+ Map<int, List<const TileMapCell *>> per_source;
+ for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) {
+ per_source[E->get().source_id].push_back(&(E->get()));
+ }
+
+ for (Map<int, List<const TileMapCell *>>::Element *E_source = per_source.front(); E_source; E_source = E_source->next()) {
+ // Per source.
+ List<const TileMapCell *> unorganized;
+ Rect2i encompassing_rect_coords;
+ Map<Vector2i, const TileMapCell *> organized_pattern;
+
+ TileSetSource *source = *tile_set->get_source(E_source->key());
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Organize using coordinates.
+ for (List<const TileMapCell *>::Element *E_cell = E_source->get().front(); E_cell; E_cell = E_cell->next()) {
+ const TileMapCell *current = E_cell->get();
+ if (organized_pattern.has(current->get_atlas_coords())) {
+ if (current->alternative_tile < organized_pattern[current->get_atlas_coords()]->alternative_tile) {
+ unorganized.push_back(organized_pattern[current->get_atlas_coords()]);
+ organized_pattern[current->get_atlas_coords()] = current;
+ } else {
+ unorganized.push_back(current);
+ }
+ } else {
+ organized_pattern[current->get_atlas_coords()] = current;
+ }
+ }
+
+ // Compute the encompassing rect for the organized pattern.
+ Map<Vector2i, const TileMapCell *>::Element *E_cell = organized_pattern.front();
+ encompassing_rect_coords = Rect2i(E_cell->key(), Vector2i(1, 1));
+ for (; E_cell; E_cell = E_cell->next()) {
+ encompassing_rect_coords.expand_to(E_cell->key() + Vector2i(1, 1));
+ encompassing_rect_coords.expand_to(E_cell->key());
+ }
+ } else {
+ // Add everything unorganized.
+ for (List<const TileMapCell *>::Element *E_cell = E_source->get().front(); E_cell; E_cell = E_cell->next()) {
+ unorganized.push_back(E_cell->get());
+ }
+ }
+
+ // Now add everything to the output pattern.
+ for (Map<Vector2i, const TileMapCell *>::Element *E_cell = organized_pattern.front(); E_cell; E_cell = E_cell->next()) {
+ selection_pattern->set_cell(E_cell->key() - encompassing_rect_coords.position, E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile);
+ }
+ Vector2i organized_size = selection_pattern->get_size();
+ for (List<const TileMapCell *>::Element *E_cell = unorganized.front(); E_cell; E_cell = E_cell->next()) {
+ selection_pattern->set_cell(Vector2(organized_size.x, 0), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile);
+ }
+ }
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
+void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern() {
+ tile_set_selection.clear();
+ TypedArray<Vector2i> used_cells = selection_pattern->get_used_cells();
+ for (int i = 0; i < used_cells.size(); i++) {
+ Vector2i coords = used_cells[i];
+ if (selection_pattern->get_cell_source_id(coords) != -1) {
+ tile_set_selection.insert(TileMapCell(selection_pattern->get_cell_source_id(coords), selection_pattern->get_cell_atlas_coords(coords), selection_pattern->get_cell_alternative_tile(coords)));
+ }
+ }
+ _update_atlas_view();
+}
+
+void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ int source_index = sources_list->get_current();
+ if (source_index < 0 || source_index >= sources_list->get_item_count()) {
+ return;
+ }
+
+ int source_id = sources_list->get_item_metadata(source_index);
+ if (!tile_set->has_source(source_id)) {
+ return;
+ }
+
+ TileSetAtlasSource *atlas = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
+ if (!atlas) {
+ return;
+ }
+
+ // Draw the selection.
+ for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) {
+ if (E->get().source_id == source_id && E->get().alternative_tile == 0) {
+ tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords()), Color(0.0, 0.0, 1.0), false);
+ }
+ }
+
+ // Draw the hovered tile.
+ if (hovered_tile.get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0 && !tile_set_dragging_selection) {
+ tile_atlas_control->draw_rect(atlas->get_tile_texture_region(hovered_tile.get_atlas_coords()), Color(1.0, 1.0, 1.0), false);
+ }
+
+ // Draw the selection rect.
+ if (tile_set_dragging_selection) {
+ Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos);
+ Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+
+ Rect2i region = Rect2i(start_tile, end_tile - start_tile).abs();
+ region.size += Vector2i(1, 1);
+
+ Set<Vector2i> to_draw;
+ for (int x = region.position.x; x < region.get_end().x; x++) {
+ for (int y = region.position.y; y < region.get_end().y; y++) {
+ Vector2i tile = atlas->get_tile_at_coords(Vector2i(x, y));
+ if (tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ to_draw.insert(tile);
+ }
+ }
+ }
+
+ for (Set<Vector2i>::Element *E = to_draw.front(); E; E = E->next()) {
+ tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get()), Color(0.8, 0.8, 1.0), false);
+ }
+ }
+}
+
+void TileMapEditorTilesPlugin::_tile_atlas_control_mouse_exited() {
+ hovered_tile.source_id = -1;
+ hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ tile_set_dragging_selection = false;
+ tile_atlas_control->update();
+}
+
+void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEvent> &p_event) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ int source_index = sources_list->get_current();
+ if (source_index < 0 || source_index >= sources_list->get_item_count()) {
+ return;
+ }
+
+ int source_id = sources_list->get_item_metadata(source_index);
+ if (!tile_set->has_source(source_id)) {
+ return;
+ }
+
+ TileSetAtlasSource *atlas = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
+ if (!atlas) {
+ return;
+ }
+
+ // Update the hovered tile
+ hovered_tile.source_id = source_id;
+ hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ coords = atlas->get_tile_at_coords(coords);
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ hovered_tile.set_atlas_coords(coords);
+ hovered_tile.alternative_tile = 0;
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ tile_atlas_control->update();
+ alternative_tiles_control->update();
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->is_pressed()) { // Pressed
+ tile_set_dragging_selection = true;
+ tile_set_drag_start_mouse_pos = tile_atlas_control->get_local_mouse_position();
+ if (!mb->get_shift()) {
+ tile_set_selection.clear();
+ }
+
+ if (hovered_tile.get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0) {
+ if (mb->get_shift() && tile_set_selection.has(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0))) {
+ tile_set_selection.erase(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0));
+ } else {
+ tile_set_selection.insert(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0));
+ }
+ }
+ _update_selection_pattern_from_tileset_selection();
+ } else { // Released
+ if (tile_set_dragging_selection) {
+ if (!mb->get_shift()) {
+ tile_set_selection.clear();
+ }
+ // Compute the covered area.
+ Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos);
+ Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ if (start_tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && end_tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ Rect2i region = Rect2i(start_tile, end_tile - start_tile).abs();
+ region.size += Vector2i(1, 1);
+
+ // To update the selection, we copy the selected/not selected status of the tiles we drag from.
+ Vector2i start_coords = atlas->get_tile_at_coords(start_tile);
+ if (mb->get_shift() && start_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && !tile_set_selection.has(TileMapCell(source_id, start_coords, 0))) {
+ // Remove from the selection.
+ for (int x = region.position.x; x < region.get_end().x; x++) {
+ for (int y = region.position.y; y < region.get_end().y; y++) {
+ Vector2i tile_coords = atlas->get_tile_at_coords(Vector2i(x, y));
+ if (tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_selection.has(TileMapCell(source_id, tile_coords, 0))) {
+ tile_set_selection.erase(TileMapCell(source_id, tile_coords, 0));
+ }
+ }
+ }
+ } else {
+ // Insert in the selection.
+ for (int x = region.position.x; x < region.get_end().x; x++) {
+ for (int y = region.position.y; y < region.get_end().y; y++) {
+ Vector2i tile_coords = atlas->get_tile_at_coords(Vector2i(x, y));
+ if (tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ tile_set_selection.insert(TileMapCell(source_id, tile_coords, 0));
+ }
+ }
+ }
+ }
+ }
+ _update_selection_pattern_from_tileset_selection();
+ }
+ tile_set_dragging_selection = false;
+ }
+ tile_atlas_control->update();
+ }
+}
+
+void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ int source_index = sources_list->get_current();
+ if (source_index < 0 || source_index >= sources_list->get_item_count()) {
+ return;
+ }
+
+ int source_id = sources_list->get_item_metadata(source_index);
+ if (!tile_set->has_source(source_id)) {
+ return;
+ }
+
+ TileSetAtlasSource *atlas = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
+ if (!atlas) {
+ return;
+ }
+
+ // Draw the selection.
+ for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) {
+ if (E->get().source_id == source_id && E->get().get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && E->get().alternative_tile > 0) {
+ Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E->get().get_atlas_coords(), E->get().alternative_tile);
+ if (rect != Rect2i()) {
+ alternative_tiles_control->draw_rect(rect, Color(0.2, 0.2, 1.0), false);
+ }
+ }
+ }
+
+ // Draw hovered tile.
+ if (hovered_tile.get_atlas_coords() != TileSetAtlasSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile > 0) {
+ Rect2i rect = tile_atlas_view->get_alternative_tile_rect(hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile);
+ if (rect != Rect2i()) {
+ alternative_tiles_control->draw_rect(rect, Color(1.0, 1.0, 1.0), false);
+ }
+ }
+}
+
+void TileMapEditorTilesPlugin::_tile_alternatives_control_mouse_exited() {
+ hovered_tile.source_id = -1;
+ hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ tile_set_dragging_selection = false;
+ alternative_tiles_control->update();
+}
+
+void TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ int source_index = sources_list->get_current();
+ if (source_index < 0 || source_index >= sources_list->get_item_count()) {
+ return;
+ }
+
+ int source_id = sources_list->get_item_metadata(source_index);
+ if (!tile_set->has_source(source_id)) {
+ return;
+ }
+
+ TileSetAtlasSource *atlas = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
+ if (!atlas) {
+ return;
+ }
+
+ // Update the hovered tile
+ hovered_tile.source_id = source_id;
+ hovered_tile.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ hovered_tile.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ Vector3i alternative_coords = tile_atlas_view->get_alternative_tile_at_pos(alternative_tiles_control->get_local_mouse_position());
+ Vector2i coords = Vector2i(alternative_coords.x, alternative_coords.y);
+ int alternative = alternative_coords.z;
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && alternative != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ hovered_tile.set_atlas_coords(coords);
+ hovered_tile.alternative_tile = alternative;
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ tile_atlas_control->update();
+ alternative_tiles_control->update();
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->is_pressed()) { // Pressed
+ // Left click pressed.
+ if (!mb->get_shift()) {
+ tile_set_selection.clear();
+ }
+
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && alternative != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ if (mb->get_shift() && tile_set_selection.has(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile))) {
+ tile_set_selection.erase(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile));
+ } else {
+ tile_set_selection.insert(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile));
+ }
+ }
+ _update_selection_pattern_from_tileset_selection();
+ }
+ tile_atlas_control->update();
+ alternative_tiles_control->update();
+ }
+}
+
+void TileMapEditorTilesPlugin::_set_tile_map_selection(const TypedArray<Vector2i> &p_selection) {
+ tile_map_selection.clear();
+ for (int i = 0; i < p_selection.size(); i++) {
+ tile_map_selection.insert(p_selection[i]);
+ }
+ _update_selection_pattern_from_tilemap_selection();
+ _update_tileset_selection_from_selection_pattern();
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
+TypedArray<Vector2i> TileMapEditorTilesPlugin::_get_tile_map_selection() const {
+ TypedArray<Vector2i> output;
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ output.push_back(E->get());
+ }
+ return output;
+}
+
+void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id) {
+ tile_map_id = p_tile_map_id;
+
+ // Clean the selection.
+ tile_set_selection.clear();
+ tile_map_selection.clear();
+ selection_pattern->clear();
+}
+
+void TileMapEditorTilesPlugin::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_set_tile_map_selection", "selection"), &TileMapEditorTilesPlugin::_set_tile_map_selection);
+ ClassDB::bind_method(D_METHOD("_get_tile_map_selection"), &TileMapEditorTilesPlugin::_get_tile_map_selection);
+}
+
+TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
+ CanvasItemEditor::get_singleton()->get_viewport_control()->connect("mouse_exited", callable_mp(this, &TileMapEditorTilesPlugin::_mouse_exited_viewport));
+
+ // --- Shortcuts ---
+ ED_SHORTCUT("tiles_editor/cut", TTR("Cut"), KEY_MASK_CMD | KEY_X);
+ ED_SHORTCUT("tiles_editor/copy", TTR("Copy"), KEY_MASK_CMD | KEY_C);
+ ED_SHORTCUT("tiles_editor/paste", TTR("Paste"), KEY_MASK_CMD | KEY_V);
+ ED_SHORTCUT("tiles_editor/cancel", TTR("Cancel"), KEY_ESCAPE);
+ ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), KEY_DELETE);
+
+ // --- Toolbar ---
+ toolbar = memnew(HBoxContainer);
+
+ HBoxContainer *tilemap_tiles_tools_buttons = memnew(HBoxContainer);
+
+ tool_buttons_group.instance();
+
+ select_tool_button = memnew(Button);
+ select_tool_button->set_flat(true);
+ select_tool_button->set_toggle_mode(true);
+ select_tool_button->set_button_group(tool_buttons_group);
+ select_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/selection_tool", "Selection", KEY_S));
+ select_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(select_tool_button);
+
+ paint_tool_button = memnew(Button);
+ paint_tool_button->set_flat(true);
+ paint_tool_button->set_toggle_mode(true);
+ paint_tool_button->set_button_group(tool_buttons_group);
+ paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", "Paint", KEY_E));
+ paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(paint_tool_button);
+
+ line_tool_button = memnew(Button);
+ line_tool_button->set_flat(true);
+ line_tool_button->set_toggle_mode(true);
+ line_tool_button->set_button_group(tool_buttons_group);
+ line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", "Line", KEY_L));
+ line_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(line_tool_button);
+
+ rect_tool_button = memnew(Button);
+ rect_tool_button->set_flat(true);
+ rect_tool_button->set_toggle_mode(true);
+ rect_tool_button->set_button_group(tool_buttons_group);
+ rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", "Rect", KEY_R));
+ rect_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(rect_tool_button);
+
+ bucket_tool_button = memnew(Button);
+ bucket_tool_button->set_flat(true);
+ bucket_tool_button->set_toggle_mode(true);
+ bucket_tool_button->set_button_group(tool_buttons_group);
+ bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", "Bucket", KEY_B));
+ bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
+
+ toolbar->add_child(tilemap_tiles_tools_buttons);
+
+ // -- TileMap tool settings --
+ tools_settings = memnew(HBoxContainer);
+ toolbar->add_child(tools_settings);
+
+ tools_settings_vsep = memnew(VSeparator);
+ tools_settings->add_child(tools_settings_vsep);
+
+ // Picker
+ picker_button = memnew(Button);
+ picker_button->set_flat(true);
+ picker_button->set_toggle_mode(true);
+ picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", KEY_P));
+ picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
+ tools_settings->add_child(picker_button);
+
+ // Erase button.
+ erase_button = memnew(Button);
+ erase_button->set_flat(true);
+ erase_button->set_toggle_mode(true);
+ erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", KEY_E));
+ erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
+ tools_settings->add_child(erase_button);
+
+ // Separator 2.
+ tools_settings_vsep_2 = memnew(VSeparator);
+ tools_settings->add_child(tools_settings_vsep_2);
+
+ // Continuous checkbox.
+ bucket_continuous_checkbox = memnew(CheckBox);
+ bucket_continuous_checkbox->set_flat(true);
+ bucket_continuous_checkbox->set_text(TTR("Contiguous"));
+ tools_settings->add_child(bucket_continuous_checkbox);
+
+ // Random tile checkbox.
+ random_tile_checkbox = memnew(CheckBox);
+ random_tile_checkbox->set_flat(true);
+ random_tile_checkbox->set_text(TTR("Place random tile"));
+ random_tile_checkbox->connect("toggled", callable_mp(this, &TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled));
+ tools_settings->add_child(random_tile_checkbox);
+
+ // Random tile scattering.
+ scatter_label = memnew(Label);
+ scatter_label->set_tooltip(TTR("Defines the probability of painting nothing instead of a randomly selected tile."));
+ scatter_label->set_text(TTR("Scattering:"));
+ tools_settings->add_child(scatter_label);
+
+ scatter_spinbox = memnew(SpinBox);
+ scatter_spinbox->set_min(0.0);
+ scatter_spinbox->set_max(1000);
+ scatter_spinbox->set_step(0.001);
+ scatter_spinbox->set_tooltip(TTR("Defines the probability of painting nothing instead of a randomly selected tile."));
+ scatter_spinbox->get_line_edit()->add_theme_constant_override("minimum_character_width", 4);
+ scatter_spinbox->connect("value_changed", callable_mp(this, &TileMapEditorTilesPlugin::_on_scattering_spinbox_changed));
+ tools_settings->add_child(scatter_spinbox);
+
+ _on_random_tile_checkbox_toggled(false);
+
+ // Default tool.
+ paint_tool_button->set_pressed(true);
+ _update_toolbar();
+
+ // --- Bottom panel ---
+ set_name("Tiles");
+
+ missing_source_label = memnew(Label);
+ missing_source_label->set_text(TTR("This TileMap's TileSet has no source configured. Edit the TileSet resource to add one."));
+ missing_source_label->set_h_size_flags(SIZE_EXPAND_FILL);
+ missing_source_label->set_v_size_flags(SIZE_EXPAND_FILL);
+ missing_source_label->set_align(Label::ALIGN_CENTER);
+ missing_source_label->set_valign(Label::VALIGN_CENTER);
+ missing_source_label->hide();
+ add_child(missing_source_label);
+
+ atlas_sources_split_container = memnew(HSplitContainer);
+ atlas_sources_split_container->set_h_size_flags(SIZE_EXPAND_FILL);
+ atlas_sources_split_container->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(atlas_sources_split_container);
+
+ sources_list = memnew(ItemList);
+ sources_list->set_fixed_icon_size(Size2i(60, 60) * EDSCALE);
+ sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ sources_list->set_stretch_ratio(0.25);
+ sources_list->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
+ sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1));
+ sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_atlas_view).unbind(1));
+ sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_sources_lists_current));
+ sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_sources_list), varray(sources_list));
+ //sources_list->set_drag_forwarding(this);
+ atlas_sources_split_container->add_child(sources_list);
+
+ tile_atlas_view = memnew(TileAtlasView);
+ tile_atlas_view->set_h_size_flags(SIZE_EXPAND_FILL);
+ tile_atlas_view->set_v_size_flags(SIZE_EXPAND_FILL);
+ tile_atlas_view->set_texture_grid_visible(false);
+ tile_atlas_view->set_tile_shape_grid_visible(false);
+ tile_atlas_view->connect("transform_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_view_transform));
+ //tile_atlas_view->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_view), varray(tile_atlas_view));
+ atlas_sources_split_container->add_child(tile_atlas_view);
+
+ tile_atlas_control = memnew(Control);
+ tile_atlas_control->connect("draw", callable_mp(this, &TileMapEditorTilesPlugin::_tile_atlas_control_draw));
+ tile_atlas_control->connect("mouse_exited", callable_mp(this, &TileMapEditorTilesPlugin::_tile_atlas_control_mouse_exited));
+ tile_atlas_control->connect("gui_input", callable_mp(this, &TileMapEditorTilesPlugin::_tile_atlas_control_gui_input));
+ tile_atlas_view->add_control_over_atlas_tiles(tile_atlas_control);
+
+ alternative_tiles_control = memnew(Control);
+ alternative_tiles_control->connect("draw", callable_mp(this, &TileMapEditorTilesPlugin::_tile_alternatives_control_draw));
+ alternative_tiles_control->connect("mouse_exited", callable_mp(this, &TileMapEditorTilesPlugin::_tile_alternatives_control_mouse_exited));
+ alternative_tiles_control->connect("gui_input", callable_mp(this, &TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input));
+ tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control);
+
+ _update_bottom_panel();
+}
+
+TileMapEditorTilesPlugin::~TileMapEditorTilesPlugin() {
+ memdelete(selection_pattern);
+ memdelete(tile_map_clipboard);
+}
+
+void TileMapEditorTerrainsPlugin::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED:
+ paint_tool_button->set_icon(get_theme_icon("Edit", "EditorIcons"));
+ picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons"));
+ erase_button->set_icon(get_theme_icon("Eraser", "EditorIcons"));
+ break;
+ }
+}
+
+void TileMapEditorTerrainsPlugin::tile_set_changed() {
+ _update_terrains_cache();
+ _update_terrains_tree();
+ _update_tiles_list();
+}
+
+void TileMapEditorTerrainsPlugin::_update_toolbar() {
+ // Hide all settings.
+ for (int i = 0; i < tools_settings->get_child_count(); i++) {
+ Object::cast_to<CanvasItem>(tools_settings->get_child(i))->hide();
+ }
+
+ // Show only the correct settings.
+ if (tool_buttons_group->get_pressed_button() == paint_tool_button) {
+ tools_settings_vsep->show();
+ picker_button->show();
+ erase_button->show();
+ }
+}
+
+Control *TileMapEditorTerrainsPlugin::get_toolbar() const {
+ return toolbar;
+}
+
+Map<Vector2i, TileSet::CellNeighbor> TileMapEditorTerrainsPlugin::Constraint::get_overlapping_coords_and_peering_bits() const {
+ Map<Vector2i, TileSet::CellNeighbor> output;
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ ERR_FAIL_COND_V(!tile_set.is_valid(), output);
+
+ TileSet::TileShape shape = tile_set->get_tile_shape();
+ if (shape == TileSet::TILE_SHAPE_SQUARE) {
+ switch (bit) {
+ case 0:
+ 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;
+ break;
+ case 1:
+ 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;
+ break;
+ case 2:
+ 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;
+ break;
+ case 3:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+ break;
+ default:
+ ERR_FAIL_V(output);
+ }
+ } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ switch (bit) {
+ case 0:
+ 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_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
+ break;
+ 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;
+ 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;
+ 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;
+ break;
+ default:
+ ERR_FAIL_V(output);
+ }
+ } else {
+ // Half offset shapes.
+ TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
+ if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (bit) {
+ case 0:
+ 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;
+ break;
+ case 1:
+ 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;
+ 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;
+ break;
+ case 3:
+ 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;
+ break;
+ case 4:
+ 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;
+ break;
+ default:
+ ERR_FAIL_V(output);
+ }
+ } else {
+ switch (bit) {
+ case 0:
+ 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;
+ break;
+ 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;
+ 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_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;
+ 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;
+ break;
+ case 4:
+ 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;
+ break;
+ default:
+ ERR_FAIL_V(output);
+ }
+ }
+ }
+ return output;
+}
+
+TileMapEditorTerrainsPlugin::Constraint::Constraint(const TileMap *p_tile_map, 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> tile_set = tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ TileSet::TileShape shape = tile_set->get_tile_shape();
+ if (shape == TileSet::TILE_SHAPE_SQUARE || shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ bit = 0;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ bit = 1;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ bit = 2;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ bit = 3;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ bit = 0;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ bit = 3;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
+ break;
+ default:
+ ERR_FAIL();
+ break;
+ }
+ } else {
+ // Half-offset shapes
+ 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:
+ bit = 0;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ bit = 1;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ bit = 2;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ bit = 3;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ bit = 4;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ bit = 0;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ bit = 3;
+ base_cell_coords = p_tile_map->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);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ bit = 4;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_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_RIGHT_SIDE);
+ break;
+ default:
+ ERR_FAIL();
+ break;
+ }
+ } else {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ bit = 0;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ bit = 1;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ bit = 2;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ bit = 3;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ bit = 0;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ bit = 4;
+ base_cell_coords = p_position;
+ 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);
+ 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);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ bit = 0;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ 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);
+ 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);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ bit = 4;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ break;
+ default:
+ ERR_FAIL();
+ break;
+ }
+ }
+ }
+ terrain = p_terrain;
+}
+
+Set<TileMapEditorTerrainsPlugin::TerrainsTilePattern> TileMapEditorTerrainsPlugin::_get_valid_terrains_tile_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<Constraint> p_constraints) const {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Set<TerrainsTilePattern>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Set<TerrainsTilePattern>();
+ }
+
+ // Returns all tiles compatible with the given constraints.
+ Set<TerrainsTilePattern> compatible_terrain_tile_patterns;
+ for (Map<TerrainsTilePattern, Set<TileMapCell>>::Element *E = per_terrain_terrains_tile_patterns_tiles[p_terrain_set].front(); E; E = E->next()) {
+ int valid = true;
+ int in_pattern_count = 0;
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
+ // Check if the bit is compatible with the constraints.
+ Constraint terrain_bit_constraint = Constraint(tile_map, p_position, bit, E->key()[in_pattern_count]);
+
+ Set<Constraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
+ if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
+ valid = false;
+ break;
+ }
+ in_pattern_count++;
+ }
+ }
+
+ if (valid) {
+ compatible_terrain_tile_patterns.insert(E->key());
+ }
+ }
+
+ return compatible_terrain_tile_patterns;
+}
+
+Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_constraints_from_removed_cells_list(const Set<Vector2i> &p_to_replace, int p_terrain_set) const {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Set<Constraint>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Set<Constraint>();
+ }
+
+ ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<Constraint>());
+
+ // Build a set of dummy constraints get the constrained points.
+ Set<Constraint> dummy_constraints;
+ for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides.
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
+ dummy_constraints.insert(Constraint(tile_map, E->get(), bit, -1));
+ }
+ }
+ }
+
+ // For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it.
+ Set<Constraint> constraints;
+ for (Set<Constraint>::Element *E = dummy_constraints.front(); E; E = E->next()) {
+ Constraint c = E->get();
+
+ Map<int, int> terrain_count;
+
+ // Count the number of occurences per terrain.
+ Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
+ for (Map<Vector2i, TileSet::CellNeighbor>::Element *E_overlapping = overlapping_terrain_bits.front(); E_overlapping; E_overlapping = E_overlapping->next()) {
+ if (!p_to_replace.has(E_overlapping->key())) {
+ TileMapCell neighbor_cell = tile_map->get_cell(E_overlapping->key());
+ TileData *neighbor_tile_data = nullptr;
+ if (terrain_tiles.has(neighbor_cell) && terrain_tiles[neighbor_cell]->get_terrain_set() == p_terrain_set) {
+ neighbor_tile_data = terrain_tiles[neighbor_cell];
+ }
+
+ int terrain = neighbor_tile_data ? neighbor_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(E_overlapping->get())) : -1;
+ if (terrain_count.has(terrain)) {
+ terrain_count[terrain] = 0;
+ }
+ terrain_count[terrain] += 1;
+ }
+ }
+
+ // Get the terrain with the max number of occurences.
+ int max = 0;
+ int max_terrain = -1;
+ for (Map<int, int>::Element *E_terrain_count = terrain_count.front(); E_terrain_count; E_terrain_count = E_terrain_count->next()) {
+ if (E_terrain_count->get() > max) {
+ max = E_terrain_count->get();
+ max_terrain = E_terrain_count->key();
+ }
+ }
+
+ // Set the adequate terrain.
+ if (max > 0) {
+ c.set_terrain(max_terrain);
+ constraints.insert(c);
+ }
+ }
+
+ return constraints;
+}
+
+Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TerrainsTilePattern p_terrains_tile_pattern) const {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Set<TileMapEditorTerrainsPlugin::Constraint>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Set<TileMapEditorTerrainsPlugin::Constraint>();
+ }
+
+ // Compute the constraints needed from the surrounding tiles.
+ Set<TileMapEditorTerrainsPlugin::Constraint> output;
+ int in_pattern_count = 0;
+ for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
+ Constraint c = Constraint(tile_map, p_position, side, p_terrains_tile_pattern[in_pattern_count]);
+ output.insert(c);
+ in_pattern_count++;
+ }
+ }
+
+ return output;
+}
+
+Map<Vector2i, TileMapEditorTerrainsPlugin::TerrainsTilePattern> TileMapEditorTerrainsPlugin::_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Map<Vector2i, TerrainsTilePattern>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Map<Vector2i, TileMapEditorTerrainsPlugin::TerrainsTilePattern>();
+ }
+
+ // Copy the constraints set.
+ Set<TileMapEditorTerrainsPlugin::Constraint> constraints = p_constraints;
+
+ // Compute all acceptable tiles for each cell.
+ Map<Vector2i, Set<TerrainsTilePattern>> per_cell_acceptable_tiles;
+ for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
+ per_cell_acceptable_tiles[E->get()] = _get_valid_terrains_tile_patterns_for_constraints(p_terrain_set, E->get(), constraints);
+ }
+
+ // Ouput map.
+ Map<Vector2i, TerrainsTilePattern> output;
+
+ // Add all positions to a set.
+ Set<Vector2i> to_replace = Set<Vector2i>(p_to_replace);
+ while (!to_replace.is_empty()) {
+ // Compute the minimum number of tile possibilities for each cell.
+ int min_nb_possibilities = 100000000;
+ for (Map<Vector2i, Set<TerrainsTilePattern>>::Element *E = per_cell_acceptable_tiles.front(); E; E = E->next()) {
+ min_nb_possibilities = MIN(min_nb_possibilities, E->get().size());
+ }
+
+ // Get the set of possible cells to fill.
+ LocalVector<Vector2i> to_choose_from;
+ for (Map<Vector2i, Set<TerrainsTilePattern>>::Element *E = per_cell_acceptable_tiles.front(); E; E = E->next()) {
+ if (E->get().size() == min_nb_possibilities) {
+ to_choose_from.push_back(E->key());
+ }
+ }
+
+ // Randomly pick a tile out of the most constrained.
+ Vector2i selected_cell_to_replace = to_choose_from[Math::random(0, to_choose_from.size() - 1)];
+
+ // Randomly select a tile out of them the put it in the grid.
+ Set<TerrainsTilePattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace];
+ if (valid_tiles.is_empty()) {
+ // No possibilities :/
+ break;
+ }
+ int random_terrain_tile_pattern_index = Math::random(0, valid_tiles.size() - 1);
+ Set<TerrainsTilePattern>::Element *E = valid_tiles.front();
+ for (int i = 0; i < random_terrain_tile_pattern_index; i++) {
+ E = E->next();
+ }
+ TerrainsTilePattern selected_terrain_tile_pattern = E->get();
+
+ // Set the selected cell into the output.
+ output[selected_cell_to_replace] = selected_terrain_tile_pattern;
+ to_replace.erase(selected_cell_to_replace);
+ per_cell_acceptable_tiles.erase(selected_cell_to_replace);
+
+ // Add the new constraints from the added tiles.
+ Set<TileMapEditorTerrainsPlugin::Constraint> new_constraints = _get_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern);
+ for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E_constraint = new_constraints.front(); E_constraint; E_constraint = E_constraint->next()) {
+ constraints.insert(E_constraint->get());
+ }
+
+ // Compute valid tiles again for neighbors.
+ for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
+ if (tile_map->is_existing_neighbor(side)) {
+ Vector2i neighbor = tile_map->get_neighbor_cell(selected_cell_to_replace, side);
+ if (to_replace.has(neighbor)) {
+ per_cell_acceptable_tiles[neighbor] = _get_valid_terrains_tile_patterns_for_constraints(p_terrain_set, neighbor, constraints);
+ }
+ }
+ }
+ }
+ return output;
+}
+
+TileMapCell TileMapEditorTerrainsPlugin::_get_random_tile_from_pattern(int p_terrain_set, TerrainsTilePattern p_terrain_tile_pattern) const {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return TileMapCell();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return TileMapCell();
+ }
+
+ // Count the sum of probabilities.
+ double sum = 0.0;
+ Set<TileMapCell> set = per_terrain_terrains_tile_patterns_tiles[p_terrain_set][p_terrain_tile_pattern];
+ for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
+ if (E->get().source_id >= 0) {
+ Ref<TileSetSource> source = tile_set->get_source(E->get().source_id);
+
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ sum += tile_data->get_probability();
+ } else {
+ sum += 1.0;
+ }
+ } else {
+ sum += 1.0;
+ }
+ }
+
+ // Generate a random number.
+ double count = 0.0;
+ double picked = Math::random(0.0, sum);
+
+ // Pick the tile.
+ for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
+ if (E->get().source_id >= 0) {
+ Ref<TileSetSource> source = tile_set->get_source(E->get().source_id);
+
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ count += tile_data->get_probability();
+ } else {
+ count += 1.0;
+ }
+ } else {
+ count += 1.0;
+ }
+
+ if (count >= picked) {
+ return E->get();
+ }
+ }
+
+ ERR_FAIL_V(TileMapCell());
+}
+
+Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map<Vector2i, TerrainsTilePattern> &p_to_paint, int p_terrain_set) const {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Map<Vector2i, TileMapCell>();
+ }
+
+ Map<Vector2i, TileMapCell> output;
+
+ // Add the constraints from the added tiles.
+ Set<TileMapEditorTerrainsPlugin::Constraint> added_tiles_constraints_set;
+ for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
+ Vector2i coords = E_to_paint->key();
+ TerrainsTilePattern terrains_tile_pattern = E_to_paint->get();
+
+ Set<TileMapEditorTerrainsPlugin::Constraint> cell_constraints = _get_constraints_from_added_tile(coords, p_terrain_set, terrains_tile_pattern);
+ for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = cell_constraints.front(); E; E = E->next()) {
+ added_tiles_constraints_set.insert(E->get());
+ }
+ }
+
+ // Build the list of potential tiles to replace.
+ Set<Vector2i> potential_to_replace;
+ for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
+ Vector2i coords = E_to_paint->key();
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (tile_map->is_existing_neighbor(TileSet::CellNeighbor(i))) {
+ Vector2i neighbor = tile_map->get_neighbor_cell(coords, TileSet::CellNeighbor(i));
+ if (!p_to_paint.has(neighbor)) {
+ potential_to_replace.insert(neighbor);
+ }
+ }
+ }
+ }
+
+ // Set of tiles to replace
+ Set<Vector2i> to_replace;
+
+ // Add the central tiles to the one to replace. TODO: maybe change that.
+ for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
+ to_replace.insert(E_to_paint->key());
+ }
+
+ // Add the constraints from the surroundings of the modified areas.
+ Set<TileMapEditorTerrainsPlugin::Constraint> removed_cells_constraints_set;
+ bool to_replace_modified = true;
+ while (to_replace_modified) {
+ // Get the constraints from the removed cells.
+ removed_cells_constraints_set = _get_constraints_from_removed_cells_list(to_replace, p_terrain_set);
+
+ // Filter the sources to make sure they are in the potential_to_replace.
+ Map<Constraint, Set<Vector2i>> source_tiles_of_constraint;
+ for (Set<Constraint>::Element *E = removed_cells_constraints_set.front(); E; E = E->next()) {
+ Map<Vector2i, TileSet::CellNeighbor> sources_of_constraint = E->get().get_overlapping_coords_and_peering_bits();
+ for (Map<Vector2i, TileSet::CellNeighbor>::Element *E_source_tile_of_constraint = sources_of_constraint.front(); E_source_tile_of_constraint; E_source_tile_of_constraint = E_source_tile_of_constraint->next()) {
+ if (potential_to_replace.has(E_source_tile_of_constraint->key())) {
+ source_tiles_of_constraint[E->get()].insert(E_source_tile_of_constraint->key());
+ }
+ }
+ }
+
+ to_replace_modified = false;
+ for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
+ Constraint c = E->get();
+ // Check if we have a conflict in constraints.
+ if (removed_cells_constraints_set.has(c) && removed_cells_constraints_set.find(c)->get().get_terrain() != c.get_terrain()) {
+ // If we do, we search for a neighbor to remove.
+ if (source_tiles_of_constraint.has(c) && !source_tiles_of_constraint[c].is_empty()) {
+ // Remove it.
+ Vector2i to_add_to_remove = source_tiles_of_constraint[c].front()->get();
+ potential_to_replace.erase(to_add_to_remove);
+ to_replace.insert(to_add_to_remove);
+ to_replace_modified = true;
+ for (Map<Constraint, Set<Vector2i>>::Element *E_source_tiles_of_constraint = source_tiles_of_constraint.front(); E_source_tiles_of_constraint; E_source_tiles_of_constraint = E_source_tiles_of_constraint->next()) {
+ E_source_tiles_of_constraint->get().erase(to_add_to_remove);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Combine all constraints together.
+ Set<TileMapEditorTerrainsPlugin::Constraint> constraints = removed_cells_constraints_set;
+ for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
+ constraints.insert(E->get());
+ }
+
+ // Run WFC to fill the holes with the constraints.
+ Map<Vector2i, TerrainsTilePattern> wfc_output = _wave_function_collapse(to_replace, p_terrain_set, constraints);
+
+ // Use the WFC run for the output.
+ for (Map<Vector2i, TerrainsTilePattern>::Element *E = wfc_output.front(); E; E = E->next()) {
+ output[E->key()] = _get_random_tile_from_pattern(p_terrain_set, E->get());
+ }
+
+ // Override the WFC results to make sure at least the painted tiles are acutally painted.
+ for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
+ output[E_to_paint->key()] = _get_random_tile_from_pattern(p_terrain_set, E_to_paint->get());
+ }
+
+ return output;
+}
+
+bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
+ if (!is_visible_in_tree()) {
+ // If the bottom editor is not visible, we ignore inputs.
+ return false;
+ }
+
+ if (CanvasItemEditor::get_singleton()->get_current_tool() != CanvasItemEditor::TOOL_SELECT) {
+ return false;
+ }
+
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return false;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return false;
+ }
+
+ // Get the selected terrain.
+ TerrainsTilePattern selected_terrains_tile_pattern;
+ int selected_terrain_set = -1;
+
+ TreeItem *selected_tree_item = terrains_tree->get_selected();
+ if (selected_tree_item && selected_tree_item->get_metadata(0)) {
+ Dictionary metadata_dict = selected_tree_item->get_metadata(0);
+ // Selected terrain
+ selected_terrain_set = metadata_dict["terrain_set"];
+
+ // Selected tile
+ if (erase_button->is_pressed()) {
+ selected_terrains_tile_pattern.clear();
+ for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(selected_terrain_set, side)) {
+ selected_terrains_tile_pattern.push_back(-1);
+ }
+ }
+ } else if (terrains_tile_list->is_anything_selected()) {
+ metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
+ selected_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"];
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
+ Vector2 mpos = xform.affine_inverse().xform(mm->get_position());
+
+ switch (drag_type) {
+ case DRAG_TYPE_PAINT: {
+ if (selected_terrain_set >= 0) {
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
+ Map<Vector2i, TerrainsTilePattern> to_draw;
+ for (int i = 0; i < line.size(); i++) {
+ to_draw[line[i]] = selected_terrains_tile_pattern;
+ }
+ Map<Vector2i, TileMapCell> modified = _draw_terrains(to_draw, selected_terrain_set);
+ for (Map<Vector2i, TileMapCell>::Element *E = modified.front(); E; E = E->next()) {
+ if (!drag_modified.has(E->key())) {
+ drag_modified[E->key()] = tile_map->get_cell(E->key());
+ }
+ tile_map->set_cell(E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ drag_last_mouse_pos = mpos;
+ CanvasItemEditor::get_singleton()->update_viewport();
+
+ return true;
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid()) {
+ Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
+ Vector2 mpos = xform.affine_inverse().xform(mb->get_position());
+
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->is_pressed()) {
+ // Pressed
+ if (picker_button->is_pressed()) {
+ drag_type = DRAG_TYPE_PICK;
+ } else {
+ // Paint otherwise.
+ if (selected_terrain_set >= 0 && !selected_terrains_tile_pattern.is_empty() && tool_buttons_group->get_pressed_button() == paint_tool_button) {
+ drag_type = DRAG_TYPE_PAINT;
+ drag_start_mouse_pos = mpos;
+
+ drag_modified.clear();
+
+ Map<Vector2i, TerrainsTilePattern> terrains_to_draw;
+ terrains_to_draw[tile_map->world_to_map(mpos)] = selected_terrains_tile_pattern;
+
+ Map<Vector2i, TileMapCell> to_draw = _draw_terrains(terrains_to_draw, selected_terrain_set);
+ for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
+ drag_modified[E->key()] = tile_map->get_cell(E->key());
+ tile_map->set_cell(E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ }
+ }
+ } else {
+ // Released
+ switch (drag_type) {
+ case DRAG_TYPE_PICK: {
+ Vector2i coords = tile_map->world_to_map(mpos);
+ TileMapCell tile = tile_map->get_cell(coords);
+
+ if (terrain_tiles.has(tile)) {
+ Array terrains_tile_pattern = _build_terrains_tile_pattern(terrain_tiles[tile]);
+
+ // Find the tree item for the right terrain set.
+ bool need_tree_item_switch = true;
+ TreeItem *tree_item = terrains_tree->get_selected();
+ if (tree_item) {
+ Dictionary metadata_dict = tree_item->get_metadata(0);
+ if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
+ int terrain_set = metadata_dict["terrain_set"];
+ int terrain_id = metadata_dict["terrain_id"];
+ if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
+ need_tree_item_switch = false;
+ }
+ }
+ }
+
+ if (need_tree_item_switch) {
+ for (tree_item = terrains_tree->get_root()->get_children(); tree_item; tree_item = tree_item->get_next_visible()) {
+ Dictionary metadata_dict = tree_item->get_metadata(0);
+ if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
+ int terrain_set = metadata_dict["terrain_set"];
+ int terrain_id = metadata_dict["terrain_id"];
+ if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
+ // Found
+ tree_item->select(0);
+ _update_tiles_list();
+ break;
+ }
+ }
+ }
+ }
+
+ // Find the list item for the given tile.
+ if (tree_item) {
+ for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
+ Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
+ TerrainsTilePattern in_meta_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"];
+ bool equals = true;
+ for (int j = 0; j < terrains_tile_pattern.size(); j++) {
+ if (terrains_tile_pattern[j] != in_meta_terrains_tile_pattern[j]) {
+ equals = false;
+ break;
+ }
+ }
+ if (equals) {
+ terrains_tile_list->select(i);
+ break;
+ }
+ }
+ } else {
+ ERR_PRINT("Terrain tile not found.");
+ }
+ }
+ picker_button->set_pressed(false);
+ } break;
+ case DRAG_TYPE_PAINT: {
+ undo_redo->create_action(TTR("Paint terrain"));
+ for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
+ undo_redo->add_do_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key()));
+ undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ undo_redo->commit_action(false);
+ } break;
+ default:
+ break;
+ }
+ drag_type = DRAG_TYPE_NONE;
+ }
+
+ CanvasItemEditor::get_singleton()->update_viewport();
+
+ return true;
+ }
+ drag_last_mouse_pos = mpos;
+ }
+
+ return false;
+}
+
+TileMapEditorTerrainsPlugin::TerrainsTilePattern TileMapEditorTerrainsPlugin::_build_terrains_tile_pattern(TileData *p_tile_data) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return TerrainsTilePattern();
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return TerrainsTilePattern();
+ }
+
+ TerrainsTilePattern output;
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (tile_set->is_valid_peering_bit_terrain(p_tile_data->get_terrain_set(), TileSet::CellNeighbor(i))) {
+ output.push_back(p_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(i)));
+ }
+ }
+ return output;
+}
+
+void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ // Compute the tile sides.
+ tile_sides.clear();
+ TileSet::TileShape shape = tile_set->get_tile_shape();
+ if (shape == TileSet::TILE_SHAPE_SQUARE) {
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_RIGHT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ } else {
+ if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_RIGHT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ } else {
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ }
+ }
+
+ // Organizes tiles into structures.
+ per_terrain_terrains_tile_patterns_tiles.resize(tile_set->get_terrain_sets_count());
+ per_terrain_terrains_tile_patterns.resize(tile_set->get_terrain_sets_count());
+ for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) {
+ per_terrain_terrains_tile_patterns_tiles[i].clear();
+ per_terrain_terrains_tile_patterns[i].resize(tile_set->get_terrains_count(i));
+ for (int j = 0; j < (int)per_terrain_terrains_tile_patterns[i].size(); j++) {
+ per_terrain_terrains_tile_patterns[i][j].clear();
+ }
+ }
+
+ for (int source_index = 0; source_index < tile_set->get_source_count(); source_index++) {
+ int source_id = tile_set->get_source_id(source_index);
+ Ref<TileSetSource> source = tile_set->get_source(source_id);
+
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ for (int tile_index = 0; tile_index < source->get_tiles_count(); tile_index++) {
+ Vector2i tile_id = source->get_tile_id(tile_index);
+ for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) {
+ int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
+
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
+ int terrain_set = tile_data->get_terrain_set();
+ if (terrain_set >= 0) {
+ ERR_FAIL_INDEX(terrain_set, (int)per_terrain_terrains_tile_patterns.size());
+
+ TileMapCell cell;
+ cell.source_id = source_id;
+ cell.set_atlas_coords(tile_id);
+ cell.alternative_tile = alternative_id;
+
+ TerrainsTilePattern terrains_tile_pattern = _build_terrains_tile_pattern(tile_data);
+
+ // Terrain bits.
+ for (int i = 0; i < terrains_tile_pattern.size(); i++) {
+ int terrain = terrains_tile_pattern[i];
+ if (terrain >= 0 && terrain < (int)per_terrain_terrains_tile_patterns[terrain_set].size()) {
+ per_terrain_terrains_tile_patterns[terrain_set][terrain].insert(terrains_tile_pattern);
+ terrain_tiles[cell] = tile_data;
+ per_terrain_terrains_tile_patterns_tiles[terrain_set][terrains_tile_pattern].insert(cell);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Add the empty cell in the possible patterns and cells.
+ for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) {
+ TerrainsTilePattern empty_pattern;
+ for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
+ if (tile_set->is_valid_peering_bit_terrain(i, TileSet::CellNeighbor(j))) {
+ empty_pattern.push_back(-1);
+ }
+ }
+
+ TileMapCell empty_cell;
+ empty_cell.source_id = -1;
+ empty_cell.set_atlas_coords(TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ empty_cell.alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ per_terrain_terrains_tile_patterns_tiles[i][empty_pattern].insert(empty_cell);
+ }
+}
+
+void TileMapEditorTerrainsPlugin::_update_terrains_tree() {
+ terrains_tree->clear();
+ terrains_tree->create_item();
+
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ // Fill in the terrain list.
+ for (int terrain_set_index = 0; terrain_set_index < tile_set->get_terrain_sets_count(); terrain_set_index++) {
+ // Add an item for the terrain set.
+ TreeItem *terrain_set_tree_item = terrains_tree->create_item();
+ String matches;
+ if (tile_set->get_terrain_set_mode(terrain_set_index) == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
+ terrain_set_tree_item->set_icon(0, get_theme_icon("TerrainMatchCornersAndSides", "EditorIcons"));
+ matches = String(TTR("Matches corners and sides"));
+ } else if (tile_set->get_terrain_set_mode(terrain_set_index) == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ terrain_set_tree_item->set_icon(0, get_theme_icon("TerrainMatchCorners", "EditorIcons"));
+ matches = String(TTR("Matches corners only"));
+ } else {
+ terrain_set_tree_item->set_icon(0, get_theme_icon("TerrainMatchSides", "EditorIcons"));
+ matches = String(TTR("Matches sides only"));
+ }
+ terrain_set_tree_item->set_text(0, vformat("Terrain set %d (%s)", terrain_set_index, matches));
+ terrain_set_tree_item->set_selectable(0, false);
+
+ for (int terrain_index = 0; terrain_index < tile_set->get_terrains_count(terrain_set_index); terrain_index++) {
+ // Compute the terrains_tile_pattern used for terrain preview (whenever possible).
+ TerrainsTilePattern terrains_tile_pattern;
+ int max_bit_count = -1;
+ for (Set<TerrainsTilePattern>::Element *E = per_terrain_terrains_tile_patterns[terrain_set_index][terrain_index].front(); E; E = E->next()) {
+ int count = 0;
+ for (int i = 0; i < E->get().size(); i++) {
+ if (int(E->get()[i]) == terrain_index) {
+ count++;
+ }
+ }
+ if (count > max_bit_count) {
+ terrains_tile_pattern = E->get();
+ max_bit_count = count;
+ }
+ }
+
+ // Get the preview.
+ Ref<Texture2D> icon;
+ Rect2 region;
+ if (max_bit_count >= 0) {
+ double max_probability = -1.0;
+ for (Set<TileMapCell>::Element *E = per_terrain_terrains_tile_patterns_tiles[terrain_set_index][terrains_tile_pattern].front(); E; E = E->next()) {
+ Ref<TileSetSource> source = tile_set->get_source(E->get().source_id);
+
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ if (tile_data->get_probability() > max_probability) {
+ icon = atlas_source->get_texture();
+ region = atlas_source->get_tile_texture_region(E->get().get_atlas_coords());
+ max_probability = tile_data->get_probability();
+ }
+ }
+ }
+ } else {
+ Ref<Image> image;
+ image.instance();
+ image->create(1, 1, false, Image::FORMAT_RGBA8);
+ image->set_pixel(0, 0, tile_set->get_terrain_color(terrain_set_index, terrain_index));
+ Ref<ImageTexture> image_texture;
+ image_texture.instance();
+ image_texture->create_from_image(image);
+ image_texture->set_size_override(Size2(32, 32) * EDSCALE);
+ icon = image_texture;
+ }
+
+ // Add the item to the terrain list.
+ TreeItem *terrain_tree_item = terrains_tree->create_item(terrain_set_tree_item);
+ terrain_tree_item->set_text(0, tile_set->get_terrain_name(terrain_set_index, terrain_index));
+ terrain_tree_item->set_icon_max_width(0, 32 * EDSCALE);
+ terrain_tree_item->set_icon(0, icon);
+ terrain_tree_item->set_icon_region(0, region);
+ Dictionary metadata_dict;
+ metadata_dict["terrain_set"] = terrain_set_index;
+ metadata_dict["terrain_id"] = terrain_index;
+ terrain_tree_item->set_metadata(0, metadata_dict);
+ }
+ }
+}
+
+void TileMapEditorTerrainsPlugin::_update_tiles_list() {
+ terrains_tile_list->clear();
+
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ TreeItem *selected_tree_item = terrains_tree->get_selected();
+ if (selected_tree_item && selected_tree_item->get_metadata(0)) {
+ Dictionary metadata_dict = selected_tree_item->get_metadata(0);
+ int selected_terrain_set = metadata_dict["terrain_set"];
+ int selected_terrain_id = metadata_dict["terrain_id"];
+ ERR_FAIL_INDEX(selected_terrain_set, (int)per_terrain_terrains_tile_patterns.size());
+ ERR_FAIL_INDEX(selected_terrain_id, (int)per_terrain_terrains_tile_patterns[selected_terrain_set].size());
+
+ // Sort the items in a map by the number of corresponding terrains.
+ Map<int, Set<TerrainsTilePattern>> sorted;
+ for (Set<TerrainsTilePattern>::Element *E = per_terrain_terrains_tile_patterns[selected_terrain_set][selected_terrain_id].front(); E; E = E->next()) {
+ // Count the number of matching sides/terrains.
+ int count = 0;
+
+ for (int i = 0; i < E->get().size(); i++) {
+ if (int(E->get()[i]) == selected_terrain_id) {
+ count++;
+ }
+ }
+ sorted[count].insert(E->get());
+ }
+
+ for (Map<int, Set<TerrainsTilePattern>>::Element *E_set = sorted.back(); E_set; E_set = E_set->prev()) {
+ for (Set<TerrainsTilePattern>::Element *E = E_set->get().front(); E; E = E->next()) {
+ TerrainsTilePattern terrains_tile_pattern = E->get();
+
+ // Get the icon.
+ Ref<Texture2D> icon;
+ Rect2 region;
+ bool transpose = false;
+
+ double max_probability = -1.0;
+ for (Set<TileMapCell>::Element *E_tile_map_cell = per_terrain_terrains_tile_patterns_tiles[selected_terrain_set][terrains_tile_pattern].front(); E_tile_map_cell; E_tile_map_cell = E_tile_map_cell->next()) {
+ Ref<TileSetSource> source = tile_set->get_source(E_tile_map_cell->get().source_id);
+
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E_tile_map_cell->get().get_atlas_coords(), E_tile_map_cell->get().alternative_tile));
+ if (tile_data->get_probability() > max_probability) {
+ icon = atlas_source->get_texture();
+ region = atlas_source->get_tile_texture_region(E_tile_map_cell->get().get_atlas_coords());
+ if (tile_data->get_flip_h()) {
+ region.position.x += region.size.x;
+ region.size.x = -region.size.x;
+ }
+ if (tile_data->get_flip_v()) {
+ region.position.y += region.size.y;
+ region.size.y = -region.size.y;
+ }
+ transpose = tile_data->get_transpose();
+ max_probability = tile_data->get_probability();
+ }
+ }
+ }
+
+ // Create the ItemList's item.
+ int item_index = terrains_tile_list->add_item("");
+ terrains_tile_list->set_item_icon(item_index, icon);
+ terrains_tile_list->set_item_icon_region(item_index, region);
+ terrains_tile_list->set_item_icon_transposed(item_index, transpose);
+ Dictionary list_metadata_dict;
+ list_metadata_dict["terrains_tile_pattern"] = terrains_tile_pattern;
+ terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
+ }
+ }
+ if (terrains_tile_list->get_item_count() > 0) {
+ terrains_tile_list->select(0);
+ }
+ }
+}
+
+void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id) {
+ tile_map_id = p_tile_map_id;
+ _update_terrains_cache();
+ _update_terrains_tree();
+ _update_tiles_list();
+}
+
+TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
+ set_name("Terrains");
+
+ HSplitContainer *tilemap_tab_terrains = memnew(HSplitContainer);
+ tilemap_tab_terrains->set_h_size_flags(SIZE_EXPAND_FILL);
+ tilemap_tab_terrains->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(tilemap_tab_terrains);
+
+ terrains_tree = memnew(Tree);
+ terrains_tree->set_h_size_flags(SIZE_EXPAND_FILL);
+ terrains_tree->set_stretch_ratio(0.25);
+ terrains_tree->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
+ terrains_tree->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
+ terrains_tree->set_hide_root(true);
+ terrains_tree->connect("item_selected", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_tiles_list));
+ tilemap_tab_terrains->add_child(terrains_tree);
+
+ terrains_tile_list = memnew(ItemList);
+ terrains_tile_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ terrains_tile_list->set_max_columns(0);
+ terrains_tile_list->set_same_column_width(true);
+ terrains_tile_list->set_fixed_icon_size(Size2(30, 30) * EDSCALE);
+ terrains_tile_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
+ tilemap_tab_terrains->add_child(terrains_tile_list);
+
+ // --- Toolbar ---
+ toolbar = memnew(HBoxContainer);
+
+ HBoxContainer *tilemap_tiles_tools_buttons = memnew(HBoxContainer);
+
+ tool_buttons_group.instance();
+
+ paint_tool_button = memnew(Button);
+ paint_tool_button->set_flat(true);
+ paint_tool_button->set_toggle_mode(true);
+ paint_tool_button->set_button_group(tool_buttons_group);
+ paint_tool_button->set_pressed(true);
+ paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", "Paint", KEY_E));
+ paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(paint_tool_button);
+
+ toolbar->add_child(tilemap_tiles_tools_buttons);
+
+ // -- TileMap tool settings --
+ tools_settings = memnew(HBoxContainer);
+ toolbar->add_child(tools_settings);
+
+ tools_settings_vsep = memnew(VSeparator);
+ tools_settings->add_child(tools_settings_vsep);
+
+ // Picker
+ picker_button = memnew(Button);
+ picker_button->set_flat(true);
+ picker_button->set_toggle_mode(true);
+ picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", KEY_P));
+ picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
+ tools_settings->add_child(picker_button);
+
+ // Erase button.
+ erase_button = memnew(Button);
+ erase_button->set_flat(true);
+ erase_button->set_toggle_mode(true);
+ erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", KEY_E));
+ erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
+ tools_settings->add_child(erase_button);
+}
+
+TileMapEditorTerrainsPlugin::~TileMapEditorTerrainsPlugin() {
+}
+
+void TileMapEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED:
+ missing_tile_texture = get_theme_icon("StatusWarning", "EditorIcons");
+ warning_pattern_texture = get_theme_icon("WarningPattern", "EditorIcons");
+ break;
+ case NOTIFICATION_INTERNAL_PROCESS:
+ if (is_visible_in_tree() && tileset_changed_needs_update) {
+ _update_bottom_panel();
+ tile_map_editor_plugins[tabs->get_current_tab()]->tile_set_changed();
+ CanvasItemEditor::get_singleton()->update_viewport();
+ tileset_changed_needs_update = false;
+ }
+ break;
+ }
+}
+
+void TileMapEditor::_update_bottom_panel() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+
+ // Update the visibility of controls.
+ missing_tileset_label->set_visible(!tile_set.is_valid());
+ if (!tile_set.is_valid()) {
+ for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
+ tile_map_editor_plugins[i]->hide();
+ }
+ } else {
+ for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
+ tile_map_editor_plugins[i]->set_visible(i == tabs->get_current_tab());
+ }
+ }
+}
+
+Vector<Vector2i> TileMapEditor::get_line(TileMap *p_tile_map, Vector2i p_from_cell, Vector2i p_to_cell) {
+ ERR_FAIL_COND_V(!p_tile_map, Vector<Vector2i>());
+
+ Ref<TileSet> tile_set = p_tile_map->get_tileset();
+ ERR_FAIL_COND_V(!tile_set.is_valid(), Vector<Vector2i>());
+
+ if (tile_set->get_tile_shape() == TileSet::TILE_SHAPE_SQUARE) {
+ return Geometry2D::bresenham_line(p_from_cell, p_to_cell);
+ } else {
+ // Adapt the bresenham line algorithm to half-offset shapes.
+ // See this blog post: http://zvold.blogspot.com/2010/01/bresenhams-line-drawing-algorithm-on_26.html
+ Vector<Point2i> points;
+
+ bool transposed = tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL;
+ p_from_cell = TileMap::transform_coords_layout(p_from_cell, tile_set->get_tile_offset_axis(), tile_set->get_tile_layout(), TileSet::TILE_LAYOUT_STACKED);
+ p_to_cell = TileMap::transform_coords_layout(p_to_cell, tile_set->get_tile_offset_axis(), tile_set->get_tile_layout(), TileSet::TILE_LAYOUT_STACKED);
+ if (transposed) {
+ SWAP(p_from_cell.x, p_from_cell.y);
+ SWAP(p_to_cell.x, p_to_cell.y);
+ }
+
+ Vector2i delta = p_to_cell - p_from_cell;
+ delta = Vector2i(2 * delta.x + ABS(p_to_cell.y % 2) - ABS(p_from_cell.y % 2), delta.y);
+ Vector2i sign = delta.sign();
+
+ Vector2i current = p_from_cell;
+ points.push_back(TileMap::transform_coords_layout(transposed ? Vector2i(current.y, current.x) : current, tile_set->get_tile_offset_axis(), TileSet::TILE_LAYOUT_STACKED, tile_set->get_tile_layout()));
+
+ int err = 0;
+ if (ABS(delta.y) < ABS(delta.x)) {
+ Vector2i err_step = 3 * delta.abs();
+ while (current != p_to_cell) {
+ err += err_step.y;
+ if (err > ABS(delta.x)) {
+ if (sign.x == 0) {
+ current += Vector2(sign.y, 0);
+ } else {
+ current += Vector2(bool(current.y % 2) ^ (sign.x < 0) ? sign.x : 0, sign.y);
+ }
+ err -= err_step.x;
+ } else {
+ current += Vector2i(sign.x, 0);
+ err += err_step.y;
+ }
+ points.push_back(TileMap::transform_coords_layout(transposed ? Vector2i(current.y, current.x) : current, tile_set->get_tile_offset_axis(), TileSet::TILE_LAYOUT_STACKED, tile_set->get_tile_layout()));
+ }
+ } else {
+ Vector2i err_step = delta.abs();
+ while (current != p_to_cell) {
+ err += err_step.x;
+ if (err > 0) {
+ if (sign.x == 0) {
+ current += Vector2(0, sign.y);
+ } else {
+ current += Vector2(bool(current.y % 2) ^ (sign.x < 0) ? sign.x : 0, sign.y);
+ }
+ err -= err_step.y;
+ } else {
+ if (sign.x == 0) {
+ current += Vector2(0, sign.y);
+ } else {
+ current += Vector2(bool(current.y % 2) ^ (sign.x > 0) ? -sign.x : 0, sign.y);
+ }
+ err += err_step.y;
+ }
+ points.push_back(TileMap::transform_coords_layout(transposed ? Vector2i(current.y, current.x) : current, tile_set->get_tile_offset_axis(), TileSet::TILE_LAYOUT_STACKED, tile_set->get_tile_layout()));
+ }
+ }
+
+ return points;
+ }
+}
+
+void TileMapEditor::_tile_map_changed() {
+ tileset_changed_needs_update = true;
+}
+
+void TileMapEditor::_tab_changed(int p_tab_id) {
+ // Make the plugin edit the correct tilemap.
+ tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id);
+
+ // Update toolbar.
+ for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
+ tile_map_editor_plugins[i]->get_toolbar()->set_visible(i == p_tab_id);
+ }
+
+ // Update visible panel.
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map || !tile_map->get_tileset().is_valid()) {
+ for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
+ tile_map_editor_plugins[i]->hide();
+ }
+ } else {
+ for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
+ tile_map_editor_plugins[i]->set_visible(i == tabs->get_current_tab());
+ }
+ }
+
+ // Graphical update.
+ tile_map_editor_plugins[tabs->get_current_tab()]->update();
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
+bool TileMapEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
+ return tile_map_editor_plugins[tabs->get_current_tab()]->forward_canvas_gui_input(p_event);
+}
+
+void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ if (!tile_map->is_visible_in_tree()) {
+ return;
+ }
+
+ Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
+ Transform2D xform_inv = xform.affine_inverse();
+ Vector2i tile_shape_size = tile_set->get_tile_size();
+
+ // Draw tiles with invalid IDs in the grid.
+ float icon_ratio = MIN(missing_tile_texture->get_size().x / tile_set->get_tile_size().x, missing_tile_texture->get_size().y / tile_set->get_tile_size().y) / 3;
+ TypedArray<Vector2i> used_cells = tile_map->get_used_cells();
+ for (int i = 0; i < used_cells.size(); i++) {
+ Vector2i coords = used_cells[i];
+ int tile_source_id = tile_map->get_cell_source_id(coords);
+ if (tile_source_id >= 0) {
+ Vector2i tile_atlas_coords = tile_map->get_cell_atlas_coords(coords);
+ int tile_alternative_tile = tile_map->get_cell_alternative_tile(coords);
+
+ TileSetSource *source = nullptr;
+ if (tile_set->has_source(tile_source_id)) {
+ source = *tile_set->get_source(tile_source_id);
+ }
+
+ if (!source || !source->has_tile(tile_atlas_coords) || !source->has_alternative_tile(tile_atlas_coords, tile_alternative_tile)) {
+ // Generate a random color from the hashed values of the tiles.
+ Array to_hash;
+ to_hash.push_back(tile_source_id);
+ to_hash.push_back(tile_atlas_coords);
+ to_hash.push_back(tile_alternative_tile);
+ uint32_t hash = RandomPCG(to_hash.hash()).rand();
+
+ Color color;
+ color = color.from_hsv(
+ (float)((hash >> 24) & 0xFF) / 256.0,
+ Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0),
+ Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0),
+ 0.8);
+
+ // Draw the scaled tile.
+ Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - Vector2(tile_shape_size) / 2, Vector2(tile_shape_size)));
+ tile_set->draw_tile_shape(p_overlay, cell_region, color, true, warning_pattern_texture);
+
+ // Draw the warning icon.
+ Rect2 rect = Rect2(xform.xform(tile_map->map_to_world(coords)) - (icon_ratio * missing_tile_texture->get_size() * xform.get_scale() / 2), icon_ratio * missing_tile_texture->get_size() * xform.get_scale());
+ p_overlay->draw_texture_rect(missing_tile_texture, rect);
+ }
+ }
+ }
+
+ // Fading on the border.
+ const int fading = 5;
+
+ // Determine the drawn area.
+ Size2 screen_size = p_overlay->get_size();
+ Rect2i screen_rect;
+ screen_rect.position = tile_map->world_to_map(xform_inv.xform(Vector2()));
+ screen_rect.expand_to(tile_map->world_to_map(xform_inv.xform(Vector2(0, screen_size.height))));
+ screen_rect.expand_to(tile_map->world_to_map(xform_inv.xform(Vector2(screen_size.width, 0))));
+ screen_rect.expand_to(tile_map->world_to_map(xform_inv.xform(screen_size)));
+ screen_rect = screen_rect.grow(1);
+
+ Rect2i tilemap_used_rect = tile_map->get_used_rect();
+
+ Rect2i displayed_rect = tilemap_used_rect.intersection(screen_rect);
+ displayed_rect = displayed_rect.grow(fading);
+
+ // Reduce the drawn area to avoid crashes if needed.
+ int max_size = 100;
+ if (displayed_rect.size.x > max_size) {
+ displayed_rect = displayed_rect.grow_individual(-(displayed_rect.size.x - max_size) / 2, 0, -(displayed_rect.size.x - max_size) / 2, 0);
+ }
+ if (displayed_rect.size.y > max_size) {
+ displayed_rect = displayed_rect.grow_individual(0, -(displayed_rect.size.y - max_size) / 2, 0, -(displayed_rect.size.y - max_size) / 2);
+ }
+
+ // Draw the grid.
+ for (int x = displayed_rect.position.x; x < (displayed_rect.position.x + displayed_rect.size.x); x++) {
+ for (int y = displayed_rect.position.y; y < (displayed_rect.position.y + displayed_rect.size.y); y++) {
+ Vector2i pos_in_rect = Vector2i(x, y) - displayed_rect.position;
+
+ // Fade out the border of the grid.
+ float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
+ float right_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.x, (float)(displayed_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f);
+ float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
+ float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
+ float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
+
+ Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size));
+ tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 0.5, 0.2, 0.5 * opacity), false);
+ }
+ }
+
+ // Draw the IDs for debug.
+ /*Ref<Font> font = get_theme_font("font", "Label");
+ for (int x = displayed_rect.position.x; x < (displayed_rect.position.x + displayed_rect.size.x); x++) {
+ for (int y = displayed_rect.position.y; y < (displayed_rect.position.y + displayed_rect.size.y); y++) {
+ p_overlay->draw_string(font, xform.xform(tile_map->map_to_world(Vector2(x, y))) + Vector2i(-tile_shape_size.x / 2, 0), vformat("%s", Vector2(x, y)));
+ }
+ }*/
+
+ // Draw the plugins.
+ tile_map_editor_plugins[tabs->get_current_tab()]->forward_canvas_draw_over_viewport(p_overlay);
+}
+
+void TileMapEditor::edit(TileMap *p_tile_map) {
+ if (p_tile_map && p_tile_map->get_instance_id() == tile_map_id) {
+ return;
+ }
+
+ // Disconnect to changes.
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (tile_map) {
+ tile_map->disconnect("changed", callable_mp(this, &TileMapEditor::_tile_map_changed));
+ }
+
+ // Change the edited object.
+ if (p_tile_map) {
+ tile_map_id = p_tile_map->get_instance_id();
+ tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ // Connect to changes.
+ if (!tile_map->is_connected("changed", callable_mp(this, &TileMapEditor::_tile_map_changed))) {
+ tile_map->connect("changed", callable_mp(this, &TileMapEditor::_tile_map_changed));
+ }
+ } else {
+ tile_map_id = ObjectID();
+ }
+
+ // Call the plugins.
+ tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id);
+
+ _tile_map_changed();
+}
+
+TileMapEditor::TileMapEditor() {
+ set_process_internal(true);
+
+ // TileMap editor plugins
+ tile_map_editor_plugins.push_back(memnew(TileMapEditorTilesPlugin));
+ tile_map_editor_plugins.push_back(memnew(TileMapEditorTerrainsPlugin));
+
+ // Tabs.
+ tabs = memnew(Tabs);
+ tabs->set_clip_tabs(false);
+ for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
+ tabs->add_tab(tile_map_editor_plugins[i]->get_name());
+ }
+ tabs->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed));
+
+ // --- TileMap toolbar ---
+ tilemap_toolbar = memnew(HBoxContainer);
+ //tilemap_toolbar->add_child(memnew(VSeparator));
+ tilemap_toolbar->add_child(tabs);
+ //tilemap_toolbar->add_child(memnew(VSeparator));
+ for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
+ tile_map_editor_plugins[i]->get_toolbar()->hide();
+ tilemap_toolbar->add_child(tile_map_editor_plugins[i]->get_toolbar());
+ }
+
+ missing_tileset_label = memnew(Label);
+ missing_tileset_label->set_text(TTR("The edited TileMap node has no TileSet resource."));
+ missing_tileset_label->set_h_size_flags(SIZE_EXPAND_FILL);
+ missing_tileset_label->set_v_size_flags(SIZE_EXPAND_FILL);
+ missing_tileset_label->set_align(Label::ALIGN_CENTER);
+ missing_tileset_label->set_valign(Label::VALIGN_CENTER);
+ missing_tileset_label->hide();
+ add_child(missing_tileset_label);
+
+ for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
+ add_child(tile_map_editor_plugins[i]);
+ tile_map_editor_plugins[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+ tile_map_editor_plugins[i]->set_v_size_flags(SIZE_EXPAND_FILL);
+ tile_map_editor_plugins[i]->set_visible(i == 0);
+ }
+
+ _tab_changed(0);
+}
+
+TileMapEditor::~TileMapEditor() {
+}
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
new file mode 100644
index 0000000000..f780686b82
--- /dev/null
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -0,0 +1,328 @@
+/*************************************************************************/
+/* tile_map_editor.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TILE_MAP_EDITOR_H
+#define TILE_MAP_EDITOR_H
+
+#include "tile_atlas_view.h"
+
+#include "core/typedefs.h"
+#include "editor/editor_node.h"
+#include "scene/2d/tile_map.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/tabs.h"
+
+class TileMapEditorPlugin : public VBoxContainer {
+public:
+ virtual Control *get_toolbar() const {
+ return memnew(Control);
+ };
+ virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; };
+ virtual void forward_canvas_draw_over_viewport(Control *p_overlay){};
+ virtual void tile_set_changed(){};
+ virtual void edit(ObjectID p_tile_map_id){};
+};
+
+class TileMapEditorTilesPlugin : public TileMapEditorPlugin {
+ GDCLASS(TileMapEditorTilesPlugin, TileMapEditorPlugin);
+
+private:
+ UndoRedo *undo_redo = EditorNode::get_undo_redo();
+ ObjectID tile_map_id;
+ virtual void edit(ObjectID p_tile_map_id) override;
+
+ // Toolbar.
+ HBoxContainer *toolbar;
+
+ Ref<ButtonGroup> tool_buttons_group;
+ Button *select_tool_button;
+ Button *paint_tool_button;
+ Button *line_tool_button;
+ Button *rect_tool_button;
+ Button *bucket_tool_button;
+ Button *picker_button;
+
+ HBoxContainer *tools_settings;
+ VSeparator *tools_settings_vsep;
+ Button *erase_button;
+ CheckBox *bucket_continuous_checkbox;
+
+ VSeparator *tools_settings_vsep_2;
+ CheckBox *random_tile_checkbox;
+ float scattering = 0.0;
+ Label *scatter_label;
+ SpinBox *scatter_spinbox;
+ void _on_random_tile_checkbox_toggled(bool p_pressed);
+ void _on_scattering_spinbox_changed(double p_value);
+
+ void _update_toolbar();
+
+ // Tilemap editing.
+ bool has_mouse = false;
+ void _mouse_exited_viewport();
+
+ enum DragType {
+ DRAG_TYPE_NONE = 0,
+ DRAG_TYPE_SELECT,
+ DRAG_TYPE_MOVE,
+ DRAG_TYPE_PAINT,
+ DRAG_TYPE_LINE,
+ DRAG_TYPE_RECT,
+ DRAG_TYPE_BUCKET,
+ DRAG_TYPE_PICK,
+ DRAG_TYPE_CLIPBOARD_PASTE,
+ };
+ DragType drag_type = DRAG_TYPE_NONE;
+ Vector2 drag_start_mouse_pos;
+ Vector2 drag_last_mouse_pos;
+ Map<Vector2i, TileMapCell> drag_modified;
+
+ TileMapCell _pick_random_tile(const TileMapPattern *p_pattern);
+ Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2i p_to_mouse_pos);
+ Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_mouse_pos, Vector2i p_end_mouse_pos);
+ Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous);
+ void _stop_dragging();
+
+ // Selection system.
+ Set<Vector2i> tile_map_selection;
+ TileMapPattern *tile_map_clipboard = memnew(TileMapPattern);
+ TileMapPattern *selection_pattern = memnew(TileMapPattern);
+ void _set_tile_map_selection(const TypedArray<Vector2i> &p_selection);
+ TypedArray<Vector2i> _get_tile_map_selection() const;
+
+ Set<TileMapCell> tile_set_selection;
+
+ void _update_selection_pattern_from_tilemap_selection();
+ void _update_selection_pattern_from_tileset_selection();
+ void _update_tileset_selection_from_selection_pattern();
+ void _update_fix_selected_and_hovered();
+
+ // Bottom panel.
+ bool tile_set_dragging_selection = false;
+ Vector2i tile_set_drag_start_mouse_pos;
+
+ Label *missing_source_label;
+ HSplitContainer *atlas_sources_split_container;
+
+ ItemList *sources_list;
+ TileAtlasView *tile_atlas_view;
+ Ref<Texture2D> missing_texture_texture;
+ void _update_tile_set_sources_list();
+ void _update_atlas_view();
+
+ void _update_bottom_panel();
+
+ TileMapCell hovered_tile;
+
+ Control *tile_atlas_control;
+ void _tile_atlas_control_mouse_exited();
+ void _tile_atlas_control_gui_input(const Ref<InputEvent> &p_event);
+ void _tile_atlas_control_draw();
+
+ Control *alternative_tiles_control;
+ void _tile_alternatives_control_draw();
+ void _tile_alternatives_control_mouse_exited();
+ void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
+
+ // Update callback
+ virtual void tile_set_changed() override;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ virtual Control *get_toolbar() const override;
+ virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
+ virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
+
+ TileMapEditorTilesPlugin();
+ ~TileMapEditorTilesPlugin();
+};
+
+class TileMapEditorTerrainsPlugin : public TileMapEditorPlugin {
+ GDCLASS(TileMapEditorTerrainsPlugin, TileMapEditorPlugin);
+
+private:
+ UndoRedo *undo_redo = EditorNode::get_undo_redo();
+ ObjectID tile_map_id;
+ virtual void edit(ObjectID p_tile_map_id) override;
+
+ // Toolbar.
+ HBoxContainer *toolbar;
+
+ Ref<ButtonGroup> tool_buttons_group;
+ Button *paint_tool_button;
+
+ HBoxContainer *tools_settings;
+ VSeparator *tools_settings_vsep;
+ Button *picker_button;
+ Button *erase_button;
+
+ void _update_toolbar();
+
+ // TileMap editing.
+ enum DragType {
+ DRAG_TYPE_NONE = 0,
+ DRAG_TYPE_PAINT,
+ DRAG_TYPE_PICK,
+ };
+ DragType drag_type = DRAG_TYPE_NONE;
+ Vector2 drag_start_mouse_pos;
+ Vector2 drag_last_mouse_pos;
+ Map<Vector2i, TileMapCell> drag_modified;
+
+ // Painting
+ class Constraint {
+ private:
+ const TileMap *tile_map;
+ Vector2i base_cell_coords = Vector2i();
+ int bit = -1;
+ int terrain = -1;
+
+ public:
+ // TODO implement difference operator.
+ bool operator<(const Constraint &p_other) const {
+ if (base_cell_coords == p_other.base_cell_coords) {
+ return bit < p_other.bit;
+ }
+ return base_cell_coords < p_other.base_cell_coords;
+ }
+
+ String to_string() const {
+ return vformat("Constraint {pos:%s, bit:%d, terrain:%d}", base_cell_coords, bit, terrain);
+ }
+
+ Vector2i get_base_cell_coords() const {
+ return base_cell_coords;
+ }
+
+ Map<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
+
+ void set_terrain(int p_terrain) {
+ terrain = p_terrain;
+ }
+
+ int get_terrain() const {
+ return terrain;
+ }
+
+ Constraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain);
+ Constraint() {}
+ };
+
+ typedef Array TerrainsTilePattern;
+
+ Set<TerrainsTilePattern> _get_valid_terrains_tile_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const;
+ Set<TileMapEditorTerrainsPlugin::Constraint> _get_constraints_from_removed_cells_list(const Set<Vector2i> &p_to_replace, int p_terrain_set) const;
+ Set<TileMapEditorTerrainsPlugin::Constraint> _get_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TerrainsTilePattern p_terrains_tile_pattern) const;
+ Map<Vector2i, TerrainsTilePattern> _wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const;
+ TileMapCell _get_random_tile_from_pattern(int p_terrain_set, TerrainsTilePattern p_terrain_tile_pattern) const;
+ Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TerrainsTilePattern> &p_to_paint, int p_terrain_set) const;
+
+ // Cached data.
+
+ TerrainsTilePattern _build_terrains_tile_pattern(TileData *p_tile_data);
+ LocalVector<Map<TerrainsTilePattern, Set<TileMapCell>>> per_terrain_terrains_tile_patterns_tiles;
+ LocalVector<LocalVector<Set<TerrainsTilePattern>>> per_terrain_terrains_tile_patterns;
+
+ Map<TileMapCell, TileData *> terrain_tiles;
+ LocalVector<TileSet::CellNeighbor> tile_sides;
+
+ // Bottom panel.
+ Tree *terrains_tree;
+ ItemList *terrains_tile_list;
+
+ // Update functions.
+ void _update_terrains_cache();
+ void _update_terrains_tree();
+ void _update_tiles_list();
+
+ // Update callback
+ virtual void tile_set_changed() override;
+
+protected:
+ void _notification(int p_what);
+ // static void _bind_methods();
+
+public:
+ virtual Control *get_toolbar() const override;
+ virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
+ //virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
+
+ TileMapEditorTerrainsPlugin();
+ ~TileMapEditorTerrainsPlugin();
+};
+
+class TileMapEditor : public VBoxContainer {
+ GDCLASS(TileMapEditor, VBoxContainer);
+
+private:
+ bool tileset_changed_needs_update = false;
+ ObjectID tile_map_id;
+
+ // Vector to keep plugins.
+ Vector<TileMapEditorPlugin *> tile_map_editor_plugins;
+
+ // Toolbar.
+ HBoxContainer *tilemap_toolbar;
+
+ // Bottom panel
+ Label *missing_tileset_label;
+ Tabs *tabs;
+ void _update_bottom_panel();
+
+ // TileMap
+ Ref<Texture2D> missing_tile_texture;
+ Ref<Texture2D> warning_pattern_texture;
+
+ // CallBack
+ void _tile_map_changed();
+ void _tab_changed(int p_tab_changed);
+
+protected:
+ void _notification(int p_what);
+ void _draw_shape(Control *p_control, Rect2 p_region, TileSet::TileShape p_shape, TileSet::TileOffsetAxis p_offset_axis, Color p_color);
+
+public:
+ bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
+ void forward_canvas_draw_over_viewport(Control *p_overlay);
+
+ void edit(TileMap *p_tile_map);
+ Control *get_toolbar() { return tilemap_toolbar; };
+
+ TileMapEditor();
+ ~TileMapEditor();
+
+ // Static functions.
+ static Vector<Vector2i> get_line(TileMap *p_tile_map, Vector2i p_from_cell, Vector2i p_to_cell);
+};
+
+#endif // TILE_MAP_EDITOR_PLUGIN_H
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
new file mode 100644
index 0000000000..8492202122
--- /dev/null
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -0,0 +1,1858 @@
+/*************************************************************************/
+/* tile_set_atlas_source_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "tile_set_atlas_source_editor.h"
+
+#include "tiles_editor_plugin.h"
+
+#include "editor/editor_inspector.h"
+#include "editor/editor_scale.h"
+#include "editor/progress_dialog.h"
+
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/control.h"
+#include "scene/gui/item_list.h"
+#include "scene/gui/separator.h"
+#include "scene/gui/split_container.h"
+#include "scene/gui/tab_container.h"
+
+#include "core/core_string_names.h"
+#include "core/math/geometry_2d.h"
+#include "core/os/keyboard.h"
+
+void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::set_id(int p_id) {
+ ERR_FAIL_COND(p_id < 0);
+ if (source_id == p_id) {
+ return;
+ }
+ ERR_FAIL_COND_MSG(tile_set->has_source(p_id), vformat("Cannot change TileSet atlas source ID. Another atlas source exists with id %d.", p_id));
+
+ int previous_source = source_id;
+ source_id = p_id; // source_id must be updated before, because it's used by the atlas source list update.
+ tile_set->set_source_id(previous_source, p_id);
+ emit_signal("changed", "id");
+}
+
+int TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::get_id() {
+ return source_id;
+}
+
+bool TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_set(const StringName &p_name, const Variant &p_value) {
+ bool valid = false;
+ tile_set_atlas_source->set(p_name, p_value, &valid);
+ if (valid) {
+ emit_signal("changed", String(p_name).utf8().get_data());
+ }
+ return valid;
+}
+
+bool TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get(const StringName &p_name, Variant &r_ret) const {
+ if (!tile_set_atlas_source) {
+ return false;
+ }
+ bool valid = false;
+ r_ret = tile_set_atlas_source->get(p_name, &valid);
+ return valid;
+}
+
+void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, ""));
+}
+
+void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_bind_methods() {
+ // -- Shape and layout --
+ ClassDB::bind_method(D_METHOD("set_id", "id"), &TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::set_id);
+ ClassDB::bind_method(D_METHOD("get_id"), &TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::get_id);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "id"), "set_id", "get_id");
+
+ ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what")));
+}
+
+void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
+ ERR_FAIL_COND(!p_tile_set.is_valid());
+ ERR_FAIL_COND(!p_tile_set_atlas_source);
+ ERR_FAIL_COND(p_source_id < 0);
+ ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
+
+ // Disconnect to changes.
+ if (tile_set_atlas_source) {
+ tile_set_atlas_source->disconnect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
+ }
+
+ tile_set = p_tile_set;
+ tile_set_atlas_source = p_tile_set_atlas_source;
+ source_id = p_source_id;
+
+ // Connect to changes.
+ if (tile_set_atlas_source) {
+ if (!tile_set_atlas_source->is_connected(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed))) {
+ tile_set_atlas_source->connect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
+ }
+ }
+
+ notify_property_list_changed();
+}
+
+// -- Proxy object used by the tile inspector --
+bool TileSetAtlasSourceEditor::TileProxyObject::_set(const StringName &p_name, const Variant &p_value) {
+ if (!tile_set_atlas_source) {
+ return false;
+ }
+
+ if (tiles.size() == 1) {
+ const Vector2i &coords = tiles.front()->get().tile;
+ const int &alternative = tiles.front()->get().alternative;
+
+ if (alternative == 0 && p_name == "atlas_coords") {
+ Vector2i as_vector2i = Vector2i(p_value);
+ ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, as_vector2i), false);
+
+ if (tiles_set_atlas_source_editor->selection.front()->get().tile == coords) {
+ tiles_set_atlas_source_editor->selection.clear();
+ tiles_set_atlas_source_editor->selection.insert({ as_vector2i, 0 });
+ tiles_set_atlas_source_editor->_update_tile_id_label();
+ }
+
+ tile_set_atlas_source->move_tile_in_atlas(coords, as_vector2i);
+ tiles.clear();
+ tiles.insert({ as_vector2i, 0 });
+ emit_signal("changed", "atlas_coords");
+ return true;
+ } else if (alternative == 0 && p_name == "size_in_atlas") {
+ Vector2i as_vector2i = Vector2i(p_value);
+ ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, TileSetAtlasSource::INVALID_ATLAS_COORDS, as_vector2i), false);
+
+ tile_set_atlas_source->move_tile_in_atlas(coords, TileSetAtlasSource::INVALID_ATLAS_COORDS, as_vector2i);
+ emit_signal("changed", "size_in_atlas");
+ return true;
+ } else if (alternative > 0 && p_name == "alternative_id") {
+ int as_int = int(p_value);
+ ERR_FAIL_COND_V(as_int < 0, false);
+ ERR_FAIL_COND_V_MSG(tile_set_atlas_source->has_alternative_tile(coords, as_int), false, vformat("Cannot change alternative tile ID. Another alternative exists with id %d for tile at coords %s.", as_int, coords));
+
+ if (tiles_set_atlas_source_editor->selection.front()->get().alternative == alternative) {
+ tiles_set_atlas_source_editor->selection.clear();
+ tiles_set_atlas_source_editor->selection.insert({ coords, as_int });
+ }
+
+ int previous_alternative_tile = alternative;
+ tiles.clear();
+ tiles.insert({ coords, as_int }); // tiles must be updated before.
+ tile_set_atlas_source->set_alternative_tile_id(coords, previous_alternative_tile, as_int);
+
+ emit_signal("changed", "alternative_id");
+ return true;
+ }
+ }
+
+ bool any_valid = false;
+ for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) {
+ const Vector2i &coords = E->get().tile;
+ const int &alternative = E->get().alternative;
+
+ bool valid = false;
+ TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ ERR_FAIL_COND_V(!tile_data, false);
+ tile_data->set(p_name, p_value, &valid);
+
+ any_valid |= valid;
+ }
+
+ if (any_valid) {
+ emit_signal("changed", String(p_name).utf8().get_data());
+ }
+
+ return any_valid;
+}
+
+bool TileSetAtlasSourceEditor::TileProxyObject::_get(const StringName &p_name, Variant &r_ret) const {
+ if (!tile_set_atlas_source) {
+ return false;
+ }
+
+ if (tiles.size() == 1) {
+ const Vector2i &coords = tiles.front()->get().tile;
+ const int &alternative = tiles.front()->get().alternative;
+
+ if (alternative == 0 && p_name == "atlas_coords") {
+ r_ret = coords;
+ return true;
+ } else if (alternative == 0 && p_name == "size_in_atlas") {
+ r_ret = tile_set_atlas_source->get_tile_size_in_atlas(coords);
+ return true;
+ } else if (alternative > 0 && p_name == "alternative_id") {
+ r_ret = alternative;
+ return true;
+ }
+ }
+
+ for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) {
+ // Return the first tile with a property matching the name.
+ // Note: It's a little bit annoying, but the behavior is the same the one in MultiNodeEdit.
+ const Vector2i &coords = E->get().tile;
+ const int &alternative = E->get().alternative;
+
+ TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ ERR_FAIL_COND_V(!tile_data, false);
+
+ bool valid = false;
+ r_ret = tile_data->get(p_name, &valid);
+ if (valid) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void TileSetAtlasSourceEditor::TileProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
+ if (!tile_set_atlas_source) {
+ return;
+ }
+
+ if (tiles.size() == 1) {
+ if (tiles.front()->get().alternative == 0) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "atlas_coords", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, ""));
+ } else {
+ p_list->push_back(PropertyInfo(Variant::INT, "alternative_id", PROPERTY_HINT_NONE, ""));
+ }
+ }
+
+ // Get the list of properties common to all tiles (similar to what's done in MultiNodeEdit).
+ struct PropertyId {
+ int occurence_id = 0;
+ String property;
+ bool operator<(const PropertyId &p_other) const {
+ return occurence_id == p_other.occurence_id ? property < p_other.property : occurence_id < p_other.occurence_id;
+ }
+ };
+ struct PLData {
+ int uses = 0;
+ PropertyInfo property_info;
+ };
+ Map<PropertyId, PLData> usage;
+
+ List<PLData *> data_list;
+ for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) {
+ const Vector2i &coords = E->get().tile;
+ const int &alternative = E->get().alternative;
+
+ TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ ERR_FAIL_COND(!tile_data);
+
+ List<PropertyInfo> list;
+ tile_data->get_property_list(&list);
+
+ Map<String, int> counts; // Counts the number of time a property appears (useful for groups that may appear more than once)
+ for (List<PropertyInfo>::Element *E_property = list.front(); E_property; E_property = E_property->next()) {
+ const String &property_string = E_property->get().name;
+ if (!tile_data->is_allowing_transform() && (property_string == "flip_h" || property_string == "flip_v" || property_string == "transpose")) {
+ continue;
+ }
+
+ if (!counts.has(property_string)) {
+ counts[property_string] = 1;
+ } else {
+ counts[property_string] += 1;
+ }
+
+ PropertyInfo stored_property_info = E_property->get();
+ stored_property_info.usage |= PROPERTY_USAGE_STORAGE; // Ignore the storage flag in comparing properties.
+
+ PropertyId id = { counts[property_string], property_string };
+ if (!usage.has(id)) {
+ usage[id] = { 1, stored_property_info };
+ data_list.push_back(&usage[id]);
+ } else if (usage[id].property_info == stored_property_info) {
+ usage[id].uses += 1;
+ }
+ }
+ }
+
+ // Add only properties that are common to all tiles.
+ for (List<PLData *>::Element *E = data_list.front(); E; E = E->next()) {
+ if (E->get()->uses == tiles.size()) {
+ p_list->push_back(E->get()->property_info);
+ }
+ }
+}
+
+void TileSetAtlasSourceEditor::TileProxyObject::edit(TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id, Set<TileSelection> p_tiles) {
+ ERR_FAIL_COND(!p_tile_set_atlas_source);
+ ERR_FAIL_COND(p_source_id < 0);
+ ERR_FAIL_COND(p_tiles.is_empty());
+ for (Set<TileSelection>::Element *E = p_tiles.front(); E; E = E->next()) {
+ ERR_FAIL_COND(E->get().tile == TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ ERR_FAIL_COND(E->get().alternative < 0);
+ }
+
+ // Disconnect to changes.
+ for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) {
+ const Vector2i &coords = E->get().tile;
+ const int &alternative = E->get().alternative;
+
+ if (tile_set_atlas_source && tile_set_atlas_source->has_tile(coords) && tile_set_atlas_source->has_alternative_tile(coords, alternative)) {
+ TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ if (tile_data->is_connected(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed))) {
+ tile_data->disconnect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
+ }
+ }
+ }
+
+ tile_set_atlas_source = p_tile_set_atlas_source;
+ source_id = p_source_id;
+ tiles = Set<TileSelection>(p_tiles);
+
+ // Connect to changes.
+ for (Set<TileSelection>::Element *E = p_tiles.front(); E; E = E->next()) {
+ const Vector2i &coords = E->get().tile;
+ const int &alternative = E->get().alternative;
+
+ if (tile_set_atlas_source->has_tile(coords) && tile_set_atlas_source->has_alternative_tile(coords, alternative)) {
+ TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ if (!tile_data->is_connected(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed))) {
+ tile_data->connect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
+ }
+ }
+ }
+
+ notify_property_list_changed();
+}
+
+void TileSetAtlasSourceEditor::TileProxyObject::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("changed", PropertyInfo(Variant::STRING, "what")));
+}
+
+void TileSetAtlasSourceEditor::_inspector_property_selected(String p_property) {
+ selected_property = p_property;
+ _update_atlas_view();
+}
+
+void TileSetAtlasSourceEditor::_update_tile_id_label() {
+ if (selection.size() == 1) {
+ TileSelection selected = selection.front()->get();
+ tool_tile_id_label->set_text(vformat("%d, %s, %d", tile_set_atlas_source_id, selected.tile, selected.alternative));
+ tool_tile_id_label->set_tooltip(vformat(TTR("Selected tile:\nSource: %d\nAtlas coordinates: %s\nAlternative: %d"), tile_set_atlas_source_id, selected.tile, selected.alternative));
+ tool_tile_id_label->show();
+ } else {
+ tool_tile_id_label->hide();
+ }
+}
+
+void TileSetAtlasSourceEditor::_update_source_inspector() {
+ // Update the proxy object.
+ atlas_source_proxy_object->edit(tile_set, tile_set_atlas_source, tile_set_atlas_source_id);
+
+ // Update the "clear outside texture" button.
+ tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, !tile_set_atlas_source->has_tiles_outside_texture());
+}
+
+void TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles() {
+ // Fix selected.
+ for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) {
+ TileSelection selected = E->get();
+ if (!tile_set_atlas_source->has_tile(selected.tile) || !tile_set_atlas_source->has_alternative_tile(selected.tile, selected.alternative)) {
+ selection.erase(E);
+ }
+ }
+
+ // Fix hovered.
+ if (!tile_set_atlas_source->has_tile(hovered_base_tile_coords)) {
+ hovered_base_tile_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS;
+ }
+ Vector2i coords = Vector2i(hovered_alternative_tile_coords.x, hovered_alternative_tile_coords.y);
+ int alternative = hovered_alternative_tile_coords.z;
+ if (!tile_set_atlas_source->has_tile(coords) || !tile_set_atlas_source->has_alternative_tile(coords, alternative)) {
+ hovered_alternative_tile_coords = Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ }
+}
+
+void TileSetAtlasSourceEditor::_update_tile_inspector() {
+ bool has_atlas_tile_selected = (tools_button_group->get_pressed_button() == tool_select_button) && !selection.is_empty();
+
+ // Update the proxy object.
+ if (has_atlas_tile_selected) {
+ tile_proxy_object->edit(tile_set_atlas_source, tile_set_atlas_source_id, selection);
+ }
+
+ // Update visibility.
+ tile_inspector_label->set_visible(has_atlas_tile_selected);
+ tile_inspector->set_visible(has_atlas_tile_selected);
+}
+
+void TileSetAtlasSourceEditor::_update_atlas_view() {
+ // Update the atlas display.
+ tile_atlas_view->set_atlas_source(*tile_set, tile_set_atlas_source, tile_set_atlas_source_id);
+
+ // Create a bunch of buttons to add alternative tiles.
+ for (int i = 0; i < alternative_tiles_control->get_child_count(); i++) {
+ alternative_tiles_control->get_child(i)->queue_delete();
+ }
+
+ Vector2i pos;
+ Vector2 texture_region_base_size = tile_set_atlas_source->get_texture_region_size();
+ int texture_region_base_size_min = MIN(texture_region_base_size.x, texture_region_base_size.y);
+ for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
+ Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
+ int alternative_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
+ if (alternative_count > 1) {
+ // Compute the right extremity of alternative.
+ int y_increment = 0;
+ pos.x = 0;
+ for (int j = 1; j < alternative_count; j++) {
+ int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
+ Rect2i rect = tile_atlas_view->get_alternative_tile_rect(tile_id, alternative_id);
+ pos.x = MAX(pos.x, rect.get_end().x);
+ y_increment = MAX(y_increment, rect.size.y);
+ }
+
+ // Create and position the button.
+ Button *button = memnew(Button);
+ alternative_tiles_control->add_child(button);
+ button->set_flat(true);
+ button->set_icon(get_theme_icon("Add", "EditorIcons"));
+ button->add_theme_style_override("normal", memnew(StyleBoxEmpty));
+ button->add_theme_style_override("hover", memnew(StyleBoxEmpty));
+ button->add_theme_style_override("focus", memnew(StyleBoxEmpty));
+ button->add_theme_style_override("pressed", memnew(StyleBoxEmpty));
+ button->connect("pressed", callable_mp(tile_set_atlas_source, &TileSetAtlasSource::create_alternative_tile), varray(tile_id, -1));
+ button->set_rect(Rect2(Vector2(pos.x, pos.y + (y_increment - texture_region_base_size.y) / 2.0), Vector2(texture_region_base_size_min, texture_region_base_size_min)));
+ button->set_expand_icon(true);
+
+ pos.y += y_increment;
+ }
+ }
+ tile_atlas_view->set_padding(Side::SIDE_RIGHT, texture_region_base_size_min);
+
+ // Redraw everything.
+ tile_atlas_control->update();
+ tile_atlas_control_unscaled->update();
+ alternative_tiles_control->update();
+ alternative_tiles_control_unscaled->update();
+ tile_atlas_view->update();
+
+ // Synchronize atlas view.
+ TilesEditor::get_singleton()->synchronize_atlas_view(tile_atlas_view);
+}
+
+void TileSetAtlasSourceEditor::_update_toolbar() {
+ // Hide all settings.
+ for (int i = 0; i < tool_settings->get_child_count(); i++) {
+ Object::cast_to<CanvasItem>(tool_settings->get_child(i))->hide();
+ }
+
+ // SHow only the correct settings.
+ if (tools_button_group->get_pressed_button() == tool_select_button) {
+ } else if (tools_button_group->get_pressed_button() == tool_add_remove_button) {
+ tool_settings_vsep->show();
+ tools_settings_erase_button->show();
+ } else if (tools_button_group->get_pressed_button() == tool_add_remove_rect_button) {
+ tool_settings_vsep->show();
+ tools_settings_erase_button->show();
+ }
+}
+
+void TileSetAtlasSourceEditor::_tile_atlas_control_mouse_exited() {
+ hovered_base_tile_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS;
+ tile_atlas_control->update();
+ tile_atlas_control_unscaled->update();
+ tile_atlas_view->update();
+}
+
+void TileSetAtlasSourceEditor::_tile_atlas_view_transform_changed() {
+ tile_atlas_control->update();
+ tile_atlas_control_unscaled->update();
+}
+
+void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEvent> &p_event) {
+ // Update the hovered coords.
+ hovered_base_tile_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+
+ // Handle the event.
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
+ Vector2i last_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_mouse_pos);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+
+ Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
+
+ if (drag_type == DRAG_TYPE_NONE) {
+ if (selection.size() == 1) {
+ // Change the cursor depending on the hovered thing.
+ TileSelection selected = selection.front()->get();
+ if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selected.alternative == 0) {
+ Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position();
+ Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
+ Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
+ Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
+ Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
+ const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
+ const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
+ CursorShape cursor_shape = CURSOR_ARROW;
+ bool can_grow[4];
+ for (int i = 0; i < 4; i++) {
+ can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
+ can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
+ }
+ for (int i = 0; i < 4; i++) {
+ Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i];
+ if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) {
+ cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE;
+ }
+ Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4];
+ if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) {
+ cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE;
+ }
+ }
+ tile_atlas_control->set_default_cursor_shape(cursor_shape);
+ }
+ }
+ } else if (drag_type == DRAG_TYPE_CREATE_BIG_TILE) {
+ // Create big tile.
+ new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1));
+
+ Rect2i new_rect = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
+ new_rect.size += Vector2i(1, 1);
+ // Check if the new tile can fit in the new rect.
+ if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) {
+ // Move and resize the tile.
+ tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size);
+ drag_current_tile = new_rect.position;
+ }
+ } else if (drag_type == DRAG_TYPE_CREATE_TILES) {
+ // Create tiles.
+ last_base_tiles_coords = last_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1));
+ new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1));
+
+ Vector<Point2i> line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords);
+ for (int i = 0; i < line.size(); i++) {
+ if (tile_set_atlas_source->get_tile_at_coords(line[i]) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ tile_set_atlas_source->create_tile(line[i]);
+ drag_modified_tiles.insert(line[i]);
+ }
+ }
+
+ drag_last_mouse_pos = tile_atlas_control->get_local_mouse_position();
+
+ } else if (drag_type == DRAG_TYPE_REMOVE_TILES) {
+ // Remove tiles.
+ last_base_tiles_coords = last_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1));
+ new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1));
+
+ Vector<Point2i> line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords);
+ for (int i = 0; i < line.size(); i++) {
+ Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(line[i]);
+ if (base_tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ drag_modified_tiles.insert(base_tile_coords);
+ }
+ }
+
+ drag_last_mouse_pos = tile_atlas_control->get_local_mouse_position();
+ } else if (drag_type == DRAG_TYPE_MOVE_TILE) {
+ // Move tile.
+ Vector2 mouse_offset = (Vector2(tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size();
+ Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() - mouse_offset);
+ coords = coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1));
+ if (drag_current_tile != coords && tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, coords)) {
+ tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, coords);
+ selection.clear();
+ selection.insert({ coords, 0 });
+ drag_current_tile = coords;
+
+ // Update only what's needed.
+ tile_set_atlas_source_changed_needs_update = false;
+ _update_tile_inspector();
+ _update_atlas_view();
+ _update_tile_id_label();
+ }
+ } else if (drag_type >= DRAG_TYPE_RESIZE_TOP_LEFT && drag_type <= DRAG_TYPE_RESIZE_LEFT) {
+ // Resizing a tile.
+ new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(-1, -1)).min(grid_size);
+
+ Rect2i old_rect = Rect2i(drag_current_tile, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile));
+ Rect2i new_rect = old_rect;
+
+ if (drag_type == DRAG_TYPE_RESIZE_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT) {
+ new_rect.position.x = MIN(new_base_tiles_coords.x + 1, old_rect.get_end().x - 1);
+ new_rect.size.x = old_rect.get_end().x - new_rect.position.x;
+ }
+ if (drag_type == DRAG_TYPE_RESIZE_TOP || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT) {
+ new_rect.position.y = MIN(new_base_tiles_coords.y + 1, old_rect.get_end().y - 1);
+ new_rect.size.y = old_rect.get_end().y - new_rect.position.y;
+ }
+
+ if (drag_type == DRAG_TYPE_RESIZE_RIGHT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) {
+ new_rect.set_end(Vector2i(MAX(new_base_tiles_coords.x, old_rect.position.x + 1), new_rect.get_end().y));
+ }
+ if (drag_type == DRAG_TYPE_RESIZE_BOTTOM || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) {
+ new_rect.set_end(Vector2i(new_rect.get_end().x, MAX(new_base_tiles_coords.y, old_rect.position.y + 1)));
+ }
+
+ if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) {
+ tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size);
+ selection.clear();
+ selection.insert({ new_rect.position, 0 });
+ drag_current_tile = new_rect.position;
+
+ // Update only what's needed.
+ tile_set_atlas_source_changed_needs_update = false;
+ _update_tile_inspector();
+ _update_atlas_view();
+ _update_tile_id_label();
+ }
+ }
+
+ // Redraw for the hovered tile.
+ tile_atlas_control->update();
+ tile_atlas_control_unscaled->update();
+ alternative_tiles_control->update();
+ alternative_tiles_control_unscaled->update();
+ tile_atlas_view->update();
+ return;
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid()) {
+ Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position();
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->is_pressed()) {
+ // Left click pressed.
+ if (tools_button_group->get_pressed_button() == tool_add_remove_button) {
+ if (tools_settings_erase_button->is_pressed()) {
+ // Remove tiles.
+
+ // Setup the dragging info.
+ drag_type = DRAG_TYPE_REMOVE_TILES;
+ drag_start_mouse_pos = mouse_local_pos;
+ drag_last_mouse_pos = drag_start_mouse_pos;
+
+ // Remove a first tile.
+ Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ coords = tile_set_atlas_source->get_tile_at_coords(coords);
+ }
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ drag_modified_tiles.insert(coords);
+ }
+ } else {
+ if (mb->get_shift()) {
+ // Create a big tile.
+ Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos);
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ // Setup the dragging info, only if we start on an empty tile.
+ drag_type = DRAG_TYPE_CREATE_BIG_TILE;
+ drag_start_mouse_pos = mouse_local_pos;
+ drag_last_mouse_pos = drag_start_mouse_pos;
+ drag_current_tile = coords;
+
+ // Create a tile.
+ tile_set_atlas_source->create_tile(coords);
+ }
+ } else {
+ // Create tiles.
+
+ // Setup the dragging info.
+ drag_type = DRAG_TYPE_CREATE_TILES;
+ drag_start_mouse_pos = mouse_local_pos;
+ drag_last_mouse_pos = drag_start_mouse_pos;
+
+ // Create a first tile if needed.
+ Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ tile_set_atlas_source->create_tile(coords);
+ drag_modified_tiles.insert(coords);
+ }
+ }
+ }
+ } else if (tools_button_group->get_pressed_button() == tool_add_remove_rect_button) {
+ if (tools_settings_erase_button->is_pressed()) {
+ // Remove tiles using rect.
+
+ // Setup the dragging info.
+ drag_type = DRAG_TYPE_REMOVE_TILES_USING_RECT;
+ drag_start_mouse_pos = mouse_local_pos;
+ drag_last_mouse_pos = drag_start_mouse_pos;
+ } else {
+ if (mb->get_shift()) {
+ // Create a big tile.
+ Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos);
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ // Setup the dragging info, only if we start on an empty tile.
+ drag_type = DRAG_TYPE_CREATE_BIG_TILE;
+ drag_start_mouse_pos = mouse_local_pos;
+ drag_last_mouse_pos = drag_start_mouse_pos;
+ drag_current_tile = coords;
+
+ // Create a tile.
+ tile_set_atlas_source->create_tile(coords);
+ }
+ } else {
+ // Create tiles using rect.
+ drag_type = DRAG_TYPE_CREATE_TILES_USING_RECT;
+ drag_start_mouse_pos = mouse_local_pos;
+ drag_last_mouse_pos = drag_start_mouse_pos;
+ }
+ }
+ } else if (tools_button_group->get_pressed_button() == tool_select_button) {
+ // Dragging a handle.
+ drag_type = DRAG_TYPE_NONE;
+ if (selection.size() == 1) {
+ TileSelection selected = selection.front()->get();
+ if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selected.alternative == 0) {
+ Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
+ Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
+ Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
+ Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
+ const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
+ const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
+ CursorShape cursor_shape = CURSOR_ARROW;
+ bool can_grow[4];
+ for (int i = 0; i < 4; i++) {
+ can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
+ can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
+ }
+ for (int i = 0; i < 4; i++) {
+ Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i];
+ if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) {
+ drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP_LEFT + i * 2);
+ drag_start_mouse_pos = mouse_local_pos;
+ drag_last_mouse_pos = drag_start_mouse_pos;
+ drag_current_tile = selected.tile;
+ drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile));
+ cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE;
+ }
+ Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4];
+ if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) {
+ drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP + i * 2);
+ drag_start_mouse_pos = mouse_local_pos;
+ drag_last_mouse_pos = drag_start_mouse_pos;
+ drag_current_tile = selected.tile;
+ drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile));
+ cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE;
+ }
+ }
+ tile_atlas_control->set_default_cursor_shape(cursor_shape);
+ }
+ }
+
+ // Selecting then dragging a tile.
+ if (drag_type == DRAG_TYPE_NONE) {
+ TileSelection selected = { TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE };
+ Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos);
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ coords = tile_set_atlas_source->get_tile_at_coords(coords);
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ selected = { coords, 0 };
+ }
+ }
+
+ bool shift = mb->get_shift();
+ if (!shift && selection.size() == 1 && selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selection.has(selected)) {
+ // Start move dragging.
+ drag_type = DRAG_TYPE_MOVE_TILE;
+ drag_start_mouse_pos = mouse_local_pos;
+ drag_last_mouse_pos = drag_start_mouse_pos;
+ drag_current_tile = selected.tile;
+ drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile));
+ tile_atlas_control->set_default_cursor_shape(CURSOR_MOVE);
+ } else {
+ // Start selection dragging.
+ drag_type = DRAG_TYPE_RECT_SELECT;
+ drag_start_mouse_pos = mouse_local_pos;
+ drag_last_mouse_pos = drag_start_mouse_pos;
+ }
+ }
+ }
+ } else {
+ // Left click released.
+ _end_dragging();
+ }
+ tile_atlas_control->update();
+ tile_atlas_control_unscaled->update();
+ alternative_tiles_control->update();
+ alternative_tiles_control_unscaled->update();
+ tile_atlas_view->update();
+ return;
+ } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ if (mb->is_pressed()) {
+ // Right click pressed.
+
+ TileSelection selected = { tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos), 0 };
+ if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ selected.tile = tile_set_atlas_source->get_tile_at_coords(selected.tile);
+ }
+
+ // Set the selection if needed.
+ if (selection.size() <= 1) {
+ if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ undo_redo->create_action(TTR("Select tiles"));
+ undo_redo->add_undo_method(this, "_set_selection_from_array", _get_selection_as_array());
+ selection.clear();
+ selection.insert(selected);
+ undo_redo->add_do_method(this, "_set_selection_from_array", _get_selection_as_array());
+ undo_redo->commit_action(false);
+ _update_tile_inspector();
+ _update_tile_id_label();
+ }
+ }
+
+ // Pops up the correct menu, depending on whether we have a tile or not.
+ if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS && selection.has(selected)) {
+ // We have a tile.
+ menu_option_coords = selected.tile;
+ menu_option_alternative = 0;
+ base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i()));
+ } else if (hovered_base_tile_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ // We don't have a tile, but can create one.
+ menu_option_coords = hovered_base_tile_coords;
+ menu_option_alternative = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ empty_base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i()));
+ }
+ } else {
+ // Right click released.
+ _end_dragging();
+ }
+ tile_atlas_control->update();
+ tile_atlas_control_unscaled->update();
+ alternative_tiles_control->update();
+ alternative_tiles_control_unscaled->update();
+ tile_atlas_view->update();
+ return;
+ }
+ }
+}
+
+void TileSetAtlasSourceEditor::_end_dragging() {
+ switch (drag_type) {
+ case DRAG_TYPE_CREATE_TILES:
+ undo_redo->create_action(TTR("Create tiles"));
+ for (Set<Vector2i>::Element *E = drag_modified_tiles.front(); E; E = E->next()) {
+ undo_redo->add_do_method(tile_set_atlas_source, "create_tile", E->get());
+ undo_redo->add_undo_method(tile_set_atlas_source, "remove_tile", E->get());
+ }
+ undo_redo->commit_action(false);
+ break;
+ case DRAG_TYPE_CREATE_BIG_TILE:
+ undo_redo->create_action(TTR("Create a tile"));
+ undo_redo->add_do_method(tile_set_atlas_source, "create_tile", drag_current_tile, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile));
+ undo_redo->add_undo_method(tile_set_atlas_source, "remove_tile", drag_current_tile);
+ undo_redo->commit_action(false);
+ break;
+ case DRAG_TYPE_REMOVE_TILES: {
+ List<PropertyInfo> list;
+ tile_set_atlas_source->get_property_list(&list);
+ Map<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source);
+ undo_redo->create_action(TTR("Remove tiles"));
+ for (Set<Vector2i>::Element *E = drag_modified_tiles.front(); E; E = E->next()) {
+ Vector2i coords = E->get();
+ undo_redo->add_do_method(tile_set_atlas_source, "remove_tile", coords);
+ undo_redo->add_undo_method(tile_set_atlas_source, "create_tile", coords);
+ if (per_tile.has(coords)) {
+ for (List<const PropertyInfo *>::Element *E_property = per_tile[coords].front(); E_property; E_property = E_property->next()) {
+ String property = E_property->get()->name;
+ Variant value = tile_set_atlas_source->get(property);
+ if (value.get_type() != Variant::NIL) {
+ undo_redo->add_undo_method(tile_set_atlas_source, "set", E_property->get()->name, value);
+ }
+ }
+ }
+ }
+ undo_redo->commit_action();
+ } break;
+ case DRAG_TYPE_CREATE_TILES_USING_RECT: {
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
+ area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size()));
+ undo_redo->create_action(TTR("Create tiles"));
+ for (int x = area.get_position().x; x < area.get_end().x; x++) {
+ for (int y = area.get_position().y; y < area.get_end().y; y++) {
+ Vector2i coords = Vector2i(x, y);
+ if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ undo_redo->add_do_method(tile_set_atlas_source, "create_tile", coords);
+ undo_redo->add_undo_method(tile_set_atlas_source, "remove_tile", coords);
+ }
+ }
+ }
+ undo_redo->commit_action();
+ } break;
+ case DRAG_TYPE_REMOVE_TILES_USING_RECT: {
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
+ area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size()));
+ List<PropertyInfo> list;
+ tile_set_atlas_source->get_property_list(&list);
+ Map<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source);
+
+ Set<Vector2i> to_delete;
+ for (int x = area.get_position().x; x < area.get_end().x; x++) {
+ for (int y = area.get_position().y; y < area.get_end().y; y++) {
+ Vector2i coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ to_delete.insert(coords);
+ }
+ }
+ }
+
+ undo_redo->create_action(TTR("Remove tiles"));
+ undo_redo->add_do_method(this, "_set_selection_from_array", Array());
+ for (Set<Vector2i>::Element *E = to_delete.front(); E; E = E->next()) {
+ Vector2i coords = E->get();
+ undo_redo->add_do_method(tile_set_atlas_source, "remove_tile", coords);
+ undo_redo->add_undo_method(tile_set_atlas_source, "create_tile", coords);
+ if (per_tile.has(coords)) {
+ for (List<const PropertyInfo *>::Element *E_property = per_tile[coords].front(); E_property; E_property = E_property->next()) {
+ String property = E_property->get()->name;
+ Variant value = tile_set_atlas_source->get(property);
+ if (value.get_type() != Variant::NIL) {
+ undo_redo->add_undo_method(tile_set_atlas_source, "set", E_property->get()->name, value);
+ }
+ }
+ }
+ }
+ undo_redo->add_undo_method(this, "_set_selection_from_array", _get_selection_as_array());
+ undo_redo->commit_action();
+ } break;
+ case DRAG_TYPE_MOVE_TILE:
+ if (drag_current_tile != drag_start_tile_shape.position) {
+ undo_redo->create_action(TTR("Move a tile"));
+ undo_redo->add_do_method(tile_set_atlas_source, "move_tile_in_atlas", drag_start_tile_shape.position, drag_current_tile, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile));
+ undo_redo->add_do_method(this, "_set_selection_from_array", _get_selection_as_array());
+ undo_redo->add_undo_method(tile_set_atlas_source, "move_tile_in_atlas", drag_current_tile, drag_start_tile_shape.position, drag_start_tile_shape.size);
+ Array array;
+ array.push_back(drag_start_tile_shape.position);
+ array.push_back(0);
+ undo_redo->add_undo_method(this, "_set_selection_from_array", array);
+ undo_redo->commit_action(false);
+ }
+ break;
+ case DRAG_TYPE_RECT_SELECT: {
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ ERR_FAIL_COND(start_base_tiles_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ ERR_FAIL_COND(new_base_tiles_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS);
+
+ Rect2i region = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
+ region.size += Vector2i(1, 1);
+
+ undo_redo->create_action(TTR("Select tiles"));
+ undo_redo->add_undo_method(this, "_set_selection_from_array", _get_selection_as_array());
+
+ // Determine if we clear, then add or remove to the selection.
+ bool add_to_selection = true;
+ if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ Vector2i coords = tile_set_atlas_source->get_tile_at_coords(start_base_tiles_coords);
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (selection.has({ coords, 0 })) {
+ add_to_selection = false;
+ }
+ }
+ } else {
+ selection.clear();
+ }
+
+ // Modify the selection.
+ for (int x = region.position.x; x < region.get_end().x; x++) {
+ for (int y = region.position.y; y < region.get_end().y; y++) {
+ Vector2i coords = Vector2i(x, y);
+ coords = tile_set_atlas_source->get_tile_at_coords(coords);
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ if (add_to_selection && !selection.has({ coords, 0 })) {
+ selection.insert({ coords, 0 });
+ } else if (!add_to_selection && selection.has({ coords, 0 })) {
+ selection.erase({ coords, 0 });
+ }
+ }
+ }
+ }
+ _update_tile_inspector();
+ _update_tile_id_label();
+ undo_redo->add_do_method(this, "_set_selection_from_array", _get_selection_as_array());
+ undo_redo->commit_action(false);
+ break;
+ }
+ case DRAG_TYPE_RESIZE_TOP_LEFT:
+ case DRAG_TYPE_RESIZE_TOP:
+ case DRAG_TYPE_RESIZE_TOP_RIGHT:
+ case DRAG_TYPE_RESIZE_RIGHT:
+ case DRAG_TYPE_RESIZE_BOTTOM_RIGHT:
+ case DRAG_TYPE_RESIZE_BOTTOM:
+ case DRAG_TYPE_RESIZE_BOTTOM_LEFT:
+ case DRAG_TYPE_RESIZE_LEFT:
+ if (drag_start_tile_shape != Rect2i(drag_current_tile, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile))) {
+ undo_redo->create_action(TTR("Resize a tile"));
+ undo_redo->add_do_method(tile_set_atlas_source, "move_tile_in_atlas", drag_start_tile_shape.position, drag_current_tile, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile));
+ undo_redo->add_do_method(this, "_set_selection_from_array", _get_selection_as_array());
+ undo_redo->add_undo_method(tile_set_atlas_source, "move_tile_in_atlas", drag_current_tile, drag_start_tile_shape.position, drag_start_tile_shape.size);
+ Array array;
+ array.push_back(drag_start_tile_shape.position);
+ array.push_back(0);
+ undo_redo->add_undo_method(this, "_set_selection_from_array", array);
+ undo_redo->commit_action(false);
+ }
+ break;
+ default:
+ break;
+ }
+
+ drag_modified_tiles.clear();
+ drag_type = DRAG_TYPE_NONE;
+ tile_atlas_control->set_default_cursor_shape(CURSOR_ARROW);
+}
+
+Map<Vector2i, List<const PropertyInfo *>> TileSetAtlasSourceEditor::_group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas) {
+ // Group properties per tile.
+ Map<Vector2i, List<const PropertyInfo *>> per_tile;
+ for (const List<PropertyInfo>::Element *E_property = r_list.front(); E_property; E_property = E_property->next()) {
+ Vector<String> components = String(E_property->get().name).split("/", true, 1);
+ if (components.size() >= 1) {
+ Vector<String> coord_arr = components[0].split(":");
+ if (coord_arr.size() == 2 && coord_arr[0].is_valid_integer() && coord_arr[1].is_valid_integer()) {
+ Vector2i coords = Vector2i(coord_arr[0].to_int(), coord_arr[1].to_int());
+ per_tile[coords].push_back(&(E_property->get()));
+ }
+ }
+ }
+ return per_tile;
+}
+
+void TileSetAtlasSourceEditor::_menu_option(int p_option) {
+ switch (p_option) {
+ case TILE_DELETE: {
+ List<PropertyInfo> list;
+ tile_set_atlas_source->get_property_list(&list);
+ Map<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source);
+ undo_redo->create_action(TTR("Remove tile"));
+
+ // Remove tiles
+ Set<Vector2i> removed;
+ for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) {
+ TileSelection selected = E->get();
+ if (selected.alternative == 0) {
+ // Remove a tile.
+ undo_redo->add_do_method(tile_set_atlas_source, "remove_tile", selected.tile);
+ undo_redo->add_undo_method(tile_set_atlas_source, "create_tile", selected.tile);
+ removed.insert(selected.tile);
+ if (per_tile.has(selected.tile)) {
+ for (List<const PropertyInfo *>::Element *E_property = per_tile[selected.tile].front(); E_property; E_property = E_property->next()) {
+ String property = E_property->get()->name;
+ Variant value = tile_set_atlas_source->get(property);
+ if (value.get_type() != Variant::NIL) {
+ undo_redo->add_undo_method(tile_set_atlas_source, "set", E_property->get()->name, value);
+ }
+ }
+ }
+ }
+ }
+
+ // Remove alternatives
+ for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) {
+ TileSelection selected = E->get();
+ if (selected.alternative > 0 && !removed.has(selected.tile)) {
+ // Remove an alternative tile.
+ undo_redo->add_do_method(tile_set_atlas_source, "remove_alternative_tile", selected.tile, selected.alternative);
+ undo_redo->add_undo_method(tile_set_atlas_source, "create_alternative_tile", selected.tile, selected.alternative);
+ if (per_tile.has(selected.tile)) {
+ for (List<const PropertyInfo *>::Element *E_property = per_tile[selected.tile].front(); E_property; E_property = E_property->next()) {
+ Vector<String> components = E_property->get()->name.split("/", true, 2);
+ if (components.size() >= 2 && components[1].is_valid_integer() && components[1].to_int() == selected.alternative) {
+ String property = E_property->get()->name;
+ Variant value = tile_set_atlas_source->get(property);
+ if (value.get_type() != Variant::NIL) {
+ undo_redo->add_undo_method(tile_set_atlas_source, "set", E_property->get()->name, value);
+ }
+ }
+ }
+ }
+ }
+ }
+ undo_redo->commit_action();
+ _update_fix_selected_and_hovered_tiles();
+ _update_tile_id_label();
+ } break;
+ case TILE_CREATE: {
+ undo_redo->create_action(TTR("Create a tile"));
+ undo_redo->add_do_method(tile_set_atlas_source, "create_tile", menu_option_coords);
+ Array array;
+ array.push_back(menu_option_coords);
+ array.push_back(0);
+ undo_redo->add_do_method(this, "_set_selection_from_array", array);
+ undo_redo->add_undo_method(tile_set_atlas_source, "remove_tile", menu_option_coords);
+ undo_redo->add_undo_method(this, "_set_selection_from_array", _get_selection_as_array());
+ undo_redo->commit_action();
+ _update_tile_id_label();
+ } break;
+ case TILE_CREATE_ALTERNATIVE: {
+ undo_redo->create_action(TTR("Create tile alternatives"));
+ Array array;
+ for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) {
+ if (E->get().alternative == 0) {
+ int next_id = tile_set_atlas_source->get_next_alternative_tile_id(E->get().tile);
+ undo_redo->add_do_method(tile_set_atlas_source, "create_alternative_tile", E->get().tile, next_id);
+ array.push_back(E->get().tile);
+ array.push_back(next_id);
+ undo_redo->add_undo_method(tile_set_atlas_source, "remove_alternative_tile", E->get().tile, next_id);
+ }
+ }
+ undo_redo->add_do_method(this, "_set_selection_from_array", array);
+ undo_redo->add_undo_method(this, "_set_selection_from_array", _get_selection_as_array());
+ undo_redo->commit_action();
+ _update_tile_id_label();
+ } break;
+ case ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE: {
+ tile_set_atlas_source->clear_tiles_outside_texture();
+ } break;
+ case ADVANCED_AUTO_CREATE_TILES: {
+ _auto_create_tiles();
+ } break;
+ case ADVANCED_AUTO_REMOVE_TILES: {
+ _auto_remove_tiles();
+ } break;
+ }
+}
+
+void TileSetAtlasSourceEditor::_unhandled_key_input(const Ref<InputEvent> &p_event) {
+ // Check for shortcuts.
+ if (ED_IS_SHORTCUT("tiles_editor/delete_tile", p_event)) {
+ if (tools_button_group->get_pressed_button() == tool_select_button && !selection.is_empty()) {
+ _menu_option(TILE_DELETE);
+ accept_event();
+ }
+ }
+}
+
+void TileSetAtlasSourceEditor::_set_selection_from_array(Array p_selection) {
+ ERR_FAIL_COND((p_selection.size() % 2) != 0);
+ selection.clear();
+ for (int i = 0; i < p_selection.size() / 2; i++) {
+ TileSelection selected = { p_selection[i * 2], p_selection[i * 2 + 1] };
+ if (tile_set_atlas_source->has_tile(selected.tile) && tile_set_atlas_source->has_alternative_tile(selected.tile, selected.alternative)) {
+ selection.insert(selected);
+ }
+ }
+ _update_tile_inspector();
+ _update_tile_id_label();
+ _update_atlas_view();
+}
+
+Array TileSetAtlasSourceEditor::_get_selection_as_array() {
+ Array output;
+ for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) {
+ output.push_back(E->get().tile);
+ output.push_back(E->get().alternative);
+ }
+ return output;
+}
+
+void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
+ // Draw the selected tile.
+ if (tools_button_group->get_pressed_button() == tool_select_button) {
+ for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) {
+ TileSelection selected = E->get();
+ if (selected.alternative == 0) {
+ // Draw the rect.
+ Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
+ tile_atlas_control->draw_rect(region, Color(0.2, 0.2, 1.0), false);
+ }
+ }
+
+ if (selection.size() == 1) {
+ // Draw the resize handles (only when it's possible to expand).
+ TileSelection selected = selection.front()->get();
+ Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
+ Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
+ Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
+ Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
+ Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
+ Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
+ bool can_grow[4];
+ for (int i = 0; i < 4; i++) {
+ can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
+ can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
+ }
+ for (int i = 0; i < 4; i++) {
+ Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i];
+ if (can_grow[i] && can_grow[(i + 3) % 4]) {
+ tile_atlas_control->draw_texture_rect(resize_handle, Rect2(pos, zoomed_size), false);
+ } else {
+ tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2(pos, zoomed_size), false);
+ }
+ Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4];
+ if (can_grow[i]) {
+ tile_atlas_control->draw_texture_rect(resize_handle, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
+ } else {
+ tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
+ }
+ }
+ }
+ }
+
+ if (drag_type == DRAG_TYPE_REMOVE_TILES) {
+ // Draw the tiles to be removed.
+ for (Set<Vector2i>::Element *E = drag_modified_tiles.front(); E; E = E->next()) {
+ tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(E->get()), Color(0.0, 0.0, 0.0), false);
+ }
+ } else if (drag_type == DRAG_TYPE_RECT_SELECT || drag_type == DRAG_TYPE_REMOVE_TILES_USING_RECT) {
+ // Draw tiles to be removed.
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
+ area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size()));
+
+ Color color = Color(0.0, 0.0, 0.0);
+ if (drag_type == DRAG_TYPE_RECT_SELECT) {
+ color = Color(0.5, 0.5, 1.0);
+ }
+
+ Set<Vector2i> to_paint;
+ for (int x = area.get_position().x; x < area.get_end().x; x++) {
+ for (int y = area.get_position().y; y < area.get_end().y; y++) {
+ Vector2i coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ to_paint.insert(coords);
+ }
+ }
+ }
+
+ for (Set<Vector2i>::Element *E = to_paint.front(); E; E = E->next()) {
+ Vector2i coords = E->get();
+ tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(coords), color, false);
+ }
+ } else if (drag_type == DRAG_TYPE_CREATE_TILES_USING_RECT) {
+ // Draw tiles to be created.
+ Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
+ Vector2i tile_size = tile_set_atlas_source->get_texture_region_size();
+
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
+ area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size()));
+ for (int x = area.get_position().x; x < area.get_end().x; x++) {
+ for (int y = area.get_position().y; y < area.get_end().y; y++) {
+ Vector2i coords = Vector2i(x, y);
+ if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ Vector2i origin = margins + (coords * (tile_size + separation));
+ tile_atlas_control->draw_rect(Rect2i(origin, tile_size), Color(1.0, 1.0, 1.0), false);
+ }
+ }
+ }
+ }
+
+ // Draw the hovered tile.
+ if (drag_type == DRAG_TYPE_REMOVE_TILES_USING_RECT || drag_type == DRAG_TYPE_CREATE_TILES_USING_RECT) {
+ // Draw the rect.
+ Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos);
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
+ Rect2i area = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
+ area.set_end((area.get_end() + Vector2i(1, 1)).min(tile_set_atlas_source->get_atlas_grid_size()));
+ Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
+ Vector2i tile_size = tile_set_atlas_source->get_texture_region_size();
+ Vector2i origin = margins + (area.position * (tile_size + separation));
+ tile_atlas_control->draw_rect(Rect2i(origin, area.size * tile_size), Color(1.0, 1.0, 1.0), false);
+ } else {
+ Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
+ if (hovered_base_tile_coords.x >= 0 && hovered_base_tile_coords.y >= 0 && hovered_base_tile_coords.x < grid_size.x && hovered_base_tile_coords.y < grid_size.y) {
+ Vector2i hovered_tile = tile_set_atlas_source->get_tile_at_coords(hovered_base_tile_coords);
+ if (hovered_tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ // Draw existing hovered tile.
+ tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile), Color(1.0, 1.0, 1.0), false);
+ } else {
+ // Draw empty tile, only in add/remove tiles mode.
+ if (tools_button_group->get_pressed_button() == tool_add_remove_button || tools_button_group->get_pressed_button() == tool_add_remove_rect_button) {
+ Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
+ Vector2i tile_size = tile_set_atlas_source->get_texture_region_size();
+ Vector2i origin = margins + (hovered_base_tile_coords * (tile_size + separation));
+ tile_atlas_control->draw_rect(Rect2i(origin, tile_size), Color(1.0, 1.0, 1.0), false);
+ }
+ }
+ }
+ }
+}
+
+void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() {
+ // Draw the preview of the selected property.
+ TileDataEditor *tile_data_editor = TileSetEditor::get_singleton()->get_tile_data_editor(selected_property);
+ if (tile_data_editor && tile_inspector->is_visible_in_tree()) {
+ for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
+ Vector2i coords = tile_set_atlas_source->get_tile_id(i);
+ Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(coords);
+ Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+
+ Transform2D xform = tile_atlas_control->get_parent_control()->get_transform();
+ xform.translate(position);
+
+ tile_data_editor->draw_over_tile(tile_atlas_control_unscaled, xform, *tile_set, tile_set_atlas_source_id, coords, 0, selected_property);
+ }
+ }
+}
+
+void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event) {
+ // Update the hovered alternative tile.
+ hovered_alternative_tile_coords = tile_atlas_view->get_alternative_tile_at_pos(alternative_tiles_control->get_local_mouse_position());
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ tile_atlas_control->update();
+ tile_atlas_control_unscaled->update();
+ alternative_tiles_control->update();
+ alternative_tiles_control_unscaled->update();
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid()) {
+ drag_type = DRAG_TYPE_NONE;
+
+ Vector2 mouse_local_pos = alternative_tiles_control->get_local_mouse_position();
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->is_pressed()) {
+ // Left click pressed.
+ if (tools_button_group->get_pressed_button() == tool_select_button) {
+ Vector3 tile = tile_atlas_view->get_alternative_tile_at_pos(mouse_local_pos);
+
+ selection.clear();
+ TileSelection selected = { Vector2i(tile.x, tile.y), int(tile.z) };
+ if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ selection.insert(selected);
+ }
+
+ _update_tile_inspector();
+ _update_tile_id_label();
+ }
+ }
+ } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ if (mb->is_pressed()) {
+ // Right click pressed
+ Vector3 tile = tile_atlas_view->get_alternative_tile_at_pos(mouse_local_pos);
+
+ selection.clear();
+ TileSelection selected = { Vector2i(tile.x, tile.y), int(tile.z) };
+ if (selected.tile != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ selection.insert(selected);
+ }
+
+ _update_tile_inspector();
+ _update_tile_id_label();
+
+ if (selection.size() == 1) {
+ selected = selection.front()->get();
+ menu_option_coords = selected.tile;
+ menu_option_alternative = selected.alternative;
+ alternative_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i()));
+ }
+ }
+ }
+ tile_atlas_control->update();
+ tile_atlas_control_unscaled->update();
+ alternative_tiles_control->update();
+ alternative_tiles_control_unscaled->update();
+ }
+}
+
+void TileSetAtlasSourceEditor::_tile_alternatives_control_mouse_exited() {
+ hovered_alternative_tile_coords = Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ tile_atlas_control->update();
+ tile_atlas_control_unscaled->update();
+ alternative_tiles_control->update();
+ alternative_tiles_control_unscaled->update();
+}
+
+void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() {
+ // Update the hovered alternative tile.
+ if (tools_button_group->get_pressed_button() == tool_select_button) {
+ // Draw hovered tile.
+ Vector2i coords = Vector2(hovered_alternative_tile_coords.x, hovered_alternative_tile_coords.y);
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ Rect2i rect = tile_atlas_view->get_alternative_tile_rect(coords, hovered_alternative_tile_coords.z);
+ if (rect != Rect2i()) {
+ alternative_tiles_control->draw_rect(rect, Color(1.0, 1.0, 1.0), false);
+ }
+ }
+
+ // Draw selected tile.
+ for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) {
+ TileSelection selected = E->get();
+ if (selected.alternative >= 1) {
+ Rect2i rect = tile_atlas_view->get_alternative_tile_rect(selected.tile, selected.alternative);
+ if (rect != Rect2i()) {
+ alternative_tiles_control->draw_rect(rect, Color(0.2, 0.2, 1.0), false);
+ }
+ }
+ }
+ }
+}
+
+void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() {
+ //TODO
+}
+
+void TileSetAtlasSourceEditor::_tile_set_atlas_source_changed() {
+ tile_set_atlas_source_changed_needs_update = true;
+}
+
+void TileSetAtlasSourceEditor::_atlas_source_proxy_object_changed(String p_what) {
+ if (p_what == "texture" && !atlas_source_proxy_object->get("texture").is_null()) {
+ confirm_auto_create_tiles->popup_centered();
+ } else if (p_what == "id") {
+ emit_signal("source_id_changed", atlas_source_proxy_object->get_id());
+ }
+}
+
+void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
+ UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
+ ERR_FAIL_COND(!undo_redo);
+
+#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, tile_data->get(property));
+
+ TileProxyObject *tile_data = Object::cast_to<TileProxyObject>(p_edited);
+ if (tile_data) {
+ Vector<String> components = String(p_property).split("/", true, 2);
+ if (components.size() == 2 && components[1] == "shapes_count") {
+ int layer_index = components[0].trim_prefix("physics_layer_").to_int();
+ int new_shapes_count = p_new_value;
+ int old_shapes_count = tile_data->get(vformat("physics_layer_%d/shapes_count", layer_index));
+ if (new_shapes_count < old_shapes_count) {
+ for (int i = new_shapes_count - 1; i < old_shapes_count; i++) {
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/shape", layer_index, i));
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way", layer_index, i));
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way_margin", layer_index, i));
+ }
+ }
+ }
+ }
+#undef ADD_UNDO
+}
+
+void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
+ ERR_FAIL_COND(!p_tile_set.is_valid());
+ ERR_FAIL_COND(!p_tile_set_atlas_source);
+ ERR_FAIL_COND(p_source_id < 0);
+ ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
+
+ if (p_tile_set == tile_set && p_tile_set_atlas_source == tile_set_atlas_source && p_source_id == tile_set_atlas_source_id) {
+ return;
+ }
+
+ // Remove listener for old objects.
+ if (tile_set_atlas_source) {
+ tile_set_atlas_source->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_atlas_source_changed));
+ }
+
+ // Clear the selection.
+ selection.clear();
+
+ // Change the edited object.
+ tile_set = p_tile_set;
+ tile_set_atlas_source = p_tile_set_atlas_source;
+ tile_set_atlas_source_id = p_source_id;
+
+ // Add the listener again.
+ if (tile_set_atlas_source) {
+ tile_set_atlas_source->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_atlas_source_changed));
+ }
+
+ // Update everything.
+ _update_source_inspector();
+
+ // Update the selected tile.
+ _update_fix_selected_and_hovered_tiles();
+ _update_tile_id_label();
+ _update_atlas_view();
+ _update_tile_inspector();
+}
+
+void TileSetAtlasSourceEditor::init_source() {
+ confirm_auto_create_tiles->popup_centered();
+}
+
+void TileSetAtlasSourceEditor::_auto_create_tiles() {
+ if (!tile_set_atlas_source) {
+ return;
+ }
+
+ Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
+ if (texture.is_valid()) {
+ Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
+ Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
+ Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
+ undo_redo->create_action(TTR("Create tiles in non-transparent texture regions"));
+ for (int y = 0; y < grid_size.y; y++) {
+ for (int x = 0; x < grid_size.x; x++) {
+ // Check if we have a tile at the coord
+ Vector2i coords = Vector2i(x, y);
+ if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ // Check if the texture is empty at the given coords.
+ Rect2i region = Rect2i(margins + (coords * (texture_region_size + separation)), texture_region_size);
+ bool is_opaque = false;
+ for (int region_x = region.get_position().x; region_x < region.get_end().x; region_x++) {
+ for (int region_y = region.get_position().y; region_y < region.get_end().y; region_y++) {
+ if (texture->is_pixel_opaque(region_x, region_y)) {
+ is_opaque = true;
+ break;
+ }
+ }
+ if (is_opaque) {
+ break;
+ }
+ }
+
+ // If we do have opaque pixels, create a tile.
+ if (is_opaque) {
+ undo_redo->add_do_method(tile_set_atlas_source, "create_tile", coords);
+ undo_redo->add_undo_method(tile_set_atlas_source, "remove_tile", coords);
+ }
+ }
+ }
+ }
+ undo_redo->commit_action();
+ }
+}
+
+void TileSetAtlasSourceEditor::_auto_remove_tiles() {
+ if (!tile_set_atlas_source) {
+ return;
+ }
+
+ Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
+ if (texture.is_valid()) {
+ Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
+ Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
+ Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
+
+ undo_redo->create_action(TTR("Remove tiles in fully transparent texture regions"));
+
+ List<PropertyInfo> list;
+ tile_set_atlas_source->get_property_list(&list);
+ Map<Vector2i, List<const PropertyInfo *>> per_tile = _group_properties_per_tiles(list, tile_set_atlas_source);
+
+ for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
+ Vector2i coords = tile_set_atlas_source->get_tile_id(i);
+ Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(coords);
+
+ // Skip tiles outside texture.
+ if ((coords.x + size_in_atlas.x) > grid_size.x || (coords.y + size_in_atlas.y) > grid_size.y) {
+ continue;
+ }
+
+ // Check if the texture is empty at the given coords.
+ Rect2i region = Rect2i(margins + (coords * (texture_region_size + separation)), texture_region_size * size_in_atlas);
+ bool is_opaque = false;
+ for (int region_x = region.get_position().x; region_x < region.get_end().x; region_x++) {
+ for (int region_y = region.get_position().y; region_y < region.get_end().y; region_y++) {
+ if (texture->is_pixel_opaque(region_x, region_y)) {
+ is_opaque = true;
+ break;
+ }
+ }
+ if (is_opaque) {
+ break;
+ }
+ }
+
+ // If we do have opaque pixels, create a tile.
+ if (!is_opaque) {
+ undo_redo->add_do_method(tile_set_atlas_source, "remove_tile", coords);
+ undo_redo->add_undo_method(tile_set_atlas_source, "create_tile", coords);
+ if (per_tile.has(coords)) {
+ for (List<const PropertyInfo *>::Element *E_property = per_tile[coords].front(); E_property; E_property = E_property->next()) {
+ String property = E_property->get()->name;
+ Variant value = tile_set_atlas_source->get(property);
+ if (value.get_type() != Variant::NIL) {
+ undo_redo->add_undo_method(tile_set_atlas_source, "set", E_property->get()->name, value);
+ }
+ }
+ }
+ }
+ }
+ undo_redo->commit_action();
+ }
+}
+
+void TileSetAtlasSourceEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED:
+ tool_select_button->set_icon(get_theme_icon("ToolSelect", "EditorIcons"));
+ tool_add_remove_button->set_icon(get_theme_icon("EditAddRemove", "EditorIcons"));
+ tool_add_remove_rect_button->set_icon(get_theme_icon("RectangleAddRemove", "EditorIcons"));
+
+ tools_settings_erase_button->set_icon(get_theme_icon("Eraser", "EditorIcons"));
+
+ tool_advanced_menu_buttom->set_icon(get_theme_icon("Tools", "EditorIcons"));
+
+ resize_handle = get_theme_icon("EditorHandle", "EditorIcons");
+ resize_handle_disabled = get_theme_icon("EditorHandleDisabled", "EditorIcons");
+ break;
+ case NOTIFICATION_INTERNAL_PROCESS:
+ if (tile_set_atlas_source_changed_needs_update) {
+ // Update everything.
+ _update_source_inspector();
+
+ // Update the selected tile.
+ _update_fix_selected_and_hovered_tiles();
+ _update_tile_id_label();
+ _update_atlas_view();
+ _update_tile_inspector();
+
+ tile_set_atlas_source_changed_needs_update = false;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void TileSetAtlasSourceEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &TileSetAtlasSourceEditor::_unhandled_key_input);
+ ClassDB::bind_method(D_METHOD("_set_selection_from_array"), &TileSetAtlasSourceEditor::_set_selection_from_array);
+
+ ADD_SIGNAL(MethodInfo("source_id_changed", PropertyInfo(Variant::INT, "source_id")));
+}
+
+TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
+ set_process_unhandled_key_input(true);
+ set_process_internal(true);
+
+ // -- Right side --
+ HSplitContainer *split_container_right_side = memnew(HSplitContainer);
+ split_container_right_side->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_child(split_container_right_side);
+
+ // Middle panel.
+ ScrollContainer *middle_panel = memnew(ScrollContainer);
+ middle_panel->set_enable_h_scroll(false);
+ middle_panel->set_custom_minimum_size(Size2i(200, 0) * EDSCALE);
+ split_container_right_side->add_child(middle_panel);
+
+ VBoxContainer *middle_vbox_container = memnew(VBoxContainer);
+ middle_vbox_container->set_h_size_flags(SIZE_EXPAND_FILL);
+ middle_panel->add_child(middle_vbox_container);
+
+ // Tile inspector.
+ tile_inspector_label = memnew(Label);
+ tile_inspector_label->set_text(TTR("Tile properties:"));
+ tile_inspector_label->hide();
+ middle_vbox_container->add_child(tile_inspector_label);
+
+ tile_proxy_object = memnew(TileProxyObject(this));
+ tile_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view).unbind(1));
+
+ tile_inspector = memnew(EditorInspector);
+ tile_inspector->set_undo_redo(undo_redo);
+ tile_inspector->set_enable_v_scroll(false);
+ tile_inspector->edit(tile_proxy_object);
+ tile_inspector->set_use_folding(true);
+ tile_inspector->connect("property_selected", callable_mp(this, &TileSetAtlasSourceEditor::_inspector_property_selected));
+ middle_vbox_container->add_child(tile_inspector);
+
+ // Atlas source inspector.
+ atlas_source_inspector_label = memnew(Label);
+ atlas_source_inspector_label->set_text(TTR("Atlas properties:"));
+ middle_vbox_container->add_child(atlas_source_inspector_label);
+
+ atlas_source_proxy_object = memnew(TileSetAtlasSourceProxyObject());
+ atlas_source_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_atlas_source_proxy_object_changed));
+
+ atlas_source_inspector = memnew(EditorInspector);
+ atlas_source_inspector->set_undo_redo(undo_redo);
+ atlas_source_inspector->set_enable_v_scroll(false);
+ atlas_source_inspector->edit(atlas_source_proxy_object);
+ middle_vbox_container->add_child(atlas_source_inspector);
+
+ // Right panel.
+ VBoxContainer *right_panel = memnew(VBoxContainer);
+ right_panel->set_h_size_flags(SIZE_EXPAND_FILL);
+ right_panel->set_v_size_flags(SIZE_EXPAND_FILL);
+ split_container_right_side->add_child(right_panel);
+
+ // -- Dialogs --
+ confirm_auto_create_tiles = memnew(AcceptDialog);
+ confirm_auto_create_tiles->set_title(TTR("Create tiles automatically in non-transparent texture regions?"));
+ confirm_auto_create_tiles->set_text(TTR("The atlas's texture was modified.\nWould you like to automatically create tiles in the atlas?"));
+ confirm_auto_create_tiles->get_ok_button()->set_text(TTR("Yes"));
+ confirm_auto_create_tiles->add_cancel_button()->set_text(TTR("No"));
+ confirm_auto_create_tiles->connect("confirmed", callable_mp(this, &TileSetAtlasSourceEditor::_auto_create_tiles));
+ add_child(confirm_auto_create_tiles);
+
+ // -- Toolbox --
+ tools_button_group.instance();
+
+ toolbox = memnew(HBoxContainer);
+ right_panel->add_child(toolbox);
+
+ tool_select_button = memnew(Button);
+ tool_select_button->set_flat(true);
+ tool_select_button->set_toggle_mode(true);
+ tool_select_button->set_pressed(true);
+ tool_select_button->set_button_group(tools_button_group);
+ tool_select_button->set_tooltip(TTR("Select tiles"));
+ tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles));
+ tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_id_label));
+ tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_inspector));
+ tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view));
+ tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_toolbar));
+ toolbox->add_child(tool_select_button);
+
+ tool_add_remove_button = memnew(Button);
+ tool_add_remove_button->set_flat(true);
+ tool_add_remove_button->set_toggle_mode(true);
+ tool_add_remove_button->set_button_group(tools_button_group);
+ tool_add_remove_button->set_tooltip(TTR("Add/Remove tiles tool (use the shift key to create big tiles)"));
+ tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles));
+ tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_id_label));
+ tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_inspector));
+ tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view));
+ tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_toolbar));
+ toolbox->add_child(tool_add_remove_button);
+
+ tool_add_remove_rect_button = memnew(Button);
+ tool_add_remove_rect_button->set_flat(true);
+ tool_add_remove_rect_button->set_toggle_mode(true);
+ tool_add_remove_rect_button->set_button_group(tools_button_group);
+ tool_add_remove_rect_button->set_tooltip(TTR("Add/Remove tiles rectangle tool (use the shift key to create big tiles)"));
+ tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles));
+ tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_id_label));
+ tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_inspector));
+ tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view));
+ tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_toolbar));
+ toolbox->add_child(tool_add_remove_rect_button);
+
+ // Tool settings.
+ tool_settings = memnew(HBoxContainer);
+ toolbox->add_child(tool_settings);
+
+ tool_settings_vsep = memnew(VSeparator);
+ tool_settings->add_child(tool_settings_vsep);
+
+ tools_settings_erase_button = memnew(Button);
+ tools_settings_erase_button->set_flat(true);
+ tools_settings_erase_button->set_toggle_mode(true);
+ tools_settings_erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", KEY_E));
+ tools_settings_erase_button->set_shortcut_context(this);
+ tool_settings->add_child(tools_settings_erase_button);
+
+ VSeparator *tool_advanced_vsep = memnew(VSeparator);
+ toolbox->add_child(tool_advanced_vsep);
+
+ tool_advanced_menu_buttom = memnew(MenuButton);
+ tool_advanced_menu_buttom->set_flat(true);
+ tool_advanced_menu_buttom->get_popup()->add_item(TTR("Cleanup tiles outside texture"), ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE);
+ tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, true);
+ tool_advanced_menu_buttom->get_popup()->add_item(TTR("Create tiles in non-transparent texture regions."), ADVANCED_AUTO_CREATE_TILES);
+ tool_advanced_menu_buttom->get_popup()->add_item(TTR("Remove tiles in fully transparent texture regions."), ADVANCED_AUTO_REMOVE_TILES);
+ tool_advanced_menu_buttom->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
+ toolbox->add_child(tool_advanced_menu_buttom);
+
+ _update_toolbar();
+
+ // Right side of toolbar.
+ Control *middle_space = memnew(Control);
+ middle_space->set_h_size_flags(SIZE_EXPAND_FILL);
+ toolbox->add_child(middle_space);
+
+ tool_tile_id_label = memnew(Label);
+ tool_tile_id_label->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ toolbox->add_child(tool_tile_id_label);
+ _update_tile_id_label();
+
+ // Tile atlas view.
+ tile_atlas_view = memnew(TileAtlasView);
+ tile_atlas_view->set_h_size_flags(SIZE_EXPAND_FILL);
+ tile_atlas_view->set_v_size_flags(SIZE_EXPAND_FILL);
+ tile_atlas_view->connect("transform_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_view_transform));
+ tile_atlas_view->connect("transform_changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_view_transform_changed).unbind(2));
+ right_panel->add_child(tile_atlas_view);
+
+ base_tile_popup_menu = memnew(PopupMenu);
+ base_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), KEY_DELETE), TILE_DELETE);
+ base_tile_popup_menu->add_item(TTR("Create an Alternative Tile"), TILE_CREATE_ALTERNATIVE);
+ base_tile_popup_menu->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
+ tile_atlas_view->add_child(base_tile_popup_menu);
+
+ empty_base_tile_popup_menu = memnew(PopupMenu);
+ empty_base_tile_popup_menu->add_item(TTR("Create a Tile"), TILE_CREATE);
+ empty_base_tile_popup_menu->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
+ tile_atlas_view->add_child(empty_base_tile_popup_menu);
+
+ tile_atlas_control = memnew(Control);
+ tile_atlas_control->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_control_draw));
+ tile_atlas_control->connect("mouse_exited", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_control_mouse_exited));
+ tile_atlas_control->connect("gui_input", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_control_gui_input));
+ tile_atlas_view->add_control_over_atlas_tiles(tile_atlas_control);
+
+ tile_atlas_control_unscaled = memnew(Control);
+ tile_atlas_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ tile_atlas_control_unscaled->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw));
+ tile_atlas_view->add_control_over_atlas_tiles(tile_atlas_control_unscaled, false);
+ tile_atlas_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+
+ alternative_tile_popup_menu = memnew(PopupMenu);
+ alternative_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete_tile", TTR("Delete"), KEY_DELETE), TILE_DELETE);
+ alternative_tile_popup_menu->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
+ tile_atlas_view->add_child(alternative_tile_popup_menu);
+
+ alternative_tiles_control = memnew(Control);
+ alternative_tiles_control->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_alternatives_control_draw));
+ alternative_tiles_control->connect("mouse_exited", callable_mp(this, &TileSetAtlasSourceEditor::_tile_alternatives_control_mouse_exited));
+ alternative_tiles_control->connect("gui_input", callable_mp(this, &TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input));
+ tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control);
+
+ alternative_tiles_control_unscaled = memnew(Control);
+ alternative_tiles_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ alternative_tiles_control_unscaled->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw));
+ tile_atlas_view->add_control_over_atlas_tiles(alternative_tiles_control_unscaled, false);
+ alternative_tiles_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+
+ tile_atlas_view_missing_source_label = memnew(Label);
+ tile_atlas_view_missing_source_label->set_text(TTR("Add or select an atlas texture to the left panel."));
+ tile_atlas_view_missing_source_label->set_align(Label::ALIGN_CENTER);
+ tile_atlas_view_missing_source_label->set_valign(Label::VALIGN_CENTER);
+ tile_atlas_view_missing_source_label->set_h_size_flags(SIZE_EXPAND_FILL);
+ tile_atlas_view_missing_source_label->set_v_size_flags(SIZE_EXPAND_FILL);
+ tile_atlas_view_missing_source_label->hide();
+ right_panel->add_child(tile_atlas_view_missing_source_label);
+}
+
+TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() {
+ memdelete(tile_proxy_object);
+ memdelete(atlas_source_proxy_object);
+}
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
new file mode 100644
index 0000000000..ff68aa8288
--- /dev/null
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -0,0 +1,260 @@
+/*************************************************************************/
+/* tile_set_atlas_source_editor.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TILE_SET_ATLAS_SOURCE_EDITOR_H
+#define TILE_SET_ATLAS_SOURCE_EDITOR_H
+
+#include "tile_atlas_view.h"
+
+#include "editor/editor_node.h"
+#include "scene/gui/split_container.h"
+#include "scene/resources/tile_set.h"
+
+class TileSet;
+
+class TileSetAtlasSourceEditor : public HBoxContainer {
+ GDCLASS(TileSetAtlasSourceEditor, HBoxContainer);
+
+private:
+ // A class to store which tiles are selected.
+ struct TileSelection {
+ Vector2i tile = TileSetAtlasSource::INVALID_ATLAS_COORDS;
+ int alternative = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+
+ bool operator<(const TileSelection &p_other) const {
+ if (tile == p_other.tile) {
+ return alternative < p_other.alternative;
+ } else {
+ return tile < p_other.tile;
+ }
+ }
+ };
+
+ // -- Proxy object for an atlas source, needed by the inspector --
+ class TileSetAtlasSourceProxyObject : public Object {
+ GDCLASS(TileSetAtlasSourceProxyObject, Object);
+
+ private:
+ Ref<TileSet> tile_set;
+ TileSetAtlasSource *tile_set_atlas_source = nullptr;
+ int source_id = -1;
+
+ protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+ static void _bind_methods();
+
+ public:
+ void set_id(int p_id);
+ int get_id();
+
+ void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id);
+ };
+
+ // -- Proxy object for a tile, needed by the inspector --
+ class TileProxyObject : public Object {
+ GDCLASS(TileProxyObject, Object);
+
+ private:
+ TileSetAtlasSourceEditor *tiles_set_atlas_source_editor;
+
+ TileSetAtlasSource *tile_set_atlas_source = nullptr;
+ int source_id;
+ Set<TileSelection> tiles = Set<TileSelection>();
+
+ protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ static void _bind_methods();
+
+ public:
+ // Update the proxyed object.
+ void edit(TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id = -1, Set<TileSelection> p_tiles = Set<TileSelection>());
+
+ TileProxyObject(TileSetAtlasSourceEditor *p_tiles_editor_source_tab) {
+ tiles_set_atlas_source_editor = p_tiles_editor_source_tab;
+ }
+ };
+
+ Ref<TileSet> tile_set;
+ TileSetAtlasSource *tile_set_atlas_source = nullptr;
+ int tile_set_atlas_source_id = -1;
+
+ UndoRedo *undo_redo = EditorNode::get_undo_redo();
+
+ bool tile_set_atlas_source_changed_needs_update = false;
+
+ // -- Inspector --
+ TileProxyObject *tile_proxy_object;
+ Label *tile_inspector_label;
+ EditorInspector *tile_inspector;
+ String selected_property;
+ void _inspector_property_selected(String p_property);
+
+ TileSetAtlasSourceProxyObject *atlas_source_proxy_object;
+ Label *atlas_source_inspector_label;
+ EditorInspector *atlas_source_inspector;
+
+ // -- Atlas view --
+ HBoxContainer *toolbox;
+ Label *tile_atlas_view_missing_source_label;
+ TileAtlasView *tile_atlas_view;
+
+ // Dragging
+ enum DragType {
+ DRAG_TYPE_NONE = 0,
+ DRAG_TYPE_CREATE_TILES,
+ DRAG_TYPE_CREATE_TILES_USING_RECT,
+ DRAG_TYPE_CREATE_BIG_TILE,
+ DRAG_TYPE_REMOVE_TILES,
+ DRAG_TYPE_REMOVE_TILES_USING_RECT,
+
+ DRAG_TYPE_MOVE_TILE,
+
+ DRAG_TYPE_RECT_SELECT,
+
+ // Warning: keep in this order.
+ DRAG_TYPE_RESIZE_TOP_LEFT,
+ DRAG_TYPE_RESIZE_TOP,
+ DRAG_TYPE_RESIZE_TOP_RIGHT,
+ DRAG_TYPE_RESIZE_RIGHT,
+ DRAG_TYPE_RESIZE_BOTTOM_RIGHT,
+ DRAG_TYPE_RESIZE_BOTTOM,
+ DRAG_TYPE_RESIZE_BOTTOM_LEFT,
+ DRAG_TYPE_RESIZE_LEFT,
+ };
+ DragType drag_type = DRAG_TYPE_NONE;
+ Vector2i drag_start_mouse_pos;
+ Vector2i drag_last_mouse_pos;
+ Vector2i drag_current_tile;
+
+ Rect2i drag_start_tile_shape;
+ Set<Vector2i> drag_modified_tiles;
+ void _end_dragging();
+
+ Map<Vector2i, List<const PropertyInfo *>> _group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas);
+
+ // Popup functions.
+ enum MenuOptions {
+ TILE_CREATE,
+ TILE_CREATE_ALTERNATIVE,
+ TILE_DELETE,
+
+ ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE,
+ ADVANCED_AUTO_CREATE_TILES,
+ ADVANCED_AUTO_REMOVE_TILES,
+ };
+ Vector2i menu_option_coords;
+ int menu_option_alternative = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ void _menu_option(int p_option);
+
+ // Tool buttons.
+ Ref<ButtonGroup> tools_button_group;
+ Button *tool_select_button;
+ Button *tool_add_remove_button;
+ Button *tool_add_remove_rect_button;
+ Label *tool_tile_id_label;
+
+ HBoxContainer *tool_settings;
+ VSeparator *tool_settings_vsep;
+ Button *tools_settings_erase_button;
+
+ MenuButton *tool_advanced_menu_buttom;
+
+ // Selection.
+ Set<TileSelection> selection;
+
+ void _set_selection_from_array(Array p_selection);
+ Array _get_selection_as_array();
+
+ // A control on the tile atlas to draw and handle input events.
+ Vector2i hovered_base_tile_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS;
+
+ PopupMenu *base_tile_popup_menu;
+ PopupMenu *empty_base_tile_popup_menu;
+ Ref<Texture2D> resize_handle;
+ Ref<Texture2D> resize_handle_disabled;
+ Control *tile_atlas_control;
+ Control *tile_atlas_control_unscaled;
+ void _tile_atlas_control_draw();
+ void _tile_atlas_control_unscaled_draw();
+ void _tile_atlas_control_mouse_exited();
+ void _tile_atlas_control_gui_input(const Ref<InputEvent> &p_event);
+ void _tile_atlas_view_transform_changed();
+
+ // A control over the alternative tiles.
+ Vector3i hovered_alternative_tile_coords = Vector3i(TileSetAtlasSource::INVALID_ATLAS_COORDS.x, TileSetAtlasSource::INVALID_ATLAS_COORDS.y, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+
+ PopupMenu *alternative_tile_popup_menu;
+ Control *alternative_tiles_control;
+ Control *alternative_tiles_control_unscaled;
+ void _tile_alternatives_control_draw();
+ void _tile_alternatives_control_unscaled_draw();
+ void _tile_alternatives_control_mouse_exited();
+ void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
+
+ // -- Update functions --
+ void _update_tile_id_label();
+ void _update_source_inspector();
+ void _update_fix_selected_and_hovered_tiles();
+ void _update_tile_inspector();
+ void _update_manage_tile_properties_button();
+ void _update_atlas_view();
+ void _update_toolbar();
+
+ // -- input events --
+ void _unhandled_key_input(const Ref<InputEvent> &p_event);
+
+ // -- Misc --
+ void _auto_create_tiles();
+ void _auto_remove_tiles();
+ AcceptDialog *confirm_auto_create_tiles;
+
+ void _tile_set_atlas_source_changed();
+ void _atlas_source_proxy_object_changed(String p_what);
+
+ void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_source, int p_source_id);
+ void init_source();
+
+ TileSetAtlasSourceEditor();
+ ~TileSetAtlasSourceEditor();
+};
+
+#endif // TILE_SET_ATLAS_SOURCE_EDITOR_H
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
new file mode 100644
index 0000000000..8a5890e9a4
--- /dev/null
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -0,0 +1,520 @@
+/*************************************************************************/
+/* tile_set_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "tile_set_editor.h"
+
+#include "tile_data_editors.h"
+#include "tiles_editor_plugin.h"
+
+#include "editor/editor_scale.h"
+
+#include "scene/gui/box_container.h"
+#include "scene/gui/control.h"
+#include "scene/gui/tab_container.h"
+
+TileSetEditor *TileSetEditor::singleton = nullptr;
+
+void TileSetEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ if (!can_drop_data_fw(p_point, p_data, p_from)) {
+ return;
+ }
+
+ if (p_from == sources_list) {
+ // Handle dropping a texture in the list of atlas resources.
+ int source_id = -1;
+ int added = 0;
+ Dictionary d = p_data;
+ Vector<String> files = d["files"];
+ for (int i = 0; i < files.size(); i++) {
+ Ref<Texture2D> resource = ResourceLoader::load(files[i]);
+ if (resource.is_valid()) {
+ // Retrieve the id for the next created source.
+ source_id = tile_set->get_next_source_id();
+
+ // Actually create the new source.
+ Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
+ atlas_source->set_texture(resource);
+ undo_redo->create_action(TTR("Add a new atlas source"));
+ undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
+ undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
+ undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
+ undo_redo->commit_action();
+ added += 1;
+ }
+ }
+
+ if (added == 1) {
+ tile_set_atlas_source_editor->init_source();
+ }
+
+ // Update the selected source (thus trigerring an update).
+ _update_atlas_sources_list(source_id);
+ }
+}
+
+bool TileSetEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+ ERR_FAIL_COND_V(!tile_set.is_valid(), false);
+
+ if (p_from == sources_list) {
+ Dictionary d = p_data;
+
+ if (!d.has("type")) {
+ return false;
+ }
+
+ // Check if we have a Texture2D.
+ if (String(d["type"]) == "files") {
+ Vector<String> files = d["files"];
+
+ if (files.size() == 0) {
+ return false;
+ }
+
+ for (int i = 0; i < files.size(); i++) {
+ String file = files[i];
+ String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
+
+ if (!ClassDB::is_parent_class(ftype, "Texture2D")) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+ return false;
+}
+
+void TileSetEditor::_update_atlas_sources_list(int force_selected_id) {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ // Get the previously selected id.
+ int old_selected = -1;
+ if (sources_list->get_current() >= 0) {
+ int source_id = sources_list->get_item_metadata(sources_list->get_current());
+ if (tile_set->has_source(source_id)) {
+ old_selected = source_id;
+ }
+ }
+
+ int to_select = -1;
+ if (force_selected_id >= 0) {
+ to_select = force_selected_id;
+ } else if (old_selected >= 0) {
+ to_select = old_selected;
+ }
+
+ // Clear the list.
+ sources_list->clear();
+
+ // Update the atlas sources.
+ for (int i = 0; i < tile_set->get_source_count(); i++) {
+ int source_id = tile_set->get_source_id(i);
+
+ // TODO: handle with virtual functions
+ TileSetSource *source = *tile_set->get_source(source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ Ref<Texture2D> texture = atlas_source->get_texture();
+ if (texture.is_valid()) {
+ sources_list->add_item(vformat("%s - (id:%d)", texture->get_path().get_file(), source_id), texture);
+ } else {
+ sources_list->add_item(vformat("No texture atlas source - (id:%d)", source_id), missing_texture_texture);
+ }
+ } else {
+ sources_list->add_item(vformat("Unknown type source - (id:%d)", source_id), missing_texture_texture);
+ }
+ sources_list->set_item_metadata(sources_list->get_item_count() - 1, source_id);
+ }
+
+ // Set again the current selected item if needed.
+ if (to_select >= 0) {
+ for (int i = 0; i < sources_list->get_item_count(); i++) {
+ if ((int)sources_list->get_item_metadata(i) == to_select) {
+ sources_list->set_current(i);
+ if (old_selected != to_select) {
+ sources_list->emit_signal("item_selected", sources_list->get_current());
+ }
+ break;
+ }
+ }
+ }
+
+ // If nothing is selected, select the first entry.
+ if (sources_list->get_current() < 0 && sources_list->get_item_count() > 0) {
+ sources_list->set_current(0);
+ if (old_selected != int(sources_list->get_item_metadata(0))) {
+ sources_list->emit_signal("item_selected", sources_list->get_current());
+ }
+ }
+
+ // If there is no source left, hide all editors and show the label.
+ _source_selected(sources_list->get_current());
+
+ // Synchronize the lists.
+ TilesEditor::get_singleton()->set_atlas_sources_lists_current(sources_list->get_current());
+}
+
+void TileSetEditor::_source_selected(int p_source_index) {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ // Update the selected source.
+ sources_delete_button->set_disabled(p_source_index < 0);
+
+ if (p_source_index >= 0) {
+ int source_id = sources_list->get_item_metadata(p_source_index);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
+ if (atlas_source) {
+ tile_set_atlas_source_editor->edit(*tile_set, atlas_source, source_id);
+ no_source_selected_label->hide();
+ tile_set_atlas_source_editor->show();
+ } else {
+ no_source_selected_label->show();
+ tile_set_atlas_source_editor->hide();
+ }
+ } else {
+ no_source_selected_label->show();
+ tile_set_atlas_source_editor->hide();
+ }
+}
+
+void TileSetEditor::_source_add_pressed() {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ int source_id = tile_set->get_next_source_id();
+
+ Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
+
+ // Add a new source.
+ undo_redo->create_action(TTR("Add atlas source"));
+ undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
+ undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
+ undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
+ undo_redo->commit_action();
+
+ _update_atlas_sources_list(source_id);
+}
+
+void TileSetEditor::_source_delete_pressed() {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ // Update the selected source.
+ int to_delete = sources_list->get_item_metadata(sources_list->get_current());
+
+ Ref<TileSetSource> source = tile_set->get_source(to_delete);
+
+ // Remove the source.
+ undo_redo->create_action(TTR("Remove source"));
+ undo_redo->add_do_method(*tile_set, "remove_source", to_delete);
+ undo_redo->add_undo_method(*tile_set, "add_source", source, to_delete);
+ undo_redo->commit_action();
+
+ _update_atlas_sources_list();
+}
+
+void TileSetEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED:
+ sources_delete_button->set_icon(get_theme_icon("Remove", "EditorIcons"));
+ sources_add_button->set_icon(get_theme_icon("Add", "EditorIcons"));
+ missing_texture_texture = get_theme_icon("TileSet", "EditorIcons");
+ break;
+ case NOTIFICATION_INTERNAL_PROCESS:
+ if (tile_set_changed_needs_update) {
+ _update_atlas_sources_list();
+ tile_set_changed_needs_update = false;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void TileSetEditor::_tile_set_changed() {
+ tile_set_changed_needs_update = true;
+}
+
+void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
+ UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
+ ERR_FAIL_COND(!undo_redo);
+
+#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, tile_data->get(property));
+ TileSet *tile_set = Object::cast_to<TileSet>(p_edited);
+ if (tile_set) {
+ Vector<String> components = p_property.split("/", true, 3);
+ for (int i = 0; i < tile_set->get_source_count(); i++) {
+ int source_id = tile_set->get_source_id(i);
+
+ Ref<TileSetAtlasSource> tas = tile_set->get_source(source_id);
+ if (tas.is_valid()) {
+ for (int j = 0; j < tas->get_tiles_count(); j++) {
+ Vector2i tile_id = tas->get_tile_id(j);
+ for (int k = 0; k < tas->get_alternative_tiles_count(tile_id); k++) {
+ int alternative_id = tas->get_alternative_tile_id(tile_id, k);
+ TileData *tile_data = Object::cast_to<TileData>(tas->get_tile_data(tile_id, alternative_id));
+ ERR_FAIL_COND(!tile_data);
+
+ if (p_property == "occlusion_layers_count") {
+ int new_layer_count = p_new_value;
+ int old_layer_count = tile_set->get_occlusion_layers_count();
+ if (new_layer_count < old_layer_count) {
+ for (int occclusion_layer_index = new_layer_count - 1; occclusion_layer_index < old_layer_count; occclusion_layer_index++) {
+ ADD_UNDO(tile_data, vformat("occlusion_layer_%d/polygon", occclusion_layer_index));
+ }
+ }
+ } else if (p_property == "physics_layers_count") {
+ int new_layer_count = p_new_value;
+ int old_layer_count = tile_set->get_physics_layers_count();
+ if (new_layer_count < old_layer_count) {
+ for (int physics_layer_index = new_layer_count - 1; physics_layer_index < old_layer_count; physics_layer_index++) {
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/shapes_count", physics_layer_index));
+ for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(physics_layer_index); shape_index++) {
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/shape", physics_layer_index, shape_index));
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way", physics_layer_index, shape_index));
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way_margin", physics_layer_index, shape_index));
+ }
+ }
+ }
+ } else if ((p_property == "terrains_sets_count" && tile_data->get_terrain_set() >= (int)p_new_value) ||
+ (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_integer() && components[1] == "mode") ||
+ (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_integer() && components[1] == "terrains_count" && tile_data->get_terrain_set() == components[0].trim_prefix("terrain_set_").to_int() && (int)p_new_value < tile_set->get_terrains_count(tile_data->get_terrain_set()))) {
+ ADD_UNDO(tile_data, "terrain_set");
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/right_side");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/right_corner");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/bottom_right_side");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/bottom_right_corner");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/bottom_side");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/bottom_corner");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/bottom_left_side");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/bottom_left_corner");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/left_side");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/left_corner");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/top_left_side");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/top_left_corner");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/top_side");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/top_corner");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/top_right_side");
+ }
+ if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/top_right_corner");
+ }
+ } else if (p_property == "navigation_layers_count") {
+ int new_layer_count = p_new_value;
+ int old_layer_count = tile_set->get_navigation_layers_count();
+ if (new_layer_count < old_layer_count) {
+ for (int navigation_layer_index = new_layer_count - 1; navigation_layer_index < old_layer_count; navigation_layer_index++) {
+ ADD_UNDO(tile_data, vformat("navigation_layer_%d/polygon", navigation_layer_index));
+ }
+ }
+ } else if (p_property == "custom_data_layers_count") {
+ int new_layer_count = p_new_value;
+ int old_layer_count = tile_set->get_custom_data_layers_count();
+ if (new_layer_count < old_layer_count) {
+ for (int custom_data_layer_index = new_layer_count - 1; custom_data_layer_index < old_layer_count; custom_data_layer_index++) {
+ ADD_UNDO(tile_data, vformat("custom_data_%d", custom_data_layer_index));
+ }
+ }
+ } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_integer() && components[1] == "type") {
+ int custom_data_layer = components[0].trim_prefix("custom_data_layer_").is_valid_integer();
+ ADD_UNDO(tile_data, vformat("custom_data_%d", custom_data_layer));
+ }
+ }
+ }
+ }
+ }
+ }
+#undef ADD_UNDO
+}
+
+void TileSetEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &TileSetEditor::can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("drop_data_fw"), &TileSetEditor::drop_data_fw);
+}
+
+TileDataEditor *TileSetEditor::get_tile_data_editor(String p_property) {
+ Vector<String> components = String(p_property).split("/", true);
+
+ if (p_property == "z_index") {
+ return tile_data_integer_editor;
+ } else if (p_property == "probability") {
+ return tile_data_float_editor;
+ } else if (p_property == "y_sort_origin") {
+ return tile_data_position_editor;
+ } else if (p_property == "texture_offset") {
+ return tile_data_texture_offset_editor;
+ } else if (components.size() >= 1 && components[0].begins_with("occlusion_layer_")) {
+ return tile_data_occlusion_shape_editor;
+ } else if (components.size() >= 1 && components[0].begins_with("physics_layer_")) {
+ return tile_data_collision_shape_editor;
+ } else if (p_property == "mode" || p_property == "terrain" || (components.size() >= 1 && components[0] == "terrains_peering_bit")) {
+ return tile_data_terrains_editor;
+ } else if (components.size() >= 1 && components[0].begins_with("navigation_layer_")) {
+ return tile_data_navigation_polygon_editor;
+ }
+
+ return nullptr;
+}
+
+void TileSetEditor::edit(Ref<TileSet> p_tile_set) {
+ if (p_tile_set == tile_set) {
+ return;
+ }
+
+ // Remove listener.
+ if (tile_set.is_valid()) {
+ tile_set->disconnect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
+ }
+
+ // Change the edited object.
+ tile_set = p_tile_set;
+
+ // Add the listener again.
+ if (tile_set.is_valid()) {
+ tile_set->connect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
+ _update_atlas_sources_list();
+ }
+
+ tile_set_atlas_source_editor->hide();
+ no_source_selected_label->show();
+}
+
+TileSetEditor::TileSetEditor() {
+ singleton = this;
+
+ set_process_internal(true);
+
+ // Split container.
+ HSplitContainer *split_container = memnew(HSplitContainer);
+ split_container->set_name(TTR("Tiles"));
+ split_container->set_h_size_flags(SIZE_EXPAND_FILL);
+ split_container->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(split_container);
+
+ // Sources list.
+ VBoxContainer *split_container_left_side = memnew(VBoxContainer);
+ split_container_left_side->set_h_size_flags(SIZE_EXPAND_FILL);
+ split_container_left_side->set_v_size_flags(SIZE_EXPAND_FILL);
+ split_container_left_side->set_stretch_ratio(0.25);
+ split_container_left_side->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
+ split_container->add_child(split_container_left_side);
+
+ sources_list = memnew(ItemList);
+ sources_list->set_fixed_icon_size(Size2i(60, 60) * EDSCALE);
+ sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ sources_list->set_v_size_flags(SIZE_EXPAND_FILL);
+ sources_list->connect("item_selected", callable_mp(this, &TileSetEditor::_source_selected));
+ sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_sources_lists_current));
+ sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_sources_list), varray(sources_list));
+ sources_list->set_drag_forwarding(this);
+ split_container_left_side->add_child(sources_list);
+
+ HBoxContainer *sources_bottom_actions = memnew(HBoxContainer);
+ sources_bottom_actions->set_alignment(HBoxContainer::ALIGN_END);
+ split_container_left_side->add_child(sources_bottom_actions);
+
+ sources_delete_button = memnew(Button);
+ sources_delete_button->set_flat(true);
+ sources_delete_button->set_disabled(true);
+ sources_delete_button->connect("pressed", callable_mp(this, &TileSetEditor::_source_delete_pressed));
+ sources_bottom_actions->add_child(sources_delete_button);
+
+ sources_add_button = memnew(Button);
+ sources_add_button->set_flat(true);
+ sources_add_button->connect("pressed", callable_mp(this, &TileSetEditor::_source_add_pressed));
+ sources_bottom_actions->add_child(sources_add_button);
+
+ // No source selected.
+ no_source_selected_label = memnew(Label);
+ no_source_selected_label->set_text(TTR("No TileSet source selected. Select or create a TileSet source."));
+ no_source_selected_label->set_h_size_flags(SIZE_EXPAND_FILL);
+ no_source_selected_label->set_v_size_flags(SIZE_EXPAND_FILL);
+ no_source_selected_label->set_align(Label::ALIGN_CENTER);
+ no_source_selected_label->set_valign(Label::VALIGN_CENTER);
+ split_container->add_child(no_source_selected_label);
+
+ // Atlases editor.
+ tile_set_atlas_source_editor = memnew(TileSetAtlasSourceEditor);
+ tile_set_atlas_source_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ tile_set_atlas_source_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ tile_set_atlas_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_atlas_sources_list));
+ split_container->add_child(tile_set_atlas_source_editor);
+ tile_set_atlas_source_editor->hide();
+
+ // Registers UndoRedo inspector callback.
+ EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback));
+}
+
+TileSetEditor::~TileSetEditor() {
+ if (tile_set.is_valid()) {
+ tile_set->disconnect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
+ }
+
+ // Delete tile data editors.
+ memdelete(tile_data_texture_offset_editor);
+ memdelete(tile_data_position_editor);
+ memdelete(tile_data_integer_editor);
+ memdelete(tile_data_float_editor);
+ memdelete(tile_data_occlusion_shape_editor);
+ memdelete(tile_data_collision_shape_editor);
+ memdelete(tile_data_terrains_editor);
+ memdelete(tile_data_navigation_polygon_editor);
+}
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
new file mode 100644
index 0000000000..c4aebb40a2
--- /dev/null
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* tile_set_editor.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TILE_SET_EDITOR_H
+#define TILE_SET_EDITOR_H
+
+#include "scene/gui/box_container.h"
+#include "scene/resources/tile_set.h"
+#include "tile_data_editors.h"
+#include "tile_set_atlas_source_editor.h"
+
+class TileSetEditor : public VBoxContainer {
+ GDCLASS(TileSetEditor, VBoxContainer);
+
+ static TileSetEditor *singleton;
+
+private:
+ Ref<TileSet> tile_set;
+ bool tile_set_changed_needs_update = false;
+
+ Label *no_source_selected_label;
+ TileSetAtlasSourceEditor *tile_set_atlas_source_editor;
+
+ UndoRedo *undo_redo = EditorNode::get_undo_redo();
+
+ void _update_atlas_sources_list(int force_selected_id = -1);
+
+ // List of tile data editors.
+ TileDataTextureOffsetEditor *tile_data_texture_offset_editor = memnew(TileDataTextureOffsetEditor);
+ TileDataPositionEditor *tile_data_position_editor = memnew(TileDataPositionEditor);
+ TileDataIntegerEditor *tile_data_integer_editor = memnew(TileDataIntegerEditor);
+ TileDataFloatEditor *tile_data_float_editor = memnew(TileDataFloatEditor);
+ TileDataOcclusionShapeEditor *tile_data_occlusion_shape_editor = memnew(TileDataOcclusionShapeEditor);
+ TileDataCollisionShapeEditor *tile_data_collision_shape_editor = memnew(TileDataCollisionShapeEditor);
+ TileDataTerrainsEditor *tile_data_terrains_editor = memnew(TileDataTerrainsEditor);
+ TileDataNavigationPolygonEditor *tile_data_navigation_polygon_editor = memnew(TileDataNavigationPolygonEditor);
+
+ // -- Sources management --
+ Button *sources_delete_button;
+ Button *sources_add_button;
+ ItemList *sources_list;
+ Ref<Texture2D> missing_texture_texture;
+ void _source_selected(int p_source_index);
+ void _source_add_pressed();
+ void _source_delete_pressed();
+
+ void _tile_set_changed();
+
+ void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ _FORCE_INLINE_ static TileSetEditor *get_singleton() { return singleton; }
+
+ TileDataEditor *get_tile_data_editor(String property);
+ void edit(Ref<TileSet> p_tile_set);
+ void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+
+ TileSetEditor();
+ ~TileSetEditor();
+};
+
+#endif // TILE_SET_EDITOR_PLUGIN_H
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
new file mode 100644
index 0000000000..971ff15073
--- /dev/null
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -0,0 +1,276 @@
+/*************************************************************************/
+/* tiles_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "tiles_editor_plugin.h"
+
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "editor/plugins/canvas_item_editor_plugin.h"
+
+#include "scene/2d/tile_map.h"
+#include "scene/resources/tile_set.h"
+
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/control.h"
+#include "scene/gui/separator.h"
+
+#include "tile_set_editor.h"
+
+TilesEditor *TilesEditor::singleton = nullptr;
+
+void TilesEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ tileset_tilemap_switch_button->set_icon(get_theme_icon("TileSet", "EditorIcons"));
+ } break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (tile_map_changed_needs_update) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (tile_map) {
+ tile_set = tile_map->get_tileset();
+ }
+ _update_switch_button();
+ _update_editors();
+ }
+ } break;
+ }
+}
+
+void TilesEditor::_tile_map_changed() {
+ tile_map_changed_needs_update = true;
+}
+
+void TilesEditor::_update_switch_button() {
+ // Force the buttons status if needed.
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (tile_map && !tile_set.is_valid()) {
+ tileset_tilemap_switch_button->set_pressed(false);
+ } else if (!tile_map && tile_set.is_valid()) {
+ tileset_tilemap_switch_button->set_pressed(true);
+ }
+}
+
+void TilesEditor::_update_editors() {
+ // Set editors visibility.
+ tilemap_toolbar->set_visible(!tileset_tilemap_switch_button->is_pressed());
+ tilemap_editor->set_visible(!tileset_tilemap_switch_button->is_pressed());
+ tileset_editor->set_visible(tileset_tilemap_switch_button->is_pressed());
+
+ // Enable/disable the switch button.
+ if (!tileset_tilemap_switch_button->is_pressed()) {
+ if (!tile_set.is_valid()) {
+ tileset_tilemap_switch_button->set_disabled(true);
+ tileset_tilemap_switch_button->set_tooltip(TTR("This TileMap has no assigned TileSet, assign a TileSet to this TileMap to edit it."));
+ } else {
+ tileset_tilemap_switch_button->set_disabled(false);
+ tileset_tilemap_switch_button->set_tooltip(TTR("Switch between TileSet/TileMap editor."));
+ }
+ } else {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ tileset_tilemap_switch_button->set_disabled(true);
+ tileset_tilemap_switch_button->set_tooltip(TTR("You are editing a TileSet resource. Select a TileMap node to paint."));
+ } else {
+ tileset_tilemap_switch_button->set_disabled(false);
+ tileset_tilemap_switch_button->set_tooltip(TTR("Switch between TileSet/TileMap editor."));
+ }
+ }
+
+ // If tile_map is not edited, we change the edited only if we are not editing a tile_set.
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (tile_map) {
+ tilemap_editor->edit(tile_map);
+ } else {
+ tilemap_editor->edit(nullptr);
+ }
+ tileset_editor->edit(tile_set);
+
+ // Update the viewport
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
+void TilesEditor::set_atlas_sources_lists_current(int p_current) {
+ atlas_sources_lists_current = p_current;
+}
+
+void TilesEditor::synchronize_atlas_sources_list(Object *p_current) {
+ ItemList *item_list = Object::cast_to<ItemList>(p_current);
+ ERR_FAIL_COND(!item_list);
+
+ if (item_list->is_visible_in_tree()) {
+ if (atlas_sources_lists_current < 0 || atlas_sources_lists_current >= item_list->get_item_count()) {
+ item_list->deselect_all();
+ } else {
+ item_list->set_current(atlas_sources_lists_current);
+ item_list->emit_signal("item_selected", atlas_sources_lists_current);
+ }
+ }
+}
+
+void TilesEditor::set_atlas_view_transform(float p_zoom, Vector2 p_scroll) {
+ atlas_view_zoom = p_zoom;
+ atlas_view_scroll = p_scroll;
+}
+
+void TilesEditor::synchronize_atlas_view(Object *p_current) {
+ TileAtlasView *tile_atlas_view = Object::cast_to<TileAtlasView>(p_current);
+ ERR_FAIL_COND(!tile_atlas_view);
+
+ if (tile_atlas_view->is_visible_in_tree()) {
+ tile_atlas_view->set_transform(atlas_view_zoom, Vector2(atlas_view_scroll.x, atlas_view_scroll.y));
+ }
+}
+
+void TilesEditor::edit(Object *p_object) {
+ // Disconnect to changes.
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (tile_map) {
+ tile_map->disconnect("changed", callable_mp(this, &TilesEditor::_tile_map_changed));
+ }
+
+ // Update edited objects.
+ tile_set = Ref<TileSet>();
+ if (p_object) {
+ if (p_object->is_class("TileMap")) {
+ tile_map_id = p_object->get_instance_id();
+ tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ tile_set = tile_map->get_tileset();
+ } else if (p_object->is_class("TileSet")) {
+ tile_set = Ref<TileSet>(p_object);
+ if (tile_map) {
+ if (tile_map->get_tileset() != tile_set) {
+ tile_map = nullptr;
+ }
+ }
+ }
+
+ // Update pressed status button.
+ if (p_object->is_class("TileMap")) {
+ tileset_tilemap_switch_button->set_pressed(false);
+ } else if (p_object->is_class("TileSet")) {
+ tileset_tilemap_switch_button->set_pressed(true);
+ }
+ }
+
+ // Update the editors.
+ _update_switch_button();
+ _update_editors();
+
+ // Add change listener.
+ if (tile_map) {
+ tile_map->connect("changed", callable_mp(this, &TilesEditor::_tile_map_changed));
+ }
+}
+
+void TilesEditor::_bind_methods() {
+}
+
+TilesEditor::TilesEditor(EditorNode *p_editor) {
+ set_process_internal(true);
+
+ // Update the singleton.
+ singleton = this;
+
+ // Toolbar.
+ HBoxContainer *toolbar = memnew(HBoxContainer);
+ add_child(toolbar);
+
+ // Switch button.
+ tileset_tilemap_switch_button = memnew(Button);
+ tileset_tilemap_switch_button->set_flat(true);
+ tileset_tilemap_switch_button->set_toggle_mode(true);
+ tileset_tilemap_switch_button->connect("toggled", callable_mp(this, &TilesEditor::_update_editors).unbind(1));
+ toolbar->add_child(tileset_tilemap_switch_button);
+
+ // Tilemap editor.
+ tilemap_editor = memnew(TileMapEditor);
+ tilemap_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ tilemap_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ tilemap_editor->hide();
+ add_child(tilemap_editor);
+
+ tilemap_toolbar = tilemap_editor->get_toolbar();
+ toolbar->add_child(tilemap_toolbar);
+
+ // Tileset editor.
+ tileset_editor = memnew(TileSetEditor);
+ tileset_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ tileset_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ tileset_editor->hide();
+ add_child(tileset_editor);
+
+ // Initialization.
+ _update_switch_button();
+ _update_editors();
+}
+
+TilesEditor::~TilesEditor() {
+}
+
+///////////////////////////////////////////////////////////////
+
+void TilesEditorPlugin::_notification(int p_what) {
+}
+
+void TilesEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ tiles_editor_button->show();
+ editor_node->make_bottom_panel_item_visible(tiles_editor);
+ //get_tree()->connect_compat("idle_frame", tileset_editor, "_on_workspace_process");
+ } else {
+ editor_node->hide_bottom_panel();
+ tiles_editor_button->hide();
+ //get_tree()->disconnect_compat("idle_frame", tileset_editor, "_on_workspace_process");
+ }
+}
+
+void TilesEditorPlugin::edit(Object *p_object) {
+ tiles_editor->edit(p_object);
+}
+
+bool TilesEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("TileMap") || p_object->is_class("TileSet");
+}
+
+TilesEditorPlugin::TilesEditorPlugin(EditorNode *p_node) {
+ editor_node = p_node;
+
+ tiles_editor = memnew(TilesEditor(p_node));
+ tiles_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
+ tiles_editor->hide();
+
+ tiles_editor_button = p_node->add_bottom_panel_item(TTR("Tiles"), tiles_editor);
+ tiles_editor_button->hide();
+}
+
+TilesEditorPlugin::~TilesEditorPlugin() {
+}
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
new file mode 100644
index 0000000000..6cc6f51598
--- /dev/null
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -0,0 +1,114 @@
+/*************************************************************************/
+/* tiles_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TILES_EDITOR_PLUGIN_H
+#define TILES_EDITOR_PLUGIN_H
+
+#include "editor/editor_plugin.h"
+#include "scene/gui/box_container.h"
+
+#include "tile_atlas_view.h"
+#include "tile_map_editor.h"
+#include "tile_set_editor.h"
+
+class TilesEditor : public VBoxContainer {
+ GDCLASS(TilesEditor, VBoxContainer);
+
+ static TilesEditor *singleton;
+
+private:
+ bool tile_map_changed_needs_update = false;
+ ObjectID tile_map_id;
+ Ref<TileSet> tile_set;
+
+ Button *tileset_tilemap_switch_button;
+
+ Control *tilemap_toolbar;
+ TileMapEditor *tilemap_editor;
+
+ TileSetEditor *tileset_editor;
+
+ void _update_switch_button();
+ void _update_editors();
+
+ // For synchronization.
+ int atlas_sources_lists_current = 0;
+ float atlas_view_zoom = 1.0;
+ Vector2 atlas_view_scroll = Vector2();
+
+ void _tile_map_changed();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ _FORCE_INLINE_ static TilesEditor *get_singleton() { return singleton; }
+
+ bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return tilemap_editor->forward_canvas_gui_input(p_event); }
+ void forward_canvas_draw_over_viewport(Control *p_overlay) { tilemap_editor->forward_canvas_draw_over_viewport(p_overlay); }
+
+ // To synchronize the atlas sources lists.
+ void set_atlas_sources_lists_current(int p_current);
+ void synchronize_atlas_sources_list(Object *p_current);
+
+ void set_atlas_view_transform(float p_zoom, Vector2 p_scroll);
+ void synchronize_atlas_view(Object *p_current);
+
+ void edit(Object *p_object);
+
+ TilesEditor(EditorNode *p_editor);
+ ~TilesEditor();
+};
+
+class TilesEditorPlugin : public EditorPlugin {
+ GDCLASS(TilesEditorPlugin, EditorPlugin);
+
+private:
+ EditorNode *editor_node;
+ TilesEditor *tiles_editor;
+ Button *tiles_editor_button;
+
+protected:
+ void _notification(int p_what);
+
+public:
+ virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return tiles_editor->forward_canvas_gui_input(p_event); }
+ virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { tiles_editor->forward_canvas_draw_over_viewport(p_overlay); }
+
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
+
+ TilesEditorPlugin(EditorNode *p_node);
+ ~TilesEditorPlugin();
+};
+
+#endif // TILES_EDITOR_PLUGIN_H
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index acc77bd098..1d4c23fee6 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -736,6 +736,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
Color background_color = EDITOR_GET("text_editor/highlighting/background_color");
Color text_color = EDITOR_GET("text_editor/highlighting/text_color");
Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+ Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
Color function_color = EDITOR_GET("text_editor/highlighting/function_color");
@@ -746,7 +747,11 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
expression_box->add_theme_color_override("background_color", background_color);
for (List<String>::Element *E = VisualShaderEditor::get_singleton()->keyword_list.front(); E; E = E->next()) {
- expression_syntax_highlighter->add_keyword_color(E->get(), keyword_color);
+ if (ShaderLanguage::is_control_flow_keyword(E->get())) {
+ expression_syntax_highlighter->add_keyword_color(E->get(), control_flow_keyword_color);
+ } else {
+ expression_syntax_highlighter->add_keyword_color(E->get(), keyword_color);
+ }
}
expression_box->add_theme_font_override("font", VisualShaderEditor::get_singleton()->get_theme_font("expression", "EditorFonts"));
@@ -1175,26 +1180,15 @@ void VisualShaderEditor::_draw_color_over_button(Object *obj, Color p_color) {
}
void VisualShaderEditor::_update_created_node(GraphNode *node) {
- if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) {
- Ref<StyleBoxFlat> sb = node->get_theme_stylebox("frame", "GraphNode");
- Color c = sb->get_border_color();
- Color ic;
- Color mono_color;
- if (((c.r + c.g + c.b) / 3) < 0.7) {
- mono_color = Color(1.0, 1.0, 1.0);
- ic = Color(0.0, 0.0, 0.0, 0.7);
- } else {
- mono_color = Color(0.0, 0.0, 0.0);
- ic = Color(1.0, 1.0, 1.0, 0.7);
- }
- mono_color.a = 0.85;
- c = mono_color;
+ const Ref<StyleBoxFlat> sb = node->get_theme_stylebox("frame", "GraphNode");
+ Color c = sb->get_border_color();
+ const Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0, 0.85) : Color(0.0, 0.0, 0.0, 0.85);
+ c = mono_color;
- node->add_theme_color_override("title_color", c);
- c.a = 0.7;
- node->add_theme_color_override("close_color", c);
- node->add_theme_color_override("resizer_color", ic);
- }
+ node->add_theme_color_override("title_color", c);
+ c.a = 0.7;
+ node->add_theme_color_override("close_color", c);
+ node->add_theme_color_override("resizer_color", c);
}
void VisualShaderEditor::_update_uniforms(bool p_update_refs) {
@@ -2803,6 +2797,7 @@ void VisualShaderEditor::_notification(int p_what) {
Color background_color = EDITOR_GET("text_editor/highlighting/background_color");
Color text_color = EDITOR_GET("text_editor/highlighting/text_color");
Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+ Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
Color function_color = EDITOR_GET("text_editor/highlighting/function_color");
@@ -2812,7 +2807,11 @@ void VisualShaderEditor::_notification(int p_what) {
preview_text->add_theme_color_override("background_color", background_color);
for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) {
- syntax_highlighter->add_keyword_color(E->get(), keyword_color);
+ if (ShaderLanguage::is_control_flow_keyword(E->get())) {
+ syntax_highlighter->add_keyword_color(E->get(), control_flow_keyword_color);
+ } else {
+ syntax_highlighter->add_keyword_color(E->get(), keyword_color);
+ }
}
preview_text->add_theme_font_override("font", get_theme_font("expression", "EditorFonts"));
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index faec3355ac..76290b4b62 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -649,8 +649,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
action_map->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed));
action_map->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed));
action_map->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered));
- action_map->set_toggle_editable_label(TTR("Show Built-in Actions"));
- action_map->set_show_uneditable(false);
tab_container->add_child(action_map);
localization_editor = memnew(LocalizationEditor);
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index 1a010b9168..8b0d9ae0fc 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -1823,7 +1823,6 @@ CustomPropertyEditor::CustomPropertyEditor() {
Vector<Variant> binds;
binds.push_back(i);
action_buttons[i]->connect("pressed", callable_mp(this, &CustomPropertyEditor::_action_pressed), binds);
- action_buttons[i]->set_flat(true);
}
color_picker = nullptr;
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 49c231de69..4068a102df 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -2587,14 +2587,16 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
}
}
- if (profile_allow_script_editing) {
+ if (profile_allow_editing) {
menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/cut_node"), TOOL_CUT);
menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/copy_node"), TOOL_COPY);
if (selection.size() == 1 && !node_clipboard.is_empty()) {
menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE);
}
menu->add_separator();
+ }
+ if (profile_allow_script_editing) {
bool add_separator = false;
if (full_selection.size() == 1) {
@@ -2622,7 +2624,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
}
}
- if (add_separator) {
+ if (add_separator && profile_allow_editing) {
menu->add_separator();
}
}
@@ -3042,7 +3044,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
ED_SHORTCUT("scene_tree/rename", TTR("Rename"), KEY_F2);
ED_SHORTCUT("scene_tree/batch_rename", TTR("Batch Rename"), KEY_MASK_SHIFT | KEY_F2);
ED_SHORTCUT("scene_tree/add_child_node", TTR("Add Child Node"), KEY_MASK_CMD | KEY_A);
- ED_SHORTCUT("scene_tree/instance_scene", TTR("Instance Child Scene"));
+ ED_SHORTCUT("scene_tree/instance_scene", TTR("Instance Child Scene"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_A);
ED_SHORTCUT("scene_tree/expand_collapse_all", TTR("Expand/Collapse All"));
ED_SHORTCUT("scene_tree/cut_node", TTR("Cut"), KEY_MASK_CMD | KEY_X);
ED_SHORTCUT("scene_tree/copy_node", TTR("Copy"), KEY_MASK_CMD | KEY_C);
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index f979f61196..d0bf83eb2d 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -540,6 +540,10 @@ void SceneTreeEditor::_update_tree(bool p_scroll_to_selected) {
return;
}
+ if (tree->is_editing()) {
+ return;
+ }
+
updating_tree = true;
tree->clear();
if (get_scene_node()) {
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index f3addd8904..c2bfdaae96 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -838,7 +838,6 @@ ScriptCreateDialog::ScriptCreateDialog() {
parent_search_button->connect("pressed", callable_mp(this, &ScriptCreateDialog::_browse_class_in_tree));
hb->add_child(parent_search_button);
parent_browse_button = memnew(Button);
- parent_browse_button->set_flat(true);
parent_browse_button->connect("pressed", callable_mp(this, &ScriptCreateDialog::_browse_path), varray(true, false));
hb->add_child(parent_browse_button);
gc->add_child(memnew(Label(TTR("Inherits:"))));
@@ -877,7 +876,6 @@ ScriptCreateDialog::ScriptCreateDialog() {
file_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hb->add_child(file_path);
path_button = memnew(Button);
- path_button->set_flat(true);
path_button->connect("pressed", callable_mp(this, &ScriptCreateDialog::_browse_path), varray(false, true));
hb->add_child(path_button);
gc->add_child(memnew(Label(TTR("Path:"))));
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp
index 81af4996ed..541ec224b9 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/settings_config_dialog.cpp
@@ -270,16 +270,17 @@ void EditorSettingsDialog::_update_shortcuts() {
Array events; // Need to get the list of events into an array so it can be set as metadata on the item.
Vector<String> event_strings;
- List<Ref<InputEvent>> defaults = InputMap::get_singleton()->get_builtins().find(action_name).value();
- // Remove all non-key events from the defaults.
- for (List<Ref<InputEvent>>::Element *I = defaults.front(); I; I = I->next()) {
+ List<Ref<InputEvent>> all_default_events = InputMap::get_singleton()->get_builtins().find(action_name).value();
+ List<Ref<InputEventKey>> key_default_events;
+ // Remove all non-key events from the defaults. Only check keys, since we are in the editor.
+ for (List<Ref<InputEvent>>::Element *I = all_default_events.front(); I; I = I->next()) {
Ref<InputEventKey> k = I->get();
- if (k.is_null()) {
- I->erase();
+ if (k.is_valid()) {
+ key_default_events.push_back(k);
}
}
- bool same_as_defaults = defaults.size() == action.inputs.size(); // Initially this is set to just whether the arrays are equal. Later we check the events if needed.
+ bool same_as_defaults = key_default_events.size() == action.inputs.size(); // Initially this is set to just whether the arrays are equal. Later we check the events if needed.
int count = 0;
for (List<Ref<InputEvent>>::Element *I = action.inputs.front(); I; I = I->next()) {
@@ -288,12 +289,8 @@ void EditorSettingsDialog::_update_shortcuts() {
event_strings.push_back(I->get()->as_text());
// Only check if the events have been the same so far - once one fails, we don't need to check any more.
- if (same_as_defaults) {
- Ref<InputEventKey> k = defaults[count];
- // Only check keys, since we are in the editor.
- if (k.is_valid() && !defaults[count]->shortcut_match(I->get())) {
- same_as_defaults = false;
- }
+ if (same_as_defaults && !key_default_events[count]->shortcut_match(I->get())) {
+ same_as_defaults = false;
}
count++;
}
diff --git a/icon_outlined.png b/icon_outlined.png
new file mode 100644
index 0000000000..5f1440f287
--- /dev/null
+++ b/icon_outlined.png
Binary files differ
diff --git a/icon_outlined.svg b/icon_outlined.svg
new file mode 100644
index 0000000000..08f9f6f863
--- /dev/null
+++ b/icon_outlined.svg
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1024"
+ height="1024"
+ id="svg3030"
+ version="1.1"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
+ sodipodi:docname="icon_outlined.svg"
+ inkscape:export-filename="/home/riteo/srg/godot-riteo/icon_outlined.png"
+ inkscape:export-xdpi="24"
+ inkscape:export-ydpi="24">
+ <defs
+ id="defs3032" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.8359375"
+ inkscape:cx="512"
+ inkscape:cy="512"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1916"
+ inkscape:window-height="1025"
+ inkscape:window-x="1360"
+ inkscape:window-y="53"
+ inkscape:window-maximized="1"
+ inkscape:document-rotation="0"
+ inkscape:pagecheckerboard="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:object-paths="true"
+ showguides="false" />
+ <metadata
+ id="metadata3035">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-98.519719)">
+ <g
+ id="g78-3-5"
+ transform="matrix(4.1626099,0,0,-4.1626099,919.24188,771.67092)"
+ style="fill:none;fill-opacity:0.527344;stroke:#ffffff;stroke-width:15.58077612;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal">
+ <path
+ id="path80-6-3"
+ style="fill:none;fill-opacity:0.527344;fill-rule:nonzero;stroke:#ffffff;stroke-width:23.49692134;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
+ d="m 226.33398,222.09961 c -15.35819,3.41427 -30.55118,8.16852 -44.79492,15.33789 0.32575,12.57731 1.13879,24.62813 2.78711,36.86914 -5.5316,3.54397 -11.34506,6.58567 -16.51172,10.73438 -5.24959,4.03861 -10.60984,7.90172 -15.36328,12.625 -9.49632,-6.28112 -19.54792,-12.18415 -29.90234,-17.39454 -11.16124,12.01179 -21.59792,24.97673 -30.123049,39.48438 6.699845,10.51682 13.887809,21.08874 20.542969,29.33203 0.0561,36.95701 -0.082,77.98955 0,113.01958 0,51.83844 65.75967,76.75448 147.46094,77.04102 h 0.0996 0.0996 c 81.70127,-0.28654 147.43945,-25.20258 147.43945,-77.04102 0.0505,-37.842 0.0165,-79.50966 0.0214,-113.01953 7.68652,-9.6765 14.96452,-20.35178 20.54102,-29.33203 -8.52211,-14.50765 -18.96376,-27.47259 -30.125,-39.48438 -10.3514,5.21039 -20.40602,11.11342 -29.90234,17.39454 -4.75194,-4.72328 -10.1042,-8.58639 -15.36133,-12.625 -5.16515,-4.14871 -10.98481,-7.19041 -16.50586,-10.73438 1.6438,-12.24101 2.45595,-24.29183 2.7832,-36.86914 -14.24524,-7.16937 -29.43699,-11.92362 -44.80273,-15.33789 -6.13484,10.31068 -11.74471,21.47716 -16.63086,32.39258 -5.79401,-0.96818 -11.61467,-1.32712 -17.44336,-1.39649 v -0.01 c -0.0407,0 -0.0786,0.01 -0.11328,0.01 -0.0362,0 -0.0732,-0.01 -0.10938,-0.01 v 0.01 c -5.83925,0.0694 -11.65565,0.42831 -17.45117,1.39649 -4.88313,-10.91542 -10.49088,-22.0819 -16.63477,-32.39258 z"
+ transform="matrix(0.66309862,0,0,-0.66309862,-270.58935,290.52015)"
+ sodipodi:nodetypes="ccccccccccccccccccccccccsccccc" />
+ </g>
+ <g
+ id="g78"
+ transform="matrix(4.162611,0,0,-4.162611,919.24059,771.67186)"
+ style="stroke-width:0.32031175">
+ <path
+ d="m 0,0 c 0,0 -0.325,1.994 -0.515,1.976 l -36.182,-3.491 c -2.879,-0.278 -5.115,-2.574 -5.317,-5.459 l -0.994,-14.247 -27.992,-1.997 -1.904,12.912 c -0.424,2.872 -2.932,5.037 -5.835,5.037 h -38.188 c -2.902,0 -5.41,-2.165 -5.834,-5.037 l -1.905,-12.912 -27.992,1.997 -0.994,14.247 c -0.202,2.886 -2.438,5.182 -5.317,5.46 l -36.2,3.49 c -0.187,0.018 -0.324,-1.978 -0.511,-1.978 l -0.049,-7.83 30.658,-4.944 1.004,-14.374 c 0.203,-2.91 2.551,-5.263 5.463,-5.472 l 38.551,-2.75 c 0.146,-0.01 0.29,-0.016 0.434,-0.016 2.897,0 5.401,2.166 5.825,5.038 l 1.959,13.286 h 28.005 l 1.959,-13.286 c 0.423,-2.871 2.93,-5.037 5.831,-5.037 0.142,0 0.284,0.005 0.423,0.015 l 38.556,2.75 c 2.911,0.209 5.26,2.562 5.463,5.472 l 1.003,14.374 30.645,4.966 z"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.32031175"
+ id="path80"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g82-3"
+ transform="matrix(4.162611,0,0,-4.162611,104.69892,525.90697)"
+ style="stroke-width:0.32031175">
+ <path
+ d="m 0,0 v -47.514 -6.035 -5.492 c 0.108,-0.001 0.216,-0.005 0.323,-0.015 l 36.196,-3.49 c 1.896,-0.183 3.382,-1.709 3.514,-3.609 l 1.116,-15.978 31.574,-2.253 2.175,14.747 c 0.282,1.912 1.922,3.329 3.856,3.329 h 38.188 c 1.933,0 3.573,-1.417 3.855,-3.329 l 2.175,-14.747 31.575,2.253 1.115,15.978 c 0.133,1.9 1.618,3.425 3.514,3.609 l 36.182,3.49 c 0.107,0.01 0.214,0.014 0.322,0.015 v 4.711 l 0.015,0.005 V 0 c 5.09692,6.4164715 9.92323,13.494208 13.621,19.449 -5.651,9.62 -12.575,18.217 -19.976,26.182 -6.864,-3.455 -13.531,-7.369 -19.828,-11.534 -3.151,3.132 -6.7,5.694 -10.186,8.372 -3.425,2.751 -7.285,4.768 -10.946,7.118 1.09,8.117 1.629,16.108 1.846,24.448 -9.446,4.754 -19.519,7.906 -29.708,10.17 -4.068,-6.837 -7.788,-14.241 -11.028,-21.479 -3.842,0.642 -7.702,0.88 -11.567,0.926 v 0.006 c -0.027,0 -0.052,-0.006 -0.075,-0.006 -0.024,0 -0.049,0.006 -0.073,0.006 V 63.652 C 93.903,63.606 90.046,63.368 86.203,62.726 82.965,69.964 79.247,77.368 75.173,84.205 64.989,81.941 54.915,78.789 45.47,74.035 45.686,65.695 46.225,57.704 47.318,49.587 43.65,47.237 39.795,45.22 36.369,42.469 32.888,39.791 29.333,37.229 26.181,34.097 19.884,38.262 13.219,42.176 6.353,45.631 -1.048,37.666 -7.968,29.069 -13.621,19.449 -9.1783421,12.475308 -4.4130298,5.4661124 0,0 Z"
+ style="fill:#478cbf;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.320312"
+ id="path84-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccsssscccccccccccccccccccsccccccccccc" />
+ </g>
+ <g
+ id="g86-7"
+ transform="matrix(4.162611,0,0,-4.162611,784.07144,817.24284)"
+ style="stroke-width:0.32031175">
+ <path
+ d="m 0,0 -1.121,-16.063 c -0.135,-1.936 -1.675,-3.477 -3.611,-3.616 l -38.555,-2.751 c -0.094,-0.007 -0.188,-0.01 -0.281,-0.01 -1.916,0 -3.569,1.406 -3.852,3.33 l -2.211,14.994 H -81.09 l -2.211,-14.994 c -0.297,-2.018 -2.101,-3.469 -4.133,-3.32 l -38.555,2.751 c -1.936,0.139 -3.476,1.68 -3.611,3.616 L -130.721,0 -163.268,3.138 c 0.015,-3.498 0.06,-7.33 0.06,-8.093 0,-34.374 43.605,-50.896 97.781,-51.086 h 0.066 0.067 c 54.176,0.19 97.766,16.712 97.766,51.086 0,0.777 0.047,4.593 0.063,8.093 z"
+ style="fill:#478cbf;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.32031175"
+ id="path88-5"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g90-3"
+ transform="matrix(4.162611,0,0,-4.162611,389.21484,625.67104)"
+ style="stroke-width:0.32031175">
+ <path
+ d="m 0,0 c 0,-12.052 -9.765,-21.815 -21.813,-21.815 -12.042,0 -21.81,9.763 -21.81,21.815 0,12.044 9.768,21.802 21.81,21.802 C -9.765,21.802 0,12.044 0,0"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.32031175"
+ id="path92-5"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g94-6"
+ transform="matrix(4.162611,0,0,-4.162611,367.36686,631.05679)"
+ style="stroke-width:0.32031175">
+ <path
+ d="m 0,0 c 0,-7.994 -6.479,-14.473 -14.479,-14.473 -7.996,0 -14.479,6.479 -14.479,14.473 0,7.994 6.483,14.479 14.479,14.479 C -6.479,14.479 0,7.994 0,0"
+ style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.32031175"
+ id="path96-2"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g98-9"
+ transform="matrix(4.162611,0,0,-4.162611,511.99336,724.73954)"
+ style="stroke-width:0.32031175">
+ <path
+ d="m 0,0 c -3.878,0 -7.021,2.858 -7.021,6.381 v 20.081 c 0,3.52 3.143,6.381 7.021,6.381 3.878,0 7.028,-2.861 7.028,-6.381 V 6.381 C 7.028,2.858 3.878,0 0,0"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.32031175"
+ id="path100-1"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g102-2"
+ transform="matrix(4.162611,0,0,-4.162611,634.78706,625.67104)"
+ style="stroke-width:0.32031175">
+ <path
+ d="m 0,0 c 0,-12.052 9.765,-21.815 21.815,-21.815 12.041,0 21.808,9.763 21.808,21.815 0,12.044 -9.767,21.802 -21.808,21.802 C 9.765,21.802 0,12.044 0,0"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.32031175"
+ id="path104-7"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g106-0"
+ transform="matrix(4.162611,0,0,-4.162611,656.64056,631.05679)"
+ style="stroke-width:0.32031175">
+ <path
+ d="m 0,0 c 0,-7.994 6.477,-14.473 14.471,-14.473 8.002,0 14.479,6.479 14.479,14.473 0,7.994 -6.477,14.479 -14.479,14.479 C 6.477,14.479 0,7.994 0,0"
+ style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.32031175"
+ id="path108-9"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g78-3"
+ transform="matrix(4.1626558,0,0,-4.1626558,918.83083,771.66929)"
+ style="fill:#000000;fill-opacity:0.527344;stroke:none;stroke-width:10.0248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/logo_outlined.png b/logo_outlined.png
new file mode 100644
index 0000000000..44b85cde57
--- /dev/null
+++ b/logo_outlined.png
Binary files differ
diff --git a/logo_outlined.svg b/logo_outlined.svg
new file mode 100644
index 0000000000..4079a1dd56
--- /dev/null
+++ b/logo_outlined.svg
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2"
+ version="1.1"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
+ xml:space="preserve"
+ width="1024"
+ height="414"
+ viewBox="0 0 960.00002 388.12499"
+ sodipodi:docname="logo_outlined.svg"
+ inkscape:export-filename="/home/riteo/srg/godot-riteo/logo_outlined.png"
+ inkscape:export-xdpi="48"
+ inkscape:export-ydpi="48"><metadata
+ id="metadata8"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+ id="defs6"><clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath16"><path
+ d="M 0,595.276 H 841.89 V 0 H 0 Z"
+ id="path18"
+ inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1916"
+ inkscape:window-height="1025"
+ id="namedview4"
+ showgrid="false"
+ inkscape:zoom="1.2041016"
+ inkscape:cx="512"
+ inkscape:cy="207"
+ inkscape:window-x="1360"
+ inkscape:window-y="53"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g14"
+ fit-margin-top="48"
+ fit-margin-left="48"
+ fit-margin-right="48"
+ fit-margin-bottom="48"
+ inkscape:document-rotation="0"
+ inkscape:pagecheckerboard="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:object-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-bbox="false"
+ inkscape:bbox-paths="false"
+ inkscape:snap-bbox-edge-midpoints="false"
+ inkscape:snap-bbox-midpoints="false"
+ inkscape:object-nodes="true"
+ inkscape:snap-others="false"
+ units="px" /><g
+ id="g10"
+ inkscape:groupmode="layer"
+ inkscape:label="godot_engine_logo_2017_curves-01"
+ transform="matrix(1.25,0,0,-1.25,-94.249997,597.49874)"><g
+ id="g12"><g
+ id="g14"
+ clip-path="url(#clipPath16)"><g
+ id="g78-3"
+ transform="matrix(1.1310535,0,0,1.1310535,348.13118,279.26721)"
+ style="fill:none;stroke:#ffffff;stroke-width:15.5885606;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"><path
+ id="path80-6"
+ style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:23.50866089;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 226.33398,222.09961 c -15.35819,3.41427 -30.55118,8.16852 -44.79492,15.33789 0.32575,12.57731 1.13879,24.62813 2.78711,36.86914 -5.5316,3.54397 -11.34506,6.58567 -16.51172,10.73438 -5.24959,4.03861 -10.60984,7.90172 -15.36328,12.625 -9.49632,-6.28112 -19.54792,-12.18415 -29.90234,-17.39454 -11.16124,12.01179 -21.59792,24.97673 -30.123049,39.48438 6.699845,10.51682 13.887809,21.08874 20.542969,29.33203 0.0561,36.95701 -0.082,77.98955 0,113.01958 0,51.83844 65.75967,76.75448 147.46094,77.04102 h 0.0996 0.0996 c 81.70127,-0.28654 147.43945,-25.20258 147.43945,-77.04102 0.0505,-37.842 0.0165,-79.50966 0.0214,-113.01953 7.68652,-9.6765 14.96452,-20.35178 20.54102,-29.33203 -8.52211,-14.50765 -18.96376,-27.47259 -30.125,-39.48438 -10.3514,5.21039 -20.40602,11.11342 -29.90234,17.39454 -4.75194,-4.72328 -10.1042,-8.58639 -15.36133,-12.625 -5.16515,-4.14871 -10.98481,-7.19041 -16.50586,-10.73438 1.6438,-12.24101 2.45595,-24.29183 2.7832,-36.86914 -14.24524,-7.16937 -29.43699,-11.92362 -44.80273,-15.33789 -6.13484,10.31068 -11.74471,21.47716 -16.63086,32.39258 -5.79401,-0.96818 -11.61467,-1.32712 -17.44336,-1.39649 v -0.01 c -0.0407,0 -0.0786,0.01 -0.11328,0.01 -0.0362,0 -0.0732,-0.01 -0.10938,-0.01 v 0.01 c -5.83925,0.0694 -11.65565,0.42831 -17.45117,1.39649 -4.88313,-10.91542 -10.49088,-22.0819 -16.63477,-32.39258 z"
+ transform="matrix(0.66309862,0,0,-0.66309862,-270.58935,290.52015)"
+ sodipodi:nodetypes="ccccccccccccccccccccccccsccccc" /></g><g
+ id="g20"
+ transform="matrix(1.1310535,0,0,1.1310535,531.44953,355.31567)"
+ style="stroke:none;stroke-width:10.60874228;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g24"
+ transform="matrix(1.1310535,0,0,1.1310535,607.8515,354.43097)"
+ style="stroke:none;stroke-width:10.60874228;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g28"
+ transform="matrix(1.1310535,0,0,1.1310535,700.81066,355.31567)"
+ style="stroke:none;stroke-width:10.60874228;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g32"
+ transform="matrix(1.1310535,0,0,1.1310535,789.01132,291.33514)"
+ style="stroke:none;stroke-width:10.60874228;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g36"
+ transform="matrix(1.1310535,0,0,1.1310535,468.26549,336.71278)"
+ style="stroke:none;stroke-width:10.60874228;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"><path
+ id="path38"
+ style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:15.5885606;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+ d="m 0,0 c -6.078,0.094 -13.034,-1.173 -13.034,-1.173 v -11.863 h 6.995 l -0.078,-5.288 c 0,-1.959 -1.942,-2.943 -5.815,-2.943 -3.878,0 -7.303,1.642 -10.274,4.917 -2.978,3.279 -4.459,8.072 -4.459,14.388 0,6.329 1.447,10.995 4.345,14.006 2.892,3.008 6.683,4.517 11.346,4.517 1.959,0 3.987,-0.316 6.096,-0.961 2.11,-0.639 3.519,-1.238 4.238,-1.799 0.713,-0.577 1.391,-0.85 2.032,-0.85 0.638,0 1.671,0.746 3.1,2.255 1.431,1.505 2.713,3.786 3.844,6.827 1.126,3.057 1.69,5.4 1.69,7.062 0,1.649 -0.036,2.786 -0.109,3.386 -1.581,1.73 -4.499,3.102 -8.755,4.122 -4.248,1.017 -9.011,1.522 -14.28,1.522 -11.594,0 -20.66,-3.65 -27.207,-10.95 -6.552,-7.303 -9.822,-16.783 -9.822,-28.452 0,-13.701 3.347,-24.087 10.041,-31.162 6.706,-7.074 15.51,-10.607 26.425,-10.607 5.87,0 11.08,0.505 15.632,1.522 4.557,1.013 7.586,2.053 9.093,3.105 l 0.452,35.33 C 11.496,-1.036 6.078,-0.104 0,0 m 283.58148,-40.1198 c 0,-1.496 -3.721,-2.255 -11.176,-2.255 -7.448,0 -11.18,0.759 -11.18,2.255 v 56.681 h -13.545 c -1.281,0 -2.185,1.727 -2.71,5.198 -0.226,1.652 -0.334,3.343 -0.334,5.077 0,1.724 0.108,3.422 0.334,5.077 0.525,3.462 1.429,5.202 2.71,5.202 h 49.112 c 1.279,0 2.179,-1.74 2.712,-5.202 0.221,-1.655 0.335,-3.353 0.335,-5.077 0,-1.734 -0.114,-3.425 -0.335,-5.077 -0.533,-3.471 -1.433,-5.198 -2.712,-5.198 h -13.211 z M 205.6005,16.447401 c -3.612,0 -6.645,-1.659 -9.095,-4.967 -2.44,-3.3110004 -3.662,-7.9580004 -3.662,-13.9380004 0,-5.993 1.169,-10.5809996 3.499,-13.7779996 2.33,-3.207 5.398,-4.804 9.2,-4.804 3.801,0 6.89,1.617 9.258,4.862 2.372,3.233 3.56,7.8609996 3.56,13.8859996 0,6.02 -1.225,10.654 -3.671,13.8900004 -2.447,3.232 -5.473,4.849 -9.089,4.849 m -0.058,-59.493 c -10.577,0 -19.193,3.46 -25.851,10.379 -6.663,6.925 -9.993,17.03 -9.993,30.3139996 0,13.2920004 3.367,23.3560004 10.1,30.2090004 6.741,6.844 15.431,10.269 26.086,10.269 10.651,0 19.246,-3.363 25.797,-10.109 6.55,-6.733 9.822,-16.94 9.822,-30.5910004 0,-13.6609996 -3.349,-23.8219996 -10.05,-30.4899996 -6.699,-6.654 -15.338,-9.981 -25.911,-9.981 m -82.13011,58.710808 v -33.768 c 0,-1.577 0.116,-2.571 0.342,-2.988 0.224,-0.415 0.903,-0.623 2.029,-0.623 4.144,0 7.283,1.548 9.429,4.634 2.151,3.083 3.215,8.2160005 3.215,15.4050005 0,7.192 -1.113,11.8779995 -3.325,14.0549995 -2.223,2.183 -5.744,3.285 -10.561,3.285 z m -21.675,-52.392 v 67.735 c 0,1.883 0.468,3.369 1.413,4.471 0.939,1.085 2.161,1.636 3.671,1.636 h 18.854 c 11.965,0 21.053,-3.018 27.257,-9.04 6.215,-6.02 9.322,-15.499 9.322,-28.44699953 0,-27.70000047 -11.821,-41.54700047 -35.456,-41.54700047 h -19.302 c -3.836,0 -5.759,1.727 -5.759,5.192 M 55.862999,16.447401 c -3.611,0 -6.636,-1.659 -9.09,-4.967 -2.441,-3.3110004 -3.668,-7.9580004 -3.668,-13.9380004 0,-5.993 1.166,-10.5809996 3.503,-13.7779996 2.333,-3.207 5.398,-4.804 9.2,-4.804 3.8,0 6.887,1.617 9.258,4.862 2.371,3.233 3.559,7.8609996 3.559,13.8859996 0,6.02 -1.227,10.654 -3.673,13.8900004 -2.443,3.232 -5.473,4.849 -9.089,4.849 m -0.055,-59.493 c -10.573,0 -19.195,3.46 -25.859,10.379 -6.655,6.925 -9.984,17.03 -9.984,30.3139996 0,13.2920004 3.367,23.3560004 10.101,30.2090004 6.736,6.844 15.431,10.269 26.082,10.269 10.649,0 19.251,-3.363 25.794,-10.109 6.555,-6.733 9.827,-16.94 9.827,-30.5910004 0,-13.6609996 -3.348,-23.8219996 -10.05,-30.4899996 -6.702,-6.654 -15.333,-9.981 -25.911,-9.981" /></g><g
+ id="g40"
+ transform="matrix(1.1310535,0,0,1.1310535,441.34721,235.75121)"
+ style="stroke:none;stroke-width:4.31066306;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g44"
+ transform="matrix(1.1310535,0,0,1.1310535,456.01527,232.82495)"
+ style="stroke:none;stroke-width:4.31066306;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g48"
+ transform="matrix(1.1310535,0,0,1.1310535,476.7303,259.10521)"
+ style="stroke:none;stroke-width:4.31066306;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g52"
+ transform="matrix(1.1310535,0,0,1.1310535,522.82277,256.83868)"
+ style="stroke:none;stroke-width:4.31066306;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g56"
+ transform="matrix(1.1310535,0,0,1.1310535,558.0805,256.83868)"
+ style="stroke:none;stroke-width:4.31066306;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g60"
+ transform="matrix(1.1310535,0,0,1.1310535,575.91679,259.10521)"
+ style="stroke:none;stroke-width:4.31066306;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g64"
+ transform="matrix(1.1310535,0,0,1.1310535,600.8685,242.30884)"
+ style="stroke:none;stroke-width:4.31066306;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g70"
+ transform="matrix(1.1310535,0,0,1.1310535,638.15379,259.10521)"
+ style="stroke:none;stroke-width:4.31066306;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" /><g
+ id="g74"
+ transform="matrix(1.1310535,0,0,1.1310535,669.70883,256.83868)"
+ style="stroke:none;stroke-width:4.31066306;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"><path
+ id="path76"
+ style="fill:#6d6e71;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:15.5885606;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+ d="M 0,0 C -1.763,0 -3.208,-0.802 -4.334,-2.402 -5.463,-4.008 -6.052,-5.987 -6.102,-8.346 H 5.56 v 1.082 c 0,2.086 -0.486,3.823 -1.47,5.201 C 3.109,-0.684 1.747,0 0,0 m 0.401,-23.76 c -2.733,0 -4.958,1.026 -6.681,3.073 -1.73,2.043 -2.595,4.657 -2.595,7.841 v 4.197 c 0,3.19 0.865,5.85 2.6,7.965 1.739,2.105 3.831,3.158 6.275,3.158 2.646,0 4.706,-0.939 6.172,-2.823 1.462,-1.887 2.195,-4.422 2.195,-7.603 v -2.773 H -6.102 v -2.102 c 0,-2.447 0.59,-4.484 1.757,-6.11 1.166,-1.63 2.748,-2.438 4.746,-2.438 1.382,0 2.579,0.244 3.578,0.724 1.012,0.491 1.869,1.179 2.591,2.082 l 1.147,-1.988 c -0.769,-0.968 -1.755,-1.75 -2.962,-2.33 -1.203,-0.577 -2.658,-0.873 -4.354,-0.873 m -28.299804,25.7639105 0.23,-4.178 c 0.676,1.48300001 1.562,2.63400001 2.678,3.435 1.115,0.805 2.422,1.213 3.916,1.213 2.258,0 3.995,-0.835 5.199,-2.51299999 1.211,-1.67500001 1.807,-4.27900001 1.807,-7.81200001 V -23.271089 h -2.825 v 15.3939995 c 0,2.888 -0.422,4.905 -1.261,6.075 -0.843,1.17000001 -2.063,1.75300001 -3.668,1.75300001 -1.434,0 -2.635,-0.466 -3.599,-1.41400001 -0.967,-0.939 -1.692,-2.19 -2.171,-3.767 V -23.271089 h -2.809 V 2.0039105 Z m -9.133591,-25.2752055 h -2.830008 V 2.0037072 h 2.830008 z m 0,32.4710013 h -2.830008 v 3.9819957 h 2.830008 z M -60.863903,-12.846289 c 0,-2.565 0.486,-4.605 1.472,-6.123 0.974,-1.532 2.457,-2.288 4.436,-2.288 1.356,0 2.498,0.361 3.435,1.101 0.934,0.74 1.672,1.77 2.218,3.077 v 12.5200001 c -0.525,1.346 -1.246,2.434 -2.157,3.272 -0.91,0.82400002 -2.062,1.23800002 -3.448,1.23800002 -1.975,0 -3.468,-0.86 -4.46,-2.58700002 -0.999,-1.73 -1.496,-3.986 -1.496,-6.756 z m -2.833,3.4540001 c 0,3.582 0.723,6.459 2.177,8.62700002 1.442,2.15699998 3.448,3.23899998 6.004,3.23899998 1.419,0 2.664,-0.346 3.728,-1.04 1.066,-0.68099998 1.947,-1.67799998 2.654,-2.946 l 0.274,3.516 h 2.381 V -23.294289 c 0,-3.239 -0.751,-5.749 -2.26,-7.525 -1.511,-1.769 -3.657,-2.665 -6.428,-2.665 -0.996,0 -2.067,0.156 -3.212,0.459 -1.147,0.303 -2.162,0.701 -3.052,1.2 l 0.776,2.463 c 0.759,-0.492 1.608,-0.873 2.548,-1.141 0.932,-0.277 1.895,-0.41 2.894,-0.41 2.009,0 3.498,0.645 4.46,1.932 0.966,1.304 1.45,3.19 1.45,5.687 v 3.057 c -0.717,-1.138 -1.597,-2.011 -2.64,-2.614 -1.039,-0.606 -2.253,-0.909 -3.622,-0.909 -2.539,0 -4.53,0.994 -5.968,2.982 -1.441,1.984 -2.164,4.631 -2.164,7.932 z m -19.227592,11.3961994 0.23,-4.178 c 0.674,1.48300001 1.564,2.63400001 2.682,3.435 1.108,0.805 2.413,1.213 3.914,1.213 2.258,0 3.988,-0.835 5.189,-2.51299999 1.214,-1.67500001 1.815,-4.27900001 1.815,-7.81200001 V -23.271089 h -2.825 v 15.3939995 c 0,2.888 -0.423,4.905 -1.264,6.075 -0.836,1.17000001 -2.065,1.75300001 -3.665,1.75300001 -1.435,0 -2.638,-0.466 -3.603,-1.41400001 -0.969,-0.939 -1.691,-2.19 -2.172,-3.767 V -23.271089 h -2.805 V 2.0039105 Z M -98.69412,0 c -1.763,0 -3.21,-0.802 -4.341,-2.402 -1.126,-1.606 -1.712,-3.585 -1.763,-5.944 h 11.663 v 1.082 c 0,2.086 -0.488,3.823 -1.474,5.201 -0.981,1.379 -2.341,2.063 -4.085,2.063 m 0.394,-23.76 c -2.726,0 -4.951,1.026 -6.679,3.073 -1.733,2.043 -2.6,4.657 -2.6,7.841 v 4.197 c 0,3.19 0.871,5.85 2.602,7.965 1.744,2.105 3.834,3.158 6.283,3.158 2.643,0 4.703,-0.939 6.164,-2.823 1.463,-1.887 2.197,-4.422 2.197,-7.603 v -2.773 h -14.465 v -2.102 c 0,-2.447 0.587,-4.484 1.76,-6.11 1.162,-1.63 2.742,-2.438 4.738,-2.438 1.387,0 2.585,0.244 3.585,0.724 1.007,0.491 1.866,1.179 2.589,2.082 l 1.141,-1.988 c -0.764,-0.968 -1.75,-1.75 -2.959,-2.33 -1.204,-0.577 -2.658,-0.873 -4.356,-0.873 M -129.86659,0 c -1.758,0 -3.202,-0.802 -4.334,-2.402 -1.133,-1.606 -1.718,-3.585 -1.765,-5.944 h 11.66 v 1.082 c 0,2.086 -0.489,3.823 -1.469,5.201 -0.986,1.379 -2.347,2.063 -4.092,2.063 m 0.397,-23.76 c -2.725,0 -4.954,1.026 -6.685,3.073 -1.726,2.043 -2.591,4.657 -2.591,7.841 v 4.197 c 0,3.19 0.867,5.85 2.602,7.965 1.739,2.105 3.828,3.158 6.277,3.158 2.648,0 4.699,-0.939 6.164,-2.823 1.468,-1.887 2.201,-4.422 2.201,-7.603 v -2.773 h -14.464 v -2.102 c 0,-2.447 0.586,-4.484 1.752,-6.11 1.168,-1.63 2.755,-2.438 4.744,-2.438 1.382,0 2.585,0.244 3.588,0.724 1.003,0.491 1.863,1.179 2.578,2.082 l 1.149,-1.988 c -0.763,-0.968 -1.752,-1.75 -2.959,-2.33 -1.204,-0.577 -2.659,-0.873 -4.356,-0.873 m -41.1488,25.7639105 0.24,-3.923 c 0.664,1.40400001 1.554,2.48600001 2.657,3.255 1.107,0.759 2.41,1.138 3.906,1.138 1.527,0 2.814,-0.444 3.852,-1.343 1.039,-0.89599999 1.805,-2.252 2.292,-4.074 0.623,1.682 1.505,3.01100001 2.65,3.973 1.145,0.964 2.534,1.444 4.143,1.444 2.217,0 3.937,-0.897 5.156,-2.69199999 1.224,-1.79900001 1.834,-4.55900001 1.834,-8.28800001 V -23.271089 h -2.823 v 14.8139995 c 0,3.1 -0.429,5.283 -1.263,6.538 -0.839,1.25700001 -2.042,1.89000001 -3.598,1.89000001 -1.637,0 -2.915,-0.691 -3.834,-2.09600001 -0.914,-1.405 -1.478,-3.161 -1.683,-5.282 v -0.655 -15.2089995 h -2.809 v 14.7979995 c 0,3.027 -0.424,5.194 -1.292,6.488 -0.864,1.29400001 -2.066,1.93600001 -3.609,1.93600001 -1.475,0 -2.668,-0.45 -3.562,-1.34200001 -0.9,-0.897 -1.54,-2.125 -1.928,-3.683 V -23.271089 h -2.806 V 2.0039105 Z M -188.9332,-21.231295 c 1.553,0 2.936,0.44 4.144,1.336 1.21,0.9 2.058,2.037 2.561,3.422 v 5.468 h -4.492 c -1.91,0 -3.44,-0.541 -4.585,-1.623 -1.148,-1.075 -1.716,-2.418 -1.716,-4.015 0,-1.349 0.355,-2.457 1.074,-3.311 0.718,-0.857 1.722,-1.277 3.014,-1.277 m 7.124,-2.04 c -0.14,0.876 -0.249,1.587 -0.318,2.144 -0.067,0.567 -0.101,1.131 -0.101,1.704 -0.767,-1.254 -1.757,-2.294 -2.98,-3.109 -1.221,-0.821 -2.579,-1.228 -4.075,-1.228 -2.092,0 -3.701,0.648 -4.84,1.946 -1.132,1.303 -1.704,3.059 -1.704,5.276 0,2.343 0.823,4.223 2.473,5.618 1.649,1.3950005 3.89,2.0920005 6.709,2.0920005 h 4.417 v 3.106 c 0,1.786 -0.456,3.193 -1.351,4.21 -0.914,1.00399996 -2.17,1.51199995911 -3.791,1.51199995911 -1.508,0 -2.752,-0.47899999911 -3.728,-1.44999995911 -0.973,-0.965 -1.456,-2.144 -1.456,-3.549 l -2.623,0.023 -0.046,0.137 c -0.074,1.906 0.647,3.591 2.168,5.08399996 1.515,1.48900004 3.459,2.22900004 5.825,2.22900004 2.338,0 4.22,-0.711 5.657,-2.12800004 1.429,-1.43099996 2.146,-3.47099996 2.146,-6.12399996 V -18.174295 c 0,-0.903 0.042,-1.78 0.121,-2.617 0.081,-0.848 0.212,-1.665 0.417,-2.48 z m -20.0925,4.627199 c -0.624,-1.28 -1.771,-2.454 -3.449,-3.516 -1.676,-1.069 -3.805,-1.6 -6.391,-1.6 -3.412,0 -6.156,1.075 -8.24,3.249 -2.076,2.157 -3.116,5.266 -3.116,9.323 v 10.116 c 0,3.969 0.98,7.013 2.946,9.138 1.962,2.108 4.59,3.177 7.872,3.177 3.208,0 5.695,-0.844 7.455,-2.513 1.755,-1.675 2.677,-4.015 2.757,-7.003 l -0.044,-0.133 h -2.619 c -0.094,2.29 -0.759,4.057 -2.01,5.305 -1.244,1.238 -3.095,1.864 -5.539,1.864 -2.473,0 -4.432,-0.837 -5.866,-2.516 -1.43,-1.675 -2.143,-4.103 -2.143,-7.293 v -10.174 c 0,-3.308 0.771,-5.83 2.311,-7.567 1.54,-1.724 3.616,-2.588 6.236,-2.588 1.913,0 3.451,0.339 4.602,1.033 1.155,0.684 1.956,1.519 2.409,2.51 v 8.861 h -7.06 v 2.463 h 9.889 z" /></g><g
+ id="g78"
+ transform="matrix(1.1310535,0,0,1.1310535,348.13109,279.2668)"
+ style="stroke-width:0.884131"><path
+ d="m 0,0 c 0,0 -0.325,1.994 -0.515,1.976 l -36.182,-3.491 c -2.879,-0.278 -5.115,-2.574 -5.317,-5.459 l -0.994,-14.247 -27.992,-1.997 -1.904,12.912 c -0.424,2.872 -2.932,5.037 -5.835,5.037 h -38.188 c -2.902,0 -5.41,-2.165 -5.834,-5.037 l -1.905,-12.912 -27.992,1.997 -0.994,14.247 c -0.202,2.886 -2.438,5.182 -5.317,5.46 l -36.2,3.49 c -0.187,0.018 -0.324,-1.978 -0.511,-1.978 l -0.049,-7.83 30.658,-4.944 1.004,-14.374 c 0.203,-2.91 2.551,-5.263 5.463,-5.472 l 38.551,-2.75 c 0.146,-0.01 0.29,-0.016 0.434,-0.016 2.897,0 5.401,2.166 5.825,5.038 l 1.959,13.286 h 28.005 l 1.959,-13.286 c 0.423,-2.871 2.93,-5.037 5.831,-5.037 0.142,0 0.284,0.005 0.423,0.015 l 38.556,2.75 c 2.911,0.209 5.26,2.562 5.463,5.472 l 1.003,14.374 30.645,4.966 z"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.884131"
+ id="path80"
+ inkscape:connector-curvature="0" /></g><g
+ id="g82"
+ transform="matrix(1.1310535,0,0,1.1310535,126.80608,346.04533)"
+ style="stroke-width:0.884131"><path
+ d="m 0,0 v -47.514 -6.035 -5.492 c 0.108,-0.001 0.216,-0.005 0.323,-0.015 l 36.196,-3.49 c 1.896,-0.183 3.382,-1.709 3.514,-3.609 l 1.116,-15.978 31.574,-2.253 2.175,14.747 c 0.282,1.912 1.922,3.329 3.856,3.329 h 38.188 c 1.933,0 3.573,-1.417 3.855,-3.329 l 2.175,-14.747 31.575,2.253 1.115,15.978 c 0.133,1.9 1.618,3.425 3.514,3.609 l 36.182,3.49 c 0.107,0.01 0.214,0.014 0.322,0.015 v 4.711 l 0.015,0.005 V 0 c 5.09692,6.4164715 9.92323,13.494208 13.621,19.449 -5.651,9.62 -12.575,18.217 -19.976,26.182 -6.864,-3.455 -13.531,-7.369 -19.828,-11.534 -3.151,3.132 -6.7,5.694 -10.186,8.372 -3.425,2.751 -7.285,4.768 -10.946,7.118 1.09,8.117 1.629,16.108 1.846,24.448 -9.446,4.754 -19.519,7.906 -29.708,10.17 -4.068,-6.837 -7.788,-14.241 -11.028,-21.479 -3.842,0.642 -7.702,0.88 -11.567,0.926 v 0.006 c -0.027,0 -0.052,-0.006 -0.075,-0.006 -0.024,0 -0.049,0.006 -0.073,0.006 V 63.652 C 93.903,63.606 90.046,63.368 86.203,62.726 82.965,69.964 79.247,77.368 75.173,84.205 64.989,81.941 54.915,78.789 45.47,74.035 45.686,65.695 46.225,57.704 47.318,49.587 43.65,47.237 39.795,45.22 36.369,42.469 32.888,39.791 29.333,37.229 26.181,34.097 19.884,38.262 13.219,42.176 6.353,45.631 -1.048,37.666 -7.968,29.069 -13.621,19.449 -9.1783421,12.475308 -4.4130298,5.4661124 0,0 Z"
+ style="fill:#478cbf;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.884131"
+ id="path84"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccsssscccccccccccccccccccsccccccccccc" /></g><g
+ id="g86"
+ transform="matrix(1.1310535,0,0,1.1310535,311.40329,266.88437)"
+ style="stroke-width:0.884131"><path
+ d="m 0,0 -1.121,-16.063 c -0.135,-1.936 -1.675,-3.477 -3.611,-3.616 l -38.555,-2.751 c -0.094,-0.007 -0.188,-0.01 -0.281,-0.01 -1.916,0 -3.569,1.406 -3.852,3.33 l -2.211,14.994 H -81.09 l -2.211,-14.994 c -0.297,-2.018 -2.101,-3.469 -4.133,-3.32 l -38.555,2.751 c -1.936,0.139 -3.476,1.68 -3.611,3.616 L -130.721,0 -163.268,3.138 c 0.015,-3.498 0.06,-7.33 0.06,-8.093 0,-34.374 43.605,-50.896 97.781,-51.086 h 0.066 0.067 c 54.176,0.19 97.766,16.712 97.766,51.086 0,0.777 0.047,4.593 0.063,8.093 z"
+ style="fill:#478cbf;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.884131"
+ id="path88"
+ inkscape:connector-curvature="0" /></g><g
+ id="g90"
+ transform="matrix(1.1310535,0,0,1.1310535,204.11393,318.93771)"
+ style="stroke-width:0.884131"><path
+ d="m 0,0 c 0,-12.052 -9.765,-21.815 -21.813,-21.815 -12.042,0 -21.81,9.763 -21.81,21.815 0,12.044 9.768,21.802 21.81,21.802 C -9.765,21.802 0,12.044 0,0"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.884131"
+ id="path92"
+ inkscape:connector-curvature="0" /></g><g
+ id="g94"
+ transform="matrix(1.1310535,0,0,1.1310535,198.17748,317.47435)"
+ style="stroke-width:0.884131"><path
+ d="m 0,0 c 0,-7.994 -6.479,-14.473 -14.479,-14.473 -7.996,0 -14.479,6.479 -14.479,14.473 0,7.994 6.483,14.479 14.479,14.479 C -6.479,14.479 0,7.994 0,0"
+ style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.884131"
+ id="path96"
+ inkscape:connector-curvature="0" /></g><g
+ id="g98"
+ transform="matrix(1.1310535,0,0,1.1310535,237.47503,292.01909)"
+ style="stroke-width:0.884131"><path
+ d="m 0,0 c -3.878,0 -7.021,2.858 -7.021,6.381 v 20.081 c 0,3.52 3.143,6.381 7.021,6.381 3.878,0 7.028,-2.861 7.028,-6.381 V 6.381 C 7.028,2.858 3.878,0 0,0"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.884131"
+ id="path100"
+ inkscape:connector-curvature="0" /></g><g
+ id="g102"
+ transform="matrix(1.1310535,0,0,1.1310535,270.84021,318.93771)"
+ style="stroke-width:0.884131"><path
+ d="m 0,0 c 0,-12.052 9.765,-21.815 21.815,-21.815 12.041,0 21.808,9.763 21.808,21.815 0,12.044 -9.767,21.802 -21.808,21.802 C 9.765,21.802 0,12.044 0,0"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.884131"
+ id="path104"
+ inkscape:connector-curvature="0" /></g><g
+ id="g106"
+ transform="matrix(1.1310535,0,0,1.1310535,276.77813,317.47435)"
+ style="stroke-width:0.884131"><path
+ d="m 0,0 c 0,-7.994 6.477,-14.473 14.471,-14.473 8.002,0 14.479,6.479 14.479,14.473 0,7.994 -6.477,14.479 -14.479,14.479 C 6.477,14.479 0,7.994 0,0"
+ style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.884131"
+ id="path108"
+ inkscape:connector-curvature="0" /></g></g></g></g></svg>
diff --git a/main/main.cpp b/main/main.cpp
index bb16c49983..67d8d93728 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2252,7 +2252,7 @@ bool Main::start() {
ProjectSettings::get_singleton()->set_custom_property_info(
"rendering/textures/canvas_textures/default_texture_filter",
PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_filter", PROPERTY_HINT_ENUM,
- "Nearest,Linear,MipmapLinear,MipmapNearest"));
+ "Nearest,Linear,Linear Mipmap,Nearest Mipmap"));
GLOBAL_DEF_BASIC("rendering/textures/canvas_textures/default_texture_repeat", 0);
ProjectSettings::get_singleton()->set_custom_property_info(
"rendering/textures/canvas_textures/default_texture_repeat",
diff --git a/misc/dist/html/fixed-size.html b/misc/dist/html/fixed-size.html
deleted file mode 100644
index 9d0a946497..0000000000
--- a/misc/dist/html/fixed-size.html
+++ /dev/null
@@ -1,389 +0,0 @@
-<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
-<head>
- <meta charset="utf-8" />
- <link id='-gd-engine-icon' rel='icon' type='image/png' href='favicon.png' />
- <title>$GODOT_PROJECT_NAME</title>
- <style type="text/css">
-
- body {
- margin: 0;
- border: 0 none;
- padding: 0;
- text-align: center;
- background-color: #222226;
- font-family: 'Noto Sans', Arial, sans-serif;
- }
-
-
- /* Godot Engine default theme style
- * ================================ */
-
- .godot {
- color: #e0e0e0;
- background-color: #3b3943;
- background-image: linear-gradient(to bottom, #403e48, #35333c);
- border: 1px solid #45434e;
- box-shadow: 0 0 1px 1px #2f2d35;
- }
-
- button.godot {
- font-family: 'Droid Sans', Arial, sans-serif; /* override user agent style */
- padding: 1px 5px;
- background-color: #37353f;
- background-image: linear-gradient(to bottom, #413e49, #3a3842);
- border: 1px solid #514f5d;
- border-radius: 1px;
- box-shadow: 0 0 1px 1px #2a2930;
- }
-
- button.godot:hover {
- color: #f0f0f0;
- background-color: #44414e;
- background-image: linear-gradient(to bottom, #494652, #423f4c);
- border: 1px solid #5a5667;
- box-shadow: 0 0 1px 1px #26252b;
- }
-
- button.godot:active {
- color: #fff;
- background-color: #3e3b46;
- background-image: linear-gradient(to bottom, #36343d, #413e49);
- border: 1px solid #4f4c59;
- box-shadow: 0 0 1px 1px #26252b;
- }
-
- button.godot:disabled {
- color: rgba(230, 230, 230, 0.2);
- background-color: #3d3d3d;
- background-image: linear-gradient(to bottom, #434343, #393939);
- border: 1px solid #474747;
- box-shadow: 0 0 1px 1px #2d2b33;
- }
-
-
- /* Canvas / wrapper
- * ================ */
-
- #container {
- display: inline-block; /* scale with canvas */
- vertical-align: top; /* prevent extra height */
- position: relative; /* root for absolutely positioned overlay */
- margin: 0;
- border: 0 none;
- padding: 0;
- background-color: #0c0c0c;
- }
-
- #canvas {
- display: block;
- margin: 0 auto;
- color: white;
- }
-
- #canvas:focus {
- outline: none;
- }
-
-
- /* Status display
- * ============== */
-
- #status {
- position: absolute;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- /* don't consume click events - make children visible explicitly */
- visibility: hidden;
- }
-
- #status-progress {
- width: 244px;
- height: 7px;
- background-color: #38363A;
- border: 1px solid #444246;
- padding: 1px;
- box-shadow: 0 0 2px 1px #1B1C22;
- border-radius: 2px;
- visibility: visible;
- }
-
- #status-progress-inner {
- height: 100%;
- width: 0;
- box-sizing: border-box;
- transition: width 0.5s linear;
- background-color: #202020;
- border: 1px solid #222223;
- box-shadow: 0 0 1px 1px #27282E;
- border-radius: 3px;
- }
-
- #status-indeterminate {
- visibility: visible;
- position: relative;
- }
-
- #status-indeterminate > div {
- width: 3px;
- height: 0;
- border-style: solid;
- border-width: 6px 2px 0 2px;
- border-color: #2b2b2b transparent transparent transparent;
- transform-origin: center 14px;
- position: absolute;
- }
-
- #status-indeterminate > div:nth-child(1) { transform: rotate( 22.5deg); }
- #status-indeterminate > div:nth-child(2) { transform: rotate( 67.5deg); }
- #status-indeterminate > div:nth-child(3) { transform: rotate(112.5deg); }
- #status-indeterminate > div:nth-child(4) { transform: rotate(157.5deg); }
- #status-indeterminate > div:nth-child(5) { transform: rotate(202.5deg); }
- #status-indeterminate > div:nth-child(6) { transform: rotate(247.5deg); }
- #status-indeterminate > div:nth-child(7) { transform: rotate(292.5deg); }
- #status-indeterminate > div:nth-child(8) { transform: rotate(337.5deg); }
-
- #status-notice {
- margin: 0 100px;
- line-height: 1.3;
- visibility: visible;
- padding: 4px 6px;
- visibility: visible;
- }
-
-
- /* Debug output
- * ============ */
-
- #output-panel {
- display: none;
- max-width: 700px;
- font-size: small;
- margin: 6px auto 0;
- padding: 0 4px 4px;
- text-align: left;
- line-height: 2.2;
- }
-
- #output-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
-
- #output-container {
- padding: 6px;
- background-color: #2c2a32;
- box-shadow: inset 0 0 1px 1px #232127;
- color: #bbb;
- }
-
- #output-scroll {
- line-height: 1;
- height: 12em;
- overflow-y: scroll;
- white-space: pre-wrap;
- font-size: small;
- font-family: "Lucida Console", Monaco, monospace;
- }
- </style>
-$GODOT_HEAD_INCLUDE
-</head>
-<body>
- <div id="container">
- <canvas id="canvas" width="640" height="480">
- HTML5 canvas appears to be unsupported in the current browser.<br />
- Please try updating or use a different browser.
- </canvas>
- <div id="status">
- <div id='status-progress' style='display: none;' oncontextmenu="event.preventDefault();"><div id ='status-progress-inner'></div></div>
- <div id='status-indeterminate' style='display: none;' oncontextmenu="event.preventDefault();">
- <div></div>
- <div></div>
- <div></div>
- <div></div>
- <div></div>
- <div></div>
- <div></div>
- <div></div>
- </div>
- <div id="status-notice" class="godot" style='display: none;'></div>
- </div>
- </div>
- <div id="output-panel" class="godot">
- <div id="output-header">
- Output:
- <button id='output-clear' class='godot' type='button' autocomplete='off'>Clear</button>
- </div>
- <div id="output-container"><div id="output-scroll"></div></div>
- </div>
-
- <script type="text/javascript" src="$GODOT_BASENAME.js"></script>
- <script type="text/javascript">//<![CDATA[
-
- var engine = new Engine;
-
- (function() {
- const EXECUTABLE_NAME = '$GODOT_BASENAME';
- const MAIN_PACK = '$GODOT_BASENAME.pck';
- const EXTRA_ARGS = JSON.parse('$GODOT_ARGS');
- const DEBUG_ENABLED = $GODOT_DEBUG_ENABLED;
- const INDETERMINATE_STATUS_STEP_MS = 100;
-
- var canvas = document.getElementById('canvas');
- var statusProgress = document.getElementById('status-progress');
- var statusProgressInner = document.getElementById('status-progress-inner');
- var statusIndeterminate = document.getElementById('status-indeterminate');
- var statusNotice = document.getElementById('status-notice');
-
- var initializing = true;
- var statusMode = 'hidden';
- var indeterminiateStatusAnimationId = 0;
-
- function setStatusMode(mode) {
- if (statusMode === mode || !initializing)
- return;
- [statusProgress, statusIndeterminate, statusNotice].forEach(elem => {
- elem.style.display = 'none';
- });
- if (indeterminiateStatusAnimationId !== 0) {
- cancelAnimationFrame(indeterminiateStatusAnimationId);
- indeterminiateStatusAnimationId = 0;
- }
- switch (mode) {
- case 'progress':
- statusProgress.style.display = 'block';
- break;
- case 'indeterminate':
- statusIndeterminate.style.display = 'block';
- indeterminiateStatusAnimationId = requestAnimationFrame(animateStatusIndeterminate);
- break;
- case 'notice':
- statusNotice.style.display = 'block';
- break;
- case 'hidden':
- break;
- default:
- throw new Error("Invalid status mode");
- }
- statusMode = mode;
- }
-
- function animateStatusIndeterminate(ms) {
- var i = Math.floor(ms / INDETERMINATE_STATUS_STEP_MS % 8);
- if (statusIndeterminate.children[i].style.borderTopColor == '') {
- Array.prototype.slice.call(statusIndeterminate.children).forEach(child => {
- child.style.borderTopColor = '';
- });
- statusIndeterminate.children[i].style.borderTopColor = '#dfdfdf';
- }
- requestAnimationFrame(animateStatusIndeterminate);
- }
-
- function setStatusNotice(text) {
- while (statusNotice.lastChild) {
- statusNotice.removeChild(statusNotice.lastChild);
- }
- var lines = text.split('\n');
- lines.forEach((line, index) => {
- statusNotice.appendChild(document.createTextNode(line));
- statusNotice.appendChild(document.createElement('br'));
- });
- };
-
- engine.setProgressFunc((current, total) => {
- if (total > 0) {
- statusProgressInner.style.width = current/total * 100 + '%';
- setStatusMode('progress');
- if (current === total) {
- // wait for progress bar animation
- setTimeout(() => {
- setStatusMode('indeterminate');
- }, 500);
- }
- } else {
- setStatusMode('indeterminate');
- }
- });
-
- if (DEBUG_ENABLED) {
- var outputRoot = document.getElementById("output-panel");
- var outputScroll = document.getElementById("output-scroll");
- var OUTPUT_MSG_COUNT_MAX = 400;
-
- document.getElementById('output-clear').addEventListener('click', () => {
- while (outputScroll.firstChild) {
- outputScroll.firstChild.remove();
- }
- });
-
- outputRoot.style.display = 'block';
-
- function print(text) {
- while (outputScroll.childElementCount >= OUTPUT_MSG_COUNT_MAX) {
- outputScroll.firstChild.remove();
- }
- var msg = document.createElement("div");
- if (String.prototype.trim.call(text).startsWith("**ERROR**")) {
- msg.style.color = "#d44";
- } else if (String.prototype.trim.call(text).startsWith("**WARNING**")) {
- msg.style.color = "#ccc000";
- } else if (String.prototype.trim.call(text).startsWith("**SCRIPT ERROR**")) {
- msg.style.color = "#c6d";
- }
- msg.textContent = text;
- var scrollToBottom = outputScroll.scrollHeight - (outputScroll.clientHeight + outputScroll.scrollTop) < 10;
- outputScroll.appendChild(msg);
- if (scrollToBottom) {
- outputScroll.scrollTop = outputScroll.scrollHeight;
- }
- };
-
- function printError(text) {
- if (!String.prototype.trim.call(text).startsWith('**ERROR**: ')) {
- text = '**ERROR**: ' + text;
- }
- print(text);
- }
-
- engine.setStdoutFunc(text => {
- print(text);
- console.log(text);
- });
-
- engine.setStderrFunc(text => {
- printError(text);
- console.warn(text);
- });
- }
-
- function displayFailureNotice(err) {
- var msg = err.message || err;
- if (DEBUG_ENABLED) {
- printError(msg);
- }
- console.error(msg);
- setStatusNotice(msg);
- setStatusMode('notice');
- initializing = false;
- };
-
- if (!Engine.isWebGLAvailable()) {
- displayFailureNotice("WebGL not available");
- } else {
- setStatusMode('indeterminate');
- engine.setCanvas(canvas);
- engine.startGame(EXECUTABLE_NAME, MAIN_PACK, EXTRA_ARGS).then(() => {
- setStatusMode('hidden');
- initializing = false;
- }, displayFailureNotice);
- }
- })();
- //]]></script>
-</body>
-</html>
diff --git a/modules/SCsub b/modules/SCsub
index 64da3bd0be..5ff4623743 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -10,6 +10,7 @@ env_modules = env.Clone()
Export("env_modules")
# Header with MODULE_*_ENABLED defines.
+env.Depends("modules_enabled.gen.h", Value(env.module_list))
env.CommandNoCache(
"modules_enabled.gen.h",
Value(env.module_list),
@@ -23,6 +24,7 @@ env.CommandNoCache(
# Header to be included in `tests/test_main.cpp` to run module-specific tests.
if env["tests"]:
+ env.Depends("modules_tests.gen.h", Value(env.module_list))
env.CommandNoCache(
"modules_tests.gen.h",
Value(env.module_list),
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index 1cf77b307d..9491373013 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -157,7 +157,7 @@ Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_por
_setup_compressor();
- IP_Address ip;
+ IPAddress ip;
if (p_address.is_valid_ip_address()) {
ip = p_address;
} else {
@@ -749,12 +749,12 @@ void NetworkedMultiplayerENet::enet_compressor_destroy(void *context) {
// Nothing to do
}
-IP_Address NetworkedMultiplayerENet::get_peer_address(int p_peer_id) const {
- ERR_FAIL_COND_V_MSG(!peer_map.has(p_peer_id), IP_Address(), vformat("Peer ID %d not found in the list of peers.", p_peer_id));
- ERR_FAIL_COND_V_MSG(!is_server() && p_peer_id != 1, IP_Address(), "Can't get the address of peers other than the server (ID -1) when acting as a client.");
- ERR_FAIL_COND_V_MSG(peer_map[p_peer_id] == nullptr, IP_Address(), vformat("Peer ID %d found in the list of peers, but is null.", p_peer_id));
+IPAddress NetworkedMultiplayerENet::get_peer_address(int p_peer_id) const {
+ ERR_FAIL_COND_V_MSG(!peer_map.has(p_peer_id), IPAddress(), vformat("Peer ID %d not found in the list of peers.", p_peer_id));
+ ERR_FAIL_COND_V_MSG(!is_server() && p_peer_id != 1, IPAddress(), "Can't get the address of peers other than the server (ID -1) when acting as a client.");
+ ERR_FAIL_COND_V_MSG(peer_map[p_peer_id] == nullptr, IPAddress(), vformat("Peer ID %d found in the list of peers, but is null.", p_peer_id));
- IP_Address out;
+ IPAddress out;
#ifdef GODOT_ENET
out.set_ipv6((uint8_t *)&(peer_map[p_peer_id]->address.host));
#else
@@ -877,7 +877,7 @@ NetworkedMultiplayerENet::NetworkedMultiplayerENet() {
enet_compressor.decompress = enet_decompress;
enet_compressor.destroy = enet_compressor_destroy;
- bind_ip = IP_Address("*");
+ bind_ip = IPAddress("*");
}
NetworkedMultiplayerENet::~NetworkedMultiplayerENet() {
@@ -888,7 +888,7 @@ NetworkedMultiplayerENet::~NetworkedMultiplayerENet() {
// Sets IP for ENet to bind when using create_server or create_client
// if no IP is set, then ENet bind to ENET_HOST_ANY
-void NetworkedMultiplayerENet::set_bind_ip(const IP_Address &p_ip) {
+void NetworkedMultiplayerENet::set_bind_ip(const IPAddress &p_ip) {
ERR_FAIL_COND_MSG(!p_ip.is_valid() && !p_ip.is_wildcard(), vformat("Invalid bind IP address: %s", String(p_ip)));
bind_ip = p_ip;
diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h
index c589cd9fbf..2d928859fa 100644
--- a/modules/enet/networked_multiplayer_enet.h
+++ b/modules/enet/networked_multiplayer_enet.h
@@ -108,7 +108,7 @@ private:
static void enet_compressor_destroy(void *context);
void _setup_compressor();
- IP_Address bind_ip;
+ IPAddress bind_ip;
bool dtls_enabled = false;
Ref<CryptoKey> dtls_key;
@@ -125,7 +125,7 @@ public:
virtual int get_packet_peer() const override;
- virtual IP_Address get_peer_address(int p_peer_id) const;
+ virtual IPAddress get_peer_address(int p_peer_id) const;
virtual int get_peer_port(int p_peer_id) const;
virtual int get_local_port() const;
void set_peer_timeout(int p_peer_id, int p_timeout_limit, int p_timeout_min, int p_timeout_max);
@@ -171,7 +171,7 @@ public:
NetworkedMultiplayerENet();
~NetworkedMultiplayerENet();
- void set_bind_ip(const IP_Address &p_ip);
+ void set_bind_ip(const IPAddress &p_ip);
void set_dtls_enabled(bool p_enabled);
bool is_dtls_enabled() const;
void set_dtls_verify_enabled(bool p_enabled);
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 3283f28de5..46af70f73c 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -1289,6 +1289,10 @@ void NativeScriptLanguage::finish() {
void NativeScriptLanguage::get_reserved_words(List<String> *p_words) const {
}
+bool NativeScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+ return false;
+}
+
void NativeScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
}
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 4bd54f9c46..ca5e76e43e 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -336,6 +336,7 @@ public:
virtual Error execute_file(const String &p_path);
virtual void finish();
virtual void get_reserved_words(List<String> *p_words) const;
+ virtual bool is_control_flow_keyword(String p_keyword) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp
index 3ed1dcaca9..1360cf0299 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_language.cpp
@@ -77,6 +77,10 @@ void PluginScriptLanguage::get_reserved_words(List<String> *p_words) const {
}
}
+bool PluginScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+ return false;
+}
+
void PluginScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
if (_desc.comment_delimiters) {
const char **w = _desc.comment_delimiters;
diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h
index 226b039265..957bf355ca 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.h
+++ b/modules/gdnative/pluginscript/pluginscript_language.h
@@ -71,6 +71,7 @@ public:
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const;
+ virtual bool is_control_flow_keyword(String p_keyword) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index ccc942d86b..ca646dff15 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -485,10 +485,15 @@ void GDScriptSyntaxHighlighter::_update_cache() {
/* Reserved words. */
const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+ const Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
List<String> keyword_list;
gdscript->get_reserved_words(&keyword_list);
for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) {
- keywords[E->get()] = keyword_color;
+ if (gdscript->is_control_flow_keyword(E->get())) {
+ keywords[E->get()] = control_flow_keyword_color;
+ } else {
+ keywords[E->get()] = keyword_color;
+ }
}
/* Comments */
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 5f590383d0..859c1acde9 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2135,6 +2135,19 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
}
}
+bool GDScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+ return p_keyword == "break" ||
+ p_keyword == "continue" ||
+ p_keyword == "elif" ||
+ p_keyword == "else" ||
+ p_keyword == "if" ||
+ p_keyword == "for" ||
+ p_keyword == "match" ||
+ p_keyword == "pass" ||
+ p_keyword == "return" ||
+ p_keyword == "while";
+}
+
bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
return p_type == "GDScript";
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 98da5ad4cb..6df66e876d 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -461,6 +461,7 @@ public:
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const;
+ virtual bool is_control_flow_keyword(String p_keywords) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual String _get_processed_template(const String &p_template, const String &p_base_class_name) const;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 099abd35a7..6ae825d2bd 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -2998,6 +2998,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
is_function = true;
[[fallthrough]];
}
+ case GDScriptParser::COMPLETION_CALL_ARGUMENTS:
case GDScriptParser::COMPLETION_IDENTIFIER: {
GDScriptParser::DataType base_type;
if (context.current_class) {
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 2e6388d92f..3f14156dfa 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -643,6 +643,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
push_error(error);
}
previous_was_underscore = true;
+ } else {
+ previous_was_underscore = false;
}
_advance();
}
@@ -714,6 +716,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
push_error(error);
}
previous_was_underscore = true;
+ } else {
+ previous_was_underscore = false;
}
_advance();
}
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index e63b6ab20e..15236d900d 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -49,8 +49,9 @@ void ExtendGDScriptParser::update_diagnostics() {
diagnostic.code = -1;
lsp::Range range;
lsp::Position pos;
- int line = LINE_NUMBER_TO_INDEX(error.line);
- const String &line_text = get_lines()[line];
+ const PackedStringArray lines = get_lines();
+ int line = CLAMP(LINE_NUMBER_TO_INDEX(error.line), 0, lines.size() - 1);
+ const String &line_text = lines[line];
pos.line = line;
pos.character = line_text.length() - line_text.strip_edges(true, false).length();
range.start = pos;
@@ -361,24 +362,73 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
r_symbol.detail += " -> " + p_func->get_datatype().to_string();
}
- for (int i = 0; i < p_func->body->locals.size(); i++) {
- const SuiteNode::Local &local = p_func->body->locals[i];
- lsp::DocumentSymbol symbol;
- symbol.name = local.name;
- symbol.kind = local.type == SuiteNode::Local::CONSTANT ? lsp::SymbolKind::Constant : lsp::SymbolKind::Variable;
- symbol.range.start.line = LINE_NUMBER_TO_INDEX(local.start_line);
- symbol.range.start.character = LINE_NUMBER_TO_INDEX(local.start_column);
- symbol.range.end.line = LINE_NUMBER_TO_INDEX(local.end_line);
- symbol.range.end.character = LINE_NUMBER_TO_INDEX(local.end_column);
- symbol.uri = uri;
- symbol.script_path = path;
- symbol.detail = SuiteNode::Local::CONSTANT ? "const " : "var ";
- symbol.detail += symbol.name;
- if (local.get_datatype().is_hard_type()) {
- symbol.detail += ": " + local.get_datatype().to_string();
+ List<GDScriptParser::SuiteNode *> function_nodes;
+
+ List<GDScriptParser::Node *> node_stack;
+ node_stack.push_back(p_func->body);
+
+ while (!node_stack.is_empty()) {
+ GDScriptParser::Node *node = node_stack[0];
+ node_stack.pop_front();
+
+ switch (node->type) {
+ case GDScriptParser::TypeNode::IF: {
+ GDScriptParser::IfNode *if_node = (GDScriptParser::IfNode *)node;
+ node_stack.push_back(if_node->true_block);
+ if (if_node->false_block) {
+ node_stack.push_back(if_node->false_block);
+ }
+ } break;
+
+ case GDScriptParser::TypeNode::FOR: {
+ GDScriptParser::ForNode *for_node = (GDScriptParser::ForNode *)node;
+ node_stack.push_back(for_node->loop);
+ } break;
+
+ case GDScriptParser::TypeNode::WHILE: {
+ GDScriptParser::WhileNode *while_node = (GDScriptParser::WhileNode *)node;
+ node_stack.push_back(while_node->loop);
+ } break;
+
+ case GDScriptParser::TypeNode::MATCH_BRANCH: {
+ GDScriptParser::MatchBranchNode *match_node = (GDScriptParser::MatchBranchNode *)node;
+ node_stack.push_back(match_node->block);
+ } break;
+
+ case GDScriptParser::TypeNode::SUITE: {
+ GDScriptParser::SuiteNode *suite_node = (GDScriptParser::SuiteNode *)node;
+ function_nodes.push_back(suite_node);
+ for (int i = 0; i < suite_node->statements.size(); ++i) {
+ node_stack.push_back(suite_node->statements[i]);
+ }
+ } break;
+
+ default:
+ continue;
+ }
+ }
+
+ for (List<GDScriptParser::SuiteNode *>::Element *N = function_nodes.front(); N; N = N->next()) {
+ const GDScriptParser::SuiteNode *suite_node = N->get();
+ for (int i = 0; i < suite_node->locals.size(); i++) {
+ const SuiteNode::Local &local = suite_node->locals[i];
+ lsp::DocumentSymbol symbol;
+ symbol.name = local.name;
+ symbol.kind = local.type == SuiteNode::Local::CONSTANT ? lsp::SymbolKind::Constant : lsp::SymbolKind::Variable;
+ symbol.range.start.line = LINE_NUMBER_TO_INDEX(local.start_line);
+ symbol.range.start.character = LINE_NUMBER_TO_INDEX(local.start_column);
+ symbol.range.end.line = LINE_NUMBER_TO_INDEX(local.end_line);
+ symbol.range.end.character = LINE_NUMBER_TO_INDEX(local.end_column);
+ symbol.uri = uri;
+ symbol.script_path = path;
+ symbol.detail = local.type == SuiteNode::Local::CONSTANT ? "const " : "var ";
+ symbol.detail += symbol.name;
+ if (local.get_datatype().is_hard_type()) {
+ symbol.detail += ": " + local.get_datatype().to_string();
+ }
+ symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(local.start_line));
+ r_symbol.children.push_back(symbol);
}
- symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(local.start_line));
- r_symbol.children.push_back(symbol);
}
}
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index 0432e7caea..c16a7fa889 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -255,7 +255,7 @@ void GDScriptLanguageProtocol::poll() {
}
}
-Error GDScriptLanguageProtocol::start(int p_port, const IP_Address &p_bind_ip) {
+Error GDScriptLanguageProtocol::start(int p_port, const IPAddress &p_bind_ip) {
return server->listen(p_port, p_bind_ip);
}
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h
index 8b08ae0655..a5c5a233b1 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.h
+++ b/modules/gdscript/language_server/gdscript_language_protocol.h
@@ -69,7 +69,7 @@ private:
static GDScriptLanguageProtocol *singleton;
HashMap<int, Ref<LSPeer>> clients;
- Ref<TCP_Server> server;
+ Ref<TCPServer> server;
int latest_client_id = 0;
int next_client_id = 0;
@@ -97,7 +97,7 @@ public:
_FORCE_INLINE_ bool is_initialized() const { return _initialized; }
void poll();
- Error start(int p_port, const IP_Address &p_bind_ip);
+ Error start(int p_port, const IPAddress &p_bind_ip);
void stop();
void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1);
diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp
index 98ada9de4d..340a7b9343 100644
--- a/modules/gdscript/language_server/gdscript_language_server.cpp
+++ b/modules/gdscript/language_server/gdscript_language_server.cpp
@@ -78,7 +78,7 @@ void GDScriptLanguageServer::thread_main(void *p_userdata) {
void GDScriptLanguageServer::start() {
port = (int)_EDITOR_GET("network/language_server/remote_port");
use_thread = (bool)_EDITOR_GET("network/language_server/use_thread");
- if (protocol.start(port, IP_Address("127.0.0.1")) == OK) {
+ if (protocol.start(port, IPAddress("127.0.0.1")) == OK) {
EditorNode::get_log()->add_message("--- GDScript language server started ---", EditorLog::MSG_TYPE_EDITOR);
if (use_thread) {
thread_running = true;
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 69cad1a335..9b7b2b36b4 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -42,6 +42,7 @@
#include "scene/resources/packed_scene.h"
void GDScriptWorkspace::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("didDeleteFiles"), &GDScriptWorkspace::did_delete_files);
ClassDB::bind_method(D_METHOD("symbol"), &GDScriptWorkspace::symbol);
ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script);
ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_local_script);
@@ -51,6 +52,16 @@ void GDScriptWorkspace::_bind_methods() {
ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api);
}
+void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) {
+ Array files = p_params["files"];
+ for (int i = 0; i < files.size(); ++i) {
+ Dictionary file = files[i];
+ String uri = file["uri"];
+ String path = get_file_path(uri);
+ parse_script(path, "");
+ }
+}
+
void GDScriptWorkspace::remove_cache_parser(const String &p_path) {
Map<String, ExtendGDScriptParser *>::Element *parser = parse_results.find(p_path);
Map<String, ExtendGDScriptParser *>::Element *script = scripts.find(p_path);
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index 7fd8bfcf20..27616a2989 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -89,6 +89,7 @@ public:
void resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list);
Dictionary generate_script_api(const String &p_path);
Error resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature);
+ void did_delete_files(const Dictionary &p_params);
GDScriptWorkspace();
~GDScriptWorkspace();
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 6635098be2..47bcfeaefc 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -1528,6 +1528,114 @@ struct SignatureHelp {
}
};
+/**
+ * A pattern to describe in which file operation requests or notifications
+ * the server is interested in.
+ */
+struct FileOperationPattern {
+ /**
+ * The glob pattern to match.
+ */
+ String glob = "**/*.gd";
+
+ /**
+ * Whether to match `file`s or `folder`s with this pattern.
+ *
+ * Matches both if undefined.
+ */
+ String matches = "file";
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["glob"] = glob;
+ dict["matches"] = matches;
+
+ return dict;
+ }
+};
+
+/**
+ * A filter to describe in which file operation requests or notifications
+ * the server is interested in.
+ */
+struct FileOperationFilter {
+ /**
+ * The actual file operation pattern.
+ */
+ FileOperationPattern pattern;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["pattern"] = pattern.to_json();
+
+ return dict;
+ }
+};
+
+/**
+ * The options to register for file operations.
+ */
+struct FileOperationRegistrationOptions {
+ /**
+ * The actual filters.
+ */
+ Vector<FileOperationFilter> filters;
+
+ FileOperationRegistrationOptions() {
+ filters.push_back(FileOperationFilter());
+ }
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ Array filts;
+ for (int i = 0; i < filters.size(); i++) {
+ filts.push_back(filters[i].to_json());
+ }
+ dict["filters"] = filts;
+
+ return dict;
+ }
+};
+
+/**
+ * The server is interested in file notifications/requests.
+ */
+struct FileOperations {
+ /**
+ * The server is interested in receiving didDeleteFiles file notifications.
+ */
+ FileOperationRegistrationOptions didDelete;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["didDelete"] = didDelete.to_json();
+
+ return dict;
+ }
+};
+
+/**
+ * Workspace specific server capabilities
+ */
+struct Workspace {
+ /**
+ * The server is interested in file notifications/requests.
+ */
+ FileOperations fileOperations;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["fileOperations"] = fileOperations.to_json();
+
+ return dict;
+ }
+};
+
struct ServerCapabilities {
/**
* Defines how text documents are synced. Is either a detailed structure defining each notification or
@@ -1590,6 +1698,11 @@ struct ServerCapabilities {
bool workspaceSymbolProvider = true;
/**
+ * The server supports workspace folder.
+ */
+ Workspace workspace;
+
+ /**
* The server provides code actions. The `CodeActionOptions` return type is only
* valid if the client signals code action literal support via the property
* `textDocument.codeAction.codeActionLiteralSupport`.
@@ -1676,6 +1789,7 @@ struct ServerCapabilities {
dict["documentHighlightProvider"] = documentHighlightProvider;
dict["documentSymbolProvider"] = documentSymbolProvider;
dict["workspaceSymbolProvider"] = workspaceSymbolProvider;
+ dict["workspace"] = workspace.to_json();
dict["codeActionProvider"] = codeActionProvider;
dict["documentFormattingProvider"] = documentFormattingProvider;
dict["documentRangeFormattingProvider"] = documentRangeFormattingProvider;
diff --git a/modules/mbedtls/packet_peer_mbed_dtls.cpp b/modules/mbedtls/packet_peer_mbed_dtls.cpp
index 342ded6ea1..d77d946a77 100644
--- a/modules/mbedtls/packet_peer_mbed_dtls.cpp
+++ b/modules/mbedtls/packet_peer_mbed_dtls.cpp
@@ -87,7 +87,7 @@ void PacketPeerMbedDTLS::_cleanup() {
int PacketPeerMbedDTLS::_set_cookie() {
// Setup DTLS session cookie for this client
uint8_t client_id[18];
- IP_Address addr = base->get_packet_address();
+ IPAddress addr = base->get_packet_address();
uint16_t port = base->get_packet_port();
memcpy(client_id, addr.get_ipv6(), 16);
memcpy(&client_id[16], (uint8_t *)&port, 2);
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 09f3ea1f50..ffb04bfd37 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -304,6 +304,26 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const {
}
}
+bool CSharpLanguage::is_control_flow_keyword(String p_keyword) const {
+ return p_keyword == "break" ||
+ p_keyword == "case" ||
+ p_keyword == "catch" ||
+ p_keyword == "continue" ||
+ p_keyword == "default" ||
+ p_keyword == "do" ||
+ p_keyword == "else" ||
+ p_keyword == "finally" ||
+ p_keyword == "for" ||
+ p_keyword == "foreach" ||
+ p_keyword == "goto" ||
+ p_keyword == "if" ||
+ p_keyword == "return" ||
+ p_keyword == "switch" ||
+ p_keyword == "throw" ||
+ p_keyword == "try" ||
+ p_keyword == "while";
+}
+
void CSharpLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("//"); // single-line comment
p_delimiters->push_back("/* */"); // delimited comment
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index dd93a86d7a..992c7e93c8 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -470,6 +470,7 @@ public:
/* EDITOR FUNCTIONS */
void get_reserved_words(List<String> *p_words) const override;
+ bool is_control_flow_keyword(String p_keyword) const;
void get_comment_delimiters(List<String> *p_delimiters) const override;
void get_string_delimiters(List<String> *p_delimiters) const override;
Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const override;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index 0c333d06ef..2a9f834aac 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -681,7 +681,7 @@ namespace Godot
name = name.Replace("_", String.Empty);
name = name.Replace("'", String.Empty);
name = name.Replace(".", String.Empty);
- name = name.ToLower();
+ name = name.ToUpper();
if (!Colors.namedColors.ContainsKey(name))
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
index d05a0414aa..4bb727bd35 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
@@ -9,301 +9,301 @@ namespace Godot
/// </summary>
public static class Colors
{
- // Color names and values are derived from core/color_names.inc
+ // Color names and values are derived from core/math/color_names.inc
internal static readonly Dictionary<string, Color> namedColors = new Dictionary<string, Color> {
- {"aliceblue", new Color(0.94f, 0.97f, 1.00f)},
- {"antiquewhite", new Color(0.98f, 0.92f, 0.84f)},
- {"aqua", new Color(0.00f, 1.00f, 1.00f)},
- {"aquamarine", new Color(0.50f, 1.00f, 0.83f)},
- {"azure", new Color(0.94f, 1.00f, 1.00f)},
- {"beige", new Color(0.96f, 0.96f, 0.86f)},
- {"bisque", new Color(1.00f, 0.89f, 0.77f)},
- {"black", new Color(0.00f, 0.00f, 0.00f)},
- {"blanchedalmond", new Color(1.00f, 0.92f, 0.80f)},
- {"blue", new Color(0.00f, 0.00f, 1.00f)},
- {"blueviolet", new Color(0.54f, 0.17f, 0.89f)},
- {"brown", new Color(0.65f, 0.16f, 0.16f)},
- {"burlywood", new Color(0.87f, 0.72f, 0.53f)},
- {"cadetblue", new Color(0.37f, 0.62f, 0.63f)},
- {"chartreuse", new Color(0.50f, 1.00f, 0.00f)},
- {"chocolate", new Color(0.82f, 0.41f, 0.12f)},
- {"coral", new Color(1.00f, 0.50f, 0.31f)},
- {"cornflower", new Color(0.39f, 0.58f, 0.93f)},
- {"cornsilk", new Color(1.00f, 0.97f, 0.86f)},
- {"crimson", new Color(0.86f, 0.08f, 0.24f)},
- {"cyan", new Color(0.00f, 1.00f, 1.00f)},
- {"darkblue", new Color(0.00f, 0.00f, 0.55f)},
- {"darkcyan", new Color(0.00f, 0.55f, 0.55f)},
- {"darkgoldenrod", new Color(0.72f, 0.53f, 0.04f)},
- {"darkgray", new Color(0.66f, 0.66f, 0.66f)},
- {"darkgreen", new Color(0.00f, 0.39f, 0.00f)},
- {"darkkhaki", new Color(0.74f, 0.72f, 0.42f)},
- {"darkmagenta", new Color(0.55f, 0.00f, 0.55f)},
- {"darkolivegreen", new Color(0.33f, 0.42f, 0.18f)},
- {"darkorange", new Color(1.00f, 0.55f, 0.00f)},
- {"darkorchid", new Color(0.60f, 0.20f, 0.80f)},
- {"darkred", new Color(0.55f, 0.00f, 0.00f)},
- {"darksalmon", new Color(0.91f, 0.59f, 0.48f)},
- {"darkseagreen", new Color(0.56f, 0.74f, 0.56f)},
- {"darkslateblue", new Color(0.28f, 0.24f, 0.55f)},
- {"darkslategray", new Color(0.18f, 0.31f, 0.31f)},
- {"darkturquoise", new Color(0.00f, 0.81f, 0.82f)},
- {"darkviolet", new Color(0.58f, 0.00f, 0.83f)},
- {"deeppink", new Color(1.00f, 0.08f, 0.58f)},
- {"deepskyblue", new Color(0.00f, 0.75f, 1.00f)},
- {"dimgray", new Color(0.41f, 0.41f, 0.41f)},
- {"dodgerblue", new Color(0.12f, 0.56f, 1.00f)},
- {"firebrick", new Color(0.70f, 0.13f, 0.13f)},
- {"floralwhite", new Color(1.00f, 0.98f, 0.94f)},
- {"forestgreen", new Color(0.13f, 0.55f, 0.13f)},
- {"fuchsia", new Color(1.00f, 0.00f, 1.00f)},
- {"gainsboro", new Color(0.86f, 0.86f, 0.86f)},
- {"ghostwhite", new Color(0.97f, 0.97f, 1.00f)},
- {"gold", new Color(1.00f, 0.84f, 0.00f)},
- {"goldenrod", new Color(0.85f, 0.65f, 0.13f)},
- {"gray", new Color(0.75f, 0.75f, 0.75f)},
- {"green", new Color(0.00f, 1.00f, 0.00f)},
- {"greenyellow", new Color(0.68f, 1.00f, 0.18f)},
- {"honeydew", new Color(0.94f, 1.00f, 0.94f)},
- {"hotpink", new Color(1.00f, 0.41f, 0.71f)},
- {"indianred", new Color(0.80f, 0.36f, 0.36f)},
- {"indigo", new Color(0.29f, 0.00f, 0.51f)},
- {"ivory", new Color(1.00f, 1.00f, 0.94f)},
- {"khaki", new Color(0.94f, 0.90f, 0.55f)},
- {"lavender", new Color(0.90f, 0.90f, 0.98f)},
- {"lavenderblush", new Color(1.00f, 0.94f, 0.96f)},
- {"lawngreen", new Color(0.49f, 0.99f, 0.00f)},
- {"lemonchiffon", new Color(1.00f, 0.98f, 0.80f)},
- {"lightblue", new Color(0.68f, 0.85f, 0.90f)},
- {"lightcoral", new Color(0.94f, 0.50f, 0.50f)},
- {"lightcyan", new Color(0.88f, 1.00f, 1.00f)},
- {"lightgoldenrod", new Color(0.98f, 0.98f, 0.82f)},
- {"lightgray", new Color(0.83f, 0.83f, 0.83f)},
- {"lightgreen", new Color(0.56f, 0.93f, 0.56f)},
- {"lightpink", new Color(1.00f, 0.71f, 0.76f)},
- {"lightsalmon", new Color(1.00f, 0.63f, 0.48f)},
- {"lightseagreen", new Color(0.13f, 0.70f, 0.67f)},
- {"lightskyblue", new Color(0.53f, 0.81f, 0.98f)},
- {"lightslategray", new Color(0.47f, 0.53f, 0.60f)},
- {"lightsteelblue", new Color(0.69f, 0.77f, 0.87f)},
- {"lightyellow", new Color(1.00f, 1.00f, 0.88f)},
- {"lime", new Color(0.00f, 1.00f, 0.00f)},
- {"limegreen", new Color(0.20f, 0.80f, 0.20f)},
- {"linen", new Color(0.98f, 0.94f, 0.90f)},
- {"magenta", new Color(1.00f, 0.00f, 1.00f)},
- {"maroon", new Color(0.69f, 0.19f, 0.38f)},
- {"mediumaquamarine", new Color(0.40f, 0.80f, 0.67f)},
- {"mediumblue", new Color(0.00f, 0.00f, 0.80f)},
- {"mediumorchid", new Color(0.73f, 0.33f, 0.83f)},
- {"mediumpurple", new Color(0.58f, 0.44f, 0.86f)},
- {"mediumseagreen", new Color(0.24f, 0.70f, 0.44f)},
- {"mediumslateblue", new Color(0.48f, 0.41f, 0.93f)},
- {"mediumspringgreen", new Color(0.00f, 0.98f, 0.60f)},
- {"mediumturquoise", new Color(0.28f, 0.82f, 0.80f)},
- {"mediumvioletred", new Color(0.78f, 0.08f, 0.52f)},
- {"midnightblue", new Color(0.10f, 0.10f, 0.44f)},
- {"mintcream", new Color(0.96f, 1.00f, 0.98f)},
- {"mistyrose", new Color(1.00f, 0.89f, 0.88f)},
- {"moccasin", new Color(1.00f, 0.89f, 0.71f)},
- {"navajowhite", new Color(1.00f, 0.87f, 0.68f)},
- {"navyblue", new Color(0.00f, 0.00f, 0.50f)},
- {"oldlace", new Color(0.99f, 0.96f, 0.90f)},
- {"olive", new Color(0.50f, 0.50f, 0.00f)},
- {"olivedrab", new Color(0.42f, 0.56f, 0.14f)},
- {"orange", new Color(1.00f, 0.65f, 0.00f)},
- {"orangered", new Color(1.00f, 0.27f, 0.00f)},
- {"orchid", new Color(0.85f, 0.44f, 0.84f)},
- {"palegoldenrod", new Color(0.93f, 0.91f, 0.67f)},
- {"palegreen", new Color(0.60f, 0.98f, 0.60f)},
- {"paleturquoise", new Color(0.69f, 0.93f, 0.93f)},
- {"palevioletred", new Color(0.86f, 0.44f, 0.58f)},
- {"papayawhip", new Color(1.00f, 0.94f, 0.84f)},
- {"peachpuff", new Color(1.00f, 0.85f, 0.73f)},
- {"peru", new Color(0.80f, 0.52f, 0.25f)},
- {"pink", new Color(1.00f, 0.75f, 0.80f)},
- {"plum", new Color(0.87f, 0.63f, 0.87f)},
- {"powderblue", new Color(0.69f, 0.88f, 0.90f)},
- {"purple", new Color(0.63f, 0.13f, 0.94f)},
- {"rebeccapurple", new Color(0.40f, 0.20f, 0.60f)},
- {"red", new Color(1.00f, 0.00f, 0.00f)},
- {"rosybrown", new Color(0.74f, 0.56f, 0.56f)},
- {"royalblue", new Color(0.25f, 0.41f, 0.88f)},
- {"saddlebrown", new Color(0.55f, 0.27f, 0.07f)},
- {"salmon", new Color(0.98f, 0.50f, 0.45f)},
- {"sandybrown", new Color(0.96f, 0.64f, 0.38f)},
- {"seagreen", new Color(0.18f, 0.55f, 0.34f)},
- {"seashell", new Color(1.00f, 0.96f, 0.93f)},
- {"sienna", new Color(0.63f, 0.32f, 0.18f)},
- {"silver", new Color(0.75f, 0.75f, 0.75f)},
- {"skyblue", new Color(0.53f, 0.81f, 0.92f)},
- {"slateblue", new Color(0.42f, 0.35f, 0.80f)},
- {"slategray", new Color(0.44f, 0.50f, 0.56f)},
- {"snow", new Color(1.00f, 0.98f, 0.98f)},
- {"springgreen", new Color(0.00f, 1.00f, 0.50f)},
- {"steelblue", new Color(0.27f, 0.51f, 0.71f)},
- {"tan", new Color(0.82f, 0.71f, 0.55f)},
- {"teal", new Color(0.00f, 0.50f, 0.50f)},
- {"thistle", new Color(0.85f, 0.75f, 0.85f)},
- {"tomato", new Color(1.00f, 0.39f, 0.28f)},
- {"transparent", new Color(1.00f, 1.00f, 1.00f, 0.00f)},
- {"turquoise", new Color(0.25f, 0.88f, 0.82f)},
- {"violet", new Color(0.93f, 0.51f, 0.93f)},
- {"webgreen", new Color(0.00f, 0.50f, 0.00f)},
- {"webgray", new Color(0.50f, 0.50f, 0.50f)},
- {"webmaroon", new Color(0.50f, 0.00f, 0.00f)},
- {"webpurple", new Color(0.50f, 0.00f, 0.50f)},
- {"wheat", new Color(0.96f, 0.87f, 0.70f)},
- {"white", new Color(1.00f, 1.00f, 1.00f)},
- {"whitesmoke", new Color(0.96f, 0.96f, 0.96f)},
- {"yellow", new Color(1.00f, 1.00f, 0.00f)},
- {"yellowgreen", new Color(0.60f, 0.80f, 0.20f)},
+ {"ALICEBLUE", new Color(0.94f, 0.97f, 1.00f)},
+ {"ANTIQUEWHITE", new Color(0.98f, 0.92f, 0.84f)},
+ {"AQUA", new Color(0.00f, 1.00f, 1.00f)},
+ {"AQUAMARINE", new Color(0.50f, 1.00f, 0.83f)},
+ {"AZURE", new Color(0.94f, 1.00f, 1.00f)},
+ {"BEIGE", new Color(0.96f, 0.96f, 0.86f)},
+ {"BISQUE", new Color(1.00f, 0.89f, 0.77f)},
+ {"BLACK", new Color(0.00f, 0.00f, 0.00f)},
+ {"BLANCHEDALMOND", new Color(1.00f, 0.92f, 0.80f)},
+ {"BLUE", new Color(0.00f, 0.00f, 1.00f)},
+ {"BLUEVIOLET", new Color(0.54f, 0.17f, 0.89f)},
+ {"BROWN", new Color(0.65f, 0.16f, 0.16f)},
+ {"BURLYWOOD", new Color(0.87f, 0.72f, 0.53f)},
+ {"CADETBLUE", new Color(0.37f, 0.62f, 0.63f)},
+ {"CHARTREUSE", new Color(0.50f, 1.00f, 0.00f)},
+ {"CHOCOLATE", new Color(0.82f, 0.41f, 0.12f)},
+ {"CORAL", new Color(1.00f, 0.50f, 0.31f)},
+ {"CORNFLOWERBLUE", new Color(0.39f, 0.58f, 0.93f)},
+ {"CORNSILK", new Color(1.00f, 0.97f, 0.86f)},
+ {"CRIMSON", new Color(0.86f, 0.08f, 0.24f)},
+ {"CYAN", new Color(0.00f, 1.00f, 1.00f)},
+ {"DARKBLUE", new Color(0.00f, 0.00f, 0.55f)},
+ {"DARKCYAN", new Color(0.00f, 0.55f, 0.55f)},
+ {"DARKGOLDENROD", new Color(0.72f, 0.53f, 0.04f)},
+ {"DARKGRAY", new Color(0.66f, 0.66f, 0.66f)},
+ {"DARKGREEN", new Color(0.00f, 0.39f, 0.00f)},
+ {"DARKKHAKI", new Color(0.74f, 0.72f, 0.42f)},
+ {"DARKMAGENTA", new Color(0.55f, 0.00f, 0.55f)},
+ {"DARKOLIVEGREEN", new Color(0.33f, 0.42f, 0.18f)},
+ {"DARKORANGE", new Color(1.00f, 0.55f, 0.00f)},
+ {"DARKORCHID", new Color(0.60f, 0.20f, 0.80f)},
+ {"DARKRED", new Color(0.55f, 0.00f, 0.00f)},
+ {"DARKSALMON", new Color(0.91f, 0.59f, 0.48f)},
+ {"DARKSEAGREEN", new Color(0.56f, 0.74f, 0.56f)},
+ {"DARKSLATEBLUE", new Color(0.28f, 0.24f, 0.55f)},
+ {"DARKSLATEGRAY", new Color(0.18f, 0.31f, 0.31f)},
+ {"DARKTURQUOISE", new Color(0.00f, 0.81f, 0.82f)},
+ {"DARKVIOLET", new Color(0.58f, 0.00f, 0.83f)},
+ {"DEEPPINK", new Color(1.00f, 0.08f, 0.58f)},
+ {"DEEPSKYBLUE", new Color(0.00f, 0.75f, 1.00f)},
+ {"DIMGRAY", new Color(0.41f, 0.41f, 0.41f)},
+ {"DODGERBLUE", new Color(0.12f, 0.56f, 1.00f)},
+ {"FIREBRICK", new Color(0.70f, 0.13f, 0.13f)},
+ {"FLORALWHITE", new Color(1.00f, 0.98f, 0.94f)},
+ {"FORESTGREEN", new Color(0.13f, 0.55f, 0.13f)},
+ {"FUCHSIA", new Color(1.00f, 0.00f, 1.00f)},
+ {"GAINSBORO", new Color(0.86f, 0.86f, 0.86f)},
+ {"GHOSTWHITE", new Color(0.97f, 0.97f, 1.00f)},
+ {"GOLD", new Color(1.00f, 0.84f, 0.00f)},
+ {"GOLDENROD", new Color(0.85f, 0.65f, 0.13f)},
+ {"GRAY", new Color(0.75f, 0.75f, 0.75f)},
+ {"GREEN", new Color(0.00f, 1.00f, 0.00f)},
+ {"GREENYELLOW", new Color(0.68f, 1.00f, 0.18f)},
+ {"HONEYDEW", new Color(0.94f, 1.00f, 0.94f)},
+ {"HOTPINK", new Color(1.00f, 0.41f, 0.71f)},
+ {"INDIANRED", new Color(0.80f, 0.36f, 0.36f)},
+ {"INDIGO", new Color(0.29f, 0.00f, 0.51f)},
+ {"IVORY", new Color(1.00f, 1.00f, 0.94f)},
+ {"KHAKI", new Color(0.94f, 0.90f, 0.55f)},
+ {"LAVENDER", new Color(0.90f, 0.90f, 0.98f)},
+ {"LAVENDERBLUSH", new Color(1.00f, 0.94f, 0.96f)},
+ {"LAWNGREEN", new Color(0.49f, 0.99f, 0.00f)},
+ {"LEMONCHIFFON", new Color(1.00f, 0.98f, 0.80f)},
+ {"LIGHTBLUE", new Color(0.68f, 0.85f, 0.90f)},
+ {"LIGHTCORAL", new Color(0.94f, 0.50f, 0.50f)},
+ {"LIGHTCYAN", new Color(0.88f, 1.00f, 1.00f)},
+ {"LIGHTGOLDENROD", new Color(0.98f, 0.98f, 0.82f)},
+ {"LIGHTGRAY", new Color(0.83f, 0.83f, 0.83f)},
+ {"LIGHTGREEN", new Color(0.56f, 0.93f, 0.56f)},
+ {"LIGHTPINK", new Color(1.00f, 0.71f, 0.76f)},
+ {"LIGHTSALMON", new Color(1.00f, 0.63f, 0.48f)},
+ {"LIGHTSEAGREEN", new Color(0.13f, 0.70f, 0.67f)},
+ {"LIGHTSKYBLUE", new Color(0.53f, 0.81f, 0.98f)},
+ {"LIGHTSLATEGRAY", new Color(0.47f, 0.53f, 0.60f)},
+ {"LIGHTSTEELBLUE", new Color(0.69f, 0.77f, 0.87f)},
+ {"LIGHTYELLOW", new Color(1.00f, 1.00f, 0.88f)},
+ {"LIME", new Color(0.00f, 1.00f, 0.00f)},
+ {"LIMEGREEN", new Color(0.20f, 0.80f, 0.20f)},
+ {"LINEN", new Color(0.98f, 0.94f, 0.90f)},
+ {"MAGENTA", new Color(1.00f, 0.00f, 1.00f)},
+ {"MAROON", new Color(0.69f, 0.19f, 0.38f)},
+ {"MEDIUMAQUAMARINE", new Color(0.40f, 0.80f, 0.67f)},
+ {"MEDIUMBLUE", new Color(0.00f, 0.00f, 0.80f)},
+ {"MEDIUMORCHID", new Color(0.73f, 0.33f, 0.83f)},
+ {"MEDIUMPURPLE", new Color(0.58f, 0.44f, 0.86f)},
+ {"MEDIUMSEAGREEN", new Color(0.24f, 0.70f, 0.44f)},
+ {"MEDIUMSLATEBLUE", new Color(0.48f, 0.41f, 0.93f)},
+ {"MEDIUMSPRINGGREEN", new Color(0.00f, 0.98f, 0.60f)},
+ {"MEDIUMTURQUOISE", new Color(0.28f, 0.82f, 0.80f)},
+ {"MEDIUMVIOLETRED", new Color(0.78f, 0.08f, 0.52f)},
+ {"MIDNIGHTBLUE", new Color(0.10f, 0.10f, 0.44f)},
+ {"MINTCREAM", new Color(0.96f, 1.00f, 0.98f)},
+ {"MISTYROSE", new Color(1.00f, 0.89f, 0.88f)},
+ {"MOCCASIN", new Color(1.00f, 0.89f, 0.71f)},
+ {"NAVAJOWHITE", new Color(1.00f, 0.87f, 0.68f)},
+ {"NAVYBLUE", new Color(0.00f, 0.00f, 0.50f)},
+ {"OLDLACE", new Color(0.99f, 0.96f, 0.90f)},
+ {"OLIVE", new Color(0.50f, 0.50f, 0.00f)},
+ {"OLIVEDRAB", new Color(0.42f, 0.56f, 0.14f)},
+ {"ORANGE", new Color(1.00f, 0.65f, 0.00f)},
+ {"ORANGERED", new Color(1.00f, 0.27f, 0.00f)},
+ {"ORCHID", new Color(0.85f, 0.44f, 0.84f)},
+ {"PALEGOLDENROD", new Color(0.93f, 0.91f, 0.67f)},
+ {"PALEGREEN", new Color(0.60f, 0.98f, 0.60f)},
+ {"PALETURQUOISE", new Color(0.69f, 0.93f, 0.93f)},
+ {"PALEVIOLETRED", new Color(0.86f, 0.44f, 0.58f)},
+ {"PAPAYAWHIP", new Color(1.00f, 0.94f, 0.84f)},
+ {"PEACHPUFF", new Color(1.00f, 0.85f, 0.73f)},
+ {"PERU", new Color(0.80f, 0.52f, 0.25f)},
+ {"PINK", new Color(1.00f, 0.75f, 0.80f)},
+ {"PLUM", new Color(0.87f, 0.63f, 0.87f)},
+ {"POWDERBLUE", new Color(0.69f, 0.88f, 0.90f)},
+ {"PURPLE", new Color(0.63f, 0.13f, 0.94f)},
+ {"REBECCAPURPLE", new Color(0.40f, 0.20f, 0.60f)},
+ {"RED", new Color(1.00f, 0.00f, 0.00f)},
+ {"ROSYBROWN", new Color(0.74f, 0.56f, 0.56f)},
+ {"ROYALBLUE", new Color(0.25f, 0.41f, 0.88f)},
+ {"SADDLEBROWN", new Color(0.55f, 0.27f, 0.07f)},
+ {"SALMON", new Color(0.98f, 0.50f, 0.45f)},
+ {"SANDYBROWN", new Color(0.96f, 0.64f, 0.38f)},
+ {"SEAGREEN", new Color(0.18f, 0.55f, 0.34f)},
+ {"SEASHELL", new Color(1.00f, 0.96f, 0.93f)},
+ {"SIENNA", new Color(0.63f, 0.32f, 0.18f)},
+ {"SILVER", new Color(0.75f, 0.75f, 0.75f)},
+ {"SKYBLUE", new Color(0.53f, 0.81f, 0.92f)},
+ {"SLATEBLUE", new Color(0.42f, 0.35f, 0.80f)},
+ {"SLATEGRAY", new Color(0.44f, 0.50f, 0.56f)},
+ {"SNOW", new Color(1.00f, 0.98f, 0.98f)},
+ {"SPRINGGREEN", new Color(0.00f, 1.00f, 0.50f)},
+ {"STEELBLUE", new Color(0.27f, 0.51f, 0.71f)},
+ {"TAN", new Color(0.82f, 0.71f, 0.55f)},
+ {"TEAL", new Color(0.00f, 0.50f, 0.50f)},
+ {"THISTLE", new Color(0.85f, 0.75f, 0.85f)},
+ {"TOMATO", new Color(1.00f, 0.39f, 0.28f)},
+ {"TRANSPARENT", new Color(1.00f, 1.00f, 1.00f, 0.00f)},
+ {"TURQUOISE", new Color(0.25f, 0.88f, 0.82f)},
+ {"VIOLET", new Color(0.93f, 0.51f, 0.93f)},
+ {"WEBGRAY", new Color(0.50f, 0.50f, 0.50f)},
+ {"WEBGREEN", new Color(0.00f, 0.50f, 0.00f)},
+ {"WEBMAROON", new Color(0.50f, 0.00f, 0.00f)},
+ {"WEBPURPLE", new Color(0.50f, 0.00f, 0.50f)},
+ {"WHEAT", new Color(0.96f, 0.87f, 0.70f)},
+ {"WHITE", new Color(1.00f, 1.00f, 1.00f)},
+ {"WHITESMOKE", new Color(0.96f, 0.96f, 0.96f)},
+ {"YELLOW", new Color(1.00f, 1.00f, 0.00f)},
+ {"YELLOWGREEN", new Color(0.60f, 0.80f, 0.20f)},
};
- public static Color AliceBlue { get { return namedColors["aliceblue"]; } }
- public static Color AntiqueWhite { get { return namedColors["antiquewhite"]; } }
- public static Color Aqua { get { return namedColors["aqua"]; } }
- public static Color Aquamarine { get { return namedColors["aquamarine"]; } }
- public static Color Azure { get { return namedColors["azure"]; } }
- public static Color Beige { get { return namedColors["beige"]; } }
- public static Color Bisque { get { return namedColors["bisque"]; } }
- public static Color Black { get { return namedColors["black"]; } }
- public static Color BlanchedAlmond { get { return namedColors["blanchedalmond"]; } }
- public static Color Blue { get { return namedColors["blue"]; } }
- public static Color BlueViolet { get { return namedColors["blueviolet"]; } }
- public static Color Brown { get { return namedColors["brown"]; } }
- public static Color BurlyWood { get { return namedColors["burlywood"]; } }
- public static Color CadetBlue { get { return namedColors["cadetblue"]; } }
- public static Color Chartreuse { get { return namedColors["chartreuse"]; } }
- public static Color Chocolate { get { return namedColors["chocolate"]; } }
- public static Color Coral { get { return namedColors["coral"]; } }
- public static Color Cornflower { get { return namedColors["cornflower"]; } }
- public static Color Cornsilk { get { return namedColors["cornsilk"]; } }
- public static Color Crimson { get { return namedColors["crimson"]; } }
- public static Color Cyan { get { return namedColors["cyan"]; } }
- public static Color DarkBlue { get { return namedColors["darkblue"]; } }
- public static Color DarkCyan { get { return namedColors["darkcyan"]; } }
- public static Color DarkGoldenrod { get { return namedColors["darkgoldenrod"]; } }
- public static Color DarkGray { get { return namedColors["darkgray"]; } }
- public static Color DarkGreen { get { return namedColors["darkgreen"]; } }
- public static Color DarkKhaki { get { return namedColors["darkkhaki"]; } }
- public static Color DarkMagenta { get { return namedColors["darkmagenta"]; } }
- public static Color DarkOliveGreen { get { return namedColors["darkolivegreen"]; } }
- public static Color DarkOrange { get { return namedColors["darkorange"]; } }
- public static Color DarkOrchid { get { return namedColors["darkorchid"]; } }
- public static Color DarkRed { get { return namedColors["darkred"]; } }
- public static Color DarkSalmon { get { return namedColors["darksalmon"]; } }
- public static Color DarkSeaGreen { get { return namedColors["darkseagreen"]; } }
- public static Color DarkSlateBlue { get { return namedColors["darkslateblue"]; } }
- public static Color DarkSlateGray { get { return namedColors["darkslategray"]; } }
- public static Color DarkTurquoise { get { return namedColors["darkturquoise"]; } }
- public static Color DarkViolet { get { return namedColors["darkviolet"]; } }
- public static Color DeepPink { get { return namedColors["deeppink"]; } }
- public static Color DeepSkyBlue { get { return namedColors["deepskyblue"]; } }
- public static Color DimGray { get { return namedColors["dimgray"]; } }
- public static Color DodgerBlue { get { return namedColors["dodgerblue"]; } }
- public static Color Firebrick { get { return namedColors["firebrick"]; } }
- public static Color FloralWhite { get { return namedColors["floralwhite"]; } }
- public static Color ForestGreen { get { return namedColors["forestgreen"]; } }
- public static Color Fuchsia { get { return namedColors["fuchsia"]; } }
- public static Color Gainsboro { get { return namedColors["gainsboro"]; } }
- public static Color GhostWhite { get { return namedColors["ghostwhite"]; } }
- public static Color Gold { get { return namedColors["gold"]; } }
- public static Color Goldenrod { get { return namedColors["goldenrod"]; } }
- public static Color Gray { get { return namedColors["gray"]; } }
- public static Color Green { get { return namedColors["green"]; } }
- public static Color GreenYellow { get { return namedColors["greenyellow"]; } }
- public static Color Honeydew { get { return namedColors["honeydew"]; } }
- public static Color HotPink { get { return namedColors["hotpink"]; } }
- public static Color IndianRed { get { return namedColors["indianred"]; } }
- public static Color Indigo { get { return namedColors["indigo"]; } }
- public static Color Ivory { get { return namedColors["ivory"]; } }
- public static Color Khaki { get { return namedColors["khaki"]; } }
- public static Color Lavender { get { return namedColors["lavender"]; } }
- public static Color LavenderBlush { get { return namedColors["lavenderblush"]; } }
- public static Color LawnGreen { get { return namedColors["lawngreen"]; } }
- public static Color LemonChiffon { get { return namedColors["lemonchiffon"]; } }
- public static Color LightBlue { get { return namedColors["lightblue"]; } }
- public static Color LightCoral { get { return namedColors["lightcoral"]; } }
- public static Color LightCyan { get { return namedColors["lightcyan"]; } }
- public static Color LightGoldenrod { get { return namedColors["lightgoldenrod"]; } }
- public static Color LightGray { get { return namedColors["lightgray"]; } }
- public static Color LightGreen { get { return namedColors["lightgreen"]; } }
- public static Color LightPink { get { return namedColors["lightpink"]; } }
- public static Color LightSalmon { get { return namedColors["lightsalmon"]; } }
- public static Color LightSeaGreen { get { return namedColors["lightseagreen"]; } }
- public static Color LightSkyBlue { get { return namedColors["lightskyblue"]; } }
- public static Color LightSlateGray { get { return namedColors["lightslategray"]; } }
- public static Color LightSteelBlue { get { return namedColors["lightsteelblue"]; } }
- public static Color LightYellow { get { return namedColors["lightyellow"]; } }
- public static Color Lime { get { return namedColors["lime"]; } }
- public static Color Limegreen { get { return namedColors["limegreen"]; } }
- public static Color Linen { get { return namedColors["linen"]; } }
- public static Color Magenta { get { return namedColors["magenta"]; } }
- public static Color Maroon { get { return namedColors["maroon"]; } }
- public static Color MediumAquamarine { get { return namedColors["mediumaquamarine"]; } }
- public static Color MediumBlue { get { return namedColors["mediumblue"]; } }
- public static Color MediumOrchid { get { return namedColors["mediumorchid"]; } }
- public static Color MediumPurple { get { return namedColors["mediumpurple"]; } }
- public static Color MediumSeaGreen { get { return namedColors["mediumseagreen"]; } }
- public static Color MediumSlateBlue { get { return namedColors["mediumslateblue"]; } }
- public static Color MediumSpringGreen { get { return namedColors["mediumspringgreen"]; } }
- public static Color MediumTurquoise { get { return namedColors["mediumturquoise"]; } }
- public static Color MediumVioletRed { get { return namedColors["mediumvioletred"]; } }
- public static Color MidnightBlue { get { return namedColors["midnightblue"]; } }
- public static Color MintCream { get { return namedColors["mintcream"]; } }
- public static Color MistyRose { get { return namedColors["mistyrose"]; } }
- public static Color Moccasin { get { return namedColors["moccasin"]; } }
- public static Color NavajoWhite { get { return namedColors["navajowhite"]; } }
- public static Color NavyBlue { get { return namedColors["navyblue"]; } }
- public static Color OldLace { get { return namedColors["oldlace"]; } }
- public static Color Olive { get { return namedColors["olive"]; } }
- public static Color OliveDrab { get { return namedColors["olivedrab"]; } }
- public static Color Orange { get { return namedColors["orange"]; } }
- public static Color OrangeRed { get { return namedColors["orangered"]; } }
- public static Color Orchid { get { return namedColors["orchid"]; } }
- public static Color PaleGoldenrod { get { return namedColors["palegoldenrod"]; } }
- public static Color PaleGreen { get { return namedColors["palegreen"]; } }
- public static Color PaleTurquoise { get { return namedColors["paleturquoise"]; } }
- public static Color PaleVioletRed { get { return namedColors["palevioletred"]; } }
- public static Color PapayaWhip { get { return namedColors["papayawhip"]; } }
- public static Color PeachPuff { get { return namedColors["peachpuff"]; } }
- public static Color Peru { get { return namedColors["peru"]; } }
- public static Color Pink { get { return namedColors["pink"]; } }
- public static Color Plum { get { return namedColors["plum"]; } }
- public static Color PowderBlue { get { return namedColors["powderblue"]; } }
- public static Color Purple { get { return namedColors["purple"]; } }
- public static Color RebeccaPurple { get { return namedColors["rebeccapurple"]; } }
- public static Color Red { get { return namedColors["red"]; } }
- public static Color RosyBrown { get { return namedColors["rosybrown"]; } }
- public static Color RoyalBlue { get { return namedColors["royalblue"]; } }
- public static Color SaddleBrown { get { return namedColors["saddlebrown"]; } }
- public static Color Salmon { get { return namedColors["salmon"]; } }
- public static Color SandyBrown { get { return namedColors["sandybrown"]; } }
- public static Color SeaGreen { get { return namedColors["seagreen"]; } }
- public static Color SeaShell { get { return namedColors["seashell"]; } }
- public static Color Sienna { get { return namedColors["sienna"]; } }
- public static Color Silver { get { return namedColors["silver"]; } }
- public static Color SkyBlue { get { return namedColors["skyblue"]; } }
- public static Color SlateBlue { get { return namedColors["slateblue"]; } }
- public static Color SlateGray { get { return namedColors["slategray"]; } }
- public static Color Snow { get { return namedColors["snow"]; } }
- public static Color SpringGreen { get { return namedColors["springgreen"]; } }
- public static Color SteelBlue { get { return namedColors["steelblue"]; } }
- public static Color Tan { get { return namedColors["tan"]; } }
- public static Color Teal { get { return namedColors["teal"]; } }
- public static Color Thistle { get { return namedColors["thistle"]; } }
- public static Color Tomato { get { return namedColors["tomato"]; } }
- public static Color Transparent { get { return namedColors["transparent"]; } }
- public static Color Turquoise { get { return namedColors["turquoise"]; } }
- public static Color Violet { get { return namedColors["violet"]; } }
- public static Color WebGreen { get { return namedColors["webgreen"]; } }
- public static Color WebGray { get { return namedColors["webgray"]; } }
- public static Color WebMaroon { get { return namedColors["webmaroon"]; } }
- public static Color WebPurple { get { return namedColors["webpurple"]; } }
- public static Color Wheat { get { return namedColors["wheat"]; } }
- public static Color White { get { return namedColors["white"]; } }
- public static Color WhiteSmoke { get { return namedColors["whitesmoke"]; } }
- public static Color Yellow { get { return namedColors["yellow"]; } }
- public static Color YellowGreen { get { return namedColors["yellowgreen"]; } }
+ public static Color AliceBlue { get { return namedColors["ALICEBLUE"]; } }
+ public static Color AntiqueWhite { get { return namedColors["ANTIQUEWHITE"]; } }
+ public static Color Aqua { get { return namedColors["AQUA"]; } }
+ public static Color Aquamarine { get { return namedColors["AQUAMARINE"]; } }
+ public static Color Azure { get { return namedColors["AZURE"]; } }
+ public static Color Beige { get { return namedColors["BEIGE"]; } }
+ public static Color Bisque { get { return namedColors["BISQUE"]; } }
+ public static Color Black { get { return namedColors["BLACK"]; } }
+ public static Color BlanchedAlmond { get { return namedColors["BLANCHEDALMOND"]; } }
+ public static Color Blue { get { return namedColors["BLUE"]; } }
+ public static Color BlueViolet { get { return namedColors["BLUEVIOLET"]; } }
+ public static Color Brown { get { return namedColors["BROWN"]; } }
+ public static Color Burlywood { get { return namedColors["BURLYWOOD"]; } }
+ public static Color CadetBlue { get { return namedColors["CADETBLUE"]; } }
+ public static Color Chartreuse { get { return namedColors["CHARTREUSE"]; } }
+ public static Color Chocolate { get { return namedColors["CHOCOLATE"]; } }
+ public static Color Coral { get { return namedColors["CORAL"]; } }
+ public static Color CornflowerBlue { get { return namedColors["CORNFLOWERBLUE"]; } }
+ public static Color Cornsilk { get { return namedColors["CORNSILK"]; } }
+ public static Color Crimson { get { return namedColors["CRIMSON"]; } }
+ public static Color Cyan { get { return namedColors["CYAN"]; } }
+ public static Color DarkBlue { get { return namedColors["DARKBLUE"]; } }
+ public static Color DarkCyan { get { return namedColors["DARKCYAN"]; } }
+ public static Color DarkGoldenrod { get { return namedColors["DARKGOLDENROD"]; } }
+ public static Color DarkGray { get { return namedColors["DARKGRAY"]; } }
+ public static Color DarkGreen { get { return namedColors["DARKGREEN"]; } }
+ public static Color DarkKhaki { get { return namedColors["DARKKHAKI"]; } }
+ public static Color DarkMagenta { get { return namedColors["DARKMAGENTA"]; } }
+ public static Color DarkOliveGreen { get { return namedColors["DARKOLIVEGREEN"]; } }
+ public static Color DarkOrange { get { return namedColors["DARKORANGE"]; } }
+ public static Color DarkOrchid { get { return namedColors["DARKORCHID"]; } }
+ public static Color DarkRed { get { return namedColors["DARKRED"]; } }
+ public static Color DarkSalmon { get { return namedColors["DARKSALMON"]; } }
+ public static Color DarkSeaGreen { get { return namedColors["DARKSEAGREEN"]; } }
+ public static Color DarkSlateBlue { get { return namedColors["DARKSLATEBLUE"]; } }
+ public static Color DarkSlateGray { get { return namedColors["DARKSLATEGRAY"]; } }
+ public static Color DarkTurquoise { get { return namedColors["DARKTURQUOISE"]; } }
+ public static Color DarkViolet { get { return namedColors["DARKVIOLET"]; } }
+ public static Color DeepPink { get { return namedColors["DEEPPINK"]; } }
+ public static Color DeepSkyBlue { get { return namedColors["DEEPSKYBLUE"]; } }
+ public static Color DimGray { get { return namedColors["DIMGRAY"]; } }
+ public static Color DodgerBlue { get { return namedColors["DODGERBLUE"]; } }
+ public static Color Firebrick { get { return namedColors["FIREBRICK"]; } }
+ public static Color FloralWhite { get { return namedColors["FLORALWHITE"]; } }
+ public static Color ForestGreen { get { return namedColors["FORESTGREEN"]; } }
+ public static Color Fuchsia { get { return namedColors["FUCHSIA"]; } }
+ public static Color Gainsboro { get { return namedColors["GAINSBORO"]; } }
+ public static Color GhostWhite { get { return namedColors["GHOSTWHITE"]; } }
+ public static Color Gold { get { return namedColors["GOLD"]; } }
+ public static Color Goldenrod { get { return namedColors["GOLDENROD"]; } }
+ public static Color Gray { get { return namedColors["GRAY"]; } }
+ public static Color Green { get { return namedColors["GREEN"]; } }
+ public static Color GreenYellow { get { return namedColors["GREENYELLOW"]; } }
+ public static Color Honeydew { get { return namedColors["HONEYDEW"]; } }
+ public static Color HotPink { get { return namedColors["HOTPINK"]; } }
+ public static Color IndianRed { get { return namedColors["INDIANRED"]; } }
+ public static Color Indigo { get { return namedColors["INDIGO"]; } }
+ public static Color Ivory { get { return namedColors["IVORY"]; } }
+ public static Color Khaki { get { return namedColors["KHAKI"]; } }
+ public static Color Lavender { get { return namedColors["LAVENDER"]; } }
+ public static Color LavenderBlush { get { return namedColors["LAVENDERBLUSH"]; } }
+ public static Color LawnGreen { get { return namedColors["LAWNGREEN"]; } }
+ public static Color LemonChiffon { get { return namedColors["LEMONCHIFFON"]; } }
+ public static Color LightBlue { get { return namedColors["LIGHTBLUE"]; } }
+ public static Color LightCoral { get { return namedColors["LIGHTCORAL"]; } }
+ public static Color LightCyan { get { return namedColors["LIGHTCYAN"]; } }
+ public static Color LightGoldenrod { get { return namedColors["LIGHTGOLDENROD"]; } }
+ public static Color LightGray { get { return namedColors["LIGHTGRAY"]; } }
+ public static Color LightGreen { get { return namedColors["LIGHTGREEN"]; } }
+ public static Color LightPink { get { return namedColors["LIGHTPINK"]; } }
+ public static Color LightSalmon { get { return namedColors["LIGHTSALMON"]; } }
+ public static Color LightSeaGreen { get { return namedColors["LIGHTSEAGREEN"]; } }
+ public static Color LightSkyBlue { get { return namedColors["LIGHTSKYBLUE"]; } }
+ public static Color LightSlateGray { get { return namedColors["LIGHTSLATEGRAY"]; } }
+ public static Color LightSteelBlue { get { return namedColors["LIGHTSTEELBLUE"]; } }
+ public static Color LightYellow { get { return namedColors["LIGHTYELLOW"]; } }
+ public static Color Lime { get { return namedColors["LIME"]; } }
+ public static Color LimeGreen { get { return namedColors["LIMEGREEN"]; } }
+ public static Color Linen { get { return namedColors["LINEN"]; } }
+ public static Color Magenta { get { return namedColors["MAGENTA"]; } }
+ public static Color Maroon { get { return namedColors["MAROON"]; } }
+ public static Color MediumAquamarine { get { return namedColors["MEDIUMAQUAMARINE"]; } }
+ public static Color MediumBlue { get { return namedColors["MEDIUMBLUE"]; } }
+ public static Color MediumOrchid { get { return namedColors["MEDIUMORCHID"]; } }
+ public static Color MediumPurple { get { return namedColors["MEDIUMPURPLE"]; } }
+ public static Color MediumSeaGreen { get { return namedColors["MEDIUMSEAGREEN"]; } }
+ public static Color MediumSlateBlue { get { return namedColors["MEDIUMSLATEBLUE"]; } }
+ public static Color MediumSpringGreen { get { return namedColors["MEDIUMSPRINGGREEN"]; } }
+ public static Color MediumTurquoise { get { return namedColors["MEDIUMTURQUOISE"]; } }
+ public static Color MediumVioletRed { get { return namedColors["MEDIUMVIOLETRED"]; } }
+ public static Color MidnightBlue { get { return namedColors["MIDNIGHTBLUE"]; } }
+ public static Color MintCream { get { return namedColors["MINTCREAM"]; } }
+ public static Color MistyRose { get { return namedColors["MISTYROSE"]; } }
+ public static Color Moccasin { get { return namedColors["MOCCASIN"]; } }
+ public static Color NavajoWhite { get { return namedColors["NAVAJOWHITE"]; } }
+ public static Color NavyBlue { get { return namedColors["NAVYBLUE"]; } }
+ public static Color OldLace { get { return namedColors["OLDLACE"]; } }
+ public static Color Olive { get { return namedColors["OLIVE"]; } }
+ public static Color OliveDrab { get { return namedColors["OLIVEDRAB"]; } }
+ public static Color Orange { get { return namedColors["ORANGE"]; } }
+ public static Color OrangeRed { get { return namedColors["ORANGERED"]; } }
+ public static Color Orchid { get { return namedColors["ORCHID"]; } }
+ public static Color PaleGoldenrod { get { return namedColors["PALEGOLDENROD"]; } }
+ public static Color PaleGreen { get { return namedColors["PALEGREEN"]; } }
+ public static Color PaleTurquoise { get { return namedColors["PALETURQUOISE"]; } }
+ public static Color PaleVioletRed { get { return namedColors["PALEVIOLETRED"]; } }
+ public static Color PapayaWhip { get { return namedColors["PAPAYAWHIP"]; } }
+ public static Color PeachPuff { get { return namedColors["PEACHPUFF"]; } }
+ public static Color Peru { get { return namedColors["PERU"]; } }
+ public static Color Pink { get { return namedColors["PINK"]; } }
+ public static Color Plum { get { return namedColors["PLUM"]; } }
+ public static Color PowderBlue { get { return namedColors["POWDERBLUE"]; } }
+ public static Color Purple { get { return namedColors["PURPLE"]; } }
+ public static Color RebeccaPurple { get { return namedColors["REBECCAPURPLE"]; } }
+ public static Color Red { get { return namedColors["RED"]; } }
+ public static Color RosyBrown { get { return namedColors["ROSYBROWN"]; } }
+ public static Color RoyalBlue { get { return namedColors["ROYALBLUE"]; } }
+ public static Color SaddleBrown { get { return namedColors["SADDLEBROWN"]; } }
+ public static Color Salmon { get { return namedColors["SALMON"]; } }
+ public static Color SandyBrown { get { return namedColors["SANDYBROWN"]; } }
+ public static Color SeaGreen { get { return namedColors["SEAGREEN"]; } }
+ public static Color Seashell { get { return namedColors["SEASHELL"]; } }
+ public static Color Sienna { get { return namedColors["SIENNA"]; } }
+ public static Color Silver { get { return namedColors["SILVER"]; } }
+ public static Color SkyBlue { get { return namedColors["SKYBLUE"]; } }
+ public static Color SlateBlue { get { return namedColors["SLATEBLUE"]; } }
+ public static Color SlateGray { get { return namedColors["SLATEGRAY"]; } }
+ public static Color Snow { get { return namedColors["SNOW"]; } }
+ public static Color SpringGreen { get { return namedColors["SPRINGGREEN"]; } }
+ public static Color SteelBlue { get { return namedColors["STEELBLUE"]; } }
+ public static Color Tan { get { return namedColors["TAN"]; } }
+ public static Color Teal { get { return namedColors["TEAL"]; } }
+ public static Color Thistle { get { return namedColors["THISTLE"]; } }
+ public static Color Tomato { get { return namedColors["TOMATO"]; } }
+ public static Color Transparent { get { return namedColors["TRANSPARENT"]; } }
+ public static Color Turquoise { get { return namedColors["TURQUOISE"]; } }
+ public static Color Violet { get { return namedColors["VIOLET"]; } }
+ public static Color WebGray { get { return namedColors["WEBGRAY"]; } }
+ public static Color WebGreen { get { return namedColors["WEBGREEN"]; } }
+ public static Color WebMaroon { get { return namedColors["WEBMAROON"]; } }
+ public static Color WebPurple { get { return namedColors["WEBPURPLE"]; } }
+ public static Color Wheat { get { return namedColors["WHEAT"]; } }
+ public static Color White { get { return namedColors["WHITE"]; } }
+ public static Color WhiteSmoke { get { return namedColors["WHITESMOKE"]; } }
+ public static Color Yellow { get { return namedColors["YELLOW"]; } }
+ public static Color YellowGreen { get { return namedColors["YELLOWGREEN"]; } }
}
}
diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub
index 68e9df5263..57120bff26 100644
--- a/modules/raycast/SCsub
+++ b/modules/raycast/SCsub
@@ -3,84 +3,101 @@
Import("env")
Import("env_modules")
-embree_src = [
- "common/sys/sysinfo.cpp",
- "common/sys/alloc.cpp",
- "common/sys/filename.cpp",
- "common/sys/library.cpp",
- "common/sys/thread.cpp",
- "common/sys/string.cpp",
- "common/sys/regression.cpp",
- "common/sys/mutex.cpp",
- "common/sys/condition.cpp",
- "common/sys/barrier.cpp",
- "common/math/constants.cpp",
- "common/simd/sse.cpp",
- "common/lexers/stringstream.cpp",
- "common/lexers/tokenstream.cpp",
- "common/tasking/taskschedulerinternal.cpp",
- "common/algorithms/parallel_for.cpp",
- "common/algorithms/parallel_reduce.cpp",
- "common/algorithms/parallel_prefix_sum.cpp",
- "common/algorithms/parallel_for_for.cpp",
- "common/algorithms/parallel_for_for_prefix_sum.cpp",
- "common/algorithms/parallel_partition.cpp",
- "common/algorithms/parallel_sort.cpp",
- "common/algorithms/parallel_set.cpp",
- "common/algorithms/parallel_map.cpp",
- "common/algorithms/parallel_filter.cpp",
- "kernels/common/device.cpp",
- "kernels/common/stat.cpp",
- "kernels/common/acceln.cpp",
- "kernels/common/accelset.cpp",
- "kernels/common/state.cpp",
- "kernels/common/rtcore.cpp",
- "kernels/common/rtcore_builder.cpp",
- "kernels/common/scene.cpp",
- "kernels/common/alloc.cpp",
- "kernels/common/geometry.cpp",
- "kernels/common/scene_triangle_mesh.cpp",
- "kernels/geometry/primitive4.cpp",
- "kernels/builders/primrefgen.cpp",
- "kernels/bvh/bvh.cpp",
- "kernels/bvh/bvh_statistics.cpp",
- "kernels/bvh/bvh4_factory.cpp",
- "kernels/bvh/bvh8_factory.cpp",
- "kernels/bvh/bvh_collider.cpp",
- "kernels/bvh/bvh_rotate.cpp",
- "kernels/bvh/bvh_refit.cpp",
- "kernels/bvh/bvh_builder.cpp",
- "kernels/bvh/bvh_builder_morton.cpp",
- "kernels/bvh/bvh_builder_sah.cpp",
- "kernels/bvh/bvh_builder_sah_spatial.cpp",
- "kernels/bvh/bvh_builder_sah_mb.cpp",
- "kernels/bvh/bvh_builder_twolevel.cpp",
- "kernels/bvh/bvh_intersector1_bvh4.cpp",
-]
-
-embree_dir = "#thirdparty/embree-aarch64/"
-
-env_embree = env_modules.Clone()
-embree_sources = [embree_dir + file for file in embree_src]
-env_embree.Prepend(CPPPATH=[embree_dir, embree_dir + "include"])
-env_embree.Append(CPPFLAGS=["-DEMBREE_TARGET_SSE2", "-DEMBREE_LOWEST_ISA", "-DTASKING_INTERNAL", "-DNDEBUG"])
-
-if not env_embree.msvc:
- env_embree.Append(CPPFLAGS=["-msse2", "-mxsave"])
+env_raycast = env_modules.Clone()
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+if env["builtin_embree"]:
+ thirdparty_dir = "#thirdparty/embree-aarch64/"
+
+ embree_src = [
+ "common/sys/sysinfo.cpp",
+ "common/sys/alloc.cpp",
+ "common/sys/filename.cpp",
+ "common/sys/library.cpp",
+ "common/sys/thread.cpp",
+ "common/sys/string.cpp",
+ "common/sys/regression.cpp",
+ "common/sys/mutex.cpp",
+ "common/sys/condition.cpp",
+ "common/sys/barrier.cpp",
+ "common/math/constants.cpp",
+ "common/simd/sse.cpp",
+ "common/lexers/stringstream.cpp",
+ "common/lexers/tokenstream.cpp",
+ "common/tasking/taskschedulerinternal.cpp",
+ "common/algorithms/parallel_for.cpp",
+ "common/algorithms/parallel_reduce.cpp",
+ "common/algorithms/parallel_prefix_sum.cpp",
+ "common/algorithms/parallel_for_for.cpp",
+ "common/algorithms/parallel_for_for_prefix_sum.cpp",
+ "common/algorithms/parallel_partition.cpp",
+ "common/algorithms/parallel_sort.cpp",
+ "common/algorithms/parallel_set.cpp",
+ "common/algorithms/parallel_map.cpp",
+ "common/algorithms/parallel_filter.cpp",
+ "kernels/common/device.cpp",
+ "kernels/common/stat.cpp",
+ "kernels/common/acceln.cpp",
+ "kernels/common/accelset.cpp",
+ "kernels/common/state.cpp",
+ "kernels/common/rtcore.cpp",
+ "kernels/common/rtcore_builder.cpp",
+ "kernels/common/scene.cpp",
+ "kernels/common/alloc.cpp",
+ "kernels/common/geometry.cpp",
+ "kernels/common/scene_triangle_mesh.cpp",
+ "kernels/geometry/primitive4.cpp",
+ "kernels/builders/primrefgen.cpp",
+ "kernels/bvh/bvh.cpp",
+ "kernels/bvh/bvh_statistics.cpp",
+ "kernels/bvh/bvh4_factory.cpp",
+ "kernels/bvh/bvh8_factory.cpp",
+ "kernels/bvh/bvh_collider.cpp",
+ "kernels/bvh/bvh_rotate.cpp",
+ "kernels/bvh/bvh_refit.cpp",
+ "kernels/bvh/bvh_builder.cpp",
+ "kernels/bvh/bvh_builder_morton.cpp",
+ "kernels/bvh/bvh_builder_sah.cpp",
+ "kernels/bvh/bvh_builder_sah_spatial.cpp",
+ "kernels/bvh/bvh_builder_sah_mb.cpp",
+ "kernels/bvh/bvh_builder_twolevel.cpp",
+ "kernels/bvh/bvh_intersector1_bvh4.cpp",
+ ]
+
+ thirdparty_sources = [thirdparty_dir + file for file in embree_src]
+
+ env_raycast.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "include"])
+ env_raycast.Append(CPPDEFINES=["EMBREE_TARGET_SSE2", "EMBREE_LOWEST_ISA", "TASKING_INTERNAL", "NDEBUG"])
+
+ if not env.msvc:
+ if env["arch"] in ["x86", "x86_64"]:
+ env_raycast.Append(CPPFLAGS=["-msse2", "-mxsave"])
+
+ if env["platform"] == "windows":
+ env_raycast.Append(CPPFLAGS=["-mstackrealign"])
+
if env["platform"] == "windows":
- env_embree.Append(CPPFLAGS=["-mstackrealign"])
+ if env.msvc:
+ env.Append(LINKFLAGS=["psapi.lib"])
+ env_raycast.Append(CPPDEFINES=["__SSE2__", "__SSE__"])
+ else:
+ env.Append(LIBS=["psapi"])
-if env["platform"] == "windows":
- if env.msvc:
- env.Append(LINKFLAGS=["psapi.lib"])
- env_embree.Append(CPPFLAGS=["-D__SSE2__", "-D__SSE__"])
- else:
- env.Append(LIBS=["psapi"])
+ env_thirdparty = env_raycast.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
-env_embree.disable_warnings()
-env_embree.add_source_files(env.modules_sources, embree_sources)
-env_raycast = env_modules.Clone()
-env_raycast.Prepend(CPPPATH=[embree_dir, embree_dir + "include", embree_dir + "common"])
+# Godot source files
+
+module_obj = []
+
+env_raycast.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-env_raycast.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/raycast/lightmap_raycaster.cpp b/modules/raycast/lightmap_raycaster.cpp
index 9039622d3d..56bdb5900b 100644
--- a/modules/raycast/lightmap_raycaster.cpp
+++ b/modules/raycast/lightmap_raycaster.cpp
@@ -32,14 +32,8 @@
#include "lightmap_raycaster.h"
-// From Embree.
-#include <math/vec2.h>
-#include <math/vec3.h>
-
#include <pmmintrin.h>
-using namespace embree;
-
LightmapRaycaster *LightmapRaycasterEmbree::create_embree_raycaster() {
return memnew(LightmapRaycasterEmbree);
}
@@ -127,25 +121,24 @@ void LightmapRaycasterEmbree::add_mesh(const Vector<Vector3> &p_vertices, const
ERR_FAIL_COND(vertex_count % 3 != 0);
ERR_FAIL_COND(vertex_count != p_uv2s.size());
+ ERR_FAIL_COND(!p_normals.is_empty() && vertex_count != p_normals.size());
- Vec3fa *embree_vertices = (Vec3fa *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, sizeof(Vec3fa), vertex_count);
- Vec2fa *embree_light_uvs = (Vec2fa *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE, 0, RTC_FORMAT_FLOAT2, sizeof(Vec2fa), vertex_count);
- uint32_t *embree_triangles = (uint32_t *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(uint32_t) * 3, vertex_count / 3);
+ Vector3 *embree_vertices = (Vector3 *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, sizeof(Vector3), vertex_count);
+ memcpy(embree_vertices, p_vertices.ptr(), sizeof(Vector3) * vertex_count);
- Vec3fa *embree_normals = nullptr;
- if (!p_normals.is_empty()) {
- embree_normals = (Vec3fa *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE, 1, RTC_FORMAT_FLOAT3, sizeof(Vec3fa), vertex_count);
- }
+ Vector2 *embree_light_uvs = (Vector2 *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE, 0, RTC_FORMAT_FLOAT2, sizeof(Vector2), vertex_count);
+ memcpy(embree_light_uvs, p_uv2s.ptr(), sizeof(Vector2) * vertex_count);
+ uint32_t *embree_triangles = (uint32_t *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(uint32_t) * 3, vertex_count / 3);
for (int i = 0; i < vertex_count; i++) {
- embree_vertices[i] = Vec3fa(p_vertices[i].x, p_vertices[i].y, p_vertices[i].z);
- embree_light_uvs[i] = Vec2fa(p_uv2s[i].x, p_uv2s[i].y);
- if (embree_normals != nullptr) {
- embree_normals[i] = Vec3fa(p_normals[i].x, p_normals[i].y, p_normals[i].z);
- }
embree_triangles[i] = i;
}
+ if (!p_normals.is_empty()) {
+ Vector3 *embree_normals = (Vector3 *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE, 1, RTC_FORMAT_FLOAT3, sizeof(Vector3), vertex_count);
+ memcpy(embree_normals, p_normals.ptr(), sizeof(Vector3) * vertex_count);
+ }
+
rtcCommitGeometry(embree_mesh);
rtcSetGeometryIntersectFilterFunction(embree_mesh, filter_function);
rtcSetGeometryUserData(embree_mesh, this);
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 765a5fe023..b863f622bd 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -2336,6 +2336,10 @@ void VisualScriptLanguage::finish() {
void VisualScriptLanguage::get_reserved_words(List<String> *p_words) const {
}
+bool VisualScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+ return false;
+}
+
void VisualScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
}
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index 72362e0ef4..cc78b3242d 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -586,6 +586,7 @@ public:
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const;
+ virtual bool is_control_flow_keyword(String p_keyword) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 527eb0abc9..dc400982e8 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -740,21 +740,8 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
Color c = sbf->get_border_color();
Color ic = c;
- c.a = 1;
- if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) {
- Color mono_color;
- if (((c.r + c.g + c.b) / 3) < 0.7) {
- mono_color = Color(1.0, 1.0, 1.0);
- ic = Color(0.0, 0.0, 0.0, 0.7);
- } else {
- mono_color = Color(0.0, 0.0, 0.0);
- ic = Color(1.0, 1.0, 1.0, 0.7);
- }
- mono_color.a = 0.85;
- c = mono_color;
- }
gnode->add_theme_color_override("title_color", c);
- c.a = 0.7;
+ c.a = 1;
gnode->add_theme_color_override("close_color", c);
gnode->add_theme_color_override("resizer_color", ic);
gnode->add_theme_style_override("frame", sbf);
@@ -3623,32 +3610,33 @@ void VisualScriptEditor::_notification(int p_what) {
bool dark_theme = tm->get_constant("dark_theme", "Editor");
- List<Pair<String, Color>> colors;
if (dark_theme) {
- colors.push_back(Pair<String, Color>("flow_control", Color(0.96, 0.96, 0.96)));
- colors.push_back(Pair<String, Color>("functions", Color(0.96, 0.52, 0.51)));
- colors.push_back(Pair<String, Color>("data", Color(0.5, 0.96, 0.81)));
- colors.push_back(Pair<String, Color>("operators", Color(0.67, 0.59, 0.87)));
- colors.push_back(Pair<String, Color>("custom", Color(0.5, 0.73, 0.96)));
- colors.push_back(Pair<String, Color>("constants", Color(0.96, 0.5, 0.69)));
+ node_colors["flow_control"] = Color(0.96, 0.96, 0.96);
+ node_colors["functions"] = Color(0.96, 0.52, 0.51);
+ node_colors["data"] = Color(0.5, 0.96, 0.81);
+ node_colors["operators"] = Color(0.67, 0.59, 0.87);
+ node_colors["custom"] = Color(0.5, 0.73, 0.96);
+ node_colors["constants"] = Color(0.96, 0.5, 0.69);
} else {
- colors.push_back(Pair<String, Color>("flow_control", Color(0.26, 0.26, 0.26)));
- colors.push_back(Pair<String, Color>("functions", Color(0.95, 0.4, 0.38)));
- colors.push_back(Pair<String, Color>("data", Color(0.07, 0.73, 0.51)));
- colors.push_back(Pair<String, Color>("operators", Color(0.51, 0.4, 0.82)));
- colors.push_back(Pair<String, Color>("custom", Color(0.31, 0.63, 0.95)));
- colors.push_back(Pair<String, Color>("constants", Color(0.94, 0.18, 0.49)));
+ node_colors["flow_control"] = Color(0.26, 0.26, 0.26);
+ node_colors["functions"] = Color(0.95, 0.4, 0.38);
+ node_colors["data"] = Color(0.07, 0.73, 0.51);
+ node_colors["operators"] = Color(0.51, 0.4, 0.82);
+ node_colors["custom"] = Color(0.31, 0.63, 0.95);
+ node_colors["constants"] = Color(0.94, 0.18, 0.49);
}
- for (List<Pair<String, Color>>::Element *E = colors.front(); E; E = E->next()) {
- Ref<StyleBoxFlat> sb = tm->get_stylebox("frame", "GraphNode");
+ for (Map<StringName, Color>::Element *E = node_colors.front(); E; E = E->next()) {
+ const Ref<StyleBoxFlat> sb = tm->get_stylebox("frame", "GraphNode");
+
if (!sb.is_null()) {
Ref<StyleBoxFlat> frame_style = sb->duplicate();
- Color c = sb->get_border_color();
- Color cn = E->get().second;
- cn.a = c.a;
- frame_style->set_border_color(cn);
- node_styles[E->get().first] = frame_style;
+ // Adjust the border color to be close to the GraphNode's background color.
+ // This keeps the node's title area from being too distracting.
+ Color color = dark_theme ? E->get().darkened(0.75) : E->get().lightened(0.75);
+ color.a = 0.9;
+ frame_style->set_border_color(color);
+ node_styles[E->key()] = frame_style;
}
}
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index bb6f194286..fc9a2df60f 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -135,6 +135,7 @@ class VisualScriptEditor : public ScriptEditorBase {
Vector<Pair<Variant::Type, String>> args;
};
+ Map<StringName, Color> node_colors;
HashMap<StringName, Ref<StyleBox>> node_styles;
void _update_graph_connections();
diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp
index 25b6d6ef0e..626498e1ae 100644
--- a/modules/websocket/emws_client.cpp
+++ b/modules/websocket/emws_client.cpp
@@ -121,8 +121,8 @@ void EMWSClient::disconnect_from_host(int p_code, String p_reason) {
_peer->close(p_code, p_reason);
}
-IP_Address EMWSClient::get_connected_host() const {
- ERR_FAIL_V_MSG(IP_Address(), "Not supported in HTML5 export.");
+IPAddress EMWSClient::get_connected_host() const {
+ ERR_FAIL_V_MSG(IPAddress(), "Not supported in HTML5 export.");
}
uint16_t EMWSClient::get_connected_port() const {
diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h
index 2ab7dc83d0..ca2d7ed986 100644
--- a/modules/websocket/emws_client.h
+++ b/modules/websocket/emws_client.h
@@ -56,7 +56,7 @@ public:
Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>());
Ref<WebSocketPeer> get_peer(int p_peer_id) const;
void disconnect_from_host(int p_code = 1000, String p_reason = "");
- IP_Address get_connected_host() const;
+ IPAddress get_connected_host() const;
uint16_t get_connected_port() const;
virtual ConnectionStatus get_connection_status() const;
int get_max_packet_size() const;
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index 5e75e10d68..1ad3bdc825 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -93,8 +93,8 @@ void EMWSPeer::close(int p_code, String p_reason) {
peer_sock = -1;
};
-IP_Address EMWSPeer::get_connected_host() const {
- ERR_FAIL_V_MSG(IP_Address(), "Not supported in HTML5 export.");
+IPAddress EMWSPeer::get_connected_host() const {
+ ERR_FAIL_V_MSG(IPAddress(), "Not supported in HTML5 export.");
};
uint16_t EMWSPeer::get_connected_port() const {
diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h
index abe5bf2bdb..73e701720b 100644
--- a/modules/websocket/emws_peer.h
+++ b/modules/websocket/emws_peer.h
@@ -73,7 +73,7 @@ public:
virtual void close(int p_code = 1000, String p_reason = "");
virtual bool is_connected_to_host() const;
- virtual IP_Address get_connected_host() const;
+ virtual IPAddress get_connected_host() const;
virtual uint16_t get_connected_port() const;
virtual WriteMode get_write_mode() const;
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
index a35d84f372..4a4f09a943 100644
--- a/modules/websocket/emws_server.cpp
+++ b/modules/websocket/emws_server.cpp
@@ -58,8 +58,8 @@ Vector<String> EMWSServer::get_protocols() const {
return out;
}
-IP_Address EMWSServer::get_peer_address(int p_peer_id) const {
- return IP_Address();
+IPAddress EMWSServer::get_peer_address(int p_peer_id) const {
+ return IPAddress();
}
int EMWSServer::get_peer_port(int p_peer_id) const {
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
index 4179b20ffe..e7285ccb86 100644
--- a/modules/websocket/emws_server.h
+++ b/modules/websocket/emws_server.h
@@ -47,7 +47,7 @@ public:
bool is_listening() const;
bool has_peer(int p_id) const;
Ref<WebSocketPeer> get_peer(int p_id) const;
- IP_Address get_peer_address(int p_peer_id) const;
+ IPAddress get_peer_address(int p_peer_id) const;
int get_peer_port(int p_peer_id) const;
void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "");
int get_max_packet_size() const;
diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h
index 0225c9b3d3..c7f17f1ffb 100644
--- a/modules/websocket/websocket_client.h
+++ b/modules/websocket/websocket_client.h
@@ -57,7 +57,7 @@ public:
virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) = 0;
virtual void disconnect_from_host(int p_code = 1000, String p_reason = "") = 0;
- virtual IP_Address get_connected_host() const = 0;
+ virtual IPAddress get_connected_host() const = 0;
virtual uint16_t get_connected_port() const = 0;
virtual bool is_server() const override;
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
index 2ba83637f9..e9bb20f21f 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -55,7 +55,7 @@ public:
virtual void close(int p_code = 1000, String p_reason = "") = 0;
virtual bool is_connected_to_host() const = 0;
- virtual IP_Address get_connected_host() const = 0;
+ virtual IPAddress get_connected_host() const = 0;
virtual uint16_t get_connected_port() const = 0;
virtual bool was_string_packet() const = 0;
virtual void set_no_delay(bool p_enabled) = 0;
diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp
index f57e8d959c..7cf68b835c 100644
--- a/modules/websocket/websocket_server.cpp
+++ b/modules/websocket/websocket_server.cpp
@@ -34,7 +34,7 @@ GDCINULL(WebSocketServer);
WebSocketServer::WebSocketServer() {
_peer_id = 1;
- bind_ip = IP_Address("*");
+ bind_ip = IPAddress("*");
}
WebSocketServer::~WebSocketServer() {
@@ -71,11 +71,11 @@ void WebSocketServer::_bind_methods() {
ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id")));
}
-IP_Address WebSocketServer::get_bind_ip() const {
+IPAddress WebSocketServer::get_bind_ip() const {
return bind_ip;
}
-void WebSocketServer::set_bind_ip(const IP_Address &p_bind_ip) {
+void WebSocketServer::set_bind_ip(const IPAddress &p_bind_ip) {
ERR_FAIL_COND(is_listening());
ERR_FAIL_COND(!p_bind_ip.is_valid() && !p_bind_ip.is_wildcard());
bind_ip = p_bind_ip;
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
index 3fbd5e3b95..10da51fce5 100644
--- a/modules/websocket/websocket_server.h
+++ b/modules/websocket/websocket_server.h
@@ -40,7 +40,7 @@ class WebSocketServer : public WebSocketMultiplayerPeer {
GDCLASS(WebSocketServer, WebSocketMultiplayerPeer);
GDCICLASS(WebSocketServer);
- IP_Address bind_ip;
+ IPAddress bind_ip;
protected:
static void _bind_methods();
@@ -57,7 +57,7 @@ public:
virtual bool is_server() const override;
ConnectionStatus get_connection_status() const override;
- virtual IP_Address get_peer_address(int p_peer_id) const = 0;
+ virtual IPAddress get_peer_address(int p_peer_id) const = 0;
virtual int get_peer_port(int p_peer_id) const = 0;
virtual void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "") = 0;
@@ -66,8 +66,8 @@ public:
void _on_disconnect(int32_t p_peer_id, bool p_was_clean);
void _on_close_request(int32_t p_peer_id, int p_code, String p_reason);
- IP_Address get_bind_ip() const;
- void set_bind_ip(const IP_Address &p_bind_ip);
+ IPAddress get_bind_ip() const;
+ void set_bind_ip(const IPAddress &p_bind_ip);
Ref<CryptoKey> get_private_key() const;
void set_private_key(Ref<CryptoKey> p_key);
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
index a075ae3982..111d2178d6 100644
--- a/modules/websocket/wsl_client.cpp
+++ b/modules/websocket/wsl_client.cpp
@@ -160,7 +160,7 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE);
_peer = Ref<WSLPeer>(memnew(WSLPeer));
- IP_Address addr;
+ IPAddress addr;
if (!p_host.is_valid_ip_address()) {
addr = IP::get_singleton()->resolve_hostname(p_host);
@@ -316,8 +316,8 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
_resp_pos = 0;
}
-IP_Address WSLClient::get_connected_host() const {
- ERR_FAIL_COND_V(!_peer->is_connected_to_host(), IP_Address());
+IPAddress WSLClient::get_connected_host() const {
+ ERR_FAIL_COND_V(!_peer->is_connected_to_host(), IPAddress());
return _peer->get_connected_host();
}
diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h
index e7c91ed333..849639ee8b 100644
--- a/modules/websocket/wsl_client.h
+++ b/modules/websocket/wsl_client.h
@@ -75,7 +75,7 @@ public:
int get_max_packet_size() const;
Ref<WebSocketPeer> get_peer(int p_peer_id) const;
void disconnect_from_host(int p_code = 1000, String p_reason = "");
- IP_Address get_connected_host() const;
+ IPAddress get_connected_host() const;
uint16_t get_connected_port() const;
virtual ConnectionStatus get_connection_status() const;
virtual void poll();
diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp
index dbbf86d0da..1dbadfed74 100644
--- a/modules/websocket/wsl_peer.cpp
+++ b/modules/websocket/wsl_peer.cpp
@@ -305,8 +305,8 @@ void WSLPeer::close(int p_code, String p_reason) {
_packet_buffer.resize(0);
}
-IP_Address WSLPeer::get_connected_host() const {
- ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), IP_Address());
+IPAddress WSLPeer::get_connected_host() const {
+ ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), IPAddress());
return _data->tcp->get_connected_host();
}
diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h
index 5e6a7e8554..f1ea98d384 100644
--- a/modules/websocket/wsl_peer.h
+++ b/modules/websocket/wsl_peer.h
@@ -90,7 +90,7 @@ public:
virtual void close_now();
virtual void close(int p_code = 1000, String p_reason = "");
virtual bool is_connected_to_host() const;
- virtual IP_Address get_connected_host() const;
+ virtual IPAddress get_connected_host() const;
virtual uint16_t get_connected_port() const;
virtual WriteMode get_write_mode() const;
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
index 437eb2061b..dc5b23c31e 100644
--- a/modules/websocket/wsl_server.cpp
+++ b/modules/websocket/wsl_server.cpp
@@ -272,8 +272,8 @@ Ref<WebSocketPeer> WSLServer::get_peer(int p_id) const {
return _peer_map[p_id];
}
-IP_Address WSLServer::get_peer_address(int p_peer_id) const {
- ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address());
+IPAddress WSLServer::get_peer_address(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), IPAddress());
return _peer_map[p_peer_id]->get_connected_host();
}
diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h
index 75669e12ee..c2cf9df58b 100644
--- a/modules/websocket/wsl_server.h
+++ b/modules/websocket/wsl_server.h
@@ -73,7 +73,7 @@ private:
int _out_pkt_size = DEF_PKT_SHIFT;
List<Ref<PendingPeer>> _pending;
- Ref<TCP_Server> _server;
+ Ref<TCPServer> _server;
Vector<String> _protocols;
public:
@@ -84,7 +84,7 @@ public:
int get_max_packet_size() const;
bool has_peer(int p_id) const;
Ref<WebSocketPeer> get_peer(int p_id) const;
- IP_Address get_peer_address(int p_peer_id) const;
+ IPAddress get_peer_address(int p_peer_id) const;
int get_peer_port(int p_peer_id) const;
void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "");
virtual void poll();
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 51cea39a43..7a32184139 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -61,7 +61,6 @@ bool DisplayServerAndroid::has_feature(Feature p_feature) const {
//case FEATURE_MOUSE_WARP:
//case FEATURE_NATIVE_DIALOG:
//case FEATURE_NATIVE_ICON:
- //case FEATURE_NATIVE_VIDEO:
//case FEATURE_WINDOW_TRANSPARENCY:
case FEATURE_CLIPBOARD:
case FEATURE_KEEP_SCREEN_ON:
diff --git a/platform/android/net_socket_android.cpp b/platform/android/net_socket_android.cpp
index ddc2368793..dbd1870ee6 100644
--- a/platform/android/net_socket_android.cpp
+++ b/platform/android/net_socket_android.cpp
@@ -103,7 +103,7 @@ Error NetSocketAndroid::set_broadcasting_enabled(bool p_enabled) {
return OK;
}
-Error NetSocketAndroid::join_multicast_group(const IP_Address &p_multi_address, String p_if_name) {
+Error NetSocketAndroid::join_multicast_group(const IPAddress &p_multi_address, String p_if_name) {
Error err = NetSocketPosix::join_multicast_group(p_multi_address, p_if_name);
if (err != OK)
return err;
@@ -115,7 +115,7 @@ Error NetSocketAndroid::join_multicast_group(const IP_Address &p_multi_address,
return OK;
}
-Error NetSocketAndroid::leave_multicast_group(const IP_Address &p_multi_address, String p_if_name) {
+Error NetSocketAndroid::leave_multicast_group(const IPAddress &p_multi_address, String p_if_name) {
Error err = NetSocketPosix::leave_multicast_group(p_multi_address, p_if_name);
if (err != OK)
return err;
diff --git a/platform/android/net_socket_android.h b/platform/android/net_socket_android.h
index cc2a68ac49..60090c26bb 100644
--- a/platform/android/net_socket_android.h
+++ b/platform/android/net_socket_android.h
@@ -67,8 +67,8 @@ public:
virtual void close();
virtual Error set_broadcasting_enabled(bool p_enabled);
- virtual Error join_multicast_group(const IP_Address &p_multi_address, String p_if_name);
- virtual Error leave_multicast_group(const IP_Address &p_multi_address, String p_if_name);
+ virtual Error join_multicast_group(const IPAddress &p_multi_address, String p_if_name);
+ virtual Error leave_multicast_group(const IPAddress &p_multi_address, String p_if_name);
NetSocketAndroid() {}
~NetSocketAndroid();
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index ee7b2f4ab5..58b574a72f 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -19,7 +19,6 @@ iphone_lib = [
"godot_view_gesture_recognizer.mm",
"device_metrics.m",
"keyboard_input_view.mm",
- "native_video_view.m",
]
env_ios = env.Clone()
diff --git a/platform/iphone/display_server_iphone.h b/platform/iphone/display_server_iphone.h
index 7bf1557873..34c56382a4 100644
--- a/platform/iphone/display_server_iphone.h
+++ b/platform/iphone/display_server_iphone.h
@@ -190,12 +190,6 @@ public:
virtual void screen_set_keep_on(bool p_enable) override;
virtual bool screen_is_kept_on() const override;
- virtual Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track, int p_screen = SCREEN_OF_MAIN_WINDOW) override;
- virtual bool native_video_is_playing() const override;
- virtual void native_video_pause() override;
- virtual void native_video_unpause() override;
- virtual void native_video_stop() override;
-
void resize_window(CGSize size);
};
diff --git a/platform/iphone/display_server_iphone.mm b/platform/iphone/display_server_iphone.mm
index a852bea207..9e74de0842 100644
--- a/platform/iphone/display_server_iphone.mm
+++ b/platform/iphone/display_server_iphone.mm
@@ -36,7 +36,6 @@
#import "godot_view.h"
#include "ios.h"
#import "keyboard_input_view.h"
-#import "native_video_view.h"
#include "os_iphone.h"
#import "view_controller.h"
@@ -305,7 +304,6 @@ bool DisplayServerIPhone::has_feature(Feature p_feature) const {
// case FEATURE_MOUSE_WARP:
// case FEATURE_NATIVE_DIALOG:
// case FEATURE_NATIVE_ICON:
- // case FEATURE_NATIVE_VIDEO:
// case FEATURE_WINDOW_TRANSPARENCY:
case FEATURE_CLIPBOARD:
case FEATURE_KEEP_SCREEN_ON:
@@ -569,69 +567,6 @@ bool DisplayServerIPhone::screen_is_kept_on() const {
return [UIApplication sharedApplication].idleTimerDisabled;
}
-Error DisplayServerIPhone::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track, int p_screen) {
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- bool exists = f && f->is_open();
-
- String user_data_dir = OSIPhone::get_singleton()->get_user_data_dir();
-
- if (!exists) {
- return FAILED;
- }
-
- String tempFile = OSIPhone::get_singleton()->get_user_data_dir();
-
- if (p_path.begins_with("res://")) {
- if (PackedData::get_singleton()->has_path(p_path)) {
- printf("Unable to play %s using the native player as it resides in a .pck file\n", p_path.utf8().get_data());
- return ERR_INVALID_PARAMETER;
- } else {
- p_path = p_path.replace("res:/", ProjectSettings::get_singleton()->get_resource_path());
- }
- } else if (p_path.begins_with("user://")) {
- p_path = p_path.replace("user:/", user_data_dir);
- }
-
- memdelete(f);
-
- printf("Playing video: %s\n", p_path.utf8().get_data());
-
- String file_path = ProjectSettings::get_singleton()->globalize_path(p_path);
-
- NSString *filePath = [[NSString alloc] initWithUTF8String:file_path.utf8().get_data()];
- NSString *audioTrack = [NSString stringWithUTF8String:p_audio_track.utf8()];
- NSString *subtitleTrack = [NSString stringWithUTF8String:p_subtitle_track.utf8()];
-
- if (![AppDelegate.viewController playVideoAtPath:filePath
- volume:p_volume
- audio:audioTrack
- subtitle:subtitleTrack]) {
- return OK;
- }
-
- return FAILED;
-}
-
-bool DisplayServerIPhone::native_video_is_playing() const {
- return [AppDelegate.viewController.videoView isVideoPlaying];
-}
-
-void DisplayServerIPhone::native_video_pause() {
- if (native_video_is_playing()) {
- [AppDelegate.viewController.videoView pauseVideo];
- }
-}
-
-void DisplayServerIPhone::native_video_unpause() {
- [AppDelegate.viewController.videoView unpauseVideo];
-};
-
-void DisplayServerIPhone::native_video_stop() {
- if (native_video_is_playing()) {
- [AppDelegate.viewController.videoView stopVideo];
- }
-}
-
void DisplayServerIPhone::resize_window(CGSize viewSize) {
Size2i size = Size2i(viewSize.width, viewSize.height) * screen_get_max_scale();
diff --git a/platform/iphone/native_video_view.m b/platform/iphone/native_video_view.m
deleted file mode 100644
index f126749600..0000000000
--- a/platform/iphone/native_video_view.m
+++ /dev/null
@@ -1,266 +0,0 @@
-/*************************************************************************/
-/* native_video_view.m */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#import "native_video_view.h"
-#import <AVFoundation/AVFoundation.h>
-
-@interface GodotNativeVideoView ()
-
-@property(strong, nonatomic) AVAsset *avAsset;
-@property(strong, nonatomic) AVPlayerItem *avPlayerItem;
-@property(strong, nonatomic) AVPlayer *avPlayer;
-@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer;
-@property(assign, nonatomic) CMTime videoCurrentTime;
-@property(assign, nonatomic) BOOL isVideoCurrentlyPlaying;
-
-@end
-
-@implementation GodotNativeVideoView
-
-- (instancetype)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
-
- if (self) {
- [self godot_commonInit];
- }
-
- return self;
-}
-
-- (instancetype)initWithCoder:(NSCoder *)coder {
- self = [super initWithCoder:coder];
-
- if (self) {
- [self godot_commonInit];
- }
-
- return self;
-}
-
-- (void)godot_commonInit {
- self.isVideoCurrentlyPlaying = NO;
- self.videoCurrentTime = kCMTimeZero;
-
- [self observeVideoAudio];
-}
-
-- (void)layoutSubviews {
- [super layoutSubviews];
-
- self.avPlayerLayer.frame = self.bounds;
-}
-
-- (void)observeVideoAudio {
- printf("******** adding observer for sound routing changes\n");
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(audioRouteChangeListenerCallback:)
- name:AVAudioSessionRouteChangeNotification
- object:nil];
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
- if (object == self.avPlayerItem && [keyPath isEqualToString:@"status"]) {
- [self handleVideoOrPlayerStatus];
- }
-
- if (object == self.avPlayer && [keyPath isEqualToString:@"rate"]) {
- [self handleVideoPlayRate];
- }
-}
-
-// MARK: Video Audio
-
-- (void)audioRouteChangeListenerCallback:(NSNotification *)notification {
- printf("*********** route changed!\n");
- NSDictionary *interuptionDict = notification.userInfo;
-
- NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
-
- switch (routeChangeReason) {
- case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
- NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
- NSLog(@"Headphone/Line plugged in");
- } break;
- case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
- NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
- NSLog(@"Headphone/Line was pulled. Resuming video play....");
- if ([self isVideoPlaying]) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
- [self.avPlayer play]; // NOTE: change this line according your current player implementation
- NSLog(@"resumed play");
- });
- }
- } break;
- case AVAudioSessionRouteChangeReasonCategoryChange: {
- // called at start - also when other audio wants to play
- NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
- } break;
- }
-}
-
-// MARK: Native Video Player
-
-- (void)handleVideoOrPlayerStatus {
- if (self.avPlayerItem.status == AVPlayerItemStatusFailed || self.avPlayer.status == AVPlayerStatusFailed) {
- [self stopVideo];
- }
-
- if (self.avPlayer.status == AVPlayerStatusReadyToPlay && self.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && CMTimeCompare(self.videoCurrentTime, kCMTimeZero) == 0) {
- // NSLog(@"time: %@", self.video_current_time);
- [self.avPlayer seekToTime:self.videoCurrentTime];
- self.videoCurrentTime = kCMTimeZero;
- }
-}
-
-- (void)handleVideoPlayRate {
- NSLog(@"Player playback rate changed: %.5f", self.avPlayer.rate);
- if ([self isVideoPlaying] && self.avPlayer.rate == 0.0 && !self.avPlayer.error) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
- [self.avPlayer play]; // NOTE: change this line according your current player implementation
- NSLog(@"resumed play");
- });
-
- NSLog(@" . . . PAUSED (or just started)");
- }
-}
-
-- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
- self.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
-
- self.avPlayerItem = [AVPlayerItem playerItemWithAsset:self.avAsset];
- [self.avPlayerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
-
- self.avPlayer = [AVPlayer playerWithPlayerItem:self.avPlayerItem];
- self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
-
- [self.avPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(playerItemDidReachEnd:)
- name:AVPlayerItemDidPlayToEndTimeNotification
- object:[self.avPlayer currentItem]];
-
- [self.avPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:0];
-
- [self.avPlayerLayer setFrame:self.bounds];
- [self.layer addSublayer:self.avPlayerLayer];
- [self.avPlayer play];
-
- AVMediaSelectionGroup *audioGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
-
- NSMutableArray *allAudioParams = [NSMutableArray array];
- for (id track in audioGroup.options) {
- NSString *language = [[track locale] localeIdentifier];
- NSLog(@"subtitle lang: %@", language);
-
- if ([language isEqualToString:audioTrack]) {
- AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
- [audioInputParams setVolume:videoVolume atTime:kCMTimeZero];
- [audioInputParams setTrackID:[track trackID]];
- [allAudioParams addObject:audioInputParams];
-
- AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
- [audioMix setInputParameters:allAudioParams];
-
- [self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:audioGroup];
- [self.avPlayer.currentItem setAudioMix:audioMix];
-
- break;
- }
- }
-
- AVMediaSelectionGroup *subtitlesGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
- NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]];
-
- for (id track in useableTracks) {
- NSString *language = [[track locale] localeIdentifier];
- NSLog(@"subtitle lang: %@", language);
-
- if ([language isEqualToString:subtitleTrack]) {
- [self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:subtitlesGroup];
- break;
- }
- }
-
- self.isVideoCurrentlyPlaying = YES;
-
- return true;
-}
-
-- (BOOL)isVideoPlaying {
- if (self.avPlayer.error) {
- printf("Error during playback\n");
- }
- return (self.avPlayer.rate > 0 && !self.avPlayer.error);
-}
-
-- (void)pauseVideo {
- self.videoCurrentTime = self.avPlayer.currentTime;
- [self.avPlayer pause];
- self.isVideoCurrentlyPlaying = NO;
-}
-
-- (void)unpauseVideo {
- [self.avPlayer play];
- self.isVideoCurrentlyPlaying = YES;
-}
-
-- (void)playerItemDidReachEnd:(NSNotification *)notification {
- [self stopVideo];
-}
-
-- (void)finishPlayingVideo {
- [self.avPlayer pause];
- [self.avPlayerLayer removeFromSuperlayer];
- self.avPlayerLayer = nil;
-
- if (self.avPlayerItem) {
- [self.avPlayerItem removeObserver:self forKeyPath:@"status"];
- self.avPlayerItem = nil;
- }
-
- if (self.avPlayer) {
- [self.avPlayer removeObserver:self forKeyPath:@"status"];
- self.avPlayer = nil;
- }
-
- self.avAsset = nil;
-
- self.isVideoCurrentlyPlaying = NO;
-}
-
-- (void)stopVideo {
- [self finishPlayingVideo];
-
- [self removeFromSuperview];
-}
-
-@end
diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm
index 51c4da2960..458834ce3a 100644
--- a/platform/iphone/os_iphone.mm
+++ b/platform/iphone/os_iphone.mm
@@ -301,10 +301,6 @@ void OSIPhone::on_focus_out() {
[AppDelegate.viewController.godotView stopRendering];
- if (DisplayServerIPhone::get_singleton() && DisplayServerIPhone::get_singleton()->native_video_is_playing()) {
- DisplayServerIPhone::get_singleton()->native_video_pause();
- }
-
audio_driver.stop();
}
}
@@ -319,10 +315,6 @@ void OSIPhone::on_focus_in() {
[AppDelegate.viewController.godotView startRendering];
- if (DisplayServerIPhone::get_singleton() && DisplayServerIPhone::get_singleton()->native_video_is_playing()) {
- DisplayServerIPhone::get_singleton()->native_video_unpause();
- }
-
audio_driver.start();
}
}
diff --git a/platform/iphone/view_controller.h b/platform/iphone/view_controller.h
index 52fb6fbbf2..400145b8b7 100644
--- a/platform/iphone/view_controller.h
+++ b/platform/iphone/view_controller.h
@@ -37,11 +37,6 @@
@interface ViewController : UIViewController
@property(nonatomic, readonly, strong) GodotView *godotView;
-@property(nonatomic, readonly, strong) GodotNativeVideoView *videoView;
@property(nonatomic, readonly, strong) GodotKeyboardInputView *keyboardView;
-// MARK: Native Video Player
-
-- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
-
@end
diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm
index 6cef244567..2723ac4706 100644
--- a/platform/iphone/view_controller.mm
+++ b/platform/iphone/view_controller.mm
@@ -34,7 +34,6 @@
#import "godot_view.h"
#import "godot_view_renderer.h"
#import "keyboard_input_view.h"
-#import "native_video_view.h"
#include "os_iphone.h"
#import <AVFoundation/AVFoundation.h>
@@ -43,7 +42,6 @@
@interface ViewController () <GodotViewDelegate>
@property(strong, nonatomic) GodotViewRenderer *renderer;
-@property(strong, nonatomic) GodotNativeVideoView *videoView;
@property(strong, nonatomic) GodotKeyboardInputView *keyboardView;
@property(strong, nonatomic) UIView *godotLoadingOverlay;
@@ -151,10 +149,6 @@
}
- (void)dealloc {
- [self.videoView stopVideo];
-
- self.videoView = nil;
-
self.keyboardView = nil;
self.renderer = nil;
@@ -243,22 +237,4 @@
}
}
-// MARK: Native Video Player
-
-- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
- // If we are showing some video already, reuse existing view for new video.
- if (self.videoView) {
- return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
- } else {
- // Create autoresizing view for video playback.
- GodotNativeVideoView *videoView = [[GodotNativeVideoView alloc] initWithFrame:self.view.bounds];
- videoView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- [self.view addSubview:videoView];
-
- self.videoView = videoView;
-
- return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
- }
-}
-
@end
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp
index 234e42376d..736a8a9a34 100644
--- a/platform/javascript/display_server_javascript.cpp
+++ b/platform/javascript/display_server_javascript.cpp
@@ -827,7 +827,6 @@ bool DisplayServerJavaScript::has_feature(Feature p_feature) const {
//case FEATURE_MOUSE_WARP:
//case FEATURE_NATIVE_DIALOG:
//case FEATURE_NATIVE_ICON:
- //case FEATURE_NATIVE_VIDEO:
//case FEATURE_WINDOW_TRANSPARENCY:
//case FEATURE_KEEP_SCREEN_ON:
//case FEATURE_ORIENTATION:
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index 14e279b45b..951d86d09e 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -41,7 +41,7 @@
class EditorHTTPServer : public Reference {
private:
- Ref<TCP_Server> server;
+ Ref<TCPServer> server;
Map<String, String> mimes;
Ref<StreamPeerTCP> tcp;
Ref<StreamPeerSSL> ssl;
@@ -100,7 +100,7 @@ public:
_clear_client();
}
- Error listen(int p_port, IP_Address p_address, bool p_use_ssl, String p_ssl_key, String p_ssl_cert) {
+ Error listen(int p_port, IPAddress p_address, bool p_use_ssl, String p_ssl_key, String p_ssl_cert) {
use_ssl = p_use_ssl;
if (use_ssl) {
Ref<Crypto> crypto = Crypto::create();
@@ -919,7 +919,7 @@ Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_prese
const uint16_t bind_port = EDITOR_GET("export/web/http_port");
// Resolve host if needed.
const String bind_host = EDITOR_GET("export/web/http_host");
- IP_Address bind_ip;
+ IPAddress bind_ip;
if (bind_host.is_valid_ip_address()) {
bind_ip = bind_host;
} else {
diff --git a/platform/javascript/http_client.h.inc b/platform/javascript/http_client.h.inc
index 842a93fcba..6544d41c98 100644
--- a/platform/javascript/http_client.h.inc
+++ b/platform/javascript/http_client.h.inc
@@ -34,7 +34,8 @@ Error make_request(Method p_method, const String &p_url, const Vector<String> &p
static void _parse_headers(int p_len, const char **p_headers, void *p_ref);
int js_id = 0;
-int read_limit = 4096;
+// 64 KiB by default (favors fast download speeds at the cost of memory usage).
+int read_limit = 65536;
Status status = STATUS_DISCONNECTED;
String host;
diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp
index 0fe95b0a8f..40771d1882 100644
--- a/platform/javascript/javascript_main.cpp
+++ b/platform/javascript/javascript_main.cpp
@@ -66,6 +66,11 @@ void main_loop_callback() {
int target_fps = Engine::get_singleton()->get_target_fps();
if (target_fps > 0) {
+ if (current_ticks - target_ticks > 1000000) {
+ // When the window loses focus, we stop getting updates and accumulate delay.
+ // For this reason, if the difference is too big, we reset target ticks to the current ticks.
+ target_ticks = current_ticks;
+ }
target_ticks += (uint64_t)(1000000 / target_fps);
}
if (os->main_loop_iterate()) {
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index adbbcaac31..1876960c57 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -325,6 +325,10 @@ def configure(env):
if not env["builtin_pcre2"]:
env.ParseConfig("pkg-config libpcre2-32 --cflags --libs")
+ if not env["builtin_embree"]:
+ # No pkgconfig file so far, hardcode expected lib name.
+ env.Append(LIBS=["embree3"])
+
## Flags
if os.system("pkg-config --exists alsa") == 0: # 0 means found
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 12f030bd58..be69b2e5da 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -2606,7 +2606,6 @@ void DisplayServerX11::_window_changed(XEvent *event) {
}
#endif
- print_line("DisplayServer::_window_changed: " + itos(window_id) + " rect: " + new_rect);
if (!wd.rect_changed_callback.is_null()) {
Rect2i r = new_rect;
@@ -3833,8 +3832,6 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
wd.position.y = xwa.y;
wd.size.width = xwa.width;
wd.size.height = xwa.height;
-
- print_line("DisplayServer::_create_window " + itos(id) + " want rect: " + p_rect + " got rect " + Rect2i(xwa.x, xwa.y, xwa.width, xwa.height));
}
//set cursor
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 33992069f9..2314a392cc 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -147,7 +147,7 @@ void OS_UWP::initialize_core() {
ticks_start = 0;
ticks_start = get_ticks_usec();
- IP_Unix::make_default();
+ IPUnix::make_default();
cursor_shape = CURSOR_ARROW;
}
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 1e9cdd241d..f517190a89 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -204,7 +204,7 @@ void OS_Windows::initialize() {
current_pi.pi.hProcess = GetCurrentProcess();
process_map->insert(GetCurrentProcessId(), current_pi);
- IP_Unix::make_default();
+ IPUnix::make_default();
main_loop = nullptr;
}
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 532a795b7c..4400198497 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -30,260 +30,323 @@
#include "tile_map.h"
-#include "collision_object_2d.h"
#include "core/io/marshalls.h"
+#include "core/math/geometry_2d.h"
#include "core/os/os.h"
-#include "scene/2d/area_2d.h"
-#include "servers/navigation_server_2d.h"
-#include "servers/physics_server_2d.h"
-int TileMap::_get_quadrant_size() const {
- if (use_y_sort) {
- return 1;
- } else {
- return quadrant_size;
- }
+void TileMapPattern::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) {
+ ERR_FAIL_COND_MSG(p_coords.x < 0 || p_coords.y < 0, vformat("Cannot set cell with negative coords in a TileMapPattern. Wrong coords: %s", p_coords));
+
+ size = size.max(p_coords + Vector2i(1, 1));
+ pattern[p_coords] = TileMapCell(p_source_id, p_atlas_coords, p_alternative_tile);
}
-void TileMap::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- if (use_parent) {
- _clear_quadrants();
- collision_parent = Object::cast_to<CollisionObject2D>(get_parent());
- }
+bool TileMapPattern::has_cell(const Vector2i &p_coords) const {
+ return pattern.has(p_coords);
+}
- pending_update = true;
- _recreate_quadrants();
- update_dirty_quadrants();
- RID space = get_world_2d()->get_space();
- _update_quadrant_transform();
- _update_quadrant_space(space);
- update_configuration_warnings();
+void TileMapPattern::remove_cell(const Vector2i &p_coords, bool p_update_size) {
+ ERR_FAIL_COND(!pattern.has(p_coords));
- } break;
+ pattern.erase(p_coords);
+ if (p_update_size) {
+ size = Vector2i();
+ for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) {
+ size = size.max(E->key() + Vector2i(1, 1));
+ }
+ }
+}
- case NOTIFICATION_EXIT_TREE: {
- _update_quadrant_space(RID());
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- for (Map<PosKey, Quadrant::NavPoly>::Element *F = q.navpoly_ids.front(); F; F = F->next()) {
- NavigationServer2D::get_singleton()->region_set_map(F->get().region, RID());
- }
- q.navpoly_ids.clear();
+int TileMapPattern::get_cell_source_id(const Vector2i &p_coords) const {
+ ERR_FAIL_COND_V(!pattern.has(p_coords), -1);
- if (collision_parent) {
- collision_parent->remove_shape_owner(q.shape_owner_id);
- q.shape_owner_id = -1;
- }
+ return pattern[p_coords].source_id;
+}
- for (Map<PosKey, Quadrant::Occluder>::Element *F = q.occluder_instances.front(); F; F = F->next()) {
- RS::get_singleton()->free(F->get().id);
- }
- q.occluder_instances.clear();
- }
+Vector2i TileMapPattern::get_cell_atlas_coords(const Vector2i &p_coords) const {
+ ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetAtlasSource::INVALID_ATLAS_COORDS);
- collision_parent = nullptr;
- } break;
+ return pattern[p_coords].get_atlas_coords();
+}
- case NOTIFICATION_TRANSFORM_CHANGED: {
- //move stuff
- _update_quadrant_transform();
+int TileMapPattern::get_cell_alternative_tile(const Vector2i &p_coords) const {
+ ERR_FAIL_COND_V(!pattern.has(p_coords), TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
- } break;
- case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- if (use_parent) {
- _recreate_quadrants();
- }
+ return pattern[p_coords].alternative_tile;
+}
- } break;
+TypedArray<Vector2i> TileMapPattern::get_used_cells() const {
+ // Returns the cells used in the tilemap.
+ TypedArray<Vector2i> a;
+ a.resize(pattern.size());
+ int i = 0;
+ for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) {
+ Vector2i p(E->key().x, E->key().y);
+ a[i++] = p;
}
+
+ return a;
}
-void TileMap::_update_quadrant_space(const RID &p_space) {
- if (!use_parent) {
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- PhysicsServer2D::get_singleton()->body_set_space(q.body, p_space);
- }
- }
+Vector2i TileMapPattern::get_size() const {
+ return size;
}
-void TileMap::_update_quadrant_transform() {
- if (!is_inside_tree()) {
- return;
+void TileMapPattern::set_size(const Vector2i &p_size) {
+ for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) {
+ Vector2i coords = E->key();
+ if (p_size.x <= coords.x || p_size.y <= coords.y) {
+ ERR_FAIL_MSG(vformat("Cannot set pattern size to %s, it contains a tile at %s. Size can only be increased.", p_size, coords));
+ };
}
- Transform2D global_transform = get_global_transform();
+ size = p_size;
+}
- Transform2D local_transform;
- if (collision_parent) {
- local_transform = get_transform();
- }
+bool TileMapPattern::is_empty() const {
+ return pattern.is_empty();
+};
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- Transform2D xform;
- xform.set_origin(q.pos);
+void TileMapPattern::clear() {
+ size = Vector2i();
+ pattern.clear();
+};
- if (!use_parent) {
- xform = global_transform * xform;
- PhysicsServer2D::get_singleton()->body_set_state(q.body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
- }
+void TileMapPattern::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(-1), DEFVAL(TileSetAtlasSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetAtlasSource::INVALID_TILE_ALTERNATIVE));
+ ClassDB::bind_method(D_METHOD("has_cell", "coords"), &TileMapPattern::has_cell);
+ ClassDB::bind_method(D_METHOD("remove_cell", "coords"), &TileMapPattern::remove_cell);
+ ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMapPattern::get_cell_source_id);
+ ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMapPattern::get_cell_atlas_coords);
+ ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapPattern::get_cell_alternative_tile);
- if (bake_navigation) {
- for (Map<PosKey, Quadrant::NavPoly>::Element *F = q.navpoly_ids.front(); F; F = F->next()) {
- NavigationServer2D::get_singleton()->region_set_transform(F->get().region, F->get().xform);
+ ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMapPattern::get_used_cells);
+ ClassDB::bind_method(D_METHOD("get_size"), &TileMapPattern::get_size);
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &TileMapPattern::set_size);
+ ClassDB::bind_method(D_METHOD("is_empty"), &TileMapPattern::is_empty);
+}
+
+Vector2i TileMap::transform_coords_layout(Vector2i p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) {
+ // Transform to stacked layout.
+ Vector2i output = p_coords;
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ SWAP(output.x, output.y);
+ }
+ switch (p_from_layout) {
+ case TileSet::TILE_LAYOUT_STACKED:
+ break;
+ case TileSet::TILE_LAYOUT_STACKED_OFFSET:
+ if (output.y % 2) {
+ output.x -= 1;
}
- }
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
+ case TileSet::TILE_LAYOUT_STAIRS_DOWN:
+ if ((p_from_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
+ if (output.y < 0 && bool(output.y % 2)) {
+ output = Vector2i(output.x + output.y / 2 - 1, output.y);
+ } else {
+ output = Vector2i(output.x + output.y / 2, output.y);
+ }
+ } else {
+ if (output.x < 0 && bool(output.x % 2)) {
+ output = Vector2i(output.x / 2 - 1, output.x + output.y * 2);
+ } else {
+ output = Vector2i(output.x / 2, output.x + output.y * 2);
+ }
+ }
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
+ case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
+ if ((p_from_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
+ if ((output.x + output.y) < 0 && (output.x - output.y) % 2) {
+ output = Vector2i((output.x + output.y) / 2 - 1, output.y - output.x);
+ } else {
+ output = Vector2i((output.x + output.y) / 2, -output.x + output.y);
+ }
+ } else {
+ if ((output.x - output.y) < 0 && (output.x + output.y) % 2) {
+ output = Vector2i((output.x - output.y) / 2 - 1, output.x + output.y);
+ } else {
+ output = Vector2i((output.x - output.y) / 2, output.x + output.y);
+ }
+ }
+ break;
+ }
- for (Map<PosKey, Quadrant::Occluder>::Element *F = q.occluder_instances.front(); F; F = F->next()) {
- RS::get_singleton()->canvas_light_occluder_set_transform(F->get().id, global_transform * F->get().xform);
- }
+ switch (p_to_layout) {
+ case TileSet::TILE_LAYOUT_STACKED:
+ break;
+ case TileSet::TILE_LAYOUT_STACKED_OFFSET:
+ if (output.y % 2) {
+ output.x += 1;
+ }
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
+ case TileSet::TILE_LAYOUT_STAIRS_DOWN:
+ if ((p_to_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
+ if (output.y < 0 && (output.y % 2)) {
+ output = Vector2i(output.x - output.y / 2 + 1, output.y);
+ } else {
+ output = Vector2i(output.x - output.y / 2, output.y);
+ }
+ } else {
+ if (output.y % 2) {
+ if (output.y < 0) {
+ output = Vector2i(2 * output.x + 1, -output.x + output.y / 2 - 1);
+ } else {
+ output = Vector2i(2 * output.x + 1, -output.x + output.y / 2);
+ }
+ } else {
+ output = Vector2i(2 * output.x, -output.x + output.y / 2);
+ }
+ }
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
+ case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
+ if ((p_to_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
+ if (output.y % 2) {
+ if (output.y > 0) {
+ output = Vector2i(output.x - output.y / 2, output.x + output.y / 2 + 1);
+ } else {
+ output = Vector2i(output.x - output.y / 2 + 1, output.x + output.y / 2);
+ }
+ } else {
+ output = Vector2i(output.x - output.y / 2, output.x + output.y / 2);
+ }
+ } else {
+ if (output.y % 2) {
+ if (output.y < 0) {
+ output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2 - 1);
+ } else {
+ output = Vector2i(output.x + output.y / 2 + 1, -output.x + output.y / 2);
+ }
+ } else {
+ output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2);
+ }
+ }
+ break;
}
-}
-void TileMap::set_tileset(const Ref<TileSet> &p_tileset) {
- if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileMap::_recreate_quadrants));
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ SWAP(output.x, output.y);
}
- _clear_quadrants();
- tile_set = p_tileset;
+ return output;
+}
- if (tile_set.is_valid()) {
- tile_set->connect("changed", callable_mp(this, &TileMap::_recreate_quadrants));
+int TileMap::get_effective_quadrant_size() const {
+ // When using YSort, the quadrant size is reduced to 1 to have one CanvasItem per quadrant
+ if (tile_set.is_valid() && tile_set->is_y_sorting()) {
+ return 1;
} else {
- clear();
+ return quadrant_size;
}
+}
- _recreate_quadrants();
- emit_signal("settings_changed");
+Vector2i TileMap::_coords_to_quadrant_coords(const Vector2i &p_coords) const {
+ int quadrant_size = get_effective_quadrant_size();
+
+ // Rounding down, instead of simply rounding towards zero (truncating)
+ return Vector2i(
+ p_coords.x > 0 ? p_coords.x / quadrant_size : (p_coords.x - (quadrant_size - 1)) / quadrant_size,
+ p_coords.y > 0 ? p_coords.y / quadrant_size : (p_coords.y - (quadrant_size - 1)) / quadrant_size);
+}
+
+void TileMap::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ pending_update = true;
+ _recreate_quadrants();
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ _clear_quadrants();
+ } break;
+ }
+
+ // Transfers the notification to tileset plugins.
+ if (tile_set.is_valid()) {
+ for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) {
+ tile_set->get_tile_set_atlas_plugins()[i]->tilemap_notification(this, p_what);
+ }
+ }
}
Ref<TileSet> TileMap::get_tileset() const {
return tile_set;
}
-void TileMap::set_cell_size(Size2 p_size) {
- ERR_FAIL_COND(p_size.x < 1 || p_size.y < 1);
+void TileMap::set_tileset(const Ref<TileSet> &p_tileset) {
+ if (p_tileset == tile_set) {
+ return;
+ }
- _clear_quadrants();
- cell_size = p_size;
- _recreate_quadrants();
- emit_signal("settings_changed");
+ // Set the tileset, registering to its changes.
+ if (tile_set.is_valid()) {
+ tile_set->disconnect("changed", callable_mp(this, &TileMap::_make_all_quadrants_dirty));
+ tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed));
+ }
+
+ if (!p_tileset.is_valid()) {
+ _clear_quadrants();
+ }
+
+ tile_set = p_tileset;
+
+ if (tile_set.is_valid()) {
+ tile_set->connect("changed", callable_mp(this, &TileMap::_make_all_quadrants_dirty), varray(true));
+ tile_set->connect("changed", callable_mp(this, &TileMap::_tile_set_changed));
+ _recreate_quadrants();
+ }
+
+ emit_signal("changed");
}
-Size2 TileMap::get_cell_size() const {
- return cell_size;
+int TileMap::get_quadrant_size() const {
+ return quadrant_size;
}
void TileMap::set_quadrant_size(int p_size) {
- ERR_FAIL_COND_MSG(p_size < 1, "Quadrant size cannot be smaller than 1.");
+ ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1.");
- _clear_quadrants();
quadrant_size = p_size;
_recreate_quadrants();
- emit_signal("settings_changed");
-}
-
-int TileMap::get_quadrant_size() const {
- return quadrant_size;
+ emit_signal("changed");
}
-void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const Vector2 &p_offset, const Size2 &p_sc) {
+void TileMap::_fix_cell_transform(Transform2D &xform, const TileMapCell &p_cell, const Vector2 &p_offset, const Size2 &p_sc) {
Size2 s = p_sc;
Vector2 offset = p_offset;
- if (compatibility_mode && !centered_textures) {
- if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
- offset.y += cell_size.y;
- } else if (tile_origin == TILE_ORIGIN_CENTER) {
- offset += cell_size / 2;
- }
-
- if (s.y > s.x) {
- if ((p_cell.flip_h && (p_cell.flip_v || p_cell.transpose)) || (p_cell.flip_v && !p_cell.transpose)) {
- offset.y += s.y - s.x;
- }
- } else if (s.y < s.x) {
- if ((p_cell.flip_v && (p_cell.flip_h || p_cell.transpose)) || (p_cell.flip_h && !p_cell.transpose)) {
- offset.x += s.x - s.y;
- }
- }
+ // Flip/transpose: update the tile transform.
+ TileSetSource *source = *tile_set->get_source(p_cell.source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (!atlas_source) {
+ return;
}
-
- if (p_cell.transpose) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_cell.get_atlas_coords(), p_cell.alternative_tile));
+ if (tile_data->get_transpose()) {
SWAP(xform.elements[0].x, xform.elements[0].y);
SWAP(xform.elements[1].x, xform.elements[1].y);
SWAP(offset.x, offset.y);
SWAP(s.x, s.y);
}
- if (p_cell.flip_h) {
+ if (tile_data->get_flip_h()) {
xform.elements[0].x = -xform.elements[0].x;
xform.elements[1].x = -xform.elements[1].x;
- if (compatibility_mode && !centered_textures) {
- if (tile_origin == TILE_ORIGIN_TOP_LEFT || tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
- offset.x = s.x - offset.x;
- } else if (tile_origin == TILE_ORIGIN_CENTER) {
- offset.x = s.x - offset.x / 2;
- }
- } else {
- offset.x = s.x - offset.x;
- }
+ offset.x = s.x - offset.x;
}
- if (p_cell.flip_v) {
+ if (tile_data->get_flip_v()) {
xform.elements[0].y = -xform.elements[0].y;
xform.elements[1].y = -xform.elements[1].y;
- if (compatibility_mode && !centered_textures) {
- if (tile_origin == TILE_ORIGIN_TOP_LEFT) {
- offset.y = s.y - offset.y;
- } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
- offset.y += s.y;
- } else if (tile_origin == TILE_ORIGIN_CENTER) {
- offset.y += s.y;
- }
- } else {
- offset.y = s.y - offset.y;
- }
+ offset.y = s.y - offset.y;
}
- if (centered_textures) {
- offset += cell_size / 2 - s / 2;
- }
xform.elements[2] += offset;
}
-void TileMap::_add_shape(int &shape_idx, const Quadrant &p_q, const Ref<Shape2D> &p_shape, const TileSet::ShapeData &p_shape_data, const Transform2D &p_xform, const Vector2 &p_metadata) {
- PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
-
- if (!use_parent) {
- ps->body_add_shape(p_q.body, p_shape->get_rid(), p_xform);
- ps->body_set_shape_metadata(p_q.body, shape_idx, p_metadata);
- ps->body_set_shape_as_one_way_collision(p_q.body, shape_idx, p_shape_data.one_way_collision, p_shape_data.one_way_collision_margin);
-
- } else if (collision_parent) {
- Transform2D xform = p_xform;
- xform.set_origin(xform.get_origin() + p_q.pos);
-
- collision_parent->shape_owner_add_shape(p_q.shape_owner_id, p_shape);
-
- int real_index = collision_parent->shape_owner_get_shape_index(p_q.shape_owner_id, shape_idx);
- RID rid = collision_parent->get_rid();
-
- if (Object::cast_to<Area2D>(collision_parent) != nullptr) {
- ps->area_set_shape_transform(rid, real_index, get_transform() * xform);
- } else {
- ps->body_set_shape_transform(rid, real_index, get_transform() * xform);
- ps->body_set_shape_metadata(rid, real_index, p_metadata);
- ps->body_set_shape_as_one_way_collision(rid, real_index, p_shape_data.one_way_collision, p_shape_data.one_way_collision_margin);
- }
- }
- shape_idx++;
-}
-
void TileMap::update_dirty_quadrants() {
if (!pending_update) {
return;
@@ -293,387 +356,47 @@ void TileMap::update_dirty_quadrants() {
return;
}
- RenderingServer *vs = RenderingServer::get_singleton();
- PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
- Vector2 tofs = get_cell_draw_offset();
- Vector2 qofs;
-
- SceneTree *st = SceneTree::get_singleton();
- Color debug_collision_color;
- Color debug_navigation_color;
-
- bool debug_shapes = st && st->is_debugging_collisions_hint();
- if (debug_shapes) {
- debug_collision_color = st->get_debug_collisions_color();
+ // Update the coords cache.
+ for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) {
+ q->self()->map_to_world.clear();
+ q->self()->world_to_map.clear();
+ for (Set<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) {
+ Vector2i pk = E->get();
+ Vector2i pk_world_coords = map_to_world(pk);
+ q->self()->map_to_world[pk] = pk_world_coords;
+ q->self()->world_to_map[pk_world_coords] = pk;
+ }
}
- bool debug_navigation = st && st->is_debugging_navigation_hint();
- if (debug_navigation) {
- debug_navigation_color = st->get_debug_navigation_color();
+ // Call the update_dirty_quadrant method on plugins.
+ for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) {
+ tile_set->get_tile_set_atlas_plugins()[i]->update_dirty_quadrants(this, dirty_quadrant_list);
}
- while (dirty_quadrant_list.first()) {
- Quadrant &q = *dirty_quadrant_list.first()->self();
-
- for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) {
- vs->free(E->get());
- }
-
- q.canvas_items.clear();
-
- if (!use_parent) {
- ps->body_clear_shapes(q.body);
- } else if (collision_parent) {
- collision_parent->shape_owner_clear_shapes(q.shape_owner_id);
- }
- int shape_idx = 0;
-
- for (Map<PosKey, Quadrant::NavPoly>::Element *E = q.navpoly_ids.front(); E; E = E->next()) {
- NavigationServer2D::get_singleton()->region_set_map(E->get().region, RID());
- }
- q.navpoly_ids.clear();
-
- for (Map<PosKey, Quadrant::Occluder>::Element *E = q.occluder_instances.front(); E; E = E->next()) {
- RS::get_singleton()->free(E->get().id);
- }
- q.occluder_instances.clear();
- Ref<ShaderMaterial> prev_material;
- int prev_z_index = 0;
- RID prev_canvas_item;
- RID prev_debug_canvas_item;
-
- for (int i = 0; i < q.cells.size(); i++) {
- Map<PosKey, Cell>::Element *E = tile_map.find(q.cells[i]);
- Cell &c = E->get();
- //moment of truth
- if (!tile_set->has_tile(c.id)) {
- continue;
- }
- Ref<Texture2D> tex = tile_set->tile_get_texture(c.id);
- Vector2 tile_ofs = tile_set->tile_get_texture_offset(c.id);
-
- Vector2 wofs = _map_to_world(E->key().x, E->key().y);
- Vector2 offset = wofs - q.pos + tofs;
-
- if (!tex.is_valid()) {
- continue;
- }
-
- Ref<ShaderMaterial> mat = tile_set->tile_get_material(c.id);
- int z_index = tile_set->tile_get_z_index(c.id);
-
- if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE ||
- tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) {
- z_index += tile_set->autotile_get_z_index(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y));
- }
-
- RID canvas_item;
- RID debug_canvas_item;
-
- if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) {
- canvas_item = vs->canvas_item_create();
- if (mat.is_valid()) {
- vs->canvas_item_set_material(canvas_item, mat->get_rid());
- }
- vs->canvas_item_set_parent(canvas_item, get_canvas_item());
- _update_item_material_state(canvas_item);
- Transform2D xform;
- xform.set_origin(q.pos);
- vs->canvas_item_set_transform(canvas_item, xform);
- vs->canvas_item_set_light_mask(canvas_item, get_light_mask());
- vs->canvas_item_set_z_index(canvas_item, z_index);
-
- vs->canvas_item_set_default_texture_filter(canvas_item, RS::CanvasItemTextureFilter(CanvasItem::get_texture_filter()));
- vs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(CanvasItem::get_texture_repeat()));
-
- q.canvas_items.push_back(canvas_item);
-
- if (debug_shapes) {
- debug_canvas_item = vs->canvas_item_create();
- vs->canvas_item_set_parent(debug_canvas_item, canvas_item);
- vs->canvas_item_set_z_as_relative_to_parent(debug_canvas_item, false);
- vs->canvas_item_set_z_index(debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1);
- q.canvas_items.push_back(debug_canvas_item);
- prev_debug_canvas_item = debug_canvas_item;
- }
-
- prev_canvas_item = canvas_item;
- prev_material = mat;
- prev_z_index = z_index;
-
- } else {
- canvas_item = prev_canvas_item;
- if (debug_shapes) {
- debug_canvas_item = prev_debug_canvas_item;
- }
- }
-
- Rect2 r = tile_set->tile_get_region(c.id);
- if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) {
- int spacing = tile_set->autotile_get_spacing(c.id);
- r.size = tile_set->autotile_get_size(c.id);
- r.position += (r.size + Vector2(spacing, spacing)) * Vector2(c.autotile_coord_x, c.autotile_coord_y);
- }
-
- Size2 s;
- if (r == Rect2()) {
- s = tex->get_size();
- } else {
- s = r.size;
- }
-
- Rect2 rect;
- rect.position = offset.floor();
- rect.size = s;
- rect.size.x += fp_adjust;
- rect.size.y += fp_adjust;
-
- if (compatibility_mode && !centered_textures) {
- if (rect.size.y > rect.size.x) {
- if ((c.flip_h && (c.flip_v || c.transpose)) || (c.flip_v && !c.transpose)) {
- tile_ofs.y += rect.size.y - rect.size.x;
- }
- } else if (rect.size.y < rect.size.x) {
- if ((c.flip_v && (c.flip_h || c.transpose)) || (c.flip_h && !c.transpose)) {
- tile_ofs.x += rect.size.x - rect.size.y;
- }
- }
- }
-
- if (c.transpose) {
- SWAP(tile_ofs.x, tile_ofs.y);
- if (centered_textures) {
- rect.position.x += cell_size.x / 2 - rect.size.y / 2;
- rect.position.y += cell_size.y / 2 - rect.size.x / 2;
- }
- } else if (centered_textures) {
- rect.position += cell_size / 2 - rect.size / 2;
- }
-
- if (c.flip_h) {
- rect.size.x = -rect.size.x;
- tile_ofs.x = -tile_ofs.x;
- }
-
- if (c.flip_v) {
- rect.size.y = -rect.size.y;
- tile_ofs.y = -tile_ofs.y;
- }
-
- if (compatibility_mode && !centered_textures) {
- if (tile_origin == TILE_ORIGIN_TOP_LEFT) {
- rect.position += tile_ofs;
-
- } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
- rect.position += tile_ofs;
-
- if (c.transpose) {
- if (c.flip_h) {
- rect.position.x -= cell_size.x;
- } else {
- rect.position.x += cell_size.x;
- }
- } else {
- if (c.flip_v) {
- rect.position.y -= cell_size.y;
- } else {
- rect.position.y += cell_size.y;
- }
- }
-
- } else if (tile_origin == TILE_ORIGIN_CENTER) {
- rect.position += tile_ofs;
-
- if (c.flip_h) {
- rect.position.x -= cell_size.x / 2;
- } else {
- rect.position.x += cell_size.x / 2;
- }
-
- if (c.flip_v) {
- rect.position.y -= cell_size.y / 2;
- } else {
- rect.position.y += cell_size.y / 2;
- }
- }
- } else {
- rect.position += tile_ofs;
- }
-
- Color modulate = tile_set->tile_get_modulate(c.id);
- Color self_modulate = get_self_modulate();
- modulate = Color(modulate.r * self_modulate.r, modulate.g * self_modulate.g,
- modulate.b * self_modulate.b, modulate.a * self_modulate.a);
- if (r == Rect2()) {
- tex->draw_rect(canvas_item, rect, false, modulate, c.transpose);
- } else {
- tex->draw_rect_region(canvas_item, rect, r, modulate, c.transpose, clip_uv);
- }
-
- Vector<TileSet::ShapeData> shapes = tile_set->tile_get_shapes(c.id);
-
- for (int j = 0; j < shapes.size(); j++) {
- Ref<Shape2D> shape = shapes[j].shape;
- if (shape.is_valid()) {
- if (tile_set->tile_get_tile_mode(c.id) == TileSet::SINGLE_TILE || (shapes[j].autotile_coord.x == c.autotile_coord_x && shapes[j].autotile_coord.y == c.autotile_coord_y)) {
- Transform2D xform;
- xform.set_origin(offset.floor());
-
- Vector2 shape_ofs = shapes[j].shape_transform.get_origin();
-
- _fix_cell_transform(xform, c, shape_ofs, s);
-
- xform *= shapes[j].shape_transform.untranslated();
-
- if (debug_canvas_item.is_valid()) {
- vs->canvas_item_add_set_transform(debug_canvas_item, xform);
- shape->draw(debug_canvas_item, debug_collision_color);
- }
-
- if (shape->has_meta("decomposed")) {
- Array _shapes = shape->get_meta("decomposed");
- for (int k = 0; k < _shapes.size(); k++) {
- Ref<ConvexPolygonShape2D> convex = _shapes[k];
- if (convex.is_valid()) {
- _add_shape(shape_idx, q, convex, shapes[j], xform, Vector2(E->key().x, E->key().y));
-#ifdef DEBUG_ENABLED
- } else {
- print_error("The TileSet assigned to the TileMap " + get_name() + " has an invalid convex shape.");
-#endif
- }
- }
- } else {
- _add_shape(shape_idx, q, shape, shapes[j], xform, Vector2(E->key().x, E->key().y));
- }
- }
- }
- }
-
- if (debug_canvas_item.is_valid()) {
- vs->canvas_item_add_set_transform(debug_canvas_item, Transform2D());
- }
-
- if (bake_navigation) {
- Ref<NavigationPolygon> navpoly;
- Vector2 npoly_ofs;
- if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) {
- navpoly = tile_set->autotile_get_navigation_polygon(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y));
- npoly_ofs = Vector2();
- } else {
- navpoly = tile_set->tile_get_navigation_polygon(c.id);
- npoly_ofs = tile_set->tile_get_navigation_polygon_offset(c.id);
- }
-
- if (navpoly.is_valid()) {
- Transform2D xform;
- xform.set_origin(offset.floor() + q.pos);
- _fix_cell_transform(xform, c, npoly_ofs, s);
-
- RID region = NavigationServer2D::get_singleton()->region_create();
- NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
- NavigationServer2D::get_singleton()->region_set_transform(region, xform);
- NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly);
-
- Quadrant::NavPoly np;
- np.region = region;
- np.xform = xform;
- q.navpoly_ids[E->key()] = np;
-
- if (debug_navigation) {
- RID debug_navigation_item = vs->canvas_item_create();
- vs->canvas_item_set_parent(debug_navigation_item, canvas_item);
- vs->canvas_item_set_z_as_relative_to_parent(debug_navigation_item, false);
- vs->canvas_item_set_z_index(debug_navigation_item, RS::CANVAS_ITEM_Z_MAX - 2); // Display one below collision debug
-
- if (debug_navigation_item.is_valid()) {
- Vector<Vector2> navigation_polygon_vertices = navpoly->get_vertices();
- int vsize = navigation_polygon_vertices.size();
-
- if (vsize > 2) {
- Vector<Color> colors;
- Vector<Vector2> vertices;
- vertices.resize(vsize);
- colors.resize(vsize);
- {
- const Vector2 *vr = navigation_polygon_vertices.ptr();
- for (int j = 0; j < vsize; j++) {
- vertices.write[j] = vr[j];
- colors.write[j] = debug_navigation_color;
- }
- }
-
- Vector<int> indices;
-
- for (int j = 0; j < navpoly->get_polygon_count(); j++) {
- Vector<int> polygon = navpoly->get_polygon(j);
-
- for (int k = 2; k < polygon.size(); k++) {
- int kofs[3] = { 0, k - 1, k };
- for (int l = 0; l < 3; l++) {
- int idx = polygon[kofs[l]];
- ERR_FAIL_INDEX(idx, vsize);
- indices.push_back(idx);
- }
- }
- }
- Transform2D navxform;
- navxform.set_origin(offset.floor());
- _fix_cell_transform(navxform, c, npoly_ofs, s);
-
- vs->canvas_item_set_transform(debug_navigation_item, navxform);
- vs->canvas_item_add_triangle_array(debug_navigation_item, indices, vertices, colors);
- }
- }
- }
- }
- }
-
- Ref<OccluderPolygon2D> occluder;
- if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) {
- occluder = tile_set->autotile_get_light_occluder(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y));
- } else {
- occluder = tile_set->tile_get_light_occluder(c.id);
- }
- if (occluder.is_valid()) {
- Vector2 occluder_ofs = tile_set->tile_get_occluder_offset(c.id);
- Transform2D xform;
- xform.set_origin(offset.floor() + q.pos);
- _fix_cell_transform(xform, c, occluder_ofs, s);
-
- RID orid = RS::get_singleton()->canvas_light_occluder_create();
- RS::get_singleton()->canvas_light_occluder_set_transform(orid, get_global_transform() * xform);
- RS::get_singleton()->canvas_light_occluder_set_polygon(orid, occluder->get_rid());
- RS::get_singleton()->canvas_light_occluder_attach_to_canvas(orid, get_canvas());
- RS::get_singleton()->canvas_light_occluder_set_light_mask(orid, occluder_light_mask);
- Quadrant::Occluder oc;
- oc.xform = xform;
- oc.id = orid;
- q.occluder_instances[E->key()] = oc;
- }
+ // Redraw the debug canvas_items.
+ RenderingServer *rs = RenderingServer::get_singleton();
+ for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) {
+ rs->canvas_item_clear(q->self()->debug_canvas_item);
+ Transform2D xform;
+ xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size()));
+ rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform);
+ for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) {
+ tile_set->get_tile_set_atlas_plugins()[i]->draw_quadrant_debug(this, q->self());
}
+ }
+ // Clear the list
+ while (dirty_quadrant_list.first()) {
dirty_quadrant_list.remove(dirty_quadrant_list.first());
- quadrant_order_dirty = true;
}
pending_update = false;
- if (quadrant_order_dirty) {
- int index = -(int64_t)0x80000000; //always must be drawn below children
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- for (List<RID>::Element *F = q.canvas_items.front(); F; F = F->next()) {
- RS::get_singleton()->canvas_item_set_draw_index(F->get(), index++);
- }
- }
-
- quadrant_order_dirty = false;
- }
-
_recompute_rect_cache();
}
void TileMap::_recompute_rect_cache() {
+ // Compute the displayed area of the tilemap.
#ifdef DEBUG_ENABLED
if (!rect_cache_dirty) {
@@ -681,12 +404,12 @@ void TileMap::_recompute_rect_cache() {
}
Rect2 r_total;
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
Rect2 r;
- r.position = _map_to_world(E->key().x * _get_quadrant_size(), E->key().y * _get_quadrant_size());
- r.expand_to(_map_to_world(E->key().x * _get_quadrant_size() + _get_quadrant_size(), E->key().y * _get_quadrant_size()));
- r.expand_to(_map_to_world(E->key().x * _get_quadrant_size() + _get_quadrant_size(), E->key().y * _get_quadrant_size() + _get_quadrant_size()));
- r.expand_to(_map_to_world(E->key().x * _get_quadrant_size(), E->key().y * _get_quadrant_size() + _get_quadrant_size()));
+ r.position = map_to_world(E->key() * get_effective_quadrant_size());
+ r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size()));
+ r.expand_to(map_to_world((E->key() + Vector2i(1, 1)) * get_effective_quadrant_size()));
+ r.expand_to(map_to_world((E->key() + Vector2i(0, 1)) * get_effective_quadrant_size()));
if (E == quadrant_map.front()) {
r_total = r;
} else {
@@ -702,83 +425,58 @@ void TileMap::_recompute_rect_cache() {
#endif
}
-Map<TileMap::PosKey, TileMap::Quadrant>::Element *TileMap::_create_quadrant(const PosKey &p_qk) {
- Transform2D xform;
- //xform.set_origin(Point2(p_qk.x,p_qk.y)*cell_size*quadrant_size);
- Quadrant q;
- q.pos = _map_to_world(p_qk.x * _get_quadrant_size(), p_qk.y * _get_quadrant_size());
- q.pos += get_cell_draw_offset();
- if (tile_origin == TILE_ORIGIN_CENTER) {
- q.pos += cell_size / 2;
- } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
- q.pos.y += cell_size.y;
- }
-
- xform.set_origin(q.pos);
- //q.canvas_item = RenderingServer::get_singleton()->canvas_item_create();
- if (!use_parent) {
- q.body = PhysicsServer2D::get_singleton()->body_create();
- PhysicsServer2D::get_singleton()->body_set_mode(q.body, use_kinematic ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC);
-
- PhysicsServer2D::get_singleton()->body_attach_object_instance_id(q.body, get_instance_id());
- PhysicsServer2D::get_singleton()->body_set_collision_layer(q.body, collision_layer);
- PhysicsServer2D::get_singleton()->body_set_collision_mask(q.body, collision_mask);
- PhysicsServer2D::get_singleton()->body_set_param(q.body, PhysicsServer2D::BODY_PARAM_FRICTION, friction);
- PhysicsServer2D::get_singleton()->body_set_param(q.body, PhysicsServer2D::BODY_PARAM_BOUNCE, bounce);
-
- if (is_inside_tree()) {
- xform = get_global_transform() * xform;
- RID space = get_world_2d()->get_space();
- PhysicsServer2D::get_singleton()->body_set_space(q.body, space);
- }
+Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(const Vector2i &p_qk) {
+ TileMapQuadrant q;
+ q.coords = p_qk;
- PhysicsServer2D::get_singleton()->body_set_state(q.body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
- } else if (collision_parent) {
- xform = get_transform() * xform;
- q.shape_owner_id = collision_parent->create_shape_owner(this);
- } else {
- q.shape_owner_id = -1;
+ rect_cache_dirty = true;
+
+ // Create the debug canvas item.
+ RenderingServer *rs = RenderingServer::get_singleton();
+ q.debug_canvas_item = rs->canvas_item_create();
+ rs->canvas_item_set_z_index(q.debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1);
+ rs->canvas_item_set_parent(q.debug_canvas_item, get_canvas_item());
+
+ // Call the create_quadrant method on plugins
+ if (tile_set.is_valid()) {
+ for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) {
+ tile_set->get_tile_set_atlas_plugins()[i]->create_quadrant(this, &q);
+ }
}
- rect_cache_dirty = true;
- quadrant_order_dirty = true;
return quadrant_map.insert(p_qk, q);
}
-void TileMap::_erase_quadrant(Map<PosKey, Quadrant>::Element *Q) {
- Quadrant &q = Q->get();
- if (!use_parent) {
- PhysicsServer2D::get_singleton()->free(q.body);
- } else if (collision_parent) {
- collision_parent->remove_shape_owner(q.shape_owner_id);
- }
+void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) {
+ // Remove a quadrant.
+ TileMapQuadrant *q = &(Q->get());
- for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) {
- RenderingServer::get_singleton()->free(E->get());
- }
- q.canvas_items.clear();
- if (q.dirty_list.in_list()) {
- dirty_quadrant_list.remove(&q.dirty_list);
+ // Call the cleanup_quadrant method on plugins.
+ if (tile_set.is_valid()) {
+ for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) {
+ tile_set->get_tile_set_atlas_plugins()[i]->cleanup_quadrant(this, q);
+ }
}
- for (Map<PosKey, Quadrant::NavPoly>::Element *E = q.navpoly_ids.front(); E; E = E->next()) {
- NavigationServer2D::get_singleton()->region_set_map(E->get().region, RID());
+ // Remove the quadrant from the dirty_list if it is there.
+ if (q->dirty_list_element.in_list()) {
+ dirty_quadrant_list.remove(&(q->dirty_list_element));
}
- q.navpoly_ids.clear();
- for (Map<PosKey, Quadrant::Occluder>::Element *E = q.occluder_instances.front(); E; E = E->next()) {
- RS::get_singleton()->free(E->get().id);
- }
- q.occluder_instances.clear();
+ // Free the debug canvas item.
+ RenderingServer *rs = RenderingServer::get_singleton();
+ rs->free(q->debug_canvas_item);
quadrant_map.erase(Q);
rect_cache_dirty = true;
}
-void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool update) {
- Quadrant &q = Q->get();
- if (!q.dirty_list.in_list()) {
- dirty_quadrant_list.add(&q.dirty_list);
+void TileMap::_make_all_quadrants_dirty(bool p_update) {
+ // Make all quandrants dirty, then trigger an update later.
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ if (!E->value().dirty_list_element.in_list()) {
+ dirty_quadrant_list.add(&E->value().dirty_list_element);
+ }
}
if (pending_update) {
@@ -788,39 +486,68 @@ void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool updat
if (!is_inside_tree()) {
return;
}
-
- if (update) {
+ if (p_update) {
call_deferred("update_dirty_quadrants");
}
}
-void TileMap::set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x, bool p_flip_y, bool p_transpose) {
- set_cell(p_pos.x, p_pos.y, p_tile, p_flip_x, p_flip_y, p_transpose);
-}
+void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q, bool p_update) {
+ // Make the given quadrant dirty, then trigger an update later.
+ TileMapQuadrant &q = Q->get();
+ if (!q.dirty_list_element.in_list()) {
+ dirty_quadrant_list.add(&q.dirty_list_element);
+ }
+
+ if (pending_update) {
+ return;
+ }
+ pending_update = true;
+ if (!is_inside_tree()) {
+ return;
+ }
-void TileMap::_set_celld(const Vector2 &p_pos, const Dictionary &p_data) {
- Variant v_pos_x = p_pos.x, v_pos_y = p_pos.y, v_tile = p_data["id"], v_flip_h = p_data["flip_h"], v_flip_v = p_data["flip_y"], v_transpose = p_data["transpose"], v_autotile_coord = p_data["auto_coord"];
- const Variant *args[7] = { &v_pos_x, &v_pos_y, &v_tile, &v_flip_h, &v_flip_v, &v_transpose, &v_autotile_coord };
- Callable::CallError ce;
- call("set_cell", args, 7, ce);
+ if (p_update) {
+ call_deferred("update_dirty_quadrants");
+ }
}
-void TileMap::set_cell(int p_x, int p_y, int p_tile, bool p_flip_x, bool p_flip_y, bool p_transpose, Vector2 p_autotile_coord) {
- PosKey pk(p_x, p_y);
+void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) {
+ // Set the current cell tile (using integer position).
+ Vector2i pk(p_coords);
+ Map<Vector2i, TileMapCell>::Element *E = tile_map.find(pk);
+
+ int source_id = p_source_id;
+ Vector2i atlas_coords = p_atlas_coords;
+ int alternative_tile = p_alternative_tile;
+
+ if ((source_id == -1 || atlas_coords == TileSetAtlasSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) &&
+ (source_id != -1 || atlas_coords != TileSetAtlasSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE)) {
+ WARN_PRINT("Setting a cell a cell as empty requires both source_id, atlas_coord and alternative_tile to be set to their respective \"invalid\" values. Values were thus changes accordingly.");
+ source_id = -1;
+ atlas_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS;
+ alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
+ }
- Map<PosKey, Cell>::Element *E = tile_map.find(pk);
- if (!E && p_tile == INVALID_CELL) {
- return; //nothing to do
+ if (!E && source_id == -1) {
+ return; // Nothing to do, the tile is already empty.
}
- PosKey qk = pk.to_quadrant(_get_quadrant_size());
- if (p_tile == INVALID_CELL) {
- //erase existing
+ // Get the quadrant
+ Vector2i qk = _coords_to_quadrant_coords(pk);
+
+ Map<Vector2i, TileMapQuadrant>::Element *Q = quadrant_map.find(qk);
+
+ if (source_id == -1) {
+ // Erase existing cell in the tile map.
tile_map.erase(pk);
- Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk);
+
+ // Erase existing cell in the quadrant.
ERR_FAIL_COND(!Q);
- Quadrant &q = Q->get();
+ TileMapQuadrant &q = Q->get();
+
q.cells.erase(pk);
+
+ // Remove or make the quadrant dirty.
if (q.cells.size() == 0) {
_erase_quadrant(Q);
} else {
@@ -828,331 +555,232 @@ void TileMap::set_cell(int p_x, int p_y, int p_tile, bool p_flip_x, bool p_flip_
}
used_size_cache_dirty = true;
- return;
- }
-
- Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk);
-
- if (!E) {
- E = tile_map.insert(pk, Cell());
- if (!Q) {
- Q = _create_quadrant(qk);
- }
- Quadrant &q = Q->get();
- q.cells.insert(pk);
} else {
- ERR_FAIL_COND(!Q); // quadrant should exist...
-
- if (E->get().id == p_tile && E->get().flip_h == p_flip_x && E->get().flip_v == p_flip_y && E->get().transpose == p_transpose && E->get().autotile_coord_x == (uint16_t)p_autotile_coord.x && E->get().autotile_coord_y == (uint16_t)p_autotile_coord.y) {
- return; //nothing changed
- }
- }
-
- Cell &c = E->get();
-
- c.id = p_tile;
- c.flip_h = p_flip_x;
- c.flip_v = p_flip_y;
- c.transpose = p_transpose;
- c.autotile_coord_x = (uint16_t)p_autotile_coord.x;
- c.autotile_coord_y = (uint16_t)p_autotile_coord.y;
-
- _make_quadrant_dirty(Q);
- used_size_cache_dirty = true;
-}
+ if (!E) {
+ // Insert a new cell in the tile map.
+ E = tile_map.insert(pk, TileMapCell());
-int TileMap::get_cellv(const Vector2 &p_pos) const {
- return get_cell(p_pos.x, p_pos.y);
-}
-
-void TileMap::make_bitmask_area_dirty(const Vector2 &p_pos) {
- for (int x = p_pos.x - 1; x <= p_pos.x + 1; x++) {
- for (int y = p_pos.y - 1; y <= p_pos.y + 1; y++) {
- PosKey p(x, y);
- if (dirty_bitmask.find(p) == nullptr) {
- dirty_bitmask.push_back(p);
- }
- }
- }
-}
-
-void TileMap::update_bitmask_area(const Vector2 &p_pos) {
- for (int x = p_pos.x - 1; x <= p_pos.x + 1; x++) {
- for (int y = p_pos.y - 1; y <= p_pos.y + 1; y++) {
- update_cell_bitmask(x, y);
- }
- }
-}
-
-void TileMap::update_bitmask_region(const Vector2 &p_start, const Vector2 &p_end) {
- if ((p_end.x < p_start.x || p_end.y < p_start.y) || (p_end.x == p_start.x && p_end.y == p_start.y)) {
- Array a = get_used_cells();
- for (int i = 0; i < a.size(); i++) {
- Vector2 vector = (Vector2)a[i];
- update_cell_bitmask(vector.x, vector.y);
- }
- return;
- }
- for (int x = p_start.x - 1; x <= p_end.x + 1; x++) {
- for (int y = p_start.y - 1; y <= p_end.y + 1; y++) {
- update_cell_bitmask(x, y);
- }
- }
-}
-
-void TileMap::update_cell_bitmask(int p_x, int p_y) {
- ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot update cell bitmask if Tileset is not open.");
- PosKey p(p_x, p_y);
- Map<PosKey, Cell>::Element *E = tile_map.find(p);
- if (E != nullptr) {
- int id = get_cell(p_x, p_y);
- if (tile_set->tile_get_tile_mode(id) == TileSet::AUTO_TILE) {
- uint16_t mask = 0;
- if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_2X2) {
- if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) {
- mask |= TileSet::BIND_TOPLEFT;
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
- mask |= TileSet::BIND_TOPRIGHT;
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) {
- mask |= TileSet::BIND_BOTTOMLEFT;
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
- mask |= TileSet::BIND_BOTTOMRIGHT;
- }
- } else {
- if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_3X3_MINIMAL) {
- if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) {
- mask |= TileSet::BIND_TOPLEFT;
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
- mask |= TileSet::BIND_TOPRIGHT;
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) {
- mask |= TileSet::BIND_BOTTOMLEFT;
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
- mask |= TileSet::BIND_BOTTOMRIGHT;
- }
- } else {
- if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1))) {
- mask |= TileSet::BIND_TOPLEFT;
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1))) {
- mask |= TileSet::BIND_TOPRIGHT;
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1))) {
- mask |= TileSet::BIND_BOTTOMLEFT;
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1))) {
- mask |= TileSet::BIND_BOTTOMRIGHT;
- }
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1))) {
- mask |= TileSet::BIND_TOP;
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) {
- mask |= TileSet::BIND_LEFT;
- }
- mask |= TileSet::BIND_CENTER;
- if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
- mask |= TileSet::BIND_RIGHT;
- }
- if (tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1))) {
- mask |= TileSet::BIND_BOTTOM;
- }
+ // Create a new quadrant if needed, then insert the cell if needed.
+ if (!Q) {
+ Q = _create_quadrant(qk);
}
- Vector2 coord = tile_set->autotile_get_subtile_for_bitmask(id, mask, this, Vector2(p_x, p_y));
- E->get().autotile_coord_x = (int)coord.x;
- E->get().autotile_coord_y = (int)coord.y;
-
- PosKey qk = p.to_quadrant(_get_quadrant_size());
- Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk);
- _make_quadrant_dirty(Q);
+ TileMapQuadrant &q = Q->get();
+ q.cells.insert(pk);
- } else if (tile_set->tile_get_tile_mode(id) == TileSet::SINGLE_TILE) {
- E->get().autotile_coord_x = 0;
- E->get().autotile_coord_y = 0;
- } else if (tile_set->tile_get_tile_mode(id) == TileSet::ATLAS_TILE) {
- if (tile_set->autotile_get_bitmask(id, Vector2(p_x, p_y)) == TileSet::BIND_CENTER) {
- Vector2 coord = tile_set->atlastile_get_subtile_by_priority(id, this, Vector2(p_x, p_y));
+ } else {
+ ERR_FAIL_COND(!Q); // TileMapQuadrant should exist...
- E->get().autotile_coord_x = (int)coord.x;
- E->get().autotile_coord_y = (int)coord.y;
+ if (E->get().source_id == source_id && E->get().get_atlas_coords() == atlas_coords && E->get().alternative_tile == alternative_tile) {
+ return; // Nothing changed.
}
}
- }
-}
-void TileMap::update_dirty_bitmask() {
- while (dirty_bitmask.size() > 0) {
- update_cell_bitmask(dirty_bitmask[0].x, dirty_bitmask[0].y);
- dirty_bitmask.pop_front();
- }
-}
+ TileMapCell &c = E->get();
-void TileMap::fix_invalid_tiles() {
- ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open.");
+ c.source_id = source_id;
+ c.set_atlas_coords(atlas_coords);
+ c.alternative_tile = alternative_tile;
- Map<PosKey, Cell> temp_tile_map = tile_map;
- for (Map<PosKey, Cell>::Element *E = temp_tile_map.front(); E; E = E->next()) {
- if (!tile_set->has_tile(get_cell(E->key().x, E->key().y))) {
- set_cell(E->key().x, E->key().y, INVALID_CELL);
- }
+ _make_quadrant_dirty(Q);
+ used_size_cache_dirty = true;
}
}
-int TileMap::get_cell(int p_x, int p_y) const {
- PosKey pk(p_x, p_y);
-
- const Map<PosKey, Cell>::Element *E = tile_map.find(pk);
+int TileMap::get_cell_source_id(const Vector2i &p_coords) const {
+ // Get a cell source id from position
+ const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords);
if (!E) {
- return INVALID_CELL;
+ return -1;
}
- return E->get().id;
+ return E->get().source_id;
}
-bool TileMap::is_cell_x_flipped(int p_x, int p_y) const {
- PosKey pk(p_x, p_y);
-
- const Map<PosKey, Cell>::Element *E = tile_map.find(pk);
+Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords) const {
+ // Get a cell source id from position
+ const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords);
if (!E) {
- return false;
+ return TileSetAtlasSource::INVALID_ATLAS_COORDS;
}
- return E->get().flip_h;
+ return E->get().get_atlas_coords();
}
-bool TileMap::is_cell_y_flipped(int p_x, int p_y) const {
- PosKey pk(p_x, p_y);
-
- const Map<PosKey, Cell>::Element *E = tile_map.find(pk);
+int TileMap::get_cell_alternative_tile(const Vector2i &p_coords) const {
+ // Get a cell source id from position
+ const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords);
if (!E) {
- return false;
+ return TileSetAtlasSource::INVALID_TILE_ALTERNATIVE;
}
- return E->get().flip_v;
+ return E->get().alternative_tile;
}
-bool TileMap::is_cell_transposed(int p_x, int p_y) const {
- PosKey pk(p_x, p_y);
+TileMapPattern *TileMap::get_pattern(TypedArray<Vector2i> p_coords_array) {
+ ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr);
- const Map<PosKey, Cell>::Element *E = tile_map.find(pk);
-
- if (!E) {
- return false;
+ TileMapPattern *output = memnew(TileMapPattern);
+ if (p_coords_array.is_empty()) {
+ return output;
}
- return E->get().transpose;
-}
-
-void TileMap::set_cell_autotile_coord(int p_x, int p_y, const Vector2 &p_coord) {
- PosKey pk(p_x, p_y);
+ Vector2i min = Vector2i(p_coords_array[0]);
+ for (int i = 1; i < p_coords_array.size(); i++) {
+ min = min.min(p_coords_array[i]);
+ }
- const Map<PosKey, Cell>::Element *E = tile_map.find(pk);
+ Vector<Vector2i> coords_in_pattern_array;
+ coords_in_pattern_array.resize(p_coords_array.size());
+ Vector2i ensure_positive_offset;
+ for (int i = 0; i < p_coords_array.size(); i++) {
+ Vector2i coords = p_coords_array[i];
+ Vector2i coords_in_pattern = coords - min;
+ 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(min.y % 2) && bool(coords_in_pattern.y % 2)) {
+ coords_in_pattern.x -= 1;
+ if (coords_in_pattern.x < 0) {
+ ensure_positive_offset.x = 1;
+ }
+ } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) {
+ coords_in_pattern.y -= 1;
+ if (coords_in_pattern.y < 0) {
+ ensure_positive_offset.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(min.y % 2) && bool(coords_in_pattern.y % 2)) {
+ coords_in_pattern.x += 1;
+ } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) {
+ coords_in_pattern.y += 1;
+ }
+ }
+ }
+ coords_in_pattern_array.write[i] = coords_in_pattern;
+ }
- if (!E) {
- return;
+ for (int i = 0; i < coords_in_pattern_array.size(); i++) {
+ Vector2i coords = p_coords_array[i];
+ Vector2i coords_in_pattern = coords_in_pattern_array[i];
+ output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(coords), get_cell_atlas_coords(coords), get_cell_alternative_tile(coords));
}
- Cell c = E->get();
- c.autotile_coord_x = p_coord.x;
- c.autotile_coord_y = p_coord.y;
- tile_map[pk] = c;
+ return output;
+}
- PosKey qk = pk.to_quadrant(_get_quadrant_size());
- Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk);
+Vector2i TileMap::map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, const TileMapPattern *p_pattern) {
+ ERR_FAIL_COND_V(!p_pattern->has_cell(p_coords_in_pattern), Vector2i());
- if (!Q) {
- return;
+ 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;
+ }
+ }
}
- _make_quadrant_dirty(Q);
+ return output;
}
-Vector2 TileMap::get_cell_autotile_coord(int p_x, int p_y) const {
- PosKey pk(p_x, p_y);
+void TileMap::set_pattern(Vector2i p_position, const TileMapPattern *p_pattern) {
+ ERR_FAIL_COND(!tile_set.is_valid());
- const Map<PosKey, Cell>::Element *E = tile_map.find(pk);
+ TypedArray<Vector2i> used_cells = p_pattern->get_used_cells();
+ for (int i = 0; i < used_cells.size(); i++) {
+ Vector2i coords = map_pattern(p_position, used_cells[i], p_pattern);
+ set_cell(coords, p_pattern->get_cell_source_id(coords), p_pattern->get_cell_atlas_coords(coords), p_pattern->get_cell_alternative_tile(coords));
+ }
+}
- if (!E) {
- return Vector2();
+TileMapCell TileMap::get_cell(const Vector2i &p_coords) const {
+ if (!tile_map.has(p_coords)) {
+ return TileMapCell();
+ } else {
+ return tile_map.find(p_coords)->get();
}
+}
- return Vector2(E->get().autotile_coord_x, E->get().autotile_coord_y);
+Map<Vector2i, TileMapQuadrant> &TileMap::get_quadrant_map() {
+ return quadrant_map;
+}
+
+void TileMap::fix_invalid_tiles() {
+ ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open.");
+ for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
+ TileSetSource *source = *tile_set->get_source(E->get().source_id);
+ if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) {
+ set_cell(E->key(), -1, TileSetAtlasSource::INVALID_ATLAS_COORDS, TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ }
+ }
}
void TileMap::_recreate_quadrants() {
+ // Clear then recreate all quadrants.
_clear_quadrants();
- for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) {
- PosKey qk = PosKey(E->key().x, E->key().y).to_quadrant(_get_quadrant_size());
+ for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
+ Vector2i qk = _coords_to_quadrant_coords(Vector2i(E->key().x, E->key().y));
- Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk);
+ Map<Vector2i, TileMapQuadrant>::Element *Q = quadrant_map.find(qk);
if (!Q) {
Q = _create_quadrant(qk);
- dirty_quadrant_list.add(&Q->get().dirty_list);
+ dirty_quadrant_list.add(&Q->get().dirty_list_element);
}
- Q->get().cells.insert(E->key());
+ Vector2i pk = E->key();
+ Q->get().cells.insert(pk);
+
_make_quadrant_dirty(Q, false);
}
+
update_dirty_quadrants();
}
void TileMap::_clear_quadrants() {
+ // Clear quadrants.
while (quadrant_map.size()) {
_erase_quadrant(quadrant_map.front());
}
-}
-void TileMap::set_material(const Ref<Material> &p_material) {
- CanvasItem::set_material(p_material);
- _update_all_items_material_state();
-}
-
-void TileMap::set_use_parent_material(bool p_use_parent_material) {
- CanvasItem::set_use_parent_material(p_use_parent_material);
- _update_all_items_material_state();
-}
-
-void TileMap::_update_all_items_material_state() {
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- for (List<RID>::Element *F = q.canvas_items.front(); F; F = F->next()) {
- _update_item_material_state(F->get());
- }
+ // Clear the dirty quadrants list.
+ while (dirty_quadrant_list.first()) {
+ dirty_quadrant_list.remove(dirty_quadrant_list.first());
}
}
-void TileMap::_update_item_material_state(const RID &p_canvas_item) {
- RS::get_singleton()->canvas_item_set_use_parent_material(p_canvas_item, get_use_parent_material() || get_material().is_valid());
-}
-
void TileMap::clear() {
+ // Remove all tiles.
_clear_quadrants();
tile_map.clear();
used_size_cache_dirty = true;
}
void TileMap::_set_tile_data(const Vector<int> &p_data) {
- ERR_FAIL_COND(format > FORMAT_2);
+ // Set data for a given tile from raw data.
+ ERR_FAIL_COND(format > FORMAT_3);
int c = p_data.size();
const int *r = p_data.ptr();
- int offset = (format == FORMAT_2) ? 3 : 2;
+ int offset = (format >= FORMAT_2) ? 3 : 2;
clear();
for (int i = 0; i < c; i += offset) {
const uint8_t *ptr = (const uint8_t *)&r[i];
uint8_t local[12];
- for (int j = 0; j < ((format == FORMAT_2) ? 12 : 8); j++) {
+ for (int j = 0; j < ((format >= FORMAT_2) ? 12 : 8); j++) {
local[j] = ptr[j];
}
@@ -1163,31 +791,49 @@ void TileMap::_set_tile_data(const Vector<int> &p_data) {
SWAP(local[4], local[7]);
SWAP(local[5], local[6]);
//TODO: ask someone to check this...
- if (FORMAT == FORMAT_2) {
+ if (FORMAT >= FORMAT_2) {
SWAP(local[8], local[11]);
SWAP(local[9], local[10]);
}
#endif
+ int16_t x = decode_uint16(&local[0]);
+ int16_t y = decode_uint16(&local[2]);
+
+ if (format == FORMAT_3) {
+ uint16_t source_id = decode_uint16(&local[4]);
+ uint16_t atlas_coords_x = decode_uint16(&local[6]);
+ uint16_t atlas_coords_y = decode_uint32(&local[8]);
+ uint16_t alternative_tile = decode_uint16(&local[10]);
+ set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile);
+ } else {
+ uint32_t v = decode_uint32(&local[4]);
+ v &= (1 << 29) - 1;
+
+ // We generate an alternative tile number out of the the flags
+ // An option should create the alternative in the tileset for compatibility
+ bool flip_h = v & (1 << 29);
+ bool flip_v = v & (1 << 30);
+ bool transpose = v & (1 << 31);
+ int16_t coord_x = 0;
+ int16_t coord_y = 0;
+ if (format == FORMAT_2) {
+ coord_x = decode_uint16(&local[8]);
+ coord_y = decode_uint16(&local[10]);
+ }
- uint16_t x = decode_uint16(&local[0]);
- uint16_t y = decode_uint16(&local[2]);
- uint32_t v = decode_uint32(&local[4]);
- bool flip_h = v & (1 << 29);
- bool flip_v = v & (1 << 30);
- bool transpose = v & (1 << 31);
- v &= (1 << 29) - 1;
- int16_t coord_x = 0;
- int16_t coord_y = 0;
- if (format == FORMAT_2) {
- coord_x = decode_uint16(&local[8]);
- coord_y = decode_uint16(&local[10]);
- }
+ int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2);
+
+ if (tile_set.is_valid()) {
+ v = tile_set->compatibility_get_source_for_tile_id(v);
+ }
- set_cell(x, y, v, flip_h, flip_v, transpose, Vector2(coord_x, coord_y));
+ set_cell(Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile);
+ }
}
}
Vector<int> TileMap::_get_tile_data() const {
+ // Export tile data to raw format
Vector<int> data;
data.resize(tile_map.size() * 3);
int *w = data.ptrw();
@@ -1195,23 +841,14 @@ Vector<int> TileMap::_get_tile_data() const {
// Save in highest format
int idx = 0;
- for (const Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) {
+ for (const Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
uint8_t *ptr = (uint8_t *)&w[idx];
- encode_uint16(E->key().x, &ptr[0]);
- encode_uint16(E->key().y, &ptr[2]);
- uint32_t val = E->get().id;
- if (E->get().flip_h) {
- val |= (1 << 29);
- }
- if (E->get().flip_v) {
- val |= (1 << 30);
- }
- if (E->get().transpose) {
- val |= (1 << 31);
- }
- encode_uint32(val, &ptr[4]);
- encode_uint16(E->get().autotile_coord_x, &ptr[8]);
- encode_uint16(E->get().autotile_coord_y, &ptr[10]);
+ encode_uint16((int16_t)(E->key().x), &ptr[0]);
+ encode_uint16((int16_t)(E->key().y), &ptr[2]);
+ encode_uint16(E->get().source_id, &ptr[4]);
+ encode_uint16(E->get().coord_x, &ptr[6]);
+ encode_uint16(E->get().coord_y, &ptr[8]);
+ encode_uint16(E->get().alternative_tile, &ptr[10]);
idx += 3;
}
@@ -1220,6 +857,7 @@ Vector<int> TileMap::_get_tile_data() const {
#ifdef TOOLS_ENABLED
Rect2 TileMap::_edit_get_rect() const {
+ // Return the visible rect of the tilemap
if (pending_update) {
const_cast<TileMap *>(this)->update_dirty_quadrants();
} else {
@@ -1229,255 +867,6 @@ Rect2 TileMap::_edit_get_rect() const {
}
#endif
-void TileMap::set_collision_layer(uint32_t p_layer) {
- collision_layer = p_layer;
- if (!use_parent) {
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- PhysicsServer2D::get_singleton()->body_set_collision_layer(q.body, collision_layer);
- }
- }
-}
-
-void TileMap::set_collision_mask(uint32_t p_mask) {
- collision_mask = p_mask;
- if (!use_parent) {
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- PhysicsServer2D::get_singleton()->body_set_collision_mask(q.body, collision_mask);
- }
- }
-}
-
-void TileMap::set_collision_layer_bit(int p_bit, bool p_value) {
- ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive.");
- uint32_t layer = get_collision_layer();
- if (p_value) {
- layer |= 1 << p_bit;
- } else {
- layer &= ~(1 << p_bit);
- }
- set_collision_layer(layer);
-}
-
-void TileMap::set_collision_mask_bit(int p_bit, bool p_value) {
- ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive.");
- uint32_t mask = get_collision_mask();
- if (p_value) {
- mask |= 1 << p_bit;
- } else {
- mask &= ~(1 << p_bit);
- }
- set_collision_mask(mask);
-}
-
-bool TileMap::get_collision_use_kinematic() const {
- return use_kinematic;
-}
-
-void TileMap::set_collision_use_kinematic(bool p_use_kinematic) {
- _clear_quadrants();
- use_kinematic = p_use_kinematic;
- _recreate_quadrants();
-}
-
-bool TileMap::get_collision_use_parent() const {
- return use_parent;
-}
-
-void TileMap::set_collision_use_parent(bool p_use_parent) {
- if (use_parent == p_use_parent) {
- return;
- }
-
- _clear_quadrants();
-
- use_parent = p_use_parent;
- set_notify_local_transform(use_parent);
-
- if (use_parent && is_inside_tree()) {
- collision_parent = Object::cast_to<CollisionObject2D>(get_parent());
- } else {
- collision_parent = nullptr;
- }
-
- _recreate_quadrants();
- notify_property_list_changed();
- update_configuration_warnings();
-}
-
-void TileMap::set_collision_friction(float p_friction) {
- friction = p_friction;
- if (!use_parent) {
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- PhysicsServer2D::get_singleton()->body_set_param(q.body, PhysicsServer2D::BODY_PARAM_FRICTION, p_friction);
- }
- }
-}
-
-float TileMap::get_collision_friction() const {
- return friction;
-}
-
-void TileMap::set_collision_bounce(float p_bounce) {
- bounce = p_bounce;
- if (!use_parent) {
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- PhysicsServer2D::get_singleton()->body_set_param(q.body, PhysicsServer2D::BODY_PARAM_BOUNCE, p_bounce);
- }
- }
-}
-
-float TileMap::get_collision_bounce() const {
- return bounce;
-}
-
-void TileMap::set_bake_navigation(bool p_bake_navigation) {
- bake_navigation = p_bake_navigation;
- for (Map<PosKey, Quadrant>::Element *F = quadrant_map.front(); F; F = F->next()) {
- _make_quadrant_dirty(F);
- }
-}
-
-bool TileMap::is_baking_navigation() {
- return bake_navigation;
-}
-
-uint32_t TileMap::get_collision_layer() const {
- return collision_layer;
-}
-
-uint32_t TileMap::get_collision_mask() const {
- return collision_mask;
-}
-
-bool TileMap::get_collision_layer_bit(int p_bit) const {
- ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive.");
- return get_collision_layer() & (1 << p_bit);
-}
-
-bool TileMap::get_collision_mask_bit(int p_bit) const {
- ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive.");
- return get_collision_mask() & (1 << p_bit);
-}
-
-void TileMap::set_mode(Mode p_mode) {
- _clear_quadrants();
- mode = p_mode;
- _recreate_quadrants();
- emit_signal("settings_changed");
-}
-
-TileMap::Mode TileMap::get_mode() const {
- return mode;
-}
-
-void TileMap::set_half_offset(HalfOffset p_half_offset) {
- _clear_quadrants();
- half_offset = p_half_offset;
- _recreate_quadrants();
- emit_signal("settings_changed");
-}
-
-void TileMap::set_tile_origin(TileOrigin p_tile_origin) {
- _clear_quadrants();
- tile_origin = p_tile_origin;
- _recreate_quadrants();
- emit_signal("settings_changed");
-}
-
-TileMap::TileOrigin TileMap::get_tile_origin() const {
- return tile_origin;
-}
-
-Vector2 TileMap::get_cell_draw_offset() const {
- switch (mode) {
- case MODE_SQUARE: {
- return Vector2();
- } break;
- case MODE_ISOMETRIC: {
- return Vector2(-cell_size.x * 0.5, 0);
-
- } break;
- case MODE_CUSTOM: {
- Vector2 min;
- min.x = MIN(custom_transform[0].x, min.x);
- min.y = MIN(custom_transform[0].y, min.y);
- min.x = MIN(custom_transform[1].x, min.x);
- min.y = MIN(custom_transform[1].y, min.y);
- return min;
- } break;
- }
-
- return Vector2();
-}
-
-TileMap::HalfOffset TileMap::get_half_offset() const {
- return half_offset;
-}
-
-Transform2D TileMap::get_cell_transform() const {
- switch (mode) {
- case MODE_SQUARE: {
- Transform2D m;
- m[0] *= cell_size.x;
- m[1] *= cell_size.y;
- return m;
- } break;
- case MODE_ISOMETRIC: {
- //isometric only makes sense when y is positive in both x and y vectors, otherwise
- //the drawing of tiles will overlap
- Transform2D m;
- m[0] = Vector2(cell_size.x * 0.5, cell_size.y * 0.5);
- m[1] = Vector2(-cell_size.x * 0.5, cell_size.y * 0.5);
- return m;
-
- } break;
- case MODE_CUSTOM: {
- return custom_transform;
- } break;
- }
-
- return Transform2D();
-}
-
-void TileMap::set_custom_transform(const Transform2D &p_xform) {
- _clear_quadrants();
- custom_transform = p_xform;
- _recreate_quadrants();
- emit_signal("settings_changed");
-}
-
-Transform2D TileMap::get_custom_transform() const {
- return custom_transform;
-}
-
-Vector2 TileMap::_map_to_world(int p_x, int p_y, bool p_ignore_ofs) const {
- Vector2 ret = get_cell_transform().xform(Vector2(p_x, p_y));
- if (!p_ignore_ofs) {
- switch (half_offset) {
- case HALF_OFFSET_X:
- case HALF_OFFSET_NEGATIVE_X: {
- if (ABS(p_y) & 1) {
- ret += get_cell_transform()[0] * (half_offset == HALF_OFFSET_X ? 0.5 : -0.5);
- }
- } break;
- case HALF_OFFSET_Y:
- case HALF_OFFSET_NEGATIVE_Y: {
- if (ABS(p_x) & 1) {
- ret += get_cell_transform()[1] * (half_offset == HALF_OFFSET_Y ? 0.5 : -0.5);
- }
- } break;
- case HALF_OFFSET_DISABLED: {
- // Nothing to do.
- }
- }
- }
- return ret;
-}
-
bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "format") {
if (p_value.get_type() == Variant::INT) {
@@ -1496,7 +885,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
bool TileMap::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "format") {
- r_ret = FORMAT_2; // When saving, always save highest format
+ r_ret = FORMAT_3; // When saving, always save highest format
return true;
} else if (p_name == "tile_data") {
r_ret = _get_tile_data();
@@ -1513,93 +902,632 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(p);
}
-void TileMap::_validate_property(PropertyInfo &property) const {
- if (use_parent && property.name != "collision_use_parent" && property.name.begins_with("collision_")) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+Vector2 TileMap::map_to_world(const Vector2 &p_pos) const {
+ // SHOULD RETURN THE CENTER OF THE TILE
+ 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;
+ }
+ }
}
-}
-Vector2 TileMap::map_to_world(const Vector2 &p_pos, bool p_ignore_ofs) const {
- return _map_to_world(p_pos.x, p_pos.y, p_ignore_ofs);
+ // 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();
}
-Vector2 TileMap::world_to_map(const Vector2 &p_pos) const {
- Vector2 ret = get_cell_transform().affine_inverse().xform(p_pos);
+Vector2i TileMap::world_to_map(const Vector2 &p_pos) const {
+ ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2());
+
+ Vector2 ret = p_pos;
+ ret /= tile_set->get_tile_size();
- // Account for precision errors on the border (GH-23250).
- // 0.00005 is 5*CMP_EPSILON, results would start being unpredictable if
- // cell size is > 15,000, but we can hardly have more precision anyway with
- // floating point.
- ret += Vector2(0.00005, 0.00005);
+ 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();
- switch (half_offset) {
- case HALF_OFFSET_X: {
- if (int(floor(ret.y)) & 1) {
- ret.x -= 0.5;
+ // 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 world 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();
}
- } break;
- case HALF_OFFSET_NEGATIVE_X: {
- if (int(floor(ret.y)) & 1) {
- ret.x += 0.5;
+
+ // Compute the tile offset, and if we might the output for a neighbour 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;
}
- } break;
- case HALF_OFFSET_Y: {
- if (int(floor(ret.x)) & 1) {
- ret.y -= 0.5;
+ } 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();
}
- } break;
- case HALF_OFFSET_NEGATIVE_Y: {
- if (int(floor(ret.x)) & 1) {
- ret.y += 0.5;
+
+ // Compute the tile offset, and if we might the output for a neighbour 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;
}
- } break;
- case HALF_OFFSET_DISABLED: {
- // Nothing to do.
}
+ } else {
+ ret = (ret + Vector2(0.00005, 0.00005)).floor();
}
-
- return ret.floor();
+ return ret;
}
-void TileMap::set_y_sort_enabled(bool p_enable) {
- _clear_quadrants();
- use_y_sort = p_enable;
- RS::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), use_y_sort);
- _recreate_quadrants();
- emit_signal("settings_changed");
+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;
+ }
+ }
}
-bool TileMap::is_y_sort_enabled() const {
- return use_y_sort;
-}
+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);
+ }
-void TileMap::set_compatibility_mode(bool p_enable) {
- _clear_quadrants();
- compatibility_mode = p_enable;
- _recreate_quadrants();
- emit_signal("settings_changed");
-}
+ } 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);
+ }
-bool TileMap::is_compatibility_mode_enabled() const {
- return compatibility_mode;
-}
+ } 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);
+ }
-void TileMap::set_centered_textures(bool p_enable) {
- _clear_quadrants();
- centered_textures = p_enable;
- _recreate_quadrants();
- emit_signal("settings_changed");
-}
+ } 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);
+ }
-bool TileMap::is_centered_textures_enabled() const {
- return centered_textures;
+ } 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);
+ }
+ }
}
TypedArray<Vector2i> TileMap::get_used_cells() const {
+ // Returns the cells used in the tilemap.
TypedArray<Vector2i> a;
a.resize(tile_map.size());
int i = 0;
- for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) {
+ for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
Vector2i p(E->key().x, E->key().y);
a[i++] = p;
}
@@ -1607,25 +1535,13 @@ TypedArray<Vector2i> TileMap::get_used_cells() const {
return a;
}
-TypedArray<Vector2i> TileMap::get_used_cells_by_index(int p_id) const {
- TypedArray<Vector2i> a;
- for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) {
- if (E->value().id == p_id) {
- Vector2i p(E->key().x, E->key().y);
- a.push_back(p);
- }
- }
-
- return a;
-}
-
Rect2 TileMap::get_used_rect() { // Not const because of cache
-
+ // Return the rect of the currently used area
if (used_size_cache_dirty) {
if (tile_map.size() > 0) {
used_size_cache = Rect2(tile_map.front()->key().x, tile_map.front()->key().y, 0, 0);
- for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) {
+ for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
used_size_cache.expand_to(Vector2(E->key().x, E->key().y));
}
@@ -1640,46 +1556,49 @@ Rect2 TileMap::get_used_rect() { // Not const because of cache
return used_size_cache;
}
-void TileMap::set_occluder_light_mask(int p_mask) {
- occluder_light_mask = p_mask;
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- for (Map<PosKey, Quadrant::Occluder>::Element *F = E->get().occluder_instances.front(); F; F = F->next()) {
- RenderingServer::get_singleton()->canvas_light_occluder_set_light_mask(F->get().id, occluder_light_mask);
- }
- }
-}
-
-int TileMap::get_occluder_light_mask() const {
- return occluder_light_mask;
-}
+// --- Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems ---
void TileMap::set_light_mask(int p_light_mask) {
+ // Occlusion: set light mask.
CanvasItem::set_light_mask(p_light_mask);
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
for (List<RID>::Element *F = E->get().canvas_items.front(); F; F = F->next()) {
RenderingServer::get_singleton()->canvas_item_set_light_mask(F->get(), get_light_mask());
}
}
}
-void TileMap::set_clip_uv(bool p_enable) {
- if (clip_uv == p_enable) {
- return;
- }
+void TileMap::set_material(const Ref<Material> &p_material) {
+ // Set material for the whole tilemap.
+ CanvasItem::set_material(p_material);
- _clear_quadrants();
- clip_uv = p_enable;
- _recreate_quadrants();
+ // Update material for the whole tilemap.
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ TileMapQuadrant &q = E->get();
+ for (List<RID>::Element *F = q.canvas_items.front(); F; F = F->next()) {
+ RS::get_singleton()->canvas_item_set_use_parent_material(F->get(), get_use_parent_material() || get_material().is_valid());
+ }
+ }
}
-bool TileMap::get_clip_uv() const {
- return clip_uv;
+void TileMap::set_use_parent_material(bool p_use_parent_material) {
+ // Set use_parent_material for the whole tilemap.
+ CanvasItem::set_use_parent_material(p_use_parent_material);
+
+ // Update use_parent_material for the whole tilemap.
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ TileMapQuadrant &q = E->get();
+ for (List<RID>::Element *F = q.canvas_items.front(); F; F = F->next()) {
+ RS::get_singleton()->canvas_item_set_use_parent_material(F->get(), get_use_parent_material() || get_material().is_valid());
+ }
+ }
}
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 (Map<PosKey, Quadrant>::Element *F = quadrant_map.front(); F; F = F->next()) {
- Quadrant &q = F->get();
+ for (Map<Vector2i, TileMapQuadrant>::Element *F = quadrant_map.front(); F; F = F->next()) {
+ TileMapQuadrant &q = F->get();
for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) {
RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(E->get(), RS::CanvasItemTextureFilter(p_texture_filter));
_make_quadrant_dirty(F);
@@ -1688,9 +1607,10 @@ 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 (Map<PosKey, Quadrant>::Element *F = quadrant_map.front(); F; F = F->next()) {
- Quadrant &q = F->get();
+ for (Map<Vector2i, TileMapQuadrant>::Element *F = quadrant_map.front(); F; F = F->next()) {
+ TileMapQuadrant &q = F->get();
for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) {
RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(E->get(), RS::CanvasItemTextureRepeat(p_texture_repeat));
_make_quadrant_dirty(F);
@@ -1698,167 +1618,153 @@ void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) {
}
}
-TypedArray<String> TileMap::get_configuration_warnings() const {
- TypedArray<String> warnings = Node::get_configuration_warnings();
+TypedArray<Vector2i> TileMap::get_surrounding_tiles(Vector2i coords) {
+ if (!tile_set.is_valid()) {
+ return TypedArray<Vector2i>();
+ }
- if (use_parent && !collision_parent) {
- warnings.push_back(TTR("TileMap with Use Parent on needs a parent CollisionObject2D to give shapes to. Please use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."));
+ TypedArray<Vector2i> around;
+ TileSet::TileShape shape = tile_set->get_tile_shape();
+ if (shape == TileSet::TILE_SHAPE_SQUARE) {
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_SIDE));
+ } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE));
+ } else {
+ if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE));
+ } else {
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_SIDE));
+ around.push_back(get_neighbor_cell(coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE));
+ }
}
- return warnings;
+ return around;
}
-void TileMap::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMap::set_tileset);
- ClassDB::bind_method(D_METHOD("get_tileset"), &TileMap::get_tileset);
-
- ClassDB::bind_method(D_METHOD("set_mode", "mode"), &TileMap::set_mode);
- ClassDB::bind_method(D_METHOD("get_mode"), &TileMap::get_mode);
+void TileMap::draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform) {
+ if (!tile_set.is_valid()) {
+ return;
+ }
- ClassDB::bind_method(D_METHOD("set_half_offset", "half_offset"), &TileMap::set_half_offset);
- ClassDB::bind_method(D_METHOD("get_half_offset"), &TileMap::get_half_offset);
+ // Create a set.
+ Vector2i tile_size = tile_set->get_tile_size();
+ Vector<Vector2> uvs;
- ClassDB::bind_method(D_METHOD("set_custom_transform", "custom_transform"), &TileMap::set_custom_transform);
- ClassDB::bind_method(D_METHOD("get_custom_transform"), &TileMap::get_custom_transform);
+ if (tile_set->get_tile_shape() == TileSet::TILE_SHAPE_SQUARE) {
+ uvs.append(Vector2(1.0, 0.0));
+ uvs.append(Vector2(1.0, 1.0));
+ uvs.append(Vector2(0.0, 1.0));
+ uvs.append(Vector2(0.0, 0.0));
+ } else {
+ float overlap = 0.0;
+ switch (tile_set->get_tile_shape()) {
+ case TileSet::TILE_SHAPE_ISOMETRIC:
+ overlap = 0.5;
+ break;
+ case TileSet::TILE_SHAPE_HEXAGON:
+ overlap = 0.25;
+ break;
+ case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE:
+ overlap = 0.0;
+ break;
+ default:
+ break;
+ }
+ uvs.append(Vector2(1.0, overlap));
+ uvs.append(Vector2(1.0, 1.0 - overlap));
+ uvs.append(Vector2(0.5, 1.0));
+ uvs.append(Vector2(0.0, 1.0 - overlap));
+ uvs.append(Vector2(0.0, overlap));
+ uvs.append(Vector2(0.5, 0.0));
+ if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ for (int i = 0; i < uvs.size(); i++) {
+ uvs.write[i] = Vector2(uvs[i].y, uvs[i].x);
+ }
+ }
+ }
- ClassDB::bind_method(D_METHOD("set_cell_size", "size"), &TileMap::set_cell_size);
- ClassDB::bind_method(D_METHOD("get_cell_size"), &TileMap::get_cell_size);
+ for (Set<Vector2i>::Element *E = p_cells.front(); E; E = E->next()) {
+ Vector2 top_left = map_to_world(E->get()) - tile_size / 2;
+ TypedArray<Vector2i> surrounding_tiles = get_surrounding_tiles(E->get());
+ for (int i = 0; i < surrounding_tiles.size(); i++) {
+ if (!p_cells.has(surrounding_tiles[i])) {
+ p_control->draw_line(p_transform.xform(top_left + uvs[i] * tile_size), p_transform.xform(top_left + uvs[(i + 1) % uvs.size()] * tile_size), p_color);
+ }
+ }
+ }
+}
- ClassDB::bind_method(D_METHOD("_set_old_cell_size", "size"), &TileMap::_set_old_cell_size);
- ClassDB::bind_method(D_METHOD("_get_old_cell_size"), &TileMap::_get_old_cell_size);
+void TileMap::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMap::set_tileset);
+ ClassDB::bind_method(D_METHOD("get_tileset"), &TileMap::get_tileset);
ClassDB::bind_method(D_METHOD("set_quadrant_size", "size"), &TileMap::set_quadrant_size);
ClassDB::bind_method(D_METHOD("get_quadrant_size"), &TileMap::get_quadrant_size);
- ClassDB::bind_method(D_METHOD("set_tile_origin", "origin"), &TileMap::set_tile_origin);
- ClassDB::bind_method(D_METHOD("get_tile_origin"), &TileMap::get_tile_origin);
-
- ClassDB::bind_method(D_METHOD("set_clip_uv", "enable"), &TileMap::set_clip_uv);
- ClassDB::bind_method(D_METHOD("get_clip_uv"), &TileMap::get_clip_uv);
-
- ClassDB::bind_method(D_METHOD("set_y_sort_enabled", "enable"), &TileMap::set_y_sort_enabled);
- ClassDB::bind_method(D_METHOD("is_y_sort_enabled"), &TileMap::is_y_sort_enabled);
-
- ClassDB::bind_method(D_METHOD("set_compatibility_mode", "enable"), &TileMap::set_compatibility_mode);
- ClassDB::bind_method(D_METHOD("is_compatibility_mode_enabled"), &TileMap::is_compatibility_mode_enabled);
-
- ClassDB::bind_method(D_METHOD("set_centered_textures", "enable"), &TileMap::set_centered_textures);
- ClassDB::bind_method(D_METHOD("is_centered_textures_enabled"), &TileMap::is_centered_textures_enabled);
-
- ClassDB::bind_method(D_METHOD("set_collision_use_kinematic", "use_kinematic"), &TileMap::set_collision_use_kinematic);
- ClassDB::bind_method(D_METHOD("get_collision_use_kinematic"), &TileMap::get_collision_use_kinematic);
-
- ClassDB::bind_method(D_METHOD("set_collision_use_parent", "use_parent"), &TileMap::set_collision_use_parent);
- ClassDB::bind_method(D_METHOD("get_collision_use_parent"), &TileMap::get_collision_use_parent);
-
- ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &TileMap::set_collision_layer);
- ClassDB::bind_method(D_METHOD("get_collision_layer"), &TileMap::get_collision_layer);
-
- ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &TileMap::set_collision_mask);
- ClassDB::bind_method(D_METHOD("get_collision_mask"), &TileMap::get_collision_mask);
-
- ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &TileMap::set_collision_layer_bit);
- ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &TileMap::get_collision_layer_bit);
-
- ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &TileMap::set_collision_mask_bit);
- ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &TileMap::get_collision_mask_bit);
-
- ClassDB::bind_method(D_METHOD("set_collision_friction", "value"), &TileMap::set_collision_friction);
- ClassDB::bind_method(D_METHOD("get_collision_friction"), &TileMap::get_collision_friction);
-
- ClassDB::bind_method(D_METHOD("set_collision_bounce", "value"), &TileMap::set_collision_bounce);
- ClassDB::bind_method(D_METHOD("get_collision_bounce"), &TileMap::get_collision_bounce);
-
- ClassDB::bind_method(D_METHOD("set_bake_navigation", "bake_navigation"), &TileMap::set_bake_navigation);
- ClassDB::bind_method(D_METHOD("is_baking_navigation"), &TileMap::is_baking_navigation);
-
- ClassDB::bind_method(D_METHOD("set_occluder_light_mask", "mask"), &TileMap::set_occluder_light_mask);
- ClassDB::bind_method(D_METHOD("get_occluder_light_mask"), &TileMap::get_occluder_light_mask);
-
- ClassDB::bind_method(D_METHOD("set_cell", "x", "y", "tile", "flip_x", "flip_y", "transpose", "autotile_coord"), &TileMap::set_cell, DEFVAL(false), DEFVAL(false), DEFVAL(false), DEFVAL(Vector2()));
- ClassDB::bind_method(D_METHOD("set_cellv", "position", "tile", "flip_x", "flip_y", "transpose"), &TileMap::set_cellv, DEFVAL(false), DEFVAL(false), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("_set_celld", "position", "data"), &TileMap::_set_celld);
- ClassDB::bind_method(D_METHOD("get_cell", "x", "y"), &TileMap::get_cell);
- ClassDB::bind_method(D_METHOD("get_cellv", "position"), &TileMap::get_cellv);
- ClassDB::bind_method(D_METHOD("is_cell_x_flipped", "x", "y"), &TileMap::is_cell_x_flipped);
- ClassDB::bind_method(D_METHOD("is_cell_y_flipped", "x", "y"), &TileMap::is_cell_y_flipped);
- ClassDB::bind_method(D_METHOD("is_cell_transposed", "x", "y"), &TileMap::is_cell_transposed);
-
- ClassDB::bind_method(D_METHOD("get_cell_autotile_coord", "x", "y"), &TileMap::get_cell_autotile_coord);
+ ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(-1), DEFVAL(TileSetAtlasSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetAtlasSource::INVALID_TILE_ALTERNATIVE));
+ ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMap::get_cell_source_id);
+ ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMap::get_cell_atlas_coords);
+ ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMap::get_cell_alternative_tile);
ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles);
+ ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles);
ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear);
ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMap::get_used_cells);
- ClassDB::bind_method(D_METHOD("get_used_cells_by_index", "index"), &TileMap::get_used_cells_by_index);
ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect);
- ClassDB::bind_method(D_METHOD("map_to_world", "map_position", "ignore_half_ofs"), &TileMap::map_to_world, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("map_to_world", "map_position"), &TileMap::map_to_world);
ClassDB::bind_method(D_METHOD("world_to_map", "world_position"), &TileMap::world_to_map);
- ClassDB::bind_method(D_METHOD("_clear_quadrants"), &TileMap::_clear_quadrants);
- ClassDB::bind_method(D_METHOD("update_dirty_quadrants"), &TileMap::update_dirty_quadrants);
+ ClassDB::bind_method(D_METHOD("get_neighbor_cell", "coords", "neighbor"), &TileMap::get_neighbor_cell);
- ClassDB::bind_method(D_METHOD("update_bitmask_area", "position"), &TileMap::update_bitmask_area);
- ClassDB::bind_method(D_METHOD("update_bitmask_region", "start", "end"), &TileMap::update_bitmask_region, DEFVAL(Vector2()), DEFVAL(Vector2()));
+ ClassDB::bind_method(D_METHOD("update_dirty_quadrants"), &TileMap::update_dirty_quadrants);
ClassDB::bind_method(D_METHOD("_set_tile_data"), &TileMap::_set_tile_data);
ClassDB::bind_method(D_METHOD("_get_tile_data"), &TileMap::_get_tile_data);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Square,Isometric,Custom"), "set_mode", "get_mode");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset");
-
- ADD_GROUP("Cell", "cell_");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cell_size", PROPERTY_HINT_RANGE, "1,8192,1"), "set_cell_size", "get_cell_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size");
- ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "cell_custom_transform"), "set_custom_transform", "get_custom_transform");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_half_offset", PROPERTY_HINT_ENUM, "Offset X,Offset Y,Disabled,Offset Negative X,Offset Negative Y"), "set_half_offset", "get_half_offset");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_tile_origin", PROPERTY_HINT_ENUM, "Top Left,Center,Bottom Left"), "set_tile_origin", "get_tile_origin");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_y_sort"), "set_y_sort_enabled", "is_y_sort_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "compatibility_mode"), "set_compatibility_mode", "is_compatibility_mode_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered_textures"), "set_centered_textures", "is_centered_textures_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_clip_uv"), "set_clip_uv", "get_clip_uv");
-
- ADD_GROUP("Collision", "collision_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_use_parent", PROPERTY_HINT_NONE, ""), "set_collision_use_parent", "get_collision_use_parent");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_use_kinematic", PROPERTY_HINT_NONE, ""), "set_collision_use_kinematic", "get_collision_use_kinematic");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_friction", "get_collision_friction");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_bounce", "get_collision_bounce");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
-
- ADD_GROUP("Occluder", "occluder_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "occluder_light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask");
-
- ADD_GROUP("Navigation", "");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bake_navigation"), "set_bake_navigation", "is_baking_navigation");
ADD_PROPERTY_DEFAULT("format", FORMAT_1);
- ADD_SIGNAL(MethodInfo("settings_changed"));
-
- BIND_CONSTANT(INVALID_CELL);
-
- BIND_ENUM_CONSTANT(MODE_SQUARE);
- BIND_ENUM_CONSTANT(MODE_ISOMETRIC);
- BIND_ENUM_CONSTANT(MODE_CUSTOM);
-
- BIND_ENUM_CONSTANT(HALF_OFFSET_X);
- BIND_ENUM_CONSTANT(HALF_OFFSET_Y);
- BIND_ENUM_CONSTANT(HALF_OFFSET_DISABLED);
- BIND_ENUM_CONSTANT(HALF_OFFSET_NEGATIVE_X);
- BIND_ENUM_CONSTANT(HALF_OFFSET_NEGATIVE_Y);
+ ADD_SIGNAL(MethodInfo("changed"));
+}
- BIND_ENUM_CONSTANT(TILE_ORIGIN_TOP_LEFT);
- BIND_ENUM_CONSTANT(TILE_ORIGIN_CENTER);
- BIND_ENUM_CONSTANT(TILE_ORIGIN_BOTTOM_LEFT);
+void TileMap::_tile_set_changed() {
+ emit_signal("changed");
+ _make_all_quadrants_dirty(true);
}
TileMap::TileMap() {
+ rect_cache_dirty = true;
+ used_size_cache_dirty = true;
+ pending_update = false;
+ quadrant_size = 16;
+ format = FORMAT_1; // Assume lowest possible format if none is present
+
set_notify_transform(true);
set_notify_local_transform(false);
}
TileMap::~TileMap() {
- clear();
+ if (tile_set.is_valid()) {
+ tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed));
+ }
+ _clear_quadrants();
}
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 9d27053fee..704897da15 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -34,193 +34,194 @@
#include "core/templates/self_list.h"
#include "core/templates/vset.h"
#include "scene/2d/node_2d.h"
+#include "scene/gui/control.h"
#include "scene/resources/tile_set.h"
-class CollisionObject2D;
+class TileSetAtlasSource;
-class TileMap : public Node2D {
- GDCLASS(TileMap, Node2D);
-
-public:
- enum Mode {
- MODE_SQUARE,
- MODE_ISOMETRIC,
- MODE_CUSTOM
+union TileMapCell {
+ struct {
+ int32_t source_id : 16;
+ int16_t coord_x : 16;
+ int16_t coord_y : 16;
+ int32_t alternative_tile : 16;
};
- enum HalfOffset {
- HALF_OFFSET_X,
- HALF_OFFSET_Y,
- HALF_OFFSET_DISABLED,
- HALF_OFFSET_NEGATIVE_X,
- HALF_OFFSET_NEGATIVE_Y,
- };
+ uint64_t _u64t;
+ TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ source_id = p_source_id;
+ set_atlas_coords(p_atlas_coords);
+ alternative_tile = p_alternative_tile;
+ }
+
+ Vector2i get_atlas_coords() const {
+ return Vector2i(coord_x, coord_y);
+ }
+
+ void set_atlas_coords(const Vector2i &r_coords) {
+ coord_x = r_coords.x;
+ coord_y = r_coords.y;
+ }
+
+ bool operator<(const TileMapCell &p_other) const {
+ if (source_id == p_other.source_id) {
+ if (coord_x == p_other.coord_x) {
+ if (coord_y == p_other.coord_y) {
+ return alternative_tile < p_other.alternative_tile;
+ } else {
+ return coord_y < p_other.coord_y;
+ }
+ } else {
+ return coord_x < p_other.coord_x;
+ }
+ } else {
+ return source_id < p_other.source_id;
+ }
+ }
- enum TileOrigin {
- TILE_ORIGIN_TOP_LEFT,
- TILE_ORIGIN_CENTER,
- TILE_ORIGIN_BOTTOM_LEFT
- };
+ bool operator!=(const TileMapCell &p_other) const {
+ return !(source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile);
+ }
+};
-private:
- enum DataFormat {
- FORMAT_1 = 0,
- FORMAT_2
+struct TileMapQuadrant {
+ struct CoordsWorldComparator {
+ _ALWAYS_INLINE_ bool operator()(const Vector2i &p_a, const Vector2i &p_b) const {
+ // We sort the cells by their world coords, as it is needed by rendering.
+ if (p_a.y == p_b.y) {
+ return p_a.x > p_b.x;
+ } else {
+ return p_a.y < p_b.y;
+ }
+ }
};
- Ref<TileSet> tile_set;
- Size2i cell_size = Size2(64, 64);
- int quadrant_size = 16;
- Mode mode = MODE_SQUARE;
- Transform2D custom_transform = Transform2D(64, 0, 0, 64, 0, 0);
- HalfOffset half_offset = HALF_OFFSET_DISABLED;
- bool use_parent = false;
- CollisionObject2D *collision_parent = nullptr;
- bool use_kinematic = false;
- bool bake_navigation = false;
-
- union PosKey {
- struct {
- int16_t x;
- int16_t y;
- };
- uint32_t key = 0;
-
- //using a more precise comparison so the regions can be sorted later
- bool operator<(const PosKey &p_k) const { return (y == p_k.y) ? x < p_k.x : y < p_k.y; }
-
- bool operator==(const PosKey &p_k) const { return (y == p_k.y && x == p_k.x); }
-
- PosKey to_quadrant(const int &p_quadrant_size) const {
- // rounding down, instead of simply rounding towards zero (truncating)
- return PosKey(
- x > 0 ? x / p_quadrant_size : (x - (p_quadrant_size - 1)) / p_quadrant_size,
- y > 0 ? y / p_quadrant_size : (y - (p_quadrant_size - 1)) / p_quadrant_size);
- }
+ // Dirty list element
+ SelfList<TileMapQuadrant> dirty_list_element;
+
+ // Quadrant coords.
+ Vector2i coords;
+
+ // TileMapCells
+ Set<Vector2i> cells;
+ // We need those two maps to sort by world position for rendering
+ // This is kind of workaround, it would be better to sort the cells directly in the "cells" set instead.
+ Map<Vector2i, Vector2i> map_to_world;
+ Map<Vector2i, Vector2i, CoordsWorldComparator> world_to_map;
+
+ // Debug.
+ RID debug_canvas_item;
+
+ // Rendering
+ List<RID> canvas_items;
+ List<RID> occluders;
+
+ // Physics.
+ List<RID> bodies;
+
+ // Navigation
+ Map<Vector2i, Vector<RID>> navigation_regions;
+
+ void operator=(const TileMapQuadrant &q) {
+ coords = q.coords;
+ debug_canvas_item = q.debug_canvas_item;
+ canvas_items = q.canvas_items;
+ occluders = q.occluders;
+ bodies = q.bodies;
+ navigation_regions = q.navigation_regions;
+ }
+
+ TileMapQuadrant(const TileMapQuadrant &q) :
+ dirty_list_element(this) {
+ coords = q.coords;
+ debug_canvas_item = q.debug_canvas_item;
+ canvas_items = q.canvas_items;
+ occluders = q.occluders;
+ bodies = q.bodies;
+ navigation_regions = q.navigation_regions;
+ }
+
+ TileMapQuadrant() :
+ dirty_list_element(this) {
+ }
+};
- PosKey(int16_t p_x, int16_t p_y) {
- x = p_x;
- y = p_y;
- }
- PosKey() {
- x = 0;
- y = 0;
- }
- };
+class TileMapPattern : public Object {
+ GDCLASS(TileMapPattern, Object);
- union Cell {
- struct {
- int32_t id : 24;
- bool flip_h : 1;
- bool flip_v : 1;
- bool transpose : 1;
- int16_t autotile_coord_x : 16;
- int16_t autotile_coord_y : 16;
- };
-
- uint64_t _u64t = 0;
- };
+ Vector2i size;
+ Map<Vector2i, TileMapCell> pattern;
- Map<PosKey, Cell> tile_map;
- List<PosKey> dirty_bitmask;
+protected:
+ static void _bind_methods();
- struct Quadrant {
- Vector2 pos;
- List<RID> canvas_items;
- RID body;
- uint32_t shape_owner_id = 0;
+public:
+ void set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile = 0);
+ bool has_cell(const Vector2i &p_coords) const;
+ void remove_cell(const Vector2i &p_coords, bool p_update_size = true);
+ int get_cell_source_id(const Vector2i &p_coords) const;
+ Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const;
+ int get_cell_alternative_tile(const Vector2i &p_coords) const;
- SelfList<Quadrant> dirty_list;
+ TypedArray<Vector2i> get_used_cells() const;
- struct NavPoly {
- RID region;
- Transform2D xform;
- };
+ Vector2i get_size() const;
+ void set_size(const Vector2i &p_size);
+ bool is_empty() const;
- struct Occluder {
- RID id;
- Transform2D xform;
- };
+ void clear();
+};
- Map<PosKey, NavPoly> navpoly_ids;
- Map<PosKey, Occluder> occluder_instances;
+class TileMap : public Node2D {
+ GDCLASS(TileMap, Node2D);
- VSet<PosKey> cells;
+public:
+private:
+ friend class TileSetPlugin;
- void operator=(const Quadrant &q) {
- pos = q.pos;
- canvas_items = q.canvas_items;
- body = q.body;
- shape_owner_id = q.shape_owner_id;
- cells = q.cells;
- navpoly_ids = q.navpoly_ids;
- occluder_instances = q.occluder_instances;
- }
- Quadrant(const Quadrant &q) :
- dirty_list(this) {
- pos = q.pos;
- canvas_items = q.canvas_items;
- body = q.body;
- shape_owner_id = q.shape_owner_id;
- cells = q.cells;
- occluder_instances = q.occluder_instances;
- navpoly_ids = q.navpoly_ids;
- }
- Quadrant() :
- dirty_list(this) {}
+ enum DataFormat {
+ FORMAT_1 = 0,
+ FORMAT_2,
+ FORMAT_3
};
- Map<PosKey, Quadrant> quadrant_map;
+ Ref<TileSet> tile_set;
+ int quadrant_size;
+ Transform2D custom_transform;
+
+ // Map of cells
+ Map<Vector2i, TileMapCell> tile_map;
- SelfList<Quadrant>::List dirty_quadrant_list;
+ Vector2i _coords_to_quadrant_coords(const Vector2i &p_coords) const;
+
+ Map<Vector2i, TileMapQuadrant> quadrant_map;
+
+ SelfList<TileMapQuadrant>::List dirty_quadrant_list;
bool pending_update = false;
Rect2 rect_cache;
bool rect_cache_dirty = true;
Rect2 used_size_cache;
- bool used_size_cache_dirty = true;
- bool quadrant_order_dirty = false;
- bool use_y_sort = false;
- bool compatibility_mode = false;
- bool centered_textures = false;
- bool clip_uv = false;
- float fp_adjust = 0.00001;
- float friction = 1.0;
- float bounce = 0.0;
- uint32_t collision_layer = 1;
- uint32_t collision_mask = 1;
- mutable DataFormat format = FORMAT_1; // Assume lowest possible format if none is present
-
- TileOrigin tile_origin = TILE_ORIGIN_TOP_LEFT;
-
- int occluder_light_mask = 1;
-
- void _fix_cell_transform(Transform2D &xform, const Cell &p_cell, const Vector2 &p_offset, const Size2 &p_sc);
-
- void _add_shape(int &shape_idx, const Quadrant &p_q, const Ref<Shape2D> &p_shape, const TileSet::ShapeData &p_shape_data, const Transform2D &p_xform, const Vector2 &p_metadata);
-
- Map<PosKey, Quadrant>::Element *_create_quadrant(const PosKey &p_qk);
- void _erase_quadrant(Map<PosKey, Quadrant>::Element *Q);
- void _make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool update = true);
+ bool used_size_cache_dirty;
+ mutable DataFormat format;
+
+ void _fix_cell_transform(Transform2D &xform, const TileMapCell &p_cell, const Vector2 &p_offset, const Size2 &p_sc);
+
+ Map<Vector2i, TileMapQuadrant>::Element *_create_quadrant(const Vector2i &p_qk);
+ void _erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q);
+ void _make_all_quadrants_dirty(bool p_update = true);
+ void _make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q, bool p_update = true);
void _recreate_quadrants();
void _clear_quadrants();
- void _update_quadrant_space(const RID &p_space);
- void _update_quadrant_transform();
void _recompute_rect_cache();
void _update_all_items_material_state();
- _FORCE_INLINE_ void _update_item_material_state(const RID &p_canvas_item);
-
- _FORCE_INLINE_ int _get_quadrant_size() const;
void _set_tile_data(const Vector<int> &p_data);
Vector<int> _get_tile_data() const;
- void _set_old_cell_size(int p_size) { set_cell_size(Size2(p_size, p_size)); }
- int _get_old_cell_size() const { return cell_size.x; }
-
- _FORCE_INLINE_ Vector2 _map_to_world(int p_x, int p_y, bool p_ignore_ofs = false) const;
+ void _tile_set_changed();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -230,9 +231,9 @@ protected:
void _notification(int p_what);
static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const override;
-
public:
+ static Vector2i transform_coords_layout(Vector2i p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout);
+
enum {
INVALID_CELL = -1
};
@@ -244,117 +245,49 @@ public:
void set_tileset(const Ref<TileSet> &p_tileset);
Ref<TileSet> get_tileset() const;
- void set_cell_size(Size2 p_size);
- Size2 get_cell_size() const;
-
void set_quadrant_size(int p_size);
int get_quadrant_size() const;
- void set_cell(int p_x, int p_y, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false, Vector2 p_autotile_coord = Vector2());
- int get_cell(int p_x, int p_y) const;
- bool is_cell_x_flipped(int p_x, int p_y) const;
- bool is_cell_y_flipped(int p_x, int p_y) const;
- bool is_cell_transposed(int p_x, int p_y) const;
- void set_cell_autotile_coord(int p_x, int p_y, const Vector2 &p_coord);
- Vector2 get_cell_autotile_coord(int p_x, int p_y) const;
+ void set_cell(const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetAtlasSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetAtlasSource::INVALID_TILE_ALTERNATIVE);
+ int get_cell_source_id(const Vector2i &p_coords) const;
+ Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const;
+ int get_cell_alternative_tile(const Vector2i &p_coords) const;
- void _set_celld(const Vector2 &p_pos, const Dictionary &p_data);
- void set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false);
- int get_cellv(const Vector2 &p_pos) const;
+ TileMapPattern *get_pattern(TypedArray<Vector2i> p_coords_array);
+ Vector2i map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, const TileMapPattern *p_pattern);
+ void set_pattern(Vector2i p_position, const TileMapPattern *p_pattern);
- void make_bitmask_area_dirty(const Vector2 &p_pos);
- void update_bitmask_area(const Vector2 &p_pos);
- void update_bitmask_region(const Vector2 &p_start = Vector2(), const Vector2 &p_end = Vector2());
- void update_cell_bitmask(int p_x, int p_y);
- void update_dirty_bitmask();
+ // Not exposed to users
+ TileMapCell get_cell(const Vector2i &p_coords) const;
+ Map<Vector2i, TileMapQuadrant> &get_quadrant_map();
+ int get_effective_quadrant_size() const;
void update_dirty_quadrants();
- void set_collision_layer(uint32_t p_layer);
- uint32_t get_collision_layer() const;
-
- void set_collision_mask(uint32_t p_mask);
- uint32_t get_collision_mask() const;
-
- void set_collision_layer_bit(int p_bit, bool p_value);
- bool get_collision_layer_bit(int p_bit) const;
-
- void set_collision_mask_bit(int p_bit, bool p_value);
- bool get_collision_mask_bit(int p_bit) const;
-
- void set_collision_use_kinematic(bool p_use_kinematic);
- bool get_collision_use_kinematic() const;
-
- void set_collision_use_parent(bool p_use_parent);
- bool get_collision_use_parent() const;
-
- void set_collision_friction(float p_friction);
- float get_collision_friction() const;
-
- void set_collision_bounce(float p_bounce);
- float get_collision_bounce() const;
-
- void set_bake_navigation(bool p_bake_navigation);
- bool is_baking_navigation();
-
- void set_mode(Mode p_mode);
- Mode get_mode() const;
+ Vector2 map_to_world(const Vector2 &p_pos) const;
+ Vector2i world_to_map(const Vector2 &p_pos) const;
- void set_half_offset(HalfOffset p_half_offset);
- HalfOffset get_half_offset() const;
-
- void set_tile_origin(TileOrigin p_tile_origin);
- TileOrigin get_tile_origin() const;
-
- void set_custom_transform(const Transform2D &p_xform);
- Transform2D get_custom_transform() const;
-
- Transform2D get_cell_transform() const;
- Vector2 get_cell_draw_offset() const;
-
- Vector2 map_to_world(const Vector2 &p_pos, bool p_ignore_ofs = false) const;
- Vector2 world_to_map(const Vector2 &p_pos) const;
-
- void set_y_sort_enabled(bool p_enable);
- bool is_y_sort_enabled() const;
-
- void set_compatibility_mode(bool p_enable);
- bool is_compatibility_mode_enabled() const;
-
- void set_centered_textures(bool p_enable);
- bool is_centered_textures_enabled() 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;
TypedArray<Vector2i> get_used_cells() const;
- TypedArray<Vector2i> get_used_cells_by_index(int p_index) const;
Rect2 get_used_rect(); // Not const because of cache
- void set_occluder_light_mask(int p_mask);
- int get_occluder_light_mask() const;
-
+ // Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems
virtual void set_light_mask(int p_light_mask) override;
-
virtual void set_material(const Ref<Material> &p_material) override;
-
virtual void set_use_parent_material(bool p_use_parent_material) override;
-
- void set_clip_uv(bool p_enable);
- bool get_clip_uv() const;
-
- TypedArray<String> get_configuration_warnings() const override;
-
virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override;
-
virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override;
void fix_invalid_tiles();
void clear();
+ // Helpers
+ TypedArray<Vector2i> get_surrounding_tiles(Vector2i coords);
+ void draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform = Transform2D());
+
TileMap();
~TileMap();
};
-
-VARIANT_ENUM_CAST(TileMap::Mode);
-VARIANT_ENUM_CAST(TileMap::HalfOffset);
-VARIANT_ENUM_CAST(TileMap::TileOrigin);
-
#endif // TILE_MAP_H
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index ebbb8985c9..59233708f6 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -237,53 +237,57 @@ void Skeleton3D::_notification(int p_what) {
for (int i = 0; i < len; i++) {
Bone &b = bonesptr[order[i]];
- if (b.global_pose_override_amount >= 0.999) {
- b.pose_global = b.global_pose_override;
- } else {
- if (b.disable_rest) {
- if (b.enabled) {
- Transform pose = b.pose;
- if (b.custom_pose_enable) {
- pose = b.custom_pose * pose;
- }
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * pose;
- } else {
- b.pose_global = pose;
- }
+ if (b.disable_rest) {
+ if (b.enabled) {
+ Transform pose = b.pose;
+ if (b.custom_pose_enable) {
+ pose = b.custom_pose * pose;
+ }
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global * pose;
+ b.pose_global_no_override = bonesptr[b.parent].pose_global * pose;
} else {
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global;
- } else {
- b.pose_global = Transform();
- }
+ b.pose_global = pose;
+ b.pose_global_no_override = pose;
}
-
} else {
- if (b.enabled) {
- Transform pose = b.pose;
- if (b.custom_pose_enable) {
- pose = b.custom_pose * pose;
- }
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
- } else {
- b.pose_global = b.rest * pose;
- }
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global;
+ b.pose_global_no_override = bonesptr[b.parent].pose_global;
} else {
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * b.rest;
- } else {
- b.pose_global = b.rest;
- }
+ b.pose_global = Transform();
+ b.pose_global_no_override = Transform();
}
}
- if (b.global_pose_override_amount >= CMP_EPSILON) {
- b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
+ } else {
+ if (b.enabled) {
+ Transform pose = b.pose;
+ if (b.custom_pose_enable) {
+ pose = b.custom_pose * pose;
+ }
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
+ b.pose_global_no_override = bonesptr[b.parent].pose_global * (b.rest * pose);
+ } else {
+ b.pose_global = b.rest * pose;
+ b.pose_global_no_override = b.rest * pose;
+ }
+ } else {
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global * b.rest;
+ b.pose_global_no_override = bonesptr[b.parent].pose_global * b.rest;
+ } else {
+ b.pose_global = b.rest;
+ b.pose_global_no_override = b.rest;
+ }
}
}
+ if (b.global_pose_override_amount >= CMP_EPSILON) {
+ b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
+ }
+
if (b.global_pose_override_reset) {
b.global_pose_override_amount = 0.0;
}
@@ -408,6 +412,14 @@ Transform Skeleton3D::get_bone_global_pose(int p_bone) const {
return bones[p_bone].pose_global;
}
+Transform Skeleton3D::get_bone_global_pose_no_override(int p_bone) const {
+ ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform());
+ if (dirty) {
+ const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
+ }
+ return bones[p_bone].pose_global_no_override;
+}
+
// skeleton creation api
void Skeleton3D::add_bone(const String &p_name) {
ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1);
@@ -912,6 +924,7 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override);
ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton3D::get_bone_global_pose);
+ ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override);
ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose);
ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose);
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index 2941ac2c45..508cd7c329 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -83,6 +83,7 @@ private:
Transform pose;
Transform pose_global;
+ Transform pose_global_no_override;
bool custom_pose_enable = false;
Transform custom_pose;
@@ -160,6 +161,7 @@ public:
void set_bone_rest(int p_bone, const Transform &p_rest);
Transform get_bone_rest(int p_bone) const;
Transform get_bone_global_pose(int p_bone) const;
+ Transform get_bone_global_pose_no_override(int p_bone) const;
void clear_bones_global_pose_override();
void set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent = false);
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index 898f94ccc1..bd1c202205 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -246,7 +246,7 @@ void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_
p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform;
} else {
// End effector in local transform
- const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose(p_task->end_effectors[0].tip_bone));
+ const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose_no_override(p_task->end_effectors[0].tip_bone));
// Update the end_effector (local transform) by blending with current pose
p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta);
@@ -270,18 +270,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
return; // Skip solving
}
- p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform(), 0.0, true);
-
- if (p_task->chain.middle_chain_item) {
- p_task->skeleton->set_bone_global_pose_override(p_task->chain.middle_chain_item->bone, Transform(), 0.0, true);
- }
-
- for (int i = 0; i < p_task->chain.tips.size(); i += 1) {
- p_task->skeleton->set_bone_global_pose_override(p_task->chain.tips[i].chain_item->bone, Transform(), 0.0, true);
- }
-
- // Update the transforms to their global poses
- // (Needed to sync IK with animation)
+ // Update the initial root transform so its synced with any animation changes
_update_chain(p_task->skeleton, &p_task->chain.chain_root);
make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta);
@@ -298,48 +287,22 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
Transform new_bone_pose(ci->initial_transform);
new_bone_pose.origin = ci->current_pos;
- // The root bone needs to be rotated differently so it isn't frozen in place.
- if (ci == &p_task->chain.chain_root && !ci->children.is_empty()) {
- new_bone_pose = new_bone_pose.looking_at(ci->children[0].current_pos);
- const Vector3 bone_rest_dir = p_task->skeleton->get_bone_rest(ci->children[0].bone).origin.normalized().abs();
- const Vector3 bone_rest_dir_abs = bone_rest_dir.abs();
- if (bone_rest_dir_abs.x > bone_rest_dir_abs.y && bone_rest_dir_abs.x > bone_rest_dir_abs.z) {
- if (bone_rest_dir.x < 0) {
- new_bone_pose.basis.rotate_local(Vector3(0, 1, 0), -Math_PI / 2.0f);
- } else {
- new_bone_pose.basis.rotate_local(Vector3(0, 1, 0), Math_PI / 2.0f);
- }
- } else if (bone_rest_dir_abs.y > bone_rest_dir_abs.x && bone_rest_dir_abs.y > bone_rest_dir_abs.z) {
- if (bone_rest_dir.y < 0) {
- new_bone_pose.basis.rotate_local(Vector3(1, 0, 0), Math_PI / 2.0f);
- } else {
- new_bone_pose.basis.rotate_local(Vector3(1, 0, 0), -Math_PI / 2.0f);
- }
- } else {
- if (bone_rest_dir.z < 0) {
- // Do nothing!
- } else {
- new_bone_pose.basis.rotate_local(Vector3(0, 0, 1), Math_PI);
- }
- }
- } else {
- if (!ci->children.is_empty()) {
- /// Rotate basis
- const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized());
- const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized());
+ if (!ci->children.is_empty()) {
+ /// Rotate basis
+ const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized());
+ const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized());
- if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) {
- const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1)));
- new_bone_pose.basis.rotate(rot_axis, rot_angle);
- }
+ if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) {
+ const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1)));
+ new_bone_pose.basis.rotate(rot_axis, rot_angle);
+ }
+ } else {
+ // Set target orientation to tip
+ if (override_tip_basis) {
+ new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis;
} else {
- // Set target orientation to tip
- if (override_tip_basis) {
- new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis;
- } else {
- new_bone_pose.basis = new_bone_pose.basis * p_task->chain.tips[0].end_effector->goal_transform.basis;
- }
+ new_bone_pose.basis = new_bone_pose.basis * p_task->chain.tips[0].end_effector->goal_transform.basis;
}
}
@@ -362,7 +325,7 @@ void FabrikInverseKinematic::_update_chain(const Skeleton3D *p_sk, ChainItem *p_
return;
}
- p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone);
+ p_chain_item->initial_transform = p_sk->get_bone_global_pose_no_override(p_chain_item->bone);
p_chain_item->current_pos = p_chain_item->initial_transform.origin;
ChainItem *items = p_chain_item->children.ptrw();
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 44f2d38a84..2ad871ba61 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -597,9 +597,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
if (path.get_subname_count() == 1 && Object::cast_to<Skeleton3D>(spatial)) {
Skeleton3D *sk = Object::cast_to<Skeleton3D>(spatial);
+ track_xform->skeleton = sk;
int bone_idx = sk->find_bone(path.get_subname(0));
if (bone_idx != -1) {
- track_xform->skeleton = sk;
track_xform->bone_idx = bone_idx;
}
}
@@ -1205,7 +1205,7 @@ void AnimationTree::_process_graph(float p_delta) {
} else if (t->skeleton && t->bone_idx >= 0) {
t->skeleton->set_bone_pose(t->bone_idx, xform);
- } else {
+ } else if (!t->skeleton) {
t->spatial->set_transform(xform);
}
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 826fd0189b..ac067aa001 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -155,6 +155,9 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
}
status.pressed = !status.pressed;
_unpress_group();
+ if (button_group.is_valid()) {
+ button_group->emit_signal("pressed", this);
+ }
_toggled(status.pressed);
_pressed();
}
@@ -218,6 +221,9 @@ void BaseButton::set_pressed(bool p_pressed) {
if (p_pressed) {
_unpress_group();
+ if (button_group.is_valid()) {
+ button_group->emit_signal("pressed", this);
+ }
}
_toggled(status.pressed);
@@ -487,6 +493,7 @@ BaseButton *ButtonGroup::get_pressed_button() {
void ButtonGroup::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pressed_button"), &ButtonGroup::get_pressed_button);
ClassDB::bind_method(D_METHOD("get_buttons"), &ButtonGroup::_get_buttons);
+ ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button")));
}
ButtonGroup::ButtonGroup() {
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index b78f9cad24..114abbd4da 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -189,6 +189,18 @@ void ColorPicker::set_pick_color(const Color &p_color) {
_set_pick_color(p_color, true); //because setters can't have more arguments
}
+void ColorPicker::set_old_color(const Color &p_color) {
+ old_color = p_color;
+}
+
+void ColorPicker::set_display_old_color(bool p_enabled) {
+ display_old_color = p_enabled;
+}
+
+bool ColorPicker::is_displaying_old_color() const {
+ return display_old_color;
+}
+
void ColorPicker::set_edit_alpha(bool p_show) {
edit_alpha = p_show;
_update_controls();
@@ -459,17 +471,39 @@ void ColorPicker::_update_text_value() {
}
void ColorPicker::_sample_draw() {
- const Rect2 r = Rect2(Point2(), Size2(sample->get_size().width, sample->get_size().height * 0.95));
+ // Covers the right half of the sample if the old color is being displayed,
+ // or the whole sample if it's not being displayed.
+ Rect2 rect_new;
+
+ if (display_old_color) {
+ rect_new = Rect2(Point2(sample->get_size().width * 0.5, 0), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95));
+
+ // Draw both old and new colors for easier comparison (only if spawned from a ColorPickerButton).
+ const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95));
+
+ if (display_old_color && old_color.a < 1.0) {
+ sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), rect_old, true);
+ }
+
+ sample->draw_rect(rect_old, old_color);
+
+ if (old_color.r > 1 || old_color.g > 1 || old_color.b > 1) {
+ // Draw an indicator to denote that the old color is "overbright" and can't be displayed accurately in the preview.
+ sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2());
+ }
+ } else {
+ rect_new = Rect2(Point2(), Size2(sample->get_size().width, sample->get_size().height * 0.95));
+ }
if (color.a < 1.0) {
- sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), r, true);
+ sample->draw_texture_rect(get_theme_icon("preset_bg", "ColorPicker"), rect_new, true);
}
- sample->draw_rect(r, color);
+ sample->draw_rect(rect_new, color);
if (color.r > 1 || color.g > 1 || color.b > 1) {
- // Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview
- sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2());
+ // Draw an indicator to denote that the new color is "overbright" and can't be displayed accurately in the preview.
+ sample->draw_texture(get_theme_icon("overbright_indicator", "ColorPicker"), Point2(uv_edit->get_size().width * 0.5, 0));
}
}
@@ -1174,6 +1208,11 @@ ColorPicker::ColorPicker() :
/////////////////
+void ColorPickerButton::_about_to_popup() {
+ set_pressed(true);
+ picker->set_old_color(color);
+}
+
void ColorPickerButton::_color_changed(const Color &p_color) {
color = p_color;
update();
@@ -1286,10 +1325,11 @@ void ColorPickerButton::_update_picker() {
popup->add_child(picker);
add_child(popup);
picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed));
- popup->connect("about_to_popup", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(true));
+ popup->connect("about_to_popup", callable_mp(this, &ColorPickerButton::_about_to_popup));
popup->connect("popup_hide", callable_mp(this, &ColorPickerButton::_modal_closed));
picker->set_pick_color(color);
picker->set_edit_alpha(edit_alpha);
+ picker->set_display_old_color(true);
emit_signal("picker_created");
}
}
@@ -1301,6 +1341,7 @@ void ColorPickerButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_popup"), &ColorPickerButton::get_popup);
ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPickerButton::set_edit_alpha);
ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPickerButton::is_editing_alpha);
+ ClassDB::bind_method(D_METHOD("_about_to_popup"), &ColorPickerButton::_about_to_popup);
ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
ADD_SIGNAL(MethodInfo("popup_closed"));
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index a0d2aa95ca..13fe5fd60e 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -86,6 +86,8 @@ private:
PickerShapeType picker_type = SHAPE_HSV_WHEEL;
Color color;
+ Color old_color;
+ bool display_old_color = false;
bool raw_mode_enabled = false;
bool hsv_mode_enabled = false;
bool deferred_mode_enabled = false;
@@ -131,6 +133,10 @@ public:
void _set_pick_color(const Color &p_color, bool p_update_sliders);
void set_pick_color(const Color &p_color);
Color get_pick_color() const;
+ void set_old_color(const Color &p_color);
+
+ void set_display_old_color(bool p_enabled);
+ bool is_displaying_old_color() const;
void set_picker_shape(PickerShapeType p_picker_type);
PickerShapeType get_picker_shape() const;
@@ -171,6 +177,7 @@ class ColorPickerButton : public Button {
Color color;
bool edit_alpha = true;
+ void _about_to_popup();
void _color_changed(const Color &p_color);
void _modal_closed();
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 4fddb4b661..0bdae2b118 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -406,6 +406,9 @@ void ItemList::remove_item(int p_idx) {
ERR_FAIL_INDEX(p_idx, items.size());
items.remove(p_idx);
+ if (current == p_idx) {
+ current = -1;
+ }
update();
shape_changed = true;
defer_select_single = -1;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index eb836b3bf7..bfd739788f 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -557,7 +557,7 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
}
Control::CursorShape LineEdit::get_cursor_shape(const Point2 &p_pos) const {
- if (!text.is_empty() && is_editable() && _is_over_clear_button(p_pos)) {
+ if ((!text.is_empty() && is_editable() && _is_over_clear_button(p_pos)) || (!is_editable() && (!is_selecting_enabled() || text.is_empty()))) {
return CURSOR_ARROW;
}
return Control::get_cursor_shape(p_pos);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 9efc1af23b..e8a908c30e 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -2615,6 +2615,7 @@ void RichTextLabel::pop() {
// Creates a new line without adding an ItemNewline to the previous line.
// Useful when wanting to calling remove_line and add a new line immediately after.
void RichTextLabel::increment_line_count() {
+ _validate_line_caches(main);
current_frame->lines.resize(current_frame->lines.size() + 1);
_invalidate_current_line(current_frame);
}
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index ff9dafa0f9..acf0641005 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -394,6 +394,7 @@ void TabContainer::_notification(int p_what) {
Vector<int> tab_widths;
for (int i = first_tab_cache; i < tabs.size(); i++) {
if (get_tab_hidden(i)) {
+ tab_widths.push_back(0);
continue;
}
int tab_width = _get_tab_width(i);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index ded912591f..c924f89709 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -4450,7 +4450,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
return CURSOR_POINTING_HAND;
}
- if ((completion_active && completion_rect.has_point(p_pos))) {
+ if ((completion_active && completion_rect.has_point(p_pos)) || (is_readonly() && (!is_selecting_enabled() || text.size() == 0))) {
return CURSOR_ARROW;
}
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 6404f6fc0d..7028d7aed4 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1672,7 +1672,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
float line_width = 1.0;
#ifdef TOOLS_ENABLED
- line_width *= EDSCALE;
+ line_width *= Math::round(EDSCALE);
#endif
Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs;
@@ -3013,6 +3013,10 @@ bool Tree::edit_selected() {
return false;
}
+bool Tree::is_editing() {
+ return popup_editor->is_visible();
+}
+
Size2 Tree::get_internal_min_size() const {
Size2i size = cache.bg->get_offset();
if (root) {
@@ -3174,7 +3178,6 @@ void Tree::_notification(int p_what) {
RID ci = get_canvas_item();
Ref<StyleBox> bg = cache.bg;
- Ref<StyleBox> bg_focus = get_theme_stylebox("bg_focus");
Color font_outline_color = get_theme_color("font_outline_color");
int outline_size = get_theme_constant("outline_size");
@@ -3183,11 +3186,6 @@ void Tree::_notification(int p_what) {
Size2 draw_size = get_size() - bg->get_minimum_size();
bg->draw(ci, Rect2(Point2(), get_size()));
- if (has_focus()) {
- RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true);
- bg_focus->draw(ci, Rect2(Point2(), get_size()));
- RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
- }
int tbh = _get_title_button_height();
@@ -3221,6 +3219,15 @@ void Tree::_notification(int p_what) {
columns[i].text_buf->draw(ci, text_pos, cache.title_button_color);
}
}
+
+ // Draw the background focus outline last, so that it is drawn in front of the section headings.
+ // Otherwise, section heading backgrounds can appear to be in front of the focus outline when scrolling.
+ if (has_focus()) {
+ RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true);
+ const Ref<StyleBox> bg_focus = get_theme_stylebox("bg_focus");
+ bg_focus->draw(ci, Rect2(Point2(), get_size()));
+ RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
+ }
}
if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) {
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index a40817b752..6d36f0df7f 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -604,6 +604,7 @@ public:
int get_item_offset(TreeItem *p_item) const;
Rect2 get_item_rect(TreeItem *p_item, int p_column = -1) const;
bool edit_selected();
+ bool is_editing();
// First item that starts with the text, from the current focused item down and wraps around.
TreeItem *search_item_text(const String &p_find, int *r_col = nullptr, bool p_selectable = false);
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 55529517f1..fa98a10a26 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -1210,7 +1210,7 @@ void CanvasItem::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
ADD_GROUP("Texture", "texture_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,MipmapNearest,MipmapLinear,MipmapNearestAniso,MipmapLinearAniso"), "set_texture_filter", "get_texture_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Aniso.,Linear Mipmap Aniso."), "set_texture_filter", "get_texture_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat");
ADD_GROUP("Material", "");
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index f861e3064c..b94a818b06 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -3608,7 +3608,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
ADD_GROUP("Canvas Items", "canvas_item_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,MipmapLinear,MipmapNearest"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirror"), "set_default_canvas_item_texture_repeat", "get_default_canvas_item_texture_repeat");
ADD_GROUP("Audio Listener", "audio_listener_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_2d"), "set_as_audio_listener_2d", "is_audio_listener_2d");
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index b16532676f..5b5eb946f0 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -655,6 +655,9 @@ void register_scene_types() {
ClassDB::register_class<GrooveJoint2D>();
ClassDB::register_class<DampedSpringJoint2D>();
ClassDB::register_class<TileSet>();
+ ClassDB::register_virtual_class<TileSetSource>();
+ ClassDB::register_class<TileSetAtlasSource>();
+ ClassDB::register_class<TileData>();
ClassDB::register_class<TileMap>();
ClassDB::register_class<ParallaxBackground>();
ClassDB::register_class<ParallaxLayer>();
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index e8157c7165..86c7bda2b3 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -2519,7 +2519,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "uv2_world_triplanar"), "set_flag", "get_flag", FLAG_UV2_USE_WORLD_TRIPLANAR);
ADD_GROUP("Sampling", "texture_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,MipmapNearest,MipmapLinear,MipmapNearestAniso,MipmapLinearAniso"), "set_texture_filter", "get_texture_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Aniso.,Linear Mipmap Aniso."), "set_texture_filter", "get_texture_filter");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "texture_repeat"), "set_flag", "get_flag", FLAG_USE_TEXTURE_REPEAT);
ADD_GROUP("Shadows", "");
diff --git a/scene/resources/shape_3d.cpp b/scene/resources/shape_3d.cpp
index 5761a405ce..cb44e059a3 100644
--- a/scene/resources/shape_3d.cpp
+++ b/scene/resources/shape_3d.cpp
@@ -102,6 +102,8 @@ void Shape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_margin", "margin"), &Shape3D::set_margin);
ClassDB::bind_method(D_METHOD("get_margin"), &Shape3D::get_margin);
+ ClassDB::bind_method(D_METHOD("get_debug_mesh"), &Shape3D::get_debug_mesh);
+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_margin", "get_margin");
}
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index c30bd7927d..ff682a40f4 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -663,6 +663,8 @@ void SurfaceTool::deindex() {
}
void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat) {
+ ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::_create_list() must be a valid object of type Mesh");
+
Array arr = p_existing->surface_get_arrays(p_surface);
ERR_FAIL_COND(arr.size() != RS::ARRAY_MAX);
_create_list_from_arrays(arr, r_vertex, r_index, lformat);
@@ -824,6 +826,8 @@ void SurfaceTool::create_from_triangle_arrays(const Array &p_arrays) {
}
void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) {
+ ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::create_from() must be a valid object of type Mesh");
+
clear();
primitive = p_existing->surface_get_primitive_type(p_surface);
_create_list(p_existing, p_surface, &vertex_array, &index_array, format);
@@ -831,6 +835,8 @@ void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) {
}
void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String &p_blend_shape_name) {
+ ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::create_from_blend_shape() must be a valid object of type Mesh");
+
clear();
primitive = p_existing->surface_get_primitive_type(p_surface);
Array arr = p_existing->surface_get_blend_shape_arrays(p_surface);
@@ -851,6 +857,8 @@ void SurfaceTool::create_from_blend_shape(const Ref<Mesh> &p_existing, int p_sur
}
void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform) {
+ ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::append_from() must be a valid object of type Mesh");
+
if (vertex_array.size() == 0) {
primitive = p_existing->surface_get_primitive_type(p_surface);
format = 0;
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 84be69d0d6..c4b8a56f54 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -30,1150 +30,4221 @@
#include "tile_set.h"
-#include "core/config/engine.h"
#include "core/math/geometry_2d.h"
-#include "core/variant/array.h"
+#include "scene/2d/navigation_region_2d.h"
+#include "scene/gui/control.h"
+#include "scene/resources/convex_polygon_shape_2d.h"
+#include "servers/navigation_server_2d.h"
-bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
- String n = p_name;
- int slash = n.find("/");
- if (slash == -1) {
- return false;
- }
- int id = String::to_int(n.get_data(), slash);
-
- if (!tile_map.has(id)) {
- create_tile(id);
- }
- String what = n.substr(slash + 1, n.length());
-
- if (what == "name") {
- tile_set_name(id, p_value);
- } else if (what == "texture") {
- tile_set_texture(id, p_value);
- } else if (what == "tex_offset") {
- tile_set_texture_offset(id, p_value);
- } else if (what == "material") {
- tile_set_material(id, p_value);
- } else if (what == "modulate") {
- tile_set_modulate(id, p_value);
- } else if (what == "region") {
- tile_set_region(id, p_value);
- } else if (what == "tile_mode") {
- tile_set_tile_mode(id, (TileMode)((int)p_value));
- } else if (what == "is_autotile") {
- // backward compatibility for Godot 3.0.x
- // autotile used to be a bool, it's now an enum
- bool is_autotile = p_value;
- if (is_autotile) {
- tile_set_tile_mode(id, AUTO_TILE);
- }
- } else if (what.left(9) == "autotile/") {
- what = what.right(9);
- if (what == "bitmask_mode") {
- autotile_set_bitmask_mode(id, (BitmaskMode)((int)p_value));
- } else if (what == "icon_coordinate") {
- autotile_set_icon_coordinate(id, p_value);
- } else if (what == "tile_size") {
- autotile_set_size(id, p_value);
- } else if (what == "spacing") {
- autotile_set_spacing(id, p_value);
- } else if (what == "bitmask_flags") {
- tile_map[id].autotile_data.flags.clear();
- if (p_value.is_array()) {
- Array p = p_value;
- Vector2 last_coord;
- while (p.size() > 0) {
- if (p[0].get_type() == Variant::VECTOR2) {
- last_coord = p[0];
- } else if (p[0].get_type() == Variant::INT) {
- autotile_set_bitmask(id, last_coord, p[0]);
- }
- p.pop_front();
- }
- }
- } else if (what == "occluder_map") {
- tile_map[id].autotile_data.occluder_map.clear();
- Array p = p_value;
- Vector2 last_coord;
- while (p.size() > 0) {
- if (p[0].get_type() == Variant::VECTOR2) {
- last_coord = p[0];
- } else if (p[0].get_type() == Variant::OBJECT) {
- autotile_set_light_occluder(id, p[0], last_coord);
- }
- p.pop_front();
- }
- } else if (what == "navpoly_map") {
- tile_map[id].autotile_data.navpoly_map.clear();
- Array p = p_value;
- Vector2 last_coord;
- while (p.size() > 0) {
- if (p[0].get_type() == Variant::VECTOR2) {
- last_coord = p[0];
- } else if (p[0].get_type() == Variant::OBJECT) {
- autotile_set_navigation_polygon(id, p[0], last_coord);
- }
- p.pop_front();
- }
- } else if (what == "priority_map") {
- tile_map[id].autotile_data.priority_map.clear();
- Array p = p_value;
- Vector3 val;
- Vector2 v;
- int priority;
- while (p.size() > 0) {
- val = p[0];
- if (val.z > 1) {
- v.x = val.x;
- v.y = val.y;
- priority = (int)val.z;
- tile_map[id].autotile_data.priority_map[v] = priority;
- }
- p.pop_front();
- }
- } else if (what == "z_index_map") {
- tile_map[id].autotile_data.z_index_map.clear();
- Array p = p_value;
- Vector3 val;
- Vector2 v;
- int z_index;
- while (p.size() > 0) {
- val = p[0];
- if (val.z != 0) {
- v.x = val.x;
- v.y = val.y;
- z_index = (int)val.z;
- tile_map[id].autotile_data.z_index_map[v] = z_index;
- }
- p.pop_front();
- }
- }
- } else if (what == "shape") {
- if (tile_get_shape_count(id) > 0) {
- for (int i = 0; i < tile_get_shape_count(id); i++) {
- tile_set_shape(id, i, p_value);
- }
- } else {
- tile_set_shape(id, 0, p_value);
- }
- } else if (what == "shape_offset") {
- if (tile_get_shape_count(id) > 0) {
- for (int i = 0; i < tile_get_shape_count(id); i++) {
- tile_set_shape_offset(id, i, p_value);
- }
- } else {
- tile_set_shape_offset(id, 0, p_value);
- }
- } else if (what == "shape_transform") {
- if (tile_get_shape_count(id) > 0) {
- for (int i = 0; i < tile_get_shape_count(id); i++) {
- tile_set_shape_transform(id, i, p_value);
- }
- } else {
- tile_set_shape_transform(id, 0, p_value);
- }
- } else if (what == "shape_one_way") {
- if (tile_get_shape_count(id) > 0) {
- for (int i = 0; i < tile_get_shape_count(id); i++) {
- tile_set_shape_one_way(id, i, p_value);
- }
- } else {
- tile_set_shape_one_way(id, 0, p_value);
- }
- } else if (what == "shape_one_way_margin") {
- if (tile_get_shape_count(id) > 0) {
- for (int i = 0; i < tile_get_shape_count(id); i++) {
- tile_set_shape_one_way_margin(id, i, p_value);
- }
- } else {
- tile_set_shape_one_way_margin(id, 0, p_value);
- }
- } else if (what == "shapes") {
- _tile_set_shapes(id, p_value);
- } else if (what == "occluder") {
- tile_set_light_occluder(id, p_value);
- } else if (what == "occluder_offset") {
- tile_set_occluder_offset(id, p_value);
- } else if (what == "navigation") {
- tile_set_navigation_polygon(id, p_value);
- } else if (what == "navigation_offset") {
- tile_set_navigation_polygon_offset(id, p_value);
- } else if (what == "z_index") {
- tile_set_z_index(id, p_value);
- } else {
- return false;
- }
+/////////////////////////////// TileSet //////////////////////////////////////
- return true;
+// --- Plugins ---
+Vector<TileSetPlugin *> TileSet::get_tile_set_atlas_plugins() const {
+ return tile_set_plugins_vector;
}
-bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
- String n = p_name;
- int slash = n.find("/");
- if (slash == -1) {
- return false;
+// -- Shape and layout --
+void TileSet::set_tile_shape(TileSet::TileShape p_shape) {
+ tile_shape = p_shape;
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
}
- int id = String::to_int(n.get_data(), slash);
-
- ERR_FAIL_COND_V(!tile_map.has(id), false);
-
- String what = n.substr(slash + 1, n.length());
-
- if (what == "name") {
- r_ret = tile_get_name(id);
- } else if (what == "texture") {
- r_ret = tile_get_texture(id);
- } else if (what == "tex_offset") {
- r_ret = tile_get_texture_offset(id);
- } else if (what == "material") {
- r_ret = tile_get_material(id);
- } else if (what == "modulate") {
- r_ret = tile_get_modulate(id);
- } else if (what == "region") {
- r_ret = tile_get_region(id);
- } else if (what == "tile_mode") {
- r_ret = tile_get_tile_mode(id);
- } else if (what.left(9) == "autotile/") {
- what = what.right(9);
- if (what == "bitmask_mode") {
- r_ret = autotile_get_bitmask_mode(id);
- } else if (what == "icon_coordinate") {
- r_ret = autotile_get_icon_coordinate(id);
- } else if (what == "tile_size") {
- r_ret = autotile_get_size(id);
- } else if (what == "spacing") {
- r_ret = autotile_get_spacing(id);
- } else if (what == "bitmask_flags") {
- Array p;
- for (Map<Vector2, uint32_t>::Element *E = tile_map[id].autotile_data.flags.front(); E; E = E->next()) {
- p.push_back(E->key());
- p.push_back(E->value());
- }
- r_ret = p;
- } else if (what == "occluder_map") {
- Array p;
- for (Map<Vector2, Ref<OccluderPolygon2D>>::Element *E = tile_map[id].autotile_data.occluder_map.front(); E; E = E->next()) {
- p.push_back(E->key());
- p.push_back(E->value());
- }
- r_ret = p;
- } else if (what == "navpoly_map") {
- Array p;
- for (Map<Vector2, Ref<NavigationPolygon>>::Element *E = tile_map[id].autotile_data.navpoly_map.front(); E; E = E->next()) {
- p.push_back(E->key());
- p.push_back(E->value());
- }
- r_ret = p;
- } else if (what == "priority_map") {
- Array p;
- Vector3 v;
- for (Map<Vector2, int>::Element *E = tile_map[id].autotile_data.priority_map.front(); E; E = E->next()) {
- if (E->value() > 1) {
- //Don't save default value
- v.x = E->key().x;
- v.y = E->key().y;
- v.z = E->value();
- p.push_back(v);
- }
- }
- r_ret = p;
- } else if (what == "z_index_map") {
- Array p;
- Vector3 v;
- for (Map<Vector2, int>::Element *E = tile_map[id].autotile_data.z_index_map.front(); E; E = E->next()) {
- if (E->value() != 0) {
- //Don't save default value
- v.x = E->key().x;
- v.y = E->key().y;
- v.z = E->value();
- p.push_back(v);
- }
- }
- r_ret = p;
- }
- } else if (what == "shape") {
- r_ret = tile_get_shape(id, 0);
- } else if (what == "shape_offset") {
- r_ret = tile_get_shape_offset(id, 0);
- } else if (what == "shape_transform") {
- r_ret = tile_get_shape_transform(id, 0);
- } else if (what == "shape_one_way") {
- r_ret = tile_get_shape_one_way(id, 0);
- } else if (what == "shape_one_way_margin") {
- r_ret = tile_get_shape_one_way_margin(id, 0);
- } else if (what == "shapes") {
- r_ret = _tile_get_shapes(id);
- } else if (what == "occluder") {
- r_ret = tile_get_light_occluder(id);
- } else if (what == "occluder_offset") {
- r_ret = tile_get_occluder_offset(id);
- } else if (what == "navigation") {
- r_ret = tile_get_navigation_polygon(id);
- } else if (what == "navigation_offset") {
- r_ret = tile_get_navigation_polygon_offset(id);
- } else if (what == "z_index") {
- r_ret = tile_get_z_index(id);
- } else {
- return false;
+
+ emit_changed();
+}
+TileSet::TileShape TileSet::get_tile_shape() const {
+ return tile_shape;
+}
+
+void TileSet::set_tile_layout(TileSet::TileLayout p_layout) {
+ tile_layout = p_layout;
+ emit_changed();
+}
+TileSet::TileLayout TileSet::get_tile_layout() const {
+ return tile_layout;
+}
+
+void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) {
+ tile_offset_axis = p_alignment;
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
}
- return true;
+ emit_changed();
+}
+TileSet::TileOffsetAxis TileSet::get_tile_offset_axis() const {
+ return tile_offset_axis;
}
-void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
- for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) {
- int id = E->key();
- String pre = itos(id) + "/";
- p_list->push_back(PropertyInfo(Variant::STRING, pre + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "tex_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::COLOR, pre + "modulate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::RECT2, pre + "region", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::INT, pre + "tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE,ATLAS_TILE", PROPERTY_USAGE_NOEDITOR));
- if (tile_get_tile_mode(id) == AUTO_TILE) {
- p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3 (minimal),3X3", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/bitmask_flags", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/icon_coordinate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/z_index_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- } else if (tile_get_tile_mode(id) == ATLAS_TILE) {
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/icon_coordinate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/z_index_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- }
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "occluder_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "occluder", PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "navigation_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "navigation", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "shape_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "shape_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::BOOL, pre + "shape_one_way", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::FLOAT, pre + "shape_one_way_margin", PROPERTY_HINT_RANGE, "0,128,0.01", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "shapes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::INT, pre + "z_index", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1", PROPERTY_USAGE_NOEDITOR));
- }
-}
-
-void TileSet::create_tile(int p_id) {
- ERR_FAIL_COND(tile_map.has(p_id));
- tile_map[p_id] = TileData();
- tile_map[p_id].autotile_data = AutotileData();
- notify_property_list_changed();
+void TileSet::set_tile_size(Size2i p_size) {
+ ERR_FAIL_COND(p_size.x < 1 || p_size.y < 1);
+ tile_size = p_size;
emit_changed();
}
+Size2i TileSet::get_tile_size() const {
+ return tile_size;
+}
-void TileSet::autotile_set_bitmask_mode(int p_id, BitmaskMode p_mode) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].autotile_data.bitmask_mode = p_mode;
- notify_property_list_changed();
+void TileSet::set_tile_skew(Vector2 p_skew) {
emit_changed();
+ tile_skew = p_skew;
+}
+Vector2 TileSet::get_tile_skew() const {
+ return tile_skew;
+}
+
+int TileSet::get_next_source_id() const {
+ return next_source_id;
}
-TileSet::BitmaskMode TileSet::autotile_get_bitmask_mode(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), BITMASK_2X2);
- return tile_map[p_id].autotile_data.bitmask_mode;
+void TileSet::_compute_next_source_id() {
+ while (sources.has(next_source_id)) {
+ next_source_id = (next_source_id + 1) % 1073741824; // 2 ** 30
+ };
}
-void TileSet::tile_set_texture(int p_id, const Ref<Texture2D> &p_texture) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].texture = p_texture;
+// Sources management
+int TileSet::add_source(Ref<TileSetAtlasSource> p_tile_atlas_source, int p_atlas_source_id_override) {
+ ERR_FAIL_COND_V(!p_tile_atlas_source.is_valid(), -1);
+ ERR_FAIL_COND_V_MSG(p_atlas_source_id_override >= 0 && (sources.has(p_atlas_source_id_override)), -1, vformat("Cannot create TileSet atlas source. Another atlas source exists with id %d.", p_atlas_source_id_override));
+
+ int new_source_id = p_atlas_source_id_override >= 0 ? p_atlas_source_id_override : next_source_id;
+ sources[new_source_id] = p_tile_atlas_source;
+ source_ids.append(new_source_id);
+ source_ids.sort();
+ p_tile_atlas_source->set_tile_set(this);
+ _compute_next_source_id();
+
+ sources[new_source_id]->connect("changed", callable_mp(this, &TileSet::_source_changed));
+
emit_changed();
+
+ return new_source_id;
}
-Ref<Texture2D> TileSet::tile_get_texture(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<Texture2D>());
- return tile_map[p_id].texture;
+void TileSet::remove_source(int p_source_id) {
+ ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot remove TileSet atlas source. No tileset atlas source with id %d.", p_source_id));
+
+ sources[p_source_id]->disconnect("changed", callable_mp(this, &TileSet::_source_changed));
+
+ sources[p_source_id]->set_tile_set(nullptr);
+ sources.erase(p_source_id);
+ source_ids.erase(p_source_id);
+ source_ids.sort();
+
+ emit_changed();
}
-void TileSet::tile_set_material(int p_id, const Ref<ShaderMaterial> &p_material) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].material = p_material;
+void TileSet::set_source_id(int p_source_id, int p_new_source_id) {
+ ERR_FAIL_COND(p_new_source_id < 0);
+ ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot change TileSet atlas source ID. No tileset atlas source with id %d.", p_source_id));
+ if (p_source_id == p_new_source_id) {
+ return;
+ }
+
+ ERR_FAIL_COND_MSG(sources.has(p_new_source_id), vformat("Cannot change TileSet atlas source ID. Another atlas source exists with id %d.", p_new_source_id));
+
+ sources[p_new_source_id] = sources[p_source_id];
+ sources.erase(p_source_id);
+
+ source_ids.erase(p_source_id);
+ source_ids.append(p_new_source_id);
+ source_ids.sort();
+
emit_changed();
}
-Ref<ShaderMaterial> TileSet::tile_get_material(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<ShaderMaterial>());
- return tile_map[p_id].material;
+bool TileSet::has_source(int p_source_id) const {
+ return sources.has(p_source_id);
+}
+
+Ref<TileSetSource> TileSet::get_source(int p_source_id) const {
+ ERR_FAIL_COND_V_MSG(!sources.has(p_source_id), nullptr, vformat("No TileSet atlas source with id %d.", p_source_id));
+
+ return sources[p_source_id];
+}
+
+int TileSet::get_source_count() const {
+ return source_ids.size();
}
-void TileSet::tile_set_modulate(int p_id, const Color &p_modulate) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].modulate = p_modulate;
+int TileSet::get_source_id(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, source_ids.size(), -1);
+ return source_ids[p_index];
+}
+
+// Rendering
+void TileSet::set_uv_clipping(bool p_uv_clipping) {
+ if (uv_clipping == p_uv_clipping) {
+ return;
+ }
+ uv_clipping = p_uv_clipping;
emit_changed();
}
+bool TileSet::is_uv_clipping() const {
+ return uv_clipping;
+};
-Color TileSet::tile_get_modulate(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Color(1, 1, 1));
- return tile_map[p_id].modulate;
+void TileSet::set_y_sorting(bool p_y_sort) {
+ if (y_sorting == p_y_sort) {
+ return;
+ }
+ y_sorting = p_y_sort;
+ emit_changed();
}
+bool TileSet::is_y_sorting() const {
+ return y_sorting;
+};
-void TileSet::tile_set_texture_offset(int p_id, const Vector2 &p_offset) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].offset = p_offset;
+void TileSet::set_occlusion_layers_count(int p_occlusion_layers_count) {
+ ERR_FAIL_COND(p_occlusion_layers_count < 0);
+ if (occlusion_layers.size() == p_occlusion_layers_count) {
+ return;
+ }
+
+ occlusion_layers.resize(p_occlusion_layers_count);
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
+ notify_property_list_changed();
emit_changed();
}
-Vector2 TileSet::tile_get_texture_offset(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- return tile_map[p_id].offset;
+int TileSet::get_occlusion_layers_count() const {
+ return occlusion_layers.size();
+};
+
+void TileSet::set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask) {
+ ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size());
+ occlusion_layers.write[p_layer_index].light_mask = p_light_mask;
+ emit_changed();
+}
+
+int TileSet::get_occlusion_layer_light_mask(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, occlusion_layers.size(), 0);
+ return occlusion_layers[p_layer_index].light_mask;
}
-void TileSet::tile_set_region(int p_id, const Rect2 &p_region) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].region = p_region;
+void TileSet::set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision) {
+ ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size());
+ occlusion_layers.write[p_layer_index].sdf_collision = p_sdf_collision;
emit_changed();
}
-Rect2 TileSet::tile_get_region(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Rect2());
- return tile_map[p_id].region;
+bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, occlusion_layers.size(), false);
+ return occlusion_layers[p_layer_index].sdf_collision;
+}
+
+void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) {
+ // TODO: optimize this with 2D meshes when they work again.
+ if (get_tile_shape() == TileSet::TILE_SHAPE_SQUARE) {
+ if (p_filled && p_texture.is_valid()) {
+ p_canvas_item->draw_texture_rect(p_texture, p_region, false, p_color);
+ } else {
+ p_canvas_item->draw_rect(p_region, p_color, p_filled);
+ }
+ } else {
+ float overlap = 0.0;
+ switch (get_tile_shape()) {
+ case TileSet::TILE_SHAPE_ISOMETRIC:
+ overlap = 0.5;
+ break;
+ case TileSet::TILE_SHAPE_HEXAGON:
+ overlap = 0.25;
+ break;
+ case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE:
+ overlap = 0.0;
+ break;
+ default:
+ break;
+ }
+
+ Vector<Vector2> uvs;
+ uvs.append(Vector2(0.5, 0.0));
+ uvs.append(Vector2(0.0, overlap));
+ uvs.append(Vector2(0.0, 1.0 - overlap));
+ uvs.append(Vector2(0.5, 1.0));
+ uvs.append(Vector2(1.0, 1.0 - overlap));
+ uvs.append(Vector2(1.0, overlap));
+ uvs.append(Vector2(0.5, 0.0));
+ if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ for (int i = 0; i < uvs.size(); i++) {
+ uvs.write[i] = Vector2(uvs[i].y, uvs[i].x);
+ }
+ }
+
+ Vector<Vector2> points;
+ for (int i = 0; i < uvs.size(); i++) {
+ points.append(p_region.position + uvs[i] * p_region.size);
+ }
+
+ if (p_filled) {
+ // This does hurt performances a lot. We should use a mesh if possible instead.
+ p_canvas_item->draw_colored_polygon(points, p_color, uvs, p_texture);
+
+ // Should improve performances, but does not work as draw_primitive does not work with textures :/ :
+ /*for (int i = 0; i < 6; i += 3) {
+ Vector<Vector2> quad;
+ quad.append(points[i]);
+ quad.append(points[(i + 1) % points.size()]);
+ quad.append(points[(i + 2) % points.size()]);
+ quad.append(points[(i + 3) % points.size()]);
+
+ Vector<Vector2> uv_quad;
+ uv_quad.append(uvs[i]);
+ uv_quad.append(uvs[(i + 1) % uvs.size()]);
+ uv_quad.append(uvs[(i + 2) % uvs.size()]);
+ uv_quad.append(uvs[(i + 3) % uvs.size()]);
+
+ p_control->draw_primitive(quad, Vector<Color>(), uv_quad, p_texture);
+ }*/
+
+ } else {
+ // This does hurt performances a lot. We should use a mesh if possible instead.
+ // tile_shape_grid->draw_polyline(points, p_color);
+ for (int i = 0; i < points.size() - 1; i++) {
+ p_canvas_item->draw_line(points[i], points[i + 1], p_color);
+ }
+ }
+ }
}
-void TileSet::tile_set_tile_mode(int p_id, TileMode p_tile_mode) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].tile_mode = p_tile_mode;
+// Physics
+void TileSet::set_physics_layers_count(int p_physics_layers_count) {
+ ERR_FAIL_COND(p_physics_layers_count < 0);
+ if (physics_layers.size() == p_physics_layers_count) {
+ return;
+ }
+
+ physics_layers.resize(p_physics_layers_count);
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
+ notify_property_list_changed();
emit_changed();
}
-TileSet::TileMode TileSet::tile_get_tile_mode(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), SINGLE_TILE);
- return tile_map[p_id].tile_mode;
+int TileSet::get_physics_layers_count() const {
+ return physics_layers.size();
}
-void TileSet::autotile_set_icon_coordinate(int p_id, Vector2 coord) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].autotile_data.icon_coord = coord;
+void TileSet::set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer) {
+ ERR_FAIL_INDEX(p_layer_index, physics_layers.size());
+ physics_layers.write[p_layer_index].collision_layer = p_layer;
emit_changed();
}
-Vector2 TileSet::autotile_get_icon_coordinate(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- return tile_map[p_id].autotile_data.icon_coord;
+uint32_t TileSet::get_physics_layer_collision_layer(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, physics_layers.size(), 0);
+ return physics_layers[p_layer_index].collision_layer;
}
-void TileSet::autotile_set_spacing(int p_id, int p_spacing) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_spacing < 0);
- tile_map[p_id].autotile_data.spacing = p_spacing;
+void TileSet::set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask) {
+ ERR_FAIL_INDEX(p_layer_index, physics_layers.size());
+ physics_layers.write[p_layer_index].collision_mask = p_mask;
emit_changed();
}
-int TileSet::autotile_get_spacing(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
- return tile_map[p_id].autotile_data.spacing;
+uint32_t TileSet::get_physics_layer_collision_mask(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, physics_layers.size(), 0);
+ return physics_layers[p_layer_index].collision_mask;
}
-void TileSet::autotile_set_size(int p_id, Size2 p_size) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0);
- tile_map[p_id].autotile_data.size = p_size;
+void TileSet::set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material) {
+ ERR_FAIL_INDEX(p_layer_index, physics_layers.size());
+ physics_layers.write[p_layer_index].physics_material = p_physics_material;
}
-Size2 TileSet::autotile_get_size(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Size2());
- return tile_map[p_id].autotile_data.size;
+Ref<PhysicsMaterial> TileSet::get_physics_layer_physics_material(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, physics_layers.size(), Ref<PhysicsMaterial>());
+ return physics_layers[p_layer_index].physics_material;
}
-void TileSet::autotile_clear_bitmask_map(int p_id) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].autotile_data.flags.clear();
+// Terrains
+void TileSet::set_terrain_sets_count(int p_terrains_sets_count) {
+ ERR_FAIL_COND(p_terrains_sets_count < 0);
+
+ terrain_sets.resize(p_terrains_sets_count);
+
+ notify_property_list_changed();
+ emit_changed();
}
-void TileSet::autotile_set_subtile_priority(int p_id, const Vector2 &p_coord, int p_priority) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_priority <= 0);
- tile_map[p_id].autotile_data.priority_map[p_coord] = p_priority;
+int TileSet::get_terrain_sets_count() const {
+ return terrain_sets.size();
}
-int TileSet::autotile_get_subtile_priority(int p_id, const Vector2 &p_coord) {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 1);
- if (tile_map[p_id].autotile_data.priority_map.has(p_coord)) {
- return tile_map[p_id].autotile_data.priority_map[p_coord];
+void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ terrain_sets.write[p_terrain_set].mode = p_terrain_mode;
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
}
- //When not custom priority set return the default value
- return 1;
+
+ notify_property_list_changed();
+ emit_changed();
}
-const Map<Vector2, int> &TileSet::autotile_get_priority_map(int p_id) const {
- static Map<Vector2, int> dummy;
- ERR_FAIL_COND_V(!tile_map.has(p_id), dummy);
- return tile_map[p_id].autotile_data.priority_map;
+TileSet::TerrainMode TileSet::get_terrain_set_mode(int p_terrain_set) const {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES);
+ return terrain_sets[p_terrain_set].mode;
}
-void TileSet::autotile_set_z_index(int p_id, const Vector2 &p_coord, int p_z_index) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].autotile_data.z_index_map[p_coord] = p_z_index;
+void TileSet::set_terrains_count(int p_terrain_set, int p_terrains_layers_count) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ ERR_FAIL_COND(p_terrains_layers_count < 0);
+ if (terrain_sets[p_terrain_set].terrains.size() == p_terrains_layers_count) {
+ return;
+ }
+
+ int old_size = terrain_sets[p_terrain_set].terrains.size();
+ terrain_sets.write[p_terrain_set].terrains.resize(p_terrains_layers_count);
+
+ // Default name and color
+ for (int i = old_size; i < terrain_sets.write[p_terrain_set].terrains.size(); i++) {
+ float hue_rotate = (i * 2 % 16) / 16.0;
+ Color c;
+ c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5);
+ terrain_sets.write[p_terrain_set].terrains.write[i].color = c;
+ terrain_sets.write[p_terrain_set].terrains.write[i].name = String(vformat("Terrain %d", i));
+ }
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
+ notify_property_list_changed();
emit_changed();
}
-int TileSet::autotile_get_z_index(int p_id, const Vector2 &p_coord) {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 1);
- if (tile_map[p_id].autotile_data.z_index_map.has(p_coord)) {
- return tile_map[p_id].autotile_data.z_index_map[p_coord];
+int TileSet::get_terrains_count(int p_terrain_set) const {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1);
+ return terrain_sets[p_terrain_set].terrains.size();
+}
+
+void TileSet::set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ ERR_FAIL_INDEX(p_terrain_index, terrain_sets[p_terrain_set].terrains.size());
+ terrain_sets.write[p_terrain_set].terrains.write[p_terrain_index].name = p_name;
+ emit_changed();
+}
+
+String TileSet::get_terrain_name(int p_terrain_set, int p_terrain_index) const {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), String());
+ ERR_FAIL_INDEX_V(p_terrain_index, terrain_sets[p_terrain_set].terrains.size(), String());
+ return terrain_sets[p_terrain_set].terrains[p_terrain_index].name;
+}
+
+void TileSet::set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color) {
+ ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
+ ERR_FAIL_INDEX(p_terrain_index, terrain_sets[p_terrain_set].terrains.size());
+ if (p_color.a != 1.0) {
+ WARN_PRINT("Terrain color should have alpha == 1.0");
+ p_color.a = 1.0;
}
- //When not custom z index set return the default value
- return 0;
+ terrain_sets.write[p_terrain_set].terrains.write[p_terrain_index].color = p_color;
+ emit_changed();
}
-const Map<Vector2, int> &TileSet::autotile_get_z_index_map(int p_id) const {
- static Map<Vector2, int> dummy;
- ERR_FAIL_COND_V(!tile_map.has(p_id), dummy);
- return tile_map[p_id].autotile_data.z_index_map;
+Color TileSet::get_terrain_color(int p_terrain_set, int p_terrain_index) const {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Color());
+ ERR_FAIL_INDEX_V(p_terrain_index, terrain_sets[p_terrain_set].terrains.size(), Color());
+ return terrain_sets[p_terrain_set].terrains[p_terrain_index].color;
}
-void TileSet::autotile_set_bitmask(int p_id, Vector2 p_coord, uint32_t p_flag) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- if (p_flag == 0) {
- if (tile_map[p_id].autotile_data.flags.has(p_coord)) {
- tile_map[p_id].autotile_data.flags.erase(p_coord);
+bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const {
+ if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) {
+ return false;
+ }
+
+ TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set);
+ if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_SIDE) {
+ return true;
+ }
+ }
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) {
+ return true;
+ }
+ }
+ } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return true;
+ }
+ }
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_CORNER) {
+ return true;
+ }
}
} else {
- tile_map[p_id].autotile_data.flags[p_coord] = p_flag;
+ if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return true;
+ }
+ }
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) {
+ return true;
+ }
+ }
+ } else {
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_SIDE ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) {
+ return true;
+ }
+ }
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER ||
+ p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+// Navigation
+void TileSet::set_navigation_layers_count(int p_navigation_layers_count) {
+ ERR_FAIL_COND(p_navigation_layers_count < 0);
+ if (navigation_layers.size() == p_navigation_layers_count) {
+ return;
+ }
+
+ navigation_layers.resize(p_navigation_layers_count);
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
}
+
+ notify_property_list_changed();
+ emit_changed();
+}
+
+int TileSet::get_navigation_layers_count() const {
+ return navigation_layers.size();
+}
+
+void TileSet::set_navigation_layer_layers(int p_layer_index, uint32_t p_layers) {
+ ERR_FAIL_INDEX(p_layer_index, navigation_layers.size());
+ navigation_layers.write[p_layer_index].layers = p_layers;
+ emit_changed();
}
-uint32_t TileSet::autotile_get_bitmask(int p_id, Vector2 p_coord) {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
- if (!tile_map[p_id].autotile_data.flags.has(p_coord)) {
- return 0;
+uint32_t TileSet::get_navigation_layer_layers(int p_layer_index) const {
+ ERR_FAIL_INDEX_V(p_layer_index, navigation_layers.size(), 0);
+ return navigation_layers[p_layer_index].layers;
+}
+
+// Custom data.
+void TileSet::set_custom_data_layers_count(int p_custom_data_layers_count) {
+ ERR_FAIL_COND(p_custom_data_layers_count < 0);
+ if (custom_data_layers.size() == p_custom_data_layers_count) {
+ return;
}
- return tile_map[p_id].autotile_data.flags[p_coord];
+
+ custom_data_layers.resize(p_custom_data_layers_count);
+
+ for (Map<String, int>::Element *E = custom_data_layers_by_name.front(); E; E = E->next()) {
+ if (E->get() >= custom_data_layers.size()) {
+ custom_data_layers_by_name.erase(E);
+ }
+ }
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
+ notify_property_list_changed();
+ emit_changed();
}
-const Map<Vector2, uint32_t> &TileSet::autotile_get_bitmask_map(int p_id) {
- static Map<Vector2, uint32_t> dummy;
- static Map<Vector2, uint32_t> dummy_atlas;
- ERR_FAIL_COND_V(!tile_map.has(p_id), dummy);
- if (tile_get_tile_mode(p_id) == ATLAS_TILE) {
- dummy_atlas = Map<Vector2, uint32_t>();
- Rect2 region = tile_get_region(p_id);
- Size2 size = autotile_get_size(p_id);
- float spacing = autotile_get_spacing(p_id);
- for (int x = 0; x < (region.size.x / (size.x + spacing)); x++) {
- for (int y = 0; y < (region.size.y / (size.y + spacing)); y++) {
- dummy_atlas.insert(Vector2(x, y), 0);
+int TileSet::get_custom_data_layers_count() const {
+ return custom_data_layers.size();
+}
+
+int TileSet::get_custom_data_layer_by_name(String p_value) const {
+ if (custom_data_layers_by_name.has(p_value)) {
+ return custom_data_layers_by_name[p_value];
+ } else {
+ return -1;
+ }
+}
+
+void TileSet::set_custom_data_name(int p_layer_id, String p_value) {
+ ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size());
+
+ // Exit if another property has the same name.
+ if (!p_value.is_empty()) {
+ for (int other_layer_id = 0; other_layer_id < get_custom_data_layers_count(); other_layer_id++) {
+ if (other_layer_id != p_layer_id && get_custom_data_name(other_layer_id) == p_value) {
+ ERR_FAIL_MSG(vformat("There is already a custom property named %s", p_value));
}
}
- return dummy_atlas;
+ }
+
+ if (p_value.is_empty() && custom_data_layers_by_name.has(p_value)) {
+ custom_data_layers_by_name.erase(p_value);
} else {
- return tile_map[p_id].autotile_data.flags;
+ custom_data_layers_by_name[p_value] = p_layer_id;
}
+
+ custom_data_layers.write[p_layer_id].name = p_value;
+ emit_changed();
+}
+
+String TileSet::get_custom_data_name(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, custom_data_layers.size(), "");
+ return custom_data_layers[p_layer_id].name;
+}
+
+void TileSet::set_custom_data_type(int p_layer_id, Variant::Type p_value) {
+ ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size());
+ custom_data_layers.write[p_layer_id].type = p_value;
+
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ E_source->get()->notify_tile_data_properties_should_change();
+ }
+
+ emit_changed();
+}
+
+Variant::Type TileSet::get_custom_data_type(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, custom_data_layers.size(), Variant::NIL);
+ return custom_data_layers[p_layer_id].type;
+}
+
+void TileSet::_source_changed() {
+ emit_changed();
+ notify_property_list_changed();
+}
+
+void TileSet::reset_state() {
+ occlusion_layers.clear();
+ physics_layers.clear();
+ custom_data_layers.clear();
}
-Vector2 TileSet::autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, const Node *p_tilemap_node, const Vector2 &p_tile_location) {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- //First try to forward selection to script
- if (p_tilemap_node->get_class_name() == "TileMap") {
- if (get_script_instance() != nullptr) {
- if (get_script_instance()->has_method("_forward_subtile_selection")) {
- Variant ret = get_script_instance()->call("_forward_subtile_selection", p_id, p_bitmask, p_tilemap_node, p_tile_location);
- if (ret.get_type() == Variant::VECTOR2) {
- return ret;
+const Vector2i TileSetAtlasSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1);
+const int TileSetAtlasSource::INVALID_TILE_ALTERNATIVE = -1;
+
+#ifndef DISABLE_DEPRECATED
+void TileSet::compatibility_conversion() {
+ for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
+ CompatibilityTileData *ctd = E->value();
+
+ // Add the texture
+ TileSetAtlasSource *atlas_source = memnew(TileSetAtlasSource);
+ int source_id = add_source(Ref<TileSetSource>(atlas_source));
+
+ atlas_source->set_texture(ctd->texture);
+
+ // Handle each tile as a new source. Not optimal but at least it should stay compatible.
+ switch (ctd->tile_mode) {
+ case 0: // SINGLE_TILE
+ // TODO
+ break;
+ case 1: // AUTO_TILE
+ // TODO
+ break;
+ case 2: // ATLAS_TILE
+ atlas_source->set_margins(ctd->region.get_position());
+ atlas_source->set_separation(Vector2i(ctd->autotile_spacing, ctd->autotile_spacing));
+ atlas_source->set_texture_region_size(ctd->autotile_tile_size);
+
+ Size2i atlas_size = ctd->region.get_size() / (ctd->autotile_tile_size + atlas_source->get_separation());
+ for (int i = 0; i < atlas_size.x; i++) {
+ for (int j = 0; j < atlas_size.y; j++) {
+ Vector2i coords = Vector2i(i, j);
+
+ for (int flags = 0; flags < 8; flags++) {
+ bool flip_h = flags & 1;
+ bool flip_v = flags & 2;
+ bool transpose = flags & 4;
+
+ int alternative_tile = 0;
+ if (!atlas_source->has_tile(coords)) {
+ atlas_source->create_tile(coords);
+ } else {
+ alternative_tile = atlas_source->create_alternative_tile(coords);
+ }
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile));
+
+ tile_data->set_flip_h(flip_h);
+ tile_data->set_flip_v(flip_v);
+ tile_data->set_transpose(transpose);
+ tile_data->tile_set_material(ctd->material);
+ tile_data->set_modulate(ctd->modulate);
+ tile_data->set_z_index(ctd->z_index);
+ if (ctd->autotile_occluder_map.has(coords)) {
+ if (get_occlusion_layers_count() < 1) {
+ set_occlusion_layers_count(1);
+ }
+ tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]);
+ }
+ if (ctd->autotile_navpoly_map.has(coords)) {
+ if (get_navigation_layers_count() < 1) {
+ set_navigation_layers_count(1);
+ }
+ tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]);
+ }
+ if (ctd->autotile_priority_map.has(coords)) {
+ tile_data->set_probability(ctd->autotile_priority_map[coords]);
+ }
+ if (ctd->autotile_z_index_map.has(coords)) {
+ tile_data->set_z_index(ctd->autotile_z_index_map[coords]);
+ }
+
+ // Add the shapes.
+ if (ctd->shapes.size() > 0) {
+ if (get_physics_layers_count() < 1) {
+ set_physics_layers_count(1);
+ }
+ }
+ for (int k = 0; k < ctd->shapes.size(); k++) {
+ CompatibilityShapeData csd = ctd->shapes[k];
+ if (csd.autotile_coords == coords) {
+ tile_data->set_collision_shapes_count(0, tile_data->get_collision_shapes_count(0) + 1);
+ int index = tile_data->get_collision_shapes_count(0) - 1;
+ tile_data->set_collision_shape_one_way(0, index, csd.one_way);
+ tile_data->set_collision_shape_one_way_margin(0, index, csd.one_way_margin);
+ tile_data->set_collision_shape_shape(0, index, csd.shape);
+ // Ignores transform for now.
+ }
+ }
+
+ // -- TODO: handle --
+ // Those are offset for the whole atlas, they are likely useless for the atlases, but might make sense for single tiles.
+ // texture offset
+ // occluder_offset
+ // navigation_offset
+
+ // For terrains, ignored for now?
+ // bitmask_mode
+ // bitmask_flags
+ }
+ }
+ }
+ break;
+ }
+
+ // Offset all shapes
+ for (int k = 0; k < ctd->shapes.size(); k++) {
+ Ref<ConvexPolygonShape2D> convex = ctd->shapes[k].shape;
+ if (convex.is_valid()) {
+ Vector<Vector2> points = convex->get_points();
+ for (int i_point = 0; i_point < points.size(); i_point++) {
+ points.write[i_point] = points[i_point] - get_tile_size() / 2;
}
+ convex->set_points(points);
}
}
+
+ // Add the mapping to the map
+ compatibility_source_mapping.insert(E->key(), source_id);
+ }
+
+ // Reset compatibility data
+ for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
+ memdelete(E->get());
}
+ compatibility_data = Map<int, CompatibilityTileData *>();
+}
+#endif // DISABLE_DEPRECATED
- List<Vector2> coords;
- List<uint32_t> priorities;
- uint32_t priority_sum = 0;
- uint32_t mask;
- uint16_t mask_;
- uint16_t mask_ignore;
- for (Map<Vector2, uint32_t>::Element *E = tile_map[p_id].autotile_data.flags.front(); E; E = E->next()) {
- mask = E->get();
- if (tile_map[p_id].autotile_data.bitmask_mode == BITMASK_2X2) {
- mask |= (BIND_IGNORE_TOP | BIND_IGNORE_LEFT | BIND_IGNORE_CENTER | BIND_IGNORE_RIGHT | BIND_IGNORE_BOTTOM);
+bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+#ifndef DISABLE_DEPRECATED
+ // TODO: THIS IS HOW WE CHECK IF WE HAVE A DEPRECATED RESOURCE
+ // This should be moved to a dedicated conversion system
+ if (components.size() >= 1 && components[0].is_valid_integer()) {
+ int id = components[0].to_int();
+
+ // Get or create the compatibility object
+ CompatibilityTileData *ctd;
+ Map<int, CompatibilityTileData *>::Element *E = compatibility_data.find(id);
+ if (!E) {
+ ctd = memnew(CompatibilityTileData);
+ compatibility_data.insert(id, ctd);
+ } else {
+ ctd = E->get();
}
- mask_ = mask & 0xFFFF;
- mask_ignore = mask >> 16;
+ if (components.size() < 2) {
+ return false;
+ }
- if (((mask_ & (~mask_ignore)) == (p_bitmask & (~mask_ignore))) && (((~mask_) | mask_ignore) == ((~p_bitmask) | mask_ignore))) {
- uint32_t priority = autotile_get_subtile_priority(p_id, E->key());
- priority_sum += priority;
- priorities.push_back(priority);
- coords.push_back(E->key());
+ String what = components[1];
+
+ if (what == "name") {
+ ctd->name = p_value;
+ } else if (what == "texture") {
+ ctd->texture = p_value;
+ } else if (what == "tex_offset") {
+ ctd->tex_offset = p_value;
+ } else if (what == "material") {
+ ctd->material = p_value;
+ } else if (what == "modulate") {
+ ctd->modulate = p_value;
+ } else if (what == "region") {
+ ctd->region = p_value;
+ } else if (what == "tile_mode") {
+ ctd->tile_mode = p_value;
+ } else if (what.left(9) == "autotile") {
+ what = what.right(9);
+ if (what == "bitmask_mode") {
+ ctd->autotile_bitmask_mode = p_value;
+ } else if (what == "icon_coordinate") {
+ ctd->autotile_icon_coordinate = p_value;
+ } else if (what == "tile_size") {
+ ctd->autotile_tile_size = p_value;
+ } else if (what == "spacing") {
+ ctd->autotile_spacing = p_value;
+ } else if (what == "bitmask_flags") {
+ if (p_value.is_array()) {
+ Array p = p_value;
+ Vector2i last_coord;
+ while (p.size() > 0) {
+ if (p[0].get_type() == Variant::VECTOR2) {
+ last_coord = p[0];
+ } else if (p[0].get_type() == Variant::INT) {
+ ctd->autotile_bitmask_flags.insert(last_coord, p[0]);
+ }
+ p.pop_front();
+ }
+ }
+ } else if (what == "occluder_map") {
+ Array p = p_value;
+ Vector2 last_coord;
+ while (p.size() > 0) {
+ if (p[0].get_type() == Variant::VECTOR2) {
+ last_coord = p[0];
+ } else if (p[0].get_type() == Variant::OBJECT) {
+ ctd->autotile_occluder_map.insert(last_coord, p[0]);
+ }
+ p.pop_front();
+ }
+ } else if (what == "navpoly_map") {
+ Array p = p_value;
+ Vector2 last_coord;
+ while (p.size() > 0) {
+ if (p[0].get_type() == Variant::VECTOR2) {
+ last_coord = p[0];
+ } else if (p[0].get_type() == Variant::OBJECT) {
+ ctd->autotile_navpoly_map.insert(last_coord, p[0]);
+ }
+ p.pop_front();
+ }
+ } else if (what == "priority_map") {
+ Array p = p_value;
+ Vector3 val;
+ Vector2 v;
+ int priority;
+ while (p.size() > 0) {
+ val = p[0];
+ if (val.z > 1) {
+ v.x = val.x;
+ v.y = val.y;
+ priority = (int)val.z;
+ ctd->autotile_priority_map.insert(v, priority);
+ }
+ p.pop_front();
+ }
+ } else if (what == "z_index_map") {
+ Array p = p_value;
+ Vector3 val;
+ Vector2 v;
+ int z_index;
+ while (p.size() > 0) {
+ val = p[0];
+ if (val.z != 0) {
+ v.x = val.x;
+ v.y = val.y;
+ z_index = (int)val.z;
+ ctd->autotile_z_index_map.insert(v, z_index);
+ }
+ p.pop_front();
+ }
+ }
+
+ } else if (what == "shapes") {
+ Array p = p_value;
+ for (int i = 0; i < p.size(); i++) {
+ CompatibilityShapeData csd;
+ Dictionary d = p[i];
+ for (int j = 0; j < d.size(); j++) {
+ String key = d.get_key_at_index(j);
+ if (key == "autotile_coord") {
+ csd.autotile_coords = d[key];
+ } else if (key == "one_way") {
+ csd.one_way = d[key];
+ } else if (key == "one_way_margin") {
+ csd.one_way_margin = d[key];
+ } else if (key == "shape") {
+ csd.shape = d[key];
+ } else if (key == "shape_transform") {
+ csd.transform = d[key];
+ }
+ }
+ ctd->shapes.push_back(csd);
+ }
+
+ /*
+ // IGNORED FOR NOW, they seem duplicated data compared to the shapes array
+ } else if (what == "shape") {
+ // TODO
+ } else if (what == "shape_offset") {
+ // TODO
+ } else if (what == "shape_transform") {
+ // TODO
+ } else if (what == "shape_one_way") {
+ // TODO
+ } else if (what == "shape_one_way_margin") {
+ // TODO
}
- }
+ // IGNORED FOR NOW, maybe useless ?
+ else if (what == "occluder_offset") {
+ // Not
+ } else if (what == "navigation_offset") {
+ // TODO
+ }
+ */
- if (coords.size() == 0) {
- return autotile_get_icon_coordinate(p_id);
+ } else if (what == "z_index") {
+ ctd->z_index = p_value;
+
+ // TODO: remove the conversion from here, it's not where it should be done
+ compatibility_conversion();
+ } else {
+ return false;
+ }
} else {
- uint32_t picked_value = Math::rand() % priority_sum;
- uint32_t upper_bound;
- uint32_t lower_bound = 0;
- Vector2 result = coords.front()->get();
- List<Vector2>::Element *coords_E = coords.front();
- List<uint32_t>::Element *priorities_E = priorities.front();
- while (priorities_E) {
- upper_bound = lower_bound + priorities_E->get();
- if (lower_bound <= picked_value && picked_value < upper_bound) {
- result = coords_E->get();
- break;
+#endif // DISABLE_DEPRECATED
+
+ // This is now a new property.
+ if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) {
+ // Occlusion layers.
+ int index = components[0].trim_prefix("occlusion_layer_").to_int();
+ ERR_FAIL_COND_V(index < 0, false);
+ if (components[1] == "light_mask") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (index >= occlusion_layers.size()) {
+ set_occlusion_layers_count(index + 1);
+ }
+ set_occlusion_layer_light_mask(index, p_value);
+ return true;
+ } else if (components[1] == "sdf_collision") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false);
+ if (index >= occlusion_layers.size()) {
+ set_occlusion_layers_count(index + 1);
+ }
+ set_occlusion_layer_sdf_collision(index, p_value);
+ return true;
+ }
+ } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) {
+ // Physics layers.
+ int index = components[0].trim_prefix("physics_layer_").to_int();
+ ERR_FAIL_COND_V(index < 0, false);
+ if (components[1] == "collision_layer") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (index >= physics_layers.size()) {
+ set_physics_layers_count(index + 1);
+ }
+ set_physics_layer_collision_layer(index, p_value);
+ return true;
+ } else if (components[1] == "collision_mask") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (index >= physics_layers.size()) {
+ set_physics_layers_count(index + 1);
+ }
+ set_physics_layer_collision_mask(index, p_value);
+ return true;
+ } else if (components[1] == "physics_material") {
+ Ref<PhysicsMaterial> physics_material = p_value;
+ ERR_FAIL_COND_V(!physics_material.is_valid(), false);
+ if (index >= physics_layers.size()) {
+ set_physics_layers_count(index + 1);
+ }
+ set_physics_layer_physics_material(index, physics_material);
+ return true;
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_integer()) {
+ // Terrains.
+ int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int();
+ ERR_FAIL_COND_V(terrain_set_index < 0, false);
+ if (components[1] == "mode") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (terrain_set_index >= terrain_sets.size()) {
+ set_terrain_sets_count(terrain_set_index + 1);
+ }
+ set_terrain_set_mode(terrain_set_index, TerrainMode(int(p_value)));
+ } else if (components[1] == "terrains_count") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (terrain_set_index >= terrain_sets.size()) {
+ set_terrain_sets_count(terrain_set_index + 1);
+ }
+ set_terrains_count(terrain_set_index, p_value);
+ return true;
+ } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_integer()) {
+ int terrain_index = components[1].trim_prefix("terrain_").to_int();
+ ERR_FAIL_COND_V(terrain_index < 0, false);
+ if (components[2] == "name") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
+ if (terrain_set_index >= terrain_sets.size()) {
+ set_terrain_sets_count(terrain_set_index + 1);
+ }
+ if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ set_terrains_count(terrain_set_index, terrain_index + 1);
+ }
+ set_terrain_name(terrain_set_index, terrain_index, p_value);
+ return true;
+ } else if (components[2] == "color") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::COLOR, false);
+ if (terrain_set_index >= terrain_sets.size()) {
+ set_terrain_sets_count(terrain_set_index + 1);
+ }
+ if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ set_terrains_count(terrain_set_index, terrain_index + 1);
+ }
+ set_terrain_color(terrain_set_index, terrain_index, p_value);
+ return true;
+ }
+ }
+ } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) {
+ // Navigation layers.
+ int index = components[0].trim_prefix("navigation_layer_").to_int();
+ ERR_FAIL_COND_V(index < 0, false);
+ if (components[1] == "layers") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (index >= navigation_layers.size()) {
+ set_navigation_layers_count(index + 1);
+ }
+ set_navigation_layer_layers(index, p_value);
+ return true;
+ }
+ } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_integer()) {
+ // Custom data layers.
+ int index = components[0].trim_prefix("custom_data_layer_").to_int();
+ ERR_FAIL_COND_V(index < 0, false);
+ if (components[1] == "name") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false);
+ if (index >= custom_data_layers.size()) {
+ set_custom_data_layers_count(index + 1);
+ }
+ set_custom_data_name(index, p_value);
+ return true;
+ } else if (components[1] == "type") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false);
+ if (index >= custom_data_layers.size()) {
+ set_custom_data_layers_count(index + 1);
+ }
+ set_custom_data_type(index, Variant::Type(int(p_value)));
+ return true;
}
- lower_bound = upper_bound;
- priorities_E = priorities_E->next();
- coords_E = coords_E->next();
+ } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_integer()) {
+ // Create atlas if it does not exists.
+ int source_id = components[1].to_int();
+
+ if (!has_source(source_id)) {
+ add_source(p_value, source_id);
+ }
+ return true;
}
- return result;
+#ifndef DISABLE_DEPRECATED
}
+#endif // DISABLE_DEPRECATED
+
+ return false;
}
-Vector2 TileSet::atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node, const Vector2 &p_tile_location) {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- //First try to forward selection to script
- if (get_script_instance() != nullptr) {
- if (get_script_instance()->has_method("_forward_atlas_subtile_selection")) {
- Variant ret = get_script_instance()->call("_forward_atlas_subtile_selection", p_id, p_tilemap_node, p_tile_location);
- if (ret.get_type() == Variant::VECTOR2) {
- return ret;
+bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+ if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) {
+ // Occlusion layers.
+ int index = components[0].trim_prefix("occlusion_layer_").to_int();
+ if (index < 0 || index >= occlusion_layers.size()) {
+ return false;
+ }
+ if (components[1] == "light_mask") {
+ r_ret = get_occlusion_layer_light_mask(index);
+ return true;
+ } else if (components[1] == "sdf_collision") {
+ r_ret = get_occlusion_layer_sdf_collision(index);
+ return true;
+ }
+ } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) {
+ // Physics layers.
+ int index = components[0].trim_prefix("physics_layer_").to_int();
+ if (index < 0 || index >= physics_layers.size()) {
+ return false;
+ }
+ if (components[1] == "collision_layer") {
+ r_ret = get_physics_layer_collision_layer(index);
+ return true;
+ } else if (components[1] == "collision_mask") {
+ r_ret = get_physics_layer_collision_mask(index);
+ return true;
+ } else if (components[1] == "physics_material") {
+ r_ret = get_physics_layer_physics_material(index);
+ return true;
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_integer()) {
+ // Terrains.
+ int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int();
+ if (terrain_set_index < 0 || terrain_set_index >= terrain_sets.size()) {
+ return false;
+ }
+ if (components[1] == "mode") {
+ r_ret = get_terrain_set_mode(terrain_set_index);
+ return true;
+ } else if (components[1] == "terrains_count") {
+ r_ret = get_terrains_count(terrain_set_index);
+ return true;
+ } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_integer()) {
+ int terrain_index = components[1].trim_prefix("terrain_").to_int();
+ if (terrain_index < 0 || terrain_index >= terrain_sets[terrain_set_index].terrains.size()) {
+ return false;
+ }
+ if (components[2] == "name") {
+ r_ret = get_terrain_name(terrain_set_index, terrain_index);
+ return true;
+ } else if (components[2] == "color") {
+ r_ret = get_terrain_color(terrain_set_index, terrain_index);
+ return true;
}
}
+ } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) {
+ // navigation layers.
+ int index = components[0].trim_prefix("navigation_layer_").to_int();
+ if (index < 0 || index >= navigation_layers.size()) {
+ return false;
+ }
+ if (components[1] == "layers") {
+ r_ret = get_navigation_layer_layers(index);
+ return true;
+ }
+ } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_integer()) {
+ // Custom data layers.
+ int index = components[0].trim_prefix("custom_data_layer_").to_int();
+ if (index < 0 || index >= custom_data_layers.size()) {
+ return false;
+ }
+ if (components[1] == "name") {
+ r_ret = get_custom_data_name(index);
+ return true;
+ } else if (components[1] == "type") {
+ r_ret = get_custom_data_type(index);
+ return true;
+ }
+ } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_integer()) {
+ // Atlases data.
+ int source_id = components[1].to_int();
+
+ if (has_source(source_id)) {
+ r_ret = get_source(source_id);
+ return true;
+ } else {
+ return false;
+ }
}
- Vector2 coord = tile_get_region(p_id).size / autotile_get_size(p_id);
+ return false;
+}
- List<Vector2> coords;
- for (int x = 0; x < coord.x; x++) {
- for (int y = 0; y < coord.y; y++) {
- for (int i = 0; i < autotile_get_subtile_priority(p_id, Vector2(x, y)); i++) {
- coords.push_back(Vector2(x, y));
- }
+void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
+ PropertyInfo property_info;
+ // Rendering.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < occlusion_layers.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("occlusion_layer_%d/light_mask", i), PROPERTY_HINT_LAYERS_2D_RENDER));
+
+ // occlusion_layer_%d/sdf_collision
+ property_info = PropertyInfo(Variant::BOOL, vformat("occlusion_layer_%d/sdf_collision", i));
+ if (occlusion_layers[i].sdf_collision == false) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
+ p_list->push_back(property_info);
}
- if (coords.size() == 0) {
- return autotile_get_icon_coordinate(p_id);
- } else {
- return coords[Math::random(0, (int)coords.size())];
+
+ // Physics.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < physics_layers.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/collision_layer", i), PROPERTY_HINT_LAYERS_2D_PHYSICS));
+
+ // physics_layer_%d/collision_mask
+ property_info = PropertyInfo(Variant::INT, vformat("physics_layer_%d/collision_mask", i), PROPERTY_HINT_LAYERS_2D_PHYSICS);
+ if (physics_layers[i].collision_mask == 1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+
+ // physics_layer_%d/physics_material
+ property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/physics_material", i), PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial");
+ if (!physics_layers[i].physics_material.is_valid()) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
}
-}
-void TileSet::tile_set_name(int p_id, const String &p_name) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].name = p_name;
- emit_changed();
+ // Terrains.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides"));
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/terrains_count", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+ for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index)));
+ p_list->push_back(PropertyInfo(Variant::COLOR, vformat("terrain_set_%d/terrain_%d/color", terrain_set_index, terrain_index)));
+ }
+ }
+
+ // Navigation.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Navigation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < navigation_layers.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("navigation_layer_%d/layers", i), PROPERTY_HINT_LAYERS_2D_NAVIGATION));
+ }
+
+ // Custom data.
+ String argt = "Any";
+ for (int i = 1; i < Variant::VARIANT_MAX; i++) {
+ argt += "," + Variant::get_type_name(Variant::Type(i));
+ }
+ p_list->push_back(PropertyInfo(Variant::NIL, "Custom data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < custom_data_layers.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("custom_data_layer_%d/name", i)));
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("custom_data_layer_%d/type", i), PROPERTY_HINT_ENUM, argt));
+ }
+
+ // Sources.
+ // Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first.
+ for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ }
}
-String TileSet::tile_get_name(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), String());
- return tile_map[p_id].name;
+void TileSet::_bind_methods() {
+ // Sources management.
+ ClassDB::bind_method(D_METHOD("get_next_source_id"), &TileSet::get_next_source_id);
+ ClassDB::bind_method(D_METHOD("add_source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("remove_source", "source_id"), &TileSet::remove_source);
+ ClassDB::bind_method(D_METHOD("set_source_id", "source_id"), &TileSet::set_source_id);
+ ClassDB::bind_method(D_METHOD("get_source_count"), &TileSet::get_source_count);
+ ClassDB::bind_method(D_METHOD("get_source_id", "index"), &TileSet::get_source_id);
+ ClassDB::bind_method(D_METHOD("has_source", "index"), &TileSet::has_source);
+ ClassDB::bind_method(D_METHOD("get_source", "index"), &TileSet::get_source);
+
+ // Shape and layout.
+ ClassDB::bind_method(D_METHOD("set_tile_shape", "shape"), &TileSet::set_tile_shape);
+ ClassDB::bind_method(D_METHOD("get_tile_shape"), &TileSet::get_tile_shape);
+ ClassDB::bind_method(D_METHOD("set_tile_layout", "layout"), &TileSet::set_tile_layout);
+ ClassDB::bind_method(D_METHOD("get_tile_layout"), &TileSet::get_tile_layout);
+ ClassDB::bind_method(D_METHOD("set_tile_offset_axis", "alignment"), &TileSet::set_tile_offset_axis);
+ ClassDB::bind_method(D_METHOD("get_tile_offset_axis"), &TileSet::get_tile_offset_axis);
+ ClassDB::bind_method(D_METHOD("set_tile_size", "size"), &TileSet::set_tile_size);
+ ClassDB::bind_method(D_METHOD("get_tile_size"), &TileSet::get_tile_size);
+ ClassDB::bind_method(D_METHOD("set_tile_skew", "skew"), &TileSet::set_tile_skew);
+ ClassDB::bind_method(D_METHOD("get_tile_skew"), &TileSet::get_tile_skew);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_shape", PROPERTY_HINT_ENUM, "Square,Isometric,Half-offset square,Hexagon"), "set_tile_shape", "get_tile_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_layout", PROPERTY_HINT_ENUM, "Stacked,Stacked Offset,Stairs Right,Stairs Down,Diamond Right,Diamond Down"), "set_tile_layout", "get_tile_layout");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_offset_axis", PROPERTY_HINT_ENUM, "Horizontal Offset,Vertical Offset"), "set_tile_offset_axis", "get_tile_offset_axis");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size"), "set_tile_size", "get_tile_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_skew"), "set_tile_skew", "get_tile_skew");
+
+ // Rendering.
+ ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping);
+ ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping);
+ ClassDB::bind_method(D_METHOD("set_y_sorting", "y_sorting"), &TileSet::set_y_sorting);
+ ClassDB::bind_method(D_METHOD("is_y_sorting"), &TileSet::is_y_sorting);
+
+ ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count);
+ ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count);
+ ClassDB::bind_method(D_METHOD("set_occlusion_layer_light_mask", "layer_index", "light_mask"), &TileSet::set_occlusion_layer_light_mask);
+ ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask"), &TileSet::get_occlusion_layer_light_mask);
+ ClassDB::bind_method(D_METHOD("set_occlusion_layer_sdf_collision", "layer_index", "sdf_collision"), &TileSet::set_occlusion_layer_sdf_collision);
+ ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision"), &TileSet::get_occlusion_layer_sdf_collision);
+
+ // Physics
+ ClassDB::bind_method(D_METHOD("set_physics_layers_count", "physics_layers_count"), &TileSet::set_physics_layers_count);
+ ClassDB::bind_method(D_METHOD("get_physics_layers_count"), &TileSet::get_physics_layers_count);
+ ClassDB::bind_method(D_METHOD("set_physics_layer_collision_layer", "layer_index", "layer"), &TileSet::set_physics_layer_collision_layer);
+ ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer);
+ ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_physics_layer_collision_mask", "layer_index"), &TileSet::get_physics_layer_collision_mask);
+ ClassDB::bind_method(D_METHOD("set_physics_layer_physics_material", "layer_index", "physics_material"), &TileSet::set_physics_layer_physics_material);
+ ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material);
+
+ // Terrains
+ ClassDB::bind_method(D_METHOD("set_terrain_sets_count", "terrain_sets_count"), &TileSet::set_terrain_sets_count);
+ ClassDB::bind_method(D_METHOD("get_terrain_sets_count"), &TileSet::get_terrain_sets_count);
+ ClassDB::bind_method(D_METHOD("set_terrain_set_mode", "terrain_set", "mode"), &TileSet::set_terrain_set_mode);
+ ClassDB::bind_method(D_METHOD("get_terrain_set_mode", "terrain_set"), &TileSet::get_terrain_set_mode);
+
+ ClassDB::bind_method(D_METHOD("set_terrains_count", "terrain_set", "terrains_count"), &TileSet::set_terrains_count);
+ ClassDB::bind_method(D_METHOD("get_terrains_count", "terrain_set"), &TileSet::get_terrains_count);
+ ClassDB::bind_method(D_METHOD("set_terrain_name", "terrain_set", "terrain_index", "name"), &TileSet::set_terrain_name);
+ ClassDB::bind_method(D_METHOD("get_terrain_name", "terrain_set", "terrain_index"), &TileSet::get_terrain_name);
+ ClassDB::bind_method(D_METHOD("set_terrain_color", "terrain_set", "terrain_index", "color"), &TileSet::set_terrain_color);
+ ClassDB::bind_method(D_METHOD("get_terrain_color", "terrain_set", "terrain_index"), &TileSet::get_terrain_color);
+
+ // Navigation
+ ClassDB::bind_method(D_METHOD("set_navigation_layers_count", "navigation_layers_count"), &TileSet::set_navigation_layers_count);
+ ClassDB::bind_method(D_METHOD("get_navigation_layers_count"), &TileSet::get_navigation_layers_count);
+ ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers);
+ ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers);
+
+ // Custom data
+ ClassDB::bind_method(D_METHOD("set_custom_data_layers_count", "custom_data_layers_count"), &TileSet::set_custom_data_layers_count);
+ ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count);
+
+ ADD_GROUP("Rendering", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "y_sorting"), "set_y_sorting", "is_y_sorting");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count");
+
+ ADD_GROUP("Physics", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_physics_layers_count", "get_physics_layers_count");
+
+ ADD_GROUP("Terrains", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "terrains_sets_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_terrain_sets_count", "get_terrain_sets_count");
+
+ ADD_GROUP("Navigation", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_navigation_layers_count", "get_navigation_layers_count");
+
+ ADD_GROUP("Custom data", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_custom_data_layers_count", "get_custom_data_layers_count");
+
+ // -- Enum binding --
+ BIND_ENUM_CONSTANT(TILE_SHAPE_SQUARE);
+ BIND_ENUM_CONSTANT(TILE_SHAPE_ISOMETRIC);
+ BIND_ENUM_CONSTANT(TILE_SHAPE_HALF_OFFSET_SQUARE);
+ BIND_ENUM_CONSTANT(TILE_SHAPE_HEXAGON);
+
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_STACKED);
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_STACKED_OFFSET);
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_STAIRS_RIGHT);
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_STAIRS_DOWN);
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_DIAMOND_RIGHT);
+ BIND_ENUM_CONSTANT(TILE_LAYOUT_DIAMOND_DOWN);
+
+ BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_HORIZONTAL);
+ BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_VERTICAL);
+
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_SIDE);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_CORNER);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_CORNER);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_CORNER);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER);
+
+ BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS_AND_SIDES);
+ BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS);
+ BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_SIDES);
}
-void TileSet::tile_clear_shapes(int p_id) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].shapes_data.clear();
+TileSet::TileSet() {
+ // Instanciatie and list all plugins.
+ tile_set_plugins_vector.append(memnew(TileSetAtlasPluginRendering));
+ tile_set_plugins_vector.append(memnew(TileSetAtlasPluginPhysics));
+ tile_set_plugins_vector.append(memnew(TileSetAtlasPluginTerrain));
+ tile_set_plugins_vector.append(memnew(TileSetAtlasPluginNavigation));
}
-void TileSet::tile_add_shape(int p_id, const Ref<Shape2D> &p_shape, const Transform2D &p_transform, bool p_one_way, const Vector2 &p_autotile_coord) {
- ERR_FAIL_COND(!tile_map.has(p_id));
+TileSet::~TileSet() {
+ for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ while (!source_ids.is_empty()) {
+ remove_source(source_ids[0]);
+ }
+ for (int i = 0; i < tile_set_plugins_vector.size(); i++) {
+ memdelete(tile_set_plugins_vector[i]);
+ }
+}
- ShapeData new_data = ShapeData();
- new_data.shape = p_shape;
- new_data.shape_transform = p_transform;
- new_data.one_way_collision = p_one_way;
- new_data.autotile_coord = p_autotile_coord;
+/////////////////////////////// TileSetSource //////////////////////////////////////
- tile_map[p_id].shapes_data.push_back(new_data);
+void TileSetSource::set_tile_set(const TileSet *p_tile_set) {
+ tile_set = p_tile_set;
}
-int TileSet::tile_get_shape_count(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
- return tile_map[p_id].shapes_data.size();
+/////////////////////////////// TileSetAtlasSource //////////////////////////////////////
+
+void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) {
+ tile_set = p_tile_set;
+
+ // Set the TileSet on all TileData.
+ for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
+ for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
+ E_alternative->get()->set_tile_set(tile_set);
+ }
+ }
}
-void TileSet::tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_shape) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_shape_id < 0);
+void TileSetAtlasSource::notify_tile_data_properties_should_change() {
+ // Set the TileSet on all TileData.
+ for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
+ for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
+ E_alternative->get()->notify_tile_data_properties_should_change();
+ }
+ }
+}
- if (p_shape_id >= tile_map[p_id].shapes_data.size()) {
- tile_map[p_id].shapes_data.resize(p_shape_id + 1);
+void TileSetAtlasSource::reset_state() {
+ // Reset all TileData.
+ for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
+ for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
+ E_alternative->get()->reset_state();
+ }
}
- tile_map[p_id].shapes_data.write[p_shape_id].shape = p_shape;
- _decompose_convex_shape(p_shape);
+}
+
+void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
+ texture = p_texture;
+
emit_changed();
}
-Ref<Shape2D> TileSet::tile_get_shape(int p_id, int p_shape_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<Shape2D>());
- ERR_FAIL_COND_V(p_shape_id < 0, Ref<Shape2D>());
+Ref<Texture2D> TileSetAtlasSource::get_texture() const {
+ return texture;
+}
- if (p_shape_id < tile_map[p_id].shapes_data.size()) {
- return tile_map[p_id].shapes_data[p_shape_id].shape;
+void TileSetAtlasSource::set_margins(Vector2i p_margins) {
+ if (p_margins.x < 0 || p_margins.y < 0) {
+ WARN_PRINT("Atlas source margins should be positive.");
+ margins = Vector2i(MAX(0, p_margins.x), MAX(0, p_margins.y));
+ } else {
+ margins = p_margins;
}
- return Ref<Shape2D>();
+ emit_changed();
+}
+Vector2i TileSetAtlasSource::get_margins() const {
+ return margins;
}
-void TileSet::tile_set_shape_transform(int p_id, int p_shape_id, const Transform2D &p_offset) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_shape_id < 0);
+void TileSetAtlasSource::set_separation(Vector2i p_separation) {
+ if (p_separation.x < 0 || p_separation.y < 0) {
+ WARN_PRINT("Atlas source separation should be positive.");
+ separation = Vector2i(MAX(0, p_separation.x), MAX(0, p_separation.y));
+ } else {
+ separation = p_separation;
+ }
+
+ emit_changed();
+}
+Vector2i TileSetAtlasSource::get_separation() const {
+ return separation;
+}
- if (p_shape_id >= tile_map[p_id].shapes_data.size()) {
- tile_map[p_id].shapes_data.resize(p_shape_id + 1);
+void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) {
+ if (p_tile_size.x <= 0 || p_tile_size.y <= 0) {
+ WARN_PRINT("Atlas source tile_size should be strictly positive.");
+ texture_region_size = Vector2i(MAX(1, p_tile_size.x), MAX(1, p_tile_size.y));
+ } else {
+ texture_region_size = p_tile_size;
}
- tile_map[p_id].shapes_data.write[p_shape_id].shape_transform = p_offset;
+
emit_changed();
}
+Vector2i TileSetAtlasSource::get_texture_region_size() const {
+ return texture_region_size;
+}
+
+Vector2i TileSetAtlasSource::get_atlas_grid_size() const {
+ Ref<Texture2D> texture = get_texture();
+ if (!texture.is_valid()) {
+ return Vector2i();
+ }
+
+ ERR_FAIL_COND_V(texture_region_size.x <= 0 || texture_region_size.y <= 0, Vector2i());
+
+ Size2i valid_area = texture->get_size() - margins;
+
+ // Compute the number of valid tiles in the tiles atlas
+ Size2i grid_size = Size2i();
+ if (valid_area.x >= texture_region_size.x && valid_area.y >= texture_region_size.y) {
+ valid_area -= texture_region_size;
+ grid_size = Size2i(1, 1) + valid_area / (texture_region_size + separation);
+ }
+ return grid_size;
+}
-Transform2D TileSet::tile_get_shape_transform(int p_id, int p_shape_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Transform2D());
- ERR_FAIL_COND_V(p_shape_id < 0, Transform2D());
+bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
- if (p_shape_id < tile_map[p_id].shapes_data.size()) {
- return tile_map[p_id].shapes_data[p_shape_id].shape_transform;
+ // Compute the vector2i if we have coordinates.
+ Vector<String> coords_split = components[0].split(":");
+ Vector2i coords = TileSetAtlasSource::INVALID_ATLAS_COORDS;
+ if (coords_split.size() == 2 && coords_split[0].is_valid_integer() && coords_split[1].is_valid_integer()) {
+ coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int());
}
- return Transform2D();
+ // Properties.
+ if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ // Create the tile if needed.
+ if (!has_tile(coords)) {
+ create_tile(coords);
+ }
+ if (components.size() >= 2) {
+ // Properties.
+ if (components[1] == "size_in_atlas") {
+ move_tile_in_atlas(coords, coords, p_value);
+ } else if (components[1] == "next_alternative_id") {
+ tiles[coords].next_alternative_id = p_value;
+ } else if (components[1].is_valid_integer()) {
+ int alternative_id = components[1].to_int();
+ if (alternative_id != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ // Create the alternative if needed ?
+ if (!has_alternative_tile(coords, alternative_id)) {
+ create_alternative_tile(coords, alternative_id);
+ }
+ if (!tiles[coords].alternatives.has(alternative_id)) {
+ tiles[coords].alternatives[alternative_id] = memnew(TileData);
+ tiles[coords].alternatives[alternative_id]->set_tile_set(tile_set);
+ tiles[coords].alternatives[alternative_id]->set_allow_transform(alternative_id > 0);
+ tiles[coords].alternatives_ids.append(alternative_id);
+ }
+ if (components.size() >= 3) {
+ bool valid;
+ tiles[coords].alternatives[alternative_id]->set(components[2], p_value, &valid);
+ return valid;
+ } else {
+ // Only create the alternative if it did not exist yet.
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
}
-void TileSet::tile_set_shape_offset(int p_id, int p_shape_id, const Vector2 &p_offset) {
- Transform2D transform = tile_get_shape_transform(p_id, p_shape_id);
- transform.set_origin(p_offset);
- tile_set_shape_transform(p_id, p_shape_id, transform);
+bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+ // Properties.
+ Vector<String> coords_split = components[0].split(":");
+ if (coords_split.size() == 2 && coords_split[0].is_valid_integer() && coords_split[1].is_valid_integer()) {
+ Vector2i coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int());
+ if (tiles.has(coords)) {
+ if (components.size() >= 2) {
+ // Properties.
+ if (components[1] == "size_in_atlas") {
+ r_ret = tiles[coords].size_in_atlas;
+ return true;
+ } else if (components[1] == "next_alternative_id") {
+ r_ret = tiles[coords].next_alternative_id;
+ return true;
+ } else if (components[1].is_valid_integer()) {
+ int alternative_id = components[1].to_int();
+ if (alternative_id != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) {
+ if (components.size() >= 3) {
+ bool valid;
+ r_ret = tiles[coords].alternatives[alternative_id]->get(components[2], &valid);
+ return valid;
+ } else {
+ // Only to notify the tile alternative exists.
+ r_ret = alternative_id;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
}
-Vector2 TileSet::tile_get_shape_offset(int p_id, int p_shape_id) const {
- return tile_get_shape_transform(p_id, p_shape_id).get_origin();
+void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
+ // Atlases data.
+ PropertyInfo property_info;
+ for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
+ List<PropertyInfo> tile_property_list;
+
+ // size_in_atlas
+ property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ if (E_tile->get().size_in_atlas == Vector2i(1, 1)) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ tile_property_list.push_back(property_info);
+
+ // next_alternative_id
+ property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
+ if (E_tile->get().next_alternative_id == 1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ tile_property_list.push_back(property_info);
+
+ for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
+ // Add a dummy property to show the alternative exists.
+ tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+
+ // Get the alternative tile's properties and append them to the list of properties.
+ List<PropertyInfo> alternative_property_list;
+ E_alternative->get()->get_property_list(&alternative_property_list);
+ for (List<PropertyInfo>::Element *E_property = alternative_property_list.front(); E_property; E_property = E_property->next()) {
+ property_info = E_property->get();
+ bool valid;
+ Variant default_value = ClassDB::class_get_default_property_value("TileData", property_info.name, &valid);
+ Variant value = E_alternative->get()->get(property_info.name);
+ if (valid && value == default_value) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), property_info.name);
+ tile_property_list.push_back(property_info);
+ }
+ }
+
+ // Add all alternative.
+ for (List<PropertyInfo>::Element *E_property = tile_property_list.front(); E_property; E_property = E_property->next()) {
+ E_property->get().name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), E_property->get().name);
+ p_list->push_back(E_property->get());
+ }
+ }
}
-void TileSet::tile_set_shape_one_way(int p_id, int p_shape_id, const bool p_one_way) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_shape_id < 0);
+void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector2i p_size) {
+ // Create a tile if it does not exists.
+ ERR_FAIL_COND(p_atlas_coords.x < 0 || p_atlas_coords.y < 0);
+ ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0);
+ for (int x = 0; x < p_size.x; x++) {
+ for (int y = 0; y < p_size.y; y++) {
+ Vector2i coords = p_atlas_coords + Vector2i(x, y);
+ ERR_FAIL_COND_MSG(tiles.has(coords), vformat("Cannot create tile at position %s with size %s. Already a tile present at %s.", p_atlas_coords, p_size, coords));
+ }
+ }
- if (p_shape_id >= tile_map[p_id].shapes_data.size()) {
- tile_map[p_id].shapes_data.resize(p_shape_id + 1);
+ // Create and resize the tile.
+ tiles.insert(p_atlas_coords, TileSetAtlasSource::TileAlternativesData());
+ tiles_ids.append(p_atlas_coords);
+ tiles_ids.sort();
+
+ tiles[p_atlas_coords].size_in_atlas = p_size;
+ tiles[p_atlas_coords].alternatives[0] = memnew(TileData);
+ tiles[p_atlas_coords].alternatives[0]->set_tile_set(tile_set);
+ tiles[p_atlas_coords].alternatives[0]->set_allow_transform(false);
+ tiles[p_atlas_coords].alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed));
+ tiles[p_atlas_coords].alternatives[0]->notify_property_list_changed();
+ tiles[p_atlas_coords].alternatives_ids.append(0);
+
+ // Add all covered positions to the mapping cache
+ for (int x = 0; x < p_size.x; x++) {
+ for (int y = 0; y < p_size.y; y++) {
+ Vector2i coords = p_atlas_coords + Vector2i(x, y);
+ _coords_mapping_cache[coords] = p_atlas_coords;
+ }
}
- tile_map[p_id].shapes_data.write[p_shape_id].one_way_collision = p_one_way;
- emit_changed();
+
+ emit_signal("changed");
}
-bool TileSet::tile_get_shape_one_way(int p_id, int p_shape_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), false);
- ERR_FAIL_COND_V(p_shape_id < 0, false);
+void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+
+ // Remove all covered positions from the mapping cache
+ Size2i size = tiles[p_atlas_coords].size_in_atlas;
- if (p_shape_id < tile_map[p_id].shapes_data.size()) {
- return tile_map[p_id].shapes_data[p_shape_id].one_way_collision;
+ for (int x = 0; x < size.x; x++) {
+ for (int y = 0; y < size.y; y++) {
+ Vector2i coords = p_atlas_coords + Vector2i(x, y);
+ _coords_mapping_cache.erase(coords);
+ }
}
- return false;
+ // Free tile data.
+ for (Map<int, TileData *>::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) {
+ memdelete(E_tile_data->get());
+ }
+
+ // Delete the tile
+ tiles.erase(p_atlas_coords);
+ tiles_ids.erase(p_atlas_coords);
+ tiles_ids.sort();
+
+ emit_signal("changed");
}
-void TileSet::tile_set_shape_one_way_margin(int p_id, int p_shape_id, float p_margin) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- ERR_FAIL_COND(p_shape_id < 0);
+bool TileSetAtlasSource::has_tile(Vector2i p_atlas_coords) const {
+ return tiles.has(p_atlas_coords);
+}
- if (p_shape_id >= tile_map[p_id].shapes_data.size()) {
- tile_map[p_id].shapes_data.resize(p_shape_id + 1);
+Vector2i TileSetAtlasSource::get_tile_at_coords(Vector2i p_atlas_coords) const {
+ if (!_coords_mapping_cache.has(p_atlas_coords)) {
+ return INVALID_ATLAS_COORDS;
}
- tile_map[p_id].shapes_data.write[p_shape_id].one_way_collision_margin = p_margin;
- emit_changed();
+
+ return _coords_mapping_cache[p_atlas_coords];
+}
+
+Vector2i TileSetAtlasSource::get_tile_size_in_atlas(Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(-1, -1), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+
+ return tiles[p_atlas_coords].size_in_atlas;
}
-float TileSet::tile_get_shape_one_way_margin(int p_id, int p_shape_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
- ERR_FAIL_COND_V(p_shape_id < 0, 0);
+int TileSetAtlasSource::get_tiles_count() const {
+ return tiles_ids.size();
+}
+
+Vector2i TileSetAtlasSource::get_tile_id(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, tiles_ids.size(), TileSetAtlasSource::INVALID_ATLAS_COORDS);
+ return tiles_ids[p_index];
+}
+
+Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+
+ Vector2i size_in_atlas = tiles[p_atlas_coords].size_in_atlas;
+ Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1));
+
+ Vector2 origin = margins + (p_atlas_coords * (texture_region_size + separation));
+
+ return Rect2(origin, region_size);
+ ;
+}
- if (p_shape_id < tile_map[p_id].shapes_data.size()) {
- return tile_map[p_id].shapes_data[p_shape_id].one_way_collision_margin;
+Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+ ERR_FAIL_COND_V_MSG(!has_alternative_tile(p_atlas_coords, p_alternative_tile), Vector2i(), vformat("TileSetAtlasSource has no alternative tile with id %d at %s.", p_alternative_tile, String(p_atlas_coords)));
+ ERR_FAIL_COND_V(!tile_set, Vector2i());
+
+ Vector2 margin = (get_tile_texture_region(p_atlas_coords).size - tile_set->get_tile_size()) / 2;
+ margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y));
+ Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset();
+ if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) {
+ effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x);
+ effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y);
}
- return 0;
+ return effective_texture_offset;
}
-void TileSet::tile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].occluder = p_light_occluder;
+bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+
+ Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords;
+ if (new_atlas_coords.x < 0 || new_atlas_coords.y < 0) {
+ return false;
+ }
+
+ Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas;
+ ERR_FAIL_COND_V(size.x <= 0 || size.y <= 0, false);
+
+ Size2i grid_size = get_atlas_grid_size();
+ if (new_atlas_coords.x + size.x > grid_size.x || new_atlas_coords.y + size.y > grid_size.y) {
+ return false;
+ }
+
+ Rect2i new_rect = Rect2i(new_atlas_coords, size);
+ // Check if the new tile can fit in the new rect.
+ for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) {
+ for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) {
+ Vector2i coords = get_tile_at_coords(Vector2i(x, y));
+ if (coords != p_atlas_coords && coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) {
+ return false;
+ }
+ }
+ }
+
+ return true;
}
-Ref<OccluderPolygon2D> TileSet::tile_get_light_occluder(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<OccluderPolygon2D>());
- return tile_map[p_id].occluder;
+void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) {
+ bool can_move = can_move_tile_in_atlas(p_atlas_coords, p_new_atlas_coords, p_new_size);
+ ERR_FAIL_COND_MSG(!can_move, vformat("Cannot move tile at position %s with size %s. Tile already present.", p_new_atlas_coords, p_new_size));
+
+ // Compute the actual new rect from arguments.
+ Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords;
+ Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas;
+
+ if (new_atlas_coords == p_atlas_coords && size == tiles[p_atlas_coords].size_in_atlas) {
+ return;
+ }
+
+ // Remove all covered positions from the mapping cache.
+ Size2i old_size = tiles[p_atlas_coords].size_in_atlas;
+ for (int x = 0; x < old_size.x; x++) {
+ for (int y = 0; y < old_size.y; y++) {
+ Vector2i coords = p_atlas_coords + Vector2i(x, y);
+ _coords_mapping_cache.erase(coords);
+ }
+ }
+
+ // Move the tile and update its size.
+ if (new_atlas_coords != p_atlas_coords) {
+ tiles[new_atlas_coords] = tiles[p_atlas_coords];
+ tiles.erase(p_atlas_coords);
+
+ tiles_ids.erase(p_atlas_coords);
+ tiles_ids.append(new_atlas_coords);
+ tiles_ids.sort();
+ }
+ tiles[new_atlas_coords].size_in_atlas = size;
+
+ // Add all covered positions to the mapping cache again.
+ for (int x = 0; x < size.x; x++) {
+ for (int y = 0; y < size.y; y++) {
+ Vector2i coords = new_atlas_coords + Vector2i(x, y);
+ _coords_mapping_cache[coords] = new_atlas_coords;
+ }
+ }
+
+ emit_signal("changed");
}
-void TileSet::autotile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder, const Vector2 &p_coord) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- if (p_light_occluder.is_null()) {
- if (tile_map[p_id].autotile_data.occluder_map.has(p_coord)) {
- tile_map[p_id].autotile_data.occluder_map.erase(p_coord);
+bool TileSetAtlasSource::has_tiles_outside_texture() {
+ Vector2i grid_size = get_atlas_grid_size();
+ Vector<Vector2i> to_remove;
+
+ for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) {
+ if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) {
+ return true;
}
- } else {
- tile_map[p_id].autotile_data.occluder_map[p_coord] = p_light_occluder;
}
+
+ return false;
}
-Ref<OccluderPolygon2D> TileSet::autotile_get_light_occluder(int p_id, const Vector2 &p_coord) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<OccluderPolygon2D>());
+void TileSetAtlasSource::clear_tiles_outside_texture() {
+ Vector2i grid_size = get_atlas_grid_size();
+ Vector<Vector2i> to_remove;
- if (!tile_map[p_id].autotile_data.occluder_map.has(p_coord)) {
- return Ref<OccluderPolygon2D>();
- } else {
- return tile_map[p_id].autotile_data.occluder_map[p_coord];
+ for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) {
+ if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) {
+ to_remove.append(E->key());
+ }
+ }
+
+ for (int i = 0; i < to_remove.size(); i++) {
+ remove_tile(to_remove[i]);
}
}
-void TileSet::tile_set_navigation_polygon_offset(int p_id, const Vector2 &p_offset) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].navigation_polygon_offset = p_offset;
+int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && (tiles[p_atlas_coords].alternatives.has(p_alternative_id_override) || tiles[p_atlas_coords].alternatives.has(p_alternative_id_override)), -1, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override));
+
+ int new_alternative_id = p_alternative_id_override >= 0 ? p_alternative_id_override : tiles[p_atlas_coords].next_alternative_id;
+
+ tiles[p_atlas_coords].alternatives[new_alternative_id] = memnew(TileData);
+ tiles[p_atlas_coords].alternatives[new_alternative_id]->set_tile_set(tile_set);
+ tiles[p_atlas_coords].alternatives[new_alternative_id]->set_allow_transform(true);
+ tiles[p_atlas_coords].alternatives[new_alternative_id]->notify_property_list_changed();
+ tiles[p_atlas_coords].alternatives_ids.append(new_alternative_id);
+ tiles[p_atlas_coords].alternatives_ids.sort();
+ _compute_next_alternative_id(p_atlas_coords);
+
+ emit_signal("changed");
+
+ return new_alternative_id;
+}
+
+void TileSetAtlasSource::remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
+ ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot remove the alternative with id 0, the base tile alternative cannot be removed.");
+
+ memdelete(tiles[p_atlas_coords].alternatives[p_alternative_tile]);
+ tiles[p_atlas_coords].alternatives.erase(p_alternative_tile);
+ tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile);
+ tiles[p_atlas_coords].alternatives_ids.sort();
+
+ emit_signal("changed");
+}
+
+void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
+ ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot change the alternative with id 0, the base tile alternative cannot be modified.");
+
+ ERR_FAIL_COND_MSG(tiles[p_atlas_coords].alternatives.has(p_new_id), vformat("TileSetAtlasSource has already an alternative with id %d at %s.", p_new_id, String(p_atlas_coords)));
+
+ tiles[p_atlas_coords].alternatives[p_new_id] = tiles[p_atlas_coords].alternatives[p_alternative_tile];
+ tiles[p_atlas_coords].alternatives_ids.append(p_new_id);
+
+ tiles[p_atlas_coords].alternatives.erase(p_alternative_tile);
+ tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile);
+ tiles[p_atlas_coords].alternatives_ids.sort();
+
+ emit_signal("changed");
+}
+
+bool TileSetAtlasSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
+ return tiles[p_atlas_coords].alternatives.has(p_alternative_tile);
+}
+
+int TileSetAtlasSource::get_next_alternative_tile_id(const Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
+ return tiles[p_atlas_coords].next_alternative_id;
}
-Vector2 TileSet::tile_get_navigation_polygon_offset(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- return tile_map[p_id].navigation_polygon_offset;
+int TileSetAtlasSource::get_alternative_tiles_count(const Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
+ return tiles[p_atlas_coords].alternatives_ids.size();
}
-void TileSet::tile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].navigation_polygon = p_navigation_polygon;
+int TileSetAtlasSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_INDEX_V(p_index, tiles[p_atlas_coords].alternatives_ids.size(), -1);
+
+ return tiles[p_atlas_coords].alternatives_ids[p_index];
}
-Ref<NavigationPolygon> TileSet::tile_get_navigation_polygon(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<NavigationPolygon>());
- return tile_map[p_id].navigation_polygon;
+Object *TileSetAtlasSource::get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
+
+ return tiles[p_atlas_coords].alternatives[p_alternative_tile];
}
-const Map<Vector2, Ref<OccluderPolygon2D>> &TileSet::autotile_get_light_oclusion_map(int p_id) const {
- static Map<Vector2, Ref<OccluderPolygon2D>> dummy;
- ERR_FAIL_COND_V(!tile_map.has(p_id), dummy);
- return tile_map[p_id].autotile_data.occluder_map;
+void TileSetAtlasSource::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TileSetAtlasSource::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &TileSetAtlasSource::get_texture);
+ ClassDB::bind_method(D_METHOD("set_margins", "margins"), &TileSetAtlasSource::set_margins);
+ ClassDB::bind_method(D_METHOD("get_margins"), &TileSetAtlasSource::get_margins);
+ ClassDB::bind_method(D_METHOD("set_separation", "separation"), &TileSetAtlasSource::set_separation);
+ ClassDB::bind_method(D_METHOD("get_separation"), &TileSetAtlasSource::get_separation);
+ ClassDB::bind_method(D_METHOD("set_texture_region_size", "texture_region_size"), &TileSetAtlasSource::set_texture_region_size);
+ ClassDB::bind_method(D_METHOD("get_texture_region_size"), &TileSetAtlasSource::get_texture_region_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_margins", "get_margins");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_separation", "get_separation");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size");
+
+ // Base tiles
+ ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1)));
+ ClassDB::bind_method(D_METHOD("remove_tile", "atlas_coords"), &TileSetAtlasSource::remove_tile); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative
+ ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetAtlasSource::has_tile);
+ ClassDB::bind_method(D_METHOD("can_move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::can_move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1)));
+ ClassDB::bind_method(D_METHOD("move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1)));
+ ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas);
+
+ ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetAtlasSource::get_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetAtlasSource::get_tile_id);
+
+ ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords);
+
+ // Alternative tiles
+ ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile);
+ ClassDB::bind_method(D_METHOD("set_alternative_tile_id", "atlas_coords", "alternative_tile", "new_id"), &TileSetAtlasSource::set_alternative_tile_id);
+ ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::has_alternative_tile);
+ ClassDB::bind_method(D_METHOD("get_next_alternative_tile_id", "atlas_coords"), &TileSetAtlasSource::get_next_alternative_tile_id);
+
+ ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetAtlasSource::get_alternative_tiles_count);
+ ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetAtlasSource::get_alternative_tile_id);
+
+ ClassDB::bind_method(D_METHOD("get_tile_data", "atlas_coords", "index"), &TileSetAtlasSource::get_tile_data);
+
+ // Helpers.
+ ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size);
+ ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture);
+ ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture);
+ ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords"), &TileSetAtlasSource::get_tile_texture_region);
}
-void TileSet::autotile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon, const Vector2 &p_coord) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- if (p_navigation_polygon.is_null()) {
- if (tile_map[p_id].autotile_data.navpoly_map.has(p_coord)) {
- tile_map[p_id].autotile_data.navpoly_map.erase(p_coord);
+TileSetAtlasSource::~TileSetAtlasSource() {
+ // Free everything needed.
+ for (Map<Vector2i, TileAlternativesData>::Element *E_alternatives = tiles.front(); E_alternatives; E_alternatives = E_alternatives->next()) {
+ for (Map<int, TileData *>::Element *E_tile_data = E_alternatives->get().alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) {
+ memdelete(E_tile_data->get());
}
- } else {
- tile_map[p_id].autotile_data.navpoly_map[p_coord] = p_navigation_polygon;
}
}
-Ref<NavigationPolygon> TileSet::autotile_get_navigation_polygon(int p_id, const Vector2 &p_coord) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<NavigationPolygon>());
- if (!tile_map[p_id].autotile_data.navpoly_map.has(p_coord)) {
- return Ref<NavigationPolygon>();
- } else {
- return tile_map[p_id].autotile_data.navpoly_map[p_coord];
+TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
+
+ return tiles[p_atlas_coords].alternatives[p_alternative_tile];
+}
+
+const TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+ ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords)));
+
+ return tiles[p_atlas_coords].alternatives[p_alternative_tile];
+}
+
+void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coords) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
+
+ while (tiles[p_atlas_coords].alternatives.has(tiles[p_atlas_coords].next_alternative_id)) {
+ tiles[p_atlas_coords].next_alternative_id = (tiles[p_atlas_coords].next_alternative_id % 1073741823) + 1; // 2 ** 30
+ };
+}
+
+/////////////////////////////// TileData //////////////////////////////////////
+
+void TileData::set_tile_set(const TileSet *p_tile_set) {
+ tile_set = p_tile_set;
+ if (tile_set) {
+ occluders.resize(tile_set->get_occlusion_layers_count());
+ physics.resize(tile_set->get_physics_layers_count());
+ navigation.resize(tile_set->get_navigation_layers_count());
+ custom_data.resize(tile_set->get_custom_data_layers_count());
+ }
+ notify_property_list_changed();
+}
+
+void TileData::notify_tile_data_properties_should_change() {
+ occluders.resize(tile_set->get_occlusion_layers_count());
+ physics.resize(tile_set->get_physics_layers_count());
+ for (int bit_index = 0; bit_index < 16; bit_index++) {
+ if (terrain_set < 0 || terrain_peering_bits[bit_index] >= tile_set->get_terrains_count(terrain_set)) {
+ terrain_peering_bits[bit_index] = -1;
+ }
+ }
+ navigation.resize(tile_set->get_navigation_layers_count());
+
+ // Convert custom data to the new type.
+ custom_data.resize(tile_set->get_custom_data_layers_count());
+ for (int i = 0; i < custom_data.size(); i++) {
+ if (custom_data[i].get_type() != tile_set->get_custom_data_type(i)) {
+ Variant new_val;
+ Callable::CallError error;
+ if (Variant::can_convert(custom_data[i].get_type(), tile_set->get_custom_data_type(i))) {
+ const Variant *args[] = { &custom_data[i] };
+ Variant::construct(tile_set->get_custom_data_type(i), new_val, args, 1, error);
+ } else {
+ Variant::construct(tile_set->get_custom_data_type(i), new_val, nullptr, 0, error);
+ }
+ custom_data.write[i] = new_val;
+ }
}
+
+ notify_property_list_changed();
+ emit_signal("changed");
+}
+
+void TileData::reset_state() {
+ occluders.clear();
+ physics.clear();
+ navigation.clear();
+ custom_data.clear();
+}
+
+void TileData::set_allow_transform(bool p_allow_transform) {
+ allow_transform = p_allow_transform;
+}
+
+bool TileData::is_allowing_transform() const {
+ return allow_transform;
+}
+
+// Rendering
+void TileData::set_flip_h(bool p_flip_h) {
+ ERR_FAIL_COND_MSG(!allow_transform && p_flip_h, "Transform is only allowed for alternative tiles (with its alternative_id != 0)");
+ flip_h = p_flip_h;
+ emit_signal("changed");
+}
+bool TileData::get_flip_h() const {
+ return flip_h;
+}
+
+void TileData::set_flip_v(bool p_flip_v) {
+ ERR_FAIL_COND_MSG(!allow_transform && p_flip_v, "Transform is only allowed for alternative tiles (with its alternative_id != 0)");
+ flip_v = p_flip_v;
+ emit_signal("changed");
+}
+
+bool TileData::get_flip_v() const {
+ return flip_v;
+}
+
+void TileData::set_transpose(bool p_transpose) {
+ ERR_FAIL_COND_MSG(!allow_transform && p_transpose, "Transform is only allowed for alternative tiles (with its alternative_id != 0)");
+ transpose = p_transpose;
+ emit_signal("changed");
+}
+bool TileData::get_transpose() const {
+ return transpose;
+}
+
+void TileData::set_texture_offset(Vector2i p_texture_offset) {
+ tex_offset = p_texture_offset;
+ emit_signal("changed");
+}
+
+Vector2i TileData::get_texture_offset() const {
+ return tex_offset;
+}
+
+void TileData::tile_set_material(Ref<ShaderMaterial> p_material) {
+ material = p_material;
+ emit_signal("changed");
+}
+Ref<ShaderMaterial> TileData::tile_get_material() const {
+ return material;
+}
+
+void TileData::set_modulate(Color p_modulate) {
+ modulate = p_modulate;
+ emit_signal("changed");
+}
+Color TileData::get_modulate() const {
+ return modulate;
+}
+
+void TileData::set_z_index(int p_z_index) {
+ z_index = p_z_index;
+ emit_signal("changed");
+}
+int TileData::get_z_index() const {
+ return z_index;
+}
+
+void TileData::set_y_sort_origin(Vector2i p_y_sort_origin) {
+ y_sort_origin = p_y_sort_origin;
+ emit_signal("changed");
+}
+Vector2i TileData::get_y_sort_origin() const {
+ return y_sort_origin;
+}
+
+void TileData::set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon) {
+ ERR_FAIL_INDEX(p_layer_id, occluders.size());
+ occluders.write[p_layer_id] = p_occluder_polygon;
+ emit_signal("changed");
+}
+
+Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), Ref<OccluderPolygon2D>());
+ return occluders[p_layer_id];
}
-const Map<Vector2, Ref<NavigationPolygon>> &TileSet::autotile_get_navigation_map(int p_id) const {
- static Map<Vector2, Ref<NavigationPolygon>> dummy;
- ERR_FAIL_COND_V(!tile_map.has(p_id), dummy);
- return tile_map[p_id].autotile_data.navpoly_map;
+// Physics
+int TileData::get_collision_shapes_count(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
+ return physics[p_layer_id].shapes.size();
}
-void TileSet::tile_set_occluder_offset(int p_id, const Vector2 &p_offset) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].occluder_offset = p_offset;
+void TileData::set_collision_shapes_count(int p_layer_id, int p_shapes_count) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ ERR_FAIL_COND(p_shapes_count < 0);
+ physics.write[p_layer_id].shapes.resize(p_shapes_count);
+ notify_property_list_changed();
+ emit_signal("changed");
+}
+
+void TileData::add_collision_shape(int p_layer_id) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ physics.write[p_layer_id].shapes.push_back(PhysicsLayerTileData::ShapeTileData());
+ emit_signal("changed");
+}
+
+void TileData::remove_collision_shape(int p_layer_id, int p_shape_index) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size());
+ physics.write[p_layer_id].shapes.remove(p_shape_index);
+ emit_signal("changed");
+}
+
+void TileData::set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref<Shape2D> p_shape) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size());
+ physics.write[p_layer_id].shapes.write[p_shape_index].shape = p_shape;
+ emit_signal("changed");
}
-Vector2 TileSet::tile_get_occluder_offset(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2());
- return tile_map[p_id].occluder_offset;
+Ref<Shape2D> TileData::get_collision_shape_shape(int p_layer_id, int p_shape_index) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Ref<Shape2D>());
+ ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), Ref<Shape2D>());
+ return physics[p_layer_id].shapes[p_shape_index].shape;
}
-void TileSet::tile_set_shapes(int p_id, const Vector<ShapeData> &p_shapes) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].shapes_data = p_shapes;
- for (int i = 0; i < p_shapes.size(); i++) {
- _decompose_convex_shape(p_shapes[i].shape);
+void TileData::set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size());
+ physics.write[p_layer_id].shapes.write[p_shape_index].one_way = p_one_way;
+ emit_signal("changed");
+}
+
+bool TileData::is_collision_shape_one_way(int p_layer_id, int p_shape_index) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), false);
+ ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), false);
+ return physics[p_layer_id].shapes[p_shape_index].one_way;
+}
+
+void TileData::set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size());
+ physics.write[p_layer_id].shapes.write[p_shape_index].one_way_margin = p_one_way_margin;
+ emit_signal("changed");
+}
+
+float TileData::get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0);
+ ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), 0.0);
+ return physics[p_layer_id].shapes[p_shape_index].one_way_margin;
+}
+
+// Terrain
+void TileData::set_terrain_set(int p_terrain_set) {
+ ERR_FAIL_COND(p_terrain_set < -1);
+ if (tile_set) {
+ ERR_FAIL_COND(p_terrain_set >= tile_set->get_terrain_sets_count());
}
- emit_changed();
+ terrain_set = p_terrain_set;
+ notify_property_list_changed();
+ emit_signal("changed");
}
-Vector<TileSet::ShapeData> TileSet::tile_get_shapes(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Vector<ShapeData>());
+int TileData::get_terrain_set() const {
+ return terrain_set;
+}
- return tile_map[p_id].shapes_data;
+void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) {
+ ERR_FAIL_COND(p_terrain_index < -1);
+ if (tile_set) {
+ ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set));
+ ERR_FAIL_COND(!is_valid_peering_bit_terrain(p_peering_bit));
+ }
+ terrain_peering_bits[p_peering_bit] = p_terrain_index;
+ emit_signal("changed");
}
-int TileSet::tile_get_z_index(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
- return tile_map[p_id].z_index;
+int TileData::get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const {
+ return terrain_peering_bits[p_peering_bit];
}
-void TileSet::tile_set_z_index(int p_id, int p_z_index) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map[p_id].z_index = p_z_index;
- emit_changed();
+bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const {
+ ERR_FAIL_COND_V(!tile_set, false);
+
+ return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit);
}
-void TileSet::_tile_set_shapes(int p_id, const Array &p_shapes) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- Vector<ShapeData> shapes_data;
- Transform2D default_transform = tile_get_shape_transform(p_id, 0);
- bool default_one_way = tile_get_shape_one_way(p_id, 0);
- Vector2 default_autotile_coord = Vector2();
- for (int i = 0; i < p_shapes.size(); i++) {
- ShapeData s = ShapeData();
+// Navigation
+void TileData::set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon) {
+ ERR_FAIL_INDEX(p_layer_id, navigation.size());
+ navigation.write[p_layer_id] = p_navigation_polygon;
+ emit_signal("changed");
+}
- if (p_shapes[i].get_type() == Variant::OBJECT) {
- Ref<Shape2D> shape = p_shapes[i];
- if (shape.is_null()) {
- continue;
+Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, navigation.size(), Ref<NavigationPolygon>());
+ return navigation[p_layer_id];
+}
+
+// Misc
+void TileData::set_probability(float p_probability) {
+ ERR_FAIL_COND(p_probability <= 0.0);
+ probability = p_probability;
+ emit_signal("changed");
+}
+float TileData::get_probability() const {
+ return probability;
+}
+
+// Custom data
+void TileData::set_custom_data(String p_layer_name, Variant p_value) {
+ ERR_FAIL_COND(!tile_set);
+ int p_layer_id = tile_set->get_custom_data_layer_by_name(p_layer_name);
+ ERR_FAIL_COND_MSG(p_layer_id < 0, vformat("TileSet has no layer with name: %s", p_layer_name));
+ set_custom_data_by_layer_id(p_layer_id, p_value);
+}
+
+Variant TileData::get_custom_data(String p_layer_name) const {
+ ERR_FAIL_COND_V(!tile_set, Variant());
+ int p_layer_id = tile_set->get_custom_data_layer_by_name(p_layer_name);
+ ERR_FAIL_COND_V_MSG(p_layer_id < 0, Variant(), vformat("TileSet has no layer with name: %s", p_layer_name));
+ return get_custom_data_by_layer_id(p_layer_id);
+}
+
+void TileData::set_custom_data_by_layer_id(int p_layer_id, Variant p_value) {
+ ERR_FAIL_INDEX(p_layer_id, custom_data.size());
+ custom_data.write[p_layer_id] = p_value;
+ emit_signal("changed");
+}
+
+Variant TileData::get_custom_data_by_layer_id(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, custom_data.size(), Variant());
+ return custom_data[p_layer_id];
+}
+
+bool TileData::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+ if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) {
+ // Occlusion layers.
+ int layer_index = components[0].trim_prefix("occlusion_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (components[1] == "polygon") {
+ Ref<OccluderPolygon2D> polygon = p_value;
+ if (!polygon.is_valid()) {
+ return false;
}
- s.shape = shape;
- s.shape_transform = default_transform;
- s.one_way_collision = default_one_way;
- s.autotile_coord = default_autotile_coord;
- } else if (p_shapes[i].get_type() == Variant::DICTIONARY) {
- Dictionary d = p_shapes[i];
+ if (layer_index >= occluders.size()) {
+ if (tile_set) {
+ return false;
+ } else {
+ occluders.resize(layer_index + 1);
+ }
+ }
+ set_occluder(layer_index, polygon);
+ return true;
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) {
+ // Physics layers.
+ int layer_index = components[0].trim_prefix("physics_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (components.size() == 2 && components[1] == "shapes_count") {
+ if (p_value.get_type() != Variant::INT) {
+ return false;
+ }
- if (d.has("shape") && d["shape"].get_type() == Variant::OBJECT) {
- s.shape = d["shape"];
- _decompose_convex_shape(s.shape);
- } else {
- continue;
+ if (layer_index >= physics.size()) {
+ if (tile_set) {
+ return false;
+ } else {
+ physics.resize(layer_index + 1);
+ }
}
+ set_collision_shapes_count(layer_index, p_value);
+ return true;
+ } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_integer()) {
+ int shape_index = components[1].trim_prefix("shape_").to_int();
+ ERR_FAIL_COND_V(shape_index < 0, false);
+
+ if (components[2] == "shape" || components[2] == "one_way" || components[2] == "one_way_margin") {
+ if (layer_index >= physics.size()) {
+ if (tile_set) {
+ return false;
+ } else {
+ physics.resize(layer_index + 1);
+ }
+ }
- if (d.has("shape_transform") && d["shape_transform"].get_type() == Variant::TRANSFORM2D) {
- s.shape_transform = d["shape_transform"];
- } else if (d.has("shape_offset") && d["shape_offset"].get_type() == Variant::VECTOR2) {
- s.shape_transform = Transform2D(0, (Vector2)d["shape_offset"]);
- } else {
- s.shape_transform = default_transform;
+ if (shape_index >= physics[layer_index].shapes.size()) {
+ physics.write[layer_index].shapes.resize(shape_index + 1);
+ }
+ }
+ if (components[2] == "shape") {
+ Ref<Shape2D> shape = p_value;
+ set_collision_shape_shape(layer_index, shape_index, shape);
+ return true;
+ } else if (components[2] == "one_way") {
+ set_collision_shape_one_way(layer_index, shape_index, p_value);
+ return true;
+ } else if (components[2] == "one_way_margin") {
+ set_collision_shape_one_way_margin(layer_index, shape_index, p_value);
+ return true;
+ }
+ }
+ } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) {
+ // Navigation layers.
+ int layer_index = components[0].trim_prefix("navigation_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (components[1] == "polygon") {
+ Ref<NavigationPolygon> polygon = p_value;
+ if (!polygon.is_valid()) {
+ return false;
}
- if (d.has("one_way") && d["one_way"].get_type() == Variant::BOOL) {
- s.one_way_collision = d["one_way"];
+ if (layer_index >= navigation.size()) {
+ if (tile_set) {
+ return false;
+ } else {
+ navigation.resize(layer_index + 1);
+ }
+ }
+ set_navigation_polygon(layer_index, polygon);
+ return true;
+ }
+ } else if (components.size() == 2 && components[0] == "terrains_peering_bit") {
+ // Terrains.
+ if (components[1] == "right_side") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, p_value);
+ } else if (components[1] == "right_corner") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER, p_value);
+ } else if (components[1] == "bottom_right_side") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, p_value);
+ } else if (components[1] == "bottom_right_corner") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, p_value);
+ } else if (components[1] == "bottom_side") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, p_value);
+ } else if (components[1] == "bottom_corner") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, p_value);
+ } else if (components[1] == "bottom_left_side") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, p_value);
+ } else if (components[1] == "bottom_left_corner") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, p_value);
+ } else if (components[1] == "left_side") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE, p_value);
+ } else if (components[1] == "left_corner") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER, p_value);
+ } else if (components[1] == "top_left_side") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, p_value);
+ } else if (components[1] == "top_left_corner") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, p_value);
+ } else if (components[1] == "top_side") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE, p_value);
+ } else if (components[1] == "top_corner") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER, p_value);
+ } else if (components[1] == "top_right_side") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, p_value);
+ } else if (components[1] == "top_right_corner") {
+ set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, p_value);
+ } else {
+ return false;
+ }
+ return true;
+ } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_integer()) {
+ // Custom data layers.
+ int layer_index = components[0].trim_prefix("custom_data_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+
+ if (layer_index >= custom_data.size()) {
+ if (tile_set) {
+ return false;
} else {
- s.one_way_collision = default_one_way;
+ custom_data.resize(layer_index + 1);
}
+ }
+ set_custom_data_by_layer_id(layer_index, p_value);
+
+ return true;
+ }
+
+ return false;
+}
- if (d.has("one_way_margin") && d["one_way_margin"].is_num()) {
- s.one_way_collision_margin = d["one_way_margin"];
+bool TileData::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+
+ if (tile_set) {
+ if (components.size() == 2 && components[0].begins_with("occlusion_layer") && components[0].trim_prefix("occlusion_layer_").is_valid_integer()) {
+ // Occlusion layers.
+ int layer_index = components[0].trim_prefix("occlusion_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (layer_index >= occluders.size()) {
+ return false;
+ }
+ if (components[1] == "polygon") {
+ r_ret = get_occluder(layer_index);
+ return true;
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_integer()) {
+ // Physics layers.
+ int layer_index = components[0].trim_prefix("physics_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (layer_index >= physics.size()) {
+ return false;
+ }
+ if (components.size() == 2 && components[1] == "shapes_count") {
+ r_ret = get_collision_shapes_count(layer_index);
+ return true;
+ } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_integer()) {
+ int shape_index = components[1].trim_prefix("shape_").to_int();
+ ERR_FAIL_COND_V(shape_index < 0, false);
+ if (shape_index >= physics[layer_index].shapes.size()) {
+ return false;
+ }
+ if (components[2] == "shape") {
+ r_ret = get_collision_shape_shape(layer_index, shape_index);
+ return true;
+ } else if (components[2] == "one_way") {
+ r_ret = is_collision_shape_one_way(layer_index, shape_index);
+ return true;
+ } else if (components[2] == "one_way_margin") {
+ r_ret = get_collision_shape_one_way_margin(layer_index, shape_index);
+ return true;
+ }
+ }
+ } else if (components.size() == 2 && components[0] == "terrains_peering_bit") {
+ // Terrains.
+ if (components[1] == "right_side") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_SIDE];
+ } else if (components[1] == "right_corner") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_CORNER];
+ } else if (components[1] == "bottom_right_side") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE];
+ } else if (components[1] == "bottom_right_corner") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER];
+ } else if (components[1] == "bottom_side") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_SIDE];
+ } else if (components[1] == "bottom_corner") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_CORNER];
+ } else if (components[1] == "bottom_left_side") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE];
+ } else if (components[1] == "bottom_left_corner") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER];
+ } else if (components[1] == "left_side") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_SIDE];
+ } else if (components[1] == "left_corner") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_CORNER];
+ } else if (components[1] == "top_left_side") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE];
+ } else if (components[1] == "top_left_corner") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER];
+ } else if (components[1] == "top_side") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_SIDE];
+ } else if (components[1] == "top_corner") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_CORNER];
+ } else if (components[1] == "top_right_side") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE];
+ } else if (components[1] == "top_right_corner") {
+ r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER];
} else {
- s.one_way_collision_margin = 1.0;
+ return false;
+ }
+ return true;
+ } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_integer()) {
+ // Occlusion layers.
+ int layer_index = components[0].trim_prefix("navigation_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (layer_index >= navigation.size()) {
+ return false;
+ }
+ if (components[1] == "polygon") {
+ r_ret = get_navigation_polygon(layer_index);
+ return true;
+ }
+ } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_integer()) {
+ // Custom data layers.
+ int layer_index = components[0].trim_prefix("custom_data_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (layer_index >= custom_data.size()) {
+ return false;
}
+ r_ret = get_custom_data_by_layer_id(layer_index);
+ return true;
+ }
+ }
- if (d.has("autotile_coord") && d["autotile_coord"].get_type() == Variant::VECTOR2) {
- s.autotile_coord = d["autotile_coord"];
- } else {
- s.autotile_coord = default_autotile_coord;
+ return false;
+}
+
+void TileData::_get_property_list(List<PropertyInfo> *p_list) const {
+ PropertyInfo property_info;
+ // Add the groups manually.
+ if (tile_set) {
+ // Occlusion layers.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < occluders.size(); i++) {
+ // occlusion_layer_%d/polygon
+ property_info = PropertyInfo(Variant::OBJECT, vformat("occlusion_layer_%d/polygon", i), PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_DEFAULT);
+ if (!occluders[i].is_valid()) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
+ p_list->push_back(property_info);
+ }
- } else {
- ERR_CONTINUE_MSG(true, "Expected an array of objects or dictionaries for tile_set_shapes.");
+ // Physics layers.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < physics.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/shapes_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
+
+ for (int j = 0; j < physics[i].shapes.size(); j++) {
+ // physics_layer_%d/shapes_count
+ property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/shape_%d/shape", i, j), PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_DEFAULT);
+ if (!physics[i].shapes[j].shape.is_valid()) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+
+ // physics_layer_%d/shape_%d/one_way
+ property_info = PropertyInfo(Variant::BOOL, vformat("physics_layer_%d/shape_%d/one_way", i, j));
+ if (physics[i].shapes[j].one_way == false) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+
+ // physics_layer_%d/shape_%d/one_way_margin
+ property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/shape_%d/one_way_margin", i, j));
+ if (physics[i].shapes[j].one_way_margin == 1.0) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ }
+
+ // Terrain data
+ if (terrain_set >= 0) {
+ p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_side");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_corner");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_side");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_corner");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_side");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_corner");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_side");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_corner");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_side");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_corner");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_side");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_corner");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_side");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_corner");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_side");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+ if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER)) {
+ property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_corner");
+ if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) == -1) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
}
- shapes_data.push_back(s);
+ // Navigation layers.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Navigation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < navigation.size(); i++) {
+ property_info = PropertyInfo(Variant::OBJECT, vformat("navigation_layer_%d/polygon", i), PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon", PROPERTY_USAGE_DEFAULT);
+ if (!navigation[i].is_valid()) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
+
+ // Custom data layers.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Custom data", PROPERTY_HINT_NONE, "custom_data_", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < custom_data.size(); i++) {
+ Variant default_val;
+ Callable::CallError error;
+ Variant::construct(custom_data[i].get_type(), default_val, nullptr, 0, error);
+ property_info = PropertyInfo(tile_set->get_custom_data_type(i), vformat("custom_data_%d", i), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT);
+ if (custom_data[i] == default_val) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ p_list->push_back(property_info);
+ }
}
+}
- tile_map[p_id].shapes_data = shapes_data;
- emit_changed();
+void TileData::_bind_methods() {
+ // Rendering.
+ ClassDB::bind_method(D_METHOD("set_flip_h", "flip_h"), &TileData::set_flip_h);
+ ClassDB::bind_method(D_METHOD("get_flip_h"), &TileData::get_flip_h);
+ ClassDB::bind_method(D_METHOD("set_flip_v", "flip_v"), &TileData::set_flip_v);
+ ClassDB::bind_method(D_METHOD("get_flip_v"), &TileData::get_flip_v);
+ ClassDB::bind_method(D_METHOD("set_transpose", "transpose"), &TileData::set_transpose);
+ ClassDB::bind_method(D_METHOD("get_transpose"), &TileData::get_transpose);
+ ClassDB::bind_method(D_METHOD("tile_set_material", "material"), &TileData::tile_set_material);
+ ClassDB::bind_method(D_METHOD("tile_get_material"), &TileData::tile_get_material);
+ ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &TileData::set_texture_offset);
+ ClassDB::bind_method(D_METHOD("get_texture_offset"), &TileData::get_texture_offset);
+ ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &TileData::set_modulate);
+ ClassDB::bind_method(D_METHOD("get_modulate"), &TileData::get_modulate);
+ ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &TileData::set_z_index);
+ ClassDB::bind_method(D_METHOD("get_z_index"), &TileData::get_z_index);
+ ClassDB::bind_method(D_METHOD("set_y_sort_origin", "y_sort_origin"), &TileData::set_y_sort_origin);
+ ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileData::get_y_sort_origin);
+
+ ClassDB::bind_method(D_METHOD("set_occluder", "layer_id", "occluder_polygon"), &TileData::set_occluder);
+ ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder);
+
+ // Physics.
+ ClassDB::bind_method(D_METHOD("get_collision_shapes_count", "layer_id"), &TileData::get_collision_shapes_count);
+ ClassDB::bind_method(D_METHOD("set_collision_shapes_count", "layer_id", "shapes_count"), &TileData::set_collision_shapes_count);
+ ClassDB::bind_method(D_METHOD("add_collision_shape", "layer_id"), &TileData::add_collision_shape);
+ ClassDB::bind_method(D_METHOD("remove_collision_shape", "layer_id", "shape_index"), &TileData::remove_collision_shape);
+ ClassDB::bind_method(D_METHOD("set_collision_shape_shape", "layer_id", "shape_index", "shape"), &TileData::set_collision_shape_shape);
+ ClassDB::bind_method(D_METHOD("get_collision_shape_shape", "layer_id", "shape_index"), &TileData::get_collision_shape_shape);
+ ClassDB::bind_method(D_METHOD("set_collision_shape_one_way", "layer_id", "shape_index", "one_way"), &TileData::set_collision_shape_one_way);
+ ClassDB::bind_method(D_METHOD("is_collision_shape_one_way", "layer_id", "shape_index"), &TileData::is_collision_shape_one_way);
+ ClassDB::bind_method(D_METHOD("set_collision_shape_one_way_margin", "layer_id", "shape_index", "one_way_margin"), &TileData::set_collision_shape_one_way_margin);
+ ClassDB::bind_method(D_METHOD("get_collision_shape_one_way_margin", "layer_id", "shape_index"), &TileData::get_collision_shape_one_way_margin);
+
+ // Terrain
+ ClassDB::bind_method(D_METHOD("set_terrain_set", "terrain_set"), &TileData::set_terrain_set);
+ ClassDB::bind_method(D_METHOD("get_terrain_set"), &TileData::get_terrain_set);
+ ClassDB::bind_method(D_METHOD("set_peering_bit_terrain", "peering_bit", "terrain"), &TileData::set_peering_bit_terrain);
+ ClassDB::bind_method(D_METHOD("get_peering_bit_terrain", "peering_bit"), &TileData::get_peering_bit_terrain);
+
+ // Navigation
+ ClassDB::bind_method(D_METHOD("set_navigation_polygon", "layer_id", "navigation_polygon"), &TileData::set_navigation_polygon);
+ ClassDB::bind_method(D_METHOD("get_navigation_polygon", "layer_id"), &TileData::get_navigation_polygon);
+
+ // Misc.
+ ClassDB::bind_method(D_METHOD("set_probability", "probability"), &TileData::set_probability);
+ ClassDB::bind_method(D_METHOD("get_probability"), &TileData::get_probability);
+
+ // Custom data.
+ ClassDB::bind_method(D_METHOD("set_custom_data", "layer_name", "value"), &TileData::set_custom_data);
+ ClassDB::bind_method(D_METHOD("get_custom_data", "layer_name"), &TileData::get_custom_data);
+ ClassDB::bind_method(D_METHOD("set_custom_data_by_layer_id", "layer_id", "value"), &TileData::set_custom_data_by_layer_id);
+ ClassDB::bind_method(D_METHOD("get_custom_data_by_layer_id", "layer_id"), &TileData::get_custom_data_by_layer_id);
+
+ ADD_GROUP("Rendering", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "get_flip_h");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "get_flip_v");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset"), "set_texture_offset", "get_texture_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin");
+
+ ADD_GROUP("Terrains", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "terrain_set"), "set_terrain_set", "get_terrain_set");
+
+ ADD_GROUP("Miscellaneous", "");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "probability"), "set_probability", "get_probability");
+
+ ADD_SIGNAL(MethodInfo("changed"));
}
-Array TileSet::_tile_get_shapes(int p_id) const {
- ERR_FAIL_COND_V(!tile_map.has(p_id), Array());
- Array arr;
+/////////////////////////////// TileSetAtlasPluginTerrain //////////////////////////////////////
+
+// --- PLUGINS ---
+void TileSetAtlasPluginTerrain::_draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ Rect2 bit_rect;
+ bit_rect.size = Vector2(p_size) / 3;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ bit_rect.position = Vector2(1, -1);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ bit_rect.position = Vector2(1, 1);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ bit_rect.position = Vector2(-1, 1);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ bit_rect.position = Vector2(-3, 1);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ bit_rect.position = Vector2(-3, -1);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ bit_rect.position = Vector2(-3, -3);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ bit_rect.position = Vector2(-1, -3);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ bit_rect.position = Vector2(1, -3);
+ break;
+ default:
+ break;
+ }
+ bit_rect.position *= Vector2(p_size) / 6.0;
+ p_canvas_item->draw_rect(bit_rect, p_color);
+}
- Vector<ShapeData> data = tile_map[p_id].shapes_data;
- for (int i = 0; i < data.size(); i++) {
- Dictionary shape_data;
- shape_data["shape"] = data[i].shape;
- shape_data["shape_transform"] = data[i].shape_transform;
- shape_data["one_way"] = data[i].one_way_collision;
- shape_data["one_way_margin"] = data[i].one_way_collision_margin;
- shape_data["autotile_coord"] = data[i].autotile_coord;
- arr.push_back(shape_data);
+void TileSetAtlasPluginTerrain::_draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ PackedColorArray color_array;
+ color_array.push_back(p_color);
+
+ Vector2 unit = Vector2(p_size) / 6.0;
+ PackedVector2Array polygon;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(3, 3) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(1, 0) * unit);
+ polygon.push_back(Vector2(1, 1) * unit);
+ polygon.push_back(Vector2(0, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(-3, 3) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(-1, 0) * unit);
+ polygon.push_back(Vector2(-1, 1) * unit);
+ polygon.push_back(Vector2(0, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(-3, -3) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(-1, 0) * unit);
+ polygon.push_back(Vector2(-1, -1) * unit);
+ polygon.push_back(Vector2(0, -1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(3, -3) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(1, 0) * unit);
+ polygon.push_back(Vector2(1, -1) * unit);
+ polygon.push_back(Vector2(0, -1) * unit);
+ break;
+ default:
+ break;
+ }
+ if (!polygon.is_empty()) {
+ p_canvas_item->draw_polygon(polygon, color_array);
}
+}
- return arr;
+void TileSetAtlasPluginTerrain::_draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ PackedColorArray color_array;
+ color_array.push_back(p_color);
+
+ Vector2 unit = Vector2(p_size) / 6.0;
+ PackedVector2Array polygon;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ polygon.push_back(Vector2(1, -1) * unit);
+ polygon.push_back(Vector2(3, -3) * unit);
+ polygon.push_back(Vector2(3, 3) * unit);
+ polygon.push_back(Vector2(1, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ polygon.push_back(Vector2(-1, 1) * unit);
+ polygon.push_back(Vector2(-3, 3) * unit);
+ polygon.push_back(Vector2(3, 3) * unit);
+ polygon.push_back(Vector2(1, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ polygon.push_back(Vector2(-1, -1) * unit);
+ polygon.push_back(Vector2(-3, -3) * unit);
+ polygon.push_back(Vector2(-3, 3) * unit);
+ polygon.push_back(Vector2(-1, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ polygon.push_back(Vector2(-1, -1) * unit);
+ polygon.push_back(Vector2(-3, -3) * unit);
+ polygon.push_back(Vector2(3, -3) * unit);
+ polygon.push_back(Vector2(1, -1) * unit);
+ break;
+ default:
+ break;
+ }
+ if (!polygon.is_empty()) {
+ p_canvas_item->draw_polygon(polygon, color_array);
+ }
}
-Array TileSet::_get_tiles_ids() const {
- Array arr;
+void TileSetAtlasPluginTerrain::_draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ PackedColorArray color_array;
+ color_array.push_back(p_color);
+
+ Vector2 unit = Vector2(p_size) / 6.0;
+ PackedVector2Array polygon;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ polygon.push_back(Vector2(1, 0) * unit);
+ polygon.push_back(Vector2(2, -1) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(2, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(Vector2(0, 1) * unit);
+ polygon.push_back(Vector2(1, 2) * unit);
+ polygon.push_back(Vector2(2, 1) * unit);
+ polygon.push_back(Vector2(1, 0) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ polygon.push_back(Vector2(0, 1) * unit);
+ polygon.push_back(Vector2(-1, 2) * unit);
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(1, 2) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(Vector2(0, 1) * unit);
+ polygon.push_back(Vector2(-1, 2) * unit);
+ polygon.push_back(Vector2(-2, 1) * unit);
+ polygon.push_back(Vector2(-1, 0) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ polygon.push_back(Vector2(-1, 0) * unit);
+ polygon.push_back(Vector2(-2, -1) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(-2, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(Vector2(0, -1) * unit);
+ polygon.push_back(Vector2(-1, -2) * unit);
+ polygon.push_back(Vector2(-2, -1) * unit);
+ polygon.push_back(Vector2(-1, 0) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ polygon.push_back(Vector2(0, -1) * unit);
+ polygon.push_back(Vector2(-1, -2) * unit);
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(1, -2) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(Vector2(0, -1) * unit);
+ polygon.push_back(Vector2(1, -2) * unit);
+ polygon.push_back(Vector2(2, -1) * unit);
+ polygon.push_back(Vector2(1, 0) * unit);
+ break;
+ default:
+ break;
+ }
+ if (!polygon.is_empty()) {
+ p_canvas_item->draw_polygon(polygon, color_array);
+ }
+}
- for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) {
- arr.push_back(E->key());
+void TileSetAtlasPluginTerrain::_draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ PackedColorArray color_array;
+ color_array.push_back(p_color);
+
+ Vector2 unit = Vector2(p_size) / 6.0;
+ PackedVector2Array polygon;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ polygon.push_back(Vector2(0.5, -0.5) * unit);
+ polygon.push_back(Vector2(1.5, -1.5) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(1.5, 1.5) * unit);
+ polygon.push_back(Vector2(0.5, 0.5) * unit);
+ polygon.push_back(Vector2(1, 0) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ polygon.push_back(Vector2(-0.5, 0.5) * unit);
+ polygon.push_back(Vector2(-1.5, 1.5) * unit);
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(1.5, 1.5) * unit);
+ polygon.push_back(Vector2(0.5, 0.5) * unit);
+ polygon.push_back(Vector2(0, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ polygon.push_back(Vector2(-0.5, -0.5) * unit);
+ polygon.push_back(Vector2(-1.5, -1.5) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(-1.5, 1.5) * unit);
+ polygon.push_back(Vector2(-0.5, 0.5) * unit);
+ polygon.push_back(Vector2(-1, 0) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ polygon.push_back(Vector2(-0.5, -0.5) * unit);
+ polygon.push_back(Vector2(-1.5, -1.5) * unit);
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(1.5, -1.5) * unit);
+ polygon.push_back(Vector2(0.5, -0.5) * unit);
+ polygon.push_back(Vector2(0, -1) * unit);
+ break;
+ default:
+ break;
}
+ if (!polygon.is_empty()) {
+ p_canvas_item->draw_polygon(polygon, color_array);
+ }
+}
- return arr;
+void TileSetAtlasPluginTerrain::_draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) {
+ PackedColorArray color_array;
+ color_array.push_back(p_color);
+
+ Vector2 unit = Vector2(p_size) / 6.0;
+ PackedVector2Array polygon;
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(Vector2(1, 0) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(0, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(Vector2(-1, 0) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(0, 3) * unit);
+ polygon.push_back(Vector2(0, 1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(Vector2(-1, 0) * unit);
+ polygon.push_back(Vector2(-3, 0) * unit);
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(0, -1) * unit);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(Vector2(1, 0) * unit);
+ polygon.push_back(Vector2(3, 0) * unit);
+ polygon.push_back(Vector2(0, -3) * unit);
+ polygon.push_back(Vector2(0, -1) * unit);
+ break;
+ default:
+ break;
+ }
+ if (!polygon.is_empty()) {
+ p_canvas_item->draw_polygon(polygon, color_array);
+ }
}
-void TileSet::_decompose_convex_shape(Ref<Shape2D> p_shape) {
- if (Engine::get_singleton()->is_editor_hint()) {
- return;
+void TileSetAtlasPluginTerrain::_draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
+ PackedColorArray color_array;
+ color_array.push_back(p_color);
+
+ PackedVector2Array point_list;
+ point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
+ point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
+ point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0));
+ point_list.push_back(Vector2(0, 3));
+ point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0));
+ point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
+ point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
+ point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
+ point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
+ point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(0, -3));
+ point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0)));
+ point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0));
+
+ Vector2 unit = Vector2(p_size) / 6.0;
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = point_list[i] * unit;
+ }
+
+ PackedVector2Array polygon;
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ polygon.push_back(point_list[17]);
+ polygon.push_back(point_list[0]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[6]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ polygon.push_back(point_list[6]);
+ polygon.push_back(point_list[7]);
+ polygon.push_back(point_list[8]);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ polygon.push_back(point_list[8]);
+ polygon.push_back(point_list[9]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ polygon.push_back(point_list[9]);
+ polygon.push_back(point_list[10]);
+ polygon.push_back(point_list[11]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(point_list[11]);
+ polygon.push_back(point_list[12]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ polygon.push_back(point_list[12]);
+ polygon.push_back(point_list[13]);
+ polygon.push_back(point_list[14]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(point_list[14]);
+ polygon.push_back(point_list[15]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ polygon.push_back(point_list[15]);
+ polygon.push_back(point_list[16]);
+ polygon.push_back(point_list[17]);
+ break;
+ default:
+ break;
+ }
+ } else {
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = Vector2(point_list[i].y, point_list[i].x);
+ }
+ }
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ polygon.push_back(point_list[17]);
+ polygon.push_back(point_list[0]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ polygon.push_back(point_list[15]);
+ polygon.push_back(point_list[16]);
+ polygon.push_back(point_list[17]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(point_list[14]);
+ polygon.push_back(point_list[15]);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ polygon.push_back(point_list[12]);
+ polygon.push_back(point_list[13]);
+ polygon.push_back(point_list[14]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(point_list[11]);
+ polygon.push_back(point_list[12]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ polygon.push_back(point_list[9]);
+ polygon.push_back(point_list[10]);
+ polygon.push_back(point_list[11]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ polygon.push_back(point_list[8]);
+ polygon.push_back(point_list[9]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ polygon.push_back(point_list[6]);
+ polygon.push_back(point_list[7]);
+ polygon.push_back(point_list[8]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[6]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ int half_polygon_size = polygon.size();
+ for (int i = 0; i < half_polygon_size; i++) {
+ polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0);
+ }
+
+ if (!polygon.is_empty()) {
+ p_canvas_item->draw_polygon(polygon, color_array);
+ }
+}
+
+void TileSetAtlasPluginTerrain::_draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
+ PackedColorArray color_array;
+ color_array.push_back(p_color);
+
+ PackedVector2Array point_list;
+ point_list.push_back(Vector2(3, 0));
+ point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
+ point_list.push_back(Vector2(0, 3));
+ point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
+ point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(-3, 0));
+ point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
+ point_list.push_back(Vector2(0, -3));
+ point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0));
+ point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0)));
+
+ Vector2 unit = Vector2(p_size) / 6.0;
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = point_list[i] * unit;
+ }
+
+ PackedVector2Array polygon;
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[6]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ polygon.push_back(point_list[6]);
+ polygon.push_back(point_list[7]);
+ polygon.push_back(point_list[8]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ polygon.push_back(point_list[8]);
+ polygon.push_back(point_list[9]);
+ polygon.push_back(point_list[10]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ polygon.push_back(point_list[10]);
+ polygon.push_back(point_list[11]);
+ polygon.push_back(point_list[0]);
+ break;
+ default:
+ break;
+ }
+ } else {
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = Vector2(point_list[i].y, point_list[i].x);
+ }
+ }
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ polygon.push_back(point_list[10]);
+ polygon.push_back(point_list[11]);
+ polygon.push_back(point_list[0]);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ polygon.push_back(point_list[8]);
+ polygon.push_back(point_list[9]);
+ polygon.push_back(point_list[10]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ polygon.push_back(point_list[6]);
+ polygon.push_back(point_list[7]);
+ polygon.push_back(point_list[8]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[6]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ int half_polygon_size = polygon.size();
+ for (int i = 0; i < half_polygon_size; i++) {
+ polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0);
+ }
+
+ if (!polygon.is_empty()) {
+ p_canvas_item->draw_polygon(polygon, color_array);
+ }
+}
+
+void TileSetAtlasPluginTerrain::_draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
+ PackedColorArray color_array;
+ color_array.push_back(p_color);
+
+ PackedVector2Array point_list;
+ point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(0, 3));
+ point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0)));
+ point_list.push_back(Vector2(0, -3));
+ point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0)));
+
+ Vector2 unit = Vector2(p_size) / 6.0;
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = point_list[i] * unit;
+ }
+
+ PackedVector2Array polygon;
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[0]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ break;
+ default:
+ break;
+ }
+ } else {
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ for (int i = 0; i < point_list.size(); i++) {
+ point_list.write[i] = Vector2(point_list[i].y, point_list[i].x);
+ }
+ }
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ polygon.push_back(point_list[0]);
+ polygon.push_back(point_list[1]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ polygon.push_back(point_list[5]);
+ polygon.push_back(point_list[0]);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ polygon.push_back(point_list[4]);
+ polygon.push_back(point_list[5]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ polygon.push_back(point_list[3]);
+ polygon.push_back(point_list[4]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ polygon.push_back(point_list[2]);
+ polygon.push_back(point_list[3]);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ polygon.push_back(point_list[1]);
+ polygon.push_back(point_list[2]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ int half_polygon_size = polygon.size();
+ for (int i = 0; i < half_polygon_size; i++) {
+ polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0);
+ }
+
+ if (!polygon.is_empty()) {
+ p_canvas_item->draw_polygon(polygon, color_array);
}
- Ref<ConvexPolygonShape2D> convex = p_shape;
- if (!convex.is_valid()) {
+}
+
+#define TERRAIN_ALPHA 0.8
+
+#define DRAW_TERRAIN_BIT(f, bit) \
+ { \
+ int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \
+ if (terrain_id >= 0) { \
+ Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \
+ color.a = TERRAIN_ALPHA; \
+ f(p_canvas_item, color, size, (bit)); \
+ } \
+ }
+
+#define DRAW_HALF_OFFSET_TERRAIN_BIT(f, bit, overlap, half_offset_axis) \
+ { \
+ int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \
+ if (terrain_id >= 0) { \
+ Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \
+ color.a = TERRAIN_ALPHA; \
+ f(p_canvas_item, color, size, (bit), overlap, half_offset_axis); \
+ } \
+ }
+
+void TileSetAtlasPluginTerrain::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data) {
+ ERR_FAIL_COND(!p_tile_set);
+ ERR_FAIL_COND(!p_tile_data);
+
+ int terrain_set = p_tile_data->get_terrain_set();
+ if (terrain_set < 0) {
return;
}
- Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(convex->get_points());
- if (decomp.size() > 1) {
- Array sub_shapes;
- for (int i = 0; i < decomp.size(); i++) {
- Ref<ConvexPolygonShape2D> _convex = memnew(ConvexPolygonShape2D);
- _convex->set_points(decomp[i]);
- sub_shapes.append(_convex);
+ TileSet::TerrainMode terrain_mode = p_tile_set->get_terrain_set_mode(terrain_set);
+
+ TileSet::TileShape shape = p_tile_set->get_tile_shape();
+ Vector2i size = p_tile_set->get_tile_size();
+
+ RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform);
+ if (shape == TileSet::TILE_SHAPE_SQUARE) {
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
+ DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE);
+ DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER);
+ DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE);
+ DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER);
+ DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER);
+ DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER);
+ } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER);
+ DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER);
+ DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER);
+ DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER);
+ } else { // TileData::TERRAIN_MODE_MATCH_SIDES
+ DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE);
+ DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE);
+ DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ }
+ } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER);
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER);
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER);
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER);
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER);
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER);
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER);
+ DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER);
+ } else { // TileData::TERRAIN_MODE_MATCH_SIDES
+ DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
+ DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+ DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
}
- convex->set_meta("decomposed", sub_shapes);
} else {
- convex->set_meta("decomposed", Variant());
+ TileSet::TileOffsetAxis offset_axis = p_tile_set->get_tile_offset_axis();
+ float overlap = 0.0;
+ switch (p_tile_set->get_tile_shape()) {
+ case TileSet::TILE_SHAPE_HEXAGON:
+ overlap = 0.25;
+ break;
+ case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE:
+ overlap = 0.0;
+ break;
+ default:
+ break;
+ }
+ if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
+ if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis);
+ } else {
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis);
+ }
+ } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
+ if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis);
+ } else {
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis);
+ }
+ } else { // TileData::TERRAIN_MODE_MATCH_SIDES
+ if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis);
+ } else {
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis);
+ DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis);
+ }
+ }
}
+ RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D());
}
-void TileSet::get_tile_list(List<int> *p_tiles) const {
- for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) {
- p_tiles->push_back(E->key());
+/////////////////////////////// TileSetAtlasPluginRendering //////////////////////////////////////
+
+void TileSetAtlasPluginRendering::tilemap_notification(TileMap *p_tile_map, int p_what) {
+ switch (p_what) {
+ case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: {
+ bool visible = p_tile_map->is_visible_in_tree();
+ for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = p_tile_map->get_quadrant_map().front(); E_quadrant; E_quadrant = E_quadrant->next()) {
+ TileMapQuadrant &q = E_quadrant->get();
+
+ // Update occluders transform.
+ for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
+ Transform2D xform;
+ xform.set_origin(E_cell->key());
+ for (List<RID>::Element *E_occluder_id = q.occluders.front(); E_occluder_id; E_occluder_id = E_occluder_id->next()) {
+ RS::get_singleton()->canvas_light_occluder_set_enabled(E_occluder_id->get(), visible);
+ }
+ }
+ }
+ } break;
+ case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
+ if (!p_tile_map->is_inside_tree()) {
+ return;
+ }
+
+ for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = p_tile_map->get_quadrant_map().front(); E_quadrant; E_quadrant = E_quadrant->next()) {
+ TileMapQuadrant &q = E_quadrant->get();
+
+ // Update occluders transform.
+ for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
+ Transform2D xform;
+ xform.set_origin(E_cell->key());
+ for (List<RID>::Element *E_occluder_id = q.occluders.front(); E_occluder_id; E_occluder_id = E_occluder_id->next()) {
+ RS::get_singleton()->canvas_light_occluder_set_transform(E_occluder_id->get(), p_tile_map->get_global_transform() * xform);
+ }
+ }
+ }
+ } break;
}
}
-bool TileSet::has_tile(int p_id) const {
- return tile_map.has(p_id);
+void TileSetAtlasPluginRendering::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation) {
+ ERR_FAIL_COND(!p_tile_set.is_valid());
+ ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id));
+ ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords));
+ ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile));
+
+ TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Get the texture.
+ Ref<Texture2D> tex = atlas_source->get_texture();
+ if (!tex.is_valid()) {
+ return;
+ }
+
+ // Get tile data.
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile));
+
+ // Compute the offset
+ Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords);
+ Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile);
+
+ // Compute the destination rectangle in the CanvasItem.
+ Rect2 dest_rect;
+ dest_rect.size = source_rect.size;
+ dest_rect.size.x += fp_adjust;
+ dest_rect.size.y += fp_adjust;
+
+ bool transpose = tile_data->get_transpose();
+ if (transpose) {
+ dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
+ } else {
+ dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset);
+ }
+
+ if (tile_data->get_flip_h()) {
+ dest_rect.size.x = -dest_rect.size.x;
+ }
+
+ if (tile_data->get_flip_v()) {
+ dest_rect.size.y = -dest_rect.size.y;
+ }
+
+ // Get the tile modulation.
+ Color modulate = tile_data->get_modulate();
+ modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a);
+
+ // Draw the tile.
+ tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
+ }
}
-bool TileSet::is_tile_bound(int p_drawn_id, int p_neighbor_id) {
- if (p_drawn_id == p_neighbor_id) {
- return true;
- } else if (get_script_instance() != nullptr) {
- if (get_script_instance()->has_method("_is_tile_bound")) {
- Variant ret = get_script_instance()->call("_is_tile_bound", p_drawn_id, p_neighbor_id);
- if (ret.get_type() == Variant::BOOL) {
- return ret;
+void TileSetAtlasPluginRendering::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ ERR_FAIL_COND(!p_tile_map);
+ ERR_FAIL_COND(!p_tile_map->is_inside_tree());
+ Ref<TileSet> tile_set = p_tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ bool visible = p_tile_map->is_visible_in_tree();
+
+ SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
+ while (q_list_element) {
+ TileMapQuadrant &q = *q_list_element->self();
+
+ RenderingServer *rs = RenderingServer::get_singleton();
+
+ // Free the canvas items.
+ for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) {
+ rs->free(E->get());
+ }
+ q.canvas_items.clear();
+
+ // Free the occluders.
+ for (List<RID>::Element *E = q.occluders.front(); E; E = E->next()) {
+ rs->free(E->get());
+ }
+ q.occluders.clear();
+
+ // Those allow to group cell per material or z-index.
+ Ref<ShaderMaterial> prev_material;
+ int prev_z_index = 0;
+ RID prev_canvas_item;
+
+ // Iterate over the cells of the quadrant.
+ for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
+ TileMapCell c = p_tile_map->get_cell(E_cell->value());
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Get the tile data.
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ Ref<ShaderMaterial> mat = tile_data->tile_get_material();
+ int z_index = tile_data->get_z_index();
+
+ // Quandrant pos.
+ Vector2 position = p_tile_map->map_to_world(q.coords * p_tile_map->get_effective_quadrant_size()) - tile_set->get_tile_size() / 2;
+
+ // --- CanvasItems ---
+ // Create two canvas items, for rendering and debug.
+ RID canvas_item;
+
+ // Check if the material or the z_index changed.
+ if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) {
+ canvas_item = rs->canvas_item_create();
+ if (mat.is_valid()) {
+ rs->canvas_item_set_material(canvas_item, mat->get_rid());
+ }
+ rs->canvas_item_set_parent(canvas_item, p_tile_map->get_canvas_item());
+ rs->canvas_item_set_use_parent_material(canvas_item, p_tile_map->get_use_parent_material() || p_tile_map->get_material().is_valid());
+ Transform2D xform;
+ xform.set_origin(position);
+
+ rs->canvas_item_set_transform(canvas_item, xform);
+ rs->canvas_item_set_light_mask(canvas_item, p_tile_map->get_light_mask());
+ rs->canvas_item_set_z_index(canvas_item, z_index);
+
+ rs->canvas_item_set_default_texture_filter(canvas_item, RS::CanvasItemTextureFilter(p_tile_map->CanvasItem::get_texture_filter()));
+ rs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(p_tile_map->CanvasItem::get_texture_repeat()));
+
+ q.canvas_items.push_back(canvas_item);
+
+ prev_canvas_item = canvas_item;
+ prev_material = mat;
+ prev_z_index = z_index;
+
+ } else {
+ // Keep the same canvas_item to draw on.
+ canvas_item = prev_canvas_item;
+ }
+
+ // Drawing the tile in the canvas item.
+ draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, p_tile_map->get_self_modulate());
+
+ // --- Occluders ---
+ for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) {
+ Transform2D xform;
+ xform.set_origin(E_cell->key());
+ if (tile_data->get_occluder(i).is_valid()) {
+ RID occluder_id = rs->canvas_light_occluder_create();
+ rs->canvas_light_occluder_set_enabled(occluder_id, visible);
+ rs->canvas_light_occluder_set_transform(occluder_id, p_tile_map->get_global_transform() * xform);
+ rs->canvas_light_occluder_set_polygon(occluder_id, tile_data->get_occluder(i)->get_rid());
+ rs->canvas_light_occluder_attach_to_canvas(occluder_id, p_tile_map->get_canvas());
+ rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i));
+ q.occluders.push_back(occluder_id);
+ }
+ }
+ }
}
}
+
+ quadrant_order_dirty = true;
+ q_list_element = q_list_element->next();
+ }
+
+ // Reset the drawing indices
+ if (quadrant_order_dirty) {
+ int index = -(int64_t)0x80000000; //always must be drawn below children.
+
+ // Sort the quadrants coords per world coordinates
+ Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map;
+ Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map();
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ world_to_map[p_tile_map->map_to_world(E->key())] = E->key();
+ }
+
+ // Sort the quadrants
+ for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E = world_to_map.front(); E; E = E->next()) {
+ TileMapQuadrant &q = quadrant_map[E->value()];
+ for (List<RID>::Element *F = q.canvas_items.front(); F; F = F->next()) {
+ RS::get_singleton()->canvas_item_set_draw_index(F->get(), index++);
+ }
+ }
+
+ quadrant_order_dirty = false;
}
- return false;
}
-void TileSet::remove_tile(int p_id) {
- ERR_FAIL_COND(!tile_map.has(p_id));
- tile_map.erase(p_id);
- notify_property_list_changed();
- emit_changed();
+void TileSetAtlasPluginRendering::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+ Ref<TileSet> tile_set = p_tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ quadrant_order_dirty = true;
}
-int TileSet::get_last_unused_tile_id() const {
- if (tile_map.size()) {
- return tile_map.back()->key() + 1;
- } else {
- return 0;
+void TileSetAtlasPluginRendering::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+ // Free the canvas items.
+ for (List<RID>::Element *E = p_quadrant->canvas_items.front(); E; E = E->next()) {
+ RenderingServer::get_singleton()->free(E->get());
+ }
+ p_quadrant->canvas_items.clear();
+
+ // Free the occluders.
+ for (List<RID>::Element *E = p_quadrant->occluders.front(); E; E = E->next()) {
+ RenderingServer::get_singleton()->free(E->get());
}
+ p_quadrant->occluders.clear();
}
-int TileSet::find_tile_by_name(const String &p_name) const {
- for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) {
- if (p_name == E->get().name) {
- return E->key();
+/////////////////////////////// TileSetAtlasPluginPhysics //////////////////////////////////////
+
+void TileSetAtlasPluginPhysics::tilemap_notification(TileMap *p_tile_map, int p_what) {
+ switch (p_what) {
+ case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
+ // Update the bodies transforms.
+ if (p_tile_map->is_inside_tree()) {
+ Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map();
+ Transform2D global_transform = p_tile_map->get_global_transform();
+
+ for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ TileMapQuadrant &q = E->get();
+
+ Transform2D xform;
+ xform.set_origin(p_tile_map->map_to_world(E->key() * p_tile_map->get_effective_quadrant_size()));
+ xform = global_transform * xform;
+
+ for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
+ PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ }
+ }
+ }
+ } break;
+ }
+}
+
+void TileSetAtlasPluginPhysics::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ ERR_FAIL_COND(!p_tile_map);
+ ERR_FAIL_COND(!p_tile_map->is_inside_tree());
+ Ref<TileSet> tile_set = p_tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ Transform2D global_transform = p_tile_map->get_global_transform();
+ PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
+
+ SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
+ while (q_list_element) {
+ TileMapQuadrant &q = *q_list_element->self();
+
+ Vector2 quadrant_pos = p_tile_map->map_to_world(q.coords * p_tile_map->get_effective_quadrant_size());
+
+ // Clear shapes.
+ for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
+ ps->body_clear_shapes(q.bodies[body_index]);
+
+ // Position the bodies.
+ Transform2D xform;
+ xform.set_origin(quadrant_pos);
+ xform = global_transform * xform;
+ ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
}
+
+ for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
+ TileMapCell c = p_tile_map->get_cell(E_cell->get());
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+
+ for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
+ // Add the shapes again.
+ for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) {
+ bool one_way_collision = tile_data->is_collision_shape_one_way(body_index, shape_index);
+ float one_way_collision_margin = tile_data->get_collision_shape_one_way_margin(body_index, shape_index);
+ Ref<Shape2D> shape = tile_data->get_collision_shape_shape(body_index, shape_index);
+ if (shape.is_valid()) {
+ Transform2D xform = Transform2D();
+ xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos);
+
+ // Add decomposed convex shapes.
+ ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform);
+ ps->body_set_shape_metadata(q.bodies[body_index], shape_index, E_cell->get());
+ ps->body_set_shape_as_one_way_collision(q.bodies[body_index], shape_index, one_way_collision, one_way_collision_margin);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ q_list_element = q_list_element->next();
}
- return -1;
}
-void TileSet::reset_state() {
- clear();
+void TileSetAtlasPluginPhysics::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+ Ref<TileSet> tile_set = p_tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ //Get the TileMap's gobla transform.
+ Transform2D global_transform;
+ if (p_tile_map->is_inside_tree()) {
+ global_transform = p_tile_map->get_global_transform();
+ }
+
+ // Clear all bodies.
+ p_quadrant->bodies.clear();
+
+ // Create the body and set its parameters.
+ for (int layer_index = 0; layer_index < tile_set->get_physics_layers_count(); layer_index++) {
+ RID body = PhysicsServer2D::get_singleton()->body_create();
+ PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC);
+
+ PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, p_tile_map->get_instance_id());
+ PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer_index));
+ PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer_index));
+
+ Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer_index);
+ if (!physics_material.is_valid()) {
+ PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
+ PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1);
+ } else {
+ PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce());
+ PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction());
+ }
+
+ if (p_tile_map->is_inside_tree()) {
+ RID space = p_tile_map->get_world_2d()->get_space();
+ PhysicsServer2D::get_singleton()->body_set_space(body, space);
+
+ Transform2D xform;
+ xform.set_origin(p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()));
+ xform = global_transform * xform;
+ PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ }
+
+ p_quadrant->bodies.push_back(body);
+ }
}
-void TileSet::clear() {
- tile_map.clear();
- notify_property_list_changed();
- emit_changed();
+void TileSetAtlasPluginPhysics::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+ // Remove a quadrant.
+ for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) {
+ PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]);
+ }
+ p_quadrant->bodies.clear();
}
-void TileSet::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_tile", "id"), &TileSet::create_tile);
- ClassDB::bind_method(D_METHOD("autotile_clear_bitmask_map", "id"), &TileSet::autotile_clear_bitmask_map);
- ClassDB::bind_method(D_METHOD("autotile_set_icon_coordinate", "id", "coord"), &TileSet::autotile_set_icon_coordinate);
- ClassDB::bind_method(D_METHOD("autotile_get_icon_coordinate", "id"), &TileSet::autotile_get_icon_coordinate);
- ClassDB::bind_method(D_METHOD("autotile_set_subtile_priority", "id", "coord", "priority"), &TileSet::autotile_set_subtile_priority);
- ClassDB::bind_method(D_METHOD("autotile_get_subtile_priority", "id", "coord"), &TileSet::autotile_get_subtile_priority);
- ClassDB::bind_method(D_METHOD("autotile_set_z_index", "id", "coord", "z_index"), &TileSet::autotile_set_z_index);
- ClassDB::bind_method(D_METHOD("autotile_get_z_index", "id", "coord"), &TileSet::autotile_get_z_index);
- ClassDB::bind_method(D_METHOD("autotile_set_light_occluder", "id", "light_occluder", "coord"), &TileSet::autotile_set_light_occluder);
- ClassDB::bind_method(D_METHOD("autotile_get_light_occluder", "id", "coord"), &TileSet::autotile_get_light_occluder);
- ClassDB::bind_method(D_METHOD("autotile_set_navigation_polygon", "id", "navigation_polygon", "coord"), &TileSet::autotile_set_navigation_polygon);
- ClassDB::bind_method(D_METHOD("autotile_get_navigation_polygon", "id", "coord"), &TileSet::autotile_get_navigation_polygon);
- ClassDB::bind_method(D_METHOD("autotile_set_bitmask", "id", "bitmask", "flag"), &TileSet::autotile_set_bitmask);
- ClassDB::bind_method(D_METHOD("autotile_get_bitmask", "id", "coord"), &TileSet::autotile_get_bitmask);
- ClassDB::bind_method(D_METHOD("autotile_set_bitmask_mode", "id", "mode"), &TileSet::autotile_set_bitmask_mode);
- ClassDB::bind_method(D_METHOD("autotile_get_bitmask_mode", "id"), &TileSet::autotile_get_bitmask_mode);
- ClassDB::bind_method(D_METHOD("autotile_set_spacing", "id", "spacing"), &TileSet::autotile_set_spacing);
- ClassDB::bind_method(D_METHOD("autotile_get_spacing", "id"), &TileSet::autotile_get_spacing);
- ClassDB::bind_method(D_METHOD("autotile_set_size", "id", "size"), &TileSet::autotile_set_size);
- ClassDB::bind_method(D_METHOD("autotile_get_size", "id"), &TileSet::autotile_get_size);
- ClassDB::bind_method(D_METHOD("tile_set_name", "id", "name"), &TileSet::tile_set_name);
- ClassDB::bind_method(D_METHOD("tile_get_name", "id"), &TileSet::tile_get_name);
- ClassDB::bind_method(D_METHOD("tile_set_texture", "id", "texture"), &TileSet::tile_set_texture);
- ClassDB::bind_method(D_METHOD("tile_get_texture", "id"), &TileSet::tile_get_texture);
- ClassDB::bind_method(D_METHOD("tile_set_material", "id", "material"), &TileSet::tile_set_material);
- ClassDB::bind_method(D_METHOD("tile_get_material", "id"), &TileSet::tile_get_material);
- ClassDB::bind_method(D_METHOD("tile_set_modulate", "id", "color"), &TileSet::tile_set_modulate);
- ClassDB::bind_method(D_METHOD("tile_get_modulate", "id"), &TileSet::tile_get_modulate);
- ClassDB::bind_method(D_METHOD("tile_set_texture_offset", "id", "texture_offset"), &TileSet::tile_set_texture_offset);
- ClassDB::bind_method(D_METHOD("tile_get_texture_offset", "id"), &TileSet::tile_get_texture_offset);
- ClassDB::bind_method(D_METHOD("tile_set_region", "id", "region"), &TileSet::tile_set_region);
- ClassDB::bind_method(D_METHOD("tile_get_region", "id"), &TileSet::tile_get_region);
- ClassDB::bind_method(D_METHOD("tile_set_shape", "id", "shape_id", "shape"), &TileSet::tile_set_shape);
- ClassDB::bind_method(D_METHOD("tile_get_shape", "id", "shape_id"), &TileSet::tile_get_shape);
- ClassDB::bind_method(D_METHOD("tile_set_shape_offset", "id", "shape_id", "shape_offset"), &TileSet::tile_set_shape_offset);
- ClassDB::bind_method(D_METHOD("tile_get_shape_offset", "id", "shape_id"), &TileSet::tile_get_shape_offset);
- ClassDB::bind_method(D_METHOD("tile_set_shape_transform", "id", "shape_id", "shape_transform"), &TileSet::tile_set_shape_transform);
- ClassDB::bind_method(D_METHOD("tile_get_shape_transform", "id", "shape_id"), &TileSet::tile_get_shape_transform);
- ClassDB::bind_method(D_METHOD("tile_set_shape_one_way", "id", "shape_id", "one_way"), &TileSet::tile_set_shape_one_way);
- ClassDB::bind_method(D_METHOD("tile_get_shape_one_way", "id", "shape_id"), &TileSet::tile_get_shape_one_way);
- ClassDB::bind_method(D_METHOD("tile_set_shape_one_way_margin", "id", "shape_id", "one_way"), &TileSet::tile_set_shape_one_way_margin);
- ClassDB::bind_method(D_METHOD("tile_get_shape_one_way_margin", "id", "shape_id"), &TileSet::tile_get_shape_one_way_margin);
- ClassDB::bind_method(D_METHOD("tile_add_shape", "id", "shape", "shape_transform", "one_way", "autotile_coord"), &TileSet::tile_add_shape, DEFVAL(false), DEFVAL(Vector2()));
- ClassDB::bind_method(D_METHOD("tile_get_shape_count", "id"), &TileSet::tile_get_shape_count);
- ClassDB::bind_method(D_METHOD("tile_set_shapes", "id", "shapes"), &TileSet::_tile_set_shapes);
- ClassDB::bind_method(D_METHOD("tile_get_shapes", "id"), &TileSet::_tile_get_shapes);
- ClassDB::bind_method(D_METHOD("tile_set_tile_mode", "id", "tilemode"), &TileSet::tile_set_tile_mode);
- ClassDB::bind_method(D_METHOD("tile_get_tile_mode", "id"), &TileSet::tile_get_tile_mode);
- ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon", "id", "navigation_polygon"), &TileSet::tile_set_navigation_polygon);
- ClassDB::bind_method(D_METHOD("tile_get_navigation_polygon", "id"), &TileSet::tile_get_navigation_polygon);
- ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon_offset", "id", "navigation_polygon_offset"), &TileSet::tile_set_navigation_polygon_offset);
- ClassDB::bind_method(D_METHOD("tile_get_navigation_polygon_offset", "id"), &TileSet::tile_get_navigation_polygon_offset);
- ClassDB::bind_method(D_METHOD("tile_set_light_occluder", "id", "light_occluder"), &TileSet::tile_set_light_occluder);
- ClassDB::bind_method(D_METHOD("tile_get_light_occluder", "id"), &TileSet::tile_get_light_occluder);
- ClassDB::bind_method(D_METHOD("tile_set_occluder_offset", "id", "occluder_offset"), &TileSet::tile_set_occluder_offset);
- ClassDB::bind_method(D_METHOD("tile_get_occluder_offset", "id"), &TileSet::tile_get_occluder_offset);
- ClassDB::bind_method(D_METHOD("tile_set_z_index", "id", "z_index"), &TileSet::tile_set_z_index);
- ClassDB::bind_method(D_METHOD("tile_get_z_index", "id"), &TileSet::tile_get_z_index);
-
- ClassDB::bind_method(D_METHOD("remove_tile", "id"), &TileSet::remove_tile);
- ClassDB::bind_method(D_METHOD("clear"), &TileSet::clear);
- ClassDB::bind_method(D_METHOD("get_last_unused_tile_id"), &TileSet::get_last_unused_tile_id);
- ClassDB::bind_method(D_METHOD("find_tile_by_name", "name"), &TileSet::find_tile_by_name);
- ClassDB::bind_method(D_METHOD("get_tiles_ids"), &TileSet::_get_tiles_ids);
-
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_tile_bound", PropertyInfo(Variant::INT, "drawn_id"), PropertyInfo(Variant::INT, "neighbor_id")));
- BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_subtile_selection", PropertyInfo(Variant::INT, "autotile_id"), PropertyInfo(Variant::INT, "bitmask"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location")));
- BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_atlas_subtile_selection", PropertyInfo(Variant::INT, "atlastile_id"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location")));
-
- BIND_ENUM_CONSTANT(BITMASK_2X2);
- BIND_ENUM_CONSTANT(BITMASK_3X3_MINIMAL);
- BIND_ENUM_CONSTANT(BITMASK_3X3);
-
- BIND_ENUM_CONSTANT(BIND_TOPLEFT);
- BIND_ENUM_CONSTANT(BIND_TOP);
- BIND_ENUM_CONSTANT(BIND_TOPRIGHT);
- BIND_ENUM_CONSTANT(BIND_LEFT);
- BIND_ENUM_CONSTANT(BIND_CENTER);
- BIND_ENUM_CONSTANT(BIND_RIGHT);
- BIND_ENUM_CONSTANT(BIND_BOTTOMLEFT);
- BIND_ENUM_CONSTANT(BIND_BOTTOM);
- BIND_ENUM_CONSTANT(BIND_BOTTOMRIGHT);
-
- BIND_ENUM_CONSTANT(SINGLE_TILE);
- BIND_ENUM_CONSTANT(AUTO_TILE);
- BIND_ENUM_CONSTANT(ATLAS_TILE);
+void TileSetAtlasPluginPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+ // Draw the debug collision shapes.
+ Ref<TileSet> tile_set = p_tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ if (!p_tile_map->get_tree() || !(Engine::get_singleton()->is_editor_hint() || p_tile_map->get_tree()->is_debugging_collisions_hint())) {
+ return;
+ }
+
+ RenderingServer *rs = RenderingServer::get_singleton();
+
+ Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size());
+
+ Color debug_collision_color = p_tile_map->get_tree()->get_debug_collisions_color();
+ for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
+ TileMapCell c = p_tile_map->get_cell(E_cell->get());
+
+ Transform2D xform;
+ xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos);
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+
+ if (tile_set->has_source(c.source_id)) {
+ TileSetSource *source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+
+ for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) {
+ for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) {
+ // Draw the debug shape.
+ Ref<Shape2D> shape = tile_data->get_collision_shape_shape(body_index, shape_index);
+ if (shape.is_valid()) {
+ shape->draw(p_quadrant->debug_canvas_item, debug_collision_color);
+ }
+ }
+ }
+ }
+ }
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D());
+ }
+};
+
+/////////////////////////////// TileSetAtlasPluginNavigation //////////////////////////////////////
+
+void TileSetAtlasPluginNavigation::tilemap_notification(TileMap *p_tile_map, int p_what) {
+ switch (p_what) {
+ case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
+ if (p_tile_map->is_inside_tree()) {
+ Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map();
+ Transform2D tilemap_xform = p_tile_map->get_global_transform();
+ for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) {
+ TileMapQuadrant &q = E_quadrant->get();
+ for (Map<Vector2i, Vector<RID>>::Element *E_region = q.navigation_regions.front(); E_region; E_region = E_region->next()) {
+ for (int layer_index = 0; layer_index < E_region->get().size(); layer_index++) {
+ RID region = E_region->get()[layer_index];
+ if (!region.is_valid()) {
+ continue;
+ }
+ Transform2D tile_transform;
+ tile_transform.set_origin(p_tile_map->map_to_world(E_region->key()));
+ NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
+ }
+ }
+ }
+ }
+ } break;
+ }
}
-TileSet::TileSet() {
+void TileSetAtlasPluginNavigation::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ ERR_FAIL_COND(!p_tile_map);
+ ERR_FAIL_COND(!p_tile_map->is_inside_tree());
+ Ref<TileSet> tile_set = p_tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ // Get colors for debug.
+ SceneTree *st = SceneTree::get_singleton();
+ Color debug_navigation_color;
+ bool debug_navigation = st && st->is_debugging_navigation_hint();
+ if (debug_navigation) {
+ debug_navigation_color = st->get_debug_navigation_color();
+ }
+
+ Transform2D tilemap_xform = p_tile_map->get_global_transform();
+ SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
+ while (q_list_element) {
+ TileMapQuadrant &q = *q_list_element->self();
+
+ // Clear navigation shapes in the quadrant.
+ for (Map<Vector2i, Vector<RID>>::Element *E = q.navigation_regions.front(); E; E = E->next()) {
+ for (int i = 0; i < E->get().size(); i++) {
+ RID region = E->get()[i];
+ if (!region.is_valid()) {
+ continue;
+ }
+ NavigationServer2D::get_singleton()->region_set_map(region, RID());
+ }
+ }
+ q.navigation_regions.clear();
+
+ // Get the navigation polygons and create regions.
+ for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
+ TileMapCell c = p_tile_map->get_cell(E_cell->get());
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count());
+
+ for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
+ Ref<NavigationPolygon> navpoly;
+ navpoly = tile_data->get_navigation_polygon(layer_index);
+
+ if (navpoly.is_valid()) {
+ Transform2D tile_transform;
+ tile_transform.set_origin(p_tile_map->map_to_world(E_cell->get()));
+
+ RID region = NavigationServer2D::get_singleton()->region_create();
+ NavigationServer2D::get_singleton()->region_set_map(region, p_tile_map->get_world_2d()->get_navigation_map());
+ NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
+ NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly);
+ q.navigation_regions[E_cell->get()].write[layer_index] = region;
+ }
+ }
+ }
+ }
+ }
+
+ q_list_element = q_list_element->next();
+ }
+}
+
+void TileSetAtlasPluginNavigation::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+ // Clear navigation shapes in the quadrant.
+ for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) {
+ for (int i = 0; i < E->get().size(); i++) {
+ RID region = E->get()[i];
+ if (!region.is_valid()) {
+ continue;
+ }
+ NavigationServer2D::get_singleton()->free(region);
+ }
+ }
+ p_quadrant->navigation_regions.clear();
+}
+
+void TileSetAtlasPluginNavigation::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) {
+ // Draw the debug collision shapes.
+ Ref<TileSet> tile_set = p_tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ if (!p_tile_map->get_tree() || !(Engine::get_singleton()->is_editor_hint() || p_tile_map->get_tree()->is_debugging_navigation_hint())) {
+ return;
+ }
+
+ RenderingServer *rs = RenderingServer::get_singleton();
+
+ Color color = p_tile_map->get_tree()->get_debug_navigation_color();
+ RandomPCG rand;
+
+ Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size());
+
+ for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
+ TileMapCell c = p_tile_map->get_cell(E_cell->get());
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+
+ Transform2D xform;
+ xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos);
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+
+ for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
+ Ref<NavigationPolygon> navpoly = tile_data->get_navigation_polygon(layer_index);
+ if (navpoly.is_valid()) {
+ PackedVector2Array navigation_polygon_vertices = navpoly->get_vertices();
+
+ for (int i = 0; i < navpoly->get_polygon_count(); i++) {
+ // An array of vertices for this polygon.
+ Vector<int> polygon = navpoly->get_polygon(i);
+ Vector<Vector2> vertices;
+ vertices.resize(polygon.size());
+ for (int j = 0; j < polygon.size(); j++) {
+ ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size());
+ vertices.write[j] = navigation_polygon_vertices[polygon[j]];
+ }
+
+ // Generate the polygon color, slightly randomly modified from the settings one.
+ Color random_variation_color;
+ random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1);
+ random_variation_color.a = color.a;
+ Vector<Color> colors;
+ colors.push_back(random_variation_color);
+
+ RS::get_singleton()->canvas_item_add_polygon(p_quadrant->debug_canvas_item, vertices, colors);
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 0a8721f35b..20cf183a20 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -32,226 +32,614 @@
#define TILE_SET_H
#include "core/io/resource.h"
-#include "core/variant/array.h"
+#include "core/object/object.h"
#include "scene/2d/light_occluder_2d.h"
#include "scene/2d/navigation_region_2d.h"
+#include "scene/main/canvas_item.h"
#include "scene/resources/convex_polygon_shape_2d.h"
+#include "scene/resources/packed_scene.h"
+#include "scene/resources/physics_material.h"
+#include "scene/resources/shape_2d.h"
+
+#ifndef DISABLE_DEPRECATED
+#include "scene/2d/light_occluder_2d.h"
+#include "scene/2d/navigation_region_2d.h"
+#include "scene/resources/shader.h"
#include "scene/resources/shape_2d.h"
#include "scene/resources/texture.h"
+#endif
+
+class TileMap;
+struct TileMapQuadrant;
+class TileSetSource;
+class TileSetAtlasSource;
+class TileData;
+
+// Forward-declare the plugins.
+class TileSetPlugin;
+class TileSetAtlasPluginRendering;
+class TileSetAtlasPluginPhysics;
+class TileSetAtlasPluginNavigation;
+class TileSetAtlasPluginTerrain;
class TileSet : public Resource {
GDCLASS(TileSet, Resource);
-public:
- struct ShapeData {
+#ifndef DISABLE_DEPRECATED
+private:
+ struct CompatibilityShapeData {
+ Vector2i autotile_coords;
+ bool one_way;
+ float one_way_margin;
Ref<Shape2D> shape;
- Transform2D shape_transform;
- Vector2 autotile_coord;
- bool one_way_collision = false;
- float one_way_collision_margin = 1.0;
+ Transform2D transform;
+ };
- ShapeData() {}
+ struct CompatibilityTileData {
+ String name;
+ Ref<Texture2D> texture;
+ Vector2 tex_offset;
+ Ref<ShaderMaterial> material;
+ Rect2 region;
+ int tile_mode;
+ Color modulate;
+
+ // Atlas or autotiles data
+ int autotile_bitmask_mode;
+ Vector2 autotile_icon_coordinate;
+ Size2i autotile_tile_size = Size2i(16, 16);
+
+ int autotile_spacing;
+ Map<Vector2i, int> autotile_bitmask_flags;
+ Map<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map;
+ Map<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map;
+ Map<Vector2i, int> autotile_priority_map;
+ Map<Vector2i, int> autotile_z_index_map;
+
+ Vector<CompatibilityShapeData> shapes;
+ Ref<OccluderPolygon2D> occluder;
+ Vector2 occluder_offset;
+ Ref<NavigationPolygon> navigation;
+ Vector2 navigation_offset;
+ int z_index;
};
- enum BitmaskMode {
- BITMASK_2X2,
- BITMASK_3X3_MINIMAL,
- BITMASK_3X3
+ Map<int, CompatibilityTileData *> compatibility_data = Map<int, CompatibilityTileData *>();
+ Map<int, int> compatibility_source_mapping = Map<int, int>();
+
+private:
+ void compatibility_conversion();
+
+public:
+ int compatibility_get_source_for_tile_id(int p_old_source) {
+ return compatibility_source_mapping[p_old_source];
};
- enum AutotileBindings {
- BIND_TOPLEFT = 1,
- BIND_TOP = 2,
- BIND_TOPRIGHT = 4,
- BIND_LEFT = 8,
- BIND_CENTER = 16,
- BIND_RIGHT = 32,
- BIND_BOTTOMLEFT = 64,
- BIND_BOTTOM = 128,
- BIND_BOTTOMRIGHT = 256,
-
- BIND_IGNORE_TOPLEFT = 1 << 16,
- BIND_IGNORE_TOP = 1 << 17,
- BIND_IGNORE_TOPRIGHT = 1 << 18,
- BIND_IGNORE_LEFT = 1 << 19,
- BIND_IGNORE_CENTER = 1 << 20,
- BIND_IGNORE_RIGHT = 1 << 21,
- BIND_IGNORE_BOTTOMLEFT = 1 << 22,
- BIND_IGNORE_BOTTOM = 1 << 23,
- BIND_IGNORE_BOTTOMRIGHT = 1 << 24
+#endif // DISABLE_DEPRECATED
+
+public:
+ enum CellNeighbor {
+ CELL_NEIGHBOR_RIGHT_SIDE = 0,
+ CELL_NEIGHBOR_RIGHT_CORNER,
+ CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE,
+ CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER,
+ CELL_NEIGHBOR_BOTTOM_SIDE,
+ CELL_NEIGHBOR_BOTTOM_CORNER,
+ CELL_NEIGHBOR_BOTTOM_LEFT_SIDE,
+ CELL_NEIGHBOR_BOTTOM_LEFT_CORNER,
+ CELL_NEIGHBOR_LEFT_SIDE,
+ CELL_NEIGHBOR_LEFT_CORNER,
+ CELL_NEIGHBOR_TOP_LEFT_SIDE,
+ CELL_NEIGHBOR_TOP_LEFT_CORNER,
+ CELL_NEIGHBOR_TOP_SIDE,
+ CELL_NEIGHBOR_TOP_CORNER,
+ CELL_NEIGHBOR_TOP_RIGHT_SIDE,
+ CELL_NEIGHBOR_TOP_RIGHT_CORNER,
+ CELL_NEIGHBOR_MAX,
};
- enum TileMode {
- SINGLE_TILE,
- AUTO_TILE,
- ATLAS_TILE
+ enum TerrainMode {
+ TERRAIN_MODE_MATCH_CORNERS_AND_SIDES = 0,
+ TERRAIN_MODE_MATCH_CORNERS,
+ TERRAIN_MODE_MATCH_SIDES,
};
- struct AutotileData {
- BitmaskMode bitmask_mode = BITMASK_2X2;
- // Default size to prevent invalid value
- Size2 size = Size2(64, 64);
- Vector2 icon_coord = Vector2(0, 0);
- int spacing = 0;
- Map<Vector2, uint32_t> flags;
- Map<Vector2, Ref<OccluderPolygon2D>> occluder_map;
- Map<Vector2, Ref<NavigationPolygon>> navpoly_map;
- Map<Vector2, int> priority_map;
- Map<Vector2, int> z_index_map;
-
- explicit AutotileData() {}
+ enum TileShape {
+ TILE_SHAPE_SQUARE,
+ TILE_SHAPE_ISOMETRIC,
+ TILE_SHAPE_HALF_OFFSET_SQUARE,
+ TILE_SHAPE_HEXAGON,
};
-private:
- struct TileData {
- String name;
- Ref<Texture2D> texture;
- Vector2 offset;
- Rect2i region;
- Vector<ShapeData> shapes_data;
- Vector2 occluder_offset;
- Ref<OccluderPolygon2D> occluder;
- Vector2 navigation_polygon_offset;
- Ref<NavigationPolygon> navigation_polygon;
- Ref<ShaderMaterial> material;
- TileMode tile_mode = SINGLE_TILE;
- // Default modulate for back-compat
- Color modulate = Color(1, 1, 1);
- AutotileData autotile_data;
- int z_index = 0;
+ enum TileLayout {
+ TILE_LAYOUT_STACKED,
+ TILE_LAYOUT_STACKED_OFFSET,
+ TILE_LAYOUT_STAIRS_RIGHT,
+ TILE_LAYOUT_STAIRS_DOWN,
+ TILE_LAYOUT_DIAMOND_RIGHT,
+ TILE_LAYOUT_DIAMOND_DOWN,
+ };
- explicit TileData() {}
+ enum TileOffsetAxis {
+ TILE_OFFSET_AXIS_HORIZONTAL,
+ TILE_OFFSET_AXIS_VERTICAL,
};
- Map<int, TileData> tile_map;
+public:
+ struct PackedSceneSource {
+ Ref<PackedScene> scene;
+ Vector2 offset;
+ };
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
- void _tile_set_shapes(int p_id, const Array &p_shapes);
- Array _tile_get_shapes(int p_id) const;
- Array _get_tiles_ids() const;
- void _decompose_convex_shape(Ref<Shape2D> p_shape);
+private:
+ // --- TileSet data ---
+ // Basic shape and layout.
+ TileShape tile_shape = TILE_SHAPE_SQUARE;
+ TileLayout tile_layout = TILE_LAYOUT_STACKED;
+ TileOffsetAxis tile_offset_axis = TILE_OFFSET_AXIS_HORIZONTAL;
+ Size2i tile_size = Size2i(16, 16); //Size2(64, 64);
+ Vector2 tile_skew = Vector2(0, 0);
+
+ // Rendering.
+ bool y_sorting = false;
+ bool uv_clipping = false;
+ struct OcclusionLayer {
+ uint32_t light_mask = 1;
+ bool sdf_collision = false;
+ };
+ Vector<OcclusionLayer> occlusion_layers;
+
+ // Physics
+ struct PhysicsLayer {
+ uint32_t collision_layer = 1;
+ uint32_t collision_mask = 1;
+ Ref<PhysicsMaterial> physics_material;
+ };
+ Vector<PhysicsLayer> physics_layers;
+
+ // Terrains
+ struct Terrain {
+ String name;
+ Color color;
+ };
+ struct TerrainSet {
+ TerrainMode mode = TERRAIN_MODE_MATCH_CORNERS_AND_SIDES;
+ Vector<Terrain> terrains;
+ };
+ Vector<TerrainSet> terrain_sets;
+
+ // Navigation
+ struct Navigationlayer {
+ uint32_t layers = 1;
+ };
+ Vector<Navigationlayer> navigation_layers;
+
+ // CustomData
+ struct CustomDataLayer {
+ String name;
+ Variant::Type type = Variant::NIL;
+ };
+ Vector<CustomDataLayer> custom_data_layers;
+ Map<String, int> custom_data_layers_by_name;
+
+ // Per Atlas source data.
+ Map<int, Ref<TileSetSource>> sources;
+ Vector<int> source_ids;
+ int next_source_id = 0;
+ // ---------------------
+
+ // Plugins themselves.
+ Vector<TileSetPlugin *> tile_set_plugins_vector;
+
+ void _compute_next_source_id();
+ void _source_changed();
+
+protected:
static void _bind_methods();
+public:
+ // --- Plugins ---
+ Vector<TileSetPlugin *> get_tile_set_atlas_plugins() const;
+
+ // --- Accessors for TileSet data ---
+
+ // -- Shape and layout --
+ void set_tile_shape(TileShape p_shape);
+ TileShape get_tile_shape() const;
+ void set_tile_layout(TileLayout p_layout);
+ TileLayout get_tile_layout() const;
+ void set_tile_offset_axis(TileOffsetAxis p_alignment);
+ TileOffsetAxis get_tile_offset_axis() const;
+ void set_tile_size(Size2i p_size);
+ Size2i get_tile_size() const;
+ void set_tile_skew(Vector2 p_skew);
+ Vector2 get_tile_skew() const;
+
+ // -- Sources management --
+ int get_next_source_id() const;
+ int get_source_count() const;
+ int get_source_id(int p_index) const;
+ int add_source(Ref<TileSetAtlasSource> p_tile_atlas_source, int p_source_id_override = -1);
+ void set_source_id(int p_source_id, int p_new_id);
+ void remove_source(int p_source_id);
+ bool has_source(int p_source_id) const;
+ Ref<TileSetSource> get_source(int p_source_id) const;
+
+ // Rendering
+ void set_y_sorting(bool p_y_sort);
+ bool is_y_sorting() const;
+
+ void set_uv_clipping(bool p_uv_clipping);
+ bool is_uv_clipping() const;
+
+ void set_occlusion_layers_count(int p_occlusion_layers_count);
+ int get_occlusion_layers_count() const;
+ void set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask);
+ int get_occlusion_layer_light_mask(int p_layer_index) const;
+ void set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision);
+ bool get_occlusion_layer_sdf_collision(int p_layer_index) const;
+
+ // Physics
+ void set_physics_layers_count(int p_physics_layers_count);
+ int get_physics_layers_count() const;
+ void set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer);
+ uint32_t get_physics_layer_collision_layer(int p_layer_index) const;
+ void set_physics_layer_collision_mask(int p_layer_index, uint32_t p_mask);
+ uint32_t get_physics_layer_collision_mask(int p_layer_index) const;
+ void set_physics_layer_physics_material(int p_layer_index, Ref<PhysicsMaterial> p_physics_material);
+ Ref<PhysicsMaterial> get_physics_layer_physics_material(int p_layer_index) const;
+
+ // Terrains
+ void set_terrain_sets_count(int p_terrains_sets_count);
+ int get_terrain_sets_count() const;
+ void set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode);
+ TerrainMode get_terrain_set_mode(int p_terrain_set) const;
+ void set_terrains_count(int p_terrain_set, int p_terrains_count);
+ int get_terrains_count(int p_terrain_set) const;
+ void set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name);
+ String get_terrain_name(int p_terrain_set, int p_terrain_index) const;
+ void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color);
+ Color get_terrain_color(int p_terrain_set, int p_terrain_index) const;
+ bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const;
+
+ // Navigation
+ void set_navigation_layers_count(int p_navigation_layers_count);
+ int get_navigation_layers_count() const;
+ void set_navigation_layer_layers(int p_layer_index, uint32_t p_layers);
+ uint32_t get_navigation_layer_layers(int p_layer_index) const;
+
+ // Custom data
+ void set_custom_data_layers_count(int p_custom_data_layers_count);
+ int get_custom_data_layers_count() const;
+ int get_custom_data_layer_by_name(String p_value) const;
+ void set_custom_data_name(int p_layer_id, String p_value);
+ String get_custom_data_name(int p_layer_id) const;
+ void set_custom_data_type(int p_layer_id, Variant::Type p_value);
+ Variant::Type get_custom_data_type(int p_layer_id) const;
+
+ // Helpers
+ void draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
+
virtual void reset_state() override;
+ TileSet();
+ ~TileSet();
+};
+
+class TileSetSource : public Resource {
+ GDCLASS(TileSetSource, Resource);
+
+protected:
+ const TileSet *tile_set = nullptr;
+
public:
- void create_tile(int p_id);
+ // Not exposed.
+ virtual void set_tile_set(const TileSet *p_tile_set);
+ virtual void notify_tile_data_properties_should_change(){};
+ virtual void reset_state() override{};
+
+ // Tiles.
+ virtual int get_tiles_count() const = 0;
+ virtual Vector2i get_tile_id(int tile_index) const = 0;
+ virtual bool has_tile(Vector2i p_atlas_coords) const = 0;
+
+ // Alternative tiles.
+ virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const = 0;
+ virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const = 0;
+ virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const = 0;
+};
- void autotile_set_bitmask_mode(int p_id, BitmaskMode p_mode);
- BitmaskMode autotile_get_bitmask_mode(int p_id) const;
+class TileSetAtlasSource : public TileSetSource {
+ GDCLASS(TileSetAtlasSource, TileSetSource);
- void tile_set_name(int p_id, const String &p_name);
- String tile_get_name(int p_id) const;
+public:
+ static const Vector2i INVALID_ATLAS_COORDS; // Vector2i(-1, -1);
+ static const int INVALID_TILE_ALTERNATIVE; // -1;
+
+ struct TileAlternativesData {
+ Vector2i size_in_atlas = Vector2i(1, 1);
+ Vector2i texture_offset;
+ Map<int, TileData *> alternatives;
+ Vector<int> alternatives_ids;
+ int next_alternative_id = 1;
+ };
- void tile_set_texture(int p_id, const Ref<Texture2D> &p_texture);
- Ref<Texture2D> tile_get_texture(int p_id) const;
+private:
+ Ref<Texture2D> texture;
+ Vector2i margins;
+ Vector2i separation;
+ Size2i texture_region_size = Size2i(16, 16);
- void tile_set_texture_offset(int p_id, const Vector2 &p_offset);
- Vector2 tile_get_texture_offset(int p_id) const;
+ Map<Vector2i, TileAlternativesData> tiles;
+ Vector<Vector2i> tiles_ids;
+ Map<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile
- void tile_set_region(int p_id, const Rect2 &p_region);
- Rect2 tile_get_region(int p_id) const;
+ TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile);
+ const TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const;
- void tile_set_tile_mode(int p_id, TileMode p_tile_mode);
- TileMode tile_get_tile_mode(int p_id) const;
+ void _compute_next_alternative_id(const Vector2i p_atlas_coords);
- void autotile_set_icon_coordinate(int p_id, Vector2 coord);
- Vector2 autotile_get_icon_coordinate(int p_id) const;
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
- void autotile_set_spacing(int p_id, int p_spacing);
- int autotile_get_spacing(int p_id) const;
+ static void _bind_methods();
- void autotile_set_size(int p_id, Size2 p_size);
- Size2 autotile_get_size(int p_id) const;
+public:
+ // Not exposed.
+ virtual void set_tile_set(const TileSet *p_tile_set) override;
+ virtual void notify_tile_data_properties_should_change() override;
+ virtual void reset_state() override;
- void autotile_clear_bitmask_map(int p_id);
- void autotile_set_subtile_priority(int p_id, const Vector2 &p_coord, int p_priority);
- int autotile_get_subtile_priority(int p_id, const Vector2 &p_coord);
- const Map<Vector2, int> &autotile_get_priority_map(int p_id) const;
+ // Base properties.
+ void set_texture(Ref<Texture2D> p_texture);
+ Ref<Texture2D> get_texture() const;
+ void set_margins(Vector2i p_margins);
+ Vector2i get_margins() const;
+ void set_separation(Vector2i p_separation);
+ Vector2i get_separation() const;
+ void set_texture_region_size(Vector2i p_tile_size);
+ Vector2i get_texture_region_size() const;
+
+ // Base tiles.
+ void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); // Create a tile if it does not exists, or add alternative tile if it does.
+ void remove_tile(Vector2i p_atlas_coords); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative
+ virtual bool has_tile(Vector2i p_atlas_coords) const override;
+ bool can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)) const;
+ void move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1));
+ Vector2i get_tile_size_in_atlas(Vector2i p_atlas_coords) const;
+
+ virtual int get_tiles_count() const override;
+ virtual Vector2i get_tile_id(int p_index) const override;
+
+ Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const;
+
+ // Alternative tiles.
+ int create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override = -1);
+ void remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile);
+ void set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id);
+ virtual bool has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const override;
+ int get_next_alternative_tile_id(const Vector2i p_atlas_coords) const;
+
+ virtual int get_alternative_tiles_count(const Vector2i p_atlas_coords) const override;
+ virtual int get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const override;
+
+ // Get data associated to a tile.
+ Object *get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const;
+
+ // Helpers.
+ Vector2i get_atlas_grid_size() const;
+ bool has_tiles_outside_texture();
+ void clear_tiles_outside_texture();
+ Rect2i get_tile_texture_region(Vector2i p_atlas_coords) const;
+ Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const;
+
+ ~TileSetAtlasSource();
+};
- void autotile_set_z_index(int p_id, const Vector2 &p_coord, int p_z_index);
- int autotile_get_z_index(int p_id, const Vector2 &p_coord);
- const Map<Vector2, int> &autotile_get_z_index_map(int p_id) const;
+class TileData : public Object {
+ GDCLASS(TileData, Object);
- void autotile_set_bitmask(int p_id, Vector2 p_coord, uint32_t p_flag);
- uint32_t autotile_get_bitmask(int p_id, Vector2 p_coord);
- const Map<Vector2, uint32_t> &autotile_get_bitmask_map(int p_id);
- Vector2 autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, const Node *p_tilemap_node = nullptr, const Vector2 &p_tile_location = Vector2());
- Vector2 atlastile_get_subtile_by_priority(int p_id, const Node *p_tilemap_node = nullptr, const Vector2 &p_tile_location = Vector2());
+private:
+ const TileSet *tile_set = nullptr;
+ bool allow_transform = true;
+
+ // Rendering
+ bool flip_h = false;
+ bool flip_v = false;
+ bool transpose = false;
+ Vector2i tex_offset = Vector2i();
+ Ref<ShaderMaterial> material = Ref<ShaderMaterial>();
+ Color modulate = Color(1.0, 1.0, 1.0, 1.0);
+ int z_index = 0;
+ Vector2i y_sort_origin = Vector2i();
+ Vector<Ref<OccluderPolygon2D>> occluders;
+
+ // Physics
+ struct PhysicsLayerTileData {
+ struct ShapeTileData {
+ Ref<Shape2D> shape = Ref<Shape2D>();
+ bool one_way = false;
+ float one_way_margin = 1.0;
+ };
+
+ Vector<ShapeTileData> shapes;
+ };
+ Vector<PhysicsLayerTileData> physics;
+ // TODO add support for areas.
- void tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_shape);
- Ref<Shape2D> tile_get_shape(int p_id, int p_shape_id) const;
+ // Terrain
+ int terrain_set = -1;
+ int terrain_peering_bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
- void tile_set_shape_transform(int p_id, int p_shape_id, const Transform2D &p_offset);
- Transform2D tile_get_shape_transform(int p_id, int p_shape_id) const;
+ // Navigation
+ Vector<Ref<NavigationPolygon>> navigation;
- void tile_set_shape_offset(int p_id, int p_shape_id, const Vector2 &p_offset);
- Vector2 tile_get_shape_offset(int p_id, int p_shape_id) const;
+ // Misc
+ double probability = 1.0;
- void tile_set_shape_one_way(int p_id, int p_shape_id, bool p_one_way);
- bool tile_get_shape_one_way(int p_id, int p_shape_id) const;
+ // Custom data
+ Vector<Variant> custom_data;
- void tile_set_shape_one_way_margin(int p_id, int p_shape_id, float p_margin);
- float tile_get_shape_one_way_margin(int p_id, int p_shape_id) const;
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+ static void _bind_methods();
- void tile_clear_shapes(int p_id);
- void tile_add_shape(int p_id, const Ref<Shape2D> &p_shape, const Transform2D &p_transform, bool p_one_way = false, const Vector2 &p_autotile_coord = Vector2());
- int tile_get_shape_count(int p_id) const;
+public:
+ // Not exposed.
+ void set_tile_set(const TileSet *p_tile_set);
+ void notify_tile_data_properties_should_change();
+ void reset_state();
+ void set_allow_transform(bool p_allow_transform);
+ bool is_allowing_transform() const;
+
+ // Rendering
+ void set_flip_h(bool p_flip_h);
+ bool get_flip_h() const;
+ void set_flip_v(bool p_flip_v);
+ bool get_flip_v() const;
+ void set_transpose(bool p_transpose);
+ bool get_transpose() const;
+
+ void set_texture_offset(Vector2i p_texture_offset);
+ Vector2i get_texture_offset() const;
+ void tile_set_material(Ref<ShaderMaterial> p_material);
+ Ref<ShaderMaterial> tile_get_material() const;
+ void set_modulate(Color p_modulate);
+ Color get_modulate() const;
+ void set_z_index(int p_z_index);
+ int get_z_index() const;
+ void set_y_sort_origin(Vector2i p_y_sort_origin);
+ Vector2i get_y_sort_origin() const;
+
+ void set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon);
+ Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const;
+
+ // Physics
+ int get_collision_shapes_count(int p_layer_id) const;
+ void set_collision_shapes_count(int p_layer_id, int p_shapes_count);
+ void add_collision_shape(int p_layer_id);
+ void remove_collision_shape(int p_layer_id, int p_shape_index);
+ void set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref<Shape2D> p_shape);
+ Ref<Shape2D> get_collision_shape_shape(int p_layer_id, int p_shape_index) const;
+ void set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way);
+ bool is_collision_shape_one_way(int p_layer_id, int p_shape_index) const;
+ void set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin);
+ float get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const;
+
+ // Terrain
+ void set_terrain_set(int p_terrain_id);
+ int get_terrain_set() const;
+ void set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_id);
+ int get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
+ bool is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
+
+ // Navigation
+ void set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon);
+ Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id) const;
+
+ // Misc
+ void set_probability(float p_probability);
+ float get_probability() const;
+
+ // Custom data.
+ void set_custom_data(String p_layer_name, Variant p_value);
+ Variant get_custom_data(String p_layer_name) const;
+ void set_custom_data_by_layer_id(int p_layer_id, Variant p_value);
+ Variant get_custom_data_by_layer_id(int p_layer_id) const;
+};
- void tile_set_shapes(int p_id, const Vector<ShapeData> &p_shapes);
- Vector<ShapeData> tile_get_shapes(int p_id) const;
+#include "scene/2d/tile_map.h"
- void tile_set_material(int p_id, const Ref<ShaderMaterial> &p_material);
- Ref<ShaderMaterial> tile_get_material(int p_id) const;
+class TileSetPlugin : public Object {
+ GDCLASS(TileSetPlugin, Object);
- void tile_set_modulate(int p_id, const Color &p_modulate);
- Color tile_get_modulate(int p_id) const;
+public:
+ // Tilemap updates.
+ virtual void tilemap_notification(TileMap *p_tile_map, int p_what){};
+ virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list){};
+ virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){};
+ virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){};
- void tile_set_occluder_offset(int p_id, const Vector2 &p_offset);
- Vector2 tile_get_occluder_offset(int p_id) const;
+ virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){};
+};
- void tile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder);
- Ref<OccluderPolygon2D> tile_get_light_occluder(int p_id) const;
+class TileSetAtlasPluginRendering : public TileSetPlugin {
+ GDCLASS(TileSetAtlasPluginRendering, TileSetPlugin);
- void autotile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder, const Vector2 &p_coord);
- Ref<OccluderPolygon2D> autotile_get_light_occluder(int p_id, const Vector2 &p_coord) const;
- const Map<Vector2, Ref<OccluderPolygon2D>> &autotile_get_light_oclusion_map(int p_id) const;
+private:
+ static constexpr float fp_adjust = 0.00001;
+ bool quadrant_order_dirty = false;
- void tile_set_navigation_polygon_offset(int p_id, const Vector2 &p_offset);
- Vector2 tile_get_navigation_polygon_offset(int p_id) const;
+public:
+ // Tilemap updates
+ virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override;
+ virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override;
+ virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
+ virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
+
+ // Other.
+ static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0));
+};
- void tile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon);
- Ref<NavigationPolygon> tile_get_navigation_polygon(int p_id) const;
+class TileSetAtlasPluginTerrain : public TileSetPlugin {
+ GDCLASS(TileSetAtlasPluginTerrain, TileSetPlugin);
- void autotile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon, const Vector2 &p_coord);
- Ref<NavigationPolygon> autotile_get_navigation_polygon(int p_id, const Vector2 &p_coord) const;
- const Map<Vector2, Ref<NavigationPolygon>> &autotile_get_navigation_map(int p_id) const;
+private:
+ static void _draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
+ static void _draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
+ static void _draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
- void tile_set_z_index(int p_id, int p_z_index);
- int tile_get_z_index(int p_id) const;
+ static void _draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
+ static void _draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
+ static void _draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit);
- void remove_tile(int p_id);
+ static void _draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
+ static void _draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
+ static void _draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
- bool has_tile(int p_id) const;
+public:
+ //virtual void tilemap_notification(const TileMap * p_tile_map, int p_what);
- bool is_tile_bound(int p_drawn_id, int p_neighbor_id);
+ static void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data);
+};
- int find_tile_by_name(const String &p_name) const;
- void get_tile_list(List<int> *p_tiles) const;
+class TileSetAtlasPluginPhysics : public TileSetPlugin {
+ GDCLASS(TileSetAtlasPluginPhysics, TileSetPlugin);
- void clear();
+public:
+ // Tilemap updates
+ virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override;
+ virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override;
+ virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
+ virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
+ virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
+};
- int get_last_unused_tile_id() const;
+class TileSetAtlasPluginNavigation : public TileSetPlugin {
+ GDCLASS(TileSetAtlasPluginNavigation, TileSetPlugin);
- TileSet();
+public:
+ // Tilemap updates
+ virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override;
+ virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override;
+ //virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
+ virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
+ virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override;
};
-VARIANT_ENUM_CAST(TileSet::AutotileBindings);
-VARIANT_ENUM_CAST(TileSet::BitmaskMode);
-VARIANT_ENUM_CAST(TileSet::TileMode);
+VARIANT_ENUM_CAST(TileSet::CellNeighbor);
+VARIANT_ENUM_CAST(TileSet::TerrainMode);
+VARIANT_ENUM_CAST(TileSet::TileShape);
+VARIANT_ENUM_CAST(TileSet::TileLayout);
+VARIANT_ENUM_CAST(TileSet::TileOffsetAxis);
#endif // TILE_SET_H
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 2fa333cc05..7bd1075006 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -253,27 +253,6 @@ bool DisplayServer::get_swap_cancel_ok() {
void DisplayServer::enable_for_stealing_focus(OS::ProcessID pid) {
}
-//plays video natively, in fullscreen, only implemented in mobile for now, likely not possible to implement on linux also.
-Error DisplayServer::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track, int p_screen) {
- ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Native video not supported by this display server.");
-}
-
-bool DisplayServer::native_video_is_playing() const {
- return false;
-}
-
-void DisplayServer::native_video_pause() {
- WARN_PRINT("Native video not supported by this display server.");
-}
-
-void DisplayServer::native_video_unpause() {
- WARN_PRINT("Native video not supported by this display server.");
-}
-
-void DisplayServer::native_video_stop() {
- WARN_PRINT("Native video not supported by this display server.");
-}
-
Error DisplayServer::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
WARN_PRINT("Native dialogs not supported by this display server.");
return OK;
@@ -477,12 +456,6 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("enable_for_stealing_focus", "process_id"), &DisplayServer::enable_for_stealing_focus);
- ClassDB::bind_method(D_METHOD("native_video_play", "path", "volume", "audio_track", "subtitle_track", "screen"), &DisplayServer::native_video_play);
- ClassDB::bind_method(D_METHOD("native_video_is_playing"), &DisplayServer::native_video_is_playing);
- ClassDB::bind_method(D_METHOD("native_video_stop"), &DisplayServer::native_video_stop);
- ClassDB::bind_method(D_METHOD("native_video_pause"), &DisplayServer::native_video_pause);
- ClassDB::bind_method(D_METHOD("native_video_unpause"), &DisplayServer::native_video_unpause);
-
ClassDB::bind_method(D_METHOD("dialog_show", "title", "description", "buttons", "callback"), &DisplayServer::dialog_show);
ClassDB::bind_method(D_METHOD("dialog_input_text", "title", "description", "existing_text", "callback"), &DisplayServer::dialog_input_text);
@@ -518,7 +491,6 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_VIRTUAL_KEYBOARD);
BIND_ENUM_CONSTANT(FEATURE_CURSOR_SHAPE);
BIND_ENUM_CONSTANT(FEATURE_CUSTOM_CURSOR_SHAPE);
- BIND_ENUM_CONSTANT(FEATURE_NATIVE_VIDEO);
BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG);
BIND_ENUM_CONSTANT(FEATURE_CONSOLE_WINDOW);
BIND_ENUM_CONSTANT(FEATURE_IME);
diff --git a/servers/display_server.h b/servers/display_server.h
index 3aab572120..f05aa1f59a 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -97,7 +97,6 @@ public:
FEATURE_VIRTUAL_KEYBOARD,
FEATURE_CURSOR_SHAPE,
FEATURE_CUSTOM_CURSOR_SHAPE,
- FEATURE_NATIVE_VIDEO,
FEATURE_NATIVE_DIALOG,
FEATURE_CONSOLE_WINDOW,
FEATURE_IME,
@@ -324,13 +323,6 @@ public:
virtual void enable_for_stealing_focus(OS::ProcessID pid);
- //plays video natively, in fullscreen, only implemented in mobile for now, likely not possible to implement on linux also.
- virtual Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track, int p_screen = SCREEN_OF_MAIN_WINDOW);
- virtual bool native_video_is_playing() const;
- virtual void native_video_pause();
- virtual void native_video_unpause();
- virtual void native_video_stop();
-
virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback);
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback);
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index b2aaa50421..041476adf3 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -1272,42 +1272,12 @@ void RenderForwardMobile::_fill_instance_data(RenderListType p_render_list, uint
rl->element_info.resize(p_offset + element_total);
- uint32_t repeats = 0;
- GeometryInstanceSurfaceDataCache *prev_surface = nullptr;
for (uint32_t i = 0; i < element_total; i++) {
GeometryInstanceSurfaceDataCache *surface = rl->elements[i + p_offset];
- GeometryInstanceForwardMobile *inst = surface->owner;
-
- bool cant_repeat = inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH || inst->mesh_instance.is_valid();
-
- if (prev_surface != nullptr && !cant_repeat && prev_surface->sort.sort_key1 == surface->sort.sort_key1 && prev_surface->sort.sort_key2 == surface->sort.sort_key2) {
- //this element is the same as the previous one, count repeats to draw it using instancing
- repeats++;
- } else {
- if (repeats > 0) {
- for (uint32_t j = 1; j <= repeats; j++) {
- rl->element_info[p_offset + i - j].repeat = j;
- }
- }
- repeats = 1;
- }
-
RenderElementInfo &element_info = rl->element_info[p_offset + i];
element_info.lod_index = surface->lod_index;
element_info.uses_lightmap = surface->sort.uses_lightmap;
-
- if (cant_repeat) {
- prev_surface = nullptr;
- } else {
- prev_surface = surface;
- }
- }
-
- if (repeats > 0) {
- for (uint32_t j = 1; j <= repeats; j++) {
- rl->element_info[p_offset + element_total - j].repeat = j;
- }
}
}
@@ -1523,13 +1493,12 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(GeometryInstanceForwardMobile::PushConstant));
- uint32_t instance_count = surf->owner->instance_count > 1 ? surf->owner->instance_count : element_info.repeat;
+ uint32_t instance_count = surf->owner->instance_count > 1 ? surf->owner->instance_count : 1;
if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_PARTICLE_TRAILS) {
instance_count /= surf->owner->trail_steps;
}
RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count);
- i += element_info.repeat - 1; //skip equal elements
}
}
@@ -1872,7 +1841,7 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI
sdcache->sort.material_id_low = p_material_id & 0x0000FFFF;
sdcache->sort.material_id_hi = p_material_id >> 16;
sdcache->sort.shader_id = p_shader_id;
- sdcache->sort.geometry_id = p_mesh.get_local_index(); //only meshes can repeat anyway
+ sdcache->sort.geometry_id = p_mesh.get_local_index();
// sdcache->sort.uses_forward_gi = ginstance->can_sdfgi;
sdcache->sort.priority = p_material->priority;
}
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
index d356d88335..232ad0066b 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -363,10 +363,9 @@ protected:
};
struct RenderElementInfo {
- uint32_t repeat : 22;
uint32_t uses_lightmap : 1;
uint32_t lod_index : 8;
- uint32_t reserved : 1; // was uses_forward_gi but we don't use that here
+ uint32_t reserved : 23;
};
template <PassMode p_pass_mode>
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 2de0549e8d..d86c44a206 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -93,10 +93,14 @@ public:
DeviceFamily device_family = DEVICE_UNKNOWN;
uint32_t version_major = 1.0;
uint32_t version_minor = 0.0;
+
// subgroup capabilities
uint32_t subgroup_size = 0;
uint32_t subgroup_in_shaders = 0; // Set flags using SHADER_STAGE_VERTEX_BIT, SHADER_STAGE_FRAGMENT_BIT, etc.
uint32_t subgroup_operations = 0; // Set flags, using SubgroupOperations
+
+ // features
+ bool supports_multiview = false; // If true this device supports multiview options
};
typedef Vector<uint8_t> (*ShaderCompileFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error, const Capabilities *p_capabilities);
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 0d6d3f5e13..a81306b97d 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -2969,6 +2969,20 @@ void ShaderLanguage::get_keyword_list(List<String> *r_keywords) {
}
}
+bool ShaderLanguage::is_control_flow_keyword(String p_keyword) {
+ return p_keyword == "break" ||
+ p_keyword == "case" ||
+ p_keyword == "continue" ||
+ p_keyword == "default" ||
+ p_keyword == "do" ||
+ p_keyword == "else" ||
+ p_keyword == "for" ||
+ p_keyword == "if" ||
+ p_keyword == "return" ||
+ p_keyword == "switch" ||
+ p_keyword == "while";
+}
+
void ShaderLanguage::get_builtin_funcs(List<String> *r_keywords) {
Set<String> kws;
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 470f3d38d5..e00f4dce19 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -748,6 +748,7 @@ public:
static uint32_t get_type_size(DataType p_type);
static void get_keyword_list(List<String> *r_keywords);
+ static bool is_control_flow_keyword(String p_keyword);
static void get_builtin_funcs(List<String> *r_keywords);
struct BuiltInInfo {
diff --git a/tests/test_string.h b/tests/test_string.h
index 94d14517ae..486c17dbbd 100644
--- a/tests/test_string.h
+++ b/tests/test_string.h
@@ -860,10 +860,10 @@ TEST_CASE("[String] match") {
}
TEST_CASE("[String] IPVX address to string") {
- IP_Address ip0("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
- IP_Address ip(0x0123, 0x4567, 0x89ab, 0xcdef, true);
- IP_Address ip2("fe80::52e5:49ff:fe93:1baf");
- IP_Address ip3("::ffff:192.168.0.1");
+ IPAddress ip0("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ IPAddress ip(0x0123, 0x4567, 0x89ab, 0xcdef, true);
+ IPAddress ip2("fe80::52e5:49ff:fe93:1baf");
+ IPAddress ip3("::ffff:192.168.0.1");
String ip4 = "192.168.0.1";
CHECK(ip4.is_valid_ip_address());
diff --git a/thirdparty/enet/godot.cpp b/thirdparty/enet/godot.cpp
index 189de6cc1f..fd7968204b 100644
--- a/thirdparty/enet/godot.cpp
+++ b/thirdparty/enet/godot.cpp
@@ -45,10 +45,10 @@
/// Abstract ENet interface for UDP/DTLS.
class ENetGodotSocket {
public:
- virtual Error bind(IP_Address p_ip, uint16_t p_port) = 0;
- virtual Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) = 0;
- virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) = 0;
- virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) = 0;
+ virtual Error bind(IPAddress p_ip, uint16_t p_port) = 0;
+ virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) = 0;
+ virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) = 0;
+ virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port) = 0;
virtual int set_option(ENetSocketOption p_option, int p_value) = 0;
virtual void close() = 0;
virtual void set_refuse_new_connections(bool p_enable) {} /* Only used by dtls server */
@@ -65,7 +65,7 @@ class ENetUDP : public ENetGodotSocket {
private:
Ref<NetSocket> sock;
- IP_Address local_address;
+ IPAddress local_address;
bool bound = false;
public:
@@ -79,21 +79,21 @@ public:
sock->close();
}
- Error bind(IP_Address p_ip, uint16_t p_port) {
+ Error bind(IPAddress p_ip, uint16_t p_port) {
local_address = p_ip;
bound = true;
return sock->bind(p_ip, p_port);
}
- Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) {
+ Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) {
return sock->get_socket_address(r_ip, r_port);
}
- Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) {
+ Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) {
return sock->sendto(p_buffer, p_len, r_sent, p_ip, p_port);
}
- Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) {
+ Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port) {
Error err = sock->poll(NetSocket::POLL_TYPE_IN, 0);
if (err != OK) {
return err;
@@ -157,7 +157,7 @@ class ENetDTLSClient : public ENetGodotSocket {
bool verify = false;
String for_hostname;
Ref<X509Certificate> cert;
- IP_Address local_address;
+ IPAddress local_address;
public:
ENetDTLSClient(ENetUDP *p_base, Ref<X509Certificate> p_cert, bool p_verify, String p_for_hostname) {
@@ -178,12 +178,12 @@ public:
close();
}
- Error bind(IP_Address p_ip, uint16_t p_port) {
+ Error bind(IPAddress p_ip, uint16_t p_port) {
local_address = p_ip;
return udp->bind(p_port, p_ip);
}
- Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) {
+ Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) {
if (!udp->is_bound()) {
return ERR_UNCONFIGURED;
}
@@ -192,7 +192,7 @@ public:
return OK;
}
- Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) {
+ Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) {
if (!connected) {
udp->connect_to_host(p_ip, p_port);
dtls->connect_to_peer(udp, verify, for_hostname, cert);
@@ -208,7 +208,7 @@ public:
return dtls->put_packet(p_buffer, p_len);
}
- Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) {
+ Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port) {
dtls->poll();
if (dtls->get_status() == PacketPeerDTLS::STATUS_HANDSHAKING) {
return ERR_BUSY;
@@ -250,7 +250,7 @@ class ENetDTLSServer : public ENetGodotSocket {
Ref<UDPServer> udp_server;
Map<String, Ref<PacketPeerDTLS>> peers;
int last_service = 0;
- IP_Address local_address;
+ IPAddress local_address;
public:
ENetDTLSServer(ENetUDP *p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert) {
@@ -273,12 +273,12 @@ public:
udp_server->set_max_pending_connections(p_refuse ? 0 : 16);
}
- Error bind(IP_Address p_ip, uint16_t p_port) {
+ Error bind(IPAddress p_ip, uint16_t p_port) {
local_address = p_ip;
return udp_server->listen(p_port, p_ip);
}
- Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) {
+ Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) {
if (!udp_server->is_listening()) {
return ERR_UNCONFIGURED;
}
@@ -287,7 +287,7 @@ public:
return OK;
}
- Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) {
+ Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) {
String key = String(p_ip) + ":" + itos(p_port);
ERR_FAIL_COND_V(!peers.has(key), ERR_UNAVAILABLE);
Ref<PacketPeerDTLS> peer = peers[key];
@@ -302,12 +302,12 @@ public:
return err;
}
- Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) {
+ Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port) {
udp_server->poll();
// TODO limits? Maybe we can better enforce allowed connections!
if (udp_server->is_connection_available()) {
Ref<PacketPeerUDP> udp = udp_server->take_connection();
- IP_Address peer_ip = udp->get_packet_address();
+ IPAddress peer_ip = udp->get_packet_address();
int peer_port = udp->get_packet_port();
Ref<PacketPeerDTLS> peer = server->take_connection(udp);
PacketPeerDTLS::Status status = peer->get_status();
@@ -397,7 +397,7 @@ void enet_time_set(enet_uint32 newTimeBase) {
}
int enet_address_set_host(ENetAddress *address, const char *name) {
- IP_Address ip = IP::get_singleton()->resolve_hostname(name);
+ IPAddress ip = IP::get_singleton()->resolve_hostname(name);
ERR_FAIL_COND_V(!ip.is_valid(), -1);
enet_address_set_ip(address, ip.get_ipv6(), 16);
@@ -442,9 +442,9 @@ void enet_host_refuse_new_connections(ENetHost *host, int p_refuse) {
}
int enet_socket_bind(ENetSocket socket, const ENetAddress *address) {
- IP_Address ip;
+ IPAddress ip;
if (address->wildcard) {
- ip = IP_Address("*");
+ ip = IPAddress("*");
} else {
ip.set_ipv6(address->host);
}
@@ -466,7 +466,7 @@ int enet_socket_send(ENetSocket socket, const ENetAddress *address, const ENetBu
ERR_FAIL_COND_V(address == nullptr, -1);
ENetGodotSocket *sock = (ENetGodotSocket *)socket;
- IP_Address dest;
+ IPAddress dest;
Error err;
size_t i = 0;
@@ -508,7 +508,7 @@ int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buf
ENetGodotSocket *sock = (ENetGodotSocket *)socket;
int read;
- IP_Address ip;
+ IPAddress ip;
Error err = sock->recvfrom((uint8_t *)buffers[0].data, buffers[0].dataLength, read, ip, address->port);
if (err == ERR_BUSY) {
@@ -525,7 +525,7 @@ int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buf
}
int enet_socket_get_address (ENetSocket socket, ENetAddress * address) {
- IP_Address ip;
+ IPAddress ip;
uint16_t port;
ENetGodotSocket *sock = (ENetGodotSocket *)socket;